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 """ 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 __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 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) elif vertex.operator is pt_opt.Operator.INTERLEAVING: random.shuffle(vertex.children) c = vertex.children[0] enabled.add(c) execution_sequence.append((c, pt_st.State.ENABLED)) else: close(vertex, enabled, open, closed, execution_sequence) return execution_sequence
def process_tree_to_binary_process_tree(tree: ProcessTree) -> ProcessTree: if len(tree.children) > 2: left_tree = tree.children[0] right_tree_op = tree.operator if tree.operator == pt_op.Operator.LOOP: right_tree_op = pt_op.Operator.XOR right_tree = ProcessTree(operator=right_tree_op, parent=tree, children=tree.children[1:]) for child in right_tree.children: child.parent = right_tree tree.children = [left_tree, right_tree] for child in tree.children: process_tree_to_binary_process_tree(child) return tree
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 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) new_subtree.parent = pt for c in pt.children: process_tree_to_binary_process_tree(c) return pt
def execute_script(): root = ProcessTree(operator=Operator.SEQUENCE) choice = ProcessTree(operator=Operator.XOR, parent=root) parallel = ProcessTree(operator=Operator.PARALLEL, parent=root) root.children.append(choice) root.children.append(parallel) leaf_A = ProcessTree(label="A", parent=choice) leaf_B = ProcessTree(label="B", parent=choice) leaf_C = ProcessTree(label="C", parent=choice) choice.children.append(leaf_A) choice.children.append(leaf_B) choice.children.append(leaf_C) leaf_D = ProcessTree(label="D", parent=parallel) leaf_E = ProcessTree(label="E", parent=parallel) leaf_F = ProcessTree(label="F", parent=parallel) parallel.children.append(leaf_D) parallel.children.append(leaf_E) parallel.children.append(leaf_F) pm4py.view_process_tree(root, format="svg") # remove leaf_C from choice choice.children.remove(leaf_C) # remove the leaf with label "E" from parallel parallel.children.remove([ parallel.children[i] for i in range(len(parallel.children)) if parallel.children[i].label == "E" ][0]) pm4py.view_process_tree(root, format="svg")
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_xor" 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_xor": 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": if spec_tree_struct.need_loop_on_subtree: final_tree_repr = ProcessTree(operator=Operator.LOOP) child_tree = ProcessTree(operator=Operator.SEQUENCE) child_tree.parent = final_tree_repr final_tree_repr.children.append(child_tree) child = ProcessTree() final_tree_repr.children.append(child) child.parent = final_tree_repr else: 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 == "xor": 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_xor" 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": 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( ProcessTree(operator=None, label=None, parent=spec_tree_struct)) 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 == "xor": 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_xor": 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_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, 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 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 force_add_skip: invisible = get_new_hidden_trans(counts, type_trans="skip") add_arc_from_to(initial_place, invisible, net) add_arc_from_to(invisible, final_place, net) 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) elif tree.operator == Operator.OR: 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) terminal_place = get_new_place(counts) net.places.add(terminal_place) add_arc_from_to(terminal_place, new_final_trans, net) first_place = get_new_place(counts) net.places.add(first_place) add_arc_from_to(new_initial_trans, first_place, net) for subtree in tree_childs: subtree_init_place = get_new_place(counts) net.places.add(subtree_init_place) add_arc_from_to(new_initial_trans, subtree_init_place, net) subtree_start_place = get_new_place(counts) net.places.add(subtree_start_place) subtree_end_place = get_new_place(counts) net.places.add(subtree_end_place) trans_start = get_new_hidden_trans(counts, type_trans="inclusiveStart") trans_later = get_new_hidden_trans(counts, type_trans="inclusiveLater") trans_skip = get_new_hidden_trans(counts, type_trans="inclusiveSkip") net.transitions.add(trans_start) net.transitions.add(trans_later) net.transitions.add(trans_skip) add_arc_from_to(first_place, trans_start, net) add_arc_from_to(subtree_init_place, trans_start, net) add_arc_from_to(trans_start, subtree_start_place, net) add_arc_from_to(trans_start, terminal_place, net) add_arc_from_to(terminal_place, trans_later, net) add_arc_from_to(subtree_init_place, trans_later, net) add_arc_from_to(trans_later, subtree_start_place, net) add_arc_from_to(trans_later, terminal_place, net) add_arc_from_to(terminal_place, trans_skip, net) add_arc_from_to(subtree_init_place, trans_skip, net) add_arc_from_to(trans_skip, terminal_place, net) add_arc_from_to(trans_skip, subtree_end_place, net) add_arc_from_to(subtree_end_place, new_final_trans, net) net, counts, intermediate_place = recursively_add_tree( tree, subtree, net, subtree_start_place, subtree_end_place, counts, rec_depth + 1) 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) 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) 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) 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) net, counts, int2 = recursively_add_tree(tree, redo, net, int1, None, counts, rec_depth + 1) net, counts, int3 = recursively_add_tree(tree, exit, net, int1, final_place, counts, rec_depth + 1) looping_place = int2 add_arc_from_to(looping_place, loop_trans, net) add_arc_from_to(loop_trans, initial_place, net) return net, counts, final_place
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 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 import_tree_from_xml_object(root, parameters=None): """ Imports a process tree from the XML object Parameters --------------- root Root of the XML object parameters Possible parameters Returns --------------- tree Process tree """ if parameters is None: parameters = {} 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] root = nodes[root] tree_sort(root) return root