''' This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de). PM4Py is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. PM4Py is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with PM4Py. If not, see . ''' import math from pm4py.objects.process_tree.obj import Operator def get_max_trace_length(tree, parameters=None): """ Get the maximum length of a trace allowed by the process tree (can be infty) Parameters --------------- tree Process tree parameters Possible parameters of the algorithm Returns -------------- max_trace_length The maximum length of a trace """ if parameters is None: parameters = {} bottomup = get_bottomup_nodes(tree, parameters=parameters) max_length_dict = {} for i in range(len(bottomup)): get_max_length_dict(bottomup[i], max_length_dict, len(bottomup)) return max_length_dict[tree] def get_min_trace_length(tree, parameters=None): """ Get the minimum length of a trace allowed by the process tree Parameters --------------- tree Process tree parameters Possible parameters of the algorithm Returns -------------- min_trace_length The minimum length of a trace """ if parameters is None: parameters = {} bottomup = get_bottomup_nodes(tree, parameters=parameters) min_length_dict = {} for i in range(len(bottomup)): get_min_length_dict(bottomup[i], min_length_dict) return min_length_dict[tree] def get_max_rem_dict(tree, parameters=None): """ Gets for each node of the tree the maximum number of activities that are inserted to 'complete' a trace of the overall tree Parameters ---------------- tree Process tree parameters Parameters of the algorithm Returns --------------- max_rem_dict Dictionary described in the docstring """ if parameters is None: parameters = {} bottomup = get_bottomup_nodes(tree, parameters=parameters) max_length_dict = {} for i in range(len(bottomup)): get_max_length_dict(bottomup[i], max_length_dict, len(bottomup)) max_rem_dict = {} for i in range(len(bottomup)): max_rem_dict[bottomup[i]] = max_length_dict[tree] - max_length_dict[bottomup[i]] return max_rem_dict def get_min_rem_dict(tree, parameters=None): """ Gets for each node of the tree the minimum number of activities that are inserted to 'complete' a trace of the overall tree Parameters ---------------- tree Process tree parameters Parameters of the algorithm Returns --------------- min_rem_dict Dictionary described in the docstring """ if parameters is None: parameters = {} bottomup = get_bottomup_nodes(tree, parameters=parameters) min_length_dict = {} for i in range(len(bottomup)): get_min_length_dict(bottomup[i], min_length_dict) min_rem_dict = {} for i in range(len(bottomup)): min_rem_dict[bottomup[i]] = min_length_dict[tree] - min_length_dict[bottomup[i]] return min_rem_dict def get_max_length_dict(node, max_length_dict, num_nodes): """ Populates, given the nodes of a tree in a bottom-up order, the maximum length dictionary (every trace generated from that point of the tree has at most length N) Parameters --------------- node Node max_length_dict Dictionary that is populated in-place num_nodes Number of nodes in the process tree """ if len(node.children) == 0: if node.label is None: max_length_dict[node] = 0 else: max_length_dict[node] = 1 elif node.operator == Operator.XOR: max_length_dict[node] = max(max_length_dict[x] for x in node.children) elif node.operator == Operator.PARALLEL or node.operator == Operator.SEQUENCE or node.operator == Operator.OR: max_length_dict[node] = sum(max_length_dict[x] for x in node.children) elif node.operator == Operator.LOOP: max_length_dict[node] = sum(max_length_dict[x] for x in node.children) + 2 ** ( 48 - math.ceil(math.log(num_nodes) / math.log(2))) def get_min_length_dict(node, min_length_dict): """ Populates, given the nodes of a tree in a bottom-up order, the minimum length dictionary (every trace generated from that point of the tree has at least length N) Parameters --------------- node Node min_length_dict Dictionary that is populated in-place """ if len(node.children) == 0: if node.label is None: min_length_dict[node] = 0 else: min_length_dict[node] = 1 elif node.operator == Operator.XOR: min_length_dict[node] = min(min_length_dict[x] for x in node.children) elif node.operator == Operator.PARALLEL or node.operator == Operator.SEQUENCE or node.operator == Operator.OR: min_length_dict[node] = sum(min_length_dict[x] for x in node.children) elif node.operator == Operator.LOOP: min_length_dict[node] = min_length_dict[node.children[0]] def get_bottomup_nodes(tree, parameters=None): """ Gets the nodes of a tree in a bottomup order (leafs come first, the master node comes after) Parameters -------------- tree Process tree parameters Parameters of the algorithm Returns ------------- bottomup_nodes Nodes of the tree in a bottomup order """ if parameters is None: parameters = {} to_visit = [tree] all_nodes = set() while len(to_visit) > 0: n = to_visit.pop(0) all_nodes.add(n) for child in n.children: to_visit.append(child) # starts to visit the tree from the leafs bottomup = [x for x in all_nodes if len(x.children) == 0] # then add iteratively the parent i = 0 while i < len(bottomup): parent = bottomup[i].parent if parent is not None and parent not in bottomup: is_ok = True for child in parent.children: if not child in bottomup: is_ok = False break if is_ok: bottomup.append(parent) i = i + 1 return bottomup