def apply(tree0, parameters=None, variant=DEFAULT_VARIANT): """ Method for Process Tree representation Parameters ----------- tree Process tree parameters Possible parameters of the algorithm: Parameters.FORMAT -> Format of the image (PDF, PNG, SVG; default PNG) variant Variant of the algorithm to use: - Variants.WO_DECORATION Returns ----------- gviz GraphViz object """ # since the process tree object needs to be sorted in the visualization, make a deepcopy of it before # proceeding tree = deepcopy(tree0) util.tree_sort(tree) return exec_utils.get_variant(variant).apply(tree, parameters=parameters)
def apply(tree0, parameters=None, variant="wo_decoration"): """ Factory method for Process Tree representation Parameters ----------- tree Process tree parameters Possible parameters of the algorithm: circle_width -> Width of the circles containing the operators circle_font_size -> Font size associated to the operators box_width -> Width of the box associated to the transitions box_font_size -> Font size associated to the transitions boxes format -> Format of the image (PDF, PNG, SVG; default PNG) variant Variant of the algorithm to use Returns ----------- gviz GraphViz object """ # since the process tree object needs to be sorted in the visualization, make a deepcopy of it before # proceeding tree = deepcopy(tree0) util.tree_sort(tree) return VERSIONS[variant](tree, parameters=parameters)
def apply(tree, parameters=None): """ Obtain a Process Tree representation through GraphViz Parameters ----------- tree Process tree parameters Possible parameters of the algorithm Returns ----------- gviz GraphViz object """ if parameters is None: parameters = {} filename = tempfile.NamedTemporaryFile(suffix='.gv') viz = Digraph("pt", filename=filename.name, engine='dot', graph_attr={'bgcolor': 'transparent'}) image_format = exec_utils.get_param_value(Parameters.FORMAT, parameters, "png") color_map = exec_utils.get_param_value(Parameters.COLOR_MAP, parameters, {}) node_color = get_color(tree, color_map) enable_deepcopy = exec_utils.get_param_value(Parameters.ENABLE_DEEPCOPY, parameters, True) if enable_deepcopy: # since the process tree object needs to be sorted in the visualization, make a deepcopy of it before # proceeding tree = deepcopy(tree) util.tree_sort(tree) # add first operator if tree.operator: viz.attr('node', shape='circle', fixedsize='true', width="0.6", fontsize="14") op_node_identifier = str(uuid.uuid4()) viz.node(op_node_identifier, str(tree.operator), color=node_color, fontcolor=node_color) viz = repr_tree(tree, viz, op_node_identifier, 0, color_map, parameters) else: viz.attr('node', shape='box', fixedsize='true', width="2.5", fontsize="8") this_trans_id = str(uuid.uuid4()) if tree.label is None: viz.node(this_trans_id, "tau", style='filled', fillcolor='black') else: viz.node(this_trans_id, str(tree), color=node_color, fontcolor=node_color) viz.attr(overlap='false') viz.attr(fontsize='11') viz.format = image_format return viz
def apply(tree, parameters=None): """ Obtain a Process Tree representation through GraphViz Parameters ----------- tree Process tree parameters Possible parameters of the algorithm Returns ----------- gviz GraphViz object """ if parameters is None: parameters = {} filename = tempfile.NamedTemporaryFile(suffix='.gv') viz = Graph("pt", filename=filename.name, engine='dot', graph_attr={'bgcolor': 'transparent'}) viz.attr('node', shape='ellipse', fixedsize='false') image_format = exec_utils.get_param_value(Parameters.FORMAT, parameters, "png") color_map = exec_utils.get_param_value(Parameters.COLOR_MAP, parameters, {}) enable_deepcopy = exec_utils.get_param_value(Parameters.ENABLE_DEEPCOPY, parameters, True) if enable_deepcopy: # since the process tree object needs to be sorted in the visualization, make a deepcopy of it before # proceeding tree = deepcopy(tree) util.tree_sort(tree) repr_tree_2(tree, viz, color_map, parameters) viz.attr(overlap='false') viz.attr(splines='false') viz.format = image_format return viz
def apply(net, im, fm, parameters=None): """ Transforms a WF-net to a process tree Parameters ------------- net Petri net im Initial marking fm Final marking Returns ------------- tree Process tree """ if parameters is None: parameters = {} debug = exec_utils.get_param_value(Parameters.DEBUG, parameters, False) fold = exec_utils.get_param_value(Parameters.FOLD, parameters, True) grouped_net = group_blocks_in_net(net, parameters=parameters) if len(grouped_net.transitions) == 1: pt_str = list(grouped_net.transitions)[0].label pt = pt_util.parse(pt_str) ret = pt_util.fold(pt) if fold else pt tree_sort(ret) return ret else: if debug: from pm4py.visualization.petrinet import visualizer as pn_viz pn_viz.view(pn_viz.apply(grouped_net, parameters={"format": "svg"})) raise ValueError('Parsing of WF-net Failed')
def apply_tree(event_log: Union[pd.DataFrame, EventLog, EventStream], parameters: Optional[Dict[str, Any]] = None) -> ProcessTree: if parameters is None: parameters = {} event_log = log_converter.apply(event_log, parameters=parameters) if type(event_log) is not EventLog: raise ValueError( 'input argument log should be of type pandas.DataFrame, Event Log or Event Stream' ) act_key = exec_utils.get_param_value(Parameters.ACTIVITY_KEY.value, parameters, xes_constants.DEFAULT_NAME_KEY) if exec_utils.get_param_value(Parameters.DFG_ONLY, parameters, False): event_log = None threshold = exec_utils.get_param_value(Parameters.NOISE_THRESHOLD, parameters, 0.0) if threshold == 0.0: # keep one trace per variant; more performant event_log = filtering_utils.keep_one_trace_per_variant( event_log, parameters=parameters) tree = inductive_miner( event_log, discover_dfg.apply( event_log, parameters={constants.PARAMETER_CONSTANT_ACTIVITY_KEY: act_key}), threshold, None, act_key, exec_utils.get_param_value(Parameters.USE_MSD_PARALLEL_CUT, parameters, True)) tree_consistency.fix_parent_pointers(tree) tree = util.fold(tree) util.tree_sort(tree) return tree
def apply_tree(log, parameters=None): """ Apply the IM algorithm to a log obtaining a process tree Parameters ---------- log Log parameters Parameters of the algorithm, including: Parameters.ACTIVITY_KEY -> attribute of the log to use as activity name (default concept:name) Returns ---------- process_tree Process tree """ if parameters is None: parameters = {} if pkgutil.find_loader("pandas"): import pandas as pd from pm4py.statistics.variants.pandas import get as variants_get if type(log) is pd.DataFrame: vars = variants_get.get_variants_count(log, parameters=parameters) return apply_tree_variants(vars, parameters=parameters) activity_key = exec_utils.get_param_value(Parameters.ACTIVITY_KEY, parameters, pmutil.xes_constants.DEFAULT_NAME_KEY) log = converter.apply(log, parameters=parameters) # since basic IM is influenced once per variant, it makes sense to keep one trace per variant log = filtering_utils.keep_one_trace_per_variant(log, parameters=parameters) # keep only the activity attribute (since the others are not used) log = filtering_utils.keep_only_one_attribute_per_event(log, activity_key) dfg = [(k, v) for k, v in dfg_inst.apply(log, parameters=parameters).items() if v > 0] c = Counts() activities = attributes_get.get_attribute_values(log, activity_key) start_activities = list(start_activities_get.get_start_activities(log, parameters=parameters).keys()) end_activities = list(end_activities_get.get_end_activities(log, parameters=parameters).keys()) contains_empty_traces = False traces_length = [len(trace) for trace in log] if traces_length: contains_empty_traces = min([len(trace) for trace in log]) == 0 recursion_depth = 0 sub = subtree.make_tree(log, dfg, dfg, dfg, activities, c, recursion_depth, 0.0, start_activities, end_activities, start_activities, end_activities, parameters) process_tree = get_tree_repr_implain.get_repr(sub, 0, contains_empty_traces=contains_empty_traces) # Ensures consistency to the parent pointers in the process tree tree_consistency.fix_parent_pointers(process_tree) # Fixes a 1 child XOR that is added when single-activities flowers are found tree_consistency.fix_one_child_xor_flower(process_tree) # folds the process tree (to simplify it in case fallthroughs/filtering is applied) process_tree = util.fold(process_tree) # sorts the process tree to ensure consistency in different executions of the algorithm tree_sort(process_tree) return process_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
def apply_tree_dfg(dfg, parameters=None, activities=None, contains_empty_traces=False, start_activities=None, end_activities=None): """ Apply the IMDF algorithm to a DFG graph obtaining a process tree Parameters ---------- dfg Directly-follows graph parameters Parameters of the algorithm, including: Parameters.ACTIVITY_KEY -> attribute of the log to use as activity name (default concept:name) activities Activities of the process (default None) contains_empty_traces Boolean value that is True if the event log from which the DFG has been extracted contains empty traces start_activities If provided, the start activities of the log end_activities If provided, the end activities of the log Returns ---------- tree Process tree """ if parameters is None: parameters = {} noise_threshold = exec_utils.get_param_value(Parameters.NOISE_THRESHOLD, parameters, 0.0) if type(dfg) is Counter or type(dfg) is dict: newdfg = [] for key in dfg: value = dfg[key] newdfg.append((key, value)) dfg = newdfg c = Counts() s = SubtreeDFGBased(dfg, dfg, dfg, activities, c, 0, noise_threshold=noise_threshold, initial_start_activities=start_activities, initial_end_activities=end_activities) tree_repr = get_tree_repr_dfg_based.get_repr( s, 0, contains_empty_traces=contains_empty_traces) # Ensures consistency to the parent pointers in the process tree tree_consistency.fix_parent_pointers(tree_repr) # Fixes a 1 child XOR that is added when single-activities flowers are found tree_consistency.fix_one_child_xor_flower(tree_repr) # folds the process tree (to simplify it in case fallthroughs/filtering is applied) tree_repr = util.fold(tree_repr) # sorts the process tree to ensure consistency in different executions of the algorithm tree_sort(tree_repr) return tree_repr
import tempfile LOGS_FOLDER = "../compressed_input_data" for log_name in os.listdir(LOGS_FOLDER): if "xes" in log_name: bpmn_output_path = tempfile.NamedTemporaryFile(suffix=".bpmn") bpmn_output_path.close() bpmn_output_path = bpmn_output_path.name log_path = os.path.join(LOGS_FOLDER, log_name) print("") print(log_path) log = pm4py.read_xes(log_path) fp_log = pm4py.algo.discovery.footprints.log.variants.entire_event_log.apply(log) tree = pm4py.discover_tree_inductive(log) util.tree_sort(tree) fp_tree = pm4py.algo.discovery.footprints.tree.variants.bottomup.apply(tree) fp_conf = pm4py.algo.conformance.footprints.variants.log_extensive.apply(fp_log, fp_tree) fitness0 = pm4py.algo.conformance.footprints.util.evaluation.fp_fitness(fp_log, fp_tree, fp_conf) precision0 = pm4py.algo.conformance.footprints.util.evaluation.fp_precision(fp_log, fp_tree) print("fitness 0 = ", fitness0) print("precision 0 = ", precision0) net, im, fm = pm4py.objects.conversion.process_tree.variants.to_petri_net.apply(tree) bpmn_graph = pm4py.objects.conversion.wf_net.variants.to_bpmn.apply(net, im, fm) bpmn_graph = layouter.apply(bpmn_graph) exporter.apply(bpmn_graph, bpmn_output_path) bpmn_graph = importer.apply(bpmn_output_path) bpmn_graph = layouter.apply(bpmn_graph) # gets the tree back net, im, fm = pm4py.objects.conversion.bpmn.variants.to_petri_net.apply(bpmn_graph) new_tree = pm4py.objects.conversion.wf_net.variants.to_process_tree.apply(net, im, fm)