From dce94e87eb4a98e1535ac5ac21410ef67c199e47 Mon Sep 17 00:00:00 2001 From: crei Date: Mon, 2 Mar 2026 10:37:42 +0100 Subject: [PATCH 01/12] Multi-tape Turing machine. --- Cslib.lean | 1 + .../Machines/MultiTapeTuring/Basic.lean | 364 ++++++++++++++++++ 2 files changed, 365 insertions(+) create mode 100644 Cslib/Computability/Machines/MultiTapeTuring/Basic.lean diff --git a/Cslib.lean b/Cslib.lean index a9d5ffc3e..d64e025ef 100644 --- a/Cslib.lean +++ b/Cslib.lean @@ -30,6 +30,7 @@ public import Cslib.Computability.Languages.OmegaLanguage public import Cslib.Computability.Languages.OmegaRegularLanguage public import Cslib.Computability.Languages.RegularLanguage public import Cslib.Computability.Machines.SingleTapeTuring.Basic +public import Cslib.Computability.Machines.MultiTapeTuring.Basic public import Cslib.Computability.URM.Basic public import Cslib.Computability.URM.Computable public import Cslib.Computability.URM.Defs diff --git a/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean new file mode 100644 index 000000000..6bf325db8 --- /dev/null +++ b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean @@ -0,0 +1,364 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module + +-- TODO create a "common file"? +public import Cslib.Computability.Machines.SingleTapeTuring.Basic + +public import Mathlib.Data.Part + +import Mathlib.Algebra.Order.BigOperators.Group.Finset + +/-! +# Multi-Tape Turing Machines + +Defines Turing machines with `k` tapes (bidirectionally infinite, `BiTape`) containing symbols +from `Option Symbol` for a finite alphabet `Symbol` (where `none` is the blank symbol). + +## Design + +The design of the multi-tape Turing machine follows the one for single-tape Turing machines. +With multiple tapes, it is not immediatly clear how to define the function computed by a Turing +machine. For a single-tape Turing machine, function composition follows easily from composition +of configurations. For multi-tape machines, we focus on composition of tape configurations +(cf. `MultiTapeTM.eval`) and defer the decision of how to define the function computed by a +Turing machine to a later stage. + +Since these Turing machines are deterministic, we base the definition of semantics on the sequence +of configurations instead of reachability in a configuration relation, although equivalence +between these two notions is proven. + +## Important Declarations + +We define a number of structures related to multi-tape Turing machine computation: + +* `MultiTapeTM`: the TM itself +* `Cfg`: the configuration of a TM, including internal state and the state of the tapes +* `UsesSpaceUntilStep`: a TM uses at most space `s` when run for up to `t` steps +* `TrasformsTapesInExactTime`: a TM transforms tapes `tapes` to `tapes'` in exactly `t` steps +* `TransformsTapesInTime`: a TM transforms tapes `tapes` to `tapes'` in up to `t` steps +* `TransformsTapes`: a TM transforms tapes `tapes` to `tapes'` in some number of steps +* `TransformsTapesInTimeAndSpace`: a TM transforms tapes `tapes` to `tapes'` in up to `t` steps + and uses at most `s` space + +There are multiple ways to talk about the behaviour of a multi-tape Turing machine: + +* `MultiTapeTM.configs`: a sequence of configurations by execution step +* `TransformsTapes`: a TM transforms initial tapes `tapes` and halts with tapes `tapes'` +* `MultiTapeTM.eval`: executes a TM on initial tapes `tapes` and returns the resulting tapes if it + eventually halts + +## TODOs + +* Define sequential composition of multi-tape Turing machines. +* Define different kinds of tapes (input-only, output-only, oracle, etc) and how they influence + how space is counted. +* Define the notion of a multi-tape Turing machine computing a function. + +-/ + +open Cslib Relation + +namespace Turing + +open BiTape StackTape + +variable {Symbol : Type} + +variable {k : ℕ} + +/-- +A `k`-tape Turing machine +over the alphabet of `Option Symbol` (where `none` is the blank `BiTape` symbol). +-/ +public structure MultiTapeTM k Symbol [Inhabited Symbol] [Fintype Symbol] where + /-- type of state labels -/ + (State : Type) + /-- finiteness of the state type -/ + [stateFintype : Fintype State] + /-- initial state -/ + (q₀ : State) + /-- transition function, mapping a state and a tuple of head symbols to a `Stmt` to invoke + for each tape and optionally the new state to transition to afterwards (`none` for halt) -/ + (tr : State → (Fin k → Option Symbol) → ((Fin k → (SingleTapeTM.Stmt Symbol)) × Option State)) + +namespace MultiTapeTM + +section Cfg + +/-! +## Configurations of a Turing Machine + +This section defines the configurations of a Turing machine, +the step function that lets the machine transition from one configuration to the next, +and the intended initial and final configurations. +-/ + +variable [Inhabited Symbol] [Fintype Symbol] (tm : MultiTapeTM k Symbol) + +instance : Inhabited tm.State := ⟨tm.q₀⟩ + +instance : Fintype tm.State := tm.stateFintype + +instance inhabitedStmt : Inhabited (SingleTapeTM.Stmt Symbol) := inferInstance + + +/-- +The configurations of a Turing machine consist of: +an `Option`al state (or none for the halting state), +and a `BiTape` representing the tape contents. +-/ +@[ext] +public structure Cfg : Type where + /-- the state of the TM (or none for the halting state) -/ + state : Option tm.State + /-- the BiTape contents -/ + tapes : Fin k → BiTape Symbol +deriving Inhabited + +/-- The step function corresponding to a `MultiTapeTM`. -/ +public def step : tm.Cfg → Option tm.Cfg + | ⟨none, _⟩ => + -- If in the halting state, there is no next configuration + none + | ⟨some q, tapes⟩ => + -- If in state q, perform look up in the transition function + match tm.tr q (fun i => (tapes i).head) with + -- and enter a new configuration with state q' (or none for halting) + -- and tapes updated according to the Stmt + | ⟨stmts, q'⟩ => some ⟨q', fun i => + ((tapes i).write (stmts i).symbol).optionMove (stmts i).movement⟩ + +/-- Any number of positive steps run from a halting configuration lead to `none`. -/ +@[simp, scoped grind =] +public lemma step_iter_none_eq_none (tapes : Fin k → BiTape Symbol) (n : ℕ) : + (Option.bind · tm.step)^[n + 1] (some ⟨none, tapes⟩) = none := by + rw [Function.iterate_succ_apply] + induction n with + | zero => simp [step] + | succ n ih => + simp only [Function.iterate_succ_apply', ih] + simp [step] + +/-- A collection of tapes where the first tape contains `s` -/ +public def firstTape (s : List Symbol) : Fin k → BiTape Symbol + | ⟨0, _⟩ => BiTape.mk₁ s + | ⟨_, _⟩ => default + +/-- +The initial configuration corresponding to a list in the input alphabet. +Note that the entries of the tape constructed by `BiTape.mk₁` are all `some` values. +This is to ensure that distinct lists map to distinct initial configurations. +-/ +@[simp] +public def initCfg (s : List Symbol) : tm.Cfg := + ⟨some tm.q₀, firstTape s⟩ + +/-- Create an initial configuration given a tuple of tapes. -/ +@[simp] +public def initCfgTapes (tapes : Fin k → BiTape Symbol) : tm.Cfg := + ⟨some tm.q₀, tapes⟩ + +/-- The final configuration corresponding to a list in the output alphabet. +(We demand that the head halts at the leftmost position of the output.) +-/ +@[simp] +public def haltCfg (s : List Symbol) : tm.Cfg := + ⟨none, firstTape s⟩ + +/-- The final configuration of a Turing machine given a tuple of tapes. -/ +@[simp] +public def haltCfgTapes (tapes : Fin k → BiTape Symbol) : tm.Cfg := + ⟨none, tapes⟩ + +/-- The sequence of configurations of the Turing machine starting with initial state and +given tapes at step `t`. +If the Turing machine halts, it will eventually get and stay `none` after reaching the halting +configuration. -/ +public def configs (tapes : Fin k → BiTape Symbol) (t : ℕ) : Option tm.Cfg := + (Option.bind · tm.step)^[t] (tm.initCfgTapes tapes) + + + +-- TODO shouldn't this be spaceUsed? (If yes, also change it in SingleTapeTM) + +/-- +The space used by a configuration is the sum of the space used by its tapes. +-/ +public def Cfg.space_used (cfg : tm.Cfg) : ℕ := ∑ i, (cfg.tapes i).space_used + +/-- +The space used by a configuration grows by at most `k` each step. +-/ +public lemma Cfg.space_used_step (cfg cfg' : tm.Cfg) + (hstep : tm.step cfg = some cfg') : cfg'.space_used ≤ cfg.space_used + k := by + obtain ⟨_ | q, tapes⟩ := cfg + · simp [step] at hstep + · simp only [step] at hstep + generalize h_tr : tm.tr q (fun i => (tapes i).head) = result at hstep + obtain ⟨stmts, q''⟩ := result + injection hstep with hstep + subst hstep + simp only [space_used] + trans ∑ i : Fin k, ((tapes i).space_used + 1) + · refine Finset.sum_le_sum fun i _ => ?_ + unfold BiTape.optionMove + grind [BiTape.space_used_write, BiTape.space_used_move] + · simp [Finset.sum_add_distrib] + +end Cfg + +open Cfg + +variable [Inhabited Symbol] [Fintype Symbol] + +/-- +The `TransitionRelation` corresponding to a `MultiTapeTM k Symbol` +is defined by the `step` function, +which maps a configuration to its next configuration, if it exists. +-/ +@[scoped grind =] +public def TransitionRelation (tm : MultiTapeTM k Symbol) (c₁ c₂ : tm.Cfg) : Prop := + tm.step c₁ = some c₂ + +/-- A proof that the Turing machine `tm` transforms tapes `tapes` to `tapes'` in exactly +`t` steps. -/ +public def TransformsTapesInExactTime + (tm : MultiTapeTM k Symbol) + (tapes tapes' : Fin k → BiTape Symbol) + (t : ℕ) : Prop := + RelatesInSteps tm.TransitionRelation (tm.initCfgTapes tapes) (tm.haltCfgTapes tapes') t + +/-- A proof that the Turing machine `tm` transforms tapes `tapes` to `tapes'` in up to +`t` steps. -/ +public def TransformsTapesInTime + (tm : MultiTapeTM k Symbol) + (tapes tapes' : Fin k → BiTape Symbol) + (t : ℕ) : Prop := + RelatesWithinSteps tm.TransitionRelation (tm.initCfgTapes tapes) (tm.haltCfgTapes tapes') t + +/-- The Turing machine `tm` transforms tapes `tapes` to `tapes'`. -/ +public def TransformsTapes + (tm : MultiTapeTM k Symbol) + (tapes tapes' : Fin k → BiTape Symbol) : Prop := + ∃ t, tm.TransformsTapesInExactTime tapes tapes' t + +/-- A proof that the Turing machine `tm` uses at most space `s` when run for up to `t` steps +on initial tapes `tapes`. -/ +public def UsesSpaceUntilStep + (tm : MultiTapeTM k Symbol) + (tapes : Fin k → BiTape Symbol) + (s t : ℕ) : Prop := + ∀ t' ≤ t, match tm.configs tapes t' with + | none => true + | some cfg => cfg.space_used ≤ s + +/-- A proof that the Turing machine `tm` transforms tapes `tapes` to `tapes'` in exactly `t` steps +and uses at most `s` space. -/ +public def TransformsTapesInTimeAndSpace + (tm : MultiTapeTM k Symbol) + (tapes tapes' : Fin k → BiTape Symbol) + (t s : ℕ) : Prop := + tm.TransformsTapesInExactTime tapes tapes' t ∧ + tm.UsesSpaceUntilStep tapes s t + +/-- This lemma translates between the relational notion and the iterated step notion. The latter +can be more convenient especially for deterministic machines as we have here. -/ +@[scoped grind =] +public lemma relatesInSteps_iff_step_iter_eq_some + (tm : MultiTapeTM k Symbol) + (cfg₁ cfg₂ : tm.Cfg) + (t : ℕ) : + RelatesInSteps tm.TransitionRelation cfg₁ cfg₂ t ↔ + (Option.bind · tm.step)^[t] cfg₁ = .some cfg₂ := by + induction t generalizing cfg₁ cfg₂ with + | zero => simp + | succ t ih => + rw [RelatesInSteps.succ_iff, Function.iterate_succ_apply'] + constructor + · grind only [TransitionRelation, = Option.bind_some] + · intro h_configs + cases h : (Option.bind · tm.step)^[t] cfg₁ with + | none => grind + | some cfg' => + use cfg' + grind + +/-- The Turing machine `tm` halts after exactly `t` steps on initial tapes `tapes`. -/ +public def haltsAtStep + (tm : MultiTapeTM k Symbol) (tapes : Fin k → BiTape Symbol) (t : ℕ) : Bool := + match (tm.configs tapes t) with + | some ⟨none, _⟩ => true + | _ => false + +/-- If a Turing machine halts, the time step is uniquely determined. -/ +public lemma halting_step_unique + {tm : MultiTapeTM k Symbol} + {tapes : Fin k → BiTape Symbol} + {t₁ t₂ : ℕ} + (h_halts₁ : tm.haltsAtStep tapes t₁) + (h_halts₂ : tm.haltsAtStep tapes 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 => + -- this is a contradiction. + unfold haltsAtStep configs at h_halts₁ h_halts₂ + split at h_halts₁ <;> try contradiction + next tapes' h_iter_t₁ => + rw [Nat.add_comm t₁ (d + 1), Function.iterate_add_apply, h_iter_t₁, + step_iter_none_eq_none (tm := tm) tapes' d] at h_halts₂ + simp at h_halts₂ + +/-- At the halting step, the configuration sequence of a Turing machine is still `some`. -/ +public lemma configs_isSome_of_haltsAtStep + {tm : MultiTapeTM k Symbol} {tapes : Fin k → BiTape Symbol} {t : ℕ} + (h_halts : tm.haltsAtStep tapes t) : + (tm.configs tapes t).isSome := by + grind [haltsAtStep] + +/-- Execute the Turing machine `tm` on initial tapes `tapes` and return the resulting tapes +if it eventually halts. -/ +public def eval (tm : MultiTapeTM k Symbol) (tapes : Fin k → BiTape Symbol) : + Part (Fin k → BiTape Symbol) := + ⟨∃ t, tm.haltsAtStep tapes t, + fun h => ((tm.configs tapes (Nat.find h)).get + (configs_isSome_of_haltsAtStep (Nat.find_spec h))).tapes⟩ + +/-- Evaluating a Turing machine on a tuple of tapes `tapes` has a value `tapes'` if and only if +it transforms `tapes` into `tapes'`. -/ +@[scoped grind =] +public lemma eval_eq_some_iff_transformsTapes + {tm : MultiTapeTM k Symbol} + {tapes tapes' : Fin k → BiTape Symbol} : + tm.eval tapes = .some tapes' ↔ tm.TransformsTapes tapes tapes' := by + simp only [eval, Part.eq_some_iff, Part.mem_mk_iff] + constructor + · intro ⟨h_dom, h_get⟩ + use Nat.find h_dom + rw [TransformsTapesInExactTime, relatesInSteps_iff_step_iter_eq_some] + rw [← configs, Option.eq_some_iff_get_eq] + use configs_isSome_of_haltsAtStep (Nat.find_spec h_dom) + ext1 + · simp + grind [haltsAtStep, Nat.find_spec h_dom] + · exact h_get + · intro ⟨t, h_iter⟩ + rw [TransformsTapesInExactTime, relatesInSteps_iff_step_iter_eq_some] at h_iter + rw [← configs] at h_iter + have h_halts_at_t : tm.haltsAtStep tapes t := by simp [haltsAtStep, h_iter] + let h_halts : ∃ t, tm.haltsAtStep tapes t := ⟨t, h_halts_at_t⟩ + use h_halts + have h_eq : Nat.find h_halts = t := halting_step_unique (Nat.find_spec h_halts) h_halts_at_t + simp [h_eq, h_iter] + +end MultiTapeTM + +end Turing From cb9e7a3d965258ffa1508102aaf0d825a92358b1 Mon Sep 17 00:00:00 2001 From: crei Date: Tue, 10 Mar 2026 17:54:17 +0100 Subject: [PATCH 02/12] Update Cslib/Computability/Machines/MultiTapeTuring/Basic.lean Co-authored-by: Chris Henson <46805207+chenson2018@users.noreply.github.com> --- .../Machines/MultiTapeTuring/Basic.lean | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean index 6bf325db8..b3c45032a 100644 --- a/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean +++ b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean @@ -343,21 +343,12 @@ public lemma eval_eq_some_iff_transformsTapes constructor · intro ⟨h_dom, h_get⟩ use Nat.find h_dom - rw [TransformsTapesInExactTime, relatesInSteps_iff_step_iter_eq_some] - rw [← configs, Option.eq_some_iff_get_eq] - use configs_isSome_of_haltsAtStep (Nat.find_spec h_dom) - ext1 - · simp - grind [haltsAtStep, Nat.find_spec h_dom] - · exact h_get + grind [TransformsTapesInExactTime, configs, haltCfgTapes, haltsAtStep] · intro ⟨t, h_iter⟩ - rw [TransformsTapesInExactTime, relatesInSteps_iff_step_iter_eq_some] at h_iter - rw [← configs] at h_iter - have h_halts_at_t : tm.haltsAtStep tapes t := by simp [haltsAtStep, h_iter] - let h_halts : ∃ t, tm.haltsAtStep tapes t := ⟨t, h_halts_at_t⟩ - use h_halts - have h_eq : Nat.find h_halts = t := halting_step_unique (Nat.find_spec h_halts) h_halts_at_t - simp [h_eq, h_iter] + rw [TransformsTapesInExactTime, relatesInSteps_iff_step_iter_eq_some, ← configs] at h_iter + have h_halts_at_t : tm.haltsAtStep tapes t := by grind [haltsAtStep] + have : ∃ t, tm.haltsAtStep tapes t := ⟨t, h_halts_at_t⟩ + grind [haltCfgTapes, halting_step_unique] end MultiTapeTM From cc78278fa4262b7df7a3fe8ea316b4ade1680983 Mon Sep 17 00:00:00 2001 From: crei Date: Tue, 10 Mar 2026 17:37:56 +0100 Subject: [PATCH 03/12] Extract common parts. --- Cslib.lean | 1 + .../Machines/MultiTapeTuring/Basic.lean | 14 ++++++------- .../Machines/SingleTapeTuring/Basic.lean | 21 ++----------------- 3 files changed, 10 insertions(+), 26 deletions(-) diff --git a/Cslib.lean b/Cslib.lean index d64e025ef..48a755f50 100644 --- a/Cslib.lean +++ b/Cslib.lean @@ -30,6 +30,7 @@ public import Cslib.Computability.Languages.OmegaLanguage public import Cslib.Computability.Languages.OmegaRegularLanguage public import Cslib.Computability.Languages.RegularLanguage public import Cslib.Computability.Machines.SingleTapeTuring.Basic +public import Cslib.Computability.Machines.TuringCommon public import Cslib.Computability.Machines.MultiTapeTuring.Basic public import Cslib.Computability.URM.Basic public import Cslib.Computability.URM.Computable diff --git a/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean index b3c45032a..2ca7e81cd 100644 --- a/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean +++ b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean @@ -6,12 +6,12 @@ Authors: Christian Reitwiessner module --- TODO create a "common file"? -public import Cslib.Computability.Machines.SingleTapeTuring.Basic - public import Mathlib.Data.Part - -import Mathlib.Algebra.Order.BigOperators.Group.Finset +public import Mathlib.Data.Fintype.Defs +public import Cslib.Foundations.Data.BiTape +public import Cslib.Foundations.Data.RelatesInSteps +public import Cslib.Computability.Machines.TuringCommon +public import Mathlib.Algebra.Order.BigOperators.Group.Finset /-! # Multi-Tape Turing Machines @@ -84,7 +84,7 @@ public structure MultiTapeTM k Symbol [Inhabited Symbol] [Fintype Symbol] where (q₀ : State) /-- transition function, mapping a state and a tuple of head symbols to a `Stmt` to invoke for each tape and optionally the new state to transition to afterwards (`none` for halt) -/ - (tr : State → (Fin k → Option Symbol) → ((Fin k → (SingleTapeTM.Stmt Symbol)) × Option State)) + (tr : State → (Fin k → Option Symbol) → ((Fin k → (Stmt Symbol)) × Option State)) namespace MultiTapeTM @@ -104,7 +104,7 @@ instance : Inhabited tm.State := ⟨tm.q₀⟩ instance : Fintype tm.State := tm.stateFintype -instance inhabitedStmt : Inhabited (SingleTapeTM.Stmt Symbol) := inferInstance +instance inhabitedStmt : Inhabited (Stmt Symbol) := inferInstance /-- diff --git a/Cslib/Computability/Machines/SingleTapeTuring/Basic.lean b/Cslib/Computability/Machines/SingleTapeTuring/Basic.lean index 4f31c1530..adec50705 100644 --- a/Cslib/Computability/Machines/SingleTapeTuring/Basic.lean +++ b/Cslib/Computability/Machines/SingleTapeTuring/Basic.lean @@ -9,6 +9,7 @@ module public import Cslib.Foundations.Data.BiTape public import Cslib.Foundations.Data.RelatesInSteps public import Mathlib.Algebra.Polynomial.Eval.Defs +public import Cslib.Computability.Machines.TuringCommon @[expose] public section @@ -44,7 +45,6 @@ for convenience in composition of machines. We define a number of structures related to Turing machine computation: -* `Stmt`: the write and movement operations a TM can do in a single step. * `SingleTapeTM`: the TM itself. * `Cfg`: the configuration of a TM, including internal and tape state. * `TimeComputable f`: a TM for computing `f`, packaged with a bound on runtime. @@ -70,21 +70,6 @@ open BiTape StackTape variable {Symbol : Type} -namespace SingleTapeTM - -/-- -A Turing machine "statement" is just a `Option`al command to move left or right, -and write a symbol (i.e. an `Option Symbol`, where `none` is the blank symbol) on the `BiTape` --/ -structure Stmt (Symbol : Type) where - /-- The symbol to write at the current head position -/ - symbol : Option Symbol - /-- The direction to move the tape head -/ - movement : Option Dir -deriving Inhabited - -end SingleTapeTM - /-- A single-tape Turing machine over the alphabet of `Option Symbol` (where `none` is the blank `BiTape` symbol). @@ -98,7 +83,7 @@ structure SingleTapeTM Symbol [Inhabited Symbol] [Fintype Symbol] where (q₀ : State) /-- Transition function, mapping a state and a head symbol to a `Stmt` to invoke, and optionally the new state to transition to afterwards (`none` for halt) -/ - (tr : State → Option Symbol → SingleTapeTM.Stmt Symbol × Option State) + (tr : State → Option Symbol → Stmt Symbol × Option State) namespace SingleTapeTM @@ -118,8 +103,6 @@ instance : Inhabited tm.State := ⟨tm.q₀⟩ instance : Fintype tm.State := tm.stateFintype -instance inhabitedStmt : Inhabited (Stmt Symbol) := inferInstance - /-- The configurations of a Turing machine consist of: an `Option`al state (or none for the halting state), From 07d7bcd3ac11e72f0823d6492bd369091594ec05 Mon Sep 17 00:00:00 2001 From: crei Date: Tue, 10 Mar 2026 17:54:30 +0100 Subject: [PATCH 04/12] Simplify definitions and proofs. --- .../Machines/MultiTapeTuring/Basic.lean | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean index 2ca7e81cd..900c4556c 100644 --- a/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean +++ b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean @@ -77,14 +77,14 @@ over the alphabet of `Option Symbol` (where `none` is the blank `BiTape` symbol) -/ public structure MultiTapeTM k Symbol [Inhabited Symbol] [Fintype Symbol] where /-- type of state labels -/ - (State : Type) + State : Type /-- finiteness of the state type -/ [stateFintype : Fintype State] /-- initial state -/ - (q₀ : State) + q₀ : State /-- transition function, mapping a state and a tuple of head symbols to a `Stmt` to invoke for each tape and optionally the new state to transition to afterwards (`none` for halt) -/ - (tr : State → (Fin k → Option Symbol) → ((Fin k → (Stmt Symbol)) × Option State)) + tr : State → (Fin k → Option Symbol) → ((Fin k → (Stmt Symbol)) × Option State) namespace MultiTapeTM @@ -139,10 +139,8 @@ public lemma step_iter_none_eq_none (tapes : Fin k → BiTape Symbol) (n : ℕ) (Option.bind · tm.step)^[n + 1] (some ⟨none, tapes⟩) = none := by rw [Function.iterate_succ_apply] induction n with - | zero => simp [step] - | succ n ih => - simp only [Function.iterate_succ_apply', ih] - simp [step] + | zero => rfl + | succ n ih => grind [Function.iterate_succ_apply'] /-- A collection of tapes where the first tape contains `s` -/ public def firstTape (s : List Symbol) : Fin k → BiTape Symbol @@ -280,7 +278,7 @@ public lemma relatesInSteps_iff_step_iter_eq_some | succ t ih => rw [RelatesInSteps.succ_iff, Function.iterate_succ_apply'] constructor - · grind only [TransitionRelation, = Option.bind_some] + · grind · intro h_configs cases h : (Option.bind · tm.step)^[t] cfg₁ with | none => grind From 841be7f40ff647e163459e2394b9f5b3889e75f0 Mon Sep 17 00:00:00 2001 From: crei Date: Tue, 10 Mar 2026 18:26:03 +0100 Subject: [PATCH 05/12] Use "@[expose] public section" --- .../Machines/MultiTapeTuring/Basic.lean | 57 +++++++++---------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean index 900c4556c..e08ab2399 100644 --- a/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean +++ b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean @@ -13,6 +13,8 @@ public import Cslib.Foundations.Data.RelatesInSteps public import Cslib.Computability.Machines.TuringCommon public import Mathlib.Algebra.Order.BigOperators.Group.Finset +@[expose] public section + /-! # Multi-Tape Turing Machines @@ -75,7 +77,7 @@ variable {k : ℕ} A `k`-tape Turing machine over the alphabet of `Option Symbol` (where `none` is the blank `BiTape` symbol). -/ -public structure MultiTapeTM k Symbol [Inhabited Symbol] [Fintype Symbol] where +structure MultiTapeTM k Symbol [Inhabited Symbol] [Fintype Symbol] where /-- type of state labels -/ State : Type /-- finiteness of the state type -/ @@ -113,7 +115,7 @@ an `Option`al state (or none for the halting state), and a `BiTape` representing the tape contents. -/ @[ext] -public structure Cfg : Type where +structure Cfg : Type where /-- the state of the TM (or none for the halting state) -/ state : Option tm.State /-- the BiTape contents -/ @@ -121,7 +123,7 @@ public structure Cfg : Type where deriving Inhabited /-- The step function corresponding to a `MultiTapeTM`. -/ -public def step : tm.Cfg → Option tm.Cfg +def step : tm.Cfg → Option tm.Cfg | ⟨none, _⟩ => -- If in the halting state, there is no next configuration none @@ -135,7 +137,7 @@ public def step : tm.Cfg → Option tm.Cfg /-- Any number of positive steps run from a halting configuration lead to `none`. -/ @[simp, scoped grind =] -public lemma step_iter_none_eq_none (tapes : Fin k → BiTape Symbol) (n : ℕ) : +lemma step_iter_none_eq_none (tapes : Fin k → BiTape Symbol) (n : ℕ) : (Option.bind · tm.step)^[n + 1] (some ⟨none, tapes⟩) = none := by rw [Function.iterate_succ_apply] induction n with @@ -143,7 +145,7 @@ public lemma step_iter_none_eq_none (tapes : Fin k → BiTape Symbol) (n : ℕ) | succ n ih => grind [Function.iterate_succ_apply'] /-- A collection of tapes where the first tape contains `s` -/ -public def firstTape (s : List Symbol) : Fin k → BiTape Symbol +def firstTape (s : List Symbol) : Fin k → BiTape Symbol | ⟨0, _⟩ => BiTape.mk₁ s | ⟨_, _⟩ => default @@ -153,46 +155,42 @@ Note that the entries of the tape constructed by `BiTape.mk₁` are all `some` v This is to ensure that distinct lists map to distinct initial configurations. -/ @[simp] -public def initCfg (s : List Symbol) : tm.Cfg := +def initCfg (s : List Symbol) : tm.Cfg := ⟨some tm.q₀, firstTape s⟩ /-- Create an initial configuration given a tuple of tapes. -/ @[simp] -public def initCfgTapes (tapes : Fin k → BiTape Symbol) : tm.Cfg := +def initCfgTapes (tapes : Fin k → BiTape Symbol) : tm.Cfg := ⟨some tm.q₀, tapes⟩ /-- The final configuration corresponding to a list in the output alphabet. (We demand that the head halts at the leftmost position of the output.) -/ @[simp] -public def haltCfg (s : List Symbol) : tm.Cfg := +def haltCfg (s : List Symbol) : tm.Cfg := ⟨none, firstTape s⟩ /-- The final configuration of a Turing machine given a tuple of tapes. -/ @[simp] -public def haltCfgTapes (tapes : Fin k → BiTape Symbol) : tm.Cfg := +def haltCfgTapes (tapes : Fin k → BiTape Symbol) : tm.Cfg := ⟨none, tapes⟩ /-- The sequence of configurations of the Turing machine starting with initial state and given tapes at step `t`. If the Turing machine halts, it will eventually get and stay `none` after reaching the halting configuration. -/ -public def configs (tapes : Fin k → BiTape Symbol) (t : ℕ) : Option tm.Cfg := +def configs (tapes : Fin k → BiTape Symbol) (t : ℕ) : Option tm.Cfg := (Option.bind · tm.step)^[t] (tm.initCfgTapes tapes) - - --- TODO shouldn't this be spaceUsed? (If yes, also change it in SingleTapeTM) - /-- The space used by a configuration is the sum of the space used by its tapes. -/ -public def Cfg.space_used (cfg : tm.Cfg) : ℕ := ∑ i, (cfg.tapes i).space_used +def Cfg.space_used (cfg : tm.Cfg) : ℕ := ∑ i, (cfg.tapes i).space_used /-- The space used by a configuration grows by at most `k` each step. -/ -public lemma Cfg.space_used_step (cfg cfg' : tm.Cfg) +lemma Cfg.space_used_step (cfg cfg' : tm.Cfg) (hstep : tm.step cfg = some cfg') : cfg'.space_used ≤ cfg.space_used + k := by obtain ⟨_ | q, tapes⟩ := cfg · simp [step] at hstep @@ -220,12 +218,12 @@ is defined by the `step` function, which maps a configuration to its next configuration, if it exists. -/ @[scoped grind =] -public def TransitionRelation (tm : MultiTapeTM k Symbol) (c₁ c₂ : tm.Cfg) : Prop := +def TransitionRelation (tm : MultiTapeTM k Symbol) (c₁ c₂ : tm.Cfg) : Prop := tm.step c₁ = some c₂ /-- A proof that the Turing machine `tm` transforms tapes `tapes` to `tapes'` in exactly `t` steps. -/ -public def TransformsTapesInExactTime +def TransformsTapesInExactTime (tm : MultiTapeTM k Symbol) (tapes tapes' : Fin k → BiTape Symbol) (t : ℕ) : Prop := @@ -233,21 +231,19 @@ public def TransformsTapesInExactTime /-- A proof that the Turing machine `tm` transforms tapes `tapes` to `tapes'` in up to `t` steps. -/ -public def TransformsTapesInTime +def TransformsTapesInTime (tm : MultiTapeTM k Symbol) (tapes tapes' : Fin k → BiTape Symbol) (t : ℕ) : Prop := RelatesWithinSteps tm.TransitionRelation (tm.initCfgTapes tapes) (tm.haltCfgTapes tapes') t /-- The Turing machine `tm` transforms tapes `tapes` to `tapes'`. -/ -public def TransformsTapes - (tm : MultiTapeTM k Symbol) - (tapes tapes' : Fin k → BiTape Symbol) : Prop := +def TransformsTapes (tm : MultiTapeTM k Symbol) (tapes tapes' : Fin k → BiTape Symbol) : Prop := ∃ t, tm.TransformsTapesInExactTime tapes tapes' t /-- A proof that the Turing machine `tm` uses at most space `s` when run for up to `t` steps on initial tapes `tapes`. -/ -public def UsesSpaceUntilStep +def UsesSpaceUntilStep (tm : MultiTapeTM k Symbol) (tapes : Fin k → BiTape Symbol) (s t : ℕ) : Prop := @@ -257,7 +253,7 @@ public def UsesSpaceUntilStep /-- A proof that the Turing machine `tm` transforms tapes `tapes` to `tapes'` in exactly `t` steps and uses at most `s` space. -/ -public def TransformsTapesInTimeAndSpace +def TransformsTapesInTimeAndSpace (tm : MultiTapeTM k Symbol) (tapes tapes' : Fin k → BiTape Symbol) (t s : ℕ) : Prop := @@ -267,7 +263,7 @@ public def TransformsTapesInTimeAndSpace /-- This lemma translates between the relational notion and the iterated step notion. The latter can be more convenient especially for deterministic machines as we have here. -/ @[scoped grind =] -public lemma relatesInSteps_iff_step_iter_eq_some +lemma relatesInSteps_iff_step_iter_eq_some (tm : MultiTapeTM k Symbol) (cfg₁ cfg₂ : tm.Cfg) (t : ℕ) : @@ -287,14 +283,13 @@ public lemma relatesInSteps_iff_step_iter_eq_some grind /-- The Turing machine `tm` halts after exactly `t` steps on initial tapes `tapes`. -/ -public def haltsAtStep - (tm : MultiTapeTM k Symbol) (tapes : Fin k → BiTape Symbol) (t : ℕ) : Bool := +def haltsAtStep (tm : MultiTapeTM k Symbol) (tapes : Fin k → BiTape Symbol) (t : ℕ) : Bool := match (tm.configs tapes t) with | some ⟨none, _⟩ => true | _ => false /-- If a Turing machine halts, the time step is uniquely determined. -/ -public lemma halting_step_unique +lemma halting_step_unique {tm : MultiTapeTM k Symbol} {tapes : Fin k → BiTape Symbol} {t₁ t₂ : ℕ} @@ -316,7 +311,7 @@ public lemma halting_step_unique simp at h_halts₂ /-- At the halting step, the configuration sequence of a Turing machine is still `some`. -/ -public lemma configs_isSome_of_haltsAtStep +lemma configs_isSome_of_haltsAtStep {tm : MultiTapeTM k Symbol} {tapes : Fin k → BiTape Symbol} {t : ℕ} (h_halts : tm.haltsAtStep tapes t) : (tm.configs tapes t).isSome := by @@ -324,7 +319,7 @@ public lemma configs_isSome_of_haltsAtStep /-- Execute the Turing machine `tm` on initial tapes `tapes` and return the resulting tapes if it eventually halts. -/ -public def eval (tm : MultiTapeTM k Symbol) (tapes : Fin k → BiTape Symbol) : +def eval (tm : MultiTapeTM k Symbol) (tapes : Fin k → BiTape Symbol) : Part (Fin k → BiTape Symbol) := ⟨∃ t, tm.haltsAtStep tapes t, fun h => ((tm.configs tapes (Nat.find h)).get @@ -333,7 +328,7 @@ public def eval (tm : MultiTapeTM k Symbol) (tapes : Fin k → BiTape Symbol) : /-- Evaluating a Turing machine on a tuple of tapes `tapes` has a value `tapes'` if and only if it transforms `tapes` into `tapes'`. -/ @[scoped grind =] -public lemma eval_eq_some_iff_transformsTapes +lemma eval_eq_some_iff_transformsTapes {tm : MultiTapeTM k Symbol} {tapes tapes' : Fin k → BiTape Symbol} : tm.eval tapes = .some tapes' ↔ tm.TransformsTapes tapes tapes' := by From abac0f11ddf6579691556eb705599353f16a5cca Mon Sep 17 00:00:00 2001 From: crei Date: Tue, 10 Mar 2026 18:26:49 +0100 Subject: [PATCH 06/12] Update Cslib.lean --- Cslib.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cslib.lean b/Cslib.lean index 48a755f50..4e8f8d568 100644 --- a/Cslib.lean +++ b/Cslib.lean @@ -29,9 +29,9 @@ public import Cslib.Computability.Languages.Language public import Cslib.Computability.Languages.OmegaLanguage public import Cslib.Computability.Languages.OmegaRegularLanguage public import Cslib.Computability.Languages.RegularLanguage +public import Cslib.Computability.Machines.MultiTapeTuring.Basic public import Cslib.Computability.Machines.SingleTapeTuring.Basic public import Cslib.Computability.Machines.TuringCommon -public import Cslib.Computability.Machines.MultiTapeTuring.Basic public import Cslib.Computability.URM.Basic public import Cslib.Computability.URM.Computable public import Cslib.Computability.URM.Defs From ae6c498526fed9a2002d0396a60068782c8e3cd7 Mon Sep 17 00:00:00 2001 From: crei Date: Tue, 10 Mar 2026 23:52:23 +0100 Subject: [PATCH 07/12] Add missed file. --- .../Computability/Machines/TuringCommon.lean | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Cslib/Computability/Machines/TuringCommon.lean diff --git a/Cslib/Computability/Machines/TuringCommon.lean b/Cslib/Computability/Machines/TuringCommon.lean new file mode 100644 index 000000000..e9e19184f --- /dev/null +++ b/Cslib/Computability/Machines/TuringCommon.lean @@ -0,0 +1,28 @@ +/- +Copyright (c) 2026 Bolton Bailey. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bolton Bailey, Pim Spelier, Daan van Gent +-/ + +module + +public import Mathlib.Computability.Tape + +@[expose] public section + +namespace Turing + +/-- +A Turing machine "statement" is just a `Option`al command to move left or right, +and write a symbol (i.e. an `Option Symbol`, where `none` is the blank symbol) on the `BiTape` +-/ +structure Stmt (Symbol : Type) where + /-- The symbol to write at the current head position -/ + symbol : Option Symbol + /-- The direction to move the tape head -/ + movement : Option Dir +deriving Inhabited + +instance inhabitedStmt : Inhabited (Stmt Symbol) := inferInstance + +end Turing From 7401a21ab47ac4cf53cd964f1f7d7e9896867a64 Mon Sep 17 00:00:00 2001 From: crei Date: Thu, 19 Mar 2026 12:15:06 +0100 Subject: [PATCH 08/12] fix import. --- Cslib/Computability/Machines/TuringCommon.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cslib/Computability/Machines/TuringCommon.lean b/Cslib/Computability/Machines/TuringCommon.lean index e9e19184f..60d063663 100644 --- a/Cslib/Computability/Machines/TuringCommon.lean +++ b/Cslib/Computability/Machines/TuringCommon.lean @@ -6,7 +6,7 @@ Authors: Bolton Bailey, Pim Spelier, Daan van Gent module -public import Mathlib.Computability.Tape +public import Mathlib.Computability.TuringMachine.Tape @[expose] public section From 3110f77821d3c3798e724c68a470e605cea22927 Mon Sep 17 00:00:00 2001 From: crei Date: Thu, 30 Apr 2026 10:25:46 +0200 Subject: [PATCH 09/12] Move "expose" command below module-level documentation. --- Cslib/Computability/Machines/MultiTapeTuring/Basic.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean index e08ab2399..91ee0c781 100644 --- a/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean +++ b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean @@ -13,8 +13,6 @@ public import Cslib.Foundations.Data.RelatesInSteps public import Cslib.Computability.Machines.TuringCommon public import Mathlib.Algebra.Order.BigOperators.Group.Finset -@[expose] public section - /-! # Multi-Tape Turing Machines @@ -63,6 +61,8 @@ There are multiple ways to talk about the behaviour of a multi-tape Turing machi -/ +@[expose] public section + open Cslib Relation namespace Turing From 883e8687949015f702629dc1722445386c2d6a5b Mon Sep 17 00:00:00 2001 From: crei Date: Tue, 2 Jun 2026 18:03:35 +0200 Subject: [PATCH 10/12] Introduce read-only input tape and write-only output tape and define time and space consumption. --- .../Machines/MultiTapeTuring/Basic.lean | 404 ++++++++++-------- 1 file changed, 223 insertions(+), 181 deletions(-) diff --git a/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean index 91ee0c781..5aed397c6 100644 --- a/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean +++ b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean @@ -8,56 +8,51 @@ module public import Mathlib.Data.Part public import Mathlib.Data.Fintype.Defs +public import Mathlib.Data.Finset.Max +public import Mathlib.Algebra.Order.BigOperators.Group.Finset +public import Mathlib.Computability.Language public import Cslib.Foundations.Data.BiTape public import Cslib.Foundations.Data.RelatesInSteps public import Cslib.Computability.Machines.TuringCommon -public import Mathlib.Algebra.Order.BigOperators.Group.Finset /-! # Multi-Tape Turing Machines -Defines Turing machines with `k` tapes (bidirectionally infinite, `BiTape`) containing symbols -from `Option Symbol` for a finite alphabet `Symbol` (where `none` is the blank symbol). +Defines Turing machines with a read-only input tape, `k` work tapes and one write-only output tape. +The tapes contain symbols from `Option Symbol` for a finite alphabet `Symbol` (where `none` is the +blank symbol). ## Design -The design of the multi-tape Turing machine follows the one for single-tape Turing machines. -With multiple tapes, it is not immediatly clear how to define the function computed by a Turing -machine. For a single-tape Turing machine, function composition follows easily from composition -of configurations. For multi-tape machines, we focus on composition of tape configurations -(cf. `MultiTapeTM.eval`) and defer the decision of how to define the function computed by a -Turing machine to a later stage. - -Since these Turing machines are deterministic, we base the definition of semantics on the sequence -of configurations instead of reachability in a configuration relation, although equivalence -between these two notions is proven. +The multi-tape Turing machine uses a read-only input tape, `k` work tapes and a write-only output +tape. +The input head can move freely on the input, but any move attempt beyond one cell outside the input +results in no movement. +The transition function can optionally output one symbol, which models the write-only output tape. +Because of these restrictions, the input and output tapes do not count towards the space usage of +the machine. The space usage of the work tapes is the number of cells the head accessed. ## Important Declarations -We define a number of structures related to multi-tape Turing machine computation: +We define a number of structures and concepts related to multi-tape Turing machine computation: * `MultiTapeTM`: the TM itself -* `Cfg`: the configuration of a TM, including internal state and the state of the tapes -* `UsesSpaceUntilStep`: a TM uses at most space `s` when run for up to `t` steps -* `TrasformsTapesInExactTime`: a TM transforms tapes `tapes` to `tapes'` in exactly `t` steps -* `TransformsTapesInTime`: a TM transforms tapes `tapes` to `tapes'` in up to `t` steps -* `TransformsTapes`: a TM transforms tapes `tapes` to `tapes'` in some number of steps -* `TransformsTapesInTimeAndSpace`: a TM transforms tapes `tapes` to `tapes'` in up to `t` steps - and uses at most `s` space - -There are multiple ways to talk about the behaviour of a multi-tape Turing machine: +* `Cfg`: the configuration of a TM, including internal state, the tapes and the output so far +* `spaceUsed`: the number of work tape cells touched by the head until a certain step +* `TransitionRelation`: the transition relation from one configuration to the next +* `ComputesInTimeAndSpace`: a proof that a TM computes an output from an input in a certain number + of steps and using a certain number of tape cells +* `ComputesFunInTimeAndSpace`: a proof that a TM computes a function (on strings) respecting a + time and space bound in the input length +* `DecidesLanguageInTimeAndSpace`: a proof that a TM decides a language within a certain time + and space bound + +There are two ways to talk about the behaviour of a multi-tape Turing machine, and they are +proven to be equivalent. * `MultiTapeTM.configs`: a sequence of configurations by execution step -* `TransformsTapes`: a TM transforms initial tapes `tapes` and halts with tapes `tapes'` -* `MultiTapeTM.eval`: executes a TM on initial tapes `tapes` and returns the resulting tapes if it - eventually halts - -## TODOs - -* Define sequential composition of multi-tape Turing machines. -* Define different kinds of tapes (input-only, output-only, oracle, etc) and how they influence - how space is counted. -* Define the notion of a multi-tape Turing machine computing a function. +* `RelatesInSteps tm.TransitionRelation cfg cfg' t`: a proof that `tm` transforms the configuration + `cfg` into `cfg'` in exactly `t` steps -/ @@ -73,9 +68,20 @@ variable {Symbol : Type} variable {k : ℕ} +/-- The output of the transition function. -/ +structure TransitionOut (k : ℕ) (Symbol State : Type) where + /-- The movement (attempt) of the input head. -/ + inputMove : Option Dir + /-- Actions on the work tapes: optionally a symbol to write and the head movement. -/ + stmts : Fin k → Stmt Symbol + /-- An optional symbol to output. -/ + outS : Option Symbol + /-- The successor state or none to halt. -/ + q' : Option State + /-- -A `k`-tape Turing machine -over the alphabet of `Option Symbol` (where `none` is the blank `BiTape` symbol). +A multi-tape Turing machine with `k` work tapes over the alphabet of `Option Symbol` (where `none` +is the blank `BiTape` symbol). -/ structure MultiTapeTM k Symbol [Inhabited Symbol] [Fintype Symbol] where /-- type of state labels -/ @@ -84,9 +90,9 @@ structure MultiTapeTM k Symbol [Inhabited Symbol] [Fintype Symbol] where [stateFintype : Fintype State] /-- initial state -/ q₀ : State - /-- transition function, mapping a state and a tuple of head symbols to a `Stmt` to invoke - for each tape and optionally the new state to transition to afterwards (`none` for halt) -/ - tr : State → (Fin k → Option Symbol) → ((Fin k → (Stmt Symbol)) × Option State) + /-- transition function, mapping a state and a tuple of head symbols to a movement for the + input head, actions on the work tape, optionally a symbol to output and the successor state -/ + tr : State → (Fin (k + 1) → Option Symbol) → TransitionOut k Symbol State namespace MultiTapeTM @@ -102,43 +108,54 @@ and the intended initial and final configurations. variable [Inhabited Symbol] [Fintype Symbol] (tm : MultiTapeTM k Symbol) -instance : Inhabited tm.State := ⟨tm.q₀⟩ - -instance : Fintype tm.State := tm.stateFintype - -instance inhabitedStmt : Inhabited (Stmt Symbol) := inferInstance - - /-- The configurations of a Turing machine consist of: -an `Option`al state (or none for the halting state), -and a `BiTape` representing the tape contents. +- an `Option`al state (or none for the halting state), +- `BiTape`s representing the tape contents and +- the output so far. -/ @[ext] structure Cfg : Type where /-- the state of the TM (or none for the halting state) -/ state : Option tm.State - /-- the BiTape contents -/ - tapes : Fin k → BiTape Symbol + /-- the tape contents -/ + tapes : Fin (k + 1) → BiTape Symbol + /-- the output so far -/ + output : List Symbol deriving Inhabited +/-- Applies the actions / statements to the tapes. +The input tape is handled specially: The machine can read one empty cell outside of the input, +but any attempted movement beyond that results in no movement. -/ +def applyTapeActions + (inputMove : Option Dir) + (stmts : Fin k → Stmt Symbol) + (tapes : Fin (k + 1) → BiTape Symbol) : + Fin (k + 1) → BiTape Symbol + | ⟨0, _⟩ => match inputMove, tapes ⟨0, by omega⟩ with + | none, t => t + | some .left, t => if t.left.toList = [] ∧ t.head = none then t else t.move_left + | some .right, t => if t.right.toList = [] ∧ t.head = none then t else t.move_right + | ⟨i + 1, _⟩ => let s := stmts ⟨i, by omega⟩ + ((tapes ⟨i + 1, by omega⟩).write s.symbol).optionMove s.movement + +/-- The output of the transition function applied to a configuration. -/ +def transitionOutput : tm.Cfg → Option (TransitionOut k Symbol tm.State) + | ⟨none, _, _⟩ => none -- halting state + | ⟨some q, tapes, _⟩ => some (tm.tr q (fun i => (tapes i).head)) + /-- The step function corresponding to a `MultiTapeTM`. -/ -def step : tm.Cfg → Option tm.Cfg - | ⟨none, _⟩ => - -- If in the halting state, there is no next configuration - none - | ⟨some q, tapes⟩ => - -- If in state q, perform look up in the transition function - match tm.tr q (fun i => (tapes i).head) with - -- and enter a new configuration with state q' (or none for halting) - -- and tapes updated according to the Stmt - | ⟨stmts, q'⟩ => some ⟨q', fun i => - ((tapes i).write (stmts i).symbol).optionMove (stmts i).movement⟩ +def step (cfg : tm.Cfg) : Option tm.Cfg := + (tm.transitionOutput cfg).map fun {inputMove, stmts, outS, q'} => + let output := match outS with + | none => cfg.output + | some s => cfg.output ++ [s] + ⟨q', applyTapeActions inputMove stmts cfg.tapes, output⟩ /-- Any number of positive steps run from a halting configuration lead to `none`. -/ @[simp, scoped grind =] -lemma step_iter_none_eq_none (tapes : Fin k → BiTape Symbol) (n : ℕ) : - (Option.bind · tm.step)^[n + 1] (some ⟨none, tapes⟩) = none := by +lemma step_iter_none_eq_none (tapes : Fin (k + 1) → BiTape Symbol) (out : List Symbol) (n : ℕ) : + (Option.bind · tm.step)^[n + 1] (some ⟨none, tapes, out⟩) = none := by rw [Function.iterate_succ_apply] induction n with | zero => rfl @@ -149,64 +166,124 @@ def firstTape (s : List Symbol) : Fin k → BiTape Symbol | ⟨0, _⟩ => BiTape.mk₁ s | ⟨_, _⟩ => default -/-- -The initial configuration corresponding to a list in the input alphabet. -Note that the entries of the tape constructed by `BiTape.mk₁` are all `some` values. -This is to ensure that distinct lists map to distinct initial configurations. --/ +/-- The initial configuration corresponding to a list in the input alphabet. -/ @[simp] def initCfg (s : List Symbol) : tm.Cfg := - ⟨some tm.q₀, firstTape s⟩ + ⟨some tm.q₀, firstTape s, []⟩ -/-- Create an initial configuration given a tuple of tapes. -/ -@[simp] -def initCfgTapes (tapes : Fin k → BiTape Symbol) : tm.Cfg := - ⟨some tm.q₀, tapes⟩ +/-- The sequence of configurations of the Turing machine starting from `cfg`. +If the Turing machine halts, it will eventually get and stay `none` after reaching the halting +configuration. -/ +def configs (cfg : tm.Cfg) (t : ℕ) : Option tm.Cfg := + (Option.bind · tm.step)^[t] cfg + +end Cfg + +section Space +/-! Now we define space usage and add some helper lemmas. -/ + +variable [Inhabited Symbol] [Fintype Symbol] (tm : MultiTapeTM k Symbol) + +/-- Convert an "optional movement" to an integer where positive is "right". -/ +@[simp, grind] +def OptionDirToInt : Option Dir → ℤ + | some .left => -1 + | none => 0 + | some .right => 1 -/-- The final configuration corresponding to a list in the output alphabet. -(We demand that the head halts at the leftmost position of the output.) +/-- The movements of the work tape heads after configuration `cfg`. -/ +def headMovements (cfg : tm.Cfg) : Fin k → ℤ + | i => match tm.transitionOutput cfg with + | some tro => OptionDirToInt (tro.stmts ⟨i, by omega⟩).movement + | none => 0 + +/-- The head positions of the work tapes as a function of the number of steps, relative to +the starting position in `cfg`. -/ +def headPositions (cfg : tm.Cfg) (t : ℕ) : Fin k → ℤ + | i => ∑ t' ∈ Finset.range t, (tm.configs cfg t').elim 0 (tm.headMovements · i) + +/-- +The number of work tape cells touched by the head of tape `i` in the computation starting from +configuration `cfg` up to step `t`. -/ -@[simp] -def haltCfg (s : List Symbol) : tm.Cfg := - ⟨none, firstTape s⟩ +def spaceUsedByTape (cfg : tm.Cfg) (t : ℕ) (i : Fin k) : ℕ := + let positions := (Finset.range (t + 1)).image (fun t' => tm.headPositions cfg t' i) + have ne := Finset.image_nonempty.mpr ⟨0, by simp⟩ + (positions.max' ne - positions.min' ne).toNat + 1 -/-- The final configuration of a Turing machine given a tuple of tapes. -/ -@[simp] -def haltCfgTapes (tapes : Fin k → BiTape Symbol) : tm.Cfg := - ⟨none, tapes⟩ +/-- +The number of work tape cells touched by a computation starting from configuration +`cfg` up to step `t`. +-/ +def spaceUsed (cfg : tm.Cfg) (t : ℕ) : ℕ := ∑ i, tm.spaceUsedByTape cfg t i -/-- The sequence of configurations of the Turing machine starting with initial state and -given tapes at step `t`. -If the Turing machine halts, it will eventually get and stay `none` after reaching the halting -configuration. -/ -def configs (tapes : Fin k → BiTape Symbol) (t : ℕ) : Option tm.Cfg := - (Option.bind · tm.step)^[t] (tm.initCfgTapes tapes) +/-- A zero-tape Turing machine uses zero space. -/ +@[simp] +lemma spaceUsed_zero_tapes_eq_zero (cfg : tm.Cfg) (t : ℕ) (h_zero : k = 0) : + tm.spaceUsed cfg t = 0 := by + unfold spaceUsed + subst h_zero + simp + +@[scoped grind .] +lemma OptionDirToInt_bound (d : Option Dir) : + -1 ≤ OptionDirToInt d ∧ OptionDirToInt d ≤ 1 := by + rcases d with _ | d + · decide + · rcases d <;> decide + +/-- A single step moves each work tape head by at most one cell. -/ +lemma step_head_movement_bound (cfg : tm.Cfg) (t : ℕ) (i : Fin k) : + -1 ≤ (tm.configs cfg t).elim 0 (tm.headMovements · i) + ∧ (tm.configs cfg t).elim 0 (tm.headMovements · i) ≤ 1 := by + unfold headMovements + dsimp + rcases h : tm.configs cfg t <;> + grind + +/-- The head position changes by the corresponding head movement on each step. -/ +lemma headPositions_succ (cfg : tm.Cfg) (t : ℕ) (i : Fin k) : + tm.headPositions cfg (t + 1) i + = tm.headPositions cfg t i + (tm.configs cfg t).elim 0 (tm.headMovements · i) := by + simp only [headPositions, Finset.sum_range_succ] /-- -The space used by a configuration is the sum of the space used by its tapes. +Inserting one new point `a`, adjacent to an existing point `q` of `s`, widens the spanned +interval `max' - min'` by at most one cell. -/ -def Cfg.space_used (cfg : tm.Cfg) : ℕ := ∑ i, (cfg.tapes i).space_used +lemma span_insert_le {s S : Finset ℤ} (hs : s.Nonempty) (hS : S.Nonempty) + {a q : ℤ} (hSeq : S = insert a s) (hq : q ∈ s) (h1 : a ≤ q + 1) (h2 : q ≤ a + 1) : + (S.max' hS - S.min' hS).toNat ≤ (s.max' hs - s.min' hs).toNat + 1 := by + subst hSeq + rw [Finset.max'_insert _ _ hs, Finset.min'_insert _ _ hs] + have hm := Finset.min'_le _ _ hq + have hM := Finset.le_max' _ _ hq + grind + +/-- The number of cells touched by a single work tape grows by at most one each step. -/ +lemma spaceUsedByTape_succ_le (cfg : tm.Cfg) (t : ℕ) (i : Fin k) : + tm.spaceUsedByTape cfg (t + 1) i ≤ tm.spaceUsedByTape cfg t i + 1 := by + unfold spaceUsedByTape + have hs := tm.headPositions_succ cfg t i + have step_bound := tm.step_head_movement_bound cfg t i + apply Nat.add_le_add_right + refine span_insert_le _ _ + (by rw [Finset.range_add_one, Finset.image_insert]) + (by exact Finset.mem_image_of_mem _ (Finset.mem_range.mpr (Nat.lt_succ_self t))) + (by grind) + (by grind) /-- The space used by a configuration grows by at most `k` each step. -/ -lemma Cfg.space_used_step (cfg cfg' : tm.Cfg) - (hstep : tm.step cfg = some cfg') : cfg'.space_used ≤ cfg.space_used + k := by - obtain ⟨_ | q, tapes⟩ := cfg - · simp [step] at hstep - · simp only [step] at hstep - generalize h_tr : tm.tr q (fun i => (tapes i).head) = result at hstep - obtain ⟨stmts, q''⟩ := result - injection hstep with hstep - subst hstep - simp only [space_used] - trans ∑ i : Fin k, ((tapes i).space_used + 1) - · refine Finset.sum_le_sum fun i _ => ?_ - unfold BiTape.optionMove - grind [BiTape.space_used_write, BiTape.space_used_move] - · simp [Finset.sum_add_distrib] +lemma spaceUsed_linear (cfg : tm.Cfg) (t : ℕ) : + tm.spaceUsed cfg (t + 1) ≤ tm.spaceUsed cfg t + k := by + calc tm.spaceUsed cfg (t + 1) + ≤ ∑ i, (tm.spaceUsedByTape cfg t i + 1) := + Finset.sum_le_sum fun i _ => tm.spaceUsedByTape_succ_le cfg t i + _ = (∑ i, tm.spaceUsedByTape cfg t i) + k := by simp [Finset.sum_add_distrib] -end Cfg +end Space open Cfg @@ -221,44 +298,38 @@ which maps a configuration to its next configuration, if it exists. def TransitionRelation (tm : MultiTapeTM k Symbol) (c₁ c₂ : tm.Cfg) : Prop := tm.step c₁ = some c₂ -/-- A proof that the Turing machine `tm` transforms tapes `tapes` to `tapes'` in exactly -`t` steps. -/ -def TransformsTapesInExactTime - (tm : MultiTapeTM k Symbol) - (tapes tapes' : Fin k → BiTape Symbol) - (t : ℕ) : Prop := - RelatesInSteps tm.TransitionRelation (tm.initCfgTapes tapes) (tm.haltCfgTapes tapes') t - -/-- A proof that the Turing machine `tm` transforms tapes `tapes` to `tapes'` in up to -`t` steps. -/ -def TransformsTapesInTime - (tm : MultiTapeTM k Symbol) - (tapes tapes' : Fin k → BiTape Symbol) - (t : ℕ) : Prop := - RelatesWithinSteps tm.TransitionRelation (tm.initCfgTapes tapes) (tm.haltCfgTapes tapes') t - -/-- The Turing machine `tm` transforms tapes `tapes` to `tapes'`. -/ -def TransformsTapes (tm : MultiTapeTM k Symbol) (tapes tapes' : Fin k → BiTape Symbol) : Prop := - ∃ t, tm.TransformsTapesInExactTime tapes tapes' t - -/-- A proof that the Turing machine `tm` uses at most space `s` when run for up to `t` steps -on initial tapes `tapes`. -/ -def UsesSpaceUntilStep - (tm : MultiTapeTM k Symbol) - (tapes : Fin k → BiTape Symbol) - (s t : ℕ) : Prop := - ∀ t' ≤ t, match tm.configs tapes t' with - | none => true - | some cfg => cfg.space_used ≤ s - -/-- A proof that the Turing machine `tm` transforms tapes `tapes` to `tapes'` in exactly `t` steps +/-- A proof that the Turing machine `tm` on input `input` outputs `output` in exactly `t` steps and uses at most `s` space. -/ -def TransformsTapesInTimeAndSpace +def ComputesInTimeAndSpace (tm : MultiTapeTM k Symbol) - (tapes tapes' : Fin k → BiTape Symbol) + (input output : List Symbol) (t s : ℕ) : Prop := - tm.TransformsTapesInExactTime tapes tapes' t ∧ - tm.UsesSpaceUntilStep tapes s t + ∃ cfg, + cfg.state = none ∧ + cfg.output = output ∧ + RelatesInSteps tm.TransitionRelation (tm.initCfg input) cfg t ∧ + tm.spaceUsed (tm.initCfg input) t = s + +/-- A proof that the Turing machine `tm` computes the function `f` such that on all inputs of +length `n` it uses at most `t n` steps and `s n` space. -/ +def ComputesFunInTimeAndSpace + (tm : MultiTapeTM k Symbol) + (f : List Symbol → List Symbol) + (t s : ℕ → ℕ) : Prop := + ∀ input, ∃ t' ≤ t input.length, ∃ s' ≤ s input.length, + ComputesInTimeAndSpace tm input (f input) t' s' + +open Classical in +/-- The indicator function of a language. -/ +noncomputable def indicator (l : Language Symbol) : List Symbol → List Symbol + | x => if x ∈ l then [default] else [] + +/-- A proof that a Turing machine `tm` decides a language `l` with time and space bounds. -/ +def DecidesLanguageInTimeAndSpace + (tm : MultiTapeTM k Symbol) + (L : Language Symbol) + (t s : ℕ → ℕ) : Prop := + ComputesFunInTimeAndSpace tm (indicator L) t s /-- This lemma translates between the relational notion and the iterated step notion. The latter can be more convenient especially for deterministic machines as we have here. -/ @@ -282,19 +353,19 @@ lemma relatesInSteps_iff_step_iter_eq_some use cfg' grind -/-- The Turing machine `tm` halts after exactly `t` steps on initial tapes `tapes`. -/ -def haltsAtStep (tm : MultiTapeTM k Symbol) (tapes : Fin k → BiTape Symbol) (t : ℕ) : Bool := - match (tm.configs tapes t) with - | some ⟨none, _⟩ => true +/-- The Turing machine `tm` halts after exactly `t` steps on input `input`. -/ +def haltsAtStep (tm : MultiTapeTM k Symbol) (input : List Symbol) (t : ℕ) : Bool := + match (tm.configs (tm.initCfg input) t) with + | some ⟨none, _, _⟩ => true | _ => false /-- If a Turing machine halts, the time step is uniquely determined. -/ lemma halting_step_unique {tm : MultiTapeTM k Symbol} - {tapes : Fin k → BiTape Symbol} + {input : List Symbol} {t₁ t₂ : ℕ} - (h_halts₁ : tm.haltsAtStep tapes t₁) - (h_halts₂ : tm.haltsAtStep tapes 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 @@ -302,46 +373,17 @@ lemma halting_step_unique cases d with | zero => rfl | succ d => - -- this is a contradiction. unfold haltsAtStep configs at h_halts₁ h_halts₂ - split at h_halts₁ <;> try contradiction - next tapes' h_iter_t₁ => - rw [Nat.add_comm t₁ (d + 1), Function.iterate_add_apply, h_iter_t₁, - step_iter_none_eq_none (tm := tm) tapes' d] at h_halts₂ - simp at h_halts₂ + rw [Nat.add_comm t₁ (d + 1), Function.iterate_add_apply] at h_halts₂ + grind /-- At the halting step, the configuration sequence of a Turing machine is still `some`. -/ lemma configs_isSome_of_haltsAtStep - {tm : MultiTapeTM k Symbol} {tapes : Fin k → BiTape Symbol} {t : ℕ} - (h_halts : tm.haltsAtStep tapes t) : - (tm.configs tapes t).isSome := by + {tm : MultiTapeTM k Symbol} {input : List Symbol} {t : ℕ} + (h_halts : tm.haltsAtStep input t) : + (tm.configs (tm.initCfg input) t).isSome := by grind [haltsAtStep] -/-- Execute the Turing machine `tm` on initial tapes `tapes` and return the resulting tapes -if it eventually halts. -/ -def eval (tm : MultiTapeTM k Symbol) (tapes : Fin k → BiTape Symbol) : - Part (Fin k → BiTape Symbol) := - ⟨∃ t, tm.haltsAtStep tapes t, - fun h => ((tm.configs tapes (Nat.find h)).get - (configs_isSome_of_haltsAtStep (Nat.find_spec h))).tapes⟩ - -/-- Evaluating a Turing machine on a tuple of tapes `tapes` has a value `tapes'` if and only if -it transforms `tapes` into `tapes'`. -/ -@[scoped grind =] -lemma eval_eq_some_iff_transformsTapes - {tm : MultiTapeTM k Symbol} - {tapes tapes' : Fin k → BiTape Symbol} : - tm.eval tapes = .some tapes' ↔ tm.TransformsTapes tapes tapes' := by - simp only [eval, Part.eq_some_iff, Part.mem_mk_iff] - constructor - · intro ⟨h_dom, h_get⟩ - use Nat.find h_dom - grind [TransformsTapesInExactTime, configs, haltCfgTapes, haltsAtStep] - · intro ⟨t, h_iter⟩ - rw [TransformsTapesInExactTime, relatesInSteps_iff_step_iter_eq_some, ← configs] at h_iter - have h_halts_at_t : tm.haltsAtStep tapes t := by grind [haltsAtStep] - have : ∃ t, tm.haltsAtStep tapes t := ⟨t, h_halts_at_t⟩ - grind [haltCfgTapes, halting_step_unique] end MultiTapeTM From 3ac3ac872da42fa2b2f45f672c7fdc49270852bd Mon Sep 17 00:00:00 2001 From: crei Date: Tue, 2 Jun 2026 18:06:03 +0200 Subject: [PATCH 11/12] Clean up imports. --- Cslib/Computability/Machines/MultiTapeTuring/Basic.lean | 2 -- 1 file changed, 2 deletions(-) diff --git a/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean index 5aed397c6..6147be726 100644 --- a/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean +++ b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean @@ -6,8 +6,6 @@ Authors: Christian Reitwiessner module -public import Mathlib.Data.Part -public import Mathlib.Data.Fintype.Defs public import Mathlib.Data.Finset.Max public import Mathlib.Algebra.Order.BigOperators.Group.Finset public import Mathlib.Computability.Language From b5689a11b478d952501d7e69c42e0514c6c9f775 Mon Sep 17 00:00:00 2001 From: crei Date: Tue, 2 Jun 2026 18:09:34 +0200 Subject: [PATCH 12/12] more cleanup --- Cslib/Computability/Machines/MultiTapeTuring/Basic.lean | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean index 6147be726..4c5bb9d50 100644 --- a/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean +++ b/Cslib/Computability/Machines/MultiTapeTuring/Basic.lean @@ -249,7 +249,7 @@ lemma headPositions_succ (cfg : tm.Cfg) (t : ℕ) (i : Fin k) : Inserting one new point `a`, adjacent to an existing point `q` of `s`, widens the spanned interval `max' - min'` by at most one cell. -/ -lemma span_insert_le {s S : Finset ℤ} (hs : s.Nonempty) (hS : S.Nonempty) +private lemma span_insert_le {s S : Finset ℤ} (hs : s.Nonempty) (hS : S.Nonempty) {a q : ℤ} (hSeq : S = insert a s) (hq : q ∈ s) (h1 : a ≤ q + 1) (h2 : q ≤ a + 1) : (S.max' hS - S.min' hS).toNat ≤ (s.max' hs - s.min' hs).toNat + 1 := by subst hSeq @@ -319,8 +319,8 @@ def ComputesFunInTimeAndSpace open Classical in /-- The indicator function of a language. -/ -noncomputable def indicator (l : Language Symbol) : List Symbol → List Symbol - | x => if x ∈ l then [default] else [] +noncomputable def indicator (L : Language Symbol) : List Symbol → List Symbol + | x => if x ∈ L then [default] else [] /-- A proof that a Turing machine `tm` decides a language `l` with time and space bounds. -/ def DecidesLanguageInTimeAndSpace