def check_non_blocking(net0): """ Checks if a workflow net is non-blocking Parameters ------------- net Petri net Returns ------------- boolean Boolean value """ net = deepcopy(net0) petri_utils.decorate_transitions_prepostset(net) graph, inv_dictionary = petri_utils.create_networkx_directed_graph(net) dictionary = {y:x for x,y in inv_dictionary.items()} source_place = [place for place in net.places if len(place.in_arcs) == 0][0] for trans in net.transitions: # transitions with 1 input arcs does not block if len(trans.in_arcs) > 1: places = [arc.source for arc in trans.in_arcs] # search the top-right intersection between a path connecting the initial marking and the input places # of such transition # # this exploration is based on heuristics; another option is to use the exploration provided in explore_path (that is based on alignments) spaths = [[inv_dictionary[y] for y in nx.algorithms.shortest_paths.generic.shortest_path(graph, dictionary[source_place], dictionary[x])] for x in places] spaths = [[y for y in x if type(y) is petrinet.PetriNet.Transition] for x in spaths] trans_dict = {} i = 0 while i < len(places): p1 = places[i] spath1 = spaths[i] j = i + 1 while j < len(places): p2 = places[j] spath2 = spaths[j] s1, s2 = [x for x in spath1 if x in spath2], [x for x in spath2 if x in spath1] if len(s1) > 0 and len(s2) > 0 and s1[-1] == s2[-1]: # there is an intersection t = s1[-1] if len(t.out_arcs) <= 1: return False else: if t not in trans_dict: trans_dict[t] = set() trans_dict[t].add(p1) trans_dict[t].add(p2) else: return False j = j + 1 i = i + 1 # after checking if the intersecting transition has at least one exit, # we check also that the number of outputs of the transition is at least the one expected for t in trans_dict: if len(t.out_arcs) < len(trans_dict[t]): return False return True
def check_loops_generating_tokens(net0): """ Check if the Petri net contains loops generating tokens Parameters ------------ net0 Petri net Returns ------------ boolean Boolean value (True if the net has loops generating tokens) """ net = deepcopy(net0) petri_utils.decorate_transitions_prepostset(net) graph, inv_dictionary = petri_utils.create_networkx_directed_graph(net) dictionary = {y:x for x,y in inv_dictionary.items()} loops_trans = petri_utils.get_cycles_petri_net_transitions(net) for loop in loops_trans: m = petrinet.Marking() for index, trans in enumerate(loop): m = m + trans.add_marking visited_couples = set() while True: changed_something = False # if there are no places with positive marking replaying the loop, then we are fine neg_places = [p for p in m if m[p] < 0] pos_places = [p for p in m if m[p] > 0] for p1 in pos_places: # if there are no places with negative marking replaying the loop, then we are doomed for p2 in neg_places: if not ((p1, p2)) in visited_couples: visited_couples.add((p1, p2)) # otherwise, do a check if there is a path in the workflow net between the two places # this exploration is based on heuristics; indeed, since we can enter the cycle multiple times # it does not really matter if we reach suddenly the optimal path # # another option is to use the exploration provided in explore_path (that is based on alignments) spath = None try: spath = nx.algorithms.shortest_paths.generic.shortest_path(graph, dictionary[p1], dictionary[p2]) except: pass if spath is not None: trans = [inv_dictionary[x] for x in spath if type(inv_dictionary[x]) is petrinet.PetriNet.Transition] sec_m0 = petrinet.Marking() for t in trans: sec_m0 = sec_m0 + t.add_marking sec_m = petrinet.Marking() for place in m: if place in sec_m0: sec_m[place] = sec_m0[place] m1 = m + sec_m # the exploration is (in part) successful if m1[p1] == 0: m = m1 changed_something = True break if not changed_something: break if not changed_something: break # at this point, we have definitely created one token if sum(m[place] for place in m) > 0: return True return False