def apply_trace_net(petri_net, initial_marking, final_marking, trace_net, trace_im, trace_fm, parameters=None): """ Performs the basic alignment search, given a trace net and a net. Parameters ---------- trace: :class:`list` input trace, assumed to be a list of events (i.e. the code will use the activity key to get the attributes) petri_net: :class:`pm4py.objects.petri.net.PetriNet` the Petri net to use in the alignment initial_marking: :class:`pm4py.objects.petri.net.Marking` initial marking in the Petri net final_marking: :class:`pm4py.objects.petri.net.Marking` final marking in the Petri net parameters: :class:`dict` (optional) dictionary containing one of the following: Parameters.PARAM_TRACE_COST_FUNCTION: :class:`list` (parameter) mapping of each index of the trace to a positive cost value Parameters.PARAM_MODEL_COST_FUNCTION: :class:`dict` (parameter) mapping of each transition in the model to corresponding model cost Parameters.PARAM_SYNC_COST_FUNCTION: :class:`dict` (parameter) mapping of each transition in the model to corresponding synchronous costs Parameters.ACTIVITY_KEY: :class:`str` (parameter) key to use to identify the activity described by the events Parameters.PARAM_TRACE_NET_COSTS: :class:`dict` (parameter) mapping between transitions and costs Returns ------- dictionary: `dict` with keys **alignment**, **cost**, **visited_states**, **queued_states** and **traversed_arcs** """ if parameters is None: parameters = {} ret_tuple_as_trans_desc = exec_utils.get_param_value(Parameters.PARAM_ALIGNMENT_RESULT_IS_SYNC_PROD_AWARE, parameters, False) trace_cost_function = exec_utils.get_param_value(Parameters.PARAM_TRACE_COST_FUNCTION, parameters, None) model_cost_function = exec_utils.get_param_value(Parameters.PARAM_MODEL_COST_FUNCTION, parameters, None) sync_cost_function = exec_utils.get_param_value(Parameters.PARAM_SYNC_COST_FUNCTION, parameters, None) trace_net_costs = exec_utils.get_param_value(Parameters.PARAM_TRACE_NET_COSTS, parameters, None) if trace_cost_function is None or model_cost_function is None or sync_cost_function is None: sync_prod, sync_initial_marking, sync_final_marking = petri.synchronous_product.construct(trace_net, trace_im, trace_fm, petri_net, initial_marking, final_marking, utils.SKIP) cost_function = utils.construct_standard_cost_function(sync_prod, utils.SKIP) else: revised_sync = dict() for t_trace in trace_net.transitions: for t_model in petri_net.transitions: if t_trace.label == t_model.label: revised_sync[(t_trace, t_model)] = sync_cost_function[t_model] sync_prod, sync_initial_marking, sync_final_marking, cost_function = construct_cost_aware( trace_net, trace_im, trace_fm, petri_net, initial_marking, final_marking, utils.SKIP, trace_net_costs, model_cost_function, revised_sync) max_align_time_trace = exec_utils.get_param_value(Parameters.PARAM_MAX_ALIGN_TIME_TRACE, parameters, sys.maxsize) return apply_sync_prod(sync_prod, sync_initial_marking, sync_final_marking, cost_function, utils.SKIP, ret_tuple_as_trans_desc=ret_tuple_as_trans_desc, max_align_time_trace=max_align_time_trace)
def apply_trace_net(petri_net, initial_marking, final_marking, trace_net, trace_im, trace_fm, parameters=None): if parameters is None: parameters = {} ret_tuple_as_trans_desc = parameters[ PARAM_ALIGNMENT_RESULT_IS_SYNC_PROD_AWARE] if PARAM_ALIGNMENT_RESULT_IS_SYNC_PROD_AWARE in parameters else False if parameters is None or PARAM_TRACE_COST_FUNCTION not in parameters or PARAM_MODEL_COST_FUNCTION not in parameters or PARAM_SYNC_COST_FUNCTION not in parameters: sync_prod, sync_initial_marking, sync_final_marking = petri.synchronous_product.construct(trace_net, trace_im, trace_fm, petri_net, initial_marking, final_marking, utils.SKIP) cost_function = utils.construct_standard_cost_function(sync_prod, utils.SKIP) else: revised_sync = dict() for t_trace in trace_net.transitions: for t_model in petri_net.transitions: if t_trace.label == t_model.label: revised_sync[(t_trace, t_model)] = parameters[PARAM_SYNC_COST_FUNCTION][t_model] sync_prod, sync_initial_marking, sync_final_marking, cost_function = construct_cost_aware( trace_net, trace_im, trace_fm, petri_net, initial_marking, final_marking, utils.SKIP, parameters[PARAM_TRACE_NET_COSTS], parameters[PARAM_MODEL_COST_FUNCTION], revised_sync) max_align_time_trace = parameters[ PARAM_MAX_ALIGN_TIME_TRACE] if PARAM_MAX_ALIGN_TIME_TRACE in parameters else DEFAULT_MAX_ALIGN_TIME_TRACE return apply_sync_prod(sync_prod, sync_initial_marking, sync_final_marking, cost_function, utils.SKIP, ret_tuple_as_trans_desc=ret_tuple_as_trans_desc, max_align_time_trace=max_align_time_trace)
def align_fake_log_stop_marking(fake_log, net, marking, final_marking, parameters=None): """ Align the 'fake' log_skeleton with all the prefixes in order to get the markings in which the alignment stops Parameters ------------- fake_log Fake log_skeleton net Petri net marking Marking final_marking Final marking parameters Parameters of the algorithm Returns ------------- alignment For each trace in the log_skeleton, return the marking in which the alignment stops (expressed as place name with count) """ if parameters is None: parameters = {} align_result = [] for i in range(len(fake_log)): trace = fake_log[i] sync_net, sync_initial_marking, sync_final_marking = build_sync_net(trace, net, marking, final_marking, parameters=parameters) stop_marking = Marking() for pl, count in sync_final_marking.items(): if pl.name[1] == utils.SKIP: stop_marking[pl] = count cost_function = utils.construct_standard_cost_function(sync_net, utils.SKIP) # perform the alignment of the prefix res = precision_utils.__search(sync_net, sync_initial_marking, sync_final_marking, stop_marking, cost_function, utils.SKIP) if res is not None: align_result.append([]) for mark in res: res2 = {} for pl in mark: # transforms the markings for easier correspondence at the end # (distributed engine friendly!) res2[(pl.name[0], pl.name[1])] = mark[pl] align_result[-1].append(res2) else: # if there is no path from the initial marking # replaying the given prefix, then add None align_result.append(None) return align_result
def align_fake_log_stop_marking(fake_log, net, marking, final_marking, parameters=None): """ Align the 'fake' log with all the prefixes in order to get the markings in which the alignment stops Parameters ------------- fake_log Fake log net Petri net marking Marking final_marking Final marking parameters Parameters of the algorithm Returns ------------- alignment For each trace in the log, return the marking in which the alignment stops (expressed as place name with count) """ if parameters is None: parameters = {} max_trace_length = max(len(x) for x in fake_log) align_result = [] for i in range(len(fake_log)): trace = fake_log[i] sync_net, sync_initial_marking, sync_final_marking = build_sync_net( trace, net, marking, final_marking) stop_marking = petri.petrinet.Marking() for pl, count in sync_final_marking.items(): if pl.name[1] == utils.SKIP: stop_marking[pl] = count cost_function = utils.construct_standard_cost_function( sync_net, utils.SKIP) res = __search(sync_net, sync_initial_marking, sync_final_marking, stop_marking, cost_function, utils.SKIP, max_trace_length) res2 = {} for pl in res: res2[(pl.name[0], pl.name[1])] = res[pl] align_result.append(res2) return align_result
def __init__(self, net: PetriNet, im: Marking, fm: Marking, parameters: Optional[Dict[Any, Any]] = None): """ Constructor Parameters --------------- net Petri net im Initial marking fm Final marking parameters Parameters of the algorithm, including: - Parameters.CASE_ID_KEY => attribute to use as case identifier - Parameters.ACTIVITY_KEY => attribute to use as activity - Parameters.COSTS => (if provided) the cost function (otherwise the default cost function is applied) - Parameters.INCIDENCE_MATRIX => (if provided) the incidence matrix of the sync product net - Parameters.A => (if provided) the A numpy matrix of the incidence matrix - Parameters.FULL_BOOTSTRAP_REQUIRED => The preset/postset of places/transitions need to be inserted """ if parameters is None: parameters = {} costs = exec_utils.get_param_value(Parameters.COSTS, parameters, None) if costs is None: costs = align_utils.construct_standard_cost_function(net, align_utils.SKIP) self.net = net self.ini = im self.fin = fm self.costs = costs self.incidence_matrix = exec_utils.get_param_value(Parameters.INCIDENCE_MATRIX, parameters, IncidenceMatrix(self.net)) self.Aeq = exec_utils.get_param_value(Parameters.A, parameters, np.asmatrix(self.incidence_matrix.a_matrix)) self.full_bootstrap_required = exec_utils.get_param_value(Parameters.FULL_BOOTSTRAP_REQUIRED, parameters, True) self.__build_entities() self.__build_problem_components()
def apply_trace_net(petri_net, initial_marking, final_marking, trace_net, trace_im, trace_fm): """ Apply the alignment to a Petri net with initial and final marking, providing the trace net Parameters ------------- petri_net Model initial_marking IM of the model final_marking FM of the model trace_net Trace net trace_im IM of the trace net trace_fm FM of the trace net Returns ------------- alignment Alignment cost_function Cost function """ sync_prod, sync_initial_marking, sync_final_marking = petri.synchronous_product.construct( trace_net, trace_im, trace_fm, petri_net, initial_marking, final_marking, utils.SKIP) cost_function = utils.construct_standard_cost_function( sync_prod, utils.SKIP) return __search(sync_prod, sync_initial_marking, sync_final_marking, cost_function, utils.SKIP), cost_function
def apply(log, net, marking, final_marking, parameters=None): """ Get Align-ET Conformance precision Parameters ---------- log Trace log net Petri net marking Initial marking final_marking Final marking parameters Parameters of the algorithm, including: pm4py.util.constants.PARAMETER_CONSTANT_ACTIVITY_KEY -> Activity key """ if parameters is None: parameters = {} activity_key = parameters[ PARAM_ACTIVITY_KEY] if PARAM_ACTIVITY_KEY in parameters else log_lib.util.xes.DEFAULT_NAME_KEY precision = 0.0 sum_ee = 0 sum_at = 0 if not (petri.check_soundness.check_wfnet(net) and petri.check_soundness.check_relaxed_soundness_net_in_fin_marking( net, marking, final_marking)): raise Exception( "trying to apply Align-ETConformance on a Petri net that is not a relaxed sound workflow net!!" ) places_corr = {p.name: p for p in net.places} prefixes, prefix_count = precision_utils.get_log_prefixes( log, activity_key=activity_key) prefixes_keys = list(prefixes.keys()) fake_log = precision_utils.form_fake_log(prefixes_keys, activity_key=activity_key) max_trace_length = max(len(x) for x in fake_log) for i in range(len(fake_log)): trace = fake_log[i] sync_net, sync_initial_marking, sync_final_marking = build_sync_net( trace, net, marking, final_marking) stop_marking = petri.petrinet.Marking() for pl, count in sync_final_marking.items(): if pl.name[1] == utils.SKIP: stop_marking[pl] = count cost_function = utils.construct_standard_cost_function( sync_net, utils.SKIP) res = __search(sync_net, sync_initial_marking, sync_final_marking, stop_marking, cost_function, utils.SKIP, max_trace_length) atm = petri.petrinet.Marking() for pl, count in res.items(): if pl.name[0] == utils.SKIP: atm[places_corr[pl.name[1]]] = count log_transitions = set(prefixes[prefixes_keys[i]]) activated_transitions_labels = set( x.label for x in utils. get_visible_transitions_eventually_enabled_by_marking(net, atm) if x.label is not None) sum_at += len(activated_transitions_labels) * prefix_count[ prefixes_keys[i]] escaping_edges = activated_transitions_labels.difference( log_transitions) sum_ee += len(escaping_edges) * prefix_count[prefixes_keys[i]] if sum_at > 0: precision = 1 - float(sum_ee) / float(sum_at) return precision
def __init__(self, trace: Trace, sync_net: PetriNet, sync_im: Marking, sync_fm: Marking, parameters: Optional[Dict[Any, Any]] = None): """ Constructor Parameters --------------- trace Trace sync_net Synchronous product net sync_im Initial marking sync_fm Final marking parameters Parameters of the algorithm, including: - Parameters.CASE_ID_KEY => attribute to use as case identifier - Parameters.ACTIVITY_KEY => attribute to use as activity - Parameters.COSTS => (if provided) the cost function (otherwise the default cost function is applied) - Parameters.SPLIT_IDX => (if provided) the split points as indices of elements of the trace (e.g. for ["A", "B", "C", "D", "E"], specifying [1,3] as split points means splitting at "B" and "D"). If not provided, some split points at uniform distances are found. - Parameters.MAX_K_VALUE => the maximum number of split points that is allowed (trim the specified indexes if necessary). - Parameters.INCIDENCE_MATRIX => (if provided) the incidence matrix associated to the sync product net - Parameters.A => (if provided) the A numpy matrix of the incidence matrix - Parameters.CONSUMPTION_MATRIX => (if provided) the consumption matrix associated to the sync product net - Parameters.C => (if provided) the C numpy matrix of the consumption matrix - Parameters.FULL_BOOTSTRAP_REQUIRED => The preset/postset of places/transitions need to be inserted """ if parameters is None: parameters = {} activity_key = exec_utils.get_param_value(Parameters.ACTIVITY_KEY, parameters, xes_constants.DEFAULT_NAME_KEY) max_k_value = exec_utils.get_param_value(Parameters.MAX_K_VALUE, parameters, 5) costs = exec_utils.get_param_value(Parameters.COSTS, parameters, None) split_idx = exec_utils.get_param_value(Parameters.SPLIT_IDX, parameters, None) self.full_bootstrap_required = exec_utils.get_param_value(Parameters.FULL_BOOTSTRAP_REQUIRED, parameters, True) self.trace = [x[activity_key] for x in trace] if costs is None: costs = align_utils.construct_standard_cost_function(sync_net, align_utils.SKIP) if split_idx is None: split_idx = [i for i in range(1, len(trace))] self.split_idx = split_idx if len(self.split_idx) > max_k_value: self.split_idx = points_subset.pick_chosen_points_list(max_k_value, self.split_idx) self.k = len(self.split_idx) if len(self.split_idx) > 1 else 2 self.sync_net = sync_net self.ini = sync_im self.fin = sync_fm self.costs = costs self.incidence_matrix = exec_utils.get_param_value(Parameters.INCIDENCE_MATRIX, parameters, IncidenceMatrix(self.sync_net)) self.consumption_matrix = exec_utils.get_param_value(Parameters.CONSUMPTION_MATRIX, parameters, ConsumptionMatrix(self.sync_net)) self.A = exec_utils.get_param_value(Parameters.A, parameters, np.asmatrix(self.incidence_matrix.a_matrix)) self.C = exec_utils.get_param_value(Parameters.C, parameters, np.asmatrix(self.consumption_matrix.c_matrix)) self.__build_entities()