def _apply_base_case(log, root, act_key): if len(log) == 0: operator = pt.ProcessTree(parent=root) return operator else: operator = pt.ProcessTree(parent=root, label=log[0][0][act_key]) return operator
def _flower(alphabet, root): operator = pt.ProcessTree(operator=pt.Operator.LOOP, parent=root) operator.children.append(pt.ProcessTree(parent=operator)) xor = pt.ProcessTree(operator=pt.Operator.XOR) operator.children.append(xor) for a in alphabet: xor.children.append(pt.ProcessTree(label=a, parent=xor)) return operator
def create_process_tree(self): self.tree = process_tree.ProcessTree() self.set_activity_labels = [item for item in self.alphabet] while (self.total_activities + 2 > len(self.set_activity_labels)): self.set_activity_labels.append("τ") step = 1 activity = self.assign_root_opeartor() step += 1 while (self.total_activities > 0): activity = self.add_node(activity) step += 1
def create_process_tree(self): self.tree = process_tree.ProcessTree() self.set_activity_labels = [] p = 1 # create labels while (self.total_activities > len(self.set_activity_labels)): # pairwise product l = itertools.product(self.alphabet, repeat=p) for item in l: label = "" for element in item: label += str(element) self.set_activity_labels.append(label) p += 1 step = 1 activity = self.assign_root_opeartor() step += 1 while (self.total_activities > 0): activity = self.add_node(activity) step += 1
def _add_operator_recursive(operator, threshold, act_key, logs, use_msd): if operator.operator != pt.Operator.LOOP: for log in logs: operator.children.append( inductive_miner( log, discover_dfg.apply( log, parameters={ constants.PARAMETER_CONSTANT_ACTIVITY_KEY: act_key }), threshold, operator, act_key, use_msd)) else: operator.children.append( inductive_miner( logs[0], discover_dfg.apply( logs[0], parameters={ constants.PARAMETER_CONSTANT_ACTIVITY_KEY: act_key }), threshold, operator, act_key, use_msd)) logs = logs[1:] if len(logs) == 1: operator.children.append( inductive_miner( logs[0], discover_dfg.apply( logs[0], parameters={ constants.PARAMETER_CONSTANT_ACTIVITY_KEY: act_key }), threshold, operator, act_key, use_msd)) else: operator.children.append( _add_operator_recursive( pt.ProcessTree(operator=pt.Operator.XOR, parent=operator), threshold, act_key, logs, use_msd)) return operator
def add_node(self, next_activity): """ Add nodes to current tree. The general procedure is as follows: Select a random leaf (leaves have label). Next step, and opertor is chosen. The chosen operator then replaces the leaf, whereby the old label is then add as a leaf to the manipulated node. Then, next activity is added as a second leaf to the new operator node or a silent acticity (tau) is added. :return: Next activity """ # Need to select random node that is not a silent activity leaf_silent = True while (leaf_silent): leaf = random.choice(self.tree._get_leaves()) if leaf.label is not None: leaf_silent = False operator_nok = True while (operator_nok): operator = self.select_operator() if self.total_activities > 1: operator_nok = False else: if operator != "loop": operator_nok = False activity = leaf._get_label() leaf._set_label(None) leaf._set_operator(assign_operator(operator)) # Will be an tau added? silent_activity = False if random.random() < self.parameters["silent"]: silent_activity = True # add two children if operator == "loop": leaf._set_operator(pt_operator.Operator.SEQUENCE) root_loop = process_tree.ProcessTree(pt_operator.Operator.LOOP) root_loop.parent = leaf leaf._children.append(root_loop) new_node = process_tree.ProcessTree(label=activity) new_node.parent = root_loop root_loop._children.append(new_node) activity = next_activity if silent_activity: new_node = process_tree.ProcessTree(label=None) new_node.parent = root_loop root_loop._children.append(new_node) else: new_node = process_tree.ProcessTree(label=activity) new_node.parent = root_loop root_loop._children.append(new_node) activity = self.get_next_activity(activity) new_node = process_tree.ProcessTree(label=activity) new_node.parent = leaf leaf._children.append(new_node) self.total_activities -= 1 else: if silent_activity and operator == "choice": number = random.choice([0, 1]) if number == 0: new_node = process_tree.ProcessTree(label=None) new_node.parent = leaf leaf._children.append(new_node) new_node = process_tree.ProcessTree(label=activity) new_node.parent = leaf leaf._children.append(new_node) else: new_node = process_tree.ProcessTree(label=activity) new_node.parent = leaf leaf._children.append(new_node) new_node = process_tree.ProcessTree(label=None) new_node.parent = leaf leaf._children.append(new_node) else: new_node = process_tree.ProcessTree(label=activity) new_node.parent = leaf leaf._children.append(new_node) activity = next_activity new_node = process_tree.ProcessTree(label=activity) new_node.parent = leaf leaf._children.append(new_node) self.total_activities -= 2 if silent_activity and operator == "choice": return next_activity else: return self.get_next_activity(activity)
def assign_root_opeartor(self): activity = "a" # is a silent activity chosen silent_activity = False if random.random() < self.parameters["silent"]: silent_activity = True root = self.tree._get_root() operator = self.select_operator() root.operator = assign_operator(operator) # if operator is loop, we use a special structure, otherwise 2 if operator == "loop": root.operator = pt_operator.Operator.SEQUENCE root_loop = process_tree.ProcessTree( operator=pt_operator.Operator.LOOP) root_loop.parent = root root._children.append(root_loop) new_node = process_tree.ProcessTree(label=activity) new_node.parent = root_loop root_loop._children.append(new_node) activity = self.get_next_activity(activity) if silent_activity: new_node = process_tree.ProcessTree(label=None) new_node.parent = root_loop root_loop._children.append(new_node) else: new_node = process_tree.ProcessTree(label=activity) new_node.parent = root_loop root_loop._children.append(new_node) activity = self.get_next_activity(activity) new_node = process_tree.ProcessTree(label=activity) new_node.parent = root root._children.append(new_node) self.total_activities -= 1 else: if silent_activity and operator == "choice": number = random.choice([0, 1]) if number == 0: new_node = process_tree.ProcessTree(label=None) new_node.parent = root root._children.append(new_node) new_node = process_tree.ProcessTree(label=activity) new_node.parent = root root._children.append(new_node) else: new_node = process_tree.ProcessTree(label=activity) new_node.parent = root root._children.append(new_node) new_node = process_tree.ProcessTree(label=None) new_node.parent = root root._children.append(new_node) else: new_node = process_tree.ProcessTree(label=activity) new_node.parent = root root._children.append(new_node) activity = self.get_next_activity(activity) new_node = process_tree.ProcessTree(label=activity) new_node.parent = root root._children.append(new_node) # always two children are added self.total_activities -= 2 return self.get_next_activity(activity)
def parse_recursive(string_rep, depth_cache, depth): """ Parse a string provided by the user to a process tree (recursive method) Parameters ------------ string_rep String representation of the process tree depth_cache Depth cache of the algorithm depth Current step depth Returns ----------- node Process tree object """ string_rep = string_rep.strip() node = None operator = None if string_rep.startswith(pt_op.Operator.LOOP.value): operator = pt_op.Operator.LOOP string_rep = string_rep[len(pt_op.Operator.LOOP.value):] elif string_rep.startswith(pt_op.Operator.PARALLEL.value): operator = pt_op.Operator.PARALLEL string_rep = string_rep[len(pt_op.Operator.PARALLEL.value):] elif string_rep.startswith(pt_op.Operator.XOR.value): operator = pt_op.Operator.XOR string_rep = string_rep[len(pt_op.Operator.XOR.value):] elif string_rep.startswith(pt_op.Operator.OR.value): operator = pt_op.Operator.OR string_rep = string_rep[len(pt_op.Operator.OR.value):] elif string_rep.startswith(pt_op.Operator.SEQUENCE.value): operator = pt_op.Operator.SEQUENCE string_rep = string_rep[len(pt_op.Operator.SEQUENCE.value):] if operator is not None: parent = None if depth == 0 else depth_cache[depth - 1] node = pt.ProcessTree(operator=operator, parent=parent) depth_cache[depth] = node if parent is not None: parent.children.append(node) depth += 1 string_rep = string_rep.strip() assert (string_rep[0] == '(') parse_recursive(string_rep[1:], depth_cache, depth) else: label = None if string_rep.startswith('\''): string_rep = string_rep[1:] escape_ext = string_rep.find('\'') label = string_rep[0:escape_ext] string_rep = string_rep[escape_ext + 1:] else: assert (string_rep.startswith('tau') or string_rep.startswith('τ') or string_rep.startswith(u'\u03c4')) if string_rep.startswith('tau'): string_rep = string_rep[len('tau'):] elif string_rep.startswith('τ'): string_rep = string_rep[len('τ'):] elif string_rep.startswith(u'\u03c4'): string_rep = string_rep[len(u'\u03c4'):] parent = None if depth == 0 else depth_cache[depth - 1] node = pt.ProcessTree(operator=operator, parent=parent, label=label) if parent is not None: parent.children.append(node) while string_rep.strip().startswith(')'): depth -= 1 string_rep = (string_rep.strip())[1:] if len(string_rep.strip()) > 0: parse_recursive((string_rep.strip())[1:], depth_cache, depth) return node
def inductive_miner(log, dfg, threshold, root, act_key, use_msd): alphabet = pm4py.get_attribute_values(log, act_key) start_activities = get_starters.get_start_activities( log, parameters={constants.PARAMETER_CONSTANT_ACTIVITY_KEY: act_key}) end_activities = get_ends.get_end_activities( log, parameters={constants.PARAMETER_CONSTANT_ACTIVITY_KEY: act_key}) empty_traces = pm4py.filter_log(lambda trace: len(trace) == 0, log) if len(empty_traces) == 0: if _is_base_case_act(log, act_key) or _is_base_case_silent(log): return _apply_base_case(log, root, act_key) pre, post = dfg_utils.get_transitive_relations(dfg, alphabet) cut = sequence_cut.detect(alphabet, pre, post) if cut is not None: return _add_operator_recursive( pt.ProcessTree(pt.Operator.SEQUENCE, root), threshold, act_key, sequence_cut.project(log, cut, act_key), use_msd) cut = xor_cut.detect(dfg, alphabet) if cut is not None: return _add_operator_recursive( pt.ProcessTree(pt.Operator.XOR, root), threshold, act_key, xor_cut.project(log, cut, act_key), use_msd) cut = concurrent_cut.detect( dfg, alphabet, start_activities, end_activities, msd=msdw_algo.derive_msd_witnesses( log, msd_algo.apply(log, parameters={ constants.PARAMETER_CONSTANT_ACTIVITY_KEY: act_key }), parameters={ constants.PARAMETER_CONSTANT_ACTIVITY_KEY: act_key }) if use_msd else None) if cut is not None: return _add_operator_recursive( pt.ProcessTree(pt.Operator.PARALLEL, root), threshold, act_key, concurrent_cut.project(log, cut, act_key), use_msd) cut = loop_cut.detect(dfg, alphabet, start_activities, end_activities) if cut is not None: return _add_operator_recursive( pt.ProcessTree(pt.Operator.LOOP, root), threshold, act_key, loop_cut.project(log, cut, act_key), use_msd) if len(empty_traces) > 0: nempty = pm4py.filter_log(lambda t: len(t) > 0, log) return _add_operator_recursive(pt.ProcessTree(pt.Operator.XOR, root), threshold, act_key, [EventLog(), nempty], use_msd) aopt = activity_once_per_trace.detect(log, alphabet, act_key) if aopt is not None: operator = pt.ProcessTree(operator=pt.Operator.PARALLEL, parent=root) operator.children.append( pt.ProcessTree(operator=None, parent=operator, label=aopt)) return _add_operator_recursive( operator, threshold, act_key, activity_once_per_trace.project(log, aopt, act_key), use_msd) act_conc = activity_concurrent.detect(log, alphabet, act_key, use_msd) if act_conc is not None: return _add_operator_recursive( pt.ProcessTree(pt.Operator.PARALLEL, root), threshold, act_key, activity_concurrent.project(log, act_conc, act_key), use_msd) stl = strict_tau_loop.detect(log, start_activities, end_activities, act_key) if stl is not None: return _add_operator_recursive(pt.ProcessTree(pt.Operator.LOOP, root), threshold, act_key, [stl, EventLog()], use_msd) tl = tau_loop.detect(log, start_activities, act_key) if tl is not None: return _add_operator_recursive(pt.ProcessTree(pt.Operator.LOOP, root), threshold, act_key, [tl, EventLog()], use_msd) return _flower(alphabet, root)