33from pm4py .objects .petri_net .utils import petri_utils
44import copy
55import numpy as np
6+ from collections import deque
67
78# Importing for place invariants related stuff (s-components, uniform and
89# weighted place invariants)
@@ -573,11 +574,12 @@ def step_7(woflan_object, return_asap_when_unsound=False):
573574 woflan_object .get_net (),
574575 )
575576 )
576- if len (check_for_improper_conditions (woflan_object .get_mcg ())) == 0 :
577+ improper = check_for_improper_conditions (woflan_object .get_mcg ())
578+ if len (improper ) == 0 :
577579 woflan_object .diagnostic_messages .append ("No improper coditions." )
578580 if woflan_object .print_diagnostics :
579581 print ("No improper conditions." )
580- if woflan_object .get_left :
582+ if woflan_object .get_left () :
581583 return step_8 (
582584 woflan_object ,
583585 return_asap_when_unsound = return_asap_when_unsound ,
@@ -588,16 +590,9 @@ def step_7(woflan_object, return_asap_when_unsound=False):
588590 return_asap_when_unsound = return_asap_when_unsound ,
589591 )
590592 else :
591- woflan_object .diagnostic_messages .append (
592- "Improper WPD. The following are the improper conditions: {}." .format (
593- check_for_improper_conditions (
594- woflan_object .get_mcg ())))
593+ woflan_object .diagnostic_messages .append ("Improper WPD. The following are the improper conditions: {}." .format (improper ))
595594 if woflan_object .print_diagnostics :
596- print (
597- "Improper WPD. The following are the improper conditions: {}." .format (
598- check_for_improper_conditions (woflan_object .get_mcg ())
599- )
600- )
595+ print ("Improper WPD. The following are the improper conditions: {}." .format (improper ))
601596 if return_asap_when_unsound :
602597 return False
603598 return step_9 (
@@ -606,14 +601,9 @@ def step_7(woflan_object, return_asap_when_unsound=False):
606601
607602
608603def step_8 (woflan_object , return_asap_when_unsound = False ):
609- if check_for_substates (woflan_object .get_mcg ()):
610- return step_10 (
611- woflan_object , return_asap_when_unsound = return_asap_when_unsound
612- )
613- else :
614- return step_10 (
615- woflan_object , return_asap_when_unsound = return_asap_when_unsound
616- )
604+ return step_10 (
605+ woflan_object , return_asap_when_unsound = return_asap_when_unsound
606+ )
617607
618608
619609def step_9 (woflan_object , return_asap_when_unsound = False ):
@@ -792,39 +782,34 @@ def compute_non_live_sequences(woflan_object):
792782 if all (np .equal (woflan_object .get_r_g ().nodes [node ]["marking" ], f_m )):
793783 sucessfull_terminate_state = node
794784 break
795- # red nodes are those from which the final marking is not reachable
796- red_nodes = []
797- for node in woflan_object .get_r_g ().nodes :
798- if not nx_utils .has_path (
799- woflan_object .get_r_g (), node , sucessfull_terminate_state
800- ):
801- red_nodes .append (node )
802- # Compute directed spanning tree
803- spanning_tree = nx_utils .Edmonds (woflan_object .get_r_g ()).find_optimum ()
804- queue = set ()
805- paths = {}
806- # root node
807- queue .add (0 )
808- paths [0 ] = []
809- processed_nodes = set ()
785+ if sucessfull_terminate_state is None :
786+ return []
787+ reversed_graph = woflan_object .get_r_g ().reverse (copy = False )
788+ can_reach_final = nx_utils .descendants (
789+ reversed_graph , sucessfull_terminate_state
790+ )
791+ can_reach_final .add (sucessfull_terminate_state )
792+ red_nodes = set (woflan_object .get_r_g ().nodes ) - can_reach_final
793+
794+ queue = deque ()
795+ queue .append ((0 , []))
796+ shortest_path_len = {0 : 0 }
810797 red_paths = []
811- while len (queue ) > 0 :
812- v = queue .pop ()
813- for node in spanning_tree .neighbors (v ):
814- if node not in paths and node not in processed_nodes :
815- paths [node ] = paths [v ].copy ()
816- # we can use directly 0 here, since we are working on a
817- # spanning tree and there should be no more edges to a node
818- paths [node ].append (
819- woflan_object .get_r_g ().get_edge_data (v , node )[0 ][
820- "transition"
821- ]
822- )
823- if node not in red_nodes :
824- queue .add (node )
825- else :
826- red_paths .append (paths [node ])
827- processed_nodes .add (v )
798+ while queue :
799+ node , path = queue .popleft ()
800+ if node in red_nodes :
801+ red_paths .append (path )
802+ continue
803+ for successor in woflan_object .get_r_g ().successors (node ):
804+ edge_data = woflan_object .get_r_g ().get_edge_data (node , successor )
805+ if not edge_data :
806+ continue
807+ transition = next (iter (edge_data .values ()))["transition" ]
808+ next_path = path + [transition ]
809+ if successor in shortest_path_len and len (next_path ) >= shortest_path_len [successor ]:
810+ continue
811+ shortest_path_len [successor ] = len (next_path )
812+ queue .append ((successor , next_path ))
828813 return red_paths
829814
830815
@@ -848,70 +833,53 @@ def check_for_markings_larger_than_final_marking(graph, f_m):
848833 woflan_object .get_net (), woflan_object .get_initial_marking ()
849834 )
850835 )
836+ tree = woflan_object .get_restricted_coverability_tree ()
851837 f_m = convert_marking (
852838 woflan_object .get_net (), woflan_object .get_final_marking ()
853839 )
854- infinite_markings = []
855- for node in woflan_object .get_restricted_coverability_tree ().nodes :
856- if (
857- np .inf
858- in woflan_object .get_restricted_coverability_tree ().nodes [node ][
859- "marking"
860- ]
861- ):
862- infinite_markings .append (node )
863- larger_markings = check_for_markings_larger_than_final_marking (
864- woflan_object .get_restricted_coverability_tree (), f_m
840+ infinite_markings = {
841+ node
842+ for node , data in tree .nodes (data = True )
843+ if np .inf in data ["marking" ]
844+ }
845+ larger_markings = set (
846+ check_for_markings_larger_than_final_marking (tree , f_m )
865847 )
866- green_markings = []
867- for node in woflan_object .get_restricted_coverability_tree ().nodes :
868- add_to_green = True
869- for marking in infinite_markings :
870- if nx_utils .has_path (
871- woflan_object .get_restricted_coverability_tree (), node , marking
872- ):
873- add_to_green = False
874- for marking in larger_markings :
875- if nx_utils .has_path (
876- woflan_object .get_restricted_coverability_tree (), node , marking
877- ):
878- add_to_green = False
879- if add_to_green :
880- green_markings .append (node )
881- red_markings = []
882- for node in woflan_object .get_restricted_coverability_tree ().nodes :
883- add_to_red = True
884- for node_green in green_markings :
885- if nx_utils .has_path (
886- woflan_object .get_restricted_coverability_tree (),
887- node ,
888- node_green ,
889- ):
890- add_to_red = False
891- break
892- if add_to_red :
893- red_markings .append (node )
848+ bad_nodes = infinite_markings | larger_markings
849+
850+ postorder_nodes = list (nx_utils .topological_sort (tree ))[::- 1 ]
851+ subtree_has_bad = {}
852+ subtree_has_green = {}
853+ green_markings = set ()
854+
855+ for node in postorder_nodes :
856+ children = list (tree .successors (node ))
857+ has_bad = node in bad_nodes or any (subtree_has_bad .get (child , False ) for child in children )
858+ is_green = not has_bad
859+ if is_green :
860+ green_markings .add (node )
861+ subtree_has_bad [node ] = has_bad
862+ subtree_has_green [node ] = is_green or any (
863+ subtree_has_green .get (child , False ) for child in children
864+ )
865+
866+ red_markings = {
867+ node for node in tree .nodes if not subtree_has_green .get (node , False )
868+ }
894869 # Make the path as short as possible. If we reach a red state, we stop and
895870 # do not go further in the "red zone".
896- queue = set ()
897- queue .add (0 )
898- paths = {}
899- paths [0 ] = []
871+ queue = deque ()
872+ queue .append ((0 , []))
900873 paths_to_red = []
901- while len (queue ) > 0 :
902- v = queue .pop ()
903- successors = (
904- woflan_object .get_restricted_coverability_tree ().successors (v )
905- )
874+ while queue :
875+ v , path = queue .popleft ()
876+ if v in red_markings :
877+ paths_to_red .append (path )
878+ continue
879+ successors = tree .successors (v )
906880 for suc in successors :
907- paths [suc ] = paths [v ].copy ()
908- paths [suc ].append (
909- woflan_object .get_restricted_coverability_tree ().get_edge_data (
910- v , suc
911- )["transition" ]
912- )
913- if suc in red_markings :
914- paths_to_red .append (paths [suc ])
915- else :
916- queue .add (suc )
881+ edge_data = tree .get_edge_data (v , suc )
882+ transition = edge_data ["transition" ] if edge_data else None
883+ next_path = path + [transition ]
884+ queue .append ((suc , next_path ))
917885 return paths_to_red
0 commit comments