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 = 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 __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 __align_trace_stop_marking(trace, net, marking, final_marking, parameters=None): 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) return res
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 = {} show_progress_bar = exec_utils.get_param_value(Parameters.SHOW_PROGRESS_BAR, parameters, True) align_result = [] progress = None if pkgutil.find_loader("tqdm") and show_progress_bar and len(fake_log) > 1: from tqdm.auto import tqdm progress = tqdm(total=len(fake_log), desc="computing precision with alignments, completed variants :: ") 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) if progress is not None: progress.update() # gracefully close progress bar if progress is not None: progress.close() del progress return align_result
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()