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
Beispiel #2
0
def compute_non_live_sequences(woflan_object):
    """
    We want to compute the sequences of transitions which lead to deadlocks.
    To do this, we first compute a reachbility graph (possible, since we know that the Petri Net is bounded) and then we
    convert it to a spanning tree. Afterwards, we compute the paths which lead to nodes from which the final marking cannot
    be reached. Note: We are searching for the shortest sequence. After the first red node, all successors are also red.
    Therefore, we do not have to consider them.
    :param woflan_object: Object that contains the necessary information
    :return: List of sequence of transitions, each sequence is a list
    """
    woflan_object.set_r_g(
        reachability_graph(woflan_object.get_net(),
                           woflan_object.get_initial_marking()))
    f_m = convert_marking(woflan_object.get_net(),
                          woflan_object.get_final_marking())
    sucessfull_terminate_state = None
    for node in woflan_object.get_r_g().nodes:
        if all(np.equal(woflan_object.get_r_g().nodes[node]['marking'], f_m)):
            sucessfull_terminate_state = node
            break
    # red nodes are those from which the final marking is not reachable
    red_nodes = []
    for node in woflan_object.get_r_g().nodes:
        if not nx.has_path(woflan_object.get_r_g(), node,
                           sucessfull_terminate_state):
            red_nodes.append(node)
    # Compute directed spanning tree
    spanning_tree = nx.algorithms.tree.Edmonds(
        woflan_object.get_r_g()).find_optimum()
    queue = set()
    paths = {}
    # root node
    queue.add(0)
    paths[0] = []
    processed_nodes = set()
    red_paths = []
    while len(queue) > 0:
        v = queue.pop()
        for node in spanning_tree.neighbors(v):
            if node not in paths and node not in processed_nodes:
                paths[node] = paths[v].copy()
                # we can use directly 0 here, since we are working on a spanning tree and there should be no more edges to a node
                paths[node].append(woflan_object.get_r_g().get_edge_data(
                    v, node)[0]['transition'])
                if node not in red_nodes:
                    queue.add(node)
                else:
                    red_paths.append(paths[node])
        processed_nodes.add(v)
    return red_paths
Beispiel #3
0
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
Beispiel #4
0
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)
Beispiel #5
0
def compute_unbounded_sequences(woflan_object):
    """
    We compute the sequences which lead to an infinite amount of tokens. To do this, we compute a restricted coverability tree.
    The tree works similar to the graph, despite we consider tree characteristics during the construction.
    :param woflan_object: Woflan object that contains all needed information.
    :return: List of unbounded sequences, each sequence is a list of transitions
    """
    def check_for_markings_larger_than_final_marking(graph, f_m):
        markings = []
        for node in graph.nodes:
            if all(np.greater_equal(graph.nodes[node]['marking'], f_m)):
                markings.append(node)
        return markings

    woflan_object.set_restricted_coverability_tree(
        restricted_coverability_tree(woflan_object.get_net(),
                                     woflan_object.get_initial_marking()))
    f_m = convert_marking(woflan_object.get_net(),
                          woflan_object.get_final_marking())
    infinite_markings = []
    for node in woflan_object.get_restricted_coverability_tree().nodes:
        if np.inf in woflan_object.get_restricted_coverability_tree(
        ).nodes[node]['marking']:
            infinite_markings.append(node)
    larger_markings = check_for_markings_larger_than_final_marking(
        woflan_object.get_restricted_coverability_tree(), f_m)
    green_markings = []
    for node in woflan_object.get_restricted_coverability_tree().nodes:
        add_to_green = True
        for marking in infinite_markings:
            if nx.has_path(woflan_object.get_restricted_coverability_tree(),
                           node, marking):
                add_to_green = False
        for marking in larger_markings:
            if nx.has_path(woflan_object.get_restricted_coverability_tree(),
                           node, marking):
                add_to_green = False
        if add_to_green:
            green_markings.append(node)
    red_markings = []
    for node in woflan_object.get_restricted_coverability_tree().nodes:
        add_to_red = True
        for node_green in green_markings:
            if nx.has_path(woflan_object.get_restricted_coverability_tree(),
                           node, node_green):
                add_to_red = False
                break
        if add_to_red:
            red_markings.append(node)
    # Make the path as short as possible. If we reach a red state, we stop and do not go further in the "red zone".
    queue = set()
    queue.add(0)
    paths = {}
    paths[0] = []
    paths_to_red = []
    while len(queue) > 0:
        v = queue.pop()
        successors = woflan_object.get_restricted_coverability_tree(
        ).successors(v)
        for suc in successors:
            paths[suc] = paths[v].copy()
            paths[suc].append(
                woflan_object.get_restricted_coverability_tree().get_edge_data(
                    v, suc)['transition'])
            if suc in red_markings:
                paths_to_red.append(paths[suc])
            else:
                queue.add(suc)
    return paths_to_red