def construct_tree(net, initial_marking): """ Construct a restricted coverability marking. For more information, see the thesis "Verification of WF-nets", 4.3. :param net: :param initial_marking: :return: """ initial_marking = helper.convert_marking(net, initial_marking) firing_dict = helper.split_incidence_matrix( helper.compute_incidence_matrix(net), net) req_dict = helper.compute_firing_requirement(net) look_up_indices = {} j = 0 coverability_graph = nx.DiGraph() coverability_graph.add_node(j, marking=initial_marking) look_up_indices[np.array2string(initial_marking)] = j j += 1 new_arc = True while new_arc: new_arc = False nodes = list(coverability_graph.nodes).copy() while len(nodes) > 0: m = nodes.pop() if not np.inf in coverability_graph.nodes[m]['marking']: possible_markings = helper.enabled_markings( firing_dict, req_dict, coverability_graph.nodes[m]['marking']) m2 = None if len(possible_markings) > 0: for marking in possible_markings: # check for m1 + since we want to construct a tree, we do not want that a marking is already in a graph since it is going to have an arc if np.array2string(marking[0]) not in look_up_indices: if check_if_transition_unique( m, coverability_graph, marking[1]): m2 = marking new_arc = True break if new_arc: break if new_arc: m3 = np.zeros(len(list(net.places))) for place in list(net.places): if check_for_smaller_marking(m2, coverability_graph, list(net.places).index(place), m, look_up_indices): m3[list(net.places).index(place)] = np.inf else: m3[list(net.places).index(place)] = m2[0][list( net.places).index(place)] coverability_graph.add_node(j, marking=m3) coverability_graph.add_edge(m, j, transition=m2[1]) look_up_indices[np.array2string(m3)] = j j += 1 return coverability_graph
def apply(net, initial_marking, original_net=None): """ Method that computes a reachability graph as networkx object :param net: Petri Net :param initial_marking: Initial Marking of the Petri Net :param original_net: Petri Net without short-circuited transition :return: Networkx Graph that represents the reachability graph of the Petri Net """ initial_marking = helper.convert_marking(net, initial_marking, original_net) firing_dict = helper.split_incidence_matrix( helper.compute_incidence_matrix(net), net) req_dict = helper.compute_firing_requirement(net) look_up_indices = {} j = 0 reachability_graph = nx.MultiDiGraph() reachability_graph.add_node(j, marking=initial_marking) working_set = set() working_set.add(j) look_up_indices[np.array2string(initial_marking)] = j j += 1 while len(working_set) > 0: m = working_set.pop() possible_markings = helper.enabled_markings( firing_dict, req_dict, reachability_graph.nodes[m]['marking']) for marking in possible_markings: if np.array2string(marking[0]) not in look_up_indices: look_up_indices[np.array2string(marking[0])] = j reachability_graph.add_node(j, marking=marking[0]) working_set.add(j) reachability_graph.add_edge(m, j, transition=marking[1]) j += 1 else: reachability_graph.add_edge(m, look_up_indices[np.array2string( marking[0])], transition=marking[1]) return reachability_graph
def minimal_coverability_tree(net, initial_marking, original_net=None): """ This method computes the minimal coverability tree. It is part of a method to obtain a minial coverability graph :param net: Petri Net :param initial_marking: Initial Marking of the Petri Net :param original_net: Petri Net without short-circuited transition :return: Minimal coverability tree """ def check_if_marking_already_in_processed_nodes(n, processed_nodes): for node in processed_nodes: if np.array_equal(G.nodes[node]['marking'], G.nodes[n]['marking']): return True return False def is_m_smaller_than_other(m, processed_nodes): for node in processed_nodes: if all(np.less_equal(m, G.nodes[node]['marking'])): return True return False def is_m_greater_than_other(m, processed_nodes): for node in processed_nodes: if all(np.greater_equal(m, G.nodes[node]['marking'])): return True return False def get_first_smaller_marking_on_path(n, m2): path = nx.shortest_path(G, source=0, target=n) for node in path: if all(np.less_equal(G.nodes[node]['marking'], m2)): return node return None def remove_subtree(tree, n): bfs_tree = nx.bfs_tree(tree, n) for edge in bfs_tree.edges: tree.remove_edge(edge[0], edge[1]) for node in bfs_tree.nodes: if node != n: tree.remove_node(node) return tree G = nx.MultiDiGraph() incidence_matrix = helper.compute_incidence_matrix(net) firing_dict = helper.split_incidence_matrix(incidence_matrix, net) req_dict = helper.compute_firing_requirement(net) initial_mark = helper.convert_marking(net, initial_marking, original_net) j = 0 unprocessed_nodes = set() G.add_node(j, marking=initial_mark) unprocessed_nodes.add(j) j += 1 processed_nodes = set() while len(unprocessed_nodes) > 0: n = unprocessed_nodes.pop() if check_if_marking_already_in_processed_nodes(n, processed_nodes): processed_nodes.add(n) elif is_m_smaller_than_other(G.nodes[n]['marking'], processed_nodes): G.remove_edge(next(G.predecessors(n)), n) G.remove_node(n) elif is_m_greater_than_other(G.nodes[n]['marking'], processed_nodes): m2 = G.nodes[n]['marking'].copy() ancestor_bool = False for ancestor in nx.ancestors(G, n): if is_m_greater_than_other(G.nodes[n]['marking'], [ancestor]): i = 0 while i < len(G.nodes[n]['marking']): if G.nodes[ancestor]['marking'][i] < G.nodes[n][ 'marking'][i]: m2[i] = np.inf i += 1 n1 = None for ancestor in nx.ancestors(G, n): if all(np.less_equal(G.nodes[ancestor]['marking'], m2)): n1 = get_first_smaller_marking_on_path(n, m2) break if n1 != None: ancestor_bool = True G.nodes[n1]['marking'] = m2.copy() subtree = nx.bfs_tree(G, n1) for node in subtree: if node in processed_nodes: processed_nodes.remove(node) if node in unprocessed_nodes: unprocessed_nodes.remove(node) G = remove_subtree(G, n1) unprocessed_nodes.add(n1) processed_nodes_copy = copy(processed_nodes) for node in processed_nodes_copy: if node in G.nodes: if all(np.less_equal(G.nodes[node]['marking'], m2)): subtree = nx.bfs_tree(G, node) for node in subtree: if node in processed_nodes: processed_nodes.remove(node) if node in unprocessed_nodes: unprocessed_nodes.remove(node) remove_subtree(G, node) G.remove_node(node) if not ancestor_bool: unprocessed_nodes.add(n) else: for el in helper.enabled_markings(firing_dict, req_dict, G.nodes[n]['marking']): G.add_node(j, marking=el[0]) G.add_edge(n, j, transition=el[1]) unprocessed_nodes.add(j) j += 1 processed_nodes.add(n) return (G, firing_dict, req_dict)