Documentation

Complexitylib.Classes.P.Cobham.Internal.Simulate

Running a machine inside the algebra — proof internals #

Everything the completeness direction needs about a run, as opposed to a single step: a total step function that stands still once the machine has halted, the standing invariants of a run (the left-end marker where it belongs, every head inside the encoded window), and the iterated versions of Cobham.stepFn and Cobham.rewindFn.

Main results #

A total run #

def Complexity.TM.runCfg {k : } (tm : TM k) (c : Cfg k tm.Q) :
Cfg k tm.Q

The configuration after n steps, standing still once halted.

Equations
Instances For
    @[simp]
    theorem Complexity.TM.runCfg_zero {k : } (tm : TM k) (c : Cfg k tm.Q) :
    tm.runCfg c 0 = c
    theorem Complexity.TM.runCfg_succ {k : } (tm : TM k) (c : Cfg k tm.Q) (n : ) :
    tm.runCfg c (n + 1) = (tm.step (tm.runCfg c n)).getD (tm.runCfg c n)
    theorem Complexity.TM.runCfg_add {k : } (tm : TM k) (c : Cfg k tm.Q) (a b : ) :
    tm.runCfg c (a + b) = tm.runCfg (tm.runCfg c a) b
    theorem Complexity.TM.runCfg_of_halted {k : } (tm : TM k) {c : Cfg k tm.Q} (h : c.state = tm.qhalt) (n : ) :
    tm.runCfg c n = c

    Once halted, the run stands still.

    theorem Complexity.TM.runCfg_of_reachesIn {k : } (tm : TM k) {c c' : Cfg k tm.Q} {t : } (h : tm.reachesIn t c c') :
    tm.runCfg c t = c'

    A run of exactly t steps is the t-th iterate.

    The standing invariants of a run #

    theorem Complexity.TM.step_startInvariant {k : } (tm : TM k) {c c' : Cfg k tm.Q} (h : tm.step c = some c') (hin : c.input.StartInvariant) (hwork : ∀ (i : Fin k), (c.work i).StartInvariant) (hout : c.output.StartInvariant) :

    One step preserves the left-end marker's position on every tape.

    theorem Complexity.TM.step_head_le {k : } (tm : TM k) {c c' : Cfg k tm.Q} (h : tm.step c = some c') :
    c'.input.head c.input.head + 1 (∀ (i : Fin k), (c'.work i).head (c.work i).head + 1) c'.output.head c.output.head + 1

    One step moves every head by at most one cell.

    theorem Complexity.TM.runCfg_startInvariant {k : } (tm : TM k) (x : List Bool) (n : ) :
    (tm.runCfg (tm.initCfg x) n).input.StartInvariant (∀ (i : Fin k), ((tm.runCfg (tm.initCfg x) n).work i).StartInvariant) (tm.runCfg (tm.initCfg x) n).output.StartInvariant

    Every tape of a run keeps its left-end marker.

    theorem Complexity.TM.runCfg_head_le {k : } (tm : TM k) (x : List Bool) (n : ) :
    (tm.runCfg (tm.initCfg x) n).input.head n (∀ (i : Fin k), ((tm.runCfg (tm.initCfg x) n).work i).head n) (tm.runCfg (tm.initCfg x) n).output.head n

    After n steps every head is within n cells of the start.

    theorem Complexity.Cobham.cfgTapes_runCfg_inv {k : } (tm : TM k) (x : List Bool) (n W : ) (hn : n W) :
    (∀ tcfgTapes (tm.runCfg (tm.initCfg x) n), t.StartInvariant) tcfgTapes (tm.runCfg (tm.initCfg x) n), t.head W

    The invariants of a run, in the form the encoding lemmas want.

    theorem Complexity.Cobham.iterate_stepFn {k : } (tm : TM k) (W : ) (x : List Bool) (hq : Fintype.card tm.Q blockWidth W) (n : ) :
    n W(stepFn tm (blockRuler W))^[n] (cfgCode W (tm.initCfg x)) = cfgCode W (tm.runCfg (tm.initCfg x) n)

    The encoded iteration tracks the run.

    The rewind iteration #

    theorem Complexity.Cobham.iterate_rewindFn {W : } (t : Tape) (hinv : t.StartInvariant) (hW : t.head W) (n : ) :
    (rewindFn (blockRuler W))^[n] (pairCode W t) = pairCode W ((fun (s : Tape) => s.move Dir3.left)^[n] t)

    The rewind iteration walks the head left.

    theorem Complexity.Cobham.rewound (t : Tape) {n : } (h : t.head n) :
    (fun (s : Tape) => s.move Dir3.left)^[n] t = { head := 0, cells := t.cells }

    After enough rewinding the head is at cell 0.

    Reading the output off the rewound tape #

    With the head at cell 0 the tape's right half-block is the whole window, in order and two bits per cell. The first bit of each cell says whether it holds data — symCode is arranged so that only 0 and 1 have it set — and the second is the bit itself. So the output is the second bits, truncated where the first bits stop: Complexity.cellBits twice and one Complexity.runTrue.

    @[simp]
    theorem Complexity.Cobham.bitOf_cellsCode (t : Tape) {w j : } (hj : j < w) {o : } (ho : o < 2) :
    bitOf (cellsCode t 0 w) (2 * j + o) = bitOf (symCode (t.cells j)) o

    Reading one bit of an aligned window is reading one bit of a cell's code.

    theorem Complexity.Cobham.drop_pairCode_rewound (W : ) (t : Tape) :
    List.drop (blockRuler W).length (pairCode W { head := 0, cells := t.cells }) = cellsCode t 0 (W + 1)

    The aligned window of a rewound tape is its right half-block.

    theorem Complexity.Cobham.output_of_cellsCode {W : } (t : Tape) (y : List Bool) (hy : t.HasOutput y) (hyW : y.length + 1 W) :
    List.take (runTrue (cellBits 2 (cellsCode t 0 (W + 1)) W) W).length (cellBits 3 (cellsCode t 0 (W + 1)) W) = y

    Reading the output off an aligned window.

    The whole simulation #

    Everything above, wired together: a clock long enough to run the machine to a halt and to rewind the output head, a first iteration that runs the machine, a second that rewinds, and the extraction.

    theorem Complexity.Cobham.initFn_length {k : } (tm : TM k) (R x : List Bool) :
    (initFn tm R x).length = (2 * (k + 2) + 1) * R.length

    The three stages, as functions of the clock #

    The clock string u fixes the encoded window: the ruler is 2|u| bits wide, so the window is W = |u| - 1 cells and u.tail is a ruler of exactly W bits.

    The block ruler belonging to a clock value.

    Equations
    Instances For
      theorem Complexity.Cobham.clockRulerFn {n : } {gu : (Fin nList Bool)List Bool} (hu : Cobham gu) :
      Cobham fun (w : Fin nList Bool) => clockRuler (gu w)
      noncomputable def Complexity.Cobham.runFn {k : } (tm : TM k) (u x : List Bool) :

      Stage one: the encoding after running the machine to a halt.

      Equations
      Instances For
        noncomputable def Complexity.Cobham.outPairFn {k : } (tm : TM k) (u x : List Bool) :

        Stage two: the output tape's two half-blocks, head rewound to cell 0.

        Equations
        • One or more equations did not get rendered due to their size.
        Instances For
          noncomputable def Complexity.Cobham.simFn {k : } (tm : TM k) (u x : List Bool) :

          Stage three: the string on the rewound output tape.

          Equations
          • One or more equations did not get rendered due to their size.
          Instances For
            theorem Complexity.Cobham.iterate_stepFn_length_le {k : } (tm : TM k) (R x : List Bool) (n : ) :
            ((stepFn tm R)^[n] (initFn tm R x)).length (2 * (k + 2) + 1) * R.length

            The simulated run never leaves its blocks.

            The rewind never leaves its two blocks.

            theorem Complexity.Cobham.simFn_mem {k : } (tm : TM k) {gu : (Fin 1List Bool)List Bool} (hu : Cobham gu) :
            Cobham fun (v : Fin 1List Bool) => simFn tm (gu v) (v 0)

            The whole simulation is in the algebra.

            theorem Complexity.Cobham.simFn_eq {k : } (tm : TM k) {T : } {f : List BoolList Bool} (hcomp : tm.ComputesInTime f T) (u x : List Bool) (hlen : T x.length + x.length + Fintype.card tm.Q + 2 u.length) :
            simFn tm u x = f x

            The simulation computes the machine's function. Provided the clock outlasts the run, covers the input and is wide enough for the state code, the three stages reproduce exactly the string the machine leaves on its output tape.

            theorem Complexity.Cobham.computes_mem_CobhamFP {k : } (tm : TM k) {T : } {S D : } (hSD : ∀ (n : ), T n S * (n + 1) ^ D) {f : List BoolList Bool} (hcomp : tm.ComputesInTime f T) :

            The completeness direction, for one machine.