Esempio n. 1
0
    def reach_fm_with_invisibles(self, marking):
        """
        Reaches the final marking using invisible transitions

        Parameters
        --------------
        marking
            Marking

        Returns
        --------------
        new_marking
            New marking (hopely equal to the final marking)
        """
        spath = None
        spath_length = sys.maxsize
        for pl in marking:
            if pl in self.dictio_spaths:
                for pl2 in self.fm:
                    if pl2 in self.dictio_spaths[pl]:
                        new_path = self.dictio_spaths[pl][pl2]
                        if len(new_path) < spath_length:
                            spath = new_path
                            spath_length = len(spath)
        if spath is not None:
            # try to fire the transitions
            for tr in spath:
                if tr in semantics.enabled_transitions(self.net, marking):
                    marking = semantics.weak_execute(tr, marking)
                else:
                    return None
            return marking
        return None
Esempio n. 2
0
def get_visible_transitions_eventually_enabled_by_marking(net, marking):
    """
    Get visible transitions eventually enabled by marking (passing possibly through hidden transitions)
    Parameters
    ----------
    net
        Petri net
    marking
        Current marking
    """
    all_enabled_transitions = sorted(list(semantics.enabled_transitions(net, marking)),
                                     key=lambda x: (str(x.name), id(x)))
    initial_all_enabled_transitions_marking_dictio = {}
    all_enabled_transitions_marking_dictio = {}
    for trans in all_enabled_transitions:
        all_enabled_transitions_marking_dictio[trans] = marking
        initial_all_enabled_transitions_marking_dictio[trans] = marking
    visible_transitions = set()
    visited_transitions = set()

    i = 0
    while i < len(all_enabled_transitions):
        t = all_enabled_transitions[i]
        marking_copy = copy(all_enabled_transitions_marking_dictio[t])

        if repr([t, marking_copy]) not in visited_transitions:
            if t.label is not None:
                visible_transitions.add(t)
            else:
                if semantics.is_enabled(t, net, marking_copy):
                    new_marking = semantics.execute(t, net, marking_copy)
                    new_enabled_transitions = sorted(list(semantics.enabled_transitions(net, new_marking)),
                                                     key=lambda x: (str(x.name), id(x)))
                    for t2 in new_enabled_transitions:
                        all_enabled_transitions.append(t2)
                        all_enabled_transitions_marking_dictio[t2] = new_marking
            visited_transitions.add(repr([t, marking_copy]))
        i = i + 1

    return visible_transitions
Esempio n. 3
0
def acyclic_net_variants(net,
                         initial_marking,
                         final_marking,
                         activity_key=xes_util.DEFAULT_NAME_KEY):
    """
    Given an acyclic accepting Petri net, initial and final marking extracts a set of variants (in form of traces)
    replayable on the net.
    Warning: this function is based on a marking exploration. If the accepting Petri net contains loops, the method
    will not work properly as it stops the search if a specific marking has already been encountered.

    Parameters
    ----------
    :param net: An acyclic workflow net
    :param initial_marking: The initial marking of the net.
    :param final_marking: The final marking of the net.
    :param activity_key: activity key to use

    Returns
    -------
    :return: variants: :class:`list` Set of variants - in the form of Trace objects - obtainable executing the net

    """
    active = {(initial_marking, ())}
    visited = set()
    variants = set()
    while active:
        curr_marking, curr_partial_trace = active.pop()
        curr_pair = (curr_marking, curr_partial_trace)
        enabled_transitions = semantics.enabled_transitions(net, curr_marking)
        for transition in enabled_transitions:
            if transition.label is not None:
                next_partial_trace = curr_partial_trace + (transition.label, )
            else:
                next_partial_trace = curr_partial_trace
            next_marking = semantics.execute(transition, net, curr_marking)
            next_pair = (next_marking, next_partial_trace)

            if next_marking == final_marking:
                variants.add(next_partial_trace)
            else:
                # If the next marking is not in visited, if the next marking+partial trace is different from the current one+partial trace
                if next_pair not in visited and curr_pair != next_pair:
                    active.add(next_pair)
        visited.add(curr_pair)
    trace_variants = []
    for variant in variants:
        trace = Trace()
        for activity_label in variant:
            trace.append(Event({activity_key: activity_label}))
        trace_variants.append(trace)
    return trace_variants
Esempio n. 4
0
def marking_flow_petri(net, im, return_eventually_enabled=False, parameters=None):
    """
    Construct the marking flow of a Petri net

    Parameters
    -----------------
    net
        Petri net
    im
        Initial marking
    return_eventually_enabled
        Return the eventually enabled (visible) transitions
    """
    if parameters is None:
        parameters = {}

    # set a maximum execution time of 1 day (it can be changed by providing the parameter)
    max_exec_time = exec_utils.get_param_value(Parameters.MAX_ELAB_TIME, parameters, 86400)

    start_time = time.time()

    incoming_transitions = {im: set()}
    outgoing_transitions = {}
    eventually_enabled = {}

    active = [im]
    while active:
        if (time.time() - start_time) >= max_exec_time:
            # interrupt the execution
            return incoming_transitions, outgoing_transitions, eventually_enabled
        m = active.pop()
        enabled_transitions = semantics.enabled_transitions(net, m)
        if return_eventually_enabled:
            eventually_enabled[m] = align_utils.get_visible_transitions_eventually_enabled_by_marking(net, m)
        outgoing_transitions[m] = {}
        for t in enabled_transitions:
            nm = semantics.weak_execute(t, m)
            outgoing_transitions[m][t] = nm
            if nm not in incoming_transitions:
                incoming_transitions[nm] = set()
                if nm not in active:
                    active.append(nm)
            incoming_transitions[nm].add(t)

    return incoming_transitions, outgoing_transitions, eventually_enabled
Esempio n. 5
0
    def enable_trans_with_invisibles(self, marking, activity):
        """
        Enables a visible transition (that is not enabled) through
        invisible transitions

        Parameters
        ----------------
        marking
            Marking
        activity
            Activity to enable

        Returns
        ---------------
        new_marking
            New marking (where the transition CAN be enabled)
        """
        corr_trans_to_act = [x for x in self.net.transitions if x.label == activity]
        spath = None
        spath_length = sys.maxsize
        for pl in marking:
            for tr in corr_trans_to_act:
                if pl in self.dictio_spaths:
                    if tr in self.dictio_spaths[pl]:
                        new_path = self.dictio_spaths[pl][tr]
                        if len(new_path) < spath_length:
                            spath = new_path
                            spath_length = len(spath)
        if spath is not None:
            # try to fire the transitions
            for tr in spath:
                if tr in semantics.enabled_transitions(self.net, marking):
                    marking = semantics.weak_execute(tr, marking)
                else:
                    return None
            return marking
        return None
Esempio n. 6
0
 """
 if activity in self.activities:
     if case not in self.case_dict:
         self.case_dict[case] = self.encode_marking(copy(self.im))
         self.missing[case] = 0
         self.remaining[case] = 0
     marking = self.decode_marking(self.case_dict[case])
     new_marking = marking
     prev_marking = None
     correct_exec = False
     numb_it = 0
     while new_marking is not None and prev_marking != new_marking:
         numb_it = numb_it + 1
         if numb_it > self.maximum_iterations_invisibles:
             break
         enabled_transitions = semantics.enabled_transitions(self.net, new_marking)
         matching_transitions = [x for x in enabled_transitions if x.label == activity]
         if matching_transitions:
             new_marking = semantics.weak_execute(matching_transitions[0], new_marking)
             self.case_dict[case] = self.encode_marking(new_marking)
             correct_exec = True
             break
         prev_marking = new_marking
         new_marking = self.enable_trans_with_invisibles(new_marking, activity)
         correct_exec = False
     if correct_exec is False:
         self.message_missing_tokens(activity, case)
         # enables one of the matching transitions
         matching_transitions = [x for x in self.net.transitions if x.label == activity]
         t = matching_transitions[0]
         for a in t.in_arcs:
Esempio n. 7
0
def apply_playout(net,
                  initial_marking,
                  no_traces=100,
                  max_trace_length=100,
                  case_id_key=xes_constants.DEFAULT_TRACEID_KEY,
                  activity_key=xes_constants.DEFAULT_NAME_KEY,
                  timestamp_key=xes_constants.DEFAULT_TIMESTAMP_KEY,
                  final_marking=None,
                  smap=None,
                  log=None,
                  return_visited_elements=False):
    """
    Do the playout of a Petrinet generating a log

    Parameters
    ----------
    net
        Petri net to play-out
    initial_marking
        Initial marking of the Petri net
    no_traces
        Number of traces to generate
    max_trace_length
        Maximum number of events per trace (do break)
    case_id_key
        Trace attribute that is the case ID
    activity_key
        Event attribute that corresponds to the activity
    timestamp_key
        Event attribute that corresponds to the timestamp
    final_marking
        If provided, the final marking of the Petri net
    smap
        Stochastic map
    log
        Log
    """
    if final_marking is None:
        # infer the final marking from the net
        final_marking = final_marking_discovery.discover_final_marking(net)
    if smap is None:
        if log is None:
            raise Exception(
                "please provide at least one between stochastic map and log")
        smap = replay.get_map_from_log_and_net(log,
                                               net,
                                               initial_marking,
                                               final_marking,
                                               parameters={
                                                   Parameters.ACTIVITY_KEY:
                                                   activity_key,
                                                   Parameters.TIMESTAMP_KEY:
                                                   timestamp_key
                                               })
    # assigns to each event an increased timestamp from 1970
    curr_timestamp = 10000000
    all_visited_elements = []

    for i in range(no_traces):
        visited_elements = []
        visible_transitions_visited = []
        marking = copy(initial_marking)

        while len(visible_transitions_visited) < max_trace_length:
            visited_elements.append(marking)

            if not semantics.enabled_transitions(
                    net, marking):  # supports nets with possible deadlocks
                break
            all_enabled_trans = semantics.enabled_transitions(net, marking)
            if final_marking is not None and marking == final_marking:
                en_t_list = list(all_enabled_trans.union({None}))
            else:
                en_t_list = list(all_enabled_trans)

            trans = stochastic_utils.pick_transition(en_t_list, smap)

            if trans is None:
                break

            visited_elements.append(trans)
            if trans.label is not None:
                visible_transitions_visited.append(trans)

            marking = semantics.execute(trans, net, marking)

        all_visited_elements.append(tuple(visited_elements))

    if return_visited_elements:
        return all_visited_elements

    log = log_instance.EventLog()

    for index, visited_elements in enumerate(all_visited_elements):
        trace = log_instance.Trace()
        trace.attributes[case_id_key] = str(index)
        for element in visited_elements:
            if type(element
                    ) is PetriNet.Transition and element.label is not None:
                event = log_instance.Event()
                event[activity_key] = element.label
                event[timestamp_key] = datetime.datetime.fromtimestamp(
                    curr_timestamp)
                trace.append(event)
                # increases by 1 second
                curr_timestamp += 1
        log.append(trace)

    return log
Esempio n. 8
0
def calculate_annotation_for_trace(trace,
                                   net,
                                   initial_marking,
                                   act_trans,
                                   activity_key,
                                   ht_perf_method="last"):
    """
    Calculate annotation for a trace in the variant, in order to retrieve information
    useful for calculate frequency/performance for all the traces belonging to the variant

    Parameters
    -----------
    trace
        Trace
    net
        Petri net
    initial_marking
        Initial marking
    act_trans
        Activated transitions during token replay of the given trace
    activity_key
        Attribute that identifies the activity (must be specified if different from concept:name)
    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
    ----------
    annotation
        Statistics annotation for the given trace
    """
    annotations_places_trans = {}
    annotations_arcs = {}
    trace_place_stats = {}
    current_trace_index = 0
    j = 0
    marking = copy(initial_marking)
    for place in marking:
        if place not in annotations_places_trans:
            annotations_places_trans[place] = {"count": 0}
            annotations_places_trans[place][
                "count"] = annotations_places_trans[place]["count"] + marking[
                    place]
        trace_place_stats[place] = [current_trace_index] * marking[place]

    for z in range(len(act_trans)):
        enabled_trans_in_marking = semantics.enabled_transitions(net, marking)
        # print("enabled_trans_in_marking", enabled_trans_in_marking)

        for trans in enabled_trans_in_marking:
            if trans not in annotations_places_trans:
                annotations_places_trans[trans] = {
                    "count": 0,
                    "performance": [],
                    "no_of_times_enabled": 0,
                    "no_of_times_activated": 0
                }
            annotations_places_trans[trans][
                "no_of_times_enabled"] = annotations_places_trans[trans][
                    "no_of_times_enabled"] + 1

        trans = act_trans[z]
        if trans not in annotations_places_trans:
            annotations_places_trans[trans] = {
                "count": 0,
                "performance": [],
                "no_of_times_enabled": 0,
                "no_of_times_activated": 0
            }
        annotations_places_trans[trans][
            "count"] = annotations_places_trans[trans]["count"] + 1
        if trans not in enabled_trans_in_marking:
            annotations_places_trans[trans][
                "no_of_times_enabled"] = annotations_places_trans[trans][
                    "no_of_times_enabled"] + 1
        annotations_places_trans[trans][
            "no_of_times_activated"] = annotations_places_trans[trans][
                "no_of_times_activated"] + 1

        new_marking = semantics.weak_execute(trans, marking)
        if not new_marking:
            break
        marking_diff = set(new_marking).difference(set(marking))
        for place in marking_diff:
            if place not in annotations_places_trans:
                annotations_places_trans[place] = {"count": 0}
                annotations_places_trans[place][
                    "count"] = annotations_places_trans[place]["count"] + max(
                        new_marking[place] - marking[place], 1)
        marking = new_marking
        if j < len(trace):
            current_trace_index = j
            if trans.label == trace[j][activity_key]:
                j = j + 1

        in_arc_indexes = [
            trace_place_stats[arc.source][0] for arc in trans.in_arcs if
            arc.source in trace_place_stats and trace_place_stats[arc.source]
        ]
        if in_arc_indexes:
            min_in_arc_indexes = min(in_arc_indexes)
            max_in_arc_indexes = max(in_arc_indexes)
        else:
            min_in_arc_indexes = None
            max_in_arc_indexes = None
        performance_for_this_trans_execution = []

        for arc in trans.in_arcs:
            source_place = arc.source
            if arc not in annotations_arcs:
                annotations_arcs[arc] = {"performance": [], "count": 0}
                annotations_arcs[arc][
                    "count"] = annotations_arcs[arc]["count"] + 1
            if source_place in trace_place_stats and trace_place_stats[
                    source_place]:
                if trans.label or ht_perf_method == "first":
                    annotations_arcs[arc]["performance"].append([
                        current_trace_index, trace_place_stats[source_place][0]
                    ])
                    performance_for_this_trans_execution.append([[
                        current_trace_index, trace_place_stats[source_place][0]
                    ], current_trace_index - trace_place_stats[source_place][0]
                                                                 ])
                elif min_in_arc_indexes:
                    annotations_arcs[arc]["performance"].append(
                        [current_trace_index, current_trace_index])
                    performance_for_this_trans_execution.append(
                        [[current_trace_index, current_trace_index], 0])

                del trace_place_stats[source_place][0]
        for arc in trans.out_arcs:
            target_place = arc.target
            if arc not in annotations_arcs:
                annotations_arcs[arc] = {"performance": [], "count": 0}
                annotations_arcs[arc][
                    "count"] = annotations_arcs[arc]["count"] + 1
            if target_place not in trace_place_stats:
                trace_place_stats[target_place] = []

            if trans.label or ht_perf_method == "first":
                trace_place_stats[target_place].append(current_trace_index)
            elif max_in_arc_indexes:
                trace_place_stats[target_place].append(max_in_arc_indexes)

        if performance_for_this_trans_execution:
            performance_for_this_trans_execution = sorted(
                performance_for_this_trans_execution, key=lambda x: x[1])

            annotations_places_trans[trans]["performance"].append(
                performance_for_this_trans_execution[0][0])

    return annotations_places_trans, annotations_arcs
Esempio n. 9
0
def apply(net, initial_marking, final_marking=None, parameters=None):
    """
    Do the playout of a Petrinet generating a log (extensive search; stop at the maximum
    trace length specified

    Parameters
    -----------
    net
        Petri net to play-out
    initial_marking
        Initial marking of the Petri net
    final_marking
        If provided, the final marking of the Petri net
    parameters
        Parameters of the algorithm:
            Parameters.MAX_TRACE_LENGTH -> Maximum trace length
    """
    if parameters is None:
        parameters = {}

    case_id_key = exec_utils.get_param_value(Parameters.CASE_ID_KEY,
                                             parameters,
                                             xes_constants.DEFAULT_TRACEID_KEY)
    activity_key = exec_utils.get_param_value(Parameters.ACTIVITY_KEY,
                                              parameters,
                                              xes_constants.DEFAULT_NAME_KEY)
    timestamp_key = exec_utils.get_param_value(
        Parameters.TIMESTAMP_KEY, parameters,
        xes_constants.DEFAULT_TIMESTAMP_KEY)
    max_trace_length = exec_utils.get_param_value(Parameters.MAX_TRACE_LENGTH,
                                                  parameters, 10)
    return_elements = exec_utils.get_param_value(Parameters.RETURN_ELEMENTS,
                                                 parameters, False)
    max_marking_occ = exec_utils.get_param_value(Parameters.MAX_MARKING_OCC,
                                                 parameters, sys.maxsize)

    # assigns to each event an increased timestamp from 1970
    curr_timestamp = 10000000

    feasible_elements = []

    to_visit = [(initial_marking, (), ())]
    visited = set()

    while len(to_visit) > 0:
        state = to_visit.pop(0)

        m = state[POSITION_MARKING]
        trace = state[POSITION_TRACE]
        elements = state[POSITION_ELEMENTS]

        if (m, trace) in visited:
            continue
        visited.add((m, trace))

        en_t = semantics.enabled_transitions(net, m)

        if (final_marking is not None
                and m == final_marking) or (final_marking is None
                                            and len(en_t) == 0):
            if len(trace) <= max_trace_length:
                feasible_elements.append(elements)

        for t in en_t:
            new_elements = elements + (m, )
            new_elements = new_elements + (t, )

            counter_elements = Counter(new_elements)

            if counter_elements[m] > max_marking_occ:
                continue

            new_m = semantics.weak_execute(t, m)
            if t.label is not None:
                new_trace = trace + (t.label, )
            else:
                new_trace = trace

            new_state = (new_m, new_trace, new_elements)

            if new_state in visited or len(new_trace) > max_trace_length:
                continue
            to_visit.append(new_state)

    if return_elements:
        return feasible_elements

    log = log_instance.EventLog()
    for elements in feasible_elements:
        log_trace = log_instance.Trace()
        log_trace.attributes[case_id_key] = str(len(log))
        activities = [
            x.label for x in elements
            if type(x) is PetriNet.Transition and x.label is not None
        ]
        for act in activities:
            curr_timestamp = curr_timestamp + 1
            log_trace.append(
                log_instance.Event({
                    activity_key:
                    act,
                    timestamp_key:
                    datetime.datetime.fromtimestamp(curr_timestamp)
                }))
        log.append(log_trace)

    return log
Esempio n. 10
0
def __transform_model_to_mem_efficient_structure(net,
                                                 im,
                                                 fm,
                                                 trace,
                                                 parameters=None):
    """
    Transform the Petri net model to a memory efficient structure

    Parameters
    --------------
    net
        Petri net
    im
        Initial marking
    fm
        Final marking
    trace
        Trace
    parameters
        Parameters

    Returns
    --------------
    model_struct
        Model data structure, including:
            PLACES_DICT: associates each place to a number
            INV_TRANS_DICT: associates a number to each transition
            LABELS_DICT: labels dictionary (a label to a number)
            TRANS_LABELS_DICT: associates each transition to the number corresponding to its label
            TRANS_PRE_DICT: preset of a transition, expressed as in this data structure
            TRANS_POST_DICT: postset of a transition, expressed as in this data structure
            TRANSF_IM: transformed initial marking
            TRANSF_FM: transformed final marking
            TRANSF_MODEL_COST_FUNCTION: transformed model cost function
    """
    if parameters is None:
        parameters = {}

    activity_key = exec_utils.get_param_value(Parameters.ACTIVITY_KEY,
                                              parameters, DEFAULT_NAME_KEY)
    labels = sorted(list(set(x[activity_key] for x in trace)))

    model_cost_function = exec_utils.get_param_value(
        Parameters.PARAM_MODEL_COST_FUNCTION, parameters, None)

    if model_cost_function is None:
        model_cost_function = {}
        for t in net.transitions:
            if t.label is not None:
                model_cost_function[t] = align_utils.STD_MODEL_LOG_MOVE_COST
            else:
                preset_t = Marking()
                for a in t.in_arcs:
                    preset_t[a.source] = a.weight
                # optimization 12/08/2020
                #
                # instead of giving undiscriminately weight 1 to
                # invisible transitions, assign weight 0 to the ones
                # for which no 'sync' transition is enabled in their
                # activation markings.
                #
                # this requires to modify the state of the alignment, keeping track
                # of the length of the alignment, to avoid loops.
                en_t = enabled_transitions(net, preset_t)
                vis_t_trace = [t for t in en_t if t.label in labels]
                if len(vis_t_trace) == 0:
                    model_cost_function[t] = 0
                else:
                    model_cost_function[t] = align_utils.STD_TAU_COST

    places_dict = {place: index for index, place in enumerate(net.places)}
    trans_dict = {trans: index for index, trans in enumerate(net.transitions)}

    labels = sorted(
        list(set(t.label for t in net.transitions if t.label is not None)))
    labels_dict = {labels[i]: i for i in range(len(labels))}

    trans_labels_dict = {}
    for t in net.transitions:
        trans_labels_dict[trans_dict[t]] = labels_dict[
            t.label] if t.label is not None else None

    trans_pre_dict = {
        trans_dict[t]: {places_dict[x.source]: x.weight
                        for x in t.in_arcs}
        for t in net.transitions
    }
    trans_post_dict = {
        trans_dict[t]: {places_dict[x.target]: x.weight
                        for x in t.out_arcs}
        for t in net.transitions
    }

    transf_im = {places_dict[p]: im[p] for p in im}
    transf_fm = {places_dict[p]: fm[p] for p in fm}

    transf_model_cost_function = {
        trans_dict[t]: model_cost_function[t]
        for t in net.transitions
    }

    inv_trans_dict = {y: x for x, y in trans_dict.items()}

    return {
        PLACES_DICT: places_dict,
        INV_TRANS_DICT: inv_trans_dict,
        LABELS_DICT: labels_dict,
        TRANS_LABELS_DICT: trans_labels_dict,
        TRANS_PRE_DICT: trans_pre_dict,
        TRANS_POST_DICT: trans_post_dict,
        TRANSF_IM: transf_im,
        TRANSF_FM: transf_fm,
        TRANSF_MODEL_COST_FUNCTION: transf_model_cost_function
    }
Esempio n. 11
0
    def run(self):
        """
        Runs the thread
        """
        if self.enable_diagnostics:
            diagnostics = SimulationDiagnostics(self)
            diagnostics.start()

        from intervaltree import IntervalTree, Interval

        logging.basicConfig()
        logger = logging.getLogger(__name__)
        logger.setLevel(logging.DEBUG)

        net, im, fm, smap, source, sink, start_time = self.net, self.im, self.fm, self.map, self.source, self.sink, self.start_time
        places_interval_trees = self.places_interval_trees
        transitions_interval_trees = self.transitions_interval_trees
        cases_ex_time = self.cases_ex_time

        current_time = start_time

        self.internal_thread_start_time = time()
        rem_time = self.get_rem_time()

        acquired_places = set()
        acquired = source.semaphore.acquire(timeout=rem_time)
        if acquired:
            acquired_places.add(source)
        source.assigned_time.append(current_time)

        current_marking = im
        et = enabled_transitions(net, current_marking)

        first_event = None
        last_event = None

        while not fm <= current_marking or len(et) == 0:
            et = list(enabled_transitions(net, current_marking))
            ct = stochastic_utils.pick_transition(et, smap)

            simulated_execution_plus_waiting_time = -1
            while simulated_execution_plus_waiting_time < 0:
                simulated_execution_plus_waiting_time = smap[ct].get_value(
                ) if ct in smap else 0.0

            # establish how much time we need to wait before firing the transition
            # (it depends on the input places tokens)
            waiting_time = 0
            for arc in ct.out_arcs:
                place = arc.target
                sem_value = int(place.semaphore._value)
                rem_time = self.get_rem_time()
                acquired = place.semaphore.acquire(timeout=rem_time)
                if acquired:
                    acquired_places.add(place)
                rem_time = self.get_rem_time()
                if rem_time == 0:
                    break
                if sem_value == 0:
                    waiting_time = max(
                        waiting_time,
                        place.assigned_time.pop(0) -
                        current_time) if place.assigned_time else waiting_time

            if rem_time == 0:
                for place in acquired_places:
                    place.semaphore.release()
                break

            # if the waiting time is greater than 0, add an interval to the interval tree denoting
            # the waiting times for the given transition
            if waiting_time > 0:
                transitions_interval_trees[ct].add(
                    Interval(current_time, current_time + waiting_time))

            # get the actual execution time of the transition as a difference between simulated_execution_plus_waiting_time
            # and the waiting time
            execution_time = max(
                simulated_execution_plus_waiting_time - waiting_time, 0)

            # increase the timing based on the waiting time and the execution time of the transition
            current_time = current_time + waiting_time + execution_time

            for arc in ct.out_arcs:
                place = arc.target
                place.assigned_time.append(current_time)
                place.assigned_time = sorted(place.assigned_time)

            current_marking = weak_execute(ct, current_marking)

            if ct.label is not None:
                eve = Event({
                    xes_constants.DEFAULT_NAME_KEY:
                    ct.label,
                    xes_constants.DEFAULT_TIMESTAMP_KEY:
                    datetime.datetime.fromtimestamp(current_time)
                })
                last_event = eve
                if first_event is None:
                    first_event = last_event
                self.list_cases[self.id].append(eve)

            for arc in ct.in_arcs:
                place = arc.source
                p_ex_time = place.assigned_time.pop(0)
                if current_time - p_ex_time > 0:
                    places_interval_trees[place].add(
                        Interval(p_ex_time, current_time))
                place.assigned_time.append(current_time)
                place.assigned_time = sorted(place.assigned_time)
                place.semaphore.release()

            # sleep before starting next iteration
            sleep((waiting_time + execution_time) / self.small_scale_factor)

        if first_event is not None and last_event is not None:
            cases_ex_time.append(
                last_event[xes_constants.DEFAULT_TIMESTAMP_KEY].timestamp() -
                first_event[xes_constants.DEFAULT_TIMESTAMP_KEY].timestamp())
        else:
            cases_ex_time.append(0)

        places_to_free = set(current_marking).union(acquired_places)

        for place in places_to_free:
            place.semaphore.release()

        rem_time = self.get_rem_time()
        if rem_time > 0:
            self.terminated_correctly = True
            if self.enable_diagnostics:
                logger.info(
                    str(time()) + " terminated successfully thread ID " +
                    str(self.id))

        if self.enable_diagnostics:
            if rem_time == 0:
                if self.enable_diagnostics:
                    logger.info(
                        str(time()) + " terminated for timeout thread ID " +
                        str(self.id))

        if self.enable_diagnostics:
            diagnostics.diagn_open = False