def add_new_node(tree, level): """ Randomly select an inner node, and add a new child, and ensure that it satisfies the tree rules. Parameters ----------- tree Original Process Tree level The maximal depth of the chosen node """ node = randomly_choose_node(tree, level) tmp_node = copy.deepcopy(node) add_node = ProcessTree( None, node, None, pt_gene_utils.get_cur_label( pt_mani_utils.non_none_leaves_number(tree) + 1)) if node.operator == Operator.LOOP: child = node.children[1] new_child = ProcessTree(Operator.XOR, node, [child, add_node], None) child.parent = new_child add_node.parent = new_child node.children[1] = new_child else: node.children.append(add_node) return tmp_node, node
def __init__(self, tree): i = 0 while i < len(tree.children): tree.children[i] = GenerationTree(tree.children[i]) tree.children[i].parent = self i = i + 1 ProcessTree.__init__(self, operator=tree.operator, parent=tree.parent, children=tree.children, label=tree.label)
def create_pt_of_three_node(root, op): child1, child2 = ProcessTree(), ProcessTree() root.children = [child1, child2] root.operator = op child1.parent = root child2.parent = root if op == Operator.LOOP: root.children.append(ProcessTree(None, root, None, None)) return [child1, child2]
def create_new_binary_process_tree(no_number): """ Random create a new tree with fixed node number Parameters ----------- no_number Node number of the created process tree Returns ------------ tree Process Tree (None, if there no normal process tree with such node) """ root = ProcessTree() if no_number == 1: return ProcessTree(None, None, None, 'a') if no_number == 2: return None operators, enable, cur_num = [_ for _ in Operator], [root], 1 while cur_num + 3 <= no_number: node = random.choice(enable) enable.remove(node) children = create_pt_of_three_node(node, random.choice(operators)) enable += children if node.parent is not None and node.operator == node.parent.operator and node.operator != Operator.LOOP: cur_num += 1 elif node.operator == Operator.LOOP: cur_num += 3 else: cur_num += 2 if no_number - cur_num == 1: # must be agree with parent and not equal LOOP flag = False for i in range(len(enable)): if enable[i].parent.operator != Operator.LOOP: node = enable.pop(i) children = create_pt_of_three_node(node, node.parent.operator) enable += children flag = True break while not flag: root = create_new_binary_process_tree(no_number) flag = False if root is None else True print('could not create a tree, try again!!! Maybe infinite!!!') elif no_number - cur_num == 2: # must differ with parent and not equal LOOP node = random.choice(enable) enable.remove(node) op = random.choice(operators) while op == Operator.LOOP or (node.parent is not None and op == node.parent.operator)\ or node.parent is None: op = random.choice(operators) children = create_pt_of_three_node(node, op) enable += children add_label_to_leaf(enable) return root
def process_tree_to_binary_process_tree(pt: ProcessTree) -> ProcessTree: if len(pt.children) > 2: new_subtree = ProcessTree() new_subtree.operator = pt.operator new_subtree.children = pt.children[1:] pt.children = pt.children[:1] pt.children.append(new_subtree) for c in pt.children: process_tree_to_binary_process_tree(c) return pt
def execute_enabled(enabled, open, closed, execution_sequence=None): """ Execute an enabled node of the process tree Parameters ----------- enabled Enabled nodes open Open nodes closed Closed nodes execution_sequence Execution sequence Returns ----------- execution_sequence Execution sequence """ execution_sequence = list( ) if execution_sequence is None else execution_sequence vertex = random.sample(list(enabled), 1)[0] enabled.remove(vertex) open.add(vertex) execution_sequence.append((vertex, pt_st.State.OPEN)) if len(vertex.children) > 0: if vertex.operator is pt_opt.Operator.LOOP: while len(vertex.children) < 3: vertex.children.append(ProcessTree(parent=vertex)) if vertex.operator is pt_opt.Operator.SEQUENCE or vertex.operator is pt_opt.Operator.LOOP: c = vertex.children[0] enabled.add(c) execution_sequence.append((c, pt_st.State.ENABLED)) elif vertex.operator is pt_opt.Operator.PARALLEL: enabled |= set(vertex.children) for x in vertex.children: if x in closed: closed.remove(x) map(lambda c: execution_sequence.append((c, pt_st.State.ENABLED)), vertex.children) elif vertex.operator is pt_opt.Operator.XOR: vc = vertex.children c = vc[random.randint(0, len(vc) - 1)] enabled.add(c) execution_sequence.append((c, pt_st.State.ENABLED)) elif vertex.operator is pt_opt.Operator.OR: some_children = [ c for c in vertex.children if random.random() < 0.5 ] enabled |= set(some_children) for x in some_children: if x in closed: closed.remove(x) map(lambda c: execution_sequence.append((c, pt_st.State.ENABLED)), some_children) else: close(vertex, enabled, open, closed, execution_sequence) return execution_sequence
def calculate_optimal_alignment(pt: ProcessTree, trace: Trace, parameters=None): if parameters is None: parameters = {} align_variant = exec_utils.get_param_value( Parameters.CLASSIC_ALIGNMENTS_VARIANT, parameters, Variants.VERSION_STATE_EQUATION_A_STAR) conversion_version = exec_utils.get_param_value( Parameters.CONVERSION_VERSION, parameters, pt_converter.Variants.TO_PETRI_NET_TRANSITION_BORDERED) parent = pt.parent pt.parent = None net, im, fm = pt_converter.apply(pt, variant=conversion_version) # in this way, also the other parameters are passed to alignments alignment_parameters = copy(parameters) alignment_parameters[ AlignParameters.PARAM_ALIGNMENT_RESULT_IS_SYNC_PROD_AWARE] = True alignment = get_alignment(trace, net, im, fm, variant=align_variant, parameters=alignment_parameters) pt.parent = parent res = [] # if the alignment has terminated prematurely due to time constraints, raise an Exception if alignment is None: raise AlignmentNoneException("alignment terminated prematurely") if conversion_version == pt_converter.Variants.TO_PETRI_NET_TRANSITION_BORDERED or conversion_version == pt_converter.Variants.TO_PETRI_NET_TRANSITION_BORDERED.value: # remove invisible model moves from alignment steps that do not belong to a silent model move in the process tree # this is possible only if the TO_PETRI_NET_TRANSITION_BORDERED variant is used for a in alignment["alignment"]: if not (a[0][0] == SKIP and not a[0][1].isdigit()): res.append(a[1]) else: for a in alignment["alignment"]: res.append(a[1]) return res
def reduce(bottomup_nodes: List[ProcessTree], fps: Dict[str, Any], activities: Set[str]) -> ProcessTree: """ Reduce a process tree replacing the skippable elements that have empty intersection with the trace. Parameters ----------------- bottomup_nodes List of nodes of the process tree (that are process trees by themselves) in a bottomup order fps Footprints of the process tree activities Set of activities in the trace Returns ------------------ tree Reduced process tree """ from pm4py.algo.discovery.footprints.outputs import Outputs i = 0 while i < len(bottomup_nodes) - 1: node = bottomup_nodes[i] parent = node.parent is_skippable = fps[id(node)][Outputs.SKIPPABLE.value] node_activities = fps[id(node)][Outputs.ACTIVITIES.value] if is_skippable and not node_activities.intersection(activities): pt = ProcessTree() pt.parent = parent parent.children[parent.children.index(node)] = pt i = i + 1 return fold(bottomup_nodes[-1])
def remove_node(tree, level): """ Randomly select one node, and remove one of the child Parameters ----------- tree Original Process Tree level The maximal depth of the chosen node """ node = randomly_choose_node(tree, level) tmp_node = copy.deepcopy(node) if node.operator == Operator.LOOP: node.children[random.randint(0, 1)] = ProcessTree(None, node, None, None) else: node.children.remove(random.choice(node.children)) pt_normalize.apply(tree) return tmp_node, node
def change_node_operator(tree, level): """ Randomly select an inner node, and replace the operator using others, and ensure that it satisfies the tree rules. Parameters ----------- tree Original Process Tree level The maximal depth of the chosen node """ node = randomly_choose_node(tree, level) tmp_node = copy.deepcopy(node) operators = [_ for _ in Operator] op = random.choice(operators) while op == node.operator: op = random.choice(operators) if op == Operator.LOOP: while len(node.children) > 2: node.children.pop() node.children.append(ProcessTree(None, node, None, None)) node.operator = op return tmp_node, node
def get_repr(spec_tree_struct, rec_depth, contains_empty_traces=False): """ Get the representation of a process tree Parameters ----------- spec_tree_struct Internal tree structure (after application of Inductive Miner) rec_depth Current recursion depth contains_empty_traces Boolean value that is True if the event log from which the DFG has been extracted contains empty traces Returns ----------- final_tree_repr Representation of the tree (could be printed, transformed, viewed) """ need_loop_on_subtree = check_loop_need(spec_tree_struct) if contains_empty_traces and rec_depth == 0: rec_depth = rec_depth + 1 child_tree = None if spec_tree_struct.detected_cut == "flower" or ( spec_tree_struct.detected_cut == "base_concurrent" and need_loop_on_subtree): final_tree_repr = ProcessTree(operator=Operator.LOOP) child_tree = ProcessTree(operator=Operator.XOR) child_tree_redo = ProcessTree(label=None) child_tree_exit = ProcessTree(label=None) final_tree_repr.children.append(child_tree) final_tree_repr.children.append(child_tree_redo) final_tree_repr.children.append(child_tree_exit) child_tree.parent = final_tree_repr child_tree_redo.parent = final_tree_repr child_tree_exit.parent = final_tree_repr elif spec_tree_struct.detected_cut == "base_concurrent": if len(spec_tree_struct.activities) > 1 or spec_tree_struct.must_insert_skip: final_tree_repr = ProcessTree(operator=Operator.XOR) child_tree = final_tree_repr else: final_tree_repr = ProcessTree(operator=None, label=None) elif spec_tree_struct.detected_cut == "sequential": final_tree_repr = ProcessTree(operator=Operator.SEQUENCE) child_tree = final_tree_repr elif spec_tree_struct.detected_cut == "loopCut": final_tree_repr = ProcessTree(operator=Operator.LOOP) child_tree = final_tree_repr elif spec_tree_struct.detected_cut == "concurrent": final_tree_repr = ProcessTree(operator=Operator.XOR) child_tree = final_tree_repr elif spec_tree_struct.detected_cut == "parallel": final_tree_repr = ProcessTree(operator=Operator.PARALLEL) child_tree = final_tree_repr if spec_tree_struct.detected_cut == "base_concurrent" or spec_tree_struct.detected_cut == "flower": for act in spec_tree_struct.activities: if child_tree is None: new_vis_trans = get_transition(act) child_tree = new_vis_trans final_tree_repr = child_tree else: new_vis_trans = get_transition(act) child_tree.children.append(new_vis_trans) new_vis_trans.parent = child_tree if spec_tree_struct.detected_cut == "sequential" or spec_tree_struct.detected_cut == "loopCut": #if spec_tree_struct.detected_cut == "loopCut": # spec_tree_struct.children[0].must_insert_skip = True for ch in spec_tree_struct.children: child = get_repr(ch, rec_depth + 1) child_tree.children.append(child) child.parent = child_tree if spec_tree_struct.detected_cut == "loopCut" and len(spec_tree_struct.children) < 2: while len(spec_tree_struct.children) < 2: child = ProcessTree() child_tree.children.append(child) child.parent = child_tree spec_tree_struct.children.append(None) if spec_tree_struct.detected_cut == "parallel": for child in spec_tree_struct.children: child_final = get_repr(child, rec_depth + 1) child_tree.children.append(child_final) child_final.parent = child_tree if spec_tree_struct.detected_cut == "concurrent": for child in spec_tree_struct.children: child_final = get_repr(child, rec_depth + 1) child_tree.children.append(child_final) child_final.parent = child_tree if spec_tree_struct.must_insert_skip: skip = get_new_hidden_trans() if spec_tree_struct.detected_cut == "base_concurrent": child_tree.children.append(skip) skip.parent = child_tree else: master_tree_repr = ProcessTree(operator=Operator.XOR) master_tree_repr.children.append(final_tree_repr) final_tree_repr.parent = master_tree_repr master_tree_repr.children.append(skip) skip.parent = master_tree_repr return master_tree_repr if contains_empty_traces and rec_depth == 1: master_tree_repr = ProcessTree(operator=Operator.XOR) master_tree_repr.children.append(final_tree_repr) final_tree_repr.parent = master_tree_repr skip_transition = ProcessTree() master_tree_repr.children.append(skip_transition) skip_transition.parent = master_tree_repr return master_tree_repr return final_tree_repr
def get_new_hidden_trans(): """ Create a hidden node (transition) in the process tree """ return ProcessTree(operator=None, label=None)
def get_repr(spec_tree_struct, rec_depth, counts, must_add_skip=False, contains_empty_traces=False): """ Get the representation of a process tree Parameters ----------- spec_tree_struct Internal tree structure (after application of Inductive Miner) rec_depth Current recursion depth counts Count object (keep track of the number of nodes (transitions) added to the tree must_add_skip Boolean value that indicate if we are forced to add the skip contains_empty_traces Boolean value that is True if the event log from which the DFG has been extracted contains empty traces Returns ----------- final_tree_repr Representation of the tree (could be printed, transformed, viewed) """ need_loop_on_subtree = check_loop_need(spec_tree_struct) final_tree_repr = ProcessTree() final_tree_repr.rec_depth = rec_depth if contains_empty_traces and rec_depth == 0: rec_depth = rec_depth + 1 child_tree = ProcessTree() if spec_tree_struct.detected_cut == "flower" or ( spec_tree_struct.detected_cut == "base_concurrent" and need_loop_on_subtree): final_tree_repr.operator = tree_constants.LOOP_OPERATOR child_tree = ProcessTree() child_tree.operator = tree_constants.EXCLUSIVE_OPERATOR rec_depth = rec_depth + 1 child_tree.rec_depth = rec_depth final_tree_repr.add_subtree(child_tree) elif spec_tree_struct.detected_cut == "base_concurrent": final_tree_repr.operator = tree_constants.EXCLUSIVE_OPERATOR child_tree = final_tree_repr elif spec_tree_struct.detected_cut == "sequential": final_tree_repr.operator = tree_constants.SEQUENTIAL_OPERATOR child_tree = final_tree_repr elif spec_tree_struct.detected_cut == "loopCut": final_tree_repr.operator = tree_constants.LOOP_OPERATOR child_tree = final_tree_repr elif spec_tree_struct.detected_cut == "concurrent": final_tree_repr.operator = tree_constants.EXCLUSIVE_OPERATOR child_tree = final_tree_repr elif spec_tree_struct.detected_cut == "parallel": final_tree_repr.operator = tree_constants.PARALLEL_OPERATOR child_tree = final_tree_repr if spec_tree_struct.detected_cut == "base_concurrent" or spec_tree_struct.detected_cut == "flower": for act in spec_tree_struct.activities: child_tree.add_transition(get_transition(counts, act)) if verify_skip_transition_necessity( must_add_skip, spec_tree_struct.initial_dfg, spec_tree_struct.activities) and counts.num_visible_trans > 0: # add skip transition child_tree.add_transition( get_new_hidden_trans(counts, type_trans="skip")) if spec_tree_struct.detected_cut == "sequential" or spec_tree_struct.detected_cut == "loopCut": for ch in spec_tree_struct.children: child, counts = get_repr( ch, rec_depth + 1, counts, must_add_skip=verify_skip_transition_necessity( False, ch.initial_dfg, ch.activities)) child_tree.add_subtree(child) if spec_tree_struct.detected_cut == "parallel": m_add_skip = verify_skip_for_parallel_cut(spec_tree_struct.dfg, spec_tree_struct.children) for child in spec_tree_struct.children: m_add_skip_final = verify_skip_transition_necessity( m_add_skip, spec_tree_struct.dfg, spec_tree_struct.activities) child_final, counts = get_repr(child, rec_depth + 1, counts, must_add_skip=m_add_skip_final) child_tree.add_subtree(child_final) if spec_tree_struct.detected_cut == "concurrent": for child in spec_tree_struct.children: m_add_skip_final = verify_skip_transition_necessity( False, spec_tree_struct.dfg, spec_tree_struct.activities) child_final, counts = get_repr(child, rec_depth + 1, counts, must_add_skip=m_add_skip_final) child_tree.add_subtree(child_final) if contains_empty_traces and rec_depth == 1: master_tree_repr = ProcessTree() master_tree_repr.rec_depth = 0 master_tree_repr.operator = tree_constants.EXCLUSIVE_OPERATOR master_tree_repr.add_transition( get_new_hidden_trans(counts, type_trans="skip")) master_tree_repr.add_subtree(final_tree_repr) return master_tree_repr, counts return final_tree_repr, counts
def recursively_add_tree(parent_tree, tree, net, initial_entity_subtree, final_entity_subtree, counts, rec_depth, force_add_skip=False): """ Recursively add the subtrees to the Petri net Parameters ----------- parent_tree Parent tree tree Current subtree net Petri net initial_entity_subtree Initial entity (place/transition) that should be attached from the subtree final_entity_subtree Final entity (place/transition) that should be attached from the subtree counts Counts object (keeps the number of places, transitions and hidden transitions) rec_depth Recursion depth of the current iteration force_add_skip Boolean value that tells if the addition of a skip is mandatory Returns ---------- net Updated Petri net counts Updated counts object (keeps the number of places, transitions and hidden transitions) final_place Last place added in this recursion """ if type(initial_entity_subtree) is PetriNet.Transition: initial_place = get_new_place(counts) net.places.add(initial_place) add_arc_from_to(initial_entity_subtree, initial_place, net) else: initial_place = initial_entity_subtree if final_entity_subtree is not None and type( final_entity_subtree) is PetriNet.Place: final_place = final_entity_subtree else: final_place = get_new_place(counts) net.places.add(final_place) if final_entity_subtree is not None and type( final_entity_subtree) is PetriNet.Transition: add_arc_from_to(final_place, final_entity_subtree, net) tree_childs = [child for child in tree.children] if tree.operator is None: trans = tree if trans.label is None: petri_trans = get_new_hidden_trans(counts, type_trans="skip") else: petri_trans = get_transition(counts, trans.label) net.transitions.add(petri_trans) add_arc_from_to(initial_place, petri_trans, net) add_arc_from_to(petri_trans, final_place, net) if tree.operator == Operator.XOR: for subtree in tree_childs: net, counts, intermediate_place = recursively_add_tree( tree, subtree, net, initial_place, final_place, counts, rec_depth + 1, force_add_skip=force_add_skip) elif tree.operator == Operator.PARALLEL: new_initial_trans = get_new_hidden_trans(counts, type_trans="tauSplit") net.transitions.add(new_initial_trans) add_arc_from_to(initial_place, new_initial_trans, net) new_final_trans = get_new_hidden_trans(counts, type_trans="tauJoin") net.transitions.add(new_final_trans) add_arc_from_to(new_final_trans, final_place, net) for subtree in tree_childs: net, counts, intermediate_place = recursively_add_tree( tree, subtree, net, new_initial_trans, new_final_trans, counts, rec_depth + 1, force_add_skip=force_add_skip) elif tree.operator == Operator.SEQUENCE: intermediate_place = initial_place for i in range(len(tree_childs)): final_connection_place = None if i == len(tree_childs) - 1: final_connection_place = final_place net, counts, intermediate_place = recursively_add_tree( tree, tree_childs[i], net, intermediate_place, final_connection_place, counts, rec_depth + 1, force_add_skip=force_add_skip) elif tree.operator == Operator.LOOP: # if not parent_tree.operator == Operator.SEQUENCE: new_initial_place = get_new_place(counts) net.places.add(new_initial_place) init_loop_trans = get_new_hidden_trans(counts, type_trans="init_loop") net.transitions.add(init_loop_trans) add_arc_from_to(initial_place, init_loop_trans, net) add_arc_from_to(init_loop_trans, new_initial_place, net) initial_place = new_initial_place loop_trans = get_new_hidden_trans(counts, type_trans="loop") net.transitions.add(loop_trans) if len(tree_childs) == 1: net, counts, intermediate_place = recursively_add_tree( tree, tree_childs[0], net, initial_place, final_place, counts, rec_depth + 1, force_add_skip=force_add_skip) add_arc_from_to(final_place, loop_trans, net) add_arc_from_to(loop_trans, initial_place, net) else: dummy = ProcessTree() do = tree_childs[0] redo = tree_childs[1] exit = tree_childs[2] if len(tree_childs) > 2 and ( tree_childs[2].label is not None or tree_childs[2].children) else dummy net, counts, int1 = recursively_add_tree( tree, do, net, initial_place, None, counts, rec_depth + 1, force_add_skip=force_add_skip) net, counts, int2 = recursively_add_tree( tree, redo, net, int1, None, counts, rec_depth + 1, force_add_skip=force_add_skip) net, counts, int3 = recursively_add_tree( tree, exit, net, int1, final_place, counts, rec_depth + 1, force_add_skip=force_add_skip) looping_place = int2 add_arc_from_to(looping_place, loop_trans, net) add_arc_from_to(loop_trans, initial_place, net) if force_add_skip: skip_trans = get_new_hidden_trans(counts, type_trans="skip") net.transitions.add(skip_trans) add_arc_from_to(initial_place, skip_trans, net) add_arc_from_to(skip_trans, final_place, net) return net, counts, final_place
def get_repr(spec_tree_struct, rec_depth, contains_empty_traces=False): """ Get the representation of a process tree Parameters ----------- spec_tree_struct Internal tree structure (after application of Inductive Miner) rec_depth Current recursion depth contains_empty_traces Boolean value that is True if the event log from which the DFG has been extracted contains empty traces Returns ----------- final_tree_repr Representation of the tree (could be printed, transformed, viewed) """ activity_key = exec_utils.get_param_value(Parameters.ACTIVITY_KEY, spec_tree_struct.parameters, xes_constants.DEFAULT_NAME_KEY) base_cases = ('empty_log', 'single_activity') cut = ('concurrent', 'sequential', 'parallel', 'loopCut') # note that the activity_once_per_trace is not included here, as it is can be dealt with as a parallel cut fall_throughs = ('empty_trace', 'strict_tau_loop', 'tau_loop', 'flower') # if a cut was detected in the current subtree: if spec_tree_struct.detected_cut in cut: if spec_tree_struct.detected_cut == "sequential": final_tree_repr = ProcessTree(operator=Operator.SEQUENCE) elif spec_tree_struct.detected_cut == "loopCut": final_tree_repr = ProcessTree(operator=Operator.LOOP) elif spec_tree_struct.detected_cut == "concurrent": final_tree_repr = ProcessTree(operator=Operator.XOR) elif spec_tree_struct.detected_cut == "parallel": final_tree_repr = ProcessTree(operator=Operator.PARALLEL) if not (spec_tree_struct.detected_cut == "loopCut" and len(spec_tree_struct.children) >= 3): for ch in spec_tree_struct.children: # get the representation of the current child (from children in the subtree-structure): child = get_repr(ch, rec_depth + 1) # add connection from child_tree to child_final and the other way around: final_tree_repr.children.append(child) child.parent = final_tree_repr else: child = get_repr(spec_tree_struct.children[0], rec_depth + 1) final_tree_repr.children.append(child) child.parent = final_tree_repr redo_child = ProcessTree(operator=Operator.XOR) for ch in spec_tree_struct.children[1:]: child = get_repr(ch, rec_depth + 1) redo_child.children.append(child) child.parent = redo_child final_tree_repr.children.append(redo_child) redo_child.parent = final_tree_repr if spec_tree_struct.detected_cut == "loopCut" and len( spec_tree_struct.children) < 3: while len(spec_tree_struct.children) < 2: child = ProcessTree() final_tree_repr.children.append(child) child.parent = final_tree_repr spec_tree_struct.children.append(None) if spec_tree_struct.detected_cut in base_cases: # in the base case of an empty log, we only return a silent transition if spec_tree_struct.detected_cut == "empty_log": return ProcessTree(operator=None, label=None) # in the base case of a single activity, we return a tree consisting of the single activity elif spec_tree_struct.detected_cut == "single_activity": act_a = spec_tree_struct.log[0][0][activity_key] return ProcessTree(operator=None, label=act_a) if spec_tree_struct.detected_cut in fall_throughs: if spec_tree_struct.detected_cut == "empty_trace": # should return XOR(tau, IM(L') ) final_tree_repr = ProcessTree(operator=Operator.XOR) final_tree_repr.children.append( ProcessTree(operator=None, label=None)) # iterate through all children of the current node for ch in spec_tree_struct.children: child = get_repr(ch, rec_depth + 1) final_tree_repr.children.append(child) child.parent = final_tree_repr elif spec_tree_struct.detected_cut == "strict_tau_loop" or spec_tree_struct.detected_cut == "tau_loop": # should return LOOP( IM(L'), tau) final_tree_repr = ProcessTree(operator=Operator.LOOP) # iterate through all children of the current node if spec_tree_struct.children: for ch in spec_tree_struct.children: child = get_repr(ch, rec_depth + 1) final_tree_repr.children.append(child) child.parent = final_tree_repr else: for ch in spec_tree_struct.activities: child = get_transition(ch) final_tree_repr.append(child) child.parent = final_tree_repr # add a silent tau transition as last child of the current node final_tree_repr.children.append( ProcessTree(operator=None, label=None)) elif spec_tree_struct.detected_cut == "flower": # should return something like LOOP(XOR(a,b,c,d,...), tau) final_tree_repr = ProcessTree(operator=Operator.LOOP) xor_child = ProcessTree(operator=Operator.XOR, parent=final_tree_repr) # append all the activities in the current subtree to the XOR part to allow for any behaviour for ch in spec_tree_struct.activities: child = get_transition(ch) xor_child.children.append(child) child.parent = xor_child final_tree_repr.children.append(xor_child) # now add the tau to the children to get the wanted output final_tree_repr.children.append( ProcessTree(operator=None, label=None)) return final_tree_repr
def export_ptree_tree(tree, parameters=None): """ Exports the XML tree from a process tree Parameters ----------------- tree Process tree parameters Parameters of the algorithm Returns ----------------- xml_tree XML tree object """ tree = copy.deepcopy(tree) if parameters is None: parameters = {} nodes = get_list_nodes_from_tree(tree, parameters=parameters) nodes_dict = {(id(x), x): str(uuid.uuid4()) for x in nodes} # make sure that in the exporting, loops have 3 children # (for ProM compatibility) # just add a skip as third child for node in nodes: if node.operator == Operator.LOOP and len(node.children) < 3: third_children = ProcessTree(operator=None, label=None) third_children.parent = node node.children.append(third_children) nodes_dict[(id(third_children), third_children)] = str(uuid.uuid4()) # repeat twice (structure has changed) nodes = get_list_nodes_from_tree(tree, parameters=parameters) nodes_dict = {(id(x), x): str(uuid.uuid4()) for x in nodes} root = etree.Element("ptml") processtree = etree.SubElement(root, "processTree") processtree.set("name", str(uuid.uuid4())) processtree.set("root", nodes_dict[(id(tree), tree)]) processtree.set("id", str(uuid.uuid4())) for node in nodes: nk = nodes_dict[(id(node), node)] child = None if node.operator is None: if node.label is None: child = etree.SubElement(processtree, "automaticTask") child.set("name", "") else: child = etree.SubElement(processtree, "manualTask") child.set("name", node.label) else: if node.operator is Operator.SEQUENCE: child = etree.SubElement(processtree, "sequence") elif node.operator is Operator.XOR: child = etree.SubElement(processtree, "xor") elif node.operator is Operator.PARALLEL: child = etree.SubElement(processtree, "and") elif node.operator is Operator.OR: child = etree.SubElement(processtree, "or") elif node.operator is Operator.LOOP: child = etree.SubElement(processtree, "xorLoop") child.set("name", "") child.set("id", nk) for node in nodes: if not node == tree: child = etree.SubElement(processtree, "parentsNode") child.set("id", str(uuid.uuid4())) child.set("sourceId", nodes_dict[(id(node.parent), node.parent)]) child.set("targetId", nodes_dict[(id(node), node)]) tree = etree.ElementTree(root) return tree
def execute_enabled(enabled, open, closed, actdict, execution_sequence=None): """ Execute an enabled node of the process tree Parameters ----------- enabled Enabled nodes open Open nodes closed Closed nodes execution_sequence Execution sequence Returns ----------- execution_sequence Execution sequence """ execution_sequence = list( ) if execution_sequence is None else execution_sequence vertex = random.sample(enabled, 1)[0] enabled.remove(vertex) open.add(vertex) #print(vertex,'vertex') execution_sequence.append((vertex, pt_st.State.OPEN)) if len(vertex.children) > 0: #print(vertex.children,'vertex.children') if vertex.operator is pt_opt.Operator.LOOP: while len(vertex.children) < 3: vertex.children.append(ProcessTree(parent=vertex)) if vertex.operator is pt_opt.Operator.SEQUENCE or vertex.operator is pt_opt.Operator.LOOP: c = vertex.children[0] enabled.add(c) execution_sequence.append((c, pt_st.State.ENABLED)) elif vertex.operator is pt_opt.Operator.PARALLEL: enabled |= set(vertex.children) #print(set(vertex.children),'set(vertex.children)') for x in vertex.children: if x in closed: closed.remove(x) map(lambda c: execution_sequence.append((c, pt_st.State.ENABLED)), vertex.children) elif vertex.operator is pt_opt.Operator.XOR: #print(vertex.parent,'vertex.parent') #print(vertex.operator,'vertex.operator') #pre = execution_sequence[-1] vc = vertex.children #print(vc,'vc') vcl = [ele.label for ele in vc] #print('line164',[ele.label for ele in vc]) #compute the number of none, and then probability. nonec = 0 probdominator = 0 allnone = 1 allnotnone = 1 for ele in vcl: if ele == None: nonec += 1 allnotnone = 0 else: probdominator += actdict[ele] allnone = 0 if allnone == 1: nonec = nonec / 2 if allnotnone == 1: factor = 1 else: factor = 0.5 vclprob = [] for ele in vcl: if ele == None and vclprob == []: #vclprob.append(1/(nonec+1)) vclprob.append(1 / (2 * nonec)) #vclprob.append(0.1) elif ele == None and vclprob != []: vclprob.append(vclprob[-1] + 1 / (2 * nonec)) #vclprob.append(0.1) else: for key in actdict: if key == ele and vclprob == []: vclprob.append(factor * actdict[key] / probdominator) break elif key == ele and vclprob != []: vclprob.append(vclprob[-1] + factor * actdict[key] / probdominator) break #print(vcl,vclprob) r = random.random() for i, ele in enumerate(vclprob): if r <= ele: index = i break c = vc[index] #c = vc[random.randint(0,len(vc)-1)] #print(c,'c') enabled.add(c) #print(execution_sequence[-1],execution_sequence[-1][0].label,'execution_sequence[-1]') execution_sequence.append((c, pt_st.State.ENABLED)) #print(execution_sequence,'execution_sequence in XOR') elif vertex.operator is pt_opt.Operator.OR: vcl = [ele.label for ele in vertex.children] vclprob = [] for ele in vcl: if ele == None: vclprob.append(0.5) else: for key in actdict: if ele == key: vclprob.append(actdict[key]) #vclprob.append(0.5) some_children = [] for i, c in enumerate(vertex.children): if random.random() <= vclprob[i]: some_children.append(c) #some_children = [c for c in vertex.children if random.random() < 0.5] enabled |= set(some_children) for x in some_children: if x in closed: closed.remove(x) map(lambda c: execution_sequence.append((c, pt_st.State.ENABLED)), some_children) #print(execution_sequence,'execution_sequence in OR') else: close(vertex, enabled, open, closed, execution_sequence) #print(execution_sequence,'line169') return execution_sequence
def get_transition(label): """ Create a node (transition) with the specified label in the process tree """ return ProcessTree(operator=None, label=label)
def get_repr(spec_tree_struct, rec_depth, must_add_skip=False, contains_empty_traces=False): """ Get the representation of a process tree Parameters ----------- spec_tree_struct Internal tree structure (after application of Inductive Miner) rec_depth Current recursion depth must_add_skip Boolean value that indicate if we are forced to add the skip contains_empty_traces Boolean value that is True if the event log from which the DFG has been extracted contains empty traces Returns ----------- final_tree_repr Representation of the tree (could be printed, transformed, viewed) """ need_loop_on_subtree = check_loop_need(spec_tree_struct) if contains_empty_traces and rec_depth == 0: rec_depth = rec_depth + 1 # TODO child_tree = ProcessTree() if spec_tree_struct.detected_cut == "flower" or ( spec_tree_struct.detected_cut == "base_concurrent" and need_loop_on_subtree): final_tree_repr = ProcessTree(operator=Operator.LOOP) child_tree = ProcessTree(operator=Operator.XOR) child_tree_redo = ProcessTree(label=None) child_tree_exit = ProcessTree(label=None) final_tree_repr.children.append(child_tree) final_tree_repr.children.append(child_tree_redo) final_tree_repr.children.append(child_tree_exit) child_tree.parent = final_tree_repr child_tree_redo.parent = final_tree_repr child_tree_exit.parent = final_tree_repr elif spec_tree_struct.detected_cut == "base_concurrent": final_tree_repr = ProcessTree(operator=Operator.XOR) child_tree = final_tree_repr elif spec_tree_struct.detected_cut == "sequential": final_tree_repr = ProcessTree(operator=Operator.SEQUENCE) child_tree = final_tree_repr elif spec_tree_struct.detected_cut == "loopCut": final_tree_repr = ProcessTree(operator=Operator.LOOP) child_tree = final_tree_repr elif spec_tree_struct.detected_cut == "concurrent": final_tree_repr = ProcessTree(operator=Operator.XOR) child_tree = final_tree_repr elif spec_tree_struct.detected_cut == "parallel": final_tree_repr = ProcessTree(operator=Operator.PARALLEL) child_tree = final_tree_repr if spec_tree_struct.detected_cut == "base_concurrent" or spec_tree_struct.detected_cut == "flower": for act in spec_tree_struct.activities: new_vis_trans = get_transition(act) child_tree.children.append(new_vis_trans) new_vis_trans.parent = child_tree if verify_skip_transition_necessity(must_add_skip, spec_tree_struct.initial_dfg, spec_tree_struct.dfg, spec_tree_struct.activities): # add skip transition new_hidden_trans = get_new_hidden_trans() child_tree.children.append(new_hidden_trans) new_hidden_trans.parent = child_tree if spec_tree_struct.detected_cut == "sequential" or spec_tree_struct.detected_cut == "loopCut": for ch in spec_tree_struct.children: child = get_repr(ch, rec_depth + 1, must_add_skip=(verify_skip_transition_necessity( False, ch.initial_dfg, ch.dfg, ch.activities)) or ch.force_loop_hidden) child_tree.children.append(child) child.parent = child_tree if spec_tree_struct.detected_cut == "loopCut" and len( spec_tree_struct.children) < 3: while len(spec_tree_struct.children) < 3: child = ProcessTree() child_tree.children.append(child) child.parent = child_tree spec_tree_struct.children.append(None) if spec_tree_struct.detected_cut == "parallel": m_add_skip = verify_skip_for_parallel_cut(spec_tree_struct.dfg, spec_tree_struct.children) for child in spec_tree_struct.children: m_add_skip_final = verify_skip_transition_necessity( m_add_skip, spec_tree_struct.initial_dfg, spec_tree_struct.dfg, spec_tree_struct.activities) child_final = get_repr(child, rec_depth + 1, must_add_skip=m_add_skip_final) child_tree.children.append(child_final) child_final.parent = child_tree if spec_tree_struct.detected_cut == "concurrent": for child in spec_tree_struct.children: m_add_skip_final = verify_skip_transition_necessity( False, spec_tree_struct.dfg, spec_tree_struct.dfg, spec_tree_struct.activities) child_final = get_repr(child, rec_depth + 1, must_add_skip=m_add_skip_final) child_tree.children.append(child_final) child_final.parent = child_tree if contains_empty_traces and rec_depth == 1: master_tree_repr = ProcessTree(operator=Operator.XOR) master_tree_repr.children.append(final_tree_repr) final_tree_repr.parent = master_tree_repr skip_transition = ProcessTree() master_tree_repr.children.append(skip_transition) skip_transition.parent = master_tree_repr return master_tree_repr return final_tree_repr
def apply(path, parameters=None): """ Imports a PTML file from the specified path Parameters --------------- path Path parameters Possible parameters Returns --------------- tree Process tree """ if parameters is None: parameters = {} parser = etree.XMLParser(remove_comments=True) xml_tree = objectify.parse(path, parser=parser) root = xml_tree.getroot() nodes = {} for c0 in root: root = c0.get("root") for child in c0: tag = child.tag id = child.get("id") name = child.get("name") sourceId = child.get("sourceId") targetId = child.get("targetId") if name is not None: # node if tag == "and": operator = Operator.PARALLEL label = None elif tag == "sequence": operator = Operator.SEQUENCE label = None elif tag == "xor": operator = Operator.XOR label = None elif tag == "xorLoop": operator = Operator.LOOP label = None elif tag == "or": operator = Operator.OR label = None elif tag == "manualTask": operator = None label = name elif tag == "automaticTask": operator = None label = None else: raise Exception("unknown tag: " + tag) tree = ProcessTree(operator=operator, label=label) nodes[id] = tree else: nodes[sourceId].children.append(nodes[targetId]) nodes[targetId].parent = nodes[sourceId] # make sure that .PTML files having loops with 3 children are imported # into the PM4Py process tree structure # we want loops to have two children for node in nodes.values(): if node.operator == Operator.LOOP and len(node.children) == 3: if not (node.children[2].operator is None and node.children[2].label is None): parent_node = node.parent new_parent_node = ProcessTree(operator=Operator.SEQUENCE, label=None) node.parent = new_parent_node new_parent_node.children.append(node) node.children[2].parent = new_parent_node new_parent_node.children.append(node.children[2]) if parent_node is not None: new_parent_node.parent = parent_node del parent_node.children[parent_node.children.index(node)] parent_node.children.append(new_parent_node) del node.children[2] return nodes[root]
def apply(parameters=None): """ Generate a process tree Parameters ------------ parameters Paramters of the algorithm, including: Parameters.REC_DEPTH -> current recursion depth Parameters.MIN_REC_DEPTH -> minimum recursion depth Parameters.MAX_REC_DEPTH -> maximum recursion depth Parameters.PROB_LEAF -> Probability to get a leaf Returns ------------ tree Process tree """ if parameters is None: parameters = {} rec_depth = exec_utils.get_param_value(Parameters.REC_DEPTH, parameters, 0) min_rec_depth = exec_utils.get_param_value(Parameters.MIN_REC_DEPTH, parameters, 1) max_rec_depth = exec_utils.get_param_value(Parameters.MAX_REC_DEPTH, parameters, 3) prob_leaf = exec_utils.get_param_value(Parameters.PROB_LEAF, parameters, 0.25) next_parameters = {Parameters.REC_DEPTH: rec_depth + 1, Parameters.MIN_REC_DEPTH: min_rec_depth, Parameters.MAX_REC_DEPTH: max_rec_depth, Parameters.PROB_LEAF: prob_leaf} is_leaf = False if min_rec_depth <= rec_depth <= max_rec_depth: r = random.random() if r < prob_leaf: is_leaf = True elif rec_depth > max_rec_depth: is_leaf = True if is_leaf: current_tree = ProcessTree(label=generate_random_string(6)) elif rec_depth == 0: current_tree = ProcessTree(operator=Operator.SEQUENCE) start = ProcessTree(label=generate_random_string(6), parent=current_tree) current_tree.children.append(start) node = apply(parameters=next_parameters) node.parent = current_tree current_tree.children.append(node) end = ProcessTree(label=generate_random_string(6)) end.parent = current_tree current_tree.children.append(end) else: o = get_random_operator() current_tree = ProcessTree(operator=o) if o == Operator.SEQUENCE: n_min = 2 n_max = 6 selected_n = random.randrange(n_min, n_max) for i in range(selected_n): child = apply(parameters=next_parameters) child.parent = current_tree current_tree.children.append(child) elif o == Operator.LOOP: do = apply(parameters=next_parameters) do.parent = current_tree current_tree.children.append(do) redo = apply(parameters=next_parameters) redo.parent = current_tree current_tree.children.append(redo) exit = ProcessTree(parent=current_tree) current_tree.children.append(exit) elif o == Operator.XOR: n_min = 2 n_max = 5 selected_n = random.randrange(n_min, n_max) for i in range(selected_n): child = apply(parameters=next_parameters) child.parent = current_tree current_tree.children.append(child) elif o == Operator.PARALLEL: n_min = 2 n_max = 4 selected_n = random.randrange(n_min, n_max) for i in range(selected_n): child = apply(parameters=next_parameters) child.parent = current_tree current_tree.children.append(child) return current_tree
def apply(parameters=None): """ Generate a process tree Parameters ------------ parameters Paramters of the algorithm, including: rec_depth -> current recursion depth min_rec_depth -> minimum recursion depth max_rec_depth -> maximum recursion depth prob_leaf -> Probability to get a leaf Returns ------------ tree Process tree """ if parameters is None: parameters = {} rec_depth = parameters["rec_depth"] if "rec_depth" in parameters else 0 min_rec_depth = parameters[ "min_rec_depth"] if "min_rec_depth" in parameters else 1 max_rec_depth = parameters[ "max_rec_depth"] if "max_rec_depth" in parameters else 3 prob_leaf = parameters["prob_leaf"] if "prob_leaf" in parameters else 0.25 next_parameters = { "rec_depth": rec_depth + 1, "min_rec_depth": min_rec_depth, "max_rec_depth": max_rec_depth, "prob_leaf": prob_leaf } is_leaf = False if min_rec_depth <= rec_depth <= max_rec_depth: r = random.random() if r < prob_leaf: is_leaf = True elif rec_depth > max_rec_depth: is_leaf = True if is_leaf: current_tree = ProcessTree(label=generate_random_string(6)) elif rec_depth == 0: current_tree = ProcessTree(operator=Operator.SEQUENCE) start = ProcessTree(label=generate_random_string(6), parent=current_tree) current_tree.children.append(start) node = apply(parameters=next_parameters) node.parent = current_tree current_tree.children.append(node) end = ProcessTree(label=generate_random_string(6)) end.parent = current_tree current_tree.children.append(end) else: o = get_random_operator() current_tree = ProcessTree(operator=o) if o == Operator.SEQUENCE: n_min = 2 n_max = 6 selected_n = random.randrange(n_min, n_max) for i in range(selected_n): child = apply(parameters=next_parameters) child.parent = current_tree current_tree.children.append(child) elif o == Operator.LOOP: do = apply(parameters=next_parameters) do.parent = current_tree current_tree.children.append(do) redo = apply(parameters=next_parameters) redo.parent = current_tree current_tree.children.append(redo) exit = ProcessTree(parent=current_tree) current_tree.children.append(exit) elif o == Operator.XOR: n_min = 2 n_max = 5 selected_n = random.randrange(n_min, n_max) for i in range(selected_n): child = apply(parameters=next_parameters) child.parent = current_tree current_tree.children.append(child) elif o == Operator.PARALLEL: n_min = 2 n_max = 4 selected_n = random.randrange(n_min, n_max) for i in range(selected_n): child = apply(parameters=next_parameters) child.parent = current_tree current_tree.children.append(child) return current_tree