The abstract semantics of Savitch's stack machine #
⚠️ Unreviewed by Bolton
Complexitylib.Classes.Containments.Internal.SavitchStep writes Savitch's
recursion as a polynomial-time function on a bitstring. This file gives the same
recursion on an ordinary inductive state — a List of frames rather than a
right-nested chain of pairs — where its correctness and its running time can be
proved by induction without any encoding in the way.
The machine is parametric in the two base tests it calls, br (is v within one
step of u) and ba (is u accepting, or within one step of accepting), and in
the all-zero code z, which is both the first midpoint every frame tries and the
width the enumeration wraps at.
Main definitions #
Complexity.Sav.Frm,Complexity.Sav.Sst— a frame and the machine's stateComplexity.Sav.step— one step of the recursionComplexity.Sav.rchB,Complexity.Sav.accB— what the recursion computesComplexity.Sav.frameVal— the value a frame is going to returnComplexity.Sav.runBound— the number of steps a level costs
Main results #
Complexity.Sav.run_frame— a frame pushed on the stack is popped again, with its value, withinrunBoundsteps, and the done flag stays down throughout
The state #
A frame of Savitch's recursion: which subproblem it is working on (kind),
which half of the interval it is trying (ph), the level in unary (lvl), the
two endpoints (u, v) and the midpoint being tried (m).
- kind : Bool
- ph : Bool
falsewhile the first half of the interval is being tried. The level, in unary:
2 ^ lvl.lengthsteps are allowed.The source endpoint.
The target endpoint; unused by an acceptance frame.
The midpoint being tried, which doubles as the enumeration's counter.
Instances For
The machine's state: the done flag, the answer, the value a finished subcall
is returning (none while descending), and the stack.
- done : Bool
The bit the space-bounded iteration watches.
- ans : Bool
The answer, once it is known.
The value a finished subcall is returning.
The stack, top frame first.
Instances For
One step #
What the recursion computes #
Try j successive midpoints, starting at m.
Equations
- Complexity.Sav.anyMid P x✝ 0 = false
- Complexity.Sav.anyMid P x✝ j.succ = (P x✝ || Complexity.Sav.anyMid P (Complexity.bumpBits x✝) j)
Instances For
Reachability within 2 ^ n steps, as the recursion computes it.
Equations
- Complexity.Sav.rchB br z 0 x✝¹ x✝ = br x✝¹ x✝
- Complexity.Sav.rchB br z n.succ x✝¹ x✝ = Complexity.Sav.anyMid (fun (m : List Bool) => Complexity.Sav.rchB br z n x✝¹ m && Complexity.Sav.rchB br z n m x✝) z (2 ^ z.length)
Instances For
Acceptance within 2 ^ n steps, as the recursion computes it.
Equations
- Complexity.Sav.accB br ba z 0 x✝ = ba x✝
- Complexity.Sav.accB br ba z n.succ x✝ = Complexity.Sav.anyMid (fun (m : List Bool) => Complexity.Sav.rchB br z n x✝ m && Complexity.Sav.accB br ba z n m) z (2 ^ z.length)
Instances For
The number of midpoints a frame has still to try.
Equations
- Complexity.Sav.midRem m = 2 ^ m.length - Complexity.binValLE m
Instances For
The invariant a frame carries #
A frame is well formed when its midpoint has the enumeration's width and, once its phase has advanced, its first half really did succeed.
The midpoint is a code.
The first half succeeded before the phase advanced.
Instances For
The number of steps #
The steps a frame at level n costs, over an enumeration of width W.
Equations
- Complexity.Sav.runBound W 0 = 1
- Complexity.Sav.runBound W n.succ = (2 * 2 ^ W + 1) * (Complexity.Sav.runBound W n + 2)
Instances For
The done flag stays down #
The done flag is down at every point of the first T steps from s.
Equations
- Complexity.Sav.DoneDown br ba z s T = ∀ j ≤ T, ((Complexity.Sav.step br ba z)^[j] s).done = false
Instances For
Unfolding a frame's value #
The candidate a frame is currently testing.
Equations
- Complexity.Sav.midVal br ba z n f = (Complexity.Sav.rchB br z n f.u f.m && if f.kind = true then Complexity.Sav.accB br ba z n f.m else Complexity.Sav.rchB br z n f.m f.v)
Instances For
Runs #
The frame f, pushed on top of fs, is popped again with its value after
exactly T steps, and the done flag stays down throughout.
Equations
- One or more equations did not get rendered due to their size.
Instances For
A run that begins by re-entering a frame of the same value.
Pushing a frame's child, running it, and processing its return.
Every pushed frame comes back #
Every pushed frame comes back. A frame at level n is popped again,
carrying its value, within runBound steps, and the done flag stays down for
the whole of that run.
The enumeration covers every candidate #
The stack invariant the encoding needs #
The number of steps is at most exponential in a polynomial #
The whole run #
The recursion terminates with its answer. From the state carrying one
frame, the machine keeps its done flag down for T steps, raises it on the next
one, and one step later the flag is the answer.
The size of the state #
The stack descends exactly one level per frame — which is what bounds its depth — and every frame's codes fit.
Equations
- Complexity.Sav.StkSize Lmax Wm [] = True
- Complexity.Sav.StkSize Lmax Wm (f :: fs) = (fs.length + f.lvl.length = Lmax ∧ Complexity.Sav.FrmSize Wm f ∧ Complexity.Sav.StkSize Lmax Wm fs)