3131# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3232
3333class TraceState :
34- def __init__ (self , n_scenarios ):
35- self ._c_pool = [False ] * n_scenarios # coverage pool: True means scenario is in trace
34+ def __init__ (self , scenario_indexes ):
35+ if len (scenario_indexes ) != len (set (scenario_indexes )):
36+ raise ValueError ("Scenarios must be uniquely identifiable" )
37+ self .c_pool = {index : 0 for index in scenario_indexes }
3638 self ._tried = [[]] # Keeps track of the scenarios already tried at each step in the trace
37- self ._trace = [] # Choice trace, when was which scenario inserted (e.g. ['1', '2.1', '3', '2.0'])
3839 self ._snapshots = [] # Keeps details for elements in trace
3940 self ._open_refinements = []
4041
4142 @property
4243 def model (self ):
4344 """returns the model as it is at the end of the current trace"""
44- return self ._snapshots [- 1 ].model if self ._trace else None
45+ return self ._snapshots [- 1 ].model if self ._snapshots else None
4546
4647 @property
4748 def tried (self ):
4849 """returns the indices that were rejected or previously inserted at the current position"""
4950 return tuple (self ._tried [- 1 ])
5051
5152 def coverage_reached (self ):
52- return all (self ._c_pool )
53+ return all (self .c_pool . values () )
5354
5455 @property
5556 def coverage_drought (self ):
5657 """Number of scenarios since last new coverage"""
5758 return self ._snapshots [- 1 ].coverage_drought if self ._snapshots else 0
5859
60+ @property
61+ def id_trace (self ):
62+ return [snap .id for snap in self ._snapshots ]
63+
5964 def get_trace (self ):
6065 return [snap .scenario for snap in self ._snapshots ]
6166
6267 def next_candidate (self , retry = False ):
63- for i in range ( len ( self ._c_pool )) :
68+ for i in self .c_pool :
6469 if i not in self ._tried [- 1 ] and not self ._is_refinement_active (i ) and self .count (i ) == 0 :
6570 return i
6671 if not retry :
6772 return None
68- for i in range ( len ( self ._c_pool )) :
73+ for i in self .c_pool :
6974 if i not in self ._tried [- 1 ] and not self ._is_refinement_active (i ):
7075 return i
7176 return None
7277
7378 def count (self , index ):
7479 """Count the number of times the index is present in the trace.
7580 unfinished partial scenarios are excluded."""
76- return self ._trace . count ( str ( index )) + self . _trace . count ( str ( f" { index } .0" ))
81+ return self .c_pool [ index ]
7782
7883 def highest_part (self , index ):
7984 """Given the current trace and an index, returns the highest part number of an ongoing
8085 refinement for the related scenario. Returns 0 when there is no refinement active."""
81- for i in range (1 , len (self ._trace )+ 1 ):
82- if self ._trace [- i ] == f'{ index } ' :
86+ for i in range (1 , len (self .id_trace )+ 1 ):
87+ if self .id_trace [- i ] == f'{ index } ' :
8388 return 0
84- if self ._trace [- i ].startswith (f'{ index } .' ):
85- return int (self ._trace [- i ].split ('.' )[1 ])
89+ if self .id_trace [- i ].startswith (f'{ index } .' ):
90+ return int (self .id_trace [- i ].split ('.' )[1 ])
8691 return 0
8792
8893 def _is_refinement_active (self , index ):
@@ -91,7 +96,7 @@ def _is_refinement_active(self, index):
9196 def find_scenarios_with_active_refinement (self ):
9297 scenarios = []
9398 for i in self ._open_refinements :
94- index = - self ._trace [::- 1 ].index (f'{ i } .1' )- 1
99+ index = - self .id_trace [::- 1 ].index (f'{ i } .1' )- 1
95100 scenarios .append (self ._snapshots [index ].scenario )
96101 return scenarios
97102
@@ -100,19 +105,15 @@ def reject_scenario(self, i_scenario):
100105 self ._tried [- 1 ].append (i_scenario )
101106
102107 def confirm_full_scenario (self , index , scenario , model ):
103- if not self ._c_pool [index ]:
104- self ._c_pool [index ] = True
105- c_drought = 0
106- else :
107- c_drought = self .coverage_drought + 1
108+ c_drought = 0 if self .c_pool [index ] == 0 else self .coverage_drought + 1
109+ self .c_pool [index ] += 1
108110 if self ._is_refinement_active (index ):
109111 id = f"{ index } .0"
110112 self ._open_refinements .pop ()
111113 else :
112114 id = str (index )
113115 self ._tried [- 1 ].append (index )
114116 self ._tried .append ([])
115- self ._trace .append (id )
116117 self ._snapshots .append (TraceSnapShot (id , scenario , model , c_drought ))
117118
118119 def push_partial_scenario (self , index , scenario , model ):
@@ -123,29 +124,28 @@ def push_partial_scenario(self, index, scenario, model):
123124 self ._tried [- 1 ].append (index )
124125 self ._tried .append ([])
125126 self ._open_refinements .append (index )
126- self ._trace .append (id )
127127 self ._snapshots .append (TraceSnapShot (id , scenario , model , self .coverage_drought ))
128128
129129 def can_rewind (self ):
130- return len (self ._trace ) > 0
130+ return len (self ._snapshots ) > 0
131131
132132 def rewind (self ):
133- id = self ._trace . pop ()
133+ id = self ._snapshots [ - 1 ]. id
134134 index = int (id .split ('.' )[0 ])
135+ self ._snapshots .pop ()
135136 if id .endswith ('.0' ):
136- self ._snapshots . pop ()
137+ self .c_pool [ index ] -= 1
137138 self ._open_refinements .append (index )
138- while self ._trace [- 1 ] != f"{ index } .1" :
139+ while self ._snapshots [- 1 ]. id != f"{ index } .1" :
139140 self .rewind ()
140141 return self .rewind ()
141142
142- self . _snapshots . pop ()
143- if '.' not in id or id . endswith ( '.1' ):
144- if self .count ( index ) == 0 :
145- self . _c_pool [ index ] = False
143+ if '.' not in id :
144+ self . _tried . pop ()
145+ self .c_pool [ index ] -= 1
146+ if id . endswith ( '.1' ):
146147 self ._tried .pop ()
147- if id .endswith ('.1' ):
148- self ._open_refinements .pop ()
148+ self ._open_refinements .pop ()
149149 return self ._snapshots [- 1 ] if self ._snapshots else None
150150
151151 def __iter__ (self ):
0 commit comments