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 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 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 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 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 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 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_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 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: 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