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
def __update_heu_dict(heu_dict, heu_max_ind_dict, mm, index, h, x, firing_sequence, incidence_matrix, cost_vec): """ Updates the heuristics dictionary on the new marking, storing the information about the heuristics and the vector """ x = copy(x) __update_heu_dict_specific_point(heu_dict, heu_max_ind_dict, mm, index, h, x) firing_sequence = list(firing_sequence) while firing_sequence: t = firing_sequence.pop(0) h, x = utils.__derive_heuristic(incidence_matrix, cost_vec, x, t, h) mm = semantics.weak_execute(t, mm) __update_heu_dict_specific_point(heu_dict, heu_max_ind_dict, mm, index, h, x)
def execute_tr(m, t, tokens_counter): for a in t.in_arcs: sp = a.source w = a.weight if sp not in m: tokens_counter["missing"] += w elif w > m[sp]: tokens_counter["missing"] += w - m[sp] tokens_counter["consumed"] += w for a in t.out_arcs: tokens_counter["produced"] += a.weight new_m = weak_execute(t, m) m = new_m return m, tokens_counter
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
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
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: pl = a.source mark = a.weight if pl not in marking or new_marking[pl] < mark:
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
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
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
def search_path_among_sol(sync_net: PetriNet, ini: Marking, fin: Marking, activated_transitions: List[PetriNet.Transition], skip=SKIP) -> Tuple[ List[PetriNet.Transition], bool, int]: """ (Efficient method) Searches a firing sequence among the X vector that is the solution of the (extended) marking equation Parameters --------------- sync_net Synchronous product net ini Initial marking of the net fin Final marking of the net activated_transitions Transitions that have non-zero occurrences in the X vector skip Skip transition Returns --------------- firing_sequence Firing sequence reach_fm Boolean value that tells if the final marking is reached by the firing sequence explained_events Number of explained events """ reach_fm = False trans_empty_preset = set(t for t in sync_net.transitions if len(t.in_arcs) == 0) trans_with_index = {} trans_wo_index = set() for t in activated_transitions: if properties.TRACE_NET_TRANS_INDEX in t.properties: trans_with_index[t.properties[properties.TRACE_NET_TRANS_INDEX]] = t else: trans_wo_index.add(t) keys = sorted(list(trans_with_index.keys())) trans_with_index = [trans_with_index[i] for i in keys] best_tuple = (0, 0, ini, list()) open_set = [best_tuple] heapq.heapify(open_set) visited = 0 closed = set() len_trace_with_index = len(trans_with_index) while len(open_set) > 0: curr = heapq.heappop(open_set) index = -curr[0] marking = curr[2] if marking in closed: continue if index == len_trace_with_index: reach_fm = True if curr[0] < best_tuple[0]: best_tuple = curr break if curr[0] < best_tuple[0]: best_tuple = curr closed.add(marking) corr_trans = trans_with_index[index] if corr_trans.sub_marking <= marking: visited += 1 new_marking = semantics.weak_execute(corr_trans, marking) heapq.heappush(open_set, (-index-1, visited, new_marking, curr[3]+[corr_trans])) else: enabled = copy(trans_empty_preset) for p in marking: for t in p.ass_trans: if t in trans_wo_index and t.sub_marking <= marking: enabled.add(t) for new_trans in enabled: visited += 1 new_marking = semantics.weak_execute(new_trans, marking) heapq.heappush(open_set, (-index, visited, new_marking, curr[3]+[new_trans])) return best_tuple[-1], reach_fm, -best_tuple[0]