Skip to content

Commit c48a6ee

Browse files
committed
TLCMC: non-strict BFS, BFS level tracking, state constraints,
and refinement to MCReachability. Parameterize TLCMC with constant K (number of workers): dequeue any of the first Min(K, Len(S)) elements from the frontier instead of always Head(S), modeling TLC's degraded BFS with multiple workers. Track BFS level of each explored state in new variable L (distance from an initial state). Add Constraint(s, l) predicate to prune successors from exploration based on state and level. Inline Min from CommunityModules to keep the spec/example self-contained. Introduce MCReachability, a high-level spec with set-based frontier S and non-deterministic exploration order. TLCMC refines MCReachability via a refinement mapping. Extract graph definitions into StateGraphs module; refactor TestGraphs and add TestMCReachability as test harnesses. Select graph and worker count via IOEnv to run all test configurations from a single .cfg file without per-graph TLC invocations. [Feature] Signed-off-by: Markus Alexander Kuppe <github.com@lemmster.de>
1 parent dc6470a commit c48a6ee

8 files changed

Lines changed: 460 additions & 138 deletions

File tree

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
---------------------------- MODULE MCReachability ----------------------------
2+
EXTENDS Naturals, Sequences, TLC
3+
(***************************************************************************)
4+
(* High-level specification of a state-graph explorer that does NOT *)
5+
(* enforce BFS ordering. The frontier S is a set and the next state *)
6+
(* to explore is chosen non-deterministically from S. *)
7+
(* *)
8+
(* The working phase (init / explore / process-successors) is determined *)
9+
(* entirely by the data variables: *)
10+
(* *)
11+
(* init phase: successors = {} /\ S \ C # {} *)
12+
(* explore phase: successors = {} /\ S \subseteq C *)
13+
(* each phase: successors # {} *)
14+
(* *)
15+
(* S \subseteq C is an invariant of the post-init phases (Each adds to *)
16+
(* both S and C when Constraint holds; Explore only removes from S), so *)
17+
(* S \ C # {} vs S \subseteq C cleanly separates init from exploration. *)
18+
(* *)
19+
(* The only "control" variable is the boolean 'done', which records *)
20+
(* forced termination (violation or deadlock). Normal completion *)
21+
(* (frontier exhausted) needs no flag: S = {} /\ successors = {} simply *)
22+
(* disables every action. *)
23+
(***************************************************************************)
24+
25+
(***************************************************************************)
26+
(* A (state) graph G is a directed graph represented by a record with *)
27+
(* 'states', 'initials', and 'actions' components. *)
28+
(* *)
29+
(* The CommunityModules Graphs.tla module is not used here because its *)
30+
(* representation [node, edge] differs from ours: we use an adjacency *)
31+
(* function (actions \in [states -> SUBSET states]) rather than an *)
32+
(* explicit edge set (edge \subseteq node \X node), and carry a *)
33+
(* distinguished 'initials' component that Graphs.tla does not model. *)
34+
(***************************************************************************)
35+
IsGraph(G) == /\ {"states", "initials", "actions"} = DOMAIN G
36+
/\ G.actions \in [G.states -> SUBSET G.states]
37+
/\ G.initials \subseteq G.states
38+
39+
(***************************************************************************)
40+
(* Successor states of s, excluding self-loops. *)
41+
(***************************************************************************)
42+
SuccessorsOf(s, SG) == {t \in SG.actions[s] : t # s}
43+
44+
(***************************************************************************)
45+
(* The predecessor of v in a spanning tree t (a set of *)
46+
(* <<predecessor, successor>> pairs) is the first element of the *)
47+
(* unique pair whose second element equals v. *)
48+
(***************************************************************************)
49+
Predecessor(t, v) == (CHOOSE pair \in t : pair[2] = v)[1]
50+
51+
CONSTANT StateGraph, ViolationStates,
52+
Constraint(_, _) \* Constraint(s,l) iff state s may be explored at BFS level l;
53+
\* successors with FALSE are not added to S or C (but are
54+
\* still recorded in T and in L).
55+
56+
ASSUME /\ IsGraph(StateGraph)
57+
/\ ViolationStates \subseteq StateGraph.states
58+
/\ \A s \in StateGraph.states, l \in Nat : Constraint(s, l) \in BOOLEAN
59+
60+
VARIABLES S, \* Frontier: set of states yet to explore
61+
C, \* Set of visited states
62+
successors, \* Subset of Nat \X StateGraph.states: pairs <<lvl,t>> still to
63+
\* process for the current Explore step (same lvl for all).
64+
done, \* TRUE after violation or deadlock detected
65+
T, \* Set of <<predecessor, successor>> pairs (spanning tree)
66+
counterexample, \* Path from initial state to violation/deadlock state
67+
L \* BFS level of each state that has been assigned a level
68+
69+
vars == <<S, C, successors, done, T, counterexample, L>>
70+
71+
Init == /\ S = StateGraph.initials
72+
/\ C = {}
73+
/\ successors = {}
74+
/\ done = FALSE
75+
/\ T = {}
76+
/\ counterexample = <<>>
77+
/\ L = [s \in {} |-> 0]
78+
79+
---------------------------------------------------------------------------
80+
81+
(***************************************************************************)
82+
(* Check an initial state for a safety violation. *)
83+
(***************************************************************************)
84+
InitCheck ==
85+
/\ ~done
86+
/\ successors = {}
87+
/\ \E s \in S \ C :
88+
/\ C' = C \cup {s}
89+
/\ L' = (s :> 0) @@ L
90+
/\ done' = (s \in ViolationStates)
91+
/\ counterexample' = IF s \in ViolationStates
92+
THEN <<s>> ELSE counterexample
93+
/\ UNCHANGED <<S, successors, T>>
94+
95+
---------------------------------------------------------------------------
96+
97+
(***************************************************************************)
98+
(* Pick any state from the frontier S for exploration. The next state *)
99+
(* is chosen non-deterministically (\E s \in S), so no ordering is *)
100+
(* imposed on the exploration. The predecessor pairs for all new *)
101+
(* successors are added to T here. *)
102+
(***************************************************************************)
103+
Explore ==
104+
/\ ~done
105+
/\ successors = {}
106+
/\ S \subseteq C
107+
/\ S # {}
108+
/\ \E s \in S :
109+
LET succs == SuccessorsOf(s, StateGraph) \ C
110+
lvl == L[s] + 1
111+
IN /\ S' = S \ {s}
112+
/\ successors' = {<<lvl, t>> : t \in succs}
113+
/\ T' = T \cup {<<s, t>> : t \in succs}
114+
/\ done' = (SuccessorsOf(s, StateGraph) = {})
115+
/\ counterexample' = IF SuccessorsOf(s, StateGraph) = {}
116+
THEN <<s>> ELSE counterexample
117+
/\ UNCHANGED <<C, L>>
118+
119+
---------------------------------------------------------------------------
120+
121+
(***************************************************************************)
122+
(* Process one successor: mark visited, add to frontier, check violation. *)
123+
(***************************************************************************)
124+
Each ==
125+
/\ ~done
126+
/\ successors # {}
127+
/\ \E succ \in successors :
128+
/\ successors' = successors \ {succ}
129+
/\ IF Constraint(succ[2], succ[1])
130+
THEN /\ C' = C \cup {succ[2]}
131+
/\ S' = S \cup {succ[2]}
132+
ELSE UNCHANGED <<C, S>>
133+
/\ L' = (succ[2] :> succ[1]) @@ L
134+
/\ done' = (succ[2] \in ViolationStates)
135+
/\ counterexample' = IF succ[2] \in ViolationStates
136+
THEN <<succ[2]>> ELSE counterexample
137+
/\ UNCHANGED T
138+
139+
---------------------------------------------------------------------------
140+
141+
(***************************************************************************)
142+
(* Trace reconstruction: walk backward through T, prepending *)
143+
(* predecessors to the counterexample until an initial state is reached. *)
144+
(***************************************************************************)
145+
Trc ==
146+
/\ done
147+
/\ counterexample # <<>>
148+
/\ Head(counterexample) \notin StateGraph.initials
149+
/\ counterexample' = <<Predecessor(T, Head(counterexample))>>
150+
\o counterexample
151+
/\ UNCHANGED <<S, C, successors, done, T, L>>
152+
153+
---------------------------------------------------------------------------
154+
155+
Terminating == done /\ UNCHANGED vars
156+
157+
Next ==
158+
\/ InitCheck
159+
\/ Explore
160+
\/ Each
161+
\/ Trc
162+
\/ Terminating
163+
164+
Spec ==
165+
/\ Init /\ [][Next]_vars
166+
/\ WF_vars(Next)
167+
168+
Termination == <>(done \/ (S = {} /\ successors = {}))
169+
170+
(***************************************************************************)
171+
(* If violation states exist, the explorer eventually finds one and *)
172+
(* constructs a valid counterexample path from an initial state to it. *)
173+
(***************************************************************************)
174+
Live == ViolationStates # {} =>
175+
<>[](/\ counterexample # <<>>
176+
/\ counterexample[Len(counterexample)] \in ViolationStates
177+
/\ counterexample[1] \in StateGraph.initials)
178+
179+
=============================================================================

specifications/TLC/StateGraphs.tla

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
---------------------------- MODULE StateGraphs ----------------------------
2+
EXTENDS Sequences, Integers, IOUtils
3+
(***************************************************************************)
4+
(* Definitions of directed state graphs and their violation sets, used *)
5+
(* for testing model-checker specifications (TLCMC, MCReachability). *)
6+
(***************************************************************************)
7+
8+
\* Graph 1.
9+
G1 == [states |-> 1..4,
10+
initials |-> {1,2},
11+
actions |-> << {1,2}, {1,3}, {4}, {3} >>
12+
]
13+
V1 == {4}
14+
15+
\* Graph 1a.
16+
G1a == [states |-> 1..4,
17+
initials |-> {3},
18+
actions |-> << {1, 2}, {1, 3}, {4}, {3} >>]
19+
\* Graph-wise it's impossible to reach state 1 from state 3
20+
V1a == {1}
21+
22+
\* Graph 2. This graph is actually a forest of two graphs
23+
\* {1,2} /\ {3,4,5}. {1,2} are an SCC.
24+
G2 == [states |-> 1..5,
25+
initials |-> {1,3},
26+
actions |-> << {1, 2}, {1}, {4, 5}, {}, {} >> ]
27+
V2 == {2,5}
28+
29+
\* Graph 3.
30+
G3 == [states |-> 1..4,
31+
initials |-> {2},
32+
actions |-> << {1,2}, {2,3}, {3,4}, {1,4} >> ]
33+
V3 == {1}
34+
35+
\* Graph 4.
36+
G4 == [states |-> 1..9,
37+
initials |-> {1,2,3},
38+
actions |-> << {3}, {4}, {5}, {6}, {7}, {7}, {8, 9}, {}, {4} >> ]
39+
V4 == {8}
40+
41+
\* Graph 5.
42+
G5 == [states |-> 1..9,
43+
initials |-> {9},
44+
actions |-> << {4,2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {2} >> ]
45+
V5 == {8}
46+
47+
\* Graph 6.
48+
G6 == [states |-> 1..5,
49+
initials |-> {5},
50+
actions |-> << {2,4,5}, {2}, {1}, {4}, {3} >> ]
51+
V6 == {2}
52+
53+
\* Graph Medium (node 22 is a sink)
54+
G7 == [states |-> 1..50,
55+
initials |-> {1},
56+
actions |-> << {8,35},{15,46,22,23,50},{20,26,34},{5,18,28,37,43},{18,28},{44},{14,29},{42,45},{20,49},{10,12,31,47},
57+
{1,8,29,30,35,42},{22,31},{10,12,22,27},{23,24,48},{9,22,49},{9,35,50},{10},{21,25,39},{7,29,33,43},{16,41},{},
58+
{4,36,39,47},{7},{12,22,23},{5,6,39,44},{3,35},{10,13,17},{6,25,33,32,43},{23,30,40,45},{23,50},{24,38},
59+
{19,33},{6,7,14,38,48},{3,9,20},{3,20,41},{10,38,47},{21,43},{6,10,36,48},{36,38,39},{19,43},{16},
60+
{29,45},{32},{38,39},{40},{9,15,16,50},{17},{24,31},{13,22,34},{35,23,50} >> ]
61+
V7 == {50}
62+
63+
\* Graph 8.
64+
G8 == [states |-> 1..4,
65+
initials |-> {1},
66+
actions |-> <<{1,2},{2,3},{3,4},{4}>>]
67+
V8 == {}
68+
69+
\* Graph 9.
70+
G9 == [states |-> {1},
71+
initials |-> {1},
72+
actions |-> <<{1}>>]
73+
V9 == {1}
74+
75+
\* Graph 10.
76+
G10 == [states |-> {},
77+
initials |-> {},
78+
actions |-> <<>>]
79+
V10 == {}
80+
81+
\* Graph 11.
82+
G11 == [states |-> 1..10,
83+
initials |-> {},
84+
actions |-> << {}, {}, {}, {}, {}, {}, {}, {}, {}, {} >>]
85+
V11 == {}
86+
87+
\* Graph 12.
88+
G12 == [states |-> 1..3,
89+
initials |-> {1,2,3},
90+
actions |-> <<{},{},{}>>]
91+
V12 == {1}
92+
93+
\* Graph 13 (simple sequence).
94+
G13 == [states |-> 1..3,
95+
initials |-> {1},
96+
actions |-> <<{2},{3},{}>>]
97+
V13 == {}
98+
99+
-----------------------------------------------------------------------------
100+
101+
GraphName == IF "GRAPH" \in DOMAIN IOEnv THEN IOEnv.GRAPH ELSE "7"
102+
103+
TestCase == CASE GraphName = "1" -> [g |-> G1, v |-> V1]
104+
[] GraphName = "1a" -> [g |-> G1a, v |-> V1a]
105+
[] GraphName = "2" -> [g |-> G2, v |-> V2]
106+
[] GraphName = "3" -> [g |-> G3, v |-> V3]
107+
[] GraphName = "4" -> [g |-> G4, v |-> V4]
108+
[] GraphName = "5" -> [g |-> G5, v |-> V5]
109+
[] GraphName = "6" -> [g |-> G6, v |-> V6]
110+
[] GraphName = "7" -> [g |-> G7, v |-> V7]
111+
[] GraphName = "8" -> [g |-> G8, v |-> V8]
112+
[] GraphName = "9" -> [g |-> G9, v |-> V9]
113+
[] GraphName = "10" -> [g |-> G10, v |-> V10]
114+
[] GraphName = "11" -> [g |-> G11, v |-> V11]
115+
[] GraphName = "12" -> [g |-> G12, v |-> V12]
116+
[] GraphName = "13" -> [g |-> G13, v |-> V13]
117+
118+
=============================================================================

0 commit comments

Comments
 (0)