def apply(log, petri_net, initial_marking, final_marking, parameters=None, variant=None): """ Apply fitness evaluation starting from an event log and a marked Petri net, by using one of the replay techniques provided by PM4Py Parameters ----------- log Trace log object petri_net Petri net initial_marking Initial marking final_marking Final marking parameters Parameters related to the replay algorithm variant Chosen variant: - Variants.ALIGNMENT_BASED - Variants.TOKEN_BASED Returns ---------- fitness_eval Fitness evaluation """ if parameters is None: parameters = {} # execute the following part of code when the variant is not specified by the user if variant is None: if not ( check_easy_soundness_net_in_fin_marking(petri_net, initial_marking, final_marking)): # in the case the net is not a easy sound workflow net, we must apply token-based replay variant = TOKEN_BASED else: # otherwise, use the align-etconformance approach (safer, in the case the model contains duplicates) variant = ALIGNMENT_BASED if variant == TOKEN_BASED: # execute the token-based replay variant return exec_utils.get_variant(variant).apply(log_conversion.apply(log, parameters, log_conversion.TO_EVENT_LOG), petri_net, initial_marking, final_marking, parameters=parameters) else: # execute the alignments based variant, with the specification of the alignments variant align_variant = exec_utils.get_param_value(Parameters.ALIGN_VARIANT, parameters, alignments.algorithm.DEFAULT_VARIANT) return exec_utils.get_variant(variant).apply(log_conversion.apply(log, parameters, log_conversion.TO_EVENT_LOG), petri_net, initial_marking, final_marking, align_variant=align_variant, parameters=parameters)
def apply(log, net, marking, final_marking, parameters=None, variant=None): """ Method to apply ET Conformance 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 variant Variant of the algorithm that should be applied: - Variants.ETCONFORMANCE_TOKEN - Variants.ALIGN_ETCONFORMANCE """ warnings.warn("Use the pm4py.algo.evaluation.precision package") if parameters is None: parameters = {} log = log_conversion.apply(log, parameters, log_conversion.TO_EVENT_LOG) # execute the following part of code when the variant is not specified by the user if variant is None: if not (check_easy_soundness_net_in_fin_marking( net, marking, final_marking)): # in the case the net is not a easy sound workflow net, we must apply token-based replay variant = ETCONFORMANCE_TOKEN else: # otherwise, use the align-etconformance approach (safer, in the case the model contains duplicates) variant = ALIGN_ETCONFORMANCE return exec_utils.get_variant(variant).apply(log, net, marking, final_marking, parameters=parameters)
def apply_log(log, petri_net, initial_marking, final_marking, parameters=None, variant=DEFAULT_VARIANT): """ apply alignments to a log Parameters ----------- log object of the form :class:`pm4py.log.log.EventLog` event log petri_net :class:`pm4py.objects.petri.petrinet.PetriNet` the model to use for the alignment initial_marking :class:`pm4py.objects.petri.petrinet.Marking` initial marking of the net final_marking :class:`pm4py.objects.petri.petrinet.Marking` final marking of the net variant selected variant of the algorithm, possible values: {\'Variants.VERSION_STATE_EQUATION_A_STAR, Variants.VERSION_DIJKSTRA_NO_HEURISTICS \'} parameters :class:`dict` parameters of the algorithm, Returns ----------- alignment :class:`list` of :class:`dict` with keys **alignment**, **cost**, **visited_states**, **queued_states** and **traversed_arcs** The alignment is a sequence of labels of the form (a,t), (a,>>), or (>>,t) representing synchronous/log/model-moves. """ if parameters is None: parameters = dict() if not check_soundness.check_easy_soundness_net_in_fin_marking( petri_net, initial_marking, final_marking): raise Exception( "trying to apply alignments on a Petri net that is not a easy sound net!!" ) start_time = time.time() max_align_time = exec_utils.get_param_value( Parameters.PARAM_MAX_ALIGN_TIME, parameters, sys.maxsize) max_align_time_case = exec_utils.get_param_value( Parameters.PARAM_MAX_ALIGN_TIME_TRACE, parameters, sys.maxsize) parameters_best_worst = copy(parameters) best_worst_cost = exec_utils.get_variant(variant).get_best_worst_cost( petri_net, initial_marking, final_marking, parameters=parameters_best_worst) variants_idxs = exec_utils.get_param_value(Parameters.VARIANTS_IDX, parameters, None) if variants_idxs is None: variants_idxs = variants_module.get_variants_from_log_trace_idx( log, parameters=parameters) one_tr_per_var = [] variants_list = [] for index_variant, var in enumerate(variants_idxs): variants_list.append(var) for var in variants_list: one_tr_per_var.append(log[variants_idxs[var][0]]) all_alignments = [] for trace in one_tr_per_var: this_max_align_time = min(max_align_time_case, (max_align_time - (time.time() - start_time)) * 0.5) parameters[Parameters.PARAM_MAX_ALIGN_TIME_TRACE] = this_max_align_time all_alignments.append( apply_trace(trace, petri_net, initial_marking, final_marking, parameters=copy(parameters), variant=variant)) al_idx = {} for index_variant, variant in enumerate(variants_idxs): for trace_idx in variants_idxs[variant]: al_idx[trace_idx] = all_alignments[index_variant] alignments = [] for i in range(len(log)): alignments.append(al_idx[i]) # assign fitness to traces for index, align in enumerate(alignments): if align is not None: unfitness_upper_part = align[ 'cost'] // align_utils.STD_MODEL_LOG_MOVE_COST if unfitness_upper_part == 0: align['fitness'] = 1 elif (len(log[index]) + best_worst_cost) > 0: align['fitness'] = 1 - ( (align['cost'] // align_utils.STD_MODEL_LOG_MOVE_COST) / (len(log[index]) + best_worst_cost)) else: align['fitness'] = 0 return alignments
def apply_log_multiprocessing(log, petri_net, initial_marking, final_marking, parameters=None, version=DEFAULT_VARIANT): warnings.warn('factory methods are deprecated, use algorithm entrypoint instead', DeprecationWarning) if parameters is None: parameters = dict() if not check_soundness.check_easy_soundness_net_in_fin_marking(petri_net, initial_marking, final_marking): raise Exception("trying to apply alignments on a Petri net that is not a relaxed sound net!!") activity_key = parameters[ PARAMETER_CONSTANT_ACTIVITY_KEY] if PARAMETER_CONSTANT_ACTIVITY_KEY in parameters else DEFAULT_NAME_KEY model_cost_function = parameters[ PARAM_MODEL_COST_FUNCTION] if PARAM_MODEL_COST_FUNCTION in parameters else None sync_cost_function = parameters[ PARAM_SYNC_COST_FUNCTION] if PARAM_SYNC_COST_FUNCTION in parameters else None if model_cost_function is None or sync_cost_function is None: # reset variables value model_cost_function = dict() sync_cost_function = dict() for t in petri_net.transitions: if t.label is not None: model_cost_function[t] = align_utils.STD_MODEL_LOG_MOVE_COST sync_cost_function[t] = 0 else: model_cost_function[t] = 1 parameters[pm4py.util.constants.PARAMETER_CONSTANT_ACTIVITY_KEY] = activity_key parameters[ PARAM_MODEL_COST_FUNCTION] = model_cost_function parameters[ PARAM_SYNC_COST_FUNCTION] = sync_cost_function parameters_best_worst = copy(parameters) if PARAM_MAX_ALIGN_TIME_TRACE in parameters_best_worst: del parameters_best_worst[PARAM_MAX_ALIGN_TIME_TRACE] best_worst_cost = VERSIONS_COST[version](petri_net, initial_marking, final_marking, parameters=parameters_best_worst) variants_idxs = parameters[VARIANTS_IDX] if VARIANTS_IDX in parameters else None if variants_idxs is None: variants_idxs = variants_module.get_variants_from_log_trace_idx(log, parameters=parameters) variants_list = [[x, len(y)] for x, y in variants_idxs.items()] no_cores = mp.cpu_count() petri_net_string = petri_exporter.export_petri_as_string(petri_net, initial_marking, final_marking) n = math.ceil(len(variants_list) / no_cores) variants_list_split = list(chunks(variants_list, n)) # Define an output queue output = mp.Queue() processes = [mp.Process( target=VERSIONS_VARIANTS_LIST_MPROCESSING[version](output, x, petri_net_string, parameters=parameters)) for x in variants_list_split] # Run processes for p in processes: p.start() results = [] for p in processes: result = output.get() results.append(result) al_idx = {} for index, el in enumerate(variants_list_split): for index2, var_item in enumerate(el): variant = var_item[0] for trace_idx in variants_idxs[variant]: al_idx[trace_idx] = results[index][variant] alignments = [] for i in range(len(log)): alignments.append(al_idx[i]) # assign fitness to traces for index, align in enumerate(alignments): if align is not None: unfitness_upper_part = align['cost'] // align_utils.STD_MODEL_LOG_MOVE_COST if unfitness_upper_part == 0: align['fitness'] = 1 elif (len(log[index]) + best_worst_cost) > 0: align['fitness'] = 1 - ( (align['cost'] // align_utils.STD_MODEL_LOG_MOVE_COST) / (len(log[index]) + best_worst_cost)) else: align['fitness'] = 0 return alignments
def apply_log(log, petri_net, initial_marking, final_marking, parameters=None, version=DEFAULT_VARIANT): """ apply alignments to a log Parameters ----------- log object of the form :class:`pm4py.log.log.EventLog` event log petri_net :class:`pm4py.objects.petri.petrinet.PetriNet` the model to use for the alignment initial_marking :class:`pm4py.objects.petri.petrinet.Marking` initial marking of the net final_marking :class:`pm4py.objects.petri.petrinet.Marking` final marking of the net version :class:`str` selected variant of the algorithm, possible values: {\'state_equation_a_star\'} parameters :class:`dict` parameters of the algorithm, for key \'state_equation_a_star\': pm4py.util.constants.PARAMETER_CONSTANT_ACTIVITY_KEY -> Attribute in the log that contains the activity pm4py.algo.conformance.alignments.variants.state_equation_a_star.PARAM_MODEL_COST_FUNCTION -> mapping of each transition in the model to corresponding synchronous costs pm4py.algo.conformance.alignments.variants.state_equation_a_star.PARAM_SYNC_COST_FUNCTION -> mapping of each transition in the model to corresponding model cost pm4py.algo.conformance.alignments.variants.state_equation_a_star.PARAM_TRACE_COST_FUNCTION -> mapping of each index of the trace to a positive cost value Returns ----------- alignment :class:`list` of :class:`dict` with keys **alignment**, **cost**, **visited_states**, **queued_states** and **traversed_arcs** The alignment is a sequence of labels of the form (a,t), (a,>>), or (>>,t) representing synchronous/log/model-moves. """ warnings.warn('factory methods are deprecated, use algorithm entrypoint instead', DeprecationWarning) if parameters is None: parameters = dict() if not check_soundness.check_easy_soundness_net_in_fin_marking(petri_net, initial_marking, final_marking): raise Exception("trying to apply alignments on a Petri net that is not a easy sound net!!") start_time = time.time() activity_key = parameters[ PARAMETER_CONSTANT_ACTIVITY_KEY] if PARAMETER_CONSTANT_ACTIVITY_KEY in parameters else DEFAULT_NAME_KEY model_cost_function = parameters[ PARAM_MODEL_COST_FUNCTION] if PARAM_MODEL_COST_FUNCTION in parameters else None sync_cost_function = parameters[ PARAM_SYNC_COST_FUNCTION] if PARAM_SYNC_COST_FUNCTION in parameters else None max_align_time = parameters[PARAM_MAX_ALIGN_TIME] if PARAM_MAX_ALIGN_TIME in parameters else DEFAULT_MAX_ALIGN_TIME max_align_time_case = parameters[ PARAM_MAX_ALIGN_TIME_TRACE] if PARAM_MAX_ALIGN_TIME_TRACE in parameters else DEFAULT_MAX_ALIGN_TIME_TRACE if model_cost_function is None or sync_cost_function is None: # reset variables value model_cost_function = dict() sync_cost_function = dict() for t in petri_net.transitions: if t.label is not None: model_cost_function[t] = align_utils.STD_MODEL_LOG_MOVE_COST sync_cost_function[t] = 0 else: model_cost_function[t] = 1 parameters[pm4py.util.constants.PARAMETER_CONSTANT_ACTIVITY_KEY] = activity_key parameters[ PARAM_MODEL_COST_FUNCTION] = model_cost_function parameters[ PARAM_SYNC_COST_FUNCTION] = sync_cost_function parameters_best_worst = copy(parameters) if PARAM_MAX_ALIGN_TIME_TRACE in parameters_best_worst: del parameters_best_worst[PARAM_MAX_ALIGN_TIME_TRACE] best_worst_cost = VERSIONS_COST[version](petri_net, initial_marking, final_marking, parameters=parameters_best_worst) variants_idxs = parameters[VARIANTS_IDX] if VARIANTS_IDX in parameters else None if variants_idxs is None: variants_idxs = variants_module.get_variants_from_log_trace_idx(log, parameters=parameters) one_tr_per_var = [] variants_list = [] for index_variant, variant in enumerate(variants_idxs): variants_list.append(variant) for variant in variants_list: one_tr_per_var.append(log[variants_idxs[variant][0]]) all_alignments = [] for trace in one_tr_per_var: this_max_align_time = min(max_align_time_case, (max_align_time - (time.time() - start_time)) * 0.5) parameters[PARAM_MAX_ALIGN_TIME_TRACE] = this_max_align_time all_alignments.append(apply_trace(trace, petri_net, initial_marking, final_marking, parameters=copy(parameters), version=version)) al_idx = {} for index_variant, variant in enumerate(variants_idxs): for trace_idx in variants_idxs[variant]: al_idx[trace_idx] = all_alignments[index_variant] alignments = [] for i in range(len(log)): alignments.append(al_idx[i]) # assign fitness to traces for index, align in enumerate(alignments): if align is not None: unfitness_upper_part = align['cost'] // align_utils.STD_MODEL_LOG_MOVE_COST if unfitness_upper_part == 0: align['fitness'] = 1 elif (len(log[index]) + best_worst_cost) > 0: align['fitness'] = 1 - ( (align['cost'] // align_utils.STD_MODEL_LOG_MOVE_COST) / (len(log[index]) + best_worst_cost)) else: align['fitness'] = 0 return alignments
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: Parameters.ACTIVITY_KEY -> Activity key """ if parameters is None: parameters = {} debug_level = parameters[ "debug_level"] if "debug_level" in parameters else 0 activity_key = exec_utils.get_param_value( Parameters.ACTIVITY_KEY, parameters, log_lib.util.xes.DEFAULT_NAME_KEY) # default value for precision, when no activated transitions (not even by looking at the initial marking) are found precision = 1.0 sum_ee = 0 sum_at = 0 unfit = 0 if not check_soundness.check_easy_soundness_net_in_fin_marking( net, marking, final_marking): raise Exception( "trying to apply Align-ETConformance on a Petri net that is not a easy sound net!!" ) 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) align_stop_marking = align_fake_log_stop_marking(fake_log, net, marking, final_marking, parameters=parameters) all_markings = transform_markings_from_sync_to_original_net( align_stop_marking, net, parameters=parameters) for i in range(len(prefixes)): markings = all_markings[i] if markings is not None: log_transitions = set(prefixes[prefixes_keys[i]]) activated_transitions_labels = set() for m in markings: # add to the set of activated transitions in the model the activated transitions # for each prefix activated_transitions_labels = activated_transitions_labels.union( x.label for x in utils. get_visible_transitions_eventually_enabled_by_marking( net, m) if x.label is not None) escaping_edges = activated_transitions_labels.difference( log_transitions) sum_at += len(activated_transitions_labels) * prefix_count[ prefixes_keys[i]] sum_ee += len(escaping_edges) * prefix_count[prefixes_keys[i]] if debug_level > 1: print("") print("prefix=", prefixes_keys[i]) print("log_transitions=", log_transitions) print("activated_transitions=", activated_transitions_labels) print("escaping_edges=", escaping_edges) else: unfit += prefix_count[prefixes_keys[i]] if debug_level > 0: print("\n") print("overall unfit", unfit) print("overall activated transitions", sum_at) print("overall escaping edges", sum_ee) # fix: also the empty prefix should be counted! start_activities = set(get_start_activities(log, parameters=parameters)) trans_en_ini_marking = set([ x.label for x in get_visible_transitions_eventually_enabled_by_marking( net, marking) ]) diff = trans_en_ini_marking.difference(start_activities) sum_at += len(log) * len(trans_en_ini_marking) sum_ee += len(log) * len(diff) # end fix if sum_at > 0: precision = 1 - float(sum_ee) / float(sum_at) return precision
def run_verifier(file): net, initial_marking, final_marking = petri_importer.apply(file) cycles = utils.get_cycles_petri_net_places(net) soundness = check_easy_soundness_net_in_fin_marking( net, initial_marking, final_marking) return {"soundness": soundness, "cycles": len(cycles)}