Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cslib.lean
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ public import Cslib.Computability.Languages.OmegaRegularLanguage
public import Cslib.Computability.Languages.RegularLanguage
public import Cslib.Computability.Languages.SafetyLiveness
public import Cslib.Computability.Machines.Turing.MultiTape.Deterministic
public import Cslib.Computability.Machines.Turing.MultiTape.Plumbing.Basic
public import Cslib.Computability.Machines.Turing.MultiTape.Plumbing.ExtendTapes
public import Cslib.Computability.Machines.Turing.MultiTape.Plumbing.Sequential
public import Cslib.Computability.Machines.Turing.MultiTape.TapeLemmas
public import Cslib.Computability.Machines.Turing.SingleTape.Defs
public import Cslib.Computability.Machines.Turing.SingleTape.Deterministic
Expand Down
111 changes: 79 additions & 32 deletions Cslib/Computability/Machines/Turing/MultiTape/Deterministic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,20 @@ lemma runFrom_of_halt (cfg : Cfg k Symbol State input) (h : cfg.state = none) {n
| succ d ih =>
rw [runFrom_succ_eq_step', ih, step_of_halt h]

/-- Once a run has halted, every later time denotes the same configuration. -/
lemma runFrom_eq_of_halt (cfg : Cfg k Symbol State input) {τ t : ℕ} (hle : τ ≤ t)
(hhalt : (tm.runFrom cfg τ).state = none) :
tm.runFrom cfg t = tm.runFrom cfg τ := by
obtain ⟨d, rfl⟩ := Nat.exists_eq_add_of_le hle
rw [runFrom_add, runFrom_of_halt _ hhalt]

/-- Halting is monotone in the number of execution steps. -/
lemma runFrom_state_eq_none_mono (cfg : Cfg k Symbol State input) :
Monotone fun t => (tm.runFrom cfg t).state = none := by
intro τ t hle hhalt
rw [runFrom_eq_of_halt cfg hle hhalt]
exact hhalt

@[simp]
lemma outputSymbol_of_halt {cfg : Cfg k Symbol State input} (h_halt : cfg.state = none) :
tm.outputSymbol cfg = none := by
Expand Down Expand Up @@ -368,9 +382,28 @@ lemma runFrom_output_eq_of_halt
(tm : MultiTapeTM k Symbol State)
(cfg : Cfg k Symbol State input) {τ t : ℕ} (hle : τ ≤ t)
(hhalt : (tm.runFrom cfg τ).state = none) :
(tm.runFrom cfg t).output = (tm.runFrom cfg τ).output := by
conv_lhs => rw [← Nat.sub_add_cancel hle, Nat.add_comm]
rw [runFrom_add, runFrom_of_halt _ hhalt]
(tm.runFrom cfg t).output = (tm.runFrom cfg τ).output :=
congrArg Cfg.output (tm.runFrom_eq_of_halt cfg hle hhalt)

/-- Output length is monotone because a machine only appends output symbols. -/
lemma runFrom_output_length_mono
(tm : MultiTapeTM k Symbol State) (cfg : Cfg k Symbol State input) :
Monotone fun t => (tm.runFrom cfg t).output.length := by
apply monotone_nat_of_le_succ
intro t
rw [runFrom_succ_eq_step', step_output, List.length_append]
exact Nat.le_add_right _ _

/-- A run can append at most one output symbol per step. -/
lemma runFrom_output_length_le
(tm : MultiTapeTM k Symbol State)
(cfg : Cfg k Symbol State input) (t : ℕ) :
(tm.runFrom cfg t).output.length ≤ cfg.output.length + t := by
induction t with
| zero => simp
| succ t ih =>
rw [runFrom_succ_eq_step', step_output, List.length_append]
grind [Option.toList]

/-- A proof that the Turing machine `tm` on input `input` outputs `output` in at most `t` steps
and uses exactly `s` space.
Expand All @@ -383,6 +416,15 @@ def ComputesInTimeAndSpace
(tm.runFrom (tm.initCfg input) t).output = output ∧
tm.spaceUsed (tm.initCfg input) t = s

/-- The output of a computation is no longer than its running time. -/
lemma output_length_le_time
{tm : MultiTapeTM k Symbol State} {input output : List Symbol} {t s : ℕ}
(h : ComputesInTimeAndSpace tm input output t s) :
output.length ≤ t := by
obtain ⟨-, hout, -⟩ := h
rw [← hout]
simpa using tm.runFrom_output_length_le (tm.initCfg input) t

/-- A machine computes `f` between the supplied encodings, with bounds depending on the input.
The machine's alphabet and state type need not be finite. -/
def ComputesFunInTimeAndSpace {α β : Type*}
Expand Down Expand Up @@ -457,35 +499,40 @@ lemma relatesInSteps_iff_runFrom_eq
use tm.step^[t] cfg₁
grind

/-- The Turing machine `tm` halts after exactly `t` steps on input `input`
if its state is `none` at step `t` and non-none at step `t - 1`.
Note that every Turing machine hast to perform at least one step to halt. -/
def haltsAtStep (tm : MultiTapeTM k Symbol State) (input : List Symbol) (t : ℕ) : Bool :=
(tm.runFrom (tm.initCfg input) t).state.isNone &&
!(tm.runFrom (tm.initCfg input) (t - 1)).state.isNone

/-- If a Turing machine halts, the time step is uniquely determined. -/
lemma halting_step_unique
{tm : MultiTapeTM k Symbol State}
{input : List Symbol}
{t₁ t₂ : ℕ}
(h_halts₁ : tm.haltsAtStep input t₁)
(h_halts₂ : tm.haltsAtStep input t₂) :
t₁ = t₂ := by
wlog h : t₁ ≤ t₂
· exact (this h_halts₂ h_halts₁ (Nat.le_of_not_le h)).symm
obtain ⟨d, rfl⟩ := Nat.exists_eq_add_of_le h
cases d with
| zero => rfl
| succ d =>
have halts₁ : (tm.runFrom (tm.initCfg input) t₁).state = none := by
simp [haltsAtStep] at h_halts₁
exact h_halts₁.left
have halts₂ : (tm.runFrom (tm.initCfg input) (d + t₁)).state ≠ none := by
grind [haltsAtStep, runFrom]
refine absurd ?_ halts₂
rw [Nat.add_comm, runFrom_add, tm.runFrom_of_halt _ halts₁]
exact halts₁
/-- The run from `cfg` first reaches a halting configuration at time `t`.
An already halted starting configuration has halting time zero. -/
structure HaltsAt (tm : MultiTapeTM k Symbol State) (cfg : Cfg k Symbol State input)
(t : ℕ) : Prop where
/-- The configuration at time `t` is halted. -/
halted : (tm.runFrom cfg t).state = none
/-- All earlier configurations are active. -/
active : ∀ s < t, (tm.runFrom cfg s).state ≠ none

/-- A run has at most one first halting time. -/
lemma HaltsAt.unique {tm : MultiTapeTM k Symbol State} {cfg : Cfg k Symbol State input}
{t₀ t₁ : ℕ} (h₀ : tm.HaltsAt cfg t₀) (h₁ : tm.HaltsAt cfg t₁) : t₀ = t₁ := by
rcases lt_trichotomy t₀ t₁ with h | h | h
· exact (h₁.active t₀ h h₀.halted).elim
· exact h
· exact (h₀.active t₁ h h₁.halted).elim

/-- The machine first halts at step `t` when started on `input`. -/
abbrev haltsAtStep (tm : MultiTapeTM k Symbol State) (input : List Symbol) (t : ℕ) : Prop :=
tm.HaltsAt (tm.initCfg input) t

/-- Every halted run has a first halting time no later than the supplied time. -/
lemma exists_minimal_halting_time (tm : MultiTapeTM k Symbol State)
(cfg : Cfg k Symbol State input) (t : ℕ)
(hhalt : (tm.runFrom cfg t).state = none) :

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really related to this PR but should we change haltsAtStep to use = none as well? Is haltsAtStep useful at all?

∃ s ≤ t, tm.HaltsAt cfg s := by
let hExists : ∃ n, (tm.runFrom cfg n).state = none := ⟨t, hhalt⟩
exact ⟨Nat.find hExists, Nat.find_min' hExists hhalt, Nat.find_spec hExists,
fun _ hs => Nat.find_min hExists hs⟩

/-- If a Turing machine halts, its first halting step is unique. -/
lemma halting_step_unique {tm : MultiTapeTM k Symbol State} {input : List Symbol}
{t₀ t₁ : ℕ} (h₀ : tm.haltsAtStep input t₀) (h₁ : tm.haltsAtStep input t₁) :
t₀ = t₁ := h₀.unique h₁

/-- If a deterministic machine repeats a non-halting configuration, it never halts,
because the sequence between the two configurations will loop forever.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/-
Copyright (c) 2026 Christian Reitwiessner. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Christian Reitwiessner, Samuel Schlesinger
-/

module

public import Cslib.Computability.Machines.Turing.MultiTape.Deterministic

/-! # Configuration state replacement and execution paths

`Cfg.withState` changes the control state, possibly changing its type, and preserves all tapes,
head positions, and accumulated output. `runFrom_map` transports runs along maps that preserve
steps, and `runFrom_eq_of_isChain` identifies the endpoint of an explicit execution path.
-/

@[expose] public section

namespace Turing.MultiTapeTM

variable {k : ℕ} {State Symbol : Type*} {input : List Symbol}

/-- The configuration `cfg` with its state replaced by `q`, possibly over a different state
type. -/
@[simps]
def Cfg.withState (cfg : Cfg k Symbol State input) {State' : Type*}
(q : Option State') : Cfg k Symbol State' input :=
⟨q, cfg.inputPos, cfg.workTapes, cfg.workTapePos, cfg.output⟩

@[simp]
lemma Cfg.withState_inputSymbol {cfg : Cfg k Symbol State input} {State' : Type*}
{q : Option State'} : (cfg.withState q).inputSymbol = cfg.inputSymbol := rfl

@[simp]
lemma Cfg.withState_workTapeSymbols {cfg : Cfg k Symbol State input} {State' : Type*}
{q : Option State'} : (cfg.withState q).workTapeSymbols = cfg.workTapeSymbols := rfl

@[simp]
lemma Cfg.withState_withState {cfg : Cfg k Symbol State input} {State' State'' : Type*}
{q : Option State'} {q' : Option State''} :
(cfg.withState q).withState q' = cfg.withState q' := rfl

@[simp]
lemma Cfg.withState_self {cfg : Cfg k Symbol State input} :
cfg.withState cfg.state = cfg := rfl

/-- A map that preserves steps maps every configuration of an execution. -/
lemma runFrom_map {k' : ℕ} {Symbol' State' : Type*} {input' : List Symbol'}
(tm : MultiTapeTM k Symbol State) (tm' : MultiTapeTM k' Symbol' State')
(embed : Cfg k Symbol State input → Cfg k' Symbol' State' input')
(hstep : ∀ cfg, tm'.step (embed cfg) = embed (tm.step cfg))
(cfg : Cfg k Symbol State input) (n : ℕ) :
tm'.runFrom (embed cfg) n = embed (tm.runFrom cfg n) := by
have h : Function.Semiconj embed tm.step tm'.step := fun cfg => (hstep cfg).symm
exact (h.iterate_right n cfg).symm

/-- An execution path ends at the configuration reached after one step per adjacent pair. -/
lemma runFrom_eq_of_isChain (tm : MultiTapeTM k Symbol State)
{cfg cfg' : Cfg k Symbol State input} {path : List (Cfg k Symbol State input)}
(hpath : path.IsChainFromTo tm.TransitionRelation cfg cfg') :
tm.runFrom cfg (path.length - 1) = cfg' := by
apply (tm.relatesInSteps_iff_runFrom_eq cfg cfg' _).mp
exact hpath.relatesInSteps (by have := hpath.length_pos; omega)

end Turing.MultiTapeTM
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/-
Copyright (c) 2026 Samuel Schlesinger. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Samuel Schlesinger
-/

module

public import Cslib.Computability.Machines.Turing.MultiTape.Plumbing.Basic
public import Mathlib.Data.Fintype.Inv
public import Mathlib.Data.Fintype.Card

/-!
# Extending and reindexing work tapes

`extendTapes` embeds a machine's work tapes along an injection. The extra tapes are idle, and each
native step still takes exactly one step. The configuration embedding permits arbitrary contents
and head positions on the extra tapes, so the transformation also applies to intermediate runs.
-/

@[expose] public section

namespace Turing.MultiTapeTM

variable {k k' : ℕ} {Symbol State : Type*} {input : List Symbol}

namespace ExtendTapes

/-- Extend a tape-indexed family along an injection, using `extra` outside its image. -/
def extend {α : Type*} (e : Fin k ↪ Fin k') (values : Fin k → α) (extra : Fin k' → α)
(j : Fin k') : α :=
if h : j ∈ Set.range e then values (e.invOfMemRange ⟨j, h⟩) else extra j

@[simp]
lemma extend_apply {α : Type*} (e : Fin k ↪ Fin k') (values : Fin k → α)
(extra : Fin k' → α) (i : Fin k) : extend e values extra (e i) = values i := by
simp [extend]

/-- Embed a native configuration while retaining arbitrary data on the unused tapes. -/
def embed (e : Fin k ↪ Fin k') (cfg : Cfg k Symbol State input)
(extraTapes : Fin k' → ℤ → Option Symbol) (extraPos : Fin k' → ℤ) :
Cfg k' Symbol State input where
state := cfg.state
inputPos := cfg.inputPos
workTapes := extend e cfg.workTapes extraTapes
workTapePos := extend e cfg.workTapePos extraPos
output := cfg.output

end ExtendTapes

/-- Relabel the work tapes by an injection, leaving every tape outside its image idle. -/
def extendTapes (tm : MultiTapeTM k Symbol State) (e : Fin k ↪ Fin k') :
MultiTapeTM k' Symbol State where
q₀ := tm.q₀
tr q input work :=
let action := tm.tr q input (work ∘ e)
⟨action.inputMove, ExtendTapes.extend e action.workActions (fun _ => (none, 0)),
action.outS, action.q'⟩

namespace ExtendTapes

variable (tm : MultiTapeTM k Symbol State) (e : Fin k ↪ Fin k')
variable (cfg : Cfg k Symbol State input)
variable (extraTapes : Fin k' → ℤ → Option Symbol) (extraPos : Fin k' → ℤ)

/-- Tape extension preserves a step and every unused tape. -/
lemma step_embed :
(tm.extendTapes e).step (embed e cfg extraTapes extraPos) =
embed e (tm.step cfg) extraTapes extraPos := by
have hwork : (embed e cfg extraTapes extraPos).workTapeSymbols ∘ e = cfg.workTapeSymbols := by
funext i
simp [embed, Cfg.workTapeSymbols]
cases hs : cfg.state with
| none => simp [step, embed, hs]
| some q =>
simp only [step, embed, hs, extendTapes, Cfg.inputSymbol] at hwork ⊢
rw [hwork]
apply Cfg.ext <;> try rfl
· funext j p
by_cases hj : j ∈ Set.range e
· obtain ⟨i, rfl⟩ := hj
simp [extend_apply]
· simp [extend, hj]
· funext j
by_cases hj : j ∈ Set.range e
· obtain ⟨i, rfl⟩ := hj
simp [extend_apply]
· simp [extend, hj]

/-- Tape extension maps each configuration of the native run, preserving the unused tapes. -/
lemma runFrom_embed (n : ℕ) :
(tm.extendTapes e).runFrom (embed e cfg extraTapes extraPos) n =
embed e (tm.runFrom cfg n) extraTapes extraPos :=
runFrom_map tm (tm.extendTapes e) (fun cfg => embed e cfg extraTapes extraPos)
(fun cfg => step_embed tm e cfg extraTapes extraPos) cfg n

/-- An injected tape visits exactly the native tape's positions. -/
lemma spaceUsedByTape_embed (n : ℕ) (i : Fin k) :
(tm.extendTapes e).spaceUsedByTape (embed e cfg extraTapes extraPos) n (e i) =
tm.spaceUsedByTape cfg n i := by
simp only [spaceUsedByTape, visitedByTapeHead, runFrom_embed]
simp only [embed, extend_apply]

/-- An unused tape visits just its initial cell. -/
lemma spaceUsedByTape_extra (n : ℕ) (j : Fin k') (hj : j ∉ Set.range e) :
(tm.extendTapes e).spaceUsedByTape (embed e cfg extraTapes extraPos) n j = 1 := by
simp only [spaceUsedByTape, visitedByTapeHead, runFrom_embed]
simp [embed, extend, hj, Finset.image_const]

/-- The extra space is exactly one visited cell for each unused tape. -/
lemma spaceUsed_embed (n : ℕ) :
(tm.extendTapes e).spaceUsed (embed e cfg extraTapes extraPos) n =
tm.spaceUsed cfg n + (k' - k) := by
unfold spaceUsed
rw [← Finset.sum_add_sum_compl (Finset.univ.map e)]
congr 1
· simp [Finset.sum_map, spaceUsedByTape_embed]
· calc
_ = ∑ j ∈ (Finset.univ.map e)ᶜ, 1 := by
apply Finset.sum_congr rfl
intro j hj
exact spaceUsedByTape_extra tm e cfg extraTapes extraPos n j (by simpa using hj)
_ = k' - k := by simp [Finset.card_compl]

end ExtendTapes

/-- Starting with blank work tapes commutes with tape extension. -/
lemma runFrom_extendTapes (tm : MultiTapeTM k Symbol State) (e : Fin k ↪ Fin k')
(input : List Symbol) (n : ℕ) :
(tm.extendTapes e).runFrom ((tm.extendTapes e).initCfg input) n =
ExtendTapes.embed e (tm.runFrom (tm.initCfg input) n) (fun _ _ => none) (fun _ => 0) := by
have hinit : (tm.extendTapes e).initCfg input =
ExtendTapes.embed e (tm.initCfg input) (fun _ _ => none) (fun _ => 0) := by
ext i p <;> simp [ExtendTapes.embed, ExtendTapes.extend, extendTapes]
rw [hinit, ExtendTapes.runFrom_embed]

end Turing.MultiTapeTM
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Machine transformations

These modules describe executable machines and their effects on configurations and `runFrom`.

- `Basic` changes a configuration's control state without changing its tapes, transports runs
along maps that preserve steps, and relates explicit execution paths to `runFrom`.
- `Sequential` runs machines with the same work-tape count consecutively. The first halting
transition hands its tapes, head positions, and accumulated output to the second machine,
which starts in its own fixed initial state.
- `ExtendTapes` places a machine's tapes along any injection. Time is unchanged; each unused tape
contributes one visited cell to space. Arbitrary data on unused tapes is preserved.
Loading
Loading