File size: 5,618 Bytes
e60e568
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
'''
    This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de).

    PM4Py is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    PM4Py is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with PM4Py.  If not, see <https://www.gnu.org/licenses/>.
'''
import numpy as np
from pm4py.util import nx_utils


def compute_incidence_matrix(net):
    """
    Given a Petri Net, the incidence matrix is computed. An incidence matrix has n rows (places) and m columns
    (transitions).
    :param net: Petri Net object
    :return: Incidence matrix
    """
    n = len(net.transitions)
    m = len(net.places)
    C = np.zeros((m, n))
    i = 0
    transition_list = sorted(list(net.transitions), key=lambda x: x.name)
    place_list = sorted(list(net.places), key=lambda x: x.name)
    while i < n:
        t = transition_list[i]
        for in_arc in t.in_arcs:
            # arcs that go to transition
            C[place_list.index(in_arc.source), i] -= (1*in_arc.weight)
        for out_arc in t.out_arcs:
            # arcs that lead away from transition
            C[place_list.index(out_arc.target), i] += (1*out_arc.weight)
        i += 1
    return C


def split_incidence_matrix(matrix, net):
    """
    We split the incidence matrix columnwise to get the firing information for each transition
    :param matrix: incidence matrix
    :param net: Petri Net
    :return: Dictionary, whereby the key is an np array that contains the firing information and the value is the name
    of the transition
    """
    transition_dict = {}
    lst_transitions = sorted(list(net.transitions), key=lambda x: x.name)
    i = 0
    while i < len(net.transitions):
        transition_dict[lst_transitions[i]] = np.hsplit(np.transpose(matrix), 1)[0][i]
        i += 1
    return transition_dict

def compute_firing_requirement(net):
    place_list=sorted(list(net.places), key=lambda x: x.name)
    transition_dict={}
    for transition in net.transitions:
        temp_array=np.zeros(len(place_list))
        for arc in transition.in_arcs:
            temp_array[place_list.index(arc.source)] -=1*arc.weight
        transition_dict[transition]=temp_array
    return transition_dict

def enabled_markings(firing_dict, req_dict,marking):
    enabled_transitions = []
    for transition, requirment in req_dict.items():
        if all(np.greater_equal(marking, requirment.copy()*-1)):
            enabled_transitions.append(transition)
    new_markings = []
    for transition in enabled_transitions:
        new_marking = marking + firing_dict[transition]
        new_markings.append((new_marking, transition))
    return new_markings

def convert_marking(net, marking, original_net=None):
    """
    Takes an marking as input and converts it into an Numpy Array
    :param net: PM4Py Petri Net object
    :param marking: Marking that should be converted
    :param original_net: PM4Py Petri Net object without short-circuited transition
    :return: Numpy array representation
    """
    #marking_list=list(el.name for el in marking.keys())
    #
    marking_list = sorted([el.name for el in marking.keys()])
    place_list = sorted(list(el.name for el in net.places))

    mark = np.zeros(len(place_list))
    for index, value in enumerate(mark):
        if place_list[index] in marking_list:
            #TODO: Is setting the value to 1 ok in this case?
            mark[index]=1
    return mark

def check_for_dead_tasks(net, graph):
    """
    We compute a list of dead tasks. A dead task is a task which does not appear in the Minimal Coverability Graph
    :param net: Petri Net representation of PM4Py
    :param graph: Minimal coverability graph. NetworkX MultiDiGraph object.
    :return: list of dead tasks
    """
    tasks=[]
    lst_transitions = sorted(list(net.transitions), key=lambda x: x.name)
    for transition in lst_transitions:
        if transition.label != None:
            tasks.append(transition)
    for node,targets in graph.edges()._adjdict.items():
        for target_node,activties in targets.items():
            for option,activity in activties.items():
                if activity['transition'] in tasks:
                    tasks.remove(activity['transition'])
    return tasks

def check_for_improper_conditions(mcg):
    """
    An improper condition is a state in the minimum-coverability graph with an possible infinite amount of tokens
    :param mcg: networkx object (minimal coverability graph)
    :return: True, if there are no improper conditions; false otherwise
    """
    improper_states=[]
    for node in mcg.nodes:
        if np.inf in mcg.nodes[node]['marking']:
            improper_states.append(node)
    return improper_states

def check_for_substates(mcg):
    """
    Checks if a substate exists in a given mcg
    :param mcg: Minimal coverability graph (networkx object)
    :return: True, if there exist no substate; False otherwise
    """
    for node in mcg.nodes:
        reachable_states = nx_utils.descendants(mcg, node)
        for state in reachable_states:
            if all(np.less(mcg.nodes[node]['marking'],mcg.nodes[state]['marking'])):
                return False
    return True