source: CIVL/mods/dev.civl.com/scripts/state_space.py

main
Last change on this file was c2b37db, checked in by Alex Wilton <awilton@…>, 8 months ago

Merged focus branch into trunk.

git-svn-id: svn://vsl.cis.udel.edu/civl/trunk@5988 fb995dde-84ed-4084-dfe6-e5aef3e2452c

  • Property mode set to 100644
File size: 12.7 KB
Line 
1import itertools
2import dataclasses
3
4class StateSpace:
5 def __init__(self):
6 self.uStateMap = {}
7 self.pStateMap = {}
8 self.outgoingMap = {}
9 self.incomingMap = {}
10 self.transitions = set()
11 self.initStates = set()
12
13 def addTransition(self, transition):
14 if transition in self.transitions:
15 return
16
17 fs = transition.fromState
18 ts = transition.toState
19 self._addState(fs)
20 self._addState(ts)
21 self.incomingMap[ts.uid()].append(transition)
22 self.outgoingMap[fs.uid()].append(transition)
23 self.transitions.add(transition)
24
25 def incoming(self, stateID):
26 return self.incomingMap.get(stateID.unique, None)
27
28 def outgoing(self, stateID):
29 return self.outgoingMap.get(stateID.unique, None)
30
31 def _addState(self, state):
32 self.uStateMap.setdefault(state.uid(), state)
33 self.pStateMap.setdefault(state.pairID(), state)
34 self.incomingMap.setdefault(state.uid(), [])
35 self.outgoingMap.setdefault(state.uid(), [])
36
37 def addInitState(self, state):
38 self._addState(state)
39 self.initStates.add(state)
40
41 def getState(self, stateID):
42 if isinstance(stateID, StateID):
43 stateID = stateID.unique
44 if isinstance(stateID, tuple):
45 assert len(stateID) == 2
46 return self.pStateMap.get(stateID, None)
47 else:
48 assert isinstance(stateID, int)
49 return self.uStateMap.get(stateID, None)
50
51 def __str__(self):
52 outStr = ""
53 seenStates = set()
54
55 def initStateAction(state):
56 nonlocal outStr, seenStates
57 outStr += str(state)
58 seenStates.add(state)
59
60 def transitionAction(trace):
61 nonlocal outStr, seenStates
62 outStr += str(trace.top()) + "\n"
63 if (nextState := trace.top().toState) not in seenStates:
64 outStr += str(nextState)
65
66 self.dfs(self.initStates, transitionAction, initStateAction)
67
68 return outStr
69
70 def printer(self):
71 """
72 Unfinished.
73
74 Meant to provide an object which can be used to interactively move around the state space and print sections of it out.
75 """
76 seenStates = set()
77
78 def initStateAction(startState):
79 nonlocal seenStates
80
81 seenStates.add(startState)
82 print("Init:")
83 print(startState)
84
85 def transitionAction(trace):
86 nonlocal seenStates
87 print(trace.top()+"\n")
88 nextState = trace.top().toState
89 if nextState in seenStates:
90 print("Seen before: "+nextState.headStr)
91 else:
92 seenStates.add(nextState)
93
94 dfsIter = self.dfsIter(self.initStates, transitionAction, initStateAction)
95
96 def dfs(self, startStates, transitionAction=lambda trace:None, initStateAction=lambda startState:None, postTransitionAction=lambda trace:None):
97 dfsIter = self.dfsIter(startStates, transitionAction, initStateAction, postTransitionAction)
98
99 try:
100 return next(dfsIter)
101 except Exception as e:
102 return None
103
104 def dfsIter(self, startStates, transitionAction=lambda trace:None, initStateAction=lambda startState:None, postTransitionAction=lambda trace:None):
105 if not isinstance(startStates, set):
106 startStates = {startStates}
107
108 seenStates = set()
109 for startState in startStates:
110 if startState in seenStates:
111 continue
112
113 actionReturn = initStateAction(startState)
114 if actionReturn:
115 yield actionReturn
116
117 currentTrace = Trace(startState)
118 seenStates.add(startState.stateID())
119
120 openList = []
121 openList.extend(zip(reversed(self.outgoing(startState.stateID())),
122 (False for _ in itertools.count())))
123
124 while len(openList) > 0:
125 transition, inTrace = openList.pop()
126 if inTrace:
127 actionReturn = postTransitionAction(currentTrace)
128 if actionReturn:
129 yield actionReturn
130
131 currentTrace.pop()
132 continue
133
134 nextStateID = transition.toState.stateID()
135
136 openList.append((transition, True))
137 currentTrace.push(transition)
138
139 actionReturn = transitionAction(currentTrace)
140 if actionReturn:
141 yield actionReturn
142
143 if nextStateID not in seenStates:
144 seenStates.add(nextStateID)
145 openList.extend(zip(reversed(self.outgoing(nextStateID)),
146 (False for _ in itertools.count())))
147
148 def maximalTraces(self):
149 lastState = None
150
151 def initStateAction(startState):
152 if len(self.outgoing(startState.stateID())) == 0:
153 return Trace(startState)
154
155 def transitionAction(trace):
156 nonlocal lastState
157 lastState = trace.top().toState()
158
159 def postTransitionAction(trace):
160 nonlocal lastState
161 if trace.top().toState() == lastState:
162 return trace
163
164 return self.dfsIter(self.initStates, transitionAction=transitionAction, postTransitionAction=postTransitionAction)
165
166 """
167
168 """
169 def tracesWithExcessiveVisits(self, threshold):
170 exVisitSpace = StateSpace()
171 thresholdStates = set()
172 locMap = {}
173 locMaxMap = {}
174 currThresholdState = None
175
176 def transitionAction(trace):
177 nonlocal exVisitSpace, locMap, locMaxMap, currThresholdState, thresholdStates
178
179 nextState = trace.top().toState
180 procState = nextState.processState(0)
181 if currThresholdState:
182 exVisitSpace.addTransition(trace.top())
183
184 if len(procState.callStack) > 0:
185 loc = procState.callStack[0].location
186 val = locMap.get(loc, 0) + 1
187 locMap[loc] = val
188 locMaxMap[loc] = max(locMaxMap.get(loc, 0), val)
189
190 if not currThresholdState and val >= threshold:
191 currThresholdState = nextState
192 thresholdStates.add(nextState)
193
194 exVisitSpace.addInitState(trace.initState)
195 for transition in trace.transitions():
196 exVisitSpace.addTransition(transition)
197
198 def initStateAction(state):
199 nonlocal exVisitSpace, locMap, locMaxMap, currThresholdState
200
201 loc = state.processState(0).callStack[0].location
202 locMap[loc] = 1
203 locMaxMap[loc] = max(locMaxMap.get(loc,0), 1)
204
205 def postTransitionAction(trace):
206 nonlocal exVisitSpace, locMap, locMaxMap, currThresholdState
207
208 procState = trace.top().toState.processState(0)
209
210 if len(procState.callStack) == 0:
211 return
212
213 loc = procState.callStack[0].location
214 val = locMap[loc] - 1
215 assert val >= 0
216 locMap[loc] = val
217
218 if trace.top().toState == currThresholdState:
219 currThresholdState = None
220
221 self.dfs(self.initStates, transitionAction, initStateAction, postTransitionAction)
222
223 return exVisitSpace, thresholdStates, locMaxMap
224
225 def reachableFrom(self, startStates):
226 subSpace = StateSpace()
227
228 def initAction(state):
229 nonlocal subSpace
230
231 subSpace.addInitState(state)
232
233 def transitionAction(trace):
234 nonlocal subSpace
235
236 subSpace.addTransition(trace.top())
237
238 self.dfs(startStates, transitionAction, initStateAction=initAction)
239
240 return subSpace
241
242 def reachableTo(self, endStates):
243 if not isinstance(endStates, set):
244 endStates = {endStates}
245 subSpace = StateSpace()
246 for endState in endStates:
247 if subSpace.getState(endState.stateID()):
248 continue
249 openList = []
250 openList.extend(self.incoming(endState.stateID()))
251 if len(openList) == 0:
252 subSpace.addInitState(endState)
253 while len(openList) > 0:
254 prevTrans = openList.pop()
255 if not subSpace.getState(prevTrans.fromState.stateID()):
256 incomingList = self.incoming(prevTrans.fromState.stateID())
257 if len(incomingList) == 0:
258 subSpace.addInitState(prevTrans.fromState)
259 else:
260 openList.extend(incomingList)
261 else:
262 subSpace.addInitState(prevTrans.fromState)
263 subSpace.addTransition(prevTrans)
264
265 subSpace._collapseInitStates()
266 return subSpace
267
268 # Removes any redundant initStates
269 def _collapseInitStates(self):
270 possibleInitStates = self.initStates.copy()
271 for initState in self.initStates:
272 if initState not in possibleInitStates:
273 continue
274 seenStates = {initState}
275 openList = []
276 openList.extend(self.outgoing(initState.stateID()))
277 while len(openList) > 0:
278 nextTrans = openList.pop()
279 if nextTrans.toState not in seenStates:
280 seenStates.add(nextTrans.toState)
281 openList.extend(self.outgoing(nextTrans.toState.stateID()))
282 seenStates.remove(initState)
283 possibleInitStates.difference_update(seenStates)
284
285 self.initStates = possibleInitStates
286
287class Trace:
288 """
289 initState - initial state. Important for when there are no transitions yet.
290 transStack - stack of Transitions
291 stateMap - map from states to the index of the transition in transStack
292 that has the state as its "fromState"
293
294 Currently assume that we don't add to a Trace if it reaches its first cycle.
295 """
296
297 def __init__(self, initState):
298 self.initState = initState
299 self.transStack = []
300 self.stateMap = {}
301
302 def states(self):
303 return self.stateMap.keys()
304
305 def outgoingIndex(self, state):
306 return self.stateMap.get(state)
307
308 def transitions(self):
309 return self.transStack
310
311 def initState(self):
312 return self.initState
313
314 def top(self):
315 return self.transStack[-1]
316
317 def push(self, transition):
318 self.stateMap[transition.fromState] = len(self.transStack)
319 self.transStack.append(transition)
320
321 def pop(self):
322 poppedTransition = self.transStack.pop()
323 del self.stateMap[poppedTransition.fromState]
324
325 return poppedTransition
326
327 def asSubSpace(self):
328 subSpaceView = StateSpace()
329
330 subSpaceView.addInitState(self.initState)
331
332 for trans in self.transStack:
333 subSpaceView.addTransition(trans)
334
335 return subSpaceView
336
337 def __str__(self):
338 return str(self.asSubSpace())
339
340class State:
341 def __init__(self, headStr, contentStr, sid, procStates):
342 self.headStr = headStr
343 self.contentStr = contentStr
344 self.sid = sid
345 self.procStates = procStates
346
347 def stateID(self):
348 return self.sid
349
350 def uid(self):
351 return self.sid.unique
352
353 def pairID(self):
354 return (self.sid.left, self.sid.right)
355
356 def processStates(self):
357 return self.procStates
358
359 def processState(self, i):
360 return self.procStates[i]
361
362 def isTerminated(self):
363 for procState in procStates:
364 if not procState.isTerminated():
365 return False
366 return True
367
368 def __str__(self):
369 return self.headStr + "\n" + self.contentStr + "\n"
370
371@dataclasses.dataclass(frozen=True)
372class StateID:
373 """
374 Class to hold the ID of a state.
375 """
376 left: int
377 right: int
378 unique: int
379
380 def __hash__(self):
381 return hash(self.unique)
382
383class ProcessState:
384 def __init__(self, pid, atomicCount, callStack):
385 self.pid = pid
386 self.atomicCount = atomicCount
387 self.callStack = callStack
388
389 def sameLocation(self, other):
390 return self.callStack[0].location == other.callStack[0].location
391
392 def isTerminated(self):
393 return len(callStack) == 0
394
395@dataclasses.dataclass(frozen=True)
396class FrameEntry:
397 function: str
398 location: int
399 source: str
400 dyscope: int
401
402@dataclasses.dataclass(frozen=True)
403class Transition:
404 contentStr: str
405 pid: int
406 fromState: State
407 toState: State
408
409 def __str__(self):
410 return "Executed by p"+str(self.pid)+" from "+self.fromState.headStr+"\n "+self.contentStr+"--> "+self.toState.headStr+"\n"
Note: See TracBrowser for help on using the repository browser.