def calculate_fitness_with_alignments(self, net, im, fm, log, parameters=None): if parameters is None: parameters = {} sum_fitness = 0 variants = log_variants_filter.get_variants_from_log_trace_idx(log, parameters=parameters) var_list = [[x, y] for x, y in variants.items()] result = self.perform_alignments_net_variants(net, im, fm, var_list=var_list, parameters=parameters) total_cases = 0 total_fit_cases = 0 best_worst_cost = state_equation_a_star.get_best_worst_cost(net, im, fm, parameters={}) for index_variant, variant in enumerate(variants): total_cases = total_cases + len(variants[variant]) if result[variant] is not None: fitness = self.fitness_alignment_internal(best_worst_cost, variant, result[variant]["cost"]) sum_fitness = sum_fitness + fitness * len(variants[variant]) if result[variant]["cost"] < 10000: total_fit_cases = total_fit_cases + len(variants[variant]) if total_cases > 0: perc_fit_traces = float(100.0 * total_fit_cases) / float(total_cases) return {"averageFitness": float(sum_fitness) / float(total_cases), "percFitTraces": perc_fit_traces} return {"averageFitness": 0.0, "percFitTraces": 0.0}
def __variantTable(self, eventLog): data = pd.DataFrame(variants_filter.get_variants_from_log_trace_idx(eventLog, parameters=None).items()) data.columns = ['Variants','Traces'] data['Variants'] = data['Variants'].apply(lambda r: r.split(',')) data.index.name = 'VariantID' return data
def calculate_fitness_with_tbr(self, net, im, fm, log, parameters=None): if parameters is None: parameters = {} variants = log_variants_filter.get_variants_from_log_trace_idx( log, parameters=parameters) var_list = [[x, y] for x, y in variants.items()] parameters["enable_parameters_precision"] = False parameters["consider_remaining_in_fitness"] = True result = self.perform_tbr_net_variants(net, im, fm, var_list=var_list, parameters=parameters) total_cases = 0 total_fit_cases = 0 sum_of_fitness = 0 total_m = 0 total_r = 0 total_c = 0 total_p = 0 for index_variant, variant in enumerate(variants): if result[index_variant] is not None: sum_of_fitness = sum_of_fitness + len( variants[variant]) * result[index_variant]["trace_fitness"] total_m = total_m + len(variants[variant]) * result[ index_variant]["missing_tokens"] total_r = total_r + len(variants[variant]) * result[ index_variant]["remaining_tokens"] total_c = total_c + len(variants[variant]) * result[ index_variant]["consumed_tokens"] total_p = total_p + len(variants[variant]) * result[ index_variant]["produced_tokens"] total_cases = total_cases + len(variants[variant]) if result[index_variant]["trace_is_fit"]: total_fit_cases = total_fit_cases + len(variants[variant]) if total_cases > 0: perc_fit_traces = float( 100.0 * total_fit_cases) / float(total_cases) average_fitness = float(sum_of_fitness) / float(total_cases) log_fitness = 0.5 * (1 - total_m / total_c) + 0.5 * ( 1 - total_r / total_p) return { "perc_fit_traces": perc_fit_traces, "average_trace_fitness": average_fitness, "log_fitness": log_fitness } return { "perc_fit_traces": 0.0, "average_trace_fitness": 0.0, "log_fitness": 0.0 }
def get_transition_performance_with_token_replay(log, net, im, fm): """ Gets the transition performance through the usage of token-based replay Parameters ------------- log Event log net Petri net im Initial marking fm Final marking Returns -------------- transition_performance Dictionary where each transition label is associated to performance measures """ variants_idx = variants_module.get_variants_from_log_trace_idx(log) aligned_traces = token_replay.apply(log, net, im, fm) element_statistics = performance_map.single_element_statistics( log, net, im, aligned_traces, variants_idx) transition_performance = {} for el in element_statistics: if type(el) is PetriNet.Transition and el.label is not None: if "log_idx" in element_statistics[ el] and "performance" in element_statistics[el]: if len(element_statistics[el]["performance"]) > 0: transition_performance[str(el)] = { "all_values": [], "case_association": {}, "mean": 0.0, "median": 0.0 } for i in range(len(element_statistics[el]["log_idx"])): if not element_statistics[el]["log_idx"][ i] in transition_performance[str( el)]["case_association"]: transition_performance[str( el)]["case_association"][element_statistics[el] ["log_idx"][i]] = [] transition_performance[str(el)]["case_association"][ element_statistics[el]["log_idx"][i]].append( element_statistics[el]["performance"][i]) transition_performance[str(el)]["all_values"].append( element_statistics[el]["performance"][i]) transition_performance[str(el)]["all_values"] = sorted( transition_performance[str(el)]["all_values"]) if transition_performance[str(el)]["all_values"]: transition_performance[str(el)]["mean"] = mean( transition_performance[str(el)]["all_values"]) transition_performance[str(el)]["median"] = median( transition_performance[str(el)]["all_values"]) return transition_performance
def calculate_precision_with_tbr(self, net, im, fm, log, parameters=None): from pm4py import util as pmutil from pm4py.algo.conformance.tokenreplay import factory as token_replay from pm4py.objects import log as log_lib from pm4py.evaluation.precision import utils as precision_utils if parameters is None: parameters = {} sum_at = 0.0 sum_ee = 0.0 prefixes, prefix_count = precision_utils.get_log_prefixes(log) print("got prefixes") prefixes_keys = list(prefixes.keys()) fake_log = precision_utils.form_fake_log(prefixes_keys) print("got fake log") variants = log_variants_filter.get_variants_from_log_trace_idx( fake_log, parameters=parameters) print("got variants from fake log") var_list = [[x, y] for x, y in variants.items()] print("got var list") parameters["enable_parameters_precision"] = True parameters["consider_remaining_in_fitness"] = False aligned_traces = self.perform_tbr_net_variants(net, im, fm, var_list=var_list, parameters=parameters) print("got aligned traces") for i in range(len(aligned_traces)): if aligned_traces[i]["trace_is_fit"]: log_transitions = set(prefixes[prefixes_keys[i]]) activated_transitions_labels = set([ x for x in aligned_traces[i] ["enabled_transitions_in_marking_labels"] if x != "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 __variantTable(self, eventLog): """ Create a table containing the following attributes: VariantID, Variants and Traces :param eventLog: an eventLog object obtained from the pm4py library :return: a pandas DataFrame with the mentioned attributes """ data = pd.DataFrame(variants_filter.get_variants_from_log_trace_idx(eventLog, parameters=None).items()) data.columns = ['Variants','Traces'] data['Variants'] = data['Variants'].apply(lambda r: r.split(',')) data.index.name = 'VariantID' return data
def perform_tbr_net_log(self, net, im, fm, log, parameters=None): if parameters is None: parameters = {} variants = log_variants_filter.get_variants_from_log_trace_idx(log, parameters=parameters) var_list = [[x, y] for x,y in variants.items()] result = self.perform_tbr_net_variants(net, im, fm, var_list=var_list, parameters=parameters) al_idx = {} for index_variant, variant in enumerate(variants): for trace_idx in variants[variant]: al_idx[trace_idx] = result[index_variant] tbr = [] for i in range(len(log)): tbr.append(al_idx[i]) return tbr
def perform_alignments_tree_log(self, tree, log, parameters=None): if parameters is None: parameters = {} variants = log_variants_filter.get_variants_from_log_trace_idx(log, parameters=parameters) var_list = [[x, y] for x, y in variants.items()] result = self.perform_alignments_tree_variants(tree, var_list=var_list, parameters=parameters) al_idx = {} for index_variant, variant in enumerate(variants): for trace_idx in variants[variant]: al_idx[trace_idx] = result[variant] alignments = [] for i in range(len(log)): alignments.append(al_idx[i]) return alignments
def apply(log, aligned_traces, parameters=None): """ Gets the alignment table visualization from the alignments output Parameters ------------- log Event log aligned_traces Aligned traces parameters Parameters of the algorithm Returns ------------- gviz Graphviz object """ if parameters is None: parameters = {} variants_idx_dict = variants_filter.get_variants_from_log_trace_idx(log, parameters=parameters) variants_idx_list = [] for variant in variants_idx_dict: variants_idx_list.append((variant, variants_idx_dict[variant])) variants_idx_list = sorted(variants_idx_list, key=lambda x: len(x[1]), reverse=True) image_format = parameters["format"] if "format" in parameters else "png" table_alignments_list = ["digraph {\n", "tbl [\n", "shape=plaintext\n", "label=<\n"] table_alignments_list.append("<table border='0' cellborder='1' color='blue' cellspacing='0'>\n") table_alignments_list.append("<tr><td>Variant</td><td>Alignment</td></tr>\n") for index, variant in enumerate(variants_idx_list): al_tr = aligned_traces[variant[1][0]] table_alignments_list.append("<tr>") table_alignments_list.append( "<td><font point-size='9'>Variant " + str(index + 1) + " (" + str(len(variant[1])) + " occurrences)<br />" + variant[ 0] + "</font></td>") table_alignments_list.append("<td><font point-size='6'><table border='0'><tr>") for move in al_tr['alignment']: move_descr = str(move[1]).replace(">", ">") if not move[0][0] == ">>" or move[0][1] == ">>": table_alignments_list.append("<td bgcolor=\"green\">" + move_descr + "</td>") elif move[0][1] == ">>": table_alignments_list.append("<td bgcolor=\"violet\">" + move_descr + "</td>") elif move[0][0] == ">>": table_alignments_list.append("<td bgcolor=\"gray\">" + move_descr + "</td>") table_alignments_list.append("</tr></table></font></td>") table_alignments_list.append("</tr>") table_alignments_list.append("</table>\n") table_alignments_list.append(">];\n") table_alignments_list.append("}\n") table_alignments = "".join(table_alignments_list) filename = tempfile.NamedTemporaryFile(suffix='.gv') gviz = Source(table_alignments, filename=filename.name) gviz.format = image_format return gviz
def apply_log(log, petri_net, initial_marking, final_marking, parameters=None, version=VERSION_STATE_EQUATION_A_STAR): """ apply alignments to a trace Parameters ----------- log object of the form :class:`pm4py.log.log.Trace` trace of events 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.versions.state_equation_a_star.PARAM_MODEL_COST_FUNCTION -> mapping of each transition in the model to corresponding synchronous costs pm4py.algo.conformance.alignments.versions.state_equation_a_star.PARAM_SYNC_COST_FUNCTION -> mapping of each transition in the model to corresponding model cost pm4py.algo.conformance.alignments.versions.state_equation_a_star.PARAM_TRACE_COST_FUNCTION -> mapping of each index of the trace to a positive cost value Returns ----------- alignment :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() 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] = ali.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 best_worst_cost = VERSIONS_COST[version](petri_net, initial_marking, final_marking, parameters=parameters) variants = variants_module.get_variants_from_log_trace_idx(log, parameters=parameters) one_tr_per_var = [] for index_variant, variant in enumerate(variants): one_tr_per_var.append(log[variants[variant][0]]) all_alignments = list(map( lambda trace: apply_trace(trace, petri_net, initial_marking, final_marking, parameters=copy(parameters), version=version), one_tr_per_var)) al_idx = {} for index_variant, variant in enumerate(variants): for trace_idx in variants[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): unfitness_upper_part = align['cost'] if unfitness_upper_part == 0: align['fitness'] = 1 elif (len(log[index]) + best_worst_cost) > 0: align['fitness'] = 1 - align['cost'] / ( len(log[index]) * ali.utils.STD_MODEL_LOG_MOVE_COST + best_worst_cost) else: align['fitness'] = 0 return alignments
def apply_multiprocessing(log, net, initial_marking, final_marking, parameters=None, variant=TOKEN_REPLAY): if parameters is None: parameters = {} if pmutil.constants.PARAMETER_CONSTANT_ACTIVITY_KEY not in parameters: parameters[pmutil.constants. PARAMETER_CONSTANT_ACTIVITY_KEY] = xes_util.DEFAULT_NAME_KEY if pmutil.constants.PARAMETER_CONSTANT_TIMESTAMP_KEY not in parameters: parameters[ pmutil.constants. PARAMETER_CONSTANT_TIMESTAMP_KEY] = xes_util.DEFAULT_TIMESTAMP_KEY if pmutil.constants.PARAMETER_CONSTANT_CASEID_KEY not in parameters: parameters[ pmutil.constants. PARAMETER_CONSTANT_CASEID_KEY] = log_util.CASE_ATTRIBUTE_GLUE petri_string = petri_exporter.export_petri_as_string( net, initial_marking, final_marking) 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( 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_MULTIPROCESSING[variant]( 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][index2] replayed_cases = [] for i in range(len(log)): replayed_cases.append(al_idx[i]) return replayed_cases
def get_decorations(log, net, initial_marking, final_marking, parameters=None, measure="frequency", ht_perf_method="last"): """ Calculate decorations in order to annotate the Petri net Parameters ----------- log Trace log net Petri net initial_marking Initial marking final_marking Final marking parameters Parameters associated to the algorithm measure Measure to represent on the process model (frequency/performance) ht_perf_method Method to use in order to annotate hidden transitions (performance value could be put on the last possible point (last) or in the first possible point (first) Returns ------------ decorations Decorations to put on the process model """ if parameters is None: parameters = {} aggregation_measure = None if "aggregationMeasure" in parameters: aggregation_measure = parameters["aggregationMeasure"] activity_key = parameters[ PARAM_ACTIVITY_KEY] if PARAM_ACTIVITY_KEY in parameters else log_lib.util.xes.DEFAULT_NAME_KEY timestamp_key = parameters[ PARAM_TIMESTAMP_KEY] if PARAM_TIMESTAMP_KEY in parameters else "time:timestamp" parameters_variants = {PARAM_ACTIVITY_KEY: activity_key} variants_idx = variants_module.get_variants_from_log_trace_idx( log, parameters=parameters_variants) variants = variants_module.convert_variants_trace_idx_to_trace_obj( log, variants_idx) parameters_tr = {PARAM_ACTIVITY_KEY: activity_key, "variants": variants} # do the replay aligned_traces = token_replay.apply(log, net, initial_marking, final_marking, parameters=parameters_tr) # apply petri_reduction technique in order to simplify the Petri net # net = reduction.apply(net, parameters={"aligned_traces": aligned_traces}) element_statistics = performance_map.single_element_statistics( log, net, initial_marking, aligned_traces, variants_idx, activity_key=activity_key, timestamp_key=timestamp_key, ht_perf_method=ht_perf_method) aggregated_statistics = performance_map.aggregate_statistics( element_statistics, measure=measure, aggregation_measure=aggregation_measure) return aggregated_statistics
def get_map_from_log_and_net(log, net, initial_marking, final_marking, force_distribution=None, parameters=None): """ Get transition stochastic distribution map given the log and the Petri net Parameters ----------- log Event log net Petri net initial_marking Initial marking of the Petri net final_marking Final marking of the Petri net force_distribution If provided, distribution to force usage (e.g. EXPONENTIAL) parameters Parameters of the algorithm, including: PARAM_ACTIVITY_KEY -> activity name PARAM_TIMESTAMP_KEY -> timestamp key Returns ----------- stochastic_map Map that to each transition associates a random variable """ stochastic_map = {} 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 timestamp_key = parameters[ PARAM_TIMESTAMP_KEY] if PARAM_TIMESTAMP_KEY in parameters else "time:timestamp" parameters_variants = {PARAM_ACTIVITY_KEY: activity_key} variants_idx = variants_module.get_variants_from_log_trace_idx( log, parameters=parameters_variants) variants = variants_module.convert_variants_trace_idx_to_trace_obj( log, variants_idx) parameters_tr = {PARAM_ACTIVITY_KEY: activity_key, "variants": variants} # do the replay aligned_traces = token_replay.apply(log, net, initial_marking, final_marking, parameters=parameters_tr) element_statistics = performance_map.single_element_statistics( log, net, initial_marking, aligned_traces, variants_idx, activity_key=activity_key, timestamp_key=timestamp_key, parameters={"business_hours": True}) for el in element_statistics: if type( el ) is PetriNet.Transition and "performance" in element_statistics[el]: values = element_statistics[el]["performance"] rand = RandomVariable() rand.calculate_parameters(values, force_distribution=force_distribution) no_of_times_enabled = element_statistics[el]['no_of_times_enabled'] no_of_times_activated = element_statistics[el][ 'no_of_times_activated'] if no_of_times_enabled > 0: rand.set_weight( float(no_of_times_activated) / float(no_of_times_enabled)) else: rand.set_weight(0.0) stochastic_map[el] = rand return stochastic_map
def apply_log_multiprocessing(log, petri_net, initial_marking, final_marking, parameters=None, version=VERSION_STATE_EQUATION_A_STAR): if parameters is None: parameters = dict() if not (check_soundness.check_wfnet(petri_net) and check_soundness.check_relaxed_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 workflow 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=VERSION_STATE_EQUATION_A_STAR): """ 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.versions.state_equation_a_star.PARAM_MODEL_COST_FUNCTION -> mapping of each transition in the model to corresponding synchronous costs pm4py.algo.conformance.alignments.versions.state_equation_a_star.PARAM_SYNC_COST_FUNCTION -> mapping of each transition in the model to corresponding model cost pm4py.algo.conformance.alignments.versions.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. """ if parameters is None: parameters = dict() if not (check_soundness.check_wfnet(petri_net) and check_soundness.check_relaxed_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 workflow 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