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 add_node(self): leaves = self.tree._get_leaves() if self.tree.operator is None: leaves.append(self.tree) visible_leaves = [x for x in leaves if x.label is not None] operator = self.select_operator() op_map = {"parallel": obj.Operator.PARALLEL, "sequence": obj.Operator.SEQUENCE, "choice": obj.Operator.XOR, "loop": obj.Operator.LOOP, "or": obj.Operator.OR} mapped_operator = op_map[operator] order = random.randrange(0, 2) chosen_leaf = random.choice(leaves) added_count = 0 label1 = chosen_leaf.label if chosen_leaf.label is None: added_count = added_count + 1 label1 = self.get_next_activity() label2 = None if chosen_leaf.parent is None: self.tree = obj.ProcessTree(operator=mapped_operator) chosen_leaf = self.tree else: parent = chosen_leaf.parent del parent.children[parent.children.index(chosen_leaf)] chosen_leaf = obj.ProcessTree(operator=mapped_operator, parent=parent) parent.children.append(chosen_leaf) r = random.random() if self.total_activities - len(visible_leaves) > added_count and not r < self.parameters["silent"]: label2 = self.get_next_activity() node1 = obj.ProcessTree(label=label1, parent=chosen_leaf) node2 = obj.ProcessTree(label=label2, parent=chosen_leaf) if order == 0: chosen_leaf.children.append(node1) chosen_leaf.children.append(node2) elif order == 1: chosen_leaf.children.append(node2) chosen_leaf.children.append(node1)
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 __add_subdfgs(dfgs, skippable, threshold, root, operator): i = 0 parent = pt.ProcessTree(operator=operator, parent=root) while i < len(dfgs): if skippable[i]: this_parent = pt.ProcessTree(operator=pt.Operator.XOR, parent=parent) parent.children.append(this_parent) skip = pt.ProcessTree(parent=this_parent) this_parent.children.append(skip) else: this_parent = parent this_parent.children.append(__imd(dfgs[i], threshold, this_parent)) i = i + 1 return parent
def create_process_tree(self): self.iter = self.iter_all_strings() self.tree = obj.ProcessTree() visible_leaves = [x for x in self.tree._get_leaves() if x.label is not None] while len(visible_leaves) < self.total_activities: self.add_node() visible_leaves = [x for x in self.tree._get_leaves() if x.label is not None]
def create_process_tree(self): self.tree = obj.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_logs(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_logs( pt.ProcessTree(operator=pt.Operator.XOR, parent=operator), threshold, act_key, logs, use_msd)) return operator
def __base_case(dfg, root): parent = pt.ProcessTree(operator=pt.Operator.XOR, parent=root) for act in dfg.act_count: node = pt.ProcessTree(label=act, parent=parent) parent.children.append(node) return parent
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 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 = obj.ProcessTree(pt_operator.Operator.LOOP) root_loop.parent = leaf leaf._children.append(root_loop) new_node = obj.ProcessTree(label=activity) new_node.parent = root_loop root_loop._children.append(new_node) activity = next_activity if silent_activity: new_node = obj.ProcessTree(label=None) new_node.parent = root_loop root_loop._children.append(new_node) else: new_node = obj.ProcessTree(label=activity) new_node.parent = root_loop root_loop._children.append(new_node) activity = self.get_next_activity(activity) new_node = obj.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 = obj.ProcessTree(label=None) new_node.parent = leaf leaf._children.append(new_node) new_node = obj.ProcessTree(label=activity) new_node.parent = leaf leaf._children.append(new_node) else: new_node = obj.ProcessTree(label=activity) new_node.parent = leaf leaf._children.append(new_node) new_node = obj.ProcessTree(label=None) new_node.parent = leaf leaf._children.append(new_node) else: new_node = obj.ProcessTree(label=activity) new_node.parent = leaf leaf._children.append(new_node) activity = next_activity new_node = obj.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 = obj.ProcessTree(operator=pt_operator.Operator.LOOP) root_loop.parent = root root._children.append(root_loop) new_node = obj.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 = obj.ProcessTree(label=None) new_node.parent = root_loop root_loop._children.append(new_node) else: new_node = obj.ProcessTree(label=activity) new_node.parent = root_loop root_loop._children.append(new_node) activity = self.get_next_activity(activity) new_node = obj.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 = obj.ProcessTree(label=None) new_node.parent = root root._children.append(new_node) new_node = obj.ProcessTree(label=activity) new_node.parent = root root._children.append(new_node) else: new_node = obj.ProcessTree(label=activity) new_node.parent = root root._children.append(new_node) new_node = obj.ProcessTree(label=None) new_node.parent = root root._children.append(new_node) else: new_node = obj.ProcessTree(label=activity) new_node.parent = root root._children.append(new_node) activity = self.get_next_activity(activity) new_node = obj.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 __inductive_miner_internal(log, dfg, threshold, root, act_key, use_msd, remove_noise=False): alphabet = pm4py.get_event_attribute_values(log, act_key) if threshold > 0 and remove_noise: end_activities = get_ends.get_end_activities( log, parameters={constants.PARAMETER_CONSTANT_ACTIVITY_KEY: act_key}) dfg = __filter_dfg_on_threshold(dfg, end_activities, threshold) original_length = len(log) log = pm4py.filter_log(lambda t: len(t) > 0, log) # revised EMPTYSTRACES if original_length - len(log) > original_length * threshold: return __add_operator_recursive_logs( pt.ProcessTree(pt.Operator.XOR, root), threshold, act_key, [EventLog(), log], use_msd) 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}) 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_logs( 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_logs( 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_logs( 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_logs( pt.ProcessTree(pt.Operator.LOOP, root), threshold, act_key, loop_cut.project(log, cut, act_key), 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_logs( 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_logs( 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_logs( 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_logs( pt.ProcessTree(pt.Operator.LOOP, root), threshold, act_key, [tl, EventLog()], use_msd) if threshold > 0 and not remove_noise: return __inductive_miner(log, dfg, threshold, root, act_key, use_msd, remove_noise=True) return __flower(alphabet, root)
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)