def token_replay_event(trans_list, net, marking, is_inv): """Performs token replay for a transition given the current marking. """ debug_msg = 'Token replaying event {} at marking {}' debug_msg = debug_msg.format(trans_list[0].label, marking) logger.debug(debug_msg) marking_seq = list() enabled_trans = list(semantics.enabled_transitions(net, marking)) target_trans = trans_list.pop(0) replayed = False while not replayed: if target_trans in enabled_trans: new_marking = semantics.execute(target_trans, net, marking) marking_seq.append(new_marking) break elif pm_extra.has_invisible(enabled_trans, is_inv): marking_seq_i = token_pull(target_trans, net, marking, is_inv) if marking_seq_i is not None: marking = marking_seq_i[-1] new_marking = semantics.execute(target_trans, net, marking) marking_seq_i.append(new_marking) marking_seq = marking_seq_i break # try next transition if trans_list: target_trans = trans_list.pop(0) # finished trying to replay transition else: marking_seq = None break return marking_seq
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 = list(semantics.enabled_transitions(net, marking)) visible_transitions = set() visited_transitions = set() for i in range(len(all_enabled_transitions)): t = all_enabled_transitions[i] if t not in visited_transitions: if t.label is not None: visible_transitions.add(t) else: marking_copy = copy(marking) if semantics.is_enabled(t, net, marking_copy): new_marking = semantics.execute(t, net, marking_copy) new_enabled_transitions = list(semantics.enabled_transitions(net, new_marking)) all_enabled_transitions = all_enabled_transitions + new_enabled_transitions visited_transitions.add(t) return visible_transitions
def log_to_tel(net, initial_marking, final_marking, tel): ''' set enabled into the log based on replaying on net Parameters ---------- :param net: petri net :param log: tel object Returns -------- translucent event log ''' for trace in tel: m = initial_marking for event in trace: act = event['concept:name'] for trans in net.transitions: if act == trans.label: t = trans break en = semantics.enabled_transitions(net, m) event.set_enabled(frozenset(en)) if m == final_marking: break m = semantics.execute(t, net, m) # find enabled activity return tel
def is_sequence_possible(net, transition_sequence): """ Helper function used to run a sequence of transitions. Returns [a,b,c] where a = True if sequence of transitions is possible in net b = the index of the transition that is not possible if a = False c = label of transition if a transition was not found in the network else False """ # Get marking object current_marking = get_marking(net) # Create a mapping from label to transition objects transition_dict = {} for transition in net.transitions: if transition.name in transition_sequence: transition_dict[transition.name] = transition index = 0 for transition_label in transition_sequence: # Transition not found if transition_label not in transition_dict.keys(): return [False, 0, transition_label] transition = transition_dict[transition_label] # Transition not enabled, sequence impossible if not semantics.is_enabled(transition, net, current_marking): return [False, index, False] # Fire the transition to enter next marking current_marking = semantics.execute(transition, net, current_marking) index += 1 # Sequence possible return [True, 0, False]
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): """ 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 """ # assigns to each event an increased timestamp from 1970 curr_timestamp = 10000000 log = log_instance.EventLog() for i in range(no_traces): trace = log_instance.Trace() trace.attributes[case_id_key] = str(i) marking = copy(initial_marking) while len(trace) < max_trace_length: 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: trans = choice(list(all_enabled_trans.union({None}))) else: trans = choice(list(all_enabled_trans)) if trans is None: break if trans.label is not None: event = log_instance.Event() event[activity_key] = trans.label event[timestamp_key] = datetime.datetime.fromtimestamp( curr_timestamp) trace.append(event) # increases by 1 second curr_timestamp += 1 marking = semantics.execute(trans, net, marking) log.append(trace) return log
def play(self): self.generatedTraces = set() self.potentials = Queue() marking_count = dict() for mark in self.initial_marking: marking_count[mark] = 1 self.potentials.put_nowait( PotentialTrace(marking=copy(self.initial_marking), firingSequence=list(), marking_count=marking_count)) while not self.potentials.empty(): potential = self.potentials.get_nowait() marking = potential.getMarking() firingSeq = potential.getFiringSequence() enabled_trans = semantics.enabled_transitions(self.net, marking) for enabled_tran in enabled_trans: new_marking = semantics.execute(enabled_tran, self.net, marking) new_firingSeq = copy(firingSeq) discard = False marking_count = copy(potential.getMarkingCount()) for mark in new_marking: if mark not in marking: if mark in marking_count.keys(): marking_count[mark] = marking_count[mark] + 1 if marking_count[mark] > self.max_loop: discard = True else: marking_count[mark] = 1 if enabled_tran.label == None: invs = potential.getInvCounter() + 1 else: new_firingSeq.append(str(enabled_tran)) invs = 0 if new_marking == self.final_marking: self.generatedTraces.add(tuple(new_firingSeq)) else: if len( new_firingSeq ) < self.maxTraceLength and invs < self.rep_inv_thresh and not discard: self.potentials.put_nowait( PotentialTrace(marking=new_marking, firingSequence=new_firingSeq, inv_counter=invs, marking_count=marking_count)) return self.generatedTraces
def enable_hidden_transitions(net, marking, activated_transitions, visited_transitions, all_visited_markings, hidden_transitions_to_enable, t): """ Actually enable hidden transitions on the Petri net Parameters ----------- net Petri net marking Current marking activated_transitions All activated transitions during the replay visited_transitions All visited transitions by the recursion all_visited_markings All visited markings hidden_transitions_to_enable List of hidden transition to enable t Transition against we should check if they are enabled """ j_indexes = [0] * len(hidden_transitions_to_enable) for z in range(10000000): something_changed = False for k in range( j_indexes[z % len(hidden_transitions_to_enable)], len(hidden_transitions_to_enable[ z % len(hidden_transitions_to_enable)])): t3 = hidden_transitions_to_enable[ z % len(hidden_transitions_to_enable)][j_indexes[ z % len(hidden_transitions_to_enable)]] if not t3 == t: if semantics.is_enabled(t3, net, marking): if t3 not in visited_transitions: marking = semantics.execute(t3, net, marking) activated_transitions.append(t3) visited_transitions.add(t3) all_visited_markings.append(marking) something_changed = True j_indexes[z % len(hidden_transitions_to_enable)] = j_indexes[ z % len(hidden_transitions_to_enable)] + 1 if semantics.is_enabled(t, net, marking): break if semantics.is_enabled(t, net, marking): break if not something_changed: break return [ marking, activated_transitions, visited_transitions, all_visited_markings ]
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
def make_enabledlog_alphaminer(log_file_path, output_file_name): ''' Convert XES to XES++ based on alpha miner (event log with enabled activities) (for test) Parameters ---------- log_file_path log's file path to be changed into XES++ output_file_name output file (XES++)'s name ''' log = xes_importer.import_log(log_file_path) net, initial_marking, final_marking = alpha_miner.apply(log) doc = ET.parse(log_file_path) root = doc.getroot() for glob in root.iter("global"): if glob.attrib["scope"] == "event": enabled = ET.SubElement(glob, "string") enabled.set("key", "enabled") enabled.set("value", "enabled") for trace in root.iter("trace"): m = ini.discover_initial_marking(net) for event in trace.iter("event"): for string in event.iter("string"): if string.attrib["key"] == "concept:name": act = string.attrib["value"] # act: current activity in the event log break for trans in net.transitions: if act == trans.label: t = trans break en = semantics.enabled_transitions(net, m) enabled = ET.SubElement(event, "string") enabled.set("key", "enabled") enabled.set("value", en) #write on the XES file if m == final_marking: break m = semantics.execute(t, net, m) doc.write(output_file_name, encoding="utf-8", xml_declaration=True)
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
def play(self): self.generatedTraces = set() for sample in range(self.samples): firingSeq = list() marking = self.initial_marking while marking != self.final_marking: enabled_trans = semantics.enabled_transitions( self.net, marking) if len(enabled_trans) > 0: tran = random.choice([i for i in enabled_trans]) if tran.label != None: firingSeq.append(str(tran)) marking = semantics.execute(tran, self.net, marking) self.generatedTraces.add(tuple(firingSeq)) else: firingSeq = list() marking = self.initial_marking return self.generatedTraces
def apply_playout(net, initial_marking, no_traces=100, max_trace_length=100): """ 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) """ # assigns to each event an increased timestamp from 1970 curr_timestamp = 10000000 log = log_instance.EventLog() for i in range(no_traces): trace = log_instance.Trace() trace.attributes["concept:name"] = str(i) marking = copy(initial_marking) for j in range(100000): if not semantics.enabled_transitions(net, marking): break all_enabled_trans = semantics.enabled_transitions(net, marking) all_enabled_trans = list(all_enabled_trans) shuffle(all_enabled_trans) trans = all_enabled_trans[0] if trans.label is not None: event = log_instance.Event() event["concept:name"] = trans.label event["time:timestamp"] = datetime.datetime.fromtimestamp( curr_timestamp) trace.append(event) # increases by 1 second curr_timestamp = curr_timestamp + 1 marking = semantics.execute(trans, net, marking) if len(trace) > max_trace_length: break if len(trace) > 0: log.append(trace) return log
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
def build_reachability_graph(net, init_marking, is_inv, staterep=default_staterep): """ Build reachability graph and keep track of to and from states connected by invisible transitions. :param net: the petrinet :param init_marking: initial marking :param is_inv: function that indicate if a transition is invisible :type is_inv: function :staterep: function that gives a state a string representation :return reachability graph, list of (from_state, inv_tran, to_state) """ # BFS with queue mark_queue = [init_marking] rg = ts.TransitionSystem(name='Reachability graph of {}'.format(net.name)) inv_states = list() init_state = ts.TransitionSystem.State(staterep(repr(init_marking))) init_state.data['disc'] = 0 rg.states.add(init_state) # mapping visited states to marking mark_to_state = dict() mark_to_state[init_marking] = init_state while mark_queue and len(rg.states) < MAX_RG_STATE: cur_mark = mark_queue.pop(0) cur_state = mark_to_state[cur_mark] enabled_trans = list(semantics.enabled_transitions(net, cur_mark)) # workout the transition arc weight n_vis = len(list(map(lambda t: not is_inv(t), enabled_trans))) weight = 1. / n_vis if n_vis > 0 else 0 for t in enabled_trans: next_mark = semantics.execute(t, net, cur_mark) next_state = mark_to_state.get(next_mark, None) if next_state is None: next_state = ts.TransitionSystem.State( staterep(repr(next_mark))) # discovered one step away from parent node next_state.data['disc'] = cur_state.data['disc'] + 1 rg.states.add(next_state) mark_to_state[next_mark] = next_state mark_queue.append(next_mark) # doesnt matter that invisible transitions also get weight since # they will be removed ultimately as well data = {'weight': weight} t_label = t.label if t.name is not None else None rg_t = add_arc_from_to(t_label, cur_state, next_state, rg, data) if is_inv(t): inv_states.append((cur_state, rg_t, next_state)) if mark_queue: msg = 'Computation of reachability graph surpass the max number of states: {}'.format( MAX_RG_STATE) warnings.warn(msg, category=UserWarning) return rg, inv_states
def enhance(self, log_wrapper: LogWrapper): """ Enhance a given petri net based on an event log. :param log_wrapper: Event log under consideration as LogWrapper :return: None """ """ Standard Enhancement """ beta = float(10) reactivation_deltas = {} for place in self.net.places: reactivation_deltas[str(place)] = list() log_wrapper.iterator_reset() last_activation = {} pbar = tqdm(total=log_wrapper.num_traces, desc="Replay for Process Model Enhancement") while log_wrapper.iterator_hasNext(): trace = log_wrapper.iterator_next() pbar.update(1) for place in self.net.places: if place in self.initial_marking: last_activation[str(place)] = trace[0]['time:timestamp'] else: last_activation[str(place)] = -1 """ Replay and estimate parameters """ places_shortest_path_by_hidden = get_places_shortest_path_by_hidden( self.net, self.MAX_REC_DEPTH) marking = copy(self.initial_marking) for event in trace: if event[self.activity_key] in self.trans_map.keys(): activated_places = [] toi = self.trans_map[event[self.activity_key]] """ If Transition of interest is not enabled yet, then go through hidden""" if not semantics.is_enabled(toi, self.net, marking): _, _, act_trans, _ = apply_hidden_trans( toi, self.net, copy(marking), places_shortest_path_by_hidden, [], 0, set(), [copy(marking)]) for act_tran in act_trans: for arc in act_tran.out_arcs: activated_places.append(arc.target) marking = semantics.execute( act_tran, self.net, marking) """ If Transition of interest is STILL not enabled yet, then naively add missing token to fulfill firing rule """ if not semantics.is_enabled(toi, self.net, marking): for arc in toi.in_arcs: if arc.source not in marking: marking[arc.source] += 1 """ Fire transition of interest """ for arc in toi.out_arcs: activated_places.append(arc.target) marking = semantics.execute(toi, self.net, marking) """ Marking is gone - transition could not be fired ...""" if marking is None: raise ValueError("Invalid Marking - Transition " + toi + " could not be fired.") """ Update Time Recordings """ for activated_place in activated_places: if last_activation[str(activated_place)] != -1: time_delta = time_delta_seconds( last_activation[str(activated_place)], event['time:timestamp']) if time_delta > 0: # noinspection PyUnboundLocalVariable reactivation_deltas[str(place)].append( time_delta) last_activation[str( activated_place)] = event['time:timestamp'] pbar.close() """ Calculate decay function parameter """ for place in self.net.places: if len(reactivation_deltas[str(place)]) > 1: self.decay_functions[str(place)] = LinearDecay( alpha=1 / np.mean(reactivation_deltas[str(place)]), beta=beta) else: self.decay_functions[str(place)] = LinearDecay( alpha=1 / log_wrapper.max_trace_duration, beta=beta) """ Get resource keys to store """ self.resource_keys = log_wrapper.getResourceKeys()
def decay_replay(self, log_wrapper: LogWrapper, resources: list = None): """ Decay Replay on given event log. :param log_wrapper: Input event log as LogWrapper to be replayed. :param resources: Resource keys to count (must have been counted during Petri net enhancement already!), as a list :return: list of timed state samples as JSON, list of timed state sample objects """ tss = list() tss_objs = list() decay_values = {} token_counts = {} marks = {} last_activation = {} """ Initialize Resource Counter """ count_resources = False resource_counter = None if log_wrapper.resource_keys is not None: count_resources = True resource_counter = dict() for key in log_wrapper.resource_keys.keys(): resource_counter[key] = 0 """ ---> """ log_wrapper.iterator_reset() pbar = tqdm(total=log_wrapper.num_traces, desc="Decay Replay on Event Log") while log_wrapper.iterator_hasNext(): trace = log_wrapper.iterator_next() pbar.update(1) resource_count = copy(resource_counter) """ Reset all counts for the next trace """ for place in self.net.places: if place in self.initial_marking: last_activation[str(place)] = trace[0]['time:timestamp'] else: last_activation[str(place)] = -1 for place in self.net.places: decay_values[str(place)] = 0.0 token_counts[str(place)] = 0.0 marks[str(place)] = 0.0 """ ----------------------------------> """ places_shortest_path_by_hidden = get_places_shortest_path_by_hidden( self.net, self.MAX_REC_DEPTH) marking = copy(self.initial_marking) """ Initialize counts based on initial marking """ for place in marking: decay_values[str(place)] = self.decay_functions[str( place)].decay(t=0) token_counts[str(place)] += 1 marks[str(place)] = 1 """ ----------------------------------> """ """ Replay """ time_recent = None init_time = None for event_id in range(len(trace)): event = trace[event_id] if event_id == 0: init_time = event['time:timestamp'] time_past = time_recent time_recent = event['time:timestamp'] if event[self.activity_key] in self.trans_map.keys(): activated_places = list() toi = self.trans_map[event[self.activity_key]] """ If Transition of interest is not enabled yet, then go through hidden""" if not semantics.is_enabled(toi, self.net, marking): _, _, act_trans, _ = apply_hidden_trans( toi, self.net, copy(marking), places_shortest_path_by_hidden, [], 0, set(), [copy(marking)]) for act_tran in act_trans: for arc in act_tran.out_arcs: activated_places.append(arc.target) marking = semantics.execute( act_tran, self.net, marking) """ If Transition of interest is STILL not enabled yet, then naively add missing token to fulfill firing rule""" if not semantics.is_enabled(toi, self.net, marking): for arc in toi.in_arcs: if arc.source not in marking: marking[arc.source] += 1 """ Fire transition of interest """ for arc in toi.out_arcs: activated_places.append(arc.target) marking = semantics.execute(toi, self.net, marking) """ Marking is gone - transition could not be fired ...""" if marking is None: raise ValueError("Invalid Marking - Transition '" + str(toi) + "' could not be fired.") """ ----->""" """ Update Time Recordings """ for activated_place in activated_places: last_activation[str( activated_place)] = event['time:timestamp'] """ Count Resources""" if count_resources and resources is not None: for resource_key in resources: if resource_key in event.keys(): val = resource_key + "_:_" + event[resource_key] if val in resource_count.keys(): resource_count[val] += 1 """ Update Vectors and create TimedStateSamples """ if time_past is not None: decay_values, token_counts = self.__updateVectors( decay_values=decay_values, last_activation=last_activation, token_counts=token_counts, activated_places=activated_places, current_time=time_recent) next_event_id = self.__findNextEventId(event_id, trace) if next_event_id is not None: next_event = trace[next_event_id][ self.activity_key] else: next_event = None if count_resources: timedstatesample = TimedStateSample( time_delta_seconds(init_time, time_recent), copy(decay_values), copy(token_counts), copy(marking), copy(self.place_list), resource_count=copy(resource_count), resource_indices=log_wrapper.getResourceKeys()) else: timedstatesample = TimedStateSample( time_delta_seconds(init_time, time_recent), copy(decay_values), copy(token_counts), copy(marking), copy(self.place_list)) timedstatesample.setLabel(next_event) tss.append(timedstatesample.export()) tss_objs.append(timedstatesample) pbar.close() return tss, tss_objs
def apply_trace(trace, net, initial_marking, final_marking, trans_map, enable_place_fitness, place_fitness, places_shortest_path_by_hidden, consider_remaining_in_fitness, activity_key="concept:name", try_to_reach_final_marking_through_hidden=True, stop_immediately_unfit=False, walk_through_hidden_trans=True, post_fix_caching=None, marking_to_activity_caching=None): """ Apply the token replaying algorithm to a trace Parameters ---------- trace Trace in the event log net Petri net initial_marking Initial marking final_marking Final marking trans_map Map between transitions labels and transitions enable_place_fitness Enable fitness calculation at place level place_fitness Current dictionary of places associated with unfit traces places_shortest_path_by_hidden Shortest paths between places by hidden transitions consider_remaining_in_fitness Boolean value telling if the remaining tokens should be considered in fitness evaluation activity_key Name of the attribute that contains the activity try_to_reach_final_marking_through_hidden Boolean value that decides if we shall try to reach the final marking through hidden transitions stop_immediately_unfit Boolean value that decides if we shall stop immediately when a non-conformance is detected walk_through_hidden_trans Boolean value that decides if we shall walk through hidden transitions in order to enable visible transitions post_fix_caching Stores the post fix caching object marking_to_activity_caching Stores the marking-to-activity cache """ trace_activities = [event[activity_key] for event in trace] act_trans = [] transitions_with_problems = [] vis_mark = [] activating_transition_index = {} activating_transition_interval = [] used_postfix_cache = False marking = copy(initial_marking) vis_mark.append(marking) missing = 0 consumed = 0 produced = 0 for i in range(len(trace)): if ENABLE_POSTFIX_CACHE and (str(trace_activities) in post_fix_caching.cache and hash(marking) in post_fix_caching.cache[str(trace_activities)]): trans_to_act = post_fix_caching.cache[str(trace_activities)][hash(marking)]["trans_to_activate"] for z in range(len(trans_to_act)): t = trans_to_act[z] act_trans.append(t) used_postfix_cache = True marking = post_fix_caching.cache[str(trace_activities)][hash(marking)]["final_marking"] break else: prev_len_activated_transitions = len(act_trans) if ENABLE_MARKTOACT_CACHE and (hash(marking) in marking_to_activity_caching.cache and trace[i][activity_key] in marking_to_activity_caching.cache[hash(marking)] and trace[i - 1][activity_key] == marking_to_activity_caching.cache[hash(marking)][trace[i][activity_key]] ["previousActivity"]): this_end_marking = marking_to_activity_caching.cache[hash(marking)][trace[i][activity_key]][ "end_marking"] this_act_trans = marking_to_activity_caching.cache[hash(marking)][trace[i][activity_key]][ "this_activated_transitions"] this_vis_markings = marking_to_activity_caching.cache[hash(marking)][trace[i][activity_key]][ "this_visited_markings"] act_trans = act_trans + this_act_trans vis_mark = vis_mark + this_vis_markings marking = copy(this_end_marking) else: if trace[i][activity_key] in trans_map: t = trans_map[trace[i][activity_key]] if walk_through_hidden_trans and not semantics.is_enabled(t, net, marking): visited_transitions = set() prev_len_activated_transitions = len(act_trans) [net, marking, act_trans, vis_mark] = apply_hidden_trans(t, net, marking, places_shortest_path_by_hidden, act_trans, 0, visited_transitions, vis_mark) if not semantics.is_enabled(t, net, marking): transitions_with_problems.append(t) if stop_immediately_unfit: missing = missing + 1 break [m, tokens_added] = add_missing_tokens(t, marking) missing = missing + m if enable_place_fitness: for place in tokens_added.keys(): if place in place_fitness: place_fitness[place]["underfed_traces"].add(trace) c = get_consumed_tokens(t) p = get_produced_tokens(t) consumed = consumed + c produced = produced + p if semantics.is_enabled(t, net, marking): marking = semantics.execute(t, net, marking) act_trans.append(t) vis_mark.append(marking) del trace_activities[0] if len(trace_activities) < MAX_POSTFIX_SUFFIX_LENGTH: activating_transition_index[str(trace_activities)] = {"index": len(act_trans), "marking": hash(marking)} if i > 0: activating_transition_interval.append( [trace[i][activity_key], prev_len_activated_transitions, len(act_trans), trace[i - 1][activity_key]]) else: activating_transition_interval.append( [trace[i][activity_key], prev_len_activated_transitions, len(act_trans), ""]) if try_to_reach_final_marking_through_hidden and not used_postfix_cache: for i in range(MAX_IT_FINAL): if not break_condition_final_marking(marking, final_marking): hidden_transitions_to_enable = get_req_transitions_for_final_marking(marking, final_marking, places_shortest_path_by_hidden) for group in hidden_transitions_to_enable: for t in group: if semantics.is_enabled(t, net, marking): marking = semantics.execute(t, net, marking) act_trans.append(t) vis_mark.append(marking) if break_condition_final_marking(marking, final_marking): break else: break # try to reach the final marking in a different fashion, if not already reached if not break_condition_final_marking(marking, final_marking): if len(final_marking) == 1: sink_place = list(final_marking)[0] connections_to_sink = [] for place in marking: if place in places_shortest_path_by_hidden and sink_place in places_shortest_path_by_hidden[place]: connections_to_sink.append([place, places_shortest_path_by_hidden[place][sink_place]]) connections_to_sink = sorted(connections_to_sink, key=lambda x: len(x[1])) for i in range(MAX_IT_FINAL): for j in range(len(connections_to_sink)): for z in range(len(connections_to_sink[j][1])): t = connections_to_sink[j][1][z] if semantics.is_enabled(t, net, marking): marking = semantics.execute(t, net, marking) act_trans.append(t) vis_mark.append(marking) continue else: break marking_before_cleaning = copy(marking) remaining = 0 for p in marking: if p in final_marking: marking[p] = max(0, marking[p] - final_marking[p]) if enable_place_fitness: if marking[p] > 0: if p in place_fitness: if trace not in place_fitness[p]["underfed_traces"]: place_fitness[p]["overfed_traces"].add(trace) remaining = remaining + marking[p] if consider_remaining_in_fitness: is_fit = (missing == 0) and (remaining == 0) else: is_fit = (missing == 0) if consumed > 0 and produced > 0: trace_fitness = (1.0 - float(missing) / float(consumed)) * (1.0 - float(remaining) / float(produced)) else: trace_fitness = 1.0 if is_fit: for suffix in activating_transition_index: if suffix not in post_fix_caching.cache: post_fix_caching.cache[suffix] = {} if activating_transition_index[suffix]["marking"] not in post_fix_caching.cache[suffix]: post_fix_caching.cache[suffix][activating_transition_index[suffix]["marking"]] = \ {"trans_to_activate": act_trans[activating_transition_index[suffix]["index"]:], "final_marking": marking} for trans in activating_transition_interval: activity = trans[0] start_marking_index = trans[1] end_marking_index = trans[2] previous_activity = trans[3] if end_marking_index < len(vis_mark): start_marking_object = vis_mark[start_marking_index] start_marking_hash = hash(start_marking_object) end_marking_object = vis_mark[end_marking_index] if activity in trans_map: this_activated_trans = act_trans[start_marking_index:end_marking_index] this_visited_markings = vis_mark[start_marking_index + 1:end_marking_index + 1] if start_marking_hash not in marking_to_activity_caching.cache: marking_to_activity_caching.cache[start_marking_hash] = {} if activity not in marking_to_activity_caching.cache[start_marking_hash]: marking_to_activity_caching.cache[start_marking_hash][activity] = { "start_marking": start_marking_object, "end_marking": end_marking_object, "this_activated_transitions": this_activated_trans, "this_visited_markings": this_visited_markings, "previousActivity": previous_activity} return [is_fit, trace_fitness, act_trans, transitions_with_problems, marking_before_cleaning, get_visible_transitions_eventually_enabled_by_marking(net, marking_before_cleaning)]
all_enabled_trans = list(all_enabled_trans) shuffle(all_enabled_trans) trans = all_enabled_trans[0] if trans.label is not None: event = log_instance.Event() event[activity_key] = trans.label results.append([ case_num, event[activity_key], datetime.now() + timedelta(seconds=env.now) ]) yield env.process( getattr(case, str(trans.label).replace(" ", ""))()) event[timestamp_key] = curr_timestamp trace.append(event) marking = semantics.execute(trans, net, marking) if len(trace) > max_trace_length: break if len(trace) > 0: log.append(trace) results.append([ case_num, "case end", datetime.now() + timedelta(seconds=env.now) ]) for row in results: thewriter.writerow(row) f.close() return log
def token_pull(trans, net, marking, is_inv): """Perform token pull given a marking and a visible transition that we would like to execute. Algorithmically speaking, it corresponds to a BFS for a marking that enables the transition. :param trans: visible transition that we would like to execute :param net: petri net model :param marking: current marking :param is_inv: function that says if a transition is invisible :return: sequence of markings that leads to the enabling of transition or None if transition cannot be eventually enabled """ debug_msg = 'Performing token pull at marking {} for target transition {}' debug_msg = debug_msg.format(marking, trans) logger.debug(debug_msg) def retrieve_marking_seq(node): debug_msg = 'Recursing from {} to get marking seq' debug_msg = debug_msg.format(node[0]) logger.debug(debug_msg) marking_seq = list() while node[2] is not None: # parent is not the start marking marking_seq.insert(0, node[0]) par_node = node[2] node = par_node return marking_seq enabled_trans = list(semantics.enabled_transitions(net, marking)) enabled_trans = list(filter(is_inv, enabled_trans)) start_node = ( marking, # current marking enabled_trans, # enabled invisible transitions at marking None # parent node ) queue = [start_node] visited = set() visited.add(marking) while queue: node = queue[0] cur_marking, enabled_trans, parent_node = node try: inv_tran = enabled_trans.pop(0) new_marking = semantics.execute(inv_tran, net, cur_marking) new_enabled_trans = list(semantics.enabled_transitions(net, new_marking)) # check if the target transition is in the enabled transitions if trans in new_enabled_trans: # we are done since new_marking enables the target transitions # recurse back to get the sequence of markings that led to the transition to become enabled marking_seq = retrieve_marking_seq(node) + [new_marking] debug_msg = 'Recursed marking sequence: {}' debug_msg = debug_msg.format(marking_seq) logger.debug(debug_msg) return marking_seq # filter out all visible transitions and add to queue if there are enabled invisible transitions # also filter out transitions that lead to an already visited marking filtered_enabled = list() for t in new_enabled_trans: is_inv_t = is_inv(t) new_marking_t = semantics.execute(t, net, cur_marking) to_new_marking = new_marking_t not in visited if is_inv_t and to_new_marking: filtered_enabled.append(t) visited.add(to_new_marking) if len(filtered_enabled): # add new_marking to queue new_node = ( new_marking, filtered_enabled, node ) queue.append(new_node) except: queue.pop(0) # cannot enable target transition return None
def apply_trace(trace, net, initial_marking, final_marking, trans_map, enable_pltr_fitness, place_fitness, transition_fitness, notexisting_activities_in_model, places_shortest_path_by_hidden, consider_remaining_in_fitness, activity_key="concept:name", try_to_reach_final_marking_through_hidden=True, stop_immediately_unfit=False, walk_through_hidden_trans=True, post_fix_caching=None, marking_to_activity_caching=None, is_reduction=False, thread_maximum_ex_time=MAX_DEF_THR_EX_TIME, enable_postfix_cache=False, enable_marktoact_cache=False, cleaning_token_flood=False, s_components=None): """ Apply the token replaying algorithm to a trace Parameters ---------- trace Trace in the event log net Petri net initial_marking Initial marking final_marking Final marking trans_map Map between transitions labels and transitions enable_pltr_fitness Enable fitness calculation at place/transition level place_fitness Current dictionary of places associated with unfit traces transition_fitness Current dictionary of transitions associated with unfit traces notexisting_activities_in_model Map that stores the notexisting activities in the model places_shortest_path_by_hidden Shortest paths between places by hidden transitions consider_remaining_in_fitness Boolean value telling if the remaining tokens should be considered in fitness evaluation activity_key Name of the attribute that contains the activity try_to_reach_final_marking_through_hidden Boolean value that decides if we shall try to reach the final marking through hidden transitions stop_immediately_unfit Boolean value that decides if we shall stop immediately when a non-conformance is detected walk_through_hidden_trans Boolean value that decides if we shall walk through hidden transitions in order to enable visible transitions post_fix_caching Stores the post fix caching object marking_to_activity_caching Stores the marking-to-activity cache is_reduction Expresses if the token-based replay is called in a reduction attempt thread_maximum_ex_time Alignment threads maximum allowed execution time enable_postfix_cache Enables postfix cache enable_marktoact_cache Enables marking to activity cache cleaning_token_flood Decides if a cleaning of the token flood shall be operated s_components S-components of the Petri net (if workflow net) """ trace_activities = [event[activity_key] for event in trace] act_trans = [] transitions_with_problems = [] vis_mark = [] activating_transition_index = {} activating_transition_interval = [] used_postfix_cache = False marking = copy(initial_marking) vis_mark.append(marking) missing = 0 consumed = 0 sum_tokens_im = 0 for place in initial_marking: sum_tokens_im = sum_tokens_im + initial_marking[place] sum_tokens_fm = 0 for place in final_marking: sum_tokens_fm = sum_tokens_fm + final_marking[place] produced = sum_tokens_im current_event_map = {} current_remaining_map = {} for i in range(len(trace)): if enable_postfix_cache and ( str(trace_activities) in post_fix_caching.cache and hash(marking) in post_fix_caching.cache[str(trace_activities)]): trans_to_act = post_fix_caching.cache[str(trace_activities)][hash( marking)]["trans_to_activate"] for z in range(len(trans_to_act)): t = trans_to_act[z] act_trans.append(t) used_postfix_cache = True marking = post_fix_caching.cache[str(trace_activities)][hash( marking)]["final_marking"] break else: prev_len_activated_transitions = len(act_trans) if enable_marktoact_cache and ( hash(marking) in marking_to_activity_caching.cache and trace[i][activity_key] in marking_to_activity_caching.cache[hash(marking)] and trace[i - 1][activity_key] == marking_to_activity_caching.cache[hash(marking)][ trace[i][activity_key]]["previousActivity"]): this_end_marking = marking_to_activity_caching.cache[hash( marking)][trace[i][activity_key]]["end_marking"] this_act_trans = marking_to_activity_caching.cache[hash( marking)][trace[i] [activity_key]]["this_activated_transitions"] this_vis_markings = marking_to_activity_caching.cache[hash( marking)][trace[i][activity_key]]["this_visited_markings"] act_trans = act_trans + this_act_trans vis_mark = vis_mark + this_vis_markings marking = copy(this_end_marking) else: if trace[i][activity_key] in trans_map: current_event_map.update(trace[i]) t = trans_map[trace[i][activity_key]] if walk_through_hidden_trans and not semantics.is_enabled( t, net, marking): visited_transitions = set() prev_len_activated_transitions = len(act_trans) [net, marking, act_trans, vis_mark] = apply_hidden_trans( t, net, marking, places_shortest_path_by_hidden, act_trans, 0, visited_transitions, vis_mark) is_initially_enabled = True old_marking_names = [x.name for x in list(marking.keys())] if not semantics.is_enabled(t, net, marking): is_initially_enabled = False transitions_with_problems.append(t) if stop_immediately_unfit: missing = missing + 1 break [m, tokens_added] = add_missing_tokens(t, marking) missing = missing + m if enable_pltr_fitness: for place in tokens_added.keys(): if place in place_fitness: place_fitness[place][ "underfed_traces"].add(trace) if trace not in transition_fitness[t][ "underfed_traces"]: transition_fitness[t]["underfed_traces"][ trace] = list() transition_fitness[t]["underfed_traces"][ trace].append(current_event_map) elif enable_pltr_fitness: if trace not in transition_fitness[t]["fit_traces"]: transition_fitness[t]["fit_traces"][trace] = list() transition_fitness[t]["fit_traces"][trace].append( current_event_map) c = get_consumed_tokens(t) p = get_produced_tokens(t) consumed = consumed + c produced = produced + p if semantics.is_enabled(t, net, marking): marking = semantics.execute(t, net, marking) act_trans.append(t) vis_mark.append(marking) if not is_initially_enabled and cleaning_token_flood: # here, a routine for cleaning token flood shall go new_marking_names = [ x.name for x in list(marking.keys()) ] new_marking_names_diff = [ x for x in new_marking_names if x not in old_marking_names ] new_marking_names_inte = [ x for x in new_marking_names if x in old_marking_names ] for p1 in new_marking_names_inte: for p2 in new_marking_names_diff: for comp in s_components: if p1 in comp and p2 in comp: place_to_delete = [ place for place in list(marking.keys()) if place.name == p1 ] if len(place_to_delete) == 1: del marking[place_to_delete[0]] if not place_to_delete[ 0] in current_remaining_map: current_remaining_map[ place_to_delete[0]] = 0 current_remaining_map[ place_to_delete[ 0]] = current_remaining_map[ place_to_delete[0]] + 1 pass else: if not trace[i][ activity_key] in notexisting_activities_in_model: notexisting_activities_in_model[trace[i] [activity_key]] = {} notexisting_activities_in_model[ trace[i][activity_key]][trace] = current_event_map del trace_activities[0] if len(trace_activities) < MAX_POSTFIX_SUFFIX_LENGTH: activating_transition_index[str(trace_activities)] = { "index": len(act_trans), "marking": hash(marking) } if i > 0: activating_transition_interval.append([ trace[i][activity_key], prev_len_activated_transitions, len(act_trans), trace[i - 1][activity_key] ]) else: activating_transition_interval.append([ trace[i][activity_key], prev_len_activated_transitions, len(act_trans), "" ]) if try_to_reach_final_marking_through_hidden and not used_postfix_cache: for i in range(MAX_IT_FINAL1): if not break_condition_final_marking(marking, final_marking): hidden_transitions_to_enable = get_req_transitions_for_final_marking( marking, final_marking, places_shortest_path_by_hidden) for group in hidden_transitions_to_enable: for t in group: if semantics.is_enabled(t, net, marking): marking = semantics.execute(t, net, marking) act_trans.append(t) vis_mark.append(marking) if break_condition_final_marking(marking, final_marking): break else: break # if i > DebugConst.REACH_ITF1: # DebugConst.REACH_ITF1 = i # try to reach the final marking in a different fashion, if not already reached if not break_condition_final_marking(marking, final_marking): if len(final_marking) == 1: sink_place = list(final_marking)[0] connections_to_sink = [] for place in marking: if place in places_shortest_path_by_hidden and sink_place in places_shortest_path_by_hidden[ place]: connections_to_sink.append([ place, places_shortest_path_by_hidden[place][sink_place] ]) connections_to_sink = sorted(connections_to_sink, key=lambda x: len(x[1])) for i in range(MAX_IT_FINAL2): for j in range(len(connections_to_sink)): for z in range(len(connections_to_sink[j][1])): t = connections_to_sink[j][1][z] if semantics.is_enabled(t, net, marking): marking = semantics.execute(t, net, marking) act_trans.append(t) vis_mark.append(marking) continue else: break # if i > DebugConst.REACH_ITF2: # DebugConst.REACH_ITF2 = i if break_condition_final_marking(marking, final_marking): consumed = consumed + sum_tokens_fm marking_before_cleaning = copy(marking) remaining = 0 for p in marking: if p in final_marking: marking[p] = max(0, marking[p] - final_marking[p]) if enable_pltr_fitness: if marking[p] > 0: if p in place_fitness: if trace not in place_fitness[p]["underfed_traces"]: place_fitness[p]["overfed_traces"].add(trace) remaining = remaining + marking[p] for p in current_remaining_map: if enable_pltr_fitness: if p in place_fitness: if trace not in place_fitness[p][ "underfed_traces"] and trace not in place_fitness[p][ "overfed_traces"]: place_fitness[p]["overfed_traces"].add(trace) remaining = remaining + current_remaining_map[p] if consider_remaining_in_fitness: is_fit = (missing == 0) and (remaining == 0) else: is_fit = (missing == 0) if consumed > 0 and produced > 0: #trace_fitness = (1.0 - float(missing) / float(consumed)) * (1.0 - float(remaining) / float(produced)) trace_fitness = 0.5 * (1.0 - float(missing) / float(consumed)) + 0.5 * ( 1.0 - float(remaining) / float(produced)) else: trace_fitness = 1.0 if is_fit: for suffix in activating_transition_index: if suffix not in post_fix_caching.cache: post_fix_caching.cache[suffix] = {} if activating_transition_index[suffix][ "marking"] not in post_fix_caching.cache[suffix]: post_fix_caching.cache[suffix][activating_transition_index[suffix]["marking"]] = \ {"trans_to_activate": act_trans[activating_transition_index[suffix]["index"]:], "final_marking": marking} for trans in activating_transition_interval: activity = trans[0] start_marking_index = trans[1] end_marking_index = trans[2] previous_activity = trans[3] if end_marking_index < len(vis_mark): start_marking_object = vis_mark[start_marking_index] start_marking_hash = hash(start_marking_object) end_marking_object = vis_mark[end_marking_index] if activity in trans_map: this_activated_trans = act_trans[ start_marking_index:end_marking_index] this_visited_markings = vis_mark[start_marking_index + 1:end_marking_index + 1] if start_marking_hash not in marking_to_activity_caching.cache: marking_to_activity_caching.cache[ start_marking_hash] = {} if activity not in marking_to_activity_caching.cache[ start_marking_hash]: marking_to_activity_caching.cache[start_marking_hash][ activity] = { "start_marking": start_marking_object, "end_marking": end_marking_object, "this_activated_transitions": this_activated_trans, "this_visited_markings": this_visited_markings, "previousActivity": previous_activity } return [ is_fit, trace_fitness, act_trans, transitions_with_problems, marking_before_cleaning, get_visible_transitions_eventually_enabled_by_marking( net, marking_before_cleaning), missing, consumed, remaining, produced ]
def apply_hidden_trans(t, net, marking, places_shortest_paths_by_hidden, act_tr, rec_depth, visit_trans, vis_mark): """ Apply hidden transitions in order to enable a given transition Parameters ---------- t Transition to eventually enable net Petri net marking Marking places_shortest_paths_by_hidden Shortest paths between places connected by hidden transitions act_tr All activated transitions rec_depth Current recursion depth visit_trans All visited transitions by hiddenTrans method vis_mark All visited markings """ if rec_depth >= MAX_REC_DEPTH_HIDTRANSENABL or t in visit_trans: return [net, marking, act_tr, vis_mark] # if rec_depth > DebugConst.REACH_MRH: # DebugConst.REACH_MRH = rec_depth visit_trans.add(t) marking_at_start = copy(marking) places_with_missing = get_places_with_missing_tokens(t, marking) hidden_transitions_to_enable = get_hidden_transitions_to_enable( marking, places_with_missing, places_shortest_paths_by_hidden) if hidden_transitions_to_enable: [marking, act_tr, visit_trans, vis_mark] = enable_hidden_transitions(net, marking, act_tr, visit_trans, vis_mark, hidden_transitions_to_enable, t) if not semantics.is_enabled(t, net, marking): hidden_transitions_to_enable = get_hidden_transitions_to_enable( marking, places_with_missing, places_shortest_paths_by_hidden) for z in range(len(hidden_transitions_to_enable)): for k in range(len(hidden_transitions_to_enable[z])): t4 = hidden_transitions_to_enable[z][k] if not t4 == t: if t4 not in visit_trans: if not semantics.is_enabled(t4, net, marking): [net, marking, act_tr, vis_mark] = apply_hidden_trans( t4, net, marking, places_shortest_paths_by_hidden, act_tr, rec_depth + 1, visit_trans, vis_mark) if semantics.is_enabled(t4, net, marking): marking = semantics.execute(t4, net, marking) act_tr.append(t4) visit_trans.add(t4) vis_mark.append(marking) if not semantics.is_enabled(t, net, marking): if not (marking_at_start == marking): [net, marking, act_tr, vis_mark] = apply_hidden_trans( t, net, marking, places_shortest_paths_by_hidden, act_tr, rec_depth + 1, visit_trans, vis_mark) return [net, marking, act_tr, vis_mark]