Documentation

Complexitylib.Models.TuringMachine.Internal

TM–NTM embedding: proof internals #

Helper lemmas for TM.toNTM_accepts_iff, showing that the DTM step function and the NTM trace on toNTM compute the same thing.

theorem Complexity.TM.toNTM_trace_reaches {n : } (tm : TM n) (c : Cfg n tm.Q) (T : ) (choices : Fin TBool) :
tm.reaches c (tm.toNTM.trace T choices c)
theorem Complexity.TM.toNTM_trace_choice_irrel {n : } (tm : TM n) (T : ) (c : Cfg n tm.Q) (ch₁ ch₂ : Fin TBool) :
tm.toNTM.trace T ch₁ c = tm.toNTM.trace T ch₂ c

For toNTM, the trace is independent of the choice sequence since both transition functions are identical.

theorem Complexity.TM.toNTM_trace_of_reachesIn {n : } (tm : TM n) {c c' : Cfg n tm.Q} {t T : } (h : tm.reachesIn t c c') (hhalt : tm.halted c') (hle : t T) (ch : Fin TBool) :
tm.toNTM.trace T ch c = c'

If a DTM halts within t ≤ T steps, then toNTM.trace T reaches the same halted configuration regardless of choices.

theorem Complexity.TM.toNTM_accepts_iff {n : } (tm : TM n) (x : List Bool) :

The DTM and its NTM embedding agree on acceptance.

theorem Complexity.TM.toNTM_decidesInTime {n : } (tm : TM n) {L : Language} {f : } (h : tm.DecidesInTime L f) :

If a DTM decides L in time f, then its NTM embedding also decides L in time f. This is the key internal lemma for DTIME ⊆ NTIME.

theorem Complexity.TM.work_head_reachesIn_bound {n : } (tm : TM n) {c c' : Cfg n tm.Q} {t : } (h : tm.reachesIn t c c') (i : Fin n) :
(c'.work i).head (c.work i).head + t

After t steps, each work tape head is at most t plus its initial value.

theorem Complexity.TM.input_head_reachesIn_bound {n : } (tm : TM n) {c c' : Cfg n tm.Q} {t : } (h : tm.reachesIn t c c') :

After t steps, the input head is at most t plus its initial value.

theorem Complexity.TM.output_head_reachesIn_bound {n : } (tm : TM n) {c c' : Cfg n tm.Q} {t : } (h : tm.reachesIn t c c') :

After t steps, the output head is at most t plus its initial value.

theorem Complexity.TM.reachesIn_right_unique {n : } {tm : TM n} {t : } {c c' c'' : Cfg n tm.Q} (h₁ : tm.reachesIn t c c') (h₂ : tm.reachesIn t c c'') :
c' = c''

Deterministic runs have unique endpoints: reaching two configurations in the same number of steps forces them to coincide.

theorem Complexity.TM.reaches_to_reachesIn {n : } (tm : TM n) {c c' : Cfg n tm.Q} (h : tm.reaches c c') :
∃ (t : ), tm.reachesIn t c c'

Convert reaches to reachesIn.

theorem Complexity.TM.reachesIn_le_halt {n : } (tm : TM n) {c c' c_halt : Cfg n tm.Q} {t t_halt : } (hr : tm.reachesIn t c c') (hh : tm.reachesIn t_halt c c_halt) (hhalt : tm.halted c_halt) :
t t_halt

If a DTM halts at step t_halt, then any reachesIn t has t ≤ t_halt.

theorem Complexity.TM.initCfg_work_head_zero {n : } (tm : TM n) (x : List Bool) (i : Fin n) :
((tm.initCfg x).work i).head = 0

Initial work tape heads are all at position 0.

theorem Complexity.TM.initCfg_input_head_zero {n : } (tm : TM n) (x : List Bool) :
(tm.initCfg x).input.head = 0

The initial input head is at position zero.

theorem Complexity.TM.initCfg_output_head_zero {n : } (tm : TM n) (x : List Bool) :
(tm.initCfg x).output.head = 0

The initial output head is at position zero.

If a DTM is a transducer, so is its NTM embedding.

theorem Complexity.TM.toNTM_decidesInSpace {n : } (tm : TM n) {L : Language} {f : } (h : tm.DecidesInSpace L f) :

If a DTM decides L in space f, then its NTM embedding also decides L in space f. The uniform time bound is constructed as the maximum halting time over all inputs of each length.

theorem Complexity.TM.output_cells_zero_eq_start_of_reachesIn {n : } {tm : TM n} {t : } {c₀ c : Cfg n tm.Q} (h : tm.reachesIn t c₀ c) (h0 : c₀.output.cells 0 = Γ.start) :
theorem Complexity.TM.output_cells_ne_start_of_reachesIn {n : } {tm : TM n} {t : } {c₀ c : Cfg n tm.Q} (h : tm.reachesIn t c₀ c) (hno : i1, c₀.output.cells i Γ.start) (i : ) :
theorem Complexity.TM.input_cells_eq_of_step {n : } {tm : TM n} {c c' : Cfg n tm.Q} (hs : tm.step c = some c') :
theorem Complexity.TM.input_cells_eq_of_reachesIn {n : } {tm : TM n} {t : } {c₀ c : Cfg n tm.Q} (h : tm.reachesIn t c₀ c) :
theorem Complexity.TM.head_le_of_reachesIn {n : } {x : List Bool} (tm : TM n) {t : } {c : Cfg n tm.Q} (hreach : tm.reachesIn t (tm.initCfg x) c) :
c.input.head t c.output.head t ∀ (i : Fin n), (c.work i).head t

A tape head moves at most 1 cell per step. After t steps starting from initCfg, the head is at position ≤ t.

theorem Complexity.Tape.StartInvariant.step {n : } (tm : TM n) {c c' : Cfg n tm.Q} (hstep : tm.step c = some c') (hinp : c.input.StartInvariant) (hwork : ∀ (i : Fin n), (c.work i).StartInvariant) (hout : c.output.StartInvariant) :

The invariant is preserved across one DTM step, on every tape.

theorem Complexity.NTM.input_cells_trace {n : } (tm : NTM n) (T : ) (choices : Fin TBool) (c : Cfg n tm.Q) :
(tm.trace T choices c).input.cells = c.input.cells

NTM traces never alter input tape cells; the input tape is read-only and only its head moves.

theorem Complexity.NTM.input_head_trace_le {n : } (tm : NTM n) (T : ) (choices : Fin TBool) (c : Cfg n tm.Q) :
(tm.trace T choices c).input.head c.input.head + T

During an NTM trace, the input head increases by at most one per step.

theorem Complexity.NTM.trace_two {n : } (tm : NTM n) (choices : Fin 2Bool) (c : Cfg n tm.Q) :
tm.trace 2 choices c = tm.trace 1 (fun (x : Fin 1) => choices 1, ) (tm.trace 1 (fun (x : Fin 1) => choices 0, ) c)

Split a two-step trace into two one-step traces.

theorem Complexity.NTM.trace_succ {n : } (tm : NTM n) (T : ) (choices : Fin (T + 1)Bool) (c : Cfg n tm.Q) :
tm.trace (T + 1) choices c = tm.trace T (fun (i : Fin T) => choices i + 1, ) (tm.trace 1 (fun (x : Fin 1) => choices 0, ) c)

Split the first step off a nonzero trace. If the machine is already halted, both sides reduce to the starting configuration.

theorem Complexity.NTM.trace_add_two {n : } (tm : NTM n) (T : ) (choices : Fin (T + 2)Bool) (c : Cfg n tm.Q) :
tm.trace (T + 2) choices c = tm.trace T (fun (i : Fin T) => choices i + 2, ) (tm.trace 2 (fun (i : Fin 2) => choices i, ) c)

Split the first two steps off a trace.

theorem Complexity.NTM.trace_cast {n : } (tm : NTM n) {T T' : } (h : T = T') (choices : Fin TBool) (c : Cfg n tm.Q) :
tm.trace T choices c = tm.trace T' (fun (i : Fin T') => choices (Fin.cast i)) c

Reindex a trace along an equality of time bounds.

theorem Complexity.NTM.trace_add {n : } (tm : NTM n) (T U : ) (choices : Fin (T + U)Bool) (c : Cfg n tm.Q) :
tm.trace (T + U) choices c = tm.trace U (fun (i : Fin U) => choices (Fin.natAdd T i)) (tm.trace T (fun (i : Fin T) => choices (Fin.castLE i)) c)

Split the first T steps off a trace.

This version uses Fin.castLE/Fin.natAdd for the prefix and suffix choice sequences, which keeps later proofs away from ad-hoc dependent index casts.