Пример #1
0
 def test_variant_state_eq_a_star(self):
     import pm4py
     log = pm4py.read_xes("input_data/running-example.xes")
     net, im, fm = pm4py.discover_petri_net_inductive(log)
     align_alg.apply(
         log,
         net,
         im,
         fm,
         variant=align_alg.Variants.VERSION_STATE_EQUATION_A_STAR)
Пример #2
0
 def test_variant_dijkstra_less_memory(self):
     import pm4py
     log = pm4py.read_xes("input_data/running-example.xes")
     net, im, fm = pm4py.discover_petri_net_inductive(log)
     align_alg.apply(
         log,
         net,
         im,
         fm,
         variant=align_alg.Variants.VERSION_DIJKSTRA_LESS_MEMORY)
Пример #3
0
 def test_alignment(self):
     log = xes_importer.apply(os.path.join("input_data", "running-example.xes"))
     from pm4py.algo.discovery.alpha import algorithm as alpha_miner
     net, im, fm = alpha_miner.apply(log)
     from pm4py.algo.conformance.alignments.petri_net import algorithm as alignments
     aligned_traces = alignments.apply(log, net, im, fm, variant=alignments.Variants.VERSION_STATE_EQUATION_A_STAR)
     aligned_traces = alignments.apply(log, net, im, fm, variant=alignments.Variants.VERSION_DIJKSTRA_NO_HEURISTICS)
     from pm4py.algo.evaluation.replay_fitness import algorithm as rp_fitness_evaluator
     fitness = rp_fitness_evaluator.apply(log, net, im, fm, variant=rp_fitness_evaluator.Variants.ALIGNMENT_BASED)
     evaluation = rp_fitness_evaluator.evaluate(aligned_traces,
                                                variant=rp_fitness_evaluator.Variants.ALIGNMENT_BASED)
     from pm4py.algo.evaluation.precision import algorithm as precision_evaluator
     precision = precision_evaluator.apply(log, net, im, fm, variant=rp_fitness_evaluator.Variants.ALIGNMENT_BASED)
Пример #4
0
def conformance_diagnostics_alignments(log: EventLog, *args, multi_processing: bool = False) -> List[Dict[str, Any]]:
    """
    Apply the alignments algorithm between a log and a process model.
    The methods return the full alignment diagnostics.

    Parameters
    -------------
    log
        Event log
    args
        Specification of the process model
    multi_processing
        Boolean value that enables the multiprocessing (default: False)

    Returns
    -------------
    aligned_traces
        A list of alignments for each trace of the log (in the same order as the traces in the event log)
    """
    if type(log) not in [pd.DataFrame, EventLog, EventStream]: raise Exception("the method can be applied only to a traditional event log!")

    if len(args) == 3:
        if type(args[0]) is PetriNet:
            # Petri net alignments
            from pm4py.algo.conformance.alignments.petri_net import algorithm as alignments
            if multi_processing:
                return alignments.apply_multiprocessing(log, args[0], args[1], args[2], parameters=get_properties(log))
            else:
                return alignments.apply(log, args[0], args[1], args[2], parameters=get_properties(log))
        elif type(args[0]) is dict or type(args[0]) is Counter:
            # DFG alignments
            from pm4py.algo.conformance.alignments.dfg import algorithm as dfg_alignment
            return dfg_alignment.apply(log, args[0], args[1], args[2], parameters=get_properties(log))
    elif len(args) == 1:
        if type(args[0]) is ProcessTree:
            # process tree alignments
            from pm4py.algo.conformance.alignments.process_tree.variants import search_graph_pt
            if multi_processing:
                return search_graph_pt.apply_multiprocessing(log, args[0], parameters=get_properties(log))
            else:
                return search_graph_pt.apply(log, args[0], parameters=get_properties(log))
    # try to convert to Petri net
    import pm4py
    from pm4py.algo.conformance.alignments.petri_net import algorithm as alignments
    net, im, fm = pm4py.convert_to_petri_net(*args)
    if multi_processing:
        return alignments.apply_multiprocessing(log, net, im, fm, parameters=get_properties(log))
    else:
        return alignments.apply(log, net, im, fm, parameters=get_properties(log))
Пример #5
0
def conformance_alignments(log: EventLog, petri_net: PetriNet, initial_marking: Marking,
                           final_marking: Marking) -> List[Dict[str, Any]]:
    warnings.warn('conformance_alignments is deprecated, use conformance_diagnostics_alignments', DeprecationWarning)
    """
    Apply the alignments algorithm between a log and a Petri net
    The methods return the full alignment diagnostics.

    Parameters
    -------------
    log
        Event log
    petri_net
        Petri net
    initial_marking
        Initial marking
    final_marking
        Final marking

    Returns
    -------------
    aligned_traces
        A list of alignments for each trace of the log
    """
    if type(log) not in [pd.DataFrame, EventLog, EventStream]: raise Exception("the method can be applied only to a traditional event log!")

    from pm4py.algo.conformance.alignments.petri_net import algorithm as alignments
    return alignments.apply(log, petri_net, initial_marking, final_marking, parameters=get_properties(log))
Пример #6
0
def conformance_alignments(log: EventLog, petri_net: PetriNet, initial_marking: Marking,
                           final_marking: Marking) -> List[Dict[str, Any]]:
    warnings.warn('conformance_alignments is deprecated, use conformance_diagnostics_alignments', DeprecationWarning)
    """
    Apply the alignments algorithm between a log and a Petri net
    The methods return the full alignment diagnostics.

    Parameters
    -------------
    log
        Event log
    petri_net
        Petri net
    initial_marking
        Initial marking
    final_marking
        Final marking

    Returns
    -------------
    aligned_traces
        A list of alignments for each trace of the log
    """
    from pm4py.algo.conformance.alignments.petri_net import algorithm as alignments
    return alignments.apply(log, petri_net, initial_marking, final_marking)
Пример #7
0
 def test_importingPetriLogAlignment(self):
     # to avoid static method warnings in tests,
     # that by construction of the unittest package have to be expressed in such way
     self.dummy_variable = "dummy_value"
     imported_petri1, marking1, fmarking1 = petri_importer.apply(
         os.path.join(INPUT_DATA_DIR, "running-example.pnml"))
     log = xes_importer.apply(
         os.path.join(INPUT_DATA_DIR, "running-example.xes"))
     final_marking = petri_net.obj.Marking()
     for p in imported_petri1.places:
         if not p.out_arcs:
             final_marking[p] = 1
     for trace in log:
         cf_result = align_alg.apply(
             trace,
             imported_petri1,
             marking1,
             final_marking,
             variant=align_alg.VERSION_DIJKSTRA_NO_HEURISTICS)['alignment']
         is_fit = True
         for couple in cf_result:
             if not (couple[0] == couple[1]
                     or couple[0] == ">>" and couple[1] is None):
                 is_fit = False
         if not is_fit:
             raise Exception("should be fit")
Пример #8
0
 def test_alignment(self):
     log = xes_importer.apply(
         os.path.join("input_data", "running-example.xes"))
     from pm4py.algo.discovery.alpha import algorithm as alpha_miner
     net, im, fm = alpha_miner.apply(log)
     from pm4py.algo.conformance.alignments.petri_net import algorithm as alignments
     aligned_traces = alignments.apply(
         log,
         net,
         im,
         fm,
         variant=alignments.Variants.VERSION_STATE_EQUATION_A_STAR)
     aligned_traces = alignments.apply(
         log,
         net,
         im,
         fm,
         variant=alignments.Variants.VERSION_DIJKSTRA_NO_HEURISTICS)
Пример #9
0
def execute_script():
    log = pm4py.read_xes(
        os.path.join("..", "tests", "input_data", "running-example.xes"))
    filtered_log = pm4py.filter_variants_top_k(log, 1)
    net, im, fm = pm4py.discover_petri_net_inductive(filtered_log)
    aligned_traces = alignments.apply(
        log, net, im, fm, parameters={"ret_tuple_as_trans_desc": True})
    enriched_log = log_enrichment.apply(log, aligned_traces)
    print(enriched_log)
Пример #10
0
 def test_inductiveminer_log(self):
     log = xes_importer.apply(os.path.join("input_data", "running-example.xes"))
     net, im, fm = inductive_miner.apply(log)
     aligned_traces_tr = tr_alg.apply(log, net, im, fm)
     aligned_traces_alignments = align_alg.apply(log, net, im, fm)
     evaluation = eval_alg.apply(log, net, im, fm)
     fitness = rp_fit.apply(log, net, im, fm)
     precision = precision_evaluator.apply(log, net, im, fm)
     gen = generalization.apply(log, net, im, fm)
     sim = simplicity.apply(net)
Пример #11
0
 def test_inductiveminer_df(self):
     log = pd.read_csv(os.path.join("input_data", "running-example.csv"))
     log = dataframe_utils.convert_timestamp_columns_in_df(log)
     net, im, fm = inductive_miner.apply(log)
     aligned_traces_tr = tr_alg.apply(log, net, im, fm)
     aligned_traces_alignments = align_alg.apply(log, net, im, fm)
     evaluation = eval_alg.apply(log, net, im, fm)
     fitness = rp_fit.apply(log, net, im, fm)
     precision = precision_evaluator.apply(log, net, im, fm)
     gen = generalization.apply(log, net, im, fm)
     sim = simplicity.apply(net)
Пример #12
0
 def test_inductiveminer_stream(self):
     df = pd.read_csv(os.path.join("input_data", "running-example.csv"))
     df = dataframe_utils.convert_timestamp_columns_in_df(df)
     stream = log_conversion.apply(df, variant=log_conversion.TO_EVENT_STREAM)
     net, im, fm = inductive_miner.apply(stream)
     aligned_traces_tr = tr_alg.apply(stream, net, im, fm)
     aligned_traces_alignments = align_alg.apply(stream, net, im, fm)
     evaluation = eval_alg.apply(stream, net, im, fm)
     fitness = rp_fit.apply(stream, net, im, fm)
     precision = precision_evaluator.apply(stream, net, im, fm)
     gen = generalization.apply(stream, net, im, fm)
     sim = simplicity.apply(net)
Пример #13
0
def execute_script():
    log = importer.apply(
        os.path.join("..", "tests", "input_data", "running-example.xes"))
    net, im, fm = inductive_miner.apply(log)
    aligned_traces = alignments.apply(log, net, im, fm)
    gviz = visualizer.apply(
        log,
        aligned_traces,
        parameters={
            visualizer.Variants.CLASSIC.value.Parameters.FORMAT: "svg"
        })
    visualizer.view(gviz)
Пример #14
0
def execute_script():
    log = pm4py.read_xes(
        os.path.join("..", "tests", "input_data", "receipt.xes"))
    print("number of cases", len(log))
    print("number of events", sum(len(x) for x in log))
    print("number of variants", len(pm4py.get_variants(log)))
    ac = get.get_attribute_values(log, "concept:name")
    dfg, sa, ea = pm4py.discover_dfg(log)
    perc = 0.5
    dfg, sa, ea, ac = dfg_filtering.filter_dfg_on_activities_percentage(
        dfg, sa, ea, ac, perc)
    dfg, sa, ea, ac = dfg_filtering.filter_dfg_on_paths_percentage(
        dfg, sa, ea, ac, perc)
    aa = time.time()
    aligned_traces = dfg_alignment.apply(log, dfg, sa, ea)
    bb = time.time()
    net, im, fm = pm4py.convert_to_petri_net(dfg, sa, ea)
    for trace in aligned_traces:
        if trace["cost"] != trace["internal_cost"]:
            print(trace)
            pass
    print(bb - aa)
    print(sum(x["visited_states"] for x in aligned_traces))
    print(
        sum(x["cost"] // align_utils.STD_MODEL_LOG_MOVE_COST
            for x in aligned_traces))
    gviz = visualizer.apply(dfg,
                            activities_count=ac,
                            parameters={
                                "start_activities": sa,
                                "end_activities": ea,
                                "format": "svg"
                            })
    visualizer.view(gviz)
    cc = time.time()
    aligned_traces2 = petri_alignments.apply(
        log,
        net,
        im,
        fm,
        variant=petri_alignments.Variants.VERSION_DIJKSTRA_LESS_MEMORY)
    dd = time.time()
    print(dd - cc)
    print(sum(x["visited_states"] for x in aligned_traces2))
    print(
        sum(x["cost"] // align_utils.STD_MODEL_LOG_MOVE_COST
            for x in aligned_traces2))
Пример #15
0
def testNoSynchronousDiscountedAlignment():
    '''
    This function runs an alignment based on the discounted edit distance
    By using the Petri net and petri_net.utils.align_utils.discountedEditDistance function
    '''
    log_path = os.path.join("..", "tests", "input_data", "running-example.xes")
    pnml_path = os.path.join("..", "tests", "input_data", "running-example.pnml")
    log = xes_importer.apply(log_path)
    net, marking, fmarking = petri_importer.apply(pnml_path)

    start=time.time()

    alignments1 = ali.apply(log._list[0], net, marking, fmarking,
                            variant=ali.VERSION_DISCOUNTED_A_STAR,
                            parameters={ali.Parameters.SYNCHRONOUS:False,ali.Parameters.EXPONENT:1.1})
    print(alignments1)
    print("Time:",(time.time()-start))
Пример #16
0
 def test_alignment_pnml(self):
     # to avoid static method warnings in tests,
     # that by construction of the unittest package have to be expressed in such way
     self.dummy_variable = "dummy_value"
     log = xes_importer.apply(
         os.path.join(INPUT_DATA_DIR, "running-example.xes"))
     net, marking, final_marking = inductive_miner.apply(log)
     for trace in log:
         cf_result = \
         align_alg.apply(trace, net, marking, final_marking, variant=align_alg.VERSION_DIJKSTRA_NO_HEURISTICS)[
             'alignment']
         is_fit = True
         for couple in cf_result:
             if not (couple[0] == couple[1]
                     or couple[0] == ">>" and couple[1] is None):
                 is_fit = False
         if not is_fit:
             raise Exception("should be fit")
Пример #17
0
def apply(log,
          petri_net,
          initial_marking,
          final_marking,
          align_variant=alignments.DEFAULT_VARIANT,
          parameters=None):
    """
    Evaluate fitness based on alignments

    Parameters
    ----------------
    log
        Event log
    petri_net
        Petri net
    initial_marking
        Initial marking
    final_marking
        Final marking
    align_variant
        Variants of the alignments to apply
    parameters
        Parameters of the algorithm

    Returns
    ---------------
    dictionary
        Containing two keys (percFitTraces and averageFitness)
    """
    if align_variant == decomp_alignments.Variants.RECOMPOS_MAXIMAL.value:
        alignment_result = decomp_alignments.apply(log,
                                                   petri_net,
                                                   initial_marking,
                                                   final_marking,
                                                   variant=align_variant,
                                                   parameters=parameters)
    else:
        alignment_result = alignments.apply(log,
                                            petri_net,
                                            initial_marking,
                                            final_marking,
                                            variant=align_variant,
                                            parameters=parameters)
    return evaluate(alignment_result)
Пример #18
0
def execute_script():
    log = pm4py.read_xes(os.path.join("..", "tests", "input_data", "roadtraffic100traces.xes"))
    net, im, fm = pm4py.read_pnml(os.path.join("..", "tests", "input_data", "data_petri_net.pnml"))
    pm4py.view_petri_net(net, im, fm, format="svg")
    aligned_traces = alignments.apply(log, net, im, fm, variant=alignments.Variants.VERSION_DIJKSTRA_LESS_MEMORY, parameters={"ret_tuple_as_trans_desc": True})
    for index, trace in enumerate(log):
        aligned_trace = aligned_traces[index]
        al = [(x[0][0], get_trans_by_name(net, x[0][1])) for x in aligned_trace["alignment"]]
        m = DataMarking(im)
        idx = 0
        for el in al:
            if el[1] is not None:
                en_t = semantics.enabled_transitions(net, m, trace[min(idx, len(trace) - 1)])
                if el[1] in en_t:
                    if "guard" in el[1].properties:
                        print(el[1], "GUARD SATISFIED", el[1].properties["guard"], m)
                    m = semantics.execute(el[1], net, m, trace[min(idx, len(trace) - 1)])
                else:
                    print("TRANSITION UNAVAILABLE! Guards are blocking")
            if el[0] != ">>":
                idx = idx + 1
Пример #19
0
def testSynchronousDiscountedAlignment():
    '''
    This function runs an alignment based on the discounted edit distance
    By using the synchronous product
    :return:
    '''
    log_path = os.path.join("..", "tests", "input_data", "running-example.xes")
    pnml_path = os.path.join("..", "tests", "input_data", "running-example.pnml")
    log = xes_importer.apply(log_path)
    net, marking, fmarking = petri_importer.apply(pnml_path)

    # to see the net :
    #vizu(net,marking,fmarking).view()

    start=time.time()

    alignments1 = ali.apply(log._list[0], net, marking, fmarking,
                            variant=ali.VERSION_DISCOUNTED_A_STAR,
                            parameters={ali.Parameters.SYNCHRONOUS:True,ali.Parameters.EXPONENT:1.1})
    print(alignments1)
    print("Time:",(time.time()-start))
Пример #20
0
def apply(log, petri_net, initial_marking, final_marking, align_variant=alignments.DEFAULT_VARIANT, parameters=None):
    """
    Evaluate fitness based on alignments

    Parameters
    ----------------
    log
        Event log
    petri_net
        Petri net
    initial_marking
        Initial marking
    final_marking
        Final marking
    align_variant
        Variants of the alignments to apply
    parameters
        Parameters of the algorithm

    Returns
    ---------------
    dictionary
        Containing two keys (percFitTraces and averageFitness)
    """
    if parameters is None:
        parameters = {}

    multiprocessing = exec_utils.get_param_value(Parameters.MULTIPROCESSING, parameters, False)

    if align_variant == decomp_alignments.Variants.RECOMPOS_MAXIMAL.value:
        alignment_result = decomp_alignments.apply(log, petri_net, initial_marking, final_marking,
                                                   variant=align_variant, parameters=parameters)
    else:
        if multiprocessing:
            alignment_result = alignments.apply_multiprocessing(log, petri_net, initial_marking, final_marking, variant=align_variant,
                                                parameters=parameters)
        else:
            alignment_result = alignments.apply(log, petri_net, initial_marking, final_marking, variant=align_variant,
                                                parameters=parameters)
    return evaluate(alignment_result)
Пример #21
0
def get_alignments_decoration(net,
                              im,
                              fm,
                              log=None,
                              aligned_traces=None,
                              parameters=None):
    """
    Get a decoration for the Petri net based on alignments

    Parameters
    -------------
    net
        Petri net
    im
        Initial marking
    fm
        Final marking
    log
        Event log
    aligned_traces
        Aligned traces
    parameters
        Parameters of the algorithm

    Returns
    -------------
    decorations
        Decorations to use
    """
    if parameters is None:
        parameters = {}
    if aligned_traces is None and log is not None:
        from pm4py.algo.conformance.alignments.petri_net import algorithm as alignments
        aligned_traces = alignments.apply(
            log, net, im, fm, parameters={"ret_tuple_as_trans_desc": True})
    decorations = {}
    net_transitions = {}
    for trans in net.transitions:
        net_transitions[trans.name] = trans
    for align_trace0 in aligned_traces:
        align_trace = align_trace0["alignment"]
        for move in align_trace:
            move_trans_name = move[0][1]
            activity_trace_name = move[0][0]
            if move_trans_name in net_transitions:
                trans = net_transitions[move_trans_name]
                if trans not in decorations:
                    decorations[trans] = {
                        "count_fit": 0,
                        "count_move_on_model": 0
                    }

                if activity_trace_name == ">>":
                    decorations[trans]["count_move_on_model"] = decorations[
                        trans]["count_move_on_model"] + 1
                else:
                    decorations[trans][
                        "count_fit"] = decorations[trans]["count_fit"] + 1

    for trans in decorations:
        if trans.label is not None:
            decorations[trans]["label"] = trans.label + " (" + str(
                decorations[trans]["count_move_on_model"]) + "," + str(
                    decorations[trans]["count_fit"]) + ")"
            decorations[trans]["color"] = get_transitions_color(
                decorations[trans]["count_move_on_model"],
                decorations[trans]["count_fit"])

    return decorations
def f():
    aa = time.time()
    aligned_traces = alignments.apply(Shared.log, Shared.net, Shared.im, Shared.fm,
                                      variant=alignments.Variants.VERSION_DIJKSTRA_LESS_MEMORY)
    bb = time.time()
    print(bb - aa)
def g():
    aa = time.time()
    aligned_traces = alignments.apply(Shared.log, Shared.net, Shared.im, Shared.fm,
                                      variant=alignments.Variants.VERSION_DIJKSTRA_NO_HEURISTICS)
    bb = time.time()
    print(bb - aa)
Пример #24
0
 def test_align(self):
     log = pm4py.read_xes("input_data/running-example.xes")
     net, im, fm = pm4py.discover_petri_net_inductive(log,
                                                      noise_threshold=0.2)
     aligned_traces = alignments.apply(log, net, im, fm)
     diagn_df = alignments.get_diagnostics_dataframe(log, aligned_traces)
Пример #25
0
def get_attributes(log, decision_points, attributes, use_trace_attributes, trace_attributes, k, net, initial_marking,
                   final_marking, decision_points_names, parameters=None):
    """
    This method aims to construct for each decision place a table where for each decision place a list if given with the
     label of the later decision and as value the given attributes
    :param log: Log on which the method is applied
    :param alignments: Computed alignments for a log and a model
    :param decision_points: Places that have multiple outgoing arcs
    :param attributes: Attributes that are considered
    :param use_trace_attributes: If trace attributes have to be considered or not
    :param trace_attributes: List of trace attributes that are considered
    :param k: Taking k last activities into account
    :return: Dictionary that has as keys the decision places. The value for this key is a list.
    The content of these lists are tuples. The first element of these tuples is information regrading the attributes,
    the second element of these tuples is the transition which chosen in a decision.
    """
    if parameters is None:
        parameters = {}
    I = {}
    for key in decision_points:
        I[key] = []
    A = {}
    for attri in attributes:
        A[attri] = None
    i = 0
    # first, take a look at the variants
    variants_idxs = variants_module.get_variants_from_log_trace_idx(log, parameters=parameters)
    one_variant = []
    for variant in variants_idxs:
        one_variant.append(variant)
        # TODO: Token based replay code mit paramter für nur varianten einbeziehen ausstatten
    replay_result = token_replay.apply(log, net, initial_marking, final_marking, parameters=parameters)
    replay_result = simplify_token_replay(replay_result)
    count = 0
    for variant in replay_result:
        if variant['trace_fitness'] == 1.0:
            for trace_index in variants_idxs[one_variant[count]]:
                last_k_list = [None] * k
                trace = log[trace_index]
                if use_trace_attributes:
                    for attribute in trace_attributes:
                        # can be done here since trace attributes does not change for whole trace
                        A[attribute] = trace.attributes[attribute]
                j = 0
                # j is a pointer which points to the current event inside a trace
                for transition in variant['activated_transitions']:
                    for key, value in decision_points_names.items():
                        if transition.label in value:
                            for element in last_k_list:
                                if element != None:
                                    if transition.label != None:
                                        I[key].append((element.copy(), transition.label))
                                    else:
                                        I[key].append((element.copy(), transition.name))
                    for attri in attributes:
                        # print(variant, transition.label, j)
                        if attri in trace[j]:
                            # only add the attribute information if it is present in the event
                            A[attri] = trace[j][attri]
                    # add A to last_k_list. Using modulo to access correct entry
                    last_k_list[j % k] = A.copy()
                    if transition.label != None:
                        if not j + 1 >= len(trace):
                            # Problem otherwise: If there are tau-transition after the last event related transition,
                            # the pointer j which points to the current event in a trace, gets out of range
                            j += 1
        else:
            example_trace = log[variants_idxs[one_variant[count]][0]]
            align_parameters = copy(parameters)
            align_parameters[star.Parameters.PARAM_ALIGNMENT_RESULT_IS_SYNC_PROD_AWARE] = True
            alignment = ali.apply(example_trace, net, initial_marking, final_marking,
                                  parameters=align_parameters)['alignment']
            for trace_index in variants_idxs[one_variant[count]]:
                last_k_list = [None] * k
                trace = log[trace_index]
                if use_trace_attributes:
                    for attribute in trace_attributes:
                        # can be done here since trace attributes does not change for whole trace
                        A[attribute] = trace.attributes[attribute]
                j = 0
                for el in alignment:
                    if el[1][1] != '>>':
                        # If move in model
                        for key, value in decision_points.items():
                            if el[0][1] in value:
                                for element in last_k_list:
                                    if element != None:
                                        # only add those entries where information is provided
                                        if el[1][1] == None:
                                            # for some dt algorithms, the entry None might be a problem, since it is left out later
                                            I[key].append((element.copy(), el[0][1]))
                                        else:
                                            I[key].append((element.copy(), el[1][1]))
                    if el[1][0] != '>>' and el[1][1] != '>>':
                        # If there is a move in log and model
                        for attri in attributes:
                            if attri in trace[j]:
                                # only add the attribute information if it is present in the event
                                A[attri] = trace[j][attri]
                        # add A to last_k_list. Using modulo to access correct entry
                        last_k_list[j % k] = A.copy()
                    if el[1][0] != '>>':
                        # only go to next event in trace if the current event has been aligned
                        # TODO: Discuss if this is correct or can lead to problems
                        j += 1
        count += 1
    return I