@@ -30,9 +30,72 @@ def violatedInvariantNames {ρ σ : Type}
3030 if !p.holdsOn th st then some p.name else none
3131
3232
33- /-- Inner loop of a single random trace: walk from `currSt` for up to
34- `stepsLeft` steps, picking a random enabled transition at each step.
35- Returns `(violation?, updatedRng, stepsTaken)`. -/
33+ /-- Lightweight scan loop: walk without building a trace.
34+ Returns `(violated?, updatedRng, stepsTaken)`. -/
35+ @ [inline, specialize]
36+ partial def scanOnceLoop {ρ σ κ : Type } {th₀ : ρ}
37+ (sys : EnumerableTransitionSystem ρ (List ρ) σ (List σ) Int κ (List (κ × ExecutionOutcome Int σ)) th₀)
38+ (params : SearchParameters ρ σ)
39+ (th : ρ)
40+ (stepsLeft : Nat)
41+ (currSt : σ)
42+ (gen : StdGen)
43+ [Inhabited (κ × σ)]
44+ : Bool × StdGen × Nat :=
45+ match stepsLeft with
46+ | 0 => (false , gen, 0 )
47+ | stepsLeft + 1 =>
48+ let outcomes := sys.tr th currSt
49+ let assertionFailureFound := outcomes.any fun (_, outcome) =>
50+ match outcome with
51+ | .assertionFailure _ _ => true
52+ | _ => false
53+ if assertionFailureFound then
54+ (true , gen, 1 )
55+ else
56+ let nexts := Concrete.extractSuccessfulTransitions outcomes
57+ if nexts.isEmpty then
58+ if !params.terminating.holdsOn th currSt then
59+ (true , gen, 0 ) -- deadlock
60+ else
61+ (false , gen, 0 )
62+ else
63+ let (idx, gen) := randNat gen 0 (nexts.length - 1 )
64+ let (_, nextSt) := nexts[idx]!
65+ let violations := violatedInvariantNames params th nextSt
66+ if !violations.isEmpty then
67+ (true , gen, 1 )
68+ else
69+ let (violated, gen, innerSteps) := scanOnceLoop sys params th stepsLeft nextSt gen
70+ (violated, gen, innerSteps + 1 )
71+
72+
73+ /-- Lightweight scan: pick random init state, walk without trace.
74+ Returns `(violated?, updatedRng, stepsTaken)`. -/
75+ @ [inline, specialize]
76+ partial def scanOnce {ρ σ κ : Type } {th₀ : ρ}
77+ (sys : EnumerableTransitionSystem ρ (List ρ) σ (List σ) Int κ (List (κ × ExecutionOutcome Int σ)) th₀)
78+ (params : SearchParameters ρ σ)
79+ (th : ρ)
80+ (gen : StdGen)
81+ (maxSteps : Nat)
82+ [Inhabited σ]
83+ [Inhabited (κ × σ)]
84+ : Bool × StdGen × Nat :=
85+ if sys.initStates.isEmpty then
86+ (false , gen, 0 )
87+ else
88+ let (idx, gen) := randNat gen 0 (sys.initStates.length - 1 )
89+ let initSt := sys.initStates[idx]!
90+ let initViolations := violatedInvariantNames params th initSt
91+ if !initViolations.isEmpty then
92+ (true , gen, 0 )
93+ else
94+ scanOnceLoop sys params th maxSteps initSt gen
95+
96+
97+ /-- Full trace loop: walk and record every step for counterexample.
98+ Returns `(violation?, updatedRng, stepsTaken)`. Used only for replay. -/
3699@ [inline, specialize]
37100partial def simulateOnceLoop {ρ σ κ : Type } {th₀ : ρ}
38101 (sys : EnumerableTransitionSystem ρ (List ρ) σ (List σ) Int κ (List (κ × ExecutionOutcome Int σ)) th₀)
@@ -48,7 +111,6 @@ partial def simulateOnceLoop {ρ σ κ : Type} {th₀ : ρ}
48111 | 0 => (none, gen, 0 )
49112 | stepsLeft + 1 =>
50113 let outcomes := sys.tr th currSt
51- -- Check assertion failures first (highest priority)
52114 let assertionFailures := outcomes.filterMap fun (_, outcome) =>
53115 match outcome with
54116 | .assertionFailure exId _ => some exId
@@ -67,7 +129,6 @@ partial def simulateOnceLoop {ρ σ κ : Type} {th₀ : ρ}
67129 let nexts := Concrete.extractSuccessfulTransitions outcomes
68130 if nexts.isEmpty then
69131 if !params.terminating.holdsOn th currSt then
70- -- No enabled transitions and not a terminating state: deadlock
71132 (some (.foundViolation () .deadlock (some trace)), gen, trace.steps.size)
72133 else
73134 (none, gen, trace.steps.size)
@@ -83,8 +144,7 @@ partial def simulateOnceLoop {ρ σ κ : Type} {th₀ : ρ}
83144 simulateOnceLoop sys params th stepsLeft nextSt trace gen
84145
85146
86- /-- Run a single random trace from a randomly chosen initial state.
87- Returns `(violation?, updatedRng, stepsTaken)`. -/
147+ /-- Full trace run from random init state. Used only for replay. -/
88148@ [inline, specialize]
89149partial def simulateOnce {ρ σ κ : Type } {th₀ : ρ}
90150 (sys : EnumerableTransitionSystem ρ (List ρ) σ (List σ) Int κ (List (κ × ExecutionOutcome Int σ)) th₀)
@@ -109,8 +169,8 @@ partial def simulateOnce {ρ σ κ : Type} {th₀ : ρ}
109169
110170
111171/-- Run `maxTraces` independent random traces, stopping on first violation.
112- Each trace uses an independent seed derived from `(masterSeed + traceIndex)`
113- for maximum prefix diversity across traces . -/
172+ Scans without trace recording for speed; replays only the violating trace.
173+ Each trace uses an independent seed derived from `(masterSeed + traceIndex)` . -/
114174@ [inline, specialize]
115175partial def simulate {ρ σ κ : Type } {th₀ : ρ}
116176 (sys : EnumerableTransitionSystem ρ (List ρ) σ (List σ) Int κ (List (κ × ExecutionOutcome Int σ)) th₀)
@@ -127,21 +187,28 @@ partial def simulate {ρ σ κ : Type} {th₀ : ρ}
127187 let mut i := 0
128188 let mut totalSteps := 0
129189 while i < cfg.maxTraces do
130- let traceGen := mkStdGen (actualSeed + i)
131- let (maybeResult, _, stepsUsed) := simulateOnce sys params th traceGen cfg.maxSteps
190+ let traceSeed := actualSeed + i
191+ -- Fast scan: no trace allocation
192+ let (violated, _, stepsUsed) := scanOnce sys params th (mkStdGen traceSeed) cfg.maxSteps
132193 totalSteps := totalSteps + stepsUsed
133- match maybeResult with
134- | some result =>
135- let elapsedMs := (← IO.monoMsNow) - startMs
136- return {
137- result := result
138- tracesRun := i + 1
139- elapsedMs := elapsedMs
140- seed := actualSeed
141- depth := stepsUsed
142- totalSteps := totalSteps
143- }
144- | none =>
194+ if violated then
195+ -- Replay with same seed to build counterexample trace
196+ let (maybeResult, _, _) := simulateOnce sys params th (mkStdGen traceSeed) cfg.maxSteps
197+ match maybeResult with
198+ | some result =>
199+ let elapsedMs := (← IO.monoMsNow) - startMs
200+ return {
201+ result := result
202+ tracesRun := i + 1
203+ elapsedMs := elapsedMs
204+ seed := actualSeed
205+ depth := stepsUsed
206+ totalSteps := totalSteps
207+ }
208+ | none =>
209+ -- Scan flagged violation but replay didn't reproduce (should not happen)
210+ i := i + 1
211+ else
145212 i := i + 1
146213 let elapsedMs := (← IO.monoMsNow) - startMs
147214 return {
0 commit comments