Documentation

Complexitylib.Classes.Containments.Internal.SavitchSem

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 #

Main results #

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

    true for is an accepting configuration reachable from u, false for is v reachable from u.

  • ph : Bool

    false while the first half of the interval is being tried.

  • lvl : List Bool

    The level, in unary: 2 ^ lvl.length steps 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.

    • ret : Option Bool

      The value a finished subcall is returning.

    • stk : List Frm

      The stack, top frame first.

    Instances For

      One step #

      The child a frame pushes: the first half of its interval while its phase is zero, the second half afterwards.

      Equations
      • One or more equations did not get rendered due to their size.
      Instances For
        def Complexity.Sav.baseVal (br : List BoolList BoolBool) (ba : List BoolBool) (f : Frm) :

        The value a level-zero frame returns.

        Equations
        Instances For
          def Complexity.Sav.step (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (s : Sst) :

          One step of Savitch's recursion, on the abstract state.

          Equations
          • One or more equations did not get rendered due to their size.
          Instances For
            @[simp]
            theorem Complexity.Sav.step_of_done (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (a : Bool) (r : Option Bool) (stk : List Frm) :
            step br ba z { done := true, ans := a, ret := r, stk := stk } = { done := a, ans := a, ret := r, stk := stk }
            @[simp]
            theorem Complexity.Sav.step_of_empty (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (a : Bool) (r : Option Bool) :
            step br ba z { done := false, ans := a, ret := r, stk := [] } = { done := true, ans := r.getD false, ret := r, stk := [] }
            theorem Complexity.Sav.step_base (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (a : Bool) (f : Frm) (fs : List Frm) (h : f.lvl = []) :
            step br ba z { done := false, ans := a, ret := none, stk := f :: fs } = { done := false, ans := a, ret := some (baseVal br ba f), stk := fs }
            theorem Complexity.Sav.step_push (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (a : Bool) (f : Frm) (fs : List Frm) (h : f.lvl []) :
            step br ba z { done := false, ans := a, ret := none, stk := f :: fs } = { done := false, ans := a, ret := none, stk := child z f :: f :: fs }
            theorem Complexity.Sav.step_ret_true_ph (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (a : Bool) (f : Frm) (fs : List Frm) (h : f.ph = true) :
            step br ba z { done := false, ans := a, ret := some true, stk := f :: fs } = { done := false, ans := a, ret := some true, stk := fs }
            theorem Complexity.Sav.step_ret_true_of_ph_false (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (a : Bool) (f : Frm) (fs : List Frm) (h : f.ph = false) :
            step br ba z { done := false, ans := a, ret := some true, stk := f :: fs } = { done := false, ans := a, ret := none, stk := { kind := f.kind, ph := true, lvl := f.lvl, u := f.u, v := f.v, m := f.m } :: fs }
            theorem Complexity.Sav.step_ret_false_over (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (a : Bool) (f : Frm) (fs : List Frm) (h : bumpOver f.m = true) :
            step br ba z { done := false, ans := a, ret := some false, stk := f :: fs } = { done := false, ans := a, ret := some false, stk := fs }
            theorem Complexity.Sav.step_ret_false_bump (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (a : Bool) (f : Frm) (fs : List Frm) (h : bumpOver f.m = false) :
            step br ba z { done := false, ans := a, ret := some false, stk := f :: fs } = { done := false, ans := a, ret := none, stk := { kind := f.kind, ph := false, lvl := f.lvl, u := f.u, v := f.v, m := bumpBits f.m } :: fs }

            What the recursion computes #

            Try j successive midpoints, starting at m.

            Equations
            Instances For
              @[simp]
              theorem Complexity.Sav.anyMid_zero (P : List BoolBool) (m : List Bool) :
              anyMid P m 0 = false
              @[simp]
              theorem Complexity.Sav.anyMid_succ (P : List BoolBool) (m : List Bool) (j : ) :
              anyMid P m (j + 1) = (P m || anyMid P (bumpBits m) j)
              def Complexity.Sav.rchB (br : List BoolList BoolBool) (z : List Bool) :
              List BoolList BoolBool

              Reachability within 2 ^ n steps, as the recursion computes it.

              Equations
              Instances For
                def Complexity.Sav.accB (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) :
                List BoolBool

                Acceptance within 2 ^ n steps, as the recursion computes it.

                Equations
                Instances For

                  The number of midpoints a frame has still to try.

                  Equations
                  Instances For
                    def Complexity.Sav.frameValAux (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (kind : Bool) (lvl u v m : List Bool) :

                    The value the frame f is going to return, given the state it is in.

                    Equations
                    Instances For
                      def Complexity.Sav.frameVal (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (f : Frm) :

                      The value the frame f is going to return.

                      Equations
                      Instances For
                        theorem Complexity.Sav.frameVal_of_nil (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) {f : Frm} (h : f.lvl = []) :
                        frameVal br ba z f = baseVal br ba f
                        theorem Complexity.Sav.frameVal_of_cons (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) {f : Frm} {b : Bool} {t : List Bool} (h : f.lvl = b :: t) :
                        frameVal br ba z f = anyMid (fun (w : List Bool) => rchB br z t.length f.u w && if f.kind = true then accB br ba z t.length w else rchB br z t.length w f.v) f.m (midRem f.m)
                        theorem Complexity.Sav.frameVal_fresh (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) {f : Frm} (hz : binValLE z = 0) (hm : f.m = z) :
                        frameVal br ba z f = if f.kind = true then accB br ba z f.lvl.length f.u else rchB br z f.lvl.length f.u f.v

                        A fresh frame — phase zero, midpoint at the bottom of the enumeration — returns exactly the value its level asks for.

                        The invariant a frame carries #

                        structure Complexity.Sav.FrmOk (br : List BoolList BoolBool) (z : List Bool) (f : Frm) :

                        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.

                        Instances For

                          The number of steps #

                          The steps a frame at level n costs, over an enumeration of width W.

                          Equations
                          Instances For

                            How much work a frame has left, within its level.

                            Equations
                            Instances For
                              theorem Complexity.Sav.mu_of_ph_true {f : Frm} (h : f.ph = true) :
                              mu f = 2 * midRem f.m
                              theorem Complexity.Sav.mu_of_ph_false {f : Frm} (h : f.ph = false) :
                              mu f = 2 * midRem f.m + 1
                              theorem Complexity.Sav.mu_pos (f : Frm) :
                              0 < mu f
                              theorem Complexity.Sav.mu_le (z : List Bool) {f : Frm} (h : f.m.length = z.length) :
                              mu f 2 * 2 ^ z.length + 1

                              The done flag stays down #

                              def Complexity.Sav.DoneDown (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (s : Sst) (T : ) :

                              The done flag is down at every point of the first T steps from s.

                              Equations
                              Instances For
                                theorem Complexity.Sav.doneDown_zero (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) {s : Sst} (h : s.done = false) :
                                DoneDown br ba z s 0
                                theorem Complexity.Sav.doneDown_add (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) {s : Sst} {T₁ T₂ : } (h₁ : DoneDown br ba z s T₁) (h₂ : DoneDown br ba z ((step br ba z)^[T₁] s) T₂) :
                                DoneDown br ba z s (T₁ + T₂)
                                theorem Complexity.Sav.doneDown_succ (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) {s : Sst} {T : } (h : DoneDown br ba z s T) (h' : ((step br ba z)^[T + 1] s).done = false) :
                                DoneDown br ba z s (T + 1)

                                Unfolding a frame's value #

                                @[simp]
                                theorem Complexity.Sav.child_lvl (z : List Bool) (f : Frm) :
                                (child z f).lvl = List.drop 1 f.lvl
                                @[simp]
                                theorem Complexity.Sav.child_m (z : List Bool) (f : Frm) :
                                (child z f).m = z
                                @[simp]
                                theorem Complexity.Sav.child_ph (z : List Bool) (f : Frm) :
                                (child z f).ph = false
                                theorem Complexity.Sav.frameVal_child_of_ph_false (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (hz : binValLE z = 0) {f : Frm} (h : f.ph = false) :
                                frameVal br ba z (child z f) = rchB br z (f.lvl.length - 1) f.u f.m
                                theorem Complexity.Sav.frameVal_child_of_ph_true (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (hz : binValLE z = 0) {f : Frm} (h : f.ph = true) :
                                frameVal br ba z (child z f) = if f.kind = true then accB br ba z (f.lvl.length - 1) f.m else rchB br z (f.lvl.length - 1) f.m f.v
                                def Complexity.Sav.midVal (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (n : ) (f : Frm) :

                                The candidate a frame is currently testing.

                                Equations
                                Instances For
                                  theorem Complexity.Sav.frameVal_of_last (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) {f : Frm} {b : Bool} {t : List Bool} (hl : f.lvl = b :: t) (hov : bumpOver f.m = true) :
                                  frameVal br ba z f = midVal br ba z t.length f
                                  theorem Complexity.Sav.frameVal_of_bump (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) {f : Frm} {b : Bool} {t : List Bool} (hl : f.lvl = b :: t) (hov : bumpOver f.m = false) :
                                  frameVal br ba z f = (midVal br ba z t.length f || frameVal br ba z { kind := f.kind, ph := false, lvl := f.lvl, u := f.u, v := f.v, m := bumpBits f.m })
                                  theorem Complexity.Sav.frameVal_of_mid (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) {f : Frm} {b : Bool} {t : List Bool} (hl : f.lvl = b :: t) (h : midVal br ba z t.length f = true) :
                                  frameVal br ba z f = true

                                  Runs #

                                  def Complexity.Sav.RunsTo (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (f : Frm) (fs : List Frm) (a : Bool) (T : ) :

                                  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
                                    theorem Complexity.Sav.runsTo_prepend (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) {f g : Frm} {fs : List Frm} {a : Bool} {T₀ T₁ : } (hd : DoneDown br ba z { done := false, ans := a, ret := none, stk := f :: fs } T₀) (hs : (step br ba z)^[T₀] { done := false, ans := a, ret := none, stk := f :: fs } = { done := false, ans := a, ret := none, stk := g :: fs }) (hr : RunsTo br ba z g fs a T₁) (hval : frameVal br ba z g = frameVal br ba z f) :
                                    RunsTo br ba z f fs a (T₁ + T₀)

                                    A run that begins by re-entering a frame of the same value.

                                    theorem Complexity.Sav.child_phase (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) {f : Frm} {fs : List Frm} {a : Bool} {Tc : } {s : Sst} (hne : f.lvl []) (hc : RunsTo br ba z (child z f) (f :: fs) a Tc) (hstep : step br ba z { done := false, ans := a, ret := some (frameVal br ba z (child z f)), stk := f :: fs } = s) (hsdone : s.done = false) :
                                    DoneDown br ba z { done := false, ans := a, ret := none, stk := f :: fs } (Tc + 1 + 1) (step br ba z)^[Tc + 1 + 1] { done := false, ans := a, ret := none, stk := f :: fs } = s

                                    Pushing a frame's child, running it, and processing its return.

                                    Every pushed frame comes back #

                                    theorem Complexity.Sav.run_frame (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (hz : binValLE z = 0) (n : ) (f : Frm) :
                                    f.lvl.length = nFrmOk br z f∀ (fs : List Frm) (a : Bool), TrunBound z.length n, RunsTo br ba z f fs a T

                                    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 #

                                    theorem Complexity.Sav.anyMid_of_lt {P : List BoolBool} {m : List Bool} {i j : } (hij : i < j) (h : P (bumpBits^[i] m) = true) :
                                    anyMid P m j = true
                                    theorem Complexity.Sav.exists_of_anyMid {P : List BoolBool} {m : List Bool} {j : } (h : anyMid P m j = true) :
                                    i < j, P (bumpBits^[i] m) = true
                                    theorem Complexity.Sav.anyMid_of_length {P : List BoolBool} {z w : List Bool} (hz : binValLE z = 0) (hw : w.length = z.length) (h : P w = true) :
                                    anyMid P z (2 ^ z.length) = true

                                    Every string of the enumeration's width is tried.

                                    The stack invariant the encoding needs #

                                    Nothing is returning only while the stack is nonempty.

                                    Equations
                                    Instances For
                                      theorem Complexity.Sav.step_stkOk (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) {s : Sst} (h : StkOk s) :
                                      StkOk (step br ba z s)

                                      The number of steps is at most exponential in a polynomial #

                                      theorem Complexity.Sav.runBound_le (W n : ) :
                                      runBound W n + 2 2 ^ ((W + 3) * n + 2)

                                      The whole run #

                                      theorem Complexity.Sav.run_top (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) (hz : binValLE z = 0) (root : Frm) (hok : FrmOk br z root) :
                                      TrunBound z.length root.lvl.length, (∀ jT, ((step br ba z)^[j] { done := false, ans := false, ret := none, stk := [root] }).done = false) (step br ba z)^[T + 1] { done := false, ans := false, ret := none, stk := [root] } = { done := true, ans := frameVal br ba z root, ret := some (frameVal br ba z root), stk := [] } (step br ba z)^[T + 2] { done := false, ans := false, ret := none, stk := [root] } = { done := frameVal br ba z root, ans := frameVal br ba z root, ret := some (frameVal br ba z root), stk := [] }

                                      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 three codes a frame carries fit in Wm bits.

                                      Equations
                                      Instances For
                                        def Complexity.Sav.StkSize (Lmax Wm : ) :

                                        The stack descends exactly one level per frame — which is what bounds its depth — and every frame's codes fit.

                                        Equations
                                        Instances For
                                          theorem Complexity.Sav.StkSize.length_le {Lmax Wm : } {stk : List Frm} :
                                          StkSize Lmax Wm stkstk.length Lmax + 1
                                          theorem Complexity.Sav.StkSize.mem_bound {Lmax Wm : } {stk : List Frm} :
                                          StkSize Lmax Wm stkfstk, f.lvl.length Lmax FrmSize Wm f
                                          theorem Complexity.Sav.step_stkSize (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) {Lmax Wm : } {s : Sst} (hzw : z.length Wm) (h : StkSize Lmax Wm s.stk) :
                                          StkSize Lmax Wm (step br ba z s).stk
                                          theorem Complexity.Sav.iterate_stkSize (br : List BoolList BoolBool) (ba : List BoolBool) (z : List Bool) {Lmax Wm : } (hzw : z.length Wm) (j : ) (s : Sst) :
                                          StkSize Lmax Wm s.stkStkSize Lmax Wm ((step br ba z)^[j] s).stk