From a5af9968b47bef8f7d05b08f8680e45a6e862e40 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sat, 31 Oct 2026 18:10:22 +0000 Subject: [PATCH 01/36] =?UTF-8?q?feat(treeeval):=20prove=20packed=20Cook?= =?UTF-8?q?=E2=80=93Mertz=20algebra=20correctness?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Complexity.lean | 2 + .../Computability/TreeEval/Ary/CookMertz.lean | 137 +++++++++++++---- .../Computability/TreeEval/Ary/Packed.lean | 143 ++++++++++++++++++ .../Data/Code/LowDegreeExtension/UnitSum.lean | 57 +++++++ ComplexityTests/TreeEvalPacked.lean | 74 +++++++++ 5 files changed, 380 insertions(+), 33 deletions(-) create mode 100644 Complexity/Computability/TreeEval/Ary/Packed.lean create mode 100644 Complexity/Foundations/Data/Code/LowDegreeExtension/UnitSum.lean create mode 100644 ComplexityTests/TreeEvalPacked.lean diff --git a/Complexity.lean b/Complexity.lean index 22abea1067..7b4c058164 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2302,6 +2302,7 @@ public import Complexity.Computability.RAM.Squaring public import Complexity.Computability.Reduction.ManyOne public import Complexity.Computability.TreeEval.Ary.Basic public import Complexity.Computability.TreeEval.Ary.CookMertz +public import Complexity.Computability.TreeEval.Ary.Packed public import Complexity.Computability.TreeEval.Basic public import Complexity.Computability.TreeEval.CookMertz public import Complexity.Computability.TreeEval.Encoding @@ -2876,6 +2877,7 @@ public import Complexity.Foundations.Data.Code.ListDecoding public import Complexity.Foundations.Data.Code.LocalDecoding public import Complexity.Foundations.Data.Code.LocalListDecoding public import Complexity.Foundations.Data.Code.LowDegreeExtension +public import Complexity.Foundations.Data.Code.LowDegreeExtension.UnitSum public import Complexity.Foundations.Data.Code.Multilinearity public import Complexity.Foundations.Data.Code.MultilinearitySoundness public import Complexity.Foundations.Data.Code.ReedMuller diff --git a/Complexity/Computability/TreeEval/Ary/CookMertz.lean b/Complexity/Computability/TreeEval/Ary/CookMertz.lean index 2659785d35..83c82be4cc 100644 --- a/Complexity/Computability/TreeEval/Ary/CookMertz.lean +++ b/Complexity/Computability/TreeEval/Ary/CookMertz.lean @@ -31,6 +31,11 @@ register `t`, and then undoes the recursive calls and the scaling. Summed over a cancel (`FiniteField.sum_units_eval_mul_add`), because the extension has degree at most `d * ℓ < |K| - 1`, and register `t` has gained `c • f (v₀, …, v_{d-1})`. +`procWith` separates the Boolean value length from the register width and accepts a supplied +value representation and gate extension. Its correctness requires only a signed unmasking law; +this lets packed finite-alphabet extensions reuse the same masking proof. The original +multilinear procedure is its exact specialization (`procWith_embed`). + This file proves correctness only; space bounds are separate. ## Main definitions @@ -39,9 +44,14 @@ This file proves correctness only; space bounds are separate. * `Cslib.TreeEval.Ary.reg`: the register of a child. * `Cslib.TreeEval.Ary.gatePoly`: the multilinear extension of an output bit of a gate. * `Cslib.TreeEval.Ary.proc`: the procedure, parametrized by an enumeration of the units of `K`. +* `Cslib.TreeEval.Ary.procWith`: the procedure with a supplied representation and gate extension. ## Main results +* `Cslib.TreeEval.Ary.procWith_eq`: correctness for any supplied extension satisfying unmasking. +* `Cslib.TreeEval.Ary.procWith_embed`: the multilinear procedure is a specialization, round for + round. + * `Cslib.TreeEval.Ary.proc_eq`: the procedure adds `c` times the value of the tree to the target register and leaves the other registers unchanged. * `Cslib.TreeEval.Ary.proc_zero`: started from zero registers, it leaves the value of the tree in @@ -132,6 +142,45 @@ noncomputable def proc (us : List Kˣ) : | _, .node f ch, c, t, S => us.foldl (fun S u => gateRound (fun j => proc us (ch j)) f c t u S) S +variable {r : ℕ} + +/-- Subtract a coefficient times a supplied gate extension from the target register. The +extension may use a different number of field coordinates from the gate's bit length. -/ +def gateUpdateWith (E : Gate d ℓ → (Fin d → Fin r → K) → Fin r → K) + (f : Gate d ℓ) (c : K) (t : Fin (d + 1)) (S : State K d r) : State K d r := + Function.update S t fun i => S t i - c * E f (fun j => S (reg t j)) i + +/-- A masking round with a supplied gate extension, sharing the same registers across recursive +calls. No polynomial representation or relation between bit length and register width is needed. -/ +def gateRoundWith (E : Gate d ℓ → (Fin d → Fin r → K) → Fin r → K) + (p : Fin d → K → Fin (d + 1) → State K d r → State K d r) + (f : Gate d ℓ) (c : K) (t : Fin (d + 1)) (u : Kˣ) (S : State K d r) : State K d r := + let S := (List.finRange d).foldl (fun S j => p j 1 (reg t j) (scale (reg t j) (u : K) S)) S + let S := gateUpdateWith E f c t S + (List.finRange d).foldl + (fun S j => scale (reg t j) ((u⁻¹ : Kˣ) : K) (p j (-1) (reg t j) S)) S + +/-- The Cook–Mertz procedure with a supplied representation of bit values and gate extension. +The representation has `r` field coordinates, independently of the `ℓ` bits in a value. -/ +def procWith (e : (Fin ℓ → Bool) → Fin r → K) + (E : Gate d ℓ → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) : + {h : ℕ} → Tree d ℓ h → K → Fin (d + 1) → State K d r → State K d r + | _, .leaf v, c, t, S => Function.update S t (S t + c • e v) + | _, .node f ch, c, t, S => + us.foldl (fun S u => gateRoundWith E (fun j => procWith e E us (ch j)) f c t u S) S + +/-- The multilinear procedure is the supplied-extension procedure with one field coordinate +per bit. This specialization preserves every recursive masking round. -/ +theorem procWith_embed (us : List Kˣ) {h : ℕ} (T : Tree d ℓ h) : + procWith embed (fun f x i => eval (fun q => x q.1 q.2) (gatePoly f i)) us T = + proc us T := by + induction T with + | leaf v => rfl + | node f ch ih => + funext c t S + simp only [procWith, proc, ih] + rfl + omit [Field K] in /-- Updating each child register in turn, as a function of its previous content, updates all registers other than the target. -/ @@ -156,63 +205,91 @@ private theorem foldl_update_reg (t : Fin (d + 1)) (g : Fin (d + 1) → (Fin ℓ /-- A round, when all subtree procedures meet their specification, changes only the target register, subtracting `c` times the gate's extension at the masked children's values. -/ -private theorem gateRound_eq {p : Fin d → K → Fin (d + 1) → State K d ℓ → State K d ℓ} - {v : Fin d → Fin ℓ → K} (hp : ∀ j c t S, p j c t S = Function.update S t (S t + c • v j)) - (f : Gate d ℓ) (c : K) (t : Fin (d + 1)) (u : Kˣ) (S : State K d ℓ) : - gateRound p f c t u S = Function.update S t fun i => - S t i - c * eval (fun q => (u : K) * S (reg t q.1) q.2 + v q.1 q.2) (gatePoly f i) := by +private theorem gateRoundWith_eq + (E : Gate d ℓ → (Fin d → Fin r → K) → Fin r → K) + {p : Fin d → K → Fin (d + 1) → State K d r → State K d r} + {v : Fin d → Fin r → K} (hp : ∀ j c t S, p j c t S = Function.update S t (S t + c • v j)) + (f : Gate d ℓ) (c : K) (t : Fin (d + 1)) (u : Kˣ) (S : State K d r) : + gateRoundWith E p f c t u S = Function.update S t fun i => + S t i - c * E f (fun j k => (u : K) * S (reg t j) k + v j k) i := by have hnd := List.nodup_finRange d - have e1 (S : State K d ℓ) : + have e1 (S : State K d r) : (List.finRange d).foldl (fun S j => p j 1 (reg t j) (scale (reg t j) (u : K) S)) S = fun r => if r ≠ t then (u : K) • S r + childVal t v r else S r := by have h1 : (fun S j => p j 1 (reg t j) (scale (reg t j) (u : K) S)) = - fun (S : State K d ℓ) j => Function.update S (reg t j) + fun (S : State K d r) j => Function.update S (reg t j) ((fun r x => (u : K) • x + childVal t v r) (reg t j) (S (reg t j))) := by funext S j simp [hp, scale] rw [h1, foldl_update_reg t (fun r x => (u : K) • x + childVal t v r) _ hnd] simp only [mem_map_reg_finRange] - have e3 (S : State K d ℓ) : + have e3 (S : State K d r) : (List.finRange d).foldl (fun S j => scale (reg t j) ((u⁻¹ : Kˣ) : K) (p j (-1) (reg t j) S)) S = fun r => if r ≠ t then ((u⁻¹ : Kˣ) : K) • (S r - childVal t v r) else S r := by have h3 : (fun S j => scale (reg t j) ((u⁻¹ : Kˣ) : K) (p j (-1) (reg t j) S)) = - fun (S : State K d ℓ) j => Function.update S (reg t j) + fun (S : State K d r) j => Function.update S (reg t j) ((fun r x => ((u⁻¹ : Kˣ) : K) • (x - childVal t v r)) (reg t j) (S (reg t j))) := by funext S j simp [hp, scale, sub_eq_add_neg] rw [h3, foldl_update_reg t (fun r x => ((u⁻¹ : Kˣ) : K) • (x - childVal t v r)) _ hnd] simp only [mem_map_reg_finRange] - dsimp only [gateRound] + have harg (t : Fin (d + 1)) : + (fun j => (u : K) • S (reg t j) + v j) = + (fun j k => (u : K) * S (reg t j) k + v j k) := rfl + dsimp only [gateRoundWith] rw [e1, e3] funext r i by_cases hr : r = t · subst hr - simp [gateUpdate, reg_ne] - · simp [gateUpdate, hr] + simp [gateUpdateWith, reg_ne, harg] + · simp [gateUpdateWith, hr] /-- The rounds for all units in `us` change only the target register, subtracting `c` times the sum of the gate's extension at the masked children's values. -/ -private theorem foldl_gateRound_eq {p : Fin d → K → Fin (d + 1) → State K d ℓ → State K d ℓ} - {v : Fin d → Fin ℓ → K} (hp : ∀ j c t S, p j c t S = Function.update S t (S t + c • v j)) - (f : Gate d ℓ) (c : K) (t : Fin (d + 1)) (us : List Kˣ) (S : State K d ℓ) : - us.foldl (fun S u => gateRound p f c t u S) S = Function.update S t fun i => +private theorem foldl_gateRoundWith_eq + (E : Gate d ℓ → (Fin d → Fin r → K) → Fin r → K) + {p : Fin d → K → Fin (d + 1) → State K d r → State K d r} + {v : Fin d → Fin r → K} (hp : ∀ j c t S, p j c t S = Function.update S t (S t + c • v j)) + (f : Gate d ℓ) (c : K) (t : Fin (d + 1)) (us : List Kˣ) (S : State K d r) : + us.foldl (fun S u => gateRoundWith E p f c t u S) S = Function.update S t fun i => S t i - c * (us.map fun u : Kˣ => - eval (fun q => (u : K) * S (reg t q.1) q.2 + v q.1 q.2) (gatePoly f i)).sum := by + E f (fun j k => (u : K) * S (reg t j) k + v j k) i).sum := by induction us generalizing S with | nil => simp | cons u us ih => - rw [List.foldl_cons, gateRound_eq hp, ih, Function.update_idem, Function.update_self] + rw [List.foldl_cons, gateRoundWith_eq E hp, ih, Function.update_idem, Function.update_self] congr 1 funext i have hreg (j : Fin d) : Function.update S t (fun i => - S t i - c * eval (fun q => (u : K) * S (reg t q.1) q.2 + v q.1 q.2) (gatePoly f i)) + S t i - c * E f (fun j k => (u : K) * S (reg t j) k + v j k) i) (reg t j) = S (reg t j) := Function.update_of_ne (reg_ne t j) _ _ simp only [hreg, List.map_cons, List.sum_cons] ring variable [Fintype K] +/-- **Correctness with a supplied gate extension.** The unmasking identity is the only +property required of the representation and extension: the sum over all nonzero masks must be +minus the represented gate value. The conclusion holds from arbitrary initial register contents. -/ +theorem procWith_eq [DecidableEq K] + (e : (Fin ℓ → Bool) → Fin r → K) + (E : Gate d ℓ → (Fin d → Fin r → K) → Fin r → K) + (hE : ∀ (f : Gate d ℓ) (v : Fin d → Fin ℓ → Bool) (τ : Fin d → Fin r → K) (i : Fin r), + ∑ u : Kˣ, E f (fun j k => (u : K) * τ j k + e (v j) k) i = -e (f v) i) + {us : List Kˣ} (hnd : us.Nodup) (hmem : ∀ u, u ∈ us) + {h : ℕ} (T : Tree d ℓ h) (c : K) (t : Fin (d + 1)) (S : State K d r) : + procWith e E us T c t S = Function.update S t (S t + c • e T.eval) := by + induction T generalizing c t S with + | leaf v => rfl + | node f ch ih => + rw [procWith, foldl_gateRoundWith_eq E (v := fun j => e (ch j).eval) fun j => ih j] + congr 1 + funext i + rw [← List.sum_toFinset _ hnd, show us.toFinset = Finset.univ by ext; simp [hmem]] + rw [hE] + simp [Tree.eval] + /-- **Correctness of the Cook–Mertz procedure for fan-in `d`.** If `us` enumerates the units of `K` without repetition and `d * ℓ < |K| - 1`, then the procedure adds `c` times the value of the tree to the target register and leaves the other registers unchanged, whatever their initial @@ -221,20 +298,14 @@ theorem proc_eq {us : List Kˣ} (hnd : us.Nodup) (hmem : ∀ u, u ∈ us) (hK : d * ℓ < Fintype.card K - 1) {h : ℕ} (T : Tree d ℓ h) (c : K) (t : Fin (d + 1)) (S : State K d ℓ) : proc us T c t S = Function.update S t (S t + c • embed T.eval) := by classical - induction T generalizing c t S with - | leaf v => rfl - | node f ch ih => - rw [proc, foldl_gateRound_eq (v := fun j => embed (ch j).eval) fun j => ih j] - congr 1 - funext i - have hdeg : (gatePoly (K := K) f i).totalDegree < Fintype.card K - 1 := - (totalDegree_multilinearExtension_le _).trans_lt <| by simpa using hK - rw [← List.sum_toFinset _ hnd, show us.toFinset = Finset.univ by ext; simp [hmem]] - have hsum := FiniteField.sum_units_eval_mul_add hdeg - (fun q => S (reg t q.1) q.2) (fun q => bitVal K ((ch q.1).eval q.2)) - simp only [embed] at hsum ⊢ - rw [hsum, gatePoly, eval_multilinearExtension] - simp [embed] + rw [← procWith_embed] + apply procWith_eq embed (fun f x i => eval (fun q => x q.1 q.2) (gatePoly f i)) + (fun f v τ i => ?_) hnd hmem + have hdeg : (gatePoly (K := K) f i).totalDegree < Fintype.card K - 1 := + (totalDegree_multilinearExtension_le _).trans_lt <| by simpa using hK + have hsum := FiniteField.sum_units_eval_mul_add hdeg + (fun q => τ q.1 q.2) (fun q => bitVal K (v q.1 q.2)) + simpa only [gatePoly, eval_multilinearExtension, embed] using hsum /-- Started from zero registers, the procedure leaves the value of the tree in the target register. -/ diff --git a/Complexity/Computability/TreeEval/Ary/Packed.lean b/Complexity/Computability/TreeEval/Ary/Packed.lean new file mode 100644 index 0000000000..fe3f8233c7 --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed.lean @@ -0,0 +1,143 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.CookMertz +public import Complexity.Foundations.Data.BitString.Block +public import Complexity.Foundations.Data.BitString.Prefix +public import Complexity.Foundations.Data.Code.LowDegreeExtension.UnitSum + +/-! +# Packed gate extensions for Cook–Mertz + +Bit strings are grouped into chunks and embedded in a finite field, so one register coordinate +can hold several bits. A final partial chunk is zero-padded. The gate extension interpolates on +the chunk alphabet rather than separately on each bit. + +This is the algebraic packing step of [Williams2025], Appendix A, equation (6). The field may +have arbitrary characteristic: signed recursive calls and unit sums replace the source's +characteristic-two cancellations. With `r` chunks and alphabet `H`, the strict degree condition +is `d * r * (|H| - 1) < |K| - 1`. + +## Main definitions + +* `Cslib.TreeEval.Ary.Packed.packBits`, `Cslib.TreeEval.Ary.Packed.unpackBits`: packing and + decoding a bit string, allowing a final partial chunk. +* `Cslib.TreeEval.Ary.Packed.gateExtension`: interpolation on the packed alphabet. + +## Main results + +* `Cslib.TreeEval.Ary.Packed.unpackBits_packBits`: decoding recovers the original bits. +* `Cslib.TreeEval.Ary.Packed.sum_units_gateExtension`: packed gates satisfy unmasking. +* `Cslib.TreeEval.Ary.Packed.procWith_gateExtension_eq`: packed procedure correctness. + +## Scope + +The results here establish algebraic correctness only. They do not certify native packing, +implicit gate queries, the refined space bound, or Williams's time-to-space simulation. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed + +open Cslib.BitString + +/-- Read an exactly aligned bit string as `r` chunks of `m` bits. -/ +@[expose] def pack {r m : ℕ} (v : Fin (r * m) → Bool) : Fin r → Fin (2 ^ m) := + fun j => equivFin m (block v j) + +/-- Concatenate the binary representations of the chunks. -/ +@[expose] def unpack {r m : ℕ} (v : Fin r → Fin (2 ^ m)) : Fin (r * m) → Bool := + blocksEquiv.symm fun j => (equivFin m).symm (v j) + +/-- Chunking preserves every bit, including zero-valued chunks. -/ +@[simp] theorem unpack_pack {r m : ℕ} (v : Fin (r * m) → Bool) : unpack (pack v) = v := by + simp only [unpack, pack, Equiv.symm_apply_apply] + exact blocksEquiv.symm_apply_apply v + +/-- All tuples of chunks are valid aligned encodings. -/ +@[simp] theorem pack_unpack {r m : ℕ} (v : Fin r → Fin (2 ^ m)) : pack (unpack v) = v := by + funext j + simp [pack, unpack] + +/-- Pack a bit string after zero-padding a final partial chunk. -/ +@[expose] def packBits {b r m : ℕ} (_hb : b ≤ r * m) (v : Fin b → Bool) : + Fin r → Fin (2 ^ m) := pack (zeroExtend v) + +/-- Decode the original prefix of a padded chunk tuple. -/ +@[expose] def unpackBits {b r m : ℕ} (hb : b ≤ r * m) (v : Fin r → Fin (2 ^ m)) : + Fin b → Bool := restrictBitsAddHom hb (unpack v) + +/-- Decoding padded chunks recovers the original bit string. -/ +@[simp] theorem unpackBits_packBits {b r m : ℕ} (hb : b ≤ r * m) (v : Fin b → Bool) : + unpackBits hb (packBits hb v) = v := by + simp [unpackBits, packBits] + +variable {K H : Type*} [Field K] [Fintype H] [DecidableEq H] {b d r : ℕ} + +/-- Extend a Boolean gate after encoding each value in `r` alphabet symbols. On symbols outside +the encoding's image, the caller's decoder determines a total table. -/ +noncomputable def gateExtension (h : H ↪ K) (encode : (Fin b → Bool) → Fin r → H) + (decode : (Fin r → H) → Fin b → Bool) (f : Gate d b) + (x : Fin d → Fin r → K) (i : Fin r) : K := + Code.lowDegreeExtension h + (fun z => h (encode (f fun j => decode (blocksEquiv z j)) i)) + (blocksEquiv.symm x) + +/-- On encoded child values the gate extension agrees with the encoded gate output. -/ +theorem gateExtension_embed (h : H ↪ K) (encode : (Fin b → Bool) → Fin r → H) + (decode : (Fin r → H) → Fin b → Bool) (hdecode : Function.LeftInverse decode encode) + (f : Gate d b) (v : Fin d → Fin b → Bool) (i : Fin r) : + gateExtension h encode decode f (fun j k => h (encode (v j) k)) i = + h (encode (f v) i) := by + change Code.lowDegreeExtension h _ + (fun p => h (blocksEquiv.symm (fun j => encode (v j)) p)) = _ + rw [Code.lowDegreeExtension_apply_embed] + simp only [Equiv.apply_symm_apply] + exact congrArg (fun w => h (encode (f w) i)) (funext fun j => hdecode (v j)) + +variable [Fintype K] + +/-- Packed interpolation satisfies the signed unmasking law needed by Cook–Mertz, with only +`d*r` field coordinates and degree at most `d*r*(|H|-1)`. -/ +theorem sum_units_gateExtension [DecidableEq K] (h : H ↪ K) (encode : (Fin b → Bool) → Fin r → H) + (decode : (Fin r → H) → Fin b → Bool) (hdecode : Function.LeftInverse decode encode) + (hD : d * r * (Fintype.card H - 1) < Fintype.card K - 1) + (f : Gate d b) (τ : Fin d → Fin r → K) (v : Fin d → Fin b → Bool) (i : Fin r) : + ∑ u : Kˣ, gateExtension h encode decode f + (fun j k => (u : K) * τ j k + h (encode (v j) k)) i = + -h (encode (f v) i) := by + classical + have hs := Code.sum_units_lowDegreeExtension_mul_add h + (fun z => h (encode (f fun j => decode (blocksEquiv z j)) i)) hD + (blocksEquiv.symm τ) (blocksEquiv.symm fun j => encode (v j)) + change (∑ u : Kˣ, Code.lowDegreeExtension h + (fun z => h (encode (f fun j => decode (blocksEquiv z j)) i)) + (fun p => (u : K) * blocksEquiv.symm τ p + + h (blocksEquiv.symm (fun j => encode (v j)) p))) = _ + rw [hs] + simp only [Equiv.apply_symm_apply] + exact congrArg (fun w => -h (encode (f w) i)) (funext fun j => hdecode (v j)) + +/-- The Cook–Mertz procedure on packed registers adds the encoded root value and restores every +other register. The representation need only have a left inverse, so a partial last chunk is +allowed. This is an algebraic specification, without a native-machine space claim. -/ +theorem procWith_gateExtension_eq (h : H ↪ K) + (encode : (Fin b → Bool) → Fin r → H) + (decode : (Fin r → H) → Fin b → Bool) (hdecode : Function.LeftInverse decode encode) + (hD : d * r * (Fintype.card H - 1) < Fintype.card K - 1) + {us : List Kˣ} (hnd : us.Nodup) (hmem : ∀ u, u ∈ us) + {height : ℕ} (T : Tree d b height) (c : K) (t : Fin (d + 1)) (S : State K d r) : + procWith (fun v i => h (encode v i)) (gateExtension h encode decode) us T c t S = + Function.update S t (S t + c • fun i => h (encode T.eval i)) := by + classical + exact procWith_eq _ _ (fun f v τ i => sum_units_gateExtension h encode decode hdecode hD f τ v i) + hnd hmem T c t S + +end Cslib.TreeEval.Ary.Packed diff --git a/Complexity/Foundations/Data/Code/LowDegreeExtension/UnitSum.lean b/Complexity/Foundations/Data/Code/LowDegreeExtension/UnitSum.lean new file mode 100644 index 0000000000..66d97c8a0c --- /dev/null +++ b/Complexity/Foundations/Data/Code/LowDegreeExtension/UnitSum.lean @@ -0,0 +1,57 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Foundations.Data.Code.LowDegreeExtension +public import Complexity.Foundations.FieldTheory.Finite.UnitSum + +/-! +# Unmasking low-degree extensions + +Summing a low-degree function along a punctured affine line removes its mask. This combines the +existing Reed–Muller polynomial witness with the finite-field unit-sum identity. For a function +on `n` coordinates over an alphabet `H`, the sufficient bound is strictly +`n * (|H| - 1) < |F| - 1`. + +## Main results + +* `Cslib.Code.sum_units_mul_add_of_mem_reedMuller`: unmask a Reed–Muller codeword. +* `Cslib.Code.sum_units_lowDegreeExtension_mul_add`: unmask an extension at a grid point. + +## References + +* [R. Williams, *Simulating Time With Square-Root Space*][Williams2025], Appendix A, + equations (5)–(6). The signed identity here works in arbitrary finite characteristic; + in characteristic two its minus sign disappears. +-/ + +public section + +namespace Cslib.Code + +variable {F : Type*} [Field F] [Fintype F] [DecidableEq F] {n D : ℕ} + +/-- A Reed–Muller codeword below the strict unit-sum degree threshold loses its mask when +summed over the units. -/ +theorem sum_units_mul_add_of_mem_reedMuller {f : (Fin n → F) → F} + (hf : f ∈ reedMuller F n D) (hD : D < Fintype.card F - 1) (τ x : Fin n → F) : + ∑ u : Fˣ, f (fun i => (u : F) * τ i + x i) = -f x := by + obtain ⟨P, hP, rfl⟩ := mem_reedMuller.mp hf + exact FiniteField.sum_units_eval_mul_add (hP.trans_lt hD) τ x + +/-- Summing the masked extension at a grid point gives minus the original table entry. +The strict bound is on the total degree, not just on the alphabet size. -/ +theorem sum_units_lowDegreeExtension_mul_add {H : Type*} [Fintype H] [DecidableEq H] + (h : H ↪ F) (f : (Fin n → H) → F) + (hD : n * (Fintype.card H - 1) < Fintype.card F - 1) + (τ : Fin n → F) (a : Fin n → H) : + ∑ u : Fˣ, lowDegreeExtension h f (fun i => (u : F) * τ i + h (a i)) = -f a := by + rw [sum_units_mul_add_of_mem_reedMuller + (lowDegreeExtension_mem_reedMuller h f) hD, lowDegreeExtension_apply_embed] + +end Cslib.Code diff --git a/ComplexityTests/TreeEvalPacked.lean b/ComplexityTests/TreeEvalPacked.lean new file mode 100644 index 0000000000..d217f54bb8 --- /dev/null +++ b/ComplexityTests/TreeEvalPacked.lean @@ -0,0 +1,74 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module + +public import Complexity.Computability.TreeEval.Ary.Packed +public import Mathlib.Data.ZMod.Basic + +/-! # Packed Cook–Mertz regressions + +A three-bit value uses two two-bit chunks, including a partially filled last chunk. The +nonbinary alphabet embeds in `ZMod 17`. Correctness holds from nonzero, unequal register contents. +The degree-two example over `ZMod 3` checks failure at the strict unit-sum threshold. +-/ + +@[expose] public section + +open Cslib.TreeEval Cslib.TreeEval.Ary Cslib.TreeEval.Ary.Packed + +namespace TreeEvalPackedTests + +instance : Fact (Nat.Prime 17) := ⟨by decide⟩ + +def alphabet : Fin 4 ↪ ZMod 17 where + toFun i := i.val + inj' := by + intro a b h + have h' := congrArg ZMod.val h + simpa only [ZMod.val_natCast, Nat.mod_eq_of_lt (a.isLt.trans (by decide : 4 < 17)), + Nat.mod_eq_of_lt (b.isLt.trans (by decide : 4 < 17)), Fin.val_inj] using h' + +def encode : (Fin 3 → Bool) → Fin 2 → Fin 4 := packBits (r := 2) (m := 2) (by decide) +def decode : (Fin 2 → Fin 4) → Fin 3 → Bool := unpackBits (r := 2) (m := 2) (by decide) + +example : encode ![true, false, true] = ![1, 1] := by decide +example : decode ![1, 3] = ![true, false, true] := by decide + +example (v : Fin 3 → Bool) : decode (encode v) = v := + unpackBits_packBits (r := 2) (m := 2) (by decide) v + +def gate : Gate 2 3 := fun v i => v 0 i ^^ v 1 i +def tree : Ary.Tree 2 3 1 := + .node gate ![.leaf ![true, false, true], .leaf ![true, true, false]] + +example : tree.eval = ![false, true, true] := by decide +example : encode tree.eval = ![2, 1] := by decide + +/-- Two packed coordinates replace three separate field coordinates; all other registers are +restored, even with arbitrary masks and a nontrivial scalar. -/ +theorem packed_procedure_spec (c : ZMod 17) (S : Ary.State (ZMod 17) 2 2) : + procWith (fun v i => alphabet (encode v i)) (gateExtension alphabet encode decode) + (Finset.univ : Finset (ZMod 17)ˣ).toList tree c 2 S = + Function.update S 2 (S 2 + c • fun i => alphabet (encode tree.eval i)) := by + apply procWith_gateExtension_eq alphabet encode decode + (fun v => unpackBits_packBits (r := 2) (m := 2) (by decide) v) + (by decide) (Finset.nodup_toList _) (fun u => Finset.mem_toList.mpr (Finset.mem_univ u)) + +/-- The packed low-degree extension really returns a non-Boolean field coordinate. -/ +example : gateExtension alphabet encode decode gate + (fun j k => alphabet (encode (![![true, false, true], ![true, true, false]] j) k)) 0 = 2 := by + rw [gateExtension_embed alphabet encode decode + (fun v => unpackBits_packBits (r := 2) (m := 2) (by decide) v)] + decide + +/-- At degree `|F|-1`, the cancellation identity can fail: `P(X,Y)=XY` on the line `(u,u)`. +Its value at the origin is zero, but its unit sum over `F₃` is two. -/ +example : (∑ u : (ZMod 3)ˣ, (u : ZMod 3) * (u : ZMod 3)) = 2 := by decide + +example : (∑ u : (ZMod 3)ˣ, (u : ZMod 3) * (u : ZMod 3)) ≠ -(0 : ZMod 3) := by decide + +end TreeEvalPackedTests From b7c410ad76df273e29c1ac045e66d9f6e20e3a1b Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sat, 31 Oct 2026 18:44:21 +0000 Subject: [PATCH 02/36] feat(treeeval): bound packed field parameters and encoded storage --- Complexity.lean | 1 + .../TreeEval/Ary/PackingParameters.lean | 210 ++++++++++++++++++ .../TreeEvalPackingParameters.lean | 76 +++++++ 3 files changed, 287 insertions(+) create mode 100644 Complexity/Computability/TreeEval/Ary/PackingParameters.lean create mode 100644 ComplexityTests/TreeEvalPackingParameters.lean diff --git a/Complexity.lean b/Complexity.lean index 22abea1067..5b9997083f 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2302,6 +2302,7 @@ public import Complexity.Computability.RAM.Squaring public import Complexity.Computability.Reduction.ManyOne public import Complexity.Computability.TreeEval.Ary.Basic public import Complexity.Computability.TreeEval.Ary.CookMertz +public import Complexity.Computability.TreeEval.Ary.PackingParameters public import Complexity.Computability.TreeEval.Basic public import Complexity.Computability.TreeEval.CookMertz public import Complexity.Computability.TreeEval.Encoding diff --git a/Complexity/Computability/TreeEval/Ary/PackingParameters.lean b/Complexity/Computability/TreeEval/Ary/PackingParameters.lean new file mode 100644 index 0000000000..7983825325 --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/PackingParameters.lean @@ -0,0 +1,210 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Foundations.Data.Nat.BinarySize +public import Complexity.Foundations.Data.Nat.CeilDiv +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Nat.Bits +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.List.Size +public import Mathlib.Data.ZMod.Basic +import Mathlib.NumberTheory.Bertrand + +/-! +# Field parameters and encoded storage for packed Cook–Mertz + +For positive fan-in `d` and Boolean value length `b`, choose chunk width `m = (d*b).size`, +chunk count `r = ⌈b/m⌉`, and the least prime `p ≥ d*r*(2^m-1)+2`. The chunk alphabet embeds +in `ZMod p`, and the packed gate's degree is strictly below `p-1`. + +Bertrand's postulate bounds the bit length of `p` by `2m+2`. The framed binary list encoding +of `(d+1)*r` residues therefore has length at most `9*(d+1)*(b+m)`. For fixed fan-in this is +affine in `b`; no uniform constant independent of `d` is asserted. + +## Main definitions + +* `Cslib.TreeEval.Ary.Packed.chunkWidth`, `Cslib.TreeEval.Ary.Packed.chunkCount`: chunk sizes. +* `Cslib.TreeEval.Ary.Packed.gateDegree`: the packed interpolation degree bound. +* `Cslib.TreeEval.Ary.Packed.fieldModulus`: a sufficient prime modulus. +* `Cslib.TreeEval.Ary.Packed.alphabetEmbedding`: the chunk alphabet in that prime field. + +## Main results + +* `Cslib.TreeEval.Ary.Packed.gateDegree_lt`: the strict unmasking threshold. +* `Cslib.TreeEval.Ary.Packed.fieldModulus_size_le`: field elements use at most `2m+2` bits. +* `Cslib.TreeEval.Ary.Packed.length_encList_residues_le`: encoded global-register storage. + +## References + +* [R. Williams, *Simulating Time With Square-Root Space*][Williams2025], Appendix A, + equation (6) and the final storage calculation. The source uses characteristic-two fields; + this prime-field parameter choice is an original variant for the signed unmasking identity. + +These are parameter and representation-length bounds. No native prime-search, arithmetic, +packed evaluator or implicit gate-query space certificate is proved here. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed + +/-- Bits per chunk: the binary length of the total Boolean input width of a gate. -/ +@[expose] def chunkWidth (d b : ℕ) : ℕ := (d * b).size + +/-- Number of chunks, with a possibly incomplete final chunk. -/ +@[expose] def chunkCount (d b : ℕ) : ℕ := b ⌈/⌉ chunkWidth d b + +/-- Total-degree bound for interpolation on the chunk alphabet. -/ +@[expose] def gateDegree (d b : ℕ) : ℕ := d * chunkCount d b * (2 ^ chunkWidth d b - 1) + +/-- The least prime strictly above the packed degree plus one. Its search cost is not certified. -/ +@[expose] def fieldModulus (d b : ℕ) : ℕ := + Nat.find (Nat.exists_infinite_primes (gateDegree d b + 2)) + +/-- The chosen modulus is prime. -/ +theorem fieldModulus_prime (d b : ℕ) : (fieldModulus d b).Prime := + (Nat.find_spec (Nat.exists_infinite_primes (gateDegree d b + 2))).2 + +/-- Two units of slack keep the degree strictly below the unit-group order. -/ +theorem gateDegree_add_two_le (d b : ℕ) : gateDegree d b + 2 ≤ fieldModulus d b := + (Nat.find_spec (Nat.exists_infinite_primes (gateDegree d b + 2))).1 + +/-- The sufficient degree threshold is strict, including the smallest parameters. -/ +theorem gateDegree_lt (d b : ℕ) : gateDegree d b < fieldModulus d b - 1 := by + have := gateDegree_add_two_le d b + omega + +/-- Bertrand's postulate bounds the least sufficient prime. -/ +theorem fieldModulus_le (d b : ℕ) : fieldModulus d b ≤ 2 * (gateDegree d b + 1) := by + obtain ⟨q, hq, hlo, hhi⟩ := + Nat.exists_prime_lt_and_le_two_mul (gateDegree d b + 1) (by omega) + have hmin := Nat.find_min' (Nat.exists_infinite_primes (gateDegree d b + 2)) + (show gateDegree d b + 2 ≤ q ∧ q.Prime from ⟨by omega, hq⟩) + exact hmin.trans hhi + +variable {d b : ℕ} + +/-- Nonempty gates and values have positive chunk width. -/ +theorem chunkWidth_pos (hd : 0 < d) (hb : 0 < b) : 0 < chunkWidth d b := + Nat.size_pos.mpr (Nat.mul_pos hd hb) + +/-- The chunks cover all bits of the value. -/ +theorem le_chunkCount_mul (hd : 0 < d) (hb : 0 < b) : + b ≤ chunkCount d b * chunkWidth d b := + Nat.le_ceilDiv_mul (chunkWidth_pos hd hb) + +/-- Padding adds less than one chunk. -/ +theorem chunkCount_mul_lt (hd : 0 < d) (hb : 0 < b) : + chunkCount d b * chunkWidth d b < b + chunkWidth d b := + Nat.ceilDiv_mul_lt (chunkWidth_pos hd hb) + +/-- Positive-width chunks are no more numerous than the original bits. -/ +theorem chunkCount_le (hd : 0 < d) (hb : 0 < b) : chunkCount d b ≤ b := by + apply (Nat.ceilDiv_le_iff (chunkWidth_pos hd hb)).mpr + have := chunkWidth_pos hd hb + nlinarith + +/-- A nonempty value occupies at least one chunk. -/ +theorem chunkCount_pos (hd : 0 < d) (hb : 0 < b) : 0 < chunkCount d b := by + have := le_chunkCount_mul hd hb + by_contra hn + have hz : chunkCount d b = 0 := by omega + rw [hz, Nat.zero_mul] at this + omega + +/-- The whole chunk alphabet fits below the modulus, not just the encoded Boolean values. -/ +theorem alphabetSize_lt (hd : 0 < d) (hb : 0 < b) : + 2 ^ chunkWidth d b < fieldModulus d b := by + have hprod : 1 ≤ d * chunkCount d b := Nat.mul_pos hd (chunkCount_pos hd hb) + have hpow : 1 ≤ 2 ^ chunkWidth d b := Nat.one_le_pow _ _ (by decide) + have hdeg := Nat.mul_le_mul_right (2 ^ chunkWidth d b - 1) hprod + have hp := gateDegree_add_two_le d b + simp only [Nat.one_mul] at hdeg + change 2 ^ chunkWidth d b - 1 ≤ gateDegree d b at hdeg + omega + +/-- Embed a chunk as its unsigned value in the selected prime field. -/ +@[expose] def alphabetEmbedding (hd : 0 < d) (hb : 0 < b) : + Fin (2 ^ chunkWidth d b) ↪ ZMod (fieldModulus d b) where + toFun a := a.val + inj' := by + have : NeZero (fieldModulus d b) := ⟨(fieldModulus_prime d b).ne_zero⟩ + intro a a' heq + have heq' := congrArg ZMod.val heq + have ha := a.isLt.trans (alphabetSize_lt hd hb) + have ha' := a'.isLt.trans (alphabetSize_lt hd hb) + simpa only [ZMod.val_natCast, Nat.mod_eq_of_lt ha, Nat.mod_eq_of_lt ha', Fin.val_inj] + using heq' + +/-- The prime field has at most twice the chunk width plus two bits per residue. -/ +theorem fieldModulus_size_le (hd : 0 < d) (hb : 0 < b) : + (fieldModulus d b).size ≤ 2 * chunkWidth d b + 2 := by + have hr := chunkCount_le hd hb + have hlt : d * b < 2 ^ chunkWidth d b := Nat.lt_size_self (d * b) + have hdegree : gateDegree d b < (2 ^ chunkWidth d b) ^ 2 := by + have hpow : 0 < 2 ^ chunkWidth d b := Nat.two_pow_pos _ + have hmul := Nat.mul_le_mul_left d hr + calc + gateDegree d b ≤ (d * chunkCount d b) * 2 ^ chunkWidth d b := + Nat.mul_le_mul_left _ (Nat.sub_le _ _) + _ < 2 ^ chunkWidth d b * 2 ^ chunkWidth d b := + Nat.mul_lt_mul_of_pos_right (hmul.trans_lt hlt) hpow + _ = (2 ^ chunkWidth d b) ^ 2 := (pow_two _).symm + have hp := fieldModulus_le d b + apply Nat.size_le.mpr + rw [Nat.mul_comm 2 (chunkWidth d b), pow_add, pow_mul] + norm_num only [Nat.reducePow] + nlinarith + +/-- One packed register has linear payload size, including a partial final chunk. -/ +theorem chunkCount_mul_fieldModulus_size_le (hd : 0 < d) (hb : 0 < b) : + chunkCount d b * (fieldModulus d b).size ≤ 4 * (b + chunkWidth d b) := by + have hm := chunkWidth_pos hd hb + have hp := fieldModulus_size_le hd hb + have hr := chunkCount_mul_lt hd hb + calc + chunkCount d b * (fieldModulus d b).size ≤ + chunkCount d b * (4 * chunkWidth d b) := Nat.mul_le_mul_left _ (by omega) + _ ≤ 4 * (b + chunkWidth d b) := by nlinarith + +/-- Actual framed binary storage for the `d+1` global registers. All residues, including zero, +and their list framing are charged. Any list with at most the full register count is allowed. -/ +theorem length_encList_residues_le (hd : 0 < d) (hb : 0 < b) (values : List ℕ) + (hlen : values.length ≤ (d + 1) * chunkCount d b) + (hres : ∀ v ∈ values, v < fieldModulus d b) : + (Turing.MultiTapeTM.encList Turing.MultiTapeTM.encNat values).length ≤ + 9 * (d + 1) * (b + chunkWidth d b) := by + have hsize (v : ℕ) (hv : v ∈ values) : + (Turing.MultiTapeTM.encNat v).length ≤ (fieldModulus d b).size := by + rw [Turing.MultiTapeTM.encNat_apply, Nat.size_eq_bits_len] + exact Nat.size_le_size (hres v hv).le + have hlist := Turing.MultiTapeTM.length_encList_le Turing.MultiTapeTM.encNat values hsize + have hp := fieldModulus_size_le hd hb + have hm := chunkWidth_pos hd hb + have hr := chunkCount_mul_lt hd hb + calc + _ ≤ values.length * (2 * (fieldModulus d b).size + 1) := hlist + _ ≤ ((d + 1) * chunkCount d b) * (9 * chunkWidth d b) := + Nat.mul_le_mul hlen (by omega) + _ ≤ 9 * (d + 1) * (b + chunkWidth d b) := by nlinarith + +/-- For a fixed machine's fan-in, the width term is affine in the value length. -/ +theorem chunkWidth_le (d b : ℕ) : chunkWidth d b ≤ d.size + b := by + have hb : b.size ≤ b := Nat.size_le.mpr (Nat.lt_two_pow_self (n := b)) + exact (Nat.size_mul_le d b).trans (by omega) + +/-- An explicit affine bound on the encoded global registers when the fan-in is fixed. -/ +theorem length_encList_residues_le_affine (hd : 0 < d) (hb : 0 < b) (values : List ℕ) + (hlen : values.length ≤ (d + 1) * chunkCount d b) + (hres : ∀ v ∈ values, v < fieldModulus d b) : + (Turing.MultiTapeTM.encList Turing.MultiTapeTM.encNat values).length ≤ + 18 * (d + 1) * b + 9 * (d + 1) * d.size := by + have h := length_encList_residues_le hd hb values hlen hres + have hm := chunkWidth_le d b + nlinarith + +end Cslib.TreeEval.Ary.Packed diff --git a/ComplexityTests/TreeEvalPackingParameters.lean b/ComplexityTests/TreeEvalPackingParameters.lean new file mode 100644 index 0000000000..65e1521f25 --- /dev/null +++ b/ComplexityTests/TreeEvalPackingParameters.lean @@ -0,0 +1,76 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module + +public import Complexity.Computability.TreeEval.Ary.PackingParameters + +/-! +# Packed field-parameter regressions + +With fan-in two and five-bit values, four-bit chunks leave three padding bits. The modulus is +prime, contains every four-bit chunk, and satisfies the strict degree threshold. Actual framed +residue lists test both zero entries and larger residues; a full register table is checked too. +-/ + +public section + +namespace ComplexityTests.TreeEvalPackingParameters + +open Cslib.TreeEval.Ary.Packed Turing.MultiTapeTM + +example : chunkWidth 2 5 = 4 := by decide +example : chunkCount 2 5 = 2 := by decide +example : chunkCount 2 5 * chunkWidth 2 5 - 5 = 3 := by decide +example : gateDegree 2 5 = 60 := by decide + +example : 62 ≤ fieldModulus 2 5 ∧ fieldModulus 2 5 ≤ 122 := + ⟨gateDegree_add_two_le 2 5, fieldModulus_le 2 5⟩ + +example : (fieldModulus 2 5).Prime := fieldModulus_prime 2 5 +example : 60 < fieldModulus 2 5 - 1 := gateDegree_lt 2 5 +example : (fieldModulus 2 5).size ≤ 10 := fieldModulus_size_le (by decide) (by decide) + +/-- The alphabet contains values beyond Boolean coordinates. -/ +theorem distinct_nonbinary_chunks : + alphabetEmbedding (d := 2) (b := 5) (by decide) (by decide) 7 ≠ + alphabetEmbedding (d := 2) (b := 5) (by decide) (by decide) 15 := + (alphabetEmbedding (d := 2) (b := 5) (by decide) (by decide)).injective.ne (by decide) + +example : (encList encNat [0, 1, 7, 15, 31, 61]).length = 44 := by decide +example : (encList encNat (List.replicate 6 0)).length = 6 := by decide + +/-- The smallest nonempty case also has a field meeting the strict degree condition. -/ +example : chunkWidth 1 1 = 1 ∧ chunkCount 1 1 = 1 ∧ gateDegree 1 1 = 1 := by decide + +example : 1 < fieldModulus 1 1 - 1 := gateDegree_lt 1 1 + +/-- Positivity of fan-in is needed for coverage: the chosen width is zero at fan-in zero. -/ +example : ¬5 ≤ chunkCount 0 5 * chunkWidth 0 5 := by decide + +example : (encList encNat [0, 1, 7, 15, 31, 61]).length ≤ 243 := by + apply length_encList_residues_le (d := 2) (b := 5) (by decide) (by decide) + _ (by decide) + intro v hv + have hp : 62 ≤ fieldModulus 2 5 := gateDegree_add_two_le 2 5 + simp only [List.mem_cons, List.not_mem_nil, or_false] at hv + rcases hv with rfl | rfl | rfl | rfl | rfl | rfl <;> omega + +instance : Fact (fieldModulus 2 5).Prime := ⟨fieldModulus_prime 2 5⟩ + +/-- Every full three-register, two-coordinate table fits the concrete bound, with all entries +charged through their canonical unsigned residues and list framing. -/ +theorem full_register_storage (S : Fin 3 → Fin 2 → ZMod (fieldModulus 2 5)) : + (encList encNat (List.ofFn fun i : Fin 6 => + (S ((finProdFinEquiv : Fin 3 × Fin 2 ≃ Fin 6).symm i).1 + ((finProdFinEquiv : Fin 3 × Fin 2 ≃ Fin 6).symm i).2).val)).length ≤ 243 := by + apply length_encList_residues_le (d := 2) (b := 5) (by decide) (by decide) + _ (by rw [List.length_ofFn]; decide) + intro v hv + obtain ⟨i, rfl⟩ := List.mem_ofFn.mp hv + exact ZMod.val_lt _ + +end ComplexityTests.TreeEvalPackingParameters From 8bdc8e526e12e0c70d77b5f7742135335f6423a5 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sat, 31 Oct 2026 21:25:09 +0000 Subject: [PATCH 03/36] chore(treeeval): order the combined packing import aggregate --- Complexity.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Complexity.lean b/Complexity.lean index aaf0582b19..dc52899211 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2302,8 +2302,8 @@ public import Complexity.Computability.RAM.Squaring public import Complexity.Computability.Reduction.ManyOne public import Complexity.Computability.TreeEval.Ary.Basic public import Complexity.Computability.TreeEval.Ary.CookMertz -public import Complexity.Computability.TreeEval.Ary.PackingParameters public import Complexity.Computability.TreeEval.Ary.Packed +public import Complexity.Computability.TreeEval.Ary.PackingParameters public import Complexity.Computability.TreeEval.Basic public import Complexity.Computability.TreeEval.CookMertz public import Complexity.Computability.TreeEval.Encoding From 6bb0602ed606afe48b00b98968c438e91624b552 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sat, 31 Oct 2026 21:35:34 +0000 Subject: [PATCH 04/36] feat(treeeval): evaluate packed interpolation weights in native space --- Complexity.lean | 4 + .../Ary/Packed/GateInterpolation.lean | 166 +++++++++++++++++ .../Ary/Packed/GateInterpolationSpace.lean | 147 +++++++++++++++ .../TreeEval/Ary/Packed/Interpolation.lean | 173 ++++++++++++++++++ .../Ary/Packed/InterpolationSpace.lean | 119 ++++++++++++ ComplexityTests/TreeEvalInterpolation.lean | 72 ++++++++ 6 files changed, 681 insertions(+) create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/GateInterpolation.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/GateInterpolationSpace.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/Interpolation.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/InterpolationSpace.lean create mode 100644 ComplexityTests/TreeEvalInterpolation.lean diff --git a/Complexity.lean b/Complexity.lean index dc52899211..c8d637eb2a 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2303,6 +2303,10 @@ public import Complexity.Computability.Reduction.ManyOne public import Complexity.Computability.TreeEval.Ary.Basic public import Complexity.Computability.TreeEval.Ary.CookMertz public import Complexity.Computability.TreeEval.Ary.Packed +public import Complexity.Computability.TreeEval.Ary.Packed.GateInterpolation +public import Complexity.Computability.TreeEval.Ary.Packed.GateInterpolationSpace +public import Complexity.Computability.TreeEval.Ary.Packed.Interpolation +public import Complexity.Computability.TreeEval.Ary.Packed.InterpolationSpace public import Complexity.Computability.TreeEval.Ary.PackingParameters public import Complexity.Computability.TreeEval.Basic public import Complexity.Computability.TreeEval.CookMertz diff --git a/Complexity/Computability/TreeEval/Ary/Packed/GateInterpolation.lean b/Complexity/Computability/TreeEval/Ary/Packed/GateInterpolation.lean new file mode 100644 index 0000000000..03e3f30c0c --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/GateInterpolation.lean @@ -0,0 +1,166 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed.Interpolation +public import Complexity.Foundations.Data.Code.LowDegreeExtension +public import Mathlib.Algebra.BigOperators.Fin + +/-! +# Counter evaluation of a packed gate extension + +The grid is numbered by its base-`s` digits. A coordinate counter multiplies modular Lagrange +weights; an assignment counter sums the weighted table entries. Neither loop constructs a list +of assignments or a truth table. The caller supplies the table entry at one assignment index. + +## Main definitions + +* `Cslib.TreeEval.Ary.Packed.gridWeight`: the product of basis weights for one assignment. +* `Cslib.TreeEval.Ary.Packed.interpolateGrid`: sum the weighted entries using a counter. + +## Main results + +* `Cslib.TreeEval.Ary.Packed.cast_interpolateGrid`: agreement with the existing low-degree + extension, including the empty coordinate tuple. + +## References + +* [R. Williams, *Simulating Time With Square-Root Space*][Williams2025], Appendix A, + equation (6). This is an executable prime-field realization of that interpolation formula. + +This file proves functional correctness and accumulator bounds. The table supplier is an +explicit parameter, without an assumed native resource bound. Its query implementation and the +native certificate for the complete loop remain separate obligations. +-/ + +@[expose] public section + +namespace Cslib.TreeEval.Ary.Packed + +open Reader + +/-- One coordinate of the assignment index, in least-significant-first order. -/ +def gridDigit (s k j : ℕ) : ℕ := k / s ^ j % s + +/-- Multiply one coordinate's basis weight into the accumulator. -/ +def gridWeightStep (p s : ℕ) (x : List ℕ) (st : ℕ × ℕ × ℕ) : ℕ × ℕ × ℕ := + (st.1 + 1, st.2.1 / s, if p = 0 then 0 else + mulMod p st.2.2 (basisWeight p s (st.2.1 % s) (x.getD st.1 0))) + +/-- Weight one grid assignment by scanning coordinates with one product accumulator. -/ +def gridWeight (p s n k : ℕ) (x : List ℕ) : ℕ := + ((gridWeightStep p s x)^[n] (0, k, 1 % p)).2.2 + +/-- The coordinate counter advances while the assignment is consumed one digit at a time. +Only division by `s` is computed at a step; no large power is materialized. -/ +theorem gridWeightStep_counters (p s k : ℕ) (x : List ℕ) (n : ℕ) : + ((gridWeightStep p s x)^[n] (0, k, 1 % p)).1 = n ∧ + ((gridWeightStep p s x)^[n] (0, k, 1 % p)).2.1 = k / s ^ n := by + induction n with + | zero => simp + | succ n ih => + rw [Function.iterate_succ_apply'] + simp [gridWeightStep, ih.1, ih.2, pow_succ, Nat.div_div_eq_div_mul] + +/-- A positive modulus keeps every product accumulator reduced. -/ +theorem gridWeight_lt {p : ℕ} (hp : 0 < p) (s n k : ℕ) (x : List ℕ) : + gridWeight p s n k x < p := by + cases n with + | zero => exact Nat.mod_lt _ hp + | succ n => + rw [gridWeight, Function.iterate_succ_apply'] + simp only [gridWeightStep, hp.ne', ↓reduceIte] + exact Nat.mod_lt _ hp + +/-- One assignment's contribution, with the table queried at its current index only. -/ +def gridSumStep (p s n : ℕ) (table : ℕ → ℕ) (x : List ℕ) (st : ℕ × ℕ) : ℕ × ℕ := + (st.1 + 1, if p = 0 then 0 else + addMod p st.2 (mulMod p (table st.1) (gridWeight p s n st.1 x))) + +/-- Evaluate the interpolation by scanning all `s^n` assignments with one sum accumulator. -/ +def interpolateGrid (p s n : ℕ) (table : ℕ → ℕ) (x : List ℕ) : ℕ := + ((gridSumStep p s n table x)^[s ^ n] (0, 0)).2 + +private theorem gridSumStep_counter (p s n : ℕ) (table : ℕ → ℕ) (x : List ℕ) (t : ℕ) : + ((gridSumStep p s n table x)^[t] (0, 0)).1 = t := by + induction t with + | zero => rfl + | succ t ih => rw [Function.iterate_succ_apply']; simp [gridSumStep, ih] + +/-- Every sum accumulator remains a reduced residue, independently of table entries. -/ +theorem interpolateGrid_lt {p : ℕ} (hp : 0 < p) (s n : ℕ) (table : ℕ → ℕ) (x : List ℕ) : + interpolateGrid p s n table x < p := by + unfold interpolateGrid + generalize s ^ n = t + cases t with + | zero => exact hp + | succ t => + rw [Function.iterate_succ_apply'] + simp only [gridSumStep, hp.ne', ↓reduceIte] + exact Nat.mod_lt _ hp + +variable {p : ℕ} [Fact p.Prime] + +private theorem cast_gridWeight_range {s : ℕ} (hs : 0 < s) (hsp : s ≤ p) (k : ℕ) + (x : List ℕ) (n : ℕ) : + ((gridWeight p s n k x : ℕ) : ZMod p) = + ∏ j ∈ Finset.range n, + (Lagrange.basis Finset.univ (fun a : Fin s => (a.val : ZMod p)) + ⟨gridDigit s k j, Nat.mod_lt _ hs⟩).eval (x.getD j 0 : ZMod p) := by + simp only [gridDigit] + induction n with + | zero => simp [gridWeight] + | succ n ih => + rw [gridWeight, Function.iterate_succ_apply'] + simp only [gridWeightStep, (gridWeightStep_counters p s k x n).1, + (gridWeightStep_counters p s k x n).2, (Fact.out : p.Prime).ne_zero, + ↓reduceIte, cast_mulMod, Finset.prod_range_succ] + change ((gridWeight p s n k x : ℕ) : ZMod p) * _ = _ + rw [ih, cast_basisWeight hsp ⟨k / s ^ n % s, Nat.mod_lt _ hs⟩] + +private theorem cast_gridSum_range (s n : ℕ) (table : ℕ → ℕ) (x : List ℕ) (t : ℕ) : + (((gridSumStep p s n table x)^[t] (0, 0)).2 : ZMod p) = + ∑ k ∈ Finset.range t, (table k : ZMod p) * (gridWeight p s n k x : ZMod p) := by + induction t with + | zero => simp + | succ t ih => + rw [Function.iterate_succ_apply'] + simp only [gridSumStep, gridSumStep_counter, (Fact.out : p.Prime).ne_zero, + ↓reduceIte, cast_addMod, cast_mulMod, Finset.sum_range_succ, ih] + +/-- The streamed modular sum is exactly the finite-grid low-degree extension. The table is +indexed by Mathlib's existing base-`s` numbering of tuples; no additional gate model is used. -/ +theorem cast_interpolateGrid {s n : ℕ} (hs : 0 < s) (hsp : s ≤ p) + (h : Fin s ↪ ZMod p) (hh : ∀ a, h a = (a.val : ZMod p)) + (table : ℕ → ℕ) (x : List ℕ) : + ((interpolateGrid p s n table x : ℕ) : ZMod p) = + Code.lowDegreeExtension h + (fun a : Fin n → Fin s => (table (finFunctionFinEquiv a) : ZMod p)) + (fun i => (x.getD i 0 : ZMod p)) := by + rw [interpolateGrid, cast_gridSum_range, Finset.sum_range] + have hemb : (fun a : Fin s => (a.val : ZMod p)) = h := funext fun a => (hh a).symm + calc + (∑ k : Fin (s ^ n), (table k : ZMod p) * (gridWeight p s n k x : ZMod p)) = + ∑ a : Fin n → Fin s, + (table (finFunctionFinEquiv a) : ZMod p) * + (gridWeight p s n (finFunctionFinEquiv a) x : ZMod p) := + (Equiv.sum_comp finFunctionFinEquiv _).symm + _ = _ := by + apply Finset.sum_congr rfl + intro a _ + congr 1 + rw [cast_gridWeight_range hs hsp, Finset.prod_range, hemb] + apply Finset.prod_congr rfl + intro i _ + have hdigit : (⟨gridDigit s (finFunctionFinEquiv a) i, Nat.mod_lt _ hs⟩ : Fin s) = a i := by + apply Fin.ext + exact (finFunctionFinEquiv_symm_apply_val (finFunctionFinEquiv a) i).symm.trans + (congrArg Fin.val (congrFun (finFunctionFinEquiv.symm_apply_apply a) i)) + rw [hdigit] + +end Cslib.TreeEval.Ary.Packed diff --git a/Complexity/Computability/TreeEval/Ary/Packed/GateInterpolationSpace.lean b/Complexity/Computability/TreeEval/Ary/Packed/GateInterpolationSpace.lean new file mode 100644 index 0000000000..8be51eed9b --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/GateInterpolationSpace.lean @@ -0,0 +1,147 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed.GateInterpolation +public import Complexity.Computability.TreeEval.Ary.Packed.InterpolationSpace + +/-! +# Packed grid weights in native work space + +A grid assignment is consumed by repeated division by the alphabet cardinality. The coordinate +counter, remaining assignment and modular accumulator are all stored in binary. Their sizes are +bounded by the encoded inputs even if the requested coordinate count exceeds the supplied point's +length, or the modulus or alphabet cardinality is zero. + +## Main definitions + +* `Cslib.TreeEval.Ary.Packed.encGridWeightArgs`: encode `((p,s,n,k),x)`. + +## Main results + +* `Cslib.TreeEval.Ary.Packed.gridWeight_inSpace`: the complete product of coordinate weights is + computed in native work space linear in the encoded arguments, without a unary charge for + the coordinate count or alphabet cardinality. + +The table-query sum and the full Tree Evaluation machine remain separate obligations. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed + +open Reader Turing MultiTapeTM ComputableInSpace + +/-- Encode the modulus, alphabet size, coordinate count, assignment index and point in binary. -/ +abbrev encGridWeightArgs : (ℕ × ℕ × ℕ × ℕ) × List ℕ ↪ List Bool := + encPair encBasisArgs (encList encNat) + +/-- All intermediate products fit within the modulus plus one, including malformed zero +moduli and the empty product. -/ +theorem gridWeight_le (p s n k : ℕ) (x : List ℕ) : gridWeight p s n k x ≤ p + 1 := by + cases n with + | zero => exact (Nat.mod_le _ _).trans (by omega) + | succ n => + rw [gridWeight, Function.iterate_succ_apply'] + simp only [gridWeightStep] + split_ifs with hz + · omega + · exact (Nat.mod_lt _ (Nat.pos_of_ne_zero hz)).le.trans (by omega) + +private abbrev GridWeightPar := (ℕ × ℕ) × List ℕ +private abbrev GridWeightState := GridWeightPar × ℕ × ℕ × ℕ + +private abbrev encGridWeightPar : GridWeightPar ↪ List Bool := + encPair (encPair encNat encNat) (encList encNat) + +private abbrev encGridWeightState : GridWeightState ↪ List Bool := + encPair encGridWeightPar encNat3 + +private def gridWeightStateStep (q : GridWeightState) : GridWeightState := + (q.1, gridWeightStep q.1.1.1 q.1.1.2 q.1.2 q.2) + +private theorem gridWeightStateStep_inSpace : + ComputableInSpace gridWeightStateStep encGridWeightState encGridWeightState + fun q => (encGridWeightState q).length := by + have hpar := fst encGridWeightPar encNat3 + have hps := fst' hpar + have hp := fst' hps + have hs := snd' hps + have hx := snd' hpar + have hst := snd encGridWeightPar encNat3 + have hj := fst' hst + have hqa := snd' hst + have hq := fst' hqa + have ha := snd' hqa + have hcell := budget_comp_lin getD (linear_mul 8) (budget_prod hx hj) + have hdigit := budget_natMod hq hs + have hfactor := budget_comp_linear basisWeight_inSpace + (budget_prod hp (budget_prod hs (budget_prod hdigit hcell))) + have hmul := budget_comp_linear mulMod_inSpace (budget_prod hp (budget_prod ha hfactor)) + have hacc := budget_ite (budget_natIsZero hp) + (budget_const (encB := encNat) 0 (.refl _)) hmul + have hnext := budget_prod hpar (budget_prod (budget_natSucc hj) + (budget_prod (budget_natDiv hq hs) hacc)) + exact hnext.of_enc_eq fun q => by + by_cases h : q.1.1.1 = 0 <;> simp [gridWeightStateStep, gridWeightStep, h] + +private theorem iterate_gridWeightStateStep (p s k : ℕ) (x : List ℕ) (n : ℕ) : + gridWeightStateStep^[n] (((p, s), x), 0, k, 1 % p) = + (((p, s), x), (gridWeightStep p s x)^[n] (0, k, 1 % p)) := by + induction n with + | zero => rfl + | succ n ih => rw [Function.iterate_succ_apply', ih, Function.iterate_succ_apply']; rfl + +/-- All coordinates of one interpolation weight are evaluated in native work space linear in +`encGridWeightArgs ((p,s,n,k),x)`. No correctness or primality assumptions are needed for this +total resource bound. -/ +theorem gridWeight_inSpace : + ComputableInSpace (fun q : (ℕ × ℕ × ℕ × ℕ) × List ℕ => + gridWeight q.1.1 q.1.2.1 q.1.2.2.1 q.1.2.2.2 q.2) + encGridWeightArgs encNat fun q => (encGridWeightArgs q).length := by + have hargs := fst encBasisArgs (encList encNat) + have hp := fst' hargs + have hrest := snd' hargs + have hs := fst' hrest + have hnk := snd' hrest + have hn := fst' hnk + have hk := snd' hnk + have hx := snd encBasisArgs (encList encNat) + have hpar := budget_prod (budget_prod hp hs) hx + have hone := budget_natMod (budget_const (encB := encNat) 1 (.refl _)) hp + have hinit := budget_prod hpar + (budget_prod (budget_const (encB := encNat) 0 (.refl _)) (budget_prod hk hone)) + have hstates (q : (ℕ × ℕ × ℕ × ℕ) × List ℕ) (j : ℕ) (hj : j ≤ q.1.2.2.1) : + (encGridWeightState (gridWeightStateStep^[j] + (((q.1.1, q.1.2.1), q.2), 0, q.1.2.2.2, 1 % q.1.1))).length ≤ + 32 * ((encGridWeightArgs q).length + 1) := by + rw [iterate_gridWeightStateStep] + have hc := gridWeightStep_counters q.1.1 q.1.2.1 q.1.2.2.2 q.2 j + have hquot := length_encNat_mono (Nat.div_le_self q.1.2.2.2 (q.1.2.1 ^ j)) + have hcounter := length_encNat_mono hj + have hacc := length_encNat_mono (gridWeight_le q.1.1 q.1.2.1 j q.1.2.2.2 q.2) + have hsucc := length_encNat_add_le_add q.1.1 1 + have h1 : (encNat 1).length = 1 := rfl + change (encGridWeightState + (((q.1.1, q.1.2.1), q.2), + ((gridWeightStep q.1.1 q.1.2.1 q.2)^[j] (0, q.1.2.2.2, 1 % q.1.1)).1, + ((gridWeightStep q.1.1 q.1.2.1 q.2)^[j] (0, q.1.2.2.2, 1 % q.1.1)).2.1, + ((gridWeightStep q.1.1 q.1.2.1 q.2)^[j] (0, q.1.2.2.2, 1 % q.1.1)).2.2)).length ≤ _ + rw [hc.1, hc.2] + change (encGridWeightState (((q.1.1, q.1.2.1), q.2), j, + q.1.2.2.2 / q.1.2.1 ^ j, gridWeight q.1.1 q.1.2.1 j q.1.2.2.2 q.2)).length ≤ _ + simp only [encGridWeightState, encGridWeightPar, encGridWeightArgs, encBasisArgs, + encNat3, length_encPair] at hquot hcounter hacc hsucc ⊢ + omega + have hrun := budget_iterate hn hinit gridWeightStateStep_inSpace hstates + (.of_le_linear 32 32 fun _ => le_rfl) + refine (snd' (snd' (snd' hrun))).of_enc_eq fun q => ?_ + rw [iterate_gridWeightStateStep] + rfl + +end Cslib.TreeEval.Ary.Packed diff --git a/Complexity/Computability/TreeEval/Ary/Packed/Interpolation.lean b/Complexity/Computability/TreeEval/Ary/Packed/Interpolation.lean new file mode 100644 index 0000000000..51695cc4b8 --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/Interpolation.lean @@ -0,0 +1,173 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Reader.Arith +public import Mathlib.LinearAlgebra.Lagrange +import Complexity.Foundations.FieldTheory.Finite.Basic + +/-! +# Modular Lagrange basis evaluation + +A Lagrange basis weight for the alphabet `0, …, s-1` is evaluated by a counter and a single +modular accumulator. Inverses use the existing Fermat power routine. This executable procedure +does not materialize a list of alphabet symbols or polynomial coefficients. + +## Main definitions + +* `Cslib.TreeEval.Ary.Packed.basisFactor`: one factor, omitting the diagonal symbol. +* `Cslib.TreeEval.Ary.Packed.basisStep`: one counter step. +* `Cslib.TreeEval.Ary.Packed.basisWeight`: the complete basis weight. + +## Main results + +* `Cslib.TreeEval.Ary.Packed.cast_basisWeight`: correctness over a prime field. +* `Cslib.TreeEval.Ary.Packed.basisWeight_lt`: the result remains a reduced residue. + +## References + +* [R. Williams, *Simulating Time With Square-Root Space*][Williams2025], Appendix A, + equation (6). The executable prime-field realization is an original implementation of the + finite-grid Lagrange factors used by that extension. +-/ + +@[expose] public section + +namespace Cslib.TreeEval.Ary.Packed + +open Reader + +/-- One factor of a Lagrange basis weight, with the diagonal factor replaced by one. +`unitInv p (v-1)` inverts the nonzero residue `v` when `p` is prime. -/ +def basisFactor (p a j x : ℕ) : ℕ := + if j = a then 1 % p else + mulMod p (addMod p x (negMod p j)) + (unitInv p (addMod p a (negMod p j) - 1)) + +/-- The parameter `(p,a,x)`, counter and accumulator used by basis evaluation. The zero-modulus +branch makes every noninitial accumulator bounded even on malformed parameters. -/ +def basisStep (s : (ℕ × ℕ × ℕ) × ℕ × ℕ) : (ℕ × ℕ × ℕ) × ℕ × ℕ := + (s.1, s.2.1 + 1, + if s.1.1 = 0 then 0 else mulMod s.1.1 s.2.2 + (basisFactor s.1.1 s.1.2.1 s.2.1 s.1.2.2)) + +/-- Evaluate the basis weight by exactly `s` counter steps. -/ +def basisWeight (p s a x : ℕ) : ℕ := + (basisStep^[s] ((p, a, x), 0, 1 % p)).2.2 + +/-- The counter enumerates the alphabet without storing its elements. -/ +theorem iterate_basisStep_counter (p a x : ℕ) (n : ℕ) : + (basisStep^[n] ((p, a, x), 0, 1 % p)).1 = (p, a, x) ∧ + (basisStep^[n] ((p, a, x), 0, 1 % p)).2.1 = n := by + induction n with + | zero => exact ⟨rfl, rfl⟩ + | succ n ih => + rw [Function.iterate_succ_apply'] + simp [basisStep, ih.1, ih.2] + +/-- Every intermediate accumulator fits within the modulus plus one, including malformed +zero-modulus inputs and the initial accumulator. -/ +theorem basisWeight_le (p n a x : ℕ) : basisWeight p n a x ≤ p + 1 := by + cases n with + | zero => exact (Nat.mod_le _ _).trans (by omega) + | succ n => + rw [basisWeight, Function.iterate_succ_apply'] + have hp := (iterate_basisStep_counter p a x n).1 + simp only [basisStep, hp] + split_ifs with hz + · omega + · exact (Nat.mod_lt _ (Nat.pos_of_ne_zero hz)).le.trans (by omega) + +/-- A positive modulus keeps the initial and every subsequent accumulator reduced. -/ +theorem basisWeight_lt {p : ℕ} (hp : 0 < p) (n a x : ℕ) : basisWeight p n a x < p := by + cases n with + | zero => exact Nat.mod_lt _ hp + | succ n => + rw [basisWeight, Function.iterate_succ_apply'] + have hpar := (iterate_basisStep_counter p a x n).1 + simp only [basisStep, hpar, hp.ne', ↓reduceIte] + exact Nat.mod_lt _ hp + +variable {p : ℕ} [Fact p.Prime] + +private theorem cast_unitInv_residue {v : ℕ} (hv : 0 < v) (hvp : v < p) : + ((unitInv p (v - 1) : ℕ) : ZMod p) = (v : ZMod p)⁻¹ := by + have hne : (v : ZMod p) ≠ 0 := by + intro h + have := congrArg ZMod.val h + simp only [ZMod.val_natCast, Nat.mod_eq_of_lt hvp, ZMod.val_zero] at this + omega + rw [unitInv, cast_powMod, Nat.sub_add_cancel hv] + exact ZMod.pow_card_sub_two_eq_inv hne + +/-- A scalar factor agrees with the corresponding field quotient on the alphabet. -/ +theorem cast_basisFactor {a j : ℕ} (ha : a < p) (hj : j < p) (x : ℕ) : + ((basisFactor p a j x : ℕ) : ZMod p) = + if j = a then 1 else ((x : ZMod p) - j) * ((a : ZMod p) - j)⁻¹ := by + by_cases hja : j = a + · simp [basisFactor, hja] + · have hne : (a : ZMod p) ≠ (j : ZMod p) := by + intro heq + have heq' := congrArg ZMod.val heq + simp only [ZMod.val_natCast, Nat.mod_eq_of_lt ha, Nat.mod_eq_of_lt hj] at heq' + exact hja heq'.symm + have hdlt : addMod p a (negMod p j) < p := Nat.mod_lt _ (Fact.out : p.Prime).pos + have hdpos : 0 < addMod p a (negMod p j) := by + by_contra hn + have hz : addMod p a (negMod p j) = 0 := by omega + have hcast := congrArg (fun n : ℕ => (n : ZMod p)) hz + simp only [cast_addMod, cast_negMod, Nat.cast_zero] at hcast + exact hne (eq_of_sub_eq_zero (by simpa only [sub_eq_add_neg] using hcast)) + rw [basisFactor, ite_eq_right hja, cast_mulMod, cast_addMod, cast_negMod, + cast_unitInv_residue hdpos hdlt, cast_addMod, cast_negMod, ite_eq_right hja] + simp only [sub_eq_add_neg] + +private theorem cast_basisWeight_prod {s a : ℕ} (ha : a < p) (hs : s ≤ p) (x : ℕ) : + ((basisWeight p s a x : ℕ) : ZMod p) = + ∏ j ∈ Finset.range s, + if j = a then 1 else ((x : ZMod p) - j) * ((a : ZMod p) - j)⁻¹ := by + induction s with + | zero => simp [basisWeight] + | succ s ih => + have hpar := (iterate_basisStep_counter p a x s).1 + have hcounter := (iterate_basisStep_counter p a x s).2 + rw [basisWeight, Function.iterate_succ_apply'] + simp only [basisStep, hpar, hcounter, (Fact.out : p.Prime).ne_zero, ↓reduceIte, + cast_mulMod, Finset.prod_range_succ] + change ((basisWeight p s a x : ℕ) : ZMod p) * _ = _ + rw [ih (by omega), cast_basisFactor ha (by omega)] + +/-- The counter procedure evaluates the finite-grid Lagrange basis polynomial. Correctness +requires the whole alphabet to embed in the prime field, not merely the chosen symbol. -/ +theorem cast_basisWeight {s : ℕ} (hs : s ≤ p) (a : Fin s) (x : ℕ) : + ((basisWeight p s a x : ℕ) : ZMod p) = + (Lagrange.basis Finset.univ (fun j : Fin s => (j.val : ZMod p)) a).eval (x : ZMod p) := by + rw [cast_basisWeight_prod (a.isLt.trans_le hs) hs] + rw [Lagrange.basis, Polynomial.eval_prod] + rw [Finset.prod_range] + calc + (∏ j : Fin s, if (j : ℕ) = a.val then (1 : ZMod p) + else ((x : ZMod p) - j.val) * ((a.val : ZMod p) - j.val)⁻¹) = + ∏ j ∈ Finset.univ.erase a, + if (j : ℕ) = a.val then (1 : ZMod p) + else ((x : ZMod p) - j.val) * ((a.val : ZMod p) - j.val)⁻¹ := by + symm + apply Finset.prod_subset (Finset.erase_subset a Finset.univ) + intro j _ hj + have heq : j = a := by simpa only [Finset.mem_erase, Finset.mem_univ, + and_true, not_not] using hj + subst j + simp + _ = _ := by + apply Finset.prod_congr rfl + intro j hj + have hne : (j : ℕ) ≠ a.val := fun heq => + (Finset.mem_erase.mp hj).1 (Fin.ext heq) + simp [hne, Lagrange.basisDivisor, mul_comm] + +end Cslib.TreeEval.Ary.Packed diff --git a/Complexity/Computability/TreeEval/Ary/Packed/InterpolationSpace.lean b/Complexity/Computability/TreeEval/Ary/Packed/InterpolationSpace.lean new file mode 100644 index 0000000000..ff393c8102 --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/InterpolationSpace.lean @@ -0,0 +1,119 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed.Interpolation +public import Complexity.Computability.TreeEval.Reader.Cert.Residue + +/-! +# Modular Lagrange basis evaluation within binary argument space + +The counter in `basisWeight p s a x` is stored in binary. Every modular accumulator is bounded +by `p+1`, including malformed zero-modulus inputs. Thus the alphabet cardinality `s` contributes +its binary encoding length, rather than `s` cells of work space. The native arithmetic +certificates and bounded-counter iteration implement every multiplication, inverse and factor. + +## Main definitions + +* `Cslib.TreeEval.Ary.Packed.encBasisArgs`: encode `(p,s,a,x)` in binary. + +## Main results + +* `Cslib.TreeEval.Ary.Packed.basisWeight_inSpace`: the full scalar basis weight is computable + in native work space linear in the encoded arguments. + +This certifies one interpolation weight, not the full grid sum or a Tree Evaluation machine. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed + +open Reader Turing MultiTapeTM ComputableInSpace + +/-- Binary encoding of four natural parameters, in the order `(p,s,a,x)` for `basisWeight`. -/ +abbrev encBasisArgs : ℕ × ℕ × ℕ × ℕ ↪ List Bool := + encPair encNat (encPair encNat (encPair encNat encNat)) + +/-- A single factor uses only linear space in its four binary arguments `(p,a,j,x)`. -/ +theorem basisFactor_inSpace : + ComputableInSpace (fun q : ℕ × ℕ × ℕ × ℕ => basisFactor q.1 q.2.1 q.2.2.1 q.2.2.2) + encBasisArgs encNat fun q => (encBasisArgs q).length := by + have hp := fst encNat (encPair encNat (encPair encNat encNat)) + have htail := snd encNat (encPair encNat (encPair encNat encNat)) + have ha := fst' htail + have hjx := snd' htail + have hj := fst' hjx + have hx := snd' hjx + have hneg := budget_comp_linear negMod_inSpace (budget_prod hp hj) + have hnum := budget_comp_linear addMod_inSpace (budget_prod hp (budget_prod hx hneg)) + have hden := budget_comp_linear addMod_inSpace (budget_prod hp (budget_prod ha hneg)) + have hinv := budget_comp_linear unitInv_inSpace (budget_prod hp (budget_natPred hden)) + have hprod := budget_comp_linear mulMod_inSpace (budget_prod hp (budget_prod hnum hinv)) + have hone := budget_natMod (budget_const (encB := encNat) 1 (.refl _)) hp + exact (budget_ite (budget_natEq hj ha) hone hprod).of_enc_eq fun q => by + by_cases h : q.2.2.1 = q.2.1 <;> simp [basisFactor, h] + +private abbrev encBasisState : (ℕ × ℕ × ℕ) × ℕ × ℕ ↪ List Bool := + encPair encNat3 (encPair encNat encNat) + +private theorem basisStep_inSpace : + ComputableInSpace basisStep encBasisState encBasisState fun q => (encBasisState q).length := by + have hpar := fst encNat3 (encPair encNat encNat) + have hp := fst' hpar + have hax := snd' hpar + have ha := fst' hax + have hx := snd' hax + have hja := snd encNat3 (encPair encNat encNat) + have hj := fst' hja + have hacc := snd' hja + have hfac := budget_comp_linear basisFactor_inSpace + (budget_prod hp (budget_prod ha (budget_prod hj hx))) + have hmul := budget_comp_linear mulMod_inSpace (budget_prod hp (budget_prod hacc hfac)) + have hnext := budget_ite (budget_natIsZero hp) + (budget_const (encB := encNat) 0 (.refl _)) hmul + exact (budget_prod hpar (budget_prod (budget_natSucc hj) hnext)).of_enc_eq fun q => by + by_cases h : q.1.1 = 0 <;> simp [basisStep, h] + +/-- The complete basis weight, including its binary counter, is computed in native work space +linear in the binary encodings of `(p,s,a,x)`. The statement is total: no prime or positivity +hypothesis is needed for the resource certificate. -/ +theorem basisWeight_inSpace : + ComputableInSpace (fun q : ℕ × ℕ × ℕ × ℕ => basisWeight q.1 q.2.1 q.2.2.1 q.2.2.2) + encBasisArgs encNat fun q => (encBasisArgs q).length := by + have hp := fst encNat (encPair encNat (encPair encNat encNat)) + have htail := snd encNat (encPair encNat (encPair encNat encNat)) + have hs := fst' htail + have hax := snd' htail + have hpar := budget_prod hp hax + have hone := budget_natMod (budget_const (encB := encNat) 1 (.refl _)) hp + have hinit := budget_prod hpar + (budget_prod (budget_const (encB := encNat) 0 (.refl _)) hone) + have hstates (q : ℕ × ℕ × ℕ × ℕ) (j : ℕ) (hj : j ≤ q.2.1) : + (encBasisState (basisStep^[j] ((q.1, q.2.2.1, q.2.2.2), 0, 1 % q.1))).length ≤ + 16 * ((encBasisArgs q).length + 1) := by + have hpar := (iterate_basisStep_counter q.1 q.2.2.1 q.2.2.2 j).1 + have hcounter := (iterate_basisStep_counter q.1 q.2.2.1 q.2.2.2 j).2 + have hacc := length_encNat_mono (basisWeight_le q.1 j q.2.2.1 q.2.2.2) + have hs := length_encNat_mono hj + have hsucc := length_encNat_add_le_add q.1 1 + have h1 : (encNat 1).length = 1 := rfl + change (encBasisState + ((basisStep^[j] ((q.1, q.2.2.1, q.2.2.2), 0, 1 % q.1)).1, + (basisStep^[j] ((q.1, q.2.2.1, q.2.2.2), 0, 1 % q.1)).2.1, + (basisStep^[j] ((q.1, q.2.2.1, q.2.2.2), 0, 1 % q.1)).2.2)).length ≤ _ + rw [hpar, hcounter] + change (encBasisState ((q.1, q.2.2.1, q.2.2.2), j, + basisWeight q.1 j q.2.2.1 q.2.2.2)).length ≤ _ + simp only [encBasisState, encBasisArgs, encNat3, length_encPair] at hacc hs hsucc ⊢ + omega + have hrun := budget_iterate hs hinit basisStep_inSpace hstates + (.of_le_linear 16 16 fun _ => le_rfl) + exact (snd' (snd' hrun)).of_enc_eq fun _ => rfl + +end Cslib.TreeEval.Ary.Packed diff --git a/ComplexityTests/TreeEvalInterpolation.lean b/ComplexityTests/TreeEvalInterpolation.lean new file mode 100644 index 0000000000..c1e4ae1263 --- /dev/null +++ b/ComplexityTests/TreeEvalInterpolation.lean @@ -0,0 +1,72 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module + +public import Complexity.Computability.TreeEval.Ary.Packed.InterpolationSpace +public import Complexity.Computability.TreeEval.Ary.Packed.GateInterpolationSpace + +/-! +# Executable packed interpolation regressions + +Three symbols over `ZMod 7` exercise non-Boolean interpolation. The basis and full two-coordinate +grid are computed through the counter procedures. The basis's native space theorem is checked +on its total binary-parameter domain, including the guarded zero-modulus cases. +-/ + +@[expose] public section + +namespace ComplexityTests.TreeEvalInterpolation + +open Cslib.TreeEval.Ary.Packed Turing.MultiTapeTM + +instance : Fact (Nat.Prime 7) := ⟨by decide⟩ + +example : basisWeight 7 3 1 0 = 0 := by decide +example : basisWeight 7 3 1 1 = 1 := by decide +example : basisWeight 7 3 1 2 = 0 := by decide +example : basisWeight 7 3 1 4 = 6 := by decide + +example : basisWeight 0 0 1 4 = 1 := by decide +example : basisWeight 0 3 1 4 = 0 := by decide +example : basisWeight 1 3 1 4 = 0 := by decide + +/-- The alphabet counter is charged in binary, without an alphabet-cardinality space term. -/ +theorem native_basis_certificate : + ComputableInSpace (fun q : ℕ × ℕ × ℕ × ℕ => basisWeight q.1 q.2.1 q.2.2.1 q.2.2.2) + encBasisArgs encNat (fun q => (encBasisArgs q).length) := basisWeight_inSpace + +/-- The whole coordinate product also has a total native certificate in the encoded inputs. -/ +theorem native_grid_weight_certificate : + ComputableInSpace (fun q : (ℕ × ℕ × ℕ × ℕ) × List ℕ => + gridWeight q.1.1 q.1.2.1 q.1.2.2.1 q.1.2.2.2 q.2) + encGridWeightArgs encNat (fun q => (encGridWeightArgs q).length) := gridWeight_inSpace + +/-- A nonbinary two-variable multiplication table, addressed by base-three digits. -/ +def table (k : ℕ) : ℕ := k % 3 * (k / 3 % 3) + +example : gridDigit 3 5 0 = 2 ∧ gridDigit 3 5 1 = 1 := by decide +example : gridWeight 7 3 2 5 [4, 5] = 1 := by decide +example : interpolateGrid 7 3 2 table [4, 5] = 6 := by decide + +/-- The empty coordinate tuple is evaluated once, rather than producing an empty sum. -/ +example : interpolateGrid 7 3 0 (fun _ => 5) [] = 5 := by decide + +example : interpolateGrid 0 3 2 table [4, 5] = 0 := by decide + +/-- Missing coordinates use the documented zero default, with no hidden well-formedness premise. -/ +example : gridWeight 7 3 2 0 [] = 1 := by decide + +/-- The executable formula agrees with the library's existing extension for every table. -/ +theorem executable_extension_agrees (h : Fin 3 ↪ ZMod 7) + (hh : ∀ a, h a = (a.val : ZMod 7)) (values : ℕ → ℕ) (x : List ℕ) : + ((interpolateGrid 7 3 2 values x : ℕ) : ZMod 7) = + Cslib.Code.lowDegreeExtension h + (fun a : Fin 2 → Fin 3 => (values (finFunctionFinEquiv a) : ZMod 7)) + (fun i => (x.getD i 0 : ZMod 7)) := + cast_interpolateGrid (by decide) (by decide) h hh values x + +end ComplexityTests.TreeEvalInterpolation From 95971156000659a0eea86e0d1fd8c4af0bc484e7 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sat, 31 Oct 2026 22:23:13 +0000 Subject: [PATCH 05/36] =?UTF-8?q?feat(treeeval):=20implement=20iterative?= =?UTF-8?q?=20fan-in-d=20Cook=E2=80=93Mertz?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Complexity.lean | 5 + .../Computability/TreeEval/Ary/Iterative.lean | 283 ++++++++++++++++++ .../TreeEval/Ary/IterativeBounds.lean | 152 ++++++++++ .../TreeEval/Ary/IterativeCorrect.lean | 264 ++++++++++++++++ .../TreeEval/Ary/IterativePacked.lean | 61 ++++ .../Computability/TreeEval/Ary/Packed.lean | 2 +- .../TreeEval/Ary/Packed/Executable.lean | 94 ++++++ ComplexityTests/TreeEvalIterative.lean | 137 +++++++++ 8 files changed, 997 insertions(+), 1 deletion(-) create mode 100644 Complexity/Computability/TreeEval/Ary/Iterative.lean create mode 100644 Complexity/Computability/TreeEval/Ary/IterativeBounds.lean create mode 100644 Complexity/Computability/TreeEval/Ary/IterativeCorrect.lean create mode 100644 Complexity/Computability/TreeEval/Ary/IterativePacked.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/Executable.lean create mode 100644 ComplexityTests/TreeEvalIterative.lean diff --git a/Complexity.lean b/Complexity.lean index c8d637eb2a..0512149dd6 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2302,7 +2302,12 @@ public import Complexity.Computability.RAM.Squaring public import Complexity.Computability.Reduction.ManyOne public import Complexity.Computability.TreeEval.Ary.Basic public import Complexity.Computability.TreeEval.Ary.CookMertz +public import Complexity.Computability.TreeEval.Ary.Iterative +public import Complexity.Computability.TreeEval.Ary.IterativeBounds +public import Complexity.Computability.TreeEval.Ary.IterativeCorrect +public import Complexity.Computability.TreeEval.Ary.IterativePacked public import Complexity.Computability.TreeEval.Ary.Packed +public import Complexity.Computability.TreeEval.Ary.Packed.Executable public import Complexity.Computability.TreeEval.Ary.Packed.GateInterpolation public import Complexity.Computability.TreeEval.Ary.Packed.GateInterpolationSpace public import Complexity.Computability.TreeEval.Ary.Packed.Interpolation diff --git a/Complexity/Computability/TreeEval/Ary/Iterative.lean b/Complexity/Computability/TreeEval/Ary/Iterative.lean new file mode 100644 index 0000000000..d91032765a --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Iterative.lean @@ -0,0 +1,283 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.CookMertz + +/-! +# Iterative control for fan-in-`d` Cook–Mertz + +The controller unravels `Ary.procWith` into calls and returns over one shared register bank. +A frame retains a round number, child index, forward/undo phase, coefficient and target register; +it stores neither a tree nor a saved register bank. Reading a node is parameterized by the +existing `Tree.Node` lookup at a path. The path is recovered from the stack's child indices. + +A forward phase scales a child's register and calls that child with coefficient one. After the +last forward call, it performs the supplied gate update. Undo phases call each child with +coefficient minus one and undo its scaling upon return. Children in both phases are processed +in increasing order, exactly as in `Ary.gateRoundWith`. + +## Main definitions + +* `Cslib.TreeEval.Ary.Iter.Frame`, `Mode`, `Cfg`: frames, call/return modes and global state. +* `Cslib.TreeEval.Ary.Iter.path`: recover the current node path without storing it per frame. +* `Cslib.TreeEval.Ary.Iter.step`, `runFrom`: the controller and its clocked iteration. + +## References + +* [O. Goldreich, *On the Cook–Mertz Tree Evaluation procedure*][Goldreich2024TreeEval], + Algorithm 1, with signed calls. +* [R. Williams, *Simulating Time With Square-Root Space*][Williams2025], Appendix A. + +This is pure control semantics for positive fan-in. Node lookup and the supplied gate extension +are parameters; their native implementation and space costs are separate obligations. +-/ + +@[expose] public section + +namespace Cslib.TreeEval.Ary.Iter + +/-- A suspended call, retaining control data only. -/ +structure Frame (K : Type*) (d : ℕ) where + /-- The unit-round index. -/ + round : ℕ + /-- The child currently being evaluated or just returned. -/ + child : Fin d + /-- False during forward calls and true during undo calls. -/ + undo : Bool + /-- The coefficient of the suspended call. -/ + coeff : K + /-- The register targeted by the suspended call. -/ + target : Fin (d + 1) + deriving DecidableEq + +/-- Calling a node with a coefficient and target, or returning from one. -/ +inductive Mode (K : Type*) (d : ℕ) where + /-- Evaluate the current node into the target with the supplied coefficient. -/ + | call (coeff : K) (target : Fin (d + 1)) + /-- Continue the innermost suspended call, or halt if there is none. -/ + | ret + deriving DecidableEq + +/-- One shared register bank, the suspended calls and the current mode. -/ +structure Cfg (K : Type*) (d r : ℕ) where + /-- Registers shared by all levels. -/ + regs : Ary.State K d r + /-- Suspended calls, innermost first. -/ + stack : List (Frame K d) + /-- The current call or return. -/ + mode : Mode K d + deriving DecidableEq + +variable {K : Type*} {d b r : ℕ} + +/-- The root-to-current-node path, read from the suspended calls. -/ +def path (st : List (Frame K d)) : List (Fin d) := st.reverse.map Frame.child + +@[simp] theorem path_nil : path ([] : List (Frame K d)) = [] := rfl + +@[simp] theorem path_cons (fr : Frame K d) (st : List (Frame K d)) : + path (fr :: st) = path st ++ [fr.child] := by + simp [path] + +@[simp] theorem length_path (st : List (Frame K d)) : (path st).length = st.length := by + simp [path] + +/-- Start a root call from arbitrary global registers. -/ +def initialCfg (S : Ary.State K d r) (c : K) (t : Fin (d + 1)) : Cfg K d r := + ⟨S, [], .call c t⟩ + +/-- A returned value with no suspended caller is final. -/ +def halted (q : Cfg K d r) : Bool := + match q.mode with + | .call _ _ => false + | .ret => q.stack.isEmpty + +@[simp] theorem halted_ret_nil (S : Ary.State K d r) : + halted (⟨S, [], .ret⟩ : Cfg K d r) = true := rfl + +variable [Field K] + +/-- Read a unit round by index; the fallback makes malformed control states total. -/ +def unitAt (us : List Kˣ) (i : ℕ) : Kˣ := us.getD i 1 + +/-- Update at a gate, leaving the registers unchanged if the requested path is invalid. -/ +def updateAt (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) + (read : List (Fin d) → Tree.Node d b) (P : List (Fin d)) (c : K) + (t : Fin (d + 1)) (S : Ary.State K d r) : Ary.State K d r := + match read P with + | .gate f => gateUpdateWith E f c t S + | _ => S + +/-- Begin the first forward child of a round, scaling before the recursive call. -/ +def beginRound (hd : 0 < d) (us : List Kˣ) (i : ℕ) (c : K) (t : Fin (d + 1)) + (S : Ary.State K d r) (st : List (Frame K d)) : Cfg K d r := + ⟨scale (reg t ⟨0, hd⟩) (unitAt us i : K) S, + ⟨i, ⟨0, hd⟩, false, c, t⟩ :: st, .call 1 (reg t ⟨0, hd⟩)⟩ + +/-- One control step of the fan-in-`d` procedure. -/ +def step (hd : 0 < d) (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) + (read : List (Fin d) → Tree.Node d b) (q : Cfg K d r) : Cfg K d r := + match q.mode with + | .call c t => + match read (path q.stack) with + | .leaf v => ⟨Function.update q.regs t (q.regs t + c • e v), q.stack, .ret⟩ + | .gate _ => + match us with + | [] => ⟨q.regs, q.stack, .ret⟩ + | _ :: _ => beginRound hd us 0 c t q.regs q.stack + | .none => ⟨q.regs, q.stack, .ret⟩ + | .ret => + match q.stack with + | [] => q + | fr :: st => + if fr.undo then + let S := scale (reg fr.target fr.child) (((unitAt us fr.round)⁻¹ : Kˣ) : K) q.regs + if h : fr.child.val + 1 < d then + ⟨S, { fr with child := ⟨fr.child.val + 1, h⟩ } :: st, + .call (-1) (reg fr.target ⟨fr.child.val + 1, h⟩)⟩ + else if fr.round + 1 < us.length then + beginRound hd us (fr.round + 1) fr.coeff fr.target S st + else ⟨S, st, .ret⟩ + else + if h : fr.child.val + 1 < d then + ⟨scale (reg fr.target ⟨fr.child.val + 1, h⟩) (unitAt us fr.round : K) q.regs, + { fr with child := ⟨fr.child.val + 1, h⟩ } :: st, + .call 1 (reg fr.target ⟨fr.child.val + 1, h⟩)⟩ + else + ⟨updateAt E read (path st) fr.coeff fr.target q.regs, + { fr with child := ⟨0, hd⟩, undo := true } :: st, + .call (-1) (reg fr.target ⟨0, hd⟩)⟩ + +/-- Iterate control steps from any supplied configuration. -/ +def runFrom (hd : 0 < d) (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) + (read : List (Fin d) → Tree.Node d b) (q : Cfg K d r) (n : ℕ) : Cfg K d r := + (step hd e E us read)^[n] q + +@[simp] theorem runFrom_zero (hd : 0 < d) (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) + (read : List (Fin d) → Tree.Node d b) (q : Cfg K d r) : + runFrom hd e E us read q 0 = q := rfl + +@[simp] theorem runFrom_one (hd : 0 < d) (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) + (read : List (Fin d) → Tree.Node d b) (q : Cfg K d r) : + runFrom hd e E us read q 1 = step hd e E us read q := rfl + +/-- A successor clock performs one more control step. -/ +theorem runFrom_succ (hd : 0 < d) (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) + (read : List (Fin d) → Tree.Node d b) (q : Cfg K d r) (n : ℕ) : + runFrom hd e E us read q (n + 1) = step hd e E us read (runFrom hd e E us read q n) := + Function.iterate_succ_apply' _ _ _ + +/-- Consecutive runs compose without resetting registers or continuation frames. -/ +theorem runFrom_add (hd : 0 < d) (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) + (read : List (Fin d) → Tree.Node d b) (q : Cfg K d r) (m n : ℕ) : + runFrom hd e E us read q (m + n) = + runFrom hd e E us read (runFrom hd e E us read q m) n := by + induction n with + | zero => simp + | succ n ih => rw [Nat.add_succ, runFrom_succ, ih, runFrom_succ] + +/-- A final return is stationary. -/ +@[simp] theorem step_ret_nil (hd : 0 < d) (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) + (read : List (Fin d) → Tree.Node d b) (S : Ary.State K d r) : + step hd e E us read ⟨S, [], .ret⟩ = ⟨S, [], .ret⟩ := rfl + +/-- A leaf call uses one control step and leaves its continuation stack intact. -/ +theorem step_call_leaf (hd : 0 < d) (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) + (read : List (Fin d) → Tree.Node d b) (S : Ary.State K d r) (st : List (Frame K d)) + (c : K) (t : Fin (d + 1)) (v : Fin b → Bool) (hv : read (path st) = .leaf v) : + step hd e E us read ⟨S, st, .call c t⟩ = + ⟨Function.update S t (S t + c • e v), st, .ret⟩ := by + simp [step, hv] + +/-- A gate starts the first round when the unit enumeration is nonempty. -/ +theorem step_call_gate_cons (hd : 0 < d) (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (u : Kˣ) (us : List Kˣ) + (read : List (Fin d) → Tree.Node d b) (S : Ary.State K d r) (st : List (Frame K d)) + (c : K) (t : Fin (d + 1)) (f : Gate d b) (hf : read (path st) = .gate f) : + step hd e E (u :: us) read ⟨S, st, .call c t⟩ = beginRound hd (u :: us) 0 c t S st := by + simp [step, hf] + +/-- With no unit rounds a gate call immediately returns unchanged registers. -/ +theorem step_call_gate_nil (hd : 0 < d) (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) + (read : List (Fin d) → Tree.Node d b) (S : Ary.State K d r) (st : List (Frame K d)) + (c : K) (t : Fin (d + 1)) (f : Gate d b) (hf : read (path st) = .gate f) : + step hd e E [] read ⟨S, st, .call c t⟩ = ⟨S, st, .ret⟩ := by + simp [step, hf] + +/-- Reading a valid gate performs exactly the supplied algebraic gate update. -/ +theorem updateAt_gate (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) + (read : List (Fin d) → Tree.Node d b) (P : List (Fin d)) (c : K) + (t : Fin (d + 1)) (S : Ary.State K d r) (f : Gate d b) (hf : read P = .gate f) : + updateAt E read P c t S = gateUpdateWith E f c t S := by + simp [updateAt, hf] + +/-- After a forward child returns, scale and call the next child if one remains. -/ +theorem step_ret_forward_next (hd : 0 < d) (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) + (read : List (Fin d) → Tree.Node d b) (S : Ary.State K d r) (fr : Frame K d) + (st : List (Frame K d)) (hu : fr.undo = false) (hj : fr.child.val + 1 < d) : + step hd e E us read ⟨S, fr :: st, .ret⟩ = + ⟨scale (reg fr.target ⟨fr.child.val + 1, hj⟩) (unitAt us fr.round : K) S, + { fr with child := ⟨fr.child.val + 1, hj⟩ } :: st, + .call 1 (reg fr.target ⟨fr.child.val + 1, hj⟩)⟩ := by + simp [step, hu, hj] + +/-- The last forward return performs the gate update and calls the first undo child. -/ +theorem step_ret_forward_last (hd : 0 < d) (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) + (read : List (Fin d) → Tree.Node d b) (S : Ary.State K d r) (fr : Frame K d) + (st : List (Frame K d)) (hu : fr.undo = false) (hj : ¬fr.child.val + 1 < d) : + step hd e E us read ⟨S, fr :: st, .ret⟩ = + ⟨updateAt E read (path st) fr.coeff fr.target S, + { fr with child := ⟨0, hd⟩, undo := true } :: st, + .call (-1) (reg fr.target ⟨0, hd⟩)⟩ := by + simp [step, hu, hj] + +/-- An undo return first cancels the scaling, then calls the next child without scaling it. -/ +theorem step_ret_undo_next (hd : 0 < d) (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) + (read : List (Fin d) → Tree.Node d b) (S : Ary.State K d r) (fr : Frame K d) + (st : List (Frame K d)) (hu : fr.undo = true) (hj : fr.child.val + 1 < d) : + step hd e E us read ⟨S, fr :: st, .ret⟩ = + ⟨scale (reg fr.target fr.child) (((unitAt us fr.round)⁻¹ : Kˣ) : K) S, + { fr with child := ⟨fr.child.val + 1, hj⟩ } :: st, + .call (-1) (reg fr.target ⟨fr.child.val + 1, hj⟩)⟩ := by + simp [step, hu, hj] + +/-- The last undo return begins the next round if one remains. -/ +theorem step_ret_undo_round (hd : 0 < d) (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) + (read : List (Fin d) → Tree.Node d b) (S : Ary.State K d r) (fr : Frame K d) + (st : List (Frame K d)) (hu : fr.undo = true) (hj : ¬fr.child.val + 1 < d) + (hi : fr.round + 1 < us.length) : + step hd e E us read ⟨S, fr :: st, .ret⟩ = + beginRound hd us (fr.round + 1) fr.coeff fr.target + (scale (reg fr.target fr.child) (((unitAt us fr.round)⁻¹ : Kˣ) : K) S) st := by + simp [step, hu, hj, hi] + +/-- The final undo return pops exactly the current call's frame. -/ +theorem step_ret_undo_last (hd : 0 < d) (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) + (read : List (Fin d) → Tree.Node d b) (S : Ary.State K d r) (fr : Frame K d) + (st : List (Frame K d)) (hu : fr.undo = true) (hj : ¬fr.child.val + 1 < d) + (hi : ¬fr.round + 1 < us.length) : + step hd e E us read ⟨S, fr :: st, .ret⟩ = + ⟨scale (reg fr.target fr.child) (((unitAt us fr.round)⁻¹ : Kˣ) : K) S, st, .ret⟩ := by + simp [step, hu, hj, hi] + +end Cslib.TreeEval.Ary.Iter diff --git a/Complexity/Computability/TreeEval/Ary/IterativeBounds.lean b/Complexity/Computability/TreeEval/Ary/IterativeBounds.lean new file mode 100644 index 0000000000..1e29282303 --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/IterativeBounds.lean @@ -0,0 +1,152 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Iterative + +/-! +# Compact control bounds for iterative Cook–Mertz + +On an existing Tree Evaluation tree of height `H`, the iterative controller keeps at most `H` +frames. Every frame's round index remains below the length of the supplied unit enumeration. +These properties hold at every clock, not only at the final return, and also when the unit list +is empty. No saved register bank or subtree is added to a frame. + +## Main definitions + +* `Cslib.TreeEval.Ary.Iter.ControlBounds`: bounded stack depth and valid round indices. + +## Main results + +* `Cslib.TreeEval.Ary.Iter.controlBounds_runFrom`: every control step preserves the bounds. +* `Cslib.TreeEval.Ary.Iter.runFrom_initial_bounds`: the bounds for a root call at every clock. + +This is a pure control invariant. It does not give a native encoding-length or machine-space +certificate for the registers, lookup operation, coefficients or gate extension. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Iter + +variable {K : Type*} {d b r H : ℕ} + +/-- The controller has bounded depth and stores only valid unit-round indices. -/ +@[expose] def ControlBounds (height rounds : ℕ) (q : Cfg K d r) : Prop := + q.stack.length ≤ height ∧ ∀ fr ∈ q.stack, fr.round < rounds + +private theorem length_lt_of_nodeAt_gate (T : Tree d b H) {P : List (Fin d)} {f : Gate d b} + (hf : T.nodeAt P = .gate f) : P.length < H := by + induction T generalizing P with + | leaf v => cases P <;> simp [Tree.nodeAt] at hf + | node g children ih => + cases P with + | nil => simp + | cons j P => + change (children j).nodeAt P = .gate f at hf + exact Nat.succ_lt_succ (ih j hf) + +private theorem controlBounds_cons (S : Ary.State K d r) (fr : Frame K d) + (st : List (Frame K d)) (mode : Mode K d) {height rounds : ℕ} + (hdepth : st.length + 1 ≤ height) (hround : fr.round < rounds) + (hframes : ∀ g ∈ st, g.round < rounds) : + ControlBounds height rounds ⟨S, fr :: st, mode⟩ := by + refine ⟨by simpa only [List.length_cons] using hdepth, ?_⟩ + intro g hg + rcases List.mem_cons.mp hg with rfl | hg + · exact hround + · exact hframes g hg + +variable [Field K] + +private theorem controlBounds_beginRound (hd : 0 < d) (us : List Kˣ) (i : ℕ) (c : K) + (t : Fin (d + 1)) (S : Ary.State K d r) (st : List (Frame K d)) + (hdepth : st.length < H) (hi : i < us.length) + (hframes : ∀ fr ∈ st, fr.round < us.length) : + ControlBounds H us.length (beginRound hd us i c t S st) := + controlBounds_cons _ _ _ _ (by omega) hi hframes + +/-- For a real tree lookup, a control step preserves depth and unit-round bounds even from an +arbitrary bounded configuration. Invalid node paths return without pushing another frame. -/ +theorem controlBounds_step (T : Tree d b H) (hd : 0 < d) + (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) + {q : Cfg K d r} (hq : ControlBounds H us.length q) : + ControlBounds H us.length (step hd e E us T.nodeAt q) := by + rcases q with ⟨S, st, mode⟩ + obtain ⟨hdepth, hframes⟩ := hq + cases mode with + | call c t => + cases hread : T.nodeAt (path st) with + | leaf v => + rw [step_call_leaf hd e E us T.nodeAt S st c t v hread] + exact ⟨hdepth, hframes⟩ + | none => + simpa only [step, hread] using (show ControlBounds H us.length ⟨S, st, .ret⟩ from + ⟨hdepth, hframes⟩) + | gate f => + have hlt : st.length < H := by + simpa only [length_path] using length_lt_of_nodeAt_gate T hread + cases us with + | nil => + rw [step_call_gate_nil hd e E T.nodeAt S st c t f hread] + exact ⟨hdepth, hframes⟩ + | cons u us => + rw [step_call_gate_cons hd e E u us T.nodeAt S st c t f hread] + exact controlBounds_beginRound hd (u :: us) 0 c t S st hlt (by simp) hframes + | ret => + cases st with + | nil => exact ⟨hdepth, hframes⟩ + | cons fr st => + have hfr : fr.round < us.length := hframes fr (List.mem_cons_self ..) + have htail : ∀ g ∈ st, g.round < us.length := fun g hg => + hframes g (List.mem_cons_of_mem _ hg) + have hcons : st.length + 1 ≤ H := by simpa only [List.length_cons] using hdepth + by_cases hu : fr.undo = true + · by_cases hj : fr.child.val + 1 < d + · rw [step_ret_undo_next hd e E us T.nodeAt S fr st hu hj] + exact controlBounds_cons _ _ _ _ hcons hfr htail + · by_cases hi : fr.round + 1 < us.length + · rw [step_ret_undo_round hd e E us T.nodeAt S fr st hu hj hi] + exact controlBounds_beginRound hd us (fr.round + 1) fr.coeff fr.target _ st + (by omega) hi htail + · rw [step_ret_undo_last hd e E us T.nodeAt S fr st hu hj hi] + exact ⟨by change st.length ≤ H; omega, htail⟩ + · have hu' : fr.undo = false := by + cases h : fr.undo with + | false => rfl + | true => exact (hu h).elim + by_cases hj : fr.child.val + 1 < d + · rw [step_ret_forward_next hd e E us T.nodeAt S fr st hu' hj] + exact controlBounds_cons _ _ _ _ hcons hfr htail + · rw [step_ret_forward_last hd e E us T.nodeAt S fr st hu' hj] + exact controlBounds_cons _ _ _ _ hcons hfr htail + +/-- Every clocked prefix preserves compact control bounds. No termination or nonempty-round +hypothesis is needed. -/ +theorem controlBounds_runFrom (T : Tree d b H) (hd : 0 < d) + (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) + {q : Cfg K d r} (hq : ControlBounds H us.length q) (n : ℕ) : + ControlBounds H us.length (runFrom hd e E us T.nodeAt q n) := by + induction n with + | zero => exact hq + | succ n ih => + rw [runFrom_succ] + exact controlBounds_step T hd e E us ih + +/-- Starting a root call gives at most one frame per tree level and bounds every round index, +at every clock, including runs with an empty unit enumeration. -/ +theorem runFrom_initial_bounds (T : Tree d b H) (hd : 0 < d) + (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) + (S : Ary.State K d r) (c : K) (t : Fin (d + 1)) (n : ℕ) : + ControlBounds H us.length (runFrom hd e E us T.nodeAt (initialCfg S c t) n) := + controlBounds_runFrom T hd e E us (by simp [ControlBounds, initialCfg]) n + +end Cslib.TreeEval.Ary.Iter diff --git a/Complexity/Computability/TreeEval/Ary/IterativeCorrect.lean b/Complexity/Computability/TreeEval/Ary/IterativeCorrect.lean new file mode 100644 index 0000000000..deec341185 --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/IterativeCorrect.lean @@ -0,0 +1,264 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Iterative + +/-! +# Correctness of the iterative fan-in-d Cook–Mertz controller + +A call evaluates the existing recursive procedure while preserving its caller's continuation +stack. Node access is supplied by a path lookup agreeing with a subtree. In particular, using +an existing `Ary.Tree` as the lookup yields a terminating root call with exactly `Ary.procWith`'s +registers. No separate tree or evaluation semantics is introduced. + +## Main results + +* `Cslib.TreeEval.Ary.Iter.exists_runFrom_call`: a subtree call implements `Ary.procWith`. +* `Cslib.TreeEval.Ary.Iter.exists_runFrom_root`: the complete root call returns and halts. + +## References + +* [O. Goldreich, *On the Cook–Mertz Tree Evaluation procedure*][Goldreich2024TreeEval], + Algorithm 1. The loop follows the signed calls of the existing fan-in-d algebra. + +This proves pure control-loop correspondence, not a native implicit-input space theorem. +The gate extension is supplied; its implementation and same-input subroutine composition are +separate from the control-loop correspondence. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Iter + +variable {K : Type*} [Field K] {d b r : ℕ} + +private def Reaches {α : Type*} (next : α → α) (a z : α) : Prop := ∃ n, next^[n] a = z + +private theorem Reaches.single {α : Type*} {next : α → α} {a z : α} + (h : next a = z) : Reaches next a z := ⟨1, h⟩ + +private theorem Reaches.trans {α : Type*} {next : α → α} {a b z : α} + (hab : Reaches next a b) (hbz : Reaches next b z) : Reaches next a z := by + obtain ⟨m, hm⟩ := hab + obtain ⟨n, hn⟩ := hbz + refine ⟨m + n, ?_⟩ + rw [Nat.add_comm, Function.iterate_add_apply, hm, hn] + +private theorem foldl_take_succ {α β : Type*} (l : List α) (f : β → α → β) (a : β) + {j : ℕ} (hj : j < l.length) : + (l.take (j + 1)).foldl f a = f ((l.take j).foldl f a) l[j] := by + rw [List.take_succ_eq_append_getElem hj, List.foldl_append] + rfl + +variable {K : Type*} [Field K] {d b r : ℕ} + +/-- A complete iterative masking round agrees with the supplied-extension algebraic round. +The child-call hypothesis permits arbitrary global registers and unchanged suspended frames. +Only control data, not register banks, are retained in the stack. -/ +private theorem exists_runFrom_round (hd : 0 < d) + (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) + (us : List Kˣ) (read : List (Fin d) → Tree.Node d b) + (p : Fin d → K → Fin (d + 1) → Ary.State K d r → Ary.State K d r) + (f : Gate d b) (st : List (Frame K d)) (c : K) (t : Fin (d + 1)) (i : ℕ) + (hgate : read (path st) = .gate f) + (hp : ∀ (fr : Frame K d) (c' : K) (t' : Fin (d + 1)) (S : Ary.State K d r), + ∃ n, runFrom hd e E us read ⟨S, fr :: st, .call c' t'⟩ n = + ⟨p fr.child c' t' S, fr :: st, .ret⟩) + (S : Ary.State K d r) : + ∃ n, runFrom hd e E us read (beginRound hd us i c t S st) n = + if i + 1 < us.length then + beginRound hd us (i + 1) c t (gateRoundWith E p f c t (unitAt us i) S) st + else ⟨gateRoundWith E p f c t (unitAt us i) S, st, .ret⟩ := by + let next := step hd e E us read + let u := unitAt us i + let F (j : ℕ) := ((List.finRange d).take j).foldl + (fun S j => p j 1 (reg t j) (scale (reg t j) (u : K) S)) S + have hF0 : F 0 = S := rfl + have hF (j : ℕ) (hj : j < d) : + F (j + 1) = p ⟨j, hj⟩ 1 (reg t ⟨j, hj⟩) + (scale (reg t ⟨j, hj⟩) (u : K) (F j)) := by + dsimp only [F] + rw [foldl_take_succ _ _ _ (by simpa using hj), List.getElem_finRange] + simp only [Fin.cast_mk] + have hforward : ∀ j (hj : j < d), Reaches next (beginRound hd us i c t S st) + ⟨F (j + 1), ⟨i, ⟨j, hj⟩, false, c, t⟩ :: st, .ret⟩ := by + intro j + induction j with + | zero => + intro hj + rw [hF 0 hj, hF0] + exact hp ⟨i, ⟨0, hj⟩, false, c, t⟩ 1 (reg t ⟨0, hj⟩) + (scale (reg t ⟨0, hj⟩) (u : K) S) + | succ j ih => + intro hj + have hj' : j < d := by omega + have hnext : next ⟨F (j + 1), ⟨i, ⟨j, hj'⟩, false, c, t⟩ :: st, .ret⟩ = + ⟨scale (reg t ⟨j + 1, hj⟩) (u : K) (F (j + 1)), + ⟨i, ⟨j + 1, hj⟩, false, c, t⟩ :: st, .call 1 (reg t ⟨j + 1, hj⟩)⟩ := by + simp [next, step, hj, u] + rw [show j.succ + 1 = (j + 1) + 1 by omega, hF (j + 1) hj] + exact (ih hj').trans (Reaches.single hnext) |>.trans + (hp ⟨i, ⟨j + 1, hj⟩, false, c, t⟩ 1 (reg t ⟨j + 1, hj⟩) + (scale (reg t ⟨j + 1, hj⟩) (u : K) (F (j + 1)))) + let G := gateUpdateWith E f c t (F d) + let B (j : ℕ) := ((List.finRange d).take j).foldl + (fun S j => scale (reg t j) ((u⁻¹ : Kˣ) : K) (p j (-1) (reg t j) S)) G + have hB0 : B 0 = G := rfl + have hB (j : ℕ) (hj : j < d) : + B (j + 1) = scale (reg t ⟨j, hj⟩) ((u⁻¹ : Kˣ) : K) + (p ⟨j, hj⟩ (-1) (reg t ⟨j, hj⟩) (B j)) := by + dsimp only [B] + rw [foldl_take_succ _ _ _ (by simpa using hj), List.getElem_finRange] + simp only [Fin.cast_mk] + let undoStart : Cfg K d r := + ⟨G, ⟨i, ⟨0, hd⟩, true, c, t⟩ :: st, .call (-1) (reg t ⟨0, hd⟩)⟩ + have hlast : d - 1 < d := by omega + have hlastEq : d - 1 + 1 = d := by omega + have hupdate : Reaches next (beginRound hd us i c t S st) undoStart := by + have hret := hforward (d - 1) hlast + rw [hlastEq] at hret + apply hret.trans + apply Reaches.single + simp [next, step, show ¬(d - 1 + 1 < d) by omega, updateAt, hgate, undoStart, G] + have hbackward : ∀ j (hj : j < d), Reaches next undoStart + ⟨p ⟨j, hj⟩ (-1) (reg t ⟨j, hj⟩) (B j), + ⟨i, ⟨j, hj⟩, true, c, t⟩ :: st, .ret⟩ := by + intro j + induction j with + | zero => + intro hj + rw [hB0] + exact hp ⟨i, ⟨0, hj⟩, true, c, t⟩ (-1) (reg t ⟨0, hj⟩) G + | succ j ih => + intro hj + have hj' : j < d := by omega + have hnext : next + ⟨p ⟨j, hj'⟩ (-1) (reg t ⟨j, hj'⟩) (B j), + ⟨i, ⟨j, hj'⟩, true, c, t⟩ :: st, .ret⟩ = + ⟨B (j + 1), ⟨i, ⟨j + 1, hj⟩, true, c, t⟩ :: st, + .call (-1) (reg t ⟨j + 1, hj⟩)⟩ := by + simp [next, step, hj, hB j hj', u] + exact (ih hj').trans (Reaches.single hnext) |>.trans + (hp ⟨i, ⟨j + 1, hj⟩, true, c, t⟩ (-1) (reg t ⟨j + 1, hj⟩) (B (j + 1))) + have hBdone : B d = gateRoundWith E p f c t u S := by + have htake : (List.finRange d).take d = List.finRange d := + List.take_of_length_le (by simp) + simp only [B, G, F, gateRoundWith, htake] + have hscaleLast : + scale (reg t ⟨d - 1, hlast⟩) ((u⁻¹ : Kˣ) : K) + (p ⟨d - 1, hlast⟩ (-1) (reg t ⟨d - 1, hlast⟩) (B (d - 1))) = B d := by + rw [← hB (d - 1) hlast, hlastEq] + have hexit : next + ⟨p ⟨d - 1, hlast⟩ (-1) (reg t ⟨d - 1, hlast⟩) (B (d - 1)), + ⟨i, ⟨d - 1, hlast⟩, true, c, t⟩ :: st, .ret⟩ = + if i + 1 < us.length then beginRound hd us (i + 1) c t (B d) st + else ⟨B d, st, .ret⟩ := by + simp [next, step, show ¬(d - 1 + 1 < d) by omega, ← hscaleLast, u] + have hrun := hupdate.trans (hbackward (d - 1) hlast) |>.trans (Reaches.single hexit) + rw [hBdone] at hrun + exact hrun + + +private theorem unitAt_getElem (us : List Kˣ) (i : ℕ) (hi : i < us.length) : + unitAt us i = us[i] := by + simp [unitAt, List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hi] + +private theorem exists_runFrom_gate (hd : 0 < d) + (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) + (us : List Kˣ) (read : List (Fin d) → Tree.Node d b) + (p : Fin d → K → Fin (d + 1) → Ary.State K d r → Ary.State K d r) + (f : Gate d b) (st : List (Frame K d)) + (hgate : read (path st) = .gate f) + (hp : ∀ (fr : Frame K d) (c : K) (t : Fin (d + 1)) (S : Ary.State K d r), + ∃ N, runFrom hd e E us read ⟨S, fr :: st, .call c t⟩ N = + ⟨p fr.child c t S, fr :: st, .ret⟩) + (S : Ary.State K d r) (c : K) (t : Fin (d + 1)) : + ∃ N, runFrom hd e E us read ⟨S, st, .call c t⟩ N = + ⟨us.foldl (fun S u => gateRoundWith E p f c t u S) S, st, .ret⟩ := by + cases hus : us with + | nil => + refine ⟨1, ?_⟩ + simp only [runFrom_one, List.foldl_nil] + exact step_call_gate_nil hd e E read S st c t f hgate + | cons u tail => + let F := fun S u => gateRoundWith E p f c t u S + have hprefix : ∀ j, j ≤ us.length → ∃ N, + runFrom hd e E us read ⟨S, st, .call c t⟩ N = + if j < us.length then beginRound hd us j c t ((us.take j).foldl F S) st + else ⟨(us.take j).foldl F S, st, .ret⟩ := by + intro j + induction j with + | zero => + intro _ + refine ⟨1, ?_⟩ + have hlen : 0 < us.length := by simp [hus] + simp only [hlen, ↓reduceIte, List.take_zero, List.foldl_nil, runFrom_one] + rw [hus] + exact step_call_gate_cons hd e E u tail read S st c t f hgate + | succ j ih => + intro hj + have hjlt : j < us.length := by omega + obtain ⟨N, hN⟩ := ih (by omega) + rw [ite_eq_left hjlt] at hN + obtain ⟨M, hM⟩ := exists_runFrom_round hd e E us read p f st c t j + hgate hp ((us.take j).foldl F S) + refine ⟨N + M, ?_⟩ + rw [runFrom_add, hN, hM] + have hfold : (us.take (j + 1)).foldl F S = + gateRoundWith E p f c t (unitAt us j) ((us.take j).foldl F S) := by + rw [foldl_take_succ us F S hjlt, unitAt_getElem us j hjlt] + rw [hfold] + obtain ⟨N, hN⟩ := hprefix us.length le_rfl + refine ⟨N, ?_⟩ + simpa only [Nat.lt_irrefl, ↓reduceIte, List.take_length, F, hus] using hN + +/-- A call on any subtree implemented by the supplied lookup runs the existing recursive +procedure and returns to the same continuation stack, from arbitrary global register contents. -/ +theorem exists_runFrom_call (hd : 0 < d) + (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) + (us : List Kˣ) (read : List (Fin d) → Tree.Node d b) + {h : ℕ} (T : Tree d b h) (st : List (Frame K d)) + (hread : ∀ P, read (path st ++ P) = T.nodeAt P) + (S : Ary.State K d r) (c : K) (t : Fin (d + 1)) : + ∃ N, runFrom hd e E us read ⟨S, st, .call c t⟩ N = + ⟨procWith e E us T c t S, st, .ret⟩ := by + induction T generalizing st S c t with + | leaf v => + refine ⟨1, ?_⟩ + have hv : read (path st) = .leaf v := by + simpa only [List.append_nil, Tree.nodeAt_leaf_nil] using hread [] + rw [runFrom_one, step_call_leaf hd e E us read S st c t v hv] + rfl + | node f ch ih => + have hgate : read (path st) = .gate f := by + simpa only [List.append_nil, Tree.nodeAt_node_nil] using hread [] + have hp (fr : Frame K d) (c' : K) (t' : Fin (d + 1)) (S' : Ary.State K d r) : + ∃ N, runFrom hd e E us read ⟨S', fr :: st, .call c' t'⟩ N = + ⟨procWith e E us (ch fr.child) c' t' S', fr :: st, .ret⟩ := by + apply ih fr.child (fr :: st) _ S' c' t' + intro P + rw [path_cons, List.append_assoc, List.singleton_append] + exact hread (fr.child :: P) + exact exists_runFrom_gate hd e E us read (fun j => procWith e E us (ch j)) + f st hgate hp S c t + +/-- The complete root call terminates with exactly the recursive procedure's registers. -/ +theorem exists_runFrom_root (hd : 0 < d) + (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) + (us : List Kˣ) {h : ℕ} (T : Tree d b h) + (S : Ary.State K d r) (c : K) (t : Fin (d + 1)) : + ∃ N, runFrom hd e E us T.nodeAt (initialCfg S c t) N = + ⟨procWith e E us T c t S, [], .ret⟩ := by + exact exists_runFrom_call hd e E us T.nodeAt T [] (fun _ => rfl) S c t + +end Cslib.TreeEval.Ary.Iter diff --git a/Complexity/Computability/TreeEval/Ary/IterativePacked.lean b/Complexity/Computability/TreeEval/Ary/IterativePacked.lean new file mode 100644 index 0000000000..1aa0137702 --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/IterativePacked.lean @@ -0,0 +1,61 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.IterativeCorrect +public import Complexity.Computability.TreeEval.Ary.Packed.Executable + +/-! +# Iterative packed Cook–Mertz with counter-based gate updates + +Instantiate the existing control loop with the counter implementation of finite-grid +interpolation. The resulting loop adds the encoded tree value to its target and restores the +other registers, from arbitrary initial contents. It uses the existing tree model and packed +encoding contract, with the strict finite-field degree threshold. + +## Main results + +* `Cslib.TreeEval.Ary.Iter.exists_runFrom_counter_root`: the packed controller computes the + encoded root value using counter-based gate updates. + +This is pure iterative correctness. Native lookup, original-input subroutine composition and +the complete machine-space certificate are not supplied by this result. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Iter + +open Packed + +/-- A packed root call using the counter-based gate extension adds the encoded root value and +preserves every other global register. The encoding has a left inverse. -/ +theorem exists_runFrom_counter_root {p s d b r : ℕ} [Fact p.Prime] + (hd : 0 < d) (hs : 0 < s) (hsp : s ≤ p) + (h : Fin s ↪ ZMod p) (hh : ∀ a, h a = (a.val : ZMod p)) + (encode : (Fin b → Bool) → Fin r → Fin s) + (decode : (Fin r → Fin s) → Fin b → Bool) (hdecode : Function.LeftInverse decode encode) + (hD : d * r * (s - 1) < p - 1) + {us : List (ZMod p)ˣ} (hnd : us.Nodup) (hmem : ∀ u, u ∈ us) + {height : ℕ} (T : Tree d b height) (S : Ary.State (ZMod p) d r) + (c : ZMod p) (t : Fin (d + 1)) : + ∃ N, runFrom hd (fun v i => h (encode v i)) (counterGateExtension hs encode decode) + us T.nodeAt (initialCfg S c t) N = + ⟨Function.update S t (S t + c • fun i => h (encode T.eval i)), [], .ret⟩ := by + obtain ⟨N, hN⟩ := exists_runFrom_root hd (fun v i => h (encode v i)) + (counterGateExtension hs encode decode) us T S c t + refine ⟨N, ?_⟩ + rw [hN] + have hE : counterGateExtension (p := p) (d := d) hs encode decode = + gateExtension h encode decode := by + funext f x i + exact counterGateExtension_eq hs hsp h hh encode decode f x i + rw [hE, procWith_gateExtension_eq h encode decode hdecode + (by simpa only [Fintype.card_fin, ZMod.card] using hD) hnd hmem] + +end Cslib.TreeEval.Ary.Iter diff --git a/Complexity/Computability/TreeEval/Ary/Packed.lean b/Complexity/Computability/TreeEval/Ary/Packed.lean index fe3f8233c7..67edeec333 100644 --- a/Complexity/Computability/TreeEval/Ary/Packed.lean +++ b/Complexity/Computability/TreeEval/Ary/Packed.lean @@ -83,7 +83,7 @@ variable {K H : Type*} [Field K] [Fintype H] [DecidableEq H] {b d r : ℕ} /-- Extend a Boolean gate after encoding each value in `r` alphabet symbols. On symbols outside the encoding's image, the caller's decoder determines a total table. -/ -noncomputable def gateExtension (h : H ↪ K) (encode : (Fin b → Bool) → Fin r → H) +@[expose] noncomputable def gateExtension (h : H ↪ K) (encode : (Fin b → Bool) → Fin r → H) (decode : (Fin r → H) → Fin b → Bool) (f : Gate d b) (x : Fin d → Fin r → K) (i : Fin r) : K := Code.lowDegreeExtension h diff --git a/Complexity/Computability/TreeEval/Ary/Packed/Executable.lean b/Complexity/Computability/TreeEval/Ary/Packed/Executable.lean new file mode 100644 index 0000000000..138fcae942 --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/Executable.lean @@ -0,0 +1,94 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed +public import Complexity.Computability.TreeEval.Ary.Packed.GateInterpolation + +/-! +# Counter implementation of packed gate extensions + +The existing finite-grid gate extension is computed by the counter-based interpolation loop. +A table query decodes one assignment index, calls the Boolean gate and encodes one output chunk. +This connects the packed algebra to the gate update of the iterative controller, without +constructing a truth table or a polynomial representation at runtime. + +## Main definitions + +* `Cslib.TreeEval.Ary.Packed.counterGateExtension`: the counter-based gate extension. + +## Main results + +* `Cslib.TreeEval.Ary.Packed.counterGateExtension_eq`: equality with `gateExtension` for an + alphabet embedded by unsigned casts in a prime field. + +This is functional correctness of the inner loop. Native gate queries and their composition +with the caller's original input still require a separate resource certificate. +-/ + +@[expose] public section + +namespace Cslib.TreeEval.Ary.Packed + +open Cslib.BitString + +/-- Read the low `n` base-`s` digits of an assignment counter. -/ +private def counterAssignment {s n : ℕ} (hs : 0 < s) (k : ℕ) : Fin n → Fin s := + fun i => ⟨gridDigit s k i, Nat.mod_lt _ hs⟩ + +private theorem counterAssignment_number {s n : ℕ} (hs : 0 < s) (a : Fin n → Fin s) : + counterAssignment hs (finFunctionFinEquiv a) = a := by + funext i + apply Fin.ext + exact (finFunctionFinEquiv_symm_apply_val (finFunctionFinEquiv a) i).symm.trans + (congrArg Fin.val (congrFun (finFunctionFinEquiv.symm_apply_apply a) i)) + +/-- Compute one chunk of a gate extension using the finite-grid counter loops. The point and +one requested table value are passed to the arithmetic loop; no table is materialized. -/ +def counterGateExtension {p s b d r : ℕ} (hs : 0 < s) + (encode : (Fin b → Bool) → Fin r → Fin s) + (decode : (Fin r → Fin s) → Fin b → Bool) (f : Gate d b) + (x : Fin d → Fin r → ZMod p) (i : Fin r) : ZMod p := + (interpolateGrid p s (d * r) + (fun k => (encode (f fun j => decode (blocksEquiv (fun q : Fin (d * r) => + (⟨gridDigit s k q, Nat.mod_lt _ hs⟩ : Fin s)) j)) i).val) + (List.ofFn fun q : Fin (d * r) => (blocksEquiv.symm x q).val) : ℕ) + +/-- The counter loop computes the same packed extension as the existing low-degree algebra. +The full alphabet, not only the encoded values, must embed in the selected field. -/ +theorem counterGateExtension_eq {p s b d r : ℕ} [Fact p.Prime] (hs : 0 < s) (hsp : s ≤ p) + (h : Fin s ↪ ZMod p) (hh : ∀ a, h a = (a.val : ZMod p)) + (encode : (Fin b → Bool) → Fin r → Fin s) + (decode : (Fin r → Fin s) → Fin b → Bool) (f : Gate d b) + (x : Fin d → Fin r → ZMod p) (i : Fin r) : + counterGateExtension hs encode decode f x i = gateExtension h encode decode f x i := by + rw [counterGateExtension, cast_interpolateGrid hs hsp h hh] + have htable : + (fun a : Fin (d * r) → Fin s => + ((encode (f fun j => decode + (blocksEquiv (counterAssignment hs (finFunctionFinEquiv a)) j)) i).val : ZMod p)) = + (fun a => h (encode (f fun j => decode (blocksEquiv a j)) i)) := by + funext a + rw [counterAssignment_number, hh] + have hpoint : + (fun q : Fin (d * r) => + (((List.ofFn fun j : Fin (d * r) => (blocksEquiv.symm x j).val).getD q.val 0 : ℕ) : + ZMod p)) = blocksEquiv.symm x := by + funext q + simp [List.getD_eq_getElem?_getD] + change Code.lowDegreeExtension h + (fun a : Fin (d * r) → Fin s => + ((encode (f fun j => decode + (blocksEquiv (counterAssignment hs (finFunctionFinEquiv a)) j)) i).val : ZMod p)) + (fun q : Fin (d * r) => + (((List.ofFn fun j : Fin (d * r) => (blocksEquiv.symm x j).val).getD q.val 0 : ℕ) : + ZMod p)) = _ + rw [htable, hpoint] + rfl + +end Cslib.TreeEval.Ary.Packed diff --git a/ComplexityTests/TreeEvalIterative.lean b/ComplexityTests/TreeEvalIterative.lean new file mode 100644 index 0000000000..3adc9b9e08 --- /dev/null +++ b/ComplexityTests/TreeEvalIterative.lean @@ -0,0 +1,137 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module + +public import Complexity.Computability.TreeEval.Ary.IterativePacked +public import Complexity.Computability.TreeEval.Ary.IterativeBounds +public import Complexity.Computability.TreeEval.Ary.Packed +public import Complexity.Computability.TreeEval.Iterative + +/-! +# Iterative fan-in-d Cook–Mertz regressions + +Positive arities one, two and three exercise all forward and undo child transitions. Pure +correspondence is checked from arbitrary registers and a nontrivial coefficient. A packed +three-bit value in two coordinates exercises the same controller with a different register width. +-/ + +@[expose] public section + +namespace ComplexityTests.TreeEvalIterative + +open Cslib.TreeEval Cslib.TreeEval.Ary + +instance : Fact (Nat.Prime 7) := ⟨by decide⟩ +instance : Fact (Nat.Prime 17) := ⟨by decide⟩ + +/-- One bit as one field coordinate. -/ +def value (v : Fin 1 → Bool) : Fin 1 → ZMod 7 := fun i => if v i then 1 else 0 + +/-- A gate reading its first child, for any positive arity. -/ +def wire (d : ℕ) (hd : 0 < d) : Ary.Gate d 1 := fun v => v ⟨0, hd⟩ + +/-- The wire gate's field extension. -/ +def extension (d : ℕ) (hd : 0 < d) : + Ary.Gate d 1 → (Fin d → Fin 1 → ZMod 7) → Fin 1 → ZMod 7 := fun _ v => v ⟨0, hd⟩ + +/-- A one-level wire tree with true at the first leaf. -/ +def sample (d : ℕ) (hd : 0 < d) : Ary.Tree d 1 1 := + .node (wire d hd) fun j => .leaf fun _ => j.val == 0 + +/-- A single unit round on this tree takes four transitions per child plus its initial step. -/ +example : (Ary.Iter.runFrom (by decide) value (extension 1 (by decide)) [1] + (sample 1 (by decide)).nodeAt (Ary.Iter.initialCfg 0 1 1) 5).regs 1 0 = 6 := by decide + +example : (Ary.Iter.runFrom (by decide) value (extension 2 (by decide)) [1] + (sample 2 (by decide)).nodeAt (Ary.Iter.initialCfg 0 1 2) 9).regs 2 0 = 6 := by decide + +example : (Ary.Iter.runFrom (by decide) value (extension 3 (by decide)) [1] + (sample 3 (by decide)).nodeAt (Ary.Iter.initialCfg 0 1 3) 13).regs 3 0 = 6 := by decide + +example : (Ary.Iter.runFrom (by decide) value (extension 3 (by decide)) [1] + (sample 3 (by decide)).nodeAt (Ary.Iter.initialCfg 0 1 3) 13).stack = [] := by decide + +/-- A second round restores dirty masks as well as traversing the round-advance branch. -/ +example : (Ary.Iter.runFrom (by decide) value (extension 2 (by decide)) [1, -1] + (sample 2 (by decide)).nodeAt (Ary.Iter.initialCfg (fun _ _ => 4) 1 2) 17).regs = + (fun r _ => if r.val = 2 then 2 else 4) := by decide + +/-- A height-two tree makes the controller resume a genuine suspended caller. -/ +def nested : Ary.Tree 2 1 2 := .node (wire 2 (by decide)) fun _ => sample 2 (by decide) + +/-- The caller frame is control data, without a saved bank of registers. -/ +def caller : Ary.Iter.Frame (ZMod 7) 2 := ⟨0, 0, false, 2, 2⟩ + +example : (Ary.Iter.runFrom (by decide) value (extension 2 (by decide)) [1] nested.nodeAt + ⟨(fun _ _ => 4), [caller], .call 1 0⟩ 9).stack = [caller] := by decide + +example : (Ary.Iter.runFrom (by decide) value (extension 2 (by decide)) [1] nested.nodeAt + (Ary.Iter.initialCfg (fun _ _ => 4) 1 2) 41).regs = + Ary.procWith value (extension 2 (by decide)) [1] nested 1 2 (fun _ _ => 4) := by decide + +example (n : ℕ) : Ary.Iter.ControlBounds 2 1 + (Ary.Iter.runFrom (by decide) value (extension 2 (by decide)) [1] nested.nodeAt + (Ary.Iter.initialCfg (fun _ _ => 4) 1 2) n) := + Ary.Iter.runFrom_initial_bounds nested (by decide) value (extension 2 (by decide)) [1] + (fun _ _ => 4) 1 2 n + +/-- Empty rounds immediately return a gate call, preserving a nonzero register bank. -/ +example : (Ary.Iter.runFrom (by decide) value (extension 3 (by decide)) [] + (sample 3 (by decide)).nodeAt (Ary.Iter.initialCfg (fun _ _ => 4) 2 3) 1).regs = + (fun _ _ => 4) := by decide + +/-- The root theorem applies to arbitrary initial masks, coefficients and nested trees. -/ +theorem arbitrary_masks {h : ℕ} (T : Ary.Tree 3 1 h) (S : Ary.State (ZMod 7) 3 1) + (c : ZMod 7) (t : Fin 4) : ∃ N, + Ary.Iter.runFrom (by decide) value (extension 3 (by decide)) [1, -1] T.nodeAt + (Ary.Iter.initialCfg S c t) N = + ⟨Ary.procWith value (extension 3 (by decide)) [1, -1] T c t S, [], .ret⟩ := + Ary.Iter.exists_runFrom_root (by decide) value (extension 3 (by decide)) [1, -1] T S c t + +/-- The existing binary recursive specification is recovered through its established bridge. -/ +theorem binary_recursive_compatibility {h : ℕ} (T : Cslib.TreeEval.Tree 1 h) + (S : Cslib.TreeEval.State (ZMod 7) 1) (c : ZMod 7) (t : Fin 3) : ∃ N, + Ary.Iter.runFrom (by decide) Cslib.TreeEval.embed + (fun f x i => MvPolynomial.eval (fun q => x q.1 q.2) (Ary.gatePoly f i)) + [1, -1] (Ary.Tree.ofBinary T).nodeAt (Ary.Iter.initialCfg S c t) N = + ⟨Cslib.TreeEval.proc [1, -1] T c t S, [], .ret⟩ := by + obtain ⟨N, hN⟩ := Ary.Iter.exists_runFrom_root (by decide) Cslib.TreeEval.embed + (fun f x i => MvPolynomial.eval (fun q => x q.1 q.2) (Ary.gatePoly f i)) + [1, -1] (Ary.Tree.ofBinary T) S c t + refine ⟨N, ?_⟩ + rw [hN, Ary.procWith_embed, Ary.proc_ofBinary] + +/-- Pack three bits into two symbols, with a partially filled last chunk. -/ +def encode : (Fin 3 → Bool) → Fin 2 → Fin 4 := + Ary.Packed.packBits (r := 2) (m := 2) (by decide) + +/-- Decode the original three-bit prefix. -/ +def decode : (Fin 2 → Fin 4) → Fin 3 → Bool := + Ary.Packed.unpackBits (r := 2) (m := 2) (by decide) + +/-- Four chunk symbols in the field. -/ +def alphabet : Fin 4 ↪ ZMod 17 where + toFun a := a.val + inj' := by + intro a b heq + have hv := congrArg ZMod.val heq + simpa only [ZMod.val_natCast, Nat.mod_eq_of_lt (a.isLt.trans (by decide : 4 < 17)), + Nat.mod_eq_of_lt (b.isLt.trans (by decide : 4 < 17)), Fin.val_inj] using hv + +/-- The iterative loop computes the encoded value with a counter-based inner gate loop. -/ +theorem packed_root {h : ℕ} (T : Ary.Tree 2 3 h) (S : Ary.State (ZMod 17) 2 2) + (c : ZMod 17) (t : Fin 3) : ∃ N, + Ary.Iter.runFrom (by decide) (fun v i => alphabet (encode v i)) + (Ary.Packed.counterGateExtension (by decide : 0 < 4) encode decode) + (Finset.univ : Finset (ZMod 17)ˣ).toList T.nodeAt (Ary.Iter.initialCfg S c t) N = + ⟨Function.update S t (S t + c • fun i => alphabet (encode T.eval i)), [], .ret⟩ := by + apply Ary.Iter.exists_runFrom_counter_root (by decide) (by decide) (by decide) + alphabet (fun _ => rfl) encode decode + (fun v => Ary.Packed.unpackBits_packBits (r := 2) (m := 2) (by decide) v) + (by decide) (Finset.nodup_toList _) (fun u => Finset.mem_toList.mpr (Finset.mem_univ u)) + +end ComplexityTests.TreeEvalIterative From e0e539539476f3b7cafcbba6a479fa97bd58f78b Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sat, 31 Oct 2026 22:34:00 +0000 Subject: [PATCH 06/36] feat(query-machine): compose same-input state-returning calls --- Complexity.lean | 3 + .../MultiTape/Complexity/InSpace/Cases.lean | 91 +++++++++++ .../LogSpace/QueryMachine/Call.lean | 142 +++++++++++++++++ .../LogSpace/QueryMachine/CallSpace.lean | 148 ++++++++++++++++++ ComplexityTests/QueryMachineCall.lean | 147 +++++++++++++++++ 5 files changed, 531 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/Cases.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine/Call.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine/CallSpace.lean create mode 100644 ComplexityTests/QueryMachineCall.lean diff --git a/Complexity.lean b/Complexity.lean index 0512149dd6..2da8ca95c9 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1731,6 +1731,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Degr public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Fold public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Bit +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Plain public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpaceLib public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LinearSpaceArith @@ -1777,6 +1778,8 @@ public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogS public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.NondeterministicQueryMachine.Routines public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.NondeterministicQueryMachine.Termination public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.Call +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.CallSpace public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.Reachability public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.Savitch public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.Words diff --git a/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/Cases.lean b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/Cases.lean new file mode 100644 index 0000000000..48e34c61f5 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/Cases.lean @@ -0,0 +1,91 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpaceLib +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Cases + +/-! +# Case analysis within linear work space + +Case analysis on a tagged sum or option preserves space linear in the encoded input. +The implementations reuse the native case combinators, forgetting their time bounds. +-/ + +public section + +namespace Turing.MultiTapeTM.ComputableInSpace + +variable {π α β ε : Type} {encP : π ↪ List Bool} {encA : α ↪ List Bool} + {encB : β ↪ List Bool} {encE : ε ↪ List Bool} + +/-- Linear-space branches give linear-space case analysis on the second component. -/ +theorem sumElimSnd {f : π × α → ε} {g : π × β → ε} + (hf : ComputableInSpace f (encPair encP encA) encE + fun p => (encPair encP encA p).length) + (hg : ComputableInSpace g (encPair encP encB) encE + fun p => (encPair encP encB p).length) : + ComputableInSpace + (fun p : π × (α ⊕ β) => p.2.elim (fun a => f (p.1, a)) (fun b => g (p.1, b))) + (encPair encP (encSum encA encB)) encE + (fun p => (encPair encP (encSum encA encB) p).length) := by + obtain ⟨_, hf⟩ := hf.exists_inO + obtain ⟨_, hg⟩ := hg.exists_inO + refine (of_inO (ComputableInO.sumElimSnd hf hg)).weaken ⟨2, ?_⟩ + rintro ⟨p, a | b⟩ <;> + simp only [length_encPair, encSum_inl, encSum_inr, List.length_cons, + Sum.elim_inl, Sum.elim_inr] <;> omega + +/-- Inspecting an option and running its selected linear-space branch uses linear space. -/ +theorem optionElimSnd {f : π → ε} {g : π × α → ε} + (hf : ComputableInSpace f encP encE fun p => (encP p).length) + (hg : ComputableInSpace g (encPair encP encA) encE + fun p => (encPair encP encA p).length) : + ComputableInSpace + (fun p : π × Option α => p.2.elim (f p.1) (fun a => g (p.1, a))) + (encPair encP (encOption encA)) encE + (fun p => (encPair encP (encOption encA) p).length) := by + obtain ⟨_, hf⟩ := hf.exists_inO + obtain ⟨_, hg⟩ := hg.exists_inO + refine (of_inO (ComputableInO.optionElimSnd hf hg)).weaken ⟨2, ?_⟩ + rintro ⟨p, _ | a⟩ <;> + simp only [length_encPair, encOption_none, encOption_some, List.length_nil, + List.length_cons, Option.elim_none, Option.elim_some] <;> omega + +/-- Case analysis on the first component preserves a shared second parameter. -/ +theorem sumElimFst {f : α × π → ε} {g : β × π → ε} + (hf : ComputableInSpace f (encPair encA encP) encE + fun p => (encPair encA encP p).length) + (hg : ComputableInSpace g (encPair encB encP) encE + fun p => (encPair encB encP p).length) : + ComputableInSpace + (fun p : (α ⊕ β) × π => p.1.elim (fun a => f (a, p.2)) (fun b => g (b, p.2))) + (encPair (encSum encA encB) encP) encE + (fun p => (encPair (encSum encA encB) encP p).length) := by + have hf' := budget_comp_linear hf (budget_prod (snd encP encA) (fst encP encA)) + have hg' := budget_comp_linear hg (budget_prod (snd encP encB) (fst encP encB)) + exact budget_comp_linear (sumElimSnd hf' hg') + (budget_prod (snd (encSum encA encB) encP) (fst (encSum encA encB) encP)) + +/-- Linear-space case analysis without an auxiliary parameter. -/ +theorem sumElim {f : α → ε} {g : β → ε} + (hf : ComputableInSpace f encA encE fun a => (encA a).length) + (hg : ComputableInSpace g encB encE fun b => (encB b).length) : + ComputableInSpace (Sum.elim f g) (encSum encA encB) encE + (fun s => (encSum encA encB s).length) := by + have hf' := budget_comp_linear hf (snd encUnit encA) + have hg' := budget_comp_linear hg (snd encUnit encB) + have hcase := sumElimSnd hf' hg' + have hid : ComputableInSpace (fun s : α ⊕ β => s) (encSum encA encB) + (encSum encA encB) (fun s => (encSum encA encB s).length) := + of_inO ComputableInDegree.id'.inO_one + exact (budget_comp_linear hcase + (budget_prod (budget_const () (.refl _)) hid)).of_enc_eq fun s => by + cases s <;> rfl + +end Turing.MultiTapeTM.ComputableInSpace diff --git a/Complexity/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine/Call.lean b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine/Call.lean new file mode 100644 index 0000000000..1001b4ed78 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine/Call.lean @@ -0,0 +1,142 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine + +/-! +# State-returning calls between query machines + +`QueryMachine.withCalls` suspends a caller while a callee runs on the same original input. +A request supplies the callee's initial state, and returning supplies its final state to the +caller. Callee emissions are discarded: this construction does not store or return an output +word. In particular, any data retained across a call is part of the explicit machine state. + +## Main definitions + +* `Turing.QueryMachine.runFrom`: a run starting at a specified state. +* `Turing.QueryMachine.withCalls`: state-returning calls on the original input. + +## Main results + +* `Turing.QueryMachine.withCalls_runFrom_subroutine_prefix`: exact simulation before return. +* `Turing.QueryMachine.withCalls_runFrom_call`: a call takes the callee's running time plus + one entry step and one return step. +-/ + +public section + +namespace Turing.QueryMachine + +variable {σ τ : Type*} + +/-- Run a query machine from an explicitly supplied state. -/ +@[expose] def runFrom (M : QueryMachine σ) (w : List Bool) (start : σ) (n : ℕ) : σ := + (M.step w)^[n] start + +@[simp] theorem runFrom_zero (M : QueryMachine σ) (w : List Bool) (start : σ) : + M.runFrom w start 0 = start := rfl + +theorem runFrom_succ (M : QueryMachine σ) (w : List Bool) (start : σ) (n : ℕ) : + M.runFrom w start (n + 1) = M.step w (M.runFrom w start n) := + Function.iterate_succ_apply' _ _ _ + +theorem runFrom_add (M : QueryMachine σ) (w : List Bool) (start : σ) (m n : ℕ) : + M.runFrom w start (m + n) = M.runFrom w (M.runFrom w start m) n := by + induction n with + | zero => rfl + | succ n ih => rw [← Nat.add_assoc, runFrom_succ, ih, runFrom_succ] + +/-- Suspend the caller on a request and resume it with the callee's final state. Both machines +read the same original input. Only ordinary caller steps emit output; call entry, callee steps, +and return emit nothing. The suspended caller and callee states are stored explicitly. -/ +@[expose] def withCalls (M : QueryMachine σ) (N : QueryMachine τ) + (request : σ → Option τ) (resume : σ → τ → σ) : QueryMachine (σ ⊕ (σ × τ)) where + init := .inl M.init + query + | .inl s => match request s with + | none => M.query s + | some _ => 0 + | .inr (_, t) => N.query t + next + | (.inl s, b) => match request s with + | none => .inl (M.next (s, b)) + | some t => .inr (s, t) + | (.inr (s, t), b) => + if N.halted t then .inl (resume s t) else .inr (s, N.next (t, b)) + emit + | (.inl s, b) => match request s with + | none => M.emit (s, b) + | some _ => [] + | (.inr _, _) => [] + halted + | .inl s => M.halted s + | .inr _ => false + finish + | .inl s => M.finish s + | .inr _ => [] + +variable (M : QueryMachine σ) (N : QueryMachine τ) + (request : σ → Option τ) (resume : σ → τ → σ) (w : List Bool) + +/-- Without a request, a composite step is precisely a caller step, including halted states. -/ +theorem withCalls_step_normal (s : σ) (h : request s = none) : + (M.withCalls N request resume).step w (.inl s) = .inl (M.step w s) := by + cases hs : M.halted s <;> simp [step, read, withCalls, h, hs] + +/-- Entering a call takes one step and preserves the suspended caller state. -/ +theorem withCalls_step_call (s : σ) (t : τ) + (hs : M.halted s = false) (h : request s = some t) : + (M.withCalls N request resume).step w (.inl s) = .inr (s, t) := by + simp [step, withCalls, hs, h] + +/-- A live callee step reads exactly the bit that the callee reads on the original input. -/ +theorem withCalls_step_subroutine (s : σ) (t : τ) (h : N.halted t = false) : + (M.withCalls N request resume).step w (.inr (s, t)) = + .inr (s, N.step w t) := by + simp [step, read, withCalls, h] + +/-- Returning from a halted callee takes one additional step. -/ +theorem withCalls_step_return (s : σ) (t : τ) (h : N.halted t = true) : + (M.withCalls N request resume).step w (.inr (s, t)) = .inl (resume s t) := by + simp [step, withCalls, h] + +/-- Up to its first halt, the callee is simulated step for step. -/ +theorem withCalls_runFrom_subroutine_prefix (s : σ) (start : τ) (T : ℕ) + (hpre : ∀ i < T, N.halted (N.runFrom w start i) = false) : + (M.withCalls N request resume).runFrom w (.inr (s, start)) T = + .inr (s, N.runFrom w start T) := by + induction T with + | zero => rfl + | succ T ih => + rw [runFrom_succ, ih (fun i hi => hpre i (Nat.lt_succ_of_lt hi))] + rw [withCalls_step_subroutine M N request resume w s _ (hpre T (Nat.lt_succ_self T))] + rw [runFrom_succ] + +/-- A callee that first halts at `T` returns its final state at time `T + 1`. -/ +theorem withCalls_runFrom_return (s : σ) (start : τ) (T : ℕ) + (hpre : ∀ i < T, N.halted (N.runFrom w start i) = false) + (hhalt : N.halted (N.runFrom w start T) = true) : + (M.withCalls N request resume).runFrom w (.inr (s, start)) (T + 1) = + .inl (resume s (N.runFrom w start T)) := by + rw [runFrom_succ, withCalls_runFrom_subroutine_prefix M N request resume w s start T hpre] + exact withCalls_step_return M N request resume w s _ hhalt + +/-- Entry and return add two steps to the running time of a state-returning call. -/ +theorem withCalls_runFrom_call (s : σ) (start : τ) (T : ℕ) + (hs : M.halted s = false) (hrequest : request s = some start) + (hpre : ∀ i < T, N.halted (N.runFrom w start i) = false) + (hhalt : N.halted (N.runFrom w start T) = true) : + (M.withCalls N request resume).runFrom w (.inl s) (T + 2) = + .inl (resume s (N.runFrom w start T)) := by + rw [show T + 2 = 1 + (T + 1) by omega, runFrom_add] + rw [show (M.withCalls N request resume).runFrom w (.inl s) 1 = + .inr (s, start) from withCalls_step_call M N request resume w s start hs hrequest] + exact withCalls_runFrom_return M N request resume w s start T hpre hhalt + +end Turing.QueryMachine diff --git a/Complexity/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine/CallSpace.lean b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine/CallSpace.lean new file mode 100644 index 0000000000..2e4eb643a2 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine/CallSpace.lean @@ -0,0 +1,148 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.Call +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases + +/-! +# Native space certificates for same-input calls + +The composite stores a caller state or a suspended caller paired with a callee state. +These component certificates charge that compact encoded state, independently of the original +read-only input. Calls return the callee state; they do not accumulate its output stream. +-/ + +public section + +namespace Turing.QueryMachine + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +variable {σ τ : Type} (M : QueryMachine σ) (N : QueryMachine τ) + (request : σ → Option τ) (resume : σ → τ → σ) + (encS : σ ↪ List Bool) (encT : τ ↪ List Bool) + +/-- A caller state pays only its payload and the sum tag. -/ +theorem withCalls_encoding_inl_length (s : σ) : + (encSum encS (encPair encS encT) (.inl s)).length = (encS s).length + 1 := rfl + +/-- A suspended call stores one caller and one callee, with explicit framing overhead. -/ +theorem withCalls_encoding_inr_length (s : σ) (t : τ) : + (encSum encS (encPair encS encT) (.inr (s, t))).length = + 2 * (encS s).length + (encT t).length + 2 := by + simp only [encSum_inr, List.length_cons, length_encPair] + omega + +/-- The composite halt test uses only the caller's halt test. -/ +theorem withCalls_halted_inSpace + (hhalted : ComputableInSpace M.halted encS encBool fun s => (encS s).length) : + ComputableInSpace (M.withCalls N request resume).halted + (encSum encS (encPair encS encT)) encBool + (fun s => (encSum encS (encPair encS encT) s).length) := by + exact (sumElim hhalted + (budget_const (encIn := encPair encS encT) false (.refl _))).of_enc_eq + fun s => by cases s <;> rfl + +/-- Only the caller contributes the final output word. -/ +theorem withCalls_finish_inSpace + (hfinish : ComputableInSpace M.finish encS encBits fun s => (encS s).length) : + ComputableInSpace (M.withCalls N request resume).finish + (encSum encS (encPair encS encT)) encBits + (fun s => (encSum encS (encPair encS encT) s).length) := by + exact (sumElim hfinish + (budget_const (encIn := encPair encS encT) [] (.refl _))).of_enc_eq + fun s => by cases s <;> rfl + +/-- A call queries the original input through its active machine, without copying that input. -/ +theorem withCalls_query_inSpace + (hqueryM : ComputableInSpace M.query encS encNat fun s => (encS s).length) + (hqueryN : ComputableInSpace N.query encT encNat fun t => (encT t).length) + (hrequest : ComputableInSpace request encS (encOption encT) + fun s => (encS s).length) : + ComputableInSpace (M.withCalls N request resume).query + (encSum encS (encPair encS encT)) encNat + (fun s => (encSum encS (encPair encS encT) s).length) := by + have hid : ComputableInSpace (fun s : σ => s) encS encS + (fun s => (encS s).length) := of_inO ComputableInDegree.id'.inO_one + have hleft := budget_comp_linear + (optionElimSnd hqueryM + (budget_const (encIn := encPair encS encT) (encB := encNat) 0 (.refl _))) + (budget_prod hid hrequest) + have hright := budget_comp_linear hqueryN (snd encS encT) + refine (sumElim hleft hright).of_enc_eq ?_ + rintro (s | ⟨s, t⟩) + · cases h : request s <;> simp [withCalls, h] + · rfl + +/-- The callee output stream is discarded, so only ordinary caller steps emit. -/ +theorem withCalls_emit_inSpace + (hemit : ComputableInSpace M.emit (encPair encS (encOption encBool)) encBits + fun p => (encPair encS (encOption encBool) p).length) + (hrequest : ComputableInSpace request encS (encOption encT) + fun s => (encS s).length) : + ComputableInSpace (M.withCalls N request resume).emit + (encPair (encSum encS (encPair encS encT)) (encOption encBool)) encBits + (fun p => (encPair (encSum encS (encPair encS encT)) (encOption encBool) p).length) := by + let encSB := encPair encS (encOption encBool) + have hid : ComputableInSpace (fun p : σ × Option Bool => p) encSB encSB + (fun p => (encSB p).length) := of_inO ComputableInDegree.id'.inO_one + have hreq := budget_comp_linear hrequest (fst encS (encOption encBool)) + have hleft := budget_comp_linear + (optionElimSnd hemit + (budget_const (encIn := encPair encSB encT) (encB := encBits) [] (.refl _))) + (budget_prod hid hreq) + refine (sumElimFst hleft + (budget_const (encIn := encPair (encPair encS encT) (encOption encBool)) + (encB := encBits) [] (.refl _))).of_enc_eq ?_ + rintro ⟨s | ⟨s, t⟩, b⟩ + · cases h : request s <;> simp [withCalls, h] + · rfl + +/-- Entering, running and returning from a call preserve linear compact-state space. -/ +theorem withCalls_next_inSpace + (hnextM : ComputableInSpace M.next (encPair encS (encOption encBool)) encS + fun p => (encPair encS (encOption encBool) p).length) + (hnextN : ComputableInSpace N.next (encPair encT (encOption encBool)) encT + fun p => (encPair encT (encOption encBool) p).length) + (hhaltedN : ComputableInSpace N.halted encT encBool fun t => (encT t).length) + (hrequest : ComputableInSpace request encS (encOption encT) + fun s => (encS s).length) + (hresume : ComputableInSpace (fun p : σ × τ => resume p.1 p.2) + (encPair encS encT) encS fun p => (encPair encS encT p).length) : + ComputableInSpace (M.withCalls N request resume).next + (encPair (encSum encS (encPair encS encT)) (encOption encBool)) + (encSum encS (encPair encS encT)) + (fun p => (encPair (encSum encS (encPair encS encT)) (encOption encBool) p).length) := by + let encSB := encPair encS (encOption encBool) + let encST := encPair encS encT + have hinl := of_inO (ComputableInO.inl encS encST) + have hinr := of_inO (ComputableInO.inr encS encST) + have hid : ComputableInSpace (fun p : σ × Option Bool => p) encSB encSB + (fun p => (encSB p).length) := of_inO ComputableInDegree.id'.inO_one + have hnormal := budget_comp_linear hinl hnextM + have henter := budget_comp_linear hinr + (budget_prod (fst' (fst encSB encT)) (snd encSB encT)) + have hreq := budget_comp_linear hrequest (fst encS (encOption encBool)) + have hleft := budget_comp_linear (optionElimSnd hnormal henter) + (budget_prod hid hreq) + have hs := fst' (fst encST (encOption encBool)) + have ht := snd' (fst encST (encOption encBool)) + have hb := snd encST (encOption encBool) + have hcallee := budget_comp_linear hnextN (budget_prod ht hb) + have hrun := budget_comp_linear hinr (budget_prod hs hcallee) + have hreturn := budget_comp_linear hinl + (budget_comp_linear hresume (fst encST (encOption encBool))) + have hhalt := budget_comp_linear hhaltedN ht + have hright := budget_ite hhalt hreturn hrun + refine (sumElimFst hleft hright).of_enc_eq ?_ + rintro ⟨s | ⟨s, t⟩, b⟩ + · cases h : request s <;> simp [withCalls, h, encST] + · rfl + +end Turing.QueryMachine diff --git a/ComplexityTests/QueryMachineCall.lean b/ComplexityTests/QueryMachineCall.lean new file mode 100644 index 0000000000..9e986c4103 --- /dev/null +++ b/ComplexityTests/QueryMachineCall.lean @@ -0,0 +1,147 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of query-machine calls) + +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.CallSpace +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Finite + +/-! +# Same-input query calls + +A caller requests two one-bit subroutines, at positions zero and one, and returns their XOR. +The callee deliberately emits junk: state-returning calls must discard it. Finite component +certificates instantiate the native composition rules without assuming an input oracle. +-/ + +@[expose] public section + +namespace ComplexityTests.QueryMachineCall + +open Turing MultiTapeTM + +abbrev Caller := Fin 3 × Bool +abbrev Callee := Bool × Bool × Bool + +/-- Read one selected bit and return it in the final state. -/ +def reader : QueryMachine Callee where + init := (false, false, false) + query s := if s.1 then 1 else 0 + next p := (p.1.1, true, p.2.getD false) + emit _ := [true, true] + halted s := s.2.1 + finish _ := [true, true] + +/-- The controller advances only when its subroutine returns. -/ +def caller : QueryMachine Caller where + init := (0, false) + query _ := 0 + next := Prod.fst + emit _ := [] + halted s := decide (s.1 = 2) + finish s := [s.2] + +def request (s : Caller) : Option Callee := + if s.1 = 2 then none else some (decide (s.1 = 1), false, false) + +def resume (s : Caller) (t : Callee) : Caller := + (if s.1 = 0 then 1 else 2, s.2 ^^ t.2.2) + +def machine := caller.withCalls reader request resume + +example : machine.output [true, false] 6 = [true] := by decide +example : machine.output [true, true] 6 = [false] := by decide +example : machine.output [] 6 = [false] := by decide +example : machine.output [true, false] 10 = [true] := by decide +example : machine.query (machine.run [true, false] 1) = 0 := by decide +example : machine.query (machine.run [true, false] 4) = 1 := by decide +example : machine.run [true, false] 6 = .inl (2, true) := by decide + +/-- An already halted callee still needs one entry and one return step. -/ +example (w : List Bool) : + (caller.withCalls reader (fun _ => some (false, true, true)) resume).runFrom w + (.inl (0, false)) 2 = .inl (1, true) := by + change _ = (Sum.inl (resume (0, false) (reader.runFrom w (false, true, true) 0)) : + Caller ⊕ (Caller × Callee)) + apply QueryMachine.withCalls_runFrom_call caller reader _ resume w (0, false) + (false, true, true) 0 + · rfl + · rfl + · intro i hi + omega + · rfl + +/-- With no requests, ordinary caller emissions and the final word are preserved. -/ +def echo : QueryMachine Bool where + init := false + query _ := 0 + next _ := true + emit p := [p.2.getD false] + halted := id + finish _ := [false] + +example : (echo.withCalls reader (fun _ => none) (fun s _ => s)).output [true] 3 = + [true, false] := by decide + +noncomputable def encCaller := encPair (encFinite (Fin 3)) encBool +noncomputable def encCallee := encPair encBool (encPair encBool encBool) +noncomputable def encState := encSum encCaller (encPair encCaller encCallee) + +example : ComputableInSpace machine.query encState encNat + (fun s => (encState s).length) := + QueryMachine.withCalls_query_inSpace caller reader request resume encCaller encCallee + (ComputableInSpace.of_finite _) (ComputableInSpace.of_finite _) + (ComputableInSpace.of_finite _) + +example : ComputableInSpace machine.halted encState encBool + (fun s => (encState s).length) := + QueryMachine.withCalls_halted_inSpace caller reader request resume encCaller encCallee + (ComputableInSpace.of_finite _) + +example : ComputableInSpace machine.finish encState encBits + (fun s => (encState s).length) := + QueryMachine.withCalls_finish_inSpace caller reader request resume encCaller encCallee + (ComputableInSpace.of_finite _) + +example : ComputableInSpace machine.next (encPair encState (encOption encBool)) encState + (fun p => (encPair encState (encOption encBool) p).length) := + QueryMachine.withCalls_next_inSpace caller reader request resume encCaller encCallee + (ComputableInSpace.of_finite _) (ComputableInSpace.of_finite _) + (ComputableInSpace.of_finite _) (ComputableInSpace.of_finite _) + (ComputableInSpace.of_finite _) + +example : ComputableInSpace machine.emit (encPair encState (encOption encBool)) encBits + (fun p => (encPair encState (encOption encBool) p).length) := + QueryMachine.withCalls_emit_inSpace caller reader request resume encCaller encCallee + (ComputableInSpace.of_finite _) (ComputableInSpace.of_finite _) + +/-- The five composed certificates feed the existing compiler unchanged. -/ +example {α β : Type} (enc : α ↪ List Bool) (encOut : β ↪ List Bool) + (f : α → β) (time S : α → ℕ) + (hhalt : ∀ a, machine.halted (machine.run (enc a) (time a)) = true) + (hpre : ∀ a, ∀ t < time a, machine.halted (machine.run (enc a) t) = false) + (hf : ∀ a, encOut (f a) = machine.output (enc a) (time a)) + (hlog : ∀ a, Nat.log2 (enc a).length + 1 ≤ S a) + (hsize : ∃ c, ∀ a, ∀ t ≤ time a, + (encState (machine.run (enc a) t)).length ≤ c * S a) : + ∃ t c, ComputableInTimeAndSpace f enc encOut t (fun a => c * S a) := by + apply machine.computableInSpace (encS := encState) + (QueryMachine.withCalls_query_inSpace caller reader request resume encCaller encCallee + (ComputableInSpace.of_finite _) (ComputableInSpace.of_finite _) + (ComputableInSpace.of_finite _)) + (QueryMachine.withCalls_next_inSpace caller reader request resume encCaller encCallee + (ComputableInSpace.of_finite _) (ComputableInSpace.of_finite _) + (ComputableInSpace.of_finite _) (ComputableInSpace.of_finite _) + (ComputableInSpace.of_finite _)) + (QueryMachine.withCalls_emit_inSpace caller reader request resume encCaller encCallee + (ComputableInSpace.of_finite _) (ComputableInSpace.of_finite _)) + (QueryMachine.withCalls_halted_inSpace caller reader request resume encCaller encCallee + (ComputableInSpace.of_finite _)) + (QueryMachine.withCalls_finish_inSpace caller reader request resume encCaller encCallee + (ComputableInSpace.of_finite _)) time hhalt hpre hf S hlog hsize + +end ComplexityTests.QueryMachineCall From 2dd0676c47780b3466363b5c2c58073271b8668e Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sat, 31 Oct 2026 23:03:46 +0000 Subject: [PATCH 07/36] feat(treeeval): run packed interpolation through same-input calls --- Complexity.lean | 5 + .../MultiTape/Complexity/InSpace/NatPow.lean | 80 +++++ .../TreeEval/Ary/IterativeEncoding.lean | 297 ++++++++++++++++++ .../TreeEval/Ary/Packed/QueryGate.lean | 214 +++++++++++++ .../TreeEval/Ary/Packed/QueryGateInit.lean | 74 +++++ .../TreeEval/Ary/Packed/QueryGateSpace.lean | 272 ++++++++++++++++ .../TreeEvalIterativeEncoding.lean | 65 ++++ ComplexityTests/TreeEvalQueryGate.lean | 152 +++++++++ 8 files changed, 1159 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/NatPow.lean create mode 100644 Complexity/Computability/TreeEval/Ary/IterativeEncoding.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/QueryGate.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/QueryGateInit.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/QueryGateSpace.lean create mode 100644 ComplexityTests/TreeEvalIterativeEncoding.lean create mode 100644 ComplexityTests/TreeEvalQueryGate.lean diff --git a/Complexity.lean b/Complexity.lean index 2da8ca95c9..9866cdb1d0 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1732,6 +1732,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Fold public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Bit public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatPow public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Plain public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpaceLib public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LinearSpaceArith @@ -2308,6 +2309,7 @@ public import Complexity.Computability.TreeEval.Ary.CookMertz public import Complexity.Computability.TreeEval.Ary.Iterative public import Complexity.Computability.TreeEval.Ary.IterativeBounds public import Complexity.Computability.TreeEval.Ary.IterativeCorrect +public import Complexity.Computability.TreeEval.Ary.IterativeEncoding public import Complexity.Computability.TreeEval.Ary.IterativePacked public import Complexity.Computability.TreeEval.Ary.Packed public import Complexity.Computability.TreeEval.Ary.Packed.Executable @@ -2315,6 +2317,9 @@ public import Complexity.Computability.TreeEval.Ary.Packed.GateInterpolation public import Complexity.Computability.TreeEval.Ary.Packed.GateInterpolationSpace public import Complexity.Computability.TreeEval.Ary.Packed.Interpolation public import Complexity.Computability.TreeEval.Ary.Packed.InterpolationSpace +public import Complexity.Computability.TreeEval.Ary.Packed.QueryGate +public import Complexity.Computability.TreeEval.Ary.Packed.QueryGateInit +public import Complexity.Computability.TreeEval.Ary.Packed.QueryGateSpace public import Complexity.Computability.TreeEval.Ary.PackingParameters public import Complexity.Computability.TreeEval.Basic public import Complexity.Computability.TreeEval.CookMertz diff --git a/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/NatPow.lean b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/NatPow.lean new file mode 100644 index 0000000000..8be0672517 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/NatPow.lean @@ -0,0 +1,80 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpaceLib +public import Complexity.Foundations.Data.Nat.BinarySize + +/-! +# Binary natural powers with a runtime exponent + +The base and exponent are binary naturals. Repeated multiplication keeps the immutable base and +one accumulator; the iteration counter is binary. The space bound charges the possible output +length, proportional to the exponent times the binary length of the base, and the binary length +of the exponent. No unary charge for the base is introduced. + +## Main results + +* `Turing.MultiTapeTM.ComputableInSpace.natPow`: power with both arguments supplied at runtime. +* `Turing.MultiTapeTM.ComputableInSpace.budget_natPow_variable`: compute a power within a + caller's budget that covers this output-length bound. +-/ + +public section + +namespace Turing.MultiTapeTM.ComputableInSpace + +private def powStep (q : ℕ × ℕ) : ℕ × ℕ := (q.1, q.2 * q.1) + +private theorem iterate_powStep (a n : ℕ) : powStep^[n] (a, 1) = (a, a ^ n) := by + induction n with + | zero => rfl + | succ n ih => rw [Function.iterate_succ_apply', ih]; rfl + +private theorem powStep_inSpace : + ComputableInSpace powStep (encPair encNat encNat) (encPair encNat encNat) + fun q => (encPair encNat encNat q).length := by + have hbase := fst encNat encNat + have hacc := snd encNat encNat + exact (budget_prod hbase (budget_natMul hacc hbase)).of_enc_eq fun _ => rfl + +/-- A runtime exponent is evaluated by repeated multiplication within the possible output +length plus the binary counter length. The bound is valid also at base or exponent zero. -/ +theorem natPow : + ComputableInSpace (fun q : ℕ × ℕ => q.1 ^ q.2) (encPair encNat encNat) encNat + fun q => (q.2 + 1) * (encNat q.1).length + (encNat q.2).length + 1 := by + let B (q : ℕ × ℕ) := (q.2 + 1) * (encNat q.1).length + (encNat q.2).length + 1 + have hin : (fun q : ℕ × ℕ => (encPair encNat encNat q).length) ≼ B := + .of_le_linear 2 0 fun q => by + dsimp only [B] + rw [length_encPair] + nlinarith + have hbase := (fst encNat encNat).weaken hin + have hn := (snd encNat encNat).weaken hin + have hinit := budget_prod hbase (budget_const (encB := encNat) 1 hin) + have hstates (q : ℕ × ℕ) (j : ℕ) (hj : j ≤ q.2) : + (encPair encNat encNat (powStep^[j] (q.1, 1))).length ≤ 2 * B q := by + rw [iterate_powStep, length_encPair] + have hp := Nat.size_pow_le q.1 j + have hmul := Nat.mul_le_mul_right q.1.size hj + simp only [length_encNat_eq_size, B] at * + nlinarith + have hrun := budget_iterate hn hinit powStep_inSpace hstates + (.of_le_linear 2 0 fun _ => le_rfl) + exact (snd' hrun).of_enc_eq fun q => by rw [iterate_powStep] + +/-- A power with a computed exponent stays within a budget covering both arguments and the +possible output length. Unlike `budget_natPow`, the exponent here is an input-dependent value. -/ +theorem budget_natPow_variable {α : Type} {encIn : α ↪ List Bool} {Bs : α → ℕ} + {a n : α → ℕ} (ha : ComputableInSpace a encIn encNat Bs) + (hn : ComputableInSpace n encIn encNat Bs) + (hbound : (fun x => (n x + 1) * (encNat (a x)).length + (encNat (n x)).length + 1) ≼ Bs) : + ComputableInSpace (fun x => a x ^ n x) encIn encNat Bs := + budget_comp natPow (budget_prod ha hn) hbound + +end Turing.MultiTapeTM.ComputableInSpace diff --git a/Complexity/Computability/TreeEval/Ary/IterativeEncoding.lean b/Complexity/Computability/TreeEval/Ary/IterativeEncoding.lean new file mode 100644 index 0000000000..a410672568 --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/IterativeEncoding.lean @@ -0,0 +1,297 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.IterativeBounds +public import Complexity.Computability.TreeEval.Ary.PackingParameters +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Pi +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Option + +/-! +# Binary encodings for iterative Cook–Mertz + +The register bank is stored once as a flat finite table. A continuation frame contains binary +round and register indices, a phase bit and one coefficient. No register bank, tree or path is +stored in a frame. The final bound charges list and pair framing, including the current mode. + +These are bounds on an actual injective representation. Native computation of the controller's +components and implicit node queries remain separate obligations. + +## Main definitions + +* `Cslib.TreeEval.Ary.Iter.encCfg`: compact state encoding, parameterized by a scalar encoding. +* `Cslib.TreeEval.Ary.Iter.encField`: binary residues in a nonzero modular field. + +## Main results + +* `Cslib.TreeEval.Ary.Iter.length_encCfg_le`: one bank plus a linear number of small frames. +* `Cslib.TreeEval.Ary.Iter.length_encCfg_runFrom_le`: every clocked prefix has that bound. +* `Cslib.TreeEval.Ary.Iter.length_encCfg_packed_runFrom_le_of_rounds_le`: the selected packed + parameters give linear bank size plus logarithmic control per level at every clock. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Iter + +open Turing.MultiTapeTM + +variable {K : Type} {d r b H : ℕ} + +/-- Finite indices use binary values, rather than unary enumeration ranks. -/ +@[expose] def encIndex (n : ℕ) : Fin n ↪ List Bool where + toFun i := encNat i.val + inj' := by + intro i j h + exact Fin.val_injective (encNat.injective h) + +@[simp] theorem length_encIndex (n : ℕ) (i : Fin n) : + (encIndex n i).length = i.val.size := by + change i.val.bits.length = i.val.size + exact Nat.size_eq_bits_len _ + +/-- Flatten the two register indices before storing the bank, avoiding nested list framing. -/ +@[expose] noncomputable def encRegs (encK : K ↪ List Bool) : Ary.State K d r ↪ List Bool where + toFun S := encPi (Index := Fin (d + 1) × Fin r) encK (fun i => S i.1 i.2) + inj' := by + intro S S' h + have heq := (encPi encK).injective h + funext i j + exact congrFun heq (i, j) + +/-- Frames contain only their five control fields, with binary indices. -/ +@[expose] def encFrame (encK : K ↪ List Bool) : Frame K d ↪ List Bool where + toFun fr := encPair encNat (encPair (encIndex d) + (encPair encBool (encPair encK (encIndex (d + 1))))) + (fr.round, fr.child, fr.undo, fr.coeff, fr.target) + inj' := by + intro fr fr' h + have heq := (encPair encNat (encPair (encIndex d) + (encPair encBool (encPair encK (encIndex (d + 1)))))).injective h + cases fr + cases fr' + simpa only [Prod.mk.injEq, Frame.mk.injEq] using heq + +/-- Return is empty; call stores its coefficient and binary target after a tag. -/ +@[expose] def encMode (encK : K ↪ List Bool) : Mode K d ↪ List Bool where + toFun mode := encOption (encPair encK (encIndex (d + 1))) (match mode with + | .call c t => some (c, t) + | .ret => none) + inj' := by + intro a b h + have heq := (encOption (encPair encK (encIndex (d + 1)))).injective h + cases a <;> cases b <;> simp_all + +/-- The bank is global; the stack and current mode are stored beside it. -/ +@[expose] noncomputable def encCfg (encK : K ↪ List Bool) : Cfg K d r ↪ List Bool where + toFun q := encPair (encRegs encK) (encPair (encList (encFrame encK)) (encMode encK)) + (q.regs, q.stack, q.mode) + inj' := by + intro q q' h + have heq := (encPair (encRegs encK) + (encPair (encList (encFrame encK)) (encMode encK))).injective h + cases q + cases q' + simpa only [Prod.mk.injEq, Cfg.mk.injEq] using heq + +/-- Binary residues give logarithmic scalar storage, including the zero residue. -/ +@[expose] def encField (p : ℕ) [NeZero p] : ZMod p ↪ List Bool where + toFun x := encNat x.val + inj' := by + intro x y h + exact ZMod.val_injective p (encNat.injective h) + +theorem length_encField_le (p : ℕ) [NeZero p] (x : ZMod p) : + (encField p x).length ≤ p.size := by + change (encNat x.val).length ≤ p.size + rw [encNat_apply, Nat.size_eq_bits_len] + exact Nat.size_le_size (ZMod.val_lt x).le + +/-- The flattened bank charges each scalar and its list framing once. -/ +theorem length_encRegs_le (encK : K ↪ List Bool) (S : Ary.State K d r) {L : ℕ} + (hK : ∀ x, (encK x).length ≤ L) : + (encRegs encK S).length ≤ (d + 1) * r * (2 * L + 1) := by + change (encPi encK (fun i : Fin (d + 1) × Fin r => S i.1 i.2)).length ≤ _ + simpa only [Fintype.card_prod, Fintype.card_fin] using + length_encPi_le encK (fun i : Fin (d + 1) × Fin r => S i.1 i.2) (fun _ => hK _) + +/-- The exact linear allowance for one bounded continuation frame. -/ +@[expose] def frameBits (d rounds L : ℕ) : ℕ := + 2 * rounds.size + 2 * d.size + 2 * L + (d + 1).size + 6 + +theorem length_encFrame_le (encK : K ↪ List Bool) (fr : Frame K d) {rounds L : ℕ} + (hK : ∀ x, (encK x).length ≤ L) (hround : fr.round < rounds) : + (encFrame encK fr).length ≤ frameBits d rounds L := by + have hr := Nat.size_le_size hround.le + have hc := Nat.size_le_size fr.child.isLt.le + have ht := Nat.size_le_size fr.target.isLt.le + have hk := hK fr.coeff + change (encPair encNat (encPair (encIndex d) + (encPair encBool (encPair encK (encIndex (d + 1))))) + (fr.round, fr.child, fr.undo, fr.coeff, fr.target)).length ≤ _ + simp only [length_encPair, length_encIndex, + encBool_apply, List.length_singleton, encNat_apply, Nat.size_eq_bits_len] + unfold frameBits + omega + +theorem length_encMode_le (encK : K ↪ List Bool) (mode : Mode K d) {L : ℕ} + (hK : ∀ x, (encK x).length ≤ L) : + (encMode encK mode).length ≤ 2 * L + (d + 1).size + 2 := by + cases mode with + | ret => simp [encMode] + | call c t => + have hk := hK c + have ht := Nat.size_le_size t.isLt.le + change (encOption (encPair encK (encIndex (d + 1))) (some (c, t))).length ≤ _ + simp only [encOption_some, List.length_cons, length_encPair, length_encIndex] + omega + +/-- Every frame and every separator is charged; the register bank is charged only once. -/ +theorem length_encCfg_le (encK : K ↪ List Bool) (q : Cfg K d r) {rounds L : ℕ} + (hK : ∀ x, (encK x).length ≤ L) (hq : ControlBounds H rounds q) : + (encCfg encK q).length ≤ + 2 * ((d + 1) * r * (2 * L + 1)) + + 2 * (H * (2 * frameBits d rounds L + 1)) + 2 * L + (d + 1).size + 4 := by + have hregs := length_encRegs_le encK q.regs hK + have hmode := length_encMode_le encK q.mode hK + have hstack := length_encList_le (encFrame encK) q.stack + (fun fr hfr => length_encFrame_le encK fr hK (hq.2 fr hfr)) + have hmul := Nat.mul_le_mul_right (2 * frameBits d rounds L + 1) hq.1 + change (encPair (encRegs encK) + (encPair (encList (encFrame encK)) (encMode encK)) (q.regs, q.stack, q.mode)).length ≤ _ + simp only [length_encPair] + omega + +/-- The compact representation bound holds at every clock of a root call. -/ +theorem length_encCfg_runFrom_le [Field K] (encK : K ↪ List Bool) {L : ℕ} + (hK : ∀ x, (encK x).length ≤ L) (T : Tree d b H) (hd : 0 < d) + (e : (Fin b → Bool) → Fin r → K) + (E : Gate d b → (Fin d → Fin r → K) → Fin r → K) (us : List Kˣ) + (S : Ary.State K d r) (c : K) (t : Fin (d + 1)) (n : ℕ) : + (encCfg encK (runFrom hd e E us T.nodeAt (initialCfg S c t) n)).length ≤ + 2 * ((d + 1) * r * (2 * L + 1)) + + 2 * (H * (2 * frameBits d us.length L + 1)) + 2 * L + (d + 1).size + 4 := + length_encCfg_le encK _ hK (runFrom_initial_bounds T hd e E us S c t n) + +/-- Binary encoding in the modulus selected by the packed parameter construction. -/ +@[expose] def encPackedField (d b : ℕ) : ZMod (Packed.fieldModulus d b) ↪ List Bool := + letI : NeZero (Packed.fieldModulus d b) := ⟨(Packed.fieldModulus_prime d b).ne_zero⟩ + encField (Packed.fieldModulus d b) + +theorem length_encPackedField_le (d b : ℕ) (x : ZMod (Packed.fieldModulus d b)) : + (encPackedField d b x).length ≤ (Packed.fieldModulus d b).size := by + let : NeZero (Packed.fieldModulus d b) := ⟨(Packed.fieldModulus_prime d b).ne_zero⟩ + exact length_encField_le _ x + +/-- The actual flat bank encoding has linear payload size for the selected parameters. -/ +theorem length_encRegs_packed_le (hd : 0 < d) (hb : 0 < b) + (S : Ary.State (ZMod (Packed.fieldModulus d b)) d (Packed.chunkCount d b)) : + (encRegs (encPackedField d b) S).length ≤ + 9 * (d + 1) * (b + Packed.chunkWidth d b) := by + have hbank := length_encRegs_le (encPackedField d b) S (length_encPackedField_le d b) + have hmul := Packed.chunkCount_mul_fieldModulus_size_le hd hb + have hr := Packed.chunkCount_le hd hb + have hscalar : Packed.chunkCount d b * (2 * (Packed.fieldModulus d b).size + 1) ≤ + 9 * (b + Packed.chunkWidth d b) := by nlinarith + calc + _ ≤ (d + 1) * Packed.chunkCount d b * (2 * (Packed.fieldModulus d b).size + 1) := + hbank + _ = (d + 1) * (Packed.chunkCount d b * (2 * (Packed.fieldModulus d b).size + 1)) := + Nat.mul_assoc .. + _ ≤ (d + 1) * (9 * (b + Packed.chunkWidth d b)) := Nat.mul_le_mul_left _ hscalar + _ = _ := by ring + +/-- The concrete packed representation has linear bank cost and small per-level control cost. +For fixed fan-in, `chunkWidth d b = (d*b).size`, so the first term is linear in `b` and each +frame uses logarithmically many bits in `b` and the round count. -/ +theorem length_encCfg_packed_le (hd : 0 < d) (hb : 0 < b) + (q : Cfg (ZMod (Packed.fieldModulus d b)) d (Packed.chunkCount d b)) {rounds : ℕ} + (hq : ControlBounds H rounds q) : + (encCfg (encPackedField d b) q).length ≤ + 18 * (d + 1) * (b + Packed.chunkWidth d b) + + 2 * (H * (2 * frameBits d rounds (2 * Packed.chunkWidth d b + 2) + 1)) + + 4 * Packed.chunkWidth d b + (d + 1).size + 8 := by + have hp := Packed.fieldModulus_size_le hd hb + have hK : ∀ x, (encPackedField d b x).length ≤ 2 * Packed.chunkWidth d b + 2 := + fun x => (length_encPackedField_le d b x).trans hp + have hregs := length_encRegs_packed_le hd hb q.regs + have hmode := length_encMode_le (encPackedField d b) q.mode hK + have hstack := length_encList_le (encFrame (encPackedField d b)) q.stack + (fun fr hfr => length_encFrame_le (encPackedField d b) fr hK (hq.2 fr hfr)) + have hmul := Nat.mul_le_mul_right + (2 * frameBits d rounds (2 * Packed.chunkWidth d b + 2) + 1) hq.1 + change (encPair (encRegs (encPackedField d b)) + (encPair (encList (encFrame (encPackedField d b))) (encMode (encPackedField d b))) + (q.regs, q.stack, q.mode)).length ≤ _ + simp only [length_encPair] + calc + _ ≤ 2 * (9 * (d + 1) * (b + Packed.chunkWidth d b)) + + 2 * (H * (2 * frameBits d rounds (2 * Packed.chunkWidth d b + 2) + 1)) + + (2 * (2 * Packed.chunkWidth d b + 2) + (d + 1).size + 2) + 2 := by omega + _ = _ := by ring + +/-- The selected packed representation satisfies the bound at every root-call clock. -/ +theorem length_encCfg_packed_runFrom_le [Fact (Packed.fieldModulus d b).Prime] + (hd : 0 < d) (hb : 0 < b) (T : Tree d b H) + (e : (Fin b → Bool) → Fin (Packed.chunkCount d b) → ZMod (Packed.fieldModulus d b)) + (E : Gate d b → (Fin d → Fin (Packed.chunkCount d b) → ZMod (Packed.fieldModulus d b)) → + Fin (Packed.chunkCount d b) → ZMod (Packed.fieldModulus d b)) + (us : List (ZMod (Packed.fieldModulus d b))ˣ) + (S : Ary.State (ZMod (Packed.fieldModulus d b)) d (Packed.chunkCount d b)) + (c : ZMod (Packed.fieldModulus d b)) (t : Fin (d + 1)) (n : ℕ) : + (encCfg (encPackedField d b) + (runFrom hd e E us T.nodeAt (initialCfg S c t) n)).length ≤ + 18 * (d + 1) * (b + Packed.chunkWidth d b) + + 2 * (H * (2 * frameBits d us.length (2 * Packed.chunkWidth d b + 2) + 1)) + + 4 * Packed.chunkWidth d b + (d + 1).size + 8 := + length_encCfg_packed_le hd hb _ (runFrom_initial_bounds T hd e E us S c t n) + +/-- At most one round per field element keeps each frame logarithmic in the value width. -/ +theorem frameBits_packed_le (hd : 0 < d) (hb : 0 < b) {rounds : ℕ} + (hrounds : rounds ≤ Packed.fieldModulus d b) : + frameBits d rounds (2 * Packed.chunkWidth d b + 2) ≤ + 8 * Packed.chunkWidth d b + 2 * d.size + (d + 1).size + 14 := by + have hsize := (Nat.size_le_size hrounds).trans (Packed.fieldModulus_size_le hd hb) + unfold frameBits + omega + +/-- The complete packed state has linear bank size plus logarithmic control per tree level. +The explicit round-count premise holds for an enumeration without repeated field units. -/ +theorem length_encCfg_packed_le_of_rounds_le (hd : 0 < d) (hb : 0 < b) + (q : Cfg (ZMod (Packed.fieldModulus d b)) d (Packed.chunkCount d b)) {rounds : ℕ} + (hq : ControlBounds H rounds q) (hrounds : rounds ≤ Packed.fieldModulus d b) : + (encCfg (encPackedField d b) q).length ≤ + 18 * (d + 1) * (b + Packed.chunkWidth d b) + + 2 * (H * (16 * Packed.chunkWidth d b + 4 * d.size + 2 * (d + 1).size + 29)) + + 4 * Packed.chunkWidth d b + (d + 1).size + 8 := by + have hqsize := length_encCfg_packed_le hd hb q hq + have hframe := frameBits_packed_le hd hb hrounds + have hfactor : 2 * frameBits d rounds (2 * Packed.chunkWidth d b + 2) + 1 ≤ + 16 * Packed.chunkWidth d b + 4 * d.size + 2 * (d + 1).size + 29 := by omega + have hmul := Nat.mul_le_mul_left H hfactor + omega + +/-- The sharp representation bound holds at every clock when the rounds fit the field. -/ +theorem length_encCfg_packed_runFrom_le_of_rounds_le [Fact (Packed.fieldModulus d b).Prime] + (hd : 0 < d) (hb : 0 < b) (T : Tree d b H) + (e : (Fin b → Bool) → Fin (Packed.chunkCount d b) → ZMod (Packed.fieldModulus d b)) + (E : Gate d b → (Fin d → Fin (Packed.chunkCount d b) → ZMod (Packed.fieldModulus d b)) → + Fin (Packed.chunkCount d b) → ZMod (Packed.fieldModulus d b)) + (us : List (ZMod (Packed.fieldModulus d b))ˣ) + (hus : us.length ≤ Packed.fieldModulus d b) + (S : Ary.State (ZMod (Packed.fieldModulus d b)) d (Packed.chunkCount d b)) + (c : ZMod (Packed.fieldModulus d b)) (t : Fin (d + 1)) (n : ℕ) : + (encCfg (encPackedField d b) + (runFrom hd e E us T.nodeAt (initialCfg S c t) n)).length ≤ + 18 * (d + 1) * (b + Packed.chunkWidth d b) + + 2 * (H * (16 * Packed.chunkWidth d b + 4 * d.size + 2 * (d + 1).size + 29)) + + 4 * Packed.chunkWidth d b + (d + 1).size + 8 := + length_encCfg_packed_le_of_rounds_le hd hb _ + (runFrom_initial_bounds T hd e E us S c t n) hus + +end Cslib.TreeEval.Ary.Iter diff --git a/Complexity/Computability/TreeEval/Ary/Packed/QueryGate.lean b/Complexity/Computability/TreeEval/Ary/Packed/QueryGate.lean new file mode 100644 index 0000000000..7edc06be77 --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/QueryGate.lean @@ -0,0 +1,214 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed.Executable +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.Call + +/-! +# Packed interpolation using same-input table calls + +The assignment loop of `interpolateGrid` is a query-machine caller. Its state contains the +parameters, one assignment index and one modular accumulator. Each iteration requests a table +entry from a subroutine on the same original input, then adds its weighted contribution. +The table is never materialized. An explicit context carries the address of the gate or output +chunk needed by the supplier; no tree or truth table is built into this construction. + +The supplied loop limit must equal `s^n` for the interpolation theorem. Computing that limit and +the native costs of supplier initialization, its computation and extracting its answer are +separate charged operations. The callee returns a state, not its output stream. +-/ + +@[expose] public section + +namespace Cslib.TreeEval.Ary.Packed.QueryGate + +open Turing Cslib.BitString + +/-- Supplier context, modulus, alphabet size, coordinate count, point and assignment limit. -/ +abbrev Params (γ : Type) := γ × (ℕ × ℕ × ℕ) × List ℕ × ℕ + +/-- Parameters, the current assignment index and the sum accumulator. -/ +abbrev State (γ : Type) := Params γ × ℕ × ℕ + +variable {γ τ : Type} + +/-- The fixed parameters with fresh assignment and sum counters. -/ +def initial (q : Params γ) : State γ := (q, 0, 0) + +/-- One local sum update after the table subroutine returns. -/ +def resume (answer : τ → ℕ) (q : State γ) (t : τ) : State γ := + (q.1, gridSumStep q.1.2.1.1 q.1.2.1.2.1 q.1.2.1.2.2 + (fun _ => answer t) q.1.2.2.1 q.2) + +/-- Request one entry whenever the assignment index is below the explicit loop limit. -/ +def request (start : State γ → τ) (q : State γ) : Option τ := + if q.2.1 < q.1.2.2.2 then some (start q) else none + +/-- The controller only advances when its table subroutine returns. -/ +def controller (q : Params γ) : QueryMachine (State γ) where + init := initial q + query _ := 0 + next := Prod.fst + emit _ := [] + halted st := decide (st.1.2.2.2 ≤ st.2.1) + finish _ := [] + +/-- The complete assignment loop and its same-original-input table subroutine. -/ +def machine (q : Params γ) (N : QueryMachine τ) (start : State γ → τ) + (answer : τ → ℕ) : QueryMachine (State γ ⊕ (State γ × τ)) := + (controller q).withCalls N (request start) (resume answer) + +/-- Pure assignment-loop states, used only to state the simulation invariant. -/ +def sumState (q : Params γ) (table : ℕ → ℕ) (j : ℕ) : State γ := + (q, (gridSumStep q.2.1.1 q.2.1.2.1 q.2.1.2.2 table q.2.2.1)^[j] (0, 0)) + +@[simp] theorem sumState_zero (q : Params γ) (table : ℕ → ℕ) : + sumState q table 0 = initial q := rfl + +/-- The assignment counter advances by one after each returned entry. -/ +theorem sumState_counter (q : Params γ) (table : ℕ → ℕ) (j : ℕ) : + (sumState q table j).2.1 = j := by + induction j with + | zero => rfl + | succ j ih => + rw [sumState, Function.iterate_succ_apply'] + change ((gridSumStep q.2.1.1 q.2.1.2.1 q.2.1.2.2 table q.2.2.1) + ((gridSumStep q.2.1.1 q.2.1.2.1 q.2.1.2.2 table q.2.2.1)^[j] (0, 0))).1 = j + 1 + change ((gridSumStep q.2.1.1 q.2.1.2.1 q.2.1.2.2 table q.2.2.1)^[j] + (0, 0)).1 = j at ih + change ((gridSumStep q.2.1.1 q.2.1.2.1 q.2.1.2.2 table q.2.2.1)^[j] + (0, 0)).1 + 1 = j + 1 + exact congrArg (· + 1) ih + +/-- Every caller-prefix accumulator is reduced; no earlier table values are retained. -/ +theorem sumState_acc_lt (q : Params γ) (table : ℕ → ℕ) (j : ℕ) + (hp : 0 < q.2.1.1) : (sumState q table j).2.2 < q.2.1.1 := by + cases j with + | zero => exact hp + | succ j => + rw [sumState, Function.iterate_succ_apply'] + simp only [gridSumStep, hp.ne', ↓reduceIte] + exact Nat.mod_lt _ hp + +/-- A correct returned table value performs precisely the next existing sum step. -/ +theorem resume_sumState (q : Params γ) (table : ℕ → ℕ) (j : ℕ) + (answer : τ → ℕ) (t : τ) (ht : answer t = table j) : + resume answer (sumState q table j) t = sumState q table (j + 1) := by + have hc := sumState_counter q table j + have hstep : gridSumStep q.2.1.1 q.2.1.2.1 q.2.1.2.2 + (fun _ => answer t) q.2.2.1 (sumState q table j).2 = + gridSumStep q.2.1.1 q.2.1.2.1 q.2.1.2.2 table q.2.2.1 + (sumState q table j).2 := by + simp only [gridSumStep, hc, ht] + change (q, gridSumStep q.2.1.1 q.2.1.2.1 q.2.1.2.2 + (fun _ => answer t) q.2.2.1 (sumState q table j).2) = _ + rw [hstep] + simp only [sumState, Function.iterate_succ_apply'] + +/-- One table call implements one sum iteration, with one entry and one return step. -/ +theorem runFrom_one (q : Params γ) (N : QueryMachine τ) (start : State γ → τ) + (answer : τ → ℕ) (w : List Bool) (table : ℕ → ℕ) (j T : ℕ) + (hj : j < q.2.2.2) + (hpre : ∀ i < T, N.halted (N.runFrom w (start (sumState q table j)) i) = false) + (hhalt : N.halted (N.runFrom w (start (sumState q table j)) T) = true) + (hanswer : answer (N.runFrom w (start (sumState q table j)) T) = table j) : + (machine q N start answer).runFrom w (.inl (sumState q table j)) (T + 2) = + .inl (sumState q table (j + 1)) := by + have hc := sumState_counter q table j + rw [← resume_sumState q table j answer _ hanswer] + apply QueryMachine.withCalls_runFrom_call + · change decide (q.2.2.2 ≤ (sumState q table j).2.1) = false + rw [hc] + exact decide_eq_false (by omega) + · change (if (sumState q table j).2.1 < q.2.2.2 then + some (start (sumState q table j)) else none) = _ + rw [hc] + simp only [hj, ↓reduceIte] + · exact hpre + · exact hhalt + +/-- Every finite prefix of the assignment loop is realized by finitely many same-input calls. +The supplier contract is required at each actual requested state, including the current sum. -/ +theorem exists_runFrom_sumState (q : Params γ) (N : QueryMachine τ) + (start : State γ → τ) (answer : τ → ℕ) (w : List Bool) (table : ℕ → ℕ) + (hsupplier : ∀ j < q.2.2.2, ∃ T, + (∀ i < T, N.halted (N.runFrom w (start (sumState q table j)) i) = false) ∧ + N.halted (N.runFrom w (start (sumState q table j)) T) = true ∧ + answer (N.runFrom w (start (sumState q table j)) T) = table j) + (j : ℕ) (hj : j ≤ q.2.2.2) : + ∃ T, (machine q N start answer).runFrom w (.inl (initial q)) T = + .inl (sumState q table j) := by + induction j with + | zero => exact ⟨0, rfl⟩ + | succ j ih => + obtain ⟨T, hT⟩ := ih (by omega) + obtain ⟨U, hpre, hhalt, hans⟩ := hsupplier j (by omega) + refine ⟨T + (U + 2), ?_⟩ + rw [QueryMachine.runFrom_add, hT] + exact runFrom_one q N start answer w table j U (by omega) hpre hhalt hans + +/-- With the full grid limit, the same-input caller returns the existing streamed interpolation. +Its final accumulator, rather than the deliberately empty output stream, is the result. -/ +theorem exists_runFrom_interpolateGrid (q : Params γ) (N : QueryMachine τ) + (start : State γ → τ) (answer : τ → ℕ) (w : List Bool) (table : ℕ → ℕ) + (hlimit : q.2.2.2 = q.2.1.2.1 ^ q.2.1.2.2) + (hsupplier : ∀ j < q.2.2.2, ∃ T, + (∀ i < T, N.halted (N.runFrom w (start (sumState q table j)) i) = false) ∧ + N.halted (N.runFrom w (start (sumState q table j)) T) = true ∧ + answer (N.runFrom w (start (sumState q table j)) T) = table j) : + ∃ T, (machine q N start answer).runFrom w (.inl (initial q)) T = + .inl (q, q.2.2.2, + interpolateGrid q.2.1.1 q.2.1.2.1 q.2.1.2.2 table q.2.2.1) := by + obtain ⟨T, hT⟩ := exists_runFrom_sumState q N start answer w table hsupplier q.2.2.2 le_rfl + refine ⟨T, hT.trans ?_⟩ + congr 1 + apply Prod.ext + · rfl + · apply Prod.ext + · exact sumState_counter q table q.2.2.2 + · simp only [sumState, interpolateGrid, hlimit] + +/-- The one-entry table consumed by the existing counter gate extension. -/ +def gateTable {s b d r : ℕ} (hs : 0 < s) + (encode : (Fin b → Bool) → Fin r → Fin s) + (decode : (Fin r → Fin s) → Fin b → Bool) (f : Gate d b) (i : Fin r) : ℕ → ℕ := + fun k => (encode (f fun j => decode (blocksEquiv (fun a : Fin (d * r) => + (⟨gridDigit s k a, Nat.mod_lt _ hs⟩ : Fin s)) j)) i).val + +/-- Parameters for one output coordinate of the existing packed extension. -/ +def gateParams {p s d r : ℕ} (context : γ) (x : Fin d → Fin r → ZMod p) : Params γ := + (context, (p, s, d * r), + List.ofFn (fun a : Fin (d * r) => (blocksEquiv.symm x a).val), s ^ (d * r)) + +/-- A table supplier with the explicit same-input contract realizes the actual packed gate +extension used by the iterative evaluator, rather than a separate interpolation specification. -/ +theorem exists_runFrom_counterGateExtension {p s b d r : ℕ} (hs : 0 < s) + (context : γ) (encode : (Fin b → Bool) → Fin r → Fin s) + (decode : (Fin r → Fin s) → Fin b → Bool) (f : Gate d b) + (x : Fin d → Fin r → ZMod p) (i : Fin r) + (N : QueryMachine τ) (start : State γ → τ) (answer : τ → ℕ) (w : List Bool) + (hsupplier : ∀ j < s ^ (d * r), ∃ T, + (∀ k < T, N.halted (N.runFrom w + (start (sumState (gateParams (s := s) context x) (gateTable hs encode decode f i) j)) + k) = false) ∧ + N.halted (N.runFrom w + (start (sumState (gateParams (s := s) context x) (gateTable hs encode decode f i) j)) + T) = true ∧ + answer (N.runFrom w + (start (sumState (gateParams (s := s) context x) (gateTable hs encode decode f i) j)) + T) = gateTable hs encode decode f i j) : + ∃ T z, (machine (gateParams (s := s) context x) N start answer).runFrom w + (.inl (initial (gateParams (s := s) context x))) T = + .inl (gateParams (s := s) context x, s ^ (d * r), z) ∧ + (z : ZMod p) = counterGateExtension hs encode decode f x i := by + obtain ⟨T, hT⟩ := exists_runFrom_interpolateGrid (gateParams (s := s) context x) + N start answer w (gateTable hs encode decode f i) rfl hsupplier + exact ⟨T, _, hT, rfl⟩ + +end Cslib.TreeEval.Ary.Packed.QueryGate diff --git a/Complexity/Computability/TreeEval/Ary/Packed/QueryGateInit.lean b/Complexity/Computability/TreeEval/Ary/Packed/QueryGateInit.lean new file mode 100644 index 0000000000..3b2a58180b --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/QueryGateInit.lean @@ -0,0 +1,74 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed.QueryGateSpace +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatPow + +/-! +# Charged initialization of the same-input interpolation loop + +The alphabet size and coordinate count determine the assignment limit. This initialization +computes that power in native space, charging its possible binary output length in addition to +the supplied context, interpolation point and parameters. The limit is not a free input oracle. +-/ + +@[expose] public section + +namespace Cslib.TreeEval.Ary.Packed.QueryGate + +open Turing MultiTapeTM ComputableInSpace Reader + +/-- Supplier context, modulus/alphabet/coordinate parameters, and the interpolation point. -/ +abbrev Input (γ : Type) := γ × (ℕ × ℕ × ℕ) × List ℕ + +/-- The original parameters without a precomputed assignment limit. -/ +abbrev encInput {γ : Type} (encG : γ ↪ List Bool) : Input γ ↪ List Bool := + encPair encG (encPair encNat3 (encList encNat)) + +/-- Compute the full grid limit, retaining the supplied parameters. -/ +def prepare {γ : Type} (q : Input γ) : Params γ := + (q.1, q.2.1, q.2.2, q.2.1.2.1 ^ q.2.1.2.2) + +/-- Input storage and the possible binary size of the assignment limit. -/ +def initializationSpace {γ : Type} (encG : γ ↪ List Bool) (q : Input γ) : ℕ := + (encInput encG q).length + (q.2.1.2.2 + 1) * (encNat q.2.1.2.1).length + + (encNat q.2.1.2.2).length + 1 + +/-- The actual grid limit can be computed without a unary space charge for the alphabet size. -/ +theorem prepare_inSpace {γ : Type} (encG : γ ↪ List Bool) : + ComputableInSpace prepare (encInput encG) (encParams encG) + (initializationSpace encG) := by + have hin : (fun q : Input γ => (encInput encG q).length) ≼ initializationSpace encG := + .of_le fun q => by simp only [initializationSpace]; omega + have hctx := (fst encG (encPair encNat3 (encList encNat))).weaken hin + have hrest := (snd encG (encPair encNat3 (encList encNat))).weaken hin + have hpsn := fst' hrest + have hpoint := snd' hrest + have hs := fst' (snd' hpsn) + have hn := snd' (snd' hpsn) + have hlimit := budget_natPow_variable hs hn + (show (fun q : Input γ => (q.2.1.2.2 + 1) * (encNat q.2.1.2.1).length + + (encNat q.2.1.2.2).length + 1) ≼ initializationSpace encG from + .of_le fun q => by simp only [initializationSpace]; omega) + exact (budget_prod hctx (budget_prod hpsn (budget_prod hpoint hlimit))).of_enc_eq + fun _ => rfl + +/-- Fresh counters are initialized after the charged power computation. -/ +theorem initial_prepare_inSpace {γ : Type} (encG : γ ↪ List Bool) : + ComputableInSpace (fun q : Input γ => initial (prepare q)) (encInput encG) + (encState encG) (initializationSpace encG) := by + have hin : (fun q : Input γ => (encInput encG q).length) ≼ initializationSpace encG := + .of_le fun q => by simp only [initializationSpace]; omega + exact budget_prod (prepare_inSpace encG) + (budget_prod (budget_const 0 hin) (budget_const 0 hin)) + +@[simp] theorem prepare_limit {γ : Type} (q : Input γ) : + (prepare q).2.2.2 = (prepare q).2.1.2.1 ^ (prepare q).2.1.2.2 := rfl + +end Cslib.TreeEval.Ary.Packed.QueryGate diff --git a/Complexity/Computability/TreeEval/Ary/Packed/QueryGateSpace.lean b/Complexity/Computability/TreeEval/Ary/Packed/QueryGateSpace.lean new file mode 100644 index 0000000000..769a3eae88 --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/QueryGateSpace.lean @@ -0,0 +1,272 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed.QueryGate +public import Complexity.Computability.TreeEval.Ary.Packed.GateInterpolationSpace +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.CallSpace + +/-! +# Native components of the same-input packed interpolation loop + +All parameters, assignment and sum counters, the point and the supplier context are charged in +the encoded caller state. Supplier initialization and extracting its answer have explicit native +space premises. These local certificates feed the existing query-machine compiler; the supplier +run-state bounds and the assignment limit are additional obligations of its consumer. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed.QueryGate + +open Turing MultiTapeTM ComputableInSpace Reader + +variable {γ τ : Type} + +/-- Binary parameters, retaining the explicitly encoded supplier context. -/ +abbrev encParams (encG : γ ↪ List Bool) : Params γ ↪ List Bool := + encPair encG (encPair encNat3 (encPair (encList encNat) encNat)) + +/-- One caller state, with one assignment counter and one accumulator. -/ +abbrev encState (encG : γ ↪ List Bool) : State γ ↪ List Bool := + encPair (encParams encG) (encPair encNat encNat) + +/-- The active caller or one suspended caller with one callee state. -/ +abbrev encMachineState (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) : + State γ ⊕ (State γ × τ) ↪ List Bool := + encSum (encState encG) (encPair (encState encG) encT) + +/-- A completed-call prefix stores only its parameters and binary counters. In particular, +the number of table assignments does not multiply the retained point or context. -/ +theorem sumState_encoding_le (encG : γ ↪ List Bool) (q : Params γ) + (table : ℕ → ℕ) (j : ℕ) (hj : j ≤ q.2.2.2) (hp : 0 < q.2.1.1) : + (encState encG (sumState q table j)).length ≤ + 8 * ((encParams encG q).length + 1) := by + have hk := length_encNat_mono hj + have ha := length_encNat_mono (sumState_acc_lt q table j hp).le + change (encNat (sumState q table j).2.2).length ≤ (encNat q.2.1.1).length at ha + have hpar : (sumState q table j).1 = q := rfl + simp only [encState, encParams, encNat3, length_encPair, hpar, sumState_counter] + omega + +/-- Every physical prefix of one returned table call pays for one caller and the actual +callee state. The retained point and context are not multiplied by the number of assignments. -/ +theorem runFrom_one_encoding_le (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (q : Params γ) (N : QueryMachine τ) (start : State γ → τ) (answer : τ → ℕ) + (w : List Bool) (table : ℕ → ℕ) (j T B : ℕ) (hj : j < q.2.2.2) + (hp : 0 < q.2.1.1) + (hpre : ∀ i < T, N.halted (N.runFrom w (start (sumState q table j)) i) = false) + (hhalt : N.halted (N.runFrom w (start (sumState q table j)) T) = true) + (hanswer : answer (N.runFrom w (start (sumState q table j)) T) = table j) + (hsize : ∀ i ≤ T, + (encT (N.runFrom w (start (sumState q table j)) i)).length ≤ B) + (i : ℕ) (hi : i ≤ T + 2) : + (encMachineState encG encT + ((machine q N start answer).runFrom w (.inl (sumState q table j)) i)).length ≤ + 16 * ((encParams encG q).length + 1) + B + 2 := by + have hcaller := sumState_encoding_le encG q table j hj.le hp + have hnext := sumState_encoding_le encG q table (j + 1) (by omega) hp + by_cases hizero : i = 0 + · subst i + simp only [QueryMachine.runFrom_zero, encMachineState, encSum_inl, List.length_cons] + omega + by_cases hifinal : i = T + 2 + · subst i + rw [runFrom_one q N start answer w table j T hj hpre hhalt hanswer] + simp only [encMachineState, encSum_inl, List.length_cons] + omega + have hiT : i - 1 ≤ T := by omega + have hentry : (machine q N start answer).runFrom w (.inl (sumState q table j)) 1 = + .inr (sumState q table j, start (sumState q table j)) := by + apply QueryMachine.withCalls_step_call + · change decide (q.2.2.2 ≤ (sumState q table j).2.1) = false + rw [sumState_counter] + exact decide_eq_false (by omega) + · change (if (sumState q table j).2.1 < q.2.2.2 then + some (start (sumState q table j)) else none) = _ + rw [sumState_counter] + simp only [hj, ↓reduceIte] + rw [show i = 1 + (i - 1) by omega, QueryMachine.runFrom_add, hentry] + have hprefix := QueryMachine.withCalls_runFrom_subroutine_prefix + (controller q) N (request start) (resume answer) w + (sumState q table j) (start (sumState q table j)) (i - 1) + (fun k hk => hpre k (by omega)) + rw [show (machine q N start answer).runFrom w + (.inr (sumState q table j, start (sumState q table j))) (i - 1) = + .inr (sumState q table j, N.runFrom w (start (sumState q table j)) (i - 1)) + from hprefix] + have hcallee := hsize (i - 1) hiT + rw [show (encMachineState encG encT + (.inr (sumState q table j, + N.runFrom w (start (sumState q table j)) (i - 1)))).length = + 2 * (encState encG (sumState q table j)).length + + (encT (N.runFrom w (start (sumState q table j)) (i - 1))).length + 2 from + QueryMachine.withCalls_encoding_inr_length (encState encG) encT _ _] + omega + +/-- Concatenating table calls preserves one uniform physical-state bound for the whole +assignment loop, including all callee steps rather than only completed-call boundaries. -/ +theorem exists_runFrom_sumState_encoding_le (encG : γ ↪ List Bool) + (encT : τ ↪ List Bool) (q : Params γ) (N : QueryMachine τ) + (start : State γ → τ) (answer : τ → ℕ) (w : List Bool) (table : ℕ → ℕ) + (B : ℕ) (hp : 0 < q.2.1.1) + (hsupplier : ∀ j < q.2.2.2, ∃ T, + (∀ i < T, N.halted (N.runFrom w (start (sumState q table j)) i) = false) ∧ + N.halted (N.runFrom w (start (sumState q table j)) T) = true ∧ + answer (N.runFrom w (start (sumState q table j)) T) = table j ∧ + (∀ i ≤ T, (encT (N.runFrom w (start (sumState q table j)) i)).length ≤ B)) + (j : ℕ) (hj : j ≤ q.2.2.2) : + ∃ T, (machine q N start answer).runFrom w (.inl (initial q)) T = + .inl (sumState q table j) ∧ + ∀ i ≤ T, (encMachineState encG encT + ((machine q N start answer).runFrom w (.inl (initial q)) i)).length ≤ + 16 * ((encParams encG q).length + 1) + B + 2 := by + induction j with + | zero => + refine ⟨0, rfl, ?_⟩ + intro i hi + have hizero : i = 0 := by omega + subst i + have hcaller := sumState_encoding_le encG q table 0 (by omega) hp + simp only [sumState_zero] at hcaller + simp only [QueryMachine.runFrom_zero, encMachineState, encSum_inl, List.length_cons] + omega + | succ j ih => + obtain ⟨T, hT, hbound⟩ := ih (by omega) + obtain ⟨U, hpre, hhalt, hans, hsize⟩ := hsupplier j (by omega) + refine ⟨T + (U + 2), ?_, ?_⟩ + · rw [QueryMachine.runFrom_add, hT] + exact runFrom_one q N start answer w table j U (by omega) hpre hhalt hans + · intro i hi + by_cases hiT : i ≤ T + · exact hbound i hiT + · rw [show i = T + (i - T) by omega, QueryMachine.runFrom_add, hT] + exact runFrom_one_encoding_le encG encT q N start answer w table j U B + (by omega) hp hpre hhalt hans hsize (i - T) (by omega) + +/-- Supplier initialization is charged before making each request. -/ +theorem request_inSpace (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (start : State γ → τ) + (hstart : ComputableInSpace start (encState encG) encT + fun q => (encState encG q).length) : + ComputableInSpace (request start) (encState encG) (encOption encT) + (fun q => (encState encG q).length) := by + have hpar := fst (encParams encG) (encPair encNat encNat) + have hrest := snd' hpar + have hpointLimit := snd' hrest + have hlimit := snd' hpointLimit + have hk := fst' (snd (encParams encG) (encPair encNat encNat)) + have hsome := budget_comp_linear + (of_inO (ComputableInDegree.optionSome (enc₁ := encT) (enc := encOption encT)).inO_one) hstart + refine (budget_ite (budget_natLt hk hlimit) hsome + (budget_const (encB := encOption encT) none (.refl _))).of_enc_eq ?_ + intro q + by_cases h : q.2.1 < q.1.2.2.2 <;> simp [request, h] + +/-- Resumption computes the complete existing coordinate weight and one modular sum update. +The cost of extracting the returned table value is explicitly required. -/ +theorem resume_inSpace (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (answer : τ → ℕ) + (hanswer : ComputableInSpace answer encT encNat fun t => (encT t).length) : + ComputableInSpace (fun q : State γ × τ => resume answer q.1 q.2) + (encPair (encState encG) encT) (encState encG) + (fun q => (encPair (encState encG) encT q).length) := by + have hq := fst (encState encG) encT + have ht := snd (encState encG) encT + have hpar := fst' hq + have hrest := snd' hpar + have hpsn := fst' hrest + have hp := fst' hpsn + have hsn := snd' hpsn + have hs := fst' hsn + have hn := snd' hsn + have hx := fst' (snd' hrest) + have hst := snd' hq + have hk := fst' hst + have hacc := snd' hst + have hweight := budget_comp_linear gridWeight_inSpace + (budget_prod (budget_prod hp (budget_prod hs (budget_prod hn hk))) hx) + have hvalue := budget_comp_linear hanswer ht + have hmul := budget_comp_linear mulMod_inSpace + (budget_prod hp (budget_prod hvalue hweight)) + have hsum := budget_comp_linear addMod_inSpace + (budget_prod hp (budget_prod hacc hmul)) + have hnew := budget_ite (budget_natIsZero hp) + (budget_const (encB := encNat) 0 (.refl _)) hsum + refine (budget_prod hpar (budget_prod (budget_natSucc hk) hnew)).of_enc_eq ?_ + intro q + by_cases h : q.1.1.2.1.1 = 0 <;> simp [resume, gridSumStep, h] + +/-- Testing the limit uses only binary comparison of the counter and the supplied limit. -/ +theorem controller_halted_inSpace (encG : γ ↪ List Bool) (q : Params γ) : + ComputableInSpace (controller q).halted (encState encG) encBool + (fun st => (encState encG st).length) := by + have hpar := fst (encParams encG) (encPair encNat encNat) + have hlimit := snd' (snd' (snd' hpar)) + have hk := fst' (snd (encParams encG) (encPair encNat encNat)) + refine (budget_finite (encC := encBool) Bool.not (budget_natLt hk hlimit)).of_enc_eq ?_ + intro st + simp only [controller] + by_cases h : st.2.1 < st.1.2.2.2 + · simp [h, Nat.not_le_of_gt h] + · simp [h, Nat.le_of_not_gt h] + +/-- The assignment caller's query component, including supplier initialization. -/ +theorem machine_query_inSpace (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (q : Params γ) (N : QueryMachine τ) (start : State γ → τ) (answer : τ → ℕ) + (hquery : ComputableInSpace N.query encT encNat fun t => (encT t).length) + (hstart : ComputableInSpace start (encState encG) encT + fun st => (encState encG st).length) : + ComputableInSpace (machine q N start answer).query (encMachineState encG encT) encNat + (fun st => (encMachineState encG encT st).length) := + QueryMachine.withCalls_query_inSpace _ _ _ _ _ _ + (budget_const 0 (.refl _)) hquery (request_inSpace encG encT start hstart) + +/-- The complete returned-table update has a native linear local-space certificate. -/ +theorem machine_next_inSpace (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (q : Params γ) (N : QueryMachine τ) (start : State γ → τ) (answer : τ → ℕ) + (hnext : ComputableInSpace N.next (encPair encT (encOption encBool)) encT + fun p => (encPair encT (encOption encBool) p).length) + (hhalted : ComputableInSpace N.halted encT encBool fun t => (encT t).length) + (hstart : ComputableInSpace start (encState encG) encT + fun st => (encState encG st).length) + (hanswer : ComputableInSpace answer encT encNat fun t => (encT t).length) : + ComputableInSpace (machine q N start answer).next + (encPair (encMachineState encG encT) (encOption encBool)) + (encMachineState encG encT) + (fun st => (encPair (encMachineState encG encT) (encOption encBool) st).length) := + QueryMachine.withCalls_next_inSpace _ _ _ _ _ _ + (fst _ _) hnext hhalted (request_inSpace encG encT start hstart) + (resume_inSpace encG encT answer hanswer) + +/-- Both the table subroutine's output and the assignment caller's output are silent. -/ +theorem machine_emit_inSpace (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (q : Params γ) (N : QueryMachine τ) (start : State γ → τ) (answer : τ → ℕ) + (hstart : ComputableInSpace start (encState encG) encT + fun st => (encState encG st).length) : + ComputableInSpace (machine q N start answer).emit + (encPair (encMachineState encG encT) (encOption encBool)) encBits + (fun st => (encPair (encMachineState encG encT) (encOption encBool) st).length) := + QueryMachine.withCalls_emit_inSpace _ _ _ _ _ _ + (budget_const [] (.refl _)) (request_inSpace encG encT start hstart) + +/-- The composite halts exactly when the assignment caller reaches its supplied limit. -/ +theorem machine_halted_inSpace (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (q : Params γ) (N : QueryMachine τ) (start : State γ → τ) (answer : τ → ℕ) : + ComputableInSpace (machine q N start answer).halted (encMachineState encG encT) encBool + (fun st => (encMachineState encG encT st).length) := + QueryMachine.withCalls_halted_inSpace _ _ _ _ _ _ (controller_halted_inSpace encG q) + +/-- The result is retained in the state rather than emitted as an uncharged return word. -/ +theorem machine_finish_inSpace (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (q : Params γ) (N : QueryMachine τ) (start : State γ → τ) (answer : τ → ℕ) : + ComputableInSpace (machine q N start answer).finish (encMachineState encG encT) encBits + (fun st => (encMachineState encG encT st).length) := + QueryMachine.withCalls_finish_inSpace _ _ _ _ _ _ (budget_const [] (.refl _)) + +end Cslib.TreeEval.Ary.Packed.QueryGate diff --git a/ComplexityTests/TreeEvalIterativeEncoding.lean b/ComplexityTests/TreeEvalIterativeEncoding.lean new file mode 100644 index 0000000000..af5851c129 --- /dev/null +++ b/ComplexityTests/TreeEvalIterativeEncoding.lean @@ -0,0 +1,65 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of iterative controller encodings) + +public import Complexity.Computability.TreeEval.Ary.IterativeEncoding + +/-! +# Compact continuation encodings + +A dirty two-coordinate bank and two suspended frames instantiate the charged representation. +The proofs use its real table encoding, not a separately asserted abstract storage cost. +-/ + +@[expose] public section + +namespace ComplexityTests.TreeEvalIterativeEncoding + +open Cslib.TreeEval Cslib.TreeEval.Ary Cslib.TreeEval.Ary.Iter + +private instance : NeZero (7 : ℕ) := ⟨by decide⟩ + +def bank : Ary.State (ZMod 7) 2 2 := fun i j => (i.val + 2 * j.val + 1 : ℕ) + +def frame : Frame (ZMod 7) 2 := ⟨1, 1, false, 3, 2⟩ + +def cfg : Cfg (ZMod 7) 2 2 := ⟨bank, [frame, frame], .call 5 1⟩ + +example : ControlBounds 2 3 cfg := by + constructor + · decide + · intro fr hfr + have h : fr = frame := by simpa only [cfg, List.mem_cons, List.not_mem_nil, + or_false, or_self] using hfr + subst fr + decide + +example : (encRegs (encField 7) bank).length ≤ 42 := by + have h := length_encRegs_le (encField 7) bank (length_encField_le 7) + norm_num at h ⊢ + exact h + +example : (encCfg (encField 7) cfg).length ≤ 276 := by + have hbounds : ControlBounds 2 3 cfg := by + constructor + · decide + · intro fr hfr + have h : fr = frame := by simpa only [cfg, List.mem_cons, List.not_mem_nil, + or_false, or_self] using hfr + subst fr + decide + have h := length_encCfg_le (encField 7) cfg (length_encField_le 7) hbounds + norm_num [frameBits] at h ⊢ + exact h + +/-- Binary indices retain their logarithmic length. -/ +example : (encIndex 1024 ⟨1023, by decide⟩).length = 10 := by + rw [length_encIndex] + decide + +end ComplexityTests.TreeEvalIterativeEncoding diff --git a/ComplexityTests/TreeEvalQueryGate.lean b/ComplexityTests/TreeEvalQueryGate.lean new file mode 100644 index 0000000000..398d5b1416 --- /dev/null +++ b/ComplexityTests/TreeEvalQueryGate.lean @@ -0,0 +1,152 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of same-input packed interpolation) + +public import Complexity.Computability.TreeEval.Ary.Packed.QueryGateInit + +/-! +# Packed interpolation with an actual input-reading supplier + +The table subroutine reads the requested bit from the original input. Two calls produce the +interpolated value without retaining a table. Its native certificates use binary indices and +finite bit decoding, and include initialization of the full assignment limit. +-/ + +@[expose] public section + +namespace ComplexityTests.TreeEvalQueryGate + +open Turing MultiTapeTM Cslib.TreeEval.Ary.Packed +open Cslib.TreeEval.Ary.Packed.QueryGate + +abbrev Entry := ℕ × Bool × ℕ +abbrev encEntry : Entry ↪ List Bool := encPair encNat (encPair encBool encNat) + +/-- Return the requested original-input bit in a numeric accumulator. -/ +def supplier : QueryMachine Entry where + init := (0, false, 0) + query := Prod.fst + next p := (p.1.1, true, if p.2.getD false then 1 else 0) + emit _ := [true] + halted s := s.2.1 + finish _ := [true] + +def start (q : State Unit) : Entry := (q.2.1, false, 0) +def answer (t : Entry) := t.2.2 + +/-- Two Boolean assignments, evaluated at the field point three modulo five. -/ +def input : Input Unit := ((), (5, 2, 1), [3]) +def params := prepare input + +def loop := machine params supplier start answer + +example : params.2.2.2 = 2 := rfl +example : loop.run [true, false] 6 = .inl (params, 2, 3) := by rfl +example : loop.run [false, true] 6 = .inl (params, 2, 3) := by rfl +example : loop.run [true, true] 6 = .inl (params, 2, 1) := by rfl +example : loop.run [] 6 = .inl (params, 2, 0) := by rfl +example : loop.halted (loop.run [true, false] 6) = true := by rfl +example : loop.output [true, false] 6 = [] := by decide + +/-- Every lookup contract is witnessed by one actual input query. -/ +example (w : List Bool) : + ∃ T, loop.runFrom w (.inl (initial params)) T = + .inl (params, 2, interpolateGrid 5 2 1 + (fun j => if w[j]?.getD false then 1 else 0) [3]) := by + apply exists_runFrom_interpolateGrid params supplier start answer w + · rfl + · intro j hj + refine ⟨1, ?_, ?_, ?_⟩ + · intro i hi + have : i = 0 := by omega + subst i + rfl + · rfl + · simp only [QueryMachine.runFrom_succ, QueryMachine.runFrom_zero, + QueryMachine.step, QueryMachine.read, supplier, start, answer, sumState_counter] + rfl + +private theorem query_native : + ComputableInSpace supplier.query encEntry encNat (fun t => (encEntry t).length) := + ComputableInSpace.fst _ _ + +private theorem halt_native : + ComputableInSpace supplier.halted encEntry encBool (fun t => (encEntry t).length) := + ComputableInSpace.fst' (ComputableInSpace.snd encNat (encPair encBool encNat)) + +private theorem next_native : + ComputableInSpace supplier.next (encPair encEntry (encOption encBool)) encEntry + (fun t => (encPair encEntry (encOption encBool) t).length) := by + open ComputableInSpace in + exact budget_prod (fst' (fst encEntry (encOption encBool))) + (budget_prod (budget_const true (.refl _)) + (budget_finite (encC := encNat) (fun b : Option Bool => if b.getD false then 1 else 0) + (snd encEntry (encOption encBool)))) + +private theorem start_native : + ComputableInSpace start (encState encUnit) encEntry + (fun q => (encState encUnit q).length) := by + open ComputableInSpace in + exact budget_prod (fst' (snd (encParams encUnit) (encPair encNat encNat))) + (budget_prod (budget_const false (.refl _)) (budget_const 0 (.refl _))) + +private theorem answer_native : + ComputableInSpace answer encEntry encNat (fun t => (encEntry t).length) := + ComputableInSpace.snd' (ComputableInSpace.snd encNat (encPair encBool encNat)) + +example : ComputableInSpace loop.next + (encPair (encMachineState encUnit encEntry) (encOption encBool)) + (encMachineState encUnit encEntry) + (fun t => (encPair (encMachineState encUnit encEntry) (encOption encBool) t).length) := + machine_next_inSpace encUnit encEntry params supplier start answer + next_native halt_native start_native answer_native + +example : ComputableInSpace (fun q : Input Unit => initial (prepare q)) + (encInput encUnit) (encState encUnit) (initializationSpace encUnit) := + initial_prepare_inSpace encUnit + +private theorem supplier_peak (w : List Bool) (k i : ℕ) (hk : k < 2) (hi : i ≤ 1) : + (encEntry (supplier.runFrom w (k, false, 0) i)).length ≤ 7 := by + have hk' : k = 0 ∨ k = 1 := by omega + have hi' : i = 0 ∨ i = 1 := by omega + rcases hk' with rfl | rfl <;> rcases hi' with rfl | rfl + · norm_num [QueryMachine.runFrom_zero, encEntry] + · cases h : w[0]?.getD false <;> simp [QueryMachine.runFrom_succ, + QueryMachine.runFrom_zero, QueryMachine.step, QueryMachine.read, supplier, + encEntry, encBool_apply, h] + · norm_num [QueryMachine.runFrom_zero, encEntry] + · cases h : w[1]?.getD false <;> simp [QueryMachine.runFrom_succ, + QueryMachine.runFrom_zero, QueryMachine.step, QueryMachine.read, supplier, + encEntry, encBool_apply, h] + +/-- A concrete supplier also satisfies the full physical-prefix space contract. -/ +example (w : List Bool) : + ∃ T, loop.runFrom w (.inl (initial params)) T = + .inl (sumState params (fun j => if w[j]?.getD false then 1 else 0) 2) ∧ + ∀ i ≤ T, (encMachineState encUnit encEntry + (loop.runFrom w (.inl (initial params)) i)).length ≤ + 16 * ((encParams encUnit params).length + 1) + 7 + 2 := by + apply exists_runFrom_sumState_encoding_le encUnit encEntry params supplier start answer w + _ 7 (by decide) _ 2 (by decide) + intro j hj + refine ⟨1, ?_, ?_, ?_, ?_⟩ + · intro i hi + have : i = 0 := by omega + subst i + rfl + · rfl + · simp only [QueryMachine.runFrom_succ, QueryMachine.runFrom_zero, + QueryMachine.step, QueryMachine.read, supplier, start, answer, sumState_counter] + rfl + · intro i hi + change (encEntry (supplier.runFrom w + ((sumState params _ j).2.1, false, 0) i)).length ≤ 7 + rw [sumState_counter] + exact supplier_peak w j i hj hi + +end ComplexityTests.TreeEvalQueryGate From a6a62fa0a83c04a4e0f9e2e89593849479f8b3ed Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sat, 31 Oct 2026 23:14:11 +0000 Subject: [PATCH 08/36] feat(turing): validate complete HPV movement schedules by local gates --- Complexity.lean | 1 + .../Turing/MultiTape/BlockComputation.lean | 422 ++++++++++++++++++ .../Turing/MultiTape/BlockComputation.lean | 77 ++++ 3 files changed, 500 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/BlockComputation.lean diff --git a/Complexity.lean b/Complexity.lean index 9866cdb1d0..87acd0f6f1 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1690,6 +1690,7 @@ public import Complexity.Computability.Machines.ReadMachine.Tidy public import Complexity.Computability.Machines.Turing.MultiTape.Alternating public import Complexity.Computability.Machines.Turing.MultiTape.Alternating.Game public import Complexity.Computability.Machines.Turing.MultiTape.Alternating.GuessVerify +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation public import Complexity.Computability.Machines.Turing.MultiTape.BlockTrace public import Complexity.Computability.Machines.Turing.MultiTape.Combinators.Append public import Complexity.Computability.Machines.Turing.MultiTape.Combinators.Comp diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation.lean new file mode 100644 index 0000000000..7b17bc177f --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation.lean @@ -0,0 +1,422 @@ +/- +Copyright (c) 2026 Christian Reitwiessner, Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner, Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.Probabilistic.BlockNetwork +public import Complexity.Computability.Machines.Turing.MultiTape.Probabilistic.Deterministic + +/-! +# Functional computation on HPV time blocks + +A guessed movement schedule determines a bounded-fan-in computation: each interval reads its +predecessor's control record and the last providers of its three-block windows. Its gate replays +one interval and checks the movement of every head, including the input head. Failure propagates +through the mandatory predecessor edge, so a successful final value validates every earlier +movement label. + +The definitions use the existing tape windows and configuration embedding. They describe pure +semantics with the original input shared by the local replays; they do not certify native input +access, graph construction or a space bound. + +## Main definitions + +* `Turing.MultiTapePTM.BlockComputation.gate`: the local interval function, with failure. +* `Turing.MultiTapePTM.BlockComputation.value`: evaluation through earlier providers. + +## Main results + +* `Turing.MultiTapePTM.BlockComputation.value_final_eq_some_iff`: success validates the complete + movement schedule and returns the true leaving view. +* `Turing.MultiTapePTM.BlockComputation.value_final_eq_none_iff`: a wrong schedule fails. +* `Turing.MultiTapePTM.BlockComputation.boundary_ofDeterministic`: the native deterministic + boundary is recovered exactly. + +The window reconstruction proof follows the block-network proof by Samuel Schlesinger. + +## References + +* [R. Williams, *Simulating Time With Square-Root Space*][Williams2025], arXiv v1, §3.2, + “Functions” and “Finishing”: local replay checks movement labels and propagates FAIL. + This library construction uses the existing aligned three-block windows and a mandatory + preceding-control edge; finite bit coding and unfolding into Tree Evaluation are not yet + supplied here. +-/ + +public section + +namespace Turing.MultiTapeTM + +variable {k : ℕ} {State : Type} {input : List Bool} + +/-- Replaying fixed coins in the deterministic embedding is the original native run. -/ +theorem stepCoins_ofDeterministic (M : MultiTapeTM k Bool State) (coins : List Bool) + (c : Cfg k Bool State input) : + (MultiTapePTM.ofDeterministic M).stepCoins coins c = M.runFrom c coins.length := by + induction coins generalizing c with + | nil => rfl + | cons bit coins ih => + rw [MultiTapePTM.stepCoins_cons, ih] + have hstep : ((MultiTapePTM.ofDeterministic M).withCoin bit).toTM.step c = M.step c := by + cases hs : c.state with + | none => + rw [MultiTapeTM.step_of_halt hs, MultiTapeTM.step_of_halt hs] + | some state => + rw [MultiTapeTM.step_of_state hs, MultiTapeTM.step_of_state hs, + MultiTapeMachine.toTM, MultiTapeTM.tr_ofTr] + rfl + rw [hstep] + change M.runFrom (M.step c) coins.length = M.runFrom c (coins.length + 1) + rw [show coins.length + 1 = 1 + coins.length by omega, M.runFrom_add] + rfl + +end Turing.MultiTapeTM + +namespace Turing.MultiTapePTM.BlockComputation + +open MultiTapeNTM.ChoiceTable + +variable {k q b : ℕ} {State : Type} + +/-- One interval's leaving control record and window contents. Failure is represented by an +outer `Option`, rather than identifying it with any valid tape contents. -/ +abbrev View (k : ℕ) (State : Type) (b : ℕ) := + BlockControl k State b × (Fin k → Fin 3 → ZMod b → Option Bool) + +variable (machine : MultiTapePTM k Bool State) (input : List Bool) (b : ℕ) [NeZero b] + (σ : Schedule k q) + +/-- The source value contains the initial control and blank work tapes. -/ +@[expose] def initialView : View k State b := + ((some machine.initial, 0, .empty), fun _ _ _ => none) + +/-- A window block is obtained from its last provider; a block never seen before is blank. -/ +@[expose] def enteringWindow (i : Fin q) (providers : Fin k → Fin 3 → Option (View k State b)) : + Fin k → Fin 3 → ZMod b → Option Bool := fun h d => + match lastBefore (InWindow σ h (portBlock σ i h d)) i with + | none => fun _ => none + | some j => ((providers h d).getD (initialView machine b)).2 h + (portOf σ j h (portBlock σ i h d)) + +open scoped Classical in +/-- The local gate has one control dependency and `3*k` window dependencies. All failed +inputs propagate. The replay checks the movement labels of all work heads and the input head. -/ +@[expose] noncomputable def gate (i : Fin q) (previous : Option (View k State b)) + (providers : Fin k → Fin 3 → Option (View k State b)) : Option (View k State b) := + previous.bind fun prev => + if ∀ h d, (providers h d).isSome then + let c := machine.stepCoins (List.replicate b false) + (embedCfg (input := input) b (scheduleBlock σ i) prev.1 + (enteringWindow machine b σ i providers)) + if ∀ head, c.blockIndex b head = scheduleBlock σ i head + σ i head then + some (c.blockControl b, c.windowContents b (scheduleBlock σ i)) + else none + else none + +/-- The value after `n` intervals. Each recursion follows either the preceding interval or an +older provider. No configuration outside the dependency values is consulted by a local gate. -/ +@[expose] noncomputable def value : ℕ → Option (View k State b) + | 0 => some (initialView machine b) + | n + 1 => + if hi : n < q then + gate machine input b σ ⟨n, hi⟩ (value n) fun h d => + match hp : lastBefore (InWindow σ h (portBlock σ ⟨n, hi⟩ h d)) ⟨n, hi⟩ with + | none => some (initialView machine b) + | some j => value (j + 1) + else none +termination_by n => n +decreasing_by + · omega + · have hj := (lastBefore_eq_some_iff.mp hp).1 + change (j : ℕ) < n at hj + omega + +omit [NeZero b] in +/-- The dependency supplying the control record is present even if no work block is needed. -/ +theorem gate_none (i : Fin q) (providers : Fin k → Fin 3 → Option (View k State b)) : + gate machine input b σ i none providers = none := by + simp [gate] + +omit [NeZero b] in +/-- Once an interval fails, the mandatory control dependency propagates failure. -/ +theorem value_succ_eq_none {n : ℕ} (h : value machine input b σ n = none) : + value machine input b σ (n + 1) = none := by + rw [value] + split_ifs with hi + · rw [h, gate_none] + · rfl + +/-- The true run at an interval boundary, with one fixed false coin per step. -/ +@[expose] noncomputable def boundary (n : ℕ) : Cfg k Bool State input := + machine.intervalStart b (List.replicate (q * b) false) (Cfg.init machine.initial input) n + +/-- The true movement schedule for the fixed-coin replay. -/ +@[expose] noncomputable def actualSchedule : Schedule k q := + machine.schedule b q (List.replicate (q * b) false) (Cfg.init machine.initial input) + +/-- Every guessed label before this boundary agrees with the true replay. -/ +@[expose] def Prefix (n : ℕ) : Prop := + ∀ j : Fin q, j.val < n → actualSchedule machine input b (q := q) j = σ j + +/-- The true value left by an interval, retaining its own window rather than the next window. -/ +@[expose] noncomputable def trueView : ℕ → View k State b + | 0 => initialView machine b + | n + 1 => ((boundary machine input b (q := q) (n + 1)).blockControl b, + (boundary machine input b (q := q) (n + 1)).windowContents b (scheduleBlock σ n)) + +omit [NeZero b] in +private theorem boundary_succ {n : ℕ} (hn : n < q) : + boundary machine input b (q := q) (n + 1) = + machine.stepCoins (List.replicate b false) (boundary machine input b (q := q) n) := by + have hmul := Nat.mul_le_mul_right b (Nat.succ_le_of_lt hn) + simp only [Nat.succ_mul] at hmul + have hlen : min b (q * b - n * b) = b := by omega + simp only [boundary, intervalStart_succ, List.drop_replicate, List.take_replicate, hlen] + +omit [NeZero b] in +private theorem control_trueView (n : ℕ) : + (trueView machine input b σ n).1 = (boundary machine input b (q := q) n).blockControl b := by + cases n with + | zero => + have h : ∀ head : Option (Fin k), + ((Cfg.init machine.initial input).headPos head : ZMod b) = 0 := + fun head => by rw [Cfg.headPos_init, Int.cast_zero] + simp only [trueView, initialView, boundary, intervalStart_zero, Cfg.blockControl, + funext h] + rfl + | succ n => rfl + +private theorem boundary_index {n : ℕ} (hn : n ≤ q) (hσ : Prefix machine input b σ n) : + (boundary machine input b (q := q) n).blockIndex b = scheduleBlock σ n := + machine.blockIndex_intervalStart_eq_scheduleBlock b _ σ hn hσ + +omit [NeZero b] in +/-- The initial boundary contains blank work tapes. -/ +private theorem boundary_zero_cell (h : Fin k) (z : ℤ) : + (boundary machine input b (q := q) 0).workTapes h z = none := by + simp [boundary] + +/-- A block outside the three aligned windows contains no cell of that window. -/ +private theorem outside_window {c : Cfg k Bool State input} {h : Fin k} {j : ℤ} + (hj : ¬ |c.blockIndex b (some h) - j| ≤ 1) (r : ZMod b) : + b * j + (r.val : ℤ) ∉ c.blockWindow b h := by + have hb : (0 : ℤ) < b := by exact_mod_cast Nat.pos_of_neZero b + have hr : (r.val : ℤ) < b := by exact_mod_cast ZMod.val_lt r + have hr0 : (0 : ℤ) ≤ r.val := by positivity + simp only [Cfg.blockIndex, Cfg.headPos_some, abs_le, not_and_or, not_le] at hj + simp only [Cfg.blockWindow, Set.mem_Ico, not_and_or, not_le, not_lt] + rcases hj with hj | hj + · right + nlinarith + · left + nlinarith + +/-- Last providers reconstruct the actual entering window whenever all prior labels agree. -/ +private theorem enteringWindow_true (i : Fin q) (hσ : Prefix machine input b σ i) + (providers : Fin k → Fin 3 → Option (View k State b)) + (hp : ∀ h d j, lastBefore (InWindow σ h (portBlock σ i h d)) i = some j → + providers h d = some (trueView machine input b σ (j + 1))) : + enteringWindow machine b σ i providers = + (boundary machine input b (q := q) i).windowContents b (scheduleBlock σ i) := by + have hindex (j : ℕ) (hj : j ≤ i) : + (boundary machine input b (q := q) j).blockIndex b = scheduleBlock σ j := + boundary_index machine input b σ (by omega) (fun j' hj' => hσ j' (by omega)) + funext h d r + set z := portBlock σ i h d + have huntouched (j : ℕ) (hj : j ≤ i) + (hnot : ∀ j' : Fin q, j ≤ j' → j' < i → ¬ InWindow σ h z j') : + (boundary machine input b (q := q) i).workTapes h (b * z + r.val) = + (boundary machine input b (q := q) j).workTapes h (b * z + r.val) := by + refine machine.workTapes_intervalStart_eq_of_notMem_blockWindow (Nat.pos_of_neZero b) + _ _ hj fun j' hj' hj'i => outside_window (input := input) (b := b) ?_ r + change ¬ |(boundary machine input b (q := q) j').blockIndex b (some h) - z| ≤ 1 + rw [hindex j' hj'i.le] + exact hnot ⟨j', by omega⟩ hj' hj'i + change enteringWindow machine b σ i providers h d r = + (boundary machine input b (q := q) i).workTapes h (b * z + r.val) + unfold enteringWindow + cases hlast : lastBefore (InWindow σ h (portBlock σ i h d)) i with + | none => + simp only + rw [huntouched 0 (Nat.zero_le _) (fun j _ hj => lastBefore_eq_none_iff.mp hlast j hj), + boundary_zero_cell] + | some j => + obtain ⟨hji, hin, hbetween⟩ := lastBefore_eq_some_iff.mp hlast + rw [hp h d j hlast] + simp only [Option.getD_some, trueView] + rw [huntouched (j + 1) (by omega) (fun j' hj' hj'i => hbetween j' (by omega) hj'i)] + change (boundary machine input b (q := q) (j + 1)).workTapes h + (b * portBlock σ j h (portOf σ j h z) + r.val) = _ + rw [portBlock_portOf hin] + +omit [NeZero b] in +private theorem gate_eq_some_iff (i : Fin q) (prev v : View k State b) + (providers : Fin k → Fin 3 → Option (View k State b)) : + gate machine input b σ i (some prev) providers = some v ↔ + (∀ h d, (providers h d).isSome) ∧ + machine.IntervalStep b input (scheduleBlock σ i) (σ i) prev.1 + (enteringWindow machine b σ i providers) (List.replicate b false) v.1 v.2 := by + classical + simp only [gate, Option.bind_some] + split_ifs with hproviders hmove <;> + simp_all [IntervalStep, Prod.ext_iff] + +omit [NeZero b] in +private theorem sign_cast_injective : Function.Injective (fun s : SignType => (s : ℤ)) := by + intro s t h + cases s <;> cases t <;> first | rfl | (simp at h) + +private theorem move_iff (i : Fin q) : + (∀ head, (boundary machine input b (q := q) (i + 1)).blockIndex b head = + (boundary machine input b (q := q) i).blockIndex b head + σ i head) ↔ + actualSchedule machine input b (q := q) i = σ i := by + rw [funext_iff] + refine forall_congr' fun head => ?_ + have h := machine.coe_schedule (Nat.pos_of_neZero b) q (List.replicate (q * b) false) + (Cfg.init machine.initial input) i head + change ((actualSchedule machine input b (q := q) i head : SignType) : ℤ) = + (boundary machine input b (q := q) (i + 1)).blockIndex b head - + (boundary machine input b (q := q) i).blockIndex b head at h + constructor + · intro hm + have heq : (actualSchedule machine input b (q := q) i head : ℤ) = σ i head := by omega + exact sign_cast_injective heq + · intro hs + rw [hs] at h + omega + +private theorem gate_true (i : Fin q) (hσ : Prefix machine input b σ i) + (providers : Fin k → Fin 3 → Option (View k State b)) + (hproviders : ∀ h d, (providers h d).isSome) + (hp : ∀ h d j, lastBefore (InWindow σ h (portBlock σ i h d)) i = some j → + providers h d = some (trueView machine input b σ (j + 1))) + (v : View k State b) : + gate machine input b σ i (some (trueView machine input b σ i)) providers = some v ↔ + actualSchedule machine input b (q := q) i = σ i ∧ + v = trueView machine input b σ (i + 1) := by + rw [gate_eq_some_iff, and_iff_right hproviders, control_trueView, + enteringWindow_true machine input b σ i hσ providers hp] + have hindex := boundary_index machine input b σ i.isLt.le hσ + rw [← hindex, machine.intervalStep_view_iff b _ (by simp), + ← boundary_succ machine input b i.isLt, move_iff] + rw [hindex] + change (_ = v.1 ∧ _ = v.2 ∧ _) ↔ _ ∧ v = (_, _) + constructor + · rintro ⟨hc, hw, hm⟩ + exact ⟨hm, Prod.ext hc.symm hw.symm⟩ + · rintro ⟨hm, rfl⟩ + exact ⟨rfl, rfl, hm⟩ + +/-- A successful interval value is exactly the true leaving view, and validates every movement +label in its prefix. In particular, success at the root validates all earlier labels. -/ +theorem value_eq_some_iff {n : ℕ} (hn : n ≤ q) (v : View k State b) : + value machine input b σ n = some v ↔ + Prefix machine input b σ n ∧ v = trueView machine input b σ n := by + induction n using Nat.strong_induction_on generalizing v with + | ind n ih => + cases n with + | zero => simp [value, Prefix, trueView, eq_comm] + | succ n => + have hnq : n < q := by omega + let i : Fin q := ⟨n, hnq⟩ + let providers : Fin k → Fin 3 → Option (View k State b) := fun h d => + match hp : lastBefore (InWindow σ h (portBlock σ i h d)) i with + | none => some (initialView machine b) + | some j => value machine input b σ (j + 1) + rw [value, dite_eq_left hnq] + change gate machine input b σ i (value machine input b σ n) providers = some v ↔ _ + have hprev := ih n (by omega) (by omega) + by_cases hσ : Prefix machine input b σ n + · have hvn : value machine input b σ n = some (trueView machine input b σ n) := + (hprev _).mpr ⟨hσ, rfl⟩ + have hp h d j (hj : lastBefore (InWindow σ h (portBlock σ i h d)) i = some j) : + providers h d = some (trueView machine input b σ (j + 1)) := by + have hji := (lastBefore_eq_some_iff.mp hj).1 + change (j : ℕ) < n at hji + have hvj := ih (j + 1) (by omega) (by omega) + (trueView machine input b σ (j + 1)) + dsimp only [providers] + split + next he => + have hc := he.symm.trans hj + cases hc + next j' he => + have heq : j' = j := Option.some.inj (he.symm.trans hj) + subst j' + exact hvj.mpr ⟨fun j' hj' => hσ j' (by omega), rfl⟩ + have hproviders h d : (providers h d).isSome := by + cases hj : lastBefore (InWindow σ h (portBlock σ i h d)) i with + | none => + dsimp only [providers] + split + next => rfl + next j he => + have hc := hj.symm.trans he + cases hc + | some j => rw [hp h d j hj]; rfl + rw [hvn, gate_true machine input b σ i hσ providers hproviders hp] + have hprefix : Prefix machine input b σ (n + 1) ↔ + actualSchedule machine input b (q := q) i = σ i := by + constructor + · exact fun h => h i (by simp [i]) + · intro h j hj + rcases Nat.lt_succ_iff_lt_or_eq.mp hj with hj | hj + · exact hσ j hj + · have heq : j = i := Fin.ext hj + simpa only [heq] using h + rw [hprefix] + · have hvn : value machine input b σ n = none := by + cases hv : value machine input b σ n with + | none => rfl + | some w => exact False.elim (hσ ((hprev w).mp hv).1) + rw [hvn, gate_none] + constructor + · intro h + cases h + · rintro ⟨hp, -⟩ + exact False.elim (hσ (fun j hj => hp j (by omega))) + +/-- At the final boundary, every guessed label has been validated. -/ +theorem value_final_eq_some_iff (v : View k State b) : + value machine input b σ q = some v ↔ + actualSchedule machine input b (q := q) = σ ∧ v = trueView machine input b σ q := by + rw [value_eq_some_iff machine input b σ le_rfl] + have hprefix : Prefix machine input b σ q ↔ actualSchedule machine input b (q := q) = σ := by + constructor + · intro h + funext j + exact h j j.isLt + · intro h j _ + exact congrFun h j + rw [hprefix] + +/-- A wrong complete movement guess fails at the final boundary. -/ +theorem value_final_eq_none_iff : + value machine input b σ q = none ↔ actualSchedule machine input b (q := q) ≠ σ := by + constructor + · intro hfail heq + have hgood := (value_final_eq_some_iff machine input b σ + (trueView machine input b σ q)).mpr ⟨heq, rfl⟩ + rw [hfail] at hgood + cases hgood + · intro hwrong + cases hv : value machine input b σ q with + | none => rfl + | some v => + exact False.elim (hwrong ((value_final_eq_some_iff machine input b σ v).mp hv).1) + +omit [NeZero b] in +/-- The fixed-coin boundaries of a deterministic embedding are the native machine boundaries. -/ +theorem boundary_ofDeterministic (M : MultiTapeTM k Bool State) {n : ℕ} (hn : n ≤ q) : + boundary (ofDeterministic M) input b (q := q) n = + M.runFrom (Cfg.init M.q₀ input) (n * b) := by + rw [boundary, intervalStart, MultiTapeTM.stepCoins_ofDeterministic] + simp only [List.length_take, List.length_replicate, + Nat.min_eq_left (Nat.mul_le_mul_right b hn)] + rfl + +end Turing.MultiTapePTM.BlockComputation diff --git a/ComplexityTests/Machines/Turing/MultiTape/BlockComputation.lean b/ComplexityTests/Machines/Turing/MultiTape/BlockComputation.lean new file mode 100644 index 0000000000..7331735c01 --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/BlockComputation.lean @@ -0,0 +1,77 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of functional HPV block computation) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation + +/-! +# Functional HPV schedule validation + +A native machine emits one bit and halts. Its actual schedule succeeds, a wrong input-head +movement fails, and failure persists through subsequent intervals. The empty schedule is also +inhabited. These test the deterministic bridge and the mandatory control dependency. +-/ + +@[expose] public section + +namespace ComplexityTests.BlockComputation + +open Turing MultiTapePTM.BlockComputation + +/-- Emit one bit while keeping the input head fixed, then halt. -/ +def emit : MultiTapeTM 0 Bool Unit := MultiTapeTM.ofTr () fun _ _ _ => + { inputTape := 0, workTapes := Fin.elim0, output := some true, state := none } + +noncomputable def machine : MultiTapePTM 0 Bool Unit := MultiTapePTM.ofDeterministic emit +noncomputable def actual : MultiTapePTM.Schedule 0 2 := actualSchedule machine [] 1 + +def wrong : MultiTapePTM.Schedule 0 2 := fun i _ => if i = 0 then 1 else 0 + +example : value machine [] 1 actual 2 = some (trueView machine [] 1 actual 2) := + (value_final_eq_some_iff machine [] 1 actual _).mpr ⟨rfl, rfl⟩ + +private theorem initial_input_fixed : + (emit.runFrom (Cfg.init () []) 1).headPos none = 0 := by + rw [MultiTapeTM.runFrom_one, MultiTapeTM.step_of_state (q := ()) rfl] + simp [emit, Action.apply, Cfg.headPos] + +private theorem actual_first : (actual 0 none : ℤ) = 0 := by + change ((machine.schedule 1 2 (List.replicate (2 * 1) false) + (Cfg.init machine.initial [])) 0 none : ℤ) = 0 + rw [MultiTapePTM.coe_schedule machine (by decide)] + change ((boundary machine [] 1 (q := 2) 1).headPos none / 1 - + (boundary machine [] 1 (q := 2) 0).headPos none / 1) = 0 + rw [machine] + rw [boundary_ofDeterministic [] 1 emit (by decide), + boundary_ofDeterministic [] 1 emit (by decide)] + change (emit.runFrom (Cfg.init () []) 1).headPos none / 1 - + (Cfg.init () []).headPos none / 1 = 0 + rw [initial_input_fixed, Cfg.headPos_init] + norm_num + +private theorem wrong_schedule : actual ≠ wrong := by + intro h + have hi := congrArg (fun σ : MultiTapePTM.Schedule 0 2 => (σ 0 none : ℤ)) h + rw [actual_first] at hi + norm_num [wrong] at hi + +example : value machine [] 1 wrong 2 = none := + (value_final_eq_none_iff machine [] 1 wrong).mpr wrong_schedule + +example : value machine [] 1 wrong 3 = none := + value_succ_eq_none machine [] 1 wrong + ((value_final_eq_none_iff machine [] 1 wrong).mpr wrong_schedule) + +/-- Empty schedules validate at the initial source without making a replay step. -/ +example (σ : MultiTapePTM.Schedule 0 0) : + value machine [] 1 σ 0 = some (initialView machine 1) := by simp only [value] + +example : boundary machine [] 1 (q := 2) 2 = emit.runFrom (Cfg.init () []) 2 := + boundary_ofDeterministic [] 1 emit le_rfl + +end ComplexityTests.BlockComputation From 3d493cfb0bdbda4598f0555809f83640ef497877 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sat, 31 Oct 2026 23:15:35 +0000 Subject: [PATCH 09/36] feat(treeeval): initialize packed field parameters in native space --- Complexity.lean | 2 + .../Complexity/InSpace/NatPrime.lean | 260 ++++++++++++++++++ .../TreeEval/Ary/Packed/ParametersSpace.lean | 119 ++++++++ ComplexityTests/TreeEvalParameters.lean | 59 ++++ 4 files changed, 440 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/NatPrime.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/ParametersSpace.lean create mode 100644 ComplexityTests/TreeEvalParameters.lean diff --git a/Complexity.lean b/Complexity.lean index 87acd0f6f1..2cf16c2528 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1734,6 +1734,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSp public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Bit public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatPow +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatPrime public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Plain public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpaceLib public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LinearSpaceArith @@ -2318,6 +2319,7 @@ public import Complexity.Computability.TreeEval.Ary.Packed.GateInterpolation public import Complexity.Computability.TreeEval.Ary.Packed.GateInterpolationSpace public import Complexity.Computability.TreeEval.Ary.Packed.Interpolation public import Complexity.Computability.TreeEval.Ary.Packed.InterpolationSpace +public import Complexity.Computability.TreeEval.Ary.Packed.ParametersSpace public import Complexity.Computability.TreeEval.Ary.Packed.QueryGate public import Complexity.Computability.TreeEval.Ary.Packed.QueryGateInit public import Complexity.Computability.TreeEval.Ary.Packed.QueryGateSpace diff --git a/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/NatPrime.lean b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/NatPrime.lean new file mode 100644 index 0000000000..10005797fe --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/NatPrime.lean @@ -0,0 +1,260 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpaceLib +public import Mathlib.Data.Nat.Prime.Defs + +/-! +# Native primality testing and bounded prime search + +Trial division stores the candidate, one binary divisor counter and a Boolean accumulator. +The counted loop does not construct a list of divisors. Running time is unrestricted; all +counters, inputs and outputs are charged in the native work-space certificates. +-/ + +public section + +namespace Turing.MultiTapeTM.ComputableInSpace + +namespace PrimeSearch + +/-- Candidate, divisor and whether all earlier tested divisors failed to divide it. -/ +abbrev TrialState := ℕ × ℕ × Bool + +/-- Binary candidate and divisor with the accumulated primality test bit. -/ +abbrev encTrialState : TrialState ↪ List Bool := encPair encNat (encPair encNat encBool) + +/-- One trial-division iteration. Divisors at or above the candidate have no effect. -/ +@[expose] def trialStep (q : TrialState) : TrialState := + (q.1, q.2.1 + 1, q.2.2 && decide (q.2.1 < q.1 → q.1 % q.2.1 ≠ 0)) + +/-- The counted trial-division run. -/ +@[expose] def trialRun (n k : ℕ) : TrialState := trialStep^[k] (n, 2, true) + +@[simp] theorem trialRun_zero (n : ℕ) : trialRun n 0 = (n, 2, true) := rfl + +theorem trialRun_succ (n k : ℕ) : trialRun n (k + 1) = trialStep (trialRun n k) := + Function.iterate_succ_apply' _ _ _ + +@[simp] theorem trialRun_candidate (n k : ℕ) : (trialRun n k).1 = n := by + induction k with + | zero => rfl + | succ k ih => rw [trialRun_succ]; exact ih + +@[simp] theorem trialRun_divisor (n k : ℕ) : (trialRun n k).2.1 = 2 + k := by + induction k with + | zero => rfl + | succ k ih => rw [trialRun_succ]; change (trialRun n k).2.1 + 1 = 2 + (k + 1); omega + +/-- Exactly the tested proper divisors contribute to the Boolean accumulator. -/ +theorem trialRun_true_iff (n k : ℕ) : + (trialRun n k).2.2 = true ↔ ∀ i < k, 2 + i < n → n % (2 + i) ≠ 0 := by + induction k with + | zero => simp [trialRun] + | succ k ih => + rw [trialRun_succ] + simp only [trialStep, trialRun_candidate, trialRun_divisor, Bool.and_eq_true, + decide_eq_true_eq] + rw [ih] + constructor + · rintro ⟨hold, hlast⟩ i hi hproper + rcases Nat.lt_or_eq_of_le (Nat.le_of_lt_succ hi) with hlt | rfl + · exact hold i hlt hproper + · exact hlast hproper + · intro h + exact ⟨fun i hi => h i (by omega), h k (by omega)⟩ + +/-- Trial division, including the explicit lower bound on prime candidates. -/ +@[expose] def isPrime (n : ℕ) : Bool := decide (2 ≤ n) && (trialRun n n).2.2 + +theorem isPrime_eq (n : ℕ) : isPrime n = decide n.Prime := by + apply Bool.eq_iff_iff.mpr + simp only [isPrime, Bool.and_eq_true, decide_eq_true_eq, trialRun_true_iff] + rw [Nat.prime_def_lt'] + constructor + · rintro ⟨hn, h⟩ + refine ⟨hn, fun m hm hmn hdvd => ?_⟩ + have hproper : 2 + (m - 2) < n := by omega + have hi : m - 2 < n := by omega + have heq : 2 + (m - 2) = m := by omega + exact h (m - 2) hi hproper (by simpa only [heq] using Nat.mod_eq_zero_of_dvd hdvd) + · rintro ⟨hn, h⟩ + refine ⟨hn, fun i hi hip hmod => ?_⟩ + exact h (2 + i) (by omega) hip (Nat.dvd_of_mod_eq_zero hmod) + +private theorem trialStep_inSpace : + ComputableInSpace trialStep encTrialState encTrialState + (fun q => (encTrialState q).length) := by + have hn := fst encNat (encPair encNat encBool) + have hrest := snd encNat (encPair encNat encBool) + have hd := fst' hrest + have hgood := snd' hrest + have hlt := budget_natLt hd hn + have hmod := budget_natMod hn hd + have hnz := budget_finite (encC := encBool) not (budget_natIsZero hmod) + have hpass := budget_ite hlt hnz (budget_const true (.refl _)) + have hacc := budget_finite (encC := encBool) (fun p : Bool × Bool => p.1 && p.2) + (budget_prod hgood hpass) + refine (budget_prod hn (budget_prod (budget_natSucc hd) hacc)).of_enc_eq ?_ + intro q + by_cases h : q.2.1 < q.1 <;> simp [trialStep, h] + +/-- Native trial division uses space linear in the candidate's binary encoding. -/ +theorem isPrime_inSpace : + ComputableInSpace isPrime encNat encBool (fun n => (encNat n).length) := by + have hid : ComputableInSpace (fun n : ℕ => n) encNat encNat + (fun n => (encNat n).length) := of_inO (ComputableInO.of_eq (f := id) fun _ => rfl) + have hinit := budget_prod hid + (budget_prod (budget_const (encB := encNat) 2 (.refl _)) + (budget_const (encB := encBool) true (.refl _))) + have hstates (n k : ℕ) (hk : k ≤ n) : + (encTrialState (trialStep^[k] (n, 2, true))).length ≤ 16 * ((encNat n).length + 1) := by + change (encTrialState (trialRun n k)).length ≤ _ + have hd := length_encNat_mono (show 2 + k ≤ n + 2 by omega) + have hsum := length_encNat_add_le_add n 2 + have htwo : (encNat 2).length = 2 := rfl + have hb : (encBool (trialRun n k).2.2).length = 1 := by simp + simp only [encTrialState, length_encPair, trialRun_candidate, trialRun_divisor] at * + omega + have hrun := budget_iterate hid hinit trialStep_inSpace hstates + (.of_le_linear 16 16 fun _ => le_rfl) + have htrial := snd' (snd' hrun) + have hlo : ComputableInSpace (fun n : ℕ => decide (2 ≤ n)) encNat encBool + (fun n => (encNat n).length) := + (budget_finite (encC := encBool) not + (budget_natLt hid (budget_const (encB := encNat) 2 (.refl _)))).of_enc_eq + fun n => by + by_cases hn : n < 2 + · have htwo : ¬2 ≤ n := by omega + simp [hn, htwo] + · have htwo : 2 ≤ n := by omega + simp [hn, htwo] + exact (budget_finite (encC := encBool) (fun p : Bool × Bool => p.1 && p.2) + (budget_prod hlo htrial)).of_enc_eq fun _ => rfl + +/-- Candidate and the first prime found, with zero standing for no answer yet. -/ +abbrev SearchState := ℕ × ℕ + +/-- Binary candidate cursor and retained first-prime answer. -/ +abbrev encSearchState : SearchState ↪ List Bool := encPair encNat encNat + +/-- Move to the next candidate, retaining the first prime once it has been found. -/ +@[expose] def searchStep (q : SearchState) : SearchState := + (q.1 + 1, if q.2 = 0 then if isPrime q.1 then q.1 else 0 else q.2) + +/-- Search a bounded number of candidates, without materializing their list. -/ +@[expose] def boundedFirstPrime (lower bound : ℕ) : ℕ := + (searchStep^[bound + 1] (lower, 0)).2 + +private theorem searchStep_found (q p k : ℕ) (hp : p ≠ 0) : + searchStep^[k] (q, p) = (q + k, p) := by + induction k with + | zero => simp + | succ k ih => + rw [Function.iterate_succ_apply', ih] + simp only [searchStep, hp, ↓reduceIte, Prod.mk.injEq, and_true] + omega + +private theorem searchStep_before (lower p : ℕ) (hlo : lower ≤ p) + (hmin : ∀ q, lower ≤ q → q < p → ¬q.Prime) (j : ℕ) (hj : j ≤ p - lower) : + searchStep^[j] (lower, 0) = (lower + j, 0) := by + induction j with + | zero => simp + | succ j ih => + rw [Function.iterate_succ_apply', ih (by omega)] + have hnot := hmin (lower + j) (by omega) (by omega) + simp only [searchStep, isPrime_eq, hnot, decide_false, Bool.false_eq_true, + ↓reduceIte, Prod.mk.injEq, and_true] + omega + +/-- A bounded search returns the least prime at or above its lower bound, provided the bound +covers that prime. The search uses `bound + 1` candidates starting at `lower`. -/ +theorem boundedFirstPrime_eq (lower bound p : ℕ) (hlo : lower ≤ p) (hbound : p ≤ bound) + (hp : p.Prime) (hmin : ∀ q, lower ≤ q → q < p → ¬q.Prime) : + boundedFirstPrime lower bound = p := by + have hbefore := searchStep_before lower p hlo hmin (p - lower) le_rfl + have hat : searchStep^[p - lower + 1] (lower, 0) = (p + 1, p) := by + rw [Function.iterate_succ_apply', hbefore] + have heq : lower + (p - lower) = p := by omega + simp only [heq, searchStep, isPrime_eq, hp, decide_true, ↓reduceIte] + have hclock : bound + 1 = (bound - (p - lower)) + (p - lower + 1) := by omega + unfold boundedFirstPrime + rw [hclock, Function.iterate_add_apply, hat, + searchStep_found (p + 1) p _ hp.ne_zero] + +/-- The bounded implementation agrees with `Nat.find` rather than merely returning some prime. -/ +theorem boundedFirstPrime_eq_find (lower bound : ℕ) + (h : ∃ p, lower ≤ p ∧ p.Prime) (hbound : Nat.find h ≤ bound) : + boundedFirstPrime lower bound = Nat.find h := by + have hp := Nat.find_spec h + apply boundedFirstPrime_eq lower bound (Nat.find h) hp.1 hbound hp.2 + intro q hlo hlt hprime + have hmin := Nat.find_min' h ⟨hlo, hprime⟩ + omega + +private theorem searchStep_inSpace : + ComputableInSpace searchStep encSearchState encSearchState + (fun q => (encSearchState q).length) := by + have hq := fst encNat encNat + have hfound := snd encNat encNat + have hz := budget_natIsZero hfound + have hp := budget_comp_linear isPrime_inSpace hq + have hanswer := budget_ite hz + (budget_ite hp hq (budget_const (encB := encNat) 0 (.refl _))) hfound + refine (budget_prod (budget_natSucc hq) hanswer).of_enc_eq ?_ + intro q + by_cases hz : q.2 = 0 <;> simp [searchStep, hz] + +private theorem searchStep_bounds (lower j : ℕ) : + (searchStep^[j] (lower, 0)).1 = lower + j ∧ + (searchStep^[j] (lower, 0)).2 ≤ lower + j := by + induction j with + | zero => simp + | succ j ih => + rw [Function.iterate_succ_apply'] + obtain ⟨hq, hf⟩ := ih + constructor + · change (searchStep^[j] (lower, 0)).1 + 1 = lower + (j + 1) + omega + · change (if (searchStep^[j] (lower, 0)).2 = 0 then + if isPrime (searchStep^[j] (lower, 0)).1 then (searchStep^[j] (lower, 0)).1 else 0 + else (searchStep^[j] (lower, 0)).2) ≤ lower + (j + 1) + split_ifs <;> omega + +/-- Both search counters are binary and the primality test is uniform in the candidate. -/ +theorem boundedFirstPrime_inSpace : + ComputableInSpace (fun q : ℕ × ℕ => boundedFirstPrime q.1 q.2) + (encPair encNat encNat) encNat (fun q => (encPair encNat encNat q).length) := by + have hlo := fst encNat encNat + have hb := snd encNat encNat + have hn := budget_natSucc hb + have hinit := budget_prod hlo (budget_const (encB := encNat) 0 (.refl _)) + have hstates (q : ℕ × ℕ) (k : ℕ) (hk : k ≤ q.2 + 1) : + (encSearchState (searchStep^[k] (q.1, 0))).length ≤ + 16 * ((encPair encNat encNat q).length + 1) := by + obtain ⟨hcursor, hfound⟩ := searchStep_bounds q.1 k + have hqbound := length_encNat_mono (show q.1 + k ≤ q.1 + q.2 + 1 by omega) + have hfbound := length_encNat_mono hfound + have hsum := length_encNat_add_le_add q.1 q.2 + have hsucc := length_encNat_add_le_add (q.1 + q.2) 1 + have hone : (encNat 1).length = 1 := rfl + simp only [encSearchState, length_encPair, hcursor] at * + omega + have hrun := budget_iterate hn hinit searchStep_inSpace hstates + (.of_le_linear 16 16 fun _ => le_rfl) + exact (snd' hrun).of_enc_eq fun _ => rfl + +end PrimeSearch + +/-- Primality with a runtime binary candidate, certified by actual trial division. -/ +theorem natPrime : ComputableInSpace (fun n : ℕ => decide n.Prime) encNat encBool + (fun n => (encNat n).length) := + PrimeSearch.isPrime_inSpace.of_enc_eq fun n => by rw [PrimeSearch.isPrime_eq] + +end Turing.MultiTapeTM.ComputableInSpace diff --git a/Complexity/Computability/TreeEval/Ary/Packed/ParametersSpace.lean b/Complexity/Computability/TreeEval/Ary/Packed/ParametersSpace.lean new file mode 100644 index 0000000000..c98f71f4b5 --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/ParametersSpace.lean @@ -0,0 +1,119 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.PackingParameters +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatPrime +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatPow +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Constructible + +/-! +# Uniform native initialization of packed field parameters + +Both fan-in and value width are runtime binary naturals. Initialization computes their chunk +width, ceiling chunk count, alphabet size and the exact least sufficient prime used by the +packed algebra. Primality uses a divisor-counter loop; the bounded first-prime search stores +only its two binary counters. No table of candidates or divisors is constructed. + +The space certificate is linear in the binary encoding of `(d,b)`, including output storage. +Running time is deliberately unrestricted. This initializes the parameters; it does not claim +the remaining implicit evaluator or its transition certificates. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed + +open Turing MultiTapeTM ComputableInSpace + +private theorem natSize_inSpace : + ComputableInSpace Nat.size encNat encNat (fun n => (encNat n).length) := by + have hlen := (of_inO ComputableInO.lengthNat).weaken + (show (fun w : List Bool => w.length + 1) ≼ fun w => (encBits w).length from + .of_le_linear 1 1 fun w => by simp [encBits_apply]) + refine (hlen.of_encIn_eq Nat.bits (fun _ => rfl)).of_enc_eq ?_ + intro n + rw [Nat.size_eq_bits_len] + +/-- Runtime chunk width is computed by measuring the binary product, not by a finite lookup. -/ +theorem chunkWidth_inSpace : + ComputableInSpace (fun q : ℕ × ℕ => chunkWidth q.1 q.2) + (encPair encNat encNat) encNat (fun q => (encPair encNat encNat q).length) := + (budget_comp_linear natSize_inSpace + (budget_natMul (fst encNat encNat) (snd encNat encNat))).of_enc_eq fun _ => rfl + +/-- Ceiling division uses binary addition, predecessor and division at runtime. -/ +theorem chunkCount_inSpace : + ComputableInSpace (fun q : ℕ × ℕ => chunkCount q.1 q.2) + (encPair encNat encNat) encNat (fun q => (encPair encNat encNat q).length) := by + have hb := snd encNat encNat + exact (budget_natDiv (budget_natPred (budget_natAdd hb chunkWidth_inSpace)) + chunkWidth_inSpace).of_enc_eq fun q => by + rw [chunkCount, Nat.ceilDiv_eq_add_pred_div] + +/-- The chunk alphabet size has only `chunkWidth + 1` binary digits. -/ +theorem alphabetSize_inSpace : + ComputableInSpace (fun q : ℕ × ℕ => 2 ^ chunkWidth q.1 q.2) + (encPair encNat encNat) encNat (fun q => (encPair encNat encNat q).length) := by + refine budget_natPow_variable (budget_const (encB := encNat) 2 (.refl _)) + chunkWidth_inSpace ?_ + refine .of_le_linear 8 8 fun q => ?_ + have hm := Nat.size_mul_le q.1 q.2 + have hlen := length_encNat_le_self (chunkWidth q.1 q.2) + have htwo : (encNat 2).length = 2 := rfl + simp only [length_encPair, length_encNat_eq_size, chunkWidth, htwo] at * + omega + +/-- The corrected strict degree threshold is computed from the runtime packed parameters. -/ +theorem gateDegree_inSpace : + ComputableInSpace (fun q : ℕ × ℕ => gateDegree q.1 q.2) + (encPair encNat encNat) encNat (fun q => (encPair encNat encNat q).length) := + (budget_natMul (budget_natMul (fst encNat encNat) chunkCount_inSpace) + (budget_natPred alphabetSize_inSpace)).of_enc_eq fun _ => rfl + +/-- A concrete bounded search computes the same least prime as the algebraic construction. -/ +theorem fieldModulus_eq_boundedFirstPrime (d b : ℕ) : + fieldModulus d b = PrimeSearch.boundedFirstPrime (gateDegree d b + 2) + (2 * (gateDegree d b + 1)) := by + symm + apply PrimeSearch.boundedFirstPrime_eq_find + (gateDegree d b + 2) (2 * (gateDegree d b + 1)) + (Nat.exists_infinite_primes (gateDegree d b + 2)) + exact fieldModulus_le d b + +/-- Native initialization finds the exact selected prime in binary-input-linear space. +The hidden constant is uniform in both runtime parameters. -/ +theorem fieldModulus_inSpace : + ComputableInSpace (fun q : ℕ × ℕ => fieldModulus q.1 q.2) + (encPair encNat encNat) encNat (fun q => (encPair encNat encNat q).length) := by + have hsucc := budget_natSucc gateDegree_inSpace + have hlo := budget_natSucc hsucc + have hbound := budget_natAdd hsucc hsucc + refine (budget_comp_linear PrimeSearch.boundedFirstPrime_inSpace + (budget_prod hlo hbound)).of_enc_eq ?_ + intro q + have hboundeq : 2 * (gateDegree q.1 q.2 + 1) = + (gateDegree q.1 q.2 + 1) + (gateDegree q.1 q.2 + 1) := by omega + rw [fieldModulus_eq_boundedFirstPrime, hboundeq] + +/-- Width, chunk count, alphabet size and modulus, all initialized from runtime parameters. -/ +@[expose] def parameters (d b : ℕ) : ℕ × ℕ × ℕ × ℕ := + (chunkWidth d b, chunkCount d b, 2 ^ chunkWidth d b, fieldModulus d b) + +/-- Framed binary encoding of width, chunk count, alphabet size and modulus. -/ +abbrev encParameters : ℕ × ℕ × ℕ × ℕ ↪ List Bool := + encPair encNat (encPair encNat (encPair encNat encNat)) + +/-- All four parameters can be initialized uniformly with their encoded output charged. -/ +theorem parameters_inSpace : + ComputableInSpace (fun q : ℕ × ℕ => parameters q.1 q.2) + (encPair encNat encNat) encParameters (fun q => (encPair encNat encNat q).length) := + budget_prod chunkWidth_inSpace + (budget_prod chunkCount_inSpace (budget_prod alphabetSize_inSpace fieldModulus_inSpace)) + +end Cslib.TreeEval.Ary.Packed diff --git a/ComplexityTests/TreeEvalParameters.lean b/ComplexityTests/TreeEvalParameters.lean new file mode 100644 index 0000000000..1b0182515a --- /dev/null +++ b/ComplexityTests/TreeEvalParameters.lean @@ -0,0 +1,59 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of uniform packed parameter initialization) + +public import Complexity.Computability.TreeEval.Ary.Packed.ParametersSpace + +/-! +# Native packed parameter initialization + +Prime testing includes zero, one, a prime and a prime square. Concrete parameter choices compare +the actual counter search with the least-prime definition used by the packed algebra, including +zero widths and a padded chunk. The native certificate is uniform in both runtime inputs. +-/ + +public section + +namespace ComplexityTests.TreeEvalParameters + +open Turing MultiTapeTM ComputableInSpace Cslib.TreeEval.Ary.Packed + +example : PrimeSearch.isPrime 0 = false := rfl +example : PrimeSearch.isPrime 1 = false := rfl +example : PrimeSearch.isPrime 2 = true := rfl +example : PrimeSearch.isPrime 49 = false := rfl +example : PrimeSearch.isPrime 97 = true := rfl + +example : fieldModulus 1 1 = 3 := by + rw [fieldModulus_eq_boundedFirstPrime] + rfl + +example : fieldModulus 2 3 = 17 := by + rw [fieldModulus_eq_boundedFirstPrime] + rfl + +example : fieldModulus 0 0 = 2 := by + rw [fieldModulus_eq_boundedFirstPrime] + rfl + +example : chunkWidth 2 5 = 4 ∧ chunkCount 2 5 = 2 := by decide + +example : PrimeSearch.boundedFirstPrime 8 11 = 11 := rfl + +-- The second argument bounds counter iterations, not the returned candidate's value. +example : PrimeSearch.boundedFirstPrime 100 1 = 101 := rfl + +example : parameters 0 0 = (0, 0, 1, 2) := by + rw [parameters, fieldModulus_eq_boundedFirstPrime] + rfl + +example : ComputableInSpace (fun q : ℕ × ℕ => parameters q.1 q.2) + (encPair encNat encNat) encParameters (fun q => (encPair encNat encNat q).length) := + parameters_inSpace + +end ComplexityTests.TreeEvalParameters From 39935ed305e66fe0f3fdd2d18f05eea8446c3e62 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 00:10:49 +0000 Subject: [PATCH 10/36] feat(turing): unfold HPV computations into explicit Boolean trees --- Complexity.lean | 4 + .../BlockComputation/EncodedTree.lean | 118 ++++++++++ .../MultiTape/BlockComputation/Encoding.lean | 215 ++++++++++++++++++ .../MultiTape/BlockComputationTree.lean | 205 +++++++++++++++++ .../TreeEval/Ary/Descending.lean | 119 ++++++++++ .../Turing/MultiTape/EncodedBlockTree.lean | 92 ++++++++ 6 files changed, 753 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/EncodedTree.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputationTree.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Descending.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/EncodedBlockTree.lean diff --git a/Complexity.lean b/Complexity.lean index 2cf16c2528..d03b8d7349 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1691,6 +1691,9 @@ public import Complexity.Computability.Machines.Turing.MultiTape.Alternating public import Complexity.Computability.Machines.Turing.MultiTape.Alternating.Game public import Complexity.Computability.Machines.Turing.MultiTape.Alternating.GuessVerify public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.EncodedTree +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputationTree public import Complexity.Computability.Machines.Turing.MultiTape.BlockTrace public import Complexity.Computability.Machines.Turing.MultiTape.Combinators.Append public import Complexity.Computability.Machines.Turing.MultiTape.Combinators.Comp @@ -2308,6 +2311,7 @@ public import Complexity.Computability.RAM.Squaring public import Complexity.Computability.Reduction.ManyOne public import Complexity.Computability.TreeEval.Ary.Basic public import Complexity.Computability.TreeEval.Ary.CookMertz +public import Complexity.Computability.TreeEval.Ary.Descending public import Complexity.Computability.TreeEval.Ary.Iterative public import Complexity.Computability.TreeEval.Ary.IterativeBounds public import Complexity.Computability.TreeEval.Ary.IterativeCorrect diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/EncodedTree.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/EncodedTree.lean new file mode 100644 index 0000000000..2a7a898c56 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/EncodedTree.lean @@ -0,0 +1,118 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputationTree +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding + +/-! +# The explicit Boolean Tree Evaluation instance for an HPV computation + +The fixed-width view codec instantiates the decreasing-dependency unfolding in the existing +`Ary.Tree`. Its root decodes to the true leaving view exactly for the actual movement schedule; +a wrong schedule decodes to FAIL. This construction supplies a concrete Boolean value width, +not an assumed encoding of an arbitrarily large alphabet. + +This is the semantic Tree Evaluation reduction. Native implicit gate/leaf access and the +complete simulator resource certificate remain separate obligations. + +## References + +* [R. Williams, *Simulating Time With Square-Root Space*][Williams2025], arXiv v1, §3.2, + “Finishing”: unfold the decreasing computation into a tree. Here `q` intervals of `b` steps + give height `q`, fan-in `3*k+1`, and the explicit `Encoding.width k s b` Boolean value width. + The aligned three-window model and source padding are library adaptations; this wrapper + does not claim the paper's native time-space simulation theorem. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation + +open Cslib.TreeEval MultiTapeNTM.ChoiceTable + +variable {k q s b : ℕ} {State : Type} [NeZero b] + +/-- The concrete Boolean tree, with one linear-width encoded interval value at every node. -/ +@[expose] noncomputable def encodedTree (machine : MultiTapePTM k Bool State) + (input : List Bool) (b : ℕ) [NeZero b] (σ : Schedule k q) (e : State ≃ Fin s) : + Ary.Tree (k * 3 + 1) (Encoding.width k s b) q := + tree machine input b σ (Encoding.encode e) (Encoding.decode e) q q + +/-- Decoding the root recovers the actual functional computation, including FAIL. -/ +theorem decode_eval_encodedTree (machine : MultiTapePTM k Bool State) (input : List Bool) + (σ : Schedule k q) (e : State ≃ Fin s) : + Encoding.decode e (encodedTree machine input b σ e).eval = value machine input b σ q := by + rw [encodedTree, eval_tree machine input b σ (Encoding.encode e) (Encoding.decode e) + (Encoding.decode_encode e) le_rfl le_rfl, Encoding.decode_encode] + +/-- A successful decoded root validates the complete schedule and has the exact true view. -/ +theorem decode_eval_encodedTree_eq_some_iff (machine : MultiTapePTM k Bool State) + (input : List Bool) (σ : Schedule k q) (e : State ≃ Fin s) (v : View k State b) : + Encoding.decode e (encodedTree machine input b σ e).eval = some v ↔ + actualSchedule machine input b (q := q) = σ ∧ v = trueView machine input b σ q := by + rw [decode_eval_encodedTree] + exact value_final_eq_some_iff machine input b σ v + +/-- A wrong movement guess produces the distinct FAIL code at the root. -/ +theorem decode_eval_encodedTree_eq_none_iff (machine : MultiTapePTM k Bool State) + (input : List Bool) (σ : Schedule k q) (e : State ≃ Fin s) : + Encoding.decode e (encodedTree machine input b σ e).eval = none ↔ + actualSchedule machine input b (q := q) ≠ σ := by + rw [decode_eval_encodedTree] + exact value_final_eq_none_iff machine input b σ + +/-- Only a halted view with exactly the accepting output is accepting. -/ +@[expose] def acceptingView (v : View k State b) : Bool := + v.1.1.isNone && decide (v.1.2.2 = OutputTrack.accept) + +omit [NeZero b] in +private theorem trueView_control (machine : MultiTapePTM k Bool State) (input : List Bool) + (σ : Schedule k q) (n : ℕ) : + (trueView machine input b σ n).1 = + (boundary machine input b (q := q) n).blockControl b := by + cases n with + | zero => + have hh : ∀ head : Option (Fin k), + ((Cfg.init machine.initial input).headPos head : ZMod b) = 0 := + fun head => by rw [Cfg.headPos_init, Int.cast_zero] + simp only [trueView, initialView, boundary, intervalStart_zero, Cfg.blockControl, funext hh] + rfl + | succ n => rfl + +/-- The Boolean root test agrees with native halting/output semantics and validates all labels. -/ +theorem accepting_eval_encodedTree_iff (machine : MultiTapePTM k Bool State) + (input : List Bool) (σ : Schedule k q) (e : State ≃ Fin s) : + (Encoding.decode e (encodedTree machine input b σ e).eval).elim false acceptingView = true ↔ + actualSchedule machine input b (q := q) = σ ∧ + (boundary machine input b (q := q) q).state = none ∧ + (boundary machine input b (q := q) q).output = [true] := by + rw [decode_eval_encodedTree] + cases hv : value machine input b σ q with + | none => + have hwrong := (value_final_eq_none_iff machine input b σ).mp hv + simp [hwrong] + | some v => + have hgood := (value_final_eq_some_iff machine input b σ v).mp hv + obtain ⟨hschedule, rfl⟩ := hgood + simp only [Option.elim_some, acceptingView, trueView_control, Cfg.blockControl, + Bool.and_eq_true, Option.isNone_iff_eq_none, decide_eq_true_eq, + OutputTrack.ofWord_eq_accept_iff, hschedule, true_and] + +/-- Specializing the decoded accepting root to the original deterministic machine. -/ +theorem accepting_eval_encodedTree_ofDeterministic_iff (M : MultiTapeTM k Bool State) + (input : List Bool) (σ : Schedule k q) (e : State ≃ Fin s) : + (Encoding.decode e (encodedTree (ofDeterministic M) input b σ e).eval).elim false + acceptingView = true ↔ + actualSchedule (ofDeterministic M) input b (q := q) = σ ∧ + (M.runFrom (Cfg.init M.q₀ input) (q * b)).state = none ∧ + (M.runFrom (Cfg.init M.q₀ input) (q * b)).output = [true] := by + rw [accepting_eval_encodedTree_iff (b := b) (ofDeterministic M) input σ e, + boundary_ofDeterministic input b M le_rfl] + +end Turing.MultiTapePTM.BlockComputation diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding.lean new file mode 100644 index 0000000000..73d35abd96 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding.lean @@ -0,0 +1,215 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation +public import Complexity.Foundations.Data.BitString.Binary +public import Complexity.Foundations.Data.BitString.Block +public import Complexity.Foundations.Data.Nat.BinarySize + +/-! +# Fixed-width binary block-computation values + +An outer bit distinguishes FAIL from a valid leaving view. The payload stores the finite control +state by a binary rank, each of the `k+1` head residues in `b.size` bits, the output track in two +bits, and every cell of the three work-tape windows in two bits. Window cells use canonical +row-major blocks: tape, window port, residue, then cell bit. No rank of the whole view or unary +encoding of window contents is used. + +The supplied equivalence enumerates only the fixed machine's finite control states. The width +is `1 + (s+1).size + (k+1)*b.size + 2 + 6*k*b`. The positive block-width assumption is essential: +`ZMod 0` is infinite. Unused state/cell/track words decode to a halted state, blank cell or +`other` output track respectively; a false outer bit always decodes to FAIL. + +These are explicit semantic codecs, not native runtime codec certificates. + +## Main results + +* `Turing.MultiTapePTM.BlockComputation.Encoding.decode_encode`: encoding then decoding is exact. +* `Turing.MultiTapePTM.BlockComputation.Encoding.width_eq`: the precise width calculation. +* `Turing.MultiTapePTM.BlockComputation.Encoding.width_le`: a linear bound for fixed machine data. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.Encoding + +open Cslib Cslib.BitString MultiTapeNTM.ChoiceTable + +variable {k s b : ℕ} {State : Type} + +/-- Width of the three two-bit-cell windows on every work tape. -/ +@[expose] def windowBits (k b : ℕ) : ℕ := k * (3 * (b * 2)) + +/-- Width of the payload before the outer FAIL tag. -/ +@[expose] def payloadBits (k s b : ℕ) : ℕ := + (s + 1).size + ((k + 1) * b.size + (2 + windowBits k b)) + +/-- Width of one optionally failing block-computation value. -/ +@[expose] def width (k s b : ℕ) : ℕ := payloadBits k s b + 1 + +theorem width_pos (k s b : ℕ) : 0 < width k s b := Nat.succ_pos _ + +/-- Only the optional finite control state is numbered, independently of the windows. -/ +@[expose] def stateIndex (e : State ≃ Fin s) : Option State ≃ Fin (s + 1) := + (Equiv.optionCongr e).trans (finSuccEquiv s).symm + +/-- Fixed-width binary code of the optional control state. -/ +@[expose] def encodeState (e : State ≃ Fin s) (state : Option State) : + BitString (s + 1).size := + (equivFin (s + 1).size).symm + ⟨stateIndex e state, (stateIndex e state).isLt.trans (Nat.lt_size_self (s + 1))⟩ + +/-- An unused state rank represents the halted control state. -/ +@[expose] def decodeState (e : State ≃ Fin s) (bits : BitString (s + 1).size) : Option State := + let rank := equivFin (s + 1).size bits + if h : rank.val < s + 1 then (stateIndex e).symm ⟨rank.val, h⟩ else none + +@[simp] theorem decodeState_encodeState (e : State ≃ Fin s) (state : Option State) : + decodeState e (encodeState e state) = state := by + simp only [decodeState, encodeState, Equiv.apply_symm_apply] + rw [dite_eq_left (stateIndex e state).isLt] + exact (stateIndex e).symm_apply_apply state + +variable [NeZero b] + +/-- The binary value of a head residue, padded to the runtime block-width bit length. -/ +@[expose] def encodeResidue (r : ZMod b) : BitString b.size := + (equivFin b.size).symm ⟨r.val, (ZMod.val_lt r).trans (Nat.lt_size_self b)⟩ + +/-- Read the binary value and reduce modulo the positive block width. -/ +@[expose] def decodeResidue (bits : BitString b.size) : ZMod b := + (equivFin b.size bits).val + +@[simp] theorem decodeResidue_encodeResidue (r : ZMod b) : + decodeResidue (encodeResidue r) = r := by + simp only [decodeResidue, encodeResidue, Equiv.apply_symm_apply, ZMod.natCast_zmod_val] + +/-- Residues in canonical `Option (Fin k)` order, with the input head included. -/ +@[expose] def encodeHeads (heads : Option (Fin k) → ZMod b) : + BitString ((k + 1) * b.size) := + blocksEquiv.symm fun i : Fin (k + 1) => encodeResidue (heads (finSuccEquiv k i)) + +/-- Read the fixed-width residues of all work heads and the input head. -/ +@[expose] def decodeHeads (bits : BitString ((k + 1) * b.size)) : + Option (Fin k) → ZMod b := + fun head => decodeResidue (blocksEquiv bits ((finSuccEquiv k).symm head)) + +@[simp] theorem decodeHeads_encodeHeads (heads : Option (Fin k) → ZMod b) : + decodeHeads (encodeHeads heads) = heads := by + funext head + simp only [decodeHeads, encodeHeads, Equiv.apply_symm_apply, decodeResidue_encodeResidue] + +/-- Two bits separate blank, written zero and written one. The fourth word is unused. -/ +@[expose] def encodeCell : Option Bool → BitString 2 + | none => ![false, false] + | some bit => ![true, bit] + +/-- Decode a cell tag, treating both words with a false first bit as blank. -/ +@[expose] def decodeCell (bits : BitString 2) : Option Bool := + if bits 0 then some (bits 1) else none + +@[simp] theorem decodeCell_encodeCell (cell : Option Bool) : + decodeCell (encodeCell cell) = cell := by + cases cell <;> rfl + +/-- The source output track has three cases: empty, exactly `[true]`, and other. -/ +@[expose] def encodeTrack : OutputTrack → BitString 2 + | .empty => ![false, false] + | .accept => ![true, false] + | .other => ![false, true] + +/-- Decode the output summary, assigning the unused word to `other`. -/ +@[expose] def decodeTrack (bits : BitString 2) : OutputTrack := + if bits 1 then .other else if bits 0 then .accept else .empty + +@[simp] theorem decodeTrack_encodeTrack (track : OutputTrack) : + decodeTrack (encodeTrack track) = track := by + cases track <;> rfl + +/-- The explicit row-major two-bit encoding of all three windows on all work tapes. -/ +@[expose] def encodeWindows (windows : Fin k → Fin 3 → ZMod b → Option Bool) : + BitString (windowBits k b) := + blocksEquiv.symm fun tape : Fin k => + blocksEquiv.symm fun port : Fin 3 => + blocksEquiv.symm fun residue : Fin b => + encodeCell (windows tape port (residue.val : ZMod b)) + +/-- Read all window cells in the same tape/port/residue order as the encoder. -/ +@[expose] def decodeWindows (bits : BitString (windowBits k b)) : + Fin k → Fin 3 → ZMod b → Option Bool := + fun tape port residue => + decodeCell (blocksEquiv (blocksEquiv (blocksEquiv bits tape) port) + ⟨residue.val, ZMod.val_lt residue⟩) + +@[simp] theorem decodeWindows_encodeWindows + (windows : Fin k → Fin 3 → ZMod b → Option Bool) : + decodeWindows (encodeWindows windows) = windows := by + funext tape port residue + simp only [decodeWindows, encodeWindows, Equiv.apply_symm_apply, + decodeCell_encodeCell, ZMod.natCast_zmod_val] + +/-- Encode the control record and all window cells with fixed field boundaries. -/ +@[expose] def encodePayload (e : State ≃ Fin s) (view : View k State b) : + BitString (payloadBits k s b) := + Fin.append (encodeState e view.1.1) + (Fin.append (encodeHeads view.1.2.1) + (Fin.append (encodeTrack view.1.2.2) (encodeWindows view.2))) + +/-- Read the control fields and window cells at their fixed field boundaries. -/ +@[expose] def decodePayload (e : State ≃ Fin s) (bits : BitString (payloadBits k s b)) : + View k State b := + ((decodeState e (fstBits bits), decodeHeads (fstBits (sndBits bits)), + decodeTrack (fstBits (sndBits (sndBits bits)))), + decodeWindows (sndBits (sndBits (sndBits bits)))) + +@[simp] theorem decodePayload_encodePayload (e : State ≃ Fin s) (view : View k State b) : + decodePayload e (encodePayload e view) = view := by + rcases view with ⟨⟨state, heads, track⟩, windows⟩ + simp only [decodePayload, encodePayload, fstBits_append, sndBits_append, + decodeState_encodeState, decodeHeads_encodeHeads, decodeTrack_encodeTrack, + decodeWindows_encodeWindows] + +/-- A false tag is FAIL; a true tag is followed by the explicit payload. -/ +@[expose] def encode (e : State ≃ Fin s) : Option (View k State b) → BitString (width k s b) + | none => Fin.cons false (fun _ => false) + | some view => Fin.cons true (encodePayload e view) + +/-- Read the outer FAIL bit before decoding a valid-view payload. -/ +@[expose] def decode (e : State ≃ Fin s) (bits : BitString (width k s b)) : + Option (View k State b) := + if bits ⟨0, width_pos k s b⟩ then some (decodePayload e (Fin.tail bits)) else none + +/-- Every genuine view, including FAIL, round-trips without identifying blank and written zero. -/ +@[simp] theorem decode_encode (e : State ≃ Fin s) (value : Option (View k State b)) : + decode e (encode e value) = value := by + cases value with + | none => rfl + | some view => + change some (decodePayload e (encodePayload e view)) = some view + rw [decodePayload_encodePayload] + +/-- The explicit codec is injective; no enumeration of all possible views is involved. -/ +theorem encode_injective (e : State ≃ Fin s) : Function.Injective (encode (k := k) (b := b) e) := + Function.LeftInverse.injective (decode_encode e) + +/-- The exact width charges all head residues, every cell, output track and FAIL. -/ +theorem width_eq (k s b : ℕ) : + width k s b = 1 + (s + 1).size + (k + 1) * b.size + 2 + 6 * k * b := by + unfold width payloadBits windowBits + ring + +/-- For fixed control and work-tape counts the full payload is linear in the block width. -/ +theorem width_le (k s b : ℕ) : + width k s b ≤ (7 * k + 1) * b + (s + 1).size + 3 := by + have hsize : b.size ≤ b := Nat.size_le.mpr (Nat.lt_two_pow_self (n := b)) + have hmul := Nat.mul_le_mul_left (k + 1) hsize + rw [width_eq] + nlinarith + +end Turing.MultiTapePTM.BlockComputation.Encoding diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputationTree.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputationTree.lean new file mode 100644 index 0000000000..c0bf44f738 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputationTree.lean @@ -0,0 +1,205 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation +public import Complexity.Computability.TreeEval.Ary.Descending + +/-! +# HPV block computation as an implicit Tree Evaluation instance + +Each interval has one control dependency and three window dependencies per work tape. Their +indices strictly decrease. The existing `Ary.Tree` unfolds those dependencies, with constant +source gates padding shorter paths. Its value is exactly the encoded functional block value. + +The supplied codec must decode every encoded value correctly. A guard maps every word outside +its image to the distinct FAIL value, so the gate is defined on all bit strings. Codec size, +native gate computation, input access and space certification are separate obligations. + +## Main results + +* `Turing.MultiTapePTM.BlockComputation.eval_tree`: the unfolded tree preserves the coded value. +* `Turing.MultiTapePTM.BlockComputation.nodeAt_tree`: every path has the implicit label. +* `Turing.MultiTapePTM.BlockComputation.eval_tree_final_eq_some_iff`: success validates the + complete movement schedule. +* `Turing.MultiTapePTM.BlockComputation.eval_tree_final_eq_fail_iff`: wrong guesses return FAIL. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation + +open Cslib.TreeEval + +variable {k q b ℓ : ℕ} {State : Type} + +/-- A checked decoder returns FAIL rather than treating an invalid word as a valid interval view. -/ +@[expose] def decodeChecked (encode : Option (View k State b) → Fin ℓ → Bool) + (decode : (Fin ℓ → Bool) → Option (View k State b)) (w : Fin ℓ → Bool) : + Option (View k State b) := + if ∀ i, encode (decode w) i = w i then decode w else none + +/-- Valid codewords are unaffected by the validity guard. -/ +theorem decodeChecked_encode (encode : Option (View k State b) → Fin ℓ → Bool) + (decode : (Fin ℓ → Bool) → Option (View k State b)) + (hdecode : Function.LeftInverse decode encode) (v : Option (View k State b)) : + decodeChecked encode decode (encode v) = v := by + simp [decodeChecked, hdecode v] + +/-- Every word outside the encoding's image is decoded as FAIL. -/ +theorem decodeChecked_invalid (encode : Option (View k State b) → Fin ℓ → Bool) + (decode : (Fin ℓ → Bool) → Option (View k State b)) (w : Fin ℓ → Bool) + (hw : ∀ v, encode v ≠ w) : decodeChecked encode decode w = none := by + have hnot : ¬ ∀ i, encode (decode w) i = w i := + fun h => hw (decode w) (funext h) + simp [decodeChecked, hnot] + +variable (machine : MultiTapePTM k Bool State) (input : List Bool) (b : ℕ) + (σ : Schedule k q) + +/-- Child zero is the preceding interval. The remaining children are the last window providers, +numbered by work tape and window entry; missing providers are the initial source. -/ +@[expose] def child : ℕ → Fin (k * 3 + 1) → ℕ + | 0, _ => 0 + | n + 1, j => + if hi : n < q then + Fin.cases n (fun p => + let hd := finProdFinEquiv.symm p + match lastBefore (InWindow σ hd.1 (portBlock σ ⟨n, hi⟩ hd.1 hd.2)) ⟨n, hi⟩ with + | none => 0 + | some i => i + 1) j + else 0 + +/-- Every dependency of a positive index is strictly earlier, including total out-of-range cases. -/ +theorem child_lt (i : ℕ) (hi : 0 < i) (j : Fin (k * 3 + 1)) : child σ i j < i := by + cases i with + | zero => omega + | succ n => + simp only [child] + split_ifs with hn + · refine Fin.cases ?_ (fun p => ?_) j + · simp only [Fin.cases_zero] + omega + · simp only [Fin.cases_succ] + split + next => omega + next i hp => + have hprev := (lastBefore_eq_some_iff.mp hp).1 + change (i : ℕ) < n at hprev + omega + · omega + +/-- The unencoded interval gate, with a source gate at zero and FAIL beyond the horizon. -/ +@[expose] noncomputable def intervalGate : + ℕ → (Fin (k * 3 + 1) → Option (View k State b)) → Option (View k State b) + | 0, _ => some (initialView machine b) + | n + 1, children => + if hi : n < q then + gate machine input b σ ⟨n, hi⟩ (children 0) + (fun h d => children (Fin.succ (finProdFinEquiv (h, d)))) + else none + +variable (encode : Option (View k State b) → Fin ℓ → Bool) + (decode : (Fin ℓ → Bool) → Option (View k State b)) + +/-- Decode all children, run the local interval gate, and encode its result. -/ +@[expose] noncomputable def codeGate (i : ℕ) : Ary.Gate (k * 3 + 1) ℓ := fun children => + encode (intervalGate machine input b σ i (fun j => decodeChecked encode decode (children j))) + +/-- The complete uniform-height Tree Evaluation instance, unfolded without a new tree model. -/ +@[expose] noncomputable def tree (height root : ℕ) : Ary.Tree (k * 3 + 1) ℓ height := + Ary.Descending.tree (encode (some (initialView machine b))) (child σ) + (codeGate machine input b σ encode decode) height root + +private theorem intervalGate_value {n : ℕ} (hn : n < q) : + intervalGate machine input b σ (n + 1) + (fun j => value machine input b σ (child σ (n + 1) j)) = + value machine input b σ (n + 1) := by + rw [value, dite_eq_left hn] + simp only [intervalGate, child, dite_eq_left hn, Fin.cases_zero, Fin.cases_succ, + Equiv.symm_apply_apply] + congr 2 + funext h d + split + next hfirst => + rw [value] + split + next => rfl + next j hsecond => + have hc := hfirst.symm.trans hsecond + cases hc + next j hfirst => + split + next hsecond => + have hc := hsecond.symm.trans hfirst + cases hc + next j' hsecond => + have heq : j = j' := Option.some.inj (hfirst.symm.trans hsecond) + subst j' + rfl + +variable (hdecode : Function.LeftInverse decode encode) + +include hdecode + +private theorem descending_value_eq {n : ℕ} (hn : n ≤ q) : + Ary.Descending.value (encode (some (initialView machine b))) (child σ) + (codeGate machine input b σ encode decode) (child_lt σ) n = + encode (value machine input b σ n) := by + induction n using Nat.strong_induction_on with + | ind n ih => + cases n with + | zero => rw [Ary.Descending.value, value] + | succ n => + rw [Ary.Descending.value] + have hchildren : + (fun j => decodeChecked encode decode + (Ary.Descending.value (encode (some (initialView machine b))) (child σ) + (codeGate machine input b σ encode decode) (child_lt σ) + (child σ (n + 1) j))) = + fun j => value machine input b σ (child σ (n + 1) j) := by + funext j + have hj := child_lt σ (n + 1) (by omega) j + rw [ih _ hj (by omega), decodeChecked_encode encode decode hdecode] + change encode (intervalGate machine input b σ (n + 1) _) = _ + rw [hchildren, intervalGate_value machine input b σ (by omega)] + +/-- Unfolding an interval computation preserves its encoded value. -/ +theorem eval_tree {height root : ℕ} (hroot : root ≤ q) (hheight : root ≤ height) : + (tree machine input b σ encode decode height root).eval = + encode (value machine input b σ root) := by + rw [tree, Ary.Descending.eval_tree _ _ _ (child_lt σ) hheight] + exact descending_value_eq machine input b σ encode decode hdecode hroot + +omit hdecode in +/-- The implicit node lookup follows dependency indices along the path. -/ +theorem nodeAt_tree (height root : ℕ) (P : List (Fin (k * 3 + 1))) : + (tree machine input b σ encode decode height root).nodeAt P = + Ary.Descending.lookup (encode (some (initialView machine b))) (child σ) + (codeGate machine input b σ encode decode) height root P := + Ary.Descending.nodeAt_tree _ _ _ _ _ _ + +variable [NeZero b] + +/-- The final tree returns the encoded true view exactly for the true complete movement guess. -/ +theorem eval_tree_final_eq_some_iff (v : View k State b) : + (tree machine input b σ encode decode q q).eval = encode (some v) ↔ + actualSchedule machine input b (q := q) = σ ∧ v = trueView machine input b σ q := by + rw [eval_tree machine input b σ encode decode hdecode le_rfl le_rfl] + rw [hdecode.injective.eq_iff] + exact value_final_eq_some_iff machine input b σ v + +/-- A wrong movement guess evaluates to the encoded FAIL value. -/ +theorem eval_tree_final_eq_fail_iff : + (tree machine input b σ encode decode q q).eval = encode none ↔ + actualSchedule machine input b (q := q) ≠ σ := by + rw [eval_tree machine input b σ encode decode hdecode le_rfl le_rfl] + rw [hdecode.injective.eq_iff] + exact value_final_eq_none_iff machine input b σ + +end Turing.MultiTapePTM.BlockComputation diff --git a/Complexity/Computability/TreeEval/Ary/Descending.lean b/Complexity/Computability/TreeEval/Ary/Descending.lean new file mode 100644 index 0000000000..dc2d581eda --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Descending.lean @@ -0,0 +1,119 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Basic + +/-! +# Unfolding computations with decreasing natural-number indices + +A computation has a source value at index zero, a gate at each positive index, and `d` strictly +earlier dependencies. Unfolding those dependencies gives the existing `Ary.Tree`. Constant gates +pad the source to obtain a uniform height. Paths can be resolved by following indices, without +materializing the tree. + +## Main results + +* `Cslib.TreeEval.Ary.Descending.eval_tree`: unfolding preserves the well-founded value. +* `Cslib.TreeEval.Ary.Descending.nodeAt_tree`: path lookup agrees with the implicit lookup. + +These are pure semantic constructions, with no native resource certificate. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Descending + +variable {d ℓ : ℕ} + +/-- A source has only source dependencies; positive indices follow the supplied dependencies. -/ +@[expose] def next (child : ℕ → Fin d → ℕ) (i : ℕ) (j : Fin d) : ℕ := + if i = 0 then 0 else child i j + +/-- A constant gate pads the source; every positive index uses its supplied gate. -/ +@[expose] def paddedGate (source : Fin ℓ → Bool) (gates : ℕ → Gate d ℓ) + (i : ℕ) : Gate d ℓ := + if i = 0 then fun _ => source else gates i + +/-- Evaluate a computation whose positive dependencies have strictly smaller indices. -/ +@[expose] def value (source : Fin ℓ → Bool) (child : ℕ → Fin d → ℕ) + (gates : ℕ → Gate d ℓ) (hchild : ∀ i, 0 < i → ∀ j, child i j < i) : + ℕ → Fin ℓ → Bool + | 0 => source + | i + 1 => gates (i + 1) fun j => value source child gates hchild (child (i + 1) j) +termination_by i => i +decreasing_by exact hchild _ (by omega) _ + +/-- Unfold dependencies to height `h`, padding source nodes with constant gates. -/ +@[expose] def tree (source : Fin ℓ → Bool) (child : ℕ → Fin d → ℕ) + (gates : ℕ → Gate d ℓ) : (h : ℕ) → ℕ → Tree d ℓ h + | 0, _ => .leaf source + | h + 1, i => .node (paddedGate source gates i) + (fun j => tree source child gates h (next child i j)) + +/-- A height at least the starting index suffices to reach every source dependency. -/ +theorem eval_tree (source : Fin ℓ → Bool) (child : ℕ → Fin d → ℕ) + (gates : ℕ → Gate d ℓ) (hchild : ∀ i, 0 < i → ∀ j, child i j < i) + {h i : ℕ} (hi : i ≤ h) : + (tree source child gates h i).eval = value source child gates hchild i := by + induction h generalizing i with + | zero => + have : i = 0 := by omega + subst i + simp only [tree, Tree.eval_leaf, value] + | succ h ih => + cases i with + | zero => simp [tree, paddedGate, value] + | succ i => + simp only [tree, Tree.eval_node, paddedGate, Nat.succ_ne_zero, ite_false, value, + next] + congr 1 + funext j + have hj := hchild (i + 1) (by omega) j + exact ih (by omega) + +/-- Resolve an implicit node index by following the path from the root. -/ +@[expose] def resolve (child : ℕ → Fin d → ℕ) (i : ℕ) (P : List (Fin d)) : ℕ := + P.foldl (next child) i + +@[simp] theorem resolve_nil (child : ℕ → Fin d → ℕ) (i : ℕ) : + resolve child i [] = i := rfl + +theorem resolve_cons (child : ℕ → Fin d → ℕ) (i : ℕ) (j : Fin d) (P : List (Fin d)) : + resolve child i (j :: P) = resolve child (next child i j) P := rfl + +/-- The label at an implicit path, including the uniform source padding and invalid paths. -/ +@[expose] def lookup (source : Fin ℓ → Bool) (child : ℕ → Fin d → ℕ) + (gates : ℕ → Gate d ℓ) (h i : ℕ) (P : List (Fin d)) : Tree.Node d ℓ := + if P.length < h then .gate (paddedGate source gates (resolve child i P)) + else if P.length = h then .leaf source else .none + +private theorem lookup_cons (source : Fin ℓ → Bool) (child : ℕ → Fin d → ℕ) + (gates : ℕ → Gate d ℓ) (h i : ℕ) (j : Fin d) (P : List (Fin d)) : + lookup source child gates (h + 1) i (j :: P) = + lookup source child gates h (next child i j) P := by + simp only [lookup, List.length_cons, Nat.add_lt_add_iff_right, Nat.add_right_cancel_iff, + resolve_cons] + +/-- The unfolded tree is read by resolving just one decreasing index along the path. -/ +theorem nodeAt_tree (source : Fin ℓ → Bool) (child : ℕ → Fin d → ℕ) + (gates : ℕ → Gate d ℓ) (h i : ℕ) (P : List (Fin d)) : + (tree source child gates h i).nodeAt P = lookup source child gates h i P := by + induction h generalizing i P with + | zero => + cases P with + | nil => simp [tree, lookup] + | cons j P => simp [tree, Tree.nodeAt, lookup] + | succ h ih => + cases P with + | nil => simp [tree, lookup] + | cons j P => + rw [tree, Tree.nodeAt_node_cons, lookup_cons] + exact ih _ _ + +end Cslib.TreeEval.Ary.Descending diff --git a/ComplexityTests/Machines/Turing/MultiTape/EncodedBlockTree.lean b/ComplexityTests/Machines/Turing/MultiTape/EncodedBlockTree.lean new file mode 100644 index 0000000000..0f65697be4 --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/EncodedBlockTree.lean @@ -0,0 +1,92 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of explicit Boolean HPV trees) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.EncodedTree +public import ComplexityTests.Machines.Turing.MultiTape.BlockComputation + +/-! +# Explicit Boolean block-computation trees + +The codec separates FAIL, blank and written zero, and a nontrivial view round-trips at the +claimed width. A native halted accepting machine instantiates the complete encoded-tree +acceptance theorem. Source padding and invalid paths are checked independently of that machine. +-/ + +@[expose] public section + +namespace ComplexityTests.EncodedBlockTree + +open Turing MultiTapePTM.BlockComputation Cslib.TreeEval +open MultiTapeNTM.ChoiceTable + +noncomputable def controlEquiv : Unit ≃ Fin 1 := Fintype.equivFin Unit + +/-- The window contains blanks, written zeros and written ones. -/ +def view : View 1 Unit 2 := + ((some (), fun _ => 1, .accept), fun _ port residue => + if port = 0 then none else if residue = 0 then some false else some true) + +example : Encoding.width 1 1 2 = 21 := by + rw [Encoding.width_eq] + decide + +example : Encoding.decode controlEquiv (Encoding.encode controlEquiv (some view)) = some view := + Encoding.decode_encode _ _ + +example : Encoding.decode (k := 1) (b := 2) controlEquiv (Encoding.encode controlEquiv none) = + none := Encoding.decode_encode _ _ + +example : Encoding.encodeCell none ≠ Encoding.encodeCell (some false) := by + intro h + have hd := congrArg Encoding.decodeCell h + simp only [Encoding.decodeCell_encodeCell] at hd + cases hd + +private theorem emitted : + let cfg := BlockComputation.emit.runFrom (Cfg.init () []) 2 + cfg.state = none ∧ cfg.output = [true] := by + have hh : (BlockComputation.emit.step (Cfg.init () [])).state = none := by + rw [MultiTapeTM.step_of_state (q := ()) rfl] + simp [BlockComputation.emit, Action.apply] + have ho : (BlockComputation.emit.step (Cfg.init () [])).output = [true] := by + rw [MultiTapeTM.step_of_state (q := ()) rfl] + simp [BlockComputation.emit, Action.apply] + change (BlockComputation.emit.step (BlockComputation.emit.step (Cfg.init () []))).state = + none ∧ (BlockComputation.emit.step (BlockComputation.emit.step (Cfg.init () []))).output = _ + rw [MultiTapeTM.step_of_halt hh] + exact ⟨hh, ho⟩ + +/-- The actual schedule gives a halted accepting decoded root of an existing Ary.Tree. -/ +example : + (Encoding.decode controlEquiv + (encodedTree BlockComputation.machine [] 1 BlockComputation.actual controlEquiv).eval).elim + false acceptingView = true := by + change (Encoding.decode controlEquiv + (encodedTree (MultiTapePTM.ofDeterministic BlockComputation.emit) [] 1 + BlockComputation.actual controlEquiv).eval).elim false acceptingView = true + rw [accepting_eval_encodedTree_ofDeterministic_iff] + exact ⟨rfl, emitted⟩ + +private def source : Fin 1 → Bool := fun _ => false +private def child (i : ℕ) (_ : Fin 2) : ℕ := i - 1 +private def gates (_ : ℕ) : Ary.Gate 2 1 := fun values _ => !(values 0 0) + +example : (Ary.Descending.tree source child gates 4 2).eval = source := rfl + +-- The computation reaches its source after two edges and pads to height four. +example : (Ary.Descending.tree source child gates 4 2).nodeAt [1, 0] = + .gate (fun _ => source) := rfl + +example : (Ary.Descending.tree source child gates 4 2).nodeAt [1, 0, 1, 0] = + .leaf source := rfl + +example : (Ary.Descending.tree source child gates 4 2).nodeAt [1, 0, 1, 0, 0] = + .none := rfl + +end ComplexityTests.EncodedBlockTree From 5df7eb5c217fbdd19584628c03339074f1fd534e Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 00:12:23 +0000 Subject: [PATCH 11/36] doc(treeeval): correct the Williams unfolding locator --- .../Turing/MultiTape/BlockComputation/EncodedTree.lean | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/EncodedTree.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/EncodedTree.lean index 2a7a898c56..acfa7e8649 100644 --- a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/EncodedTree.lean +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/EncodedTree.lean @@ -24,7 +24,8 @@ complete simulator resource certificate remain separate obligations. ## References * [R. Williams, *Simulating Time With Square-Root Space*][Williams2025], arXiv v1, §3.2, - “Finishing”: unfold the decreasing computation into a tree. Here `q` intervals of `b` steps + “Construct a Tree Evaluation Instance”: unfold the decreasing computation into a tree. + Here `q` intervals of `b` steps give height `q`, fan-in `3*k+1`, and the explicit `Encoding.width k s b` Boolean value width. The aligned three-window model and source padding are library adaptations; this wrapper does not claim the paper's native time-space simulation theorem. From 3acc25bdb0be3edf4358805aa029257331483480 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 00:41:08 +0000 Subject: [PATCH 12/36] feat(tree-eval): certify the uniform raw query evaluator --- Complexity.lean | 6 + .../TreeEval/Ary/Packed/RawController.lean | 425 ++++++++++++ .../Ary/Packed/RawControllerCorrect.lean | 541 +++++++++++++++ .../Ary/Packed/RawControllerEncoding.lean | 218 ++++++ .../Ary/Packed/RawControllerNative.lean | 651 +++++++++++++++++ .../Ary/Packed/RawControllerQuery.lean | 655 ++++++++++++++++++ .../Ary/Packed/RawControllerSpace.lean | 426 ++++++++++++ ComplexityTests/TreeEvalRawController.lean | 134 ++++ 8 files changed, 3056 insertions(+) create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/RawController.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/RawControllerCorrect.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/RawControllerEncoding.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/RawControllerNative.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/RawControllerQuery.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/RawControllerSpace.lean create mode 100644 ComplexityTests/TreeEvalRawController.lean diff --git a/Complexity.lean b/Complexity.lean index d03b8d7349..959d469ccb 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2327,6 +2327,12 @@ public import Complexity.Computability.TreeEval.Ary.Packed.ParametersSpace public import Complexity.Computability.TreeEval.Ary.Packed.QueryGate public import Complexity.Computability.TreeEval.Ary.Packed.QueryGateInit public import Complexity.Computability.TreeEval.Ary.Packed.QueryGateSpace +public import Complexity.Computability.TreeEval.Ary.Packed.RawController +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerCorrect +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerEncoding +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerNative +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerQuery +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerSpace public import Complexity.Computability.TreeEval.Ary.PackingParameters public import Complexity.Computability.TreeEval.Basic public import Complexity.Computability.TreeEval.CookMertz diff --git a/Complexity/Computability/TreeEval/Ary/Packed/RawController.lean b/Complexity/Computability/TreeEval/Ary/Packed/RawController.lean new file mode 100644 index 0000000000..1a9b42fac8 --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/RawController.lean @@ -0,0 +1,425 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Iterative +public import Complexity.Computability.TreeEval.Reader.Arith +public import Mathlib.Logic.Equiv.Fin.Basic + +/-! +# Uniform raw control for packed Cook–Mertz + +Runtime naturals hold the field modulus, fan-in and register width. One flat list holds the +register bank; continuation frames retain only round, child, phase, coefficient and target. +A stored scalar is offset by the modulus, so even a zero residue retains the field's bit width. +The offset disappears when the cell is read in `ZMod p`. + +Unit rounds are numbered by naturals: round `j` uses `j + 1`, and its inverse uses the existing +modular inverse loop. No list of all field units is part of the machine state. Node values and +gate-extension values arrive as explicit replies; the raw controller stores no tree or gate. +-/ + +@[expose] public section + +namespace Cslib.TreeEval.Ary.Packed.RawController + +open Reader + +/-- Runtime modulus, fan-in and register width. -/ +abbrev Params := ℕ × ℕ × ℕ + +/-- Round, child, undo phase, coefficient and target, with binary natural indices. -/ +abbrev Frame := ℕ × ℕ × Bool × ℕ × ℕ + +/-- One global bank, its continuation stack and a call coefficient/target or return. -/ +abbrev State := List ℕ × List Frame × Option (ℕ × ℕ) + +/-- Row-major bank position, matching the existing finite-product equivalence. -/ +def index (r t i : ℕ) : ℕ := i + r * t + +/-- The child register modulo the number of available registers. -/ +def reg (d t j : ℕ) : ℕ := (t + (j + 1)) % (d + 1) + +/-- Read the raw bank as the existing finite field register bank. -/ +def toRegs (p d r : ℕ) (bank : List ℕ) : Ary.State (ZMod p) d r := + fun t i => (bank.getD (index r t i) 0 : ZMod p) + +/-- Canonical padded cells for the existing register bank, flattened once. -/ +noncomputable def ofRegs {p d r : ℕ} (S : Ary.State (ZMod p) d r) : List ℕ := + List.ofFn fun k : Fin ((d + 1) * r) => + let ti := finProdFinEquiv.symm k + p + (S ti.1 ti.2).val + +/-- Scale one register, retaining the global bank and all other cells. -/ +def scaleBank (p r t a : ℕ) (bank : List ℕ) : List ℕ := + bank.mapIdx fun k v => if k / r = t then p + mulMod p a v else v + +/-- Add a supplied vector times a coefficient into one register. -/ +def addBank (p r t c : ℕ) (values bank : List ℕ) : List ℕ := + bank.mapIdx fun k v => if k / r = t then + p + addMod p v (mulMod p c (values.getD (k % r) 0)) else v + +/-- Subtract a supplied gate-extension vector times a coefficient from one register. -/ +def subBank (p r t c : ℕ) (values bank : List ℕ) : List ℕ := + bank.mapIdx fun k v => if k / r = t then + p + addMod p v (negMod p (mulMod p c (values.getD (k % r) 0))) else v + +@[simp] theorem length_ofRegs {p d r : ℕ} (S : Ary.State (ZMod p) d r) : + (ofRegs S).length = (d + 1) * r := by simp [ofRegs] + +@[simp] theorem length_scaleBank (p r t a : ℕ) (bank : List ℕ) : + (scaleBank p r t a bank).length = bank.length := by simp [scaleBank] + +@[simp] theorem length_addBank (p r t c : ℕ) (values bank : List ℕ) : + (addBank p r t c values bank).length = bank.length := by simp [addBank] + +@[simp] theorem length_subBank (p r t c : ℕ) (values bank : List ℕ) : + (subBank p r t c values bank).length = bank.length := by simp [subBank] + +private theorem index_lt {d r : ℕ} (t : Fin (d + 1)) (i : Fin r) : + index r t i < (d + 1) * r := + (finProdFinEquiv (t, i)).isLt + +private theorem index_div {d r : ℕ} (t : Fin (d + 1)) (i : Fin r) : + index r t i / r = t := by + have hr : 0 < r := Nat.pos_of_ne_zero fun h => by simpa [h] using i.isLt + simp [index, Nat.add_mul_div_left _ _ hr, Nat.div_eq_of_lt i.isLt] + +private theorem index_mod {d r : ℕ} (t : Fin (d + 1)) (i : Fin r) : + index r t i % r = i := by + simp [index, Nat.add_mul_mod_self_left, Nat.mod_eq_of_lt i.isLt] + +/-- Reading the canonical flat bank recovers the existing register representation. -/ +theorem toRegs_ofRegs {p d r : ℕ} [NeZero p] (S : Ary.State (ZMod p) d r) : + toRegs p d r (ofRegs S) = S := by + funext t i + have hk := index_lt t i + have hnat : (ofRegs S).getD (index r t i) 0 = p + (S t i).val := by + simp only [ofRegs, List.getD_eq_getElem?_getD, List.getElem?_ofFn, hk, + ↓reduceDIte, Option.getD_some] + have hfin : (⟨index r t i, hk⟩ : Fin ((d + 1) * r)) = finProdFinEquiv (t, i) := rfl + rw [hfin, Equiv.symm_apply_apply] + change ((ofRegs S).getD (index r t i) 0 : ZMod p) = S t i + rw [hnat] + simp + +/-- Raw child-register arithmetic agrees with the existing finite register API. -/ +theorem reg_eq {d : ℕ} (t : Fin (d + 1)) (j : Fin d) : + reg d t j = (Ary.reg t j).val := by + simp [reg, Ary.reg, Fin.val_add] + +/-- Scaling the flat bank is exactly scaling one existing typed register. -/ +theorem toRegs_scaleBank {p d r : ℕ} [Fact p.Prime] (bank : List ℕ) + (hlen : bank.length = (d + 1) * r) (t : Fin (d + 1)) (a : ℕ) : + toRegs p d r (scaleBank p r t a bank) = + Ary.scale t (a : ZMod p) (toRegs p d r bank) := by + funext t' i + have hk : index r t' i < bank.length := hlen ▸ index_lt t' i + have hd := index_div t' i + by_cases ht : t' = t + · subst t' + simp [toRegs, scaleBank, List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hk, + hd, Ary.scale] + · have htn : t'.val ≠ t.val := fun h => ht (Fin.ext h) + simp [toRegs, scaleBank, List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hk, + hd, htn, Ary.scale, ht] + +/-- Adding a returned leaf vector is the existing typed leaf update. -/ +theorem toRegs_addBank {p d r : ℕ} (bank values : List ℕ) + (hlen : bank.length = (d + 1) * r) (t : Fin (d + 1)) (c : ℕ) : + toRegs p d r (addBank p r t c values bank) = + Function.update (toRegs p d r bank) t + (toRegs p d r bank t + (c : ZMod p) • fun i : Fin r => (values.getD i 0 : ZMod p)) := by + funext t' i + have hk : index r t' i < bank.length := hlen ▸ index_lt t' i + have hd := index_div t' i + have hm := index_mod t' i + by_cases ht : t' = t + · subst t' + simp [toRegs, addBank, List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hk, + hd, hm] + · have htn : t'.val ≠ t.val := fun h => ht (Fin.ext h) + simp [toRegs, addBank, List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hk, + hd, htn, ht] + +/-- Subtracting returned packed gate values is the existing typed gate update. -/ +theorem toRegs_subBank {p d r : ℕ} [NeZero p] (bank values : List ℕ) + (hlen : bank.length = (d + 1) * r) (t : Fin (d + 1)) (c : ℕ) : + toRegs p d r (subBank p r t c values bank) = + Function.update (toRegs p d r bank) t + (fun i => toRegs p d r bank t i - (c : ZMod p) * (values.getD i 0 : ZMod p)) := by + funext t' i + have hk : index r t' i < bank.length := hlen ▸ index_lt t' i + have hd := index_div t' i + have hm := index_mod t' i + by_cases ht : t' = t + · subst t' + simp [toRegs, subBank, List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hk, + hd, hm, sub_eq_add_neg] + · have htn : t'.val ≠ t.val := fun h => ht (Fin.ext h) + simp [toRegs, subBank, List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hk, + hd, htn, ht] + +/-- Recover the path from child indices; it is not duplicated in continuation frames. -/ +def path (stack : List Frame) : List ℕ := stack.reverse.map (fun fr => fr.2.1) + +/-- The child-register point is the cyclic bank with its target register omitted. It uses +only two slices, including when the register width or fan-in is zero. -/ +def point (d r t : ℕ) (bank : List ℕ) : List ℕ := + let target := t % (d + 1) + bank.drop ((target + 1) * r) ++ bank.take (target * r) + +/-- Node requests use tag zero; gate-extension requests use tag one and carry one point. +The final natural is the requested output chunk; zero begins a vector-reading subroutine. -/ +abbrev Need := ℕ × List ℕ × List ℕ × ℕ + +/-- Request only the external data needed by the current control step. -/ +def needCore (d r : ℕ) (q : State) : Option Need := + match q.2.2 with + | some _ => some (0, path q.2.1, [], 0) + | none => match q.2.1 with + | [] => none + | fr :: st => + if fr.2.2.1 || decide (fr.2.1 + 1 < d) then none + else some (1, path st, point d r fr.2.2.2.2 q.1, 0) + +/-- A node reply has tag zero for invalid, one for a leaf, or two for a gate. The payload is +one packed value or one gate-extension value, never a table of assignments. -/ +abbrev Reply := ℕ × List ℕ + +/-- Check the padding needed by native register transformations. Malformed states can be +rejected before a transformation expands a short list of zeros into full-width residues. -/ +def validBank (p d r : ℕ) (bank : List ℕ) : Bool := + decide (2 ≤ p) && decide (bank.length = (d + 1) * r) && + bank.all (fun v => decide (p ≤ v)) + +/-- Reject malformed storage before constructing a request point. -/ +def need (p d r : ℕ) (q : State) : Option Need := + if validBank p d r q.1 then needCore d r q else none + +/-- The executable guard states the bank shape and per-cell padding explicitly. -/ +theorem validBank_eq_true (p d r : ℕ) (bank : List ℕ) : + validBank p d r bank = true ↔ + 2 ≤ p ∧ bank.length = (d + 1) * r ∧ ∀ v ∈ bank, p ≤ v := by + simp [validBank, Bool.and_eq_true, List.all_eq_true, and_assoc] + +private theorem padding_mapIdx {p : ℕ} {bank : List ℕ} {f : ℕ → ℕ → ℕ} + (hbank : ∀ v ∈ bank, p ≤ v) (hf : ∀ i v, p ≤ v → p ≤ f i v) : + ∀ v ∈ bank.mapIdx f, p ≤ v := by + intro v hv + obtain ⟨i, hi, rfl⟩ := List.mem_mapIdx.mp hv + exact hf i bank[i] (hbank _ (List.getElem_mem hi)) + +/-- Scaling preserves the encoded-width padding invariant. -/ +theorem validBank_scaleBank (p d r t a : ℕ) (bank : List ℕ) + (hbank : validBank p d r bank = true) : + validBank p d r (scaleBank p r t a bank) = true := by + obtain ⟨hp, hlen, hpad⟩ := (validBank_eq_true p d r bank).mp hbank + apply (validBank_eq_true p d r _).mpr + refine ⟨hp, by simpa only [length_scaleBank] using hlen, ?_⟩ + apply padding_mapIdx hpad + intro i v hv + split_ifs <;> omega + +/-- A returned value preserves the padding even when some supplied coordinates are missing. -/ +theorem validBank_addBank (p d r t c : ℕ) (values bank : List ℕ) + (hbank : validBank p d r bank = true) : + validBank p d r (addBank p r t c values bank) = true := by + obtain ⟨hp, hlen, hpad⟩ := (validBank_eq_true p d r bank).mp hbank + apply (validBank_eq_true p d r _).mpr + refine ⟨hp, by simpa only [length_addBank] using hlen, ?_⟩ + apply padding_mapIdx hpad + intro i v hv + split_ifs <;> omega + +/-- A returned extension preserves the padding through a subtractive update. -/ +theorem validBank_subBank (p d r t c : ℕ) (values bank : List ℕ) + (hbank : validBank p d r bank = true) : + validBank p d r (subBank p r t c values bank) = true := by + obtain ⟨hp, hlen, hpad⟩ := (validBank_eq_true p d r bank).mp hbank + apply (validBank_eq_true p d r _).mpr + refine ⟨hp, by simpa only [length_subBank] using hlen, ?_⟩ + apply padding_mapIdx hpad + intro i v hv + split_ifs <;> omega + +/-- Start one round using the counter's unit, without retaining an enumeration of units. -/ +def beginRound (p d r round c t : ℕ) (bank : List ℕ) (stack : List Frame) : State := + (scaleBank p r (reg d t 0) (round + 1) bank, + (round, 0, false, c, t) :: stack, some (1, reg d t 0)) + +/-- The arithmetic/control step, after any required reply has returned. -/ +def stepCore (p d r : ℕ) (reply : Reply) (q : State) : State := + match q.2.2 with + | some (c, t) => + if reply.1 = 1 then (addBank p r t c reply.2 q.1, q.2.1, none) + else if reply.1 = 2 then + if p - 1 = 0 then (q.1, q.2.1, none) + else beginRound p d r 0 c t q.1 q.2.1 + else (q.1, q.2.1, none) + | none => match q.2.1 with + | [] => q + | (round, child, undo, c, t) :: st => + if undo then + let bank := scaleBank p r (reg d t child) (unitInv p round) q.1 + if child + 1 < d then + (bank, (round, child + 1, true, c, t) :: st, + some (p - 1, reg d t (child + 1))) + else if round + 1 < p - 1 then beginRound p d r (round + 1) c t bank st + else (bank, st, none) + else + if child + 1 < d then + (scaleBank p r (reg d t (child + 1)) (round + 1) q.1, + (round, child + 1, false, c, t) :: st, some (1, reg d t (child + 1))) + else + (subBank p r t c reply.2 q.1, (round, 0, true, c, t) :: st, + some (p - 1, reg d t 0)) + +/-- Reject malformed storage without transforming it. -/ +def step (p d r : ℕ) (reply : Reply) (q : State) : State := + if validBank p d r q.1 then stepCore p d r reply q else q + +/-- Typed continuation frames are represented by their five small control fields. -/ +def ofFrame {p d : ℕ} (fr : Ary.Iter.Frame (ZMod p) d) : Frame := + (fr.round, fr.child.val, fr.undo, fr.coeff.val, fr.target.val) + +/-- The raw mode uses the same return/call convention as the existing controller. -/ +def ofMode {p d : ℕ} (mode : Ary.Iter.Mode (ZMod p) d) : Option (ℕ × ℕ) := + match mode with + | .call c t => some (c.val, t.val) + | .ret => none + +/-- The canonical raw representation has one bank and no saved banks in its frames. -/ +noncomputable def ofCfg {p d r : ℕ} (q : Ary.Iter.Cfg (ZMod p) d r) : State := + (ofRegs q.regs, q.stack.map ofFrame, ofMode q.mode) + +/-- Reading an individual raw frame uses the existing finite index types. -/ +def toFrame {p d : ℕ} (hd : 0 < d) (fr : Frame) : Ary.Iter.Frame (ZMod p) d := + ⟨fr.1, ⟨fr.2.1 % d, Nat.mod_lt _ hd⟩, fr.2.2.1, (fr.2.2.2.1 : ZMod p), + ⟨fr.2.2.2.2 % (d + 1), Nat.mod_lt _ (Nat.succ_pos d)⟩⟩ + +/-- Decode only the control fields and scalar values; no tree or table is reconstructed. -/ +def toMode (p d : ℕ) (mode : Option (ℕ × ℕ)) : Ary.Iter.Mode (ZMod p) d := + match mode with + | some (c, t) => .call (c : ZMod p) ⟨t % (d + 1), Nat.mod_lt _ (Nat.succ_pos d)⟩ + | none => .ret + +/-- Read a raw configuration in the existing iterative semantics. -/ +def toCfg (p d r : ℕ) (hd : 0 < d) (q : State) : Ary.Iter.Cfg (ZMod p) d r := + ⟨toRegs p d r q.1, q.2.1.map (toFrame hd), toMode p d q.2.2⟩ + +@[simp] theorem toFrame_ofFrame {p d : ℕ} [NeZero p] (hd : 0 < d) + (fr : Ary.Iter.Frame (ZMod p) d) : toFrame hd (ofFrame fr) = fr := by + cases fr with + | mk round child undo coeff target => + simp only [toFrame, ofFrame, Nat.mod_eq_of_lt child.isLt, + Nat.mod_eq_of_lt target.isLt, ZMod.natCast_zmod_val] + +@[simp] theorem toMode_ofMode {p d : ℕ} [NeZero p] (mode : Ary.Iter.Mode (ZMod p) d) : + toMode p d (ofMode mode) = mode := by + cases mode with + | ret => rfl + | call c t => + simp only [toMode, ofMode, Nat.mod_eq_of_lt t.isLt, ZMod.natCast_zmod_val] + +/-- The raw representation faithfully exposes the existing controller's configurations. -/ +theorem toCfg_ofCfg {p d r : ℕ} [NeZero p] (hd : 0 < d) + (q : Ary.Iter.Cfg (ZMod p) d r) : toCfg p d r hd (ofCfg q) = q := by + cases q + simp [toCfg, ofCfg, toRegs_ofRegs, List.map_map, Function.comp_def] + +/-- The stack-derived request path is the existing controller's path. -/ +theorem path_ofFrame {p d : ℕ} (st : List (Ary.Iter.Frame (ZMod p) d)) : + path (st.map ofFrame) = (Ary.Iter.path st).map Fin.val := by + simp [path, Ary.Iter.path, List.map_reverse, List.map_map, ofFrame] + +/-- Canonical field banks satisfy the executable padding guard. -/ +theorem validBank_ofRegs {p d r : ℕ} (hp : 2 ≤ p) + (S : Ary.State (ZMod p) d r) : validBank p d r (ofRegs S) = true := by + simp only [validBank, hp, decide_true, length_ofRegs, Bool.true_and] + apply List.all_eq_true.mpr + intro v hv + obtain ⟨k, rfl⟩ := List.mem_ofFn.mp hv + simp + +/-- A returned leaf changes only its target bank and returns to its continuation. -/ +theorem step_call_leaf (p d r c t : ℕ) (values bank : List ℕ) (stack : List Frame) + (hvalid : validBank p d r bank = true) : + step p d r (1, values) (bank, stack, some (c, t)) = + (addBank p r t c values bank, stack, none) := by + simp [step, hvalid, stepCore] + +/-- The concrete raw leaf transition implements the existing iterative controller's leaf step. -/ +theorem toCfg_step_call_leaf {p d r b : ℕ} [Fact p.Prime] (hd : 0 < d) + (e : (Fin b → Bool) → Fin r → ZMod p) + (E : Gate d b → (Fin d → Fin r → ZMod p) → Fin r → ZMod p) + (us : List (ZMod p)ˣ) (read : List (Fin d) → Tree.Node d b) + (S : Ary.State (ZMod p) d r) (st : List (Ary.Iter.Frame (ZMod p) d)) + (c : ZMod p) (t : Fin (d + 1)) (v : Fin b → Bool) (values : List ℕ) + (hread : read (Ary.Iter.path st) = .leaf v) + (hvalues : ∀ i : Fin r, (values.getD i 0 : ZMod p) = e v i) : + toCfg p d r hd + (step p d r (1, values) (ofCfg (⟨S, st, .call c t⟩ : Ary.Iter.Cfg (ZMod p) d r))) = + Ary.Iter.step hd e E us read ⟨S, st, .call c t⟩ := by + rw [Ary.Iter.step_call_leaf hd e E us read S st c t v hread] + change toCfg p d r hd + (step p d r (1, values) (ofRegs S, st.map ofFrame, some (c.val, t.val))) = _ + rw [step_call_leaf p d r c.val t.val values (ofRegs S) (st.map ofFrame) + (validBank_ofRegs (Fact.out : p.Prime).two_le S)] + have hregs := toRegs_addBank (p := p) (d := d) (r := r) + (ofRegs S) values (length_ofRegs S) t c.val + rw [toRegs_ofRegs, ZMod.natCast_zmod_val] at hregs + have hvec : (fun i : Fin r => (values.getD i 0 : ZMod p)) = e v := funext hvalues + rw [hvec] at hregs + simp only [toCfg, toMode, List.map_map] + rw [hregs] + congr 1 + simp only [Function.comp_def, toFrame_ofFrame, List.map_id_fun', id_eq] + +/-- A counter-numbered raw round starts the same typed round when its unit is the supplied +list's unit. The list belongs only to the comparison theorem, not to the raw configuration. -/ +theorem toCfg_beginRound {p d r : ℕ} [Fact p.Prime] (hd : 0 < d) + (us : List (ZMod p)ˣ) (round : ℕ) (c : ZMod p) (t : Fin (d + 1)) + (bank : List ℕ) (hlen : bank.length = (d + 1) * r) + (st : List (Ary.Iter.Frame (ZMod p) d)) + (hunit : ((round + 1 : ℕ) : ZMod p) = (Ary.Iter.unitAt us round : ZMod p)) : + toCfg p d r hd (beginRound p d r round c.val t.val bank (st.map ofFrame)) = + Ary.Iter.beginRound hd us round c t (toRegs p d r bank) st := by + let child : Fin d := ⟨0, hd⟩ + have hreg : reg d t.val 0 = (Ary.reg t child).val := reg_eq t child + have hscale := toRegs_scaleBank (p := p) (d := d) (r := r) + bank hlen (Ary.reg t child) (round + 1) + rw [hunit] at hscale + have hstack : (st.map ofFrame).map (toFrame hd) = st := by + simp only [List.map_map, Function.comp_def, toFrame_ofFrame, + List.map_id_fun', id_eq] + simp only [beginRound, toCfg, List.map_cons, hstack] + simp only [toFrame, toMode, Nat.zero_mod, Nat.mod_eq_of_lt t.isLt, + ZMod.natCast_zmod_val, Nat.cast_one] + rw [hreg, hscale] + simp only [Ary.Iter.beginRound, child, Nat.mod_eq_of_lt (Ary.reg t child).isLt] + +/-- The comparison enumeration's unit is obtained directly from its round index. -/ +theorem unitAt_zmodUnits {p : ℕ} [Fact p.Prime] {j : ℕ} (hj : j < p - 1) : + Ary.Iter.unitAt ((List.range (p - 1)).map (TreeEval.Iter.zmodUnit p)) j = + TreeEval.Iter.zmodUnit p j := by + simp [Ary.Iter.unitAt, List.getD_eq_getElem?_getD, hj] + +/-- In the canonical prime-field enumeration, the raw round counter implements the existing +round initializer without ever constructing the comparison enumeration at runtime. -/ +theorem toCfg_beginRound_zmodUnits {p d r : ℕ} [Fact p.Prime] (hd : 0 < d) + (round : ℕ) (hround : round < p - 1) (c : ZMod p) (t : Fin (d + 1)) + (bank : List ℕ) (hlen : bank.length = (d + 1) * r) + (st : List (Ary.Iter.Frame (ZMod p) d)) : + toCfg p d r hd (beginRound p d r round c.val t.val bank (st.map ofFrame)) = + Ary.Iter.beginRound hd ((List.range (p - 1)).map (TreeEval.Iter.zmodUnit p)) + round c t (toRegs p d r bank) st := by + apply toCfg_beginRound hd _ round c t bank hlen st + rw [unitAt_zmodUnits hround, TreeEval.Iter.zmodUnit_val hround] + +end Cslib.TreeEval.Ary.Packed.RawController diff --git a/Complexity/Computability/TreeEval/Ary/Packed/RawControllerCorrect.lean b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerCorrect.lean new file mode 100644 index 0000000000..f581f011ef --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerCorrect.lean @@ -0,0 +1,541 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed.RawController +import Complexity.Computability.TreeEval.Ary.IterativeCorrect +import Complexity.Computability.TreeEval.Ary.IterativeBounds + +/-! +# Raw-controller transition correspondence + +The runtime controller is compared with the existing iterative Cook–Mertz semantics. The +comparison enumeration appears only in theorem statements. An external reply must describe the +node currently requested, or the packed gate extension at the current child registers. These +explicit obligations are discharged by the actual node and gate-reading subroutines; no gate +function or tree is stored in a raw controller state. + +## Main definitions + +* `ReplyCorrect`: the external node or gate-extension reply needed by one transition. +* `Canonical`: exact padded bank cells and finite control representatives. + +## Main results + +* `step_ofCfg`: one raw transition preserves the existing configuration representation exactly. +* `iterate_step_ofCfg`: consecutive raw transitions realize the existing iterative controller. +* `exists_iterate_step_root`: the raw kernel returns the existing complete masking procedure's + result, subject to the explicit external-reply contract. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed.RawController + +variable {p d r b : ℕ} [Fact p.Prime] + +/-- The canonical enumeration used only by the comparison semantics. -/ +@[expose] noncomputable def units (p : ℕ) [Fact p.Prime] : List (ZMod p)ˣ := + (List.range (p - 1)).map (TreeEval.Iter.zmodUnit p) + +/-- A gate reply's field vector; invalid or leaf labels require the zero update. -/ +@[expose] def expectedGate (E : Gate d b → (Fin d → Fin r → ZMod p) → Fin r → ZMod p) + (read : List (Fin d) → Tree.Node d b) (S : Ary.State (ZMod p) d r) + (st : List (Ary.Iter.Frame (ZMod p) d)) (t : Fin (d + 1)) : Fin r → ZMod p := + match read (Ary.Iter.path st) with + | .gate f => E f (fun j => S (Ary.reg t j)) + | _ => 0 + +/-- The reply contract for the exact lookup or gate update requested by a typed state. -/ +@[expose] def ReplyCorrect (e : (Fin b → Bool) → Fin r → ZMod p) + (E : Gate d b → (Fin d → Fin r → ZMod p) → Fin r → ZMod p) + (read : List (Fin d) → Tree.Node d b) (q : Ary.Iter.Cfg (ZMod p) d r) + (reply : Reply) : Prop := + match q.mode with + | .call _ _ => match read (Ary.Iter.path q.stack) with + | .leaf v => reply.1 = 1 ∧ ∀ i : Fin r, (reply.2.getD i 0 : ZMod p) = e v i + | .gate _ => reply.1 = 2 + | .none => reply.1 = 0 + | .ret => match q.stack with + | [] => True + | fr :: st => if fr.undo = false ∧ ¬fr.child.val + 1 < d then + ∀ i : Fin r, (reply.2.getD i 0 : ZMod p) = expectedGate E read q.regs st fr.target i + else True + +private theorem toCfg_ret (hd : 0 < d) (bank : List ℕ) + (st : List (Ary.Iter.Frame (ZMod p) d)) : + toCfg p d r hd (bank, st.map ofFrame, none) = ⟨toRegs p d r bank, st, .ret⟩ := by + simp only [toCfg, toMode, List.map_map, Function.comp_def, toFrame_ofFrame, + List.map_id_fun', id_eq] + +private theorem toCfg_call (hd : 0 < d) (bank : List ℕ) + (st : List (Ary.Iter.Frame (ZMod p) d)) (c : ℕ) (t : Fin (d + 1)) : + toCfg p d r hd (bank, st.map ofFrame, some (c, t.val)) = + ⟨toRegs p d r bank, st, .call (c : ZMod p) t⟩ := by + simp only [toCfg, toMode, List.map_map, Function.comp_def, toFrame_ofFrame, + List.map_id_fun', id_eq, Nat.mod_eq_of_lt t.isLt] + +private theorem cast_negOne : ((p - 1 : ℕ) : ZMod p) = -1 := by + rw [Nat.cast_sub (Fact.out : p.Prime).one_lt.le, ZMod.natCast_self, Nat.cast_one, zero_sub] + +private theorem unitAt_units {j : ℕ} (hj : j < p - 1) : + ((j + 1 : ℕ) : ZMod p) = (Ary.Iter.unitAt (units p) j : ZMod p) := by + rw [units, unitAt_zmodUnits hj, TreeEval.Iter.zmodUnit_val hj] + +private theorem inverse_unitAt_units {j : ℕ} (hj : j < p - 1) : + ((Reader.unitInv p j : ℕ) : ZMod p) = + (((Ary.Iter.unitAt (units p) j)⁻¹ : (ZMod p)ˣ) : ZMod p) := by + rw [units, unitAt_zmodUnits hj] + exact Reader.cast_unitInv_eq hj + +private theorem toRegs_subBank_expected (E : Gate d b → (Fin d → Fin r → ZMod p) → + Fin r → ZMod p) (read : List (Fin d) → Tree.Node d b) + (S : Ary.State (ZMod p) d r) (st : List (Ary.Iter.Frame (ZMod p) d)) + (c : ZMod p) (t : Fin (d + 1)) (values : List ℕ) + (hvalues : ∀ i : Fin r, (values.getD i 0 : ZMod p) = expectedGate E read S st t i) : + toRegs p d r (subBank p r t.val c.val values (ofRegs S)) = + Ary.Iter.updateAt E read (Ary.Iter.path st) c t S := by + rw [toRegs_subBank (ofRegs S) values (length_ofRegs S) t c.val, + toRegs_ofRegs, ZMod.natCast_zmod_val] + unfold Ary.Iter.updateAt + cases hread : read (Ary.Iter.path st) with + | none => + have hv : ∀ i : Fin r, (values.getD i 0 : ZMod p) = 0 := by + simpa only [expectedGate, hread, Pi.zero_apply] using hvalues + have hzero : (fun i : Fin r => S t i - c * (values.getD i 0 : ZMod p)) = S t := by + funext i + rw [hv i, mul_zero, sub_zero] + rw [hzero] + exact Function.update_eq_self t S + | leaf v => + have hv : ∀ i : Fin r, (values.getD i 0 : ZMod p) = 0 := by + simpa only [expectedGate, hread, Pi.zero_apply] using hvalues + have hzero : (fun i : Fin r => S t i - c * (values.getD i 0 : ZMod p)) = S t := by + funext i + rw [hv i, mul_zero, sub_zero] + rw [hzero] + exact Function.update_eq_self t S + | gate f => + simp only [Ary.gateUpdateWith] + congr 1 + funext i + rw [hvalues] + simp only [expectedGate, hread] + +private theorem toRegs_scale_unit (S : Ary.State (ZMod p) d r) + (t : Fin (d + 1)) (j : Fin d) (round : ℕ) (hr : round < p - 1) : + toRegs p d r (scaleBank p r (reg d t j) (round + 1) (ofRegs S)) = + Ary.scale (Ary.reg t j) (Ary.Iter.unitAt (units p) round : ZMod p) S := by + rw [reg_eq, toRegs_scaleBank (ofRegs S) (length_ofRegs S), + toRegs_ofRegs, unitAt_units hr] + +private theorem toRegs_scale_inverse (S : Ary.State (ZMod p) d r) + (t : Fin (d + 1)) (j : Fin d) (round : ℕ) (hr : round < p - 1) : + toRegs p d r (scaleBank p r (reg d t j) (Reader.unitInv p round) (ofRegs S)) = + Ary.scale (Ary.reg t j) + (((Ary.Iter.unitAt (units p) round)⁻¹ : (ZMod p)ˣ) : ZMod p) S := by + rw [reg_eq, toRegs_scaleBank (ofRegs S) (length_ofRegs S), + toRegs_ofRegs, inverse_unitAt_units hr] + +/-- Every call, forward return, undo return and round transition agrees with the existing +controller, given exactly the external reply that step needs. The runtime controller itself +contains neither the comparison unit list nor the tree/gate functions. -/ +theorem toCfg_step_ofCfg (hd : 0 < d) + (e : (Fin b → Bool) → Fin r → ZMod p) + (E : Gate d b → (Fin d → Fin r → ZMod p) → Fin r → ZMod p) + (read : List (Fin d) → Tree.Node d b) (q : Ary.Iter.Cfg (ZMod p) d r) + (reply : Reply) (hreply : ReplyCorrect e E read q reply) + (hround : ∀ fr ∈ q.stack, fr.round < p - 1) : + toCfg p d r hd (step p d r reply (ofCfg q)) = + Ary.Iter.step hd e E (units p) read q := by + rcases q with ⟨S, st, mode⟩ + have hvalid := validBank_ofRegs (Fact.out : p.Prime).two_le S + cases mode with + | call c t => + cases hread : read (Ary.Iter.path st) with + | leaf v => + have hrp : reply.1 = 1 ∧ ∀ i : Fin r, (reply.2.getD i 0 : ZMod p) = e v i := by + simpa only [ReplyCorrect, hread] using hreply + obtain ⟨htag, hvalues⟩ := hrp + rw [show reply = (1, reply.2) from Prod.ext htag rfl] + exact toCfg_step_call_leaf hd e E (units p) read S st c t v reply.2 hread hvalues + | none => + have htag : reply.1 = 0 := by simpa only [ReplyCorrect, hread] using hreply + rw [Ary.Iter.step, hread] + change toCfg p d r hd + (step p d r reply (ofRegs S, st.map ofFrame, some (c.val, t.val))) = _ + simp only [step, hvalid, ↓reduceIte, stepCore, htag, + show (0 : ℕ) ≠ 1 by decide, show (0 : ℕ) ≠ 2 by decide, ↓reduceIte] + rw [toCfg_ret, toRegs_ofRegs] + | gate f => + have htag : reply.1 = 2 := by simpa only [ReplyCorrect, hread] using hreply + have hnonempty : units p ≠ [] := by + intro h + have hlen := congrArg List.length h + simp only [units, List.length_map, List.length_range, List.length_nil] at hlen + have := (Fact.out : p.Prime).two_le + omega + have hp1 : p - 1 ≠ 0 := by have := (Fact.out : p.Prime).two_le; omega + change toCfg p d r hd + (step p d r reply (ofRegs S, st.map ofFrame, some (c.val, t.val))) = _ + simp only [step, hvalid, ↓reduceIte, stepCore, htag, + show (2 : ℕ) ≠ 1 by decide, hp1, ↓reduceIte] + rw [toCfg_beginRound hd (units p) 0 c t (ofRegs S) (length_ofRegs S) st + (unitAt_units (by omega)), toRegs_ofRegs] + cases hus : units p with + | nil => exact False.elim (hnonempty hus) + | cons u us => simp [Ary.Iter.step, hread] + | ret => + cases st with + | nil => + rw [Ary.Iter.step_ret_nil] + change toCfg p d r hd (step p d r reply (ofRegs S, [], none)) = _ + simp only [step, hvalid, ↓reduceIte, stepCore, toCfg, toMode, + List.map_nil, toRegs_ofRegs] + | cons fr st => + have hr : fr.round < p - 1 := hround fr (by simp) + change toCfg p d r hd + (step p d r reply (ofRegs S, ofFrame fr :: st.map ofFrame, none)) = _ + by_cases hu : fr.undo = true + · by_cases hj : fr.child.val + 1 < d + · let child : Fin d := ⟨fr.child.val + 1, hj⟩ + have hreg : reg d fr.target.val (fr.child.val + 1) = + (Ary.reg fr.target child).val := reg_eq fr.target child + rw [Ary.Iter.step_ret_undo_next hd e E (units p) read S fr st hu hj] + simp only [step, hvalid, ↓reduceIte, stepCore, ofFrame, hu, hj, ↓reduceIte] + rw [hreg] + change toCfg p d r hd + (scaleBank p r (reg d fr.target fr.child) (Reader.unitInv p fr.round) (ofRegs S), + ({ fr with child := child, undo := true } :: st).map ofFrame, + some (p - 1, (Ary.reg fr.target child).val)) = _ + rw [toCfg_call, cast_negOne, toRegs_scale_inverse S fr.target fr.child fr.round hr] + · by_cases hi : fr.round + 1 < p - 1 + · rw [Ary.Iter.step_ret_undo_round hd e E (units p) read S fr st hu hj] + · simp only [step, hvalid, ↓reduceIte, stepCore, ofFrame, hu, hj, hi, ↓reduceIte] + rw [toCfg_beginRound hd (units p) (fr.round + 1) fr.coeff fr.target _ + (by simp) st (unitAt_units hi), + toRegs_scale_inverse S fr.target fr.child fr.round hr] + · simpa only [units, List.length_map, List.length_range] using hi + · rw [Ary.Iter.step_ret_undo_last hd e E (units p) read S fr st hu hj] + · simp only [step, hvalid, ↓reduceIte, stepCore, ofFrame, hu, hj, hi, ↓reduceIte] + rw [toCfg_ret, toRegs_scale_inverse S fr.target fr.child fr.round hr] + · simpa only [units, List.length_map, List.length_range] using hi + · have huf : fr.undo = false := Bool.eq_false_iff.mpr hu + by_cases hj : fr.child.val + 1 < d + · let child : Fin d := ⟨fr.child.val + 1, hj⟩ + have hreg : reg d fr.target.val (fr.child.val + 1) = + (Ary.reg fr.target child).val := reg_eq fr.target child + rw [Ary.Iter.step_ret_forward_next hd e E (units p) read S fr st huf hj] + simp only [step, hvalid, ↓reduceIte, stepCore, ofFrame, huf, hj, + Bool.false_eq_true, ↓reduceIte] + rw [hreg] + change toCfg p d r hd + (scaleBank p r (reg d fr.target child) (fr.round + 1) (ofRegs S), + ({ fr with child := child, undo := false } :: st).map ofFrame, + some (1, (Ary.reg fr.target child).val)) = _ + rw [toCfg_call, Nat.cast_one, toRegs_scale_unit S fr.target child fr.round hr] + · have hv : ∀ i : Fin r, (reply.2.getD i 0 : ZMod p) = + expectedGate E read S st fr.target i := by + simpa only [ReplyCorrect, huf, hj, not_false_eq_true, and_self, ↓reduceIte] + using hreply + rw [Ary.Iter.step_ret_forward_last hd e E (units p) read S fr st huf hj] + simp only [step, hvalid, ↓reduceIte, stepCore, ofFrame, huf, hj, + Bool.false_eq_true, ↓reduceIte] + have hreg : reg d fr.target.val 0 = (Ary.reg fr.target ⟨0, hd⟩).val := + reg_eq fr.target ⟨0, hd⟩ + rw [hreg] + change toCfg p d r hd + (subBank p r fr.target fr.coeff.val reply.2 (ofRegs S), + ({ fr with child := ⟨0, hd⟩, undo := true } :: st).map ofFrame, + some (p - 1, (Ary.reg fr.target ⟨0, hd⟩).val)) = _ + rw [toCfg_call, cast_negOne, toRegs_subBank_expected E read S st fr.coeff + fr.target reply.2 hv] + +/-- Canonical padded cells retain exactly one modulus offset. -/ +@[expose] def CanonicalBank (p d r : ℕ) (bank : List ℕ) : Prop := + bank.length = (d + 1) * r ∧ ∀ v ∈ bank, p ≤ v ∧ v < 2 * p + +/-- Canonical raw control fields use the exact finite indices and field representatives. -/ +@[expose] def CanonicalFrame (p d : ℕ) (fr : Frame) : Prop := + fr.1 < p - 1 ∧ fr.2.1 < d ∧ fr.2.2.2.1 < p ∧ fr.2.2.2.2 < d + 1 + +/-- The raw configuration has no hidden slack or alternative field representatives. -/ +@[expose] def Canonical (p d r : ℕ) (q : State) : Prop := + CanonicalBank p d r q.1 ∧ (∀ fr ∈ q.2.1, CanonicalFrame p d fr) ∧ + ∀ c t, q.2.2 = some (c, t) → c < p ∧ t < d + 1 + +private theorem canonical_cell (v : ℕ) (hv : p ≤ v) (hv2 : v < 2 * p) : + p + (v : ZMod p).val = v := by + rw [ZMod.val_natCast, Nat.mod_eq_sub_mod hv, Nat.mod_eq_of_lt (by omega)] + omega + +private theorem canonicalBank_ofRegs (S : Ary.State (ZMod p) d r) : + CanonicalBank p d r (ofRegs S) := by + refine ⟨length_ofRegs S, ?_⟩ + intro v hv + obtain ⟨k, rfl⟩ := List.mem_ofFn.mp hv + have hval := ZMod.val_lt (S (finProdFinEquiv.symm k).1 (finProdFinEquiv.symm k).2) + change p ≤ p + (S (finProdFinEquiv.symm k).1 (finProdFinEquiv.symm k).2).val ∧ + p + (S (finProdFinEquiv.symm k).1 (finProdFinEquiv.symm k).2).val < 2 * p + exact ⟨Nat.le_add_right _ _, by omega⟩ + +private theorem canonicalBank_valid {bank : List ℕ} (hbank : CanonicalBank p d r bank) : + validBank p d r bank = true := + (validBank_eq_true p d r bank).mpr + ⟨(Fact.out : p.Prime).two_le, hbank.1, fun v hv => (hbank.2 v hv).1⟩ + +private theorem ofRegs_toRegs (bank : List ℕ) (hbank : CanonicalBank p d r bank) : + ofRegs (toRegs p d r bank) = bank := by + apply List.ext_getElem + · simpa only [length_ofRegs] using hbank.1.symm + · intro k hk hk' + let z : Fin ((d + 1) * r) := ⟨k, by simpa only [length_ofRegs] using hk⟩ + have hindex : index r (finProdFinEquiv.symm z).1 (finProdFinEquiv.symm z).2 = k := + congrArg Fin.val (finProdFinEquiv.apply_symm_apply z) + have hcell := hbank.2 bank[k] (List.getElem_mem hk') + simp only [ofRegs, List.getElem_ofFn, toRegs] + change p + ((bank.getD (index r (finProdFinEquiv.symm z).1 + (finProdFinEquiv.symm z).2) 0 : ZMod p)).val = bank[k] + rw [hindex] + simp only [List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hk', Option.getD_some] + exact canonical_cell bank[k] hcell.1 hcell.2 + +omit [Fact p.Prime] in +private theorem ofFrame_toFrame (hd : 0 < d) (fr : Frame) + (hfr : CanonicalFrame p d fr) : ofFrame (toFrame (p := p) hd fr) = fr := by + rcases fr with ⟨round, child, undo, coeff, target⟩ + rcases hfr with ⟨hr, hc, hv, ht⟩ + simp only [toFrame, ofFrame, ZMod.val_natCast, Nat.mod_eq_of_lt hc, + Nat.mod_eq_of_lt hv, Nat.mod_eq_of_lt ht] + +omit [Fact p.Prime] in +private theorem ofMode_toMode (mode : Option (ℕ × ℕ)) + (hmode : ∀ c t, mode = some (c, t) → c < p ∧ t < d + 1) : + ofMode (toMode p d mode) = mode := by + cases mode with + | none => rfl + | some ct => + rcases ct with ⟨c, t⟩ + obtain ⟨hc, ht⟩ := hmode c t rfl + simp only [ofMode, toMode, ZMod.val_natCast, Nat.mod_eq_of_lt hc, + Nat.mod_eq_of_lt ht] + +/-- Canonical raw states are exactly represented by the existing typed configurations. -/ +theorem ofCfg_toCfg (hd : 0 < d) (q : State) (hq : Canonical p d r q) : + ofCfg (toCfg p d r hd q) = q := by + rcases q with ⟨bank, stack, mode⟩ + rcases hq with ⟨hbank, hstack, hmode⟩ + simp only [ofCfg, toCfg, ofRegs_toRegs bank hbank, List.map_map, + ofMode_toMode mode hmode] + congr 2 + calc + stack.map (ofFrame ∘ toFrame hd) = stack.map id := + List.map_congr_left fun fr hfr => ofFrame_toFrame hd fr (hstack fr hfr) + _ = stack := List.map_id stack + +omit [Fact p.Prime] in +private theorem canonicalBank_mapIdx {bank : List ℕ} (hbank : CanonicalBank p d r bank) + (f : ℕ → ℕ → ℕ) + (hf : ∀ i v, p ≤ v → v < 2 * p → p ≤ f i v ∧ f i v < 2 * p) : + CanonicalBank p d r (bank.mapIdx f) := by + refine ⟨by simpa only [List.length_mapIdx] using hbank.1, ?_⟩ + intro v hv + obtain ⟨i, hi, rfl⟩ := List.mem_mapIdx.mp hv + have hcell := hbank.2 bank[i] (List.getElem_mem hi) + exact hf i bank[i] hcell.1 hcell.2 + +private theorem canonicalBank_scaleBank {bank : List ℕ} (hbank : CanonicalBank p d r bank) + (t a : ℕ) : CanonicalBank p d r (scaleBank p r t a bank) := by + apply canonicalBank_mapIdx hbank + intro i v hv hv2 + split_ifs + · have hm : Reader.mulMod p a v < p := Nat.mod_lt _ (Fact.out : p.Prime).pos + exact ⟨Nat.le_add_right _ _, by omega⟩ + · exact ⟨hv, hv2⟩ + +private theorem canonicalBank_addBank {bank : List ℕ} (hbank : CanonicalBank p d r bank) + (t c : ℕ) (values : List ℕ) : CanonicalBank p d r (addBank p r t c values bank) := by + apply canonicalBank_mapIdx hbank + intro i v hv hv2 + split_ifs + · have hm : Reader.addMod p v (Reader.mulMod p c (values.getD (i % r) 0)) < p := + Nat.mod_lt _ (Fact.out : p.Prime).pos + exact ⟨Nat.le_add_right _ _, by omega⟩ + · exact ⟨hv, hv2⟩ + +private theorem canonicalBank_subBank {bank : List ℕ} (hbank : CanonicalBank p d r bank) + (t c : ℕ) (values : List ℕ) : CanonicalBank p d r (subBank p r t c values bank) := by + apply canonicalBank_mapIdx hbank + intro i v hv hv2 + split_ifs + · have hm : Reader.addMod p v + (Reader.negMod p (Reader.mulMod p c (values.getD (i % r) 0))) < p := + Nat.mod_lt _ (Fact.out : p.Prime).pos + exact ⟨Nat.le_add_right _ _, by omega⟩ + · exact ⟨hv, hv2⟩ + +omit [Fact p.Prime] in +private theorem canonical_ret {bank : List ℕ} {st : List Frame} + (hbank : CanonicalBank p d r bank) (hst : ∀ fr ∈ st, CanonicalFrame p d fr) : + Canonical p d r (bank, st, none) := + ⟨hbank, hst, fun c t h => by cases h⟩ + +omit [Fact p.Prime] in +private theorem canonical_call {bank : List ℕ} {st : List Frame} {c t : ℕ} + (hbank : CanonicalBank p d r bank) (hst : ∀ fr ∈ st, CanonicalFrame p d fr) + (hc : c < p) (ht : t < d + 1) : Canonical p d r (bank, st, some (c, t)) := by + refine ⟨hbank, hst, ?_⟩ + intro c' t' h + simp only [Option.some.injEq, Prod.mk.injEq] at h + rcases h with ⟨rfl, rfl⟩ + exact ⟨hc, ht⟩ + +private theorem canonical_beginRound (hd : 0 < d) {bank : List ℕ} {st : List Frame} + (hbank : CanonicalBank p d r bank) (hst : ∀ fr ∈ st, CanonicalFrame p d fr) + {round c t : ℕ} (hr : round < p - 1) (hc : c < p) (ht : t < d + 1) : + Canonical p d r (beginRound p d r round c t bank st) := by + apply canonical_call (canonicalBank_scaleBank hbank _ _) + · intro fr hfr + rcases List.mem_cons.mp hfr with rfl | hfr + · exact ⟨hr, hd, hc, ht⟩ + · exact hst fr hfr + · exact (Fact.out : p.Prime).one_lt + · exact Nat.mod_lt _ (Nat.succ_pos d) + +/-- Canonical storage and control representatives survive every raw transition. -/ +theorem canonical_step (hd : 0 < d) (reply : Reply) (q : State) + (hq : Canonical p d r q) : Canonical p d r (step p d r reply q) := by + rcases q with ⟨bank, stack, mode⟩ + rcases hq with ⟨hbank, hstack, hmode⟩ + have hvalid := canonicalBank_valid hbank + simp only [step, hvalid, ↓reduceIte] + cases mode with + | some ct => + rcases ct with ⟨c, t⟩ + obtain ⟨hc, ht⟩ := hmode c t rfl + simp only [stepCore] + split_ifs with hleaf hgate hempty + · exact canonical_ret (canonicalBank_addBank hbank t c reply.2) hstack + · have := (Fact.out : p.Prime).two_le + omega + · exact canonical_beginRound hd hbank hstack (by have := (Fact.out : p.Prime).two_le; omega) + hc ht + · exact canonical_ret hbank hstack + | none => + cases stack with + | nil => exact ⟨hbank, hstack, hmode⟩ + | cons fr st => + rcases fr with ⟨round, child, undo, coeff, target⟩ + obtain ⟨hr, hj, hc, ht⟩ := hstack (round, child, undo, coeff, target) (by simp) + have hst : ∀ fr ∈ st, CanonicalFrame p d fr := fun fr hfr => hstack fr (by simp [hfr]) + have hreg (j : ℕ) : reg d target j < d + 1 := Nat.mod_lt _ (Nat.succ_pos d) + have hneg : p - 1 < p := by have := (Fact.out : p.Prime).pos; omega + cases undo with + | true => + simp only [stepCore, ↓reduceIte] + have hscaled := canonicalBank_scaleBank hbank (reg d target child) + (Reader.unitInv p round) + split_ifs with hjnext hrnext + · apply canonical_call hscaled _ hneg (hreg _) + intro fr hfr + rcases List.mem_cons.mp hfr with rfl | hfr + · exact ⟨hr, hjnext, hc, ht⟩ + · exact hst fr hfr + · exact canonical_beginRound hd hscaled hst hrnext hc ht + · exact canonical_ret hscaled hst + | false => + simp only [stepCore, Bool.false_eq_true, ↓reduceIte] + split_ifs with hjnext + · apply canonical_call (canonicalBank_scaleBank hbank _ _) _ + (Fact.out : p.Prime).one_lt (hreg _) + intro fr hfr + rcases List.mem_cons.mp hfr with rfl | hfr + · exact ⟨hr, hjnext, hc, ht⟩ + · exact hst fr hfr + · apply canonical_call (canonicalBank_subBank hbank target coeff reply.2) _ hneg (hreg _) + intro fr hfr + rcases List.mem_cons.mp hfr with rfl | hfr + · exact ⟨hr, hd, hc, ht⟩ + · exact hst fr hfr + +private theorem canonical_ofCfg (q : Ary.Iter.Cfg (ZMod p) d r) + (hround : ∀ fr ∈ q.stack, fr.round < p - 1) : Canonical p d r (ofCfg q) := by + refine ⟨canonicalBank_ofRegs q.regs, ?_, ?_⟩ + · intro fr hfr + obtain ⟨typed, htyped, rfl⟩ := List.mem_map.mp hfr + exact ⟨hround typed htyped, typed.child.isLt, ZMod.val_lt typed.coeff, + typed.target.isLt⟩ + · cases hm : q.mode with + | ret => + intro c t h + simp only [ofCfg, ofMode, hm] at h + cases h + | call c t => + intro c' t' h + simp only [ofCfg, ofMode, hm, Option.some.injEq, Prod.mk.injEq] at h + rcases h with ⟨rfl, rfl⟩ + exact ⟨ZMod.val_lt c, t.isLt⟩ + +/-- The physical raw transition preserves the canonical representation exactly, not only after +decoding its field values. This permits consecutive raw transitions without re-encoding. -/ +theorem step_ofCfg (hd : 0 < d) + (e : (Fin b → Bool) → Fin r → ZMod p) + (E : Gate d b → (Fin d → Fin r → ZMod p) → Fin r → ZMod p) + (read : List (Fin d) → Tree.Node d b) (q : Ary.Iter.Cfg (ZMod p) d r) + (reply : Reply) (hreply : ReplyCorrect e E read q reply) + (hround : ∀ fr ∈ q.stack, fr.round < p - 1) : + step p d r reply (ofCfg q) = ofCfg (Ary.Iter.step hd e E (units p) read q) := by + have hcanonical := canonical_step hd reply (ofCfg q) (canonical_ofCfg q hround) + have heq := ofCfg_toCfg hd (step p d r reply (ofCfg q)) hcanonical + rw [toCfg_step_ofCfg hd e E read q reply hreply hround] at heq + exact heq.symm + +/-- Every consecutive raw transition realizes the existing iterative controller, from its +canonical initial representation, when each requested reply satisfies its explicit contract. -/ +theorem iterate_step_ofCfg (hd : 0 < d) + (e : (Fin b → Bool) → Fin r → ZMod p) + (E : Gate d b → (Fin d → Fin r → ZMod p) → Fin r → ZMod p) + (read : List (Fin d) → Tree.Node d b) (q : Ary.Iter.Cfg (ZMod p) d r) + (answer : State → Reply) + (hanswer : ∀ n, ReplyCorrect e E read (Ary.Iter.runFrom hd e E (units p) read q n) + (answer (ofCfg (Ary.Iter.runFrom hd e E (units p) read q n)))) + (hround : ∀ n, ∀ fr ∈ (Ary.Iter.runFrom hd e E (units p) read q n).stack, + fr.round < p - 1) (n : ℕ) : + (fun raw => step p d r (answer raw) raw)^[n] (ofCfg q) = + ofCfg (Ary.Iter.runFrom hd e E (units p) read q n) := by + induction n with + | zero => rfl + | succ n ih => + rw [Function.iterate_succ_apply', ih, + step_ofCfg hd e E read _ _ (hanswer n) (hround n), Ary.Iter.runFrom_succ] + +/-- The consecutive raw controller realizes the existing complete masking procedure. All +external access is isolated in the explicit reply contract; native query subroutines must still +supply those replies and charge their computation. -/ +theorem exists_iterate_step_root (hd : 0 < d) + (e : (Fin b → Bool) → Fin r → ZMod p) + (E : Gate d b → (Fin d → Fin r → ZMod p) → Fin r → ZMod p) + {H : ℕ} (T : Tree d b H) (S : Ary.State (ZMod p) d r) + (c : ZMod p) (t : Fin (d + 1)) (answer : State → Reply) + (hanswer : ∀ n, ReplyCorrect e E T.nodeAt + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n) + (answer (ofCfg + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n)))) : + ∃ N, (fun raw => step p d r (answer raw) raw)^[N] + (ofCfg (Ary.Iter.initialCfg S c t)) = + ofCfg (⟨Ary.procWith e E (units p) T c t S, [], .ret⟩ : Ary.Iter.Cfg (ZMod p) d r) := by + obtain ⟨N, hN⟩ := Ary.Iter.exists_runFrom_root hd e E (units p) T S c t + refine ⟨N, ?_⟩ + rw [iterate_step_ofCfg hd e E T.nodeAt (Ary.Iter.initialCfg S c t) answer hanswer] + · rw [hN] + · intro n fr hfr + have hbounds := Ary.Iter.runFrom_initial_bounds T hd e E (units p) S c t n + have hr := hbounds.2 fr hfr + simpa only [units, List.length_map, List.length_range] using hr + +end Cslib.TreeEval.Ary.Packed.RawController diff --git a/Complexity/Computability/TreeEval/Ary/Packed/RawControllerEncoding.lean b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerEncoding.lean new file mode 100644 index 0000000000..072c93bc15 --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerEncoding.lean @@ -0,0 +1,218 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerCorrect +public import Complexity.Computability.TreeEval.Ary.IterativeEncoding +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LinearSpaceArith + +/-! +# Uniform binary encoding of the raw packed controller + +One raw state type serves all runtime moduli, fan-ins and register widths. All natural fields +are binary, the bank is one flat framed list, and each continuation frame stores only its five +control fields. Cells are stored as `p + residue`; the padding is charged explicitly. + +The size bounds below apply to the physical raw state and its runtime parameters, rather than +using a separate typed representation as a substitute for an encoded machine state. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed.RawController + +open Turing.MultiTapeTM + +/-- Binary runtime modulus, fan-in and register width. -/ +abbrev encParams : Params ↪ List Bool := encPair encNat (encPair encNat encNat) + +/-- Binary round, child, phase, coefficient and target. -/ +abbrev encFrame : Frame ↪ List Bool := + encPair encNat (encPair encNat (encPair encBool (encPair encNat encNat))) + +/-- One framed bank, one framed continuation stack and the optional call mode. -/ +abbrev encState : State ↪ List Bool := + encPair (encList encNat) (encPair (encList encFrame) (encOption (encPair encNat encNat))) + +/-- Runtime parameters stored beside their state, with all framing charged. -/ +abbrev encFullState : Params × State ↪ List Bool := encPair encParams encState + +variable {p d r b H : ℕ} + +/-- Padding a field residue by its modulus costs at most twice the modulus's bit length. -/ +theorem length_paddedResidue_le [NeZero p] (x : ZMod p) : + (encNat (p + x.val)).length ≤ 2 * p.size := by + have hp : 0 < p.size := Nat.size_pos.mpr (NeZero.pos p) + have hx := ZMod.val_lt x + have hpow := Nat.lt_size_self p + have hfit : p + x.val < 2 ^ (p.size + 1) := by + rw [pow_succ] + omega + rw [length_encNat_eq_size] + exact (Nat.size_le.mpr hfit).trans (by omega) + +/-- All padded bank entries and their framing are charged once. -/ +theorem length_encBank_ofRegs_le [NeZero p] (S : Ary.State (ZMod p) d r) : + (encList encNat (ofRegs S)).length ≤ (d + 1) * r * (4 * p.size + 1) := by + have hlist := length_encList_le encNat (ofRegs S) (fun v hv => by + obtain ⟨i, rfl⟩ := List.mem_ofFn.mp hv + exact length_paddedResidue_le _) + have heq : 2 * (2 * p.size) + 1 = 4 * p.size + 1 := by omega + simpa only [length_ofRegs, heq] using hlist + +/-- Bits reserved for a single round-bounded frame, independent of register width. -/ +@[expose] def frameSpace (p d : ℕ) : ℕ := + 4 * p.size + 2 * d.size + (d + 1).size + 6 + +/-- The raw frame's binary encoding is exactly the typed frame's binary representation. -/ +theorem encFrame_ofFrame [NeZero p] (fr : Ary.Iter.Frame (ZMod p) d) : + encFrame (ofFrame fr) = Ary.Iter.encFrame (Ary.Iter.encField p) fr := rfl + +theorem length_encFrame_ofFrame_le [NeZero p] (fr : Ary.Iter.Frame (ZMod p) d) + (hr : fr.round < p - 1) : + (encFrame (ofFrame fr)).length ≤ frameSpace p d := by + rw [encFrame_ofFrame] + have hbound := Ary.Iter.length_encFrame_le (Ary.Iter.encField p) fr + (Ary.Iter.length_encField_le p) (show fr.round < p from by omega) + simp only [Ary.Iter.frameBits] at hbound + unfold frameSpace + omega + +private theorem encMode_ofMode [NeZero p] (mode : Ary.Iter.Mode (ZMod p) d) : + encOption (encPair encNat encNat) (ofMode mode) = + Ary.Iter.encMode (Ary.Iter.encField p) mode := by + cases mode <;> rfl + +/-- The binary parameter header is stored once beside the bank and control. -/ +@[expose] def fullSpaceBound (p d r H : ℕ) : ℕ := + 2 * ((d + 1) * r * (4 * p.size + 1)) + + 2 * (H * (2 * frameSpace p d + 1)) + + 2 * (encParams (p, d, r)).length + 2 * p.size + (d + 1).size + 5 + +/-- The raw encoding charges the padded physical bank and every continuation separator. -/ +theorem length_encFullState_ofCfg_le [NeZero p] (q : Ary.Iter.Cfg (ZMod p) d r) + (hq : Ary.Iter.ControlBounds H (p - 1) q) : + (encFullState ((p, d, r), ofCfg q)).length ≤ fullSpaceBound p d r H := by + have hbank := length_encBank_ofRegs_le q.regs + have hstack := length_encList_le encFrame (q.stack.map ofFrame) (fun raw hraw => by + obtain ⟨fr, hfr, heq⟩ := List.mem_map.mp hraw + rw [← heq] + exact length_encFrame_ofFrame_le fr (hq.2 fr hfr)) + have hmul := Nat.mul_le_mul_right (2 * frameSpace p d + 1) hq.1 + have hmode := Ary.Iter.length_encMode_le (Ary.Iter.encField p) q.mode + (Ary.Iter.length_encField_le p) + rw [← encMode_ofMode] at hmode + simp only [encFullState, encState, ofCfg, length_encPair, List.length_map] at * + unfold fullSpaceBound + simp only [encParams, length_encPair, length_encNat_eq_size] + omega + +/-- Every physical raw prefix has the bound when its actual external replies meet the +controller's explicit contracts. No unit enumeration is stored in the raw state. -/ +theorem length_encFullState_iterate_root_le [Fact p.Prime] (hd : 0 < d) + (e : (Fin b → Bool) → Fin r → ZMod p) + (E : Gate d b → (Fin d → Fin r → ZMod p) → Fin r → ZMod p) + (T : Tree d b H) (S : Ary.State (ZMod p) d r) (c : ZMod p) (t : Fin (d + 1)) + (answer : State → Reply) + (hanswer : ∀ n, ReplyCorrect e E T.nodeAt + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n) + (answer (ofCfg + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n)))) + (n : ℕ) : + (encFullState ((p, d, r), + (fun raw => step p d r (answer raw) raw)^[n] + (ofCfg (Ary.Iter.initialCfg S c t)))).length ≤ fullSpaceBound p d r H := by + have hbounds (j : ℕ) : Ary.Iter.ControlBounds H (p - 1) + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) j) := by + simpa only [units, List.length_map, List.length_range] using + Ary.Iter.runFrom_initial_bounds T hd e E (units p) S c t j + rw [iterate_step_ofCfg hd e E T.nodeAt (Ary.Iter.initialCfg S c t) answer hanswer + (fun j => (hbounds j).2)] + exact length_encFullState_ofCfg_le _ (hbounds n) + +/-- The padded physical bank still has linear storage for the selected packed parameters. -/ +theorem length_encBank_packed_le (hd : 0 < d) (hb : 0 < b) + (S : Ary.State (ZMod (fieldModulus d b)) d (chunkCount d b)) : + (encList encNat (ofRegs S)).length ≤ 17 * (d + 1) * (b + chunkWidth d b) := by + let : NeZero (fieldModulus d b) := ⟨(fieldModulus_prime d b).ne_zero⟩ + have hbank := length_encBank_ofRegs_le S + have hprod := chunkCount_mul_fieldModulus_size_le hd hb + have hr := chunkCount_le hd hb + have hscalar : chunkCount d b * (4 * (fieldModulus d b).size + 1) ≤ + 17 * (b + chunkWidth d b) := by nlinarith + calc + _ ≤ (d + 1) * chunkCount d b * (4 * (fieldModulus d b).size + 1) := hbank + _ = (d + 1) * (chunkCount d b * (4 * (fieldModulus d b).size + 1)) := Nat.mul_assoc .. + _ ≤ (d + 1) * (17 * (b + chunkWidth d b)) := Nat.mul_le_mul_left _ hscalar + _ = _ := by ring + +private theorem selected_parameter_sizes (hd : 0 < d) (hb : 0 < b) : + d.size ≤ chunkWidth d b ∧ (chunkCount d b).size ≤ chunkWidth d b ∧ + (d + 1).size ≤ chunkWidth d b + 2 := by + have hdle : d ≤ d * b := Nat.le_mul_of_pos_right d hb + have hble : b ≤ d * b := by nlinarith + have hdsize := Nat.size_le_size hdle + have hrsize := Nat.size_le_size ((chunkCount_le hd hb).trans hble) + have hsucc := Turing.MultiTapeTM.length_encNat_add_le_add d 1 + have hone : (encNat 1).length = 1 := rfl + simp only [hone, length_encNat_eq_size, chunkWidth] at * + omega + +/-- Explicit selected-parameter accounting for the runtime header, padded bank and stack. -/ +theorem fullSpaceBound_packed_le (hd : 0 < d) (hb : 0 < b) : + fullSpaceBound (fieldModulus d b) d (chunkCount d b) H ≤ + 34 * (d + 1) * (b + chunkWidth d b) + + 2 * (H * (22 * chunkWidth d b + 33)) + 20 * chunkWidth d b + 30 := by + have hp := fieldModulus_size_le hd hb + obtain ⟨hdsize, hrsize, htarget⟩ := selected_parameter_sizes hd hb + have hprod := chunkCount_mul_fieldModulus_size_le hd hb + have hr := chunkCount_le hd hb + have hscalar : chunkCount d b * (4 * (fieldModulus d b).size + 1) ≤ + 17 * (b + chunkWidth d b) := by nlinarith + have hbank := Nat.mul_le_mul_left (d + 1) hscalar + have hframe : 2 * frameSpace (fieldModulus d b) d + 1 ≤ 22 * chunkWidth d b + 33 := by + unfold frameSpace + omega + have hstack := Nat.mul_le_mul_left H hframe + have hmeta : 2 * (encParams (fieldModulus d b, d, chunkCount d b)).length + + 2 * (fieldModulus d b).size + (d + 1).size + 5 ≤ 20 * chunkWidth d b + 30 := by + simp only [encParams, length_encPair, length_encNat_eq_size] + omega + unfold fullSpaceBound + calc + _ ≤ 2 * ((d + 1) * (17 * (b + chunkWidth d b))) + + 2 * (H * (22 * chunkWidth d b + 33)) + 20 * chunkWidth d b + 30 := by + rw [Nat.mul_assoc] at ⊢ + omega + _ = _ := by ring + +/-- In the usual fixed-fan-in form, the raw storage is `O(b + H*log b)`. +The displayed constant is uniform; fan-in is charged explicitly. -/ +theorem fullSpaceBound_packed_le_linear (hd : 0 < d) (hb : 0 < b) : + fullSpaceBound (fieldModulus d b) d (chunkCount d b) H ≤ + 100 * (d + 1) * (b + H * (chunkWidth d b + 1) + d.size + 1) := by + have h := fullSpaceBound_packed_le (H := H) hd hb + have hm := chunkWidth_le d b + have hframe : 2 * (H * (22 * chunkWidth d b + 33)) ≤ + 66 * (d + 1) * (H * (chunkWidth d b + 1)) := by + calc + _ ≤ 66 * (H * (chunkWidth d b + 1)) := by nlinarith + _ ≤ 66 * ((d + 1) * (H * (chunkWidth d b + 1))) := + Nat.mul_le_mul_left _ (Nat.le_mul_of_pos_left _ (Nat.succ_pos d)) + _ = _ := by ring + have hwidth := Nat.mul_le_mul_left (34 * (d + 1)) hm + have hmeta : 20 * chunkWidth d b + 30 ≤ + 30 * (d + 1) * (b + d.size + 1) := by + calc + _ ≤ 30 * (b + d.size + 1) := by omega + _ ≤ 30 * ((d + 1) * (b + d.size + 1)) := + Nat.mul_le_mul_left _ (Nat.le_mul_of_pos_left _ (Nat.succ_pos d)) + _ = _ := by ring + nlinarith + +end Cslib.TreeEval.Ary.Packed.RawController diff --git a/Complexity/Computability/TreeEval/Ary/Packed/RawControllerNative.lean b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerNative.lean new file mode 100644 index 0000000000..e4cbe598aa --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerNative.lean @@ -0,0 +1,651 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerEncoding +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerSpace +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases +import Mathlib.Data.List.GetD + +/-! +# Native local computation of the uniform raw controller + +All counters and arithmetic parameters are binary and runtime values. The bank updates use the +streamed checked native operations. Gate points use cyclic bank slices, so zero register width +never materializes a list of fan-in-many empty registers. The executable padding guard rejects +malformed storage before requesting data or transforming the bank. + +## Main results + +* `need_inSpace`: construct one request in native linear space. +* `step_inSpace`: apply one returned reply and retain the runtime parameter header. +* `halted_inSpace`: test completion or reject malformed storage. +* `point_getD`, `length_point`: the cyclic slices contain exactly the designated child registers. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed.RawController + +open Turing MultiTapeTM ComputableInSpace Reader + +/-- An external node or gate-vector reply. -/ +abbrev encReply : Reply ↪ List Bool := encPair encNat (encList encNat) + +/-- Request tag, path, child-register point and first output chunk. -/ +abbrev encNeed : Need ↪ List Bool := + encPair encNat (encPair (encList encNat) (encPair (encList encNat) encNat)) + +/-- Runtime parameters and state, together with one returned reply. -/ +abbrev encStepArgs : ((Params × State) × Reply) ↪ List Bool := + encPair encFullState encReply + +private theorem length_encList_reverse {α : Type} (enc : α ↪ List Bool) (l : List α) : + (encList enc l.reverse).length = (encList enc l).length := by + simp only [length_encList, List.map_reverse, List.sum_reverse] + +private theorem listReverse_inSpace {α : Type} (enc : α ↪ List Bool) : + ComputableInSpace List.reverse (encList enc) (encList enc) + (fun l => (encList enc l).length) := by + have hlist : ComputableInSpace (fun l : List α => l) (encList enc) (encList enc) + (fun l => (encList enc l).length) := of_inO ComputableInDegree.id'.inO_one + have hinit : ComputableInSpace (fun _ : List α => ([] : List α)) + (encList enc) (encList enc) (fun l => (encList enc l).length) := + budget_const [] (.refl _) + have hstep : ComputableInSpace (fun q : List α × α => q.2 :: q.1) + (encPair (encList enc) enc) (encList enc) + (fun q => (encPair (encList enc) enc q).length) := + budget_cons (snd (encList enc) enc) (fst (encList enc) enc) + have hstate (l : List α) (i : ℕ) (_hi : i ≤ l.length) : + (encList enc ((l.take i).foldl (fun acc a => a :: acc) [])).length ≤ + (encList enc l).length := by + rw [List.foldl_flip_cons_eq_append', List.append_nil, length_encList_reverse] + exact length_encList_take_le enc l i + have hspace (l : List α) (i : ℕ) (hi : i < l.length) : + (encPair (encList enc) enc + ((l.take i).foldl (fun acc a => a :: acc) [], l[i])).length ≤ + 4 * ((encList enc l).length + 1) := by + have ha := length_le_encList_of_mem enc (List.getElem_mem hi) + have hs := hstate l i hi.le + simp only [length_encPair] + omega + have hfold := ComputableInSpace.foldl hlist hinit hstep hspace hstate + refine hfold.weaken (.of_le_linear 8 8 fun _ => by omega) |>.of_enc_eq ?_ + intro l + simp only [List.foldl_flip_cons_eq_append', List.append_nil] + +private theorem listDrop_inSpace {α : Type} (enc : α ↪ List Bool) : + ComputableInSpace (fun q : List α × ℕ => q.1.drop q.2) + (encPair (encList enc) encNat) (encList enc) + (fun q => (encPair (encList enc) encNat q).length) := by + have htail : ComputableInSpace List.tail (encList enc) (encList enc) + (fun l => (encList enc l).length) := + of_inO (.of_linear (ListEncoding.tail (enc₁ := enc))) + have hlen (q : List α × ℕ) (k : ℕ) : + (encList enc (List.tail^[k] q.1)).length ≤ (encList enc q.1).length := by + rw [List.tail_iterate] + rw [← q.1.take_append_drop k, encList_append, List.length_append] + simp + have h := iterate (snd (encList enc) encNat) (fst (encList enc) encNat) htail + (Q := fun q => (encList enc q.1).length) (B := fun q => (encList enc q.1).length) + (fun q k _ => hlen q k) (fun q k _ => hlen q k) + refine h.weaken (.of_le_linear 8 8 fun q => ?_) |>.of_enc_eq ?_ + · simp only [length_encPair] + omega + · intro q + rw [List.tail_iterate] + +private theorem listTake_inSpace {α : Type} (enc : α ↪ List Bool) : + ComputableInSpace (fun q : List α × ℕ => q.1.take q.2) + (encPair (encList enc) encNat) (encList enc) + (fun q => (encPair (encList enc) encNat q).length) := by + have hl := fst (encList enc) encNat + have hn := snd (encList enc) encNat + have hrev := budget_comp_linear (listReverse_inSpace enc) hl + have hlen := budget_length hl + have hcount := budget_natSub hlen hn + have hdrop := budget_comp_linear (listDrop_inSpace enc) (budget_prod hrev hcount) + refine (budget_comp_linear (listReverse_inSpace enc) hdrop).of_enc_eq ?_ + intro q + rw [← List.reverse_take, List.reverse_reverse] + +/-- The bank point is computed with binary slicing counters and linear retained output. -/ +theorem point_inSpace : + ComputableInSpace (fun q : (ℕ × ℕ × ℕ) × List ℕ => + point q.1.1 q.1.2.1 q.1.2.2 q.2) + (encPair encNat3 (encList encNat)) (encList encNat) + (fun q => (encPair encNat3 (encList encNat) q).length) := by + have hpar := fst encNat3 (encList encNat) + have hd := fst' hpar + have hr := fst' (snd' hpar) + have ht := snd' (snd' hpar) + have hbank := snd encNat3 (encList encNat) + have htarget := budget_natMod ht (budget_natSucc hd) + have hdrop := budget_comp_linear (listDrop_inSpace encNat) + (budget_prod hbank (budget_natMul (budget_natSucc htarget) hr)) + have htake := budget_comp_linear (listTake_inSpace encNat) + (budget_prod hbank (budget_natMul htarget hr)) + refine (budget_comp_linear (of_inO (ComputableInDegree.listAppend encNat).inO_one) + (budget_prod hdrop htake)).of_enc_eq ?_ + intro q + rfl + +private theorem path_encoding_le (st : List Frame) : + (encList encNat (path st)).length ≤ (encList encFrame st).length := by + induction st with + | nil => simp [path] + | cons fr st ih => + have hc : (encNat fr.2.1).length ≤ (encFrame fr).length := by + simp only [encFrame, length_encPair] + omega + have hp : path (fr :: st) = path st ++ [fr.2.1] := by simp [path] + rw [hp, encList_append, List.length_append] + simp only [encList_cons, encList_nil, List.append_nil, List.length_append, + length_frame] at ih ⊢ + omega + +private theorem path_inSpace : + ComputableInSpace path (encList encFrame) (encList encNat) + (fun st => (encList encFrame st).length) := by + have hid : ComputableInSpace (fun st : List Frame => st) (encList encFrame) (encList encFrame) + (fun st => (encList encFrame st).length) := of_inO ComputableInDegree.id'.inO_one + have hinit : ComputableInSpace (fun _ : List Frame => ([] : List ℕ)) + (encList encFrame) (encList encNat) (fun st => (encList encFrame st).length) := + budget_const [] (.refl _) + have hstep : ComputableInSpace (fun q : List ℕ × Frame => q.2.2.1 :: q.1) + (encPair (encList encNat) encFrame) (encList encNat) + (fun q => (encPair (encList encNat) encFrame q).length) := by + have hfr := snd (encList encNat) encFrame + exact budget_cons (fst' (snd' hfr)) (fst (encList encNat) encFrame) + have hstate (st : List Frame) (i : ℕ) (_hi : i ≤ st.length) : + (encList encNat ((st.take i).foldl (fun acc fr => fr.2.1 :: acc) [])).length ≤ + (encList encFrame st).length := by + have hpath := path_encoding_le (st.take i) + simp only [List.foldl_flip_cons_eq_append, List.append_nil, + ← List.map_reverse] at ⊢ + exact hpath.trans (length_encList_take_le encFrame st i) + have hspace (st : List Frame) (i : ℕ) (hi : i < st.length) : + (encPair (encList encNat) encFrame + ((st.take i).foldl (fun acc fr => fr.2.1 :: acc) [], st[i])).length ≤ + 4 * ((encList encFrame st).length + 1) := by + have hf := length_le_encList_of_mem encFrame (List.getElem_mem hi) + have hs := hstate st i hi.le + simp only [encFrame, length_encPair] at hf hs ⊢ + omega + have hfold := ComputableInSpace.foldl hid hinit hstep hspace hstate + refine hfold.weaken (.of_le_linear 8 8 fun _ => by omega) |>.of_enc_eq ?_ + intro st + simp [List.foldl_flip_cons_eq_append, path, List.map_reverse] + +/-- The executable shape and padding guard has a uniform native certificate. -/ +theorem validBank_inSpace : + ComputableInSpace (fun q : Params × State => validBank q.1.1 q.1.2.1 q.1.2.2 q.2.1) + encFullState encBool (fun q => (encFullState q).length) := by + have hpar := fst encParams encState + have hp := fst' hpar + have hd := fst' (snd' hpar) + have hr := snd' (snd' hpar) + have hbank := fst' (snd encParams encState) + have hpad := budget_comp_linear bankPadding_inSpace (budget_prod hp hbank) + have hlength := budget_length hbank + have hshape := budget_natEq hlength (budget_natMul (budget_natSucc hd) hr) + have htwo := budget_const (encIn := encFullState) (encB := encNat) 2 (.refl _) + have hpok := budget_finite (encC := encBool) Bool.not (budget_natLt hp htwo) + have hand := budget_finite (encC := encBool) (fun q : Bool × Bool => q.1 && q.2) + (budget_prod hpok hshape) + refine (budget_finite (encC := encBool) (fun q : Bool × Bool => q.1 && q.2) + (budget_prod hand hpad)).of_enc_eq ?_ + intro q + by_cases h : q.1.1 < 2 + · have hnot : ¬2 ≤ q.1.1 := by omega + simp [validBank, h, hnot] + · have hle : 2 ≤ q.1.1 := by omega + simp [validBank, h, hle] + +private theorem optionSome_budget {α β : Type} {encIn : α ↪ List Bool} + {encB : β ↪ List Bool} {f : α → β} {S : α → ℕ} + (hf : ComputableInSpace f encIn encB S) : + ComputableInSpace (fun a => some (f a)) encIn (encOption encB) S := + budget_comp_linear + (of_inO (ComputableInDegree.optionSome (enc₁ := encB) (enc := encOption encB)).inO_one) hf + +private theorem optionIsSome_budget {α β : Type} {encIn : α ↪ List Bool} + {encB : β ↪ List Bool} {f : α → Option β} {S : α → ℕ} + (hf : ComputableInSpace f encIn (encOption encB) S) : + ComputableInSpace (fun a => (f a).isSome) encIn encBool S := + budget_comp_linear (of_inO (ComputableInDegree.isSome encB).inO_one) hf + +private theorem needCore_inSpace : + ComputableInSpace (fun q : Params × State => needCore q.1.2.1 q.1.2.2 q.2) + encFullState (encOption encNeed) (fun q => (encFullState q).length) := by + have hpar := fst encParams encState + have hd := fst' (snd' hpar) + have hr := snd' (snd' hpar) + have hstate := snd encParams encState + have hbank := fst' hstate + have hrest := snd' hstate + have hstack := fst' hrest + have hmode := snd' hrest + have hhead := budget_headD (0, 0, false, 0, 0) hstack + have htail := budget_tail hstack + have hchild := fst' (snd' hhead) + have hundo := fst' (snd' (snd' hhead)) + have htarget := snd' (snd' (snd' (snd' hhead))) + have hpath := budget_comp_linear path_inSpace hstack + have hparent := budget_comp_linear path_inSpace htail + have hpoint := budget_comp_linear point_inSpace + (budget_prod (budget_prod hd (budget_prod hr htarget)) hbank) + have hz := budget_const (encIn := encFullState) (encB := encNat) 0 (.refl _) + have hnode := optionSome_budget (budget_prod hz (budget_prod hpath + (budget_prod (budget_const (encB := encList encNat) [] (.refl _)) hz))) + have hgate := optionSome_budget (budget_prod + (budget_const (encB := encNat) 1 (.refl _)) (budget_prod hparent (budget_prod hpoint hz))) + have hnone := budget_const (encIn := encFullState) (encB := encOption encNeed) none (.refl _) + have hskip := budget_finite (encC := encBool) (fun q : Bool × Bool => q.1 || q.2) + (budget_prod hundo (budget_natLt (budget_natSucc hchild) hd)) + refine (budget_ite (optionIsSome_budget hmode) hnode + (budget_ite (budget_isEmpty hstack) hnone (budget_ite hskip hnone hgate))).of_enc_eq ?_ + rintro ⟨⟨p, d, r⟩, bank, stack, mode⟩ + cases mode with + | some ct => rfl + | none => + cases stack with + | nil => rfl + | cons fr st => rfl + +/-- Requests have a native certificate linear in the actual encoded runtime state. -/ +theorem need_inSpace : + ComputableInSpace (fun q : Params × State => need q.1.1 q.1.2.1 q.1.2.2 q.2) + encFullState (encOption encNeed) (fun q => (encFullState q).length) := + (budget_ite validBank_inSpace needCore_inSpace + (budget_const none (.refl _))).of_enc_eq fun _ => rfl + +private theorem reg_budget {α : Type} {encIn : α ↪ List Bool} {S : α → ℕ} + {d t j : α → ℕ} (hd : ComputableInSpace d encIn encNat S) + (ht : ComputableInSpace t encIn encNat S) (hj : ComputableInSpace j encIn encNat S) : + ComputableInSpace (fun a => reg (d a) (t a) (j a)) encIn encNat S := + budget_natMod (budget_natAdd ht (budget_natSucc hj)) (budget_natSucc hd) + +private theorem frame_budget {α : Type} {encIn : α ↪ List Bool} {S : α → ℕ} + {round child coeff target : α → ℕ} {undo : α → Bool} + (hr : ComputableInSpace round encIn encNat S) + (hj : ComputableInSpace child encIn encNat S) + (hu : ComputableInSpace undo encIn encBool S) + (hc : ComputableInSpace coeff encIn encNat S) + (ht : ComputableInSpace target encIn encNat S) : + ComputableInSpace (fun a => (round a, child a, undo a, coeff a, target a)) encIn encFrame S := + budget_prod hr (budget_prod hj (budget_prod hu (budget_prod hc ht))) + +private theorem state_budget {α : Type} {encIn : α ↪ List Bool} {S : α → ℕ} + {bank : α → List ℕ} {stack : α → List Frame} {mode : α → Option (ℕ × ℕ)} + (hb : ComputableInSpace bank encIn (encList encNat) S) + (hs : ComputableInSpace stack encIn (encList encFrame) S) + (hm : ComputableInSpace mode encIn (encOption (encPair encNat encNat)) S) : + ComputableInSpace (fun a => (bank a, stack a, mode a)) encIn encState S := + budget_prod hb (budget_prod hs hm) + +private theorem scale_budget {α : Type} {encIn : α ↪ List Bool} {S : α → ℕ} + {p r t c : α → ℕ} {bank : α → List ℕ} + (hp : ComputableInSpace p encIn encNat S) (hr : ComputableInSpace r encIn encNat S) + (ht : ComputableInSpace t encIn encNat S) (hc : ComputableInSpace c encIn encNat S) + (hb : ComputableInSpace bank encIn (encList encNat) S) : + ComputableInSpace (fun a => checkedScaleBank ((p a, r a, t a, c a), bank a)) + encIn (encList encNat) S := + budget_comp_linear checkedScaleBank_inSpace + (budget_prod (budget_prod hp (budget_prod hr (budget_prod ht hc))) hb) + +private theorem add_budget {α : Type} {encIn : α ↪ List Bool} {S : α → ℕ} + {p r t c : α → ℕ} {values bank : α → List ℕ} + (hp : ComputableInSpace p encIn encNat S) (hr : ComputableInSpace r encIn encNat S) + (ht : ComputableInSpace t encIn encNat S) (hc : ComputableInSpace c encIn encNat S) + (hv : ComputableInSpace values encIn (encList encNat) S) + (hb : ComputableInSpace bank encIn (encList encNat) S) : + ComputableInSpace (fun a => checkedAddBank ((p a, r a, t a, c a), values a, bank a)) + encIn (encList encNat) S := + budget_comp_linear checkedAddBank_inSpace + (budget_prod (budget_prod hp (budget_prod hr (budget_prod ht hc))) (budget_prod hv hb)) + +private theorem sub_budget {α : Type} {encIn : α ↪ List Bool} {S : α → ℕ} + {p r t c : α → ℕ} {values bank : α → List ℕ} + (hp : ComputableInSpace p encIn encNat S) (hr : ComputableInSpace r encIn encNat S) + (ht : ComputableInSpace t encIn encNat S) (hc : ComputableInSpace c encIn encNat S) + (hv : ComputableInSpace values encIn (encList encNat) S) + (hb : ComputableInSpace bank encIn (encList encNat) S) : + ComputableInSpace (fun a => checkedSubBank ((p a, r a, t a, c a), values a, bank a)) + encIn (encList encNat) S := + budget_comp_linear checkedSubBank_inSpace + (budget_prod (budget_prod hp (budget_prod hr (budget_prod ht hc))) (budget_prod hv hb)) + +private def checkedBeginRound (p d r round c t : ℕ) (bank : List ℕ) + (stack : List Frame) : State := + (checkedScaleBank ((p, r, reg d t 0, round + 1), bank), + (round, 0, false, c, t) :: stack, some (1, reg d t 0)) + +private theorem beginRound_budget {α : Type} {encIn : α ↪ List Bool} {S : α → ℕ} + {p d r round c t : α → ℕ} {bank : α → List ℕ} {stack : α → List Frame} + (hp : ComputableInSpace p encIn encNat S) (hd : ComputableInSpace d encIn encNat S) + (hr : ComputableInSpace r encIn encNat S) (hi : ComputableInSpace round encIn encNat S) + (hc : ComputableInSpace c encIn encNat S) (ht : ComputableInSpace t encIn encNat S) + (hb : ComputableInSpace bank encIn (encList encNat) S) + (hs : ComputableInSpace stack encIn (encList encFrame) S) + (hlen : (fun a => (encIn a).length) ≼ S) : + ComputableInSpace (fun a => checkedBeginRound (p a) (d a) (r a) (round a) (c a) (t a) + (bank a) (stack a)) encIn encState S := by + have hz := budget_const (encB := encNat) 0 hlen + have hone := budget_const (encB := encNat) 1 hlen + have hreg := reg_budget hd ht hz + exact (state_budget (scale_budget hp hr hreg (budget_natSucc hi) hb) + (budget_cons (frame_budget hi hz (budget_const false hlen) hc ht) hs) + (optionSome_budget (budget_prod hone hreg))).of_enc_eq fun _ => rfl + +private def checkedCall (p d r : ℕ) (reply : Reply) (bank : List ℕ) (stack : List Frame) + (ct : ℕ × ℕ) : State := + if reply.1 = 1 then (checkedAddBank ((p, r, ct.2, ct.1), reply.2, bank), stack, none) + else if reply.1 = 2 then + if p - 1 = 0 then (bank, stack, none) else checkedBeginRound p d r 0 ct.1 ct.2 bank stack + else (bank, stack, none) + +private theorem call_budget {α : Type} {encIn : α ↪ List Bool} {S : α → ℕ} + {p d r c t tag : α → ℕ} {bank values : α → List ℕ} {stack : α → List Frame} + (hp : ComputableInSpace p encIn encNat S) (hd : ComputableInSpace d encIn encNat S) + (hr : ComputableInSpace r encIn encNat S) (hc : ComputableInSpace c encIn encNat S) + (ht : ComputableInSpace t encIn encNat S) (htag : ComputableInSpace tag encIn encNat S) + (hb : ComputableInSpace bank encIn (encList encNat) S) + (hv : ComputableInSpace values encIn (encList encNat) S) + (hs : ComputableInSpace stack encIn (encList encFrame) S) + (hlen : (fun a => (encIn a).length) ≼ S) : + ComputableInSpace (fun a => checkedCall (p a) (d a) (r a) (tag a, values a) + (bank a) (stack a) (c a, t a)) encIn encState S := by + have hz := budget_const (encB := encNat) 0 hlen + have hone := budget_const (encB := encNat) 1 hlen + have htwo := budget_const (encB := encNat) 2 hlen + have hnone := budget_const (encB := encOption (encPair encNat encNat)) none hlen + have hreturn := state_budget hb hs hnone + have hleaf := state_budget (add_budget hp hr ht hc hv hb) hs hnone + have hbegin := beginRound_budget hp hd hr hz hc ht hb hs hlen + refine (budget_ite (budget_natEq htag hone) hleaf + (budget_ite (budget_natEq htag htwo) + (budget_ite (budget_natIsZero (budget_natPred hp)) hreturn hbegin) hreturn)).of_enc_eq ?_ + intro a + by_cases h1 : tag a = 1 <;> by_cases h2 : tag a = 2 <;> by_cases hz : p a - 1 = 0 <;> + simp [checkedCall, h1, h2, hz] + +private def checkedReturn (p d r : ℕ) (reply : Reply) (bank : List ℕ) (fr : Frame) + (st : List Frame) : State := + if fr.2.2.1 then + let scaled := checkedScaleBank ((p, r, reg d fr.2.2.2.2 fr.2.1, + unitInv p fr.1), bank) + if fr.2.1 + 1 < d then + (scaled, (fr.1, fr.2.1 + 1, true, fr.2.2.2.1, fr.2.2.2.2) :: st, + some (p - 1, reg d fr.2.2.2.2 (fr.2.1 + 1))) + else if fr.1 + 1 < p - 1 then + checkedBeginRound p d r (fr.1 + 1) fr.2.2.2.1 fr.2.2.2.2 scaled st + else (scaled, st, none) + else + if fr.2.1 + 1 < d then + (checkedScaleBank ((p, r, reg d fr.2.2.2.2 (fr.2.1 + 1), fr.1 + 1), bank), + (fr.1, fr.2.1 + 1, false, fr.2.2.2.1, fr.2.2.2.2) :: st, + some (1, reg d fr.2.2.2.2 (fr.2.1 + 1))) + else + (checkedSubBank ((p, r, fr.2.2.2.2, fr.2.2.2.1), reply.2, bank), + (fr.1, 0, true, fr.2.2.2.1, fr.2.2.2.2) :: st, + some (p - 1, reg d fr.2.2.2.2 0)) + +private theorem checkedReturn_reply (p d r : ℕ) (reply : Reply) (bank : List ℕ) + (fr : Frame) (st : List Frame) : + checkedReturn p d r reply bank fr st = checkedReturn p d r (0, reply.2) bank fr st := rfl + +private theorem return_budget {α : Type} {encIn : α ↪ List Bool} {S : α → ℕ} + {p d r : α → ℕ} {bank values : α → List ℕ} + {fr : α → Frame} {st : α → List Frame} + (hp : ComputableInSpace p encIn encNat S) (hd : ComputableInSpace d encIn encNat S) + (hr : ComputableInSpace r encIn encNat S) + (hb : ComputableInSpace bank encIn (encList encNat) S) + (hv : ComputableInSpace values encIn (encList encNat) S) + (hf : ComputableInSpace fr encIn encFrame S) + (hs : ComputableInSpace st encIn (encList encFrame) S) + (hlen : (fun a => (encIn a).length) ≼ S) : + ComputableInSpace (fun a => checkedReturn (p a) (d a) (r a) (0, values a) + (bank a) (fr a) (st a)) encIn encState S := by + have hround := fst' hf + have hrest := snd' hf + have hchild := fst' hrest + have htail := snd' hrest + have hundo := fst' htail + have hct := snd' htail + have hc := fst' hct + have ht := snd' hct + have hz := budget_const (encB := encNat) 0 hlen + have hone := budget_const (encB := encNat) 1 hlen + have htrue := budget_const (encB := encBool) true hlen + have hfalse := budget_const (encB := encBool) false hlen + have hnone := budget_const (encB := encOption (encPair encNat encNat)) none hlen + have hnext := budget_natSucc hchild + have hreg := reg_budget hd ht hchild + have hregNext := reg_budget hd ht hnext + have hregZero := reg_budget hd ht hz + have hunitInv := budget_comp_linear unitInv_inSpace (budget_prod hp hround) + have hscaled := scale_budget hp hr hreg hunitInv hb + have hneg := budget_natPred hp + have hundoNext := state_budget hscaled + (budget_cons (frame_budget hround hnext htrue hc ht) hs) + (optionSome_budget (budget_prod hneg hregNext)) + have hnextRound := beginRound_budget hp hd hr (budget_natSucc hround) hc ht hscaled hs hlen + have hpop := state_budget hscaled hs hnone + have hundoResult := budget_ite (budget_natLt hnext hd) hundoNext + (budget_ite (budget_natLt (budget_natSucc hround) hneg) hnextRound hpop) + have hforwardNext := state_budget + (scale_budget hp hr hregNext (budget_natSucc hround) hb) + (budget_cons (frame_budget hround hnext hfalse hc ht) hs) + (optionSome_budget (budget_prod hone hregNext)) + have hforwardLast := state_budget (sub_budget hp hr ht hc hv hb) + (budget_cons (frame_budget hround hz htrue hc ht) hs) + (optionSome_budget (budget_prod hneg hregZero)) + refine (budget_ite hundo hundoResult + (budget_ite (budget_natLt hnext hd) hforwardNext hforwardLast)).of_enc_eq ?_ + intro a + by_cases hu : (fr a).2.2.1 = true <;> + by_cases hj : (fr a).2.1 + 1 < d a <;> + by_cases hi : (fr a).1 + 1 < p a - 1 <;> simp [checkedReturn, hu, hj, hi] + +private def checkedStepCore (par : Params) (reply : Reply) (q : State) : State := + q.2.2.elim + (if q.2.1.isEmpty then q else + checkedReturn par.1 par.2.1 par.2.2 reply q.1 + (q.2.1.headD (0, 0, false, 0, 0)) q.2.1.tail) + (fun ct => checkedCall par.1 par.2.1 par.2.2 reply q.1 q.2.1 ct) + +private theorem checkedStepCore_inSpace : + ComputableInSpace (fun a : (Params × State) × Reply => checkedStepCore a.1.1 a.2 a.1.2) + encStepArgs encState (fun a => (encStepArgs a).length) := by + have hfull := fst encFullState encReply + have hpar := fst' hfull + have hp := fst' hpar + have hd := fst' (snd' hpar) + have hr := snd' (snd' hpar) + have hq := snd' hfull + have hbank := fst' hq + have hstack := fst' (snd' hq) + have hmode := snd' (snd' hq) + have hreply := snd encFullState encReply + have hvalues := snd' hreply + have hret := return_budget hp hd hr hbank hvalues + (budget_headD (0, 0, false, 0, 0) hstack) (budget_tail hstack) (.refl _) + have hstop := budget_ite (budget_isEmpty hstack) hq hret + have hctx := fst encStepArgs (encPair encNat encNat) + have hctxFull := budget_comp_linear hfull hctx + have hctxPar := fst' hctxFull + have hctxState := snd' hctxFull + have hctxReply := budget_comp_linear hreply hctx + have hct := snd encStepArgs (encPair encNat encNat) + have hcall := call_budget (fst' hctxPar) (fst' (snd' hctxPar)) (snd' (snd' hctxPar)) + (fst' hct) (snd' hct) (fst' hctxReply) + (fst' hctxState) (snd' hctxReply) (fst' (snd' hctxState)) (.refl _) + have hid : ComputableInSpace (fun a : (Params × State) × Reply => a) encStepArgs encStepArgs + (fun a => (encStepArgs a).length) := of_inO ComputableInDegree.id'.inO_one + refine (budget_comp_linear (optionElimSnd hstop hcall) (budget_prod hid hmode)).of_enc_eq ?_ + intro a + cases hm : a.1.2.2.2 with + | none => simp [checkedStepCore, hm, checkedReturn_reply] + | some ct => simp [checkedStepCore, hm] + +private theorem checkedBeginRound_eq (p d r round c t : ℕ) (bank : List ℕ) + (st : List Frame) (hbank : validBank p d r bank = true) : + checkedBeginRound p d r round c t bank st = beginRound p d r round c t bank st := by + rw [checkedBeginRound, checkedScaleBank_eq p d r _ _ bank hbank] + rfl + +private theorem checkedStepCore_eq (p d r : ℕ) (reply : Reply) (q : State) + (hbank : validBank p d r q.1 = true) : checkedStepCore (p, d, r) reply q = + stepCore p d r reply q := by + rcases q with ⟨bank, stack, mode⟩ + cases mode with + | some ct => + rcases ct with ⟨c, t⟩ + simp only [checkedStepCore, Option.elim_some, checkedCall, stepCore] + split_ifs + · rw [checkedAddBank_eq p d r _ _ _ bank hbank] + · rfl + · rw [checkedBeginRound_eq p d r _ _ _ bank stack hbank] + · rfl + | none => + cases stack with + | nil => rfl + | cons fr st => + rcases fr with ⟨round, child, undo, c, t⟩ + simp only [checkedStepCore, Option.elim_none, List.isEmpty_cons, + Bool.false_eq_true, ↓reduceIte, List.headD_cons, List.tail_cons, checkedReturn, stepCore] + cases undo with + | true => + simp only [↓reduceIte] + rw [checkedScaleBank_eq p d r _ _ bank hbank] + have hscaled := validBank_scaleBank p d r (reg d t child) (unitInv p round) bank hbank + by_cases hj : child + 1 < d + · simp only [hj, ↓reduceIte] + · by_cases hi : round + 1 < p - 1 + · simp only [hj, hi, ↓reduceIte] + rw [checkedBeginRound_eq p d r _ _ _ _ st hscaled] + · simp only [hj, hi, ↓reduceIte] + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + by_cases hj : child + 1 < d + · simp only [hj, ↓reduceIte] + rw [checkedScaleBank_eq p d r _ _ bank hbank] + · simp only [hj, ↓reduceIte] + rw [checkedSubBank_eq p d r _ _ _ bank hbank] + +/-- One returned reply advances the actual raw controller in native linear work space. -/ +theorem stepState_inSpace : + ComputableInSpace (fun a : (Params × State) × Reply => + step a.1.1.1 a.1.1.2.1 a.1.1.2.2 a.2 a.1.2) + encStepArgs encState (fun a => (encStepArgs a).length) := by + have hfull := fst encFullState encReply + have hq := snd' hfull + have hguard := budget_comp_linear validBank_inSpace hfull + refine (budget_ite hguard checkedStepCore_inSpace hq).of_enc_eq ?_ + rintro ⟨⟨⟨p, d, r⟩, bank, stack, mode⟩, reply⟩ + by_cases h : validBank p d r bank = true + · simp only [step, h, ↓reduceIte] + exact congrArg encState (checkedStepCore_eq p d r reply (bank, stack, mode) h).symm + · simp only [step, h, Bool.false_eq_true, ↓reduceIte] + +/-- The runtime parameter header is retained beside the updated raw state. -/ +theorem step_inSpace : + ComputableInSpace (fun a : (Params × State) × Reply => + (a.1.1, step a.1.1.1 a.1.1.2.1 a.1.1.2.2 a.2 a.1.2)) + encStepArgs encFullState (fun a => (encStepArgs a).length) := + budget_prod (fst' (fst encFullState encReply)) stepState_inSpace + +/-- The caller halts at an empty-stack return, and rejects malformed storage immediately. -/ +theorem halted_inSpace : + ComputableInSpace (fun q : Params × State => + !validBank q.1.1 q.1.2.1 q.1.2.2 q.2.1 || (q.2.2.2.isNone && q.2.2.1.isEmpty)) + encFullState encBool (fun q => (encFullState q).length) := by + have hstate := snd encParams encState + have hrest := snd' hstate + have hstack := fst' hrest + have hmode := snd' hrest + have hnone := budget_finite (encC := encBool) Bool.not (optionIsSome_budget hmode) + have hret := budget_finite (encC := encBool) (fun q : Bool × Bool => q.1 && q.2) + (budget_prod hnone (budget_isEmpty hstack)) + have hbad := budget_finite (encC := encBool) Bool.not validBank_inSpace + refine (budget_finite (encC := encBool) (fun q : Bool × Bool => q.1 || q.2) + (budget_prod hbad hret)).of_enc_eq ?_ + intro q + cases q.2.2.2 <;> rfl + +/-- A valid bank request retains exactly the child registers and omits the target. -/ +theorem length_point {d r : ℕ} (bank : List ℕ) (hlen : bank.length = (d + 1) * r) + (t : Fin (d + 1)) : (point d r t bank).length = d * r := by + have ht : t.val ≤ d := by omega + simp only [point, Nat.mod_eq_of_lt t.isLt, List.length_append, List.length_drop, + List.length_take, hlen] + rw [Nat.min_eq_left (Nat.mul_le_mul_right r t.isLt.le), ← Nat.sub_mul] + have hsub : d + 1 - (t.val + 1) = d - t.val := by omega + rw [hsub, ← Nat.add_mul, Nat.sub_add_cancel ht] + +/-- Every coordinate of a cyclic bank slice is the existing child's designated register. -/ +theorem point_getD {d r : ℕ} (bank : List ℕ) (hlen : bank.length = (d + 1) * r) + (t : Fin (d + 1)) (j : Fin d) (i : Fin r) : + (point d r t bank).getD (index r j i) 0 = + bank.getD (index r (reg d t j) i) 0 := by + have ht : t.val ≤ d := by omega + have htail : (bank.drop ((t.val + 1) * r)).length = (d - t.val) * r := by + rw [List.length_drop, hlen, ← Nat.sub_mul] + congr 1 + omega + rw [point, Nat.mod_eq_of_lt t.isLt] + by_cases hj : j.val < d - t.val + · have hpos : index r j i < (bank.drop ((t.val + 1) * r)).length := by + rw [htail] + calc + index r j i < (j.val + 1) * r := by unfold index; nlinarith [i.isLt] + _ ≤ (d - t.val) * r := Nat.mul_le_mul_right r (Nat.succ_le_of_lt hj) + have hreg : reg d t j = t.val + (j.val + 1) := by + apply Nat.mod_eq_of_lt + omega + have hindex : (t.val + 1) * r + index r j i = index r (reg d t j) i := by + rw [hreg] + unfold index + ring + rw [List.getD_append _ _ 0 _ hpos] + simp only [List.getD_eq_getElem?_getD, List.getElem?_drop, hindex] + · have hjge : d - t.val ≤ j.val := by omega + have hjsmall : j.val - (d - t.val) < t.val := by omega + have hpos : index r (j.val - (d - t.val)) i < t.val * r := by + calc + index r (j.val - (d - t.val)) i < (j.val - (d - t.val) + 1) * r := by + unfold index + nlinarith [i.isLt] + _ ≤ t.val * r := Nat.mul_le_mul_right r (Nat.succ_le_of_lt hjsmall) + have hindex : index r j i = (d - t.val) * r + index r (j.val - (d - t.val)) i := by + have hsplit : j.val = (d - t.val) + (j.val - (d - t.val)) := by omega + calc + index r j i = i.val + r * ((d - t.val) + (j.val - (d - t.val))) := by + rw [← hsplit] + rfl + _ = _ := by unfold index; ring + have hright : (bank.drop ((t.val + 1) * r)).length ≤ index r j i := by + rw [htail, hindex] + omega + have hreg : reg d t j = j.val - (d - t.val) := by + have hsum : t.val + (j.val + 1) = (d + 1) + (j.val - (d - t.val)) := by omega + rw [reg, hsum, Nat.add_mod_left, Nat.mod_eq_of_lt (by omega)] + rw [List.getD_append_right _ _ 0 _ hright, htail, hindex, Nat.add_sub_cancel_left] + simp only [List.getD_eq_getElem?_getD, List.getElem?_take_of_lt hpos, hreg] + +/-- The requested raw point has exactly the field coordinates consumed by the existing gate +extension. Modulus-offset padding contributes zero in the field. -/ +theorem point_toRegs {p d r : ℕ} (bank : List ℕ) (hlen : bank.length = (d + 1) * r) + (t : Fin (d + 1)) (j : Fin d) (i : Fin r) : + ((point d r t bank).getD (index r j i) 0 : ZMod p) = + toRegs p d r bank (Ary.reg t j) i := by + rw [point_getD bank hlen, reg_eq] + rfl + +end Cslib.TreeEval.Ary.Packed.RawController diff --git a/Complexity/Computability/TreeEval/Ary/Packed/RawControllerQuery.lean b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerQuery.lean new file mode 100644 index 0000000000..e12d12105f --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerQuery.lean @@ -0,0 +1,655 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerNative +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.CallSpace +import Complexity.Computability.TreeEval.Ary.IterativeCorrect + +/-! +# Same-input subroutines for the uniform raw evaluator + +The raw controller keeps its context and runtime parameter header while a node or gate-vector +subroutine runs on the same original input. Requests contain only context, parameters, path, +point and chunk information. Initializing the subroutine and extracting its returned reply are +explicitly charged native computations. No input word, tree or truth table is copied into a +request. + +The `initial` argument is a fixed query-machine template state. The simulation theorems start +from arbitrary states with `runFrom`; computing an input-dependent initial state is a separate +operation and is not supplied by fixing that argument. + +## Main results + +* `machine_query_inSpace` through `machine_finish_inSpace`: all five native components. +* `exists_runFrom_root`: execution from actual requested node/gate subroutines. +* `exists_runFrom_root_bounded`: correctness and a bound at every physical clock, including + all supplier execution states. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed.RawController.Query + +open Turing MultiTapeTM ComputableInSpace + +/-- Reader and packing context, with one runtime raw configuration. -/ +abbrev Context (γ : Type) := γ × (Params × State) + +/-- Context and parameters accompany one concrete node or gate request. -/ +abbrev RequestArgs (γ : Type) := γ × Params × Need + +/-- Encode the reader context beside the one uniform controller state. -/ +abbrev encContext {γ : Type} (encG : γ ↪ List Bool) : Context γ ↪ List Bool := + encPair encG encFullState + +/-- Encode a concrete reader request, without the suspended register bank. -/ +abbrev encRequestArgs {γ : Type} (encG : γ ↪ List Bool) : RequestArgs γ ↪ List Bool := + encPair encG (encPair encParams encNeed) + +variable {γ τ : Type} + +/-- Apply a returned node or gate-vector reply, retaining context and parameters. -/ +@[expose] def advance (q : Context γ) (reply : Reply) : Context γ := + (q.1, q.2.1, step q.2.1.1 q.2.1.2.1 q.2.1.2.2 reply q.2.2) + +/-- Halt at a completed return or reject malformed bank storage. -/ +@[expose] def halted (q : Context γ) : Bool := + !validBank q.2.1.1 q.2.1.2.1 q.2.1.2.2 q.2.2.1 || + (q.2.2.2.2.isNone && q.2.2.2.1.isEmpty) + +/-- Ordinary steps consume no input bit; required external data is obtained by subroutine calls. -/ +@[expose] def caller (initial : Context γ) : QueryMachine (Context γ) where + init := initial + query _ := 0 + next p := advance p.1 (0, []) + emit _ := [] + halted := halted + finish _ := [] + +/-- Construct a callee state only when the raw controller actually requests external data. -/ +@[expose] def request (start : RequestArgs γ → τ) (q : Context γ) : Option τ := + (need q.2.1.1 q.2.1.2.1 q.2.1.2.2 q.2.2).map fun req => start (q.1, q.2.1, req) + +/-- The reply is read from the callee's final state, with no stored output stream. -/ +@[expose] def resume (answer : τ → Reply) (q : Context γ) (t : τ) : Context γ := + advance q (answer t) + +/-- The actual raw evaluator caller and its original-input node/gate subroutine. -/ +@[expose] def machine (initial : Context γ) (N : QueryMachine τ) (start : RequestArgs γ → τ) + (answer : τ → Reply) : QueryMachine (Context γ ⊕ (Context γ × τ)) := + (caller initial).withCalls N (request start) (resume answer) + +/-- Encode an active caller or a suspended caller paired with its callee state. -/ +abbrev encMachineState (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) : + Context γ ⊕ (Context γ × τ) ↪ List Bool := + encSum (encContext encG) (encPair (encContext encG) encT) + +/-- A non-requesting raw transition is independent of the default reply supplied to it. -/ +theorem step_reply_independent {p d r : ℕ} (q : State) (hneed : need p d r q = none) + (a b : Reply) : step p d r a q = step p d r b q := by + by_cases hv : validBank p d r q.1 = true + · simp only [need, hv, ↓reduceIte] at hneed + rcases q with ⟨bank, st, mode⟩ + cases mode with + | some ct => simp [needCore] at hneed + | none => + cases st with + | nil => rfl + | cons fr st => + rcases fr with ⟨round, child, undo, coeff, target⟩ + cases undo with + | true => simp [step, hv, stepCore] + | false => + by_cases hj : child + 1 < d + · simp [step, hv, stepCore, hj] + · simp [needCore, hj] at hneed + · simp [step, hv] + +/-- A halted raw controller is stationary for every possible returned reply. -/ +theorem advance_of_halted (q : Context γ) (reply : Reply) (hh : halted q = true) : + advance q reply = q := by + rcases q with ⟨ctx, ⟨p, d, r⟩, bank, st, mode⟩ + by_cases hv : validBank p d r bank = true + · simp only [halted, hv, Bool.not_true, Bool.false_or, Bool.and_eq_true, + Option.isNone_iff_eq_none, List.isEmpty_iff] at hh + rcases hh with ⟨rfl, rfl⟩ + simp [advance, step, hv, stepCore] + · simp [advance, step, hv] + +/-- A pending request always belongs to a live caller. -/ +theorem halted_of_need_some (q : Context γ) (req : Need) + (hneed : need q.2.1.1 q.2.1.2.1 q.2.1.2.2 q.2.2 = some req) : halted q = false := by + rcases q with ⟨ctx, ⟨p, d, r⟩, bank, st, mode⟩ + by_cases hv : validBank p d r bank = true + · simp only [halted, hv, Bool.not_true, Bool.false_or] + cases mode with + | some ct => rfl + | none => + cases st with + | nil => simp [need, hv, needCore] at hneed + | cons fr st => rfl + · simp [need, hv] at hneed + +/-- An ordinary controller transition consumes one physical step, including stationary halts. -/ +theorem runFrom_normal (initial : Context γ) (N : QueryMachine τ) + (start : RequestArgs γ → τ) (answer : τ → Reply) (w : List Bool) (q : Context γ) + (hneed : need q.2.1.1 q.2.1.2.1 q.2.1.2.2 q.2.2 = none) : + (machine initial N start answer).runFrom w (.inl q) 1 = + .inl (advance q (0, [])) := by + have hreq : request start q = none := by simp [request, hneed] + have hnormal := QueryMachine.withCalls_step_normal (caller initial) N + (request start) (resume answer) w q hreq + change ((caller initial).withCalls N (request start) (resume answer)).step w (.inl q) = _ + rw [hnormal] + by_cases hh : halted q = true + · simp only [QueryMachine.step, caller, hh, ↓reduceIte] + rw [advance_of_halted q (0, []) hh] + · simp [QueryMachine.step, caller, hh] + +/-- An actual requested subroutine runs against the original input and returns its reply. -/ +theorem runFrom_requested (initial : Context γ) (N : QueryMachine τ) + (start : RequestArgs γ → τ) (answer : τ → Reply) (w : List Bool) (q : Context γ) + (req : Need) (hneed : need q.2.1.1 q.2.1.2.1 q.2.1.2.2 q.2.2 = some req) (T : ℕ) + (hpre : ∀ i < T, N.halted (N.runFrom w (start (q.1, q.2.1, req)) i) = false) + (hhalt : N.halted (N.runFrom w (start (q.1, q.2.1, req)) T) = true) : + (machine initial N start answer).runFrom w (.inl q) (T + 2) = + .inl (advance q (answer (N.runFrom w (start (q.1, q.2.1, req)) T))) := by + apply QueryMachine.withCalls_runFrom_call + · exact halted_of_need_some q req hneed + · simp [request, hneed] + · exact hpre + · exact hhalt + +private theorem some_budget {α β : Type} {encIn : α ↪ List Bool} {encB : β ↪ List Bool} + {f : α → β} {S : α → ℕ} (hf : ComputableInSpace f encIn encB S) : + ComputableInSpace (fun a => some (f a)) encIn (encOption encB) S := + budget_comp_linear + (of_inO (ComputableInDegree.optionSome (enc₁ := encB) (enc := encOption encB)).inO_one) hf + +private theorem request_inSpace (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (start : RequestArgs γ → τ) + (hstart : ComputableInSpace start (encRequestArgs encG) encT + (fun q => (encRequestArgs encG q).length)) : + ComputableInSpace (request start) (encContext encG) (encOption encT) + (fun q => (encContext encG q).length) := by + have hctx := fst (encContext encG) encNeed + have hmeta := fst' hctx + have hpar := fst' (snd' hctx) + have hreq := snd (encContext encG) encNeed + have harg := budget_prod hmeta (budget_prod hpar hreq) + have hsome := some_budget (budget_comp_linear hstart harg) + have hnone : ComputableInSpace (fun _ : Context γ => (none : Option τ)) + (encContext encG) (encOption encT) (fun q => (encContext encG q).length) := + budget_const none (.refl _) + have hid : ComputableInSpace (fun q : Context γ => q) (encContext encG) (encContext encG) + (fun q => (encContext encG q).length) := of_inO ComputableInDegree.id'.inO_one + have hneed := budget_comp_linear need_inSpace (snd encG encFullState) + refine (budget_comp_linear (optionElimSnd hnone hsome) (budget_prod hid hneed)).of_enc_eq ?_ + intro q + cases hn : need q.2.1.1 q.2.1.2.1 q.2.1.2.2 q.2.2 <;> simp [request, hn] + +private theorem advance_inSpace (encG : γ ↪ List Bool) : + ComputableInSpace (fun a : Context γ × Reply => advance a.1 a.2) + (encPair (encContext encG) encReply) (encContext encG) + (fun a => (encPair (encContext encG) encReply a).length) := by + have hq := fst (encContext encG) encReply + have hmeta := fst' hq + have hfull := snd' hq + have hreply := snd (encContext encG) encReply + exact (budget_prod hmeta + (budget_comp_linear step_inSpace (budget_prod hfull hreply))).of_enc_eq fun _ => rfl + +private theorem resume_inSpace (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (answer : τ → Reply) + (hanswer : ComputableInSpace answer encT encReply (fun t => (encT t).length)) : + ComputableInSpace (fun a : Context γ × τ => resume answer a.1 a.2) + (encPair (encContext encG) encT) (encContext encG) + (fun a => (encPair (encContext encG) encT a).length) := by + have hq := fst (encContext encG) encT + have ht := snd (encContext encG) encT + exact budget_comp_linear (advance_inSpace encG) + (budget_prod hq (budget_comp_linear hanswer ht)) + +private theorem caller_next_inSpace (encG : γ ↪ List Bool) (initial : Context γ) : + ComputableInSpace (caller initial).next (encPair (encContext encG) (encOption encBool)) + (encContext encG) (fun a => (encPair (encContext encG) (encOption encBool) a).length) := by + have h := budget_comp_linear (advance_inSpace encG) + (budget_prod (fst (encContext encG) (encOption encBool)) + (budget_const (encB := encReply) (0, []) (.refl _))) + exact h.of_enc_eq fun _ => rfl + +private theorem caller_halted_inSpace (encG : γ ↪ List Bool) (initial : Context γ) : + ComputableInSpace (caller initial).halted (encContext encG) encBool + (fun q => (encContext encG q).length) := by + have h := budget_comp_linear halted_inSpace (snd encG encFullState) + exact h.of_enc_eq fun _ => rfl + +/-- The concrete same-input evaluator's native query component. -/ +theorem machine_query_inSpace (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (initial : Context γ) (N : QueryMachine τ) + (start : RequestArgs γ → τ) (answer : τ → Reply) + (hquery : ComputableInSpace N.query encT encNat (fun t => (encT t).length)) + (hstart : ComputableInSpace start (encRequestArgs encG) encT + (fun q => (encRequestArgs encG q).length)) : + ComputableInSpace (machine initial N start answer).query (encMachineState encG encT) encNat + (fun q => (encMachineState encG encT q).length) := + QueryMachine.withCalls_query_inSpace _ _ _ _ _ _ + (budget_const 0 (.refl _)) hquery (request_inSpace encG encT start hstart) + +/-- The concrete evaluator charges caller computation, supplier execution and reply extraction. -/ +theorem machine_next_inSpace (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (initial : Context γ) (N : QueryMachine τ) + (start : RequestArgs γ → τ) (answer : τ → Reply) + (hnext : ComputableInSpace N.next (encPair encT (encOption encBool)) encT + (fun q => (encPair encT (encOption encBool) q).length)) + (hhalt : ComputableInSpace N.halted encT encBool (fun t => (encT t).length)) + (hstart : ComputableInSpace start (encRequestArgs encG) encT + (fun q => (encRequestArgs encG q).length)) + (hanswer : ComputableInSpace answer encT encReply (fun t => (encT t).length)) : + ComputableInSpace (machine initial N start answer).next + (encPair (encMachineState encG encT) (encOption encBool)) (encMachineState encG encT) + (fun q => (encPair (encMachineState encG encT) (encOption encBool) q).length) := + QueryMachine.withCalls_next_inSpace _ _ _ _ _ _ (caller_next_inSpace encG initial) + hnext hhalt (request_inSpace encG encT start hstart) (resume_inSpace encG encT answer hanswer) + +/-- State-returning evaluation emits neither the supplier stream nor an intermediate result. -/ +theorem machine_emit_inSpace (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (initial : Context γ) (N : QueryMachine τ) + (start : RequestArgs γ → τ) (answer : τ → Reply) + (hstart : ComputableInSpace start (encRequestArgs encG) encT + (fun q => (encRequestArgs encG q).length)) : + ComputableInSpace (machine initial N start answer).emit + (encPair (encMachineState encG encT) (encOption encBool)) encBits + (fun q => (encPair (encMachineState encG encT) (encOption encBool) q).length) := + QueryMachine.withCalls_emit_inSpace _ _ _ _ _ _ (budget_const [] (.refl _)) + (request_inSpace encG encT start hstart) + +/-- The composite halting test uses the actual native raw-controller guard. -/ +theorem machine_halted_inSpace (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (initial : Context γ) (N : QueryMachine τ) + (start : RequestArgs γ → τ) (answer : τ → Reply) : + ComputableInSpace (machine initial N start answer).halted (encMachineState encG encT) encBool + (fun q => (encMachineState encG encT q).length) := + QueryMachine.withCalls_halted_inSpace _ _ _ _ _ _ (caller_halted_inSpace encG initial) + +/-- The result remains in the returned raw state, with no implicit captured output word. -/ +theorem machine_finish_inSpace (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (initial : Context γ) (N : QueryMachine τ) + (start : RequestArgs γ → τ) (answer : τ → Reply) : + ComputableInSpace (machine initial N start answer).finish (encMachineState encG encT) encBits + (fun q => (encMachineState encG encT q).length) := + QueryMachine.withCalls_finish_inSpace _ _ _ _ _ _ (budget_const [] (.refl _)) + +/-- A suspended call stores exactly one caller and one callee. Every physical clock, including +entry, the callee run and return, has the stated bound when both boundary callers fit `R`. -/ +theorem runFrom_requested_encoding_le (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (initial : Context γ) (N : QueryMachine τ) (start : RequestArgs γ → τ) + (answer : τ → Reply) (w : List Bool) (q : Context γ) (req : Need) + (hneed : need q.2.1.1 q.2.1.2.1 q.2.1.2.2 q.2.2 = some req) (T R B : ℕ) + (hpre : ∀ i < T, N.halted (N.runFrom w (start (q.1, q.2.1, req)) i) = false) + (hhalt : N.halted (N.runFrom w (start (q.1, q.2.1, req)) T) = true) + (hcaller : (encContext encG q).length ≤ R) + (hreturn : (encContext encG + (advance q (answer (N.runFrom w (start (q.1, q.2.1, req)) T)))).length ≤ R) + (hcallee : ∀ i ≤ T, (encT (N.runFrom w (start (q.1, q.2.1, req)) i)).length ≤ B) + (i : ℕ) (hi : i ≤ T + 2) : + (encMachineState encG encT ((machine initial N start answer).runFrom w (.inl q) i)).length ≤ + 2 * R + B + 2 := by + by_cases hizero : i = 0 + · subst i + simp only [QueryMachine.runFrom_zero, encMachineState, encSum_inl, List.length_cons] + omega + by_cases hifinal : i = T + 2 + · subst i + rw [runFrom_requested initial N start answer w q req hneed T hpre hhalt] + simp only [encMachineState, encSum_inl, List.length_cons] + omega + have hiT : i - 1 ≤ T := by omega + have hentry : (machine initial N start answer).runFrom w (.inl q) 1 = + .inr (q, start (q.1, q.2.1, req)) := by + apply QueryMachine.withCalls_step_call + · exact halted_of_need_some q req hneed + · simp [request, hneed] + rw [show i = 1 + (i - 1) by omega, QueryMachine.runFrom_add, hentry] + have hprefix := QueryMachine.withCalls_runFrom_subroutine_prefix (caller initial) N + (request start) (resume answer) w q (start (q.1, q.2.1, req)) (i - 1) + (fun k hk => hpre k (by omega)) + rw [show (machine initial N start answer).runFrom w + (.inr (q, start (q.1, q.2.1, req))) (i - 1) = + .inr (q, N.runFrom w (start (q.1, q.2.1, req)) (i - 1)) from hprefix] + rw [show (encMachineState encG encT + (.inr (q, N.runFrom w (start (q.1, q.2.1, req)) (i - 1)))).length = + 2 * (encContext encG q).length + + (encT (N.runFrom w (start (q.1, q.2.1, req)) (i - 1))).length + 2 from + QueryMachine.withCalls_encoding_inr_length (encContext encG) encT _ _] + have hc := hcallee (i - 1) hiT + omega + +/-- Reader context, one packed caller and the peak supplier state, including call framing. -/ +@[expose] def queryStateBound (contextBits p d r height supplierBits : ℕ) : ℕ := + 4 * contextBits + 2 * fullSpaceBound p d r height + supplierBits + 4 + +/-- The selected field parameters give one linear bank plus logarithmic control per level. -/ +theorem queryStateBound_packed_le (contextBits supplierBits : ℕ) {d b H : ℕ} + (hd : 0 < d) (hb : 0 < b) : + queryStateBound contextBits (fieldModulus d b) d (chunkCount d b) H supplierBits ≤ + 4 * contextBits + + 200 * (d + 1) * (b + H * (chunkWidth d b + 1) + d.size + 1) + supplierBits + 4 := by + have h := fullSpaceBound_packed_le_linear (H := H) hd hb + unfold queryStateBound + calc + _ ≤ 4 * contextBits + + 2 * (100 * (d + 1) * (b + H * (chunkWidth d b + 1) + d.size + 1)) + + supplierBits + 4 := by omega + _ = _ := by ring + +section Correctness + +variable {p d r b : ℕ} [Fact p.Prime] + +/-- A canonical state with no request needs no information from an external supplier. -/ +theorem replyCorrect_of_need_none + (e : (Fin b → Bool) → Fin r → ZMod p) + (E : Gate d b → (Fin d → Fin r → ZMod p) → Fin r → ZMod p) + (read : List (Fin d) → Tree.Node d b) (q : Ary.Iter.Cfg (ZMod p) d r) + (hneed : need p d r (ofCfg q) = none) (reply : Reply) : + ReplyCorrect e E read q reply := by + rcases q with ⟨S, stack, mode⟩ + have hvalid := validBank_ofRegs (Fact.out : p.Prime).two_le S + cases mode with + | call c t => simp [need, ofCfg, ofMode, hvalid, needCore] at hneed + | ret => + cases stack with + | nil => trivial + | cons fr st => + by_cases hu : fr.undo = false ∧ ¬fr.child.val + 1 < d + · obtain ⟨hundo, hchild⟩ := hu + simp [need, ofCfg, ofMode, hvalid, needCore, ofFrame, hundo, hchild] at hneed + · simp only [ReplyCorrect, hu, ↓reduceIte] + +private theorem advance_ofCfg (hd : 0 < d) + (e : (Fin b → Bool) → Fin r → ZMod p) + (E : Gate d b → (Fin d → Fin r → ZMod p) → Fin r → ZMod p) + (read : List (Fin d) → Tree.Node d b) (ctx : γ) (q : Ary.Iter.Cfg (ZMod p) d r) + (reply : Reply) (hreply : ReplyCorrect e E read q reply) + (hround : ∀ fr ∈ q.stack, fr.round < p - 1) : + advance (ctx, (p, d, r), ofCfg q) reply = + (ctx, (p, d, r), ofCfg (Ary.Iter.step hd e E (units p) read q)) := by + rw [advance, step_ofCfg hd e E read q reply hreply hround] + +/-- Only actual requested states require the supplier contract. Non-requesting control steps +use a proved reply-independent transition. Each callee run uses the very same original `w`. -/ +theorem exists_runFrom_typedPrefix (initial : Context γ) (N : QueryMachine τ) + (start : RequestArgs γ → τ) (answer : τ → Reply) (w : List Bool) (ctx : γ) + (hd : 0 < d) (e : (Fin b → Bool) → Fin r → ZMod p) + (E : Gate d b → (Fin d → Fin r → ZMod p) → Fin r → ZMod p) + {H : ℕ} (T : Tree d b H) (S : Ary.State (ZMod p) d r) (c : ZMod p) (t : Fin (d + 1)) + (hsupplier : ∀ n req, + need p d r (ofCfg + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n)) = some req → + ∃ U, + (∀ i < U, N.halted (N.runFrom w (start (ctx, (p, d, r), req)) i) = false) ∧ + N.halted (N.runFrom w (start (ctx, (p, d, r), req)) U) = true ∧ + ReplyCorrect e E T.nodeAt + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n) + (answer (N.runFrom w (start (ctx, (p, d, r), req)) U))) (n : ℕ) : + ∃ clock, (machine initial N start answer).runFrom w + (.inl (ctx, (p, d, r), ofCfg (Ary.Iter.initialCfg S c t))) clock = + .inl (ctx, (p, d, r), + ofCfg (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n)) := by + induction n with + | zero => exact ⟨0, rfl⟩ + | succ n ih => + obtain ⟨clock, hclock⟩ := ih + let typed := Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n + let q : Context γ := (ctx, (p, d, r), ofCfg typed) + have hround : ∀ fr ∈ typed.stack, fr.round < p - 1 := by + have hb := Ary.Iter.runFrom_initial_bounds T hd e E (units p) S c t n + intro fr hfr + have hr := hb.2 fr hfr + simpa only [units, List.length_map, List.length_range] using hr + have hmacro : ∃ U, (machine initial N start answer).runFrom w (.inl q) U = + .inl (ctx, (p, d, r), ofCfg (Ary.Iter.step hd e E (units p) T.nodeAt typed)) := by + cases hn : need p d r (ofCfg typed) with + | none => + have hreply := replyCorrect_of_need_none e E T.nodeAt typed hn (0, []) + refine ⟨1, ?_⟩ + rw [runFrom_normal initial N start answer w q hn, + advance_ofCfg hd e E T.nodeAt ctx typed (0, []) hreply hround] + | some req => + obtain ⟨U, hpre, hhalt, hreply⟩ := hsupplier n req hn + refine ⟨U + 2, ?_⟩ + rw [runFrom_requested initial N start answer w q req hn U hpre hhalt, + advance_ofCfg hd e E T.nodeAt ctx typed _ hreply hround] + obtain ⟨U, hU⟩ := hmacro + refine ⟨clock + U, ?_⟩ + rw [QueryMachine.runFrom_add, hclock] + exact hU.trans (congrArg (fun typed => Sum.inl (ctx, (p, d, r), ofCfg typed)) + (Ary.Iter.runFrom_succ hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n).symm) + +/-- The actual same-input query machine realizes the existing complete masking procedure. +This is an execution theorem for a certified supplier contract, not a standalone class bound +obtained by capturing the input-dependent initial parameters in the machine's fixed `init`. -/ +theorem exists_runFrom_root (initial : Context γ) (N : QueryMachine τ) + (start : RequestArgs γ → τ) (answer : τ → Reply) (w : List Bool) (ctx : γ) + (hd : 0 < d) (e : (Fin b → Bool) → Fin r → ZMod p) + (E : Gate d b → (Fin d → Fin r → ZMod p) → Fin r → ZMod p) + {H : ℕ} (T : Tree d b H) (S : Ary.State (ZMod p) d r) (c : ZMod p) (t : Fin (d + 1)) + (hsupplier : ∀ n req, + need p d r (ofCfg + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n)) = some req → + ∃ U, + (∀ i < U, N.halted (N.runFrom w (start (ctx, (p, d, r), req)) i) = false) ∧ + N.halted (N.runFrom w (start (ctx, (p, d, r), req)) U) = true ∧ + ReplyCorrect e E T.nodeAt + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n) + (answer (N.runFrom w (start (ctx, (p, d, r), req)) U))) : + ∃ clock, (machine initial N start answer).runFrom w + (.inl (ctx, (p, d, r), ofCfg (Ary.Iter.initialCfg S c t))) clock = + .inl (ctx, (p, d, r), + ofCfg (⟨Ary.procWith e E (units p) T c t S, [], .ret⟩ : + Ary.Iter.Cfg (ZMod p) d r)) := by + obtain ⟨n, hn⟩ := Ary.Iter.exists_runFrom_root hd e E (units p) T S c t + obtain ⟨clock, hclock⟩ := exists_runFrom_typedPrefix initial N start answer w ctx + hd e E T S c t hsupplier n + exact ⟨clock, hclock.trans (congrArg + (fun typed => Sum.inl (ctx, (p, d, r), ofCfg typed)) hn)⟩ + +/-- The complete physical prefix bound includes ordinary controller steps, call entry, every +supplier step and return. The context and header are charged in `R`; actual callee states are +charged in `B` only for the requests that occur. -/ +theorem exists_runFrom_typedPrefix_bounded (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (initial : Context γ) (N : QueryMachine τ) + (start : RequestArgs γ → τ) (answer : τ → Reply) (w : List Bool) (ctx : γ) + (hd : 0 < d) (e : (Fin b → Bool) → Fin r → ZMod p) + (E : Gate d b → (Fin d → Fin r → ZMod p) → Fin r → ZMod p) + {H : ℕ} (T : Tree d b H) (S : Ary.State (ZMod p) d r) (c : ZMod p) (t : Fin (d + 1)) + (R B : ℕ) + (hcaller : ∀ n, (encContext encG (ctx, (p, d, r), ofCfg + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n))).length ≤ R) + (hsupplier : ∀ n req, + need p d r (ofCfg + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n)) = some req → + ∃ U, + (∀ i < U, N.halted (N.runFrom w (start (ctx, (p, d, r), req)) i) = false) ∧ + N.halted (N.runFrom w (start (ctx, (p, d, r), req)) U) = true ∧ + ReplyCorrect e E T.nodeAt + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n) + (answer (N.runFrom w (start (ctx, (p, d, r), req)) U)) ∧ + (∀ i ≤ U, + (encT (N.runFrom w (start (ctx, (p, d, r), req)) i)).length ≤ B)) (n : ℕ) : + ∃ clock, (machine initial N start answer).runFrom w + (.inl (ctx, (p, d, r), ofCfg (Ary.Iter.initialCfg S c t))) clock = + .inl (ctx, (p, d, r), + ofCfg (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n)) ∧ + ∀ i ≤ clock, (encMachineState encG encT + ((machine initial N start answer).runFrom w + (.inl (ctx, (p, d, r), ofCfg (Ary.Iter.initialCfg S c t))) i)).length ≤ + 2 * R + B + 2 := by + induction n with + | zero => + refine ⟨0, rfl, ?_⟩ + intro i hi + have hizero : i = 0 := by omega + subst i + have hq := hcaller 0 + simp only [Ary.Iter.runFrom_zero] at hq + simp only [QueryMachine.runFrom_zero, encMachineState, encSum_inl, List.length_cons] + omega + | succ n ih => + obtain ⟨clock, hclock, hclockBound⟩ := ih + let typed := Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n + let typedNext := + Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) (n + 1) + let q : Context γ := (ctx, (p, d, r), ofCfg typed) + let qnext : Context γ := (ctx, (p, d, r), ofCfg typedNext) + have hq : (encContext encG q).length ≤ R := hcaller n + have hqnext : (encContext encG qnext).length ≤ R := hcaller (n + 1) + have hround : ∀ fr ∈ typed.stack, fr.round < p - 1 := by + have hb := Ary.Iter.runFrom_initial_bounds T hd e E (units p) S c t n + intro fr hfr + have hr := hb.2 fr hfr + simpa only [units, List.length_map, List.length_range] using hr + have hmacro : ∃ U, (machine initial N start answer).runFrom w (.inl q) U = .inl qnext ∧ + ∀ i ≤ U, (encMachineState encG encT + ((machine initial N start answer).runFrom w (.inl q) i)).length ≤ 2 * R + B + 2 := by + cases hn : need p d r (ofCfg typed) with + | none => + have hreply := replyCorrect_of_need_none e E T.nodeAt typed hn (0, []) + have hadvance : advance q (0, []) = qnext := + (advance_ofCfg hd e E T.nodeAt ctx typed (0, []) hreply hround).trans + (congrArg (fun typed => (ctx, (p, d, r), ofCfg typed)) + (Ary.Iter.runFrom_succ hd e E (units p) T.nodeAt + (Ary.Iter.initialCfg S c t) n).symm) + have hnormal := runFrom_normal initial N start answer w q hn + rw [hadvance] at hnormal + refine ⟨1, hnormal, ?_⟩ + intro i hi + by_cases hizero : i = 0 + · subst i + simp only [QueryMachine.runFrom_zero, encMachineState, encSum_inl, List.length_cons] + omega + · have hione : i = 1 := by omega + subst i + rw [hnormal] + simp only [encMachineState, encSum_inl, List.length_cons] + omega + | some req => + obtain ⟨U, hpre, hhalt, hreply, hcallee⟩ := hsupplier n req hn + have hadvance : advance q + (answer (N.runFrom w (start (ctx, (p, d, r), req)) U)) = qnext := + (advance_ofCfg hd e E T.nodeAt ctx typed _ hreply hround).trans + (congrArg (fun typed => (ctx, (p, d, r), ofCfg typed)) + (Ary.Iter.runFrom_succ hd e E (units p) T.nodeAt + (Ary.Iter.initialCfg S c t) n).symm) + have hreturn : (encContext encG (advance q + (answer (N.runFrom w (start (ctx, (p, d, r), req)) U)))).length ≤ R := by + rw [hadvance] + exact hqnext + refine ⟨U + 2, ?_, ?_⟩ + · rw [runFrom_requested initial N start answer w q req hn U hpre hhalt, hadvance] + · exact fun i hi => runFrom_requested_encoding_le encG encT initial N start answer + w q req hn U R B hpre hhalt hq hreturn hcallee i hi + obtain ⟨U, hU, hUBound⟩ := hmacro + refine ⟨clock + U, ?_, ?_⟩ + · rw [QueryMachine.runFrom_add, hclock] + exact hU + · intro i hi + by_cases hiclock : i ≤ clock + · exact hclockBound i hiclock + · rw [show i = clock + (i - clock) by omega, QueryMachine.runFrom_add, hclock] + exact hUBound (i - clock) (by omega) + +/-- The actual same-input evaluator reaches the masking result with a bound on every physical +state, under native-supplier state bounds and the separately charged caller representation. -/ +theorem exists_runFrom_root_bounded (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (initial : Context γ) (N : QueryMachine τ) + (start : RequestArgs γ → τ) (answer : τ → Reply) (w : List Bool) (ctx : γ) + (hd : 0 < d) (e : (Fin b → Bool) → Fin r → ZMod p) + (E : Gate d b → (Fin d → Fin r → ZMod p) → Fin r → ZMod p) + {H : ℕ} (T : Tree d b H) (S : Ary.State (ZMod p) d r) (c : ZMod p) (t : Fin (d + 1)) + (R B : ℕ) + (hcaller : ∀ n, (encContext encG (ctx, (p, d, r), ofCfg + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n))).length ≤ R) + (hsupplier : ∀ n req, + need p d r (ofCfg + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n)) = some req → + ∃ U, + (∀ i < U, N.halted (N.runFrom w (start (ctx, (p, d, r), req)) i) = false) ∧ + N.halted (N.runFrom w (start (ctx, (p, d, r), req)) U) = true ∧ + ReplyCorrect e E T.nodeAt + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n) + (answer (N.runFrom w (start (ctx, (p, d, r), req)) U)) ∧ + (∀ i ≤ U, (encT (N.runFrom w (start (ctx, (p, d, r), req)) i)).length ≤ B)) : + ∃ clock, (machine initial N start answer).runFrom w + (.inl (ctx, (p, d, r), ofCfg (Ary.Iter.initialCfg S c t))) clock = + .inl (ctx, (p, d, r), ofCfg + (⟨Ary.procWith e E (units p) T c t S, [], .ret⟩ : Ary.Iter.Cfg (ZMod p) d r)) ∧ + ∀ i ≤ clock, (encMachineState encG encT + ((machine initial N start answer).runFrom w + (.inl (ctx, (p, d, r), ofCfg (Ary.Iter.initialCfg S c t))) i)).length ≤ + 2 * R + B + 2 := by + obtain ⟨n, hn⟩ := Ary.Iter.exists_runFrom_root hd e E (units p) T S c t + obtain ⟨clock, hclock, hbound⟩ := exists_runFrom_typedPrefix_bounded encG encT initial N + start answer w ctx hd e E T S c t R B hcaller hsupplier n + refine ⟨clock, ?_, hbound⟩ + exact hclock.trans (congrArg (fun typed => Sum.inl (ctx, (p, d, r), ofCfg typed)) hn) + +/-- The concrete evaluator's physical-state bound, with caller storage discharged by the +actual padded representation theorem rather than assumed separately. Only the actual requested +supplier computations and their peak states remain in the contract. -/ +theorem exists_runFrom_root_encoded_bound (encG : γ ↪ List Bool) (encT : τ ↪ List Bool) + (initial : Context γ) (N : QueryMachine τ) + (start : RequestArgs γ → τ) (answer : τ → Reply) (w : List Bool) (ctx : γ) + (hd : 0 < d) (e : (Fin b → Bool) → Fin r → ZMod p) + (E : Gate d b → (Fin d → Fin r → ZMod p) → Fin r → ZMod p) + {H : ℕ} (T : Tree d b H) (S : Ary.State (ZMod p) d r) (c : ZMod p) (t : Fin (d + 1)) + (B : ℕ) + (hsupplier : ∀ n req, + need p d r (ofCfg + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n)) = some req → + ∃ U, + (∀ i < U, N.halted (N.runFrom w (start (ctx, (p, d, r), req)) i) = false) ∧ + N.halted (N.runFrom w (start (ctx, (p, d, r), req)) U) = true ∧ + ReplyCorrect e E T.nodeAt + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n) + (answer (N.runFrom w (start (ctx, (p, d, r), req)) U)) ∧ + (∀ i ≤ U, (encT (N.runFrom w (start (ctx, (p, d, r), req)) i)).length ≤ B)) : + ∃ clock, (machine initial N start answer).runFrom w + (.inl (ctx, (p, d, r), ofCfg (Ary.Iter.initialCfg S c t))) clock = + .inl (ctx, (p, d, r), ofCfg + (⟨Ary.procWith e E (units p) T c t S, [], .ret⟩ : Ary.Iter.Cfg (ZMod p) d r)) ∧ + ∀ i ≤ clock, (encMachineState encG encT + ((machine initial N start answer).runFrom w + (.inl (ctx, (p, d, r), ofCfg (Ary.Iter.initialCfg S c t))) i)).length ≤ + queryStateBound (encG ctx).length p d r H B := by + have hcaller (n : ℕ) : + (encContext encG (ctx, (p, d, r), ofCfg + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n))).length ≤ + 2 * (encG ctx).length + 1 + fullSpaceBound p d r H := by + have hc := Ary.Iter.runFrom_initial_bounds T hd e E (units p) S c t n + have hc' : Ary.Iter.ControlBounds H (p - 1) + (Ary.Iter.runFrom hd e E (units p) T.nodeAt (Ary.Iter.initialCfg S c t) n) := by + simpa only [units, List.length_map, List.length_range] using hc + have hr := length_encFullState_ofCfg_le _ hc' + rw [length_encPair] + dsimp only [Prod.fst, Prod.snd] + omega + obtain ⟨clock, hresult, hbound⟩ := exists_runFrom_root_bounded encG encT initial N start + answer w ctx hd e E T S c t (2 * (encG ctx).length + 1 + fullSpaceBound p d r H) + B hcaller hsupplier + refine ⟨clock, hresult, ?_⟩ + intro i hi + have h := hbound i hi + unfold queryStateBound + omega + +end Correctness + +end Cslib.TreeEval.Ary.Packed.RawController.Query diff --git a/Complexity/Computability/TreeEval/Ary/Packed/RawControllerSpace.lean b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerSpace.lean new file mode 100644 index 0000000000..26b3f8df9e --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerSpace.lean @@ -0,0 +1,426 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed.RawController +public import Complexity.Computability.TreeEval.Reader.Cert.Residue + +/-! +# Native streamed updates of the flat register bank + +Indexed bank updates are streamed with a binary index and an output prefix. No list of indices +is materialized. The budget charges the possible field width of every output cell; the raw +controller's padding guard will bound that charge by the already encoded input bank. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed.RawController + +open Turing MultiTapeTM ComputableInSpace Reader + +private abbrev BankPar := (ℕ × ℕ × ℕ × ℕ) × List ℕ × Bool × Bool +private abbrev encBankPar : BankPar ↪ List Bool := + encPair (encPair encNat (encPair encNat (encPair encNat encNat))) + (encPair (encList encNat) (encPair encBool encBool)) +private abbrev encBankAcc : (ℕ × List ℕ) ↪ List Bool := encPair encNat (encList encNat) + +private def bankCell (q : BankPar) (k v : ℕ) : ℕ := + if q.1.1 = 0 then v else + if k / q.1.2.1 = q.1.2.2.1 then + q.1.1 + if q.2.2.1 then + addMod q.1.1 v (if q.2.2.2 then + negMod q.1.1 (mulMod q.1.1 q.1.2.2.2 (q.2.1.getD (k % q.1.2.1) 0)) + else mulMod q.1.1 q.1.2.2.2 (q.2.1.getD (k % q.1.2.1) 0)) + else mulMod q.1.1 q.1.2.2.2 v + else v + +private def bankFoldStep (q : BankPar) (st : ℕ × List ℕ) (v : ℕ) : ℕ × List ℕ := + (st.1 + 1, st.2 ++ [bankCell q st.1 v]) + +private theorem foldl_bankFoldStep (q : BankPar) (l : List ℕ) (j : ℕ) (out : List ℕ) : + l.foldl (bankFoldStep q) (j, out) = + (j + l.length, out ++ l.mapIdx (fun k v => bankCell q (j + k) v)) := by + induction l generalizing j out with + | nil => simp + | cons a l ih => + rw [List.foldl_cons, bankFoldStep, ih] + simp only [List.length_cons, List.mapIdx_cons] + have hf : (fun k v => bankCell q (j + 1 + k) v) = + (fun k v => bankCell q (j + (k + 1)) v) := by + funext k v + congr 1 + omega + rw [hf] + simp only [List.append_assoc, List.singleton_append] + congr 1 + omega + +private theorem bankCell_length (q : BankPar) (k v : ℕ) : + (encNat (bankCell q k v)).length ≤ (encNat v).length + + 2 * (encNat q.1.1).length + 1 := by + by_cases hp : q.1.1 = 0 + · simp [bankCell, hp] + by_cases hk : k / q.1.2.1 = q.1.2.2.1 + · have hres : bankCell q k v < q.1.1 + q.1.1 := by + simp only [bankCell, hp, ↓reduceIte, hk] + split_ifs <;> simp only [addMod, mulMod] <;> + exact Nat.add_lt_add_left (Nat.mod_lt _ (Nat.pos_of_ne_zero hp)) _ + have hb := length_encNat_mono hres.le + have ha := length_encNat_add_le_add q.1.1 q.1.1 + omega + · simp only [bankCell, hp, hk, ↓reduceIte] + omega + +private theorem length_bankMap (q : BankPar) (l : List ℕ) (j : ℕ) : + (encList encNat (l.mapIdx (fun k v => bankCell q (j + k) v))).length ≤ + (encList encNat l).length + l.length * (4 * (encNat q.1.1).length + 2) := by + induction l generalizing j with + | nil => simp + | cons v l ih => + have hc := bankCell_length q j v + have ht := ih (j + 1) + have hf : (fun k v => bankCell q (j + (k + 1)) v) = + (fun k v => bankCell q (j + 1 + k) v) := by + funext k v + congr 1 + omega + simp only [List.mapIdx_cons, hf, encList_cons, List.length_append, length_frame, + List.length_cons, Nat.add_zero] + nlinarith + +private theorem bankFoldStep_inSpace : + ComputableInSpace (fun a : (BankPar × (ℕ × List ℕ)) × ℕ => + bankFoldStep a.1.1 a.1.2 a.2) + (encPair (encPair encBankPar encBankAcc) encNat) encBankAcc + (fun a => (encPair (encPair encBankPar encBankAcc) encNat a).length) := by + have hqa := fst (encPair encBankPar encBankAcc) encNat + have hq := fst' hqa + have hst := snd' hqa + have hj := fst' hst + have hout := snd' hst + have hv := snd (encPair encBankPar encBankAcc) encNat + have hpars := fst' hq + have hp := fst' hpars + have hrest := snd' hpars + have hr := fst' hrest + have ht := fst' (snd' hrest) + have hc := snd' (snd' hrest) + have hvals := fst' (snd' hq) + have hflags := snd' (snd' hq) + have huse := fst' hflags + have hsub := snd' hflags + have hval := budget_comp_lin getD (linear_mul 8) + (budget_prod hvals (budget_natMod hj hr)) + have hprod := budget_comp_linear mulMod_inSpace + (budget_prod hp (budget_prod hc hval)) + have hneg := budget_comp_linear negMod_inSpace (budget_prod hp hprod) + have hadd := budget_comp_linear addMod_inSpace + (budget_prod hp (budget_prod hv (budget_ite hsub hneg hprod))) + have hscale := budget_comp_linear mulMod_inSpace (budget_prod hp (budget_prod hc hv)) + have hupdated := budget_natAdd hp (budget_ite huse hadd hscale) + have hcell := budget_ite (budget_natIsZero hp) hv + (budget_ite (budget_natEq (budget_natDiv hj hr) ht) hupdated hv) + refine (budget_prod (budget_natSucc hj) (budget_snoc hout hcell)).of_enc_eq ?_ + intro a + by_cases hp : a.1.1.1.1 = 0 <;> + by_cases hk : a.1.2.1 / a.1.1.1.2.1 = a.1.1.1.2.2.1 <;> + simp [bankFoldStep, bankCell, hp, hk] + +/-- A streamed indexed update charges only the input and the possible field width per cell. +The zero-modulus branch retains the input bank, so malformed parameters do not expand cells. -/ +private def bankUpdateSpace (q : BankPar × List ℕ) : ℕ := + (encPair encBankPar (encList encNat) q).length + + q.2.length * ((encNat q.1.1.1).length + 1) + 1 + +private theorem bankMap_inSpace : + ComputableInSpace (fun q : BankPar × List ℕ => q.2.mapIdx (bankCell q.1)) + (encPair encBankPar (encList encNat)) (encList encNat) bankUpdateSpace := by + let encIn := encPair encBankPar (encList encNat) + have hpar := fst encBankPar (encList encNat) + have hlist := snd encBankPar (encList encNat) + have hinit : ComputableInSpace (fun _ : BankPar × List ℕ => (0, ([] : List ℕ))) + encIn encBankAcc (fun q => (encIn q).length) := budget_const (0, []) (.refl _) + have hstate (q : BankPar × List ℕ) (i : ℕ) (hi : i ≤ q.2.length) : + (encBankAcc ((q.2.take i).foldl (bankFoldStep q.1) (0, []))).length ≤ + 16 * bankUpdateSpace q := by + rw [foldl_bankFoldStep] + have hc := length_encNat_le_self (q.2.take i).length + have hl := length_bankMap q.1 (q.2.take i) 0 + have htake := length_encList_sublist encNat (List.take_sublist i q.2) + have hn := length_le_length_encList encNat q.2 + have hmul := Nat.mul_le_mul_right (4 * (encNat q.1.1.1).length + 2) + (List.length_take_le i q.2) + simp only [Nat.zero_add, List.nil_append, encBankAcc, length_encPair] at * + dsimp only [bankUpdateSpace] + simp only [length_encPair] + nlinarith + have hspace (q : BankPar × List ℕ) (i : ℕ) (hi : i < q.2.length) : + (encPair (encPair encBankPar encBankAcc) encNat + ((q.1, (q.2.take i).foldl (bankFoldStep q.1) (0, [])), q.2[i])).length ≤ + 128 * bankUpdateSpace q := by + have hs := hstate q i hi.le + have hv := length_le_encList_of_mem encNat (List.getElem_mem hi) + change (encNat q.2[i]).length ≤ (encList encNat q.2).length at hv + simp only [length_encPair] + dsimp only [bankUpdateSpace] at hs ⊢ + simp only [length_encPair] at hs ⊢ + omega + have hfold := ComputableInSpace.foldlWith hpar hlist hinit bankFoldStep_inSpace hspace hstate + refine (snd' hfold).weaken ?_ |>.of_enc_eq ?_ + · refine .of_le_linear 256 256 fun q => ?_ + have hb : (encIn q).length ≤ bankUpdateSpace q := by + dsimp only [bankUpdateSpace, encIn] + omega + have hp := length_le_encPair_left encBankPar (encList encNat) q + have hl := length_le_encPair_right encBankPar (encList encNat) q + dsimp only [encIn] at hb ⊢ + omega + · intro q + rw [foldl_bankFoldStep] + simp only [Nat.zero_add, List.nil_append] + +/-- Padding each cell by the modulus pays its field width in the actual bank encoding. -/ +theorem padded_width_le_encoding (p : ℕ) (bank : List ℕ) + (hpad : ∀ v ∈ bank, p ≤ v) : + bank.length * (encNat p).length ≤ (encList encNat bank).length := by + induction bank with + | nil => simp + | cons v bank ih => + have hv := length_encNat_mono (hpad v List.mem_cons_self) + have ht := ih (fun v hv => hpad v (List.mem_cons_of_mem _ hv)) + simp only [List.length_cons, encList_cons, List.length_append, length_frame] + nlinarith + +private def paddingStep (p : ℕ) (good : Bool) (v : ℕ) : Bool := + good && decide (p ≤ v) + +private theorem foldl_paddingStep (p : ℕ) (l : List ℕ) (good : Bool) : + l.foldl (paddingStep p) good = (good && l.all (fun v => decide (p ≤ v))) := by + induction l generalizing good with + | nil => simp + | cons v l ih => simp [List.foldl_cons, paddingStep, ih, Bool.and_assoc] + +private theorem padding_inSpace : + ComputableInSpace (fun q : BankPar × List ℕ => + q.2.all (fun v => decide (q.1.1.1 ≤ v))) + (encPair encBankPar (encList encNat)) encBool + (fun q => (encPair encBankPar (encList encNat) q).length) := by + let encIn := encPair encBankPar (encList encNat) + have hpar := fst' (fst' (fst encBankPar (encList encNat))) + have hlist := snd encBankPar (encList encNat) + have hinit : ComputableInSpace (fun _ : BankPar × List ℕ => true) encIn encBool + (fun q => (encIn q).length) := budget_const true (.refl _) + have hstep : ComputableInSpace + (fun q : (ℕ × Bool) × ℕ => paddingStep q.1.1 q.1.2 q.2) + (encPair (encPair encNat encBool) encNat) encBool + (fun q => (encPair (encPair encNat encBool) encNat q).length) := by + have hp := fst' (fst (encPair encNat encBool) encNat) + have hg := snd' (fst (encPair encNat encBool) encNat) + have hv := snd (encPair encNat encBool) encNat + have hle := budget_finite (encC := encBool) Bool.not (budget_natLt hv hp) + refine (budget_finite (encC := encBool) (fun q : Bool × Bool => q.1 && q.2) + (budget_prod hg hle)).of_enc_eq ?_ + intro q + by_cases h : q.1.1 ≤ q.2 + · simp [paddingStep, h, show ¬q.2 < q.1.1 by omega] + · simp [paddingStep, h, show q.2 < q.1.1 by omega] + have hp (q : BankPar × List ℕ) : (encNat q.1.1.1).length ≤ (encIn q).length := by + simp only [encIn, encBankPar, length_encPair] + omega + have hl (q : BankPar × List ℕ) : (encList encNat q.2).length ≤ (encIn q).length := + length_le_encPair_right encBankPar (encList encNat) q + have hspace (q : BankPar × List ℕ) (i : ℕ) (hi : i < q.2.length) : + (encPair (encPair encNat encBool) encNat + ((q.1.1.1, (q.2.take i).foldl (paddingStep q.1.1.1) true), q.2[i])).length ≤ + 8 * ((encIn q).length + 1) := by + have hv := length_le_encList_of_mem encNat (List.getElem_mem hi) + have hb := hp q + have hc := hl q + simp only [length_encPair, encBool_apply, List.length_singleton] + omega + have hstate (q : BankPar × List ℕ) (i : ℕ) (hi : i ≤ q.2.length) : + (encBool ((q.2.take i).foldl (paddingStep q.1.1.1) true)).length ≤ 1 := by simp + have hfold := ComputableInSpace.foldlWith hpar hlist hinit hstep hspace hstate + refine hfold.weaken ?_ |>.of_enc_eq ?_ + · refine .of_le_linear 32 32 fun q => ?_ + have hb := hp q + have hc := hl q + dsimp only [encIn] at hb hc ⊢ + omega + · intro q + rw [foldl_paddingStep] + simp + +private def checkedBankMap (q : BankPar × List ℕ) : List ℕ := + if q.2.all (fun v => decide (q.1.1.1 ≤ v)) then q.2.mapIdx (bankCell q.1) else q.2 + +private theorem checkedBankMap_inSpace : + ComputableInSpace checkedBankMap (encPair encBankPar (encList encNat)) (encList encNat) + (fun q => (encPair encBankPar (encList encNat) q).length) := by + have h := padding_inSpace.ite bankMap_inSpace (snd encBankPar (encList encNat)) + refine h.weaken ?_ |>.of_enc_eq fun _ => rfl + refine .of_le_linear 8 8 fun q => ?_ + have hl := length_le_encPair_right encBankPar (encList encNat) q + have hn := length_le_length_encList encNat q.2 + cases hc : q.2.all (fun v => decide (q.1.1.1 ≤ v)) with + | false => simp only [Bool.false_eq_true, ↓reduceIte]; omega + | true => + have hw := padded_width_le_encoding q.1.1.1 q.2 + (fun v hv => of_decide_eq_true (List.all_eq_true.mp hc v hv)) + simp only [↓reduceIte] + dsimp only [bankUpdateSpace] + nlinarith + +/-- Runtime modulus, register width, target and multiplier, followed by the bank. -/ +abbrev BankArgs := (ℕ × ℕ × ℕ × ℕ) × List ℕ + +/-- Binary encoding of one scalar bank-update request. -/ +abbrev encBankArgs : BankArgs ↪ List Bool := + encPair (encPair encNat (encPair encNat (encPair encNat encNat))) (encList encNat) + +/-- Runtime scalar parameters, a returned vector and the bank. -/ +abbrev VectorBankArgs := (ℕ × ℕ × ℕ × ℕ) × List ℕ × List ℕ + +/-- Binary encoding of one returned-vector bank-update request. -/ +abbrev encVectorBankArgs : VectorBankArgs ↪ List Bool := + encPair (encPair encNat (encPair encNat (encPair encNat encNat))) + (encPair (encList encNat) (encList encNat)) + +/-- Check the width charge before scaling. Invalid storage or a zero modulus is unchanged. -/ +@[expose] def checkedScaleBank (q : BankArgs) : List ℕ := + if q.2.all (fun v => decide (q.1.1 ≤ v)) then + if q.1.1 = 0 then q.2 else scaleBank q.1.1 q.1.2.1 q.1.2.2.1 q.1.2.2.2 q.2 + else q.2 + +/-- Check the width charge before adding a returned vector. -/ +@[expose] def checkedAddBank (q : VectorBankArgs) : List ℕ := + if q.2.2.all (fun v => decide (q.1.1 ≤ v)) then + if q.1.1 = 0 then q.2.2 else addBank q.1.1 q.1.2.1 q.1.2.2.1 q.1.2.2.2 q.2.1 q.2.2 + else q.2.2 + +/-- Check the width charge before subtracting a returned vector. -/ +@[expose] def checkedSubBank (q : VectorBankArgs) : List ℕ := + if q.2.2.all (fun v => decide (q.1.1 ≤ v)) then + if q.1.1 = 0 then q.2.2 else subBank q.1.1 q.1.2.1 q.1.2.2.1 q.1.2.2.2 q.2.1 q.2.2 + else q.2.2 + +private theorem mapIdx_identity (bank : List ℕ) : bank.mapIdx (fun _ v => v) = bank := by + apply List.ext_getElem (by simp) + intro i h₁ h₂ + simp + +private theorem mapIdx_bankCell (q : BankPar) (bank : List ℕ) : + bank.mapIdx (bankCell q) = bank.mapIdx (fun k v => +if q.1.1 = 0 then v else + if k / q.1.2.1 = q.1.2.2.1 then + q.1.1 + if q.2.2.1 then + addMod q.1.1 v (if q.2.2.2 then + negMod q.1.1 (mulMod q.1.1 q.1.2.2.2 (q.2.1.getD (k % q.1.2.1) 0)) + else mulMod q.1.1 q.1.2.2.2 (q.2.1.getD (k % q.1.2.1) 0)) + else mulMod q.1.1 q.1.2.2.2 v + else v ) := rfl + +/-- Scaling uses native space linear in the encoded arguments, including small zero cells. -/ +theorem checkedScaleBank_inSpace : + ComputableInSpace checkedScaleBank encBankArgs (encList encNat) + (fun q => (encBankArgs q).length) := by + have hpar := fst (encPair encNat (encPair encNat (encPair encNat encNat))) (encList encNat) + have hbank := snd (encPair encNat (encPair encNat (encPair encNat encNat))) (encList encNat) + have hprep := budget_prod + (budget_prod hpar (budget_prod (budget_const (encB := encList encNat) [] (.refl _)) + (budget_const (encB := encPair encBool encBool) (false, false) (.refl _)))) hbank + refine (budget_comp_linear checkedBankMap_inSpace hprep).of_enc_eq ?_ + intro q + by_cases hpad : q.2.all (fun v => decide (q.1.1 ≤ v)) = true + · by_cases hp : q.1.1 = 0 + · simp [checkedBankMap, mapIdx_bankCell, checkedScaleBank, hp, mapIdx_identity] + · simp [checkedBankMap, mapIdx_bankCell, checkedScaleBank, hpad, hp, scaleBank] + · simp [checkedBankMap, checkedScaleBank, hpad] + +private theorem vectorBank_inSpace (subtract : Bool) : + ComputableInSpace (fun q : VectorBankArgs => + checkedBankMap ((q.1, q.2.1, true, subtract), q.2.2)) + encVectorBankArgs (encList encNat) (fun q => (encVectorBankArgs q).length) := by + have hpar := fst (encPair encNat (encPair encNat (encPair encNat encNat))) + (encPair (encList encNat) (encList encNat)) + have hrest := snd (encPair encNat (encPair encNat (encPair encNat encNat))) + (encPair (encList encNat) (encList encNat)) + have hprep := budget_prod + (budget_prod hpar (budget_prod (fst' hrest) + (budget_const (encB := encPair encBool encBool) (true, subtract) (.refl _)))) + (snd' hrest) + exact budget_comp_linear checkedBankMap_inSpace hprep + +/-- Adding a returned vector has a uniform native linear-space certificate. -/ +theorem checkedAddBank_inSpace : + ComputableInSpace checkedAddBank encVectorBankArgs (encList encNat) + (fun q => (encVectorBankArgs q).length) := by + refine (vectorBank_inSpace false).of_enc_eq ?_ + intro q + by_cases hpad : q.2.2.all (fun v => decide (q.1.1 ≤ v)) = true + · by_cases hp : q.1.1 = 0 + · simp [checkedBankMap, mapIdx_bankCell, checkedAddBank, hp, mapIdx_identity] + · simp [checkedBankMap, mapIdx_bankCell, checkedAddBank, hpad, hp, addBank] + · simp [checkedBankMap, checkedAddBank, hpad] + +/-- Subtracting returned gate values has a uniform native linear-space certificate. -/ +theorem checkedSubBank_inSpace : + ComputableInSpace checkedSubBank encVectorBankArgs (encList encNat) + (fun q => (encVectorBankArgs q).length) := by + refine (vectorBank_inSpace true).of_enc_eq ?_ + intro q + by_cases hpad : q.2.2.all (fun v => decide (q.1.1 ≤ v)) = true + · by_cases hp : q.1.1 = 0 + · simp [checkedBankMap, mapIdx_bankCell, checkedSubBank, hp, mapIdx_identity] + · simp [checkedBankMap, mapIdx_bankCell, checkedSubBank, hpad, hp, subBank] + · simp [checkedBankMap, checkedSubBank, hpad] + +/-- On the actual controller's valid bank the checked scale is its original scale. -/ +theorem checkedScaleBank_eq (p d r t a : ℕ) (bank : List ℕ) + (hbank : validBank p d r bank = true) : + checkedScaleBank ((p, r, t, a), bank) = scaleBank p r t a bank := by + obtain ⟨hp, _, hpad⟩ := (validBank_eq_true p d r bank).mp hbank + have hall : bank.all (fun v => decide (p ≤ v)) = true := by + simpa only [List.all_eq_true, decide_eq_true_eq] using hpad + simp [checkedScaleBank, hall, show p ≠ 0 by omega] + +/-- On a valid bank the checked additive update is the actual controller update. -/ +theorem checkedAddBank_eq (p d r t c : ℕ) (values bank : List ℕ) + (hbank : validBank p d r bank = true) : + checkedAddBank ((p, r, t, c), values, bank) = addBank p r t c values bank := by + obtain ⟨hp, _, hpad⟩ := (validBank_eq_true p d r bank).mp hbank + have hall : bank.all (fun v => decide (p ≤ v)) = true := by + simpa only [List.all_eq_true, decide_eq_true_eq] using hpad + simp [checkedAddBank, hall, show p ≠ 0 by omega] + +/-- On a valid bank the checked subtractive update is the actual controller update. -/ +theorem checkedSubBank_eq (p d r t c : ℕ) (values bank : List ℕ) + (hbank : validBank p d r bank = true) : + checkedSubBank ((p, r, t, c), values, bank) = subBank p r t c values bank := by + obtain ⟨hp, _, hpad⟩ := (validBank_eq_true p d r bank).mp hbank + have hall : bank.all (fun v => decide (p ≤ v)) = true := by + simpa only [List.all_eq_true, decide_eq_true_eq] using hpad + simp [checkedSubBank, hall, show p ≠ 0 by omega] + +/-- The per-cell padding check is itself uniformly native in the binary modulus and bank. -/ +theorem bankPadding_inSpace : + ComputableInSpace (fun q : ℕ × List ℕ => q.2.all (fun v => decide (q.1 ≤ v))) + (encPair encNat (encList encNat)) encBool + (fun q => (encPair encNat (encList encNat) q).length) := by + have hp := fst encNat (encList encNat) + have hbank := snd encNat (encList encNat) + have hz := budget_const (encIn := encPair encNat (encList encNat)) + (encB := encNat) 0 (.refl _) + have hpar := budget_prod hp (budget_prod hz (budget_prod hz hz)) + have hcontext := budget_prod hpar + (budget_prod (budget_const (encB := encList encNat) [] (.refl _)) + (budget_const (encB := encPair encBool encBool) (false, false) (.refl _))) + exact (budget_comp_linear padding_inSpace (budget_prod hcontext hbank)).of_enc_eq fun _ => rfl + +end Cslib.TreeEval.Ary.Packed.RawController diff --git a/ComplexityTests/TreeEvalRawController.lean b/ComplexityTests/TreeEvalRawController.lean new file mode 100644 index 0000000000..efe56fdc99 --- /dev/null +++ b/ComplexityTests/TreeEvalRawController.lean @@ -0,0 +1,134 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of the uniform implicit evaluator) + +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerQuery + +/-! +# Uniform raw evaluation with actual input queries + +Cyclic points, dirty banks, characteristic two and malformed storage exercise the executable +kernel. A real bit-reading supplier instantiates the native call interface and complete +physical-prefix bound. Its fixed template differs from the actual supplied initial state. +-/ + +@[expose] public section + +namespace ComplexityTests.TreeEvalRawController + +open Turing MultiTapeTM Cslib.TreeEval Cslib.TreeEval.Ary.Packed +open Cslib.TreeEval.Ary.Packed.RawController + +private instance : Fact (Nat.Prime 5) := ⟨by decide⟩ + +def bank : List ℕ := [5, 6, 7, 8, 9, 5, 6, 7] + +example : point 3 2 2 bank = [6, 7, 5, 6, 7, 8] := rfl +example : point 3 2 3 bank = [5, 6, 7, 8, 9, 5] := rfl +example : point 100000 0 1000 [] = [] := rfl + +example : step 5 3 2 (1, [2, 4]) (bank, [], some (2, 2)) = + ([5, 6, 7, 8, 8, 8, 6, 7], [], none) := rfl + +example : step 5 3 2 (0, [1, 3]) (bank, [(0, 2, false, 2, 2)], none) = + ([5, 6, 7, 8, 7, 9, 6, 7], [(0, 0, true, 2, 2)], some (4, 3)) := rfl + +example : step 2 1 1 (1, [1]) ([2, 3], [], some (1, 0)) = ([3, 3], [], none) := rfl + +example : step 5 1 1 (1, [1]) ([0, 0], [], some (1, 0)) = + ([0, 0], [], some (1, 0)) := rfl +example : need 5 1 1 ([0, 0], [], some (1, 0)) = none := rfl + +example : checkedScaleBank ((5, 2, 1, 2), bank) = [5, 6, 9, 6, 9, 5, 6, 7] := rfl +example : checkedScaleBank ((5, 2, 0, 2), [0, 0]) = [0, 0] := rfl + +/-- Halt after returning the original input's first bit. The emitted junk must be discarded. -/ +def bitReader : QueryMachine (Bool × Bool) where + init := (false, false) + query _ := 0 + next p := (true, p.2.getD false) + emit _ := [true, true] + halted := Prod.fst + finish _ := [true, true] + +abbrev encReader : Bool × Bool ↪ List Bool := encPair encBool encBool + +def start (_ : Query.RequestArgs Unit) : Bool × Bool := (false, false) +def answer (q : Bool × Bool) : Reply := (1, [if q.2 then 1 else 0]) + +def root : Query.Context Unit := ((), (5, 1, 1), [5, 5], [], some (1, 0)) +def template : Query.Context Unit := ((), (2, 0, 0), [], [], none) +def evaluator := Query.machine template bitReader start answer + +example : evaluator.runFrom [true] (.inl root) 3 = + .inl ((), (5, 1, 1), [6, 5], [], none) := rfl +example : evaluator.runFrom [] (.inl root) 3 = + .inl ((), (5, 1, 1), [5, 5], [], none) := rfl +example : evaluator.runFrom [true] (.inl root) 5 = + .inl ((), (5, 1, 1), [6, 5], [], none) := rfl +example : (Query.machine root bitReader start answer).output [true] 3 = [] := rfl + +example : ComputableInSpace evaluator.next + (encPair (Query.encMachineState encUnit encReader) (encOption encBool)) + (Query.encMachineState encUnit encReader) + (fun q => (encPair (Query.encMachineState encUnit encReader) (encOption encBool) q).length) := + Query.machine_next_inSpace encUnit encReader template bitReader start answer + (ComputableInSpace.of_finite _) (ComputableInSpace.of_finite _) + (ComputableInSpace.budget_const _ (.refl _)) (ComputableInSpace.of_finite _) + +def tree (w : List Bool) : Ary.Tree 1 1 0 := .leaf (fun _ => w[0]?.getD false) +def encodeBit (v : Fin 1 → Bool) : Fin 1 → ZMod 5 := fun _ => if v 0 then 1 else 0 +def extension : Ary.Gate 1 1 → (Fin 1 → Fin 1 → ZMod 5) → Fin 1 → ZMod 5 := + fun _ _ _ => 0 +def zeroBank : Ary.State (ZMod 5) 1 1 := fun _ _ => 0 + +/-- Actual input reads witness all requested-supplier premises, including physical peak states. -/ +example (w : List Bool) : + ∃ clock, evaluator.runFrom w + (.inl ((), (5, 1, 1), ofCfg (Ary.Iter.initialCfg zeroBank 1 0))) clock = + .inl ((), (5, 1, 1), ofCfg + (⟨Ary.procWith encodeBit extension (units 5) (tree w) 1 0 zeroBank, [], .ret⟩ : + Ary.Iter.Cfg (ZMod 5) 1 1)) ∧ + ∀ i ≤ clock, (Query.encMachineState encUnit encReader + (evaluator.runFrom w + (.inl ((), (5, 1, 1), ofCfg (Ary.Iter.initialCfg zeroBank 1 0))) i)).length ≤ + Query.queryStateBound (encUnit ()).length 5 1 1 0 4 := by + apply Query.exists_runFrom_root_encoded_bound encUnit encReader template bitReader start + answer w () (by decide) encodeBit extension (tree w) zeroBank 1 0 4 + intro n req hneed + refine ⟨1, ?_, ?_, ?_, ?_⟩ + · intro i hi + have : i = 0 := by omega + subst i + rfl + · rfl + · let q := Ary.Iter.runFrom (by decide) encodeBit extension (units 5) (tree w).nodeAt + (Ary.Iter.initialCfg zeroBank 1 0) n + have hbound := Ary.Iter.runFrom_initial_bounds (tree w) (by decide) encodeBit extension + (units 5) zeroBank 1 0 n + have hstack : q.stack = [] := by + apply List.eq_nil_of_length_eq_zero + have hs := hbound.1 + change q.stack.length ≤ 0 at hs + omega + change ReplyCorrect encodeBit extension (tree w).nodeAt q _ + cases hm : q.mode with + | ret => simp [ReplyCorrect, hm, hstack] + | call c t => + simp only [ReplyCorrect, hm, hstack, Ary.Iter.path_nil, tree, Ary.Tree.nodeAt] + refine ⟨rfl, ?_⟩ + intro i + have hi : i = 0 := Subsingleton.elim _ _ + subst i + change ((if w[0]?.getD false then 1 else 0 : ℕ) : ZMod 5) = + if w[0]?.getD false then 1 else 0 + cases w[0]?.getD false <;> rfl + · intro i hi + simp [encReader, encBool_apply] + +end ComplexityTests.TreeEvalRawController From 09c22f8bf3ee8a3d4f6d855fe07f250aab4c9b96 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 00:44:47 +0000 Subject: [PATCH 13/36] feat(turing): certify local block replay through input queries --- Complexity.lean | 2 + .../BlockComputation/QueryReplay.lean | 461 ++++++++++++++++++ .../BlockComputation/QueryReplaySpace.lean | 321 ++++++++++++ .../Turing/MultiTape/QueryReplay.lean | 113 +++++ 4 files changed, 897 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplay.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySpace.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/QueryReplay.lean diff --git a/Complexity.lean b/Complexity.lean index 959d469ccb..43f20abc4c 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1693,6 +1693,8 @@ public import Complexity.Computability.Machines.Turing.MultiTape.Alternating.Gue public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.EncodedTree public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplay +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputationTree public import Complexity.Computability.Machines.Turing.MultiTape.BlockTrace public import Complexity.Computability.Machines.Turing.MultiTape.Combinators.Append diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplay.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplay.lean new file mode 100644 index 0000000000..b8ccd48298 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplay.lean @@ -0,0 +1,461 @@ +/- +Copyright (c) 2026 Christian Reitwiessner, Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner, Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation +public import Complexity.Computability.Machines.Turing.MultiTape.BlockTrace +public import + Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.Call +public import + Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.ConfigurationGraph.Basic +import Complexity.Foundations.Data.List.Lookup + +/-! +# Query-machine replay inside a local tape window + +The query machine stores three blocks per work tape, binary head positions and a remaining-step +counter. The original input is read only through the query-machine input channel. Both native +input boundaries read blank and input movement uses the existing clamping arithmetic. + +A halted simulated machine keeps its data fixed while the replay counter continues to zero. +Only the three-valued output track is retained; no accumulated output word is stored or emitted. +Initialization receives the input length explicitly; computing that length and decoding a caller's +block labels are charged by the caller. + +## Main results + +* `Turing.MultiTapePTM.BlockComputation.QueryReplay.runFrom_loadView_represents`: every clocked + prefix represents the native replay from the existing configuration embedding. +* `Turing.MultiTapePTM.BlockComputation.QueryReplay.result_runFrom_loadView`: the returned view + agrees with the native block replay. +* `Turing.MultiTapePTM.BlockComputation.QueryReplay.halted_runFrom_loadView`, + `live_runFrom_loadView`: the subroutine takes exactly its requested duration. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay + +open MultiTapeNTM.ChoiceTable +open MultiTapeNTM.ConfigurationGraph + +variable {k : ℕ} {Q : Type} + +/-- Decode the existing ternary cell code; malformed codes read blank. -/ +@[expose] def cellOfCode (n : ℕ) : Option Bool := + if n = 1 then some false else if n = 2 then some true else none + +@[simp] theorem cellOfCode_symbolCode (b : Option Bool) : cellOfCode (symbolCode b) = b := by + rcases b with _ | _ | _ <;> rfl + +/-- Finite local storage. Tape families are fixed-size tuples, not finite lookup tables over +whole windows. Every runtime work cell and every head position is explicitly encoded. -/ +@[ext] structure Config (k : ℕ) (Q : Type) where + /-- Length of the original read-only input. -/ + inputLength : ℕ + /-- Runtime block width. -/ + width : ℕ + /-- Ticks still to replay. -/ + remaining : ℕ + /-- Native padded input position, including both blank boundaries. -/ + inputPos : ℕ + /-- Current simulated control, or halted. -/ + control : Option Q + /-- Local work-head positions. -/ + heads : Fin k → ℕ + /-- Explicit local windows using ternary symbol codes. -/ + cells : Fin k → List ℕ + /-- Finite summary of emitted output. -/ + track : OutputTrack + +/-- Signed movement on the natural coordinates of a finite window. Valid replay heads do not +attempt a left move at zero before their remaining-step counter reaches zero. -/ +@[expose] def move (p : ℕ) : SignType → ℕ + | .neg => p - 1 + | .zero => p + | .pos => p + 1 + +/-- The symbol currently under each local work head. -/ +@[expose] def symbols (s : Config k Q) (h : Fin k) : Option Bool := + cellOfCode ((s.cells h).getD (s.heads h) 0) + +/-- Apply a native action to the fixed local windows. Writes outside a malformed short list do +not allocate a long gap. -/ +@[expose] def applyAction (s : Config k Q) (a : Action k Bool Q) : Config k Q := + { s with + inputPos := inputMove s.inputLength s.inputPos a.inputTape + control := a.state + heads := fun h => move (s.heads h) (a.workTapes h).2 + cells := fun h => match (a.workTapes h).1 with + | none => s.cells h + | some b => (s.cells h).set (s.heads h) (symbolCode b) + track := s.track.emit a.output } + +/-- One replay tick. Even after the simulated machine halts, the duration counter decreases. -/ +@[expose] noncomputable def tick (M : MultiTapeTM k Bool Q) (s : Config k Q) + (symbol : Option Bool) : Config k Q := + let after := s.control.elim s fun q => + applyAction s (M.tr q (inputSymbol s.inputLength s.inputPos symbol) (symbols s)) + { after with remaining := s.remaining - 1 } + +/-- A silent state-returning query subroutine. Callers supply an initialized state via `runFrom`. -/ +@[expose] noncomputable def machine (M : MultiTapeTM k Bool Q) : QueryMachine (Config k Q) where + init := ⟨0, 0, 0, 0, none, fun _ => 0, fun _ => [], .empty⟩ + query s := s.inputPos - 1 + next p := tick M p.1 p.2 + emit _ := [] + halted s := decide (s.remaining = 0) + finish _ := [] + +@[simp] theorem tick_remaining (M : MultiTapeTM k Bool Q) (s : Config k Q) (symbol) : + (tick M s symbol).remaining = s.remaining - 1 := rfl + +@[simp] theorem tick_width (M : MultiTapeTM k Bool Q) (s : Config k Q) (symbol) : + (tick M s symbol).width = s.width := by + cases hs : s.control <;> simp [QueryReplay.tick, hs, QueryReplay.applyAction] + +@[simp] theorem tick_inputLength (M : MultiTapeTM k Bool Q) (s : Config k Q) (symbol) : + (tick M s symbol).inputLength = s.inputLength := by + cases hs : s.control <;> simp [QueryReplay.tick, hs, QueryReplay.applyAction] + +/-- Each local tape keeps exactly its original number of cells. -/ +theorem length_cells_tick (M : MultiTapeTM k Bool Q) (s : Config k Q) (symbol) (h : Fin k) : + ((tick M s symbol).cells h).length = (s.cells h).length := by + cases hs : s.control with + | none => simp [QueryReplay.tick, hs] + | some q => + simp only [QueryReplay.tick, hs, Option.elim_some, QueryReplay.applyAction] + split <;> simp + +variable {w : List Bool} + +/-- A local window agrees with the native configuration on every stored cell. Absolute tape +origins belong to this proof relation, not to the runtime tape data. -/ +structure Represents (s : Config k Q) (c : Cfg k Bool Q w) (base : Fin k → ℤ) : Prop where + inputLength : s.inputLength = w.length + inputPos : s.inputPos = c.inputPos.val + control : s.control = c.state + track : s.track = OutputTrack.ofWord c.output + heads : ∀ h, c.workTapePos h = base h + (s.heads h : ℤ) + cells : ∀ h i, i < (s.cells h).length → + cellOfCode ((s.cells h).getD i 0) = c.workTapes h (base h + i) + +private theorem inputSymbol_read (s : Config k Q) (c : Cfg k Bool Q w) + (hN : s.inputLength = w.length) (hp : s.inputPos = c.inputPos.val) : + inputSymbol s.inputLength s.inputPos w[s.inputPos - 1]? = c.inputSymbol := by + rw [hN, hp] + by_cases hz : c.inputPos.val = 0 + · rw [inputSymbol_eq_none_of_boundary (Or.inl hz)] + simp [inputSymbol, hz] + · by_cases hr : c.inputPos.val = w.length + 1 + · rw [inputSymbol_eq_none_of_boundary (Or.inr hr)] + simp [inputSymbol, hr] + · have hlt : c.inputPos.val - 1 < w.length := by have := c.inputPos.isLt; omega + rw [inputSymbolInner (c.inputPos.val - 1) (by omega) hlt] + simp [inputSymbol, hz, hr, List.getElem?_eq_getElem hlt] + +private theorem inputMove_native (c : Cfg k Bool Q w) (d : SignType) : + inputMove w.length c.inputPos.val d = (moveInputPos c.inputPos d).val := by + have h := val_moveInputPos_eq c.inputPos d + have hb := c.inputPos.isLt + cases d <;> simp only [inputMove, SignType.cast] at * <;> omega + +private theorem move_cast (p : ℕ) (d : SignType) (hp : 0 < p) : + (move p d : ℤ) = (p : ℤ) + d := by + cases d <;> simp only [move, SignType.cast] <;> omega + +/-- Applying the same action preserves every stored cell and the output track. -/ +theorem Represents.applyAction {s : Config k Q} {c : Cfg k Bool Q w} {base : Fin k → ℤ} + (hs : Represents s c base) (a : Action k Bool Q) + (hin : ∀ h, s.heads h < (s.cells h).length) (hpos : ∀ h, 0 < s.heads h) : + Represents (applyAction s a) (a.apply c) base := by + refine ⟨hs.inputLength, ?_, rfl, ?_, ?_, ?_⟩ + · simp only [QueryReplay.applyAction, Action.apply_inputPos, hs.inputLength, hs.inputPos] + exact inputMove_native c a.inputTape + · simp only [QueryReplay.applyAction, Action.apply_output, hs.track, + OutputTrack.ofWord_append] + · intro h + simp only [QueryReplay.applyAction, Action.apply_workTapePos, hs.heads h, + move_cast _ _ (hpos h)] + ring + · intro h i hi + simp only [QueryReplay.applyAction] at hi ⊢ + have hlen : i < (s.cells h).length := by + cases hw : (a.workTapes h).1 <;> simpa only [hw, List.length_set] using hi + cases hw : (a.workTapes h).1 with + | none => simpa only [Action.apply_workTapes, hw] using hs.cells h i hlen + | some b => + simp only [Action.apply_workTapes, hw] + by_cases he : i = s.heads h + · subst i + rw [List.getD_set_self (hin h), cellOfCode_symbolCode, hs.heads h, + Function.update_self] + · rw [List.getD_set_ne (Ne.symm he), hs.cells h i hlen] + have hne : base h + (i : ℤ) ≠ c.workTapePos h := by + rw [hs.heads h] + intro heq + exact he (by exact_mod_cast (add_left_cancel heq)) + rw [Function.update_of_ne hne] + +/-- The duration counter does not change the represented native data. -/ +theorem Represents.withRemaining {s : Config k Q} {c : Cfg k Bool Q w} {base : Fin k → ℤ} + (hs : Represents s c base) (remaining : ℕ) : + Represents { s with remaining := remaining } c base := + ⟨hs.inputLength, hs.inputPos, hs.control, hs.track, hs.heads, hs.cells⟩ + +/-- A tick simulates one native step whenever all current work heads are in the stored window. -/ +theorem Represents.tick {s : Config k Q} {c : Cfg k Bool Q w} {base : Fin k → ℤ} + (M : MultiTapeTM k Bool Q) (hs : Represents s c base) + (hin : ∀ h, s.heads h < (s.cells h).length) (hpos : ∀ h, 0 < s.heads h) : + Represents (tick M s w[s.inputPos - 1]?) (M.step c) base := by + cases hcontrol : c.state with + | none => + rw [MultiTapeTM.step_of_halt hcontrol] + simpa only [QueryReplay.tick, hs.control, hcontrol, Option.elim_none] using + hs.withRemaining (s.remaining - 1) + | some q => + have hsymbols : symbols s = c.workTapeSymbols := by + funext h + exact (hs.cells h (s.heads h) (hin h)).trans (by rw [← hs.heads h]; rfl) + rw [MultiTapeTM.step_of_state hcontrol] + have ha := hs.applyAction (M.tr q c.inputSymbol c.workTapeSymbols) hin hpos + simpa only [QueryReplay.tick, hs.control, hcontrol, Option.elim_some, + inputSymbol_read s c hs.inputLength hs.inputPos, hsymbols] using + ha.withRemaining (s.remaining - 1) + +/-- A live duration counter takes one local replay tick. -/ +theorem step_eq_tick (M : MultiTapeTM k Bool Q) (w : List Bool) (s : Config k Q) + (hrem : 0 < s.remaining) : + (machine M).step w s = tick M s w[s.inputPos - 1]? := by + simp [QueryMachine.step, QueryMachine.read, machine, ne_of_gt hrem] + +private theorem remaining_step (M : MultiTapeTM k Bool Q) (w : List Bool) (s : Config k Q) : + ((machine M).step w s).remaining = s.remaining - 1 := by + by_cases hz : s.remaining = 0 + · simp [QueryMachine.step, machine, hz] + · rw [step_eq_tick M w s (Nat.pos_of_ne_zero hz), tick_remaining] + +private theorem width_step (M : MultiTapeTM k Bool Q) (w : List Bool) (s : Config k Q) : + ((machine M).step w s).width = s.width := by + by_cases hz : s.remaining = 0 + · simp [QueryMachine.step, machine, hz] + · rw [step_eq_tick M w s (Nat.pos_of_ne_zero hz), tick_width] + +private theorem length_cells_step (M : MultiTapeTM k Bool Q) (w : List Bool) + (s : Config k Q) (h : Fin k) : + (((machine M).step w s).cells h).length = (s.cells h).length := by + by_cases hz : s.remaining = 0 + · simp [QueryMachine.step, machine, hz] + · rw [step_eq_tick M w s (Nat.pos_of_ne_zero hz), length_cells_tick] + +/-- The counter uses exactly the requested duration, including halted simulated prefixes. -/ +theorem remaining_runFrom (M : MultiTapeTM k Bool Q) (w : List Bool) (s : Config k Q) (t : ℕ) : + ((machine M).runFrom w s t).remaining = s.remaining - t := by + induction t with + | zero => rfl + | succ t ih => + rw [QueryMachine.runFrom_succ, remaining_step, ih] + omega + +/-- Replay never changes the block width. -/ +theorem width_runFrom (M : MultiTapeTM k Bool Q) (w : List Bool) (s : Config k Q) (t : ℕ) : + ((machine M).runFrom w s t).width = s.width := by + induction t with + | zero => rfl + | succ t ih => rw [QueryMachine.runFrom_succ, width_step, ih] + +/-- Replay never allocates cells beyond its supplied fixed windows. -/ +theorem length_cells_runFrom (M : MultiTapeTM k Bool Q) (w : List Bool) + (s : Config k Q) (t : ℕ) (h : Fin k) : + (((machine M).runFrom w s t).cells h).length = (s.cells h).length := by + induction t with + | zero => rfl + | succ t ih => rw [QueryMachine.runFrom_succ, length_cells_step, ih] + +/-- Replaying a block preserves the native state, heads, stored windows and output track at +all clocked prefixes. The initial heads are in the middle block, leaving room for `width` moves. -/ +theorem runFrom_represents (M : MultiTapeTM k Bool Q) {s : Config k Q} + {c : Cfg k Bool Q w} {base : Fin k → ℤ} (hs : Represents s c base) + (hrem : s.remaining = s.width) + (hlen : ∀ h, (s.cells h).length = 3 * s.width) + (hheads : ∀ h, s.width ≤ s.heads h ∧ s.heads h < 2 * s.width) + {t : ℕ} (ht : t ≤ s.width) : + Represents ((machine M).runFrom w s t) (M.runFrom c t) base := by + induction t with + | zero => exact hs + | succ t ih => + have hprev := ih (by omega) + have hcounter : 0 < ((machine M).runFrom w s t).remaining := by + rw [remaining_runFrom, hrem] + omega + have hinterior (h : Fin k) : + 0 < ((machine M).runFrom w s t).heads h ∧ + ((machine M).runFrom w s t).heads h < + (((machine M).runFrom w s t).cells h).length := by + have hdist := abs_le.mp (M.workTapePos_runFrom_abs_sub_le c t h) + rw [hprev.heads h, hs.heads h] at hdist + have hstart := hheads h + rw [length_cells_runFrom, hlen h] + constructor <;> omega + rw [QueryMachine.runFrom_succ, step_eq_tick M w _ hcounter, MultiTapeTM.runFrom_succ] + exact hprev.tick M (fun h => (hinterior h).2) (fun h => (hinterior h).1) + +/-- Initialize the local replay from decoded block data. The input length is an explicit +argument, and every work cell is stored using the existing ternary symbol code. This initializer +has no native decoding certificate yet. -/ +@[expose] def loadView (inputLength width : ℕ) + (J : Option (Fin k) → ℤ) (v : View k Q width) : Config k Q := + { inputLength := inputLength + width := width + remaining := width + inputPos := + let p : ℤ := width * J none + (v.1.2.1 none).val + 1 + if 0 ≤ p ∧ p < inputLength + 2 then p.toNat else 0 + control := v.1.1 + heads := fun h => width + (v.1.2.1 (some h)).val + cells := fun h => List.ofFn fun r : Fin (3 * width) => + symbolCode (windowCell width J v.2 h (width * (J (some h) - 1) + r)) + track := v.1.2.2 } + +/-- Decoded local data represents precisely the existing embedded native configuration. -/ +theorem loadView_represents (width : ℕ) (J : Option (Fin k) → ℤ) + (v : View k Q width) : + Represents (loadView w.length width J v) (embedCfg (input := w) width J v.1 v.2) + (fun h => width * (J (some h) - 1)) := by + refine ⟨rfl, ?_, rfl, ?_, ?_, ?_⟩ + · simp only [loadView, embedCfg, inputPosOfCoord] + split_ifs <;> rfl + · simp only [loadView, embedCfg, OutputTrack.ofWord_toWord] + · intro h + simp only [loadView, embedCfg] + push_cast + ring + · intro h i hi + have hi' : i < 3 * width := by simpa only [loadView, List.length_ofFn] using hi + simp only [loadView] + rw [List.getD_eq_getElem _ _ (by simpa only [List.length_ofFn] using hi')] + simp only [List.getElem_ofFn, cellOfCode_symbolCode, embedCfg] + +/-- The actual query subroutine realizes the native block replay on the original input, without +storing that word in its state. -/ +theorem runFrom_loadView_represents (M : MultiTapeTM k Bool Q) (width : ℕ) [NeZero width] + (J : Option (Fin k) → ℤ) (v : View k Q width) {t : ℕ} (ht : t ≤ width) : + Represents ((machine M).runFrom w (loadView w.length width J v) t) + (M.runFrom (embedCfg (input := w) width J v.1 v.2) t) + (fun h => width * (J (some h) - 1)) := by + apply runFrom_represents M (loadView_represents width J v) rfl + · intro h + simp only [loadView, List.length_ofFn] + · intro h + have ho := ZMod.val_lt (v.1.2.1 (some h)) + simp only [loadView] + omega + · exact ht + +/-- A requested replay is halted at exactly its width, even if the native machine halts sooner. -/ +theorem halted_runFrom_loadView (M : MultiTapeTM k Bool Q) (width : ℕ) + (J : Option (Fin k) → ℤ) (v : View k Q width) : + (machine M).halted ((machine M).runFrom w (loadView w.length width J v) width) = true := by + change decide (((machine M).runFrom w (loadView w.length width J v) width).remaining = 0) = true + rw [remaining_runFrom] + change decide (width - width = 0) = true + simp only [Nat.sub_self, decide_true] + +/-- Every proper prefix remains live, even if the simulated machine has already halted. -/ +theorem live_runFrom_loadView (M : MultiTapeTM k Bool Q) (width : ℕ) + (J : Option (Fin k) → ℤ) (v : View k Q width) {t : ℕ} (ht : t < width) : + (machine M).halted ((machine M).runFrom w (loadView w.length width J v) t) = false := by + change decide (((machine M).runFrom w (loadView w.length width J v) t).remaining = 0) = false + rw [remaining_runFrom] + change decide (width - t = 0) = false + exact decide_eq_false_iff_not.mpr (by omega) + +/-- Decode the final local data back to a leaving view. Absolute work origins are multiples of +`width`, so they do not affect the head residues. -/ +@[expose] def result (width : ℕ) (s : Config k Q) : View k Q width := + ((s.control, fun head => match head with + | none => (((s.inputPos : ℤ) - 1 : ℤ) : ZMod width) + | some h => (s.heads h : ZMod width), s.track), + fun h d r => cellOfCode ((s.cells h).getD (d * width + r.val) 0)) + +/-- A represented replay returns the native control and the contents of the original windows. -/ +theorem Represents.result_eq {s : Config k Q} {c : Cfg k Bool Q w} + (width : ℕ) [NeZero width] (J : Option (Fin k) → ℤ) + (hs : Represents s c (fun h => width * (J (some h) - 1))) + (hlen : ∀ h, (s.cells h).length = 3 * width) : + result width s = (c.blockControl width, c.windowContents width J) := by + refine Prod.ext (Prod.ext hs.control (Prod.ext ?_ hs.track)) ?_ + · funext head + cases head with + | none => simp only [result, Cfg.blockControl, Cfg.headPos_none, hs.inputPos] + | some h => + change (s.heads h : ZMod width) = (c.workTapePos h : ZMod width) + rw [hs.heads h] + push_cast + simp + · funext h d r + have hr := ZMod.val_lt r + have hd := d.isLt + change cellOfCode ((s.cells h).getD (d * width + r.val) 0) = + c.workTapes h (width * (J (some h) + d - 1) + r.val) + rw [hs.cells h (d * width + r.val) (by rw [hlen h]; nlinarith)] + congr 1 + push_cast + ring + +/-- The state-returning query subroutine computes exactly the native leaving view of the block. -/ +theorem result_runFrom_loadView (M : MultiTapeTM k Bool Q) (width : ℕ) [NeZero width] + (J : Option (Fin k) → ℤ) (v : View k Q width) : + result width ((machine M).runFrom w (loadView w.length width J v) width) = + ((M.runFrom (embedCfg (input := w) width J v.1 v.2) width).blockControl width, + (M.runFrom (embedCfg (input := w) width J v.1 v.2) width).windowContents width J) := by + apply Represents.result_eq width J (runFrom_loadView_represents M width J v le_rfl) + intro h + rw [length_cells_runFrom] + simp only [loadView, List.length_ofFn] + +/-- Every new work-cell code remains one of the three canonical symbols. -/ +theorem cells_lt_three_tick (M : MultiTapeTM k Bool Q) (s : Config k Q) (symbol : Option Bool) + (hcells : ∀ h a, a ∈ s.cells h → a < 3) : + ∀ h a, a ∈ (tick M s symbol).cells h → a < 3 := by + intro h + cases hc : s.control with + | none => simpa only [QueryReplay.tick, hc, Option.elim_none] using hcells h + | some q => + simp only [QueryReplay.tick, hc, Option.elim_some, QueryReplay.applyAction] + split + · exact hcells h + · exact List.forall_mem_set_of_forall_mem (hcells h) (symbolCode_lt _) + +private theorem cells_lt_three_step (M : MultiTapeTM k Bool Q) (w : List Bool) (s : Config k Q) + (hcells : ∀ h a, a ∈ s.cells h → a < 3) : + ∀ h a, a ∈ ((machine M).step w s).cells h → a < 3 := by + by_cases hz : s.remaining = 0 + · simpa [QueryMachine.step, machine, hz] using hcells + · rw [step_eq_tick M w s (Nat.pos_of_ne_zero hz)] + exact cells_lt_three_tick M s _ hcells + +/-- Replay preserves canonical ternary cell codes at every clocked prefix. -/ +theorem cells_lt_three_runFrom (M : MultiTapeTM k Bool Q) (w : List Bool) (s : Config k Q) + (hcells : ∀ h a, a ∈ s.cells h → a < 3) (t : ℕ) : + ∀ h a, a ∈ ((machine M).runFrom w s t).cells h → a < 3 := by + induction t with + | zero => exact hcells + | succ t ih => + rw [QueryMachine.runFrom_succ] + exact cells_lt_three_step M w _ ih + +/-- The view loader writes only the existing blank/false/true codes. -/ +theorem cells_lt_three_loadView (width : ℕ) (J : Option (Fin k) → ℤ) + (v : View k Q width) (h : Fin k) : + ∀ a ∈ (loadView w.length width J v).cells h, a < 3 := by + intro a ha + obtain ⟨i, rfl⟩ := List.mem_ofFn.mp ha + exact symbolCode_lt _ + +end Turing.MultiTapePTM.BlockComputation.QueryReplay diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySpace.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySpace.lean new file mode 100644 index 0000000000..4744e11e64 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySpace.lean @@ -0,0 +1,321 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplay +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpaceLib +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Pi +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Pi +import Complexity.Foundations.Data.List.Fold +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.NatLength + +/-! +# Binary storage for the local replay query machine + +Only the fixed control and output tags use finite encodings. Runtime window widths, counters, +input positions and head positions use binary encodings, and every window cell is stored +explicitly. No original input word occurs in the storage encoding. + +`length_encConfig_le` bounds this representation by the local window width and the logarithm of +the original input length. All five native query-machine component certificates use the same +representation, including the step that updates each explicit local tape. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay + +open MultiTapeTM MultiTapeTM.ComputableInSpace MultiTapeNTM.ChoiceTable +open MultiTapeNTM.ConfigurationGraph + +variable {k : ℕ} {Q : Type} + +/-- The replay storage as a tuple of runtime fields. -/ +@[expose] def Config.equivProd : Config k Q ≃ + ℕ × ℕ × ℕ × ℕ × Option Q × (Fin k → ℕ) × (Fin k → List ℕ) × OutputTrack where + toFun s := (s.inputLength, s.width, s.remaining, s.inputPos, s.control, s.heads, s.cells, s.track) + invFun := fun (inputLength, width, remaining, inputPos, control, heads, cells, track) => + ⟨inputLength, width, remaining, inputPos, control, heads, cells, track⟩ + left_inv _ := rfl + right_inv _ := rfl + +variable [Finite Q] + +/-- Binary numeric fields and explicit fixed families of local work cells. -/ +noncomputable def encConfig : Config k Q ↪ List Bool := + encOfEquiv Config.equivProd (encPair encNat (encPair encNat (encPair encNat + (encPair encNat (encPair (encFinite (Option Q)) (encPair (encPi encNat) + (encPair (encPi (encList encNat)) (encFinite OutputTrack)))))))) + +private theorem config_parts : ComputableInSpace (Config.equivProd (k := k) (Q := Q)) + encConfig (encPair encNat (encPair encNat (encPair encNat + (encPair encNat (encPair (encFinite (Option Q)) (encPair (encPi encNat) + (encPair (encPi (encList encNat)) (encFinite OutputTrack)))))))) + (fun s => (encConfig s).length) := + of_inO (ComputableInO.of_eq fun _ => rfl) + +/-- The runtime input length can be read in linear encoded-state space. -/ +theorem inputLength_inSpace : ComputableInSpace (Config.inputLength (k := k) (Q := Q)) + encConfig encNat (fun s => (encConfig s).length) := config_parts.fst' + +/-- The runtime block width can be read in linear encoded-state space. -/ +theorem width_inSpace : ComputableInSpace (Config.width (k := k) (Q := Q)) + encConfig encNat (fun s => (encConfig s).length) := config_parts.snd'.fst' + +/-- The remaining replay duration can be read in linear encoded-state space. -/ +theorem remaining_inSpace : ComputableInSpace (Config.remaining (k := k) (Q := Q)) + encConfig encNat (fun s => (encConfig s).length) := config_parts.snd'.snd'.fst' + +/-- The original input pointer can be read in linear encoded-state space. -/ +theorem inputPos_inSpace : ComputableInSpace (Config.inputPos (k := k) (Q := Q)) + encConfig encNat (fun s => (encConfig s).length) := config_parts.snd'.snd'.snd'.fst' + +/-- Native original-input query component. -/ +theorem machine_query_inSpace (M : MultiTapeTM k Bool Q) : + ComputableInSpace (machine M).query encConfig encNat (fun s => (encConfig s).length) := + budget_natPred inputPos_inSpace + +/-- Native duration-zero stopping test. -/ +theorem machine_halted_inSpace (M : MultiTapeTM k Bool Q) : + ComputableInSpace (machine M).halted encConfig encBool (fun s => (encConfig s).length) := + budget_natIsZero remaining_inSpace + +/-- Native silent emission component; accumulated output is never copied into storage. -/ +theorem machine_emit_inSpace (M : MultiTapeTM k Bool Q) : + ComputableInSpace (machine M).emit (encPair encConfig (encOption encBool)) encBits + (fun p => (encPair encConfig (encOption encBool) p).length) := + budget_const [] (.refl _) + +/-- Native silent final-output component. The caller reads the returned state separately. -/ +theorem machine_finish_inSpace (M : MultiTapeTM k Bool Q) : + ComputableInSpace (machine M).finish encConfig encBits (fun s => (encConfig s).length) := + budget_const [] (.refl _) + +/-- Explicit storage bound for valid local replay data. The coefficient depends only on the +fixed tape count and finite tags; the original input contributes only its logarithm. -/ +theorem length_encConfig_le (s : Config k Q) + (hremaining : s.remaining ≤ s.width) (hinput : s.inputPos ≤ s.inputLength + 1) + (hheads : ∀ h, s.heads h ≤ 3 * s.width) + (hcells : ∀ h, (s.cells h).length ≤ 3 * s.width) + (hcodes : ∀ h n, n ∈ s.cells h → n ≤ 2) : + (encConfig s).length ≤ 1024 * (k + 1) * + (s.width + Nat.log2 s.inputLength + Nat.card (Option Q) + Nat.card OutputTrack + 1) := by + have hnat (n : ℕ) : (encNat n).length ≤ n := by + rw [encNat_apply, Nat.size_eq_bits_len, Nat.size_le] + exact Nat.lt_two_pow_self + have hN := length_encNat_le_log2 (j := s.inputLength) le_rfl + have hb := hnat s.width + have hr := (hnat s.remaining).trans hremaining + have hi := length_encNat_le_log2_add_two hinput + have hq := (length_encFinite_lt s.control).le + have ht := (length_encFinite_lt s.track).le + have hh : (encPi encNat s.heads).length ≤ k * (6 * s.width + 1) := by + simpa only [Fintype.card_fin, ← Nat.mul_assoc] using length_encPi_le encNat s.heads + (fun h => (hnat (s.heads h)).trans (hheads h)) + have hw : (encPi (encList encNat) s.cells).length ≤ k * (30 * s.width + 1) := by + have hl (h : Fin k) : (encList encNat (s.cells h)).length ≤ 15 * s.width := by + have hl' := length_encList_le encNat (s.cells h) + (fun n hn => (hnat n).trans (hcodes h n hn)) + nlinarith [hcells h] + simpa only [Fintype.card_fin, ← Nat.mul_assoc] using length_encPi_le (encList encNat) s.cells hl + change (encPair encNat (encPair encNat (encPair encNat + (encPair encNat (encPair (encFinite (Option Q)) (encPair (encPi encNat) + (encPair (encPi (encList encNat)) (encFinite OutputTrack))))))) + (Config.equivProd s)).length ≤ _ + simp only [length_encPair, Config.equivProd, Equiv.coe_fn_mk] + nlinarith + +private theorem finiteCases {α β Tag : Type} [Finite Tag] + {input : α ↪ List Bool} {output : β ↪ List Bool} {tagEncoding : Tag ↪ List Bool} + {tag : α → Tag} {branch : Tag → α → β} + (htag : ComputableInSpace tag input tagEncoding (fun a => (input a).length)) + (hbranch : ∀ choice, ComputableInSpace (branch choice) input output + (fun a => (input a).length)) : + ComputableInSpace (fun a => branch (tag a) a) input output + (fun a => (input a).length) := by + classical + let _ := Fintype.ofFinite Tag + cases isEmpty_or_nonempty Tag with + | inl hempty => + let : IsEmpty α := ⟨fun a => isEmptyElim (tag a)⟩ + exact of_finite _ + | inr hnonempty => + let fallback := Classical.choice hnonempty + have hselect (tags : List Tag) : ComputableInSpace (fun a => + tags.foldr (fun choice rest => if tag a = choice then branch choice a else rest) + (branch fallback a)) input output (fun a => (input a).length) := by + induction tags with + | nil => exact hbranch fallback + | cons choice tags ih => + have htest := budget_finite (encC := encBool) (fun t : Tag => decide (t = choice)) htag + simpa only [List.foldr_cons, Bool.decide_iff] using + (budget_ite htest (hbranch choice) ih) + simpa only [List.foldr_ite_eq_of_mem, Finset.mem_toList, Finset.mem_univ] using + hselect (Finset.univ.toList : List Tag) + +private theorem piLinear {α Index Value : Type} [Fintype Index] + {input : α ↪ List Bool} {enc : Value ↪ List Bool} {values : α → Index → Value} + (hvalues : ∀ i, ComputableInSpace (fun a => values a i) input enc + (fun a => (input a).length)) : + ComputableInSpace values input (encPi enc) (fun a => (input a).length) := by + classical + choose T hT using fun i => (hvalues i).exists_inO + refine (of_inO (ComputableInO.pi hT)).weaken ?_ + exact ⟨Fintype.card Index + 1, fun a => by simp; nlinarith⟩ + +private theorem piApplyLinear {Index Value : Type} [Fintype Index] + (enc : Value ↪ List Bool) (i : Index) : + ComputableInSpace (fun q : Index → Value => q i) (encPi enc) enc + (fun q => (encPi enc q).length) := + of_inO (ComputableInO.pi_apply enc i) + +private theorem control_inSpace : ComputableInSpace (Config.control (k := k) (Q := Q)) + encConfig (encFinite (Option Q)) (fun s => (encConfig s).length) := + config_parts.snd'.snd'.snd'.snd'.fst' + +private theorem heads_inSpace : ComputableInSpace (Config.heads (k := k) (Q := Q)) + encConfig (encPi encNat) (fun s => (encConfig s).length) := + config_parts.snd'.snd'.snd'.snd'.snd'.fst' + +private theorem cells_inSpace : ComputableInSpace (Config.cells (k := k) (Q := Q)) + encConfig (encPi (encList encNat)) (fun s => (encConfig s).length) := + config_parts.snd'.snd'.snd'.snd'.snd'.snd'.fst' + +private theorem track_inSpace : ComputableInSpace (Config.track (k := k) (Q := Q)) + encConfig (encFinite OutputTrack) (fun s => (encConfig s).length) := + config_parts.snd'.snd'.snd'.snd'.snd'.snd'.snd' + +private theorem cellOfCode_inSpace : ComputableInSpace cellOfCode encNat + (encFinite (Option Bool)) (fun n => (encNat n).length) := by + have hid : ComputableInSpace (fun n : ℕ => n) encNat encNat + (fun n => (encNat n).length) := of_inO ComputableInDegree.id'.inO_one + refine (budget_ite (budget_natEq hid (budget_const 1 (.refl _))) + (budget_const (encB := encFinite (Option Bool)) (some false) (.refl _)) + (budget_ite (budget_natEq hid (budget_const 2 (.refl _))) + (budget_const (encB := encFinite (Option Bool)) (some true) (.refl _)) + (budget_const (encB := encFinite (Option Bool)) none (.refl _)))).of_enc_eq ?_ + intro n + simp only [cellOfCode, Bool.decide_iff] + +/-- Reading the symbols uses finite tuples only for the fixed number of tapes. -/ +theorem symbols_inSpace : ComputableInSpace (symbols (k := k) (Q := Q)) encConfig + (encPi (encFinite (Option Bool))) (fun s => (encConfig s).length) := by + refine piLinear fun h => ?_ + have hh := budget_comp_linear (piApplyLinear encNat h) (heads_inSpace (k := k) (Q := Q)) + have hc := budget_comp_linear (piApplyLinear (encList encNat) h) (cells_inSpace (k := k) (Q := Q)) + have hread := budget_comp_lin getD (linear_mul 8) (budget_prod hc hh) + exact budget_comp_linear cellOfCode_inSpace hread + +private theorem configMk {α : Type} {input : α ↪ List Bool} {S : α → ℕ} + {N b r p : α → ℕ} {q : α → Option Q} {heads : α → Fin k → ℕ} + {cells : α → Fin k → List ℕ} {track : α → OutputTrack} + (hN : ComputableInSpace N input encNat S) + (hb : ComputableInSpace b input encNat S) + (hr : ComputableInSpace r input encNat S) + (hp : ComputableInSpace p input encNat S) + (hq : ComputableInSpace q input (encFinite (Option Q)) S) + (hh : ComputableInSpace heads input (encPi encNat) S) + (hc : ComputableInSpace cells input (encPi (encList encNat)) S) + (ht : ComputableInSpace track input (encFinite OutputTrack) S) : + ComputableInSpace (fun a => (⟨N a, b a, r a, p a, q a, heads a, cells a, track a⟩ : + Config k Q)) input encConfig S := + (budget_prod hN (budget_prod hb (budget_prod hr (budget_prod hp + (budget_prod hq (budget_prod hh (budget_prod hc ht))))))).of_enc_eq fun _ => rfl + +private theorem fixedActionTick_inSpace (a : Option (Action k Bool Q)) : + ComputableInSpace (fun s : Config k Q => + { a.elim s (applyAction s) with remaining := s.remaining - 1 }) + encConfig encConfig (fun s => (encConfig s).length) := by + cases a with + | none => + exact configMk inputLength_inSpace width_inSpace (budget_natPred remaining_inSpace) + inputPos_inSpace control_inSpace heads_inSpace cells_inSpace track_inSpace + | some a => + have hp : ComputableInSpace (fun s : Config k Q => + MultiTapeNTM.ConfigurationGraph.inputMove s.inputLength s.inputPos a.inputTape) + encConfig encNat (fun s => (encConfig s).length) := by + cases hd : a.inputTape with + | neg => exact budget_natPred inputPos_inSpace + | zero => exact inputPos_inSpace + | pos => + have hpos := budget_natSucc (inputPos_inSpace (k := k) (Q := Q)) + have hN := budget_natSucc (inputLength_inSpace (k := k) (Q := Q)) + refine (budget_ite (budget_natLt hpos hN) hpos hN).of_enc_eq ?_ + intro s + simp only [MultiTapeNTM.ConfigurationGraph.inputMove, Bool.decide_iff] + congr 1 + split <;> omega + have hh : ComputableInSpace (fun s : Config k Q => + fun h => move (s.heads h) (a.workTapes h).2) encConfig (encPi encNat) + (fun s => (encConfig s).length) := by + refine piLinear fun h => ?_ + have hhead := budget_comp_linear (piApplyLinear encNat h) + (heads_inSpace (k := k) (Q := Q)) + cases (a.workTapes h).2 with + | neg => exact budget_natPred hhead + | zero => exact hhead + | pos => exact budget_natSucc hhead + have hc : ComputableInSpace (fun s : Config k Q => fun h => + match (a.workTapes h).1 with + | none => s.cells h + | some b => (s.cells h).set (s.heads h) (symbolCode b)) + encConfig (encPi (encList encNat)) (fun s => (encConfig s).length) := by + refine piLinear fun h => ?_ + have hcell := budget_comp_linear (piApplyLinear (encList encNat) h) + (cells_inSpace (k := k) (Q := Q)) + have hhead := budget_comp_linear (piApplyLinear encNat h) + (heads_inSpace (k := k) (Q := Q)) + cases hw : (a.workTapes h).1 with + | none => exact hcell + | some b => + exact budget_comp_lin set (linear_mul 128) + (budget_prod hcell (budget_prod hhead (budget_const (symbolCode b) (.refl _)))) + have ht := budget_finite (encC := encFinite OutputTrack) (fun t : OutputTrack => + t.emit a.output) (track_inSpace (k := k) (Q := Q)) + exact configMk inputLength_inSpace width_inSpace (budget_natPred remaining_inSpace) + hp (budget_const a.state (.refl _)) hh hc ht + +/-- Native step component. Transition lookup ranges only over fixed finite control, input +symbols and the fixed tuple of work symbols; the windows themselves remain explicit lists. -/ +theorem machine_next_inSpace (M : MultiTapeTM k Bool Q) : + ComputableInSpace (machine M).next (encPair encConfig (encOption encBool)) encConfig + (fun p => (encPair encConfig (encOption encBool) p).length) := by + classical + have hs := fst (encConfig (k := k) (Q := Q)) (encOption encBool) + have hsymbol := snd (encConfig (k := k) (Q := Q)) (encOption encBool) + have hN := budget_comp_linear inputLength_inSpace hs + have hp := budget_comp_linear inputPos_inSpace hs + have hcontrol := budget_comp_linear control_inSpace hs + have hsymbols := budget_comp_linear symbols_inSpace hs + have hsymbols' := budget_finite (encC := encFinite (Fin k → Option Bool)) + (fun x : Fin k → Option Bool => x) hsymbols + have hsymbol' := budget_finite (encC := encFinite (Option Bool)) + (fun x : Option Bool => x) hsymbol + have hleft := budget_natIsZero hp + have hright := budget_natEq hp (budget_natSucc hN) + have hboundary := budget_finite (encC := encBool) (fun q : Bool × Bool => q.1 || q.2) + (budget_prod hleft hright) + have hread := budget_ite hboundary (budget_const none (.refl _)) hsymbol' + have ha := budget_finite (encC := encFinite (Option (Action k Bool Q))) + (fun q : Option Q × Option Bool × (Fin k → Option Bool) => + q.1.map (fun c => M.tr c q.2.1 q.2.2)) + (budget_prod hcontrol (budget_prod hread hsymbols')) + have hbranches (a : Option (Action k Bool Q)) := + budget_comp_linear (fixedActionTick_inSpace a) hs + refine (finiteCases ha hbranches).of_enc_eq ?_ + intro p + simp only [machine, tick] + have hread_eq : (if (decide (p.1.inputPos = 0) || + decide (p.1.inputPos = p.1.inputLength + 1)) then none else p.2) = + MultiTapeNTM.ConfigurationGraph.inputSymbol p.1.inputLength p.1.inputPos p.2 := by + simp only [MultiTapeNTM.ConfigurationGraph.inputSymbol, Bool.or_eq_true, + Bool.decide_iff] + rw [hread_eq] + cases p.1.control <;> rfl + +end Turing.MultiTapePTM.BlockComputation.QueryReplay diff --git a/ComplexityTests/Machines/Turing/MultiTape/QueryReplay.lean b/ComplexityTests/Machines/Turing/MultiTape/QueryReplay.lean new file mode 100644 index 0000000000..153279415e --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/QueryReplay.lean @@ -0,0 +1,113 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of local input-query replay) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySpace + +/-! +# Local replay boundary and update regressions + +A transition records the queried input symbol on its work tape. Both input boundaries must mask +that symbol. Writes distinguish erasing a cell from leaving it unchanged, and halted native +control still consumes the subroutine's duration counter. +-/ + +@[expose] public section + +namespace ComplexityTests.QueryReplay + +open Turing MultiTapeTM MultiTapePTM.BlockComputation +open MultiTapePTM.BlockComputation.QueryReplay + +/-- Read once, write the symbol, emit an accepting bit and halt. -/ +def reader : MultiTapeTM 1 Bool Unit := MultiTapeTM.ofTr () fun _ input _ => + { inputTape := .zero, workTapes := fun _ => (some input, .zero), + output := some true, state := none } + +def state (pos : ℕ) : Config 1 Unit := + ⟨1, 1, 1, pos, some (), fun _ => 1, fun _ => [0, 1, 0], .empty⟩ + +example : (tick reader (state 0) (some true)).cells 0 = [0, 0, 0] := by + dsimp [tick, reader, state, applyAction, + MultiTapeNTM.ConfigurationGraph.inputSymbol, MultiTapeNTM.ConfigurationGraph.inputMove, + machine, QueryMachine.runFrom, QueryMachine.step] + simp only [MultiTapeTM.tr_ofTr] + decide +example : (tick reader (state 1) (some true)).cells 0 = [0, 2, 0] := by + dsimp [tick, reader, state, applyAction, + MultiTapeNTM.ConfigurationGraph.inputSymbol, MultiTapeNTM.ConfigurationGraph.inputMove, + machine, QueryMachine.runFrom, QueryMachine.step] + simp only [MultiTapeTM.tr_ofTr] + decide +example : (tick reader (state 2) (some true)).cells 0 = [0, 0, 0] := by + dsimp [tick, reader, state, applyAction, + MultiTapeNTM.ConfigurationGraph.inputSymbol, MultiTapeNTM.ConfigurationGraph.inputMove, + machine, QueryMachine.runFrom, QueryMachine.step] + simp only [MultiTapeTM.tr_ofTr] + decide +example : (tick reader (state 1) (some true)).track = .accept := by + dsimp [tick, reader, state, applyAction, + MultiTapeNTM.ConfigurationGraph.inputSymbol, MultiTapeNTM.ConfigurationGraph.inputMove, + machine, QueryMachine.runFrom, QueryMachine.step] + simp only [MultiTapeTM.tr_ofTr] + decide +example : (tick reader { state 1 with track := .accept } (some true)).track = .other := by + dsimp [tick, reader, state, applyAction, + MultiTapeNTM.ConfigurationGraph.inputSymbol, MultiTapeNTM.ConfigurationGraph.inputMove, + machine, QueryMachine.runFrom, QueryMachine.step] + simp only [MultiTapeTM.tr_ofTr] + decide + +example : (applyAction (state 1) + { inputTape := .neg, workTapes := fun _ => (none, .zero), output := none, + state := none }).cells 0 = [0, 1, 0] := by + dsimp [tick, reader, state, applyAction, + MultiTapeNTM.ConfigurationGraph.inputSymbol, MultiTapeNTM.ConfigurationGraph.inputMove, + machine, QueryMachine.runFrom, QueryMachine.step] +example : (applyAction (state 1) + { inputTape := .neg, workTapes := fun _ => (some none, .zero), output := none, + state := none }).cells 0 = [0, 0, 0] := by + dsimp [tick, reader, state, applyAction, + MultiTapeNTM.ConfigurationGraph.inputSymbol, MultiTapeNTM.ConfigurationGraph.inputMove, + machine, QueryMachine.runFrom, QueryMachine.step] + decide + +example : (tick reader { state 1 with control := none, remaining := 2 } (some true)).remaining + = 1 := by + dsimp [tick, reader, state, applyAction, + MultiTapeNTM.ConfigurationGraph.inputSymbol, MultiTapeNTM.ConfigurationGraph.inputMove, + machine, QueryMachine.runFrom, QueryMachine.step] +example : (tick reader { state 1 with control := none, remaining := 2 } (some true)).cells 0 + = [0, 1, 0] := by + dsimp [tick, reader, state, applyAction, + MultiTapeNTM.ConfigurationGraph.inputSymbol, MultiTapeNTM.ConfigurationGraph.inputMove, + machine, QueryMachine.runFrom, QueryMachine.step] +example : ((machine reader).runFrom [true] (state 1) 1).cells 0 = [0, 2, 0] := by + dsimp [tick, reader, state, applyAction, + MultiTapeNTM.ConfigurationGraph.inputSymbol, MultiTapeNTM.ConfigurationGraph.inputMove, + machine, QueryMachine.runFrom, QueryMachine.step] + simp only [MultiTapeTM.tr_ofTr] + decide +example : ((machine reader).runFrom [true] (state 0) 1).cells 0 = [0, 0, 0] := by + dsimp [tick, reader, state, applyAction, + MultiTapeNTM.ConfigurationGraph.inputSymbol, MultiTapeNTM.ConfigurationGraph.inputMove, + machine, QueryMachine.runFrom, QueryMachine.step] + simp only [MultiTapeTM.tr_ofTr] + decide +/-- The same concrete replay is certified with its actual binary local-window encoding. -/ +example : ComputableInSpace (machine reader).next + (encPair encConfig (encOption encBool)) encConfig + (fun p => (encPair encConfig (encOption encBool) p).length) := + machine_next_inSpace reader + +example : (encConfig (state 1)).length ≤ 1024 * (1 + 1) * + (1 + Nat.log2 1 + Nat.card (Option Unit) + + Nat.card MultiTapeNTM.ChoiceTable.OutputTrack + 1) := by + apply length_encConfig_le <;> decide + +end ComplexityTests.QueryReplay From f84ffb889816810a087a1417bac1174da6919d1c Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 00:51:03 +0000 Subject: [PATCH 14/36] feat(turing): certify original input length and replay result access --- Complexity.lean | 2 + .../BlockComputation/QueryReplayAccess.lean | 190 ++++++++++++++++++ .../BlockComputation/QueryReplaySpace.lean | 2 +- .../LogSpace/QueryMachine/InputLength.lean | 115 +++++++++++ .../Turing/MultiTape/QueryReplayAccess.lean | 59 ++++++ 5 files changed, 367 insertions(+), 1 deletion(-) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAccess.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine/InputLength.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/QueryReplayAccess.lean diff --git a/Complexity.lean b/Complexity.lean index 43f20abc4c..6e6aa525d0 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1694,6 +1694,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.EncodedTree public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplay +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAccess public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputationTree public import Complexity.Computability.Machines.Turing.MultiTape.BlockTrace @@ -1788,6 +1789,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogS public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.Call public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.CallSpace +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.InputLength public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.Reachability public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.Savitch public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.Words diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAccess.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAccess.lean new file mode 100644 index 0000000000..559577e366 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAccess.lean @@ -0,0 +1,190 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySpace +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Pi + +/-! +# Native extraction of a local replay result + +`RawResult` stores the finite control and output tags, binary head residues and the explicit +local tape lists. Its type is independent of the runtime block width. `rawResult` is computed +in linear encoded-state space; casting its residues and decoding its cells gives precisely the +existing semantic `result` at positive widths. The original input pointer zero represents the +blank left boundary, whose residue is `width - 1`, rather than zero. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay + +open MultiTapeTM MultiTapeTM.ComputableInSpace MultiTapeNTM.ChoiceTable + +variable {k : ℕ} {Q : Type} + +/-- Runtime-width local data, with the input head indexed by `none`. -/ +abbrev RawResult (k : ℕ) (Q : Type) := + Option Q × (Option (Fin k) → ℕ) × OutputTrack × (Fin k → List ℕ) + +/-- A work-head residue, with zero as the guarded zero-width result. -/ +@[expose] def workResidue (width position : ℕ) : ℕ := + if width = 0 then 0 else position % width + +/-- The original input uses physical coordinates with a blank boundary at zero. -/ +@[expose] def inputResidue (width position : ℕ) : ℕ := + if width = 0 then 0 else if position = 0 then width - 1 else (position - 1) % width + +/-- Extract returned local data without a width-indexed runtime type. -/ +@[expose] def rawResult (s : Config k Q) : RawResult k Q := + (s.control, fun h => match h with + | none => inputResidue s.width s.inputPos + | some i => workResidue s.width (s.heads i), s.track, s.cells) + +/-- Interpret explicit returned data at a positive block width. -/ +@[expose] def RawResult.toView (width : ℕ) : RawResult k Q → View k Q width := + fun (control, residues, track, cells) => + ((control, fun h => (residues h : ZMod width), track), + fun h d r => cellOfCode ((cells h).getD (d * width + r.val) 0)) + +@[simp] theorem workResidue_cast (width position : ℕ) [NeZero width] : + (workResidue width position : ZMod width) = (position : ZMod width) := by + simp only [workResidue, NeZero.ne width, ↓reduceIte, ZMod.natCast_mod] + +@[simp] theorem inputResidue_cast (width position : ℕ) [NeZero width] : + (inputResidue width position : ZMod width) = (((position : ℤ) - 1 : ℤ) : ZMod width) := by + have hw : 1 ≤ width := Nat.one_le_iff_ne_zero.mpr (NeZero.ne width) + cases position with + | zero => simp [inputResidue, NeZero.ne width, Nat.cast_sub hw] + | succ n => simp [inputResidue, NeZero.ne width] + +/-- Native result extraction agrees with the existing semantic view, including both input +boundaries and every explicitly stored work-window cell. -/ +theorem toView_rawResult (s : Config k Q) (width : ℕ) [NeZero width] + (hwidth : s.width = width) : + (rawResult s).toView width = result width s := by + apply Prod.ext + · apply Prod.ext + · rfl + · apply Prod.ext + · funext h + cases h <;> simp [RawResult.toView, rawResult, result, hwidth] + · rfl + · rfl + +/-- The guarded work residue never exceeds the runtime block width. -/ +theorem workResidue_le (width position : ℕ) : workResidue width position ≤ width := by + by_cases hw : width = 0 + · simp [workResidue, hw] + · simpa only [workResidue, hw, ↓reduceIte] using + (Nat.mod_lt position (Nat.pos_of_ne_zero hw)).le + +/-- The guarded input residue never exceeds the runtime block width. -/ +theorem inputResidue_le (width position : ℕ) : inputResidue width position ≤ width := by + by_cases hw : width = 0 + · simp [inputResidue, hw] + · by_cases hp : position = 0 + · simp [inputResidue, hw, hp] + · simpa only [inputResidue, hw, hp, ↓reduceIte] using + (Nat.mod_lt (position - 1) (Nat.pos_of_ne_zero hw)).le + +variable [Finite Q] + +/-- Finite tags, binary residues and explicit lists, without a runtime-width finite lookup. -/ +@[expose] noncomputable def encRawResult : RawResult k Q ↪ List Bool := + encPair (encFinite (Option Q)) (encPair (encPi encNat) + (encPair (encFinite OutputTrack) (encPi (encList encNat)))) + +private theorem configParts : ComputableInSpace (Config.equivProd (k := k) (Q := Q)) + encConfig (encPair encNat (encPair encNat (encPair encNat + (encPair encNat (encPair (encFinite (Option Q)) (encPair (encPi encNat) + (encPair (encPi (encList encNat)) (encFinite OutputTrack)))))))) + (fun s => (encConfig s).length) := + of_inO (ComputableInO.of_eq fun _ => rfl) + +private theorem workResidue_inSpace : ComputableInSpace + (fun p : ℕ × ℕ => workResidue p.1 p.2) (encPair encNat encNat) encNat + (fun p => (encPair encNat encNat p).length) := by + have hw := fst encNat encNat + have hp := snd encNat encNat + refine (budget_ite (budget_natIsZero hw) (budget_const 0 (.refl _)) + (budget_natMod hp hw)).of_enc_eq ?_ + intro p + simp only [workResidue, Bool.decide_iff] + +private theorem inputResidue_inSpace : ComputableInSpace + (fun p : ℕ × ℕ => inputResidue p.1 p.2) (encPair encNat encNat) encNat + (fun p => (encPair encNat encNat p).length) := by + have hw := fst encNat encNat + have hp := snd encNat encNat + refine (budget_ite (budget_natIsZero hw) (budget_const 0 (.refl _)) + (budget_ite (budget_natIsZero hp) (budget_natPred hw) + (budget_natMod (budget_natPred hp) hw))).of_enc_eq ?_ + intro p + simp only [inputResidue, Bool.decide_iff] + +/-- Uniform native extraction of the state-returning local replay result. -/ +theorem rawResult_inSpace : ComputableInSpace (rawResult (k := k) (Q := Q)) encConfig + encRawResult (fun s => (encConfig s).length) := by + classical + have hcontrol := (configParts (k := k) (Q := Q)).snd'.snd'.snd'.snd'.fst' + have hheads := (configParts (k := k) (Q := Q)).snd'.snd'.snd'.snd'.snd'.fst' + have hcells := (configParts (k := k) (Q := Q)).snd'.snd'.snd'.snd'.snd'.snd'.fst' + have htrack := (configParts (k := k) (Q := Q)).snd'.snd'.snd'.snd'.snd'.snd'.snd' + have hres (h : Option (Fin k)) : ComputableInSpace + (fun s : Config k Q => (rawResult s).2.1 h) encConfig encNat + (fun s => (encConfig s).length) := by + cases h with + | none => + exact budget_comp_linear inputResidue_inSpace + (budget_prod width_inSpace inputPos_inSpace) + | some i => + have hhead := budget_comp_linear (of_inO (ComputableInO.pi_apply encNat i)) hheads + exact budget_comp_linear workResidue_inSpace (budget_prod width_inSpace hhead) + choose T hT using fun i => (hres i).exists_inO + have hr := (of_inO (ComputableInO.pi hT)).weaken + (show (fun s => (encConfig s).length + + ∑ _i : Option (Fin k), (encConfig s).length) ≼ fun s => (encConfig s).length from + ⟨Fintype.card (Option (Fin k)) + 1, fun s => by simp; nlinarith⟩) + exact (budget_prod hcontrol (budget_prod hr (budget_prod htrack hcells))).of_enc_eq + fun _ => rfl + +/-- Returned data fits in linear local-window space. Its type and encoding do not contain the +original input, and all head residues are bounded by the runtime width. -/ +theorem length_encRawResult_le (s : Config k Q) + (hcells : ∀ h, (s.cells h).length ≤ 3 * s.width) + (hcodes : ∀ h n, n ∈ s.cells h → n ≤ 2) : + (encRawResult (rawResult s)).length ≤ 128 * (k + 1) * + (s.width + Nat.card (Option Q) + Nat.card OutputTrack + 1) := by + have hnat (n : ℕ) : (encNat n).length ≤ n := by + rw [encNat_apply, Nat.size_eq_bits_len, Nat.size_le] + exact Nat.lt_two_pow_self + have hq := (length_encFinite_lt s.control).le + have ht := (length_encFinite_lt s.track).le + have hres (h : Option (Fin k)) : (encNat ((rawResult s).2.1 h)).length ≤ s.width := by + apply (hnat _).trans + cases h + · exact inputResidue_le _ _ + · exact workResidue_le _ _ + have hh : (encPi encNat (rawResult s).2.1).length ≤ (k + 1) * (2 * s.width + 1) := by + simpa only [Fintype.card_option, Fintype.card_fin] using + length_encPi_le encNat (rawResult s).2.1 hres + have hw : (encPi (encList encNat) s.cells).length ≤ k * (30 * s.width + 1) := by + have hl (h : Fin k) : (encList encNat (s.cells h)).length ≤ 15 * s.width := by + have hl' := length_encList_le encNat (s.cells h) + (fun n hn => (hnat n).trans (hcodes h n hn)) + nlinarith [hcells h] + simpa only [Fintype.card_fin, ← Nat.mul_assoc] using + length_encPi_le (encList encNat) s.cells hl + change (encPair (encFinite (Option Q)) (encPair (encPi encNat) + (encPair (encFinite OutputTrack) (encPi (encList encNat)))) (rawResult s)).length ≤ _ + simp only [length_encPair, rawResult] + simp only [rawResult] at hh + nlinarith + +end Turing.MultiTapePTM.BlockComputation.QueryReplay diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySpace.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySpace.lean index 4744e11e64..b2ee5ea37c 100644 --- a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySpace.lean +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySpace.lean @@ -47,7 +47,7 @@ variable {k : ℕ} {Q : Type} variable [Finite Q] /-- Binary numeric fields and explicit fixed families of local work cells. -/ -noncomputable def encConfig : Config k Q ↪ List Bool := +@[expose] noncomputable def encConfig : Config k Q ↪ List Bool := encOfEquiv Config.equivProd (encPair encNat (encPair encNat (encPair encNat (encPair encNat (encPair (encFinite (Option Q)) (encPair (encPi encNat) (encPair (encPi (encList encNat)) (encFinite OutputTrack)))))))) diff --git a/Complexity/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine/InputLength.lean b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine/InputLength.lean new file mode 100644 index 0000000000..be49569e13 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine/InputLength.lean @@ -0,0 +1,115 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.CallSpace +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpaceLib +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.NatLength + +/-! +# Finding the original input length by queries + +A silent state-returning subroutine scans the original input until the first missing bit. Its +fixed initial state contains no input-dependent data. After exactly `input.length + 1` clocks, +the returned binary counter is the input length. Every physical state occupies logarithmic +space; all five components have native linear-space certificates. +-/ + +public section + +namespace Turing.QueryMachine.InputLength + +open MultiTapeTM ComputableInSpace + +/-- Current input index and whether the first missing bit has been encountered. -/ +abbrev State := ℕ × Bool + +/-- Binary index followed by the stopping flag. -/ +abbrev encState : State ↪ List Bool := encPair encNat encBool + +/-- Scan the original input, retaining only a binary counter. No word is emitted. -/ +@[expose] def machine : QueryMachine State where + init := (0, false) + query := Prod.fst + next := fun ((n, _), bit) => if bit.isSome then (n + 1, false) else (n, true) + emit _ := [] + halted := Prod.snd + finish _ := [] + +/-- Every present input bit increments the counter once. -/ +theorem run_before_end (w : List Bool) (t : ℕ) (ht : t ≤ w.length) : + machine.run w t = (t, false) := by + induction t with + | zero => rfl + | succ t ih => + rw [QueryMachine.run_succ, ih (by omega)] + have hlt : t < w.length := by omega + have hget : w[t]? = some (w[t]) := List.getElem?_eq_getElem hlt + have hsome : (w[t]?).isSome = true := by rw [hget]; rfl + simp [QueryMachine.step, QueryMachine.read, machine, hsome] + +/-- The first absent bit returns the actual length, without copying the input. -/ +theorem run_at_end (w : List Bool) : machine.run w (w.length + 1) = (w.length, true) := by + rw [QueryMachine.run_succ, run_before_end w _ le_rfl] + simp [QueryMachine.step, QueryMachine.read, machine] + +/-- The subroutine has not stopped before its first absent-bit query. -/ +theorem live (w : List Bool) {t : ℕ} (ht : t < w.length + 1) : + machine.halted (machine.run w t) = false := by + rw [run_before_end w t (by omega)] + rfl + +/-- The first absent-bit query stops the scan. -/ +theorem halted (w : List Bool) : machine.halted (machine.run w (w.length + 1)) = true := by + rw [run_at_end] + rfl + +/-- The scan counter is bounded by the input length even after it stops. -/ +theorem counter_le (w : List Bool) (t : ℕ) : (machine.run w t).1 ≤ w.length := by + by_cases ht : t ≤ w.length + · rw [run_before_end w t ht] + exact ht + · rw [machine.run_eq_of_le w (halted w) (by omega), run_at_end] + +/-- Every clock, including clocks after stopping, stores only a logarithmic counter and flag. -/ +theorem length_run_le (w : List Bool) (t : ℕ) : + (encState (machine.run w t)).length ≤ 2 * (Nat.log2 w.length + 1) + 2 := by + have hn := length_encNat_le_log2 (counter_le w t) + simp only [encState, length_encPair, encBool_apply, List.length_singleton] + omega + +/-- The original-input query is the stored binary counter. -/ +theorem query_inSpace : ComputableInSpace machine.query encState encNat + (fun s => (encState s).length) := fst encNat encBool + +/-- The halt test is the stored flag. -/ +theorem halted_inSpace : ComputableInSpace machine.halted encState encBool + (fun s => (encState s).length) := snd encNat encBool + +/-- Silent emission has constant native space. -/ +theorem emit_inSpace : ComputableInSpace machine.emit (encPair encState (encOption encBool)) + encBits (fun p => (encPair encState (encOption encBool) p).length) := + budget_const [] (.refl _) + +/-- Silent final output has constant native space; callers read the returned counter. -/ +theorem finish_inSpace : ComputableInSpace machine.finish encState encBits + (fun s => (encState s).length) := budget_const [] (.refl _) + +/-- The native update only increments the binary index or sets the stopping flag. -/ +theorem next_inSpace : ComputableInSpace machine.next (encPair encState (encOption encBool)) + encState (fun p => (encPair encState (encOption encBool) p).length) := by + have hn := (fst encState (encOption encBool)).fst' + have hbit := snd encState (encOption encBool) + have hs : ComputableInSpace (Option.isSome : Option Bool → Bool) + (encOption encBool) encBool (fun b => (encOption encBool b).length) := of_finite _ + have hsome := budget_comp_linear hs hbit + exact budget_ite hsome + (budget_prod (budget_natSucc hn) (budget_const false (.refl _))) + (budget_prod hn (budget_const true (.refl _))) + +end Turing.QueryMachine.InputLength diff --git a/ComplexityTests/Machines/Turing/MultiTape/QueryReplayAccess.lean b/ComplexityTests/Machines/Turing/MultiTape/QueryReplayAccess.lean new file mode 100644 index 0000000000..81b6a2ded6 --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/QueryReplayAccess.lean @@ -0,0 +1,59 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of native replay result extraction and input scanning) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAccess +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.InputLength +public import ComplexityTests.Machines.Turing.MultiTape.QueryReplay + +/-! +# Native replay access + +The scan starts with a fixed counter and finds empty and nonempty input lengths. Result +extraction checks the negative input coordinate at the left blank boundary, zero width and +work-head residues. Its native certificate uses the same concrete representation as replay. +-/ + +@[expose] public section + +namespace ComplexityTests.QueryReplayAccess + +open Turing MultiTapeTM MultiTapePTM.BlockComputation.QueryReplay + +example : QueryMachine.InputLength.machine.run [] 1 = (0, true) := rfl +example : QueryMachine.InputLength.machine.run [false, true, false] 4 = (3, true) := rfl +example : QueryMachine.InputLength.machine.run [false, true, false] 10 = (3, true) := rfl +example (w : List Bool) : + (QueryMachine.InputLength.encState + (QueryMachine.InputLength.machine.run w (w.length + 100))).length ≤ + 2 * (Nat.log2 w.length + 1) + 2 := QueryMachine.InputLength.length_run_le _ _ + +example : inputResidue 4 0 = 3 := rfl +example : inputResidue 4 1 = 0 := rfl +example : inputResidue 4 6 = 1 := rfl +example : inputResidue 0 100 = 0 := rfl +example : workResidue 4 9 = 1 := rfl + +example : (rawResult { QueryReplay.state 0 with width := 4 }).2.1 none = 3 := rfl +example : (rawResult { QueryReplay.state 1 with width := 4, heads := fun _ => 9 }).2.1 + (some 0) = 1 := rfl +example : (rawResult { QueryReplay.state 0 with width := 0 }).2.1 none = 0 := rfl + +/-- Every field of the extracted raw result is realized using the actual binary state. -/ +example : ComputableInSpace (rawResult (k := 1) (Q := Unit)) encConfig encRawResult + (fun s => (encConfig s).length) := rawResult_inSpace + +/-- Native extraction bridges to the same semantic leaving-view interface. -/ +example : (rawResult { QueryReplay.state 0 with width := 4 }).toView 4 = + result 4 { QueryReplay.state 0 with width := 4 } := toView_rawResult _ _ rfl + +/-- Invalid absolute input coordinates use the existing embedding's blank-left convention. -/ +example : (loadView (k := 0) (Q := Unit) 3 1 (fun _ => -100) + ((some (), fun _ => 0, .empty), Fin.elim0)).inputPos = 0 := by decide + +end ComplexityTests.QueryReplayAccess From 462315ae1e8d79d6621517131a75b61deb2f3888 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 00:53:51 +0000 Subject: [PATCH 15/36] feat(tree-eval): decode packed evaluator results in native space --- Complexity.lean | 2 + .../MultiTape/Complexity/InSpace/NatSize.lean | 33 ++ .../TreeEval/Ary/Packed/ParametersSpace.lean | 12 +- .../Ary/Packed/RawControllerOutput.lean | 294 ++++++++++++++++++ ComplexityTests/TreeEvalRawOutput.lean | 38 +++ 5 files changed, 369 insertions(+), 10 deletions(-) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/NatSize.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/RawControllerOutput.lean create mode 100644 ComplexityTests/TreeEvalRawOutput.lean diff --git a/Complexity.lean b/Complexity.lean index 6e6aa525d0..6fd6b835c2 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1741,6 +1741,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSp public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatPow public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatPrime +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatSize public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Plain public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpaceLib public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LinearSpaceArith @@ -2335,6 +2336,7 @@ public import Complexity.Computability.TreeEval.Ary.Packed.RawController public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerCorrect public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerEncoding public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerNative +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerOutput public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerQuery public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerSpace public import Complexity.Computability.TreeEval.Ary.PackingParameters diff --git a/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/NatSize.lean b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/NatSize.lean new file mode 100644 index 0000000000..6183ba3b2f --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/NatSize.lean @@ -0,0 +1,33 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpaceLib +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Constructible + +/-! +# Native binary-width measurement + +The binary size of a runtime natural is computed by measuring its encoded digits. +-/ + +public section + +namespace Turing.MultiTapeTM.ComputableInSpace + +/-- Measure the binary width of a runtime natural in linear encoded-input space. -/ +theorem natSize : + ComputableInSpace Nat.size encNat encNat (fun n => (encNat n).length) := by + have hlen := (of_inO ComputableInO.lengthNat).weaken + (show (fun w : List Bool => w.length + 1) ≼ fun w => (encBits w).length from + .of_le_linear 1 1 fun w => by simp [encBits_apply]) + refine (hlen.of_encIn_eq Nat.bits (fun _ => rfl)).of_enc_eq ?_ + intro n + rw [Nat.size_eq_bits_len] + +end Turing.MultiTapeTM.ComputableInSpace diff --git a/Complexity/Computability/TreeEval/Ary/Packed/ParametersSpace.lean b/Complexity/Computability/TreeEval/Ary/Packed/ParametersSpace.lean index c98f71f4b5..e67dde5aab 100644 --- a/Complexity/Computability/TreeEval/Ary/Packed/ParametersSpace.lean +++ b/Complexity/Computability/TreeEval/Ary/Packed/ParametersSpace.lean @@ -8,6 +8,7 @@ module -- upstream: candidate public import Complexity.Computability.TreeEval.Ary.PackingParameters +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatSize public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatPrime public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatPow import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Constructible @@ -31,20 +32,11 @@ namespace Cslib.TreeEval.Ary.Packed open Turing MultiTapeTM ComputableInSpace -private theorem natSize_inSpace : - ComputableInSpace Nat.size encNat encNat (fun n => (encNat n).length) := by - have hlen := (of_inO ComputableInO.lengthNat).weaken - (show (fun w : List Bool => w.length + 1) ≼ fun w => (encBits w).length from - .of_le_linear 1 1 fun w => by simp [encBits_apply]) - refine (hlen.of_encIn_eq Nat.bits (fun _ => rfl)).of_enc_eq ?_ - intro n - rw [Nat.size_eq_bits_len] - /-- Runtime chunk width is computed by measuring the binary product, not by a finite lookup. -/ theorem chunkWidth_inSpace : ComputableInSpace (fun q : ℕ × ℕ => chunkWidth q.1 q.2) (encPair encNat encNat) encNat (fun q => (encPair encNat encNat q).length) := - (budget_comp_linear natSize_inSpace + (budget_comp_linear natSize (budget_natMul (fst encNat encNat) (snd encNat encNat))).of_enc_eq fun _ => rfl /-- Ceiling division uses binary addition, predecessor and division at runtime. -/ diff --git a/Complexity/Computability/TreeEval/Ary/Packed/RawControllerOutput.lean b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerOutput.lean new file mode 100644 index 0000000000..c370a9802a --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerOutput.lean @@ -0,0 +1,294 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerNative +public import Complexity.Computability.TreeEval.Ary.Packed +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatSize +public import Complexity.Foundations.Data.BitString.Binary +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Bit + +/-! +# Native decoding of a raw packed register + +The value width, chunk width, modulus, chunk count and target register are runtime binary +naturals. A storage guard bounds the output length by the actual padded bank encoding before +any output loop runs. Decoding reads binary digits directly; it never constructs `2^m`. +The semantic statements distinguish decoding a register from obtaining the root value: the +latter requires that the masking procedure started with a zero target and coefficient one. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed.RawController.Output + +open Turing MultiTapeTM ComputableInSpace Cslib.BitString + +/-- Value width, chunk width, modulus, chunk count, target and the physical bank. -/ +abbrev Args := (ℕ × ℕ × ℕ × ℕ × ℕ) × List ℕ + +/-- Every parameter is binary; the bank retains the raw controller's framed cell encoding. -/ +abbrev encArgs : Args ↪ List Bool := + encPair (encPair encNat (encPair encNat (encPair encNat (encPair encNat encNat)))) + (encList encNat) + +/-- Check output capacity and field-width payment using the actual input bank. No exponential +is computed, including on rejected inputs. -/ +@[expose] def valid (q : Args) : Bool := + let ((b, m, p, r, _), bank) := q + decide (0 < m) && decide (m ≤ p.size) && decide (b ≤ r * m) && + decide (r ≤ bank.length) && bank.all (fun v => decide (p ≤ v)) + +/-- One bit of the selected register, reduced in the runtime field before digit access. -/ +@[expose] def bit (q : Args) (j : ℕ) : Bool := + let ((_, m, p, r, target), bank) := q + (bank.getD (target * r + j / m) 0 % p).testBit (j % m) + +private def count (q : Args) : ℕ := if valid q then q.1.1 else 0 + +private abbrev Work := Args × ℕ × List Bool +private abbrev encWork : Work ↪ List Bool := + encPair encArgs (encPair encNat (encList encBool)) + +private def step (s : Work) : Work := (s.1, s.2.1 + 1, s.2.2 ++ [bit s.1 s.2.1]) + +/-- Rejected inputs return the empty word. Accepted inputs stream exactly `b` output bits. -/ +@[expose] def decode (q : Args) : List Bool := + if valid q then List.ofFn (fun j : Fin q.1.1 => bit q j) else [] + +private theorem iterate_step (q : Args) (j : ℕ) (out : List Bool) (n : ℕ) : + step^[n] (q, j, out) = + (q, j + n, out ++ (List.range n).map (fun i => bit q (j + i))) := by + induction n with + | zero => simp + | succ n ih => + rw [Function.iterate_succ_apply', ih] + simp only [step, List.range_succ, List.map_append, List.map_singleton, List.append_assoc] + congr 2 + +/-- The executable loop has the ordinary coordinatewise register-decoding semantics. -/ +theorem decode_eq (q : Args) (hq : valid q = true) : + decode q = List.ofFn (fun j : Fin q.1.1 => bit q j) := by + simp only [decode, hq, ↓reduceIte] + +private theorem decode_eq_loop (q : Args) : + decode q = ((step^[count q]) (q, 0, [])).2.2 := by + rw [iterate_step] + by_cases hq : valid q = true + · simp only [decode, count, hq, ↓reduceIte, Nat.zero_add, List.nil_append] + apply List.ext_getElem (by simp) + intro i hi hi' + simp + · simp [decode, count, hq] + +/-- The guard charges every output bit to the actual encoded padded cells. -/ +private theorem count_le_encoding (q : Args) : count q ≤ (encList encNat q.2).length := by + by_cases h : valid q = true + · have hs : 0 < q.1.2.1 ∧ q.1.2.1 ≤ q.1.2.2.1.size ∧ + q.1.1 ≤ q.1.2.2.2.1 * q.1.2.1 ∧ q.1.2.2.2.1 ≤ q.2.length ∧ + ∀ v ∈ q.2, q.1.2.2.1 ≤ v := by + simpa only [valid, Bool.and_eq_true, decide_eq_true_eq, List.all_eq_true, and_assoc] using h + have hw := padded_width_le_encoding q.1.2.2.1 q.2 hs.2.2.2.2 + rw [length_encNat_eq_size] at hw + have hm := Nat.mul_le_mul hs.2.2.2.1 hs.2.1 + simp only [count, h, ↓reduceIte] + exact hs.2.2.1.trans (hm.trans hw) + · simp [count, h] + +private theorem valid_inSpace : + ComputableInSpace valid encArgs encBool (fun q => (encArgs q).length) := by + have hpar := fst (encPair encNat (encPair encNat (encPair encNat (encPair encNat encNat)))) + (encList encNat) + have hb := fst' hpar + have hm := fst' (snd' hpar) + have hp := fst' (snd' (snd' hpar)) + have hr := fst' (snd' (snd' (snd' hpar))) + have hbank := snd (encPair encNat (encPair encNat (encPair encNat (encPair encNat encNat)))) + (encList encNat) + have hnot := fun {f : Args → Bool} (h : ComputableInSpace f encArgs encBool + (fun q => (encArgs q).length)) => budget_finite (encC := encBool) Bool.not h + have hand := fun {f g : Args → Bool} (hf : ComputableInSpace f encArgs encBool + (fun q => (encArgs q).length)) (hg : ComputableInSpace g encArgs encBool + (fun q => (encArgs q).length)) => budget_finite (encC := encBool) + (fun a : Bool × Bool => a.1 && a.2) (budget_prod hf hg) + have hpos := hnot (budget_natIsZero hm) + have hwidth := hnot (budget_natLt (budget_comp_linear natSize hp) hm) + have hcapacity := hnot (budget_natLt (budget_natMul hr hm) hb) + have hrows := hnot (budget_natLt (budget_length hbank) hr) + have hpad := budget_comp_linear bankPadding_inSpace (budget_prod hp hbank) + refine (hand (hand (hand (hand hpos hwidth) hcapacity) hrows) hpad).of_enc_eq ?_ + intro q + have hle (a b : ℕ) : (!decide (a < b)) = decide (b ≤ a) := by + by_cases h : a < b + · simp [h, show ¬b ≤ a by omega] + · simp [h, show b ≤ a by omega] + have hpos' : (!decide (q.1.2.1 = 0)) = decide (0 < q.1.2.1) := by + by_cases h : q.1.2.1 = 0 + · simp [h] + · simp [h, Nat.pos_of_ne_zero h] + simp only [valid, hle, hpos'] + +private theorem step_inSpace : + ComputableInSpace step encWork encWork (fun s => (encWork s).length) := by + have hq := fst encArgs (encPair encNat (encList encBool)) + have hrest := snd encArgs (encPair encNat (encList encBool)) + have hj := fst' hrest + have hout := snd' hrest + have hpar := fst' hq + have hm := fst' (snd' hpar) + have hp := fst' (snd' (snd' hpar)) + have hr := fst' (snd' (snd' (snd' hpar))) + have ht := snd' (snd' (snd' (snd' hpar))) + have hbank := snd' hq + have hindex := budget_natAdd (budget_natMul ht hr) (budget_natDiv hj hm) + have hcell := budget_comp_lin getD (linear_mul 8) (budget_prod hbank hindex) + have hbit := budget_comp_lin natTestBit (linear_mul 32) + (budget_prod (budget_natMod hcell hp) (budget_natMod hj hm)) + exact (budget_prod hq (budget_prod (budget_natSucc hj) (budget_snoc hout hbit))).of_enc_eq + fun _ => rfl + +/-- Uniform native work space is linear in the actual binary arguments, including the output +word. No finite lookup on banks and no list of bit indices is used by the certificate. -/ +theorem decode_inSpace : + ComputableInSpace decode encArgs (encList encBool) (fun q => (encArgs q).length) := by + have hb := fst' (fst + (encPair encNat (encPair encNat (encPair encNat (encPair encNat encNat)))) + (encList encNat)) + have hn := budget_ite valid_inSpace hb + (budget_const (encB := encNat) 0 (.refl _)) + have hid : ComputableInSpace (fun q : Args => q) encArgs encArgs + (fun q => (encArgs q).length) := of_inO ComputableInDegree.id'.inO_one + have hinit := budget_prod hid (budget_prod + (budget_const (encB := encNat) 0 (.refl _)) + (budget_const (encB := encList encBool) [] (.refl _))) + have hstate (q : Args) (i : ℕ) (hi : i ≤ count q) : + (encWork (step^[i] (q, 0, []))).length ≤ 16 * ((encArgs q).length + 1) := by + rw [iterate_step] + have hn := count_le_encoding q + have hl := length_le_encPair_right + (encPair encNat (encPair encNat (encPair encNat (encPair encNat encNat)))) + (encList encNat) q + have hc := length_encNat_le_self i + have hout : (encList encBool ((List.range i).map (fun j => bit q j))).length = + 3 * i := by + simp [length_encList, List.map_map, Function.comp_def, encBool_apply, Nat.mul_comm] + simp only [encWork, length_encPair, Nat.zero_add, List.nil_append] at ⊢ + rw [hout] + simp only [length_encPair] at hl + omega + have hloop := ComputableInSpace.iterate (hn.of_enc_eq fun _ => rfl) hinit step_inSpace + (Q := fun q => 16 * ((encArgs q).length + 1)) + (B := fun q => 16 * ((encArgs q).length + 1)) hstate hstate + refine (snd' (snd' hloop)).weaken ?_ |>.of_enc_eq ?_ + · refine .of_le_linear 128 128 fun q => ?_ + -- The iteration combinator separately charges only the binary loop bound. + have hb' := length_encNat_mono (count_le_encoding q) + have hb'' := length_encNat_le_self (encList encNat q.2).length + have hl := length_le_encPair_right + (encPair encNat (encPair encNat (encPair encNat (encPair encNat encNat)))) + (encList encNat) q + change (encList encNat q.2).length ≤ (encArgs q).length at hl + dsimp only [count] at hb' ⊢ + omega + · intro q + exact congrArg (encList encBool) (decode_eq_loop q) + +/-- A raw register agrees with the existing unpacking map whenever its reduced cells equal +those chunks. This is a register statement and makes no claim about dirty target contents. -/ +theorem bit_eq_unpackBits {b m p r t : ℕ} (hb : b ≤ r * m) (hm : 0 < m) + (bank : List ℕ) (v : Fin r → Fin (2 ^ m)) + (hbank : ∀ i : Fin r, bank.getD (t * r + i) 0 % p = (v i).val) (j : Fin b) : + bit ((b, m, p, r, t), bank) j = unpackBits hb v j := by + have hj : j.val < r * m := j.isLt.trans_le hb + have hdiv : j.val / m < r := (Nat.div_lt_iff_lt_mul hm).mpr hj + change (bank.getD (t * r + j.val / m) 0 % p).testBit (j.val % m) = _ + rw [hbank ⟨j.val / m, hdiv⟩] + change _ = (equivFin m).symm (v ((finProdFinEquiv.symm (Fin.castLE hb j)).1)) + ((finProdFinEquiv.symm (Fin.castLE hb j)).2) + rw [equivFin_symm_apply] + rfl + +/-- A supplied register containing packed chunks decodes to the original Boolean word. -/ +theorem decode_packBits {b m p r t : ℕ} (hb : b ≤ r * m) (hm : 0 < m) + (bank : List ℕ) (v : Fin b → Bool) + (hvalid : valid ((b, m, p, r, t), bank) = true) + (hbank : ∀ i : Fin r, bank.getD (t * r + i) 0 % p = (packBits hb v i).val) : + decode ((b, m, p, r, t), bank) = List.ofFn v := by + rw [decode_eq _ hvalid] + congr 1 + funext j + rw [bit_eq_unpackBits hb hm bank (packBits hb v) hbank j, unpackBits_packBits] + +/-- The canonical physical bank decodes any target that actually contains the packed word. -/ +theorem decode_ofRegs {b m p d r : ℕ} [NeZero p] (hb : b ≤ r * m) (hm : 0 < m) + (halphabet : 2 ^ m ≤ p) + (S : Ary.State (ZMod p) d r) (t : Fin (d + 1)) (v : Fin b → Bool) + (htarget : ∀ i, S t i = ((packBits hb v i).val : ZMod p)) : + decode ((b, m, p, r, t.val), ofRegs S) = List.ofFn v := by + have hwidth : m ≤ p.size := by + have hw := Nat.size_le_size halphabet + rw [Nat.size_pow] at hw + omega + have hrows : r ≤ (d + 1) * r := by nlinarith + have hvalid : valid ((b, m, p, r, t.val), ofRegs S) = true := by + simp only [valid, hm, hwidth, hb, decide_true, Bool.true_and, length_ofRegs, hrows] + apply List.all_eq_true.mpr + intro x hx + obtain ⟨k, rfl⟩ := List.mem_ofFn.mp hx + simp + apply decode_packBits hb hm (ofRegs S) v hvalid + intro i + have he := congrFun (congrFun (toRegs_ofRegs S) t) i + change ((ofRegs S).getD (index r t i) 0 : ZMod p) = S t i at he + rw [htarget] at he + have hv := congrArg ZMod.val he + have hlt := (packBits hb v i).isLt.trans_le halphabet + simpa only [ZMod.val_natCast, Nat.mod_eq_of_lt hlt, index, Nat.mul_comm r t.val, + Nat.add_comm i.val (t.val * r)] using hv + +/-- After a coefficient-one masking computation with an initially zero target, the actual +padded target decodes to the root word. Other registers may start dirty. The zero-target +hypothesis is essential: a general dirty target stores its mask plus the root encoding. -/ +theorem decode_procWith_zero_target {b m p d r height : ℕ} [Fact p.Prime] + (hb : b ≤ r * m) (hm : 0 < m) (halphabet : 2 ^ m ≤ p) + (h : Fin (2 ^ m) ↪ ZMod p) (hcast : ∀ a, h a = (a.val : ZMod p)) + (hD : d * r * (Fintype.card (Fin (2 ^ m)) - 1) < Fintype.card (ZMod p) - 1) + (T : Ary.Tree d b height) (S : Ary.State (ZMod p) d r) (t : Fin (d + 1)) + (hzero : S t = 0) : + decode ((b, m, p, r, t.val), ofRegs + (Ary.procWith (fun v i => h (packBits hb v i)) + (gateExtension h (packBits hb) (unpackBits hb)) (units p) T 1 t S)) = + List.ofFn T.eval := by + classical + apply decode_ofRegs hb hm halphabet _ t T.eval + intro i + unfold units + rw [procWith_gateExtension_eq h (packBits hb) (unpackBits hb) + (unpackBits_packBits hb) hD TreeEval.Iter.nodup_zmodUnits TreeEval.Iter.mem_zmodUnits] + simp only [Function.update_self, hzero, one_smul, zero_add, hcast] + +/-- With the library's selected packed field and chunk parameters, a zero-target masking run +returns a physical register whose native decoder yields exactly the root's Boolean word. +All non-target initial registers may be dirty. This does not construct the run's suppliers. -/ +theorem decode_packed_procWith_zero_target {d b height : ℕ} (hd : 0 < d) (hb : 0 < b) + (T : Ary.Tree d b height) + (S : Ary.State (ZMod (fieldModulus d b)) d (chunkCount d b)) + (t : Fin (d + 1)) (hzero : S t = 0) : + letI : Fact (fieldModulus d b).Prime := ⟨fieldModulus_prime d b⟩ + decode ((b, chunkWidth d b, fieldModulus d b, chunkCount d b, t.val), ofRegs + (Ary.procWith + (fun v i => alphabetEmbedding hd hb (packBits (le_chunkCount_mul hd hb) v i)) + (gateExtension (alphabetEmbedding hd hb) (packBits (le_chunkCount_mul hd hb)) + (unpackBits (le_chunkCount_mul hd hb))) + (units (fieldModulus d b)) T 1 t S)) = List.ofFn T.eval := by + let : Fact (fieldModulus d b).Prime := ⟨fieldModulus_prime d b⟩ + apply decode_procWith_zero_target (le_chunkCount_mul hd hb) (chunkWidth_pos hd hb) + (alphabetSize_lt hd hb).le (alphabetEmbedding hd hb) (fun _ => rfl) _ T S t hzero + simpa only [Fintype.card_fin, ZMod.card, gateDegree] using gateDegree_lt d b + +end Cslib.TreeEval.Ary.Packed.RawController.Output diff --git a/ComplexityTests/TreeEvalRawOutput.lean b/ComplexityTests/TreeEvalRawOutput.lean new file mode 100644 index 0000000000..35ab8bf823 --- /dev/null +++ b/ComplexityTests/TreeEvalRawOutput.lean @@ -0,0 +1,38 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of native packed-register output) + +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerOutput + +/-! +# Native packed output regressions + +Two-bit chunks decode a final partial chunk, and target selection reads a later register. +Malformed output capacity, unpaid cells and binary-large chunk widths are rejected before the +output loop. The native certificate includes the actual output's storage. +-/ + +@[expose] public section + +namespace ComplexityTests.TreeEvalRawOutput + +open Turing MultiTapeTM Cslib.TreeEval.Ary.Packed.RawController.Output + +example : decode ((3, 2, 5, 2, 0), [8, 6]) = [true, true, true] := by decide +example : decode ((3, 2, 5, 2, 1), [5, 5, 8, 6]) = [true, true, true] := by decide +example : decode ((3, 2, 5, 2, 0), [5, 5]) = [false, false, false] := by decide +example : decode ((0, 1000000000, 5, 0, 0), []) = [] := by decide +example : decode ((3, 2, 5, 1, 0), [8]) = [] := by decide +example : decode ((3, 2, 5, 2, 0), [0, 0]) = [] := by decide +example : decode ((3, 0, 5, 2, 0), [8, 6]) = [] := by decide + +/-- The tested decoder is realized with its actual framed output representation. -/ +example : ComputableInSpace decode encArgs (encList encBool) + (fun q => (encArgs q).length) := decode_inSpace + +end ComplexityTests.TreeEvalRawOutput From 5bf9a53e74ebc5e6a9458b2012e87d7fd624214d Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 00:55:40 +0000 Subject: [PATCH 16/36] style(tree-eval): name the decoded output width --- .../Computability/TreeEval/Ary/Packed/RawControllerOutput.lean | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Complexity/Computability/TreeEval/Ary/Packed/RawControllerOutput.lean b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerOutput.lean index c370a9802a..8afd869fda 100644 --- a/Complexity/Computability/TreeEval/Ary/Packed/RawControllerOutput.lean +++ b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerOutput.lean @@ -59,7 +59,8 @@ private def step (s : Work) : Work := (s.1, s.2.1 + 1, s.2.2 ++ [bit s.1 s.2.1]) /-- Rejected inputs return the empty word. Accepted inputs stream exactly `b` output bits. -/ @[expose] def decode (q : Args) : List Bool := - if valid q then List.ofFn (fun j : Fin q.1.1 => bit q j) else [] + let ((b, _, _, _, _), _) := q + if valid q then List.ofFn (fun j : Fin b => bit q j) else [] private theorem iterate_step (q : Args) (j : ℕ) (out : List Bool) (n : ℕ) : step^[n] (q, j, out) = From d40bb06dc5fefd14758b338c0e18f585dc90c856 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 01:13:51 +0000 Subject: [PATCH 17/36] feat(turing): certify canonical HPV label conversion and replay bounds --- Complexity.lean | 5 + .../Encoding/NativeChecked.lean | 106 ++++ .../Encoding/NativeDecode.lean | 557 ++++++++++++++++++ .../Encoding/NativeEncode.lean | 402 +++++++++++++ .../BlockComputation/QueryReplayBounds.lean | 117 ++++ .../MultiTape/Complexity/InSpace/BitList.lean | 58 ++ .../Turing/MultiTape/NativeBlockCodec.lean | 68 +++ .../Turing/MultiTape/QueryReplayBounds.lean | 54 ++ 8 files changed, 1367 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeChecked.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeEncode.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayBounds.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/BitList.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/NativeBlockCodec.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/QueryReplayBounds.lean diff --git a/Complexity.lean b/Complexity.lean index 6fd6b835c2..8cc0a3f100 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1693,8 +1693,12 @@ public import Complexity.Computability.Machines.Turing.MultiTape.Alternating.Gue public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.EncodedTree public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeChecked +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeDecode +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeEncode public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplay public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAccess +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayBounds public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputationTree public import Complexity.Computability.Machines.Turing.MultiTape.BlockTrace @@ -1738,6 +1742,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Degr public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Fold public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Bit +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.BitList public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatPow public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatPrime diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeChecked.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeChecked.lean new file mode 100644 index 0000000000..647f132ba4 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeChecked.lean @@ -0,0 +1,106 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeDecode +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeEncode +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputationTree +import Complexity.Computability.Machines.Turing.MultiTape.Internal.EqualityPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.DegreeOptionPrimitives + +/-! +# Native canonical-code validation + +The semantic decoder assigns defaults to unused field words. The functional tree gate instead +requires an exact codeword. Re-serialization and comparison reject unused ranks, noncanonical +residues, track words and blank-cell words before local replay can start. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.Encoding.NativeChecked + +open Cslib Cslib.BitString MultiTapeTM ComputableInSpace QueryReplay + +variable {k s : ℕ} {Q : Type} [Finite Q] + +/-- Parse a successful label only if re-encoding its decoded data reproduces every input bit. +Rejected and FAIL labels return `none`; no default view is silently fed to the tree gate. -/ +@[expose] noncomputable def parse (e : Q ≃ Fin s) (p : ℕ × List Bool) : + Option (RawResult k Q) := + let decoded := NativeDecode.parse e p + let raw := decoded.getD (none, fun _ => 0, .empty, fun _ => []) + if NativeEncode.serialize e (p.1, true, raw) = p.2 then decoded else none + +/-- Exact validation uses native linear space in the actual input word and binary width. +Equality uses its existing independent linear workspace bound, not its quadratic time bound. -/ +theorem parse_inSpace (e : Q ≃ Fin s) : + ComputableInSpace (parse (k := k) e) NativeDecode.encArgs (encOption encRawResult) + (fun p => (NativeDecode.encArgs p).length) := by + let empty : RawResult k Q := (none, fun _ => 0, .empty, fun _ => []) + have hp := NativeDecode.parse_inSpace (k := k) e + have hget : ComputableInSpace (fun x : Option (RawResult k Q) => x.getD empty) + (encOption encRawResult) encRawResult + (fun x => (encOption encRawResult x).length) := + of_inO (ComputableInDegree.getD (enc₁ := encRawResult) empty).inO_one + have hr := budget_comp_linear hget hp + have hinput := budget_prod (fst encNat encBits) + (budget_prod (budget_const (encB := encBool) true (.refl _)) hr) + have hout := budget_comp_linear (NativeEncode.serialize_bits_inSpace e) hinput + have heq := budget_comp_linear (of_inO (ComputableInO.eq encBits)) + (budget_prod hout (snd encNat encBits)) + refine (budget_ite heq hp (budget_const none (.refl _))).of_enc_eq ?_ + intro p + simp only [parse, Bool.decide_iff, empty] + +omit [Finite Q] in +private theorem valid_of_parse_some (e : Q ≃ Fin s) (b : ℕ) [NeZero b] + (bits : BitString (width k s b)) (raw : RawResult k Q) + (h : NativeDecode.parse e (b, List.ofFn bits) = some raw) : + NativeEncode.valid (b, true, raw) = true := by + simp only [NativeDecode.parse, NeZero.ne b, List.length_ofFn, ne_eq, + not_true_eq_false, or_self, ↓reduceIte] at h + split at h + · have hr := Option.some.inj h + subst raw + apply List.all_eq_true.mpr + intro flag hflag + obtain ⟨i, rfl⟩ := List.mem_ofFn.mp hflag + exact decide_eq_true (NativeDecode.length_payload_cells e b bits i) + · contradiction + +omit [Finite Q] in +/-- The native canonical-code guard is exactly the one used by the functional tree gate. +Unused field words become FAIL even though the underlying semantic decoder supplies defaults. -/ +theorem parse_ofFn (e : Q ≃ Fin s) (b : ℕ) [NeZero b] + (bits : BitString (width k s b)) : + (parse e (b, List.ofFn bits)).map (RawResult.toView b) = + decodeChecked (encode e) (decode e) bits := by + have hdecoded := NativeDecode.parse_ofFn e b bits + cases hp : NativeDecode.parse (k := k) e (b, List.ofFn bits) with + | none => + have hd : decode e bits = none := by + simpa only [hp, Option.map_none] using hdecoded.symm + simp [parse, hp, decodeChecked, hd] + | some raw => + have hd : decode e bits = some (raw.toView b) := by + simpa only [hp, Option.map_some] using hdecoded.symm + have hv := valid_of_parse_some e b bits raw hp + have hs := NativeEncode.serialize_some e raw hv + have heq : (NativeEncode.serialize e (b, true, raw) = List.ofFn bits) ↔ + ∀ i, encode e (some (raw.toView b)) i = bits i := by + rw [hs, List.ofFn_inj] + exact funext_iff + simp only [parse, hp, Option.getD_some, decodeChecked, hd] + by_cases hc : NativeEncode.serialize e (b, true, raw) = List.ofFn bits + · simp [hc, heq.mp hc] + · have hx : ¬ ∀ i, encode e (some (raw.toView b)) i = bits i := + fun h => hc (heq.mpr h) + simp [hc, hx] + +end Turing.MultiTapePTM.BlockComputation.Encoding.NativeChecked diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean new file mode 100644 index 0000000000..e10178fac7 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean @@ -0,0 +1,557 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAccess +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatSize +import Complexity.Computability.Machines.Turing.MultiTape.Internal.BinaryNormalization +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Constructible +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Pi +import Mathlib.Data.List.Induction + +/-! +# Native decoding of runtime-width Boolean block labels + +The decoder checks the positive width and exact word length before reading the existing explicit +layout. It reads only fixed control tags by a finite table. Every runtime residue is binary, +and work windows are decoded by streaming pairs of actual input bits. A malformed short word +never allocates cells according to an untrusted binary width. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.Encoding.NativeDecode + +open Cslib Cslib.BitString MultiTapeTM MultiTapeTM.ComputableInSpace +open MultiTapeNTM.ChoiceTable QueryReplay + +variable {k s : ℕ} {Q : Type} + +/-- Read a little-endian numeric field from an explicitly stored bit word. -/ +@[expose] def readNat (word : List Bool) (offset count : ℕ) : ℕ := + ofBits ((word.drop offset).take count) + +/-- Canonical replay codes distinguish blank, written zero and written one. -/ +@[expose] def cellCode (first second : Bool) : ℕ := + if first then if second then 2 else 1 else 0 + +/-- Decode adjacent bit pairs, discarding an unmatched final bit. -/ +@[expose] def cells : List Bool → List ℕ + | first :: second :: rest => cellCode first second :: cells rest + | _ => [] + +/-- Decode the fixed finite control rank, treating unused ranks as halted. -/ +@[expose] def state (e : Q ≃ Fin s) (rank : ℕ) : Option Q := + if h : rank < s + 1 then (stateIndex e).symm ⟨rank, h⟩ else none + +/-- First output-track bit in the full word, including the outer FAIL bit. -/ +@[expose] def trackOffset (k s b : ℕ) : ℕ := 1 + (s + 1).size + (k + 1) * b.size + +/-- Decode fields without a width-dependent runtime type. Window slices are bounded by actual +input storage, even when their declared binary width is malformed. -/ +@[expose] noncomputable def payload (e : Q ≃ Fin s) (b : ℕ) (word : List Bool) : RawResult k Q := + (state e (readNat word 1 (s + 1).size), + fun h => readNat word (1 + (s + 1).size + ((finSuccEquiv k).symm h).val * b.size) + b.size % b, + if word.getD (trackOffset k s b + 1) false then .other + else if word.getD (trackOffset k s b) false then .accept else .empty, + fun h => cells ((word.drop (trackOffset k s b + 2 + h.val * (6 * b))).take (6 * b))) + +/-- Reject zero width, malformed word length and the existing outer FAIL tag. -/ +@[expose] noncomputable def parse (e : Q ≃ Fin s) (p : ℕ × List Bool) : Option (RawResult k Q) := + if p.1 = 0 ∨ p.2.length ≠ width k s p.1 then none + else if p.2.headD false then some (payload e p.1 p.2) else none + +private theorem wordDrop_inSpace : ComputableInSpace + (fun p : List Bool × ℕ => p.1.drop p.2) (encPair encBits encNat) encBits + (fun p => (encPair encBits encNat p).length) := by + have htail : ComputableInSpace List.tail encBits encBits (fun w => w.length) := + of_inO (.of_linear (ListEncoding.tail encBool)) + have h := iterate (snd encBits encNat) (fst encBits encNat) htail + (Q := fun p => p.1.length) (B := fun p => p.1.length) + (fun p i _ => by simp [List.tail_iterate]) (fun p i _ => by simp [List.tail_iterate]) + refine h.weaken (.of_le_linear 8 8 fun p => ?_) |>.of_enc_eq ?_ + · simp only [length_encPair, encBits_apply] + omega + · intro p + rw [List.tail_iterate] + +private theorem wordReverse_inSpace : ComputableInSpace List.reverse encBits encBits + (fun w => w.length) := of_inO ComputableInDegree.bitsReverse.inO_one + +private theorem wordLength_inSpace : ComputableInSpace List.length encBits encNat + (fun w => w.length) := + (of_inO ComputableInO.lengthNat).weaken (.of_le_linear 1 1 fun _ => by omega) + +private theorem wordTake_inSpace : ComputableInSpace + (fun p : List Bool × ℕ => p.1.take p.2) (encPair encBits encNat) encBits + (fun p => (encPair encBits encNat p).length) := by + have hw := fst encBits encNat + have hn := snd encBits encNat + have hrev := budget_comp_linear wordReverse_inSpace hw + have hlen := budget_comp_linear wordLength_inSpace hw + have hdrop := budget_comp_linear wordDrop_inSpace + (budget_prod hrev (budget_natSub hlen hn)) + refine (budget_comp_linear wordReverse_inSpace hdrop).of_enc_eq ?_ + intro p + rw [← List.reverse_take, List.reverse_reverse] + +/-- Slice a runtime bit word using binary offset and count, in linear encoded-input space. -/ +theorem slice_inSpace : ComputableInSpace + (fun p : List Bool × ℕ × ℕ => (p.1.drop p.2.1).take p.2.2) + (encPair encBits (encPair encNat encNat)) encBits + (fun p => (encPair encBits (encPair encNat encNat) p).length) := by + have hw := fst encBits (encPair encNat encNat) + have hn := snd encBits (encPair encNat encNat) + exact budget_comp_linear wordTake_inSpace + (budget_prod (budget_comp_linear wordDrop_inSpace (budget_prod hw hn.fst')) hn.snd') + +/-- Numeric fields use the direct charged binary-word normalization machine. -/ +theorem readNat_inSpace : ComputableInSpace + (fun p : List Bool × ℕ × ℕ => readNat p.1 p.2.1 p.2.2) + (encPair encBits (encPair encNat encNat)) encNat + (fun p => (encPair encBits (encPair encNat encNat) p).length) := + budget_comp_linear (of_inO ComputableInO.bitsToNat) slice_inSpace + +private theorem bit_inSpace : ComputableInSpace + (fun p : List Bool × ℕ => p.1.getD p.2 false) (encPair encBits encNat) encBool + (fun p => (encPair encBits encNat p).length) := by + have hhead : ComputableInSpace (fun w : List Bool => w.headD false) encBits encBool + (fun w => w.length) := of_inO (.of_linear (ListEncoding.headD false)) + refine (budget_comp_linear hhead wordDrop_inSpace).of_enc_eq ?_ + intro p + rw [List.getD_eq_getElem?_getD, List.headD_eq_head?_getD, List.head?_drop] + +private abbrev Scan := Bool × Bool × List ℕ +private abbrev encScan : Scan ↪ List Bool := encPair encBool (encPair encBool (encList encNat)) + +private def scan (q : Scan) (bit : Bool) : Scan := + if q.1 then (false, false, cellCode q.2.1 bit :: q.2.2) else (true, bit, q.2.2) + +private theorem scan_inSpace : ComputableInSpace (fun p : Scan × Bool => scan p.1 p.2) + (encPair encScan encBool) encScan (fun p => (encPair encScan encBool p).length) := by + have hq := fst encScan encBool + have hbit := snd encScan encBool + have hfirst := hq.snd'.fst' + have hacc := hq.snd'.snd' + have hcode := budget_finite (encC := encNat) (fun p : Bool × Bool => cellCode p.1 p.2) + (budget_prod hfirst hbit) + exact budget_ite hq.fst' + (budget_prod (budget_const false (.refl _)) + (budget_prod (budget_const false (.refl _)) (budget_cons hcode hacc))) + (budget_prod (budget_const true (.refl _)) (budget_prod hbit hacc)) + +private theorem scan_length (q : Scan) (bit : Bool) : + (encList encNat (scan q bit).2.2).length ≤ (encList encNat q.2.2).length + 5 := by + rcases q with ⟨a, b, acc⟩ + cases a <;> cases b <;> cases bit <;> + simp [scan, cellCode, encList_cons, show Nat.bits 2 = [false, true] from rfl] + +private theorem scan_fold_length (word : List Bool) (q : Scan) : + (encList encNat (word.foldl scan q).2.2).length ≤ + (encList encNat q.2.2).length + 5 * word.length := by + induction word generalizing q with + | nil => simp + | cons bit word ih => + have h := ih (scan q bit) + have hstep := scan_length q bit + simp only [List.foldl_cons, List.length_cons] + omega + +private theorem scan_fold_cells (word : List Bool) (acc : List ℕ) : + (word.foldl scan (false, false, acc)).2.2 = (cells word).reverse ++ acc := by + induction word using List.twoStepInduction generalizing acc with + | nil => rfl + | singleton bit => rfl + | cons_cons first second word ih _ => + change (word.foldl scan (false, false, cellCode first second :: acc)).2.2 = + (cellCode first second :: cells word).reverse ++ acc + rw [ih] + simp only [List.reverse_cons, List.append_assoc, List.singleton_append] + +private theorem listReverse_inSpace : ComputableInSpace List.reverse (encList encNat) + (encList encNat) (fun l => (encList encNat l).length) := by + have hl : ComputableInSpace (fun l : List ℕ => l) (encList encNat) (encList encNat) + (fun l => (encList encNat l).length) := of_inO ComputableInDegree.id'.inO_one + have hinit := budget_const (encB := encList encNat) [] + (show (fun l : List ℕ => (encList encNat l).length) ≼ + fun l => (encList encNat l).length from .refl _) + have hstep : ComputableInSpace (fun p : List ℕ × ℕ => p.2 :: p.1) + (encPair (encList encNat) encNat) (encList encNat) + (fun p => (encPair (encList encNat) encNat p).length) := + budget_cons (snd _ _) (fst _ _) + have hstate (l : List ℕ) (i : ℕ) (_hi : i ≤ l.length) : + (encList encNat ((l.take i).foldl (fun acc n => n :: acc) [])).length ≤ + (encList encNat l).length := by + rw [List.foldl_flip_cons_eq_append', List.append_nil] + simp only [length_encList, List.map_reverse, List.sum_reverse] + simpa only [length_encList] using length_encList_take_le encNat l i + have hspace (l : List ℕ) (i : ℕ) (hi : i < l.length) : + (encPair (encList encNat) encNat + ((l.take i).foldl (fun acc n => n :: acc) [], l[i])).length ≤ + 4 * ((encList encNat l).length + 1) := by + have ha := length_le_encList_of_mem encNat (List.getElem_mem hi) + have hs := hstate l i hi.le + simp only [length_encPair] + omega + have hf := ComputableInSpace.foldl hl hinit hstep hspace hstate + refine hf.weaken (.of_le_linear 8 8 fun _ => by omega) |>.of_enc_eq ?_ + intro l + simp only [List.foldl_flip_cons_eq_append', List.append_nil] + +/-- Decode actual pairs of input bits in linear space, with at most one cell per two bits. -/ +theorem cells_inSpace : ComputableInSpace cells encBits (encList encNat) + (fun word => word.length) := by + have hl : ComputableInSpace (fun word : List Bool => word) encBits encBits + (fun word => word.length) := of_inO ComputableInDegree.id'.inO_one + have hinit : ComputableInSpace (fun _ : List Bool => ((false, false, []) : Scan)) encBits + encScan (fun word => word.length) := budget_const _ (.refl _) + have hstate (word : List Bool) (i : ℕ) (hi : i ≤ word.length) : + (encScan ((word.take i).foldl scan (false, false, []))).length ≤ 6 + 5 * word.length := by + have h := scan_fold_length (word.take i) (false, false, []) + simp only [encList_nil, List.length_nil, List.length_take] at h + simp only [encScan, length_encPair, encBool_apply, List.length_singleton] + omega + have hspace (word : List Bool) (i : ℕ) (hi : i < word.length) : + (encPair encScan encBool ((word.take i).foldl scan (false, false, []), word[i])).length ≤ + 20 * (word.length + 1) := by + have h := hstate word i hi.le + rw [length_encPair] + simp only [encBool_apply, List.length_singleton] + omega + have hf := ComputableInSpace.foldl hl hinit scan_inSpace hspace hstate + have hout := budget_comp_linear listReverse_inSpace hf.snd'.snd' + refine hout.weaken (.of_le_linear 100 100 fun _ => by + simp only [encBits_apply] + omega) |>.of_enc_eq ?_ + intro word + simp only [scan_fold_cells, List.append_nil, List.reverse_reverse] + +/-- Streaming bit pairs creates exactly one cell for every two input bits. -/ +theorem length_cells (word : List Bool) : (cells word).length = word.length / 2 := by + induction word using List.twoStepInduction with + | nil => rfl + | singleton bit => simp [cells] + | cons_cons first second word ih _ => + simp only [cells, List.length_cons, ih] + omega + +/-- A correctly sized full label yields exactly three blocks of cells on every work tape, +including unused bit words and labels whose outer tag is FAIL. -/ +theorem length_payload_cells_of_length (e : Q ≃ Fin s) (b : ℕ) (word : List Bool) + (hlen : word.length = width k s b) (h : Fin k) : + ((payload e b word).2.2.2 h).length = 3 * b := by + change (cells ((word.drop (trackOffset k s b + 2 + h.val * (6 * b))).take (6 * b))).length = _ + rw [length_cells] + have hoff : trackOffset k s b + 2 + h.val * (6 * b) + 6 * b ≤ word.length := by + have hmul := Nat.mul_le_mul_right (6 * b) (Nat.succ_le_of_lt h.isLt) + rw [hlen, width_eq] + unfold trackOffset + nlinarith + simp only [List.length_take, List.length_drop] + rw [min_eq_left (by omega)] + omega + +/-- The explicit fixed-width Boolean representation supplies canonical-sized replay windows. -/ +theorem length_payload_cells (e : Q ≃ Fin s) (b : ℕ) (bits : BitString (width k s b)) + (h : Fin k) : ((payload e b (List.ofFn bits)).2.2.2 h).length = 3 * b := + length_payload_cells_of_length e b (List.ofFn bits) (List.length_ofFn) h + +private theorem piLinear {α Index Value : Type} [Fintype Index] + {input : α ↪ List Bool} {enc : Value ↪ List Bool} {values : α → Index → Value} + (hvalues : ∀ i, ComputableInSpace (fun a => values a i) input enc + (fun a => (input a).length)) : + ComputableInSpace values input (encPi enc) (fun a => (input a).length) := by + classical + choose T hT using fun i => (hvalues i).exists_inO + refine (of_inO (ComputableInO.pi hT)).weaken ?_ + exact ⟨Fintype.card Index + 1, fun a => by simp; nlinarith⟩ + +variable [Finite Q] + +private theorem state_inSpace (e : Q ≃ Fin s) : ComputableInSpace (state e) encNat + (encFinite (Option Q)) (fun n => (encNat n).length) := by + classical + let tags := List.ofFn fun i : Fin (s + 1) => (stateIndex e).symm i + have h := dispatch tags none (fun tag _ => tag) + (fun tag => budget_const (encB := encFinite (Option Q)) tag (.refl _)) + (of_inO (ComputableInDegree.id' (enc := encNat)).inO_one) + refine h.of_enc_eq ?_ + intro n + by_cases hn : n < s + 1 + · simp only [state, hn, ↓reduceDIte] + rw [List.getD_eq_getElem _ _ (by simpa only [tags, List.length_ofFn] using hn)] + simp only [tags, List.getElem_ofFn] + · simp only [state, hn, ↓reduceDIte] + rw [List.getD_eq_default _ _ (by simpa only [tags, List.length_ofFn] using Nat.le_of_not_gt hn)] + +/-- Runtime width and an actual stored Boolean label. -/ +abbrev encArgs : (ℕ × List Bool) ↪ List Bool := encPair encNat encBits + +private theorem trackOffset_inSpace : ComputableInSpace + (fun p : ℕ × List Bool => trackOffset k s p.1) encArgs encNat + (fun p => (encArgs p).length) := by + have hb := fst encNat encBits + have hsize := budget_comp_linear natSize hb + exact budget_natAdd (budget_const (1 + (s + 1).size) (.refl _)) + (budget_natMul (budget_const (k + 1) (.refl _)) hsize) + +private theorem payload_inSpace (e : Q ≃ Fin s) : ComputableInSpace + (fun p : ℕ × List Bool => payload (k := k) e p.1 p.2) encArgs encRawResult + (fun p => (encArgs p).length) := by + classical + have hb := fst encNat encBits + have hw := snd encNat encBits + have hsize := budget_comp_linear natSize hb + have hstateRead := budget_comp_linear readNat_inSpace + (budget_prod hw (budget_prod (budget_const 1 (.refl _)) + (budget_const (s + 1).size (.refl _)))) + have hstate := budget_comp_linear (state_inSpace e) hstateRead + have hheads := piLinear (Index := Option (Fin k)) fun i => + budget_natMod (budget_comp_linear readNat_inSpace + (budget_prod hw (budget_prod + (budget_natAdd (budget_const (1 + (s + 1).size) (.refl _)) + (budget_natMul (budget_const ((finSuccEquiv k).symm i).val (.refl _)) hsize)) + hsize))) hb + have htrack0 := budget_comp_linear bit_inSpace + (budget_prod hw (trackOffset_inSpace (k := k) (s := s))) + have htrack1 := budget_comp_linear bit_inSpace + (budget_prod hw (budget_natSucc (trackOffset_inSpace (k := k) (s := s)))) + have htrack := budget_finite (encC := encFinite OutputTrack) + (fun q : Bool × Bool => if q.2 then .other else if q.1 then .accept else .empty) + (budget_prod htrack0 htrack1) + have hwindowSize := budget_natMul (budget_const (encB := encNat) 6 (.refl _)) hb + have hcells := piLinear (Index := Fin k) fun i => + budget_comp_linear cells_inSpace (budget_comp_linear slice_inSpace + (budget_prod hw (budget_prod + (budget_natAdd (budget_natAdd (trackOffset_inSpace (k := k) (s := s)) + (budget_const 2 (.refl _))) + (budget_natMul (budget_const i.val (.refl _)) hwindowSize)) hwindowSize))) + exact (budget_prod hstate (budget_prod hheads (budget_prod htrack hcells))).of_enc_eq + fun _ => rfl + +/-- Uniform native parser. Zero width, malformed length and FAIL all return `none`. -/ +theorem parse_inSpace (e : Q ≃ Fin s) : ComputableInSpace (parse (k := k) e) encArgs + (encOption encRawResult) (fun p => (encArgs p).length) := by + have hb := fst encNat encBits + have hw := snd encNat encBits + have hsize := budget_comp_linear natSize hb + have hwidth := budget_natAdd + (budget_natAdd (budget_const (1 + (s + 1).size) (.refl _)) + (budget_natMul (budget_const (k + 1) (.refl _)) hsize)) + (budget_natAdd (budget_const 2 (.refl _)) + (budget_natMul (budget_const (6 * k) (.refl _)) hb)) + have hlength := budget_comp_linear wordLength_inSpace hw + have hbad := budget_finite (encC := encBool) (fun q : Bool × Bool => q.1 || !q.2) + (budget_prod (budget_natIsZero hb) (budget_natEq hlength hwidth)) + have htag : ComputableInSpace (fun p : ℕ × List Bool => p.2.headD false) encArgs encBool + (fun p => (encArgs p).length) := + budget_comp_linear (of_inO (.of_linear (ListEncoding.headD false))) hw + have hsome := budget_comp_linear + (of_inO (ComputableInDegree.optionSome (enc₁ := encRawResult (k := k) (Q := Q)) + (enc := encOption (encRawResult (k := k) (Q := Q)))).inO_one) (payload_inSpace (k := k) e) + have hnone : ComputableInSpace (fun _ : ℕ × List Bool => + (none : Option (RawResult k Q))) encArgs (encOption encRawResult) + (fun p => (encArgs p).length) := budget_const none (.refl _) + refine (budget_ite hbad hnone (budget_ite htag hsome hnone)).of_enc_eq ?_ + intro p + have hwidth : 1 + (s + 1).size + (k + 1) * p.1.size + (2 + 6 * k * p.1) = + width k s p.1 := by rw [width_eq]; omega + simp only [parse, hwidth, Bool.or_eq_true, Bool.not_eq_true', Bool.decide_iff, + decide_eq_false_iff_not] + +private theorem cellOfCode_cellCode (first second : Bool) : + cellOfCode (cellCode first second) = if first then some second else none := by + cases first <;> cases second <;> rfl + +private theorem cells_getD (word : List Bool) (i : ℕ) (hi : 2 * i + 1 < word.length) : + (cells word).getD i 0 = cellCode (word.getD (2 * i) false) (word.getD (2 * i + 1) false) := by + induction i generalizing word with + | zero => + rcases word with _ | ⟨first, word⟩ + · simp at hi + rcases word with _ | ⟨second, word⟩ + · simp at hi + rfl + | succ i ih => + rcases word with _ | ⟨first, word⟩ + · simp at hi + rcases word with _ | ⟨second, word⟩ + · simp at hi + have hrest : 2 * i + 1 < word.length := by simp only [List.length_cons] at hi; omega + rw [cells, List.getD_cons_succ] + rw [show 2 * (i + 1) + 1 = ((2 * i + 1) + 1) + 1 from by omega, + show 2 * (i + 1) = (2 * i + 1) + 1 from by omega] + simp only [List.getD_cons_succ] + exact ih word hrest + +private theorem slice_ofFn {n : ℕ} (bits : BitString n) (offset count : ℕ) + (h : offset + count ≤ n) : + ((List.ofFn bits).drop offset).take count = + List.ofFn (fun i : Fin count => bits ⟨offset + i.val, by omega⟩) := by + apply List.ext_getElem + · simp only [List.length_take, List.length_drop, List.length_ofFn] + omega + · intro i hi hj + simp only [List.getElem_take, List.getElem_drop, List.getElem_ofFn] + +private theorem ofBits_ofFn {n : ℕ} (bits : BitString n) : + ofBits (List.ofFn bits) = (equivFin n bits).val := by + rw [← lsbValue_ofFn] + exact (lsbValue_eq_foldr_bit _).symm + +/-- Reading an in-range numeric field agrees with the existing finite bit-string numbering. -/ +theorem readNat_ofFn {n : ℕ} (bits : BitString n) (offset count : ℕ) + (h : offset + count ≤ n) : + readNat (List.ofFn bits) offset count = + (equivFin count (fun i : Fin count => bits ⟨offset + i.val, by omega⟩)).val := by + rw [readNat, slice_ofFn bits offset count h, ofBits_ofFn] + +private theorem slice_getD_ofFn {n : ℕ} (bits : BitString n) (offset count : ℕ) + (h : offset + count ≤ n) (i : ℕ) (hi : i < count) : + (((List.ofFn bits).drop offset).take count).getD i false = + bits ⟨offset + i, by omega⟩ := by + rw [List.getD_eq_getElem _ _ (by + simp only [List.length_take, List.length_drop, List.length_ofFn]; omega)] + simp only [List.getElem_take, List.getElem_drop, List.getElem_ofFn] + +private theorem readNat_field {n count : ℕ} (bits : BitString n) (field : BitString count) + (offset : ℕ) (h : offset + count ≤ n) + (hfield : ∀ i, bits ⟨offset + i.val, by omega⟩ = field i) : + readNat (List.ofFn bits) offset count = (equivFin count field).val := by + rw [readNat_ofFn bits offset count h] + apply congrArg (fun f : BitString count => (equivFin count f).val) + funext i + exact hfield i + +private theorem getD_ofFn {n : ℕ} (bits : BitString n) (i : ℕ) (hi : i < n) : + (List.ofFn bits).getD i false = bits ⟨i, hi⟩ := by + rw [List.getD_eq_getElem _ _ (by simpa only [List.length_ofFn] using hi)] + simp only [List.getElem_ofFn] + +omit [Finite Q] in +/-- The untyped payload agrees field by field with the explicit semantic codec. -/ +theorem toView_payload (e : Q ≃ Fin s) (b : ℕ) [NeZero b] + (bits : BitString (width k s b)) : + (payload e b (List.ofFn bits)).toView b = decodePayload e (Fin.tail bits) := by + change BitString ((s + 1).size + ((k + 1) * b.size + (2 + windowBits k b)) + 1) at bits + have hw : (s + 1).size + ((k + 1) * b.size + (2 + windowBits k b)) + 1 = + 1 + (s + 1).size + (k + 1) * b.size + 2 + 6 * k * b := by + unfold windowBits + ring + have hstate : readNat (List.ofFn bits) 1 (s + 1).size = + (equivFin (s + 1).size (fstBits (Fin.tail bits))).val := by + apply readNat_field bits (fstBits (Fin.tail bits)) 1 (by omega) + intro i + simp only [fstBits_apply, Fin.tail] + apply congrArg bits + apply Fin.ext + simp only [Fin.val_succ, Fin.val_castAdd] + omega + have hheads (head : Option (Fin k)) : + readNat (List.ofFn bits) + (1 + (s + 1).size + ((finSuccEquiv k).symm head).val * b.size) b.size = + (equivFin b.size + (blocksEquiv (fstBits (sndBits (Fin.tail bits))) ((finSuccEquiv k).symm head))).val := by + apply readNat_field bits + (blocksEquiv (fstBits (sndBits (Fin.tail bits))) ((finSuccEquiv k).symm head)) + (1 + (s + 1).size + ((finSuccEquiv k).symm head).val * b.size) + (by + have hindex := Nat.mul_le_mul_right b.size + (Nat.succ_le_of_lt ((finSuccEquiv k).symm head).isLt) + nlinarith) + intro i + simp only [blocksEquiv_apply, block_apply, fstBits_apply, sndBits_apply, Fin.tail] + apply congrArg bits + apply Fin.ext + simp only [Fin.val_succ, Fin.val_castAdd, Fin.val_natAdd, finProdFinEquiv_apply_val] + ring + have htrack (i : Fin 2) : + (List.ofFn bits).getD (trackOffset k s b + i.val) false = + fstBits (sndBits (sndBits (Fin.tail bits))) i := by + rw [getD_ofFn bits _ (by unfold trackOffset; have := i.isLt; omega)] + simp only [fstBits_apply, sndBits_apply, Fin.tail] + congr 1 + ext + simp only [trackOffset, Fin.val_succ, Fin.val_castAdd, Fin.val_natAdd] + omega + apply Prod.ext + · apply Prod.ext + · change state e (readNat (List.ofFn bits) 1 (s + 1).size) = _ + rw [hstate] + rfl + · apply Prod.ext + · funext head + change (readNat (List.ofFn bits) + (1 + (s + 1).size + ((finSuccEquiv k).symm head).val * b.size) b.size % b : + ZMod b) = _ + rw [ZMod.natCast_mod, hheads] + rfl + · change (if (List.ofFn bits).getD (trackOffset k s b + 1) false then _ else + if (List.ofFn bits).getD (trackOffset k s b) false then _ else _) = _ + have hzero := htrack (0 : Fin 2) + have hone := htrack (1 : Fin 2) + simp only [Fin.val_zero, Fin.val_one, Nat.add_zero] at hzero hone + rw [hzero, hone] + rfl + · funext tape port residue + let offset := trackOffset k s b + 2 + tape.val * (6 * b) + let index := port.val * b + residue.val + have hp := port.isLt + have hr := ZMod.val_lt residue + have ht := tape.isLt + have hoff : offset + 6 * b ≤ + (s + 1).size + ((k + 1) * b.size + (2 + windowBits k b)) + 1 := by + have hmul := Nat.mul_le_mul_right (6 * b) (Nat.succ_le_of_lt ht) + dsimp [offset, trackOffset] + nlinarith + have hind : 2 * index + 1 < 6 * b := by + have hmul := Nat.mul_le_mul_right b (Nat.le_of_lt_succ hp) + dsimp [index] + nlinarith + change cellOfCode ((cells (((List.ofFn bits).drop offset).take (6 * b))).getD index 0) = _ + rw [cells_getD _ index (by + simp only [List.length_take, List.length_drop, List.length_ofFn]; omega), + slice_getD_ofFn bits offset (6 * b) hoff (2 * index) (by omega), + slice_getD_ofFn bits offset (6 * b) hoff (2 * index + 1) hind, + cellOfCode_cellCode] + have hbit (i : Fin 2) : bits ⟨offset + (2 * index + i.val), by have := i.isLt; omega⟩ = + blocksEquiv (blocksEquiv (blocksEquiv + (sndBits (sndBits (sndBits (Fin.tail bits)))) tape) port) + ⟨residue.val, hr⟩ i := by + simp only [blocksEquiv_apply, block_apply, sndBits_apply] + congr 1 + ext + dsimp [offset, index, trackOffset, windowBits] + simp only [finProdFinEquiv_apply_val] + ring + have hzero := hbit (0 : Fin 2) + have hone := hbit (1 : Fin 2) + simp only [Fin.val_zero, Fin.val_one, Nat.add_zero] at hzero hone + rw [hzero, hone] + rfl + +omit [Finite Q] in +/-- Native parsing realizes the existing explicit label decoder, including FAIL and unused +control/cell/track words. Only the finite machine control is enumerated. -/ +theorem parse_ofFn (e : Q ≃ Fin s) (b : ℕ) [NeZero b] + (bits : BitString (width k s b)) : + (parse e (b, List.ofFn bits)).map (RawResult.toView b) = decode e bits := by + have hlen : (List.ofFn bits).length = width k s b := List.length_ofFn + have htag : (List.ofFn bits).headD false = bits ⟨0, width_pos k s b⟩ := by + rw [List.headD_eq_head?_getD, List.head?_eq_getElem?] + exact getD_ofFn bits 0 (width_pos k s b) + simp only [parse, NeZero.ne b, hlen, ne_eq, not_true_eq_false, or_self, ↓reduceIte] + rw [htag] + by_cases h : bits ⟨0, width_pos k s b⟩ = true + · simp only [h, ↓reduceIte, Option.map_some, decode] + exact congrArg some (toView_payload e b bits) + · simp [decode, h] + +end Turing.MultiTapePTM.BlockComputation.Encoding.NativeDecode diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeEncode.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeEncode.lean new file mode 100644 index 0000000000..7cf5892310 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeEncode.lean @@ -0,0 +1,402 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAccess +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Bit +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.BitList +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatSize +import Complexity.Foundations.Data.BitString.List +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Pi + +/-! +# Native serialization of runtime-width block results + +The finite machine and its control numbering are fixed; block width, residues and all window +cells are runtime data. The serializer uses exactly the semantic codec's FAIL bit, control +rank, input-first head ordering, track bits and row-major two-bit cells. Raw ternary symbol +codes are decoded before serialization: written one is `[true,true]`, not the binary code 2. + +Both FAIL and successful results retain explicit window buffers at the input boundary. +Every tape must contain exactly three blocks before serialization is accepted. Thus even the +zero-filled FAIL word is paid by actual stored windows, with binary width metadata paying for +head padding when there are no work tapes. Allocating these buffers remains a caller operation. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.Encoding.NativeEncode + +open Cslib Cslib.BitString MultiTapeTM MultiTapeTM.ComputableInSpace QueryReplay +open MultiTapeNTM.ChoiceTable + +variable {k s : ℕ} {Q : Type} [Finite Q] + +/-- Runtime block width, success tag and explicit returned local data. FAIL also retains its +buffers, so its fixed-width serialized output has an actual encoded space charge. -/ +abbrev Args (k : ℕ) (Q : Type) := ℕ × Bool × RawResult k Q + +/-- Binary width and head residues, finite control tags and explicit framed cell lists. -/ +@[expose] noncomputable def encArgs : Args k Q ↪ List Bool := + encPair encNat (encPair encBool (encRawResult (k := k) (Q := Q))) + +/-- Exactly three blocks on every tape. A rejected call returns the empty word. -/ +@[expose] def valid (a : Args k Q) : Bool := + let (b, _, (_, _, _, cells)) := a + (List.ofFn fun tape : Fin k => decide ((cells tape).length = 3 * b)).all id + +/-- Fixed-width little-endian binary word. It reads bits directly without allocating a power. -/ +@[expose] def word (b n : ℕ) : List Bool := + List.ofFn fun j : Fin b.size => n.testBit j + +/-- Raw ternary cells pass through the same semantic cell decoder as replay results. -/ +@[expose] def cellWord (n : ℕ) : List Bool := + List.ofFn (encodeCell (cellOfCode n)) + +/-- Serialize one explicit local window. The list is streamed, without an index list. -/ +@[expose] def windowWord (cells : List ℕ) : List Bool := cells.flatMap cellWord + +/-- The payload in the semantic codec's field order. In particular, `finSuccEquiv k` places +`none` (the input head) first, followed by work heads. -/ +@[expose] def payload (e : Q ≃ Fin s) (b : ℕ) (raw : RawResult k Q) : List Bool := + let (control, residues, track, cells) := raw + List.ofFn (encodeState e control) ++ + (List.ofFn fun h : Fin (k + 1) => word b (residues (finSuccEquiv k h) % b)).flatten ++ + List.ofFn (encodeTrack track) ++ (List.ofFn fun tape : Fin k => windowWord (cells tape)).flatten + +/-- Serialize a success or FAIL using the existing fixed layout. FAIL blanks every payload +bit by streaming the charged payload, rather than allocating an uncharged width-sized word. -/ +@[expose] def serialize (e : Q ≃ Fin s) (a : Args k Q) : List Bool := + let (b, success, raw) := a + if valid a then success :: (payload e b raw).map (fun bit => success && bit) else [] + +private theorem append_budget {α : Type} {enc : α ↪ List Bool} {S : α → ℕ} + {f g : α → List Bool} + (hf : ComputableInSpace f enc (encList encBool) S) + (hg : ComputableInSpace g enc (encList encBool) S) : + ComputableInSpace (fun a => f a ++ g a) enc (encList encBool) S := + (hf.append hg (fun _ => encList_append _ _ _)).weaken (.add (.refl _) (.refl _)) + +private theorem finiteAppend {α : Type} {enc : α ↪ List Bool} {S : α → ℕ} + (hlen : (fun a => (enc a).length) ≼ S) {n : ℕ} {f : Fin n → α → List Bool} + (hf : ∀ i, ComputableInSpace (f i) enc (encList encBool) S) : + ComputableInSpace (fun a => (List.ofFn fun i => f i a).flatten) + enc (encList encBool) S := by + induction n with + | zero => exact (budget_const [] hlen).of_enc_eq fun _ => rfl + | succ n ih => + have ht := ih (f := fun i => f i.succ) (fun i => hf i.succ) + refine (append_budget (hf 0) ht).of_enc_eq ?_ + intro a + simp only [List.ofFn_succ, List.flatten_cons] + +private abbrev WordState := (ℕ × ℕ) × ℕ × List Bool +private abbrev encWordState : WordState ↪ List Bool := + encPair (encPair encNat encNat) (encPair encNat (encList encBool)) + +private def wordStep (q : WordState) : WordState := + (q.1, q.2.1 + 1, q.2.2 ++ [q.1.2.testBit q.2.1]) + +private theorem iterate_wordStep (b n j : ℕ) (out : List Bool) (t : ℕ) : + wordStep^[t] ((b, n), j, out) = + ((b, n), j + t, out ++ (List.range t).map (fun i => n.testBit (j + i))) := by + induction t with + | zero => simp + | succ t ih => + rw [Function.iterate_succ_apply', ih] + simp only [wordStep, List.range_succ, List.map_append, List.map_singleton, List.append_assoc] + congr 2 + +private theorem wordStep_inSpace : + ComputableInSpace wordStep encWordState encWordState + (fun q => (encWordState q).length) := by + have hp := fst (encPair encNat encNat) (encPair encNat (encList encBool)) + have hrest := snd (encPair encNat encNat) (encPair encNat (encList encBool)) + have hj := fst' hrest + have hout := snd' hrest + have hbit := budget_comp_lin natTestBit (linear_mul 32) (budget_prod (snd' hp) hj) + exact (budget_prod hp (budget_prod (budget_natSucc hj) (budget_snoc hout hbit))).of_enc_eq + fun _ => rfl + +/-- Binary width metadata pays the fixed-width head word, including at tape count zero. -/ +theorem word_inSpace : ComputableInSpace (fun q : ℕ × ℕ => word q.1 q.2) + (encPair encNat encNat) (encList encBool) + (fun q => (encPair encNat encNat q).length) := by + have hb := fst encNat encNat + have hn := budget_comp_linear natSize hb + have hid : ComputableInSpace (fun q : ℕ × ℕ => q) (encPair encNat encNat) + (encPair encNat encNat) (fun q => (encPair encNat encNat q).length) := + of_inO ComputableInDegree.id'.inO_one + have hinit := budget_prod hid (budget_prod (budget_const (encB := encNat) 0 (.refl _)) + (budget_const (encB := encList encBool) [] (.refl _))) + have hstate (q : ℕ × ℕ) (i : ℕ) (hi : i ≤ q.1.size) : + (encWordState (wordStep^[i] (q, 0, []))).length ≤ + 16 * ((encPair encNat encNat q).length + 1) := by + rcases q with ⟨b, n⟩ + rw [iterate_wordStep] + have hc := length_encNat_le_self i + change i ≤ b.size at hi + rw [← length_encNat_eq_size b] at hi + have hout : (encList encBool ((List.range i).map (fun j => n.testBit j))).length = 3 * i := by + simp [length_encList, List.map_map, Function.comp_def, encBool_apply, Nat.mul_comm] + simp only [encWordState, length_encPair, Nat.zero_add, List.nil_append] + rw [hout] + omega + have h := ComputableInSpace.iterate hn hinit wordStep_inSpace + (Q := fun q => 16 * ((encPair encNat encNat q).length + 1)) + (B := fun q => 16 * ((encPair encNat encNat q).length + 1)) hstate hstate + refine (snd' (snd' h)).weaken ?_ |>.of_enc_eq ?_ + · refine .of_le_linear 128 128 fun q => ?_ + have hc := length_encNat_le_self q.1.size + have hbsize := length_encNat_eq_size q.1 + rw [← hbsize] at hc ⊢ + simp only [length_encPair] + omega + · intro q + rw [iterate_wordStep] + apply congrArg (encList encBool) + simp only [word, Nat.zero_add, List.nil_append] + apply List.ext_getElem (by simp) + intro i hi hi' + simp + +private theorem cellWord_inSpace : ComputableInSpace cellWord encNat (encList encBool) + (fun n => (encNat n).length) := by + have hn : ComputableInSpace (fun n : ℕ => n) encNat encNat + (fun n => (encNat n).length) := of_inO ComputableInDegree.id'.inO_one + have ho := budget_const (encIn := encNat) (encB := encNat) 1 (.refl _) + have ht := budget_const (encIn := encNat) (encB := encNat) 2 (.refl _) + refine (budget_ite (budget_natEq hn ho) + (budget_const (encB := encList encBool) [true, false] (.refl _)) + (budget_ite (budget_natEq hn ht) + (budget_const (encB := encList encBool) [true, true] (.refl _)) + (budget_const (encB := encList encBool) [false, false] (.refl _)))).of_enc_eq ?_ + intro n + by_cases h1 : n = 1 <;> by_cases h2 : n = 2 <;> simp [cellWord, cellOfCode, h1, h2, encodeCell] + +private theorem length_windowWord (cells : List ℕ) : + (windowWord cells).length = 2 * cells.length := by + simp [windowWord, cellWord, List.length_flatMap, Nat.mul_comm] + +private theorem foldl_cells (cells : List ℕ) (out : List Bool) : + cells.foldl (fun acc n => acc ++ cellWord n) out = out ++ windowWord cells := by + induction cells generalizing out with + | nil => simp [windowWord] + | cons n cells ih => simp [List.foldl_cons, ih, windowWord, List.append_assoc] + +/-- Cell conversion streams the supplied list, so malformed short buffers never cause padding +up to the requested block width. -/ +theorem windowWord_inSpace : ComputableInSpace windowWord (encList encNat) (encList encBool) + (fun cells => (encList encNat cells).length) := by + have hid : ComputableInSpace (fun l : List ℕ => l) (encList encNat) (encList encNat) + (fun l => (encList encNat l).length) := of_inO ComputableInDegree.id'.inO_one + have hs := append_budget (fst (encList encBool) encNat) + (budget_comp_linear cellWord_inSpace (snd (encList encBool) encNat)) + have hstate (cells : List ℕ) (i : ℕ) (hi : i ≤ cells.length) : + (encList encBool ((cells.take i).foldl (fun acc n => acc ++ cellWord n) [])).length ≤ + 6 * (encList encNat cells).length := by + rw [foldl_cells, List.nil_append] + have hl := length_le_length_encList encNat cells + have hw := length_windowWord (cells.take i) + have hbool (l : List Bool) : (encList encBool l).length = 3 * l.length := by + simp [length_encList, encBool_apply, Nat.mul_comm] + rw [hbool, hw] + nlinarith [List.length_take_le i cells] + have hspace (cells : List ℕ) (i : ℕ) (hi : i < cells.length) : + (encPair (encList encBool) encNat + ((cells.take i).foldl (fun acc n => acc ++ cellWord n) [], cells[i])).length ≤ + 16 * ((encList encNat cells).length + 1) := by + have h := hstate cells i hi.le + have hv := length_le_encList_of_mem encNat (List.getElem_mem hi) + simp only [length_encPair] + omega + have h := ComputableInSpace.foldl hid (budget_const [] (.refl _)) hs hspace hstate + refine h.weaken (.of_le_linear 64 64 fun _ => by omega) |>.of_enc_eq ?_ + intro cells + simp only [foldl_cells, List.nil_append] + +private theorem finitePi {α I V : Type} [Fintype I] {enc : α ↪ List Bool} + {encV : V ↪ List Bool} {f : α → I → V} + (hf : ∀ i, ComputableInSpace (fun a => f a i) enc encV (fun a => (enc a).length)) : + ComputableInSpace f enc (encPi encV) (fun a => (enc a).length) := by + choose T hT using fun i => (hf i).exists_inO + exact (of_inO (ComputableInO.pi hT)).weaken + ⟨Fintype.card I + 1, fun a => by simp; nlinarith⟩ + +private theorem valid_inSpace : ComputableInSpace (valid (k := k) (Q := Q)) + encArgs encBool (fun a => (encArgs a).length) := by + classical + have hb := fst encNat (encPair encBool (encRawResult (k := k) (Q := Q))) + have hraw := snd' (snd encNat (encPair encBool (encRawResult (k := k) (Q := Q)))) + have hcells := snd' (snd' (snd' hraw)) + have hbits (i : Fin k) := budget_natEq + (budget_length (budget_comp_linear (of_inO (ComputableInO.pi_apply (encList encNat) i)) hcells)) + (budget_natMul (budget_const (encB := encNat) 3 (.refl _)) hb) + have htuple := finitePi hbits + refine (budget_finite (encC := encBool) + (fun checks : Fin k → Bool => (List.ofFn checks).all id) htuple).of_enc_eq fun _ => rfl + +/-- All data fields are serialized in native space linear in their actual encodings. -/ +theorem payload_inSpace (e : Q ≃ Fin s) : + ComputableInSpace (fun a : ℕ × RawResult k Q => payload e a.1 a.2) + (encPair encNat (encRawResult (k := k) (Q := Q))) (encList encBool) + (fun a => (encPair encNat (encRawResult (k := k) (Q := Q)) a).length) := by + classical + have hb := fst encNat (encRawResult (k := k) (Q := Q)) + have hr := snd encNat (encRawResult (k := k) (Q := Q)) + have hcontrol := fst' hr + have hres := fst' (snd' hr) + have htrack := fst' (snd' (snd' hr)) + have hcells := snd' (snd' (snd' hr)) + have hs := budget_finite (encC := encList encBool) + (fun q : Option Q => List.ofFn (encodeState e q)) hcontrol + have hh (i : Fin (k + 1)) := budget_comp_linear word_inSpace + (budget_prod hb (budget_natMod + (budget_comp_linear (of_inO (ComputableInO.pi_apply encNat (finSuccEquiv k i))) hres) hb)) + have ht := budget_finite (encC := encList encBool) + (fun t : OutputTrack => List.ofFn (encodeTrack t)) htrack + have hw (i : Fin k) := budget_comp_linear windowWord_inSpace + (budget_comp_linear (of_inO (ComputableInO.pi_apply (encList encNat) i)) hcells) + exact (append_budget (append_budget (append_budget hs (finiteAppend (.refl _) hh)) ht) + (finiteAppend (.refl _) hw)).of_enc_eq fun _ => rfl + +/-- Both tag branches have a native linear-space certificate. The FAIL branch streams the +retained payload; it cannot create a width-sized word from binary width metadata alone. -/ +theorem serialize_inSpace (e : Q ≃ Fin s) : + ComputableInSpace (serialize e : Args k Q → List Bool) encArgs (encList encBool) + (fun a => (encArgs a).length) := by + classical + have hb := fst encNat (encPair encBool (encRawResult (k := k) (Q := Q))) + have hrest := snd encNat (encPair encBool (encRawResult (k := k) (Q := Q))) + have htag := fst' hrest + have hraw := snd' hrest + have hpayload := budget_comp_linear (payload_inSpace e) (budget_prod hb hraw) + -- Mapping the tag over a Boolean word is a fixed Boolean operation, not a lookup on words. + have hmap := ComputableInSpace.mapWith + (enc := encBool) (encP := encBool) (encK := encBool) + (budget_finite (encC := encBool) (fun p : Bool × Bool => p.1 && p.2) + (of_inO ComputableInDegree.id'.inO_one)) htag hpayload + have hmap' : ComputableInSpace + (fun a : Args k Q => (payload e a.1 a.2.2).map (fun bit => a.2.1 && bit)) + encArgs (encList encBool) (fun a => (encArgs a).length) := by + refine hmap.weaken ?_ + obtain ⟨c, hc⟩ := hpayload.length_le + refine .of_le_linear (32 * (c + 1)) (32 * (c + 1)) fun a => ?_ + have hm : (encList encBool ((payload e a.1 a.2.2).map (fun bit => a.2.1 && bit))).length = + (encList encBool (payload e a.1 a.2.2)).length := by + simp [length_encList, List.map_map, Function.comp_def, encBool_apply] + have htaglen : (encBool a.2.1).length = 1 := rfl + simp only [hm, htaglen] + have hmax : listMax (fun p : Bool => (encPair encBool encBool (a.2.1, p)).length) + (payload e a.1 a.2.2) ≤ 4 := by + apply listMax_le + intro bit hbit + simp [encBool_apply] + have hc' := hc a + dsimp only [encArgs] at ⊢ + nlinarith + exact (budget_ite valid_inSpace (budget_cons htag hmap') + (budget_const [] (.refl _))).of_enc_eq fun _ => rfl + +/-- Raw-word output composes directly with the native parser's `encBits` input. -/ +theorem serialize_bits_inSpace (e : Q ≃ Fin s) : + ComputableInSpace (serialize e : Args k Q → List Bool) encArgs encBits + (fun a => (encArgs a).length) := + (budget_comp_linear (f := serialize e) (g := fun l : List Bool => l) + listToBits (serialize_inSpace e)).of_enc_eq fun _ => rfl + +/-- Direct bit access uses the semantic codec's little-endian convention. -/ +theorem word_encodeResidue {b : ℕ} [NeZero b] (n : ℕ) : + word b (n % b) = List.ofFn (encodeResidue (n : ZMod b)) := by + unfold word + congr 1 + funext j + simp only [encodeResidue, equivFin_symm_apply, ZMod.val_natCast] + +private theorem windowWord_encode {b : ℕ} (cells : List ℕ) + (hlen : cells.length = 3 * b) : + windowWord cells = List.ofFn + (blocksEquiv.symm fun port : Fin 3 => blocksEquiv.symm fun r : Fin b => + encodeCell (cellOfCode (cells.getD (port.val * b + r.val) 0))) := by + rw [ofFn_blocksEquiv_symm] + simp only [ofFn_blocksEquiv_symm] + have hlist : List.ofFn (fun i : Fin (3 * b) => cells.getD i 0) = cells := by + apply List.ext_getElem (by simp [hlen]) + intro i hi hi' + simp [List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hi'] + have hflat : windowWord cells = + (List.ofFn fun i : Fin (3 * b) => cellWord (cells.getD i 0)).flatten := by + have hmap := List.map_ofFn (f := fun i : Fin (3 * b) => cells.getD i 0) (g := cellWord) + rw [hlist] at hmap + simp only [Function.comp_def] at hmap + rw [← hmap] + rfl + rw [hflat, List.ofFn_mul, List.flatten_flatten, List.map_ofFn] + rfl + +private theorem ofFn_append {m n : ℕ} (x : Fin m → Bool) (y : Fin n → Bool) : + List.ofFn (Fin.append x y) = List.ofFn x ++ List.ofFn y := by + rw [List.ofFn_add] + simp + +omit [Finite Q] in +/-- Canonical window buffers serialize exactly as the existing positive-width codec. Head +residues may be arbitrary naturals: runtime modular reduction agrees with `RawResult.toView`. -/ +theorem payload_eq_encodePayload (e : Q ≃ Fin s) {b : ℕ} [NeZero b] + (raw : RawResult k Q) (hvalid : valid (b, true, raw) = true) : + payload e b raw = List.ofFn (encodePayload e (raw.toView b)) := by + rcases raw with ⟨control, residues, track, cells⟩ + have hlen (i : Fin k) : (cells i).length = 3 * b := by + have h := List.all_eq_true.mp hvalid _ (List.mem_ofFn.mpr ⟨i, rfl⟩) + exact of_decide_eq_true h + have hh : (List.ofFn fun h : Fin (k + 1) => + word b (residues (finSuccEquiv k h) % b)).flatten = + List.ofFn (encodeHeads (fun h => (residues h : ZMod b))) := by + rw [encodeHeads, ofFn_blocksEquiv_symm] + apply congrArg List.flatten + apply congrArg List.ofFn + funext i + exact word_encodeResidue (residues (finSuccEquiv k i)) + have hw : (List.ofFn fun tape : Fin k => windowWord (cells tape)).flatten = + List.ofFn (encodeWindows (b := b) (fun h d r => + cellOfCode ((cells h).getD (d.val * b + r.val) 0))) := by + unfold windowBits + rw [encodeWindows, ofFn_blocksEquiv_symm] + apply congrArg List.flatten + apply congrArg List.ofFn + funext i + have hmod (r : Fin b) : r.val % b = r.val := Nat.mod_eq_of_lt r.isLt + simpa only [ZMod.val_natCast, hmod] using windowWord_encode (cells i) (hlen i) + unfold payloadBits + simp only [payload, RawResult.toView, encodePayload, ofFn_append, List.append_assoc] + rw [hh, hw] + +omit [Finite Q] in +/-- Successful serialization preserves the outer tag and every field of the semantic codec. -/ +theorem serialize_some (e : Q ≃ Fin s) {b : ℕ} [NeZero b] (raw : RawResult k Q) + (hvalid : valid (b, true, raw) = true) : + serialize e (b, true, raw) = List.ofFn (encode e (some (raw.toView b))) := by + unfold width + simp only [serialize, hvalid, ↓reduceIte, Bool.true_and, List.map_id_fun', id_eq, + payload_eq_encodePayload e raw hvalid, encode, List.ofFn_succ] + rfl + +omit [Finite Q] in +/-- FAIL is encoded at the same paid width; its buffers remain part of the caller's input. -/ +theorem serialize_fail (e : Q ≃ Fin s) {b : ℕ} [NeZero b] (raw : RawResult k Q) + (hvalid : valid (b, false, raw) = true) : + serialize e (b, false, raw) = List.ofFn (encode (k := k) (b := b) e none) := by + have hv : valid (b, true, raw) = true := hvalid + have hp := payload_eq_encodePayload e raw hv + unfold width + simp only [serialize, hvalid, ↓reduceIte, Bool.false_and, hp, List.map_ofFn, + encode, List.ofFn_succ, Function.comp_def] + rfl + +end Turing.MultiTapePTM.BlockComputation.Encoding.NativeEncode diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayBounds.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayBounds.lean new file mode 100644 index 0000000000..30cf045a89 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayBounds.lean @@ -0,0 +1,117 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAccess + +/-! +# Storage of actual local replay runs + +The conditional representation bound is discharged for every physical clock of replay from a +loaded view, including clocks after stopping. The original input contributes only its logarithm; +no caller-supplied bound on intermediate windows or heads remains. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay + +variable {k : ℕ} {Q : Type} + +private theorem runFrom_fixed {S : Type} (M : QueryMachine S) (w : List Bool) (s : S) + (h : M.halted s = true) (n : ℕ) : M.runFrom w s n = s := by + induction n with + | zero => rfl + | succ n ih => rw [QueryMachine.runFrom_succ, ih]; simp [QueryMachine.step, h] + +/-- Once the exact replay duration is exhausted, the physical state stays fixed. -/ +theorem runFrom_loadView_min (M : MultiTapeTM k Bool Q) (w : List Bool) + (b : ℕ) (J : Option (Fin k) → ℤ) (v : View k Q b) (t : ℕ) : + (machine M).runFrom w (loadView w.length b J v) t = + (machine M).runFrom w (loadView w.length b J v) (min t b) := by + by_cases ht : t ≤ b + · rw [Nat.min_eq_left ht] + · rw [Nat.min_eq_right (by omega)] + conv_lhs => rw [show t = b + (t - b) by omega] + rw [QueryMachine.runFrom_add] + exact runFrom_fixed _ _ _ (halted_runFrom_loadView M b J v) _ + +variable [Finite Q] + +/-- All physical replay clocks satisfy the explicit local-window encoding bound. This includes +malformed absolute input origins, whose loader follows the existing blank-boundary convention. -/ +theorem length_runFrom_loadView_le (M : MultiTapeTM k Bool Q) (w : List Bool) + (b : ℕ) [NeZero b] (J : Option (Fin k) → ℤ) (v : View k Q b) (t : ℕ) : + (encConfig ((machine M).runFrom w (loadView w.length b J v) t)).length ≤ + 1024 * (k + 1) * + (b + Nat.log2 w.length + Nat.card (Option Q) + + Nat.card MultiTapeNTM.ChoiceTable.OutputTrack + 1) := by + rw [runFrom_loadView_min M w b J v t] + let u := min t b + let state := (machine M).runFrom w (loadView w.length b J v) u + have hu : u ≤ b := Nat.min_le_right _ _ + have hrep := runFrom_loadView_represents (w := w) M b J v hu + have hstart := loadView_represents (w := w) b J v + have hwidth : state.width = b := width_runFrom M w _ u + have hrem : state.remaining ≤ state.width := by + rw [hwidth] + change ((machine M).runFrom w (loadView w.length b J v) u).remaining ≤ b + rw [remaining_runFrom] + change b - u ≤ b + omega + have hin : state.inputPos ≤ state.inputLength + 1 := by + rw [hrep.inputLength, hrep.inputPos] + have hp := (M.runFrom (embedCfg (input := w) b J v.1 v.2) u).inputPos.isLt + omega + have hheads (h : Fin k) : state.heads h ≤ 3 * state.width := by + have hdist := abs_le.mp + (M.workTapePos_runFrom_abs_sub_le (embedCfg (input := w) b J v.1 v.2) u h) + rw [hrep.heads h, hstart.heads h] at hdist + have hr := ZMod.val_lt (v.1.2.1 (some h)) + rw [show (loadView w.length b J v).heads h = b + (v.1.2.1 (some h)).val from rfl] + at hdist + push_cast at hdist + rw [hwidth] + change ((machine M).runFrom w (loadView w.length b J v) u).heads h ≤ 3 * b + omega + have hcells (h : Fin k) : (state.cells h).length ≤ 3 * state.width := by + rw [hwidth] + change (((machine M).runFrom w (loadView w.length b J v) u).cells h).length ≤ 3 * b + rw [length_cells_runFrom] + simp [loadView] + have hcodes (h : Fin k) (n : ℕ) (hn : n ∈ state.cells h) : n ≤ 2 := by + have hc := cells_lt_three_runFrom M w (loadView w.length b J v) + (cells_lt_three_loadView b J v) u h n hn + omega + have hb := length_encConfig_le state hrem hin hheads hcells hcodes + rw [hwidth, hrep.inputLength] at hb + exact hb + +/-- Extracting any actual replay prefix retains only linear local-window data. The original +input length does not appear in the returned representation. -/ +theorem length_rawResult_runFrom_loadView_le (M : MultiTapeTM k Bool Q) (w : List Bool) + (b : ℕ) (J : Option (Fin k) → ℤ) (v : View k Q b) (t : ℕ) : + (encRawResult (rawResult ((machine M).runFrom w (loadView w.length b J v) t))).length ≤ + 128 * (k + 1) * + (b + Nat.card (Option Q) + Nat.card MultiTapeNTM.ChoiceTable.OutputTrack + 1) := by + let state := (machine M).runFrom w (loadView w.length b J v) t + have hwidth : state.width = b := width_runFrom M w _ t + have hcells (h : Fin k) : (state.cells h).length ≤ 3 * state.width := by + rw [hwidth] + change (((machine M).runFrom w (loadView w.length b J v) t).cells h).length ≤ 3 * b + rw [length_cells_runFrom] + simp [loadView] + have hcodes (h : Fin k) (n : ℕ) (hn : n ∈ state.cells h) : n ≤ 2 := by + have hc := cells_lt_three_runFrom M w (loadView w.length b J v) + (cells_lt_three_loadView b J v) t h n hn + omega + have hb := length_encRawResult_le state hcells hcodes + rw [hwidth] at hb + exact hb + +end Turing.MultiTapePTM.BlockComputation.QueryReplay diff --git a/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/BitList.lean b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/BitList.lean new file mode 100644 index 0000000000..c56b832286 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/BitList.lean @@ -0,0 +1,58 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpaceLib + +/-! +# Removing Boolean-list framing in native linear space + +`listToBits` copies a framed list of Boolean values to a raw word without changing its contents. +The native fold keeps only an output prefix, whose length is bounded by the input list length. +This permits state-returning serializers to supply raw words to runtime codecs. +-/ + +public section + +namespace Turing.MultiTapeTM.ComputableInSpace + +/-- Remove list framing in work space linear in the actual framed input. -/ +theorem listToBits : ComputableInSpace (fun l : List Bool => l) (encList encBool) encBits + (fun l => (encList encBool l).length) := by + have hid : ComputableInSpace (fun l : List Bool => l) (encList encBool) (encList encBool) + (fun l => (encList encBool l).length) := of_inO (ComputableInO.of_eq fun _ => rfl) + have hs : ComputableInSpace (fun p : List Bool × Bool => p.1 ++ [p.2]) + (encPair encBits encBool) encBits + (fun p => (encPair encBits encBool p).length) := by + have hword := fst encBits encBool + have hbit := (snd encBits encBool).of_enc_eq + (encC := encBits) (g := fun p : List Bool × Bool => [p.2]) (fun _ => rfl) + exact (hword.append hbit (fun _ => rfl)).weaken (.add (.refl _) (.refl _)) + have hfold (l out : List Bool) : + l.foldl (fun acc bit => acc ++ [bit]) out = out ++ l := by + induction l generalizing out with + | nil => simp + | cons bit l ih => simp [List.foldl_cons, ih, List.append_assoc] + have hstate (l : List Bool) (i : ℕ) (hi : i ≤ l.length) : + (encBits ((l.take i).foldl (fun acc bit => acc ++ [bit]) [])).length ≤ + (encList encBool l).length := by + rw [hfold, List.nil_append] + exact (List.length_take_le i l).trans (hi.trans (length_le_length_encList encBool l)) + have hspace (l : List Bool) (i : ℕ) (hi : i < l.length) : + (encPair encBits encBool + ((l.take i).foldl (fun acc bit => acc ++ [bit]) [], l[i])).length ≤ + 4 * ((encList encBool l).length + 1) := by + have h := hstate l i hi.le + simp only [length_encPair, encBool_apply, List.length_singleton] + omega + have h := foldl hid (budget_const [] (.refl _)) hs hspace hstate + refine h.weaken (.of_le_linear 16 16 fun _ => by omega) |>.of_enc_eq ?_ + intro l + rw [hfold, List.nil_append] + +end Turing.MultiTapeTM.ComputableInSpace diff --git a/ComplexityTests/Machines/Turing/MultiTape/NativeBlockCodec.lean b/ComplexityTests/Machines/Turing/MultiTape/NativeBlockCodec.lean new file mode 100644 index 0000000000..a0247e488f --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/NativeBlockCodec.lean @@ -0,0 +1,68 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of the native block label codec) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeChecked + +/-! +# Native block-label codec regressions + +The explicit windows contain blank, written zero and written one. Successful and FAIL labels +share a charged buffer, malformed buffers are rejected, and parsing follows the exact existing +semantic codec rather than a second label format. +-/ + +@[expose] public section + +namespace ComplexityTests.NativeBlockCodec + +open Turing MultiTapeTM MultiTapePTM.BlockComputation +open MultiTapePTM.BlockComputation.QueryReplay + +abbrev code : Fin 1 ≃ Fin 1 := Equiv.refl _ + +def raw : RawResult 1 (Fin 1) := + (some 0, fun _ => 0, .accept, fun _ => [0, 1, 2]) + +def good := Encoding.NativeEncode.serialize code (1, true, raw) +def fail := Encoding.NativeEncode.serialize code (1, false, raw) + +example : good = [true, true, false, false, false, true, false, + false, false, true, false, true, true] := by decide + +example : good.length = Encoding.width 1 1 1 := by decide +example : fail = List.replicate (Encoding.width 1 1 1) false := by decide +example : Encoding.NativeEncode.serialize code + (2, true, raw) = [] := by decide +example : Encoding.NativeDecode.cells [false, true, true, false, true, true, true] = + [0, 1, 2] := rfl +example : Encoding.NativeDecode.readNat [true, false, true, true] 1 3 = 6 := by decide +example : Encoding.NativeDecode.parse (k := 1) code (1, fail) = none := by decide +example : Encoding.NativeDecode.parse (k := 1) code (0, good) = none := rfl +example : Encoding.NativeDecode.parse (k := 1) code (1, []) = none := by decide +example : Option.map (fun r : RawResult 1 (Fin 1) => let (_, _, _, cells) := r; cells 0) + (Encoding.NativeDecode.parse code (1, good)) = some [0, 1, 2] := by decide + +example : Encoding.NativeDecode.parse code (1, good) = some raw := by decide + +def emptyRaw : RawResult 0 (Fin 1) := (none, fun _ => 123, .other, Fin.elim0) + +-- With no work tapes the actual payload grows with the binary width, not the block width. +example : (Encoding.NativeEncode.serialize code (1000000000, true, emptyRaw)).length = + Encoding.width 0 1 1000000000 := by decide + +/-- The lenient decoder supplies defaults; the gate-facing canonical decoder must reject them. -/ +def noncanonicalBlank := good.set 8 true + +example : Encoding.NativeDecode.parse code (1, noncanonicalBlank) = some raw := by decide +example : Encoding.NativeChecked.parse (k := 1) code (1, noncanonicalBlank) = none := by decide +example : Encoding.NativeChecked.parse (k := 1) code (1, good) = some raw := by decide +example : Encoding.NativeChecked.parse (k := 1) code (1, good.set 2 true) = none := by decide +example : Encoding.NativeChecked.parse (k := 1) code (1, good.set 6 true) = none := by decide + +end ComplexityTests.NativeBlockCodec diff --git a/ComplexityTests/Machines/Turing/MultiTape/QueryReplayBounds.lean b/ComplexityTests/Machines/Turing/MultiTape/QueryReplayBounds.lean new file mode 100644 index 0000000000..435d85868d --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/QueryReplayBounds.lean @@ -0,0 +1,54 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of physical local-replay space bounds) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayBounds +public import ComplexityTests.Machines.Turing.MultiTape.QueryReplay + +/-! +# Actual replay storage bounds + +A nontrivial three-window view starts at the left input boundary. Its head residues and +canonical cell codes instantiate the all-clock bounds before and after replay halts. +-/ + +@[expose] public section + +namespace ComplexityTests.QueryReplayBounds + +open Turing MultiTapeTM MultiTapePTM.BlockComputation +open MultiTapePTM.BlockComputation.QueryReplay + +def view : View 1 Unit 2 := + ((some (), fun _ => 1, .empty), fun _ port residue => + if port = 0 then none else some (residue = 0)) + +def origin : Option (Fin 1) → ℤ := fun _ => -1 + +example (w : List Bool) (clock : ℕ) : + (encConfig ((machine QueryReplay.reader).runFrom w + (loadView w.length 2 origin view) clock)).length ≤ + 1024 * (1 + 1) * (2 + Nat.log2 w.length + Nat.card (Option Unit) + + Nat.card MultiTapeNTM.ChoiceTable.OutputTrack + 1) := + length_runFrom_loadView_le _ _ _ _ _ _ + +example (w : List Bool) (clock : ℕ) : + (encRawResult (rawResult ((machine QueryReplay.reader).runFrom w + (loadView w.length 2 origin view) clock))).length ≤ + 128 * (1 + 1) * (2 + Nat.card (Option Unit) + + Nat.card MultiTapeNTM.ChoiceTable.OutputTrack + 1) := + length_rawResult_runFrom_loadView_le _ _ _ _ _ _ + +example : (loadView 3 2 origin view).inputPos = 0 := by decide +example : ((machine QueryReplay.reader).runFrom [true] (loadView 1 2 origin view) 2).remaining + = 0 := by rw [remaining_runFrom]; rfl +example : (machine QueryReplay.reader).runFrom [true] (loadView 1 2 origin view) 100 = + (machine QueryReplay.reader).runFrom [true] (loadView 1 2 origin view) 2 := by + exact runFrom_loadView_min QueryReplay.reader [true] 2 origin view 100 + +end ComplexityTests.QueryReplayBounds From d540f54a745b4f2b57fcf5274eec9f144caa4b2b Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 01:30:52 +0000 Subject: [PATCH 18/36] feat(turing): initialize native replay from checked HPV labels --- Complexity.lean | 4 + .../Encoding/NativeDecode.lean | 49 +++-- .../Encoding/NativeEncode.lean | 11 +- .../BlockComputation/QueryReplay.lean | 4 +- .../BlockComputation/QueryReplayAccess.lean | 7 +- .../QueryReplayLabelLoad.lean | 82 +++++++++ .../BlockComputation/QueryReplayLoad.lean | 103 +++++++++++ .../QueryReplayLoadCorrect.lean | 169 ++++++++++++++++++ .../BlockComputation/QueryReplaySpace.lean | 17 +- .../MultiTape/Complexity/InSpace/Pi.lean | 45 +++++ .../Probabilistic/IntervalSimulation.lean | 2 +- .../Turing/MultiTape/QueryReplayLoad.lean | 61 +++++++ 12 files changed, 512 insertions(+), 42 deletions(-) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayLabelLoad.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayLoad.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayLoadCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/Pi.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/QueryReplayLoad.lean diff --git a/Complexity.lean b/Complexity.lean index 8cc0a3f100..323e4231f4 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1699,6 +1699,9 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplay public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAccess public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayBounds +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayLabelLoad +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayLoad +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayLoadCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputationTree public import Complexity.Computability.Machines.Turing.MultiTape.BlockTrace @@ -1747,6 +1750,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSp public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatPow public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatPrime public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatSize +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Pi public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Plain public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpaceLib public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LinearSpaceArith diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean index e10178fac7..92a15e41d2 100644 --- a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean @@ -12,6 +12,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatSize import Complexity.Computability.Machines.Turing.MultiTape.Internal.BinaryNormalization import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Constructible +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Pi import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Pi import Mathlib.Data.List.Induction @@ -68,6 +69,28 @@ input storage, even when their declared binary width is malformed. -/ if p.1 = 0 ∨ p.2.length ≠ width k s p.1 then none else if p.2.headD false then some (payload e p.1 p.2) else none +/-- Successful parsing exposes the exact input shape and the decoded payload. -/ +theorem parse_eq_some_iff (e : Q ≃ Fin s) (b : ℕ) (word : List Bool) (raw : RawResult k Q) : + parse e (b, word) = some raw ↔ b ≠ 0 ∧ word.length = width k s b ∧ + word.headD false = true ∧ raw = payload e b word := by + constructor + · intro hp + unfold parse at hp + split at hp + · contradiction + · rename_i hbad + have hb : b ≠ 0 := fun h => hbad (Or.inl h) + have hw : word.length = width k s b := by + by_contra h + exact hbad (Or.inr h) + split at hp + · rename_i ht + exact ⟨hb, hw, ht, (Option.some.inj hp).symm⟩ + · contradiction + · rintro ⟨hb, hw, ht, rfl⟩ + simp only [parse, hb, hw, ne_eq, not_true_eq_false, or_self, ↓reduceIte, ht] + + private theorem wordDrop_inSpace : ComputableInSpace (fun p : List Bool × ℕ => p.1.drop p.2) (encPair encBits encNat) encBits (fun p => (encPair encBits encNat p).length) := by @@ -242,6 +265,18 @@ theorem length_cells (word : List Bool) : (cells word).length = word.length / 2 simp only [cells, List.length_cons, ih] omega +/-- Every streamed cell is one of the three canonical replay codes. -/ +theorem mem_cells_le (word : List Bool) : ∀ n ∈ cells word, n ≤ 2 := by + induction word using List.twoStepInduction with + | nil => simp [cells] + | singleton bit => simp [cells] + | cons_cons first second word ih _ => + intro n hn + simp only [cells, List.mem_cons] at hn + rcases hn with rfl | hn + · cases first <;> cases second <;> decide + · exact ih n hn + /-- A correctly sized full label yields exactly three blocks of cells on every work tape, including unused bit words and labels whose outer tag is FAIL. -/ theorem length_payload_cells_of_length (e : Q ≃ Fin s) (b : ℕ) (word : List Bool) @@ -263,16 +298,6 @@ theorem length_payload_cells (e : Q ≃ Fin s) (b : ℕ) (bits : BitString (widt (h : Fin k) : ((payload e b (List.ofFn bits)).2.2.2 h).length = 3 * b := length_payload_cells_of_length e b (List.ofFn bits) (List.length_ofFn) h -private theorem piLinear {α Index Value : Type} [Fintype Index] - {input : α ↪ List Bool} {enc : Value ↪ List Bool} {values : α → Index → Value} - (hvalues : ∀ i, ComputableInSpace (fun a => values a i) input enc - (fun a => (input a).length)) : - ComputableInSpace values input (encPi enc) (fun a => (input a).length) := by - classical - choose T hT using fun i => (hvalues i).exists_inO - refine (of_inO (ComputableInO.pi hT)).weaken ?_ - exact ⟨Fintype.card Index + 1, fun a => by simp; nlinarith⟩ - variable [Finite Q] private theorem state_inSpace (e : Q ≃ Fin s) : ComputableInSpace (state e) encNat @@ -313,7 +338,7 @@ private theorem payload_inSpace (e : Q ≃ Fin s) : ComputableInSpace (budget_prod hw (budget_prod (budget_const 1 (.refl _)) (budget_const (s + 1).size (.refl _)))) have hstate := budget_comp_linear (state_inSpace e) hstateRead - have hheads := piLinear (Index := Option (Fin k)) fun i => + have hheads := pi_linear (Index := Option (Fin k)) fun i => budget_natMod (budget_comp_linear readNat_inSpace (budget_prod hw (budget_prod (budget_natAdd (budget_const (1 + (s + 1).size) (.refl _)) @@ -327,7 +352,7 @@ private theorem payload_inSpace (e : Q ≃ Fin s) : ComputableInSpace (fun q : Bool × Bool => if q.2 then .other else if q.1 then .accept else .empty) (budget_prod htrack0 htrack1) have hwindowSize := budget_natMul (budget_const (encB := encNat) 6 (.refl _)) hb - have hcells := piLinear (Index := Fin k) fun i => + have hcells := pi_linear (Index := Fin k) fun i => budget_comp_linear cells_inSpace (budget_comp_linear slice_inSpace (budget_prod hw (budget_prod (budget_natAdd (budget_natAdd (trackOffset_inSpace (k := k) (s := s)) diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeEncode.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeEncode.lean index 7cf5892310..53016144a7 100644 --- a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeEncode.lean +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeEncode.lean @@ -14,6 +14,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSp public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatSize import Complexity.Foundations.Data.BitString.List import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Pi +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Pi /-! # Native serialization of runtime-width block results @@ -221,14 +222,6 @@ theorem windowWord_inSpace : ComputableInSpace windowWord (encList encNat) (encL intro cells simp only [foldl_cells, List.nil_append] -private theorem finitePi {α I V : Type} [Fintype I] {enc : α ↪ List Bool} - {encV : V ↪ List Bool} {f : α → I → V} - (hf : ∀ i, ComputableInSpace (fun a => f a i) enc encV (fun a => (enc a).length)) : - ComputableInSpace f enc (encPi encV) (fun a => (enc a).length) := by - choose T hT using fun i => (hf i).exists_inO - exact (of_inO (ComputableInO.pi hT)).weaken - ⟨Fintype.card I + 1, fun a => by simp; nlinarith⟩ - private theorem valid_inSpace : ComputableInSpace (valid (k := k) (Q := Q)) encArgs encBool (fun a => (encArgs a).length) := by classical @@ -238,7 +231,7 @@ private theorem valid_inSpace : ComputableInSpace (valid (k := k) (Q := Q)) have hbits (i : Fin k) := budget_natEq (budget_length (budget_comp_linear (of_inO (ComputableInO.pi_apply (encList encNat) i)) hcells)) (budget_natMul (budget_const (encB := encNat) 3 (.refl _)) hb) - have htuple := finitePi hbits + have htuple := pi_linear hbits refine (budget_finite (encC := encBool) (fun checks : Fin k → Bool => (List.ofFn checks).all id) htuple).of_enc_eq fun _ => rfl diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplay.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplay.lean index b8ccd48298..1a45db6a65 100644 --- a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplay.lean +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplay.lean @@ -306,8 +306,8 @@ theorem runFrom_represents (M : MultiTapeTM k Bool Q) {s : Config k Q} exact hprev.tick M (fun h => (hinterior h).2) (fun h => (hinterior h).1) /-- Initialize the local replay from decoded block data. The input length is an explicit -argument, and every work cell is stored using the existing ternary symbol code. This initializer -has no native decoding certificate yet. -/ +argument, and every work cell is stored using the existing ternary symbol code. Native loading +and its semantic correspondence are provided by `QueryReplayLoad` and `QueryReplayLoadCorrect`. -/ @[expose] def loadView (inputLength width : ℕ) (J : Option (Fin k) → ℤ) (v : View k Q width) : Config k Q := { inputLength := inputLength diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAccess.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAccess.lean index 559577e366..41d0c8397a 100644 --- a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAccess.lean +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAccess.lean @@ -8,6 +8,7 @@ module -- upstream: candidate public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySpace +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Pi import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Pi /-! @@ -146,11 +147,7 @@ theorem rawResult_inSpace : ComputableInSpace (rawResult (k := k) (Q := Q)) encC | some i => have hhead := budget_comp_linear (of_inO (ComputableInO.pi_apply encNat i)) hheads exact budget_comp_linear workResidue_inSpace (budget_prod width_inSpace hhead) - choose T hT using fun i => (hres i).exists_inO - have hr := (of_inO (ComputableInO.pi hT)).weaken - (show (fun s => (encConfig s).length + - ∑ _i : Option (Fin k), (encConfig s).length) ≼ fun s => (encConfig s).length from - ⟨Fintype.card (Option (Fin k)) + 1, fun s => by simp; nlinarith⟩) + have hr := pi_linear hres exact (budget_prod hcontrol (budget_prod hr (budget_prod htrack hcells))).of_enc_eq fun _ => rfl diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayLabelLoad.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayLabelLoad.lean new file mode 100644 index 0000000000..af2c760a35 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayLabelLoad.lean @@ -0,0 +1,82 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayLoad +public import + Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeChecked +import Complexity.Computability.Machines.Turing.MultiTape.Internal.DegreeOptionPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Degree.Bounds + +/-! +# Native loading from a checked block label + +The loader validates an actual serialized label before constructing a replay state. FAIL and +noncanonical labels return `none`; accepted labels supply the explicit windows used by the +existing loader. Input length, block width and both parts of the signed input origin are binary +runtime arguments. The original input word is absent from these arguments. Computing its length +and obtaining an input-dependent label and origin remain separately charged caller operations. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Load.Label + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +variable {k s : ℕ} {Q : Type} + +/-- Original input length, block width, signed origin parts and an actual serialized label. -/ +abbrev Args := ℕ × ℕ × (ℕ × ℕ) × List Bool + +/-- Binary numeric metadata and the raw label word, without a copy of the original input. -/ +abbrev encArgs : Args ↪ List Bool := + encPair encNat (encPair encNat (encPair (encPair encNat encNat) encBits)) + +/-- Validate the serialized block label and initialize a replay only from accepted data. -/ +@[expose] noncomputable def load (e : Q ≃ Fin s) : Args → Option (Config k Q) := + fun (N, b, origins, word) => + (Encoding.NativeChecked.parse e (b, word)).map fun raw => + QueryReplay.Load.load (N, b, origins, raw) + +variable [Finite Q] + +/-- Parsing, validation and replay initialization all use native space linear in the actual +encoded arguments. The certificate is uniform in all numeric metadata and label contents. -/ +theorem load_inSpace (e : Q ≃ Fin s) : + ComputableInSpace (load (k := k) e) encArgs (encOption (encConfig (k := k) (Q := Q))) + (fun a => (encArgs a).length) := by + classical + let empty : RawResult k Q := (none, fun _ => 0, .empty, fun _ => []) + have hN := fst encNat (encPair encNat (encPair (encPair encNat encNat) encBits)) + have hrest := snd encNat (encPair encNat (encPair (encPair encNat encNat) encBits)) + have hb := hrest.fst' + have horigins := hrest.snd'.fst' + have hword := hrest.snd'.snd' + have hp := budget_comp_linear (Encoding.NativeChecked.parse_inSpace (k := k) e) + (budget_prod hb hword) + have hget : ComputableInSpace (fun o : Option (RawResult k Q) => o.getD empty) + (encOption (encRawResult (k := k) (Q := Q))) (encRawResult (k := k) (Q := Q)) + (fun o => (encOption (encRawResult (k := k) (Q := Q)) o).length) := + of_inO (ComputableInDegree.getD (enc₁ := (encRawResult (k := k) (Q := Q))) empty).inO_one + have hraw := budget_comp_linear hget hp + have hargs := budget_prod hN (budget_prod hb (budget_prod horigins hraw)) + have hload := budget_comp_linear (QueryReplay.Load.load_inSpace (k := k) (Q := Q)) hargs + have hsome := budget_comp_linear + (of_inO (ComputableInDegree.optionSome (enc₁ := (encConfig (k := k) (Q := Q))) + (enc := encOption (encConfig (k := k) (Q := Q)))).inO_one) hload + have hisSome := budget_comp_linear + (of_inO (ComputableInDegree.isSome (encRawResult (k := k) (Q := Q))).inO_one) hp + refine (budget_ite hisSome hsome + (budget_const (encB := encOption (encConfig (k := k) (Q := Q))) none (.refl _))).of_enc_eq ?_ + intro a + rcases a with ⟨N, b, origins, word⟩ + cases h : Encoding.NativeChecked.parse (k := k) e (b, word) <;> + simp [load, h, empty] + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Load.Label diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayLoad.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayLoad.lean new file mode 100644 index 0000000000..8feb78508c --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayLoad.lean @@ -0,0 +1,103 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAccess +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Pi +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Pi + +/-! +# Native loading of a decoded local block + +The loader receives runtime binary input length, block width and the positive and negative parts +of the input block origin. The supplied explicit windows are retained exactly. Work heads are +centered in the middle block, and the input head uses the native blank boundary convention. +No runtime-width list or table is allocated from the binary width. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Load + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +variable {k : ℕ} {Q : Type} + +/-- Input length, local width, signed input-block origin and decoded explicit local data. -/ +abbrev Args (k : ℕ) (Q : Type) := ℕ × ℕ × (ℕ × ℕ) × RawResult k Q + +/-- Native input-head coordinate. An out-of-range signed coordinate is the blank left boundary. +Zero width is supported by the arithmetic, although semantic block replay uses positive widths. -/ +@[expose] def inputPos (N b positive negative residue : ℕ) : ℕ := + let lo := b * negative + let hi := b * positive + residue % b + 1 + if lo ≤ hi ∧ hi - lo < N + 2 then hi - lo else 0 + +/-- Load exactly the supplied windows, with binary positions and a runtime duration counter. -/ +@[expose] def load : Args k Q → Config k Q := + fun (N, b, (positive, negative), (control, residues, track, cells)) => + { inputLength := N + width := b + remaining := b + inputPos := inputPos N b positive negative (residues none) + control := control + heads := fun h => b + residues (some h) % b + cells := cells + track := track } + +variable [Finite Q] + +/-- Every numeric argument is binary; the decoded windows remain explicit fixed tape families. -/ +@[expose] noncomputable def encArgs : Args k Q ↪ List Bool := + encPair encNat (encPair encNat (encPair (encPair encNat encNat) encRawResult)) + +/-- All initialization is charged to actual encoded arguments. The certificate is uniform in +input length, block width and both binary parts of the signed origin. -/ +theorem load_inSpace : ComputableInSpace (load (k := k) (Q := Q)) encArgs encConfig + (fun a => (encArgs a).length) := by + classical + have hN := fst encNat (encPair encNat (encPair (encPair encNat encNat) + (encRawResult (k := k) (Q := Q)))) + have hrest := snd encNat (encPair encNat (encPair (encPair encNat encNat) + (encRawResult (k := k) (Q := Q)))) + have hb := hrest.fst' + have horigin := hrest.snd'.fst' + have hp := horigin.fst' + have hn := horigin.snd' + have hraw := hrest.snd'.snd' + have hcontrol := hraw.fst' + have hresidues := hraw.snd'.fst' + have htrack := hraw.snd'.snd'.fst' + have hcells := hraw.snd'.snd'.snd' + have hinputResidue := budget_comp_linear + (of_inO (ComputableInO.pi_apply encNat (none : Option (Fin k)))) hresidues + have hlo := budget_natMul hb hn + have hhi := budget_natSucc (budget_natAdd (budget_natMul hb hp) + (budget_natMod hinputResidue hb)) + have hcoordinate := budget_natSub hhi hlo + have hnonnegative := budget_finite (encC := encBool) Bool.not (budget_natLt hhi hlo) + have hinRange := budget_natLt hcoordinate (budget_natAdd hN (budget_const 2 (.refl _))) + have hvalid := budget_finite (encC := encBool) (fun p : Bool × Bool => p.1 && p.2) + (budget_prod hnonnegative hinRange) + have hpos := budget_ite hvalid hcoordinate (budget_const (encB := encNat) 0 (.refl _)) + have hheads := pi_linear (Index := Fin k) fun h => + budget_natAdd hb (budget_natMod (budget_comp_linear + (of_inO (ComputableInO.pi_apply encNat (some h))) hresidues) hb) + refine (budget_prod hN (budget_prod hb (budget_prod hb (budget_prod hpos + (budget_prod hcontrol (budget_prod hheads (budget_prod hcells htrack))))))).of_enc_eq ?_ + intro a + rcases a with ⟨N, b, ⟨positive, negative⟩, ⟨control, residues, track, cells⟩⟩ + simp only [load, inputPos, encConfig, Config.equivProd, encOfEquiv_apply, Equiv.coe_fn_mk, + Bool.and_eq_true, Bool.not_eq_true', Bool.decide_iff] + by_cases hnonneg : b * negative ≤ b * positive + residues none % b + 1 + · have hnot : ¬b * positive + residues none % b + 1 < b * negative := by omega + simp [hnonneg, hnot] + · have hlt : b * positive + residues none % b + 1 < b * negative := by omega + simp [hnonneg, hlt] + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Load diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayLoadCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayLoadCorrect.lean new file mode 100644 index 0000000000..cf7bfd6a9e --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayLoadCorrect.lean @@ -0,0 +1,169 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayLabelLoad +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayBounds +import Complexity.Computability.Machines.Turing.MultiTape.Probabilistic.IntervalSimulation + +/-! +# Correctness of native local replay initialization + +Explicit canonical windows agree with the existing embedded configuration's three blocks. +The work origins belong only to the semantic correspondence; loading retains relative cells. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Load + +open MultiTapeNTM.ChoiceTable MultiTapeNTM.ConfigurationGraph + +variable {k s : ℕ} {Q : Type} + +/-- Canonical local buffers have exactly three blocks and use only the three cell codes. -/ +@[expose] def CanonicalWindows (b : ℕ) : RawResult k Q → Prop := + fun (_, _, _, cells) => (∀ h, (cells h).length = 3 * b) ∧ + ∀ h n, n ∈ cells h → n ≤ 2 + +private theorem symbolCode_cellOfCode (n : ℕ) (hn : n ≤ 2) : + symbolCode (cellOfCode n) = n := by + have h : n = 0 ∨ n = 1 ∨ n = 2 := by omega + rcases h with rfl | rfl | rfl <;> rfl + +/-- Interpreting a raw canonical window preserves its explicit row-major cells. -/ +private theorem windowCells_toView (b : ℕ) [NeZero b] (raw : RawResult k Q) + (h : Fin k) (hlen : (raw.2.2.2 h).length = 3 * b) : + windowCells b ((raw.toView b).2 h) = (raw.2.2.2 h).map cellOfCode := by + apply List.ext_getElem + · simp [windowCells, hlen] + · intro i hi hright + have hi' : i < 3 * b := by simpa only [windowCells, List.length_map, + List.length_range] using hi + have hd : i / b < 3 := (Nat.div_lt_iff_lt_mul (Nat.pos_of_neZero b)).mpr hi' + simp only [windowCells, List.getElem_map, List.getElem_range, hd, ↓reduceDIte, + RawResult.toView, ZMod.val_natCast] + have hid : i / b * b + i % b = i := by + simpa only [Nat.mul_comm] using Nat.div_add_mod i b + rw [hid] + have hc : i < (raw.2.2.2 h).length := by simpa only [List.length_map] using hright + rw [List.getD_eq_getElem _ _ hc] + +private theorem inputPos_eq (N b positive negative residue : ℕ) : + inputPos N b positive negative residue = + let p : ℤ := b * ((positive : ℤ) - negative) + residue % b + 1 + if 0 ≤ p ∧ p < N + 2 then p.toNat else 0 := by + let hi := b * positive + residue % b + 1 + let lo := b * negative + have hp : (b : ℤ) * ((positive : ℤ) - negative) + residue % b + 1 = + (hi : ℤ) - lo := by dsimp only [hi, lo]; push_cast; ring + rw [hp] + change (if lo ≤ hi ∧ hi - lo < N + 2 then hi - lo else 0) = _ + by_cases hle : lo ≤ hi + · have hcast : ((hi - lo : ℕ) : ℤ) = (hi : ℤ) - lo := Nat.cast_sub hle + have hto : ((hi : ℤ) - lo).toNat = hi - lo := by rw [← hcast]; simp + by_cases hb : hi - lo < N + 2 + · have hz : (hi : ℤ) - lo < N + 2 := by omega + simp [hle, hb, hz, hto] + · have hz : ¬ (hi : ℤ) - lo < N + 2 := by omega + simp [hle, hb, hz] + · simp [hle] + +/-- Native loading is exactly the semantic view loader. Only the input block origin is stored +at runtime; work origins merely locate the unchanged relative windows in the correctness proof. -/ +theorem load_eq_loadView (N b positive negative : ℕ) [NeZero b] + (raw : RawResult k Q) (hcanonical : CanonicalWindows b raw) + (J : Option (Fin k) → ℤ) (hinput : J none = (positive : ℤ) - negative) : + load (N, b, (positive, negative), raw) = loadView N b J (raw.toView b) := by + rcases raw with ⟨control, residues, track, cells⟩ + rcases hcanonical with ⟨hlen, hcodes⟩ + apply Config.ext + · rfl + · rfl + · rfl + · simp only [load, loadView, RawResult.toView, hinput, ZMod.val_natCast] + exact inputPos_eq N b positive negative (residues none) + · rfl + · funext h + simp only [load, loadView, RawResult.toView, ZMod.val_natCast] + · funext h + change cells h = List.ofFn (fun r : Fin (3 * b) => symbolCode (windowCell b J + (RawResult.toView b (control, residues, track, cells)).2 h + (b * (J (some h) - 1) + (r : ℤ)))) + apply List.ext_getElem + · simp [hlen] + · intro i hi hright + have hi' : i < 3 * b := by simpa only [List.length_ofFn] using hright + have hcell := windowCell_eq b J + (RawResult.toView b (control, residues, track, cells)).2 h (i : ℤ) + rw [ite_eq_left (Int.natCast_nonneg i), Int.toNat_natCast, + windowCells_toView b (control, residues, track, cells) h (hlen h)] at hcell + have hc : i < (cells h).length := hi + rw [List.getElem_ofFn] + rw [hcell] + change (cells h)[i] = symbolCode (((cells h).map cellOfCode).getD i none) + rw [List.getD_eq_getElem _ _ (by simpa only [List.length_map] using hc), + List.getElem_map] + exact (symbolCode_cellOfCode _ (hcodes h _ (List.getElem_mem hc))).symm + · rfl + +/-- The decoder constructs canonical explicit windows from every successful input label. -/ +theorem canonical_of_parse_some (e : Q ≃ Fin s) (b : ℕ) (word : List Bool) + (raw : RawResult k Q) (hp : Encoding.NativeDecode.parse e (b, word) = some raw) : + CanonicalWindows b raw := by + obtain ⟨_, hlen, _, rfl⟩ := (Encoding.NativeDecode.parse_eq_some_iff e b word raw).mp hp + constructor + · exact Encoding.NativeDecode.length_payload_cells_of_length e b word hlen + · intro h n hn + exact Encoding.NativeDecode.mem_cells_le _ n hn + +/-- Canonical-code validation also provides the loader's concrete window invariants. -/ +theorem canonical_of_checked_some (e : Q ≃ Fin s) (b : ℕ) (word : List Bool) + (raw : RawResult k Q) (hp : Encoding.NativeChecked.parse e (b, word) = some raw) : + CanonicalWindows b raw := by + apply canonical_of_parse_some e b word raw + dsimp only [Encoding.NativeChecked.parse] at hp + split at hp + · exact hp + · contradiction + +/-- Checked label loading agrees exactly with semantic loading after the tree gate's +canonical-code guard. FAIL and noncanonical labels never initialize a replay state. -/ +theorem load_label_ofFn (e : Q ≃ Fin s) (N b positive negative : ℕ) [NeZero b] + (bits : Cslib.BitString (Encoding.width k s b)) (J : Option (Fin k) → ℤ) + (hinput : J none = (positive : ℤ) - negative) : + Label.load e (N, b, (positive, negative), List.ofFn bits) = + (decodeChecked (Encoding.encode e) (Encoding.decode e) bits).map + (loadView N b J) := by + have hd := Encoding.NativeChecked.parse_ofFn e b bits + cases hp : Encoding.NativeChecked.parse (k := k) e (b, List.ofFn bits) with + | none => + rw [hp, Option.map_none] at hd + rw [← hd] + simp only [Label.load, hp, Option.map_none] + | some raw => + rw [hp, Option.map_some] at hd + rw [← hd] + simp only [Label.load, hp, Option.map_some] + exact congrArg some (load_eq_loadView N b positive negative raw + (canonical_of_checked_some e b (List.ofFn bits) raw hp) J hinput) + +variable [Finite Q] + +/-- Every physical clock of native-loaded canonical data has the proved local replay bound. -/ +theorem length_runFrom_load_le (M : MultiTapeTM k Bool Q) (w : List Bool) + (b positive negative : ℕ) [NeZero b] (raw : RawResult k Q) + (hcanonical : CanonicalWindows b raw) (J : Option (Fin k) → ℤ) + (hinput : J none = (positive : ℤ) - negative) (t : ℕ) : + (encConfig ((machine M).runFrom w (load (w.length, b, (positive, negative), raw)) t)).length ≤ + 1024 * (k + 1) * (b + Nat.log2 w.length + Nat.card (Option Q) + + Nat.card OutputTrack + 1) := by + rw [load_eq_loadView w.length b positive negative raw hcanonical J hinput] + exact length_runFrom_loadView_le M w b J (raw.toView b) t + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Load diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySpace.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySpace.lean index b2ee5ea37c..79715dfae0 100644 --- a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySpace.lean +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySpace.lean @@ -10,6 +10,7 @@ module public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplay public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpaceLib public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Pi +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Pi import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Pi import Complexity.Foundations.Data.List.Fold import Complexity.Computability.Machines.Turing.MultiTape.Encodings.NatLength @@ -158,16 +159,6 @@ private theorem finiteCases {α β Tag : Type} [Finite Tag] simpa only [List.foldr_ite_eq_of_mem, Finset.mem_toList, Finset.mem_univ] using hselect (Finset.univ.toList : List Tag) -private theorem piLinear {α Index Value : Type} [Fintype Index] - {input : α ↪ List Bool} {enc : Value ↪ List Bool} {values : α → Index → Value} - (hvalues : ∀ i, ComputableInSpace (fun a => values a i) input enc - (fun a => (input a).length)) : - ComputableInSpace values input (encPi enc) (fun a => (input a).length) := by - classical - choose T hT using fun i => (hvalues i).exists_inO - refine (of_inO (ComputableInO.pi hT)).weaken ?_ - exact ⟨Fintype.card Index + 1, fun a => by simp; nlinarith⟩ - private theorem piApplyLinear {Index Value : Type} [Fintype Index] (enc : Value ↪ List Bool) (i : Index) : ComputableInSpace (fun q : Index → Value => q i) (encPi enc) enc @@ -205,7 +196,7 @@ private theorem cellOfCode_inSpace : ComputableInSpace cellOfCode encNat /-- Reading the symbols uses finite tuples only for the fixed number of tapes. -/ theorem symbols_inSpace : ComputableInSpace (symbols (k := k) (Q := Q)) encConfig (encPi (encFinite (Option Bool))) (fun s => (encConfig s).length) := by - refine piLinear fun h => ?_ + refine pi_linear fun h => ?_ have hh := budget_comp_linear (piApplyLinear encNat h) (heads_inSpace (k := k) (Q := Q)) have hc := budget_comp_linear (piApplyLinear (encList encNat) h) (cells_inSpace (k := k) (Q := Q)) have hread := budget_comp_lin getD (linear_mul 8) (budget_prod hc hh) @@ -253,7 +244,7 @@ private theorem fixedActionTick_inSpace (a : Option (Action k Bool Q)) : have hh : ComputableInSpace (fun s : Config k Q => fun h => move (s.heads h) (a.workTapes h).2) encConfig (encPi encNat) (fun s => (encConfig s).length) := by - refine piLinear fun h => ?_ + refine pi_linear fun h => ?_ have hhead := budget_comp_linear (piApplyLinear encNat h) (heads_inSpace (k := k) (Q := Q)) cases (a.workTapes h).2 with @@ -265,7 +256,7 @@ private theorem fixedActionTick_inSpace (a : Option (Action k Bool Q)) : | none => s.cells h | some b => (s.cells h).set (s.heads h) (symbolCode b)) encConfig (encPi (encList encNat)) (fun s => (encConfig s).length) := by - refine piLinear fun h => ?_ + refine pi_linear fun h => ?_ have hcell := budget_comp_linear (piApplyLinear (encList encNat) h) (cells_inSpace (k := k) (Q := Q)) have hhead := budget_comp_linear (piApplyLinear encNat h) diff --git a/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/Pi.lean b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/Pi.lean new file mode 100644 index 0000000000..ce1b542e6b --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/Pi.lean @@ -0,0 +1,45 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Plain +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Pi +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Pi +import Mathlib.Tactic.Linarith + +/-! +# Fixed finite families in linear native space + +A fixed finite family whose entries are each computed in linear encoded-input space can be +assembled within the same asymptotic space bound. The native tuple constructor retains the +input and sums component bounds; the fixed number of components is absorbed into its constant. +Neither the entries nor their encodings need to be finite or uniformly bounded. + +## Main results + +* `Turing.MultiTapeTM.ComputableInSpace.pi_linear`: assemble a fixed finite family in linear + encoded-input space. +-/ + +public section + +namespace Turing.MultiTapeTM.ComputableInSpace + +/-- Assemble a fixed finite family from native linear-space component certificates. +The hidden constant may depend on the fixed index type, never on runtime values. -/ +theorem pi_linear {α Index Value : Type} [Fintype Index] + {input : α ↪ List Bool} {enc : Value ↪ List Bool} {values : α → Index → Value} + (hvalues : ∀ i, ComputableInSpace (fun a => values a i) input enc + (fun a => (input a).length)) : + ComputableInSpace values input (encPi enc) (fun a => (input a).length) := by + classical + choose T hT using fun i => (hvalues i).exists_inO + refine (of_inO (ComputableInO.pi hT)).weaken ?_ + exact ⟨Fintype.card Index + 1, fun a => by simp; nlinarith⟩ + +end Turing.MultiTapeTM.ComputableInSpace diff --git a/Complexity/Computability/Machines/Turing/MultiTape/Probabilistic/IntervalSimulation.lean b/Complexity/Computability/Machines/Turing/MultiTape/Probabilistic/IntervalSimulation.lean index cba94354ce..4549eebb94 100644 --- a/Complexity/Computability/Machines/Turing/MultiTape/Probabilistic/IntervalSimulation.lean +++ b/Complexity/Computability/Machines/Turing/MultiTape/Probabilistic/IntervalSimulation.lean @@ -246,7 +246,7 @@ with its head on cell `b + offset`, in the middle block. -/ /-- The window cells hold the window: the cell at offset `c` from the start of the window is entry `c / b` of the window at `c mod b`. -/ -private theorem windowCell_eq (J : Option (Fin k) → ℤ) (w : Fin k → Fin 3 → ZMod b → Option Bool) +theorem windowCell_eq (J : Option (Fin k) → ℤ) (w : Fin k → Fin 3 → ZMod b → Option Bool) (h : Fin k) (c : ℤ) : windowCell b J w h (b * (J (some h) - 1) + c) = if 0 ≤ c then (windowCells b (w h)).getD c.toNat none else none := by diff --git a/ComplexityTests/Machines/Turing/MultiTape/QueryReplayLoad.lean b/ComplexityTests/Machines/Turing/MultiTape/QueryReplayLoad.lean new file mode 100644 index 0000000000..419412283e --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/QueryReplayLoad.lean @@ -0,0 +1,61 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of native replay initialization) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayLoadCorrect +public import ComplexityTests.Machines.Turing.MultiTape.NativeBlockCodec + +/-! +# Native loading from local labels + +Signed origins exercise interior and blank input positions. Explicit cells are retained even on +malformed raw widths, while checked label loading rejects malformed and noncanonical labels. +-/ + +@[expose] public section + +namespace ComplexityTests.QueryReplayLoad + +open Turing MultiTapeTM MultiTapePTM.BlockComputation +open MultiTapePTM.BlockComputation.QueryReplay QueryReplay.Load + +def raw : RawResult 1 (Fin 1) := + (some 0, fun head => head.elim 5 (fun _ => 7), .empty, fun _ => [0, 1, 2, 0, 1, 2]) + +example : inputPos 3 2 0 0 5 = 2 := rfl +example : inputPos 3 2 0 1 5 = 0 := rfl +example : inputPos 3 2 100 0 5 = 0 := rfl +example : inputPos 3 2 0 100 5 = 0 := rfl +example : inputPos 3 2 1 0 5 = 4 := rfl +example : (load (3, 2, (0, 1), raw)).heads 0 = 3 := rfl +example : (load (3, 1000000000, (0, 1), raw)).cells 0 = [0, 1, 2, 0, 1, 2] := rfl + +example : CanonicalWindows 2 raw := by simp [CanonicalWindows, raw] + +example : load (3, 2, (0, 1), raw) = + loadView 3 2 (fun _ => -1) (raw.toView 2) := + load_eq_loadView 3 2 0 1 raw (by simp [CanonicalWindows, raw]) (fun _ => -1) rfl + +example : Label.load NativeBlockCodec.code (3, 1, (0, 0), NativeBlockCodec.good) = + some (load (3, 1, (0, 0), NativeBlockCodec.raw)) := by + have hp : Encoding.NativeChecked.parse (k := 1) NativeBlockCodec.code + (1, NativeBlockCodec.good) = some NativeBlockCodec.raw := by decide + simp only [Label.load, hp, Option.map_some] +example : Label.load (k := 1) NativeBlockCodec.code + (3, 1, (0, 0), NativeBlockCodec.fail) = none := by decide +example : Label.load (k := 1) NativeBlockCodec.code + (3, 1, (0, 0), NativeBlockCodec.noncanonicalBlank) = none := by decide +example : Label.load (k := 1) NativeBlockCodec.code + (3, 1000000000, (0, 0), NativeBlockCodec.good) = none := by decide + +/-- Actual checked input and supplied metadata pay for the entire initialized replay state. -/ +example : ComputableInSpace (Label.load (k := 1) NativeBlockCodec.code) + Label.encArgs (encOption encConfig) (fun a => (Label.encArgs a).length) := + Label.load_inSpace _ + +end ComplexityTests.QueryReplayLoad From 904a8a034265db1d674d70d05de7cd2de48fdf09 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 01:55:00 +0000 Subject: [PATCH 19/36] feat(turing): certify native local replay gate and all-head motion checks --- Complexity.lean | 4 + .../BlockComputation/QueryReplayAccess.lean | 15 +-- .../BlockComputation/QueryReplayGate.lean | 111 ++++++++++++++++++ .../QueryReplayGateCorrect.lean | 108 +++++++++++++++++ .../BlockComputation/QueryReplayMotion.lean | 106 +++++++++++++++++ .../QueryReplayMotionCorrect.lean | 89 ++++++++++++++ .../BlockComputation/QueryReplaySpace.lean | 19 +-- .../Turing/MultiTape/QueryReplayGate.lean | 83 +++++++++++++ 8 files changed, 515 insertions(+), 20 deletions(-) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayGate.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayGateCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayMotion.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayMotionCorrect.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/QueryReplayGate.lean diff --git a/Complexity.lean b/Complexity.lean index 323e4231f4..39edad17e8 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1699,9 +1699,13 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplay public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAccess public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayBounds +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayGate +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayGateCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayLabelLoad public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayLoad public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayLoadCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayMotion +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayMotionCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputationTree public import Complexity.Computability.Machines.Turing.MultiTape.BlockTrace diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAccess.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAccess.lean index 41d0c8397a..5c4e5f9e79 100644 --- a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAccess.lean +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAccess.lean @@ -101,13 +101,6 @@ variable [Finite Q] encPair (encFinite (Option Q)) (encPair (encPi encNat) (encPair (encFinite OutputTrack) (encPi (encList encNat)))) -private theorem configParts : ComputableInSpace (Config.equivProd (k := k) (Q := Q)) - encConfig (encPair encNat (encPair encNat (encPair encNat - (encPair encNat (encPair (encFinite (Option Q)) (encPair (encPi encNat) - (encPair (encPi (encList encNat)) (encFinite OutputTrack)))))))) - (fun s => (encConfig s).length) := - of_inO (ComputableInO.of_eq fun _ => rfl) - private theorem workResidue_inSpace : ComputableInSpace (fun p : ℕ × ℕ => workResidue p.1 p.2) (encPair encNat encNat) encNat (fun p => (encPair encNat encNat p).length) := by @@ -133,10 +126,10 @@ private theorem inputResidue_inSpace : ComputableInSpace theorem rawResult_inSpace : ComputableInSpace (rawResult (k := k) (Q := Q)) encConfig encRawResult (fun s => (encConfig s).length) := by classical - have hcontrol := (configParts (k := k) (Q := Q)).snd'.snd'.snd'.snd'.fst' - have hheads := (configParts (k := k) (Q := Q)).snd'.snd'.snd'.snd'.snd'.fst' - have hcells := (configParts (k := k) (Q := Q)).snd'.snd'.snd'.snd'.snd'.snd'.fst' - have htrack := (configParts (k := k) (Q := Q)).snd'.snd'.snd'.snd'.snd'.snd'.snd' + have hcontrol := (equivProd_inSpace (k := k) (Q := Q)).snd'.snd'.snd'.snd'.fst' + have hheads := (equivProd_inSpace (k := k) (Q := Q)).snd'.snd'.snd'.snd'.snd'.fst' + have hcells := (equivProd_inSpace (k := k) (Q := Q)).snd'.snd'.snd'.snd'.snd'.snd'.fst' + have htrack := (equivProd_inSpace (k := k) (Q := Q)).snd'.snd'.snd'.snd'.snd'.snd'.snd' have hres (h : Option (Fin k)) : ComputableInSpace (fun s : Config k Q => (rawResult s).2.1 h) encConfig encNat (fun s => (encConfig s).length) := by diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayGate.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayGate.lean new file mode 100644 index 0000000000..cda48cf18d --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayGate.lean @@ -0,0 +1,111 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayMotion +public import + Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeEncode + +/-! +# Native local replay gate with serialized output + +The machine retains the signed input-block origin and expected schedule directions beside the +explicit replay state. Replay reads the original input through its query channel and emits no +intermediate output. At completion the motion check chooses success or FAIL, and the actual +returned local windows pay for serialization in either case. + +The machine's fixed initial state contains only zeros and empty buffers. Actual local calls +start through the separately charged checked label loader and `QueryMachine.runFrom`. +No input-dependent initialization, label acquisition or original-input length computation is +provided by fixing this machine's template. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Gate + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +variable {k s : ℕ} {Q : Type} + +/-- Positive and negative input-origin parts and the expected direction of every head. -/ +abbrev Context (k : ℕ) := ℕ × ℕ × (Option (Fin k) → SignType) + +/-- The retained scheduling context and the actual runtime replay configuration. -/ +abbrev State (k : ℕ) (Q : Type) := Context k × Config k Q + +/-- Binary origins and a fixed finite family of direction tags. -/ +@[expose] noncomputable def encContext : Context k ↪ List Bool := + encPair encNat (encPair encNat (encPi (encFinite SignType))) + +/-- Silent replay with a checked motion tag and exact serialized local output. The fixed +initial state is a template; callers separately compute actual `runFrom` starting states. -/ +@[expose] noncomputable def machine (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + QueryMachine (State k Q) where + init := ((0, 0, fun _ => .zero), (QueryReplay.machine M).init) + query := fun (_, q) => (QueryReplay.machine M).query q + next := fun ((context, q), symbol) => (context, (QueryReplay.machine M).next (q, symbol)) + emit _ := [] + halted := fun (_, q) => (QueryReplay.machine M).halted q + finish := fun ((positive, negative, expected), q) => + Encoding.NativeEncode.serialize e + (q.width, Motion.valid (positive, negative, expected, q), rawResult q) + +variable [Finite Q] + +/-- Explicit binary replay storage, with the retained scheduling fields charged once. -/ +@[expose] noncomputable def encState : State k Q ↪ List Bool := + encPair encContext encConfig + +/-- The original-input query is delegated to the active replay configuration. -/ +theorem machine_query_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).query encState encNat (fun a => (encState a).length) := by + have hq := snd (encContext (k := k)) (encConfig (k := k) (Q := Q)) + exact (budget_comp_linear (QueryReplay.machine_query_inSpace M) hq).of_enc_eq fun _ => rfl + +/-- One replay step retains its context and charges the full current runtime storage. -/ +theorem machine_next_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).next (encPair encState (encOption encBool)) encState + (fun a => (encPair encState (encOption encBool) a).length) := by + have hs := fst (encState (k := k) (Q := Q)) (encOption encBool) + have hcontext := hs.fst' + have hq := hs.snd' + have hsymbol := snd (encState (k := k) (Q := Q)) (encOption encBool) + have hstep := budget_comp_linear (QueryReplay.machine_next_inSpace M) + (budget_prod hq hsymbol) + exact (budget_prod hcontext hstep).of_enc_eq fun _ => rfl + +/-- The gate remains silent until its final serialized result. -/ +theorem machine_emit_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).emit (encPair encState (encOption encBool)) encBits + (fun a => (encPair encState (encOption encBool) a).length) := + budget_const [] (.refl _) + +/-- Stopping depends on the actual replay duration counter. -/ +theorem machine_halted_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).halted encState encBool (fun a => (encState a).length) := by + have hq := snd (encContext (k := k)) (encConfig (k := k) (Q := Q)) + exact (budget_comp_linear (QueryReplay.machine_halted_inSpace M) hq).of_enc_eq fun _ => rfl + +/-- Final output charges motion verification, result extraction and both serialization tags. +The retained windows pay for FAIL as well as success; no callee output stream is stored. -/ +theorem machine_finish_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).finish encState encBits (fun a => (encState a).length) := by + have hcontext := fst (encContext (k := k)) (encConfig (k := k) (Q := Q)) + have hq := snd (encContext (k := k)) (encConfig (k := k) (Q := Q)) + have hp := hcontext.fst' + have hn := hcontext.snd'.fst' + have hexpected := hcontext.snd'.snd' + have hmotion := budget_comp_linear (Motion.valid_inSpace (k := k) (Q := Q)) + (budget_prod hp (budget_prod hn (budget_prod hexpected hq))) + have hraw := budget_comp_linear (rawResult_inSpace (k := k) (Q := Q)) hq + have hwidth := budget_comp_linear (width_inSpace (k := k) (Q := Q)) hq + exact (budget_comp_linear (Encoding.NativeEncode.serialize_bits_inSpace (k := k) e) + (budget_prod hwidth (budget_prod hmotion hraw))).of_enc_eq fun _ => rfl + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Gate diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayGateCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayGateCorrect.lean new file mode 100644 index 0000000000..51545edb0a --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayGateCorrect.lean @@ -0,0 +1,108 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayGate +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayMotionCorrect + +/-! +# Correctness of the native local gate subroutine + +The subroutine queries the original input and emits the existing Boolean label of the native +leaving view precisely when all movement labels hold. The fixed template supplies no free +input-dependent initial data: initialization and run time are explicit in the statements. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Gate + +open MultiTapeTM MultiTapeNTM.ChoiceTable + +variable {k s : ℕ} {Q : Type} + +/-- Retaining scheduling context preserves each physical replay transition exactly. -/ +theorem runFrom_eq (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (context : Context k) (q : Config k Q) (t : ℕ) : + (machine M e).runFrom w (context, q) t = + (context, (QueryReplay.machine M).runFrom w q t) := by + induction t with + | zero => rfl + | succ t ih => + rw [QueryMachine.runFrom_succ, ih, QueryMachine.runFrom_succ] + simp only [QueryMachine.step, machine, QueryMachine.read] + by_cases h : (QueryReplay.machine M).halted ((QueryReplay.machine M).runFrom w q t) = true + · simp [h] + · simp [h] + +/-- Finishing a loaded replay emits exactly the encoded leaving view on successful all-head +motion validation, and the exact FAIL code otherwise. Retained context and windows are explicit. -/ +theorem finish_runFrom_load (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b positive negative : ℕ) [NeZero b] (expected : Option (Fin k) → SignType) + (raw : RawResult k Q) (hcanonical : Load.CanonicalWindows b raw) + (J : Option (Fin k) → ℤ) (hinput : J none = (positive : ℤ) - negative) : + let view := raw.toView b + let c := M.runFrom (embedCfg (input := w) b J view.1 view.2) b + (machine M e).finish ((machine M e).runFrom w + ((positive, negative, expected), Load.load (w.length, b, (positive, negative), raw)) b) = + List.ofFn (Encoding.encode e + (if ∀ head, c.blockIndex b head = J head + (expected head : ℤ) + then some (c.blockControl b, c.windowContents b J) else none)) := by + dsimp only + rw [runFrom_eq, Load.load_eq_loadView w.length b positive negative raw hcanonical J hinput] + let view := raw.toView b + let q := (QueryReplay.machine M).runFrom w (loadView w.length b J view) b + let c := M.runFrom (embedCfg (input := w) b J view.1 view.2) b + have hwidth : q.width = b := width_runFrom M w _ b + have hrep := runFrom_loadView_represents (w := w) M b J view le_rfl + have hmotion := Motion.valid_iff positive negative expected q c J hwidth hinput hrep + have hview : (rawResult q).toView b = (c.blockControl b, c.windowContents b J) := + (toView_rawResult q b hwidth).trans (result_runFrom_loadView (w := w) M b J view) + have hvalid : Encoding.NativeEncode.valid (b, true, rawResult q) = true := by + apply List.all_eq_true.mpr + intro flag hflag + obtain ⟨h, rfl⟩ := List.mem_ofFn.mp hflag + apply decide_eq_true + change (((QueryReplay.machine M).runFrom w (loadView w.length b J view) b).cells h).length = _ + rw [length_cells_runFrom] + simp [loadView] + change Encoding.NativeEncode.serialize e + (q.width, Motion.valid (positive, negative, expected, q), rawResult q) = + List.ofFn (Encoding.encode e (if ∀ head, + c.blockIndex b head = J head + (expected head : ℤ) + then some (c.blockControl b, c.windowContents b J) else none)) + rw [hwidth] + by_cases hm : ∀ head, c.blockIndex b head = J head + (expected head : ℤ) + · have hc : Motion.valid (positive, negative, expected, q) = true := hmotion.mpr hm + rw [hc, Encoding.NativeEncode.serialize_some e (rawResult q) hvalid, hview] + simp [hm] + · have hc : Motion.valid (positive, negative, expected, q) = false := + Bool.eq_false_iff.mpr (fun h => hm (hmotion.mp h)) + rw [hc, Encoding.NativeEncode.serialize_fail e (rawResult q) hvalid] + simp [hm] + +variable [Finite Q] + +/-- Every physical subroutine clock charges retained context plus the actual replay state. -/ +theorem length_runFrom_load_le (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b positive negative : ℕ) [NeZero b] (expected : Option (Fin k) → SignType) + (raw : RawResult k Q) (hcanonical : Load.CanonicalWindows b raw) + (J : Option (Fin k) → ℤ) (hinput : J none = (positive : ℤ) - negative) (t : ℕ) : + (encState ((machine M e).runFrom w + ((positive, negative, expected), + Load.load (w.length, b, (positive, negative), raw)) t)).length ≤ + 2 * (encContext (positive, negative, expected)).length + 1 + + 1024 * (k + 1) * (b + Nat.log2 w.length + Nat.card (Option Q) + + Nat.card OutputTrack + 1) := by + rw [runFrom_eq] + have h := Load.length_runFrom_load_le M w b positive negative raw hcanonical J hinput t + rw [encState, length_encPair] + dsimp only [Prod.fst, Prod.snd] + omega + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Gate diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayMotion.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayMotion.lean new file mode 100644 index 0000000000..8e7c123f0b --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayMotion.lean @@ -0,0 +1,106 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySpace +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Pi +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Pi + +/-! +# Native verification of local block motion + +Work-head positions use the three explicitly stored blocks. Input-head positions use the native +padded coordinates, including physical position zero for the blank left boundary. The original +input-block origin is supplied as positive and negative binary parts. The verifier compares the +expected schedule directions using binary division and addition; it rejects zero block width. +Schedule directions form a fixed finite family; origins and head coordinates remain binary. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Motion + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +variable {k : ℕ} {Q : Type} + +/-- The three neighboring block indices, relative to the leftmost stored block. -/ +@[expose] def code : SignType → ℕ + | .neg => 0 + | .zero => 1 + | .pos => 2 + +/-- Compare the input head against its expected displacement from a signed origin. +Physical position zero has native head coordinate `-1`, whose positive-width block is `-1`. -/ +@[expose] def inputMatches (b positive negative position : ℕ) (label : SignType) : Bool := + if position = 0 then decide (positive + code label = negative) + else decide ((position - 1) / b + negative + 1 = positive + code label) + +/-- A work head lies in the expected one of the three stored blocks. -/ +@[expose] def workMatches (b relative : ℕ) (label : SignType) : Bool := + decide (relative / b = code label) + +/-- Signed input origin, fixed tape-direction family and the returned explicit replay state. -/ +abbrev Args (k : ℕ) (Q : Type) := ℕ × ℕ × (Option (Fin k) → SignType) × Config k Q + +/-- Check the input direction and every work-tape direction at a positive runtime width. -/ +@[expose] def valid : Args k Q → Bool := + fun (positive, negative, expected, q) => + (decide (0 < q.width) && inputMatches q.width positive negative q.inputPos (expected none)) && + decide (∀ h : Fin k, workMatches q.width (q.heads h) (expected (some h)) = true) + +variable [Finite Q] + +/-- Binary origin registers, a fixed finite direction family and all actual replay storage. -/ +@[expose] noncomputable def encArgs : Args k Q ↪ List Bool := + encPair encNat (encPair encNat + (encPair (encPi (Index := Option (Fin k)) (encFinite SignType)) encConfig)) + +/-- Uniform native motion verification. Constants depend only on the fixed tape/control types; +widths, origins, positions and windows are runtime data charged by their actual encodings. -/ +theorem valid_inSpace : ComputableInSpace (valid (k := k) (Q := Q)) encArgs encBool + (fun a => (encArgs a).length) := by + classical + have hp := fst encNat (encPair encNat + (encPair (encPi (Index := Option (Fin k)) (encFinite SignType)) (encConfig (k := k) (Q := Q)))) + have hrest := snd encNat (encPair encNat + (encPair (encPi (Index := Option (Fin k)) (encFinite SignType)) (encConfig (k := k) (Q := Q)))) + have hn := hrest.fst' + have hexpected := hrest.snd'.fst' + have hstate := hrest.snd'.snd' + have hb := budget_comp_linear width_inSpace hstate + have hposition := budget_comp_linear inputPos_inSpace hstate + have hheads := budget_comp_linear + (equivProd_inSpace (k := k) (Q := Q)).snd'.snd'.snd'.snd'.snd'.fst' hstate + have hinputTag := budget_comp_linear + (of_inO (ComputableInO.pi_apply (encFinite SignType) (none : Option (Fin k)))) hexpected + have hinputCode := budget_finite (encC := encNat) code hinputTag + have hright := budget_natAdd hp hinputCode + have hleft := budget_natSucc (budget_natAdd + (budget_natDiv (budget_natPred hposition) hb) hn) + have hinput : ComputableInSpace + (fun a : Args k Q => inputMatches a.2.2.2.width a.1 a.2.1 a.2.2.2.inputPos + (a.2.2.1 none)) encArgs encBool (fun a => (encArgs a).length) := by + refine (budget_ite (budget_natIsZero hposition) (budget_natEq hright hn) + (budget_natEq hleft hright)).of_enc_eq ?_ + intro a + simp only [inputMatches, Bool.decide_iff] + have hwork := pi_linear (Index := Fin k) fun h => + budget_natEq (budget_natDiv + (budget_comp_linear (of_inO (ComputableInO.pi_apply encNat h)) hheads) hb) + (budget_finite (encC := encNat) code (budget_comp_linear + (of_inO (ComputableInO.pi_apply (encFinite SignType) (some h))) hexpected)) + have hall := budget_finite (encC := encBool) + (fun f : Fin k → Bool => decide (∀ h, f h = true)) hwork + have hguard := budget_natLt (budget_const (encB := encNat) 0 (.refl _)) hb + have hinputGuard := budget_finite (encC := encBool) (fun p : Bool × Bool => p.1 && p.2) + (budget_prod hguard hinput) + exact (budget_finite (encC := encBool) (fun p : Bool × Bool => p.1 && p.2) + (budget_prod hinputGuard hall)).of_enc_eq fun _ => rfl + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Motion diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayMotionCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayMotionCorrect.lean new file mode 100644 index 0000000000..182308d167 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayMotionCorrect.lean @@ -0,0 +1,89 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayMotion +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayLoadCorrect + +/-! +# All-head correctness of native movement verification + +The finite displacement check is exactly the native block-index check, including the input +head's negative coordinate at the left blank boundary. Work origins are proof-only translations +of the stored relative positions. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Motion + +variable {k : ℕ} {Q : Type} {w : List Bool} + +private theorem code_cast (label : SignType) : (code label : ℤ) = (label : ℤ) + 1 := by + cases label <;> rfl + +/-- The native input-direction test checks its signed block coordinate, including position zero. -/ +theorem inputMatches_iff (b positive negative position : ℕ) [NeZero b] (label : SignType) : + inputMatches b positive negative position label = true ↔ + ((position : ℤ) - 1) / b = (positive : ℤ) - negative + (label : ℤ) := by + have hc := code_cast label + have hb : (0 : ℤ) < b := by exact_mod_cast Nat.pos_of_neZero b + by_cases hp : position = 0 + · subst position + have hd : (-1 : ℤ) / b = -1 := by simp [Int.neg_one_ediv, Int.sign_eq_one_of_pos hb] + simp only [inputMatches, ↓reduceIte, decide_eq_true_eq, Int.natCast_zero, zero_sub, hd] + omega + · have hs : ((position - 1 : ℕ) : ℤ) = (position : ℤ) - 1 := by omega + simp only [inputMatches, hp, ↓reduceIte, decide_eq_true_eq] + rw [← hs, ← Int.natCast_ediv] + omega + +/-- A relative work-head position checks the same block as its translated native coordinate. -/ +theorem workMatches_iff (b relative : ℕ) [NeZero b] (origin : ℤ) (label : SignType) : + workMatches b relative label = true ↔ + ((b : ℤ) * (origin - 1) + relative) / b = origin + (label : ℤ) := by + have hc := code_cast label + have hb : (b : ℤ) ≠ 0 := by exact_mod_cast NeZero.ne b + have hd : ((b : ℤ) * (origin - 1) + relative) / b = + origin - 1 + (relative : ℤ) / b := by + rw [add_comm, Int.add_mul_ediv_left _ _ hb, add_comm] + rw [hd, ← Int.natCast_ediv] + simp only [workMatches, decide_eq_true_eq] + omega + +/-- Native movement verification validates the input head and every work head against the +same expected schedule row as the functional block gate. -/ +theorem valid_iff {b : ℕ} [NeZero b] (positive negative : ℕ) + (expected : Option (Fin k) → SignType) (q : Config k Q) (c : Cfg k Bool Q w) + (J : Option (Fin k) → ℤ) (hwidth : q.width = b) + (hinput : J none = (positive : ℤ) - negative) + (hrep : Represents q c (fun h => b * (J (some h) - 1))) : + valid (positive, negative, expected, q) = true ↔ + ∀ head, c.blockIndex b head = J head + (expected head : ℤ) := by + have hb : 0 < b := Nat.pos_of_neZero b + simp only [valid, hwidth, hb, decide_true, Bool.true_and, Bool.and_eq_true, + decide_eq_true_eq] + constructor + · rintro ⟨hi, hw⟩ head + cases head with + | none => + have h := (inputMatches_iff b positive negative q.inputPos (expected none)).mp hi + simpa only [Cfg.blockIndex, Cfg.headPos_none, hrep.inputPos, hinput] using h + | some head => + have h := (workMatches_iff b (q.heads head) (J (some head)) + (expected (some head))).mp (hw head) + simpa only [Cfg.blockIndex, Cfg.headPos_some, hrep.heads head] using h + · intro h + constructor + · apply (inputMatches_iff b positive negative q.inputPos (expected none)).mpr + simpa only [Cfg.blockIndex, Cfg.headPos_none, hrep.inputPos, hinput] using h none + · intro head + apply (workMatches_iff b (q.heads head) (J (some head)) (expected (some head))).mpr + simpa only [Cfg.blockIndex, Cfg.headPos_some, hrep.heads head] using h (some head) + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Motion diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySpace.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySpace.lean index 79715dfae0..289fa88f7f 100644 --- a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySpace.lean +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySpace.lean @@ -53,7 +53,8 @@ variable [Finite Q] (encPair encNat (encPair (encFinite (Option Q)) (encPair (encPi encNat) (encPair (encPi (encList encNat)) (encFinite OutputTrack)))))))) -private theorem config_parts : ComputableInSpace (Config.equivProd (k := k) (Q := Q)) +/-- Read all named replay fields through their defining product encoding in native space. -/ +theorem equivProd_inSpace : ComputableInSpace (Config.equivProd (k := k) (Q := Q)) encConfig (encPair encNat (encPair encNat (encPair encNat (encPair encNat (encPair (encFinite (Option Q)) (encPair (encPi encNat) (encPair (encPi (encList encNat)) (encFinite OutputTrack)))))))) @@ -62,19 +63,19 @@ private theorem config_parts : ComputableInSpace (Config.equivProd (k := k) (Q : /-- The runtime input length can be read in linear encoded-state space. -/ theorem inputLength_inSpace : ComputableInSpace (Config.inputLength (k := k) (Q := Q)) - encConfig encNat (fun s => (encConfig s).length) := config_parts.fst' + encConfig encNat (fun s => (encConfig s).length) := equivProd_inSpace.fst' /-- The runtime block width can be read in linear encoded-state space. -/ theorem width_inSpace : ComputableInSpace (Config.width (k := k) (Q := Q)) - encConfig encNat (fun s => (encConfig s).length) := config_parts.snd'.fst' + encConfig encNat (fun s => (encConfig s).length) := equivProd_inSpace.snd'.fst' /-- The remaining replay duration can be read in linear encoded-state space. -/ theorem remaining_inSpace : ComputableInSpace (Config.remaining (k := k) (Q := Q)) - encConfig encNat (fun s => (encConfig s).length) := config_parts.snd'.snd'.fst' + encConfig encNat (fun s => (encConfig s).length) := equivProd_inSpace.snd'.snd'.fst' /-- The original input pointer can be read in linear encoded-state space. -/ theorem inputPos_inSpace : ComputableInSpace (Config.inputPos (k := k) (Q := Q)) - encConfig encNat (fun s => (encConfig s).length) := config_parts.snd'.snd'.snd'.fst' + encConfig encNat (fun s => (encConfig s).length) := equivProd_inSpace.snd'.snd'.snd'.fst' /-- Native original-input query component. -/ theorem machine_query_inSpace (M : MultiTapeTM k Bool Q) : @@ -167,19 +168,19 @@ private theorem piApplyLinear {Index Value : Type} [Fintype Index] private theorem control_inSpace : ComputableInSpace (Config.control (k := k) (Q := Q)) encConfig (encFinite (Option Q)) (fun s => (encConfig s).length) := - config_parts.snd'.snd'.snd'.snd'.fst' + equivProd_inSpace.snd'.snd'.snd'.snd'.fst' private theorem heads_inSpace : ComputableInSpace (Config.heads (k := k) (Q := Q)) encConfig (encPi encNat) (fun s => (encConfig s).length) := - config_parts.snd'.snd'.snd'.snd'.snd'.fst' + equivProd_inSpace.snd'.snd'.snd'.snd'.snd'.fst' private theorem cells_inSpace : ComputableInSpace (Config.cells (k := k) (Q := Q)) encConfig (encPi (encList encNat)) (fun s => (encConfig s).length) := - config_parts.snd'.snd'.snd'.snd'.snd'.snd'.fst' + equivProd_inSpace.snd'.snd'.snd'.snd'.snd'.snd'.fst' private theorem track_inSpace : ComputableInSpace (Config.track (k := k) (Q := Q)) encConfig (encFinite OutputTrack) (fun s => (encConfig s).length) := - config_parts.snd'.snd'.snd'.snd'.snd'.snd'.snd' + equivProd_inSpace.snd'.snd'.snd'.snd'.snd'.snd'.snd' private theorem cellOfCode_inSpace : ComputableInSpace cellOfCode encNat (encFinite (Option Bool)) (fun n => (encNat n).length) := by diff --git a/ComplexityTests/Machines/Turing/MultiTape/QueryReplayGate.lean b/ComplexityTests/Machines/Turing/MultiTape/QueryReplayGate.lean new file mode 100644 index 0000000000..384f083c18 --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/QueryReplayGate.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of the native local replay gate) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayGateCorrect +public import ComplexityTests.Machines.Turing.MultiTape.NativeBlockCodec + +/-! +# Native local-gate regressions + +The input head reaches the right blank boundary while a work head moves to the left block. +Correct labels return the updated window; changing either head's expected label returns FAIL. +The left blank boundary and the positive-width guard are tested separately. +-/ + +@[expose] public section + +namespace ComplexityTests.QueryReplayGate + +open Turing MultiTapeTM MultiTapePTM.BlockComputation +open MultiTapePTM.BlockComputation.QueryReplay + +example : Motion.inputMatches 2 0 0 0 .neg = true := rfl +example : Motion.inputMatches 2 0 0 0 .zero = false := rfl +example : Motion.inputMatches 2 0 1 0 .zero = true := rfl +example : Motion.inputMatches 2 0 1 1 .pos = true := rfl +example : Motion.inputMatches 2 0 0 3 .pos = true := rfl +example : Motion.workMatches 2 0 .neg = true := rfl +example : Motion.workMatches 2 2 .zero = true := rfl +example : Motion.workMatches 2 4 .pos = true := rfl + +def row (input work : SignType) : Option (Fin 1) → SignType := + fun h => h.elim input (fun _ => work) + +def raw : RawResult 1 (Fin 1) := (some 0, fun _ => 0, .empty, fun _ => [0, 1, 2]) + +def reader : MultiTapeTM 1 Bool (Fin 1) := MultiTapeTM.ofTr 0 fun _ input _ => + { inputTape := .pos, workTapes := fun _ => (some input, .neg), output := some true, + state := none } + +noncomputable def gate := Gate.machine reader NativeBlockCodec.code + +def start (expected : Option (Fin 1) → SignType) : Gate.State 1 (Fin 1) := + ((0, 0, expected), Load.load (1, 1, (0, 0), raw)) + +def done (expected : Option (Fin 1) → SignType) : Gate.State 1 (Fin 1) := + ((0, 0, expected), ⟨1, 1, 0, 2, none, fun _ => 0, fun _ => [0, 2, 2], .accept⟩) + +private theorem run_one (expected : Option (Fin 1) → SignType) : + gate.runFrom [true] (start expected) 1 = done expected := by + unfold gate start + rw [Gate.runFrom_eq] + simp only [QueryMachine.runFrom_succ, QueryMachine.runFrom_zero, QueryMachine.step, + QueryReplay.machine, QueryMachine.read, tick, reader, MultiTapeTM.tr_ofTr] + rfl + +example : Motion.valid (0, 0, row .pos .neg, (done (row .pos .neg)).2) = true := rfl +example : Motion.valid (0, 0, row .zero .neg, (done (row .zero .neg)).2) = false := rfl +example : Motion.valid (0, 0, row .pos .zero, (done (row .pos .zero)).2) = false := rfl +example : Motion.valid (0, 0, row .zero .zero, + { (done (row .zero .zero)).2 with width := 0 }) = false := rfl + +example : Encoding.NativeDecode.parse (k := 1) NativeBlockCodec.code + (1, gate.finish (gate.runFrom [true] (start (row .pos .neg)) 1)) = + some (none, fun _ => 0, .accept, fun _ => [0, 2, 2]) := by + rw [run_one] + decide + +example : gate.finish (gate.runFrom [true] (start (row .zero .neg)) 1) = + List.replicate (Encoding.width 1 1 1) false := by rw [run_one]; decide +example : gate.finish (gate.runFrom [true] (start (row .pos .zero)) 1) = + List.replicate (Encoding.width 1 1 1) false := by rw [run_one]; decide + +/-- The actual final word, including FAIL, has a native certificate using retained storage. -/ +example : ComputableInSpace gate.finish Gate.encState encBits + (fun state => (Gate.encState state).length) := Gate.machine_finish_inSpace _ _ + +end ComplexityTests.QueryReplayGate From 764844514549e9d534891af8e9375479e8a5aafa Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 01:58:24 +0000 Subject: [PATCH 20/36] feat(turing): expose exact local call stopping time --- .../QueryReplayGateCorrect.lean | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayGateCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayGateCorrect.lean index 51545edb0a..31435c444a 100644 --- a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayGateCorrect.lean +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayGateCorrect.lean @@ -40,6 +40,32 @@ theorem runFrom_eq (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) · simp [h] · simp [h] +/-- A local call stops at its requested runtime width, even if native control halted earlier. -/ +theorem halted_runFrom_load (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (N b positive negative : ℕ) (expected : Option (Fin k) → SignType) (raw : RawResult k Q) : + (machine M e).halted ((machine M e).runFrom w + ((positive, negative, expected), Load.load (N, b, (positive, negative), raw)) b) = true := by + rw [runFrom_eq] + change decide (((QueryReplay.machine M).runFrom w + (Load.load (N, b, (positive, negative), raw)) b).remaining = 0) = true + rw [remaining_runFrom] + change decide (b - b = 0) = true + simp + +/-- Before the requested duration, the local call remains live. Together with the stopping +lemma this supplies the exact first-halt contract for state-returning query calls. -/ +theorem live_runFrom_load (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (N b positive negative : ℕ) (expected : Option (Fin k) → SignType) (raw : RawResult k Q) + {t : ℕ} (ht : t < b) : + (machine M e).halted ((machine M e).runFrom w + ((positive, negative, expected), Load.load (N, b, (positive, negative), raw)) t) = false := by + rw [runFrom_eq] + change decide (((QueryReplay.machine M).runFrom w + (Load.load (N, b, (positive, negative), raw)) t).remaining = 0) = false + rw [remaining_runFrom] + change decide (b - t = 0) = false + exact decide_eq_false_iff_not.mpr (by omega) + /-- Finishing a loaded replay emits exactly the encoded leaving view on successful all-head motion validation, and the exact FAIL code otherwise. Retained context and windows are explicit. -/ theorem finish_runFrom_load (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) From 35eb2780522e366db54dae090914e82f60b06cd0 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 02:22:31 +0000 Subject: [PATCH 21/36] feat(hpv): assemble checked provider windows natively --- Complexity.lean | 4 + .../Encoding/NativeDecode.lean | 33 +--- .../QueryReplayProviderLabels.lean | 116 +++++++++++++ .../QueryReplayProviders.lean | 161 ++++++++++++++++++ .../QueryReplayProvidersCorrect.lean | 157 +++++++++++++++++ .../Complexity/InSpace/ListSlice.lean | 104 +++++++++++ .../Ary/Packed/RawControllerNative.lean | 74 +------- .../MultiTape/QueryReplayProviders.lean | 57 +++++++ 8 files changed, 604 insertions(+), 102 deletions(-) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayProviderLabels.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayProviders.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayProvidersCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/ListSlice.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/QueryReplayProviders.lean diff --git a/Complexity.lean b/Complexity.lean index 39edad17e8..6b5e0ebcf7 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1706,6 +1706,9 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayLoadCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayMotion public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayMotionCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProviderLabels +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProviders +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProvidersCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputationTree public import Complexity.Computability.Machines.Turing.MultiTape.BlockTrace @@ -1751,6 +1754,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSp public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Bit public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.BitList public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.ListSlice public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatPow public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatPrime public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatSize diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean index 92a15e41d2..fca30b6c34 100644 --- a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean @@ -14,6 +14,7 @@ import Complexity.Computability.Machines.Turing.MultiTape.Internal.BinaryNormali import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Constructible import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Pi import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Pi +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.ListSlice import Mathlib.Data.List.Induction /-! @@ -198,36 +199,6 @@ private theorem scan_fold_cells (word : List Bool) (acc : List ℕ) : rw [ih] simp only [List.reverse_cons, List.append_assoc, List.singleton_append] -private theorem listReverse_inSpace : ComputableInSpace List.reverse (encList encNat) - (encList encNat) (fun l => (encList encNat l).length) := by - have hl : ComputableInSpace (fun l : List ℕ => l) (encList encNat) (encList encNat) - (fun l => (encList encNat l).length) := of_inO ComputableInDegree.id'.inO_one - have hinit := budget_const (encB := encList encNat) [] - (show (fun l : List ℕ => (encList encNat l).length) ≼ - fun l => (encList encNat l).length from .refl _) - have hstep : ComputableInSpace (fun p : List ℕ × ℕ => p.2 :: p.1) - (encPair (encList encNat) encNat) (encList encNat) - (fun p => (encPair (encList encNat) encNat p).length) := - budget_cons (snd _ _) (fst _ _) - have hstate (l : List ℕ) (i : ℕ) (_hi : i ≤ l.length) : - (encList encNat ((l.take i).foldl (fun acc n => n :: acc) [])).length ≤ - (encList encNat l).length := by - rw [List.foldl_flip_cons_eq_append', List.append_nil] - simp only [length_encList, List.map_reverse, List.sum_reverse] - simpa only [length_encList] using length_encList_take_le encNat l i - have hspace (l : List ℕ) (i : ℕ) (hi : i < l.length) : - (encPair (encList encNat) encNat - ((l.take i).foldl (fun acc n => n :: acc) [], l[i])).length ≤ - 4 * ((encList encNat l).length + 1) := by - have ha := length_le_encList_of_mem encNat (List.getElem_mem hi) - have hs := hstate l i hi.le - simp only [length_encPair] - omega - have hf := ComputableInSpace.foldl hl hinit hstep hspace hstate - refine hf.weaken (.of_le_linear 8 8 fun _ => by omega) |>.of_enc_eq ?_ - intro l - simp only [List.foldl_flip_cons_eq_append', List.append_nil] - /-- Decode actual pairs of input bits in linear space, with at most one cell per two bits. -/ theorem cells_inSpace : ComputableInSpace cells encBits (encList encNat) (fun word => word.length) := by @@ -249,7 +220,7 @@ theorem cells_inSpace : ComputableInSpace cells encBits (encList encNat) simp only [encBool_apply, List.length_singleton] omega have hf := ComputableInSpace.foldl hl hinit scan_inSpace hspace hstate - have hout := budget_comp_linear listReverse_inSpace hf.snd'.snd' + have hout := budget_comp_linear (listReverse encNat) hf.snd'.snd' refine hout.weaken (.of_le_linear 100 100 fun _ => by simp only [encBits_apply] omega) |>.of_enc_eq ?_ diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayProviderLabels.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayProviderLabels.lean new file mode 100644 index 0000000000..4f854300d1 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayProviderLabels.lean @@ -0,0 +1,116 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import + Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProviders +public import + Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeChecked +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Pi +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Pi +import Complexity.Computability.Machines.Turing.MultiTape.Internal.DegreeOptionPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Degree.Bounds + +/-! +# Checked labels for native provider-window assembly + +The input contains actual previous and provider label words, the runtime block width and a +fixed finite port map. Every label must pass the exact canonical-code parser, including labels +whose port map entry is `none`. Only after all checks succeed are decoded windows passed to the +native provider assembler. Rejected labels cannot become successful default windows. + +These words are gate-assignment data, not a stored copy of the simulated machine's original +input. Acquiring assignment labels and computing the port map remain caller operations. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Providers.Labels + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +variable {k s : ℕ} {Q : Type} + +/-- Actual provider label words, indexed by fixed work tape and neighboring block. -/ +abbrev ProviderWords (k : ℕ) := Fin k → Fin 3 → List Bool + +/-- Binary block width, port choices, previous label and every provider label. -/ +abbrev Args (k : ℕ) := ℕ × PortMap k × List Bool × ProviderWords k + +/-- Each actual label word is charged in the fixed finite family encoding. -/ +@[expose] noncomputable def encProviderWords : ProviderWords k ↪ List Bool := + encPi (encPi encBits) + +/-- Runtime width and all actual gate-assignment data, with no implicit label table. -/ +@[expose] noncomputable def encArgs : Args k ↪ List Bool := + encPair encNat (encPair encPortMap (encPair encBits encProviderWords)) + +/-- Reject unless the previous label and every provider label are accepted, even when a port +is unused. Default values are extracted only under this complete acceptance guard. -/ +@[expose] noncomputable def combine (e : Q ≃ Fin s) : Args k → Option (RawResult k Q) := + fun (b, ports, previousWord, providerWords) => + let empty : RawResult k Q := (none, fun _ => 0, .empty, fun _ => []) + let previous := Encoding.NativeChecked.parse e (b, previousWord) + let providers := fun h d => Encoding.NativeChecked.parse e (b, providerWords h d) + if previous.isSome && decide (∀ h d, (providers h d).isSome = true) then + some (Providers.combine (b, ports, previous.getD empty, + fun h d => (providers h d).getD empty)) + else none + +variable [Finite Q] + +/-- Complete canonical validation and window assembly use native space linear in the actual +encoded arguments. Only the fixed tape and three-port families are enumerated. -/ +theorem combine_inSpace (e : Q ≃ Fin s) : + ComputableInSpace (combine (k := k) e) encArgs + (encOption (encRawResult (k := k) (Q := Q))) (fun a => (encArgs a).length) := by + classical + let empty : RawResult k Q := (none, fun _ => 0, .empty, fun _ => []) + have hb := fst encNat (encPair (encPortMap (k := k)) + (encPair encBits (encProviderWords (k := k)))) + have hrest := snd encNat (encPair (encPortMap (k := k)) + (encPair encBits (encProviderWords (k := k)))) + have hports := hrest.fst' + have hpreviousWord := hrest.snd'.fst' + have hproviderWords := hrest.snd'.snd' + have hprevious := budget_comp_linear (Encoding.NativeChecked.parse_inSpace (k := k) e) + (budget_prod hb hpreviousWord) + have hprovider (h : Fin k) (d : Fin 3) := + budget_comp_linear (Encoding.NativeChecked.parse_inSpace (k := k) e) + (budget_prod hb (budget_comp_linear + (of_inO (ComputableInO.pi_apply encBits d)) + (budget_comp_linear (of_inO (ComputableInO.pi_apply (encPi encBits) h)) hproviderWords))) + have hget : ComputableInSpace (fun o : Option (RawResult k Q) => o.getD empty) + (encOption (encRawResult (k := k) (Q := Q))) (encRawResult (k := k) (Q := Q)) + (fun o => (encOption (encRawResult (k := k) (Q := Q)) o).length) := + of_inO (ComputableInDegree.getD (enc₁ := encRawResult) empty).inO_one + have hisSome : ComputableInSpace (fun o : Option (RawResult k Q) => o.isSome) + (encOption (encRawResult (k := k) (Q := Q))) encBool + (fun o => (encOption (encRawResult (k := k) (Q := Q)) o).length) := + of_inO (ComputableInDegree.isSome (encRawResult (k := k) (Q := Q))).inO_one + have hpreviousRaw := budget_comp_linear hget hprevious + have hproviderRaw := pi_linear (Index := Fin k) fun h => + pi_linear (Index := Fin 3) fun d => budget_comp_linear hget (hprovider h d) + have hflags := pi_linear (Index := Fin k) fun h => + pi_linear (Index := Fin 3) fun d => budget_comp_linear hisSome (hprovider h d) + have hall := budget_finite (encC := encBool) + (fun flags : Fin k → Fin 3 → Bool => decide (∀ h d, flags h d = true)) hflags + have hguard := budget_finite (encC := encBool) (fun p : Bool × Bool => p.1 && p.2) + (budget_prod (budget_comp_linear hisSome hprevious) hall) + have hargs := budget_prod hb (budget_prod hports (budget_prod hpreviousRaw hproviderRaw)) + have hcombined := budget_comp_linear (Providers.combine_inSpace (k := k) (Q := Q)) hargs + have hsome := budget_comp_linear + (of_inO (ComputableInDegree.optionSome (enc₁ := encRawResult (k := k) (Q := Q)) + (enc := encOption (encRawResult (k := k) (Q := Q)))).inO_one) hcombined + refine (budget_ite hguard hsome + (budget_const (encB := encOption (encRawResult (k := k) (Q := Q))) none (.refl _))).of_enc_eq ?_ + intro a + rcases a with ⟨b, ports, previousWord, providerWords⟩ + simp only [combine, empty] + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Providers.Labels diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayProviders.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayProviders.lean new file mode 100644 index 0000000000..3aceca3ddd --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayProviders.lean @@ -0,0 +1,161 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAccess +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Pi +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.ListSlice +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Pi + +/-! +# Native assembly of provider windows + +Each of the three blocks of a work-tape window is selected from its explicit provider buffer. +An absent provider produces a blank prefix by mapping the previous buffer's paid prefix to zero. +All slices are bounded by actual stored buffers; a binary width never creates a fresh list of +that length. The previous control, head residues and output summary are retained exactly. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Providers + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +variable {k : ℕ} {Q : Type} + +/-- For each tape and destination block, the supplying block of its provider, or a blank source. -/ +abbrev PortMap (k : ℕ) := Fin k → Fin 3 → Option (Fin 3) + +/-- Explicit local data for the fixed family of tape/block provider nodes. -/ +abbrev ProviderValues (k : ℕ) (Q : Type) := Fin k → Fin 3 → RawResult k Q + +/-- Runtime width, provider-port map, previous value and every supplied provider value. -/ +abbrev Args (k : ℕ) (Q : Type) := ℕ × PortMap k × RawResult k Q × ProviderValues k Q + +/-- Select an actual stored block, or blank an actual prefix of the previous window. -/ +@[expose] def block (b : ℕ) (ports : PortMap k) (previous : RawResult k Q) + (providers : ProviderValues k Q) (h : Fin k) (d : Fin 3) : List ℕ := + let (_, _, _, previousCells) := previous + match ports h d with + | none => ((previousCells h).take b).map (fun _ => 0) + | some p => + let (_, _, _, providerCells) := providers h d + ((providerCells h).drop (p.val * b)).take b + +/-- Assemble the three destination blocks and retain the previous finite control record. -/ +@[expose] def combine : Args k Q → RawResult k Q := + fun (b, ports, previous, providers) => + let (control, residues, track, _) := previous + (control, residues, track, fun h => + block b ports previous providers h 0 ++ block b ports previous providers h 1 ++ + block b ports previous providers h 2) + +/-- The fixed provider ports are encoded individually, independently of runtime windows. -/ +@[expose] noncomputable def encPortMap : PortMap k ↪ List Bool := + encPi (encPi (encOption (encFinite (Fin 3)))) + +variable [Finite Q] + +/-- Every provider value is stored explicitly in a fixed tape/block family. -/ +@[expose] noncomputable def encProviderValues : ProviderValues k Q ↪ List Bool := + encPi (encPi encRawResult) + +/-- Binary width and all actual buffers, including the previous value used to pay blank blocks. -/ +@[expose] noncomputable def encArgs : Args k Q ↪ List Bool := + encPair encNat (encPair encPortMap (encPair encRawResult encProviderValues)) + +private theorem zeroMap_inSpace : ComputableInSpace (fun l : List ℕ => l.map (fun _ => 0)) + (encList encNat) (encList encNat) (fun l => (encList encNat l).length) := by + have hid : ComputableInSpace (fun l : List ℕ => l) (encList encNat) (encList encNat) + (fun l => (encList encNat l).length) := of_inO ComputableInDegree.id'.inO_one + have hstep : ComputableInSpace (fun p : List ℕ × ℕ => p.1 ++ [0]) + (encPair (encList encNat) encNat) (encList encNat) + (fun p => (encPair (encList encNat) encNat p).length) := + budget_snoc (fst _ _) (budget_const 0 (.refl _)) + have hfold (l out : List ℕ) : + l.foldl (fun acc _ => acc ++ [0]) out = out ++ l.map (fun _ => 0) := by + induction l generalizing out with + | nil => simp + | cons n l ih => simp [List.foldl_cons, ih, List.append_assoc] + have hmap (l : List ℕ) : (encList encNat (l.map (fun _ => 0))).length ≤ + (encList encNat l).length := + length_encList_map_le encNat encNat (by intro n _; simp) + have hstate (l : List ℕ) (i : ℕ) (_hi : i ≤ l.length) : + (encList encNat ((l.take i).foldl (fun acc _ => acc ++ [0]) [])).length ≤ + (encList encNat l).length := by + rw [hfold, List.nil_append] + exact (hmap _).trans (length_encList_take_le encNat l i) + have hspace (l : List ℕ) (i : ℕ) (hi : i < l.length) : + (encPair (encList encNat) encNat + ((l.take i).foldl (fun acc _ => acc ++ [0]) [], l[i])).length ≤ + 4 * ((encList encNat l).length + 1) := by + have ha := length_le_encList_of_mem encNat (List.getElem_mem hi) + have hs := hstate l i hi.le + simp only [length_encPair] + omega + have hf := ComputableInSpace.foldl hid (budget_const [] (.refl _)) hstep hspace hstate + refine hf.weaken (.of_le_linear 16 16 fun _ => by omega) |>.of_enc_eq ?_ + intro l + rw [hfold, List.nil_append] + +private theorem block_inSpace (h : Fin k) (d : Fin 3) : ComputableInSpace + (fun a : Args k Q => block a.1 a.2.1 a.2.2.1 a.2.2.2 h d) encArgs (encList encNat) + (fun a => (encArgs a).length) := by + have hb := fst encNat (encPair (encPortMap (k := k)) + (encPair (encRawResult (k := k) (Q := Q)) (encProviderValues (k := k) (Q := Q)))) + have hrest := snd encNat (encPair (encPortMap (k := k)) + (encPair (encRawResult (k := k) (Q := Q)) (encProviderValues (k := k) (Q := Q)))) + have hports := hrest.fst' + have hprevious := hrest.snd'.fst' + have hproviders := hrest.snd'.snd' + have hport := budget_comp_linear + (of_inO (ComputableInO.pi_apply (encOption (encFinite (Fin 3))) d)) + (budget_comp_linear (of_inO (ComputableInO.pi_apply + (encPi (encOption (encFinite (Fin 3)))) h)) hports) + have hpreviousCells := budget_comp_linear (of_inO (ComputableInO.pi_apply (encList encNat) h)) + hprevious.snd'.snd'.snd' + have hblank := budget_comp_linear zeroMap_inSpace + (budget_comp_linear (listTake encNat) (budget_prod hpreviousCells hb)) + have hprovider := budget_comp_linear + (of_inO (ComputableInO.pi_apply (encRawResult (k := k) (Q := Q)) d)) + (budget_comp_linear + (of_inO (ComputableInO.pi_apply (encPi (encRawResult (k := k) (Q := Q))) h)) hproviders) + have hproviderCells := budget_comp_linear (of_inO (ComputableInO.pi_apply (encList encNat) h)) + hprovider.snd'.snd'.snd' + have hpair := fst (encArgs (k := k) (Q := Q)) (encFinite (Fin 3)) + have hselected := budget_comp_linear (listTake encNat) + (budget_prod (budget_comp_linear (listDrop encNat) + (budget_prod (budget_comp_linear hproviderCells hpair) + (budget_natMul (budget_finite (encC := encNat) Fin.val + (snd (encArgs (k := k) (Q := Q)) (encFinite (Fin 3)))) + (budget_comp_linear hb hpair)))) (budget_comp_linear hb hpair)) + have hid : ComputableInSpace (fun a : Args k Q => a) encArgs encArgs + (fun a => (encArgs a).length) := of_inO ComputableInDegree.id'.inO_one + refine (budget_comp_linear (optionElimSnd hblank hselected) (budget_prod hid hport)).of_enc_eq ?_ + intro a + cases hp : a.2.1 h d <;> simp [block, hp] + +/-- Uniform native assembly, charging every supplied buffer and retaining the control record +without a lookup over possible window values. -/ +theorem combine_inSpace : ComputableInSpace (combine (k := k) (Q := Q)) encArgs encRawResult + (fun a => (encArgs a).length) := by + have hprevious := (snd encNat (encPair (encPortMap (k := k)) + (encPair (encRawResult (k := k) (Q := Q)) (encProviderValues (k := k) (Q := Q))))).snd'.fst' + have hcells := pi_linear (Index := Fin k) fun h => + budget_comp_linear (of_inO (ComputableInDegree.listAppend encNat).inO_one) + (budget_prod (block_inSpace (Q := Q) h 0) + (budget_comp_linear (of_inO (ComputableInDegree.listAppend encNat).inO_one) + (budget_prod (block_inSpace (Q := Q) h 1) (block_inSpace (Q := Q) h 2)))) + refine (budget_prod hprevious.fst' + (budget_prod hprevious.snd'.fst' (budget_prod hprevious.snd'.snd'.fst' hcells))).of_enc_eq ?_ + rintro ⟨b, ports, ⟨control, residues, track, cells⟩, providers⟩ + simp only [combine, encRawResult, List.append_assoc] + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Providers diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayProvidersCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayProvidersCorrect.lean new file mode 100644 index 0000000000..38019e7a37 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayProvidersCorrect.lean @@ -0,0 +1,157 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProviderLabels +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayLoadCorrect +import Complexity.Foundations.Data.List.Lookup +import Mathlib.Tactic.FinCases + +/-! +# Correct provider-window assembly + +The raw combiner preserves canonical buffers and selects the same blocks as the semantic +entering window. Checked labels must all succeed, including labels at unused provider ports. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Providers + +variable {k s : ℕ} {Q : Type} + +private theorem length_block (b : ℕ) (ports : PortMap k) (previous : RawResult k Q) + (providers : ProviderValues k Q) (hprevious : Load.CanonicalWindows b previous) + (hproviders : ∀ h d, Load.CanonicalWindows b (providers h d)) (h : Fin k) (d : Fin 3) : + (block b ports previous providers h d).length = b := by + have hp := hprevious.1 h + have hh := (hproviders h d).1 h + cases ht : ports h d with + | none => + simp only [block, ht, List.length_map, List.length_take, hp] + omega + | some p => + have hfit := Nat.mul_le_mul_right b (Nat.succ_le_of_lt p.isLt) + rw [Nat.succ_mul] at hfit + simp only [block, ht, List.length_take, List.length_drop, hh] + omega + +private theorem code_block (b : ℕ) (ports : PortMap k) (previous : RawResult k Q) + (providers : ProviderValues k Q) (hproviders : ∀ h d, Load.CanonicalWindows b (providers h d)) + (h : Fin k) (d : Fin 3) (n : ℕ) (hn : n ∈ block b ports previous providers h d) : n ≤ 2 := by + cases ht : ports h d with + | none => + simp only [block, ht, List.mem_map] at hn + obtain ⟨_, _, rfl⟩ := hn + decide + | some p => + simp only [block, ht] at hn + exact (hproviders h d).2 h n (List.mem_of_mem_drop (List.mem_of_mem_take hn)) + +/-- Assembling valid provider blocks preserves the loader's canonical explicit buffers. -/ +theorem canonical_combine (b : ℕ) (ports : PortMap k) (previous : RawResult k Q) + (providers : ProviderValues k Q) (hprevious : Load.CanonicalWindows b previous) + (hproviders : ∀ h d, Load.CanonicalWindows b (providers h d)) : + Load.CanonicalWindows b (combine (b, ports, previous, providers)) := by + constructor + · intro h + simp only [List.length_append, + length_block b ports previous providers hprevious hproviders] + omega + · intro h n hn + simp only [List.mem_append] at hn + rcases hn with (hn | hn) | hn + · exact code_block b ports previous providers hproviders h 0 n hn + · exact code_block b ports previous providers hproviders h 1 n hn + · exact code_block b ports previous providers hproviders h 2 n hn + +private theorem getD_block (b : ℕ) (ports : PortMap k) (previous : RawResult k Q) + (providers : ProviderValues k Q) (h : Fin k) (d : Fin 3) (r : ℕ) (hr : r < b) : + cellOfCode ((block b ports previous providers h d).getD r 0) = + match ports h d with + | none => none + | some p => cellOfCode (((providers h d).2.2.2 h).getD (p.val * b + r) 0) := by + cases ht : ports h d with + | none => simp [block, ht, cellOfCode] + | some p => rw [block, ht, List.getD_take_drop _ _ hr] + +/-- Interpreting the native result gives exactly the selected semantic entering windows. -/ +theorem toView_combine (b : ℕ) [NeZero b] (ports : PortMap k) (previous : RawResult k Q) + (providers : ProviderValues k Q) (hprevious : Load.CanonicalWindows b previous) + (hproviders : ∀ h d, Load.CanonicalWindows b (providers h d)) : + RawResult.toView b (combine (b, ports, previous, providers)) = + ((RawResult.toView b previous).1, fun h d r => + match ports h d with + | none => none + | some p => (RawResult.toView b (providers h d)).2 h p r) := by + apply Prod.ext + · rfl + · funext h d r + have hr := ZMod.val_lt r + have hl (d : Fin 3) := length_block b ports previous providers hprevious hproviders h d + have hread : + (block b ports previous providers h 0 ++ block b ports previous providers h 1 ++ + block b ports previous providers h 2).getD (d.val * b + r.val) 0 = + (block b ports previous providers h d).getD r.val 0 := by + fin_cases d + · rw [List.getD_append _ _ _ _ (by simp only [List.length_append, hl]; omega)] + simpa using + List.getD_append (block b ports previous providers h 0) + (block b ports previous providers h 1) 0 r.val (by simpa only [hl] using hr) + · rw [List.getD_append _ _ _ _ (by simp only [List.length_append, hl]; omega)] + rw [List.getD_append_right _ _ _ _ (by simp only [hl]; omega)] + simp only [hl] + congr 1 + omega + · rw [List.getD_append_right _ _ _ _ (by simp only [List.length_append, hl]; omega)] + simp only [List.length_append, hl] + congr 1 + omega + change cellOfCode ((block b ports previous providers h 0 ++ + block b ports previous providers h 1 ++ block b ports previous providers h 2).getD + (d.val * b + r.val) 0) = _ + rw [hread, getD_block b ports previous providers h d r.val hr] + cases ht : ports h d <;> simp [ht, RawResult.toView] + +/-- Successful checked parsing of every supplied label gives the assembled raw windows. +The condition includes provider words whose selected port is absent. -/ +theorem Labels.combine_of_parse {s : ℕ} (e : Q ≃ Fin s) (b : ℕ) (ports : PortMap k) + (previousWord : List Bool) (providerWords : Labels.ProviderWords k) + (previous : RawResult k Q) (providers : ProviderValues k Q) + (hprevious : Encoding.NativeChecked.parse e (b, previousWord) = some previous) + (hproviders : ∀ h d, + Encoding.NativeChecked.parse e (b, providerWords h d) = some (providers h d)) : + Labels.combine e (b, ports, previousWord, providerWords) = + some (Providers.combine (b, ports, previous, providers)) := by + simp [Labels.combine, hprevious, hproviders] + +/-- The semantic schedule chooses the last earlier interval containing each entering block, +then reads that block at its port in the provider's window. A never visited block is blank. +This finite map specifies the native assembler's input; computing it is a caller operation. -/ +@[expose] def portMap {q : ℕ} (σ : Schedule k q) (i : Fin q) : PortMap k := fun h d => + (lastBefore (InWindow σ h (portBlock σ i h d)) i).map + fun j => portOf σ j h (portBlock σ i h d) + +/-- With the schedule's provider ports, native assembly reconstructs exactly the functional +block gate's entering window. The preceding control record is retained. All provider values +here have already succeeded; checking that condition belongs to the checked-label wrapper. -/ +theorem toView_combine_enteringWindow {q : ℕ} (machine : MultiTapePTM k Bool Q) + (b : ℕ) [NeZero b] (σ : Schedule k q) (i : Fin q) (previous : RawResult k Q) + (providers : ProviderValues k Q) (hprevious : Load.CanonicalWindows b previous) + (hproviders : ∀ h d, Load.CanonicalWindows b (providers h d)) : + RawResult.toView b (combine (b, portMap σ i, previous, providers)) = + ((RawResult.toView b previous).1, + enteringWindow machine b σ i (fun h d => some (RawResult.toView b (providers h d)))) := by + rw [toView_combine b (portMap σ i) previous providers hprevious hproviders] + apply Prod.ext + · rfl + · funext h d r + cases ht : lastBefore (InWindow σ h (portBlock σ i h d)) i <;> + simp [portMap, enteringWindow, ht] + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Providers diff --git a/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/ListSlice.lean b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/ListSlice.lean new file mode 100644 index 0000000000..fb7f6a9f2a --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/InSpace/ListSlice.lean @@ -0,0 +1,104 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpaceLib +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Quantitative +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Degree.Bounds +import Mathlib.Data.List.GetD + +/-! +# Native reversal and binary slicing of framed lists + +Reversal and taking or dropping a binary-counted prefix use space linear in the actual encoded +list and counter. Every intermediate list is a prefix, suffix or reversal of stored input data; +large binary counts never cause a list of that length to be allocated. + +## Main results + +* `Turing.MultiTapeTM.ComputableInSpace.listReverse`: reverse a framed list. +* `Turing.MultiTapeTM.ComputableInSpace.listDrop`, `.listTake`: slice with a binary counter. +-/ + +public section + +namespace Turing.MultiTapeTM.ComputableInSpace + +private theorem length_encList_reverse {α : Type} (enc : α ↪ List Bool) (l : List α) : + (encList enc l.reverse).length = (encList enc l).length := by + simp only [length_encList, List.map_reverse, List.sum_reverse] + +/-- Reverse a framed list in native space linear in its actual encoded storage. -/ +theorem listReverse {α : Type} (enc : α ↪ List Bool) : + ComputableInSpace List.reverse (encList enc) (encList enc) + (fun l => (encList enc l).length) := by + have hlist : ComputableInSpace (fun l : List α => l) (encList enc) (encList enc) + (fun l => (encList enc l).length) := of_inO ComputableInDegree.id'.inO_one + have hinit : ComputableInSpace (fun _ : List α => ([] : List α)) + (encList enc) (encList enc) (fun l => (encList enc l).length) := + budget_const [] (.refl _) + have hstep : ComputableInSpace (fun q : List α × α => q.2 :: q.1) + (encPair (encList enc) enc) (encList enc) + (fun q => (encPair (encList enc) enc q).length) := + budget_cons (snd (encList enc) enc) (fst (encList enc) enc) + have hstate (l : List α) (i : ℕ) (_hi : i ≤ l.length) : + (encList enc ((l.take i).foldl (fun acc a => a :: acc) [])).length ≤ + (encList enc l).length := by + rw [List.foldl_flip_cons_eq_append', List.append_nil, length_encList_reverse] + exact length_encList_take_le enc l i + have hspace (l : List α) (i : ℕ) (hi : i < l.length) : + (encPair (encList enc) enc + ((l.take i).foldl (fun acc a => a :: acc) [], l[i])).length ≤ + 4 * ((encList enc l).length + 1) := by + have ha := length_le_encList_of_mem enc (List.getElem_mem hi) + have hs := hstate l i hi.le + simp only [length_encPair] + omega + have hfold := ComputableInSpace.foldl hlist hinit hstep hspace hstate + refine hfold.weaken (.of_le_linear 8 8 fun _ => by omega) |>.of_enc_eq ?_ + intro l + simp only [List.foldl_flip_cons_eq_append', List.append_nil] + +/-- Drop a binary-counted prefix without allocating storage according to its numeric length. -/ +theorem listDrop {α : Type} (enc : α ↪ List Bool) : + ComputableInSpace (fun q : List α × ℕ => q.1.drop q.2) + (encPair (encList enc) encNat) (encList enc) + (fun q => (encPair (encList enc) encNat q).length) := by + have htail : ComputableInSpace List.tail (encList enc) (encList enc) + (fun l => (encList enc l).length) := + of_inO (.of_linear (ListEncoding.tail (enc₁ := enc))) + have hlen (q : List α × ℕ) (k : ℕ) : + (encList enc (List.tail^[k] q.1)).length ≤ (encList enc q.1).length := by + rw [List.tail_iterate] + rw [← q.1.take_append_drop k, encList_append, List.length_append] + simp + have h := iterate (snd (encList enc) encNat) (fst (encList enc) encNat) htail + (Q := fun q => (encList enc q.1).length) (B := fun q => (encList enc q.1).length) + (fun q k _ => hlen q k) (fun q k _ => hlen q k) + refine h.weaken (.of_le_linear 8 8 fun q => ?_) |>.of_enc_eq ?_ + · simp only [length_encPair] + omega + · intro q + rw [List.tail_iterate] + +/-- Take a binary-counted prefix using space linear in the actual framed list and counter. -/ +theorem listTake {α : Type} (enc : α ↪ List Bool) : + ComputableInSpace (fun q : List α × ℕ => q.1.take q.2) + (encPair (encList enc) encNat) (encList enc) + (fun q => (encPair (encList enc) encNat q).length) := by + have hl := fst (encList enc) encNat + have hn := snd (encList enc) encNat + have hrev := budget_comp_linear (listReverse enc) hl + have hlen := budget_length hl + have hcount := budget_natSub hlen hn + have hdrop := budget_comp_linear (listDrop enc) (budget_prod hrev hcount) + refine (budget_comp_linear (listReverse enc) hdrop).of_enc_eq ?_ + intro q + rw [← List.reverse_take, List.reverse_reverse] + +end Turing.MultiTapeTM.ComputableInSpace diff --git a/Complexity/Computability/TreeEval/Ary/Packed/RawControllerNative.lean b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerNative.lean index e4cbe598aa..e5916e8db4 100644 --- a/Complexity/Computability/TreeEval/Ary/Packed/RawControllerNative.lean +++ b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerNative.lean @@ -10,6 +10,7 @@ module public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerEncoding public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerSpace public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.ListSlice import Mathlib.Data.List.GetD /-! @@ -45,75 +46,6 @@ abbrev encNeed : Need ↪ List Bool := abbrev encStepArgs : ((Params × State) × Reply) ↪ List Bool := encPair encFullState encReply -private theorem length_encList_reverse {α : Type} (enc : α ↪ List Bool) (l : List α) : - (encList enc l.reverse).length = (encList enc l).length := by - simp only [length_encList, List.map_reverse, List.sum_reverse] - -private theorem listReverse_inSpace {α : Type} (enc : α ↪ List Bool) : - ComputableInSpace List.reverse (encList enc) (encList enc) - (fun l => (encList enc l).length) := by - have hlist : ComputableInSpace (fun l : List α => l) (encList enc) (encList enc) - (fun l => (encList enc l).length) := of_inO ComputableInDegree.id'.inO_one - have hinit : ComputableInSpace (fun _ : List α => ([] : List α)) - (encList enc) (encList enc) (fun l => (encList enc l).length) := - budget_const [] (.refl _) - have hstep : ComputableInSpace (fun q : List α × α => q.2 :: q.1) - (encPair (encList enc) enc) (encList enc) - (fun q => (encPair (encList enc) enc q).length) := - budget_cons (snd (encList enc) enc) (fst (encList enc) enc) - have hstate (l : List α) (i : ℕ) (_hi : i ≤ l.length) : - (encList enc ((l.take i).foldl (fun acc a => a :: acc) [])).length ≤ - (encList enc l).length := by - rw [List.foldl_flip_cons_eq_append', List.append_nil, length_encList_reverse] - exact length_encList_take_le enc l i - have hspace (l : List α) (i : ℕ) (hi : i < l.length) : - (encPair (encList enc) enc - ((l.take i).foldl (fun acc a => a :: acc) [], l[i])).length ≤ - 4 * ((encList enc l).length + 1) := by - have ha := length_le_encList_of_mem enc (List.getElem_mem hi) - have hs := hstate l i hi.le - simp only [length_encPair] - omega - have hfold := ComputableInSpace.foldl hlist hinit hstep hspace hstate - refine hfold.weaken (.of_le_linear 8 8 fun _ => by omega) |>.of_enc_eq ?_ - intro l - simp only [List.foldl_flip_cons_eq_append', List.append_nil] - -private theorem listDrop_inSpace {α : Type} (enc : α ↪ List Bool) : - ComputableInSpace (fun q : List α × ℕ => q.1.drop q.2) - (encPair (encList enc) encNat) (encList enc) - (fun q => (encPair (encList enc) encNat q).length) := by - have htail : ComputableInSpace List.tail (encList enc) (encList enc) - (fun l => (encList enc l).length) := - of_inO (.of_linear (ListEncoding.tail (enc₁ := enc))) - have hlen (q : List α × ℕ) (k : ℕ) : - (encList enc (List.tail^[k] q.1)).length ≤ (encList enc q.1).length := by - rw [List.tail_iterate] - rw [← q.1.take_append_drop k, encList_append, List.length_append] - simp - have h := iterate (snd (encList enc) encNat) (fst (encList enc) encNat) htail - (Q := fun q => (encList enc q.1).length) (B := fun q => (encList enc q.1).length) - (fun q k _ => hlen q k) (fun q k _ => hlen q k) - refine h.weaken (.of_le_linear 8 8 fun q => ?_) |>.of_enc_eq ?_ - · simp only [length_encPair] - omega - · intro q - rw [List.tail_iterate] - -private theorem listTake_inSpace {α : Type} (enc : α ↪ List Bool) : - ComputableInSpace (fun q : List α × ℕ => q.1.take q.2) - (encPair (encList enc) encNat) (encList enc) - (fun q => (encPair (encList enc) encNat q).length) := by - have hl := fst (encList enc) encNat - have hn := snd (encList enc) encNat - have hrev := budget_comp_linear (listReverse_inSpace enc) hl - have hlen := budget_length hl - have hcount := budget_natSub hlen hn - have hdrop := budget_comp_linear (listDrop_inSpace enc) (budget_prod hrev hcount) - refine (budget_comp_linear (listReverse_inSpace enc) hdrop).of_enc_eq ?_ - intro q - rw [← List.reverse_take, List.reverse_reverse] - /-- The bank point is computed with binary slicing counters and linear retained output. -/ theorem point_inSpace : ComputableInSpace (fun q : (ℕ × ℕ × ℕ) × List ℕ => @@ -126,9 +58,9 @@ theorem point_inSpace : have ht := snd' (snd' hpar) have hbank := snd encNat3 (encList encNat) have htarget := budget_natMod ht (budget_natSucc hd) - have hdrop := budget_comp_linear (listDrop_inSpace encNat) + have hdrop := budget_comp_linear (listDrop encNat) (budget_prod hbank (budget_natMul (budget_natSucc htarget) hr)) - have htake := budget_comp_linear (listTake_inSpace encNat) + have htake := budget_comp_linear (listTake encNat) (budget_prod hbank (budget_natMul htarget hr)) refine (budget_comp_linear (of_inO (ComputableInDegree.listAppend encNat).inO_one) (budget_prod hdrop htake)).of_enc_eq ?_ diff --git a/ComplexityTests/Machines/Turing/MultiTape/QueryReplayProviders.lean b/ComplexityTests/Machines/Turing/MultiTape/QueryReplayProviders.lean new file mode 100644 index 0000000000..c046afa8d1 --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/QueryReplayProviders.lean @@ -0,0 +1,57 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of native provider-window assembly) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProvidersCorrect +public import ComplexityTests.Machines.Turing.MultiTape.NativeBlockCodec + +/-! +# Native provider-window regressions + +Provider selection preserves the previous control and uses blank windows at absent ports. +Every provider label is checked even when all ports are absent. +-/ + +public section + +namespace ComplexityTests.QueryReplayProviders + +open Turing.MultiTapePTM.BlockComputation.QueryReplay +open Providers +open NativeBlockCodec + +def ports : PortMap 1 := fun _ d => if d = 0 then some 2 else if d = 2 then some 0 else none + +def previous : RawResult 1 (Fin 1) := + (some 0, fun _ => 1, .accept, fun _ => [2, 1, 1, 2, 0, 0]) + +def providers : ProviderValues 1 (Fin 1) := + fun _ _ => (none, fun _ => 0, .empty, fun _ => [1, 2, 0, 0, 2, 1]) + +example : (let (_, _, _, cells) := combine (2, ports, previous, providers); cells 0) = + [2, 1, 0, 0, 1, 2] := by decide + +example : (let (control, _, _, _) := combine (2, ports, previous, providers); control) = + some 0 := by decide + +-- A huge binary width cannot allocate windows from a short malformed buffer. +def malformed := combine + (1000000000, ((fun _ _ => none) : PortMap 1), raw, fun _ _ => raw) + +example : (let (_, _, _, cells) := malformed; cells 0).length = 9 := by decide + +def absent : PortMap 1 := fun _ _ => none + +example : (Labels.combine code (1, absent, good, fun _ _ => good)).isSome = true := by decide +example : Labels.combine code (1, absent, fail, fun _ _ => good) = none := by decide +example : Labels.combine code (1, absent, good, + fun _ d => if d = 2 then fail else good) = none := by decide +example : Labels.combine code (1, absent, good, + fun _ d => if d = 1 then noncanonicalBlank else good) = none := by decide + +end ComplexityTests.QueryReplayProviders From dfe88a56b31e6608a6831c3d19bead00aac26916 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 02:31:05 +0000 Subject: [PATCH 22/36] feat(hpv): read schedule provider ports in native space --- Complexity.lean | 7 + .../BlockComputation/ScheduleRead.lean | 38 +++++ .../BlockComputation/ScheduleReadCorrect.lean | 74 +++++++++ .../BlockComputation/ScheduleReadPort.lean | 132 +++++++++++++++ .../BlockComputation/ScheduleReadSearch.lean | 153 ++++++++++++++++++ .../ScheduleReadSearchSpace.lean | 88 ++++++++++ .../BlockComputation/ScheduleReadSpace.lean | 84 ++++++++++ .../BlockComputation/ScheduleReadWindow.lean | 78 +++++++++ .../Turing/MultiTape/ScheduleRead.lean | 52 ++++++ 9 files changed, 706 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleRead.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadPort.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadSearch.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadSearchSpace.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadSpace.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadWindow.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/ScheduleRead.lean diff --git a/Complexity.lean b/Complexity.lean index 6b5e0ebcf7..9886343b46 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1710,6 +1710,13 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProviders public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProvidersCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySpace +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleRead +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadPort +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadSearch +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadSearchSpace +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadSpace +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadWindow public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputationTree public import Complexity.Computability.Machines.Turing.MultiTape.BlockTrace public import Complexity.Computability.Machines.Turing.MultiTape.Combinators.Append diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleRead.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleRead.lean new file mode 100644 index 0000000000..db1164b7c6 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleRead.lean @@ -0,0 +1,38 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Logic.Function.Iterate + +/-! +# Streaming a column of a ternary block schedule + +A fixed tape count determines the constant radix of one schedule row. The reader discards one +row at each iteration and increments a binary counter when its selected column has the requested +digit. Runtime interval indices control iteration; no list of earlier indices or power with a +runtime exponent is allocated. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ScheduleRead + +/-- Remaining schedule rows and the number of matching digits already encountered. -/ +abbrev State := ℕ × ℕ + +/-- Consume one schedule row, counting a selected column's digit. The parameters `k`, `c` and +`δ` are fixed for a reader; only the suffix and counter are runtime state. -/ +@[expose] def step (k c δ : ℕ) : State → State := fun (suffix, count) => + (suffix / 3 ^ (k + 1), count + if suffix / 3 ^ c % 3 = δ then 1 else 0) + +/-- Count the selected digit in the first `i` rows of the actual binary schedule number. -/ +@[expose] def read (k c δ : ℕ) (a : ℕ × ℕ) : ℕ := + ((step k c δ)^[a.2] (a.1, 0)).2 + +end Turing.MultiTapePTM.BlockComputation.ScheduleRead diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadCorrect.lean new file mode 100644 index 0000000000..8a8588522e --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadCorrect.lean @@ -0,0 +1,74 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleRead +public import + Complexity.Computability.Machines.Turing.MultiTape.Probabilistic.ExplicitBlockNetwork.Graph + +/-! +# Correctness of streaming schedule digit counts + +The streamed counter agrees with the existing numeric schedule semantics `digitCount`. Its +remaining suffix never exceeds the original schedule number, and its counter never exceeds the +number of processed rows, including when the schedule number is zero. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ScheduleRead + +/-- The remaining schedule is the original number with exactly `i` rows discarded. This +identity specifies the stream; its runtime implementation does not construct the power. -/ +theorem iterate_fst (k c δ σ i : ℕ) : + ((step k c δ)^[i] (σ, 0)).1 = σ / 3 ^ ((k + 1) * i) := by + induction i with + | zero => simp + | succ i ih => + rw [Function.iterate_succ_apply'] + change ((step k c δ)^[i] (σ, 0)).1 / 3 ^ (k + 1) = _ + rw [ih, Nat.div_div_eq_div_mul, ← pow_add, Nat.mul_succ] + +/-- Counting streamed rows gives the existing schedule digit count. -/ +theorem iterate_snd (k c δ σ i : ℕ) : + ((step k c δ)^[i] (σ, 0)).2 = digitCount k σ c δ i := by + induction i with + | zero => simp [digitCount] + | succ i ih => + rw [Function.iterate_succ_apply'] + change ((step k c δ)^[i] (σ, 0)).2 + + (if ((step k c δ)^[i] (σ, 0)).1 / 3 ^ c % 3 = δ then 1 else 0) = _ + rw [ih, iterate_fst] + have hd : σ / 3 ^ ((k + 1) * i) / 3 ^ c = σ / 3 ^ (c + (k + 1) * i) := by + rw [Nat.div_div_eq_div_mul, ← pow_add, Nat.add_comm ((k + 1) * i) c] + simp only [hd, digitCount, List.range_succ, List.countP_append, List.countP_singleton, + decide_eq_true_eq] + +/-- Streamed counting uses the library's existing numeric schedule interpretation. -/ +theorem read_eq_digitCount (k c δ σ i : ℕ) : + read k c δ (σ, i) = digitCount k σ c δ i := + iterate_snd k c δ σ i + +/-- Discarding rows cannot enlarge the stored schedule number. -/ +theorem iterate_fst_le (k c δ σ i : ℕ) : + ((step k c δ)^[i] (σ, 0)).1 ≤ σ := by + rw [iterate_fst] + exact Nat.div_le_self _ _ + +/-- The counter grows by at most one per processed row. -/ +theorem iterate_snd_le (k c δ σ i : ℕ) : + ((step k c δ)^[i] (σ, 0)).2 ≤ i := by + induction i with + | zero => simp + | succ i ih => + rw [Function.iterate_succ_apply'] + change ((step k c δ)^[i] (σ, 0)).2 + + (if ((step k c δ)^[i] (σ, 0)).1 / 3 ^ c % 3 = δ then 1 else 0) ≤ i + 1 + split_ifs <;> omega + +end Turing.MultiTapePTM.BlockComputation.ScheduleRead diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadPort.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadPort.lean new file mode 100644 index 0000000000..beba93570b --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadPort.lean @@ -0,0 +1,132 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadSearchSpace +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Pi +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProvidersCorrect + +/-! +# Native provider-port readers + +After locating a previous provider interval, the reader computes the target block's port from +streamed positive and negative digit counts. The optional provider port has exactly the meaning +of the functional assembler's `portMap`. Tape count and column offset remain fixed parameters; +the schedule, target interval and port are binary runtime data. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ScheduleRead + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +/-- Read a target block's port in another interval's window from streamed digit counts. -/ +@[expose] def port (k c : ℕ) : WindowArgs → Fin 3 := fun (σ, i, δ, j) => + let pi := read k c 2 (σ, i) + let ni := read k c 0 (σ, i) + let pj := read k c 2 (σ, j) + let nj := read k c 0 (σ, j) + ⟨(pi + nj + δ - (ni + pj)) % 3, Nat.mod_lt _ (by decide)⟩ + +/-- The streamed port agrees with the existing numeric port convention. -/ +theorem val_port_eq_portOfNum (k c σ i δ j : ℕ) : + (port k c (σ, i, δ, j)).val = portOfNum k σ c i j δ := by + simp only [port, read_eq_digitCount, portOfNum] + +/-- The selected port is computed in native space linear in actual binary input storage. -/ +theorem port_inSpace (k c : ℕ) : + ComputableInSpace (port k c) encWindowArgs (encFinite (Fin 3)) + (fun a => (encWindowArgs a).length) := by + have hs := fst encNat (encPair encNat (encPair encNat encNat)) + have ht := snd encNat (encPair encNat (encPair encNat encNat)) + have hi := budget_comp_linear (fst encNat (encPair encNat encNat)) ht + have hdj := budget_comp_linear (snd encNat (encPair encNat encNat)) ht + have hd := budget_comp_linear (fst encNat encNat) hdj + have hj := budget_comp_linear (snd encNat encNat) hdj + have hpi := budget_comp_linear (read_inSpace k c 2) (budget_prod hs hi) + have hni := budget_comp_linear (read_inSpace k c 0) (budget_prod hs hi) + have hpj := budget_comp_linear (read_inSpace k c 2) (budget_prod hs hj) + have hnj := budget_comp_linear (read_inSpace k c 0) (budget_prod hs hj) + have hvalue := budget_natSub (budget_natAdd (budget_natAdd hpi hnj) hd) + (budget_natAdd hni hpj) + have hfin := budget_finMod (by decide : 0 < 3) hvalue (.refl _) + exact (budget_finite (encC := encFinite (Fin 3)) id hfin).of_enc_eq + fun ⟨σ, i, δ, j⟩ => rfl + +/-- Locate the last earlier provider, returning its port or absence for a blank target block. -/ +@[expose] def providerPort (k c : ℕ) (a : SearchArgs) : Option (Fin 3) := + let (σ, i, δ) := a + (search k c a).map fun j => port k c (σ, i, δ, j) + +/-- Native optional provider ports include the predecessor search and use only actual binary +argument storage. A missing predecessor remains absent. -/ +theorem providerPort_inSpace (k c : ℕ) : + ComputableInSpace (providerPort k c) encSearchArgs (encOption (encFinite (Fin 3))) + (fun a => (encSearchArgs a).length) := by + have hs := fst encNat (encPair encNat encNat) + have ht := snd encNat (encPair encNat encNat) + have hi := ht.fst' + have hd := ht.snd' + have hsearch := search_inSpace k c + have hget : ComputableInSpace (fun o : Option ℕ => o.getD 0) + (encOption encNat) encNat (fun o => (encOption encNat o).length) := + of_inO (ComputableInDegree.getD (enc₁ := encNat) 0).inO_one + have hisSome : ComputableInSpace (fun o : Option ℕ => o.isSome) + (encOption encNat) encBool (fun o => (encOption encNat o).length) := + of_inO (ComputableInDegree.isSome encNat).inO_one + have hj := budget_comp_linear hget hsearch + have hp := budget_comp_linear (port_inSpace k c) + (budget_prod hs (budget_prod hi (budget_prod hd hj))) + have hsome := budget_finite (encC := encOption (encFinite (Fin 3))) some hp + have hnone := budget_const (encB := encOption (encFinite (Fin 3))) none + (show (fun a => (encSearchArgs a).length) ≼ fun a => (encSearchArgs a).length from .refl _) + refine (budget_ite (budget_comp_linear hisSome hsearch) hsome hnone).of_enc_eq ?_ + rintro ⟨σ, i, δ⟩ + cases ht : search k c (σ, i, δ) <;> simp [providerPort, ht] + +/-- The native optional port is exactly the one supplied to the functional entering-window +assembler by the same numbered schedule. -/ +theorem providerPort_eq_portMap {k q : ℕ} (σ : ℕ) (i : Fin q) (h : Fin k) (δ : Fin 3) : + providerPort k (h + 1) (σ, i, δ) = + QueryReplay.Providers.portMap (scheduleOfNat k q σ) i h δ := by + rw [providerPort, search_eq_lastBefore] + unfold QueryReplay.Providers.portMap + cases hl : lastBefore (InWindow (scheduleOfNat k q σ) h + (portBlock (scheduleOfNat k q σ) i h δ)) i with + | none => simp + | some j => + simp only [Option.map_some] + congr 1 + apply Fin.ext + rw [val_port_eq_portOfNum, val_portOf] + +/-- Compute every fixed work-head/neighbor port from the actual binary schedule and interval. -/ +@[expose] def providerMap (k : ℕ) (a : ℕ × ℕ) : QueryReplay.Providers.PortMap k := + let (σ, i) := a + fun h δ => providerPort k (h + 1) (σ, i, δ) + +/-- All provider ports can be computed natively; their fixed family size depends only on the +simulated machine's number of work tapes. -/ +theorem providerMap_inSpace (k : ℕ) : + ComputableInSpace (providerMap k) encState QueryReplay.Providers.encPortMap + (fun a => (encState a).length) := by + have hs := fst encNat encNat + have hi := snd encNat encNat + have hentry (h : Fin k) (δ : Fin 3) := + budget_comp_linear (providerPort_inSpace k (h + 1)) + (budget_prod hs (budget_prod hi (budget_const (encB := encNat) δ.val (.refl _)))) + exact pi_linear fun h => pi_linear fun δ => hentry h δ + +/-- The complete native provider map is the existing functional assembler's map. -/ +theorem providerMap_eq_portMap {k q : ℕ} (σ : ℕ) (i : Fin q) : + providerMap k (σ, i) = QueryReplay.Providers.portMap (scheduleOfNat k q σ) i := by + funext h δ + exact providerPort_eq_portMap σ i h δ + +end Turing.MultiTapePTM.BlockComputation.ScheduleRead diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadSearch.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadSearch.lean new file mode 100644 index 0000000000..3cea1f5251 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadSearch.lean @@ -0,0 +1,153 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadWindow +public import Complexity.Computability.Machines.Turing.MultiTape.Probabilistic.ExplicitBlockNetwork.Table + +/-! +# Streaming the last earlier provider interval + +The search retains the binary schedule, target interval and port, a binary cursor and at most one +previous matching index. It visits earlier intervals in increasing order, replacing the saved +index on a match. Window membership is computed by streamed digit counts. No list of indices or +runtime power is allocated. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ScheduleRead + +/-- Actual schedule number, target interval and target port. -/ +abbrev SearchArgs := ℕ × ℕ × ℕ + +/-- Retained search arguments, next candidate interval and last matching interval. -/ +abbrev SearchState := SearchArgs × ℕ × Option ℕ + +/-- Check one candidate and advance the binary cursor, retaining the original arguments. -/ +@[expose] def searchStep (k c : ℕ) : SearchState → SearchState := + fun (a, j, previous) => + let (σ, i, δ) := a + (a, j + 1, if inWindow k c (σ, i, δ, j) then some j else previous) + +/-- Search the earlier intervals without materializing their indices. -/ +@[expose] def search (k c : ℕ) (a : SearchArgs) : Option ℕ := + let (_, i, _) := a + let (_, _, previous) := (searchStep k c)^[i] (a, 0, none) + previous + +/-- Every iteration retains the original schedule and target. -/ +theorem searchIterate_context (k c : ℕ) (a : SearchArgs) (j : ℕ) : + ((searchStep k c)^[j] (a, 0, none)).1 = a := by + induction j with + | zero => rfl + | succ j ih => + rw [Function.iterate_succ_apply'] + simpa only [searchStep] using ih + +/-- The search cursor equals the number of completed iterations. -/ +theorem searchIterate_cursor (k c : ℕ) (a : SearchArgs) (j : ℕ) : + ((searchStep k c)^[j] (a, 0, none)).2.1 = j := by + induction j with + | zero => rfl + | succ j ih => + rw [Function.iterate_succ_apply'] + simpa only [searchStep] using congrArg Nat.succ ih + +/-- The retained option is absent exactly before any match, and otherwise records a greatest +matching candidate among the intervals already visited. -/ +theorem searchIterate_spec (k c σ i δ j : ℕ) : + match ((searchStep k c)^[j] ((σ, i, δ), 0, none)).2.2 with + | none => ∀ n, n < j → inWindow k c (σ, i, δ, n) = false + | some n => n < j ∧ inWindow k c (σ, i, δ, n) = true ∧ + ∀ m, m < j → inWindow k c (σ, i, δ, m) = true → m ≤ n := by + induction j with + | zero => simp + | succ j ih => + rw [Function.iterate_succ_apply'] + change (match (if inWindow k c + (((searchStep k c)^[j] ((σ, i, δ), 0, none)).1.1, + ((searchStep k c)^[j] ((σ, i, δ), 0, none)).1.2.1, + ((searchStep k c)^[j] ((σ, i, δ), 0, none)).1.2.2, + ((searchStep k c)^[j] ((σ, i, δ), 0, none)).2.1) then + some (((searchStep k c)^[j] ((σ, i, δ), 0, none)).2.1) else + ((searchStep k c)^[j] ((σ, i, δ), 0, none)).2.2) with + | none => _ + | some n => _) + rw [searchIterate_context, searchIterate_cursor] + cases hp : inWindow k c (σ, i, δ, j) with + | true => + simp only [hp, ↓reduceIte] + exact ⟨by omega, trivial, fun m hm _ => by omega⟩ + | false => + simp only [Bool.false_eq_true, ↓reduceIte] + cases hl : ((searchStep k c)^[j] ((σ, i, δ), 0, none)).2.2 with + | none => + simp only [hl] at ih ⊢ + intro m hm + by_cases hmj : m < j + · exact ih m hmj + · have : m = j := by omega + simpa only [this] using hp + | some n => + simp only [hl] at ih ⊢ + refine ⟨by omega, ih.2.1, ?_⟩ + intro m hm hmatch + by_cases hmj : m < j + · exact ih.2.2 m hmj hmatch + · have : m = j := by omega + rw [this, hp] at hmatch + contradiction + +/-- A retained predecessor is always smaller than the number of visited intervals. -/ +theorem searchIterate_latest_bound (k c : ℕ) (a : SearchArgs) (j n : ℕ) + (h : ((searchStep k c)^[j] (a, 0, none)).2.2 = some n) : n < j := by + rcases a with ⟨σ, i, δ⟩ + have hs := searchIterate_spec k c σ i δ j + rw [h] at hs + exact hs.1 + +/-- Streaming search computes the existing numeric last-provider operation. -/ +theorem search_eq_lastBeforeNum (k c σ i δ : ℕ) : + search k c (σ, i, δ) = lastBeforeNum k σ c i δ := by + have hs := searchIterate_spec k c σ i δ i + change ((searchStep k c)^[i] ((σ, i, δ), 0, none)).2.2 = _ + cases hl : ((searchStep k c)^[i] ((σ, i, δ), 0, none)).2.2 with + | none => + simp only [hl] at hs + symm + simp only [lastBeforeNum, Option.map_eq_none_iff, List.find?_eq_none, List.mem_range] + intro j hj hmatch + rw [← inWindow_eq, hs (i - 1 - j) (by omega)] at hmatch + contradiction + | some n => + simp only [hl] at hs + have hfind : ((List.range i).find? fun j => + inWindowNum k σ c i δ (i - 1 - j)) = some (i - 1 - n) := by + rw [List.find?_range_eq_some] + refine ⟨?_, List.mem_range.mpr (by omega), ?_⟩ + · rw [show i - 1 - (i - 1 - n) = n by omega, ← inWindow_eq] + exact hs.2.1 + · intro j hj + rw [Bool.not_eq_true', Bool.eq_false_iff] + intro hmatch + rw [← inWindow_eq] at hmatch + have hm := hs.2.2 (i - 1 - j) (by omega) hmatch + omega + simp only [lastBeforeNum, hfind, Option.map_some] + congr 1 + omega + +/-- The native reader finds exactly the functional schedule's last earlier provider. -/ +theorem search_eq_lastBefore {k q : ℕ} (σ : ℕ) (i : Fin q) (h : Fin k) (δ : Fin 3) : + search k (h + 1) (σ, i, δ) = + (lastBefore (InWindow (scheduleOfNat k q σ) h + (portBlock (scheduleOfNat k q σ) i h δ)) i).map Fin.val := by + rw [search_eq_lastBeforeNum, lastBeforeNum_eq] + +end Turing.MultiTapePTM.BlockComputation.ScheduleRead diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadSearchSpace.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadSearchSpace.lean new file mode 100644 index 0000000000..b4ce70ddfe --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadSearchSpace.lean @@ -0,0 +1,88 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadSearch +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadWindow +import Complexity.Computability.Machines.Turing.MultiTape.Internal.BinarySizeFacts +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Degree.Bounds + +/-! +# Native space for streamed predecessor search + +The search retains its binary schedule and target, one binary cursor and the latest matching +interval. Each membership test reuses the streamed digit-count reader. Thus even a very large +binary target does not allocate an interval list or a power with runtime exponent. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ScheduleRead + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +/-- Binary schedule, target interval and target port. -/ +@[expose] noncomputable def encSearchArgs : SearchArgs ↪ List Bool := + encPair encNat (encPair encNat encNat) + +/-- Immutable search arguments, binary cursor and optional last matching interval. -/ +@[expose] noncomputable def encSearchState : SearchState ↪ List Bool := + encPair encSearchArgs (encPair encNat (encOption encNat)) + +/-- A search step uses native space linear in its actual encoded state. -/ +theorem searchStep_inSpace (k c : ℕ) : + ComputableInSpace (searchStep k c) encSearchState encSearchState + (fun a => (encSearchState a).length) := by + have ha := fst encSearchArgs (encPair encNat (encOption encNat)) + have hr := snd encSearchArgs (encPair encNat (encOption encNat)) + have hj := hr.fst' + have hlast := hr.snd' + have hwargs := budget_prod ha.fst' + (budget_prod ha.snd'.fst' (budget_prod ha.snd'.snd' hj)) + have htest := budget_comp_linear (inWindow_inSpace k c) hwargs + have hfound := budget_ite htest (budget_optionSome hj) hlast + exact (budget_prod ha (budget_prod (budget_natSucc hj) hfound)).of_enc_eq + fun ⟨⟨σ, i, δ⟩, j, latest⟩ => by + simp only [searchStep, encSearchState, encSearchArgs] + +/-- A predecessor search stores only binary counters and the latest witness, with native +space linear in the actual schedule/target/port encoding. -/ +theorem search_inSpace (k c : ℕ) : + ComputableInSpace (search k c) encSearchArgs (encOption encNat) + (fun a => (encSearchArgs a).length) := by + have ha : ComputableInSpace (fun a : SearchArgs => a) encSearchArgs encSearchArgs + (fun a => (encSearchArgs a).length) := of_inO (ComputableInO.of_eq (f := id) fun _ => rfl) + have hn := ha.snd'.fst' + have hinit := budget_prod ha (budget_prod + (budget_const (encB := encNat) 0 (.refl _)) + (budget_const (encB := encOption encNat) none (.refl _))) + have hstates (a : SearchArgs) (j : ℕ) (hj : j ≤ a.2.1) : + (encSearchState ((searchStep k c)^[j] (a, 0, none))).length ≤ + 8 * (encSearchArgs a).length + 8 := by + have hcursor := searchIterate_cursor k c a j + have hcontext := searchIterate_context k c a j + have hc := ComputableTidilyInO.length_encNat_mono hj + have hlatest : + (encOption encNat (((searchStep k c)^[j] (a, 0, none)).2.2)).length ≤ + (encNat a.2.1).length + 1 := by + cases hl : ((searchStep k c)^[j] (a, 0, none)).2.2 with + | none => simp + | some v => + have hv := searchIterate_latest_bound k c a j v hl + have hb := ComputableTidilyInO.length_encNat_mono (hv.le.trans hj) + simpa only [encOption_some, List.length_cons] using Nat.add_le_add_right hb 1 + simp only [encSearchState, length_encPair, hcontext, hcursor] + have harg : 2 * (encNat a.2.1).length ≤ (encSearchArgs a).length := by + simp only [encSearchArgs, length_encPair] + omega + omega + have hrun := budget_iterate hn hinit (searchStep_inSpace k c) hstates + (.of_le_linear 8 8 fun _ => le_rfl) + exact hrun.snd'.snd' |>.of_enc_eq fun _ => rfl + +end Turing.MultiTapePTM.BlockComputation.ScheduleRead diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadSpace.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadSpace.lean new file mode 100644 index 0000000000..a5aede681a --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadSpace.lean @@ -0,0 +1,84 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpaceLib +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Degree.Bounds +import Complexity.Computability.Machines.Turing.MultiTape.Internal.BinarySizeFacts + +/-! +# Native space bounds for streamed schedule digit counts + +The number of tapes, head offset and queried digit are fixed parameters. The schedule number +and interval count are binary runtime inputs. Reading rows repeatedly divides by the fixed row +radix; the state retains only the unread suffix and one count. In particular, it does not build +a list of intervals or a power with runtime exponent. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ScheduleRead + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +/-- The unread suffix and binary count use their actual storage. -/ +@[expose] noncomputable def encState : State ↪ List Bool := encPair encNat encNat + +/-- One fixed-head digit-count step uses native space linear in its stored state. -/ +theorem step_inSpace (k c δ : ℕ) : + ComputableInSpace (step k c δ) encState encState (fun a => (encState a).length) := by + have hs := fst encNat encNat + have hn := snd encNat encNat + have hnext := budget_natDiv hs (budget_const (encB := encNat) (3 ^ (k + 1)) (.refl _)) + have hdigit := budget_natMod + (budget_natDiv hs (budget_const (encB := encNat) (3 ^ c) (.refl _))) + (budget_const (encB := encNat) 3 (.refl _)) + have htest := budget_natEq hdigit (budget_const (encB := encNat) δ (.refl _)) + have hinc := budget_ite htest + (budget_const (encB := encNat) 1 (.refl _)) + (budget_const (encB := encNat) 0 (.refl _)) + exact (budget_prod hnext (budget_natAdd hn hinc)).of_enc_eq fun a => by + simp only [step, encState, decide_eq_true_eq] + +/-- Counting a fixed schedule digit uses space linear in the binary schedule and interval +count, including when the schedule is zero and the interval count is very large. -/ +theorem read_inSpace (k c δ : ℕ) : + ComputableInSpace (read k c δ) encState encNat (fun a => (encState a).length) := by + have hinit := budget_prod (fst encNat encNat) + (budget_const (encB := encNat) 0 (.refl _)) + have hstates (a : ℕ × ℕ) (j : ℕ) (hj : j ≤ a.2) : + (encState ((step k c δ)^[j] (a.1, 0))).length ≤ (encState a).length := by + have hs := ComputableTidilyInO.length_encNat_mono (iterate_fst_le k c δ a.1 j) + have hn := ComputableTidilyInO.length_encNat_mono + ((iterate_snd_le k c δ a.1 j).trans hj) + simp only [encState, length_encPair] + omega + have hrun := budget_iterate (snd encNat encNat) hinit (step_inSpace k c δ) + hstates (.refl _) + exact (snd' hrun).of_enc_eq fun _ => rfl + +/-- Positive and negative movement counts give the signed block origin without storing +an integer whose magnitude is charged separately. -/ +@[expose] def origin (k c : ℕ) (a : ℕ × ℕ) : ℕ × ℕ := + (read k c 2 a, read k c 0 a) + +/-- Native signed-origin preparation keeps both binary counts within the input budget. -/ +theorem origin_inSpace (k c : ℕ) : + ComputableInSpace (origin k c) encState encState (fun a => (encState a).length) := + budget_prod (read_inSpace k c 2) (read_inSpace k c 0) + +/-- The two prepared counters represent the functional schedule's exact block origin. -/ +theorem origin_sub_eq_scheduleBlock {k q : ℕ} (σ i : ℕ) (hi : i ≤ q) + (head : Option (Fin k)) : + let (positive, negative) := origin k ((finSuccEquiv k).symm head) (σ, i) + (positive : ℤ) - negative = scheduleBlock (scheduleOfNat k q σ) i head := by + simpa only [origin, read_eq_digitCount] using + (scheduleBlock_scheduleOfNat (k := k) σ hi head).symm + +end Turing.MultiTapePTM.BlockComputation.ScheduleRead diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadWindow.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadWindow.lean new file mode 100644 index 0000000000..672daa8cf6 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadWindow.lean @@ -0,0 +1,78 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadSpace +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadCorrect + +/-! +# Streamed schedule window membership + +Window membership is computed from four streamed digit counts. The schedule, target interval, +port and candidate predecessor are binary runtime data. No interval list or runtime power is +allocated; tape number and digit offset are fixed machine parameters. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ScheduleRead + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +/-- Schedule, target interval, target port and candidate preceding interval. -/ +abbrev WindowArgs := ℕ × ℕ × ℕ × ℕ + +/-- Binary encoding of all window-membership inputs. -/ +@[expose] noncomputable def encWindowArgs : WindowArgs ↪ List Bool := + encPair encNat (encPair encNat (encPair encNat encNat)) + +/-- Compare the positive/negative counts defining two windows, using streamed readers. -/ +@[expose] def inWindow (k c : ℕ) : WindowArgs → Bool := fun (σ, i, δ, j) => + let pi := read k c 2 (σ, i) + let ni := read k c 0 (σ, i) + let pj := read k c 2 (σ, j) + let nj := read k c 0 (σ, j) + decide (pj + ni + 1 ≤ nj + pi + δ + 1 ∧ nj + pi + δ ≤ pj + ni + 2) + +/-- The streamed comparison has exactly the existing numeric schedule semantics. -/ +theorem inWindow_eq (k c σ i δ j : ℕ) : + inWindow k c (σ, i, δ, j) = inWindowNum k σ c i δ j := by + simp only [inWindow, read_eq_digitCount, inWindowNum] + +/-- Native window membership uses space linear in actual binary input storage. -/ +theorem inWindow_inSpace (k c : ℕ) : + ComputableInSpace (inWindow k c) encWindowArgs encBool + (fun a => (encWindowArgs a).length) := by + have hs := fst encNat (encPair encNat (encPair encNat encNat)) + have ht := snd encNat (encPair encNat (encPair encNat encNat)) + have hi := budget_comp_linear (fst encNat (encPair encNat encNat)) ht + have hdj := budget_comp_linear (snd encNat (encPair encNat encNat)) ht + have hd := budget_comp_linear (fst encNat encNat) hdj + have hj := budget_comp_linear (snd encNat encNat) hdj + have hpi := budget_comp_linear (read_inSpace k c 2) (budget_prod hs hi) + have hni := budget_comp_linear (read_inSpace k c 0) (budget_prod hs hi) + have hpj := budget_comp_linear (read_inSpace k c 2) (budget_prod hs hj) + have hnj := budget_comp_linear (read_inSpace k c 0) (budget_prod hs hj) + have hone := budget_const (encB := encNat) 1 (show + (fun a => (encWindowArgs a).length) ≼ fun a => (encWindowArgs a).length from .refl _) + have htwo := budget_const (encB := encNat) 2 (show + (fun a => (encWindowArgs a).length) ≼ fun a => (encWindowArgs a).length from .refl _) + have ha := budget_natAdd hpj hni + have hb := budget_natAdd (budget_natAdd hnj hpi) hd + have hleft := budget_natLt (budget_natAdd hb hone) (budget_natAdd ha hone) + have hright := budget_natLt (budget_natAdd ha htwo) hb + have hfalse := budget_const (encB := encBool) false (show + (fun a => (encWindowArgs a).length) ≼ fun a => (encWindowArgs a).length from .refl _) + have htrue := budget_const (encB := encBool) true (show + (fun a => (encWindowArgs a).length) ≼ fun a => (encWindowArgs a).length from .refl _) + exact (budget_ite hleft hfalse (budget_ite hright hfalse htrue)).of_enc_eq + fun ⟨σ, i, δ, j⟩ => by + simp only [inWindow, decide_eq_true_eq] + split_ifs <;> simp_all + +end Turing.MultiTapePTM.BlockComputation.ScheduleRead diff --git a/ComplexityTests/Machines/Turing/MultiTape/ScheduleRead.lean b/ComplexityTests/Machines/Turing/MultiTape/ScheduleRead.lean new file mode 100644 index 0000000000..58c7a72d4f --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/ScheduleRead.lean @@ -0,0 +1,52 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of native streamed schedule readers) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadPort + +/-! +# Streamed schedule reader regressions + +The two-head schedule has rows `(2,0)`, `(0,2)`, `(1,1)`. Zero schedule suffixes continue +with digit zero, which denotes negative movement rather than a stationary head. +-/ + +public section + +namespace ComplexityTests.ScheduleRead + +open Turing.MultiTapePTM.BlockComputation.ScheduleRead + +example : read 1 0 2 (380, 3) = 1 := by decide +example : read 1 0 0 (380, 3) = 1 := by decide +example : read 1 1 2 (380, 3) = 1 := by decide +example : read 1 1 1 (380, 3) = 1 := by decide +example : read 1 0 0 (380, 5) = 3 := by decide +example : read 1 0 2 (380, 0) = 0 := rfl +example : read 0 0 0 (0, 100) = 100 := by decide +example : read 0 0 2 (0, 100) = 0 := by decide +example : inWindow 0 0 (0, 3, 0, 2) = false := by decide +example : inWindow 0 0 (0, 3, 1, 2) = true := by decide +example : inWindow 0 0 (0, 3, 2, 0) = false := by decide + +example : search 0 0 (0, 0, 1) = none := rfl +example : search 0 0 (0, 3, 0) = none := by decide +example : search 0 0 (0, 3, 1) = some 2 := by decide +example : search 0 0 (0, 3, 2) = some 2 := by decide +example : search 1 0 (380, 3, 0) = some 2 := by decide + +-- The latest interval misses this block; the preceding one is the provider. +example : search 0 0 (20, 3, 2) = some 1 := by decide +example : origin 0 0 (0, 100) = (0, 100) := by decide +example : providerPort 0 0 (0, 3, 0) = none := by decide +example : providerPort 0 0 (20, 3, 2) = some 2 := by decide + +example : providerMap 1 (380, 3) 0 2 = some 2 := by decide +example : providerMap 1 (0, 3) 0 0 = none := by decide + +end ComplexityTests.ScheduleRead From e99dbe08149f7c03cbff5e39c17d37b05cc61258 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 02:43:45 +0000 Subject: [PATCH 23/36] feat(hpv): acquire block labels from packed assignments --- Complexity.lean | 5 + .../QueryReplayAssignments.lean | 69 ++++++++++++ .../QueryReplayAssignmentsCorrect.lean | 49 +++++++++ .../TreeEval/Ary/Packed/AssignmentLabels.lean | 34 ++++++ .../Ary/Packed/AssignmentLabelsCorrect.lean | 102 ++++++++++++++++++ .../Ary/Packed/AssignmentLabelsSpace.lean | 57 ++++++++++ .../Turing/MultiTape/AssignmentLabels.lean | 52 +++++++++ 7 files changed, 368 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAssignments.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAssignmentsCorrect.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/AssignmentLabels.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/AssignmentLabelsCorrect.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/AssignmentLabelsSpace.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/AssignmentLabels.lean diff --git a/Complexity.lean b/Complexity.lean index 9886343b46..5578a23498 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1698,6 +1698,8 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeEncode public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplay public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAccess +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAssignments +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAssignmentsCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayBounds public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayGate public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayGateCorrect @@ -2347,6 +2349,9 @@ public import Complexity.Computability.TreeEval.Ary.IterativeCorrect public import Complexity.Computability.TreeEval.Ary.IterativeEncoding public import Complexity.Computability.TreeEval.Ary.IterativePacked public import Complexity.Computability.TreeEval.Ary.Packed +public import Complexity.Computability.TreeEval.Ary.Packed.AssignmentLabels +public import Complexity.Computability.TreeEval.Ary.Packed.AssignmentLabelsCorrect +public import Complexity.Computability.TreeEval.Ary.Packed.AssignmentLabelsSpace public import Complexity.Computability.TreeEval.Ary.Packed.Executable public import Complexity.Computability.TreeEval.Ary.Packed.GateInterpolation public import Complexity.Computability.TreeEval.Ary.Packed.GateInterpolationSpace diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAssignments.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAssignments.lean new file mode 100644 index 0000000000..92301ec58f --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAssignments.lean @@ -0,0 +1,69 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed.AssignmentLabelsSpace +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProviderLabels +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Pi + +/-! +# Native replay labels from packed assignments + +The Boolean block gate has one preceding-control child followed by three providers for each +work tape. These words are sliced from the actual binary interpolation counter, padded using +its explicit loop limit. No truth table or interval-index list is constructed. The number of +children is fixed by the simulated machine; stride and label width remain runtime inputs. + +Acquiring these words does not assert they are successful block labels: the checked provider +parser still validates every word, including words at unused ports. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Assignments + +open MultiTapeTM MultiTapeTM.ComputableInSpace +open Cslib.TreeEval.Ary.Packed + +/-- Explicit assignment limit, counter, padded child stride and actual label width. -/ +abbrev Args := ℕ × ℕ × ℕ × ℕ + +/-- Every binary assignment register is charged by its actual encoding. -/ +@[expose] noncomputable def encArgs : Args ↪ List Bool := + encPair encNat (encPair encNat (encPair encNat encNat)) + +/-- Previous label followed by the fixed family of work-tape provider labels. -/ +abbrev Words (k : ℕ) := List Bool × Providers.Labels.ProviderWords k + +/-- Actual extracted words, in the checked provider parser's encoding. -/ +@[expose] noncomputable def encWords (k : ℕ) : Words k ↪ List Bool := + encPair encBits Providers.Labels.encProviderWords + +/-- The first child is the preceding control; subsequent children are ordered by work tape, +then by the three neighboring blocks. -/ +@[expose] def labels (k : ℕ) : Args → Words k := fun (limit, counter, stride, width) => + (AssignmentLabels.label (limit, counter, stride, width, 0), fun h δ => + AssignmentLabels.label (limit, counter, stride, width, 1 + 3 * h.val + δ.val)) + +/-- Label acquisition is native and linear in the actual binary assignment storage. The +constant depends only on the fixed number of simulated work tapes. -/ +theorem labels_inSpace (k : ℕ) : + ComputableInSpace (labels k) encArgs (encWords k) (fun a => (encArgs a).length) := by + have hlimit := fst encNat (encPair encNat (encPair encNat encNat)) + have hrest := snd encNat (encPair encNat (encPair encNat encNat)) + have hcounter := hrest.fst' + have hstride := hrest.snd'.fst' + have hwidth := hrest.snd'.snd' + have hlabel (child : ℕ) := budget_comp_linear AssignmentLabels.label_bits_inSpace + (budget_prod hlimit (budget_prod hcounter (budget_prod hstride + (budget_prod hwidth (budget_const (encB := encNat) child (.refl _)))))) + have hproviders := pi_linear fun h : Fin k => pi_linear fun δ : Fin 3 => + hlabel (1 + 3 * h.val + δ.val) + exact budget_prod (hlabel 0) hproviders + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Assignments diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAssignmentsCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAssignmentsCorrect.lean new file mode 100644 index 0000000000..32d1f5026c --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayAssignmentsCorrect.lean @@ -0,0 +1,49 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAssignments +public import Complexity.Computability.TreeEval.Ary.Packed.AssignmentLabelsCorrect + +/-! +# Packed assignment labels in block-gate child order + +The native assignment reader reconstructs the same previous/provider words as the Boolean +block gate receives after unpacking its row-major interpolation assignment. The preceding +control is child zero; the provider at tape `h` and port `δ` is the successor of the product +index `(h, δ)`. All child words are retained for canonical checking, including unused providers. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Assignments + +open Cslib.BitString Cslib.TreeEval.Ary.Packed + +/-- The native word family is exactly the packed gate decoder's child family, reordered into +previous control and work-tape providers as in the existing interval gate. -/ +theorem labels_eq_unpackBits {k r m b : ℕ} (hb : b ≤ r * m) (hm : 0 < m) + (counter : ℕ) : + let words := fun child : Fin (k * 3 + 1) => + List.ofFn (unpackBits hb (blocksEquiv (fun q : Fin ((k * 3 + 1) * r) => + (⟨gridDigit (2 ^ m) counter q, Nat.mod_lt _ (by positivity)⟩ : Fin (2 ^ m))) child)) + labels k ((2 ^ m) ^ ((k * 3 + 1) * r), counter, r * m, b) = + (words 0, fun h δ => words (Fin.succ (finProdFinEquiv (h, δ)))) := by + apply Prod.ext + · exact AssignmentLabels.label_eq_ofFn_unpackBits hb hm counter (0 : Fin (k * 3 + 1)) + · funext h δ + have hindex : 1 + 3 * h.val + δ.val = + (Fin.succ (finProdFinEquiv (h, δ)) : Fin (k * 3 + 1)).val := by + simp only [Fin.val_succ, finProdFinEquiv_apply_val] + omega + change AssignmentLabels.label (_, _, _, _, 1 + 3 * h.val + δ.val) = _ + rw [hindex] + exact AssignmentLabels.label_eq_ofFn_unpackBits hb hm counter + (Fin.succ (finProdFinEquiv (h, δ)) : Fin (k * 3 + 1)) + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Assignments diff --git a/Complexity/Computability/TreeEval/Ary/Packed/AssignmentLabels.lean b/Complexity/Computability/TreeEval/Ary/Packed/AssignmentLabels.lean new file mode 100644 index 0000000000..00b4c8cf6e --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/AssignmentLabels.lean @@ -0,0 +1,34 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeEncode + +/-! +# Reading a child label from a packed assignment counter + +The interpolation caller supplies its actual binary loop limit and assignment counter. The +limit's binary size pays the zero-padded assignment word. A child label is a stored slice of that +word, with runtime stride, label width and child index. A slice never exceeds its stored source +word; a large binary width cannot allocate its numeric magnitude. No runtime power is +constructed here. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed.AssignmentLabels + +/-- Actual interpolation limit, assignment counter, padded child stride, label width and child. -/ +abbrev Args := ℕ × ℕ × ℕ × ℕ × ℕ + +/-- Read one label from the actual counter's padded binary word. -/ +@[expose] def label : Args → List Bool := fun (limit, counter, stride, width, child) => + ((Turing.MultiTapePTM.BlockComputation.Encoding.NativeEncode.word limit counter).drop + (child * stride)).take width + +end Cslib.TreeEval.Ary.Packed.AssignmentLabels diff --git a/Complexity/Computability/TreeEval/Ary/Packed/AssignmentLabelsCorrect.lean b/Complexity/Computability/TreeEval/Ary/Packed/AssignmentLabelsCorrect.lean new file mode 100644 index 0000000000..190b17ee4c --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/AssignmentLabelsCorrect.lean @@ -0,0 +1,102 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed.AssignmentLabels +public import Complexity.Computability.TreeEval.Ary.Packed.Executable +public import Complexity.Foundations.Data.BitString.Binary +import Complexity.Foundations.Data.List.Lookup +import Mathlib.Data.Nat.Size + +/-! +# Packed assignment labels agree with the existing gate decoder + +The binary slices have their full label width whenever the supplied interpolation limit pays +the requested coordinates. On the actual packed grid, a child's slice is exactly the word +produced by `unpackBits` from the existing row-major `gridDigit` assignment. Powers occur only +in semantic specifications; the runtime reader uses the explicit limit's binary representation. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed.AssignmentLabels + +open Cslib.BitString Turing.MultiTapePTM.BlockComputation.Encoding + +/-- An in-capacity label has its full requested width. -/ +theorem length_label (limit counter stride width child : ℕ) + (hcapacity : child * stride + width ≤ limit.size) : + (label (limit, counter, stride, width, child)).length = width := by + simp only [label, NativeEncode.word, List.length_take, List.length_drop, List.length_ofFn] + omega + +/-- Reading an in-range label coordinate reads the corresponding assignment counter bit. -/ +theorem getD_label (limit counter stride width child j : ℕ) + (hcapacity : child * stride + width ≤ limit.size) (hj : j < width) : + (label (limit, counter, stride, width, child)).getD j false = + counter.testBit (child * stride + j) := by + rw [label, List.getD_take_drop _ _ hj] + exact List.getD_ofFn_of_lt _ (by omega) _ + +/-- The actual packed interpolation limit has room for every child's Boolean label. -/ +theorem capacity {d r m b : ℕ} (hb : b ≤ r * m) (child : Fin d) : + child.val * (r * m) + b ≤ ((2 ^ m) ^ (d * r)).size := by + rw [← pow_mul, Nat.size_pow] + have hc := Nat.mul_le_mul_right (r * m) (Nat.succ_le_of_lt child.isLt) + rw [Nat.succ_mul] at hc + have heq : d * (r * m) = m * (d * r) := by ac_rfl + omega + +/-- A flattened grid coordinate decodes to its consecutive counter bits. -/ +theorem unpackBits_gridDigit {d r m b : ℕ} (hb : b ≤ r * m) (hm : 0 < m) + (counter : ℕ) (child : Fin d) (j : Fin b) : + unpackBits hb (blocksEquiv (fun q : Fin (d * r) => + (⟨gridDigit (2 ^ m) counter q, Nat.mod_lt _ (by positivity)⟩ : Fin (2 ^ m))) child) j = + counter.testBit (child.val * (r * m) + j.val) := by + have hj : j.val < r * m := j.isLt.trans_le hb + have hdiv : j.val / m < r := (Nat.div_lt_iff_lt_mul hm).mpr hj + change (equivFin m).symm + ((blocksEquiv (fun q : Fin (d * r) => + (⟨gridDigit (2 ^ m) counter q, Nat.mod_lt _ (by positivity)⟩ : Fin (2 ^ m))) child) + ⟨j.val / m, hdiv⟩) ⟨j.val % m, Nat.mod_lt _ hm⟩ = _ + rw [equivFin_symm_apply] + simp only [blocksEquiv, Equiv.coe_fn_mk, block, gridDigit, finProdFinEquiv_apply_val] + rw [Nat.mul_comm r child.val, Nat.add_comm (j.val / m) (child.val * r)] + rw [Nat.testBit_mod_two_pow] + simp only [Nat.mod_lt _ hm, decide_true, Bool.true_and] + rw [← pow_mul, Nat.testBit_div_two_pow] + congr 1 + simp only [Nat.mul_add] + have heq : m * (child.val * r) = child.val * (r * m) := by ac_rfl + rw [heq] + have hquot := Nat.mod_add_div j.val m + omega + +/-- The native slice is exactly the existing unpacked child word on every grid assignment. -/ +theorem getD_label_eq_unpackBits {d r m b : ℕ} (hb : b ≤ r * m) (hm : 0 < m) + (counter : ℕ) (child : Fin d) (j : Fin b) : + (label ((2 ^ m) ^ (d * r), counter, r * m, b, child)).getD j false = + unpackBits hb (blocksEquiv (fun q : Fin (d * r) => + (⟨gridDigit (2 ^ m) counter q, Nat.mod_lt _ (by positivity)⟩ : Fin (2 ^ m))) child) j := by + rw [getD_label _ _ _ _ _ _ (capacity hb child) j.isLt, unpackBits_gridDigit hb hm] + +/-- Whole native label words agree with the packed gate decoder, including zero padding. -/ +theorem label_eq_ofFn_unpackBits {d r m b : ℕ} (hb : b ≤ r * m) (hm : 0 < m) + (counter : ℕ) (child : Fin d) : + label ((2 ^ m) ^ (d * r), counter, r * m, b, child) = + List.ofFn (unpackBits hb (blocksEquiv (fun q : Fin (d * r) => + (⟨gridDigit (2 ^ m) counter q, Nat.mod_lt _ (by positivity)⟩ : Fin (2 ^ m))) child)) := by + apply List.ext_getElem + · simp only [length_label _ _ _ _ _ (capacity hb child), List.length_ofFn] + · intro j hj hj' + rw [List.getElem_ofFn] + rw [← List.getD_eq_getElem _ (d := false) hj] + exact getD_label_eq_unpackBits hb hm counter child + ⟨j, by simpa only [List.length_ofFn] using hj'⟩ + +end Cslib.TreeEval.Ary.Packed.AssignmentLabels diff --git a/Complexity/Computability/TreeEval/Ary/Packed/AssignmentLabelsSpace.lean b/Complexity/Computability/TreeEval/Ary/Packed/AssignmentLabelsSpace.lean new file mode 100644 index 0000000000..fb99f2307f --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/AssignmentLabelsSpace.lean @@ -0,0 +1,57 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed.AssignmentLabels +public import + Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeEncode +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.ListSlice + +/-! +# Native assignment-label extraction + +The explicit assignment limit pays the binary word from which each child label is sliced. +The counter, stride, requested label width and child index are also runtime binary data. Taking +and dropping prefixes cannot allocate beyond that paid word, even for malformed large widths. +The raw-bit certificate composes directly with the canonical block-label parser. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed.AssignmentLabels + +open Turing.MultiTapeTM Turing.MultiTapeTM.ComputableInSpace +open Turing.MultiTapePTM.BlockComputation.Encoding + +/-- Actual binary assignment limit, counter, stride, requested width and child index. -/ +@[expose] noncomputable def encArgs : Args ↪ List Bool := + encPair encNat (encPair encNat (encPair encNat (encPair encNat encNat))) + +/-- Extract a child label in native space linear in its actual encoded arguments. The binary +limit pays the source word; binary widths alone cannot allocate a list of their numeric size. -/ +theorem label_inSpace : ComputableInSpace label encArgs (encList encBool) + (fun a => (encArgs a).length) := by + have hlimit := fst encNat (encPair encNat (encPair encNat (encPair encNat encNat))) + have hrest := snd encNat (encPair encNat (encPair encNat (encPair encNat encNat))) + have hcounter := hrest.fst' + have hstride := hrest.snd'.fst' + have hwidth := hrest.snd'.snd'.fst' + have hchild := hrest.snd'.snd'.snd' + have hword := budget_comp_linear NativeEncode.word_inSpace (budget_prod hlimit hcounter) + have hstart := budget_natMul hchild hstride + have hdrop := budget_comp_linear (listDrop encBool) (budget_prod hword hstart) + have htake := budget_comp_linear (listTake encBool) (budget_prod hdrop hwidth) + exact htake.of_enc_eq fun ⟨limit, counter, stride, width, child⟩ => rfl + +/-- The extracted label is available as raw bits for canonical block-label checking. -/ +theorem label_bits_inSpace : ComputableInSpace label encArgs encBits + (fun a => (encArgs a).length) := + (budget_comp_linear (f := label) (g := fun l : List Bool => l) + listToBits label_inSpace).of_enc_eq fun _ => rfl + +end Cslib.TreeEval.Ary.Packed.AssignmentLabels diff --git a/ComplexityTests/Machines/Turing/MultiTape/AssignmentLabels.lean b/ComplexityTests/Machines/Turing/MultiTape/AssignmentLabels.lean new file mode 100644 index 0000000000..5327aabcfe --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/AssignmentLabels.lean @@ -0,0 +1,52 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of packed assignment label acquisition) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAssignmentsCorrect +public import ComplexityTests.Machines.Turing.MultiTape.NativeBlockCodec + +/-! +# Packed assignment label regressions + +Child slices omit per-child padding, retain leading zero bits and use the same child order as +the block gate. Malformed binary widths cannot allocate their requested numeric magnitude. +The final examples pass extracted words through the existing canonical block parser. +-/ + +public section + +namespace ComplexityTests.AssignmentLabels + +open Cslib Cslib.TreeEval.Ary.Packed Turing.MultiTapeTM +open Turing.MultiTapePTM.BlockComputation +open QueryReplay +open Turing.MultiTapeNTM.ChoiceTable +open NativeBlockCodec + +example : AssignmentLabels.label (65536, 165, 4, 3, 0) = [true, false, true] := by decide +example : AssignmentLabels.label (65536, 165, 4, 3, 1) = [false, true, false] := by decide +example : AssignmentLabels.label (65536, 0, 4, 3, 3) = [false, false, false] := by decide +example : AssignmentLabels.label (1, 0, 1000000000, 1000000000, 0) = [false] := by decide +example : AssignmentLabels.label (1, 0, 1000000000, 1000000000, 1) = [] := by decide +example : AssignmentLabels.label (65536, 165, 4, 0, 0) = [] := rfl + +example : (Assignments.labels 1 (65536, 165, 4, 3)).1 = [true, false, true] := by decide +example : (Assignments.labels 1 (65536, 165, 4, 3)).2 0 0 = [false, true, false] := by decide +example : (Assignments.labels 1 (65536, 165, 4, 3)).2 0 2 = [false, false, false] := by decide + +-- Four canonical labels, each with one padding bit, in the actual row-major assignment. +def counter : ℕ := ofBits ((List.replicate 4 (good ++ [false])).flatten) +def words := Assignments.labels 1 (2 ^ 56, counter, 14, 13) + +example : words.1 = good := by decide +example : words.2 0 2 = good := by decide +example : Encoding.NativeChecked.parse code (1, words.1) = some raw := by decide +example : (Providers.Labels.combine code + (1, (fun _ _ => none), words.1, words.2)).isSome = true := by decide + +end ComplexityTests.AssignmentLabels From 93634866261c8e7df2e469fda9a3b5a6d7e20e4b Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 02:59:34 +0000 Subject: [PATCH 24/36] feat(hpv): assemble checked native block gate supplier --- Complexity.lean | 7 + .../QueryReplayCheckedGate.lean | 155 ++++++++++++++++++ .../QueryReplayProviderLabelsCorrect.lean | 93 +++++++++++ .../QueryReplaySupplierBounds.lean | 94 +++++++++++ .../QueryReplaySupplierCorrect.lean | 109 ++++++++++++ .../QueryReplaySupplierInit.lean | 121 ++++++++++++++ .../QueryReplaySupplierTotal.lean | 137 ++++++++++++++++ .../BlockComputation/ScheduleReadRow.lean | 104 ++++++++++++ .../Turing/MultiTape/QueryReplaySupplier.lean | 90 ++++++++++ 9 files changed, 910 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayCheckedGate.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayProviderLabelsCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierBounds.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierInit.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierTotal.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadRow.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/QueryReplaySupplier.lean diff --git a/Complexity.lean b/Complexity.lean index 5578a23498..9cbe82e49d 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1701,6 +1701,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAssignments public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAssignmentsCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayBounds +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayCheckedGate public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayGate public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayGateCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayLabelLoad @@ -1709,12 +1710,18 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayMotion public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayMotionCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProviderLabels +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProviderLabelsCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProviders public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProvidersCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySpace +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierBounds +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierInit +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierTotal public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleRead public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadPort +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadRow public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadSearch public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadSearchSpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadSpace diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayCheckedGate.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayCheckedGate.lean new file mode 100644 index 0000000000..bd85877213 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayCheckedGate.lean @@ -0,0 +1,155 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayGate +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Sum +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.Call +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Sum + +/-! +# Total checked replay with paid rejection output + +A rejected call retains its actual previous-label word and halts immediately. Its final word +zeros that stored buffer, so rejection cannot allocate a width-sized result from binary metadata. +An accepted call delegates to the native local replay gate on the same original input. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.CheckedGate + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +variable {k s : ℕ} {Q : Type} + +/-- A paid rejection buffer or an active local replay. -/ +abbrev State (k : ℕ) (Q : Type) := List Bool ⊕ Gate.State k Q + +/-- Rejected calls halt without querying the input; accepted calls perform the existing replay. -/ +@[expose] noncomputable def machine (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + QueryMachine (State k Q) where + init := .inl [] + query := Sum.elim (fun _ => 0) (Gate.machine M e).query + next := fun (q, symbol) => q.elim (fun word => .inl word) + (fun active => .inr ((Gate.machine M e).next (active, symbol))) + emit _ := [] + halted := Sum.elim (fun _ => true) (Gate.machine M e).halted + finish := Sum.elim (List.map (fun _ => false)) (Gate.machine M e).finish + +variable [Finite Q] + +/-- The rejection word is stored explicitly, rather than replaced by a binary length. -/ +@[expose] noncomputable def encState : State k Q ↪ List Bool := + encSum encBits (Gate.encState (k := k) (Q := Q)) + +private theorem blankFold (l out : List Bool) : + l.foldl (fun acc _ => acc ++ [false]) out = out ++ l.map (fun _ => false) := by + induction l generalizing out with + | nil => simp + | cons bit l ih => simp [List.foldl_cons, ih, List.append_assoc] + +/-- Zero an actual bit word in linear native space. -/ +theorem blank_inSpace : ComputableInSpace (List.map (fun _ : Bool => false)) encBits encBits + (fun word => (encBits word).length) := by + have hid : ComputableInSpace (fun word : List Bool => word) encBits encBits + (fun word => (encBits word).length) := of_inO (ComputableInO.of_eq fun _ => rfl) + have hinit := budget_const (encB := encBits) [] + (show (fun word => (encBits word).length) ≼ fun word => (encBits word).length from .refl _) + have hstep : ComputableInSpace (fun q : List Bool × Bool => q.1 ++ [false]) + (encPair encBits encBool) encBits (fun q => (encPair encBits encBool q).length) := by + exact ((fst encBits encBool).append + (budget_const (encB := encBits) [false] (.refl _)) (fun _ => rfl)).weaken + (.add (.refl _) (.refl _)) + have hstate (word : List Bool) (i : ℕ) (hi : i ≤ word.length) : + (encBits ((word.take i).foldl (fun acc _ => acc ++ [false]) [])).length ≤ + 4 * ((encBits word).length + 1) := by + simp only [blankFold, List.nil_append, encBits_apply, List.length_map, List.length_take] + omega + have hspace (word : List Bool) (i : ℕ) (hi : i < word.length) : + (encPair encBits encBool + ((word.take i).foldl (fun acc _ => acc ++ [false]) [], word[i])).length ≤ + 4 * ((encBits word).length + 1) := by + simp only [blankFold, List.nil_append, length_encPair, encBits_apply, encBool_apply, + List.length_map, List.length_take, List.length_singleton] + omega + have h := foldl hid hinit hstep hspace hstate + exact h.weaken (.of_le_linear 16 16 fun _ => by omega) |>.of_enc_eq fun word => by + simp only [blankFold, List.nil_append] + +/-- Queries preserve the active gate's original-input interface. -/ +theorem machine_query_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).query encState encNat (fun a => (encState a).length) := + sumElim (budget_const (encIn := encBits) (encB := encNat) 0 (.refl _)) + (Gate.machine_query_inSpace M e) + +/-- A transition preserves its sum tag and charges the complete actual active state. -/ +theorem machine_next_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).next (encPair encState (encOption encBool)) encState + (fun a => (encPair encState (encOption encBool) a).length) := by + have hleft := budget_comp_linear + (of_inO (ComputableInO.inl encBits (Gate.encState (k := k) (Q := Q)))) + (fst encBits (encOption encBool)) + have hright := budget_comp_linear + (of_inO (ComputableInO.inr encBits (Gate.encState (k := k) (Q := Q)))) + (Gate.machine_next_inSpace M e) + exact sumElimFst hleft hright + +/-- No branch emits intermediate output. -/ +theorem machine_emit_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).emit (encPair encState (encOption encBool)) encBits + (fun a => (encPair encState (encOption encBool) a).length) := + budget_const [] (.refl _) + +/-- Rejection is immediately halted; accepted calls use the replay counter. -/ +theorem machine_halted_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).halted encState encBool (fun a => (encState a).length) := + sumElim (budget_const (encIn := encBits) (encB := encBool) true (.refl _)) + (Gate.machine_halted_inSpace M e) + +/-- Both final branches are paid by their actual stored words/windows. -/ +theorem machine_finish_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).finish encState encBits (fun a => (encState a).length) := + sumElim blank_inSpace (Gate.machine_finish_inSpace M e) + +omit [Finite Q] in +/-- Rejection serializes exactly a FAIL word of the stored buffer's width. -/ +theorem finish_inl (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (word : List Bool) : + (machine M e).finish (.inl word) = List.replicate word.length false := by + simp [machine] + +omit [Finite Q] in +/-- A successful branch step is precisely the underlying local gate step. -/ +theorem step_inr (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (q : Gate.State k Q) : + (machine M e).step w (.inr q) = .inr ((Gate.machine M e).step w q) := by + by_cases h : (Gate.machine M e).halted q = true <;> + simp [QueryMachine.step, machine, QueryMachine.read, h] + +omit [Finite Q] in +/-- Every accepted replay prefix lifts without changing the underlying state. -/ +theorem runFrom_inr (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (q : Gate.State k Q) (n : ℕ) : + (machine M e).runFrom w (.inr q) n = .inr ((Gate.machine M e).runFrom w q n) := by + induction n with + | zero => rfl + | succ n ih => rw [QueryMachine.runFrom_succ, ih, step_inr, QueryMachine.runFrom_succ] + +omit [Finite Q] in +/-- A rejected call remains at its paid rejection buffer at every clock. -/ +theorem runFrom_inl (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (word : List Bool) (n : ℕ) : + (machine M e).runFrom w (.inl word) n = .inl word := by + induction n with + | zero => rfl + | succ n ih => + rw [QueryMachine.runFrom_succ, ih] + simp [QueryMachine.step, machine] + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.CheckedGate diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayProviderLabelsCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayProviderLabelsCorrect.lean new file mode 100644 index 0000000000..d462edd2c9 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayProviderLabelsCorrect.lean @@ -0,0 +1,93 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProvidersCorrect + +/-! +# Invariants and rejection of checked provider labels + +A successful checked combination has canonical explicit replay buffers. Rejection occurs +exactly when the previous label or at least one provider label fails canonical decoding. The +criterion checks every provider, including labels for windows absent from the current schedule. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Providers.Labels + +variable {k s : ℕ} {Q : Type} + +/-- Every successful checked combination supplies the canonical buffers needed by native replay. -/ +theorem canonical_of_combine_some (e : Q ≃ Fin s) (b : ℕ) (ports : PortMap k) + (previousWord : List Bool) (providerWords : ProviderWords k) (raw : RawResult k Q) + (hcombined : combine e (b, ports, previousWord, providerWords) = some raw) : + Load.CanonicalWindows b raw := by + classical + let previous := Encoding.NativeChecked.parse (k := k) e (b, previousWord) + let providers := fun h δ => Encoding.NativeChecked.parse (k := k) e (b, providerWords h δ) + let empty : RawResult k Q := (none, fun _ => 0, .empty, fun _ => []) + change (if previous.isSome && decide (∀ h δ, (providers h δ).isSome = true) then + some (Providers.combine (b, ports, previous.getD empty, + fun h δ => (providers h δ).getD empty)) else none) = some raw at hcombined + split at hcombined + next hguard => + have hg : previous.isSome = true ∧ ∀ h δ, (providers h δ).isSome = true := by + simpa only [Bool.and_eq_true, decide_eq_true_eq] using hguard + have hp : Load.CanonicalWindows b (previous.getD empty) := by + cases hparse : previous with + | none => simp [hparse] at hg + | some value => + simpa only [hparse, Option.getD_some] using + Load.canonical_of_checked_some e b previousWord value hparse + have hps (h : Fin k) (δ : Fin 3) : + Load.CanonicalWindows b ((providers h δ).getD empty) := by + cases hparse : providers h δ with + | none => + have hsome := hg.2 h δ + simp [hparse] at hsome + | some value => + simpa only [hparse, Option.getD_some] using + Load.canonical_of_checked_some e b (providerWords h δ) value hparse + have hraw := Option.some.inj hcombined + rw [← hraw] + exact Providers.canonical_combine b ports (previous.getD empty) + (fun h δ => (providers h δ).getD empty) hp hps + next => contradiction + +/-- Checked combination rejects precisely the failed previous/provider labels, even when the +port map ignores that provider's window. No default value turns failure into success. -/ +theorem combine_eq_none_iff (e : Q ≃ Fin s) (b : ℕ) (ports : PortMap k) + (previousWord : List Bool) (providerWords : ProviderWords k) : + combine e (b, ports, previousWord, providerWords) = none ↔ + Encoding.NativeChecked.parse (k := k) e (b, previousWord) = none ∨ + ∃ h δ, Encoding.NativeChecked.parse (k := k) e (b, providerWords h δ) = none := by + classical + cases hprevious : Encoding.NativeChecked.parse (k := k) e (b, previousWord) with + | none => simp [combine, hprevious] + | some previous => + simp only [Option.some_ne_none, false_or] + constructor + · intro hcombined + by_contra hfailed + have hall : ∀ h δ, + (Encoding.NativeChecked.parse (k := k) e (b, providerWords h δ)).isSome = true := by + intro h δ + cases hparse : Encoding.NativeChecked.parse (k := k) e (b, providerWords h δ) with + | none => exact False.elim (hfailed ⟨h, δ, hparse⟩) + | some value => rfl + simp [combine, hprevious, hall] at hcombined + · rintro ⟨h, δ, hparse⟩ + have hfailed : ¬∀ h δ, + (Encoding.NativeChecked.parse (k := k) e (b, providerWords h δ)).isSome = true := by + intro hall + have hsome := hall h δ + simp [hparse] at hsome + simp [combine, hprevious, hfailed] + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Providers.Labels diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierBounds.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierBounds.lean new file mode 100644 index 0000000000..010d5a69ed --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierBounds.lean @@ -0,0 +1,94 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProviderLabelsCorrect + +/-! +# Every-clock storage bound for the checked gate supplier + +Rejected initialization retains only a slice of one actual assignment word. Accepted +initialization has canonical three-block windows and hence satisfies the native replay bound +at every clock. Scheduling context is charged explicitly; the original input remains on the +query input. The result holds for every numeric schedule and interval, without a promise that +metadata denotes a correct schedule. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Supplier + +open MultiTapeTM MultiTapeNTM.ChoiceTable + +variable {k s : ℕ} {Q : Type} [Finite Q] + +/-- The actual previous-label slice never exceeds the requested label width. -/ +theorem length_previous_label_le (limit counter stride b : ℕ) : + ((Assignments.labels k (limit, counter, stride, Encoding.width k s b)).1).length ≤ + Encoding.width k s b := by + change (Cslib.TreeEval.Ary.Packed.AssignmentLabels.label + (limit, counter, stride, Encoding.width k s b, 0)).length ≤ _ + simp only [Cslib.TreeEval.Ary.Packed.AssignmentLabels.label, List.length_take] + exact Nat.min_le_left _ _ + +/-- Native supplier storage is bounded at every clock, including rejected assignments and +clocks after halting. The extra label-width term pays for the rejection branch. -/ +theorem length_runFrom_prepare_le (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (b : ℕ) [NeZero b] (σ i limit counter stride t : ℕ) : + let (positive, negative) := ScheduleRead.origin k 0 (σ, i) + (CheckedGate.encState ((CheckedGate.machine M e).runFrom w + (prepare e (w.length, b, σ, i, limit, counter, stride)) t)).length ≤ + (Encoding.width k s b + 1) + + (2 * (Gate.encContext + (positive, negative, ScheduleRead.fullrow k (σ, i))).length + 2 + + 1024 * (k + 1) * (b + Nat.log2 w.length + Nat.card (Option Q) + + Nat.card OutputTrack + 1)) := by + let words := Assignments.labels k (limit, counter, stride, Encoding.width k s b) + let ports := ScheduleRead.providerMap k (σ, i) + rcases horigin : ScheduleRead.origin k 0 (σ, i) with ⟨positive, negative⟩ + dsimp only + cases hc : Providers.Labels.combine e (b, ports, words.1, words.2) with + | none => + rw [prepare_eq_of_combined_none e w.length b σ i limit counter stride hc, + CheckedGate.runFrom_inl] + simp only [CheckedGate.encState, encSum_inl, List.length_cons, encBits_apply] + have hw := length_previous_label_le (k := k) (s := s) limit counter stride b + omega + | some raw => + have hcanonical := Providers.Labels.canonical_of_combine_some e b ports + words.1 words.2 raw hc + rw [prepare_eq_of_combined_some e w.length b σ i limit counter stride raw hc, + horigin, CheckedGate.runFrom_inr] + simp only [CheckedGate.encState, encSum_inr, List.length_cons] + have hbound := Gate.length_runFrom_load_le M e w b positive negative + (ScheduleRead.fullrow k (σ, i)) raw hcanonical + (fun _ => (positive : ℤ) - negative) rfl t + omega + +omit [Finite Q] in +/-- Every prepared local call is halted by its requested replay duration. Rejected labels +halt immediately, while successful initialization uses the existing replay counter. -/ +theorem halted_runFrom_prepare (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (N b σ i limit counter stride : ℕ) : + (CheckedGate.machine M e).halted ((CheckedGate.machine M e).runFrom w + (prepare e (N, b, σ, i, limit, counter, stride)) b) = true := by + cases hc : Providers.Labels.combine e (b, ScheduleRead.providerMap k (σ, i), + (Assignments.labels k (limit, counter, stride, Encoding.width k s b)).1, + (Assignments.labels k (limit, counter, stride, Encoding.width k s b)).2) with + | none => + rw [prepare_eq_of_combined_none e N b σ i limit counter stride hc, + CheckedGate.runFrom_inl] + rfl + | some raw => + rw [prepare_eq_of_combined_some e N b σ i limit counter stride raw hc] + rcases ScheduleRead.origin k 0 (σ, i) with ⟨positive, negative⟩ + rw [CheckedGate.runFrom_inr] + exact Gate.halted_runFrom_load M e w N b positive negative _ raw + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Supplier diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierCorrect.lean new file mode 100644 index 0000000000..b8a03d3840 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierCorrect.lean @@ -0,0 +1,109 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierInit +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayGateCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProvidersCorrect + +/-! +# Correctness of checked local gate preparation + +Successful canonical parsing of every assignment label initializes exactly the functional +block gate's entering control/window and schedule metadata. Running that prepared supplier for +one block emits precisely the existing deterministic block gate's encoded result, including +motion-check FAIL. Rejected parsing selects an explicit paid rejection buffer. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Supplier + +open MultiTapeTM + +variable {k s : ℕ} {Q : Type} + +/-- A rejected label combination retains the actual previous-label word. -/ +theorem prepare_eq_of_combined_none (e : Q ≃ Fin s) (N b σ i limit counter stride : ℕ) + (hcombined : Providers.Labels.combine e (b, ScheduleRead.providerMap k (σ, i), + (Assignments.labels k (limit, counter, stride, Encoding.width k s b)).1, + (Assignments.labels k (limit, counter, stride, Encoding.width k s b)).2) = none) : + prepare (k := k) e (N, b, σ, i, limit, counter, stride) = + .inl (Assignments.labels k (limit, counter, stride, Encoding.width k s b)).1 := by + simp only [prepare, hcombined] + +/-- A successful combination supplies the exact native replay state with computed scheduling +metadata; no prepared semantic window is supplied by an oracle. -/ +theorem prepare_eq_of_combined_some (e : Q ≃ Fin s) (N b σ i limit counter stride : ℕ) + (raw : RawResult k Q) + (hcombined : Providers.Labels.combine e (b, ScheduleRead.providerMap k (σ, i), + (Assignments.labels k (limit, counter, stride, Encoding.width k s b)).1, + (Assignments.labels k (limit, counter, stride, Encoding.width k s b)).2) = some raw) : + prepare (k := k) e (N, b, σ, i, limit, counter, stride) = + let (positive, negative) := ScheduleRead.origin k 0 (σ, i) + .inr ((positive, negative, ScheduleRead.fullrow k (σ, i)), + Load.load (N, b, (positive, negative), raw)) := by + simp only [prepare, hcombined] + +/-- When the previous and every provider label pass canonical parsing, the prepared supplier +emits exactly the encoded existing block gate. Failed head-motion validation still emits FAIL. +The premise includes providers whose windows are unused by the current schedule. -/ +theorem finish_prepare_of_parses {q : ℕ} (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (b : ℕ) [NeZero b] (σ : ℕ) (i : Fin q) + (limit counter stride : ℕ) (previous : RawResult k Q) + (providers : Providers.ProviderValues k Q) + (hprevious : Encoding.NativeChecked.parse e + (b, (Assignments.labels k (limit, counter, stride, Encoding.width k s b)).1) = + some previous) + (hproviders : ∀ h δ, Encoding.NativeChecked.parse e + (b, (Assignments.labels k (limit, counter, stride, Encoding.width k s b)).2 h δ) = + some (providers h δ)) : + (CheckedGate.machine M e).finish ((CheckedGate.machine M e).runFrom w + (prepare e (w.length, b, σ, i, limit, counter, stride)) b) = + List.ofFn (Encoding.encode e + (gate (MultiTapePTM.ofDeterministic M) w b + (scheduleOfNat k q σ) i (some (previous.toView b)) + (fun h δ => some ((providers h δ).toView b)))) := by + classical + let schedule := scheduleOfNat k q σ + let raw := Providers.combine (b, Providers.portMap schedule i, previous, providers) + have hcombined : Providers.Labels.combine e (b, ScheduleRead.providerMap k (σ, i), + (Assignments.labels k (limit, counter, stride, Encoding.width k s b)).1, + (Assignments.labels k (limit, counter, stride, Encoding.width k s b)).2) = some raw := by + rw [ScheduleRead.providerMap_eq_portMap] + exact Providers.Labels.combine_of_parse e b (Providers.portMap schedule i) _ _ + previous providers hprevious hproviders + have hp := Load.canonical_of_checked_some e b _ previous hprevious + have hps (h : Fin k) (δ : Fin 3) := + Load.canonical_of_checked_some e b _ (providers h δ) (hproviders h δ) + have hcanonical : Load.CanonicalWindows b raw := + Providers.canonical_combine b (Providers.portMap schedule i) previous providers hp hps + have hview : raw.toView b = + ((previous.toView b).1, enteringWindow (MultiTapePTM.ofDeterministic M) b schedule i + (fun h δ => some ((providers h δ).toView b))) := + Providers.toView_combine_enteringWindow (MultiTapePTM.ofDeterministic M) b schedule i + previous providers hp hps + rcases horigin : ScheduleRead.origin k 0 (σ, i) with ⟨positive, negative⟩ + have hinput : scheduleBlock schedule i none = (positive : ℤ) - negative := by + have ho := ScheduleRead.origin_sub_eq_scheduleBlock (k := k) (q := q) + σ i.val i.isLt.le none + change (let (p, n) := ScheduleRead.origin k 0 (σ, i.val); + (p : ℤ) - n = scheduleBlock schedule i none) at ho + rw [horigin] at ho + exact ho.symm + rw [prepare_eq_of_combined_some e w.length b σ i limit counter stride raw hcombined, + horigin, ScheduleRead.fullrow_eq_scheduleOfNat, CheckedGate.runFrom_inr] + change (Gate.machine M e).finish ((Gate.machine M e).runFrom w + ((positive, negative, schedule i), Load.load (w.length, b, (positive, negative), raw)) b) = _ + have hf := Gate.finish_runFrom_load M e w b positive negative (schedule i) raw + hcanonical (scheduleBlock schedule i) hinput + dsimp only at hf + rw [hview] at hf + simpa [gate, MultiTapeTM.stepCoins_ofDeterministic, schedule] using hf + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Supplier diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierInit.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierInit.lean new file mode 100644 index 0000000000..6519b12ac5 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierInit.lean @@ -0,0 +1,121 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayCheckedGate +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAssignments +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayLoad +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadPort +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadRow +import Complexity.Computability.Machines.Turing.MultiTape.Internal.DegreeOptionPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Sum + +/-! +# Native initialization of the checked local gate supplier + +Initialization reads the previous/provider labels from the actual interpolation counter, +computes the schedule's provider map, input origin and expected head directions, and checks all +labels. Rejection retains an actual label word for paid FAIL output. Acceptance loads the +assembled windows into the original-input replay kernel. + +The input length is supplied as binary metadata; the caller must obtain it from the existing +original-input length scanner. The interval address is local: source/horizon handling and +implicit tree-path navigation are separate caller operations. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Supplier + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +variable {k s : ℕ} {Q : Type} [Finite Q] + +/-- Input length, block width, schedule, interval, assignment limit/counter and padded stride. -/ +abbrev Args := ℕ × ℕ × ℕ × ℕ × ℕ × ℕ × ℕ + +/-- Binary runtime metadata and the actual assignment counter/limit storage. -/ +@[expose] noncomputable def encArgs : Args ↪ List Bool := + encPair encNat (encPair encNat (encPair encNat (encPair encNat + (encPair encNat (encPair encNat encNat))))) + +/-- Initialize a total checked supplier from actual assignment and schedule data. -/ +@[expose] noncomputable def prepare (e : Q ≃ Fin s) : Args → CheckedGate.State k Q := + fun (N, b, σ, i, limit, counter, stride) => + let words := Assignments.labels k (limit, counter, stride, Encoding.width k s b) + let ports := ScheduleRead.providerMap k (σ, i) + match Providers.Labels.combine e (b, ports, words.1, words.2) with + | none => .inl words.1 + | some raw => + let (positive, negative) := ScheduleRead.origin k 0 (σ, i) + .inr ((positive, negative, ScheduleRead.fullrow k (σ, i)), + Load.load (N, b, (positive, negative), raw)) + +private theorem labelWidth_inSpace (k s : ℕ) : + ComputableInSpace (Encoding.width k s) encNat encNat (fun b => (encNat b).length) := by + have hb : ComputableInSpace (fun b : ℕ => b) encNat encNat + (fun b => (encNat b).length) := + of_inO (ComputableInO.of_eq (f := id) fun _ => rfl) + have hsize := budget_comp_linear natSize hb + have hwindow := budget_natMul (budget_const (encB := encNat) k (.refl _)) + (budget_natMul (budget_const (encB := encNat) 3 (.refl _)) + (budget_natMul hb (budget_const (encB := encNat) 2 (.refl _)))) + exact (budget_natSucc (budget_natAdd + (budget_const (encB := encNat) (s + 1).size (.refl _)) + (budget_natAdd (budget_natMul (budget_const (encB := encNat) (k + 1) (.refl _)) hsize) + (budget_natAdd (budget_const (encB := encNat) 2 (.refl _)) hwindow)))).of_enc_eq + fun _ => rfl + +/-- All preparation work uses native space linear in actual binary metadata and assignment +storage. Original input symbols remain on the query input, outside this state. -/ +theorem prepare_inSpace (e : Q ≃ Fin s) : + ComputableInSpace (prepare (k := k) e) encArgs CheckedGate.encState + (fun a => (encArgs a).length) := by + have hN := fst encNat (encPair encNat (encPair encNat (encPair encNat + (encPair encNat (encPair encNat encNat)))) ) + have hrest := snd encNat (encPair encNat (encPair encNat (encPair encNat + (encPair encNat (encPair encNat encNat)))) ) + have hb := hrest.fst' + have hσ := hrest.snd'.fst' + have hi := hrest.snd'.snd'.fst' + have hlimit := hrest.snd'.snd'.snd'.fst' + have hcounter := hrest.snd'.snd'.snd'.snd'.fst' + have hstride := hrest.snd'.snd'.snd'.snd'.snd' + have haddress := budget_prod hσ hi + have hwidth := budget_comp_linear (labelWidth_inSpace k s) hb + have hwords := budget_comp_linear (Assignments.labels_inSpace k) + (budget_prod hlimit (budget_prod hcounter (budget_prod hstride hwidth))) + have hports := budget_comp_linear (ScheduleRead.providerMap_inSpace k) haddress + have hcombined := budget_comp_linear (Providers.Labels.combine_inSpace (k := k) e) + (budget_prod hb (budget_prod hports (budget_prod hwords.fst' hwords.snd'))) + let empty : RawResult k Q := (none, fun _ => 0, .empty, fun _ => []) + have hget : ComputableInSpace (fun o : Option (RawResult k Q) => o.getD empty) + (encOption encRawResult) encRawResult (fun o => (encOption encRawResult o).length) := + of_inO (ComputableInDegree.getD (enc₁ := encRawResult) empty).inO_one + have hisSome : ComputableInSpace (fun o : Option (RawResult k Q) => o.isSome) + (encOption encRawResult) encBool (fun o => (encOption encRawResult o).length) := + of_inO (ComputableInDegree.isSome encRawResult).inO_one + have horigin := budget_comp_linear (ScheduleRead.origin_inSpace k 0) haddress + have hrow := budget_comp_linear (ScheduleRead.fullrow_inSpace k) haddress + have hload := budget_comp_linear (Load.load_inSpace (k := k) (Q := Q)) + (budget_prod hN (budget_prod hb (budget_prod horigin + (budget_comp_linear hget hcombined)))) + have hcontext := budget_prod horigin.fst' (budget_prod horigin.snd' hrow) + have hsuccess := budget_comp_linear + (of_inO (ComputableInO.inr encBits (Gate.encState (k := k) (Q := Q)))) + (budget_prod hcontext hload) + have hfailure := budget_comp_linear + (of_inO (ComputableInO.inl encBits (Gate.encState (k := k) (Q := Q)))) hwords.fst' + refine (budget_ite (budget_comp_linear hisSome hcombined) hsuccess hfailure).of_enc_eq ?_ + rintro ⟨N, b, σ, i, limit, counter, stride⟩ + cases hc : Providers.Labels.combine e (b, ScheduleRead.providerMap k (σ, i), + (Assignments.labels k (limit, counter, stride, Encoding.width k s b)).1, + (Assignments.labels k (limit, counter, stride, Encoding.width k s b)).2) <;> + simp [prepare, hc, CheckedGate.encState] + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Supplier diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierTotal.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierTotal.lean new file mode 100644 index 0000000000..c53459a70e --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierTotal.lean @@ -0,0 +1,137 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProviderLabelsCorrect +public import Complexity.Computability.TreeEval.Ary.Packed.AssignmentLabelsCorrect + +/-! +# Total local gate supplier correctness + +The native supplier computes the existing functional gate on every assignment, including failed +canonical labels and invalid motion. The explicit interpolation limit must pay the previous +label's width so that rejected calls emit the exact fixed-width FAIL code. No assumption that +all assignment labels succeed is imposed by the total theorem. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Supplier + +open MultiTapeTM Cslib.TreeEval.Ary.Packed + +variable {k s : ℕ} {Q : Type} + +/-- The existing Boolean codec's FAIL value is exactly a fixed-width zero word. -/ +theorem ofFn_encode_none (e : Q ≃ Fin s) (b : ℕ) [NeZero b] : + List.ofFn (Encoding.encode (k := k) (b := b) e none) = + List.replicate (Encoding.width k s b) false := by + have hzero : Encoding.encode (k := k) (b := b) e none = fun _ => false := by + funext j + unfold Encoding.encode + exact Fin.cases rfl (fun _ => rfl) j + rw [hzero] + simp + +/-- Rejected parsing emits the full paid FAIL word, independently of the rejection time. -/ +theorem finish_prepare_of_combined_none (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (N b σ i limit counter stride t : ℕ) + (hcapacity : Encoding.width k s b ≤ limit.size) + (hcombined : Providers.Labels.combine e (b, ScheduleRead.providerMap k (σ, i), + (Assignments.labels k (limit, counter, stride, Encoding.width k s b)).1, + (Assignments.labels k (limit, counter, stride, Encoding.width k s b)).2) = none) : + (CheckedGate.machine M e).finish ((CheckedGate.machine M e).runFrom w + (prepare e (N, b, σ, i, limit, counter, stride)) t) = + List.replicate (Encoding.width k s b) false := by + rw [prepare_eq_of_combined_none e N b σ i limit counter stride hcombined, + CheckedGate.runFrom_inl, CheckedGate.finish_inl] + change List.replicate + (AssignmentLabels.label (limit, counter, stride, Encoding.width k s b, 0)).length false = _ + rw [AssignmentLabels.length_label _ _ _ _ _ (by simpa using hcapacity)] + +/-- Failure of any checked input forces the functional gate to FAIL, even when that provider's +window is absent and its contents would otherwise be unused. -/ +theorem gate_eq_none_of_failed_parse {q : ℕ} (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (b : ℕ) (σ : Schedule k q) (i : Fin q) + (previousWord : List Bool) (providerWords : Providers.Labels.ProviderWords k) + (hfailed : Encoding.NativeChecked.parse (k := k) e (b, previousWord) = none ∨ + ∃ h δ, Encoding.NativeChecked.parse (k := k) e (b, providerWords h δ) = none) : + gate (MultiTapePTM.ofDeterministic M) w b σ i + ((Encoding.NativeChecked.parse (k := k) e (b, previousWord)).map fun raw => raw.toView b) + (fun h δ => (Encoding.NativeChecked.parse (k := k) e (b, providerWords h δ)).map + fun raw => raw.toView b) = none := by + classical + rcases hfailed with hp | ⟨h, δ, hp⟩ + · simp [gate, hp] + · have hnot : ¬∀ h' δ', + (Encoding.NativeChecked.parse (k := k) e (b, providerWords h' δ')).isSome = true := by + intro hall + have hsome := hall h δ + simp [hp] at hsome + cases hprevious : Encoding.NativeChecked.parse (k := k) e (b, previousWord) with + | none => simp [gate] + | some previous => simp [gate, hnot] + +/-- On every assignment, the complete native local supplier emits exactly the encoded existing +block gate applied to the checked decoded labels. The only capacity premise pays the previous +word used to serialize rejection; provider failure still propagates at every port. -/ +theorem finish_prepare_eq_gate {q : ℕ} (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (b : ℕ) [NeZero b] (σ : ℕ) (i : Fin q) + (limit counter stride : ℕ) (hcapacity : Encoding.width k s b ≤ limit.size) : + let words := Assignments.labels k (limit, counter, stride, Encoding.width k s b) + (CheckedGate.machine M e).finish ((CheckedGate.machine M e).runFrom w + (prepare e (w.length, b, σ, i, limit, counter, stride)) b) = + List.ofFn (Encoding.encode e (gate (MultiTapePTM.ofDeterministic M) w b + (scheduleOfNat k q σ) i + ((Encoding.NativeChecked.parse (k := k) e (b, words.1)).map fun raw => raw.toView b) + (fun h δ => (Encoding.NativeChecked.parse (k := k) e (b, words.2 h δ)).map + fun raw => raw.toView b))) := by + classical + let words := Assignments.labels k (limit, counter, stride, Encoding.width k s b) + change (CheckedGate.machine M e).finish ((CheckedGate.machine M e).runFrom w + (prepare e (w.length, b, σ, i, limit, counter, stride)) b) = + List.ofFn (Encoding.encode e (gate (MultiTapePTM.ofDeterministic M) w b + (scheduleOfNat k q σ) i + ((Encoding.NativeChecked.parse (k := k) e (b, words.1)).map fun raw => raw.toView b) + (fun h δ => (Encoding.NativeChecked.parse (k := k) e (b, words.2 h δ)).map + fun raw => raw.toView b))) + cases hcombined : Providers.Labels.combine e + (b, ScheduleRead.providerMap k (σ, i), words.1, words.2) with + | none => + have hfailed := (Providers.Labels.combine_eq_none_iff e b + (ScheduleRead.providerMap k (σ, i)) words.1 words.2).mp hcombined + have hgate := gate_eq_none_of_failed_parse M e w b (scheduleOfNat k q σ) i + words.1 words.2 hfailed + rw [finish_prepare_of_combined_none M e w w.length b σ i limit counter stride b + hcapacity hcombined, hgate, ofFn_encode_none] + | some combined => + cases hprevious : Encoding.NativeChecked.parse (k := k) e (b, words.1) with + | none => + have hnone := (Providers.Labels.combine_eq_none_iff e b + (ScheduleRead.providerMap k (σ, i)) words.1 words.2).mpr (Or.inl hprevious) + rw [hcombined] at hnone + contradiction + | some previous => + let empty : RawResult k Q := (none, fun _ => 0, .empty, fun _ => []) + let providers : Providers.ProviderValues k Q := fun h δ => + (Encoding.NativeChecked.parse (k := k) e (b, words.2 h δ)).getD empty + have hproviders (h : Fin k) (δ : Fin 3) : + Encoding.NativeChecked.parse (k := k) e (b, words.2 h δ) = some (providers h δ) := by + cases hparse : Encoding.NativeChecked.parse (k := k) e (b, words.2 h δ) with + | none => + have hnone := (Providers.Labels.combine_eq_none_iff e b + (ScheduleRead.providerMap k (σ, i)) words.1 words.2).mpr (Or.inr ⟨h, δ, hparse⟩) + rw [hcombined] at hnone + contradiction + | some value => simp [providers, hparse] + have hf := finish_prepare_of_parses M e w b σ i limit counter stride + previous providers hprevious hproviders + simpa only [hprevious, hproviders, Option.map_some] using hf + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Supplier diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadRow.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadRow.lean new file mode 100644 index 0000000000..5aaa2a8589 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleReadRow.lean @@ -0,0 +1,104 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadSpace +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Pi +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Finite +import Complexity.Computability.Machines.Turing.MultiTape.Internal.BinarySizeFacts + +/-! +# Native schedule direction rows + +A schedule suffix is obtained by repeatedly dividing by the fixed row radix. A fixed column +then yields one direction, and the fixed family of input/work heads yields the complete row. +The schedule and interval are binary runtime inputs: neither the interval list nor a power with +runtime exponent is allocated. The supplied row agrees with `scheduleOfNat`. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ScheduleRead + +open MultiTapeTM MultiTapeTM.ComputableInSpace MultiTapeNTM.ChoiceTable + +/-- Discard a binary-counted number of schedule rows using fixed-radix division. -/ +@[expose] def suffix (k : ℕ) (a : ℕ × ℕ) : ℕ := + (fun n : ℕ => n / 3 ^ (k + 1))^[a.2] a.1 + +/-- The streamed suffix has the existing digit-position meaning. The power is a specification, +not an operation performed by the native reader. -/ +theorem suffix_eq (k σ i : ℕ) : + suffix k (σ, i) = σ / 3 ^ ((k + 1) * i) := by + change (fun n : ℕ => n / 3 ^ (k + 1))^[i] σ = _ + induction i with + | zero => simp + | succ i ih => + rw [Function.iterate_succ_apply'] + rw [ih, Nat.div_div_eq_div_mul, ← pow_add, Nat.mul_succ] + +/-- Discarding rows cannot enlarge the stored schedule. -/ +theorem suffix_le (k σ i : ℕ) : suffix k (σ, i) ≤ σ := by + rw [suffix_eq] + exact Nat.div_le_self _ _ + +/-- Native suffix extraction uses space linear in actual binary schedule and interval storage. -/ +theorem suffix_inSpace (k : ℕ) : + ComputableInSpace (suffix k) encState encNat (fun a => (encState a).length) := by + have hid : ComputableInSpace (fun n : ℕ => n) encNat encNat + (fun n => (encNat n).length) := + of_inO (ComputableInO.of_eq (f := id) fun _ => rfl) + have hnext := budget_natDiv hid + (budget_const (encB := encNat) (3 ^ (k + 1)) (.refl _)) + have hstates (a : ℕ × ℕ) (j : ℕ) (_hj : j ≤ a.2) : + (encNat ((fun n : ℕ => n / 3 ^ (k + 1))^[j] a.1)).length ≤ + (encState a).length := by + have hmono := ComputableTidilyInO.length_encNat_mono (suffix_le k a.1 j) + change (encNat (suffix k (a.1, j))).length ≤ _ + simp only [encState, length_encPair] + omega + exact (budget_iterate (snd encNat encNat) (fst encNat encNat) hnext hstates + (.refl _)).of_enc_eq fun _ => rfl + +/-- Read one fixed head column of the actual runtime schedule row. -/ +@[expose] def row (k c : ℕ) (a : ℕ × ℕ) : SignType := + signEquiv.symm ⟨suffix k a / 3 ^ c % 3, Nat.mod_lt _ (by decide)⟩ + +/-- A fixed-column direction reader is native and charges actual binary input storage. -/ +theorem row_inSpace (k c : ℕ) : + ComputableInSpace (row k c) encState (encFinite SignType) + (fun a => (encState a).length) := by + have hquot := budget_natDiv (suffix_inSpace k) + (budget_const (encB := encNat) (3 ^ c) (.refl _)) + have hfin := budget_finMod (by decide : 0 < 3) hquot (.refl _) + exact (budget_finite (encC := encFinite SignType) signEquiv.symm hfin).of_enc_eq + fun _ => rfl + +/-- The complete direction row, with the input head in column zero. -/ +@[expose] def fullrow (k : ℕ) (a : ℕ × ℕ) : Option (Fin k) → SignType := + fun head => row k ((finSuccEquiv k).symm head) a + +/-- The finite head family is fixed by the simulated machine, so its native space constant +is independent of the runtime schedule and interval. -/ +theorem fullrow_inSpace (k : ℕ) : + ComputableInSpace (fullrow k) encState (encPi (encFinite SignType)) + (fun a => (encState a).length) := + pi_linear fun head => row_inSpace k ((finSuccEquiv k).symm head) + +/-- The native direction family is exactly the existing functional schedule's row. -/ +theorem fullrow_eq_scheduleOfNat {k q : ℕ} (σ : ℕ) (i : Fin q) : + fullrow k (σ, i) = scheduleOfNat k q σ i := by + funext head + change signEquiv.symm _ = signEquiv.symm _ + apply congrArg signEquiv.symm + apply Fin.ext + change suffix k (σ, i) / 3 ^ (((finSuccEquiv k).symm head : Fin (k + 1)) : ℕ) % 3 = _ + rw [suffix_eq, Nat.div_div_eq_div_mul, ← pow_add, + Nat.add_comm ((k + 1) * i.val) (((finSuccEquiv k).symm head : Fin (k + 1)) : ℕ)] + +end Turing.MultiTapePTM.BlockComputation.ScheduleRead diff --git a/ComplexityTests/Machines/Turing/MultiTape/QueryReplaySupplier.lean b/ComplexityTests/Machines/Turing/MultiTape/QueryReplaySupplier.lean new file mode 100644 index 0000000000..d3f663ec79 --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/QueryReplaySupplier.lean @@ -0,0 +1,90 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of native checked gate preparation) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierTotal +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierBounds +public import ComplexityTests.Machines.Turing.MultiTape.QueryReplayGate +import Mathlib.Tactic.FinCases + +/-! +# Checked gate supplier regressions + +The supplier reads actual packed assignment words, prepares blank entering windows at the first +interval, and queries the original input during replay. It checks unused provider labels and +keeps failed output bounded by actual stored words, even with malformed large binary widths. +-/ + +public section + +namespace ComplexityTests.QueryReplaySupplier + +open Turing MultiTapeTM MultiTapePTM.BlockComputation +open MultiTapePTM.BlockComputation.QueryReplay +open NativeBlockCodec + +@[expose] def word := Encoding.NativeEncode.serialize code (1, true, QueryReplayGate.raw) +@[expose] def counter := ofBits ((List.replicate 4 (word ++ [false])).flatten) +@[expose] noncomputable def prepared (σ : ℕ) : CheckedGate.State 1 (Fin 1) := + Supplier.prepare code (1, 1, σ, 0, 2 ^ 56, counter, 14) +@[expose] noncomputable def supplier := CheckedGate.machine QueryReplayGate.reader code + +private def active (expected : Option (Fin 1) → SignType) : Gate.State 1 (Fin 1) := + ((0, 0, expected), Load.load (1, 1, (0, 0), + (some 0, fun _ => 0, .empty, fun _ => [0, 0, 0]))) + +private def done (expected : Option (Fin 1) → SignType) : Gate.State 1 (Fin 1) := + ((0, 0, expected), ⟨1, 1, 0, 2, none, fun _ => 0, fun _ => [0, 2, 0], .accept⟩) + +private theorem prepared_eq (σ : ℕ) : + prepared σ = .inr (active (ScheduleRead.fullrow 1 (σ, 0))) := by + change Sum.inr _ = Sum.inr _ + congr 1 + apply Prod.ext + · rfl + · apply Config.ext <;> first | rfl | (funext h; fin_cases h; rfl) + +private theorem run_one (expected : Option (Fin 1) → SignType) : + (Gate.machine QueryReplayGate.reader code).runFrom [true] (active expected) 1 = + done expected := by + unfold active + rw [Gate.runFrom_eq] + simp only [QueryMachine.runFrom_succ, QueryMachine.runFrom_zero, QueryMachine.step, + QueryReplay.machine, QueryMachine.read, tick, QueryReplayGate.reader, MultiTapeTM.tr_ofTr] + rfl + +example : (prepared 2).isRight = true := by decide +example : supplier.halted (prepared 2) = false := by decide +example : supplier.halted (supplier.runFrom [true] (prepared 2) 1) = true := by decide +example : Encoding.NativeDecode.parse (k := 1) code + (1, supplier.finish (supplier.runFrom [true] (prepared 2) 1)) = + some (none, fun _ => 0, .accept, fun _ => [0, 2, 0]) := by + unfold supplier + rw [prepared_eq, CheckedGate.runFrom_inr, run_one] + decide +example : supplier.finish (supplier.runFrom [true] (prepared 1) 1) = + List.replicate (Encoding.width 1 1 1) false := by + unfold supplier + rw [prepared_eq, CheckedGate.runFrom_inr, run_one] + decide + +@[expose] def badCounter := ofBits + ([word ++ [false], fail ++ [false], word ++ [false], word ++ [false]].flatten) +@[expose] noncomputable def rejected : CheckedGate.State 1 (Fin 1) := + Supplier.prepare code (1, 1, 2, 0, 2 ^ 56, badCounter, 14) + +-- All provider ports are absent at interval zero, but the failed provider is still rejected. +example : rejected.isLeft = true := by decide +example : supplier.halted rejected = true := by decide +example : supplier.finish rejected = List.replicate 13 false := by decide + +-- A rejected malformed call cannot allocate a billion-cell FAIL buffer. +example : (Supplier.prepare (k := 1) code (0, 1000000000, 0, 0, 1, 0, 14)).elim + id (fun _ => []) = [false] := by decide + +end ComplexityTests.QueryReplaySupplier From e69c9c8f6e689aad54e3bdc6d43084b7d4cc5a33 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 03:11:53 +0000 Subject: [PATCH 25/36] feat(hpv): return packed chunks from native gate replay --- Complexity.lean | 6 + .../QueryReplayChunkBits.lean | 88 ++++++++++++ .../QueryReplaySupplierChunk.lean | 129 ++++++++++++++++++ .../QueryReplaySupplierChunkCorrect.lean | 49 +++++++ .../QueryReplaySupplierChunkInit.lean | 88 ++++++++++++ .../QueryReplaySupplierChunkTable.lean | 54 ++++++++ .../LogSpace/QueryMachine/FirstHalt.lean | 44 ++++++ .../MultiTape/QueryReplaySupplierChunk.lean | 66 +++++++++ 8 files changed, 524 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayChunkBits.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierChunk.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierChunkCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierChunkInit.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierChunkTable.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine/FirstHalt.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/QueryReplaySupplierChunk.lean diff --git a/Complexity.lean b/Complexity.lean index 9cbe82e49d..beadd6fa0e 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1702,6 +1702,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAssignmentsCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayBounds public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayCheckedGate +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayChunkBits public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayGate public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayGateCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayLabelLoad @@ -1715,6 +1716,10 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayProvidersCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierBounds +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierChunk +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierChunkCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierChunkInit +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierChunkTable public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierInit public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierTotal @@ -1823,6 +1828,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogS public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.Call public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.CallSpace +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.FirstHalt public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.InputLength public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.Reachability public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.Savitch diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayChunkBits.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayChunkBits.lean new file mode 100644 index 0000000000..6bb65643ad --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplayChunkBits.lean @@ -0,0 +1,88 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeDecode +public import Complexity.Computability.TreeEval.Ary.Packed +public import Complexity.Foundations.Data.BitString.List +import Complexity.Foundations.Data.BitString.Binary +import Complexity.Foundations.Data.List.Lookup + +/-! +# Numeric chunks of an explicitly stored gate label + +The native numeric reader truncates its slice at the end of the actual word. Reading that +short slice as a little-endian number agrees with the existing packed bit-string encoder, +which fills the final partial chunk with zero bits. This is an equality of specifications; +no padded word or runtime power is added to the implementation. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.SupplierChunk + +open MultiTapeTM Cslib.BitString Cslib.TreeEval.Ary + +private theorem ofBits_replicate_false (n : ℕ) : + ofBits (List.replicate n false) = 0 := by + induction n with + | zero => rfl + | succ n ih => + rw [List.replicate_succ, ofBits, List.foldr_cons, ← ofBits, ih] + rfl + +private theorem ofBits_ofFn_ofList (word : List Bool) (n : ℕ) (h : word.length ≤ n) : + ofBits (List.ofFn (ofList n word)) = ofBits word := by + induction word generalizing n with + | nil => + have hz : ofList n ([] : List Bool) = fun _ => false := by + funext i + simp [ofList] + rw [hz, List.ofFn_const] + exact ofBits_replicate_false n + | cons bit word ih => + cases n with + | zero => simp at h + | succ n => + rw [List.ofFn_succ] + change Nat.bit bit (ofBits (List.ofFn (ofList n word))) = Nat.bit bit (ofBits word) + rw [ih n (by simpa using h)] + +private theorem ofBits_ofFn {n : ℕ} (bits : Cslib.BitString n) : + ofBits (List.ofFn bits) = (equivFin n bits).val := by + rw [← lsbValue_ofFn] + exact (lsbValue_eq_foldr_bit _).symm + +/-- Numeric reads of a complete or partial label chunk agree with the packed encoder. +Missing high-order bits are zero, including when the chunk width itself is zero. -/ +theorem readNat_ofFn_eq_packBits {b r m : ℕ} (hb : b ≤ r * m) + (bits : Fin b → Bool) (j : Fin r) : + Encoding.NativeDecode.readNat (List.ofFn bits) (j.val * m) m = + (Packed.packBits hb bits j).val := by + let slice := ((List.ofFn bits).drop (j.val * m)).take m + have hlength : slice.length ≤ m := by + simp only [slice, List.length_take] + exact Nat.min_le_left _ _ + have hview : ofList m slice = block (zeroExtend (d' := r * m) bits) j := by + funext i + change slice.getD i.val false = _ + rw [List.getD_take_drop _ _ i.isLt] + have hindex : (finProdFinEquiv (j, i) : ℕ) = j.val * m + i.val := by + simp only [finProdFinEquiv_apply_val] + ac_rfl + simp only [block, zeroExtend, hindex] + by_cases hi : j.val * m + i.val < b + · simp only [hi, ↓reduceDIte] + exact List.getD_ofFn_of_lt bits hi false + · simp only [hi, ↓reduceDIte] + exact List.getD_eq_default _ _ (by simp only [List.length_ofFn]; omega) + change ofBits slice = (Packed.packBits hb bits j).val + rw [← ofBits_ofFn_ofList slice m hlength, hview, ofBits_ofFn] + rfl + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.SupplierChunk diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierChunk.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierChunk.lean new file mode 100644 index 0000000000..635e45f11a --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierChunk.lean @@ -0,0 +1,129 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayCheckedGate +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeDecode + +/-! +# Packed numeric answers from returned replay states + +The supplier retains a binary chunk width and requested chunk index beside the checked local +replay state. Its query machine is silent: the interpolation caller extracts one numeric answer +from the returned state, rather than storing a callee output stream. Extraction reads only a +slice of the already paid Boolean gate result, including a zero-padded final partial chunk. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.SupplierChunk + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +variable {k s : ℕ} {Q : Type} + +/-- Runtime chunk width and requested chunk index. -/ +abbrev Context := ℕ × ℕ + +/-- Retained packed-answer metadata and the actual checked replay state. -/ +abbrev State (k : ℕ) (Q : Type) := Context × CheckedGate.State k Q + +/-- Binary metadata has its actual encoded storage charge. -/ +@[expose] noncomputable def encContext : Context ↪ List Bool := encPair encNat encNat + +/-- Replay on the same original input, retaining packed-answer metadata without an output +stream. Actual input-dependent initial states are supplied by the caller. -/ +@[expose] noncomputable def machine (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + QueryMachine (State k Q) where + init := ((0, 0), (CheckedGate.machine M e).init) + query := fun (_, q) => (CheckedGate.machine M e).query q + next := fun ((context, q), symbol) => + (context, (CheckedGate.machine M e).next (q, symbol)) + emit _ := [] + halted := fun (_, q) => (CheckedGate.machine M e).halted q + finish _ := [] + +/-- Extract one numeric packed chunk from the returned checked gate state. Reading a partial +final chunk implicitly pads the missing high bits with zero. -/ +@[expose] noncomputable def answer (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + State k Q → ℕ := fun ((m, chunk), q) => + Encoding.NativeDecode.readNat ((CheckedGate.machine M e).finish q) (chunk * m) m + +variable [Finite Q] + +/-- Retained numeric metadata and checked replay storage are both charged explicitly. -/ +@[expose] noncomputable def encState : State k Q ↪ List Bool := + encPair encContext (CheckedGate.encState (k := k) (Q := Q)) + +/-- Queries are delegated to the checked original-input replay. -/ +theorem machine_query_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).query encState encNat (fun a => (encState a).length) := by + have hq := snd encContext (CheckedGate.encState (k := k) (Q := Q)) + exact (budget_comp_linear (CheckedGate.machine_query_inSpace M e) hq).of_enc_eq + fun _ => rfl + +/-- Transitions retain chunk metadata and charge the complete active state. -/ +theorem machine_next_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).next (encPair encState (encOption encBool)) encState + (fun a => (encPair encState (encOption encBool) a).length) := by + have hs := fst (encState (k := k) (Q := Q)) (encOption encBool) + have hcontext := hs.fst' + have hq := hs.snd' + have hsymbol := snd (encState (k := k) (Q := Q)) (encOption encBool) + have hnext := budget_comp_linear (CheckedGate.machine_next_inSpace M e) + (budget_prod hq hsymbol) + exact (budget_prod hcontext hnext).of_enc_eq fun _ => rfl + +/-- Intermediate output is empty; numeric answers are read from returned states. -/ +theorem machine_emit_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).emit (encPair encState (encOption encBool)) encBits + (fun a => (encPair encState (encOption encBool) a).length) := + budget_const [] (.refl _) + +/-- Stopping is exactly that of the checked replay. -/ +theorem machine_halted_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).halted encState encBool (fun a => (encState a).length) := by + have hq := snd encContext (CheckedGate.encState (k := k) (Q := Q)) + exact (budget_comp_linear (CheckedGate.machine_halted_inSpace M e) hq).of_enc_eq + fun _ => rfl + +/-- The query-machine finish stream is empty, because the caller consumes `answer` instead. -/ +theorem machine_finish_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).finish encState encBits (fun a => (encState a).length) := + budget_const [] (.refl _) + +/-- Packed-answer extraction uses native space linear in the actual returned-state storage. +Neither the chunk width nor its offset causes allocation of its numeric magnitude. -/ +theorem answer_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (answer M e) encState encNat (fun a => (encState a).length) := by + have hcontext := fst encContext (CheckedGate.encState (k := k) (Q := Q)) + have hq := snd encContext (CheckedGate.encState (k := k) (Q := Q)) + have hm := hcontext.fst' + have hchunk := hcontext.snd' + have hword := budget_comp_linear (CheckedGate.machine_finish_inSpace M e) hq + exact (budget_comp_linear Encoding.NativeDecode.readNat_inSpace + (budget_prod hword (budget_prod (budget_natMul hchunk hm) hm))).of_enc_eq + fun _ => rfl + +omit [Finite Q] in +/-- Retained answer metadata does not change physical replay transitions. -/ +theorem runFrom_eq (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (context : Context) (q : CheckedGate.State k Q) (t : ℕ) : + (machine M e).runFrom w (context, q) t = + (context, (CheckedGate.machine M e).runFrom w q t) := by + induction t with + | zero => rfl + | succ t ih => + rw [QueryMachine.runFrom_succ, ih, QueryMachine.runFrom_succ] + simp only [QueryMachine.step, machine, QueryMachine.read] + by_cases h : (CheckedGate.machine M e).halted + ((CheckedGate.machine M e).runFrom w q t) = true + · simp [h] + · simp [h] + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.SupplierChunk diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierChunkCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierChunkCorrect.lean new file mode 100644 index 0000000000..5be504b223 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierChunkCorrect.lean @@ -0,0 +1,49 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierChunkInit +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayChunkBits +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierTotal + +/-! +# Exact numeric answers from checked local replay + +Extracting a requested chunk after replay returns the existing packed encoding of the existing +functional gate, on every assignment. This includes partial final chunks and every FAIL case; +no high-order padding is materialized by the numeric reader. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.SupplierChunk + +open MultiTapeTM Cslib.TreeEval.Ary + +variable {k s : ℕ} {Q : Type} + +/-- The numeric answer of a prepared local call is the packed chunk of the existing gate's +Boolean output, including all rejected-label and failed-motion cases. -/ +theorem answer_runFrom_prepare {q r m : ℕ} (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (b : ℕ) [NeZero b] (σ : ℕ) (i : Fin q) + (limit counter stride : ℕ) (hb : Encoding.width k s b ≤ r * m) (chunk : Fin r) + (hcapacity : Encoding.width k s b ≤ limit.size) : + let words := Assignments.labels k (limit, counter, stride, Encoding.width k s b) + let value := gate (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) i + ((Encoding.NativeChecked.parse (k := k) e (b, words.1)).map fun raw => raw.toView b) + (fun h δ => (Encoding.NativeChecked.parse (k := k) e (b, words.2 h δ)).map + fun raw => raw.toView b) + answer M e ((machine M e).runFrom w + (prepare e ((m, chunk), (w.length, b, σ, i, limit, counter, stride))) b) = + (Packed.packBits hb (Encoding.encode e value) chunk).val := by + rw [prepare, runFrom_eq] + dsimp only [answer] + rw [Supplier.finish_prepare_eq_gate M e w b σ i limit counter stride hcapacity] + exact readNat_ofFn_eq_packBits hb _ chunk + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.SupplierChunk diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierChunkInit.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierChunkInit.lean new file mode 100644 index 0000000000..2f980bad07 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierChunkInit.lean @@ -0,0 +1,88 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierChunk +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierBounds +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.FirstHalt + +/-! +# Native initialization and bounds for packed local answers + +The requested chunk metadata accompanies the existing checked Boolean supplier. Initialization +is native, every clock charges both retained metadata and actual replay storage, and stopping +is inherited from the checked local call. The original input is never copied into this state. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.SupplierChunk + +open MultiTapeTM MultiTapeTM.ComputableInSpace MultiTapeNTM.ChoiceTable + +variable {k s : ℕ} {Q : Type} + +/-- Requested chunk metadata and actual checked-supplier initialization arguments. -/ +abbrev Args := Context × Supplier.Args + +/-- Actual binary metadata and assignment-counter storage. -/ +@[expose] noncomputable def encArgs : Args ↪ List Bool := encPair encContext Supplier.encArgs + +/-- Prepare the checked replay while retaining the requested packed chunk. -/ +@[expose] noncomputable def prepare (e : Q ≃ Fin s) : Args → State k Q := + fun (context, args) => (context, Supplier.prepare e args) + +variable [Finite Q] + +/-- Packed-answer preparation uses space linear in the actual binary argument storage. -/ +theorem prepare_inSpace (e : Q ≃ Fin s) : + ComputableInSpace (prepare (k := k) e) encArgs encState + (fun a => (encArgs a).length) := + budget_prod (fst encContext Supplier.encArgs) + (budget_comp_linear (Supplier.prepare_inSpace (k := k) e) + (snd encContext Supplier.encArgs)) + +/-- Every clock includes the requested chunk metadata and the complete checked replay state. -/ +theorem length_runFrom_prepare_le (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (b : ℕ) [NeZero b] (σ i limit counter stride m chunk t : ℕ) : + let (positive, negative) := ScheduleRead.origin k 0 (σ, i) + (encState ((machine M e).runFrom w + (prepare e ((m, chunk), (w.length, b, σ, i, limit, counter, stride))) t)).length ≤ + 2 * (encContext (m, chunk)).length + 1 + (Encoding.width k s b + 1) + + (2 * (Gate.encContext + (positive, negative, ScheduleRead.fullrow k (σ, i))).length + 2 + + 1024 * (k + 1) * (b + Nat.log2 w.length + Nat.card (Option Q) + + Nat.card OutputTrack + 1)) := by + rw [prepare, runFrom_eq] + have h := Supplier.length_runFrom_prepare_le M e w b σ i limit counter stride t + dsimp only at h ⊢ + simp only [encState, length_encPair] + omega + +omit [Finite Q] in +/-- Requested chunk metadata does not delay stopping of a checked local call. -/ +theorem halted_runFrom_prepare (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (N b σ i limit counter stride m chunk : ℕ) : + (machine M e).halted ((machine M e).runFrom w + (prepare e ((m, chunk), (N, b, σ, i, limit, counter, stride))) b) = true := by + rw [prepare, runFrom_eq] + exact Supplier.halted_runFrom_prepare M e w N b σ i limit counter stride + +omit [Finite Q] in +/-- A prepared numeric supplier has the exact first-return clock needed by the same-input +call rules, and returns the state used by the packed-answer correctness theorem. -/ +theorem exists_first_return (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (N b σ i limit counter stride m chunk : ℕ) : + let start := prepare e ((m, chunk), (N, b, σ, i, limit, counter, stride)) + ∃ T, T ≤ b ∧ (machine M e).halted ((machine M e).runFrom w start T) = true ∧ + (∀ t < T, (machine M e).halted ((machine M e).runFrom w start t) = false) ∧ + (machine M e).runFrom w start T = (machine M e).runFrom w start b := by + exact QueryMachine.exists_first_halt_le _ w _ b + (halted_runFrom_prepare M e w N b σ i limit counter stride m chunk) + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.SupplierChunk diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierChunkTable.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierChunkTable.lean new file mode 100644 index 0000000000..7407809152 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/QueryReplaySupplierChunkTable.lean @@ -0,0 +1,54 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierChunkCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAssignmentsCorrect + +/-! +# Numeric table answers for the existing packed block gate + +On the actual packed interpolation grid, native assignment slices are the existing unpacked +child values, and the checked native parser is the existing checked Boolean gate decoder. +Thus one completed local call returns exactly the requested packed chunk of `codeGate`, without +constructing its truth table or a parallel gate definition. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.SupplierChunk + +open MultiTapeTM Cslib.BitString Cslib.TreeEval.Ary + +variable {k s : ℕ} {Q : Type} + +/-- A native table call at a grid assignment returns the requested chunk of the existing +Boolean `codeGate`. The actual assignment limit pays all padded child words. -/ +theorem answer_grid_eq_codeGate {q r m : ℕ} (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (b : ℕ) [NeZero b] (σ : ℕ) (i : Fin q) + (counter : ℕ) (hb : Encoding.width k s b ≤ r * m) (hm : 0 < m) (chunk : Fin r) : + let children := fun child : Fin (k * 3 + 1) => + Packed.unpackBits hb (blocksEquiv (fun j : Fin ((k * 3 + 1) * r) => + (⟨Packed.gridDigit (2 ^ m) counter j, Nat.mod_lt _ (by positivity)⟩ : + Fin (2 ^ m))) child) + answer M e ((machine M e).runFrom w + (prepare e ((m, chunk), (w.length, b, σ, i, + (2 ^ m) ^ ((k * 3 + 1) * r), counter, r * m))) b) = + (Packed.packBits hb + (codeGate (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) + (Encoding.encode e) (Encoding.decode e) (i.val + 1) children) chunk).val := by + have hcapacity := Packed.AssignmentLabels.capacity hb (0 : Fin (k * 3 + 1)) + simp only [Fin.val_zero, zero_mul, zero_add] at hcapacity + have hf := answer_runFrom_prepare M e w b σ i + ((2 ^ m) ^ ((k * 3 + 1) * r)) counter (r * m) hb chunk hcapacity + dsimp only at hf + rw [Assignments.labels_eq_unpackBits hb hm counter] at hf + simp only [Encoding.NativeChecked.parse_ofFn] at hf + simpa only [codeGate, intervalGate, i.isLt, ↓reduceDIte] using hf + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.SupplierChunk diff --git a/Complexity/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine/FirstHalt.lean b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine/FirstHalt.lean new file mode 100644 index 0000000000..2a96640bd7 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/LogSpace/QueryMachine/FirstHalt.lean @@ -0,0 +1,44 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.Call +import Mathlib.Data.Nat.Find + +/-! +# First returns of bounded query-machine calls + +A known halting bound supplies the exact first-return clock required by the same-input call +rules. The first returned state equals the state at the known bound, because halted states are +fixed. This is an existence theorem, not a requirement that callers compute a time bound. +-/ + +public section + +namespace Turing.QueryMachine + +/-- A bounded call has a first halted clock and returns the same state as at its known bound. -/ +theorem exists_first_halt_le {σ : Type} (M : QueryMachine σ) (w : List Bool) (start : σ) + (bound : ℕ) (hbound : M.halted (M.runFrom w start bound) = true) : + ∃ T, T ≤ bound ∧ M.halted (M.runFrom w start T) = true ∧ + (∀ t < T, M.halted (M.runFrom w start t) = false) ∧ + M.runFrom w start T = M.runFrom w start bound := by + have hex : ∃ t, M.halted (M.runFrom w start t) = true := ⟨bound, hbound⟩ + let T := Nat.find hex + have hT : M.halted (M.runFrom w start T) = true := Nat.find_spec hex + have hle : T ≤ bound := Nat.find_min' hex hbound + refine ⟨T, hle, hT, ?_, ?_⟩ + · intro t ht + exact Bool.eq_false_iff.mpr (Nat.find_min hex ht) + · have hstable := ({ M with init := start } : QueryMachine σ).run_add_of_halted + w hT (bound - T) + change M.runFrom w start (T + (bound - T)) = M.runFrom w start T at hstable + rw [Nat.add_sub_of_le hle] at hstable + exact hstable.symm + +end Turing.QueryMachine diff --git a/ComplexityTests/Machines/Turing/MultiTape/QueryReplaySupplierChunk.lean b/ComplexityTests/Machines/Turing/MultiTape/QueryReplaySupplierChunk.lean new file mode 100644 index 0000000000..e6ea69d6b8 --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/QueryReplaySupplierChunk.lean @@ -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 +-/ + +module +-- upstream: local-only (tests of numeric packed replay answers) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierChunkTable +public import ComplexityTests.Machines.Turing.MultiTape.QueryReplayGate + +/-! +# Packed local-answer regressions + +The last chunk is partial and contains a nonzero retained bit. Missing high bits are zero. +Rejection and motion failure return zero chunks, while huge binary offsets do not allocate +huge padded words. A concrete replay queries the original input before extracting its answer. +-/ + +public section + +namespace ComplexityTests.QueryReplaySupplierChunk + +open Turing MultiTapeTM MultiTapePTM.BlockComputation +open MultiTapePTM.BlockComputation.QueryReplay +open NativeBlockCodec + +private theorem run_one (context : SupplierChunk.Context) + (expected : Option (Fin 1) → SignType) : + (SupplierChunk.machine QueryReplayGate.reader code).runFrom [true] + (context, .inr (QueryReplayGate.start expected)) 1 = + (context, .inr (QueryReplayGate.done expected)) := by + rw [SupplierChunk.runFrom_eq, CheckedGate.runFrom_inr] + apply congrArg (fun q => (context, Sum.inr q)) + unfold QueryReplayGate.start QueryReplayGate.done + rw [Gate.runFrom_eq] + simp only [QueryMachine.runFrom_succ, QueryMachine.runFrom_zero, QueryMachine.step, + QueryReplay.machine, QueryMachine.read, tick, QueryReplayGate.reader, MultiTapeTM.tr_ofTr] + rfl + +def allFive : Fin 5 → Bool := fun _ => true + +example : Encoding.NativeDecode.readNat [true, true, true, true, true] 3 3 = 3 := by decide +example : (Cslib.TreeEval.Ary.Packed.packBits (by decide : 5 ≤ 2 * 3) + allFive (1 : Fin 2)).val = 3 := by decide + +-- The completed 13-bit label has bit 12 set; its last 2-bit chunk therefore encodes one. +example : SupplierChunk.answer QueryReplayGate.reader code + ((SupplierChunk.machine QueryReplayGate.reader code).runFrom [true] + ((2, 6), .inr (QueryReplayGate.start (QueryReplayGate.row .pos .neg))) 1) = 1 := by + rw [run_one] + decide +example : SupplierChunk.answer QueryReplayGate.reader code + ((SupplierChunk.machine QueryReplayGate.reader code).runFrom [true] + ((2, 0), .inr (QueryReplayGate.start (QueryReplayGate.row .zero .neg))) 1) = 0 := by + rw [run_one] + decide +example : SupplierChunk.answer QueryReplayGate.reader code + ((2, 6), .inl [true, true, true]) = 0 := by decide +example : SupplierChunk.answer QueryReplayGate.reader code + ((0, 1000000000), .inr (QueryReplayGate.done (QueryReplayGate.row .pos .neg))) = 0 := by decide +example : SupplierChunk.answer QueryReplayGate.reader code + ((1000000000, 1), .inr (QueryReplayGate.done (QueryReplayGate.row .pos .neg))) = 0 := by decide + +end ComplexityTests.QueryReplaySupplierChunk From 319f6f63a47ce415703672f7b4f9260903ec5269 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 03:29:02 +0000 Subject: [PATCH 26/36] feat(hpv): navigate implicit paths and source chunks --- Complexity.lean | 6 + .../BlockComputation/ImplicitPath.lean | 57 +++++++ .../BlockComputation/ImplicitPathCorrect.lean | 107 ++++++++++++ .../BlockComputation/ImplicitPathLookup.lean | 129 ++++++++++++++ .../BlockComputation/ImplicitPathSpace.lean | 159 ++++++++++++++++++ .../BlockComputation/InitialChunk.lean | 53 ++++++ .../BlockComputation/InitialChunkCorrect.lean | 114 +++++++++++++ .../Turing/MultiTape/ImplicitBlockPath.lean | 56 ++++++ 8 files changed, 681 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitPath.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitPathCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitPathLookup.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitPathSpace.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialChunk.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialChunkCorrect.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/ImplicitBlockPath.lean diff --git a/Complexity.lean b/Complexity.lean index beadd6fa0e..2973e167e5 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1696,6 +1696,12 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeChecked public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeDecode public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeEncode +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitPath +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitPathCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitPathLookup +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitPathSpace +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialChunk +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialChunkCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplay public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAccess public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAssignments diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitPath.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitPath.lean new file mode 100644 index 0000000000..444f8049c9 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitPath.lean @@ -0,0 +1,57 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadSearch + +/-! +# Implicit block-tree addresses + +A path is followed by retaining one interval index. Child zero selects the preceding interval; +other children select the last earlier window provider. Missing providers and the padded source +stay at index zero. Out-of-range child numbers and intervals also return zero. + +Provider selection uses a fixed family indexed by the simulated machine's work tapes and three +ports. Thus each head offset in a schedule reader is fixed, rather than an unchecked runtime +exponent. No tree, interval list or table is constructed. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ImplicitPath + +/-- Read one fixed work-head/port provider, using zero for the initial source. -/ +@[expose] def provider (k : ℕ) (a : ℕ × ℕ) (p : Fin (k * 3)) : ℕ := + let (σ, n) := a + let (h, δ) : Fin k × Fin 3 := finProdFinEquiv.symm p + ((ScheduleRead.search k (h + 1) (σ, n, δ)).map fun j => j + 1).getD 0 + +/-- The fixed finite family of providers for one interval. -/ +@[expose] def providers (k : ℕ) (a : ℕ × ℕ) : List ℕ := + List.ofFn fun p : Fin (k * 3) => provider k a p + +/-- Schedule, horizon, current interval index and child number. -/ +abbrev Args := ℕ × ℕ × ℕ × ℕ + +/-- Follow one child without constructing the unfolded tree. -/ +@[expose] def child (k : ℕ) : Args → ℕ := fun (σ, q, index, j) => + match index with + | 0 => 0 + | n + 1 => + if n < q ∧ j < k * 3 + 1 then + if j = 0 then n else (providers k (σ, n)).getD (j - 1) 0 + else 0 + +/-- Binary schedule and horizon, followed by an explicitly stored sequence of child numbers. -/ +abbrev WalkArgs := ℕ × ℕ × List ℕ + +/-- Fold a path from the root index, retaining one current interval. -/ +@[expose] def walk (k : ℕ) : WalkArgs → ℕ := fun (σ, q, path) => + path.foldl (fun index j => child k (σ, q, index, j)) q + +end Turing.MultiTapePTM.BlockComputation.ImplicitPath diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitPathCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitPathCorrect.lean new file mode 100644 index 0000000000..5899d06dd2 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitPathCorrect.lean @@ -0,0 +1,107 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitPath +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputationTree +import Mathlib.Data.List.GetD +import Complexity.Foundations.Data.List.Lookup + +/-! +# Correct implicit block-tree navigation + +Numeric child selection agrees with the existing descending block tree, including source +padding and out-of-horizon indices. Every child is no later than its parent, and a positive +parent strictly decreases. Folding typed child paths preserves that exact interpretation. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ImplicitPath + +/-- A provider is either the source or the successor of an earlier interval. -/ +theorem provider_le (k σ n : ℕ) (p : Fin (k * 3)) : provider k (σ, n) p ≤ n := by + unfold provider + rcases finProdFinEquiv.symm p with ⟨h, δ⟩ + cases hs : ScheduleRead.search k (h + 1) (σ, n, δ) with + | none => simp [hs] + | some j => + have hj := ScheduleRead.searchIterate_latest_bound k (h + 1) (σ, n, δ) n j hs + simp only [hs, Option.map_some, Option.getD_some] + omega + +/-- Every numeric child of a positive parent is strictly earlier, even malformed requests. -/ +theorem child_lt (k σ q n j : ℕ) (hn : 0 < n) : child k (σ, q, n, j) < n := by + cases n with + | zero => omega + | succ n => + simp only [child] + split_ifs with h hzero + · omega + · have hindex : j - 1 < k * 3 := by omega + rw [providers, List.getD_ofFn_of_lt _ hindex] + have hp := provider_le k σ n ⟨j - 1, hindex⟩ + omega + · omega + +/-- No child advances the current interval. -/ +theorem child_le (k σ q n j : ℕ) : child k (σ, q, n, j) ≤ n := by + cases n with + | zero => simp [child] + | succ n => exact (child_lt k σ q (n + 1) j (by omega)).le + +/-- Numeric navigation agrees with the existing functional tree's typed child map. -/ +theorem child_eq {k q : ℕ} (σ : ℕ) (n : ℕ) (j : Fin (k * 3 + 1)) : + child k (σ, q, n, j) = + BlockComputation.child (scheduleOfNat k q σ) n j := by + cases n with + | zero => rfl + | succ n => + by_cases hn : n < q + · simp only [child, hn, j.isLt, and_self, ↓reduceIte, BlockComputation.child] + refine Fin.cases ?_ (fun p => ?_) j + · simp + · simp only [Fin.val_succ, Fin.cases_succ, Nat.add_sub_cancel_right] + have hp : (p.val + 1 ≠ 0) := by omega + simp only [hp, ↓reduceIte, providers] + rw [List.getD_ofFn_of_lt _ p.isLt] + unfold provider + rcases hd : finProdFinEquiv.symm p with ⟨h, δ⟩ + simp only [↓reduceDIte] + rw [ScheduleRead.search_eq_lastBefore σ ⟨n, hn⟩ h δ] + cases lastBefore (InWindow (scheduleOfNat k q σ) h + (portBlock (scheduleOfNat k q σ) ⟨n, hn⟩ h δ)) ⟨n, hn⟩ <;> rfl + · simp [child, hn, BlockComputation.child] + +private theorem foldl_child_eq {k q : ℕ} (σ : ℕ) (path : List (Fin (k * 3 + 1))) (n : ℕ) : + (path.map Fin.val).foldl (fun index j => child k (σ, q, index, j)) n = + path.foldl (BlockComputation.child (scheduleOfNat k q σ)) n := by + induction path generalizing n with + | nil => rfl + | cons j path ih => + simp only [List.map_cons, List.foldl_cons, child_eq] + exact ih _ + +/-- Walking the stored numeric path is exactly walking the existing descending tree's path. -/ +theorem walk_eq {k q : ℕ} (σ : ℕ) (path : List (Fin (k * 3 + 1))) : + walk k (σ, q, path.map Fin.val) = + path.foldl (BlockComputation.child (scheduleOfNat k q σ)) q := + foldl_child_eq σ path q + +/-- A walked interval stays within its initial horizon. -/ +theorem walk_le (k σ q : ℕ) (path : List ℕ) : walk k (σ, q, path) ≤ q := by + unfold walk + have hfold (path : List ℕ) (n : ℕ) : + path.foldl (fun index j => child k (σ, q, index, j)) n ≤ n := by + induction path generalizing n with + | nil => exact le_rfl + | cons j path ih => + exact (ih _).trans (child_le k σ q n j) + exact hfold path q + +end Turing.MultiTapePTM.BlockComputation.ImplicitPath diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitPathLookup.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitPathLookup.lean new file mode 100644 index 0000000000..13111e6add --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitPathLookup.lean @@ -0,0 +1,129 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitPathSpace +public import Complexity.Computability.TreeEval.Ary.Descending + +/-! +# Checked implicit block-tree addresses + +A raw path is accepted only if its depth is at most the supplied horizon and every child +number is in the fixed fan-in. Accepted paths resolve to the existing descending tree's node +index. Rejected paths are reported as absent rather than silently becoming the padded source. +Both validation and navigation use native space linear in the stored binary path and metadata. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ImplicitPath + +open MultiTapeTM MultiTapeTM.ComputableInSpace +open Cslib.TreeEval.Ary + +/-- Valid depth and in-range child numbers for a raw stored path. -/ +@[expose] def valid (k : ℕ) : WalkArgs → Bool := fun (_, q, path) => + decide (path.length ≤ q) && path.all (fun j => decide (j < k * 3 + 1)) + +/-- Resolve only well-formed raw addresses; absence remains distinct from the source index. -/ +@[expose] def lookupIndex (k : ℕ) (a : WalkArgs) : Option ℕ := + if valid k a then some (walk k a) else none + +private theorem fold_check (k : ℕ) (path : List ℕ) (ok : Bool) : + path.foldl (fun good j => good && decide (j < k * 3 + 1)) ok = + (ok && path.all (fun j => decide (j < k * 3 + 1))) := by + induction path generalizing ok with + | nil => simp + | cons j path ih => + simp only [List.foldl_cons, ih, List.all_cons, Bool.and_assoc] + +private theorem all_inSpace (k : ℕ) : + ComputableInSpace (fun a : WalkArgs => a.2.2.all (fun j => decide (j < k * 3 + 1))) + encWalkArgs encBool (fun a => (encWalkArgs a).length) := by + have hpath := (snd encNat (encPair encNat (encList encNat))).snd' + have hinit : ComputableInSpace (fun _ : WalkArgs => true) encWalkArgs encBool + (fun a => (encWalkArgs a).length) := budget_const true (.refl _) + have hstep : ComputableInSpace (fun p : Bool × ℕ => p.1 && decide (p.2 < k * 3 + 1)) + (encPair encBool encNat) encBool (fun p => (encPair encBool encNat p).length) := + budget_finite (encC := encBool) (fun p : Bool × Bool => p.1 && p.2) + (budget_prod (fst encBool encNat) + (budget_natLt (snd encBool encNat) + (budget_const (encB := encNat) (k * 3 + 1) (.refl _)))) + have hspace (a : WalkArgs) (i : ℕ) (hi : i < a.2.2.length) : + (encPair encBool encNat + ((a.2.2.take i).foldl (fun good j => good && decide (j < k * 3 + 1)) true, + a.2.2[i])).length ≤ 4 * ((encWalkArgs a).length + 1) := by + have hm := length_le_encList_of_mem encNat (List.getElem_mem hi) + simp only [encWalkArgs, length_encPair, encBool_apply, List.length_singleton] at * + omega + have hstates (a : WalkArgs) (i : ℕ) (_hi : i ≤ a.2.2.length) : + (encBool ((a.2.2.take i).foldl + (fun good j => good && decide (j < k * 3 + 1)) true)).length ≤ + 4 * ((encWalkArgs a).length + 1) := by + simp only [encBool_apply, List.length_singleton] + omega + have hfold := ComputableInSpace.foldl + (encIn := encWalkArgs) (encA := encNat) (encL := encList encNat) (encB := encBool) + (list := fun a : WalkArgs => a.2.2) (init := fun _ : WalkArgs => true) + (step := fun good j => good && decide (j < k * 3 + 1)) + (Sl := fun a => (encWalkArgs a).length) (Si := fun a => (encWalkArgs a).length) + (Q := fun a => 4 * ((encWalkArgs a).length + 1)) + (B := fun a => 4 * ((encWalkArgs a).length + 1)) + hpath hinit hstep hspace hstates + exact (hfold.weaken (.of_le_linear 16 16 fun a => by + simp only [encWalkArgs, length_encPair] + omega)).of_enc_eq + fun a => by simp only [fold_check, Bool.true_and] + +/-- Raw-address validation is native and charges only stored path and metadata. -/ +theorem valid_inSpace (k : ℕ) : + ComputableInSpace (valid k) encWalkArgs encBool (fun a => (encWalkArgs a).length) := by + have hrest := snd encNat (encPair encNat (encList encNat)) + have hq := hrest.fst' + have hlength := budget_length hrest.snd' + have hdepth := budget_finite (encC := encBool) Bool.not (budget_natLt hq hlength) + exact (budget_finite (encC := encBool) (fun p : Bool × Bool => p.1 && p.2) + (budget_prod hdepth (all_inSpace k))).of_enc_eq fun ⟨σ, q, path⟩ => by + by_cases h : path.length ≤ q + · have hn : ¬q < path.length := by omega + simp [valid, h, hn] + · have hn : q < path.length := by omega + simp [valid, h, hn] + +/-- Native lookup preserves malformed-address rejection, including excess path depth. -/ +theorem lookupIndex_inSpace (k : ℕ) : + ComputableInSpace (lookupIndex k) encWalkArgs (encOption encNat) + (fun a => (encWalkArgs a).length) := by + have hsome := budget_optionSome (walk_inSpace k) + exact (budget_ite (valid_inSpace k) hsome + (budget_const (encB := encOption encNat) none (.refl _))).of_enc_eq fun _ => rfl + +private theorem next_eq_child {k q : ℕ} (σ : ℕ) : + Descending.next (BlockComputation.child (scheduleOfNat k q σ)) = + BlockComputation.child (scheduleOfNat k q σ) := by + funext n j + cases n with + | zero => rfl + | succ n => simp [Descending.next] + +/-- Typed paths pass the raw child-number guard and resolve to the existing descending model; +only the exact horizon-depth guard remains. -/ +theorem lookupIndex_eq_resolve {k q : ℕ} (σ : ℕ) (path : List (Fin (k * 3 + 1))) : + lookupIndex k (σ, q, path.map Fin.val) = + if path.length ≤ q then + some (Descending.resolve (BlockComputation.child (scheduleOfNat k q σ)) q path) + else none := by + have hall : (path.map Fin.val).all (fun j => decide (j < k * 3 + 1)) = true := by + apply List.all_eq_true.mpr + intro j hj + obtain ⟨p, _, rfl⟩ := List.mem_map.mp hj + exact decide_eq_true p.isLt + simp only [lookupIndex, valid, List.length_map, hall, Bool.and_true, decide_eq_true_eq, + walk_eq, Descending.resolve, next_eq_child] + +end Turing.MultiTapePTM.BlockComputation.ImplicitPath diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitPathSpace.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitPathSpace.lean new file mode 100644 index 0000000000..eb11f086f5 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitPathSpace.lean @@ -0,0 +1,159 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitPathCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadSearchSpace +import Complexity.Computability.Machines.Turing.MultiTape.Internal.DegreeOptionPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Degree.Bounds +import Complexity.Computability.Machines.Turing.MultiTape.Internal.BinarySizeFacts +import Mathlib.Data.List.FinRange + +/-! +# Native implicit block-tree navigation + +Every provider branch is compiled for a fixed simulated work head. A fixed finite list of +provider searches is assembled and indexed by the runtime child number. Thus no head exponent +depends on runtime data. Following a path retains only its binary interval cursor and schedule. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ImplicitPath + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +private theorem fixedList {α β ι : Type} (encA : α ↪ List Bool) (encB : β ↪ List Bool) + (items : List ι) (f : ι → α → β) + (hf : ∀ i, ComputableInSpace (f i) encA encB (fun a => (encA a).length)) : + ComputableInSpace (fun a => items.map (fun i => f i a)) encA (encList encB) + (fun a => (encA a).length) := by + induction items with + | nil => exact budget_const [] (.refl _) + | cons i items ih => exact budget_cons (hf i) ih + +/-- Binary schedule and current source interval. -/ +@[expose] noncomputable def encProviderArgs : (ℕ × ℕ) ↪ List Bool := encPair encNat encNat + +/-- Source schedule, horizon, current interval and requested child. -/ +@[expose] noncomputable def encChildArgs : (ℕ × ℕ × ℕ × ℕ) ↪ List Bool := + encPair encNat (encPair encNat (encPair encNat encNat)) + +/-- One fixed work-head provider is computed in native linear space. -/ +theorem provider_inSpace (k : ℕ) (p : Fin (k * 3)) : + ComputableInSpace (fun a => provider k a p) encProviderArgs encNat + (fun a => (encProviderArgs a).length) := by + let hd := finProdFinEquiv.symm p + have hs := fst encNat encNat + have hn := snd encNat encNat + have hsearch := budget_comp_linear (ScheduleRead.search_inSpace k (hd.1.val + 1)) + (budget_prod hs (budget_prod hn (budget_const (encB := encNat) hd.2.val (.refl _)))) + have hget : ComputableInSpace (fun o : Option ℕ => o.getD 0) + (encOption encNat) encNat (fun o => (encOption encNat o).length) := + of_inO (ComputableInDegree.getD (enc₁ := encNat) 0).inO_one + have hsome : ComputableInSpace (fun o : Option ℕ => o.isSome) + (encOption encNat) encBool (fun o => (encOption encNat o).length) := + of_inO (ComputableInDegree.isSome encNat).inO_one + have hindex := budget_natSucc (budget_comp_linear hget hsearch) + refine (budget_ite (budget_comp_linear hsome hsearch) hindex + (budget_const (encB := encNat) 0 (.refl _))).of_enc_eq ?_ + rintro ⟨σ, n⟩ + change encNat (((ScheduleRead.search k (hd.1.val + 1) (σ, n, hd.2.val)).map + fun j => j + 1).getD 0) = encNat (if + (ScheduleRead.search k (hd.1.val + 1) (σ, n, hd.2.val)).isSome then + (ScheduleRead.search k (hd.1.val + 1) (σ, n, hd.2.val)).getD 0 + 1 else 0) + cases ScheduleRead.search k (hd.1.val + 1) (σ, n, hd.2.val) <;> simp + +/-- The provider family is fixed by the simulated machine, not by the runtime horizon. -/ +theorem providers_inSpace (k : ℕ) : + ComputableInSpace (providers k) encProviderArgs (encList encNat) + (fun a => (encProviderArgs a).length) := by + have h := fixedList encProviderArgs encNat (List.finRange (k * 3)) + (fun p a => provider k a p) (provider_inSpace k) + exact h.of_enc_eq fun _ => by simp only [providers, List.ofFn_eq_map] + +/-- Child navigation uses only binary comparisons and the fixed provider family. -/ +theorem child_inSpace (k : ℕ) : + ComputableInSpace (child k) encChildArgs encNat (fun a => (encChildArgs a).length) := by + have hs := fst encNat (encPair encNat (encPair encNat encNat)) + have hr := snd encNat (encPair encNat (encPair encNat encNat)) + have hq := hr.fst' + have hn := hr.snd'.fst' + have hj := hr.snd'.snd' + have hzero := budget_const (encIn := encChildArgs) (encB := encNat) 0 (.refl _) + have hpred := budget_natSub hn (budget_const (encB := encNat) 1 (.refl _)) + have hbelow := budget_natLt hpred hq + have hchild := budget_natLt hj (budget_const (encB := encNat) (k * 3 + 1) (.refl _)) + have hguard := budget_finite (encC := encBool) (fun p : Bool × Bool => p.1 && p.2) + (budget_prod hbelow hchild) + have hproviders := budget_comp_linear (providers_inSpace k) (budget_prod hs hpred) + have hlookup := budget_comp_linear (getD.weaken (.of_le_linear 8 8 fun _ => le_rfl)) + (budget_prod hproviders + (budget_natSub hj (budget_const (encB := encNat) 1 (.refl _)))) + refine (budget_ite (budget_natIsZero hn) hzero (budget_ite hguard + (budget_ite (budget_natIsZero hj) hpred hlookup) hzero)).of_enc_eq ?_ + rintro ⟨σ, q, n, j⟩ + cases n with + | zero => simp [child] + | succ n => + by_cases hn : n < q <;> by_cases hj : j < k * 3 + 1 <;> + simp [child, hn, hj] + +/-- The actual path and its binary schedule/horizon storage. -/ +@[expose] noncomputable def encWalkArgs : WalkArgs ↪ List Bool := + encPair encNat (encPair encNat (encList encNat)) + +private theorem pathStep_inSpace (k : ℕ) : + ComputableInSpace (fun p : ((ℕ × ℕ) × ℕ) × ℕ => + child k (p.1.1.1, p.1.1.2, p.1.2, p.2)) + (encPair (encPair encProviderArgs encNat) encNat) encNat + (fun p => (encPair (encPair encProviderArgs encNat) encNat p).length) := by + have hp := fst (encPair encProviderArgs encNat) encNat + have hj := snd (encPair encProviderArgs encNat) encNat + exact budget_comp_linear (child_inSpace k) + (budget_prod hp.fst'.fst' (budget_prod hp.fst'.snd' (budget_prod hp.snd' hj))) + + +/-- Walking a stored path keeps only its decreasing binary interval cursor and fixed metadata. -/ +theorem walk_inSpace (k : ℕ) : + ComputableInSpace (walk k) encWalkArgs encNat (fun a => (encWalkArgs a).length) := by + have hs := fst encNat (encPair encNat (encList encNat)) + have hr := snd encNat (encPair encNat (encList encNat)) + have hq := hr.fst' + have hpath := hr.snd' + have hpar := budget_prod hs hq + have hstates (a : WalkArgs) (i : ℕ) (_hi : i ≤ a.2.2.length) : + (encNat ((a.2.2.take i).foldl (fun n j => child k (a.1, a.2.1, n, j)) + a.2.1)).length ≤ (encNat a.2.1).length := + ComputableTidilyInO.length_encNat_mono (walk_le k a.1 a.2.1 (a.2.2.take i)) + have hspace (a : WalkArgs) (i : ℕ) (hi : i < a.2.2.length) : + (encPair (encPair encProviderArgs encNat) encNat + (((a.1, a.2.1), (a.2.2.take i).foldl + (fun n j => child k (a.1, a.2.1, n, j)) a.2.1), a.2.2[i])).length ≤ + 16 * ((encWalkArgs a).length + 1) := by + have hc := hstates a i hi.le + have hm := length_le_encList_of_mem encNat (List.getElem_mem hi) + simp only [length_encPair, encWalkArgs, encProviderArgs] at * + omega + have hfold := ComputableInSpace.foldlWith + (encIn := encWalkArgs) (encP := encProviderArgs) (encB := encNat) + (encA := encNat) (encL := encList encNat) + (par := fun a : WalkArgs => (a.1, a.2.1)) + (list := fun a : WalkArgs => a.2.2) (init := fun a : WalkArgs => a.2.1) + (Sp := fun a => (encWalkArgs a).length) (Sl := fun a => (encWalkArgs a).length) + (Si := fun a => (encWalkArgs a).length) + (Q := fun a => 16 * ((encWalkArgs a).length + 1)) + (B := fun a => (encNat a.2.1).length) + (step := fun par n j => child k (par.1, par.2, n, j)) + hpar hpath hq (pathStep_inSpace k) + hspace hstates + exact hfold.weaken (.of_le_linear 100 100 fun a => by + simp only [encWalkArgs, encProviderArgs, length_encPair] + omega) + +end Turing.MultiTapePTM.BlockComputation.ImplicitPath diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialChunk.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialChunk.lean new file mode 100644 index 0000000000..973f83311a --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialChunk.lean @@ -0,0 +1,53 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeDecode + +/-! +# Native packed chunks of the initial block source + +Only the success tag and initial finite control may contain nonzero bits in the initial label. +Reading a requested numeric chunk of this fixed prefix therefore avoids allocating the runtime +blank windows or a padded label. Chunk width and index remain binary runtime inputs. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.InitialChunk + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +variable {k s : ℕ} {Q : Type} + +/-- The initial label's potentially nonzero prefix; the remaining head/window bits are zero. -/ +@[expose] def headerWord (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : List Bool := + true :: List.ofFn (Encoding.encodeState e (some M.q₀)) + +/-- Binary requested chunk width and index. -/ +abbrev Args := ℕ × ℕ + +/-- Both requested chunk coordinates are charged by actual binary storage. -/ +@[expose] noncomputable def encArgs : Args ↪ List Bool := encPair encNat encNat + +/-- Read an initial-source chunk without constructing the label's blank suffix. -/ +@[expose] def chunk (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : Args → ℕ := + fun (m, j) => Encoding.NativeDecode.readNat (headerWord M e) (j * m) m + +/-- The fixed control prefix is part of the machine; runtime chunk metadata pays only its +actual binary digits. A large binary chunk width does not allocate its numeric magnitude. -/ +theorem chunk_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (chunk M e) encArgs encNat (fun a => (encArgs a).length) := by + have hm := fst encNat encNat + have hj := snd encNat encNat + have hprefix := budget_const (encB := encBits) (headerWord M e) + (show (fun a => (encArgs a).length) ≼ fun a => (encArgs a).length from .refl _) + exact (budget_comp_linear Encoding.NativeDecode.readNat_inSpace + (budget_prod hprefix (budget_prod (budget_natMul hj hm) hm))).of_enc_eq fun _ => rfl + +end Turing.MultiTapePTM.BlockComputation.InitialChunk diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialChunkCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialChunkCorrect.lean new file mode 100644 index 0000000000..78c08f59cc --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialChunkCorrect.lean @@ -0,0 +1,114 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialChunk +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputationTree +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayChunkBits +import Mathlib.Tactic.FinCases + +/-! +# Correct initial-source packed chunks + +The fixed control prefix followed by zero bits is the existing Boolean encoding of the initial +view. Numeric reads ignore appended zero suffixes, so the native headerWord reader gives exactly +the existing packed encoding, including a final partial chunk. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.InitialChunk + +open MultiTapeTM Cslib.BitString Cslib.TreeEval.Ary + +variable {k s : ℕ} {Q : Type} + +private theorem ofBits_replicate_false (n : ℕ) : ofBits (List.replicate n false) = 0 := by + induction n with + | zero => rfl + | succ n ih => + rw [List.replicate_succ, ofBits, List.foldr_cons, ← ofBits, ih] + rfl + +private theorem readNat_append_false (word : List Bool) (n offset count : ℕ) : + Encoding.NativeDecode.readNat (word ++ List.replicate n false) offset count = + Encoding.NativeDecode.readNat word offset count := by + induction word generalizing offset count with + | nil => + simp only [List.nil_append, Encoding.NativeDecode.readNat, List.drop_replicate, + List.take_replicate, ofBits_replicate_false, List.drop_nil, List.take_nil] + rfl + | cons bit word ih => + cases offset with + | succ offset => + simpa only [Encoding.NativeDecode.readNat, List.cons_append, List.drop_succ_cons] using + ih offset count + | zero => + cases count with + | zero => rfl + | succ count => + change Nat.bit bit (Encoding.NativeDecode.readNat + (word ++ List.replicate n false) 0 count) = + Nat.bit bit (Encoding.NativeDecode.readNat word 0 count) + rw [ih 0 count] + +/-- The source's full semantic word is its fixed prefix followed by blank bits. This identity +is a specification only; the native implementation never allocates this suffix. -/ +theorem encoded_initial_eq (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (b : ℕ) [NeZero b] : + List.ofFn (Encoding.encode e (some (initialView (MultiTapePTM.ofDeterministic M) b))) = + headerWord M e ++ + List.replicate ((k + 1) * b.size + (2 + Encoding.windowBits k b)) false := by + have hresidue : Encoding.encodeResidue (0 : ZMod b) = fun _ => false := by + funext j + simp [Encoding.encodeResidue, equivFin_symm_apply] + have hheads : Encoding.encodeHeads (fun _ : Option (Fin k) => (0 : ZMod b)) = + fun _ => false := by + simp only [Encoding.encodeHeads, hresidue] + rfl + have htrack : Encoding.encodeTrack .empty = fun _ => false := by + funext j + fin_cases j <;> rfl + have hcell : Encoding.encodeCell none = fun _ => false := by + funext j + fin_cases j <;> rfl + have hwindows : Encoding.encodeWindows (fun (_ : Fin k) (_ : Fin 3) (_ : ZMod b) => none) = + fun _ => false := by + simp only [Encoding.encodeWindows, hcell] + rfl + change @List.ofFn Bool + ((s + 1).size + ((k + 1) * b.size + (2 + Encoding.windowBits k b)) + 1) + (Fin.cons true (Fin.append (Encoding.encodeState e (some M.q₀)) + (Fin.append (Encoding.encodeHeads (fun _ : Option (Fin k) => (0 : ZMod b))) + (Fin.append (Encoding.encodeTrack .empty) + (Encoding.encodeWindows (fun (_ : Fin k) (_ : Fin 3) (_ : ZMod b) => none)))))) = _ + rw [List.ofFn_cons] + simp only [List.ofFn_fin_append, hheads, htrack, hwindows, + List.ofFn_const, headerWord, ← List.replicate_add, List.cons_append] + + +/-- The native fixed-prefix reader returns the existing packed initial-source value. -/ +theorem chunk_eq_packBits {r m : ℕ} (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (b : ℕ) [NeZero b] (hb : Encoding.width k s b ≤ r * m) (j : Fin r) : + chunk M e (m, j) = + (Packed.packBits hb + (Encoding.encode e (some (initialView (MultiTapePTM.ofDeterministic M) b))) j).val := by + rw [← QueryReplay.SupplierChunk.readNat_ofFn_eq_packBits hb _ j, encoded_initial_eq, + readNat_append_false] + rfl + +/-- Source padding is the same constant Boolean gate already used by the implicit tree. -/ +theorem chunk_eq_codeGate_zero {q r m : ℕ} (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (b : ℕ) [NeZero b] (σ : Schedule k q) + (hb : Encoding.width k s b ≤ r * m) (j : Fin r) + (children : Fin (k * 3 + 1) → Fin (Encoding.width k s b) → Bool) : + chunk M e (m, j) = (Packed.packBits hb + (codeGate (MultiTapePTM.ofDeterministic M) w b σ + (Encoding.encode e) (Encoding.decode e) 0 children) j).val := by + simpa only [codeGate, intervalGate] using chunk_eq_packBits M e b hb j + +end Turing.MultiTapePTM.BlockComputation.InitialChunk diff --git a/ComplexityTests/Machines/Turing/MultiTape/ImplicitBlockPath.lean b/ComplexityTests/Machines/Turing/MultiTape/ImplicitBlockPath.lean new file mode 100644 index 0000000000..833da0a3a0 --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/ImplicitBlockPath.lean @@ -0,0 +1,56 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of implicit paths and source chunks) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitPathLookup +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialChunkCorrect +public import ComplexityTests.Machines.Turing.MultiTape.QueryReplayGate + +/-! +# Native implicit path and source regressions + +Paths exercise previous/provider edges, missing providers and invalid addresses. Source chunks +read a fixed initial-control prefix even for huge requested widths, without building blank tape +windows or a tree. +-/ + +public section + +namespace ComplexityTests.ImplicitBlockPath + +open Turing MultiTapePTM.BlockComputation +open NativeBlockCodec + +-- The nonstationary two-head schedule has rows (2,0), (0,2), (1,1). +def schedule : ℕ := 380 + +example : ImplicitPath.child 1 (schedule, 3, 3, 0) = 2 := by decide +example : ImplicitPath.child 1 (schedule, 3, 3, 3) = 1 := by decide +example : ImplicitPath.child 1 (0, 3, 3, 1) = 0 := by decide +example : ImplicitPath.child 1 (schedule, 3, 4, 0) = 0 := by decide +example : ImplicitPath.child 1 (schedule, 3, 3, 4) = 0 := by decide +example : ImplicitPath.walk 1 (schedule, 3, [3, 0]) = 0 := by decide +example : ImplicitPath.walk 1 (schedule, 3, [0, 3]) = 1 := by decide +example : ImplicitPath.walk 0 (0, 4, [0, 0, 0, 0]) = 0 := by decide +example : ImplicitPath.walk 1 (schedule, 3, []) = 3 := rfl + +example : ImplicitPath.lookupIndex 1 (schedule, 3, [3, 0]) = some 0 := by decide +example : ImplicitPath.lookupIndex 1 (schedule, 3, [0, 3]) = some 1 := by decide +example : ImplicitPath.lookupIndex 1 (schedule, 3, [4]) = none := by decide +example : ImplicitPath.lookupIndex 1 (schedule, 3, [0, 0, 0, 0]) = none := by decide +example : ImplicitPath.lookupIndex 0 (0, 0, []) = some 0 := rfl +example : ImplicitPath.lookupIndex 0 (0, 0, [0]) = none := by decide + +example : (InitialChunk.headerWord QueryReplayGate.reader code).length = 3 := by decide +example : InitialChunk.chunk QueryReplayGate.reader code (2, 0) = 3 := by decide +example : InitialChunk.chunk QueryReplayGate.reader code (1, 1) = 1 := by decide +example : InitialChunk.chunk QueryReplayGate.reader code (2, 6) = 0 := by decide +example : InitialChunk.chunk QueryReplayGate.reader code (1000000000, 0) = 3 := by decide +example : InitialChunk.chunk QueryReplayGate.reader code (1000000000, 1) = 0 := by decide + +end ComplexityTests.ImplicitBlockPath From c573d2253da09b7b0a63e7e32be865dbf12fe0f8 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 03:39:30 +0000 Subject: [PATCH 27/36] feat(hpv): provide paid implicit node replies --- Complexity.lean | 6 + .../BlockComputation/ImplicitNode.lean | 55 +++++++ .../BlockComputation/ImplicitNodeCorrect.lean | 53 ++++++ .../BlockComputation/InitialVector.lean | 153 ++++++++++++++++++ .../InitialVectorCorrect.lean | 47 ++++++ .../MultiTape/BlockComputation/NodeReply.lean | 71 ++++++++ .../BlockComputation/NodeReplyCorrect.lean | 83 ++++++++++ .../Machines/Turing/MultiTape/NodeReply.lean | 47 ++++++ 8 files changed, 515 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitNode.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitNodeCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialVector.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialVectorCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/NodeReply.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/NodeReplyCorrect.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/NodeReply.lean diff --git a/Complexity.lean b/Complexity.lean index 2973e167e5..99e4a0f225 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1696,12 +1696,18 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeChecked public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeDecode public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeEncode +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitNode +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitNodeCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitPath public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitPathCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitPathLookup public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitPathSpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialChunk public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialChunkCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialVector +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialVectorCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NodeReply +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NodeReplyCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplay public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAccess public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAssignments diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitNode.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitNode.lean new file mode 100644 index 0000000000..95d0f1c30c --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitNode.lean @@ -0,0 +1,55 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitPathLookup + +/-! +# Native classification of implicit block-tree nodes + +Node replies use the existing evaluator's tags: zero for an invalid address, one for a leaf, +and two for a gate. Classification checks the actual raw path before comparing its depth to +the supplied horizon. Source padding remains a gate at internal depths. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ImplicitNode + +open MultiTapeTM MultiTapeTM.ComputableInSpace Cslib.TreeEval.Ary + +/-- Binary schedule/horizon metadata and an actual stored path. -/ +abbrev Args := ImplicitPath.WalkArgs + +/-- The evaluator's existing node-reply tag, forgetting the payload. -/ +@[expose] def kind {d ℓ : ℕ} : Tree.Node d ℓ → ℕ + | .none => 0 + | .leaf _ => 1 + | .gate _ => 2 + +/-- Classify a checked address without constructing its node or the unfolded tree. -/ +@[expose] def classify (k : ℕ) : Args → ℕ := fun (σ, q, path) => + if ImplicitPath.valid k (σ, q, path) then + if path.length = q then 1 else 2 + else 0 + +/-- Native classification charges the actual stored path and binary metadata. -/ +theorem classify_inSpace (k : ℕ) : + ComputableInSpace (classify k) ImplicitPath.encWalkArgs encNat + (fun a => (ImplicitPath.encWalkArgs a).length) := by + have hrest := snd encNat (encPair encNat (encList encNat)) + have hq := hrest.fst' + have hlength := budget_length hrest.snd' + have heq := budget_natEq hlength hq + have hleaf := budget_const (encIn := ImplicitPath.encWalkArgs) (encB := encNat) 1 (.refl _) + have hgate := budget_const (encIn := ImplicitPath.encWalkArgs) (encB := encNat) 2 (.refl _) + have hinvalid := budget_const (encIn := ImplicitPath.encWalkArgs) (encB := encNat) 0 (.refl _) + exact (budget_ite (ImplicitPath.valid_inSpace k) (budget_ite heq hleaf hgate) + hinvalid).of_enc_eq fun ⟨σ, q, path⟩ => by simp only [classify, decide_eq_true_eq] + +end Turing.MultiTapePTM.BlockComputation.ImplicitNode diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitNodeCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitNodeCorrect.lean new file mode 100644 index 0000000000..db32ebdaad --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ImplicitNodeCorrect.lean @@ -0,0 +1,53 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitNode +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.EncodedTree + +/-! +# Implicit node tags agree with the existing Boolean tree + +Classification reports the kind of the existing `encodedTree` node reached by the typed path. +The proof reuses the descending-tree lookup theorem, including uniform-height source padding +and rejection of paths beyond the horizon. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ImplicitNode + +open Cslib.TreeEval.Ary + +/-- Native raw-path classification agrees with the existing encoded tree's node tag. -/ +theorem classify_eq_nodeAt {k q s : ℕ} {Q : Type} + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (σ : ℕ) (path : List (Fin (k * 3 + 1))) : + classify k (σ, q, path.map Fin.val) = + kind ((encodedTree (MultiTapePTM.ofDeterministic M) w b + (scheduleOfNat k q σ) e).nodeAt path) := by + have hall : (path.map Fin.val).all (fun j => decide (j < k * 3 + 1)) = true := by + apply List.all_eq_true.mpr + intro j hj + obtain ⟨p, _, rfl⟩ := List.mem_map.mp hj + exact decide_eq_true p.isLt + have hv : ImplicitPath.valid k (σ, q, path.map Fin.val) = + decide (path.length ≤ q) := by + simp only [ImplicitPath.valid, List.length_map, hall, Bool.and_true] + rw [encodedTree, tree, Descending.nodeAt_tree] + simp only [classify, hv, List.length_map, decide_eq_true_eq, Descending.lookup] + by_cases hlt : path.length < q + · have hle : path.length ≤ q := hlt.le + have hne : path.length ≠ q := by omega + simp only [hlt, hle, hne, ↓reduceIte, kind] + · by_cases heq : path.length = q + · simp [heq, kind] + · have hle : ¬path.length ≤ q := by omega + simp only [hlt, hle, heq, ↓reduceIte, kind] + +end Turing.MultiTapePTM.BlockComputation.ImplicitNode diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialVector.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialVector.lean new file mode 100644 index 0000000000..0330346147 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialVector.lean @@ -0,0 +1,153 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialChunk +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeEncode +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Degree.Bounds +import Complexity.Computability.Machines.Turing.MultiTape.Internal.BinarySizeFacts +import Mathlib.Data.List.OfFn + +/-! +# Paid native initial-source vectors + +A runtime vector length is paid by the actual binary interpolation limit. The implementation +returns at most one coordinate per stored limit bit, so malformed capacity metadata shortens +its result rather than allocating the numeric magnitude of a binary length. Each coordinate +reads only the fixed initial-control prefix. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.InitialVector + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +variable {k s : ℕ} {Q : Type} + +/-- Actual binary capacity, requested chunk width and requested vector length. -/ +abbrev Args := ℕ × ℕ × ℕ + +/-- All runtime registers are charged by their actual binary storage. -/ +@[expose] noncomputable def encArgs : Args ↪ List Bool := + encPair encNat (encPair encNat encNat) + +/-- Initial chunks indexed by an already paid source-word prefix. -/ +@[expose] def values (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : Args → List ℕ := + fun (limit, m, r) => ((Encoding.NativeEncode.word limit 0).take r).mapIdx + fun j _ => InitialChunk.chunk M e (m, j) + +/-- The returned vector cannot exceed either its requested length or actual capacity. -/ +theorem length_values (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (limit m r : ℕ) : + (values M e (limit, m, r)).length = min r limit.size := by + simp only [values, List.length_mapIdx, List.length_take, Encoding.NativeEncode.word, + List.length_ofFn] + +/-- Stored slices agree with an indexed vector of their actual, possibly truncated length. -/ +theorem values_eq_ofFn (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (limit m r : ℕ) : + values M e (limit, m, r) = + List.ofFn (fun j : Fin (min r limit.size) => InitialChunk.chunk M e (m, j)) := by + apply List.ext_getElem + · simp only [length_values, List.length_ofFn] + · intro i hi hi' + simp only [values, List.getElem_mapIdx, List.getElem_ofFn] + +private theorem length_encNat_ofBits_le (word : List Bool) : + (encNat (ofBits word)).length ≤ word.length := by + rw [length_encNat_eq_size, ← Nat.size_eq_bits_len, bits_ofBits] + exact (List.rdropWhile_prefix _ _).length_le + +/-- Source coordinates have bounded encoded size independently of runtime chunk metadata. -/ +theorem length_chunk_le (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (m j : ℕ) : + (encNat (InitialChunk.chunk M e (m, j))).length ≤ (InitialChunk.headerWord M e).length := by + have h := length_encNat_ofBits_le + (((InitialChunk.headerWord M e).drop (j * m)).take m) + change (encNat (InitialChunk.chunk M e (m, j))).length ≤ _ at h + have hl : (((InitialChunk.headerWord M e).drop (j * m)).take m).length ≤ + (InitialChunk.headerWord M e).length := by + simp only [List.length_take, List.length_drop] + omega + exact h.trans hl + +private abbrev BuildState := ℕ × ℕ × List ℕ +private noncomputable def encBuildState : BuildState ↪ List Bool := + encPair encNat (encPair encNat (encList encNat)) + +private def buildStep (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : BuildState → BuildState := + fun (m, j, out) => (m, j + 1, out ++ [InitialChunk.chunk M e (m, j)]) + +private theorem iterate_buildStep (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (m n : ℕ) : + (buildStep M e)^[n] (m, 0, []) = + (m, n, List.ofFn (fun j : Fin n => InitialChunk.chunk M e (m, j))) := by + induction n with + | zero => rfl + | succ n ih => + rw [Function.iterate_succ_apply', ih] + simp only [buildStep] + rw [List.ofFn_succ_last] + rfl + +private theorem buildStep_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (buildStep M e) encBuildState encBuildState + (fun a => (encBuildState a).length) := by + have hm := fst encNat (encPair encNat (encList encNat)) + have hr := snd encNat (encPair encNat (encList encNat)) + have hj := hr.fst' + have hout := hr.snd' + have hchunk := budget_comp_linear (InitialChunk.chunk_inSpace M e) (budget_prod hm hj) + exact budget_prod hm (budget_prod (budget_natSucc hj) (budget_snoc hout hchunk)) + +/-- Initial vector construction is native and linear in actual binary capacity storage. +The constant may depend on the fixed machine/control prefix, never on runtime lengths. -/ +theorem values_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (values M e) encArgs (encList encNat) + (fun a => (encArgs a).length) := by + let H := (InitialChunk.headerWord M e).length + have hlimit := fst encNat (encPair encNat encNat) + have hr := snd encNat (encPair encNat encNat) + have hm := hr.fst' + have hrequested := hr.snd' + have hsize := budget_comp_linear natSize hlimit + have hn := budget_ite (budget_natLt hrequested hsize) hrequested hsize + have hinit := budget_prod hm (budget_prod + (budget_const (encB := encNat) 0 (.refl _)) + (budget_const (encB := encList encNat) [] (.refl _))) + have hstates (a : Args) (i : ℕ) (hi : i ≤ min a.2.2 a.1.size) : + (encBuildState ((buildStep M e)^[i] (a.2.1, 0, []))).length ≤ + 8 * (H + 2) * ((encArgs a).length + 1) := by + rw [iterate_buildStep] + have hout := length_encList_le encNat + (List.ofFn (fun j : Fin i => InitialChunk.chunk M e (a.2.1, j))) + (b := H) (fun value hv => by + obtain ⟨j, rfl⟩ := List.mem_ofFn.mp hv + exact length_chunk_le M e _ _) + have hj := length_encNat_le_self i + have hpaid : i ≤ (encNat a.1).length := by + rw [length_encNat_eq_size] + exact hi.trans (Nat.min_le_right _ _) + simp only [encBuildState, encArgs, length_encPair, List.length_ofFn] at * + nlinarith + have hrun := ComputableInSpace.iterate + (n := fun a : Args => min a.2.2 a.1.size) + (init := fun a : Args => (a.2.1, 0, [])) (step := buildStep M e) + (hn.of_enc_eq fun a => by + by_cases h : a.2.2 < a.1.size + · simp [h, Nat.min_eq_left h.le] + · simp [h, Nat.min_eq_right (Nat.le_of_not_gt h)]) + hinit (buildStep_inSpace M e) hstates hstates + refine hrun.snd'.snd'.weaken + (.of_le_linear (100 * (H + 2)) (100 * (H + 2)) fun a => by + have hpaid : min a.2.2 a.1.size ≤ (encNat a.1).length := by + simpa only [length_encNat_eq_size] using Nat.min_le_right a.2.2 a.1.size + have hc := (length_encNat_le_self (min a.2.2 a.1.size)).trans hpaid + simp only [encArgs, length_encPair] at * + nlinarith) |>.of_enc_eq ?_ + rintro ⟨limit, m, r⟩ + rw [iterate_buildStep, values_eq_ofFn] + +end Turing.MultiTapePTM.BlockComputation.InitialVector diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialVectorCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialVectorCorrect.lean new file mode 100644 index 0000000000..3f9871d614 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialVectorCorrect.lean @@ -0,0 +1,47 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialVector +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialChunkCorrect + +/-! +# Correct paid initial-source vectors + +Sufficient actual capacity yields every requested initial coordinate. These are the existing +packed source values, including partial final chunks; no alternate leaf semantics is used. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.InitialVector + +open Cslib.TreeEval.Ary + +variable {k s : ℕ} {Q : Type} + +/-- Sufficient binary capacity recovers the full requested initial vector. -/ +theorem values_of_capacity (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (limit m r : ℕ) (hcapacity : r ≤ limit.size) : + values M e (limit, m, r) = + List.ofFn (fun j : Fin r => InitialChunk.chunk M e (m, j)) := by + rw [values_eq_ofFn, Nat.min_eq_left hcapacity] + +/-- The full paid leaf vector is exactly the existing packed initial view. -/ +theorem values_eq_packBits {r m : ℕ} (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (limit b : ℕ) [NeZero b] (hcapacity : r ≤ limit.size) + (hb : Encoding.width k s b ≤ r * m) : + values M e (limit, m, r) = List.ofFn (fun j : Fin r => + (Packed.packBits hb + (Encoding.encode e (some (initialView (MultiTapePTM.ofDeterministic M) b))) j).val) := by + rw [values_of_capacity M e limit m r hcapacity] + congr 1 + funext j + exact InitialChunk.chunk_eq_packBits M e b hb j + +end Turing.MultiTapePTM.BlockComputation.InitialVector diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/NodeReply.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/NodeReply.lean new file mode 100644 index 0000000000..467b902af9 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/NodeReply.lean @@ -0,0 +1,71 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitNode +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialVector +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerNative + +/-! +# Native node replies for the implicit block evaluator + +Node replies use the evaluator's existing tags: invalid zero, leaf one and gate two. A leaf +returns its packed source vector only when actual assignment-capacity storage pays its runtime +length. Gates return only a tag; gate interpolation is a separate supplier operation. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.NodeReply + +open MultiTapeTM MultiTapeTM.ComputableInSpace +open Cslib.TreeEval.Ary.Packed + +variable {k s : ℕ} {Q : Type} + +/-- Actual path metadata and the paid source-vector parameters. -/ +abbrev Args := ImplicitPath.WalkArgs × InitialVector.Args + +/-- The raw path, schedule and all binary capacity/packing data are charged explicitly. -/ +@[expose] noncomputable def encArgs : Args ↪ List Bool := + encPair ImplicitPath.encWalkArgs InitialVector.encArgs + +/-- Produce exactly the raw evaluator's node-reply format, rejecting unpaid leaf vectors. -/ +@[expose] def reply (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : Args → RawController.Reply := + fun (address, (limit, m, r)) => + let tag := ImplicitNode.classify k address + if tag = 1 then + if r ≤ limit.size then (1, InitialVector.values M e (limit, m, r)) else (0, []) + else (tag, []) + +/-- Classification and paid leaf construction are native in actual encoded-argument space. -/ +theorem reply_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (reply M e) encArgs RawController.encReply + (fun a => (encArgs a).length) := by + have ha := fst ImplicitPath.encWalkArgs InitialVector.encArgs + have hv := snd ImplicitPath.encWalkArgs InitialVector.encArgs + have htag := budget_comp_linear (ImplicitNode.classify_inSpace k) ha + have hleaf := budget_natEq htag (budget_const (encB := encNat) 1 (.refl _)) + have hlimit := hv.fst' + have hr := hv.snd'.snd' + have hsize := budget_comp_linear natSize hlimit + have hcapacity := budget_finite (encC := encBool) Bool.not (budget_natLt hsize hr) + have hvalues := budget_comp_linear (InitialVector.values_inSpace M e) hv + have hempty := budget_const (encIn := encArgs) (encB := encList encNat) [] (.refl _) + have hsuccess := budget_prod (budget_const (encB := encNat) 1 (.refl _)) hvalues + have hfailure := budget_prod (budget_const (encB := encNat) 0 (.refl _)) hempty + have hother := budget_prod htag hempty + refine (budget_ite hleaf (budget_ite hcapacity hsuccess hfailure) hother).of_enc_eq ?_ + rintro ⟨address, limit, m, r⟩ + by_cases hc : r ≤ limit.size + · have hn : ¬limit.size < r := by omega + simp [reply, hc, hn] + · have hn : limit.size < r := by omega + simp [reply, hc, hn] + +end Turing.MultiTapePTM.BlockComputation.NodeReply diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/NodeReplyCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/NodeReplyCorrect.lean new file mode 100644 index 0000000000..1a429bdcd2 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/NodeReplyCorrect.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NodeReply +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitNodeCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialVectorCorrect +import Complexity.Foundations.Data.List.Lookup + +/-! +# Native node replies agree with the existing encoded tree + +On typed paths, paid node replies have exactly the existing tree's invalid/leaf/gate tags. +Every coordinate of a leaf reply is the existing packed leaf value. The capacity condition +pays the full runtime leaf-vector length; classification alone never authorizes allocation. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.NodeReply + +open Cslib.TreeEval.Ary + +/-- The native node reply reports the existing node kind and, for leaves, every packed value. -/ +theorem reply_correct_nodeAt {k q s r m : ℕ} {Q : Type} + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (σ limit : ℕ) (path : List (Fin (k * 3 + 1))) + (hcapacity : r ≤ limit.size) (hb : Encoding.width k s b ≤ r * m) : + match (encodedTree (MultiTapePTM.ofDeterministic M) w b + (scheduleOfNat k q σ) e).nodeAt path with + | .none => (reply M e ((σ, q, path.map Fin.val), (limit, m, r))).1 = 0 + | .gate _ => (reply M e ((σ, q, path.map Fin.val), (limit, m, r))).1 = 2 + | .leaf v => (reply M e ((σ, q, path.map Fin.val), (limit, m, r))).1 = 1 ∧ + ∀ j : Fin r, + ((reply M e ((σ, q, path.map Fin.val), (limit, m, r))).2).getD j.val 0 = + (Packed.packBits hb v j).val := by + have hclass := ImplicitNode.classify_eq_nodeAt (q := q) M e w b σ path + rw [encodedTree, tree, Descending.nodeAt_tree] at hclass ⊢ + by_cases hlt : path.length < q + · simp only [Descending.lookup, hlt, ↓reduceIte, ImplicitNode.kind] at hclass ⊢ + simp [reply, hclass] + · by_cases heq : path.length = q + · simp only [Descending.lookup, heq, lt_self_iff_false, ↓reduceIte, + ImplicitNode.kind] at hclass ⊢ + constructor + · simp [reply, hclass, hcapacity] + · intro j + simp only [reply, hclass, hcapacity, ↓reduceIte] + rw [InitialVector.values_eq_packBits M e limit b hcapacity hb] + exact List.getD_ofFn_of_lt _ j.isLt 0 + · simp only [Descending.lookup, hlt, heq, ↓reduceIte, ImplicitNode.kind] at hclass ⊢ + simp [reply, hclass] + +/-- A call-mode evaluator state receives exactly the existing node-reply contract. -/ +theorem reply_correct_call {k q s r m p : ℕ} {Q : Type} [Fact p.Prime] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (σ limit : ℕ) (hcapacity : r ≤ limit.size) + (hb : Encoding.width k s b ≤ r * m) + (E : Gate (k * 3 + 1) (Encoding.width k s b) → + (Fin (k * 3 + 1) → Fin r → ZMod p) → Fin r → ZMod p) + (cfg : Iter.Cfg (ZMod p) (k * 3 + 1) r) (c : ZMod p) + (target : Fin (k * 3 + 1 + 1)) (hmode : cfg.mode = .call c target) : + Packed.RawController.ReplyCorrect + (fun bits j => ((Packed.packBits hb bits j).val : ZMod p)) E + (encodedTree (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) e).nodeAt cfg + (reply M e ((σ, q, (Iter.path cfg.stack).map Fin.val), (limit, m, r))) := by + rw [Packed.RawController.ReplyCorrect, hmode] + have hs := reply_correct_nodeAt (q := q) M e w b σ limit (Iter.path cfg.stack) hcapacity hb + cases hn : (encodedTree (MultiTapePTM.ofDeterministic M) w b + (scheduleOfNat k q σ) e).nodeAt (Iter.path cfg.stack) with + | none => simpa only [hn] using hs + | gate f => simpa only [hn] using hs + | leaf v => + simp only [hn] at hs + exact ⟨hs.1, fun j => congrArg (fun n : ℕ => (n : ZMod p)) (hs.2 j)⟩ + +end Turing.MultiTapePTM.BlockComputation.NodeReply diff --git a/ComplexityTests/Machines/Turing/MultiTape/NodeReply.lean b/ComplexityTests/Machines/Turing/MultiTape/NodeReply.lean new file mode 100644 index 0000000000..259f547abf --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/NodeReply.lean @@ -0,0 +1,47 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of paid implicit node replies) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NodeReplyCorrect +public import ComplexityTests.Machines.Turing.MultiTape.QueryReplayGate + +/-! +# Paid node-reply regressions + +Leaves return the complete packed initial value; gates return only their tag. Invalid paths +and insufficient capacity are rejected. Large requested vector lengths cannot allocate their +numeric magnitude from short binary metadata. +-/ + +public section + +namespace ComplexityTests.NodeReply + +open Turing MultiTapePTM.BlockComputation +open NativeBlockCodec + +def leafArgs : NodeReply.Args := ((0, 0, []), (2 ^ 14, 2, 7)) + +example : InitialVector.values QueryReplayGate.reader code (2 ^ 14, 2, 7) = + [3, 0, 0, 0, 0, 0, 0] := by decide +example : InitialVector.values QueryReplayGate.reader code (1, 2, 1000000000) = [3] := by decide +example : InitialVector.values QueryReplayGate.reader code (0, 2, 1000000000) = [] := by decide +example : NodeReply.reply QueryReplayGate.reader code leafArgs = + (1, [3, 0, 0, 0, 0, 0, 0]) := by decide +example : NodeReply.reply QueryReplayGate.reader code ((0, 1, []), (0, 2, 7)) = + (2, []) := by decide +example : NodeReply.reply QueryReplayGate.reader code ((0, 1, [0]), (2 ^ 14, 2, 7)) = + (1, [3, 0, 0, 0, 0, 0, 0]) := by decide +example : NodeReply.reply QueryReplayGate.reader code ((0, 1, [4]), (2 ^ 14, 2, 7)) = + (0, []) := by decide +example : NodeReply.reply QueryReplayGate.reader code ((0, 1, [0, 0]), (2 ^ 14, 2, 7)) = + (0, []) := by decide +example : NodeReply.reply QueryReplayGate.reader code ((0, 0, []), (1, 2, 1000000000)) = + (0, []) := by decide + +end ComplexityTests.NodeReply From be04add1a12504e20acde81c47d2a895c7157815 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 03:52:57 +0000 Subject: [PATCH 28/36] feat(hpv): interpolate native packed gate answers --- Complexity.lean | 3 + .../BlockComputation/GateInterpolation.lean | 160 ++++++++++++++++++ .../GateInterpolationBounds.lean | 126 ++++++++++++++ .../GateInterpolationCorrect.lean | 131 ++++++++++++++ .../Turing/MultiTape/GateInterpolation.lean | 49 ++++++ 5 files changed, 469 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateInterpolation.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateInterpolationBounds.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateInterpolationCorrect.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/GateInterpolation.lean diff --git a/Complexity.lean b/Complexity.lean index 99e4a0f225..6318978916 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1696,6 +1696,9 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeChecked public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeDecode public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeEncode +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateInterpolation +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateInterpolationBounds +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateInterpolationCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitNode public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitNodeCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitPath diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateInterpolation.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateInterpolation.lean new file mode 100644 index 0000000000..383b4501a5 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateInterpolation.lean @@ -0,0 +1,160 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import + Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierChunkInit +public import Complexity.Computability.TreeEval.Ary.Packed.QueryGateSpace +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Sum + +/-! +# Native interpolation with checked block-table calls + +The interpolation loop retains actual binary simulation metadata and asks the checked numeric +supplier for one table value at each assignment counter. The supplier receives the loop's +stored limit and current counter, rather than a precomputed table. The original input remains +the shared query input throughout both machines. + +The fixed template initializes only an unused default state. Every runtime parameter is read +from an explicitly supplied caller state; changing the template does not affect `runFrom`. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Interpolation + +open MultiTapeTM MultiTapeTM.ComputableInSpace +open Cslib.TreeEval.Ary.Packed + +variable {k s : ℕ} {Q : Type} + +/-- Input length, block width, schedule, interval, chunk width/index and padded child stride. -/ +abbrev Context := ℕ × ℕ × ℕ × ℕ × ℕ × ℕ × ℕ + +/-- The actual binary supplier metadata is charged beside the interpolation point. -/ +@[expose] noncomputable def encContext : Context ↪ List Bool := + encPair encNat (encPair encNat (encPair encNat (encPair encNat + (encPair encNat (encPair encNat encNat))))) + +/-- A running interpolation caller, or that caller suspended beside one table supplier. -/ +abbrev State (k : ℕ) (Q : Type) := + QueryGate.State Context ⊕ (QueryGate.State Context × SupplierChunk.State k Q) + +/-- Initialize the next actual table request from stored metadata, limit and counter. -/ +@[expose] noncomputable def start (e : Q ≃ Fin s) : QueryGate.State Context → + SupplierChunk.State k Q := + fun (((N, b, σ, i, m, chunk, stride), _, _, limit), counter, _) => + SupplierChunk.prepare e ((m, chunk), (N, b, σ, i, limit, counter, stride)) + +/-- Default metadata affects only `init`; actual interpolation states are prepared separately. -/ +@[expose] def template : QueryGate.Params Context := + ((0, 0, 0, 0, 0, 0, 0), (0, 0, 0), [], 0) + +/-- Fresh caller counters for actual prepared parameters, rather than the template metadata. -/ +@[expose] def initial : QueryGate.Params Context → State k Q := + fun q => .inl (QueryGate.initial q) + +/-- One fixed machine executes interpolation using actual runtime parameters in its state. -/ +@[expose] noncomputable def machine (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + QueryMachine (State k Q) := + QueryGate.machine template (SupplierChunk.machine M e) (start e) (SupplierChunk.answer M e) + +variable [Finite Q] + +/-- Both the suspended caller and the complete numeric supplier storage are explicit. -/ +@[expose] noncomputable def encState : State k Q ↪ List Bool := + QueryGate.encMachineState encContext (SupplierChunk.encState (k := k) (Q := Q)) + +/-- Actual interpolation parameters initialize their caller state in native linear space. -/ +theorem initial_inSpace : ComputableInSpace (initial (k := k) (Q := Q)) + (QueryGate.encParams encContext) encState + (fun a => (QueryGate.encParams encContext a).length) := by + have hid : ComputableInSpace (fun q : QueryGate.Params Context => q) + (QueryGate.encParams encContext) (QueryGate.encParams encContext) + (fun q => (QueryGate.encParams encContext q).length) := + of_inO (ComputableInO.of_eq (f := id) fun _ => rfl) + have hcaller := budget_prod hid (budget_prod + (budget_const (encB := encNat) 0 (.refl _)) + (budget_const (encB := encNat) 0 (.refl _))) + exact (budget_comp_linear + (of_inO (ComputableInO.inl (QueryGate.encState encContext) + (encPair (QueryGate.encState encContext) (SupplierChunk.encState (k := k) (Q := Q))))) + hcaller).of_enc_eq fun _ => rfl + +/-- Preparing each table request uses native space linear in the actual caller storage. -/ +theorem start_inSpace (e : Q ≃ Fin s) : + ComputableInSpace (start (k := k) e) (QueryGate.encState encContext) + SupplierChunk.encState (fun a => (QueryGate.encState encContext a).length) := by + have hpar := fst (QueryGate.encParams encContext) (encPair encNat encNat) + have hctx := hpar.fst' + have hN := hctx.fst' + have hb := hctx.snd'.fst' + have hσ := hctx.snd'.snd'.fst' + have hi := hctx.snd'.snd'.snd'.fst' + have hm := hctx.snd'.snd'.snd'.snd'.fst' + have hchunk := hctx.snd'.snd'.snd'.snd'.snd'.fst' + have hstride := hctx.snd'.snd'.snd'.snd'.snd'.snd' + have hlimit := hpar.snd'.snd'.snd' + have hcounter := (snd (QueryGate.encParams encContext) (encPair encNat encNat)).fst' + exact (budget_comp_linear (SupplierChunk.prepare_inSpace (k := k) e) + (budget_prod (budget_prod hm hchunk) + (budget_prod hN (budget_prod hb (budget_prod hσ (budget_prod hi + (budget_prod hlimit (budget_prod hcounter hstride)))))))).of_enc_eq fun _ => rfl + +/-- Queries include native initialization of the requested checked table call. -/ +theorem machine_query_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).query encState encNat + (fun a => (encState a).length) := + QueryGate.machine_query_inSpace encContext SupplierChunk.encState template + (SupplierChunk.machine M e) (start e) (SupplierChunk.answer M e) + (SupplierChunk.machine_query_inSpace M e) (start_inSpace e) + +/-- A local transition includes initialization, table steps and extracting the returned value. -/ +theorem machine_next_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).next (encPair encState (encOption encBool)) encState + (fun a => (encPair encState (encOption encBool) a).length) := + QueryGate.machine_next_inSpace encContext SupplierChunk.encState template + (SupplierChunk.machine M e) (start e) (SupplierChunk.answer M e) + (SupplierChunk.machine_next_inSpace M e) (SupplierChunk.machine_halted_inSpace M e) + (start_inSpace e) (SupplierChunk.answer_inSpace M e) + +/-- The interpolation/table composition emits no intermediate output. -/ +theorem machine_emit_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).emit (encPair encState (encOption encBool)) encBits + (fun a => (encPair encState (encOption encBool) a).length) := + QueryGate.machine_emit_inSpace encContext SupplierChunk.encState template + (SupplierChunk.machine M e) (start e) (SupplierChunk.answer M e) (start_inSpace e) + +/-- The supplied runtime limit controls completion. -/ +theorem machine_halted_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).halted encState encBool + (fun a => (encState a).length) := + QueryGate.machine_halted_inSpace encContext SupplierChunk.encState template + (SupplierChunk.machine M e) (start e) (SupplierChunk.answer M e) + +/-- The modular sum remains in the returned caller state, with an empty finish stream. -/ +theorem machine_finish_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).finish encState encBits + (fun a => (encState a).length) := + QueryGate.machine_finish_inSpace encContext SupplierChunk.encState template + (SupplierChunk.machine M e) (start e) (SupplierChunk.answer M e) + +omit [Finite Q] in +/-- The fixed template has no effect on executions from actual runtime caller states. -/ +theorem machine_runFrom_eq_params (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (q : QueryGate.Params Context) (w : List Bool) (state : State k Q) (t : ℕ) : + (machine M e).runFrom w state t = + (QueryGate.machine q (SupplierChunk.machine M e) (start e) + (SupplierChunk.answer M e)).runFrom w state t := by + induction t with + | zero => rfl + | succ t ih => + rw [QueryMachine.runFrom_succ, ih, QueryMachine.runFrom_succ] + rfl + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Interpolation diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateInterpolationBounds.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateInterpolationBounds.lean new file mode 100644 index 0000000000..e2db10b967 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateInterpolationBounds.lean @@ -0,0 +1,126 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateInterpolationCorrect + +/-! +# Physical space bounds for native gate interpolation + +Every assignment call is charged for its retained chunk metadata and actual local replay. +The full interpolation loop retains one caller and one callee, independent of the number of +assignments. The bound covers every physical clock, including the table subroutine's clocks. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Interpolation + +open MultiTapeTM MultiTapeNTM.ChoiceTable Cslib.TreeEval.Ary +open Cslib.TreeEval.Ary.Packed + +variable {k s : ℕ} {Q : Type} [Finite Q] + +/-- Space charged to one native table call, independent of its assignment counter. -/ +@[expose] noncomputable def calleeSpace (k s : ℕ) (Q : Type) (context : Context) : ℕ := + let (N, b, σ, i, m, chunk, _) := context + let (positive, negative) := ScheduleRead.origin k 0 (σ, i) + 2 * (SupplierChunk.encContext (m, chunk)).length + 1 + (Encoding.width k s b + 1) + + (2 * (Gate.encContext (positive, negative, ScheduleRead.fullrow k (σ, i))).length + 2 + + 1024 * (k + 1) * (b + Nat.log2 N + Nat.card (Option Q) + Nat.card OutputTrack + 1)) + +/-- The completed native interpolation and its actual physical states share one bound. -/ +theorem exists_runFrom_counterGate_bounded {p q r m : ℕ} (M : MultiTapeTM k Bool Q) + (e : Q ≃ Fin s) (w : List Bool) (b : ℕ) [NeZero b] (σ : ℕ) (i : Fin q) + (x : Fin (k * 3 + 1) → Fin r → ZMod p) (hp : 0 < p) + (hb : Encoding.width k s b ≤ r * m) (hm : 0 < m) (chunk : Fin r) : + let f := codeGate (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) + (Encoding.encode e) (Encoding.decode e) (i.val + 1) + let context : Context := (w.length, b, σ, i.val, m, chunk.val, r * m) + let params := QueryGate.gateParams (s := 2 ^ m) context x + ∃ T z, (machine M e).runFrom w (initial params) T = + .inl (params, (2 ^ m) ^ ((k * 3 + 1) * r), z) ∧ + (z : ZMod p) = Packed.counterGateExtension (by positivity : 0 < 2 ^ m) + (Packed.packBits hb) (Packed.unpackBits hb) f x chunk ∧ + (machine M e).halted ((machine M e).runFrom w (initial params) T) = true ∧ + ∀ t ≤ T, (encState ((machine M e).runFrom w (initial params) t)).length ≤ + 16 * ((QueryGate.encParams encContext params).length + 1) + + calleeSpace k s Q context + 2 := by + let f := codeGate (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) + (Encoding.encode e) (Encoding.decode e) (i.val + 1) + let context : Context := (w.length, b, σ, i.val, m, chunk.val, r * m) + let params := QueryGate.gateParams (s := 2 ^ m) context x + let table := QueryGate.gateTable (by positivity : 0 < 2 ^ m) + (Packed.packBits hb) (Packed.unpackBits hb) f chunk + have hsupplier : ∀ j < params.2.2.2, ∃ T, + (∀ t < T, (SupplierChunk.machine M e).halted ((SupplierChunk.machine M e).runFrom w + (start e (QueryGate.sumState params table j)) t) = false) ∧ + (SupplierChunk.machine M e).halted ((SupplierChunk.machine M e).runFrom w + (start e (QueryGate.sumState params table j)) T) = true ∧ + SupplierChunk.answer M e ((SupplierChunk.machine M e).runFrom w + (start e (QueryGate.sumState params table j)) T) = table j ∧ + (∀ t ≤ T, (SupplierChunk.encState ((SupplierChunk.machine M e).runFrom w + (start e (QueryGate.sumState params table j)) t)).length ≤ calleeSpace k s Q context) := by + intro j hj + obtain ⟨T, hpre, hhalt, hans⟩ := table_supplier M e w b σ i x hb hm chunk j hj + refine ⟨T, hpre, hhalt, hans, ?_⟩ + intro t _ht + rw [start_sumState] + exact SupplierChunk.length_runFrom_prepare_le M e w b σ i + ((2 ^ m) ^ ((k * 3 + 1) * r)) j (r * m) m chunk t + obtain ⟨T, hrun, hsize⟩ := QueryGate.exists_runFrom_sumState_encoding_le + encContext (SupplierChunk.encState (k := k) (Q := Q)) params (SupplierChunk.machine M e) + (start e) (SupplierChunk.answer M e) w table (calleeSpace k s Q context) hp hsupplier + params.2.2.2 le_rfl + have hfinal : QueryGate.sumState params table params.2.2.2 = + (params, params.2.2.2, + interpolateGrid p (2 ^ m) ((k * 3 + 1) * r) table params.2.2.1) := by + apply Prod.ext + · rfl + · apply Prod.ext + · exact QueryGate.sumState_counter params table params.2.2.2 + · rfl + have hactual : (machine M e).runFrom w (initial params) T = + .inl (params, params.2.2.2, + interpolateGrid p (2 ^ m) ((k * 3 + 1) * r) table params.2.2.1) := by + rw [machine_runFrom_eq_params M e params w (initial params) T] + simpa only [initial, hfinal] using hrun + refine ⟨T, _, hactual, rfl, ?_, ?_⟩ + · rw [hactual] + change decide (params.2.2.2 ≤ params.2.2.2) = true + simp + · intro t ht + rw [machine_runFrom_eq_params M e params w (initial params) t] + exact hsize t ht + +/-- The selected-field gate extension and its whole physical execution have the same bound. -/ +theorem exists_runFrom_gateExtension_bounded {p q r m : ℕ} [Fact p.Prime] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (σ : ℕ) (i : Fin q) + (x : Fin (k * 3 + 1) → Fin r → ZMod p) + (hb : Encoding.width k s b ≤ r * m) (hm : 0 < m) (chunk : Fin r) + (hsp : 2 ^ m ≤ p) (h : Fin (2 ^ m) ↪ ZMod p) + (hh : ∀ a, h a = (a.val : ZMod p)) : + let f := codeGate (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) + (Encoding.encode e) (Encoding.decode e) (i.val + 1) + let context : Context := (w.length, b, σ, i.val, m, chunk.val, r * m) + let params := QueryGate.gateParams (s := 2 ^ m) context x + ∃ T z, (machine M e).runFrom w (initial params) T = + .inl (params, (2 ^ m) ^ ((k * 3 + 1) * r), z) ∧ + (z : ZMod p) = Packed.gateExtension h (Packed.packBits hb) (Packed.unpackBits hb) + f x chunk ∧ + (machine M e).halted ((machine M e).runFrom w (initial params) T) = true ∧ + ∀ t ≤ T, (encState ((machine M e).runFrom w (initial params) t)).length ≤ + 16 * ((QueryGate.encParams encContext params).length + 1) + + calleeSpace k s Q context + 2 := by + obtain ⟨T, z, hrun, hz, hhalt, hsize⟩ := exists_runFrom_counterGate_bounded M e w b σ i x + (Fact.out : p.Prime).pos hb hm chunk + refine ⟨T, z, hrun, ?_, hhalt, hsize⟩ + exact hz.trans (Packed.counterGateExtension_eq (by positivity) hsp h hh _ _ _ _ _) + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Interpolation diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateInterpolationCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateInterpolationCorrect.lean new file mode 100644 index 0000000000..8f4807754e --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateInterpolationCorrect.lean @@ -0,0 +1,131 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateInterpolation +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierChunkTable + +/-! +# Same-input interpolation of the implicit block gate + +The actual numeric table supplier discharges every assignment call of the existing packed +interpolation loop. The fixed machine starts from runtime parameters, and its final accumulator +is the existing counter gate extension. Neither the gate table nor a runtime-dependent machine +is supplied by an oracle. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.Interpolation + +open MultiTapeTM Cslib.TreeEval.Ary +open Cslib.TreeEval.Ary.Packed + +variable {k s : ℕ} {Q : Type} + +/-- A real caller prefix initializes its table request from its stored assignment counter. -/ +theorem start_sumState {p r m : ℕ} (e : Q ≃ Fin s) (N b σ i chunk stride : ℕ) + (x : Fin (k * 3 + 1) → Fin r → ZMod p) (table : ℕ → ℕ) (j : ℕ) : + start (k := k) e (QueryGate.sumState + (QueryGate.gateParams (s := 2 ^ m) (N, b, σ, i, m, chunk, stride) x) table j) = + SupplierChunk.prepare e ((m, chunk), + (N, b, σ, i, (2 ^ m) ^ ((k * 3 + 1) * r), j, stride)) := by + change SupplierChunk.prepare e ((m, chunk), + (N, b, σ, i, (2 ^ m) ^ ((k * 3 + 1) * r), + (QueryGate.sumState + (QueryGate.gateParams (s := 2 ^ m) (N, b, σ, i, m, chunk, stride) x) table j).2.1, + stride)) = _ + rw [QueryGate.sumState_counter] + +/-- Every actual interpolation assignment is answered by the original-input native supplier. +The contract includes the first-return clock required by the same-input call rule. -/ +theorem table_supplier {p q r m : ℕ} (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (b : ℕ) [NeZero b] (σ : ℕ) (i : Fin q) + (x : Fin (k * 3 + 1) → Fin r → ZMod p) + (hb : Encoding.width k s b ≤ r * m) (hm : 0 < m) (chunk : Fin r) : + let f := codeGate (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) + (Encoding.encode e) (Encoding.decode e) (i.val + 1) + let table := QueryGate.gateTable (by positivity : 0 < 2 ^ m) + (Packed.packBits hb) (Packed.unpackBits hb) f chunk + let params := QueryGate.gateParams (s := 2 ^ m) + (w.length, b, σ, i.val, m, chunk.val, r * m) x + ∀ j < (2 ^ m) ^ ((k * 3 + 1) * r), ∃ T, + (∀ t < T, (SupplierChunk.machine M e).halted ((SupplierChunk.machine M e).runFrom w + (start e (QueryGate.sumState params table j)) t) = false) ∧ + (SupplierChunk.machine M e).halted ((SupplierChunk.machine M e).runFrom w + (start e (QueryGate.sumState params table j)) T) = true ∧ + SupplierChunk.answer M e ((SupplierChunk.machine M e).runFrom w + (start e (QueryGate.sumState params table j)) T) = table j := by + dsimp only + intro j _hj + obtain ⟨T, _hle, hhalt, hpre, hstate⟩ := SupplierChunk.exists_first_return M e w + w.length b σ i ((2 ^ m) ^ ((k * 3 + 1) * r)) j (r * m) m chunk + refine ⟨T, ?_, ?_, ?_⟩ + · simpa only [start_sumState] using hpre + · simpa only [start_sumState] using hhalt + · rw [start_sumState, hstate] + exact SupplierChunk.answer_grid_eq_codeGate M e w b σ i j hb hm chunk + +/-- One fixed native machine completes the actual packed counter extension for an interval +and requested output chunk, with no external table-supplier premise. -/ +theorem exists_runFrom_counterGate {p q r m : ℕ} (M : MultiTapeTM k Bool Q) + (e : Q ≃ Fin s) (w : List Bool) (b : ℕ) [NeZero b] (σ : ℕ) (i : Fin q) + (x : Fin (k * 3 + 1) → Fin r → ZMod p) + (hb : Encoding.width k s b ≤ r * m) (hm : 0 < m) (chunk : Fin r) : + let f := codeGate (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) + (Encoding.encode e) (Encoding.decode e) (i.val + 1) + let params := QueryGate.gateParams (s := 2 ^ m) + (w.length, b, σ, i.val, m, chunk.val, r * m) x + ∃ T z, (machine M e).runFrom w (initial params) T = + .inl (params, (2 ^ m) ^ ((k * 3 + 1) * r), z) ∧ + (z : ZMod p) = Packed.counterGateExtension (by positivity : 0 < 2 ^ m) + (Packed.packBits hb) (Packed.unpackBits hb) f x chunk ∧ + (machine M e).halted ((machine M e).runFrom w (initial params) T) = true := by + let f := codeGate (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) + (Encoding.encode e) (Encoding.decode e) (i.val + 1) + let params := QueryGate.gateParams (s := 2 ^ m) + (w.length, b, σ, i.val, m, chunk.val, r * m) x + obtain ⟨T, z, hrun, hz⟩ := QueryGate.exists_runFrom_counterGateExtension + (by positivity : 0 < 2 ^ m) + (w.length, b, σ, i.val, m, chunk.val, r * m) + (Packed.packBits hb) (Packed.unpackBits hb) f x chunk + (SupplierChunk.machine M e) (start e) (SupplierChunk.answer M e) w + (table_supplier M e w b σ i x hb hm chunk) + have hactual : (machine M e).runFrom w (initial params) T = + .inl (params, (2 ^ m) ^ ((k * 3 + 1) * r), z) := by + rw [machine_runFrom_eq_params M e params w (initial params) T] + exact hrun + refine ⟨T, z, hactual, hz, ?_⟩ + rw [hactual] + change decide ((2 ^ m) ^ ((k * 3 + 1) * r) ≤ + (2 ^ m) ^ ((k * 3 + 1) * r)) = true + simp + +/-- In a selected prime field containing the unsigned chunk alphabet, actual interpolation +returns the existing packed low-degree gate extension. -/ +theorem exists_runFrom_gateExtension {p q r m : ℕ} [Fact p.Prime] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (σ : ℕ) (i : Fin q) + (x : Fin (k * 3 + 1) → Fin r → ZMod p) + (hb : Encoding.width k s b ≤ r * m) (hm : 0 < m) (chunk : Fin r) + (hsp : 2 ^ m ≤ p) (h : Fin (2 ^ m) ↪ ZMod p) + (hh : ∀ a, h a = (a.val : ZMod p)) : + let f := codeGate (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) + (Encoding.encode e) (Encoding.decode e) (i.val + 1) + let params := QueryGate.gateParams (s := 2 ^ m) + (w.length, b, σ, i.val, m, chunk.val, r * m) x + ∃ T z, (machine M e).runFrom w (initial params) T = + .inl (params, (2 ^ m) ^ ((k * 3 + 1) * r), z) ∧ + (z : ZMod p) = Packed.gateExtension h (Packed.packBits hb) (Packed.unpackBits hb) + f x chunk ∧ + (machine M e).halted ((machine M e).runFrom w (initial params) T) = true := by + obtain ⟨T, z, hrun, hz, hhalt⟩ := exists_runFrom_counterGate M e w b σ i x hb hm chunk + refine ⟨T, z, hrun, ?_, hhalt⟩ + exact hz.trans (Packed.counterGateExtension_eq (by positivity) hsp h hh _ _ _ _ _) + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.Interpolation diff --git a/ComplexityTests/Machines/Turing/MultiTape/GateInterpolation.lean b/ComplexityTests/Machines/Turing/MultiTape/GateInterpolation.lean new file mode 100644 index 0000000000..b452593f86 --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/GateInterpolation.lean @@ -0,0 +1,49 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of native same-input gate interpolation) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateInterpolationBounds + +/-! +# Native interpolation caller regressions + +Actual parameters override the fixed halted template. The first two assignments exercise an +immediately rejected label and a live one-step replay with halted control. The caller retains +one modular accumulator rather than a table of assignments. +-/ + +public section + +namespace ComplexityTests.GateInterpolation + +open Turing MultiTapeTM MultiTapePTM.BlockComputation +open MultiTapePTM.BlockComputation.QueryReplay +open Cslib.TreeEval.Ary.Packed + +abbrev code : Fin 1 ≃ Fin 1 := Equiv.refl _ + +def haltedControl : MultiTapeTM 0 Bool (Fin 1) := MultiTapeTM.ofTr 0 fun _ _ _ => + { inputTape := .zero, workTapes := fun _ => (none, .zero), output := none, state := none } + +def params : QueryGate.Params Interpolation.Context := + ((1, 1, 2, 0, 1, 0, 13), (17, 2, 13), List.replicate 13 0, 8192) + +def observe : Interpolation.State 0 (Fin 1) → Option (ℕ × ℕ) := + Sum.elim (fun q => some q.2) (fun _ => none) + +example : (Interpolation.machine haltedControl code).halted + (Interpolation.machine haltedControl code).init = true := by decide +example : (Interpolation.machine haltedControl code).halted + (Interpolation.initial params) = false := by decide +example : observe ((Interpolation.machine haltedControl code).runFrom [true] + (Interpolation.initial params) 2) = some (1, 0) := by decide +example : observe ((Interpolation.machine haltedControl code).runFrom [true] + (Interpolation.initial params) 5) = some (2, 0) := by decide +example : (Interpolation.start (k := 0) code (params, 1, 0)).1 = (1, 0) := rfl + +end ComplexityTests.GateInterpolation From 1c61d16822d18e54626771051450ee479cd1f1f8 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 04:09:15 +0000 Subject: [PATCH 29/36] feat(hpv): stream complete native gate vectors --- Complexity.lean | 4 + .../BlockComputation/GateVector.lean | 262 ++++++++++++++++++ .../BlockComputation/GateVectorBounds.lean | 255 +++++++++++++++++ .../BlockComputation/GateVectorCorrect.lean | 183 ++++++++++++ .../BlockComputation/NormalizePoint.lean | 122 ++++++++ .../Machines/Turing/MultiTape/GateVector.lean | 68 +++++ 6 files changed, 894 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateVector.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateVectorBounds.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateVectorCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/NormalizePoint.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/GateVector.lean diff --git a/Complexity.lean b/Complexity.lean index 6318978916..a2dfafc34a 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1699,6 +1699,9 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateInterpolation public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateInterpolationBounds public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateInterpolationCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateVector +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateVectorBounds +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateVectorCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitNode public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitNodeCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitPath @@ -1711,6 +1714,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialVectorCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NodeReply public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NodeReplyCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NormalizePoint public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplay public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAccess public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAssignments diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateVector.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateVector.lean new file mode 100644 index 0000000000..1c75a2a23b --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateVector.lean @@ -0,0 +1,262 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateInterpolation +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.CallSpace +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Sum + +/-! +# Streaming complete packed gate vectors + +The caller retains interpolation parameters, an output-coordinate cursor and one accumulated +output prefix. Each coordinate invokes the same native interpolation machine on the original +query input. Returned values are reduced modulo the retained modulus before one element is +appended. No runtime-length vector is allocated by initialization. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.GateVector + +open MultiTapeTM MultiTapeTM.ComputableInSpace +open Cslib.TreeEval.Ary.Packed + +variable {k s : ℕ} {Q : Type} + +/-- Actual interpolation parameters and the number of requested output coordinates. -/ +abbrev Params := QueryGate.Params Interpolation.Context × ℕ + +/-- Parameters, the next coordinate and the already returned coordinate prefix. -/ +abbrev CallerState := Params × ℕ × List ℕ + +/-- A vector caller, or one suspended vector caller beside its interpolation callee. -/ +abbrev State (k : ℕ) (Q : Type) := + CallerState ⊕ (CallerState × Interpolation.State k Q) + +/-- Change only the output-coordinate field of actual interpolation metadata. -/ +@[expose] def setChunk : QueryGate.Params Interpolation.Context → ℕ → + QueryGate.Params Interpolation.Context := + fun ((N, b, σ, i, m, _, stride), parameters, point, limit) chunk => + ((N, b, σ, i, m, chunk, stride), parameters, point, limit) + +/-- Initialize one coordinate's interpolation from the caller's actual stored parameters. -/ +@[expose] def start : CallerState → Interpolation.State k Q := + fun ((params, _), chunk, _) => Interpolation.initial (setChunk params chunk) + +/-- Read a completed interpolation accumulator; suspended states have no returned answer. -/ +@[expose] def answer : Interpolation.State k Q → ℕ := + Sum.elim (fun (_, _, value) => value) (fun _ => 0) + +/-- Return one reduced coordinate and advance without retaining the callee. -/ +@[expose] def resume : CallerState → Interpolation.State k Q → CallerState := + fun (params, chunk, values) returned => + let ((_, (p, _, _), _, _), _) := params + (params, chunk + 1, values ++ [answer returned % p]) + +/-- Request another coordinate exactly while the output cursor is below the runtime count. -/ +@[expose] def request : CallerState → Option (Interpolation.State k Q) := + fun ((params, count), chunk, values) => + if chunk < count then some (start ((params, count), chunk, values)) else none + +/-- The default caller is halted; actual states are supplied through `initial`. -/ +@[expose] def caller : QueryMachine CallerState where + init := ((Interpolation.template, 0), 0, []) + query _ := 0 + next := Prod.fst + emit _ := [] + halted := fun ((_, count), chunk, _) => decide (count ≤ chunk) + finish _ := [] + +/-- One fixed vector machine reuses one interpolation callee at a time. -/ +@[expose] noncomputable def machine (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + QueryMachine (State k Q) := + caller.withCalls (Interpolation.machine M e) request resume + +/-- Actual parameters start with an empty prefix and zero cursor. -/ +@[expose] def initial (params : Params) : State k Q := .inl (params, 0, []) + +/-- All parameters are explicitly retained beside the runtime output count. -/ +@[expose] noncomputable def encParams : Params ↪ List Bool := + encPair (QueryGate.encParams Interpolation.encContext) encNat + +/-- The growing prefix is charged by its actual framed natural-number encoding. -/ +@[expose] noncomputable def encCaller : CallerState ↪ List Bool := + encPair encParams (encPair encNat (encList encNat)) + +variable [Finite Q] + +/-- A suspended caller pays for its complete interpolation state. -/ +@[expose] noncomputable def encState : State k Q ↪ List Bool := + encSum encCaller (encPair encCaller Interpolation.encState) + +/-- Replacing the requested chunk retains the actual point and assignment limit. -/ +theorem setChunk_inSpace : ComputableInSpace (fun a => setChunk a.1 a.2) + (encPair (QueryGate.encParams Interpolation.encContext) encNat) + (QueryGate.encParams Interpolation.encContext) + (fun a => (encPair (QueryGate.encParams Interpolation.encContext) encNat a).length) := by + have hpar := fst (QueryGate.encParams Interpolation.encContext) encNat + have hchunk := snd (QueryGate.encParams Interpolation.encContext) encNat + have hctx := hpar.fst' + have hrest := hpar.snd' + have hN := hctx.fst' + have hb := hctx.snd'.fst' + have hσ := hctx.snd'.snd'.fst' + have hi := hctx.snd'.snd'.snd'.fst' + have hm := hctx.snd'.snd'.snd'.snd'.fst' + have hstride := hctx.snd'.snd'.snd'.snd'.snd'.snd' + exact (budget_prod + (budget_prod hN (budget_prod hb (budget_prod hσ (budget_prod hi + (budget_prod hm (budget_prod hchunk hstride)))))) hrest).of_enc_eq fun _ => rfl + +/-- Preparing one coordinate is native in the actual caller-state storage. -/ +theorem start_inSpace : ComputableInSpace (start (k := k) (Q := Q)) encCaller + Interpolation.encState (fun a => (encCaller a).length) := by + have hparams := fst encParams (encPair encNat (encList encNat)) + have hchunk := (snd encParams (encPair encNat (encList encNat))).fst' + have hargs : ComputableInSpace + (fun q : CallerState => (q.1.1, q.2.1)) encCaller + (encPair (QueryGate.encParams Interpolation.encContext) encNat) + (fun a => (encCaller a).length) := + budget_prod (f := fun q : CallerState => q.1.1) (g := fun q => q.2.1) + hparams.fst' hchunk + have hchunked : ComputableInSpace + (fun q : CallerState => setChunk q.1.1 q.2.1) encCaller + (QueryGate.encParams Interpolation.encContext) (fun a => (encCaller a).length) := + budget_comp_linear (f := fun q : CallerState => (q.1.1, q.2.1)) + (g := fun a => setChunk a.1 a.2) (encIn := encCaller) + (encB := encPair (QueryGate.encParams Interpolation.encContext) encNat) + (encC := QueryGate.encParams Interpolation.encContext) setChunk_inSpace hargs + exact (budget_comp_linear + (f := fun q : CallerState => setChunk q.1.1 q.2.1) + (g := Interpolation.initial (k := k) (Q := Q)) (encIn := encCaller) + (encB := QueryGate.encParams Interpolation.encContext) + (encC := Interpolation.encState (k := k) (Q := Q)) + (Interpolation.initial_inSpace (k := k) (Q := Q)) hchunked).of_enc_eq fun _ => rfl + +/-- Reading a completed accumulator needs no output stream. -/ +theorem answer_inSpace : ComputableInSpace (answer (k := k) (Q := Q)) + Interpolation.encState encNat (fun a => (Interpolation.encState a).length) := by + have hleft := (snd (QueryGate.encParams Interpolation.encContext) (encPair encNat encNat)).snd' + exact sumElim hleft + (budget_const (encIn := encPair (QueryGate.encState Interpolation.encContext) + (SupplierChunk.encState (k := k) (Q := Q))) (encB := encNat) 0 (.refl _)) + +/-- Resumption charges the appended returned coordinate and the retained output prefix. -/ +theorem resume_inSpace : ComputableInSpace + (fun a : CallerState × Interpolation.State k Q => resume a.1 a.2) + (encPair encCaller Interpolation.encState) encCaller + (fun a => (encPair encCaller Interpolation.encState a).length) := by + have hc := fst encCaller (Interpolation.encState (k := k) (Q := Q)) + have ht := snd encCaller (Interpolation.encState (k := k) (Q := Q)) + have hparams := hc.fst' + have hchunk := hc.snd'.fst' + have hvalues := hc.snd'.snd' + have hp := hparams.fst'.snd'.fst'.fst' + have hv := budget_natMod (budget_comp_linear answer_inSpace ht) hp + exact (budget_prod hparams (budget_prod (budget_natSucc hchunk) + (budget_snoc hvalues hv))).of_enc_eq fun _ => rfl + +/-- A call is requested only while a coordinate remains. -/ +theorem request_inSpace : ComputableInSpace (request (k := k) (Q := Q)) encCaller + (encOption Interpolation.encState) (fun a => (encCaller a).length) := by + have hparams := fst encParams (encPair encNat (encList encNat)) + have hcount := hparams.snd' + have hchunk := (snd encParams (encPair encNat (encList encNat))).fst' + have hSome : ComputableInSpace (fun q => some (start (k := k) (Q := Q) q)) + encCaller (encOption (Interpolation.encState (k := k) (Q := Q))) + (fun a => (encCaller a).length) := + budget_comp_linear (f := start (k := k) (Q := Q)) (g := Option.some) + (encIn := encCaller) (encB := Interpolation.encState (k := k) (Q := Q)) + (encC := encOption (Interpolation.encState (k := k) (Q := Q))) + (of_inO (ComputableInDegree.optionSome + (enc₁ := Interpolation.encState (k := k) (Q := Q))).inO_one) + (start_inSpace (k := k) (Q := Q)) + refine (budget_ite (budget_natLt hchunk hcount) hSome + (budget_const (encB := encOption Interpolation.encState) none (.refl _))).of_enc_eq ?_ + rintro ⟨⟨params, count⟩, chunk, values⟩ + by_cases h : chunk < count <;> simp [request, h] + +/-- Runtime vector parameters initialize only fresh small counters and an empty prefix. -/ +theorem initial_inSpace : ComputableInSpace (initial (k := k) (Q := Q)) encParams encState + (fun a => (encParams a).length) := by + have hid : ComputableInSpace (fun a : Params => a) encParams encParams + (fun a => (encParams a).length) := of_inO (ComputableInO.of_eq (f := id) fun _ => rfl) + have hcaller := budget_prod hid (budget_prod + (budget_const (encB := encNat) 0 (.refl _)) + (budget_const (encB := encList encNat) [] (.refl _))) + exact (budget_comp_linear + (of_inO (ComputableInO.inl encCaller + (encPair encCaller (Interpolation.encState (k := k) (Q := Q))))) hcaller).of_enc_eq + fun _ => rfl + +private theorem caller_query_inSpace : ComputableInSpace caller.query encCaller encNat + (fun a => (encCaller a).length) := budget_const 0 (.refl _) + +private theorem caller_next_inSpace : ComputableInSpace caller.next + (encPair encCaller (encOption encBool)) encCaller + (fun a => (encPair encCaller (encOption encBool) a).length) := + fst encCaller (encOption encBool) + +private theorem caller_emit_inSpace : ComputableInSpace caller.emit + (encPair encCaller (encOption encBool)) encBits + (fun a => (encPair encCaller (encOption encBool) a).length) := budget_const [] (.refl _) + +private theorem caller_halted_inSpace : ComputableInSpace caller.halted encCaller encBool + (fun a => (encCaller a).length) := by + have hparams := fst encParams (encPair encNat (encList encNat)) + have hcount := hparams.snd' + have hchunk := (snd encParams (encPair encNat (encList encNat))).fst' + exact (budget_finite (encC := encBool) Bool.not (budget_natLt hchunk hcount)).of_enc_eq + fun ⟨⟨_, count⟩, chunk, _⟩ => by + by_cases h : count ≤ chunk + · have hn : ¬chunk < count := by omega + simp [caller, h, hn] + · have hn : chunk < count := by omega + simp [caller, h, hn] + +private theorem caller_finish_inSpace : ComputableInSpace caller.finish encCaller encBits + (fun a => (encCaller a).length) := budget_const [] (.refl _) + +/-- The active interpolation callee reads the same original query input. -/ +theorem machine_query_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).query encState encNat (fun a => (encState a).length) := + QueryMachine.withCalls_query_inSpace caller (Interpolation.machine M e) request resume + encCaller Interpolation.encState caller_query_inSpace + (Interpolation.machine_query_inSpace M e) request_inSpace + +/-- One transition either enters a coordinate call, advances it, or appends its returned value. -/ +theorem machine_next_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).next (encPair encState (encOption encBool)) encState + (fun a => (encPair encState (encOption encBool) a).length) := + QueryMachine.withCalls_next_inSpace caller (Interpolation.machine M e) request resume + encCaller Interpolation.encState caller_next_inSpace + (Interpolation.machine_next_inSpace M e) (Interpolation.machine_halted_inSpace M e) + request_inSpace resume_inSpace + +/-- Calls and returns do not retain output streams. -/ +theorem machine_emit_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).emit (encPair encState (encOption encBool)) encBits + (fun a => (encPair encState (encOption encBool) a).length) := + QueryMachine.withCalls_emit_inSpace caller (Interpolation.machine M e) request resume + encCaller Interpolation.encState caller_emit_inSpace request_inSpace + +/-- Completion depends only on the caller's actual cursor and coordinate count. -/ +theorem machine_halted_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).halted encState encBool (fun a => (encState a).length) := + QueryMachine.withCalls_halted_inSpace caller (Interpolation.machine M e) request resume + encCaller Interpolation.encState caller_halted_inSpace + +/-- The final coordinate vector remains in the returned state, not an output stream. -/ +theorem machine_finish_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).finish encState encBits (fun a => (encState a).length) := + QueryMachine.withCalls_finish_inSpace caller (Interpolation.machine M e) request resume + encCaller Interpolation.encState caller_finish_inSpace + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.GateVector diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateVectorBounds.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateVectorBounds.lean new file mode 100644 index 0000000000..b79e390e70 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateVectorBounds.lean @@ -0,0 +1,255 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateVectorCorrect +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.List.Size +import Complexity.Foundations.Data.List.Lookup + +/-! +# Physical space for complete native gate vectors + +The vector caller retains its parameters once and appends one reduced field element per returned +coordinate. The bounds cover every physical clock, including interpolation and replay calls. +The number of coordinates multiplies only the encoded field-element width. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.GateVector + +open MultiTapeTM Cslib.TreeEval.Ary +open Cslib.TreeEval.Ary.Packed + +variable {k s : ℕ} {Q : Type} [Finite Q] + +/-- The modulus of the field elements retained by the vector caller. -/ +@[expose] def modulus (params : Params) : ℕ := + let ((_, (p, _, _), _, _), _) := params + p + +/-- One retained parameter record and space for the reduced output vector. -/ +@[expose] noncomputable def callerSpace (params : Params) : ℕ := + let ((_, (p, _, _), _, _), count) := params + 8 * ((encParams params).length + 1) + count * (2 * (encNat p).length + 1) + +/-- Prefix storage charges each returned scalar at most the binary modulus width. -/ +theorem prefixState_encoding_le (params : Params) (values : ℕ → ℕ) (j : ℕ) + (hj : j ≤ params.2) (hp : 0 < modulus params) : + (encCaller (prefixState params values j)).length ≤ callerSpace params := by + rcases params with ⟨⟨context, ⟨p, alphabet, coordinates⟩, point, limit⟩, count⟩ + change 0 < p at hp + have hc := length_encNat_mono hj + have hl := length_encList_le encNat + (List.ofFn fun i : Fin j => values i % p) (b := (encNat p).length) + (by + intro a ha + obtain ⟨i, rfl⟩ := List.mem_ofFn.mp ha + exact length_encNat_mono (Nat.mod_lt _ hp).le) + simp only [List.length_ofFn] at hl + have hmul := Nat.mul_le_mul_right (2 * (encNat p).length + 1) hj + dsimp only at hc hmul + simp only [prefixState, encCaller, encParams, length_encPair, callerSpace] + omega + +/-- Every clock of one call pays for one suspended vector prefix and one actual callee. -/ +theorem runFrom_one_encoding_le (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (params : Params) (values : ℕ → ℕ) (w : List Bool) (j T B : ℕ) + (hj : j < params.2) (hp : 0 < modulus params) + (hpre : ∀ t < T, (Interpolation.machine M e).halted + ((Interpolation.machine M e).runFrom w (start (prefixState params values j)) t) = false) + (hhalt : (Interpolation.machine M e).halted + ((Interpolation.machine M e).runFrom w (start (prefixState params values j)) T) = true) + (hanswer : answer ((Interpolation.machine M e).runFrom w + (start (prefixState params values j)) T) = values j) + (hsize : ∀ t ≤ T, (Interpolation.encState ((Interpolation.machine M e).runFrom w + (start (prefixState params values j)) t)).length ≤ B) + (t : ℕ) (ht : t ≤ T + 2) : + (encState ((machine M e).runFrom w (.inl (prefixState params values j)) t)).length ≤ + 2 * callerSpace params + B + 2 := by + have hcaller := prefixState_encoding_le params values j hj.le hp + have hnext := prefixState_encoding_le params values (j + 1) (by omega) hp + by_cases htzero : t = 0 + · subst t + simp only [QueryMachine.runFrom_zero, encState, encSum_inl, List.length_cons] + omega + by_cases htfinal : t = T + 2 + · subst t + rw [runFrom_one M e params values w j T hj hpre hhalt hanswer] + simp only [encState, encSum_inl, List.length_cons] + omega + have htT : t - 1 ≤ T := by omega + have hentry : (machine M e).runFrom w (.inl (prefixState params values j)) 1 = + .inr (prefixState params values j, start (prefixState params values j)) := by + apply QueryMachine.withCalls_step_call + · change decide (params.2 ≤ j) = false + exact decide_eq_false (by omega) + · change (if j < params.2 then some (start (prefixState params values j)) else none) = _ + simp only [hj, ↓reduceIte] + rw [show t = 1 + (t - 1) by omega, QueryMachine.runFrom_add, hentry] + have hprefix := QueryMachine.withCalls_runFrom_subroutine_prefix + caller (Interpolation.machine M e) request resume w + (prefixState params values j) (start (prefixState params values j)) (t - 1) + (fun u hu => hpre u (by omega)) + rw [show (machine M e).runFrom w + (.inr (prefixState params values j, start (prefixState params values j))) (t - 1) = + .inr (prefixState params values j, + (Interpolation.machine M e).runFrom w (start (prefixState params values j)) (t - 1)) + from hprefix] + have hcallee := hsize (t - 1) htT + rw [show (encState (.inr (prefixState params values j, + (Interpolation.machine M e).runFrom w + (start (prefixState params values j)) (t - 1)))).length = + 2 * (encCaller (prefixState params values j)).length + + (Interpolation.encState ((Interpolation.machine M e).runFrom w + (start (prefixState params values j)) (t - 1))).length + 2 from + QueryMachine.withCalls_encoding_inr_length encCaller Interpolation.encState _ _] + omega + +/-- Concatenated coordinate calls obey one bound at all physical clocks. -/ +theorem exists_runFrom_prefix_encoding_le (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (params : Params) (w : List Bool) (values : ℕ → ℕ) (B : ℕ) + (hp : 0 < modulus params) + (hsupplier : ∀ j < params.2, ∃ T, + (∀ t < T, (Interpolation.machine M e).halted ((Interpolation.machine M e).runFrom w + (start (prefixState params values j)) t) = false) ∧ + (Interpolation.machine M e).halted ((Interpolation.machine M e).runFrom w + (start (prefixState params values j)) T) = true ∧ + answer ((Interpolation.machine M e).runFrom w + (start (prefixState params values j)) T) = values j ∧ + (∀ t ≤ T, (Interpolation.encState ((Interpolation.machine M e).runFrom w + (start (prefixState params values j)) t)).length ≤ B)) + (j : ℕ) (hj : j ≤ params.2) : + ∃ T, (machine M e).runFrom w (initial params) T = .inl (prefixState params values j) ∧ + ∀ t ≤ T, (encState ((machine M e).runFrom w (initial params) t)).length ≤ + 2 * callerSpace params + B + 2 := by + induction j with + | zero => + refine ⟨0, rfl, ?_⟩ + intro t ht + have : t = 0 := by omega + subst t + have hc := prefixState_encoding_le params values 0 (by omega) hp + change (encCaller (prefixState params values 0)).length + 1 ≤ _ + omega + | succ j ih => + obtain ⟨T, hT, hbound⟩ := ih (by omega) + obtain ⟨U, hpre, hhalt, hans, hsize⟩ := hsupplier j (by omega) + refine ⟨T + (U + 2), ?_, ?_⟩ + · rw [QueryMachine.runFrom_add, hT] + exact runFrom_one M e params values w j U (by omega) hpre hhalt hans + · intro t ht + by_cases htT : t ≤ T + · exact hbound t htT + · rw [show t = T + (t - T) by omega, QueryMachine.runFrom_add, hT] + exact runFrom_one_encoding_le M e params values w j U B (by omega) hp + hpre hhalt hans hsize (t - T) (by omega) + +/-- Only the binary chunk-index metadata grows when the requested coordinate increases. -/ +theorem setChunk_encoding_mono (params : QueryGate.Params Interpolation.Context) + {a b : ℕ} (hab : a ≤ b) : + (QueryGate.encParams Interpolation.encContext (setChunk params a)).length ≤ + (QueryGate.encParams Interpolation.encContext (setChunk params b)).length := by + rcases params with ⟨⟨N, block, σ, i, m, previous, stride⟩, parameters, point, limit⟩ + have h := length_encNat_mono hab + simp only [setChunk, QueryGate.encParams, Interpolation.encContext, length_encPair] + omega + +omit [Finite Q] in +/-- The replay allowance is monotone in the binary chunk-index metadata. -/ +theorem calleeSpace_chunk_mono (N block σ i m stride : ℕ) {a b : ℕ} (hab : a ≤ b) : + Interpolation.calleeSpace k s Q (N, block, σ, i, m, a, stride) ≤ + Interpolation.calleeSpace k s Q (N, block, σ, i, m, b, stride) := by + have h := length_encNat_mono hab + simp only [Interpolation.calleeSpace, SupplierChunk.encContext, length_encPair] + omega + +/-- The complete selected-field vector is returned with a bound at every physical clock. -/ +theorem exists_runFrom_gateExtension_bounded {p q r m : ℕ} [Fact p.Prime] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (σ : ℕ) (i : Fin q) + (x : Fin (k * 3 + 1) → Fin r → ZMod p) + (hb : Encoding.width k s b ≤ r * m) (hm : 0 < m) + (hsp : 2 ^ m ≤ p) (h : Fin (2 ^ m) ↪ ZMod p) (hh : ∀ a, h a = (a.val : ZMod p)) : + let f := codeGate (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) + (Encoding.encode e) (Encoding.decode e) (i.val + 1) + let params : Params := (QueryGate.gateParams (s := 2 ^ m) + (w.length, b, σ, i.val, m, 0, r * m) x, r) + let maxContext : Interpolation.Context := (w.length, b, σ, i.val, m, r, r * m) + let maxParams := QueryGate.gateParams (s := 2 ^ m) maxContext x + ∃ T values, (machine M e).runFrom w (initial params) T = .inl (params, r, values) ∧ + values.length = r ∧ (∀ value ∈ values, value < p) ∧ + (∀ j : Fin r, (values.getD j.val 0 : ZMod p) = + Packed.gateExtension h (Packed.packBits hb) (Packed.unpackBits hb) f x j) ∧ + (machine M e).halted ((machine M e).runFrom w (initial params) T) = true ∧ + ∀ t ≤ T, (encState ((machine M e).runFrom w (initial params) t)).length ≤ + 2 * callerSpace params + 16 * ((QueryGate.encParams Interpolation.encContext + maxParams).length + 1) + Interpolation.calleeSpace k s Q maxContext + 4 := by + classical + let f := codeGate (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) + (Encoding.encode e) (Encoding.decode e) (i.val + 1) + let params : Params := (QueryGate.gateParams (s := 2 ^ m) + (w.length, b, σ, i.val, m, 0, r * m) x, r) + let maxContext : Interpolation.Context := (w.length, b, σ, i.val, m, r, r * m) + let maxParams := QueryGate.gateParams (s := 2 ^ m) maxContext x + let B := 16 * ((QueryGate.encParams Interpolation.encContext maxParams).length + 1) + + Interpolation.calleeSpace k s Q maxContext + 2 + choose times scalars hpre hhalt hans hscalar hsize using fun j : Fin r => + coordinate_supplier_bounded M e w b σ i x hb hm j hsp h hh + let values : ℕ → ℕ := fun j => if hj : j < r then scalars ⟨j, hj⟩ else 0 + have hsupplier : ∀ j < params.2, ∃ T, + (∀ t < T, (Interpolation.machine M e).halted ((Interpolation.machine M e).runFrom w + (start (prefixState params values j)) t) = false) ∧ + (Interpolation.machine M e).halted ((Interpolation.machine M e).runFrom w + (start (prefixState params values j)) T) = true ∧ + answer ((Interpolation.machine M e).runFrom w + (start (prefixState params values j)) T) = values j ∧ + (∀ t ≤ T, (Interpolation.encState ((Interpolation.machine M e).runFrom w + (start (prefixState params values j)) t)).length ≤ B) := by + intro j hj + have hjr : j < r := hj + refine ⟨times ⟨j, hjr⟩, ?_, ?_, ?_, ?_⟩ + · simpa only [start, prefixState, params, setChunk_gateParams] using hpre ⟨j, hjr⟩ + · simpa only [start, prefixState, params, setChunk_gateParams] using hhalt ⟨j, hjr⟩ + · simpa only [start, prefixState, params, setChunk_gateParams, values, hjr, ↓reduceDIte] + using hans ⟨j, hjr⟩ + · intro t ht + have hpar := setChunk_encoding_mono + (QueryGate.gateParams (s := 2 ^ m) + (w.length, b, σ, i.val, m, 0, r * m) x) (Nat.le_of_lt hjr) + simp only [setChunk_gateParams] at hpar + have hcallee := calleeSpace_chunk_mono (k := k) (s := s) (Q := Q) + w.length b σ i.val m (r * m) (Nat.le_of_lt hjr) + have hlocal := hsize ⟨j, hjr⟩ t ht + dsimp only at hlocal + have hbound : (Interpolation.encState ((Interpolation.machine M e).runFrom w + (Interpolation.initial (QueryGate.gateParams (s := 2 ^ m) + (w.length, b, σ, i.val, m, j, r * m) x)) t)).length ≤ B := by + dsimp only [B, maxParams, maxContext] + omega + simpa only [start, prefixState, params, setChunk_gateParams] using hbound + obtain ⟨T, hT, hbound⟩ := exists_runFrom_prefix_encoding_le M e params w values B + (Fact.out : p.Prime).pos hsupplier r le_rfl + refine ⟨T, _, hT, ?_, ?_, ?_, ?_, ?_⟩ + · exact List.length_ofFn + · intro value hvalue + change value ∈ List.ofFn (fun j : Fin r => values j % p) at hvalue + obtain ⟨j, rfl⟩ := List.mem_ofFn.mp hvalue + exact Nat.mod_lt _ (Fact.out : p.Prime).pos + · intro j + rw [List.getD_ofFn_of_lt _ j.isLt 0, ZMod.natCast_mod] + simpa only [values, j.isLt, ↓reduceDIte] using hscalar j + · rw [hT] + change decide (r ≤ r) = true + simp + · intro t ht + have hbnd := hbound t ht + dsimp only [B, params, maxParams, maxContext] at hbnd ⊢ + omega + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.GateVector diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateVectorCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateVectorCorrect.lean new file mode 100644 index 0000000000..a22dfda956 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateVectorCorrect.lean @@ -0,0 +1,183 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateVector +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateInterpolationBounds +import Complexity.Foundations.Data.List.Lookup +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.FirstHalt + +/-! +# Complete native gate vectors + +One coordinate's interpolation is called at a time, its reduced answer is appended, and the +callee is discarded. Every prefix is realized by finitely many same-original-input calls. On +actual packed parameters the completed vector is the existing gate extension at every output +coordinate, with no external supplier premise. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.GateVector + +open MultiTapeTM Cslib.TreeEval.Ary +open Cslib.TreeEval.Ary.Packed + +variable {k s : ℕ} {Q : Type} + +/-- Completed-coordinate prefixes, used only to specify the streamed execution. -/ +@[expose] def prefixState (params : Params) (values : ℕ → ℕ) (j : ℕ) : CallerState := + let ((_, (p, _, _), _, _), _) := params + (params, j, List.ofFn fun i : Fin j => values i % p) + +/-- Returning the requested scalar extends exactly one coordinate prefix. -/ +theorem resume_prefix (params : Params) (values : ℕ → ℕ) (j : ℕ) + (returned : Interpolation.State k Q) (hanswer : answer returned = values j) : + resume (prefixState params values j) returned = prefixState params values (j + 1) := by + rcases params with ⟨params, count⟩ + rcases params with ⟨context, ⟨p, alphabet, coordinates⟩, point, limit⟩ + change (((context, (p, alphabet, coordinates), point, limit), count), j + 1, + (List.ofFn fun i : Fin j => values i % p) ++ [answer returned % p]) = _ + rw [hanswer] + simp only [prefixState, List.ofFn_succ_last, Fin.val_castSucc, Fin.val_last] + +/-- A first-return interpolation call implements one vector coordinate. -/ +theorem runFrom_one (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (params : Params) + (values : ℕ → ℕ) (w : List Bool) (j T : ℕ) (hj : j < params.2) + (hpre : ∀ t < T, (Interpolation.machine M e).halted + ((Interpolation.machine M e).runFrom w (start (prefixState params values j)) t) = false) + (hhalt : (Interpolation.machine M e).halted + ((Interpolation.machine M e).runFrom w (start (prefixState params values j)) T) = true) + (hanswer : answer ((Interpolation.machine M e).runFrom w + (start (prefixState params values j)) T) = values j) : + (machine M e).runFrom w (.inl (prefixState params values j)) (T + 2) = + .inl (prefixState params values (j + 1)) := by + rw [← resume_prefix params values j _ hanswer] + apply QueryMachine.withCalls_runFrom_call + · change decide (params.2 ≤ j) = false + exact decide_eq_false (by omega) + · change (if j < params.2 then some (start (prefixState params values j)) else none) = _ + simp only [hj, ↓reduceIte] + · exact hpre + · exact hhalt + +/-- Every prefix of the output vector is reached through actual completed interpolation calls. -/ +theorem exists_runFrom_prefix (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (params : Params) + (w : List Bool) (values : ℕ → ℕ) + (hsupplier : ∀ j < params.2, ∃ T, + (∀ t < T, (Interpolation.machine M e).halted + ((Interpolation.machine M e).runFrom w (start (prefixState params values j)) t) = false) ∧ + (Interpolation.machine M e).halted ((Interpolation.machine M e).runFrom w + (start (prefixState params values j)) T) = true ∧ + answer ((Interpolation.machine M e).runFrom w + (start (prefixState params values j)) T) = values j) + (j : ℕ) (hj : j ≤ params.2) : + ∃ T, (machine M e).runFrom w (initial params) T = .inl (prefixState params values j) := by + induction j with + | zero => exact ⟨0, rfl⟩ + | succ j ih => + obtain ⟨T, hT⟩ := ih (by omega) + obtain ⟨U, hpre, hhalt, hans⟩ := hsupplier j (by omega) + refine ⟨T + (U + 2), ?_⟩ + rw [QueryMachine.runFrom_add, hT] + exact runFrom_one M e params values w j U (by omega) hpre hhalt hans + +/-- Changing the requested coordinate preserves the actual field point and loop limit. -/ +theorem setChunk_gateParams {p r m : ℕ} (N b σ i previous chunk stride : ℕ) + (x : Fin (k * 3 + 1) → Fin r → ZMod p) : + setChunk (QueryGate.gateParams (s := 2 ^ m) (N, b, σ, i, m, previous, stride) x) chunk = + QueryGate.gateParams (s := 2 ^ m) (N, b, σ, i, m, chunk, stride) x := rfl + +/-- Every coordinate is returned at its first halt, with its actual physical state bound. -/ +theorem coordinate_supplier_bounded {p q r m : ℕ} [Fact p.Prime] [Finite Q] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (σ : ℕ) (i : Fin q) + (x : Fin (k * 3 + 1) → Fin r → ZMod p) + (hb : Encoding.width k s b ≤ r * m) (hm : 0 < m) (chunk : Fin r) + (hsp : 2 ^ m ≤ p) (h : Fin (2 ^ m) ↪ ZMod p) (hh : ∀ a, h a = (a.val : ZMod p)) : + let f := codeGate (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) + (Encoding.encode e) (Encoding.decode e) (i.val + 1) + let context : Interpolation.Context := (w.length, b, σ, i.val, m, chunk.val, r * m) + let params := QueryGate.gateParams (s := 2 ^ m) context x + ∃ T z, + (∀ t < T, (Interpolation.machine M e).halted + ((Interpolation.machine M e).runFrom w (Interpolation.initial params) t) = false) ∧ + (Interpolation.machine M e).halted + ((Interpolation.machine M e).runFrom w (Interpolation.initial params) T) = true ∧ + answer ((Interpolation.machine M e).runFrom w (Interpolation.initial params) T) = z ∧ + (z : ZMod p) = Packed.gateExtension h (Packed.packBits hb) (Packed.unpackBits hb) + f x chunk ∧ + ∀ t ≤ T, (Interpolation.encState ((Interpolation.machine M e).runFrom w + (Interpolation.initial params) t)).length ≤ + 16 * ((QueryGate.encParams Interpolation.encContext params).length + 1) + + Interpolation.calleeSpace k s Q context + 2 := by + dsimp only + obtain ⟨U, z, hrun, hz, hhalt, hsize⟩ := + Interpolation.exists_runFrom_gateExtension_bounded M e w b σ i x hb hm chunk hsp h hh + obtain ⟨T, hle, hT, hpre, hstate⟩ := QueryMachine.exists_first_halt_le + (Interpolation.machine M e) w _ U hhalt + refine ⟨T, z, hpre, hT, ?_, hz, ?_⟩ + · rw [hstate, hrun] + rfl + · intro t ht + exact hsize t (ht.trans hle) + +/-- The complete selected-field vector is returned by one fixed native vector machine. -/ +theorem exists_runFrom_gateExtension {p q r m : ℕ} [Fact p.Prime] [Finite Q] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (σ : ℕ) (i : Fin q) + (x : Fin (k * 3 + 1) → Fin r → ZMod p) + (hb : Encoding.width k s b ≤ r * m) (hm : 0 < m) + (hsp : 2 ^ m ≤ p) (h : Fin (2 ^ m) ↪ ZMod p) (hh : ∀ a, h a = (a.val : ZMod p)) : + let f := codeGate (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) + (Encoding.encode e) (Encoding.decode e) (i.val + 1) + let params : Params := (QueryGate.gateParams (s := 2 ^ m) + (w.length, b, σ, i.val, m, 0, r * m) x, r) + ∃ T values, (machine M e).runFrom w (initial params) T = .inl (params, r, values) ∧ + values.length = r ∧ (∀ value ∈ values, value < p) ∧ + (∀ j : Fin r, (values.getD j.val 0 : ZMod p) = + Packed.gateExtension h (Packed.packBits hb) (Packed.unpackBits hb) f x j) ∧ + (machine M e).halted ((machine M e).runFrom w (initial params) T) = true := by + classical + let f := codeGate (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) + (Encoding.encode e) (Encoding.decode e) (i.val + 1) + let params : Params := (QueryGate.gateParams (s := 2 ^ m) + (w.length, b, σ, i.val, m, 0, r * m) x, r) + choose times scalars hpre hhalt hans hscalar hsize using fun j : Fin r => + coordinate_supplier_bounded M e w b σ i x hb hm j hsp h hh + let values : ℕ → ℕ := fun j => if hj : j < r then scalars ⟨j, hj⟩ else 0 + have hsupplier : ∀ j < params.2, ∃ T, + (∀ t < T, (Interpolation.machine M e).halted ((Interpolation.machine M e).runFrom w + (start (prefixState params values j)) t) = false) ∧ + (Interpolation.machine M e).halted ((Interpolation.machine M e).runFrom w + (start (prefixState params values j)) T) = true ∧ + answer ((Interpolation.machine M e).runFrom w + (start (prefixState params values j)) T) = values j := by + intro j hj + have hjr : j < r := hj + refine ⟨times ⟨j, hjr⟩, ?_, ?_, ?_⟩ + · simpa only [start, prefixState, params, setChunk_gateParams] using hpre ⟨j, hjr⟩ + · simpa only [start, prefixState, params, setChunk_gateParams] using hhalt ⟨j, hjr⟩ + · simpa only [start, prefixState, params, setChunk_gateParams, values, hjr, ↓reduceDIte] + using hans ⟨j, hjr⟩ + obtain ⟨T, hT⟩ := exists_runFrom_prefix M e params w values hsupplier r le_rfl + refine ⟨T, _, hT, ?_, ?_, ?_, ?_⟩ + · exact List.length_ofFn + · intro value hvalue + change value ∈ List.ofFn (fun j : Fin r => values j % p) at hvalue + simp only [List.mem_ofFn] at hvalue + obtain ⟨j, rfl⟩ := hvalue + exact Nat.mod_lt _ (Fact.out : p.Prime).pos + · intro j + rw [List.getD_ofFn_of_lt _ j.isLt 0, ZMod.natCast_mod] + simpa only [values, j.isLt, ↓reduceDIte] using hscalar j + · rw [hT] + change decide (r ≤ r) = true + simp + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.GateVector diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/NormalizePoint.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/NormalizePoint.lean new file mode 100644 index 0000000000..feca9d893c --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/NormalizePoint.lean @@ -0,0 +1,122 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpaceLib +public import Mathlib.Data.ZMod.Basic +import Complexity.Computability.Machines.Turing.MultiTape.Internal.BinarySizeFacts +import Mathlib.Data.List.GetD +import Mathlib.Data.List.OfFn + +/-! +# Canonical field coordinates from padded register points + +The raw evaluator stores each register coordinate with modulus padding. Reducing the actual +stored point modulo that modulus gives the canonical unsigned coordinates used by the gate +interpolation specification. Reduction preserves list length and never enlarges a cell encoding. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.NormalizePoint + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +/-- Reduce every explicitly stored coordinate to its canonical residue. -/ +@[expose] def normalize (p : ℕ) (point : List ℕ) : List ℕ := point.map (fun v => v % p) + +/-- Modulus and actual stored point. -/ +abbrev Args := ℕ × List ℕ + +/-- Both the modulus and the complete point storage are charged. -/ +@[expose] noncomputable def encArgs : Args ↪ List Bool := encPair encNat (encList encNat) + +/-- Canonicalization preserves the actual number of point coordinates. -/ +theorem length_normalize (p : ℕ) (point : List ℕ) : + (normalize p point).length = point.length := List.length_map .. + +/-- Coordinate lookup commutes with normalization, with zero as the missing-coordinate value. -/ +theorem getD_normalize (p : ℕ) (point : List ℕ) (i : ℕ) : + (normalize p point).getD i 0 = point.getD i 0 % p := by + simpa only [normalize, Nat.zero_mod] using + (List.getD_map point 0 (n := i) (fun v => v % p)) + +/-- Normalization does not change the field point, including missing coordinates. -/ +theorem getD_normalize_cast (p : ℕ) (point : List ℕ) (i : ℕ) : + ((normalize p point).getD i 0 : ZMod p) = (point.getD i 0 : ZMod p) := by + rw [getD_normalize, ZMod.natCast_mod] + +/-- The evaluator's padded coordinates reduce to their canonical unsigned field values. -/ +theorem normalize_ofFn {p n : ℕ} (hp : 0 < p) (x : Fin n → ZMod p) : + normalize p (List.ofFn (fun i => p + (x i).val)) = + List.ofFn (fun i => (x i).val) := by + have : NeZero p := ⟨hp.ne'⟩ + simp only [normalize, List.map_ofFn] + congr 1 + funext i + change (p + (x i).val) % p = (x i).val + rw [Nat.add_mod_left, Nat.mod_eq_of_lt (ZMod.val_lt (x i))] + +private theorem length_enc_normalize_le (p : ℕ) (point : List ℕ) : + (encList encNat (normalize p point)).length ≤ (encList encNat point).length := by + apply length_encList_map_le encNat encNat + intro v _hv + exact ComputableTidilyInO.length_encNat_mono (Nat.mod_le v p) + +private theorem fold_normalize (p : ℕ) (point out : List ℕ) : + point.foldl (fun acc v => acc ++ [v % p]) out = out ++ normalize p point := by + induction point generalizing out with + | nil => simp [normalize] + | cons v point ih => simp [List.foldl_cons, normalize, ih, List.append_assoc] + +/-- Native reduction uses space linear in actual modulus and point storage. -/ +theorem normalize_inSpace : + ComputableInSpace (fun a : Args => normalize a.1 a.2) encArgs (encList encNat) + (fun a => (encArgs a).length) := by + have hp := fst encNat (encList encNat) + have hl := snd encNat (encList encNat) + have hinit : ComputableInSpace (fun _ : Args => ([] : List ℕ)) encArgs (encList encNat) + (fun a => (encArgs a).length) := budget_const [] (.refl _) + have hstep : ComputableInSpace (fun a : (ℕ × List ℕ) × ℕ => a.1.2 ++ [a.2 % a.1.1]) + (encPair (encPair encNat (encList encNat)) encNat) (encList encNat) + (fun a => (encPair (encPair encNat (encList encNat)) encNat a).length) := by + have hs := fst (encPair encNat (encList encNat)) encNat + have hv := snd (encPair encNat (encList encNat)) encNat + exact budget_snoc hs.snd' (budget_natMod hv hs.fst') + have hstates (a : Args) (i : ℕ) (_hi : i ≤ a.2.length) : + (encList encNat ((a.2.take i).foldl (fun acc v => acc ++ [v % a.1]) [])).length ≤ + (encArgs a).length := by + rw [fold_normalize, List.nil_append] + have hn := length_enc_normalize_le a.1 (a.2.take i) + have ht := length_encList_take_le encNat a.2 i + simp only [encArgs, length_encPair] + omega + have hspace (a : Args) (i : ℕ) (hi : i < a.2.length) : + (encPair (encPair encNat (encList encNat)) encNat + ((a.1, (a.2.take i).foldl (fun acc v => acc ++ [v % a.1]) []), a.2[i])).length ≤ + 16 * ((encArgs a).length + 1) := by + have hs := hstates a i hi.le + have hv := length_le_encList_of_mem encNat (List.getElem_mem hi) + simp only [encArgs, length_encPair] at * + omega + have hfold := ComputableInSpace.foldlWith + (encIn := encArgs) (encP := encNat) (encA := encNat) (encL := encList encNat) + (encB := encList encNat) (par := fun a : Args => a.1) (list := fun a : Args => a.2) + (init := fun _ : Args => ([] : List ℕ)) + (step := fun p acc v => acc ++ [v % p]) + (Sp := fun a => (encArgs a).length) (Sl := fun a => (encArgs a).length) + (Si := fun a => (encArgs a).length) + (Q := fun a => 16 * ((encArgs a).length + 1)) (B := fun a => (encArgs a).length) + hp hl hinit hstep hspace hstates + refine hfold.weaken (.of_le_linear 100 100 fun a => by + simp only [encArgs, length_encPair] + omega) |>.of_enc_eq ?_ + intro a + simp only [fold_normalize, List.nil_append] + +end Turing.MultiTapePTM.BlockComputation.NormalizePoint diff --git a/ComplexityTests/Machines/Turing/MultiTape/GateVector.lean b/ComplexityTests/Machines/Turing/MultiTape/GateVector.lean new file mode 100644 index 0000000000..3588b95e57 --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/GateVector.lean @@ -0,0 +1,68 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of native complete gate vectors) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateVectorCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NormalizePoint + +/-! +# Complete gate-vector regressions + +Zero-assignment calls return immediately, but each coordinate still requires a call and return. +The caller retains only the reduced output prefix, and padded raw-register points normalize to +the unsigned field coordinates used by interpolation. +-/ + +public section + +namespace ComplexityTests.GateVector + +open Turing MultiTapeTM MultiTapePTM.BlockComputation +open MultiTapePTM.BlockComputation.QueryReplay +open Cslib.TreeEval.Ary.Packed + +/-- The singleton control-state encoding. -/ +abbrev code : Fin 1 ≃ Fin 1 := Equiv.refl _ + +/-- A deterministic machine that halts after one transition. -/ +def haltedControl : MultiTapeTM 0 Bool (Fin 1) := MultiTapeTM.ofTr 0 fun _ _ _ => + { inputTape := .zero, workTapes := fun _ => (none, .zero), output := none, state := none } + +/-- Three requested coordinates with an empty interpolation assignment range. -/ +def params : GateVector.Params := + (((1, 1, 2, 0, 1, 0, 13), (17, 2, 13), List.replicate 13 0, 0), 3) + +/-- Observe completed prefixes while distinguishing an active interpolation call. -/ +def observe : GateVector.State 0 (Fin 1) → Option (ℕ × List ℕ) := + Sum.elim (fun (_, cursor, values) => some (cursor, values)) (fun _ => none) + +example : (GateVector.machine haltedControl code).halted + (GateVector.initial (params.1, 0)) = true := by decide + +example : (GateVector.machine haltedControl code).halted + (GateVector.initial params) = false := by decide + +example : observe ((GateVector.machine haltedControl code).runFrom [true] + (GateVector.initial params) 2) = some (1, [0]) := by decide + +example : observe ((GateVector.machine haltedControl code).runFrom [true] + (GateVector.initial params) 6) = some (3, [0, 0, 0]) := by decide + +example : (GateVector.machine haltedControl code).halted + ((GateVector.machine haltedControl code).runFrom [true] + (GateVector.initial params) 6) = true := by decide + +example : GateVector.resume (params, 1, [4]) + (.inl (params.1, 0, 35) : Interpolation.State 0 (Fin 1)) = + (params, 2, [4, 1]) := rfl + +example : NormalizePoint.normalize 17 [17, 18, 33] = [0, 1, 16] := by decide + +example : NormalizePoint.normalize 17 [34, 0, 52, 100] = [0, 0, 1, 15] := by decide + +end ComplexityTests.GateVector From 7d8dcba53305c3ef517a647c258deebbd47e6432 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 04:21:13 +0000 Subject: [PATCH 30/36] feat(hpv): route implicit packed gate replies --- Complexity.lean | 6 + .../MultiTape/BlockComputation/GateReply.lean | 188 +++++++++++++++ .../BlockComputation/GateReplyBounds.lean | 219 ++++++++++++++++++ .../BlockComputation/GateReplyCorrect.lean | 197 ++++++++++++++++ .../BlockComputation/InitialVectorGate.lean | 43 ++++ .../TreeEval/Ary/Packed/Constant.lean | 36 +++ .../Code/LowDegreeExtension/Constant.lean | 44 ++++ .../Machines/Turing/MultiTape/GateReply.lean | 62 +++++ 8 files changed, 795 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateReply.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateReplyBounds.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateReplyCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialVectorGate.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/Constant.lean create mode 100644 Complexity/Foundations/Data/Code/LowDegreeExtension/Constant.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/GateReply.lean diff --git a/Complexity.lean b/Complexity.lean index a2dfafc34a..c206a0c3aa 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1699,6 +1699,9 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateInterpolation public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateInterpolationBounds public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateInterpolationCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateReply +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateReplyBounds +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateReplyCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateVector public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateVectorBounds public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateVectorCorrect @@ -1712,6 +1715,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialChunkCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialVector public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialVectorCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialVectorGate public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NodeReply public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NodeReplyCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NormalizePoint @@ -2384,6 +2388,7 @@ public import Complexity.Computability.TreeEval.Ary.Packed public import Complexity.Computability.TreeEval.Ary.Packed.AssignmentLabels public import Complexity.Computability.TreeEval.Ary.Packed.AssignmentLabelsCorrect public import Complexity.Computability.TreeEval.Ary.Packed.AssignmentLabelsSpace +public import Complexity.Computability.TreeEval.Ary.Packed.Constant public import Complexity.Computability.TreeEval.Ary.Packed.Executable public import Complexity.Computability.TreeEval.Ary.Packed.GateInterpolation public import Complexity.Computability.TreeEval.Ary.Packed.GateInterpolationSpace @@ -2975,6 +2980,7 @@ public import Complexity.Foundations.Data.Code.ListDecoding public import Complexity.Foundations.Data.Code.LocalDecoding public import Complexity.Foundations.Data.Code.LocalListDecoding public import Complexity.Foundations.Data.Code.LowDegreeExtension +public import Complexity.Foundations.Data.Code.LowDegreeExtension.Constant public import Complexity.Foundations.Data.Code.LowDegreeExtension.UnitSum public import Complexity.Foundations.Data.Code.Multilinearity public import Complexity.Foundations.Data.Code.MultilinearitySoundness diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateReply.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateReply.lean new file mode 100644 index 0000000000..a9750f1ceb --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateReply.lean @@ -0,0 +1,188 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateVectorBounds +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NodeReply +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NormalizePoint +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Sum + +/-! +# Native gate replies at implicit addresses + +An actual implicit address selects either a paid initial-source vector or the complete native +interpolation machine. Invalid and nongate addresses return an empty reply. Interpolation uses +the schedule and interval resolved from the address, with the actual stored point reduced modulo +its retained modulus. All branches use the raw evaluator's existing reply format. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.GateReply + +open MultiTapeTM MultiTapeTM.ComputableInSpace Cslib.TreeEval.Ary.Packed + +variable {k s : ℕ} {Q : Type} + +/-- An actual implicit address and actual interpolation parameters with output count. -/ +abbrev Args := ImplicitPath.WalkArgs × GateVector.Params + +/-- A completed immediate reply or the complete active interpolation-vector state. -/ +abbrev State (k : ℕ) (Q : Type) := RawController.Reply ⊕ GateVector.State k Q + +/-- Resolve interval metadata and normalize the retained field point. -/ +@[expose] def prepare (k : ℕ) : Args → GateVector.Params := + fun (address, ((N, b, _, _, m, _, stride), (p, alphabet, n), point, limit), count) => + let (σ, _, _) := address + (((N, b, σ, ImplicitPath.walk k address - 1, m, 0, stride), + (p, alphabet, n), NormalizePoint.normalize p point, limit), count) + +/-- Immediate nongate rejection, paid source padding, or a fresh positive-interval vector. -/ +@[expose] def initial (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : Args → State k Q := + fun (address, params) => + let (gateParams, count) := params + let ((_, _, _, _, m, _, _), _, _, limit) := gateParams + if ImplicitNode.classify k address = 2 then + if ImplicitPath.walk k address = 0 then + .inl (0, InitialVector.values M e (limit, m, count)) + else .inr (GateVector.initial (prepare k (address, params))) + else .inl (0, []) + +/-- The immediate branch halts; the active branch runs on the same original input. -/ +@[expose] noncomputable def machine (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + QueryMachine (State k Q) where + init := .inl (0, []) + query := Sum.elim (fun _ => 0) (GateVector.machine M e).query + next := fun (q, symbol) => q.elim (fun reply => .inl reply) + (fun active => .inr ((GateVector.machine M e).next (active, symbol))) + emit _ := [] + halted := Sum.elim (fun _ => true) (GateVector.machine M e).halted + finish _ := [] + +/-- Read the retained completed vector without constructing an uncharged return word. -/ +@[expose] def answer : State k Q → RawController.Reply := + Sum.elim id (Sum.elim (fun (_, _, values) => (0, values)) (fun _ => (0, []))) + +/-- Both address storage and retained vector parameters are charged. -/ +@[expose] noncomputable def encArgs : Args ↪ List Bool := + encPair ImplicitPath.encWalkArgs GateVector.encParams + +variable [Finite Q] + +/-- Immediate replies and active states each retain their actual payload. -/ +@[expose] noncomputable def encState : State k Q ↪ List Bool := + encSum RawController.encReply (GateVector.encState (k := k) (Q := Q)) + +/-- Preparing the actual point and resolved interval uses native linear local space. -/ +theorem prepare_inSpace : ComputableInSpace (prepare k) encArgs GateVector.encParams + (fun a => (encArgs a).length) := by + have ha := fst ImplicitPath.encWalkArgs GateVector.encParams + have hv := snd ImplicitPath.encWalkArgs GateVector.encParams + have hpar := hv.fst' + have hc := hpar.fst' + have hrest := hpar.snd' + have hparameters := hrest.fst' + have hpoint := hrest.snd'.fst' + have hlimit := hrest.snd'.snd' + have hN := hc.fst' + have hb := hc.snd'.fst' + have hm := hc.snd'.snd'.snd'.snd'.fst' + have hstride := hc.snd'.snd'.snd'.snd'.snd'.snd' + have hindex := budget_natSub (budget_comp_linear (ImplicitPath.walk_inSpace k) ha) + (budget_const (encB := encNat) 1 (.refl _)) + have hnorm := budget_comp_linear NormalizePoint.normalize_inSpace + (budget_prod hparameters.fst' hpoint) + exact (budget_prod + (budget_prod + (budget_prod hN (budget_prod hb (budget_prod ha.fst' (budget_prod hindex + (budget_prod hm (budget_prod (budget_const (encB := encNat) 0 (.refl _)) hstride)))))) + (budget_prod hparameters (budget_prod hnorm hlimit))) hv.snd').of_enc_eq + fun _ => rfl + +/-- Dispatch and paid source construction use the actual encoded arguments. -/ +theorem initial_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (initial M e) encArgs encState (fun a => (encArgs a).length) := by + have ha := fst ImplicitPath.encWalkArgs GateVector.encParams + have hv := snd ImplicitPath.encWalkArgs GateVector.encParams + have hpar := hv.fst' + have hc := hpar.fst' + have hlimit := hpar.snd'.snd'.snd' + have hm := hc.snd'.snd'.snd'.snd'.fst' + have hsource := budget_comp_linear (InitialVector.values_inSpace M e) + (budget_prod hlimit (budget_prod hm hv.snd')) + have hinl := of_inO (ComputableInO.inl RawController.encReply + (GateVector.encState (k := k) (Q := Q))) + have hinr := of_inO (ComputableInO.inr RawController.encReply + (GateVector.encState (k := k) (Q := Q))) + have hsource' := budget_comp_linear hinl + (budget_prod (budget_const (encB := encNat) 0 (.refl _)) hsource) + have hactive := budget_comp_linear hinr + (budget_comp_linear (GateVector.initial_inSpace (k := k) (Q := Q)) (prepare_inSpace (k := k))) + have hgate := budget_natEq (budget_comp_linear (ImplicitNode.classify_inSpace k) ha) + (budget_const (encB := encNat) 2 (.refl _)) + have hzero := budget_natIsZero (budget_comp_linear (ImplicitPath.walk_inSpace k) ha) + have hempty := budget_const (encIn := encArgs) (encB := encState (k := k) (Q := Q)) + (.inl (0, [])) (.refl _) + refine (budget_ite hgate (budget_ite hzero hsource' hactive) hempty).of_enc_eq ?_ + rintro ⟨address, params, count⟩ + by_cases hg : ImplicitNode.classify k address = 2 <;> + by_cases hz : ImplicitPath.walk k address = 0 <;> simp [initial, hg, hz] <;> rfl + +/-- Reading a reply retains the existing raw-controller encoding. -/ +theorem answer_inSpace : ComputableInSpace (answer (k := k) (Q := Q)) encState + RawController.encReply (fun a => (encState a).length) := by + have hid : ComputableInSpace (fun a : RawController.Reply => a) + RawController.encReply RawController.encReply + (fun a => (RawController.encReply a).length) := + of_inO (ComputableInO.of_eq (f := id) fun _ => rfl) + have hvalues := (snd GateVector.encParams (encPair encNat (encList encNat))).snd' + have hcaller := budget_prod + (budget_const (encIn := GateVector.encCaller) (encB := encNat) 0 (.refl _)) hvalues + have hsuspended := budget_const + (encIn := encPair GateVector.encCaller (Interpolation.encState (k := k) (Q := Q))) + (encB := RawController.encReply) (0, []) (.refl _) + exact sumElim hid (sumElim hcaller hsuspended) + +/-- Queries preserve the vector machine's original-input interface. -/ +theorem machine_query_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).query encState encNat (fun a => (encState a).length) := + sumElim (budget_const (encIn := RawController.encReply) (encB := encNat) 0 (.refl _)) + (GateVector.machine_query_inSpace M e) + +/-- One native transition preserves the immediate or active branch. -/ +theorem machine_next_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).next (encPair encState (encOption encBool)) encState + (fun a => (encPair encState (encOption encBool) a).length) := by + have hleft := budget_comp_linear + (of_inO (ComputableInO.inl RawController.encReply + (GateVector.encState (k := k) (Q := Q)))) + (fst RawController.encReply (encOption encBool)) + have hright := budget_comp_linear + (of_inO (ComputableInO.inr RawController.encReply + (GateVector.encState (k := k) (Q := Q)))) (GateVector.machine_next_inSpace M e) + exact sumElimFst hleft hright + +/-- Replies emit no intermediate words. -/ +theorem machine_emit_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).emit (encPair encState (encOption encBool)) encBits + (fun a => (encPair encState (encOption encBool) a).length) := + budget_const [] (.refl _) + +/-- Immediate replies halt; active vectors use their existing halt predicate. -/ +theorem machine_halted_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).halted encState encBool (fun a => (encState a).length) := + sumElim (budget_const (encIn := RawController.encReply) (encB := encBool) true (.refl _)) + (GateVector.machine_halted_inSpace M e) + +/-- The reply remains in the state rather than an output stream. -/ +theorem machine_finish_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).finish encState encBits (fun a => (encState a).length) := + budget_const [] (.refl _) + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.GateReply diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateReplyBounds.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateReplyBounds.lean new file mode 100644 index 0000000000..132f265717 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateReplyBounds.lean @@ -0,0 +1,219 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateReplyCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateVectorBounds +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.List.Size + +/-! +# Physical space of routed implicit gate replies + +Every physical clock of the addressed gate reply is charged, including all native interpolation +and replay clocks. The prepared point and metadata occur only a constant number of times; only +the binary field-element width is multiplied by the number of output coordinates. Immediate +source replies use the same field-width allowance, without a gate-supplier premise. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.GateReply + +open MultiTapeTM Cslib.TreeEval.Ary Cslib.TreeEval.Ary.Packed +open Cslib.BitString + +/-- Uniform reply allowance, using the largest requested chunk's binary metadata. -/ +@[expose] noncomputable def allowance (k s : ℕ) (Q : Type) (args : Args) : ℕ := + let params := prepare k args + let (gateParams, count) := params + let maxParams := GateVector.setChunk gateParams count + let (context, _, _, _) := maxParams + 2 * GateVector.callerSpace params + + 16 * ((QueryGate.encParams Interpolation.encContext maxParams).length + 1) + + Interpolation.calleeSpace k s Q context + 5 + +private theorem source_encoding_le {k s p r m : ℕ} {Q : Type} [Finite Q] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (limit b : ℕ) [NeZero b] + (hcapacity : r ≤ limit.size) (hb : Encoding.width k s b ≤ r * m) + (hsp : 2 ^ m ≤ p) : + (encState (.inl (0, InitialVector.values M e (limit, m, r)) : State k Q)).length ≤ + r * (2 * (encNat p).length + 1) + 4 := by + rw [InitialVector.values_eq_packBits M e limit b hcapacity hb] + have hlist := length_encList_le encNat + (List.ofFn fun j : Fin r => (Packed.packBits hb + (Encoding.encode e (some (initialView (MultiTapePTM.ofDeterministic M) b))) j).val) + (b := (encNat p).length) (by + intro value hvalue + obtain ⟨j, rfl⟩ := List.mem_ofFn.mp hvalue + exact length_encNat_mono ((Fin.isLt _).trans_le hsp).le) + simp only [List.length_ofFn] at hlist + simp only [encState, encSum_inl, List.length_cons, RawController.encReply, length_encPair] + have hzero : (encNat 0).length = 0 := rfl + omega + +/-- Any explicitly stored point reducing to the canonical typed point gives the existing tree's +requested gate update. The only hypotheses on storage concern its actual representation. -/ +theorem exists_runFrom_nodeAt_bounded {k q s p r m : ℕ} {Q : Type} [Finite Q] [Fact p.Prime] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (σ : ℕ) (path : List (Fin (k * 3 + 1))) + (x : Fin (k * 3 + 1) → Fin r → ZMod p) (point : List ℕ) + (hpoint : NormalizePoint.normalize p point = + List.ofFn (fun a : Fin ((k * 3 + 1) * r) => (blocksEquiv.symm x a).val)) + (hcapacity : r ≤ ((2 ^ m) ^ ((k * 3 + 1) * r)).size) + (hb : Encoding.width k s b ≤ r * m) (hm : 0 < m) (hsp : 2 ^ m ≤ p) + (h : Fin (2 ^ m) ↪ ZMod p) (hh : ∀ a, h a = (a.val : ZMod p)) : + let params : GateVector.Params := (((w.length, b, 0, 0, m, 0, r * m), + (p, 2 ^ m, (k * 3 + 1) * r), point, (2 ^ m) ^ ((k * 3 + 1) * r)), r) + let args : Args := ((σ, q, path.map Fin.val), params) + ∃ T values, (machine M e).halted ((machine M e).runFrom w (initial M e args) T) = true ∧ + answer ((machine M e).runFrom w (initial M e args) T) = (0, values) ∧ + (∀ j : Fin r, (values.getD j.val 0 : ZMod p) = + match (encodedTree (MultiTapePTM.ofDeterministic M) w b + (scheduleOfNat k q σ) e).nodeAt path with + | .gate f => Packed.gateExtension h (Packed.packBits hb) (Packed.unpackBits hb) f x j + | _ => 0) ∧ + ∀ t ≤ T, (encState ((machine M e).runFrom w (initial M e args) t)).length ≤ + allowance k s Q args := by + classical + dsimp only + let params : GateVector.Params := (((w.length, b, 0, 0, m, 0, r * m), + (p, 2 ^ m, (k * 3 + 1) * r), point, (2 ^ m) ^ ((k * 3 + 1) * r)), r) + let address : ImplicitPath.WalkArgs := (σ, q, path.map Fin.val) + let index := ImplicitPath.walk k address + have hprepare : prepare k (address, params) = + (QueryGate.gateParams (s := 2 ^ m) (w.length, b, σ, index - 1, m, 0, r * m) x, r) := by + change (((w.length, b, σ, index - 1, m, 0, r * m), + (p, 2 ^ m, (k * 3 + 1) * r), NormalizePoint.normalize p point, + (2 ^ m) ^ ((k * 3 + 1) * r)), r) = _ + rw [hpoint] + rfl + have hclass := ImplicitNode.classify_eq_nodeAt (q := q) M e w b σ path + by_cases hdepth : path.length < q + · have hnode := nodeAt_eq_gate M e w b σ path hdepth + have htag : ImplicitNode.classify k address = 2 := by + simpa only [address, hnode, ImplicitNode.kind] using hclass + by_cases hz : index = 0 + · have hstart : initial M e (address, params) = + .inl (0, InitialVector.values M e ((2 ^ m) ^ ((k * 3 + 1) * r), m, r)) := by + change ((if ImplicitNode.classify k address = 2 then + if index = 0 then .inl (0, InitialVector.values M e + ((2 ^ m) ^ ((k * 3 + 1) * r), m, r)) else _ else _) : State k Q) = _ + rw [ite_eq_left htag, ite_eq_left hz] + refine ⟨0, InitialVector.values M e ((2 ^ m) ^ ((k * 3 + 1) * r), m, r), + ?_, ?_, ?_, ?_⟩ + · change (machine M e).halted (initial M e (address, params)) = true + rw [hstart] + rfl + · change answer (initial M e (address, params)) = _ + rw [hstart] + rfl + · intro j + rw [hnode] + change _ = Packed.gateExtension h (Packed.packBits hb) (Packed.unpackBits hb) + (codeGate (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) + (Encoding.encode e) (Encoding.decode e) index) x j + rw [hz] + exact InitialVector.values_gateExtension_zero M e w _ b + (scheduleOfNat k q σ) hcapacity hb h hh x j + · intro t ht + have htzero : t = 0 := by omega + subst t + have hsource := source_encoding_le M e ((2 ^ m) ^ ((k * 3 + 1) * r)) b + hcapacity hb hsp + change (encState ((machine M e).runFrom w (initial M e (address, params)) 0)).length ≤ _ + rw [QueryMachine.runFrom_zero, hstart] + rw [allowance, hprepare] + dsimp only [GateVector.callerSpace, QueryGate.gateParams] + omega + · have hle : index ≤ q := ImplicitPath.walk_le k σ q (path.map Fin.val) + let i : Fin q := ⟨index - 1, by omega⟩ + have hi : i.val + 1 = index := by dsimp only [i]; omega + obtain ⟨T, values, hrun, hlength, hcanonical, hvalues, hhalt, hsize⟩ := + GateVector.exists_runFrom_gateExtension_bounded M e w b σ i x hb hm hsp h hh + have hstart : initial M e (address, params) = + .inr (GateVector.initial + (QueryGate.gateParams (s := 2 ^ m) (w.length, b, σ, i.val, m, 0, r * m) x, r)) := by + simp only [initial, htag, show ImplicitPath.walk k address ≠ 0 from hz, + ↓reduceIte, hprepare, i] + refine ⟨T, values, ?_, ?_, ?_, ?_⟩ + · rw [hstart, runFrom_inr] + exact hhalt + · rw [hstart, runFrom_inr, hrun] + rfl + · intro j + rw [hnode] + rw [hi] at hvalues + exact hvalues j + · intro t ht + change (encState ((machine M e).runFrom w (initial M e (address, params)) t)).length ≤ _ + rw [hstart, runFrom_inr] + have hlocal := hsize t ht + change (GateVector.encState ((GateVector.machine M e).runFrom w + (GateVector.initial (QueryGate.gateParams (s := 2 ^ m) + (w.length, b, σ, i.val, m, 0, r * m) x, r)) t)).length + 1 ≤ _ + rw [allowance, hprepare] + dsimp only [i] at hlocal ⊢ + simp only [GateVector.setChunk_gateParams] + dsimp only [QueryGate.gateParams] at hlocal ⊢ + omega + · have htag : ImplicitNode.classify k address ≠ 2 := by + rw [show address = (σ, q, path.map Fin.val) from rfl, hclass] + rw [encodedTree, tree, Descending.nodeAt_tree] + simp only [Descending.lookup, hdepth, ↓reduceIte] + split <;> simp [ImplicitNode.kind] + have hstart : initial M e (address, params) = .inl (0, []) := by + change ((if ImplicitNode.classify k address = 2 then _ else .inl (0, [])) : State k Q) = _ + rw [ite_eq_right htag] + refine ⟨0, [], ?_, ?_, ?_, ?_⟩ + · change (machine M e).halted (initial M e (address, params)) = true + rw [hstart] + rfl + · change answer (initial M e (address, params)) = _ + rw [hstart] + rfl + · intro j + rw [encodedTree, tree, Descending.nodeAt_tree] + simp only [Descending.lookup, hdepth, ↓reduceIte] + by_cases heq : path.length = q <;> simp [heq] + · intro t ht + have htzero : t = 0 := by omega + subst t + change (encState ((machine M e).runFrom w (initial M e (address, params)) 0)).length ≤ _ + rw [QueryMachine.runFrom_zero, hstart] + have hempty : (encState (.inl (0, []) : State k Q)).length = 2 := rfl + rw [hempty] + dsimp only [allowance] + omega + +/-- The actual modulus-padded register representation discharges normalization internally. -/ +theorem exists_runFrom_nodeAt_padded_bounded {k q s p r m : ℕ} {Q : Type} + [Finite Q] [Fact p.Prime] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (σ : ℕ) (path : List (Fin (k * 3 + 1))) + (x : Fin (k * 3 + 1) → Fin r → ZMod p) + (hcapacity : r ≤ ((2 ^ m) ^ ((k * 3 + 1) * r)).size) + (hb : Encoding.width k s b ≤ r * m) (hm : 0 < m) (hsp : 2 ^ m ≤ p) + (h : Fin (2 ^ m) ↪ ZMod p) (hh : ∀ a, h a = (a.val : ZMod p)) : + let point := List.ofFn (fun a : Fin ((k * 3 + 1) * r) => p + (blocksEquiv.symm x a).val) + let params : GateVector.Params := (((w.length, b, 0, 0, m, 0, r * m), + (p, 2 ^ m, (k * 3 + 1) * r), point, (2 ^ m) ^ ((k * 3 + 1) * r)), r) + let args : Args := ((σ, q, path.map Fin.val), params) + ∃ T values, (machine M e).halted ((machine M e).runFrom w (initial M e args) T) = true ∧ + answer ((machine M e).runFrom w (initial M e args) T) = (0, values) ∧ + (∀ j : Fin r, (values.getD j.val 0 : ZMod p) = + match (encodedTree (MultiTapePTM.ofDeterministic M) w b + (scheduleOfNat k q σ) e).nodeAt path with + | .gate f => Packed.gateExtension h (Packed.packBits hb) (Packed.unpackBits hb) f x j + | _ => 0) ∧ + ∀ t ≤ T, (encState ((machine M e).runFrom w (initial M e args) t)).length ≤ + allowance k s Q args := by + exact exists_runFrom_nodeAt_bounded M e w b σ path x _ + (NormalizePoint.normalize_ofFn (Fact.out : p.Prime).pos (blocksEquiv.symm x)) + hcapacity hb hm hsp h hh + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.GateReply diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateReplyCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateReplyCorrect.lean new file mode 100644 index 0000000000..50981bf842 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/GateReplyCorrect.lean @@ -0,0 +1,197 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateReply +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateVectorCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialVectorGate +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ImplicitNodeCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NormalizePoint + +/-! +# Native gate replies for the existing encoded tree + +Typed paths are routed to the existing descending tree's interval gate, including constant +source padding. Actual padded register coordinates are normalized before the complete native +gate-vector computation. No gate-table oracle or supplier premise is used. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.QueryReplay.GateReply + +open Cslib.TreeEval.Ary Cslib.TreeEval.Ary.Packed Cslib.BitString + +private theorem resolve_eq_walk {k q : ℕ} (σ : ℕ) + (path : List (Fin (k * 3 + 1))) : + Descending.resolve (BlockComputation.child (scheduleOfNat k q σ)) q path = + ImplicitPath.walk k (σ, q, path.map Fin.val) := by + have hnext : Descending.next (BlockComputation.child (scheduleOfNat k q σ)) = + BlockComputation.child (scheduleOfNat k q σ) := by + funext n j + cases n with + | zero => rfl + | succ n => simp [Descending.next] + rw [Descending.resolve, hnext, ImplicitPath.walk_eq] + +/-- At an internal typed path, lookup uses exactly the interval reached by native navigation. -/ +theorem nodeAt_eq_gate {k q s : ℕ} {Q : Type} + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (σ : ℕ) (path : List (Fin (k * 3 + 1))) + (hdepth : path.length < q) : + (encodedTree (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) e).nodeAt path = + .gate (codeGate (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) + (Encoding.encode e) (Encoding.decode e) + (ImplicitPath.walk k (σ, q, path.map Fin.val))) := by + rw [encodedTree, tree, Descending.nodeAt_tree] + simp only [Descending.lookup, hdepth, ↓reduceIte, resolve_eq_walk] + congr 1 + unfold Descending.paddedGate + split_ifs with hz + · rw [hz] + rfl + · rfl + +/-- The active branch preserves every physical clock of the vector subroutine. -/ +theorem runFrom_inr {k s : ℕ} {Q : Type} (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (state : GateVector.State k Q) (t : ℕ) : + (machine M e).runFrom w (.inr state) t = + .inr ((GateVector.machine M e).runFrom w state t) := by + have hstep (q : GateVector.State k Q) : + (machine M e).step w (.inr q) = .inr ((GateVector.machine M e).step w q) := by + simp only [QueryMachine.step, QueryMachine.read, machine, Sum.elim_inr] + by_cases h : (GateVector.machine M e).halted q = true <;> + simp only [h, Bool.false_eq_true, ↓reduceIte] + induction t with + | zero => rfl + | succ t ih => rw [QueryMachine.runFrom_succ, ih, hstep, QueryMachine.runFrom_succ] + +/-- Any explicitly stored point reducing to the canonical typed point gives the existing tree's +requested gate update. The only hypotheses on storage concern its actual representation. -/ +theorem exists_runFrom_nodeAt {k q s p r m : ℕ} {Q : Type} [Finite Q] [Fact p.Prime] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (σ : ℕ) (path : List (Fin (k * 3 + 1))) + (x : Fin (k * 3 + 1) → Fin r → ZMod p) (point : List ℕ) + (hpoint : NormalizePoint.normalize p point = + List.ofFn (fun a : Fin ((k * 3 + 1) * r) => (blocksEquiv.symm x a).val)) + (hcapacity : r ≤ ((2 ^ m) ^ ((k * 3 + 1) * r)).size) + (hb : Encoding.width k s b ≤ r * m) (hm : 0 < m) (hsp : 2 ^ m ≤ p) + (h : Fin (2 ^ m) ↪ ZMod p) (hh : ∀ a, h a = (a.val : ZMod p)) : + let params : GateVector.Params := (((w.length, b, 0, 0, m, 0, r * m), + (p, 2 ^ m, (k * 3 + 1) * r), point, (2 ^ m) ^ ((k * 3 + 1) * r)), r) + let args : Args := ((σ, q, path.map Fin.val), params) + ∃ T values, (machine M e).halted ((machine M e).runFrom w (initial M e args) T) = true ∧ + answer ((machine M e).runFrom w (initial M e args) T) = (0, values) ∧ + ∀ j : Fin r, (values.getD j.val 0 : ZMod p) = + match (encodedTree (MultiTapePTM.ofDeterministic M) w b + (scheduleOfNat k q σ) e).nodeAt path with + | .gate f => Packed.gateExtension h (Packed.packBits hb) (Packed.unpackBits hb) f x j + | _ => 0 := by + classical + dsimp only + let params : GateVector.Params := (((w.length, b, 0, 0, m, 0, r * m), + (p, 2 ^ m, (k * 3 + 1) * r), point, (2 ^ m) ^ ((k * 3 + 1) * r)), r) + let address : ImplicitPath.WalkArgs := (σ, q, path.map Fin.val) + let index := ImplicitPath.walk k address + have hprepare : prepare k (address, params) = + (QueryGate.gateParams (s := 2 ^ m) (w.length, b, σ, index - 1, m, 0, r * m) x, r) := by + change (((w.length, b, σ, index - 1, m, 0, r * m), + (p, 2 ^ m, (k * 3 + 1) * r), NormalizePoint.normalize p point, + (2 ^ m) ^ ((k * 3 + 1) * r)), r) = _ + rw [hpoint] + rfl + have hclass := ImplicitNode.classify_eq_nodeAt (q := q) M e w b σ path + by_cases hdepth : path.length < q + · have hnode := nodeAt_eq_gate M e w b σ path hdepth + have htag : ImplicitNode.classify k address = 2 := by + simpa only [address, hnode, ImplicitNode.kind] using hclass + by_cases hz : index = 0 + · have hstart : initial M e (address, params) = + .inl (0, InitialVector.values M e ((2 ^ m) ^ ((k * 3 + 1) * r), m, r)) := by + change ((if ImplicitNode.classify k address = 2 then + if index = 0 then .inl (0, InitialVector.values M e + ((2 ^ m) ^ ((k * 3 + 1) * r), m, r)) else _ else _) : State k Q) = _ + rw [ite_eq_left htag, ite_eq_left hz] + refine ⟨0, InitialVector.values M e ((2 ^ m) ^ ((k * 3 + 1) * r), m, r), ?_, ?_, ?_⟩ + · change (machine M e).halted (initial M e (address, params)) = true + rw [hstart] + rfl + · change answer (initial M e (address, params)) = _ + rw [hstart] + rfl + · intro j + rw [hnode] + change _ = Packed.gateExtension h (Packed.packBits hb) (Packed.unpackBits hb) + (codeGate (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) + (Encoding.encode e) (Encoding.decode e) index) x j + rw [hz] + exact InitialVector.values_gateExtension_zero M e w _ b + (scheduleOfNat k q σ) hcapacity hb h hh x j + · have hle : index ≤ q := ImplicitPath.walk_le k σ q (path.map Fin.val) + let i : Fin q := ⟨index - 1, by omega⟩ + have hi : i.val + 1 = index := by dsimp only [i]; omega + obtain ⟨T, values, hrun, hlength, hcanonical, hvalues, hhalt⟩ := + GateVector.exists_runFrom_gateExtension M e w b σ i x hb hm hsp h hh + have hstart : initial M e (address, params) = + .inr (GateVector.initial + (QueryGate.gateParams (s := 2 ^ m) (w.length, b, σ, i.val, m, 0, r * m) x, r)) := by + simp only [initial, htag, show ImplicitPath.walk k address ≠ 0 from hz, + ↓reduceIte, hprepare, i] + refine ⟨T, values, ?_, ?_, ?_⟩ + · rw [hstart, runFrom_inr] + exact hhalt + · rw [hstart, runFrom_inr, hrun] + rfl + · intro j + rw [hnode] + rw [hi] at hvalues + exact hvalues j + · have htag : ImplicitNode.classify k address ≠ 2 := by + rw [show address = (σ, q, path.map Fin.val) from rfl, hclass] + rw [encodedTree, tree, Descending.nodeAt_tree] + simp only [Descending.lookup, hdepth, ↓reduceIte] + split <;> simp [ImplicitNode.kind] + have hstart : initial M e (address, params) = .inl (0, []) := by + change ((if ImplicitNode.classify k address = 2 then _ else .inl (0, [])) : State k Q) = _ + rw [ite_eq_right htag] + refine ⟨0, [], ?_, ?_, ?_⟩ + · change (machine M e).halted (initial M e (address, params)) = true + rw [hstart] + rfl + · change answer (initial M e (address, params)) = _ + rw [hstart] + rfl + · intro j + rw [encodedTree, tree, Descending.nodeAt_tree] + simp only [Descending.lookup, hdepth, ↓reduceIte] + by_cases heq : path.length = q <;> simp [heq] + +/-- The actual modulus-padded register representation discharges normalization internally. -/ +theorem exists_runFrom_nodeAt_padded {k q s p r m : ℕ} {Q : Type} [Finite Q] [Fact p.Prime] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (σ : ℕ) (path : List (Fin (k * 3 + 1))) + (x : Fin (k * 3 + 1) → Fin r → ZMod p) + (hcapacity : r ≤ ((2 ^ m) ^ ((k * 3 + 1) * r)).size) + (hb : Encoding.width k s b ≤ r * m) (hm : 0 < m) (hsp : 2 ^ m ≤ p) + (h : Fin (2 ^ m) ↪ ZMod p) (hh : ∀ a, h a = (a.val : ZMod p)) : + let point := List.ofFn (fun a : Fin ((k * 3 + 1) * r) => p + (blocksEquiv.symm x a).val) + let params : GateVector.Params := (((w.length, b, 0, 0, m, 0, r * m), + (p, 2 ^ m, (k * 3 + 1) * r), point, (2 ^ m) ^ ((k * 3 + 1) * r)), r) + let args : Args := ((σ, q, path.map Fin.val), params) + ∃ T values, (machine M e).halted ((machine M e).runFrom w (initial M e args) T) = true ∧ + answer ((machine M e).runFrom w (initial M e args) T) = (0, values) ∧ + ∀ j : Fin r, (values.getD j.val 0 : ZMod p) = + match (encodedTree (MultiTapePTM.ofDeterministic M) w b + (scheduleOfNat k q σ) e).nodeAt path with + | .gate f => Packed.gateExtension h (Packed.packBits hb) (Packed.unpackBits hb) f x j + | _ => 0 := by + exact exists_runFrom_nodeAt M e w b σ path x _ + (NormalizePoint.normalize_ofFn (Fact.out : p.Prime).pos (blocksEquiv.symm x)) + hcapacity hb hm hsp h hh + +end Turing.MultiTapePTM.BlockComputation.QueryReplay.GateReply diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialVectorGate.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialVectorGate.lean new file mode 100644 index 0000000000..5d7155708f --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitialVectorGate.lean @@ -0,0 +1,43 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialVectorCorrect +public import Complexity.Computability.TreeEval.Ary.Packed.Constant +import Complexity.Foundations.Data.List.Lookup + +/-! +# Initial vectors supply padded source gates + +Internal source padding is a constant Boolean gate. Its extension at an arbitrary field point +is therefore the same paid initial vector already used for leaves. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.InitialVector + +open Cslib.TreeEval.Ary + +/-- Paid source vectors compute the existing constant gate extension at every field point. -/ +theorem values_gateExtension_zero {k s p q r m : ℕ} {Q : Type} [Fact p.Prime] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (limit b : ℕ) [NeZero b] (σ : Schedule k q) + (hcapacity : r ≤ limit.size) (hb : Encoding.width k s b ≤ r * m) + (h : Fin (2 ^ m) ↪ ZMod p) (hh : ∀ a, h a = (a.val : ZMod p)) + (x : Fin (k * 3 + 1) → Fin r → ZMod p) (j : Fin r) : + ((values M e (limit, m, r)).getD j.val 0 : ZMod p) = + Packed.gateExtension h (Packed.packBits hb) (Packed.unpackBits hb) + (codeGate (MultiTapePTM.ofDeterministic M) w b σ + (Encoding.encode e) (Encoding.decode e) 0) x j := by + rw [values_eq_packBits M e limit b hcapacity hb, List.getD_ofFn_of_lt _ j.isLt 0] + change _ = Packed.gateExtension h (Packed.packBits hb) (Packed.unpackBits hb) + (fun _ => Encoding.encode e (some (initialView (MultiTapePTM.ofDeterministic M) b))) x j + rw [Packed.gateExtension_const, hh] + +end Turing.MultiTapePTM.BlockComputation.InitialVector diff --git a/Complexity/Computability/TreeEval/Ary/Packed/Constant.lean b/Complexity/Computability/TreeEval/Ary/Packed/Constant.lean new file mode 100644 index 0000000000..de5636de7a --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/Constant.lean @@ -0,0 +1,36 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed +public import Complexity.Foundations.Data.Code.LowDegreeExtension.Constant + +/-! +# Constant packed gate extensions + +A constant Boolean gate extends to its constant packed vector, including at field points +outside the encoded alphabet. This does not require the decoder to invert the encoder. + +## Main results + +* `Cslib.TreeEval.Ary.Packed.gateExtension_const`: constant gates have constant extensions. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed + +/-- A constant gate extends to the encoded constant at every field point. -/ +theorem gateExtension_const {K H : Type*} [Field K] [Fintype H] [DecidableEq H] + [Nonempty H] {b d r : ℕ} (h : H ↪ K) + (encode : (Fin b → Bool) → Fin r → H) (decode : (Fin r → H) → Fin b → Bool) + (v : Fin b → Bool) (x : Fin d → Fin r → K) (i : Fin r) : + gateExtension h encode decode (fun _ => v) x i = h (encode v i) := by + exact Code.lowDegreeExtension_const h (h (encode v i)) _ + +end Cslib.TreeEval.Ary.Packed diff --git a/Complexity/Foundations/Data/Code/LowDegreeExtension/Constant.lean b/Complexity/Foundations/Data/Code/LowDegreeExtension/Constant.lean new file mode 100644 index 0000000000..f9933faa43 --- /dev/null +++ b/Complexity/Foundations/Data/Code/LowDegreeExtension/Constant.lean @@ -0,0 +1,44 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Foundations.Data.Code.LowDegreeExtension +import Mathlib.Algebra.BigOperators.Ring.Finset + +/-! +# Constant low-degree extensions + +Interpolation on a nonempty alphabet preserves constant tables at every field point. + +## Main results + +* `Cslib.Code.lowDegreeExtension_const`: the extension of a constant table is constant. +-/ + +public section + +namespace Cslib.Code + +open Finset + +/-- A constant table has the same constant extension at arbitrary field points. +The nonempty alphabet ensures that the Lagrange basis sums to one. -/ +theorem lowDegreeExtension_const {F H : Type*} [Field F] [Fintype H] [DecidableEq H] + [Nonempty H] {m : ℕ} (h : H ↪ F) (c : F) (x : Fin m → F) : + lowDegreeExtension h (fun _ => c) x = c := by + classical + have hs (i : Fin m) : ∑ a : H, (Lagrange.basis univ h a).eval (x i) = 1 := by + simpa only [Polynomial.eval_finsetSum, Polynomial.eval_one] using + congrArg (Polynomial.eval (x i)) + (Lagrange.sum_basis h.injective.injOn univ_nonempty) + rw [lowDegreeExtension, ← mul_sum, + ← Fintype.prod_sum (κ := fun _ : Fin m => H) + (fun i a => (Lagrange.basis univ h a).eval (x i))] + simp only [hs, prod_const_one, mul_one] + +end Cslib.Code diff --git a/ComplexityTests/Machines/Turing/MultiTape/GateReply.lean b/ComplexityTests/Machines/Turing/MultiTape/GateReply.lean new file mode 100644 index 0000000000..71a75fe612 --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/GateReply.lean @@ -0,0 +1,62 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of concrete implicit gate replies) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateReplyCorrect + +/-! +# Implicit gate-reply routing regressions + +Invalid and leaf addresses require zero gate updates. Internal source padding instead returns +the initial packed vector, while positive intervals delegate to the actual interpolation loop. +-/ + +public section + +namespace ComplexityTests.GateReply + +open Turing MultiTapeTM MultiTapePTM.BlockComputation +open MultiTapePTM.BlockComputation.QueryReplay + +/-- The singleton control-state encoding. -/ +abbrev code : Fin 1 ≃ Fin 1 := Equiv.refl _ + +/-- A one-step reader with one work tape and the existing packed source layout. -/ +def reader : MultiTapeTM 1 Bool (Fin 1) := MultiTapeTM.ofTr 0 fun _ input _ => + { inputTape := .pos, workTapes := fun _ => (some input, .neg), output := some true, + state := none } + +/-- Paid seven-coordinate parameters with deliberately padded raw point coordinates. -/ +def params : GateVector.Params := + (((1, 1, 99, 99, 2, 99, 14), (17, 4, 28), [17, 18, 33], 2 ^ 14), 7) + +/-- Observe completed immediate replies without collapsing active interpolation states. -/ +def immediate : GateReply.State 1 (Fin 1) → Option (ℕ × List ℕ) := + Sum.elim some (fun _ => none) + +example : immediate (GateReply.initial reader code ((0, 2, [4]), params)) = + some (0, []) := by decide + +example : immediate (GateReply.initial reader code ((0, 1, [0]), params)) = + some (0, []) := by decide + +example : ImplicitPath.walk 1 (0, 2, [1]) = 0 := by decide + +example : immediate (GateReply.initial reader code ((0, 2, [1]), params)) = + some (0, [3, 0, 0, 0, 0, 0, 0]) := by decide + +example : immediate (GateReply.initial reader code ((0, 2, []), params)) = + none := by decide + +example : GateReply.prepare 1 ((0, 2, []), params) = + (((1, 1, 0, 1, 2, 0, 14), (17, 4, 28), [0, 1, 16], 2 ^ 14), 7) := rfl + +example : (GateReply.machine reader code).halted + (GateReply.initial reader code ((0, 2, [1]), params)) = true := by decide + +end ComplexityTests.GateReply From 1da89f13e77e9f21cdfc4e3c80b9dcb97f3e3f1a Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 04:37:19 +0000 Subject: [PATCH 31/36] feat(hpv): instantiate the concrete implicit evaluator --- Complexity.lean | 6 + .../BlockComputation/ConcreteEvaluator.lean | 90 ++++++++++ .../ConcreteEvaluatorCorrect.lean | 100 +++++++++++ .../BlockComputation/ConcreteSupplier.lean | 133 ++++++++++++++ .../ConcreteSupplierCorrect.lean | 165 +++++++++++++++++ .../ConcreteSupplierSpace.lean | 167 ++++++++++++++++++ .../Ary/Packed/RawControllerPoint.lean | 56 ++++++ .../Turing/MultiTape/ConcreteEvaluator.lean | 56 ++++++ 8 files changed, 773 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteEvaluator.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteEvaluatorCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteSupplier.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteSupplierCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteSupplierSpace.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/RawControllerPoint.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/ConcreteEvaluator.lean diff --git a/Complexity.lean b/Complexity.lean index c206a0c3aa..89e13ef457 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1691,6 +1691,11 @@ public import Complexity.Computability.Machines.Turing.MultiTape.Alternating public import Complexity.Computability.Machines.Turing.MultiTape.Alternating.Game public import Complexity.Computability.Machines.Turing.MultiTape.Alternating.GuessVerify public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteEvaluator +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteEvaluatorCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteSupplier +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteSupplierCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteSupplierSpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.EncodedTree public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeChecked @@ -2403,6 +2408,7 @@ public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerCorrect public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerEncoding public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerNative public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerOutput +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerPoint public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerQuery public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerSpace public import Complexity.Computability.TreeEval.Ary.PackingParameters diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteEvaluator.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteEvaluator.lean new file mode 100644 index 0000000000..d3bef81734 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteEvaluator.lean @@ -0,0 +1,90 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteSupplier +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerQuery + +/-! +# A fixed native evaluator with concrete same-input suppliers + +The uniform raw evaluator calls the concrete node and gate-vector suppliers on its original +read-only input. Its fixed template contains only zero metadata and empty storage. Actual runtime +parameters and an actual register bank are supplied through `runFrom`; computing that initial +bank remains a separate obligation. No input word, tree or supplier oracle is captured by the +machine definition. All five native components reuse the existing raw-controller calculus. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ConcreteEvaluator + +open MultiTapeTM Cslib.TreeEval.Ary.Packed +open QueryReplay + +variable {k s : ℕ} {Q : Type} + +/-- An active raw evaluator or one suspended evaluator paired with its concrete supplier. -/ +abbrev State (k : ℕ) (Q : Type) := + RawController.Query.Context ConcreteSupplier.Context ⊕ + (RawController.Query.Context ConcreteSupplier.Context × GateReply.State k Q) + +/-- A fixed empty halted template, independent of runtime inputs and evaluator parameters. -/ +@[expose] def template : RawController.Query.Context ConcreteSupplier.Context := + ((0, 0, 0, 0, 0, 0, 0), (0, 0, 0), [], [], none) + +/-- The raw evaluator with concrete node and gate-vector calls on the same original input. -/ +@[expose] noncomputable def machine (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + QueryMachine (State k Q) := + RawController.Query.machine template (GateReply.machine M e) + (ConcreteSupplier.start M e) GateReply.answer + +variable [Finite Q] + +/-- Reuse the raw caller/callee encoding, charging every stored bank, stack and supplier state. -/ +@[expose] noncomputable def encState : State k Q ↪ List Bool := + RawController.Query.encMachineState ConcreteSupplier.encContext + (GateReply.encState (k := k) (Q := Q)) + +/-- Native queries use only the current actual caller or concrete supplier state. -/ +theorem machine_query_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).query encState encNat (fun a => (encState a).length) := + RawController.Query.machine_query_inSpace ConcreteSupplier.encContext GateReply.encState + template (GateReply.machine M e) (ConcreteSupplier.start M e) GateReply.answer + (GateReply.machine_query_inSpace M e) (ConcreteSupplier.start_inSpace M e) + +/-- Native transitions charge request initialization and extraction of the actual reply. -/ +theorem machine_next_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).next (encPair encState (encOption encBool)) encState + (fun a => (encPair encState (encOption encBool) a).length) := + RawController.Query.machine_next_inSpace ConcreteSupplier.encContext GateReply.encState + template (GateReply.machine M e) (ConcreteSupplier.start M e) GateReply.answer + (GateReply.machine_next_inSpace M e) (GateReply.machine_halted_inSpace M e) + (ConcreteSupplier.start_inSpace M e) GateReply.answer_inSpace + +/-- Evaluation returns its state and emits no intermediate word. -/ +theorem machine_emit_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).emit (encPair encState (encOption encBool)) encBits + (fun a => (encPair encState (encOption encBool) a).length) := + RawController.Query.machine_emit_inSpace ConcreteSupplier.encContext GateReply.encState + template (GateReply.machine M e) (ConcreteSupplier.start M e) GateReply.answer + (ConcreteSupplier.start_inSpace M e) + +/-- The existing native raw-controller guard determines completion. -/ +theorem machine_halted_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).halted encState encBool (fun a => (encState a).length) := + RawController.Query.machine_halted_inSpace ConcreteSupplier.encContext GateReply.encState + template (GateReply.machine M e) (ConcreteSupplier.start M e) GateReply.answer + +/-- The final bank remains in the actual returned state, rather than an output stream. -/ +theorem machine_finish_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).finish encState encBits (fun a => (encState a).length) := + RawController.Query.machine_finish_inSpace ConcreteSupplier.encContext GateReply.encState + template (GateReply.machine M e) (ConcreteSupplier.start M e) GateReply.answer + +end Turing.MultiTapePTM.BlockComputation.ConcreteEvaluator diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteEvaluatorCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteEvaluatorCorrect.lean new file mode 100644 index 0000000000..93d56c4d89 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteEvaluatorCorrect.lean @@ -0,0 +1,100 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteEvaluator +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteSupplierCorrect +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerOutput + +/-! +# The concrete implicit evaluator computes the encoded block tree + +The fixed same-input evaluator now has concrete suppliers in both request modes. It realizes +the complete masking procedure with an explicit bound at every physical clock. With the usual +degree condition and an initially zero target, its native register decoder returns the existing +encoded block tree's Boolean value. No supplier assumption remains. + +These execution theorems start from actual supplied runtime metadata and a canonical bank. +They do not construct the outer simulator's initializer or time/schedule-guessing loop. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ConcreteEvaluator + +open Cslib.TreeEval.Ary Cslib.TreeEval.Ary.Packed +open QueryReplay + +/-- Concrete node and gate suppliers realize the complete masking run, with all-clock space. -/ +theorem exists_runFrom_root {k q s p r m : ℕ} {Q : Type} [Finite Q] [Fact p.Prime] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (σ : ℕ) + (hcapacity : r ≤ ((2 ^ m) ^ ((k * 3 + 1) * r)).size) + (hb : Encoding.width k s b ≤ r * m) (hm : 0 < m) (hsp : 2 ^ m ≤ p) + (h : Fin (2 ^ m) ↪ ZMod p) (hh : ∀ a, h a = (a.val : ZMod p)) + (S : Cslib.TreeEval.Ary.State (ZMod p) (k * 3 + 1) r) (c : ZMod p) + (target : Fin (k * 3 + 1 + 1)) : + let ctx : ConcreteSupplier.Context := + (w.length, b, σ, q, m, 2 ^ m, (2 ^ m) ^ ((k * 3 + 1) * r)) + let encode := fun bits j => ((Packed.packBits hb bits j).val : ZMod p) + let extend := Packed.gateExtension h (Packed.packBits hb) (Packed.unpackBits hb) + let tree := encodedTree (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) e + let initial : State k Q := + .inl (ctx, (p, k * 3 + 1, r), RawController.ofCfg (Iter.initialCfg S c target)) + ∃ clock, (machine M e).runFrom w initial clock = + .inl (ctx, (p, k * 3 + 1, r), RawController.ofCfg + (⟨procWith encode extend (RawController.units p) tree c target S, [], .ret⟩ : + Iter.Cfg (ZMod p) (k * 3 + 1) r)) ∧ + ∀ i ≤ clock, (encState ((machine M e).runFrom w initial i)).length ≤ + RawController.Query.queryStateBound (ConcreteSupplier.encContext ctx).length + p (k * 3 + 1) r q + (ConcreteSupplier.replySpace k s Q w.length b σ q m (2 ^ m) + ((2 ^ m) ^ ((k * 3 + 1) * r)) p (k * 3 + 1) r) := by + apply RawController.Query.exists_runFrom_root_encoded_bound + ConcreteSupplier.encContext GateReply.encState template (GateReply.machine M e) + (ConcreteSupplier.start M e) GateReply.answer w _ (by omega) + intro n req hneed + exact ConcreteSupplier.exists_first_reply_correct M e w b σ hcapacity hb hm hsp h hh _ req hneed + +/-- A zero-target concrete run returns a physical bank whose native decoder yields the root. -/ +theorem exists_runFrom_decode_root {k q s p r m : ℕ} {Q : Type} [Finite Q] [Fact p.Prime] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (σ : ℕ) + (hcapacity : r ≤ ((2 ^ m) ^ ((k * 3 + 1) * r)).size) + (hb : Encoding.width k s b ≤ r * m) (hm : 0 < m) (hsp : 2 ^ m ≤ p) + (h : Fin (2 ^ m) ↪ ZMod p) (hh : ∀ a, h a = (a.val : ZMod p)) + (hdegree : (k * 3 + 1) * r * (2 ^ m - 1) < p - 1) + (S : Cslib.TreeEval.Ary.State (ZMod p) (k * 3 + 1) r) + (target : Fin (k * 3 + 1 + 1)) (hzero : S target = 0) : + let ctx : ConcreteSupplier.Context := + (w.length, b, σ, q, m, 2 ^ m, (2 ^ m) ^ ((k * 3 + 1) * r)) + let tree := encodedTree (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) e + let initial : State k Q := + .inl (ctx, (p, k * 3 + 1, r), RawController.ofCfg (Iter.initialCfg S 1 target)) + ∃ clock bank, (machine M e).runFrom w initial clock = + .inl (ctx, (p, k * 3 + 1, r), bank, [], none) ∧ + RawController.Output.decode ((Encoding.width k s b, m, p, r, target.val), bank) = + List.ofFn tree.eval ∧ + ∀ i ≤ clock, (encState ((machine M e).runFrom w initial i)).length ≤ + RawController.Query.queryStateBound (ConcreteSupplier.encContext ctx).length + p (k * 3 + 1) r q + (ConcreteSupplier.replySpace k s Q w.length b σ q m (2 ^ m) + ((2 ^ m) ^ ((k * 3 + 1) * r)) p (k * 3 + 1) r) := by + classical + obtain ⟨clock, hrun, hbound⟩ := exists_runFrom_root M e w b σ hcapacity hb hm hsp h hh S 1 target + refine ⟨clock, _, hrun, ?_, hbound⟩ + have hdecode := RawController.Output.decode_procWith_zero_target hb hm hsp h hh + (by simpa only [Fintype.card_fin, ZMod.card] using hdegree) + (encodedTree (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) e) S target hzero + have hencode : (fun bits j => h (Packed.packBits hb bits j)) = + (fun bits j => ((Packed.packBits hb bits j).val : ZMod p)) := + funext fun _ => funext fun _ => hh _ + rw [hencode] at hdecode + exact hdecode + +end Turing.MultiTapePTM.BlockComputation.ConcreteEvaluator diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteSupplier.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteSupplier.lean new file mode 100644 index 0000000000..694d52fd5c --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteSupplier.lean @@ -0,0 +1,133 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateReplyBounds +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NodeReply +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerQuery +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Sum + +/-! +# Concrete native requests for the uniform raw evaluator + +Node requests return the existing paid node reply. Gate requests initialize the existing routed +gate-vector supplier on the same original input. Runtime alphabet and assignment capacity are +stored explicitly; initialization does not expand binary chunk-width metadata into an alphabet +or a vector. The existing gate-reply machine, answer and encoding serve both request kinds. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ConcreteSupplier + +open MultiTapeTM MultiTapeTM.ComputableInSpace Cslib.TreeEval.Ary.Packed + +variable {k s : ℕ} {Q : Type} + +/-- Input length, block width, schedule, horizon, chunk width, alphabet and paid capacity. -/ +abbrev Context := ℕ × ℕ × ℕ × ℕ × ℕ × ℕ × ℕ + +/-- The raw evaluator's actual request, including its runtime field/arity/count header. -/ +abbrev Args := RawController.Query.RequestArgs Context + +/-- Seven explicitly retained binary metadata registers. -/ +@[expose] noncomputable def encContext : Context ↪ List Bool := + encPair encNat (encPair encNat (encPair encNat (encPair encNat + (encPair encNat (encPair encNat encNat))))) + +/-- Reuse the raw evaluator's concrete request encoding. -/ +@[expose] noncomputable def encArgs : Args ↪ List Bool := + RawController.Query.encRequestArgs encContext + +/-- Pass the actual path and paid source-vector metadata to node classification. -/ +@[expose] def nodeArgs : Args → NodeReply.Args := + fun ((_, _, σ, q, m, _, limit), (_, _, r), (_, path, _, _)) => + ((σ, q, path), (limit, m, r)) + +/-- Pass the actual stored point to routed interpolation, retaining the supplied alphabet. -/ +@[expose] def gateArgs : Args → QueryReplay.GateReply.Args := + fun ((N, b, σ, q, m, alphabet, limit), (p, d, r), (_, path, point, chunk)) => + ((σ, q, path), (((N, b, σ, 0, m, chunk, r * m), + (p, alphabet, d * r), point, limit), r)) + +/-- Node replies are immediate; gate requests enter the existing concrete gate supplier. -/ +@[expose] def start (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + Args → QueryReplay.GateReply.State k Q := fun args => + let (_, _, (tag, _, _, _)) := args + if tag = 0 then .inl (NodeReply.reply M e (nodeArgs args)) + else if tag = 1 then QueryReplay.GateReply.initial M e (gateArgs args) + else .inl (0, []) + +/-- Node argument extraction is native in actual request storage. -/ +theorem nodeArgs_inSpace : ComputableInSpace nodeArgs encArgs NodeReply.encArgs + (fun a => (encArgs a).length) := by + have hc := fst encContext (encPair RawController.encParams RawController.encNeed) + have hr := snd encContext (encPair RawController.encParams RawController.encNeed) + have hp := hr.fst' + have hn := hr.snd' + have hσ := hc.snd'.snd'.fst' + have hq := hc.snd'.snd'.snd'.fst' + have hm := hc.snd'.snd'.snd'.snd'.fst' + have hlimit := hc.snd'.snd'.snd'.snd'.snd'.snd' + have hpath := hn.snd'.fst' + have hcount := hp.snd'.snd' + exact (budget_prod (budget_prod hσ (budget_prod hq hpath)) + (budget_prod hlimit (budget_prod hm hcount))).of_enc_eq fun _ => rfl + +/-- Gate argument extraction performs only binary products and retains the actual point. -/ +theorem gateArgs_inSpace : ComputableInSpace gateArgs encArgs QueryReplay.GateReply.encArgs + (fun a => (encArgs a).length) := by + have hc := fst encContext (encPair RawController.encParams RawController.encNeed) + have hr := snd encContext (encPair RawController.encParams RawController.encNeed) + have hp := hr.fst' + have hn := hr.snd' + have hN := hc.fst' + have hb := hc.snd'.fst' + have hσ := hc.snd'.snd'.fst' + have hq := hc.snd'.snd'.snd'.fst' + have hm := hc.snd'.snd'.snd'.snd'.fst' + have halphabet := hc.snd'.snd'.snd'.snd'.snd'.fst' + have hlimit := hc.snd'.snd'.snd'.snd'.snd'.snd' + have hmodulus := hp.fst' + have hd := hp.snd'.fst' + have hcount := hp.snd'.snd' + have hpath := hn.snd'.fst' + have hpoint := hn.snd'.snd'.fst' + have hchunk := hn.snd'.snd'.snd' + have hzero := budget_const (encIn := encArgs) (encB := encNat) 0 (.refl _) + have hcontext := budget_prod hN (budget_prod hb (budget_prod hσ (budget_prod hzero + (budget_prod hm (budget_prod hchunk (budget_natMul hcount hm)))))) + have hparameters := budget_prod hmodulus (budget_prod halphabet (budget_natMul hd hcount)) + exact (budget_prod (budget_prod hσ (budget_prod hq hpath)) + (budget_prod (budget_prod hcontext (budget_prod hparameters (budget_prod hpoint hlimit))) + hcount)).of_enc_eq fun _ => rfl + +variable [Finite Q] + +/-- Request dispatch and concrete supplier initialization use native linear local space. -/ +theorem start_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (start M e) encArgs QueryReplay.GateReply.encState + (fun a => (encArgs a).length) := by + have hr := snd encContext (encPair RawController.encParams RawController.encNeed) + have htag := hr.snd'.fst' + have hnode := budget_natIsZero htag + have hgate := budget_natEq htag (budget_const (encB := encNat) 1 (.refl _)) + have hreply := budget_comp_linear (NodeReply.reply_inSpace M e) nodeArgs_inSpace + have hleft := budget_comp_linear + (of_inO (ComputableInO.inl RawController.encReply + (QueryReplay.GateVector.encState (k := k) (Q := Q)))) hreply + have hright := budget_comp_linear (QueryReplay.GateReply.initial_inSpace M e) gateArgs_inSpace + have hempty := budget_const (encIn := encArgs) + (encB := QueryReplay.GateReply.encState (k := k) (Q := Q)) + (.inl (0, [])) (.refl _) + refine (budget_ite hnode hleft (budget_ite hgate hright hempty)).of_enc_eq ?_ + rintro ⟨context, params, tag, path, point, chunk⟩ + by_cases hz : tag = 0 <;> by_cases ho : tag = 1 <;> simp [start, hz, ho] <;> rfl + +end Turing.MultiTapePTM.BlockComputation.ConcreteSupplier diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteSupplierCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteSupplierCorrect.lean new file mode 100644 index 0000000000..0c57318756 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteSupplierCorrect.lean @@ -0,0 +1,165 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteSupplier +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteSupplierSpace +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NodeReplyCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateReplyBounds +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerPoint +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerQuery +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.FirstHalt + +/-! +# Concrete replies discharge both evaluator request cases + +Actual requests from canonical evaluator states receive the existing node or gate-update +contract. The same original input is used throughout. Neither a tree lookup oracle nor a gate +supplier premise is assumed. A uniform metadata allowance covers every physical clock through +the first halt, including all suspended interpolation calls. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ConcreteSupplier + +open Cslib.TreeEval.Ary Cslib.TreeEval.Ary.Packed +open QueryReplay + +private theorem need_call {p d r : ℕ} [Fact p.Prime] + (S : Cslib.TreeEval.Ary.State (ZMod p) d r) (stack : List (Iter.Frame (ZMod p) d)) + (c : ZMod p) (target : Fin (d + 1)) : + RawController.need p d r (RawController.ofCfg ⟨S, stack, .call c target⟩) = + some (0, (Iter.path stack).map Fin.val, [], 0) := by + have hv := RawController.validBank_ofRegs (Fact.out : p.Prime).two_le S + simp only [RawController.need, RawController.ofCfg, hv, ↓reduceIte, + RawController.needCore, RawController.ofMode, RawController.path_ofFrame] + +private theorem need_ret {p d r : ℕ} [Fact p.Prime] + (S : Cslib.TreeEval.Ary.State (ZMod p) d r) (fr : Iter.Frame (ZMod p) d) + (stack : List (Iter.Frame (ZMod p) d)) + (hu : fr.undo = false) (hc : ¬fr.child.val + 1 < d) : + RawController.need p d r (RawController.ofCfg ⟨S, fr :: stack, .ret⟩) = + some (1, (Iter.path stack).map Fin.val, + RawController.point d r fr.target.val (RawController.ofRegs S), 0) := by + have hv := RawController.validBank_ofRegs (Fact.out : p.Prime).two_le S + simp only [RawController.need, RawController.ofCfg, hv, ↓reduceIte, + RawController.needCore, RawController.ofMode, List.map_cons, RawController.ofFrame, + hu, hc, decide_false, Bool.false_or, Bool.false_eq_true, RawController.path_ofFrame] + +/-- One uniform allowance covers both immediate node replies and routed gate computations. -/ +@[expose] noncomputable def replySpace (k s : ℕ) (Q : Type) + (N b σ q m alphabet limit p d r : ℕ) : ℕ := + gateSpace k s Q N b σ q m alphabet limit p d r + + r * (2 * (MultiTapeTM.encNat p).length + 1) + 9 + +/-- Every actual canonical request returns at its first halt and satisfies `ReplyCorrect`. -/ +theorem exists_first_reply_correct {k q s p r m : ℕ} {Q : Type} [Finite Q] [Fact p.Prime] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (σ : ℕ) + (hcapacity : r ≤ ((2 ^ m) ^ ((k * 3 + 1) * r)).size) + (hb : Encoding.width k s b ≤ r * m) (hm : 0 < m) (hsp : 2 ^ m ≤ p) + (h : Fin (2 ^ m) ↪ ZMod p) (hh : ∀ a, h a = (a.val : ZMod p)) + (cfg : Iter.Cfg (ZMod p) (k * 3 + 1) r) (req : RawController.Need) + (hneed : RawController.need p (k * 3 + 1) r (RawController.ofCfg cfg) = some req) : + let ctx : Context := (w.length, b, σ, q, m, 2 ^ m, (2 ^ m) ^ ((k * 3 + 1) * r)) + let actual := start M e (ctx, (p, k * 3 + 1, r), req) + ∃ U, + (∀ t < U, (GateReply.machine M e).halted + ((GateReply.machine M e).runFrom w actual t) = false) ∧ + (GateReply.machine M e).halted ((GateReply.machine M e).runFrom w actual U) = true ∧ + RawController.ReplyCorrect + (fun bits j => ((Packed.packBits hb bits j).val : ZMod p)) + (Packed.gateExtension h (Packed.packBits hb) (Packed.unpackBits hb)) + (encodedTree (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) e).nodeAt cfg + (GateReply.answer ((GateReply.machine M e).runFrom w actual U)) ∧ + ∀ t ≤ U, (GateReply.encState ((GateReply.machine M e).runFrom w actual t)).length ≤ + replySpace k s Q w.length b σ q m (2 ^ m) + ((2 ^ m) ^ ((k * 3 + 1) * r)) p (k * 3 + 1) r := by + classical + let limit := (2 ^ m) ^ ((k * 3 + 1) * r) + let ctx : Context := (w.length, b, σ, q, m, 2 ^ m, limit) + change ∃ U, _ + rcases cfg with ⟨S, stack, mode⟩ + cases mode with + | call c target => + rw [need_call] at hneed + cases hneed + refine ⟨0, ?_, ?_, ?_, ?_⟩ + · intro t ht + omega + · rfl + · change RawController.ReplyCorrect _ _ _ ⟨S, stack, .call c target⟩ + (NodeReply.reply M e ((σ, q, (Iter.path stack).map Fin.val), (limit, m, r))) + exact NodeReply.reply_correct_call M e w b σ limit hcapacity hb _ + ⟨S, stack, .call c target⟩ c target rfl + · intro t ht + have htzero : t = 0 := by omega + subst t + have hbound := node_reply_encoding_le M e limit b σ q + ((Iter.path stack).map Fin.val) hcapacity hb hsp + change (RawController.encReply (NodeReply.reply M e + ((σ, q, (Iter.path stack).map Fin.val), (limit, m, r)))).length + 1 ≤ _ + dsimp only [replySpace] + omega + | ret => + cases stack with + | nil => + have hv := RawController.validBank_ofRegs (Fact.out : p.Prime).two_le S + simp [RawController.need, RawController.ofCfg, RawController.ofMode, hv, + RawController.needCore] at hneed + | cons fr stack => + by_cases hguard : fr.undo = false ∧ ¬fr.child.val + 1 < k * 3 + 1 + · rw [need_ret S fr stack hguard.1 hguard.2] at hneed + cases hneed + rw [RawController.point_ofRegs] + obtain ⟨T, values, hhalt, hans, hvalues, hsize⟩ := + GateReply.exists_runFrom_nodeAt_padded_bounded + M e w b σ (Iter.path stack) (fun j => S (Cslib.TreeEval.Ary.reg fr.target j)) + hcapacity hb hm hsp h hh + obtain ⟨U, hle, hU, hpre, hstate⟩ := QueryMachine.exists_first_halt_le + (GateReply.machine M e) w _ T hhalt + refine ⟨U, hpre, hU, ?_, ?_⟩ + · change RawController.ReplyCorrect _ _ _ ⟨S, fr :: stack, .ret⟩ + (GateReply.answer ((GateReply.machine M e).runFrom w + (GateReply.initial M e ((σ, q, (Iter.path stack).map Fin.val), + (((w.length, b, 0, 0, m, 0, r * m), (p, 2 ^ m, (k * 3 + 1) * r), + List.ofFn (fun a => p + (Cslib.BitString.blocksEquiv.symm + (fun j => S (Cslib.TreeEval.Ary.reg fr.target j)) a).val), limit), r))) U)) + rw [hstate] + simp only [RawController.ReplyCorrect, hguard] + rw [hans] + intro j + cases hn : (encodedTree (MultiTapePTM.ofDeterministic M) w b + (scheduleOfNat k q σ) e).nodeAt (Iter.path stack) <;> + simpa only [RawController.expectedGate, hn, Pi.zero_apply] using hvalues j + · intro t ht + have hlocal := hsize t (ht.trans hle) + have huniform := allowance_le_padded (k := k) (s := s) (Q := Q) + w.length b σ q m (2 ^ m) limit p (k * 3 + 1) r 1 0 + ((Iter.path stack).map Fin.val) + (Cslib.BitString.blocksEquiv.symm + (fun j => S (Cslib.TreeEval.Ary.reg fr.target j))) + (Fact.out : p.Prime).pos + change (GateReply.encState ((GateReply.machine M e).runFrom w + (GateReply.initial M e ((σ, q, (Iter.path stack).map Fin.val), + (((w.length, b, 0, 0, m, 0, r * m), (p, 2 ^ m, (k * 3 + 1) * r), + List.ofFn (fun a => p + (Cslib.BitString.blocksEquiv.symm + (fun j => S (Cslib.TreeEval.Ary.reg fr.target j)) a).val), limit), r))) t)).length + ≤ _ + apply hlocal.trans + change GateReply.allowance k s Q + (gateArgs (ctx, (p, k * 3 + 1, r), (1, (Iter.path stack).map Fin.val, + List.ofFn (fun a => p + (Cslib.BitString.blocksEquiv.symm + (fun j => S (Cslib.TreeEval.Ary.reg fr.target j)) a).val), 0))) ≤ _ + exact huniform.trans (by dsimp only [replySpace, limit]; omega) + · have hv := RawController.validBank_ofRegs (Fact.out : p.Prime).two_le S + simp_all [RawController.need, RawController.ofCfg, RawController.ofMode, + RawController.needCore, RawController.ofFrame] + +end Turing.MultiTapePTM.BlockComputation.ConcreteSupplier diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteSupplierSpace.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteSupplierSpace.lean new file mode 100644 index 0000000000..fff4d05b4c --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ConcreteSupplierSpace.lean @@ -0,0 +1,167 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteSupplier +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialVectorCorrect +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.List.Size +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Pi + +/-! +# Explicit uniform storage for concrete evaluator suppliers + +The bounds use binary metadata at the supplied horizon and the actual field-point dimensions. +The point and assignment capacity are charged with constant coefficients, independent of the +number of coordinates and the schedule horizon. Replay uses the existing linear local replay +allowance, with binary origins bounded by the horizon and a fixed finite head-row allowance. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ConcreteSupplier + +open MultiTapeTM Cslib.TreeEval.Ary Cslib.TreeEval.Ary.Packed +open QueryReplay +open MultiTapeNTM.ChoiceTable + +/-- Point-free binary metadata at the horizon and largest requested output coordinate. -/ +@[expose] noncomputable def metadataSpace + (N b σ q m alphabet limit p d r : ℕ) : ℕ := + (QueryGate.encParams Interpolation.encContext + ((N, b, σ, q, m, r, r * m), (p, alphabet, d * r), [], limit)).length + +/-- Uniform replay allowance: origins are binary horizon counts and the head family is fixed. -/ +@[expose] noncomputable def replaySpace (k s : ℕ) (Q : Type) + (N b q m r : ℕ) : ℕ := + 2 * (SupplierChunk.encContext (m, r)).length + 1 + (Encoding.width k s b + 1) + + (2 * (4 * (encNat q).length + + Fintype.card (Option (Fin k)) * (2 * Nat.card SignType + 1) + 2) + 2 + + 1024 * (k + 1) * (b + Nat.log2 N + Nat.card (Option Q) + Nat.card OutputTrack + 1)) + +/-- Concrete gate storage, without multiplying the point or assignment limit by the horizon. -/ +@[expose] noncomputable def gateSpace (k s : ℕ) (Q : Type) + (N b σ q m alphabet limit p d r : ℕ) : ℕ := + let cell := 2 * (encNat p).length + 1 + 48 * (metadataSpace N b σ q m alphabet limit p d r + 2 * (d * r * cell) + 1) + + 16 * (encNat r).length + 2 * r * cell + replaySpace k s Q N b q m r + 5 + +private theorem normalized_point_encoding_le {p d r : ℕ} (point : List ℕ) + (hp : 0 < p) (hlen : point.length ≤ d * r) : + (encList encNat (NormalizePoint.normalize p point)).length ≤ + d * r * (2 * (encNat p).length + 1) := by + have h := length_encList_le encNat (NormalizePoint.normalize p point) + (b := (encNat p).length) (by + intro value hvalue + obtain ⟨v, _, rfl⟩ := List.mem_map.mp hvalue + exact length_encNat_mono (Nat.mod_lt v hp).le) + rw [NormalizePoint.length_normalize] at h + exact h.trans (Nat.mul_le_mul_right _ hlen) + +/-- The replay's two origin counters never exceed the supplied horizon. -/ +theorem calleeSpace_le_replaySpace {k s : ℕ} {Q : Type} + (N b σ q m r i : ℕ) (hi : i ≤ q) : + Interpolation.calleeSpace k s Q (N, b, σ, i, m, r, r * m) ≤ + replaySpace k s Q N b q m r := by + have hp := length_encNat_mono ((ScheduleRead.iterate_snd_le k 0 2 σ i).trans hi) + have hn := length_encNat_mono ((ScheduleRead.iterate_snd_le k 0 0 σ i).trans hi) + have hrow := length_encPi_le (encFinite SignType) (ScheduleRead.fullrow k (σ, i)) + (bound := Nat.card SignType) (fun _ => (length_encFinite_lt _).le) + change (encNat (ScheduleRead.read k 0 2 (σ, i))).length ≤ (encNat q).length at hp + change (encNat (ScheduleRead.read k 0 0 (σ, i))).length ≤ (encNat q).length at hn + simp only [Interpolation.calleeSpace, ScheduleRead.origin, Gate.encContext, + length_encPair, replaySpace] + omega + +private theorem interpolation_params_encoding_le + (N b σ q m alphabet limit p d r i chunk : ℕ) (point : List ℕ) + (hi : i ≤ q) (hc : chunk ≤ r) + (hpoint : (encList encNat point).length ≤ d * r * (2 * (encNat p).length + 1)) : + (QueryGate.encParams Interpolation.encContext + ((N, b, σ, i, m, chunk, r * m), (p, alphabet, d * r), point, limit)).length ≤ + metadataSpace N b σ q m alphabet limit p d r + + 2 * (d * r * (2 * (encNat p).length + 1)) := by + have hi' := length_encNat_mono hi + have hc' := length_encNat_mono hc + simp only [metadataSpace, QueryGate.encParams, Interpolation.encContext, encNat3, + length_encPair] + have hempty : (encList encNat []).length = 0 := rfl + rw [hempty] + omega + +/-- Every stored request point of the stated dimensions has one explicit uniform gate bound. +No validity or typed-path premise is needed: navigation is bounded even on malformed paths. -/ +theorem allowance_le {k s : ℕ} {Q : Type} + (N b σ q m alphabet limit p d r tag chunk : ℕ) (path point : List ℕ) + (hp : 0 < p) (hlen : point.length ≤ d * r) : + GateReply.allowance k s Q + (gateArgs ((N, b, σ, q, m, alphabet, limit), (p, d, r), (tag, path, point, chunk))) ≤ + gateSpace k s Q N b σ q m alphabet limit p d r := by + let i := ImplicitPath.walk k (σ, q, path) - 1 + have hi : i ≤ q := (Nat.sub_le _ _).trans (ImplicitPath.walk_le k σ q path) + have hpoint := normalized_point_encoding_le point hp hlen + have hparams := interpolation_params_encoding_le N b σ q m alphabet limit p d r i 0 + (NormalizePoint.normalize p point) hi (Nat.zero_le r) hpoint + have hmax := interpolation_params_encoding_le N b σ q m alphabet limit p d r i r + (NormalizePoint.normalize p point) hi le_rfl hpoint + have hcallee := calleeSpace_le_replaySpace (k := k) (s := s) (Q := Q) + N b σ q m r i hi + change 2 * GateVector.callerSpace + (((N, b, σ, i, m, 0, r * m), (p, alphabet, d * r), + NormalizePoint.normalize p point, limit), r) + + 16 * ((QueryGate.encParams Interpolation.encContext + ((N, b, σ, i, m, r, r * m), (p, alphabet, d * r), + NormalizePoint.normalize p point, limit)).length + 1) + + Interpolation.calleeSpace k s Q (N, b, σ, i, m, r, r * m) + 5 ≤ _ + simp only [GateVector.callerSpace, GateVector.encParams, length_encPair, gateSpace] + at hparams hmax ⊢ + simp only [Nat.mul_assoc] at hparams hmax ⊢ + omega + +/-- Actual modulus-padded field points satisfy the uniform dimension bound directly. -/ +theorem allowance_le_padded {k s : ℕ} {Q : Type} + (N b σ q m alphabet limit p d r tag chunk : ℕ) (path : List ℕ) + (x : Fin (d * r) → ZMod p) (hp : 0 < p) : + GateReply.allowance k s Q + (gateArgs ((N, b, σ, q, m, alphabet, limit), (p, d, r), + (tag, path, List.ofFn (fun j => p + (x j).val), chunk))) ≤ + gateSpace k s Q N b σ q m alphabet limit p d r := + allowance_le N b σ q m alphabet limit p d r tag chunk path _ hp + (by simp only [List.length_ofFn, le_refl]) + +/-- Immediate node replies pay only for one field vector and a fixed reply tag. -/ +theorem node_reply_encoding_le {k s p r m : ℕ} {Q : Type} + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (limit b σ q : ℕ) [NeZero b] + (path : List ℕ) (hcapacity : r ≤ limit.size) + (hb : Encoding.width k s b ≤ r * m) (hsp : 2 ^ m ≤ p) : + (RawController.encReply (NodeReply.reply M e ((σ, q, path), (limit, m, r)))).length ≤ + r * (2 * (encNat p).length + 1) + 8 := by + have hvalues := length_encList_le encNat (InitialVector.values M e (limit, m, r)) + (b := (encNat p).length) (by + intro value hvalue + rw [InitialVector.values_eq_packBits M e limit b hcapacity hb] at hvalue + obtain ⟨j, rfl⟩ := List.mem_ofFn.mp hvalue + exact length_encNat_mono ((Fin.isLt _).trans_le hsp).le) + have hlength : (InitialVector.values M e (limit, m, r)).length = r := by + rw [InitialVector.length_values, Nat.min_eq_left hcapacity] + rw [hlength] at hvalues + have htag : ImplicitNode.classify k (σ, q, path) ≤ 2 := by + simp only [ImplicitNode.classify] + split <;> (try split) <;> omega + have htaglen := length_encNat_mono htag + have htwo : (encNat 2).length = 2 := rfl + have hone : (encNat 1).length = 1 := rfl + simp only [NodeReply.reply, hcapacity, ↓reduceIte] + split + · simp only [RawController.encReply, length_encPair] + omega + · simp only [RawController.encReply, length_encPair] + have hempty : (encList encNat []).length = 0 := rfl + rw [hempty] + omega + +end Turing.MultiTapePTM.BlockComputation.ConcreteSupplier diff --git a/Complexity/Computability/TreeEval/Ary/Packed/RawControllerPoint.lean b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerPoint.lean new file mode 100644 index 0000000000..3b962cbda5 --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerPoint.lean @@ -0,0 +1,56 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerNative +public import Complexity.Foundations.Data.BitString.Block +import Complexity.Foundations.Data.List.Lookup +import Mathlib.Data.List.GetD +import Mathlib.Data.List.OfFn + +/-! +# Exact register points requested by the raw controller + +Dropping the target register and concatenating the remaining bank in cyclic order gives the +existing typed child-register point, with the original modulus padding on every cell. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed.RawController + +/-- The physical request point contains exactly the cyclic typed registers, still padded. -/ +theorem point_ofRegs {p d r : ℕ} (S : Ary.State (ZMod p) d r) (t : Fin (d + 1)) : + point d r t.val (ofRegs S) = List.ofFn + (fun a : Fin (d * r) => p + + (Cslib.BitString.blocksEquiv.symm (fun j => S (Ary.reg t j)) a).val) := by + have hlength := length_point (ofRegs S) (length_ofRegs S) t + apply List.ext_getElem (hlength.trans List.length_ofFn.symm) + intro a ha _ + let ai : Fin (d * r) := ⟨a, by omega⟩ + rcases hpair : finProdFinEquiv.symm ai with ⟨j, i⟩ + have hai : finProdFinEquiv (j, i) = ai := by + rw [← hpair, Equiv.apply_symm_apply] + have hav : i.val + r * j.val = a := congrArg Fin.val hai + have hread := point_getD (ofRegs S) (length_ofRegs S) t j i + rw [reg_eq] at hread + change (point d r t.val (ofRegs S)).getD (i.val + r * j.val) 0 = + (ofRegs S).getD (i.val + r * (Ary.reg t j).val) 0 at hread + rw [hav] at hread + rw [← List.getD_eq_getElem _ _ ha] + rw [hread] + change (ofRegs S).getD (finProdFinEquiv (Ary.reg t j, i)).val 0 = _ + rw [ofRegs, List.getD_ofFn_of_lt _ (finProdFinEquiv (Ary.reg t j, i)).isLt 0] + simp only [List.getElem_ofFn] + change p + (S (finProdFinEquiv.symm (finProdFinEquiv (Ary.reg t j, i))).1 + (finProdFinEquiv.symm (finProdFinEquiv (Ary.reg t j, i))).2).val = + p + (S (Ary.reg t (finProdFinEquiv.symm ai).1) (finProdFinEquiv.symm ai).2).val + rw [Equiv.symm_apply_apply] + rw [hpair] + +end Cslib.TreeEval.Ary.Packed.RawController diff --git a/ComplexityTests/Machines/Turing/MultiTape/ConcreteEvaluator.lean b/ComplexityTests/Machines/Turing/MultiTape/ConcreteEvaluator.lean new file mode 100644 index 0000000000..4de174d3b8 --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/ConcreteEvaluator.lean @@ -0,0 +1,56 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of concrete implicit evaluation) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteEvaluatorCorrect + +/-! +# Concrete evaluator regressions + +A complete height-zero tree evaluation calls the concrete node supplier, returns its initial +source vector and decodes its actual physical bank. The fixed machine's default initialization +is halted; runtime metadata and bank are supplied explicitly. +-/ + +public section + +namespace ComplexityTests.ConcreteEvaluator + +open Turing MultiTapeTM MultiTapePTM.BlockComputation +open Cslib.TreeEval.Ary.Packed + +/-- A singleton control-state encoding. -/ +abbrev code : Fin 1 ≃ Fin 1 := Equiv.refl _ + +/-- The computation used by the implicit tree has no work tapes and halts in one step. -/ +def control : MultiTapeTM 0 Bool (Fin 1) := MultiTapeTM.ofTr 0 fun _ _ _ => + { inputTape := .zero, workTapes := fun _ => (none, .zero), output := none, state := none } + +/-- A zero bank with paid assignment capacity and a coefficient-one root request. -/ +def initial : ConcreteEvaluator.State 0 (Fin 1) := + .inl ((1, 1, 0, 0, 1, 2, 8192), (17, 1, 13), List.replicate 26 17, [], some (1, 0)) + +/-- Observe the physical bank only while the evaluator caller is active. -/ +def observe : ConcreteEvaluator.State 0 (Fin 1) → Option (List ℕ) := + Sum.elim (fun (_, _, bank, _, _) => some bank) (fun _ => none) + +example : (ConcreteEvaluator.machine control code).halted + (ConcreteEvaluator.machine control code).init = true := by decide + +example : (ConcreteEvaluator.machine control code).halted initial = false := by decide + +example : observe ((ConcreteEvaluator.machine control code).runFrom [true] initial 2) = + some ([18, 18] ++ List.replicate 24 17) := by decide + +example : (ConcreteEvaluator.machine control code).halted + ((ConcreteEvaluator.machine control code).runFrom [true] initial 2) = true := by decide + +example : RawController.Output.decode ((13, 1, 17, 13, 0), [18, 18] ++ List.replicate 24 17) = + [true, true] ++ List.replicate 11 false := by decide + +end ComplexityTests.ConcreteEvaluator From 03b99a16cc507ded363d3063d4e0e2ffb0629ee1 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 04:55:28 +0000 Subject: [PATCH 32/36] feat(hpv): initialize the concrete evaluator at runtime --- Complexity.lean | 7 + .../BlockComputation/Encoding/WidthSpace.lean | 40 ++++ .../InitializedEvaluator.lean | 119 +++++++++++ .../InitializedEvaluatorCorrect.lean | 196 ++++++++++++++++++ .../BlockComputation/Initializer.lean | 191 +++++++++++++++++ .../BlockComputation/InitializerBounds.lean | 103 +++++++++ .../BlockComputation/InitializerCorrect.lean | 119 +++++++++++ .../Ary/Packed/RawControllerZero.lean | 35 ++++ .../MultiTape/InitializedEvaluator.lean | 83 ++++++++ 9 files changed, 893 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/WidthSpace.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitializedEvaluator.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitializedEvaluatorCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Initializer.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitializerBounds.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitializerCorrect.lean create mode 100644 Complexity/Computability/TreeEval/Ary/Packed/RawControllerZero.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/InitializedEvaluator.lean diff --git a/Complexity.lean b/Complexity.lean index 89e13ef457..33a91949e7 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1701,6 +1701,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeChecked public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeDecode public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeEncode +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.WidthSpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateInterpolation public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateInterpolationBounds public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.GateInterpolationCorrect @@ -1721,6 +1722,11 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialVector public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialVectorCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitialVectorGate +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitializedEvaluator +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitializedEvaluatorCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Initializer +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitializerBounds +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitializerCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NodeReply public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NodeReplyCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NormalizePoint @@ -2411,6 +2417,7 @@ public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerOutput public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerPoint public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerQuery public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerSpace +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerZero public import Complexity.Computability.TreeEval.Ary.PackingParameters public import Complexity.Computability.TreeEval.Basic public import Complexity.Computability.TreeEval.CookMertz diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/WidthSpace.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/WidthSpace.lean new file mode 100644 index 0000000000..4fc7ed48f3 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/WidthSpace.lean @@ -0,0 +1,40 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatSize + +/-! +# Native block-value width initialization + +The encoded value width is computed from the actual binary block width. Machine tape and finite +control counts are fixed parameters. No block-sized intermediate word is allocated. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.Encoding + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +/-- Compute the complete block-value width in space linear in the actual binary block width. -/ +theorem width_inSpace (k s : ℕ) : ComputableInSpace (width k s) encNat encNat + (fun b => (encNat b).length) := by + have hid : ComputableInSpace (fun b : ℕ => b) encNat encNat + (fun b => (encNat b).length) := of_inO (ComputableInO.of_eq (f := id) fun _ => rfl) + have hconst (n : ℕ) : ComputableInSpace (fun _ : ℕ => n) encNat encNat + (fun b => (encNat b).length) := budget_const n (.refl _) + have hsize := budget_comp_linear natSize hid + have hwindow := budget_natMul (hconst k) + (budget_natMul (hconst 3) (budget_natMul hid (hconst 2))) + exact (budget_natSucc (budget_natAdd (hconst (s + 1).size) + (budget_natAdd (budget_natMul (hconst (k + 1)) hsize) + (budget_natAdd (hconst 2) hwindow)))).of_enc_eq fun _ => rfl + +end Turing.MultiTapePTM.BlockComputation.Encoding diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitializedEvaluator.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitializedEvaluator.lean new file mode 100644 index 0000000000..a40c2b5b59 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitializedEvaluator.lean @@ -0,0 +1,119 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Initializer +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteEvaluator +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Sum + +/-! +# A native evaluator with streamed runtime initialization + +One fixed machine first builds the selected metadata and padded bank, then enters the concrete +evaluator on the same original read-only input. Actual binary arguments enter through `start`; +the fixed template contains neither runtime parameters nor an input word. Native component +certificates cover both phases and their handoff. This construction does not assert a complexity +class inclusion or initialize an outer schedule/time search. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.InitializedEvaluator + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +variable {k s : ℕ} {Q : Type} + +/-- A streamed initializer or the concrete evaluator with its actual bank and supplier state. -/ +abbrev State (k : ℕ) (Q : Type) := Initializer.State ⊕ ConcreteEvaluator.State k Q + +/-- Runtime arguments enter a fresh streamed initializer. -/ +@[expose] def start (k s : ℕ) (args : Initializer.Args) : State k Q := + .inl (Initializer.prepare k s args) + +/-- Enter the concrete evaluator using only the actual completed initializer state. -/ +@[expose] def resume (k : ℕ) (state : Initializer.State) : ConcreteEvaluator.State k Q := + .inl (Initializer.result k state) + +/-- Fixed same-input initialization followed by concrete evaluation. -/ +@[expose] noncomputable def machine (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + QueryMachine (State k Q) where + init := .inr (ConcreteEvaluator.machine M e).init + query := Sum.elim (fun _ => 0) (ConcreteEvaluator.machine M e).query + next := fun (state, symbol) => state.elim + (fun initial => if (Initializer.machine k).halted initial then .inr (resume k initial) + else .inl (Initializer.step k initial)) + (fun active => .inr ((ConcreteEvaluator.machine M e).next (active, symbol))) + emit _ := [] + halted := Sum.elim (fun _ => false) (ConcreteEvaluator.machine M e).halted + finish _ := [] + +variable [Finite Q] + +/-- Each phase retains and charges its actual payload. -/ +@[expose] noncomputable def encState : State k Q ↪ List Bool := + encSum Initializer.encState (ConcreteEvaluator.encState (k := k) (Q := Q)) + +/-- The handoff charges the actual selected header and constructed bank. -/ +theorem resume_inSpace : ComputableInSpace (resume (k := k) (Q := Q)) Initializer.encState + ConcreteEvaluator.encState (fun a => (Initializer.encState a).length) := by + have hinl := of_inO (ComputableInO.inl + (Cslib.TreeEval.Ary.Packed.RawController.Query.encContext ConcreteSupplier.encContext) + (encPair + (Cslib.TreeEval.Ary.Packed.RawController.Query.encContext ConcreteSupplier.encContext) + (QueryReplay.GateReply.encState (k := k) (Q := Q)))) + exact (budget_comp_linear hinl (Initializer.result_inSpace k)).of_enc_eq fun _ => rfl + +/-- Native entry computes selected metadata from actual binary arguments. -/ +theorem start_inSpace : ComputableInSpace (start (Q := Q) k s) + Initializer.encArgs encState (fun a => (Initializer.encArgs a).length) := by + have hinl := of_inO (ComputableInO.inl Initializer.encState + (ConcreteEvaluator.encState (k := k) (Q := Q))) + exact (budget_comp_linear hinl (Initializer.prepare_inSpace k s)).of_enc_eq fun _ => rfl + +/-- Initializer clocks need no input; evaluator clocks preserve the original-input queries. -/ +theorem machine_query_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).query encState encNat (fun a => (encState a).length) := + sumElim (budget_const (encIn := Initializer.encState) (encB := encNat) 0 (.refl _)) + (ConcreteEvaluator.machine_query_inSpace M e) + +/-- Native transitions cover streamed growth, handoff and concrete evaluator execution. -/ +theorem machine_next_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).next (encPair encState (encOption encBool)) encState + (fun a => (encPair encState (encOption encBool) a).length) := by + have hs := fst Initializer.encState (encOption encBool) + have hinl := of_inO (ComputableInO.inl Initializer.encState + (ConcreteEvaluator.encState (k := k) (Q := Q))) + have hinr := of_inO (ComputableInO.inr Initializer.encState + (ConcreteEvaluator.encState (k := k) (Q := Q))) + have hrun := budget_comp_linear hinl (budget_comp_linear (Initializer.step_inSpace k) hs) + have hresume := budget_comp_linear hinr (budget_comp_linear resume_inSpace hs) + have hhalt := budget_comp_linear (Initializer.machine_halted_inSpace k) hs + have hleft := budget_ite hhalt hresume hrun + have hright := budget_comp_linear hinr (ConcreteEvaluator.machine_next_inSpace M e) + exact sumElimFst hleft hright + +/-- Both phases keep their result in state and emit no intermediate word. -/ +theorem machine_emit_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).emit (encPair encState (encOption encBool)) encBits + (fun a => (encPair encState (encOption encBool) a).length) := + budget_const [] (.refl _) + +/-- Initialization remains live until handoff; concrete evaluation decides final completion. -/ +theorem machine_halted_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).halted encState encBool (fun a => (encState a).length) := + sumElim (budget_const (encIn := Initializer.encState) (encB := encBool) false (.refl _)) + (ConcreteEvaluator.machine_halted_inSpace M e) + +/-- The final bank is read from the actual returned state. -/ +theorem machine_finish_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).finish encState encBits (fun a => (encState a).length) := + budget_const [] (.refl _) + +end Turing.MultiTapePTM.BlockComputation.InitializedEvaluator diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitializedEvaluatorCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitializedEvaluatorCorrect.lean new file mode 100644 index 0000000000..2f3f895de9 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitializedEvaluatorCorrect.lean @@ -0,0 +1,196 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitializedEvaluator +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitializerBounds +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteEvaluatorCorrect + +/-! +# Initialized same-input evaluation of the encoded block tree + +The fixed machine selects its packing parameters, allocates its actual padded bank one cell +per clock, and enters the concrete evaluator. Its final bank decodes to the existing encoded +tree's root. Every physical clock in both phases is covered by an explicit allowance. Outer +time and schedule search and the standard Turing-machine compilation remain separate tasks. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.InitializedEvaluator + +open Cslib.TreeEval.Ary Cslib.TreeEval.Ary.Packed + +variable {k s : ℕ} {Q : Type} + +/-- The active branch preserves all physical evaluator clocks. -/ +theorem runFrom_inr (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (state : ConcreteEvaluator.State k Q) (t : ℕ) : + (machine M e).runFrom w (.inr state) t = + .inr ((ConcreteEvaluator.machine M e).runFrom w state t) := by + have hstep (q : ConcreteEvaluator.State k Q) : + (machine M e).step w (.inr q) = .inr ((ConcreteEvaluator.machine M e).step w q) := by + simp only [QueryMachine.step, QueryMachine.read, machine, Sum.elim_inr] + by_cases h : (ConcreteEvaluator.machine M e).halted q = true <;> + simp only [h, Bool.false_eq_true, ↓reduceIte] + induction t with + | zero => rfl + | succ t ih => rw [QueryMachine.runFrom_succ, ih, hstep, QueryMachine.runFrom_succ] + +/-- Initialization is reproduced clock for clock through the last allocation. -/ +theorem runFrom_initializer_prefix (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (args : Initializer.Args) (w : List Bool) (j : ℕ) + (hj : j ≤ Initializer.bankCells k ((Initializer.prepare k s args).1)) : + (machine M e).runFrom w (start k s args) j = + .inl ((Initializer.machine k).runFrom w (Initializer.prepare k s args) j) := by + induction j with + | zero => rfl + | succ j ih => + have hjlt : j < Initializer.bankCells k ((Initializer.prepare k s args).1) := by omega + have hhalt : (Initializer.machine k).halted + ((Initializer.machine k).runFrom w (Initializer.prepare k s args) j) = false := by + change (Initializer.machine k).halted ((Initializer.machine k).runFrom w + ((Initializer.prepare k s args).1, 0, [], 1) j) = false + rw [Initializer.runFrom_prefix k ((Initializer.prepare k s args).1) w j hjlt.le] + change decide (Initializer.bankCells k ((Initializer.prepare k s args).1) ≤ j) = false + exact decide_eq_false (by omega) + rw [QueryMachine.runFrom_succ, ih (by omega), QueryMachine.runFrom_succ] + change (if (Initializer.machine k).halted + ((Initializer.machine k).runFrom w (Initializer.prepare k s args) j) then _ else _) = _ + rw [hhalt] + rw [QueryMachine.step, hhalt] + rfl + +/-- The clock after the final allocation enters the concrete evaluator on the completed bank. -/ +theorem runFrom_entry (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (N b σ q : ℕ) (w : List Bool) : + let cells := ((k * 3 + 1) + 1) * chunkCount (k * 3 + 1) (Encoding.width k s b) + (machine M e).runFrom w (start k s (N, b, σ, q)) (cells + 1) = + .inr (resume k ((Initializer.machine k).runFrom w + (Initializer.prepare k s (N, b, σ, q)) cells)) := by + dsimp only + rw [QueryMachine.runFrom_succ, runFrom_initializer_prefix M e (N, b, σ, q) w + (((k * 3 + 1) + 1) * chunkCount (k * 3 + 1) (Encoding.width k s b)) le_rfl] + change (if (Initializer.machine k).halted + ((Initializer.machine k).runFrom w (Initializer.prepare k s (N, b, σ, q)) + (((k * 3 + 1) + 1) * chunkCount (k * 3 + 1) (Encoding.width k s b))) then _ else _) = _ + simp only [Initializer.halted_prepare, ↓reduceIte] + +/-- One metadata-only allowance covers initialization and every evaluator subroutine clock. -/ +@[expose] noncomputable def allowance (k s : ℕ) (Q : Type) (N b σ q : ℕ) : ℕ := + let d := k * 3 + 1 + let B := Encoding.width k s b + let m := chunkWidth d B + let r := chunkCount d B + let p := fieldModulus d B + let limit := (2 ^ m) ^ (d * r) + let ctx : ConcreteSupplier.Context := (N, b, σ, q, m, 2 ^ m, limit) + Initializer.space k ((Initializer.prepare k s (N, b, σ, q)).1) + + RawController.Query.queryStateBound (ConcreteSupplier.encContext ctx).length p d r q + (ConcreteSupplier.replySpace k s Q N b σ q m (2 ^ m) limit p d r) + 2 + +/-- A phase handoff combines the actual initializer and evaluator storage bounds. -/ +theorem length_runFrom_entry_le [Finite Q] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (args : Initializer.Args) + (w : List Bool) (active : ConcreteEvaluator.State k Q) (T C : ℕ) + (hentry : (machine M e).runFrom w (start k s args) + (Initializer.bankCells k ((Initializer.prepare k s args).1) + 1) = .inr active) + (hspace : ∀ u ≤ T, + (ConcreteEvaluator.encState ((ConcreteEvaluator.machine M e).runFrom w active u)).length ≤ C) + (t : ℕ) (ht : t ≤ Initializer.bankCells k ((Initializer.prepare k s args).1) + 1 + T) : + (encState ((machine M e).runFrom w (start k s args) t)).length ≤ + Initializer.space k ((Initializer.prepare k s args).1) + C + 2 := by + let cells := Initializer.bankCells k ((Initializer.prepare k s args).1) + by_cases hinit : t ≤ cells + · have hbound : (Initializer.encState ((Initializer.machine k).runFrom w + (Initializer.prepare k s args) t)).length ≤ + Initializer.space k ((Initializer.prepare k s args).1) := + Initializer.length_runFrom_prepare_le k s args w t hinit + rw [runFrom_initializer_prefix M e args w t hinit] + change (Initializer.encState ((Initializer.machine k).runFrom w + (Initializer.prepare k s args) t)).length + 1 ≤ _ + omega + · have htge : cells + 1 ≤ t := by omega + obtain ⟨u, rfl⟩ := Nat.exists_eq_add_of_le htge + have hu : u ≤ T := by change cells + 1 + u ≤ cells + 1 + T at ht; omega + rw [QueryMachine.runFrom_add, hentry, runFrom_inr] + have hbound := hspace u hu + change (ConcreteEvaluator.encState + ((ConcreteEvaluator.machine M e).runFrom w active u)).length + 1 ≤ _ + omega + +/-- Native initialization followed by concrete evaluation returns the existing encoded tree's +root, with selected packing and field hypotheses discharged internally. -/ +theorem exists_runFrom_decode_root [Finite Q] (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (b : ℕ) [NeZero b] (σ q : ℕ) : + let d := k * 3 + 1 + let B := Encoding.width k s b + let m := chunkWidth d B + let r := chunkCount d B + let p := fieldModulus d B + let ctx : ConcreteSupplier.Context := (w.length, b, σ, q, m, 2 ^ m, (2 ^ m) ^ (d * r)) + ∃ clock bank, + (machine M e).runFrom w (start k s (w.length, b, σ, q)) clock = + .inr (.inl (ctx, (p, d, r), bank, [], none)) ∧ + RawController.Output.decode ((B, m, p, r, 0), bank) = + List.ofFn (encodedTree (MultiTapePTM.ofDeterministic M) w b + (scheduleOfNat k q σ) e).eval ∧ + (machine M e).halted + ((machine M e).runFrom w (start k s (w.length, b, σ, q)) clock) = true ∧ + ∀ t ≤ clock, + (encState ((machine M e).runFrom w (start k s (w.length, b, σ, q)) t)).length ≤ + allowance k s Q w.length b σ q := by + classical + let d := k * 3 + 1 + let B := Encoding.width k s b + let m := chunkWidth d B + let r := chunkCount d B + let p := fieldModulus d B + let limit := (2 ^ m) ^ (d * r) + let ctx : ConcreteSupplier.Context := (w.length, b, σ, q, m, 2 ^ m, limit) + let cells := (d + 1) * r + have hd : 0 < d := by dsimp only [d]; omega + have hB : 0 < B := Encoding.width_pos k s b + have hm : 0 < m := chunkWidth_pos hd hB + have hb : Encoding.width k s b ≤ r * m := le_chunkCount_mul hd hB + have hsp : 2 ^ m ≤ p := (alphabetSize_lt hd hB).le + have hp : Fact p.Prime := ⟨fieldModulus_prime d B⟩ + have hcapacity : r ≤ ((2 ^ m) ^ (d * r)).size := by + rw [← pow_mul, Nat.size_pow] + have hmr : r ≤ m * (d * r) := by + have := Nat.mul_le_mul_right r (Nat.mul_pos hm hd) + nlinarith + omega + obtain ⟨T, bank, hrun, hdecode, hspace⟩ := ConcreteEvaluator.exists_runFrom_decode_root + (q := q) M e w b σ hcapacity hb hm hsp (alphabetEmbedding hd hB) (fun _ => rfl) + (gateDegree_lt d B) (0 : Cslib.TreeEval.Ary.State (ZMod p) d r) 0 rfl + have hentry : (machine M e).runFrom w (start k s (w.length, b, σ, q)) (cells + 1) = + .inr (.inl (ctx, (p, d, r), RawController.ofCfg + (Iter.initialCfg (0 : Cslib.TreeEval.Ary.State (ZMod p) d r) 1 (0 : Fin (d + 1))))) := by + rw [runFrom_entry] + unfold resume + rw [Initializer.result_runFrom_prepare] + have hafter (u : ℕ) : + (machine M e).runFrom w (start k s (w.length, b, σ, q)) ((cells + 1) + u) = + .inr ((ConcreteEvaluator.machine M e).runFrom w + (.inl (ctx, (p, d, r), RawController.ofCfg + (Iter.initialCfg (0 : Cslib.TreeEval.Ary.State (ZMod p) d r) 1 0))) u) := by + rw [QueryMachine.runFrom_add, hentry, runFrom_inr] + refine ⟨cells + 1 + T, bank, ?_, hdecode, ?_, ?_⟩ + · rw [hafter, hrun] + · rw [hafter, hrun] + change !RawController.validBank p d r bank || (true && true) = true + simp + · exact length_runFrom_entry_le M e (w.length, b, σ, q) w + (.inl (ctx, (p, d, r), RawController.ofCfg + (Iter.initialCfg (0 : Cslib.TreeEval.Ary.State (ZMod p) d r) 1 0))) T + (RawController.Query.queryStateBound (ConcreteSupplier.encContext ctx).length + p d r q (ConcreteSupplier.replySpace k s Q w.length b σ q m (2 ^ m) + limit p d r)) hentry hspace + +end Turing.MultiTapePTM.BlockComputation.InitializedEvaluator diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Initializer.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Initializer.lean new file mode 100644 index 0000000000..24b2eff7f7 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Initializer.lean @@ -0,0 +1,191 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteEvaluator +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.WidthSpace +public import Complexity.Computability.TreeEval.Ary.Packed.ParametersSpace +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases + +/-! +# Streamed native initialization of the concrete evaluator + +Runtime input length, block width, schedule and horizon determine the selected packed parameters. +Each subsequent clock appends one padded zero field cell. The assignment capacity grows by one +alphabet factor during each child-coordinate clock, so no binary length expands into a bank or +power in a single transition. The final state supplies the existing raw evaluator's initial call. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.Initializer + +open MultiTapeTM MultiTapeTM.ComputableInSpace Cslib.TreeEval.Ary.Packed + +/-- Runtime input length, block width, schedule number and horizon. -/ +abbrev Args := ℕ × ℕ × ℕ × ℕ + +/-- Actual runtime arguments and selected chunk width/count, alphabet and field modulus. -/ +abbrev Metadata := Args × (ℕ × ℕ × ℕ × ℕ) + +/-- Retained metadata, cursor, accumulated padded bank and assignment capacity. -/ +abbrev State := Metadata × ℕ × List ℕ × ℕ + +/-- Runtime arguments are stored as four binary naturals. -/ +@[expose] noncomputable def encArgs : Args ↪ List Bool := + encPair encNat (encPair encNat (encPair encNat encNat)) + +/-- Selected parameters are charged beside the original runtime arguments. -/ +@[expose] noncomputable def encMetadata : Metadata ↪ List Bool := + encPair encArgs encParameters + +/-- Every accumulated field cell and capacity bit is charged as it is produced. -/ +@[expose] noncomputable def encState : State ↪ List Bool := + encPair encMetadata (encPair encNat (encPair (encList encNat) encNat)) + +/-- Compute selected packing metadata without allocating the bank or assignment capacity. -/ +@[expose] def prepare (k s : ℕ) : Args → State := fun args => + let (_, b, _, _) := args + ((args, parameters (k * 3 + 1) (Encoding.width k s b)), 0, [], 1) + +/-- Append one padded zero cell and grow capacity only during actual child-coordinate clocks. -/ +@[expose] def step (k : ℕ) : State → State := + fun (metadata, cursor, bank, limit) => + let (_, (_, r, alphabet, p)) := metadata + let d := k * 3 + 1 + if cursor < (d + 1) * r then + (metadata, cursor + 1, bank ++ [p], if cursor < d * r then limit * alphabet else limit) + else (metadata, cursor, bank, limit) + +/-- Fixed empty halted template; actual runtime initialization starts through `runFrom`. -/ +@[expose] def template : State := (((0, 0, 0, 0), (0, 0, 0, 0)), 0, [], 1) + +/-- Silent streamed bank construction with one cell appended per physical clock. -/ +@[expose] def machine (k : ℕ) : QueryMachine State where + init := template + query _ := 0 + next := fun (state, _) => step k state + emit _ := [] + halted := fun (metadata, cursor, _, _) => + let (_, (_, r, _, _)) := metadata + decide (((k * 3 + 1) + 1) * r ≤ cursor) + finish _ := [] + +/-- Read the concrete evaluator's actual runtime initial caller from the completed bank. -/ +@[expose] def result (k : ℕ) : State → RawController.Query.Context ConcreteSupplier.Context := + fun (((N, b, σ, q), (m, r, alphabet, p)), _, bank, limit) => + ((N, b, σ, q, m, alphabet, limit), (p, k * 3 + 1, r), bank, [], some (1, 0)) + +/-- Preparing selected runtime metadata is linear in actual binary argument storage. -/ +theorem prepare_inSpace (k s : ℕ) : ComputableInSpace (prepare k s) encArgs encState + (fun a => (encArgs a).length) := by + have hid : ComputableInSpace (fun a : Args => a) encArgs encArgs + (fun a => (encArgs a).length) := of_inO (ComputableInO.of_eq (f := id) fun _ => rfl) + have hb := (snd encNat (encPair encNat (encPair encNat encNat))).fst' + have hwidth := budget_comp_linear (Encoding.width_inSpace k s) hb + have hparams := budget_comp_linear parameters_inSpace + (budget_prod (budget_const (encB := encNat) (k * 3 + 1) (.refl _)) hwidth) + exact (budget_prod (budget_prod hid hparams) (budget_prod + (budget_const (encB := encNat) 0 (.refl _)) (budget_prod + (budget_const (encB := encList encNat) [] (.refl _)) + (budget_const (encB := encNat) 1 (.refl _))))).of_enc_eq fun _ => rfl + +/-- Each streamed update uses space linear in its actual accumulated state. -/ +theorem step_inSpace (k : ℕ) : ComputableInSpace (step k) encState encState + (fun a => (encState a).length) := by + have hmeta := fst encMetadata (encPair encNat (encPair (encList encNat) encNat)) + have hrest := snd encMetadata (encPair encNat (encPair (encList encNat) encNat)) + have hparams := hmeta.snd' + have hr := hparams.snd'.fst' + have halphabet := hparams.snd'.snd'.fst' + have hp := hparams.snd'.snd'.snd' + have hcursor := hrest.fst' + have hbank := hrest.snd'.fst' + have hlimit := hrest.snd'.snd' + have hd := budget_const (encIn := encState) (encB := encNat) (k * 3 + 1) (.refl _) + have htotal := budget_natMul (budget_natSucc hd) hr + have hchildren := budget_natMul hd hr + have hlimit' := budget_ite (budget_natLt hcursor hchildren) + (budget_natMul hlimit halphabet) hlimit + have hnext := budget_prod hmeta (budget_prod (budget_natSucc hcursor) + (budget_prod (budget_snoc hbank hp) hlimit')) + have hid : ComputableInSpace (fun a : State => a) encState encState + (fun a => (encState a).length) := of_inO (ComputableInO.of_eq (f := id) fun _ => rfl) + refine (budget_ite (budget_natLt hcursor htotal) hnext hid).of_enc_eq ?_ + rintro ⟨⟨args, m, r, alphabet, p⟩, cursor, bank, limit⟩ + by_cases ht : cursor < (k * 3 + 1 + 1) * r <;> + by_cases hc : cursor < (k * 3 + 1) * r <;> + simp [step, ht, hc, encState, encPair_apply] + +/-- Extract the actual evaluator header, padded bank and initial call mode natively. -/ +theorem result_inSpace (k : ℕ) : ComputableInSpace (result k) encState + (RawController.Query.encContext ConcreteSupplier.encContext) + (fun a => (encState a).length) := by + have hmeta := fst encMetadata (encPair encNat (encPair (encList encNat) encNat)) + have hrest := snd encMetadata (encPair encNat (encPair (encList encNat) encNat)) + have hargs := hmeta.fst' + have hparams := hmeta.snd' + have hN := hargs.fst' + have hb := hargs.snd'.fst' + have hσ := hargs.snd'.snd'.fst' + have hq := hargs.snd'.snd'.snd' + have hm := hparams.fst' + have hr := hparams.snd'.fst' + have halphabet := hparams.snd'.snd'.fst' + have hp := hparams.snd'.snd'.snd' + have hbank := hrest.snd'.fst' + have hlimit := hrest.snd'.snd' + have hcontext := budget_prod hN (budget_prod hb (budget_prod hσ (budget_prod hq + (budget_prod hm (budget_prod halphabet hlimit))))) + have hheader := budget_prod hp (budget_prod + (budget_const (encB := encNat) (k * 3 + 1) (.refl _)) hr) + have hstack := budget_const (encIn := encState) (encB := encList RawController.encFrame) + [] (.refl _) + have hmode := budget_const (encIn := encState) + (encB := encOption (encPair encNat encNat)) (some (1, 0)) (.refl _) + exact (budget_prod hcontext (budget_prod hheader + (budget_prod hbank (budget_prod hstack hmode)))).of_enc_eq fun _ => rfl + +/-- No original input bit is needed during bank construction. -/ +theorem machine_query_inSpace (k : ℕ) : ComputableInSpace (machine k).query encState encNat + (fun a => (encState a).length) := budget_const 0 (.refl _) + +/-- Machine transitions use the certified streamed update. -/ +theorem machine_next_inSpace (k : ℕ) : ComputableInSpace (machine k).next + (encPair encState (encOption encBool)) encState + (fun a => (encPair encState (encOption encBool) a).length) := by + have h : ComputableInSpace (fun a : State × Option Bool => step k a.1) + (encPair encState (encOption encBool)) encState + (fun a => (encPair encState (encOption encBool) a).length) := + budget_comp_linear (step_inSpace k) (fst encState (encOption encBool)) + exact h.of_enc_eq fun _ => rfl + +/-- Initialization emits no word. -/ +theorem machine_emit_inSpace (k : ℕ) : ComputableInSpace (machine k).emit + (encPair encState (encOption encBool)) encBits + (fun a => (encPair encState (encOption encBool) a).length) := budget_const [] (.refl _) + +/-- The final cursor comparison uses only actual binary metadata. -/ +theorem machine_halted_inSpace (k : ℕ) : ComputableInSpace (machine k).halted encState encBool + (fun a => (encState a).length) := by + have hmeta := fst encMetadata (encPair encNat (encPair (encList encNat) encNat)) + have hrest := snd encMetadata (encPair encNat (encPair (encList encNat) encNat)) + have hr := hmeta.snd'.snd'.fst' + have htotal := budget_natMul + (budget_const (encIn := encState) (encB := encNat) (k * 3 + 1 + 1) (.refl _)) hr + exact (budget_finite (encC := encBool) Bool.not (budget_natLt hrest.fst' htotal)).of_enc_eq + fun ⟨⟨args, m, r, alphabet, p⟩, cursor, bank, limit⟩ => by + by_cases h : cursor < (k * 3 + 1 + 1) * r + · simp [machine, h, Nat.not_le_of_gt h] + · simp [machine, h, Nat.le_of_not_gt h] + +/-- The completed bank is returned as state rather than an output stream. -/ +theorem machine_finish_inSpace (k : ℕ) : ComputableInSpace (machine k).finish encState encBits + (fun a => (encState a).length) := budget_const [] (.refl _) + +end Turing.MultiTapePTM.BlockComputation.Initializer diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitializerBounds.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitializerBounds.lean new file mode 100644 index 0000000000..149bd889e0 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitializerBounds.lean @@ -0,0 +1,103 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitializerCorrect +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.List.Size +import Complexity.Computability.Machines.Turing.MultiTape.Internal.BinarySizeFacts + +/-! +# Space for streamed runtime initialization + +Every allocation clock retains one metadata record, a prefix of the padded bank and the +growing assignment capacity. A uniform bound is linear in the number of field cells and +their binary width, including the capacity accumulated by repeated multiplication. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.Initializer + +open MultiTapeTM +open Cslib.TreeEval.Ary.Packed + +/-- Metadata plus a constant number of bank-width allocations pays every initializer state. -/ +@[expose] noncomputable def space (k : ℕ) (metadata : Metadata) : ℕ := + let (_, (_, _, alphabet, p)) := metadata + 16 * ((encMetadata metadata).length + 1) + + 16 * bankCells k metadata * ((encNat p).length + (encNat alphabet).length + 1) + +/-- The power accumulator and allocated list fit the same bank-width budget at every prefix. -/ +theorem prefixState_encoding_le (k : ℕ) (metadata : Metadata) (j : ℕ) + (hj : j ≤ bankCells k metadata) : + (encState (prefixState k metadata j)).length ≤ space k metadata := by + rcases metadata with ⟨args, m, r, alphabet, p⟩ + dsimp only [bankCells] at hj + have hcursor := length_encNat_le_self j + have hlist := length_encList_le encNat (List.replicate j p) + (b := (encNat p).length) (by + intro value hvalue + have he : value = p := List.eq_of_mem_replicate hvalue + subst value + exact le_rfl) + simp only [List.length_replicate] at hlist + have hbank := hlist.trans (Nat.mul_le_mul_right (2 * (encNat p).length + 1) hj) + have hpower := Nat.size_pow_le alphabet (min j ((k * 3 + 1) * r)) + have hmul := Nat.mul_le_mul_right alphabet.size + ((Nat.min_le_left j ((k * 3 + 1) * r)).trans hj) + simp only [prefixState, encState, length_encPair, space, bankCells, + length_encNat_eq_size] + simp only [length_encNat_eq_size] at hcursor hbank + nlinarith + +/-- All actual physical allocation clocks obey one explicit state bound. -/ +theorem length_runFrom_le (k : ℕ) (metadata : Metadata) (w : List Bool) + (j : ℕ) (hj : j ≤ bankCells k metadata) : + (encState ((machine k).runFrom w (metadata, 0, [], 1) j)).length ≤ space k metadata := by + rw [runFrom_prefix k metadata w j hj] + exact prefixState_encoding_le k metadata j hj + +/-- In particular, the runtime-selected initialization has a bound through its final clock. -/ +theorem length_runFrom_prepare_le (k s : ℕ) (args : Args) (w : List Bool) + (j : ℕ) (hj : j ≤ bankCells k ((prepare k s args).1)) : + (encState ((machine k).runFrom w (prepare k s args) j)).length ≤ + space k ((prepare k s args).1) := + length_runFrom_le k ((prepare k s args).1) w j hj + +/-- Selected initialization uses linear value-width storage, apart from its retained metadata. -/ +theorem space_prepare_le (k s N b σ q : ℕ) : + let d := k * 3 + 1 + let B := Encoding.width k s b + space k ((prepare k s (N, b, σ, q)).1) ≤ + 16 * ((encMetadata ((prepare k s (N, b, σ, q)).1)).length + 1) + + 192 * (d + 1) * (2 * B + d.size) := by + let d := k * 3 + 1 + let B := Encoding.width k s b + have hd : 0 < d := by omega + have hB : 0 < B := Encoding.width_pos k s b + have ha := length_encNat_mono (alphabetSize_lt hd hB).le + have hp : 1 ≤ (fieldModulus d B).size := + Nat.size_pos.mpr (fieldModulus_prime d B).pos + rw [length_encNat_eq_size, length_encNat_eq_size] at ha + have hregister := chunkCount_mul_fieldModulus_size_le hd hB + have hcells : chunkCount d B * + ((fieldModulus d B).size + (2 ^ chunkWidth d B).size + 1) ≤ + 12 * (B + chunkWidth d B) := by + calc + _ ≤ chunkCount d B * (3 * (fieldModulus d B).size) := + Nat.mul_le_mul_left _ (by omega) + _ ≤ 12 * (B + chunkWidth d B) := by nlinarith + have hm := chunkWidth_le d B + have hwidth : B + chunkWidth d B ≤ 2 * B + d.size := by omega + have htotal := Nat.mul_le_mul_left (16 * (d + 1)) hcells + have hfinal := Nat.mul_le_mul_left (192 * (d + 1)) hwidth + simp only [space, prepare, parameters, bankCells, length_encNat_eq_size] + dsimp only [d, B] at htotal hfinal + nlinarith + +end Turing.MultiTapePTM.BlockComputation.Initializer diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitializerCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitializerCorrect.lean new file mode 100644 index 0000000000..594c359480 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/InitializerCorrect.lean @@ -0,0 +1,119 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Initializer +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerZero + +/-! +# Exact native initialization of the raw evaluator + +Each physical clock appends one modulus-padded zero cell. The assignment capacity is grown +only during the child-register portion of the bank. The completed state is exactly the +existing raw encoding of the typed evaluator's zero-bank initial configuration. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.Initializer + +open Cslib.TreeEval.Ary Cslib.TreeEval.Ary.Packed + +/-- Number of padded cells in the complete runtime bank. -/ +@[expose] def bankCells (k : ℕ) (metadata : Metadata) : ℕ := + let (_, (_, r, _, _)) := metadata + ((k * 3 + 1) + 1) * r + +/-- The exact bank and capacity accumulated after a specified number of allocation clocks. -/ +@[expose] def prefixState (k : ℕ) (metadata : Metadata) (j : ℕ) : State := + let (_, (_, r, alphabet, p)) := metadata + (metadata, j, List.replicate j p, alphabet ^ min j ((k * 3 + 1) * r)) + +/-- Every prefix uses exactly one physical clock per allocated padded cell. -/ +theorem runFrom_prefix (k : ℕ) (metadata : Metadata) (w : List Bool) (j : ℕ) + (hj : j ≤ bankCells k metadata) : + (machine k).runFrom w (metadata, 0, [], 1) j = prefixState k metadata j := by + rcases metadata with ⟨args, m, r, alphabet, p⟩ + dsimp only [bankCells] at hj + induction j with + | zero => rfl + | succ j ih => + have hjtotal : j < ((k * 3 + 1) + 1) * r := by omega + rw [QueryMachine.runFrom_succ, ih (by omega)] + have hhalt : (machine k).halted (prefixState k (args, m, r, alphabet, p) j) = false := by + change decide (((k * 3 + 1) + 1) * r ≤ j) = false + exact decide_eq_false (by omega) + rw [QueryMachine.step, hhalt] + change step k (prefixState k (args, m, r, alphabet, p) j) = + prefixState k (args, m, r, alphabet, p) (j + 1) + simp only [step, prefixState, hjtotal, ↓reduceIte] + by_cases hchild : j < (k * 3 + 1) * r + · have hjle : j ≤ (k * 3 + 1) * r := hchild.le + have hsuccle : j + 1 ≤ (k * 3 + 1) * r := by omega + simp only [hchild, ↓reduceIte, Nat.min_eq_left hjle, Nat.min_eq_left hsuccle, + pow_succ, List.replicate_succ'] + · have hle : (k * 3 + 1) * r ≤ j := by omega + have hlesucc : (k * 3 + 1) * r ≤ j + 1 := by omega + simp only [hchild, ↓reduceIte, Nat.min_eq_right hle, Nat.min_eq_right hlesucc, + List.replicate_succ'] + +/-- The last allocation clock produces exactly the full bank and assignment capacity. -/ +theorem runFrom_complete (k : ℕ) (args : Args) (m r alphabet p : ℕ) (w : List Bool) : + (machine k).runFrom w ((args, m, r, alphabet, p), 0, [], 1) + (((k * 3 + 1) + 1) * r) = + ((args, m, r, alphabet, p), ((k * 3 + 1) + 1) * r, + List.replicate (((k * 3 + 1) + 1) * r) p, alphabet ^ ((k * 3 + 1) * r)) := by + rw [runFrom_prefix k (args, m, r, alphabet, p) w (((k * 3 + 1) + 1) * r) le_rfl] + have hle : (k * 3 + 1) * r ≤ ((k * 3 + 1) + 1) * r := + Nat.mul_le_mul_right r (Nat.le_succ _) + simp only [prefixState, Nat.min_eq_right hle] + +/-- Completion is a true halt, including the empty-bank edge case. -/ +theorem halted_complete (k : ℕ) (args : Args) (m r alphabet p : ℕ) (w : List Bool) : + (machine k).halted ((machine k).runFrom w ((args, m, r, alphabet, p), 0, [], 1) + (((k * 3 + 1) + 1) * r)) = true := by + rw [runFrom_complete] + change decide (((k * 3 + 1) + 1) * r ≤ ((k * 3 + 1) + 1) * r) = true + exact decide_eq_true le_rfl + +/-- Reading the completed generic allocation gives exactly the typed zero-bank initial call. -/ +theorem result_runFrom_complete {p : ℕ} [Fact p.Prime] + (k N b σ q m r alphabet : ℕ) (w : List Bool) : + result k ((machine k).runFrom w (((N, b, σ, q), m, r, alphabet, p), 0, [], 1) + (((k * 3 + 1) + 1) * r)) = + ((N, b, σ, q, m, alphabet, alphabet ^ ((k * 3 + 1) * r)), + (p, k * 3 + 1, r), RawController.ofCfg + (Iter.initialCfg (0 : Cslib.TreeEval.Ary.State (ZMod p) (k * 3 + 1) r) + 1 (0 : Fin ((k * 3 + 1) + 1)))) := by + rw [runFrom_complete, RawController.ofCfg_initial_zero] + rfl + +/-- Runtime selection and streamed initialization recover the existing evaluator's actual +selected-field initial configuration, with the complete assignment capacity retained. -/ +theorem result_runFrom_prepare (k s N b σ q : ℕ) (w : List Bool) : + let d := k * 3 + 1 + let B := Encoding.width k s b + let m := chunkWidth d B + let r := chunkCount d B + let alphabet := 2 ^ m + let p := fieldModulus d B + result k ((machine k).runFrom w (prepare k s (N, b, σ, q)) ((d + 1) * r)) = + ((N, b, σ, q, m, alphabet, alphabet ^ (d * r)), (p, d, r), + RawController.ofCfg (Iter.initialCfg + (0 : Cslib.TreeEval.Ary.State (ZMod p) d r) 1 (0 : Fin (d + 1)))) := by + have : Fact (fieldModulus (k * 3 + 1) (Encoding.width k s b)).Prime := + ⟨fieldModulus_prime _ _⟩ + exact result_runFrom_complete k N b σ q _ _ _ w + +/-- The selected initializer halts at the exact full-bank allocation clock. -/ +theorem halted_prepare (k s N b σ q : ℕ) (w : List Bool) : + (machine k).halted ((machine k).runFrom w (prepare k s (N, b, σ, q)) + (((k * 3 + 1) + 1) * chunkCount (k * 3 + 1) (Encoding.width k s b))) = true := + halted_complete k (N, b, σ, q) _ _ _ _ w + +end Turing.MultiTapePTM.BlockComputation.Initializer diff --git a/Complexity/Computability/TreeEval/Ary/Packed/RawControllerZero.lean b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerZero.lean new file mode 100644 index 0000000000..fd47631768 --- /dev/null +++ b/Complexity/Computability/TreeEval/Ary/Packed/RawControllerZero.lean @@ -0,0 +1,35 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.TreeEval.Ary.Packed.RawController + +/-! +# Canonical zero-bank initialization + +The raw encoding of the typed evaluator's initial zero registers consists of one +modulus-padded zero per bank cell and the initial call control. +-/ + +public section + +namespace Cslib.TreeEval.Ary.Packed.RawController + +/-- A zero field bank retains one modulus-padded cell for each register coordinate. -/ +theorem ofRegs_zero {p d r : ℕ} : + ofRegs (0 : Ary.State (ZMod p) d r) = List.replicate ((d + 1) * r) p := by + simp only [ofRegs, Pi.zero_apply, ZMod.val_zero, Nat.add_zero, List.ofFn_const] + +/-- The initial typed call has precisely the freshly allocated zero-bank representation. -/ +theorem ofCfg_initial_zero {p d r : ℕ} [Fact p.Prime] : + ofCfg (Iter.initialCfg (0 : Ary.State (ZMod p) d r) 1 (0 : Fin (d + 1))) = + (List.replicate ((d + 1) * r) p, [], some (1, 0)) := by + simp only [ofCfg, Iter.initialCfg, ofRegs_zero, List.map_nil, ofMode, + ZMod.val_one_eq_one_mod, Nat.mod_eq_of_lt (Fact.out : p.Prime).one_lt, Fin.val_zero] + +end Cslib.TreeEval.Ary.Packed.RawController diff --git a/ComplexityTests/Machines/Turing/MultiTape/InitializedEvaluator.lean b/ComplexityTests/Machines/Turing/MultiTape/InitializedEvaluator.lean new file mode 100644 index 0000000000..5356630329 --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/InitializedEvaluator.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of streamed initialized evaluation) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitializedEvaluatorCorrect + +/-! +# Streamed initialization and evaluation regressions + +The assignment capacity stops growing when the child-register portion is complete. Selected +metadata then drives a complete initialized height-zero evaluation without a supplied bank. +-/ + +public section + +namespace ComplexityTests.InitializedEvaluator + +open Turing MultiTapeTM MultiTapePTM.BlockComputation +open Cslib.TreeEval.Ary.Packed + +/-- A small generic initializer with two child cells and four total bank cells. -/ +def seed : Initializer.State := (((1, 1, 0, 0), (1, 2, 2, 17)), 0, [], 1) + +example : (Initializer.machine 0).runFrom [] seed 1 = + (((1, 1, 0, 0), (1, 2, 2, 17)), 1, [17], 2) := by decide + +example : (Initializer.machine 0).runFrom [] seed 3 = + (((1, 1, 0, 0), (1, 2, 2, 17)), 3, [17, 17, 17], 4) := by decide + +example : (Initializer.machine 0).runFrom [] seed 4 = + (((1, 1, 0, 0), (1, 2, 2, 17)), 4, [17, 17, 17, 17], 4) := by decide + +example : (Initializer.machine 0).halted + (((1, 1, 0, 0), (0, 0, 0, 0)), 0, [], 1) = true := by decide + +example : Initializer.step 0 (((0, 0, 0, 0), (0, 1, 0, 0)), 0, [], 1) = + (((0, 0, 0, 0), (0, 1, 0, 0)), 1, [0], 0) := by decide + +/-- The singleton source-machine control encoding. -/ +abbrev code : Fin 1 ≃ Fin 1 := Equiv.refl _ + +/-- A source machine with no work tapes. -/ +def control : MultiTapeTM 0 Bool (Fin 1) := MultiTapeTM.ofTr 0 fun _ _ _ => + { inputTape := .zero, workTapes := fun _ => (none, .zero), output := none, state := none } + +/-- Observe the physical bank only after entering the concrete evaluator caller. -/ +def observe : InitializedEvaluator.State 0 (Fin 1) → Option (List ℕ) := + Sum.elim (fun _ => none) + (Sum.elim (fun (_, _, bank, _, _) => some bank) (fun _ => none)) + +/-- The actual selected prime for six-bit values and fan-in one. -/ +theorem selected_prime : fieldModulus 1 6 = 17 := by + rw [fieldModulus_eq_boundedFirstPrime] + rfl + +/-- Actual metadata selection agrees with the small concrete execution below. -/ +theorem prepared : Initializer.prepare 0 1 (1, 1, 0, 0) = + (((1, 1, 0, 0), (3, 2, 8, 17)), 0, [], 1) := by + simp only [Initializer.prepare, parameters] + change (((1, 1, 0, 0), (3, 2, 8, fieldModulus 1 6)), 0, [], 1) = _ + rw [selected_prime] + +example : observe ((InitializedEvaluator.machine control code).runFrom [true] + (InitializedEvaluator.start 0 1 (1, 1, 0, 0)) 7) = + some ([20] ++ List.replicate 3 17) := by + rw [InitializedEvaluator.start, prepared] + decide + +example : (InitializedEvaluator.machine control code).halted + ((InitializedEvaluator.machine control code).runFrom [true] + (InitializedEvaluator.start 0 1 (1, 1, 0, 0)) 7) = true := by + rw [InitializedEvaluator.start, prepared] + decide + +example : RawController.Output.decode ((6, 3, 17, 2, 0), [20] ++ List.replicate 3 17) = + [true, true] ++ List.replicate 4 false := by decide + +end ComplexityTests.InitializedEvaluator From 5af327bd3e0ebd6782be3a3d020b9b3affac596b Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 05:39:12 +0000 Subject: [PATCH 33/36] feat(hpv): search native block schedules --- Complexity.lean | 6 + .../Encoding/NativeDecode.lean | 13 +- .../BlockComputation/RootDecision.lean | 123 +++++++++ .../BlockComputation/RootDecisionCorrect.lean | 67 +++++ .../BlockComputation/ScheduleCounter.lean | 96 +++++++ .../BlockComputation/ScheduleSearch.lean | 253 +++++++++++++++++ .../ScheduleSearchCorrect.lean | 254 ++++++++++++++++++ .../BlockComputation/ScheduleSearchSpace.lean | 160 +++++++++++ .../Turing/MultiTape/ScheduleSearch.lean | 64 +++++ 9 files changed, 1032 insertions(+), 4 deletions(-) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/RootDecision.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/RootDecisionCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleCounter.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleSearch.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleSearchCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleSearchSpace.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/ScheduleSearch.lean diff --git a/Complexity.lean b/Complexity.lean index 33a91949e7..7897f3c1f6 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1757,6 +1757,9 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierInit public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplaySupplierTotal +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.RootDecision +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.RootDecisionCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleCounter public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleRead public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadPort @@ -1765,6 +1768,9 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadSearchSpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadSpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadWindow +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleSearch +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleSearchCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleSearchSpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputationTree public import Complexity.Computability.Machines.Turing.MultiTape.BlockTrace public import Complexity.Computability.Machines.Turing.MultiTape.Combinators.Append diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean index fca30b6c34..fc13eeeb3e 100644 --- a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Encoding/NativeDecode.lean @@ -109,7 +109,8 @@ private theorem wordDrop_inSpace : ComputableInSpace private theorem wordReverse_inSpace : ComputableInSpace List.reverse encBits encBits (fun w => w.length) := of_inO ComputableInDegree.bitsReverse.inO_one -private theorem wordLength_inSpace : ComputableInSpace List.length encBits encNat +/-- Native length measurement of an actual stored bit word. -/ +theorem wordLength_inSpace : ComputableInSpace List.length encBits encNat (fun w => w.length) := (of_inO ComputableInO.lengthNat).weaken (.of_le_linear 1 1 fun _ => by omega) @@ -143,7 +144,9 @@ theorem readNat_inSpace : ComputableInSpace (fun p => (encPair encBits (encPair encNat encNat) p).length) := budget_comp_linear (of_inO ComputableInO.bitsToNat) slice_inSpace -private theorem bit_inSpace : ComputableInSpace +/-- Read one stored bit natively. Header-only root classification uses this without decoding +windows. -/ +theorem bit_inSpace : ComputableInSpace (fun p : List Bool × ℕ => p.1.getD p.2 false) (encPair encBits encNat) encBool (fun p => (encPair encBits encNat p).length) := by have hhead : ComputableInSpace (fun w : List Bool => w.headD false) encBits encBool @@ -271,7 +274,8 @@ theorem length_payload_cells (e : Q ≃ Fin s) (b : ℕ) (bits : BitString (widt variable [Finite Q] -private theorem state_inSpace (e : Q ≃ Fin s) : ComputableInSpace (state e) encNat +/-- Decode a finite control rank natively, including unused halted ranks for root classification. -/ +theorem state_inSpace (e : Q ≃ Fin s) : ComputableInSpace (state e) encNat (encFinite (Option Q)) (fun n => (encNat n).length) := by classical let tags := List.ofFn fun i : Fin (s + 1) => (stateIndex e).symm i @@ -290,7 +294,8 @@ private theorem state_inSpace (e : Q ≃ Fin s) : ComputableInSpace (state e) en /-- Runtime width and an actual stored Boolean label. -/ abbrev encArgs : (ℕ × List Bool) ↪ List Bool := encPair encNat encBits -private theorem trackOffset_inSpace : ComputableInSpace +/-- Compute the output-summary offset without allocating the skipped head or window fields. -/ +theorem trackOffset_inSpace : ComputableInSpace (fun p : ℕ × List Bool => trackOffset k s p.1) encArgs encNat (fun p => (encArgs p).length) := by have hb := fst encNat encBits diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/RootDecision.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/RootDecision.lean new file mode 100644 index 0000000000..47c976845c --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/RootDecision.lean @@ -0,0 +1,123 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeDecode +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.WidthSpace +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.EncodedTree +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases + +/-! +# Header-only decisions for encoded block-tree roots + +A malformed word, FAIL or a live control state gives no decision. A halted state gives the exact +accept/reject bit of the existing output summary. Only the control rank and output-summary bits +are inspected; no head residues or work-window cells are parsed or stored. Unused control ranks +retain the semantic decoder's convention of representing halted control. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.RootDecision + +open Cslib Cslib.BitString MultiTapeTM MultiTapeTM.ComputableInSpace +open MultiTapeNTM.ChoiceTable + +variable {k s b : ℕ} {Q : Type} + +/-- Actual block width and stored Boolean root word. -/ +abbrev Args := ℕ × List Bool + +/-- Reuse the native decoder's actual binary-width/word encoding. -/ +abbrev encArgs : Args ↪ List Bool := Encoding.NativeDecode.encArgs + +/-- The semantic outcome of a decoded root: only halted views return a decision. -/ +@[expose] def decision : Option (View k Q b) → Option Bool + | none => none + | some ((control, _, track), _) => + if control.isNone then some (decide (track = OutputTrack.accept)) else none + +/-- Classify the stored root using its control header and two output-summary bits only. -/ +@[expose] def classify (k : ℕ) (e : Q ≃ Fin s) : Args → Option Bool := fun (b, word) => + if b = 0 ∨ word.length ≠ Encoding.width k s b then none + else if word.headD false then + if (Encoding.NativeDecode.state e + (Encoding.NativeDecode.readNat word 1 (s + 1).size)).isNone then + let offset := Encoding.NativeDecode.trackOffset k s b + some (word.getD offset false && !word.getD (offset + 1) false) + else none + else none + +/-- Header-only classification agrees with the existing parser's semantic view. -/ +theorem classify_eq_decision_parse (e : Q ≃ Fin s) (b : ℕ) (word : List Bool) : + classify k e (b, word) = decision (k := k) + ((Encoding.NativeDecode.parse (k := k) e (b, word)).map + (QueryReplay.RawResult.toView b)) := by + classical + by_cases hbad : b = 0 ∨ word.length ≠ Encoding.width k s b + · simp [classify, Encoding.NativeDecode.parse, hbad, decision] + · by_cases htag : word.headD false = true + · simp only [classify, Encoding.NativeDecode.parse, hbad, htag, ↓reduceIte, + Option.map_some, Encoding.NativeDecode.payload, QueryReplay.RawResult.toView, decision] + split + · cases hfirst : word.getD (Encoding.NativeDecode.trackOffset k s b) false <;> + cases hsecond : word.getD (Encoding.NativeDecode.trackOffset k s b + 1) false <;> + simp_all + · rfl + · simp only [classify, Encoding.NativeDecode.parse, hbad, htag, Bool.false_eq_true, ↓reduceIte, + Option.map_none, decision] + +/-- Stored width-correct roots have exactly the existing decoder's semantic decision. -/ +theorem classify_ofFn_eq_decision_decode (e : Q ≃ Fin s) (b : ℕ) [NeZero b] + (bits : BitString (Encoding.width k s b)) : + classify k e (b, List.ofFn bits) = decision (Encoding.decode e bits) := by + rw [classify_eq_decision_parse, Encoding.NativeDecode.parse_ofFn] + +variable [Finite Q] + +/-- Root classification is native in space linear in the actual stored word and binary width. -/ +theorem classify_inSpace (e : Q ≃ Fin s) : + ComputableInSpace (classify k e) encArgs (encOption encBool) + (fun a => (encArgs a).length) := by + classical + have hb := fst encNat encBits + have hw := snd encNat encBits + have hwordLength : ComputableInSpace List.length encBits encNat + (fun word => (encBits word).length) := + Encoding.NativeDecode.wordLength_inSpace + have hlength := budget_comp_linear hwordLength hw + have hwidth := budget_comp_linear (Encoding.width_inSpace k s) hb + have hshape := budget_finite (encC := encBool) + (fun pair : Bool × Bool => pair.1 || !pair.2) + (budget_prod (budget_natIsZero hb) (budget_natEq hlength hwidth)) + have htag := budget_comp_linear Encoding.NativeDecode.bit_inSpace + (budget_prod hw (budget_const (encB := encNat) 0 (.refl _))) + have hrank := budget_comp_linear Encoding.NativeDecode.readNat_inSpace + (budget_prod hw (budget_prod (budget_const (encB := encNat) 1 (.refl _)) + (budget_const (encB := encNat) (s + 1).size (.refl _)))) + have hcontrol := budget_comp_linear (Encoding.NativeDecode.state_inSpace e) hrank + have hhalt := budget_finite (encC := encBool) (Option.isNone : Option Q → Bool) hcontrol + have hoffset := Encoding.NativeDecode.trackOffset_inSpace (k := k) (s := s) + have hfirst := budget_comp_linear Encoding.NativeDecode.bit_inSpace (budget_prod hw hoffset) + have hsecond := budget_comp_linear Encoding.NativeDecode.bit_inSpace + (budget_prod hw (budget_natSucc hoffset)) + have haccept := budget_finite (encC := encBool) + (fun pair : Bool × Bool => pair.1 && !pair.2) (budget_prod hfirst hsecond) + have hsome := budget_comp_linear (encC := encOption encBool) + (of_inO (ComputableInDegree.optionSome (enc₁ := encBool)).inO_one) haccept + have hnone := budget_const (encIn := encArgs) (encB := encOption encBool) none (.refl _) + refine (budget_ite hshape hnone + (budget_ite htag (budget_ite hhalt hsome hnone) hnone)).of_enc_eq ?_ + rintro ⟨b, word⟩ + rw [show word.getD 0 false = word.headD false from by + cases word <;> rfl] + by_cases hzero : b = 0 <;> + by_cases hlength : word.length = Encoding.width k s b <;> + simp [classify, hzero, hlength] + +end Turing.MultiTapePTM.BlockComputation.RootDecision diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/RootDecisionCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/RootDecisionCorrect.lean new file mode 100644 index 0000000000..42188f714e --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/RootDecisionCorrect.lean @@ -0,0 +1,67 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.RootDecision +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.EncodedTree +public import Complexity.Computability.Machines.Turing.MultiTape.Probabilistic.ExplicitBlockNetwork.Graph + +/-! +# Root classification agrees with native halting and output + +Wrong schedules give no decision. The actual movement schedule gives a decision precisely when +the source machine has halted by the block boundary; both acceptance and rejection are retained. +The native classifier is connected to the existing functional computation, not to a new model. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.RootDecision + +open MultiTapeNTM.ChoiceTable + +/-- Native halting at a clock yields its Boolean acceptance decision; live states yield none. -/ +@[expose] noncomputable def boundaryDecision {k : ℕ} {Q : Type} + (M : MultiTapeTM k Bool Q) (w : List Bool) (clock : ℕ) : Option Bool := + let cfg := M.runFrom (Cfg.init M.q₀ w) clock + if cfg.state.isNone then some (decide (cfg.output = [true])) else none + +private theorem decision_trueView {k q : ℕ} {Q : Type} + (M : MultiTapeTM k Bool Q) (w : List Bool) (b : ℕ) + (σ : Schedule k q) : + decision (some (trueView (MultiTapePTM.ofDeterministic M) w b σ q)) = + boundaryDecision M w (q * b) := by + unfold boundaryDecision + rw [← boundary_ofDeterministic (input := w) (b := b) M le_rfl] + cases q with + | zero => simp [decision, trueView, initialView, boundary, intervalStart_zero, Cfg.init] + | succ q => + simp only [decision, trueView, Cfg.blockControl, OutputTrack.ofWord_eq_accept_iff] + +/-- A correctly classified root is the native boundary decision exactly for the actual schedule. -/ +theorem decision_eval_eq_boundary {k q s : ℕ} {Q : Type} + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (σ : ℕ) : + decision (Encoding.decode e (encodedTree (MultiTapePTM.ofDeterministic M) w b + (scheduleOfNat k q σ) e).eval) = + if actualSchedule (MultiTapePTM.ofDeterministic M) w b (q := q) = scheduleOfNat k q σ + then boundaryDecision M w (q * b) else none := by + classical + rw [decode_eval_encodedTree] + cases hv : value (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) q with + | none => + have hwrong := (value_final_eq_none_iff (MultiTapePTM.ofDeterministic M) w b + (scheduleOfNat k q σ)).mp hv + simp only [decision, hwrong, ↓reduceIte] + | some v => + obtain ⟨hactual, rfl⟩ := (value_final_eq_some_iff (MultiTapePTM.ofDeterministic M) w b + (scheduleOfNat k q σ) v).mp hv + rw [ite_eq_left hactual] + exact decision_trueView M w b (scheduleOfNat k q σ) + +end Turing.MultiTapePTM.BlockComputation.RootDecision diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleCounter.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleCounter.lean new file mode 100644 index 0000000000..d9172cf706 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleCounter.lean @@ -0,0 +1,96 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadSpace +import Complexity.Computability.Machines.Turing.MultiTape.Internal.BinarySizeFacts + +/-! +# A native bound guard for sequential schedule search + +Repeatedly discarding one fixed-radix schedule row tests whether a binary counter names a +schedule at the supplied horizon. The runtime guard does not construct the exponential upper +bound. Its semantic bound is exact, and every existing typed schedule has a valid counter. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ScheduleCounter + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +/-- The unread suffix after consuming exactly the supplied number of schedule rows. -/ +@[expose] def remaining (k : ℕ) (a : ℕ × ℕ) : ℕ := + ((ScheduleRead.step k 0 0)^[a.2] (a.1, 0)).1 + +/-- A schedule number is in range exactly when no unread row remains. -/ +@[expose] def valid (k : ℕ) (a : ℕ × ℕ) : Bool := decide (remaining k a = 0) + +/-- Both the schedule counter and horizon are explicitly stored binary naturals. -/ +abbrev encArgs : (ℕ × ℕ) ↪ List Bool := encPair encNat encNat + +/-- The streamed suffix has the exact division semantics, without computing this power. -/ +theorem remaining_eq_div (k σ q : ℕ) : + remaining k (σ, q) = σ / 3 ^ ((k + 1) * q) := + ScheduleRead.iterate_fst k 0 0 σ q + +/-- The native guard accepts precisely the complete finite numbering of existing schedules. -/ +theorem valid_eq_true_iff (k σ q : ℕ) : + valid k (σ, q) = true ↔ σ < 3 ^ ((k + 1) * q) := by + simp only [valid, decide_eq_true_eq, remaining_eq_div] + exact Nat.div_eq_zero_iff_lt (by positivity) + +/-- The first excluded counter is rejected, so sequential search need not store its bound. -/ +theorem valid_bound (k q : ℕ) : valid k (3 ^ ((k + 1) * q), q) = false := by + apply Bool.eq_false_iff.mpr + intro h + exact Nat.lt_irrefl _ ((valid_eq_true_iff k _ q).mp h) + +/-- Advancing an accepted counter stays at most the first excluded counter. -/ +theorem succ_le_bound {k σ q : ℕ} (hvalid : valid k (σ, q) = true) : + σ + 1 ≤ 3 ^ ((k + 1) * q) := + Nat.succ_le_of_lt ((valid_eq_true_iff k σ q).mp hvalid) + +/-- At horizon zero the unique valid number is zero. -/ +theorem valid_zero_horizon (k σ : ℕ) : valid k (σ, 0) = true ↔ σ = 0 := by + rw [valid_eq_true_iff] + simp + +/-- Every typed movement schedule is represented by an accepted numeric counter. -/ +theorem exists_valid_schedule {k q : ℕ} (schedule : Schedule k q) : + ∃ σ, valid k (σ, q) = true ∧ scheduleOfNat k q σ = schedule := by + let number := scheduleEquiv k q schedule + refine ⟨number.val, (valid_eq_true_iff k number.val q).mpr number.isLt, ?_⟩ + rw [scheduleOfNat_eq_symm number.isLt] + exact (scheduleEquiv k q).symm_apply_apply schedule + +/-- Discarding runtime-many fixed-radix rows uses space linear in the actual input storage. -/ +theorem remaining_inSpace (k : ℕ) : + ComputableInSpace (remaining k) encArgs encNat (fun a => (encArgs a).length) := by + have hinit := budget_prod (fst encNat encNat) + (budget_const (encB := encNat) 0 (.refl _)) + have hstates (a : ℕ × ℕ) (j : ℕ) (hj : j ≤ a.2) : + (ScheduleRead.encState ((ScheduleRead.step k 0 0)^[j] (a.1, 0))).length ≤ + (ScheduleRead.encState a).length := by + have hs := ComputableTidilyInO.length_encNat_mono + (ScheduleRead.iterate_fst_le k 0 0 a.1 j) + have hn := ComputableTidilyInO.length_encNat_mono + ((ScheduleRead.iterate_snd_le k 0 0 a.1 j).trans hj) + simp only [ScheduleRead.encState, length_encPair] + omega + have hrun := budget_iterate (snd encNat encNat) hinit + (ScheduleRead.step_inSpace k 0 0) hstates (.refl _) + exact (fst' hrun).of_enc_eq fun _ => rfl + +/-- The complete range guard is native and stores neither the exponential bound nor a list. -/ +theorem valid_inSpace (k : ℕ) : + ComputableInSpace (valid k) encArgs encBool (fun a => (encArgs a).length) := + (budget_natEq (remaining_inSpace k) + (budget_const (encB := encNat) 0 (.refl _))).of_enc_eq fun _ => rfl + +end Turing.MultiTapePTM.BlockComputation.ScheduleCounter diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleSearch.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleSearch.lean new file mode 100644 index 0000000000..67597c6d3e --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleSearch.lean @@ -0,0 +1,253 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitializedEvaluator +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleCounter +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.RootDecision +public import Complexity.Computability.TreeEval.Ary.Packed.RawControllerOutput +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.BitList +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases + +/-! +# Sequential native search over movement schedules + +The caller retains a binary schedule counter and one optional decision. Each candidate invokes +the initialized concrete evaluator on the same original input. A valid halted root ends the +search; FAIL or live roots advance the counter. The streamed range guard constructs neither a +list of schedules nor the exponential range bound. Runtime arguments enter through `initial`. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ScheduleSearch + +open MultiTapeTM MultiTapeTM.ComputableInSpace Cslib.TreeEval.Ary.Packed + +variable {k s : ℕ} {Q : Type} + +/-- Runtime input length, block width and schedule horizon. -/ +abbrev Args := ℕ × ℕ × ℕ +/-- Actual arguments, schedule cursor and optional halted-root decision. -/ +abbrev CallerState := Args × ℕ × Option Bool + +/-- Original input length retained by a search. -/ +@[expose] def inputLength : Args → ℕ := fun (N, _, _) => N +/-- Block width retained by a search. -/ +@[expose] def blockWidth : Args → ℕ := fun (_, b, _) => b +/-- Number of block intervals retained by a search. -/ +@[expose] def horizon : Args → ℕ := fun (_, _, q) => q +/-- Numeric schedule currently retained by a caller. -/ +@[expose] def cursor : CallerState → ℕ := fun (_, σ, _) => σ + +/-- Active search or one suspended search with its initialized evaluator. -/ +abbrev State (k : ℕ) (Q : Type) := + CallerState ⊕ (CallerState × InitializedEvaluator.State k Q) + +/-- The caller continues only before finding a decision and while its schedule is in range. -/ +@[expose] def pending (k : ℕ) : CallerState → Bool := + fun ((_, _, q), σ, result) => !result.isSome && ScheduleCounter.valid k (σ, q) + +/-- Start the first numeric schedule with no decision. -/ +@[expose] def initial (args : Args) : State k Q := .inl (args, 0, none) + +/-- Request one concrete initialized evaluator run. -/ +@[expose] def request (k s : ℕ) : CallerState → Option (InitializedEvaluator.State k Q) := + fun ((N, b, q), σ, result) => + if pending k ((N, b, q), σ, result) then + some (InitializedEvaluator.start k s (N, b, σ, q)) else none + +/-- Decode only returned active evaluator banks; incomplete or suspended states give no decision. -/ +@[expose] def outcome (e : Q ≃ Fin s) : Args × InitializedEvaluator.State k Q → Option Bool + | ((_, b, _), .inr (.inl ((_, _, _, _, m, _, _), (p, _, r), bank, _, _))) => + RootDecision.classify k e (b, RawController.Output.decode + ((Encoding.width k s b, m, p, r, 0), bank)) + | _ => none + +/-- Preserve a halted-root decision or advance to the next schedule after a failed candidate. -/ +@[expose] def resume (e : Q ≃ Fin s) : + CallerState → InitializedEvaluator.State k Q → CallerState := fun (args, σ, _) returned => + match outcome e (args, returned) with + | some result => (args, σ, some result) + | none => (args, σ + 1, none) + +/-- The fixed caller template is already halted. -/ +@[expose] def caller (k : ℕ) : QueryMachine CallerState where + init := ((0, 0, 0), 0, some false) + query _ := 0 + next := Prod.fst + emit _ := [] + halted := fun state => !pending k state + finish _ := [] + +/-- One fixed search machine calls the concrete evaluator on the original read-only input. -/ +@[expose] noncomputable def machine (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + QueryMachine (State k Q) := + (caller k).withCalls (InitializedEvaluator.machine M e) (request k s) (resume e) + +/-- Only a returned caller state exposes a decision. -/ +@[expose] def answer : State k Q → Option Bool := + Sum.elim (fun (_, _, result) => result) (fun _ => none) + +/-- Binary input length, block width and horizon. -/ +@[expose] noncomputable def encArgs : Args ↪ List Bool := + encPair encNat (encPair encNat encNat) +/-- Binary schedule counter and decision beside the actual arguments. -/ +@[expose] noncomputable def encCaller : CallerState ↪ List Bool := + encPair encArgs (encPair encNat (encOption encBool)) + +variable [Finite Q] + +/-- Actual search payloads and suspended evaluator states are charged explicitly. -/ +@[expose] noncomputable def encState : State k Q ↪ List Bool := + encSum encCaller (encPair encCaller (InitializedEvaluator.encState (k := k) (Q := Q))) + +/-- Native initialization stores only arguments, a zero counter and no decision. -/ +theorem initial_inSpace : ComputableInSpace (initial (k := k) (Q := Q)) encArgs encState + (fun a => (encArgs a).length) := by + have hid : ComputableInSpace (fun a : Args => a) encArgs encArgs + (fun a => (encArgs a).length) := of_inO (ComputableInO.of_eq (f := id) fun _ => rfl) + have hc := budget_prod hid (budget_prod (budget_const (encB := encNat) 0 (.refl _)) + (budget_const (encB := encOption encBool) none (.refl _))) + exact (budget_comp_linear (of_inO (ComputableInO.inl encCaller + (encPair encCaller (InitializedEvaluator.encState (k := k) (Q := Q))))) hc).of_enc_eq + fun _ => rfl + +omit [Finite Q] in +/-- The native guard tests only actual counters and the stored decision. -/ +theorem pending_inSpace (k : ℕ) : ComputableInSpace (pending k) encCaller encBool + (fun a => (encCaller a).length) := by + have ha := fst encArgs (encPair encNat (encOption encBool)) + have hr := snd encArgs (encPair encNat (encOption encBool)) + have hvalid := budget_comp_linear (ScheduleCounter.valid_inSpace k) + (budget_prod hr.fst' ha.snd'.snd') + have hsome := budget_finite (encC := encBool) (Option.isSome : Option Bool → Bool) hr.snd' + exact (budget_finite (encC := encBool) (fun q : Bool × Bool => !q.1 && q.2) + (budget_prod hsome hvalid)).of_enc_eq fun _ => rfl + +/-- The request initializer charges the complete actual runtime arguments. -/ +theorem request_inSpace : ComputableInSpace (request (Q := Q) k s) encCaller + (encOption InitializedEvaluator.encState) (fun a => (encCaller a).length) := by + have ha := fst encArgs (encPair encNat (encOption encBool)) + have hr := snd encArgs (encPair encNat (encOption encBool)) + have hargs := budget_prod ha.fst' + (budget_prod ha.snd'.fst' (budget_prod hr.fst' ha.snd'.snd')) + have hstart := budget_comp_linear (InitializedEvaluator.start_inSpace (k := k) (s := s) + (Q := Q)) hargs + have hsome := budget_comp_linear + (encC := encOption (InitializedEvaluator.encState (k := k) (Q := Q))) + (of_inO (ComputableInDegree.optionSome + (enc₁ := InitializedEvaluator.encState (k := k) (Q := Q))).inO_one) hstart + exact (budget_ite (pending_inSpace k) hsome + (budget_const (encB := encOption InitializedEvaluator.encState) none (.refl _))).of_enc_eq + fun _ => rfl + +/-- Returned-bank extraction and header-only root classification are native. -/ +theorem outcome_inSpace (e : Q ≃ Fin s) : ComputableInSpace (outcome (k := k) e) + (encPair encArgs InitializedEvaluator.encState) (encOption encBool) + (fun a => (encPair encArgs InitializedEvaluator.encState a).length) := by + let encC := RawController.Query.encContext ConcreteSupplier.encContext + have ha := fst encArgs encC + have hc := snd encArgs encC + have hb := ha.snd'.fst' + have hcontext := hc.fst' + have hfull := hc.snd' + have hpar := hfull.fst' + have hraw := hfull.snd' + have hm := hcontext.snd'.snd'.snd'.snd'.fst' + have hp := hpar.fst' + have hr := hpar.snd'.snd' + have hbank := hraw.fst' + have hwidth := budget_comp_linear (Encoding.width_inSpace k s) hb + have hdecodeArgs := budget_prod (budget_prod hwidth (budget_prod hm (budget_prod hp + (budget_prod hr (budget_const (encB := encNat) 0 (.refl _)))))) hbank + have hword := budget_comp_linear listToBits + (budget_comp_linear RawController.Output.decode_inSpace hdecodeArgs) + have hroot := budget_comp_linear (RootDecision.classify_inSpace (k := k) e) + (budget_prod hb hword) + have hsuspended := budget_const + (encIn := encPair encArgs (encPair encC (QueryReplay.GateReply.encState (k := k) (Q := Q)))) + (encB := encOption encBool) none (.refl _) + have hinitial := budget_const (encIn := encPair encArgs Initializer.encState) + (encB := encOption encBool) none (.refl _) + exact (sumElimSnd hinitial (sumElimSnd hroot hsuspended)).of_enc_eq fun a => by + rcases a with ⟨args, initial | (active | suspended)⟩ <;> rfl + +/-- Native resumption retains arguments and either a decision or the incremented counter. -/ +theorem resume_inSpace (e : Q ≃ Fin s) : ComputableInSpace + (fun a : CallerState × InitializedEvaluator.State k Q => resume e a.1 a.2) + (encPair encCaller InitializedEvaluator.encState) encCaller + (fun a => (encPair encCaller InitializedEvaluator.encState a).length) := by + have hc := fst encCaller (InitializedEvaluator.encState (k := k) (Q := Q)) + have ht := snd encCaller (InitializedEvaluator.encState (k := k) (Q := Q)) + have ha := hc.fst' + have hσ := hc.snd'.fst' + have hout := budget_comp_linear (outcome_inSpace e) (budget_prod ha ht) + have hisSome := budget_finite (encC := encBool) (Option.isSome : Option Bool → Bool) hout + have hσ' := budget_ite hisSome hσ (budget_natSucc hσ) + refine (budget_prod ha (budget_prod hσ' hout)).of_enc_eq ?_ + rintro ⟨⟨args, σ, result⟩, returned⟩ + cases ho : outcome e (args, returned) <;> simp [resume, ho, encCaller, encPair_apply] + +/-- Extract the stored caller decision without constructing an output word. -/ +theorem answer_inSpace : ComputableInSpace (answer (k := k) (Q := Q)) encState + (encOption encBool) (fun a => (encState a).length) := + sumElim (snd encArgs (encPair encNat (encOption encBool))).snd' + (budget_const (encIn := encPair encCaller InitializedEvaluator.encState) none (.refl _)) + +private theorem caller_query_inSpace (k : ℕ) : ComputableInSpace (caller k).query encCaller + encNat (fun a => (encCaller a).length) := budget_const 0 (.refl _) +private theorem caller_next_inSpace (k : ℕ) : ComputableInSpace (caller k).next + (encPair encCaller (encOption encBool)) encCaller + (fun a => (encPair encCaller (encOption encBool) a).length) := fst _ _ +private theorem caller_emit_inSpace (k : ℕ) : ComputableInSpace (caller k).emit + (encPair encCaller (encOption encBool)) encBits + (fun a => (encPair encCaller (encOption encBool) a).length) := budget_const [] (.refl _) +private theorem caller_halted_inSpace (k : ℕ) : ComputableInSpace (caller k).halted encCaller + encBool (fun a => (encCaller a).length) := + budget_finite Bool.not (pending_inSpace k) +private theorem caller_finish_inSpace (k : ℕ) : ComputableInSpace (caller k).finish encCaller + encBits (fun a => (encCaller a).length) := budget_const [] (.refl _) + +/-- Native query component of the concrete sequential search. -/ +theorem machine_query_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).query encState encNat (fun a => (encState a).length) := + QueryMachine.withCalls_query_inSpace (caller k) (InitializedEvaluator.machine M e) + (request k s) (resume e) encCaller InitializedEvaluator.encState (caller_query_inSpace k) + (InitializedEvaluator.machine_query_inSpace M e) request_inSpace + +/-- Native transitions cover actual candidate execution and classified resumption. -/ +theorem machine_next_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).next (encPair encState (encOption encBool)) encState + (fun a => (encPair encState (encOption encBool) a).length) := + QueryMachine.withCalls_next_inSpace (caller k) (InitializedEvaluator.machine M e) + (request k s) (resume e) encCaller InitializedEvaluator.encState (caller_next_inSpace k) + (InitializedEvaluator.machine_next_inSpace M e) + (InitializedEvaluator.machine_halted_inSpace M e) request_inSpace (resume_inSpace e) + +/-- Candidate output streams are discarded. -/ +theorem machine_emit_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).emit (encPair encState (encOption encBool)) encBits + (fun a => (encPair encState (encOption encBool) a).length) := + QueryMachine.withCalls_emit_inSpace (caller k) (InitializedEvaluator.machine M e) + (request k s) (resume e) encCaller InitializedEvaluator.encState + (caller_emit_inSpace k) request_inSpace + +/-- Search completion uses only the native stored-decision/range guard. -/ +theorem machine_halted_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).halted encState encBool (fun a => (encState a).length) := + QueryMachine.withCalls_halted_inSpace (caller k) (InitializedEvaluator.machine M e) + (request k s) (resume e) encCaller InitializedEvaluator.encState (caller_halted_inSpace k) + +/-- The final decision remains stored in the returned caller state. -/ +theorem machine_finish_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).finish encState encBits (fun a => (encState a).length) := + QueryMachine.withCalls_finish_inSpace (caller k) (InitializedEvaluator.machine M e) + (request k s) (resume e) encCaller InitializedEvaluator.encState (caller_finish_inSpace k) + +end Turing.MultiTapePTM.BlockComputation.ScheduleSearch diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleSearchCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleSearchCorrect.lean new file mode 100644 index 0000000000..5da706f01b --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleSearchCorrect.lean @@ -0,0 +1,254 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleSearch +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleSearchSpace +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitializedEvaluatorCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.RootDecisionCorrect +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.FirstHalt + +/-! +# Exact finite schedule search on the original input + +Each counter invokes the complete initialized evaluator. Wrong schedules have no outcome; +the actual schedule gives precisely the simulated boundary's halted decision. Exhausting the +native range guard therefore returns no decision exactly when that boundary is still live. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ScheduleSearch + +open Cslib.TreeEval.Ary Cslib.TreeEval.Ary.Packed + +variable {k s : ℕ} {Q : Type} + +/-- One actual initialized evaluation returns the classification of the existing tree root at +its first halt, retaining its physical-clock storage bound. -/ +theorem exists_first_outcome [Finite Q] (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (b : ℕ) [NeZero b] (σ q : ℕ) : + ∃ T, + (∀ t < T, (InitializedEvaluator.machine M e).halted + ((InitializedEvaluator.machine M e).runFrom w + (InitializedEvaluator.start k s (w.length, b, σ, q)) t) = false) ∧ + (InitializedEvaluator.machine M e).halted ((InitializedEvaluator.machine M e).runFrom w + (InitializedEvaluator.start k s (w.length, b, σ, q)) T) = true ∧ + outcome e ((w.length, b, q), (InitializedEvaluator.machine M e).runFrom w + (InitializedEvaluator.start k s (w.length, b, σ, q)) T) = + RootDecision.classify k e (b, List.ofFn + (encodedTree (MultiTapePTM.ofDeterministic M) w b + (scheduleOfNat k q σ) e).eval) ∧ + ∀ t ≤ T, (InitializedEvaluator.encState ((InitializedEvaluator.machine M e).runFrom w + (InitializedEvaluator.start k s (w.length, b, σ, q)) t)).length ≤ + InitializedEvaluator.allowance k s Q w.length b σ q := by + obtain ⟨U, bank, hrun, hdecode, hhalt, hsize⟩ := + InitializedEvaluator.exists_runFrom_decode_root M e w b σ q + obtain ⟨T, hle, hT, hpre, hstate⟩ := QueryMachine.exists_first_halt_le + (InitializedEvaluator.machine M e) w _ U hhalt + refine ⟨T, hpre, hT, ?_, ?_⟩ + · rw [hstate, hrun] + change RootDecision.classify k e (b, _) = _ + rw [hdecode] + · intro t ht + exact hsize t (ht.trans hle) + +/-- Entry and return add exactly two clocks to the first-halt evaluator run. -/ +theorem runFrom_one (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (args : Args) + (w : List Bool) (σ T : ℕ) (hvalid : ScheduleCounter.valid k (σ, (horizon args)) = true) + (hpre : ∀ t < T, (InitializedEvaluator.machine M e).halted + ((InitializedEvaluator.machine M e).runFrom w + (InitializedEvaluator.start k s (inputLength args, blockWidth args, σ, horizon args)) + t) = false) + (hhalt : (InitializedEvaluator.machine M e).halted + ((InitializedEvaluator.machine M e).runFrom w + (InitializedEvaluator.start k s (inputLength args, blockWidth args, σ, horizon args)) + T) = true) : + (machine M e).runFrom w (.inl (args, σ, none)) (T + 2) = + .inl (resume e (args, σ, none) ((InitializedEvaluator.machine M e).runFrom w + (InitializedEvaluator.start k s (inputLength args, blockWidth args, σ, horizon args)) + T)) := by + apply QueryMachine.withCalls_runFrom_call + · change (!pending k (args, σ, none)) = false + rcases args with ⟨N, b, q⟩ + dsimp only [horizon] at hvalid + simp only [pending, Option.isSome_none, Bool.not_false, Bool.true_and, hvalid, Bool.not_true] + · rcases args with ⟨N, b, q⟩ + dsimp only [horizon] at hvalid + simp only [inputLength, blockWidth, horizon, request, pending, Option.isSome_none, + Bool.not_false, Bool.true_and, + hvalid, ↓reduceIte] + · exact hpre + · exact hhalt + +/-- Finite search is complete whenever every candidate either has no outcome or the specified +outcome, and a nonempty specified outcome occurs among the remaining candidates. -/ +theorem exists_runFrom_candidates [Finite Q] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (args : Args) (w : List Bool) (values : ℕ → Option Bool) (result : Option Bool) (B : ℕ) + (hcaller : ∀ σ ≤ 3 ^ ((k + 1) * (horizon args)), ∀ result : Option Bool, + (encState (.inl (args, σ, result) : State k Q)).length ≤ B) + (hstep : ∀ σ < 3 ^ ((k + 1) * (horizon args)), ∃ T, + (machine M e).runFrom w (.inl (args, σ, none)) T = + .inl (match values σ with + | none => (args, σ + 1, none) + | some answer => (args, σ, some answer)) ∧ + ∀ t ≤ T, (encState ((machine M e).runFrom w (.inl (args, σ, none)) t)).length ≤ B) + (hsafe : ∀ σ < 3 ^ ((k + 1) * (horizon args)), values σ = none ∨ values σ = result) + (hcomplete : result ≠ none → ∃ σ < 3 ^ ((k + 1) * (horizon args)), values σ = result) : + ∃ T σ, (machine M e).runFrom w (initial args) T = .inl (args, σ, result) ∧ + (machine M e).halted (.inl (args, σ, result)) = true ∧ + ∀ t ≤ T, (encState ((machine M e).runFrom w (initial args) t)).length ≤ B := by + let bound := 3 ^ ((k + 1) * (horizon args)) + have haux (n : ℕ) : ∀ σ, σ + n = bound → + (result ≠ none → ∃ j, σ ≤ j ∧ j < bound ∧ values j = result) → + ∃ T cursor, (machine M e).runFrom w (.inl (args, σ, none)) T = + .inl (args, cursor, result) ∧ + (machine M e).halted (.inl (args, cursor, result)) = true ∧ + ∀ t ≤ T, (encState ((machine M e).runFrom w (.inl (args, σ, none)) t)).length ≤ B := by + induction n with + | zero => + intro σ hσ hex + have hnone : result = none := by + by_contra h + obtain ⟨j, hlo, hhi, _⟩ := hex h + omega + subst result + have heq : σ = bound := by omega + refine ⟨0, σ, rfl, ?_, ?_⟩ + · subst σ + rcases args with ⟨N, b, q⟩ + change !ScheduleCounter.valid k (3 ^ ((k + 1) * q), q) = true + rw [ScheduleCounter.valid_bound] + rfl + · intro t ht + have htzero : t = 0 := by omega + subst t + exact hcaller σ (by omega) none + | succ n ih => + intro σ hσ hex + have hlt : σ < bound := by omega + obtain ⟨T, hT, hTspace⟩ := hstep σ hlt + cases hv : values σ with + | none => + have hfuture : result ≠ none → + ∃ j, σ + 1 ≤ j ∧ j < bound ∧ values j = result := by + intro hresult + obtain ⟨j, hlo, hhi, hj⟩ := hex hresult + have hne : j ≠ σ := by intro heq; subst j; rw [hv] at hj; exact hresult hj.symm + exact ⟨j, by omega, hhi, hj⟩ + obtain ⟨U, cursor, hU, hhalt, hUspace⟩ := ih (σ + 1) (by omega) hfuture + refine ⟨T + U, cursor, ?_, hhalt, ?_⟩ + · rw [QueryMachine.runFrom_add, hT, hv, hU] + · intro t ht + by_cases hbefore : t ≤ T + · exact hTspace t hbefore + · have hafter : T ≤ t := by omega + obtain ⟨u, rfl⟩ := Nat.exists_eq_add_of_le hafter + rw [QueryMachine.runFrom_add, hT, hv] + exact hUspace u (by omega) + | some answer => + have hresult : some answer = result := by + rcases hsafe σ hlt with hnone | hresult + · rw [hv] at hnone + cases hnone + · rw [hv] at hresult + exact hresult + refine ⟨T, σ, ?_, ?_, hTspace⟩ + · rw [hv] at hT + rw [← hresult] + exact hT + · rw [← hresult] + rcases args with ⟨N, b, q⟩ + change !pending k ((N, b, q), σ, some answer) = true + rfl + obtain ⟨T, σ, hrun, hhalt, hspace⟩ := haux bound 0 (by omega) (by + intro hresult + obtain ⟨σ, hσ, hv⟩ := hcomplete hresult + exact ⟨σ, Nat.zero_le _, hσ, hv⟩) + exact ⟨T, σ, hrun, hhalt, hspace⟩ + +/-- Sequential native schedule search returns exactly the original machine's bounded halted +decision. Every candidate is evaluated on the original input; no lookup or supplier oracle is +assumed, and a live boundary exhausts the schedule range with no decision. -/ +theorem exists_runFrom_boundaryDecision [Finite Q] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (b : ℕ) [NeZero b] (q : ℕ) : + ∃ T, (machine M e).halted + ((machine M e).runFrom w (initial (w.length, b, q)) T) = true ∧ + answer ((machine M e).runFrom w (initial (w.length, b, q)) T) = + RootDecision.boundaryDecision M w (q * b) ∧ + ∀ t ≤ T, (encState ((machine M e).runFrom w (initial (w.length, b, q)) t)).length ≤ + space k s Q w.length b q := by + classical + let values : ℕ → Option Bool := fun σ => RootDecision.classify k e + (b, List.ofFn (encodedTree (MultiTapePTM.ofDeterministic M) w b + (scheduleOfNat k q σ) e).eval) + let result := RootDecision.boundaryDecision M w (q * b) + have hsemantic (σ : ℕ) : values σ = + if actualSchedule (MultiTapePTM.ofDeterministic M) w b (q := q) = scheduleOfNat k q σ + then result else none := by + change RootDecision.classify k e (b, List.ofFn + (encodedTree (MultiTapePTM.ofDeterministic M) w b (scheduleOfNat k q σ) e).eval) = _ + rw [RootDecision.classify_ofFn_eq_decision_decode] + exact RootDecision.decision_eval_eq_boundary M e w b σ + have hstep : ∀ σ < 3 ^ ((k + 1) * q), ∃ T, + (machine M e).runFrom w (.inl ((w.length, b, q), σ, none)) T = + .inl (match values σ with + | none => ((w.length, b, q), σ + 1, none) + | some answer => ((w.length, b, q), σ, some answer)) ∧ + ∀ t ≤ T, (encState ((machine M e).runFrom w + (.inl ((w.length, b, q), σ, none)) t)).length ≤ space k s Q w.length b q := by + intro σ hσ + obtain ⟨T, hpre, hhalt, houtcome, hsize⟩ := exists_first_outcome M e w b σ q + have hvalid : ScheduleCounter.valid k (σ, q) = true := + (ScheduleCounter.valid_eq_true_iff k σ q).mpr hσ + have hrun := runFrom_one M e (w.length, b, q) w σ T hvalid hpre hhalt + have houtcome' : outcome e ((w.length, b, q), (InitializedEvaluator.machine M e).runFrom w + (InitializedEvaluator.start k s (w.length, b, σ, q)) T) = values σ := houtcome + refine ⟨T + 2, ?_, ?_⟩ + · cases hv : values σ <;> + simpa only [resume, inputLength, blockWidth, horizon, houtcome', hv] using hrun + · have hpending : pending k ((w.length, b, q), σ, none) = true := by + simpa only [pending, Option.isSome_none, Bool.not_false, Bool.true_and] using hvalid + have hsize' : ∀ u ≤ T, (InitializedEvaluator.encState + ((InitializedEvaluator.machine M e).runFrom w + (InitializedEvaluator.start k s (w.length, b, σ, q)) u)).length ≤ + candidateSpace k s Q w.length b q := by + intro u hu + exact (hsize u hu).trans (candidate_allowance_le k s Q w.length b q σ hσ.le) + exact runFrom_one_encoding_le M e w.length b q σ none w T + hpending hpre hhalt hsize' + have hsafe : ∀ σ < 3 ^ ((k + 1) * q), values σ = none ∨ values σ = result := by + intro σ _ + rw [hsemantic] + split <;> simp + have hcomplete : result ≠ none → ∃ σ < 3 ^ ((k + 1) * q), values σ = result := by + intro _ + obtain ⟨σ, hvalid, hschedule⟩ := ScheduleCounter.exists_valid_schedule + (actualSchedule (MultiTapePTM.ofDeterministic M) w b (q := q)) + refine ⟨σ, (ScheduleCounter.valid_eq_true_iff k σ q).mp hvalid, ?_⟩ + rw [hsemantic] + simp only [hschedule, ↓reduceIte] + have hcaller : ∀ σ ≤ 3 ^ ((k + 1) * q), ∀ result : Option Bool, + (encState (.inl ((w.length, b, q), σ, result) : State k Q)).length ≤ + space k s Q w.length b q := by + intro σ hσ result + have hc := caller_encoding_le k w.length b q σ result hσ + change (encCaller ((w.length, b, q), σ, result)).length + 1 ≤ _ + dsimp only [space] + omega + obtain ⟨T, σ, hrun, hhalt, hspace⟩ := exists_runFrom_candidates M e (w.length, b, q) w + values result (space k s Q w.length b q) hcaller hstep hsafe hcomplete + refine ⟨T, ?_, ?_, hspace⟩ + · rw [hrun] + exact hhalt + · rw [hrun] + rfl + +end Turing.MultiTapePTM.BlockComputation.ScheduleSearch diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleSearchSpace.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleSearchSpace.lean new file mode 100644 index 0000000000..9514eebe50 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleSearchSpace.lean @@ -0,0 +1,160 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleSearch +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.InitializedEvaluatorCorrect +import Complexity.Foundations.Data.Nat.BinarySize + +/-! +# Uniform physical storage for sequential schedule search + +All candidate executions share one allowance evaluated at the first excluded schedule number. +This power occurs only in mathematical bounds: the machine's actual guard streams schedule rows. +Only one suspended caller and one concrete initialized evaluator are retained at any clock. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ScheduleSearch + +open MultiTapeTM Cslib.TreeEval.Ary.Packed +open QueryReplay + +/-- Actual search arguments and a binary counter through the first excluded schedule number. -/ +@[expose] noncomputable def callerSpace (k : ℕ) (args : Args) : ℕ := + let (_, _, q) := args + 8 * ((encArgs args).length + 2 * (k + 1) * q + 1) + +/-- One candidate allowance, uniformly enlarged to the first excluded schedule metadata. -/ +@[expose] noncomputable def candidateSpace (k s : ℕ) (Q : Type) (N b q : ℕ) : ℕ := + InitializedEvaluator.allowance k s Q N b (3 ^ ((k + 1) * q)) q + +/-- One retained caller plus one initialized concrete evaluator, with call framing. -/ +@[expose] noncomputable def space (k s : ℕ) (Q : Type) (N b q : ℕ) : ℕ := + 2 * callerSpace k (N, b, q) + candidateSpace k s Q N b q + 2 + +/-- The complete schedule counter uses only linearly many horizon bits. -/ +theorem counter_encoding_le (k q σ : ℕ) (hσ : σ ≤ 3 ^ ((k + 1) * q)) : + (encNat σ).length ≤ 2 * (k + 1) * q + 1 := by + have h := (Nat.size_le_size hσ).trans (Nat.size_pow_le 3 ((k + 1) * q)) + have hthree : Nat.size 3 = 2 := rfl + rw [hthree] at h + rw [length_encNat_eq_size] + nlinarith + +/-- Every possible decision payload and every in-range cursor fit the same caller allowance. -/ +theorem caller_encoding_le (k N b q σ : ℕ) (result : Option Bool) + (hσ : σ ≤ 3 ^ ((k + 1) * q)) : + (encCaller ((N, b, q), σ, result)).length ≤ callerSpace k (N, b, q) := by + have hc := counter_encoding_le k q σ hσ + have hr : (encOption encBool result).length ≤ 2 := by + cases result <;> simp only [encOption_none, encOption_some, List.length_nil, + List.length_cons, encBool_apply] <;> omega + simp only [encCaller, length_encPair, callerSpace] + omega + +/-- Schedule metadata enters the candidate allowance only through its binary encoded size. -/ +theorem allowance_schedule_mono (k s : ℕ) (Q : Type) (N b q σ τ : ℕ) (hστ : σ ≤ τ) : + InitializedEvaluator.allowance k s Q N b σ q ≤ + InitializedEvaluator.allowance k s Q N b τ q := by + have h := length_encNat_mono hστ + simp only [InitializedEvaluator.allowance, Initializer.space, Initializer.prepare, + Initializer.bankCells, parameters, Initializer.encMetadata, Initializer.encArgs, + encParameters, RawController.Query.queryStateBound, ConcreteSupplier.encContext, + ConcreteSupplier.replySpace, ConcreteSupplier.gateSpace, ConcreteSupplier.metadataSpace, + QueryGate.encParams, Interpolation.encContext, encNat3, length_encPair] + omega + +/-- Every accepted schedule's concrete execution allowance fits the uniform candidate bound. -/ +theorem candidate_allowance_le (k s : ℕ) (Q : Type) (N b q σ : ℕ) + (hσ : σ ≤ 3 ^ ((k + 1) * q)) : + InitializedEvaluator.allowance k s Q N b σ q ≤ candidateSpace k s Q N b q := + allowance_schedule_mono k s Q N b q σ _ hσ + +/-- Resumption cannot pass the first excluded schedule number. -/ +theorem resume_counter_le {k s : ℕ} {Q : Type} (e : Q ≃ Fin s) + (N b q σ : ℕ) (result : Option Bool) (returned : InitializedEvaluator.State k Q) + (hpending : pending k ((N, b, q), σ, result) = true) : + cursor (resume e ((N, b, q), σ, result) returned) ≤ 3 ^ ((k + 1) * q) := by + have hp := hpending + simp only [pending, Bool.and_eq_true] at hp + have hv : ScheduleCounter.valid k (σ, q) = true := hp.2 + have hσ := ScheduleCounter.succ_le_bound hv + cases ho : outcome e ((N, b, q), returned) <;> + simp only [resume, ho, cursor] <;> omega + +/-- Each physical prefix of one candidate stores just one caller and the actual callee. -/ +theorem runFrom_one_encoding_le {k s : ℕ} {Q : Type} [Finite Q] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (N b q σ : ℕ) (result : Option Bool) + (w : List Bool) (T : ℕ) + (hpending : pending k ((N, b, q), σ, result) = true) + (hpre : ∀ u < T, (InitializedEvaluator.machine M e).halted + ((InitializedEvaluator.machine M e).runFrom w + (InitializedEvaluator.start k s (N, b, σ, q)) u) = false) + (hhalt : (InitializedEvaluator.machine M e).halted + ((InitializedEvaluator.machine M e).runFrom w + (InitializedEvaluator.start k s (N, b, σ, q)) T) = true) + (hsize : ∀ u ≤ T, (InitializedEvaluator.encState + ((InitializedEvaluator.machine M e).runFrom w + (InitializedEvaluator.start k s (N, b, σ, q)) u)).length ≤ candidateSpace k s Q N b q) + (t : ℕ) (ht : t ≤ T + 2) : + (encState ((machine M e).runFrom w (.inl ((N, b, q), σ, result)) t)).length ≤ + space k s Q N b q := by + let c : CallerState := ((N, b, q), σ, result) + let start := InitializedEvaluator.start (Q := Q) k s (N, b, σ, q) + have hp := hpending + simp only [pending, Bool.and_eq_true] at hp + have hv : ScheduleCounter.valid k (σ, q) = true := hp.2 + have hσ := (ScheduleCounter.valid_eq_true_iff k σ q).mp hv + have hcaller := caller_encoding_le k N b q σ result hσ.le + have hnext : (encCaller (resume e c + ((InitializedEvaluator.machine M e).runFrom w start T))).length ≤ + callerSpace k (N, b, q) := by + change (encCaller (match outcome e ((N, b, q), + (InitializedEvaluator.machine M e).runFrom w start T) with + | some answer => ((N, b, q), σ, some answer) + | none => ((N, b, q), σ + 1, none))).length ≤ _ + cases ho : outcome e ((N, b, q), (InitializedEvaluator.machine M e).runFrom w start T) <;> + apply caller_encoding_le <;> omega + have hreq : request (Q := Q) k s c = some start := by + simp only [request, c, hpending, ↓reduceIte, start] + have hlive : (caller k).halted c = false := by + simp only [caller, c, hpending, Bool.not_true] + by_cases hzero : t = 0 + · subst t + change (encCaller c).length + 1 ≤ _ + dsimp only [space, c, start] at hnext ⊢ + omega + by_cases hfinal : t = T + 2 + · subst t + rw [show (machine M e).runFrom w (.inl c) (T + 2) = + .inl (resume e c ((InitializedEvaluator.machine M e).runFrom w start T)) from + QueryMachine.withCalls_runFrom_call (caller k) (InitializedEvaluator.machine M e) + (request k s) (resume e) w c start T hlive hreq hpre hhalt] + change (encCaller (resume e c + ((InitializedEvaluator.machine M e).runFrom w start T))).length + 1 ≤ _ + dsimp only [space, c, start] at hnext ⊢ + omega + have hentry : (machine M e).runFrom w (.inl c) 1 = .inr (c, start) := + QueryMachine.withCalls_step_call (caller k) (InitializedEvaluator.machine M e) + (request k s) (resume e) w c start hlive hreq + rw [show t = 1 + (t - 1) by omega, QueryMachine.runFrom_add, hentry] + have hpref := QueryMachine.withCalls_runFrom_subroutine_prefix (caller k) + (InitializedEvaluator.machine M e) (request k s) (resume e) w c start (t - 1) + (fun u hu => hpre u (by omega)) + rw [show (machine M e).runFrom w (.inr (c, start)) (t - 1) = + .inr (c, (InitializedEvaluator.machine M e).runFrom w start (t - 1)) from hpref] + have hcallee := hsize (t - 1) (by omega) + change (encSum encCaller (encPair encCaller InitializedEvaluator.encState) + (.inr (c, (InitializedEvaluator.machine M e).runFrom w start (t - 1)))).length ≤ _ + rw [QueryMachine.withCalls_encoding_inr_length] + dsimp only [space, c, start] at hcallee ⊢ + omega + +end Turing.MultiTapePTM.BlockComputation.ScheduleSearch diff --git a/ComplexityTests/Machines/Turing/MultiTape/ScheduleSearch.lean b/ComplexityTests/Machines/Turing/MultiTape/ScheduleSearch.lean new file mode 100644 index 0000000000..4399dffb8f --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/ScheduleSearch.lean @@ -0,0 +1,64 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of native root classification and schedule search) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleSearchCorrect + +/-! +# Root decisions and sequential search regressions + +Both accepting and rejecting halted roots stop search; a live root advances the counter. +Header classification includes the existing decoder's unused-control-rank behavior. Counter +tests cover the exact first excluded schedule and the zero-horizon boundary. +-/ + +public section + +namespace ComplexityTests.ScheduleSearch + +open Turing MultiTapeTM MultiTapePTM.BlockComputation + +/-- The singleton control-state encoding used by the numeric header examples. -/ +abbrev code : Fin 1 ≃ Fin 1 := Equiv.refl _ + +example : RootDecision.classify 0 code (1, [true, false, false, false, true, false]) = + some true := by decide +example : RootDecision.classify 0 code (1, [true, false, false, false, false, false]) = + some false := by decide +example : RootDecision.classify 0 code (1, [true, false, false, false, true, true]) = + some false := by decide +example : RootDecision.classify 0 code (1, [true, true, false, false, true, false]) = + none := by decide +example : RootDecision.classify 0 code (1, [false, false, false, false, true, false]) = + none := by decide +example : RootDecision.classify 0 code (1, [true, true, true, false, true, false]) = + some true := by decide +example : RootDecision.classify 0 code (1, []) = none := by decide +example : RootDecision.classify 0 code (0, [true]) = none := by decide + +example : ScheduleCounter.valid 0 (8, 2) = true := by decide +example : ScheduleCounter.valid 0 (9, 2) = false := by decide +example : ScheduleCounter.remaining 0 (9, 2) = 1 := by decide +example : ScheduleCounter.valid 0 (0, 0) = true := by decide +example : ScheduleCounter.valid 0 (1, 0) = false := by decide + +/-- A completed initialized evaluator with a two-chunk physical target bank. -/ +def returned (first second : ℕ) : InitializedEvaluator.State 0 (Fin 1) := + .inr (.inl ((1, 1, 0, 1, 3, 8, 64), (17, 1, 2), [first, second, 17, 17], [], none)) + +example : ScheduleSearch.resume code ((1, 1, 1), 1, none) (returned 18 17) = + ((1, 1, 1), 1, some false) := by decide +example : ScheduleSearch.resume code ((1, 1, 1), 1, none) (returned 18 19) = + ((1, 1, 1), 1, some true) := by decide +example : ScheduleSearch.resume code ((1, 1, 1), 1, none) (returned 20 17) = + ((1, 1, 1), 2, none) := by decide +example : (ScheduleSearch.caller 0).halted ((1, 1, 1), 1, some false) = true := by decide +example : (ScheduleSearch.caller 0).halted ((1, 1, 1), 3, none) = true := by decide +example : (ScheduleSearch.caller 0).halted ((1, 1, 1), 2, none) = false := by decide + +end ComplexityTests.ScheduleSearch From f0a787a1a382d87bd54dd4003db71738db5fff02 Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 05:51:57 +0000 Subject: [PATCH 34/36] feat(hpv): compose the native increasing-guess driver --- Complexity.lean | 7 + .../BlockComputation/BalancedParameters.lean | 197 +++++++++++++++ .../MultiTape/BlockComputation/Driver.lean | 102 ++++++++ .../BlockComputation/DriverComputable.lean | 53 +++++ .../BlockComputation/DriverCorrect.lean | 132 +++++++++++ .../BlockComputation/OuterSearch.lean | 188 +++++++++++++++ .../BlockComputation/OuterSearchCorrect.lean | 224 ++++++++++++++++++ .../BlockComputation/OuterSearchSpace.lean | 122 ++++++++++ .../Turing/MultiTape/OuterSearch.lean | 74 ++++++ ROADMAP.md | 9 +- 10 files changed, 1105 insertions(+), 3 deletions(-) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/BalancedParameters.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Driver.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/DriverComputable.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/DriverCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/OuterSearch.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/OuterSearchCorrect.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/OuterSearchSpace.lean create mode 100644 ComplexityTests/Machines/Turing/MultiTape/OuterSearch.lean diff --git a/Complexity.lean b/Complexity.lean index 7897f3c1f6..9156d660ee 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1691,11 +1691,15 @@ public import Complexity.Computability.Machines.Turing.MultiTape.Alternating public import Complexity.Computability.Machines.Turing.MultiTape.Alternating.Game public import Complexity.Computability.Machines.Turing.MultiTape.Alternating.GuessVerify public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.BalancedParameters public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteEvaluator public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteEvaluatorCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteSupplier public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteSupplierCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteSupplierSpace +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Driver +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.DriverComputable +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.DriverCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.EncodedTree public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Encoding.NativeChecked @@ -1730,6 +1734,9 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NodeReply public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NodeReplyCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.NormalizePoint +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.OuterSearch +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.OuterSearchCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.OuterSearchSpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplay public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAccess public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.QueryReplayAssignments diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/BalancedParameters.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/BalancedParameters.lean new file mode 100644 index 0000000000..1620c30ab3 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/BalancedParameters.lean @@ -0,0 +1,197 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatSize +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.NatPow +public import Mathlib.Algebra.Order.Floor.Div +import Complexity.Computability.Machines.Turing.MultiTape.Internal.BinarySizeFacts + +/-! +# Native dyadic parameters for block simulation + +The block width is a power of two near the square root of the time guess times its binary +length. The horizon is ceiling division by this positive width. Computing these parameters +uses space linear in the actual binary time guess and does not require a time-constructibility +hypothesis. The square inequalities state the approximation without real-valued square roots. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.BalancedParameters + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +/-- A dyadic approximation to the square root of time times binary time length. -/ +@[expose] def blockWidth (T : ℕ) : ℕ := 2 ^ ((T.size + T.size.size) / 2) + +/-- The number of blocks needed to cover the complete time guess. -/ +@[expose] def horizon (T : ℕ) : ℕ := T ⌈/⌉ blockWidth T + +/-- The width is positive even at a zero time guess. -/ +theorem blockWidth_pos (T : ℕ) : 0 < blockWidth T := by + exact Nat.pow_pos (by decide) + +/-- The selected horizon covers every step of the time guess. -/ +theorem coverage (T : ℕ) : T ≤ horizon T * blockWidth T := + by simpa only [horizon, smul_eq_mul, Nat.mul_comm] using + (le_smul_ceilDiv (α := ℕ) (b := T) (blockWidth_pos T)) + +/-- Rounding the horizon adds less than one block. -/ +theorem horizon_mul_lt (T : ℕ) : horizon T * blockWidth T < T + blockWidth T := + by + have h := Nat.div_mul_le_self (T + blockWidth T - 1) (blockWidth T) + have hp := blockWidth_pos T + change (T + blockWidth T - 1) / blockWidth T * blockWidth T < _ + omega + +/-- A positive time guess gives a nonzero schedule horizon. -/ +theorem horizon_pos {T : ℕ} (hT : 0 < T) : 0 < horizon T := by + have h := coverage T + by_contra hn + have hz : horizon T = 0 := by omega + rw [hz, Nat.zero_mul] at h + omega + +private theorem pow_size_le_twice {n : ℕ} (hn : 0 < n) : 2 ^ n.size ≤ 2 * n := by + have hs : 0 < n.size := Nat.size_pos.mpr hn + have hlo : 2 ^ (n.size - 1) ≤ n := Nat.lt_size.mp (by omega) + calc + 2 ^ n.size = 2 ^ (n.size - 1) * 2 := by + rw [← pow_succ] + congr 1 + omega + _ ≤ n * 2 := Nat.mul_le_mul_right 2 hlo + _ = 2 * n := Nat.mul_comm _ _ + +/-- The chosen block square is at most four times the intended time-length product. -/ +theorem blockWidth_sq_le {T : ℕ} (hT : 0 < T) : + blockWidth T ^ 2 ≤ 4 * (T * T.size) := by + have hs : 0 < T.size := Nat.size_pos.mpr hT + have h₁ := pow_size_le_twice hT + have h₂ := pow_size_le_twice hs + have hexp : 2 * ((T.size + T.size.size) / 2) ≤ T.size + T.size.size := by omega + have hp := Nat.pow_le_pow_right (by decide : 1 ≤ (2 : ℕ)) hexp + have hsq : 2 ^ (2 * ((T.size + T.size.size) / 2)) = blockWidth T ^ 2 := by + rw [blockWidth, ← pow_mul] + congr 1 + omega + rw [hsq, pow_add] at hp + have hmul := Nat.mul_le_mul h₁ h₂ + nlinarith + +/-- The chosen block square is never smaller than half the intended time-length product. -/ +theorem time_mul_size_lt_twice_blockWidth_sq (T : ℕ) : + T * T.size < 2 * blockWidth T ^ 2 := by + have h₁ := Nat.lt_size_self T + have h₂ := Nat.lt_size_self T.size + have hprod : T * T.size < 2 ^ T.size * 2 ^ T.size.size := by + calc + T * T.size ≤ T * 2 ^ T.size.size := Nat.mul_le_mul_left T h₂.le + _ < 2 ^ T.size * 2 ^ T.size.size := + Nat.mul_lt_mul_of_pos_right h₁ (Nat.pow_pos (by decide)) + have hexp : T.size + T.size.size ≤ 2 * ((T.size + T.size.size) / 2) + 1 := by omega + have hp := Nat.pow_le_pow_right (by decide : 1 ≤ (2 : ℕ)) hexp + have hsq : 2 ^ (2 * ((T.size + T.size.size) / 2)) = blockWidth T ^ 2 := by + rw [blockWidth, ← pow_mul] + congr 1 + omega + simp only [pow_add, pow_one, hsq] at hp + nlinarith + +/-- The selected width itself has only a linear number of binary digits. -/ +theorem blockWidth_size_le (T : ℕ) : (blockWidth T).size ≤ T.size + 1 := by + have h := length_encNat_le_self T.size + rw [length_encNat_eq_size] at h + rw [blockWidth, Nat.size_pow] + omega + +/-- The binary time length is smaller than twice the selected block width. -/ +theorem size_lt_twice_blockWidth (T : ℕ) : T.size < 2 * blockWidth T := by + have hsize := length_encNat_le_self T + rw [length_encNat_eq_size] at hsize + have hsq := Nat.mul_le_mul_right T.size hsize + have hbalance := time_mul_size_lt_twice_blockWidth_sq T + nlinarith + +/-- Horizon overhead times binary time length is paid by the selected block and one length. -/ +theorem horizon_mul_size_lt {T : ℕ} (hT : 0 < T) : + horizon T * T.size < 2 * blockWidth T + T.size := by + have hs : 0 < T.size := Nat.size_pos.mpr hT + have hround := Nat.mul_lt_mul_of_pos_right (horizon_mul_lt T) hs + have hbalance := time_mul_size_lt_twice_blockWidth_sq T + apply Nat.lt_of_mul_lt_mul_left (a := blockWidth T) + nlinarith + +/-- Block storage and horizon times binary time length balance within a fixed constant. -/ +theorem combined_le (T : ℕ) : + blockWidth T + horizon T * T.size ≤ 5 * blockWidth T := by + by_cases hz : T = 0 + · subst T + simp only [Nat.size_zero, Nat.mul_zero, Nat.add_zero] + omega + · have hq := horizon_mul_size_lt (Nat.pos_of_ne_zero hz) + have hs := size_lt_twice_blockWidth T + omega + +/-- An integer square bound for the complete balanced storage term. -/ +theorem combined_sq_le {T : ℕ} (hT : 0 < T) : + (blockWidth T + horizon T * T.size) ^ 2 ≤ 100 * (T * T.size) := by + have h := combined_le T + have hmul := Nat.mul_le_mul h h + have hsq := blockWidth_sq_le hT + nlinarith + +/-- Increasing the time guess never decreases the dyadic block width. -/ +theorem blockWidth_mono {T U : ℕ} (h : T ≤ U) : blockWidth T ≤ blockWidth U := by + have hs := Nat.size_le_size h + have hss := Nat.size_le_size hs + exact Nat.pow_le_pow_right (by decide : 1 ≤ (2 : ℕ)) (by omega) + +/-- The binary-width stack cost is bounded by a constant times the selected block width. -/ +theorem storage_le (T : ℕ) : + blockWidth T + horizon T * ((blockWidth T).size + 1) ≤ 15 * blockWidth T := by + by_cases hz : T = 0 + · subst T + decide + · have hs : 0 < T.size := Nat.size_pos.mpr (Nat.pos_of_ne_zero hz) + have hbits := blockWidth_size_le T + have hscalar : (blockWidth T).size + 1 ≤ 3 * T.size := by omega + have hmul := Nat.mul_le_mul_left (horizon T) hscalar + have hcombined := combined_le T + nlinarith + +/-- The dyadic exponent and its output bits fit the actual binary time storage. -/ +theorem blockWidth_inSpace : ComputableInSpace blockWidth encNat encNat + (fun T => (encNat T).length) := by + have hid : ComputableInSpace (fun T : ℕ => T) encNat encNat + (fun T => (encNat T).length) := of_inO (ComputableInO.of_eq (f := id) fun _ => rfl) + have hsize := budget_comp_linear natSize hid + have hsizeSize := budget_comp_linear natSize hsize + have hexp := budget_natDiv (budget_natAdd hsize hsizeSize) + (budget_const (encB := encNat) 2 (.refl _)) + refine (budget_natPow_variable (budget_const (encB := encNat) 2 (.refl _)) hexp + (.of_le_linear 4 4 fun T => ?_)).of_enc_eq fun _ => rfl + have hsize_le := length_encNat_le_self T.size + have hexp_le : (T.size + T.size.size) / 2 ≤ T.size := by + rw [length_encNat_eq_size] at hsize_le + omega + have hbits := (length_encNat_le_self ((T.size + T.size.size) / 2)).trans hexp_le + have htwo : (encNat 2).length = 2 := rfl + simp only [htwo, length_encNat_eq_size] at hbits ⊢ + omega + +/-- Ceiling division computes the horizon in actual binary-input-linear space. -/ +theorem horizon_inSpace : ComputableInSpace horizon encNat encNat + (fun T => (encNat T).length) := by + have hid : ComputableInSpace (fun T : ℕ => T) encNat encNat + (fun T => (encNat T).length) := of_inO (ComputableInO.of_eq (f := id) fun _ => rfl) + exact (budget_natDiv (budget_natPred (budget_natAdd hid blockWidth_inSpace)) + blockWidth_inSpace).of_enc_eq fun T => by rw [horizon, Nat.ceilDiv_eq_add_pred_div] + +end Turing.MultiTapePTM.BlockComputation.BalancedParameters diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Driver.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Driver.lean new file mode 100644 index 0000000000..6abb265f12 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/Driver.lean @@ -0,0 +1,102 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.InputLength +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.OuterSearch +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.InSpace.Cases +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Sum + +/-! +# Fixed square-root-space simulation driver + +A fixed machine first scans its original read-only input, then starts the increasing-guess +outer search with the measured length. Neither a supplied input length nor a time bound enters +its initial state. Intermediate stages are silent; the final output is one Boolean. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.Driver + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +variable {k s : ℕ} {Q : Type} + +/-- Input-length scanner or the active increasing-guess search. -/ +abbrev State (k : ℕ) (Q : Type) := QueryMachine.InputLength.State ⊕ OuterSearch.State k Q + +/-- The measured counter enters the outer search without copying the input. -/ +@[expose] def resume (state : QueryMachine.InputLength.State) : OuterSearch.State k Q := + OuterSearch.initial state.1 + +/-- Scan first, then simulate the source on the same original input. -/ +@[expose] noncomputable def machine (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + QueryMachine (State k Q) where + init := .inl QueryMachine.InputLength.machine.init + query := Sum.elim QueryMachine.InputLength.machine.query (OuterSearch.machine M e).query + next := fun (state, symbol) => state.elim + (fun scan => if QueryMachine.InputLength.machine.halted scan then .inr (resume scan) + else .inl (QueryMachine.InputLength.machine.next (scan, symbol))) + (fun active => .inr ((OuterSearch.machine M e).next (active, symbol))) + emit _ := [] + halted := Sum.elim (fun _ => false) (OuterSearch.machine M e).halted + finish := Sum.elim (fun _ => []) (OuterSearch.machine M e).finish + +variable [Finite Q] + +/-- Actual scan and search payloads, including their phase tag. -/ +@[expose] noncomputable def encState : State k Q ↪ List Bool := + encSum QueryMachine.InputLength.encState (OuterSearch.encState (k := k) (Q := Q)) + +/-- Native handoff computes the positive starting guess from the actual measured counter. -/ +theorem resume_inSpace : ComputableInSpace (resume (k := k) (Q := Q)) + QueryMachine.InputLength.encState OuterSearch.encState + (fun a => (QueryMachine.InputLength.encState a).length) := + (budget_comp_linear (g := OuterSearch.initial (k := k) (Q := Q)) + OuterSearch.initial_inSpace (fst encNat encBool)).of_enc_eq fun _ => rfl + +/-- Original-input queries are preserved in both phases. -/ +theorem machine_query_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).query encState encNat (fun a => (encState a).length) := + sumElim QueryMachine.InputLength.query_inSpace (OuterSearch.machine_query_inSpace M e) + +/-- Native scanning, handoff and search transitions. -/ +theorem machine_next_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).next (encPair encState (encOption encBool)) encState + (fun a => (encPair encState (encOption encBool) a).length) := by + have hs := fst QueryMachine.InputLength.encState (encOption encBool) + have hinl := of_inO (ComputableInO.inl QueryMachine.InputLength.encState + (OuterSearch.encState (k := k) (Q := Q))) + have hinr := of_inO (ComputableInO.inr QueryMachine.InputLength.encState + (OuterSearch.encState (k := k) (Q := Q))) + have hrun := budget_comp_linear hinl QueryMachine.InputLength.next_inSpace + have hresume := budget_comp_linear hinr (budget_comp_linear resume_inSpace hs) + have hhalt := budget_comp_linear QueryMachine.InputLength.halted_inSpace hs + have hleft := budget_ite hhalt hresume hrun + have hright := budget_comp_linear hinr (OuterSearch.machine_next_inSpace M e) + exact sumElimFst hleft hright + +/-- All intermediate phases are silent. -/ +theorem machine_emit_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).emit (encPair encState (encOption encBool)) encBits + (fun a => (encPair encState (encOption encBool) a).length) := budget_const [] (.refl _) + +/-- The driver stops precisely when its outer search stops. -/ +theorem machine_halted_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).halted encState encBool (fun a => (encState a).length) := + sumElim (budget_const (encIn := QueryMachine.InputLength.encState) false (.refl _)) + (OuterSearch.machine_halted_inSpace M e) + +/-- The final returned Boolean is the driver output. -/ +theorem machine_finish_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).finish encState encBits (fun a => (encState a).length) := + sumElim (budget_const (encIn := QueryMachine.InputLength.encState) [] (.refl _)) + (OuterSearch.machine_finish_inSpace M e) + +end Turing.MultiTapePTM.BlockComputation.Driver diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/DriverComputable.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/DriverComputable.lean new file mode 100644 index 0000000000..4f36761731 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/DriverComputable.lean @@ -0,0 +1,53 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.DriverCorrect + +/-! +# Compiling the native simulation driver + +The existing query-machine compiler turns the complete driver into an ordinary multitape +computation. Its space is bounded by the maximum physical storage of its candidate searches. +The supplied source halting clock is a semantic witness only: the compiled machine obtains its +input length and time guesses itself. Reducing this allowance to the square-root expression is +a separate quantitative step. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.Driver + +open MultiTapeTM + +variable {k s : ℕ} {Q : Type} [Finite Q] + +/-- A total deterministic source's Boolean decision is computed by an ordinary multitape +machine with the complete driver's physical-space allowance. No computability assumption on +its source halting-clock witness is needed. -/ +theorem computableInSpace_decision (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + {α : Type} (enc : α ↪ List Bool) (τ : α → ℕ) + (hτ : ∀ a, (M.runFrom (Cfg.init M.q₀ (enc a)) (τ a)).Halted) : + ∃ time c, ComputableInTimeAndSpace + (fun a => decide ((M.runFrom (Cfg.init M.q₀ (enc a)) (τ a)).output = [true])) + enc encBool time (fun a => c * space k s Q (enc a).length (τ a)) := by + classical + choose clock hpre hhalt hout hsize using fun a => exists_first_decision M e (enc a) (τ a) (hτ a) + refine QueryMachine.computableInSpace (encS := encState) (machine M e) + (machine_query_inSpace M e) (machine_next_inSpace M e) (machine_emit_inSpace M e) + (machine_halted_inSpace M e) (machine_finish_inSpace M e) clock hhalt hpre ?_ + (fun a => space k s Q (enc a).length (τ a)) ?_ ⟨1, ?_⟩ + · intro w + exact (hout w).symm + · intro w + dsimp only [space] + omega + · intro w t ht + simpa only [Nat.one_mul] using hsize w t ht + +end Turing.MultiTapePTM.BlockComputation.Driver diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/DriverCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/DriverCorrect.lean new file mode 100644 index 0000000000..048ed4d516 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/DriverCorrect.lean @@ -0,0 +1,132 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Driver +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.OuterSearchCorrect + +/-! +# Scanning and entering the increasing-guess simulation + +The fixed driver's first phase measures the actual input length. Its next clock enters the +outer search, which thereafter runs on the same original input. The scan costs only the +binary counter and one phase tag. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.Driver + +variable {k s : ℕ} {Q : Type} + +/-- Once entered, the outer search runs step for step, including halted clocks. -/ +theorem runFrom_inr (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (state : OuterSearch.State k Q) (t : ℕ) : + (machine M e).runFrom w (.inr state) t = + .inr ((OuterSearch.machine M e).runFrom w state t) := by + have hstep (q : OuterSearch.State k Q) : + (machine M e).step w (.inr q) = .inr ((OuterSearch.machine M e).step w q) := by + simp only [QueryMachine.step, QueryMachine.read, machine, Sum.elim_inr] + by_cases h : (OuterSearch.machine M e).halted q = true <;> + simp only [h, Bool.false_eq_true, ↓reduceIte] + induction t with + | zero => rfl + | succ t ih => rw [QueryMachine.runFrom_succ, ih, hstep, QueryMachine.runFrom_succ] + +/-- Every scan clock reproduces the existing native length scanner. -/ +theorem run_scan (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (j : ℕ) (hj : j ≤ w.length + 1) : + (machine M e).run w j = .inl (QueryMachine.InputLength.machine.run w j) := by + induction j with + | zero => rfl + | succ j ih => + have hpre := QueryMachine.InputLength.live w (show j < w.length + 1 by omega) + rw [QueryMachine.run_succ, ih (by omega), QueryMachine.run_succ] + simp only [QueryMachine.step, QueryMachine.read, machine, Sum.elim_inl, hpre, + Bool.false_eq_true, ↓reduceIte] + +/-- The first clock after measuring length starts the actual outer search. -/ +theorem run_entry (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) : + (machine M e).run w (w.length + 2) = .inr (OuterSearch.initial w.length) := by + rw [show w.length + 2 = (w.length + 1) + 1 by omega, QueryMachine.run_succ, + run_scan M e w _ le_rfl, QueryMachine.InputLength.run_at_end] + simp only [QueryMachine.step, machine, Sum.elim_inl, Bool.false_eq_true, ↓reduceIte, + QueryMachine.InputLength.machine, resume] + +/-- Scan, handoff, and every subsequent outer-search clock share the original input. -/ +theorem run_after_entry (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (w : List Bool) + (t : ℕ) : + (machine M e).run w (w.length + 2 + t) = + .inr ((OuterSearch.machine M e).runFrom w (OuterSearch.initial w.length) t) := by + change (machine M e).runFrom w (machine M e).init (w.length + 2 + t) = _ + rw [QueryMachine.runFrom_add] + change (machine M e).runFrom w ((machine M e).run w (w.length + 2)) t = _ + rw [run_entry, runFrom_inr] + +/-- The complete scan prefix has logarithmic encoded state length. -/ +theorem length_run_scan_le [Finite Q] (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (j : ℕ) (hj : j ≤ w.length + 1) : + (encState ((machine M e).run w j)).length ≤ 2 * (Nat.log2 w.length + 1) + 3 := by + rw [run_scan M e w j hj] + change (QueryMachine.InputLength.encState (QueryMachine.InputLength.machine.run w j)).length + + 1 ≤ _ + have h := QueryMachine.InputLength.length_run_le w j + omega + +/-- Uniform scan and outer-search allowance through a given source halting clock. -/ +@[expose] noncomputable def space (k s : ℕ) (Q : Type) (N τ : ℕ) : ℕ := + 2 * (Nat.log2 N + 1) + OuterSearch.space k s Q N (max (N + 1) τ) + 4 + +/-- A halting source yields a first halt of the fixed driver, the exact Boolean decision, and +an all-clock bound. The source time is used only in this proof and its mathematical allowance. -/ +theorem exists_first_decision [Finite Q] (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (τ : ℕ) (hhalt : (M.runFrom (Cfg.init M.q₀ w) τ).Halted) : + ∃ T, + (∀ t < T, (machine M e).halted ((machine M e).run w t) = false) ∧ + (machine M e).halted ((machine M e).run w T) = true ∧ + (machine M e).output w T = + [decide ((M.runFrom (Cfg.init M.q₀ w) τ).output = [true])] ∧ + ∀ t ≤ T, (encState ((machine M e).run w t)).length ≤ space k s Q w.length τ := by + classical + obtain ⟨U, guess, hguess, hpre, hrun, hhalted, hspace⟩ := + OuterSearch.exists_first_decision M e w τ hhalt + let T := w.length + 2 + U + have hfinal : (machine M e).run w T = .inr + (.inl (w.length, guess, + some (decide ((M.runFrom (Cfg.init M.q₀ w) τ).output = [true])))) := by + rw [run_after_entry, hrun] + have hbefore : ∀ t < T, (machine M e).halted ((machine M e).run w t) = false := by + intro t ht + by_cases hscan : t ≤ w.length + 1 + · rw [run_scan M e w t hscan] + rfl + · obtain ⟨u, rfl⟩ := Nat.exists_eq_add_of_le (show w.length + 2 ≤ t by omega) + rw [run_after_entry] + exact hpre u (by dsimp only [T] at ht; omega) + refine ⟨T, hbefore, ?_, ?_, ?_⟩ + · rw [hfinal] + rfl + · rw [QueryMachine.output_eq_of_not_halted _ _ hbefore] + have hemit : (machine M e).emit = fun _ => [] := rfl + simp only [hemit, List.map_const', List.flatten_replicate_nil, List.nil_append] + rw [hfinal] + rfl + · intro t ht + by_cases hscan : t ≤ w.length + 1 + · have hs := length_run_scan_le M e w t hscan + dsimp only [space] + omega + · obtain ⟨u, rfl⟩ := Nat.exists_eq_add_of_le (show w.length + 2 ≤ t by omega) + rw [run_after_entry] + change (OuterSearch.encState ((OuterSearch.machine M e).runFrom w + (OuterSearch.initial w.length) u)).length + 1 ≤ _ + have hs := hspace u (show u ≤ U by dsimp only [T] at ht; omega) + dsimp only [space] + omega + +end Turing.MultiTapePTM.BlockComputation.Driver diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/OuterSearch.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/OuterSearch.lean new file mode 100644 index 0000000000..a4310d4014 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/OuterSearch.lean @@ -0,0 +1,188 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleSearch +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.BalancedParameters + +/-! +# Increasing time guesses on the original input + +The outer caller stores the input length, a binary time guess and an optional final decision. +Each guess runs the complete schedule search with balanced parameters. An undecided search +increments the guess; either Boolean decision stops. Only the final Boolean is emitted. +No time bound or time-constructibility witness is consulted by the machine. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.OuterSearch + +open MultiTapeTM MultiTapeTM.ComputableInSpace + +variable {k s : ℕ} {Q : Type} + +/-- Original input length, current time guess and a returned decision. -/ +abbrev CallerState := ℕ × ℕ × Option Bool +/-- One outer caller or one suspended caller with its schedule-search state. -/ +abbrev State (k : ℕ) (Q : Type) := CallerState ⊕ (CallerState × ScheduleSearch.State k Q) + +/-- Begin with a positive guess at least the input length. -/ +@[expose] def initial (N : ℕ) : State k Q := .inl (N, N + 1, none) + +/-- Actual runtime arguments for the current guess. -/ +@[expose] def parameters : CallerState → ScheduleSearch.Args := fun (N, T, _) => + (N, BalancedParameters.blockWidth T, BalancedParameters.horizon T) + +/-- Both accepting and rejecting returned decisions stop the caller. -/ +@[expose] def pending : CallerState → Bool := fun (_, _, result) => !result.isSome + +/-- Start exactly one native schedule search on the original input. -/ +@[expose] def request : CallerState → Option (ScheduleSearch.State k Q) := fun state => + if pending state then some (ScheduleSearch.initial (parameters state)) else none + +/-- A live result advances the guess; a decision, including false, is retained. -/ +@[expose] def resume : CallerState → ScheduleSearch.State k Q → CallerState := + fun (N, T, _) returned => match ScheduleSearch.answer returned with + | none => (N, T + 1, none) + | some result => (N, T, some result) + +/-- The fixed caller template is halted; actual lengths enter through `initial`. -/ +@[expose] def caller : QueryMachine CallerState where + init := (0, 0, some false) + query _ := 0 + next := Prod.fst + emit _ := [] + halted := fun state => !pending state + finish := fun (_, _, result) => result.toList + +/-- The fixed machine does not capture any runtime time bound. -/ +@[expose] noncomputable def machine (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + QueryMachine (State k Q) := + caller.withCalls (ScheduleSearch.machine M e) request resume + +/-- Returned decision, absent while a schedule search is suspended. -/ +@[expose] def answer : State k Q → Option Bool := + Sum.elim (fun (_, _, result) => result) (fun _ => none) + +/-- Explicit binary caller framing. -/ +@[expose] noncomputable def encCaller : CallerState ↪ List Bool := + encPair encNat (encPair encNat (encOption encBool)) + +variable [Finite Q] + +/-- The caller and the single active search are charged separately. -/ +@[expose] noncomputable def encState : State k Q ↪ List Bool := + encSum encCaller (encPair encCaller (ScheduleSearch.encState (k := k) (Q := Q))) + +/-- Native positive starting guess from the measured input length. -/ +theorem initial_inSpace : ComputableInSpace (initial (k := k) (Q := Q)) encNat encState + (fun N => (encNat N).length) := by + have hid : ComputableInSpace (fun N : ℕ => N) encNat encNat + (fun N => (encNat N).length) := of_inO ComputableInDegree.id'.inO_one + have hc := budget_prod hid (budget_prod (budget_natSucc hid) + (budget_const (encB := encOption encBool) none (.refl _))) + exact (budget_comp_linear (of_inO (ComputableInO.inl encCaller + (encPair encCaller (ScheduleSearch.encState (k := k) (Q := Q))))) hc).of_enc_eq + fun _ => rfl + +/-- Native balanced arguments are computed from the actual binary guess. -/ +theorem parameters_inSpace : ComputableInSpace parameters encCaller ScheduleSearch.encArgs + (fun a => (encCaller a).length) := by + have hN := fst encNat (encPair encNat (encOption encBool)) + have hT := (snd encNat (encPair encNat (encOption encBool))).fst' + exact (budget_prod hN (budget_prod + (budget_comp_linear BalancedParameters.blockWidth_inSpace hT) + (budget_comp_linear BalancedParameters.horizon_inSpace hT))).of_enc_eq fun _ => rfl + +/-- Native stopping guard depends only on the returned Boolean option. -/ +theorem pending_inSpace : ComputableInSpace pending encCaller encBool + (fun a => (encCaller a).length) := + budget_finite (fun result : Option Bool => !result.isSome) + (snd encNat (encPair encNat (encOption encBool))).snd' + +/-- One schedule-search request stores its actual runtime arguments. -/ +theorem request_inSpace : ComputableInSpace (request (k := k) (Q := Q)) encCaller + (encOption ScheduleSearch.encState) (fun a => (encCaller a).length) := by + have hstart := budget_comp_linear (ScheduleSearch.initial_inSpace (k := k) (Q := Q)) + parameters_inSpace + have hsome := budget_comp_linear + (encC := encOption (ScheduleSearch.encState (k := k) (Q := Q))) + (of_inO (ComputableInDegree.optionSome + (enc₁ := ScheduleSearch.encState (k := k) (Q := Q))).inO_one) hstart + exact budget_ite pending_inSpace hsome + (budget_const (encB := encOption ScheduleSearch.encState) none (.refl _)) + +/-- Native return handling charges the saved caller and the actual returned search state. -/ +theorem resume_inSpace : ComputableInSpace + (fun a : CallerState × ScheduleSearch.State k Q => resume a.1 a.2) + (encPair encCaller ScheduleSearch.encState) encCaller + (fun a => (encPair encCaller ScheduleSearch.encState a).length) := by + have hc := fst encCaller (ScheduleSearch.encState (k := k) (Q := Q)) + have ht := snd encCaller (ScheduleSearch.encState (k := k) (Q := Q)) + have hN := hc.fst' + have hT := hc.snd'.fst' + have hout := budget_comp_linear ScheduleSearch.answer_inSpace ht + have hisSome := budget_finite (encC := encBool) (Option.isSome : Option Bool → Bool) hout + have hT' := budget_ite hisSome hT (budget_natSucc hT) + refine (budget_prod hN (budget_prod hT' hout)).of_enc_eq ?_ + rintro ⟨⟨N, T, result⟩, returned⟩ + cases ho : ScheduleSearch.answer returned <;> simp [resume, ho, encCaller, encPair_apply] + +private theorem caller_query_inSpace : ComputableInSpace caller.query encCaller encNat + (fun a => (encCaller a).length) := budget_const 0 (.refl _) +private theorem caller_next_inSpace : ComputableInSpace caller.next + (encPair encCaller (encOption encBool)) encCaller + (fun a => (encPair encCaller (encOption encBool) a).length) := fst _ _ +private theorem caller_emit_inSpace : ComputableInSpace caller.emit + (encPair encCaller (encOption encBool)) encBits + (fun a => (encPair encCaller (encOption encBool) a).length) := budget_const [] (.refl _) +private theorem caller_halted_inSpace : ComputableInSpace caller.halted encCaller encBool + (fun a => (encCaller a).length) := budget_finite Bool.not pending_inSpace +private theorem caller_finish_inSpace : ComputableInSpace caller.finish encCaller encBits + (fun a => (encCaller a).length) := + (budget_comp_linear (g := Option.toList (α := Bool)) + (of_inO ComputableInDegree.optionBoolToList.inO_one) + (snd encNat (encPair encNat (encOption encBool))).snd').of_enc_eq fun _ => rfl + +/-- Native queries of the outer caller and its active schedule search. -/ +theorem machine_query_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).query encState encNat (fun a => (encState a).length) := + QueryMachine.withCalls_query_inSpace caller (ScheduleSearch.machine M e) request resume + encCaller ScheduleSearch.encState caller_query_inSpace + (ScheduleSearch.machine_query_inSpace M e) request_inSpace + +/-- Native transitions include entry, return and all schedule-search clocks. -/ +theorem machine_next_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).next (encPair encState (encOption encBool)) encState + (fun a => (encPair encState (encOption encBool) a).length) := + QueryMachine.withCalls_next_inSpace caller (ScheduleSearch.machine M e) request resume + encCaller ScheduleSearch.encState caller_next_inSpace + (ScheduleSearch.machine_next_inSpace M e) (ScheduleSearch.machine_halted_inSpace M e) + request_inSpace resume_inSpace + +/-- Only the eventual final Boolean is output. -/ +theorem machine_emit_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).emit (encPair encState (encOption encBool)) encBits + (fun a => (encPair encState (encOption encBool) a).length) := + QueryMachine.withCalls_emit_inSpace caller (ScheduleSearch.machine M e) request resume + encCaller ScheduleSearch.encState caller_emit_inSpace request_inSpace + +/-- Either Boolean decision ends the outer search. -/ +theorem machine_halted_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).halted encState encBool (fun a => (encState a).length) := + QueryMachine.withCalls_halted_inSpace caller (ScheduleSearch.machine M e) request resume + encCaller ScheduleSearch.encState caller_halted_inSpace + +/-- The final output is the singleton list containing the returned Boolean. -/ +theorem machine_finish_inSpace (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) : + ComputableInSpace (machine M e).finish encState encBits (fun a => (encState a).length) := + QueryMachine.withCalls_finish_inSpace caller (ScheduleSearch.machine M e) request resume + encCaller ScheduleSearch.encState caller_finish_inSpace + +end Turing.MultiTapePTM.BlockComputation.OuterSearch diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/OuterSearchCorrect.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/OuterSearchCorrect.lean new file mode 100644 index 0000000000..d81a6a5775 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/OuterSearchCorrect.lean @@ -0,0 +1,224 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.OuterSearch +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.OuterSearchSpace +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleSearchCorrect +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LogSpace.QueryMachine.FirstHalt + +/-! +# Increasing time guesses recover the source machine's decision + +Each guess invokes the complete native schedule search. Source configurations are stationary +after halting, so every returned decision agrees with the original halted computation. A guess +at least the source halting time covers that time and returns a decision. The final guess is +bounded by the larger of that time and the positive initial input-length guess. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.OuterSearch + +variable {k s : ℕ} {Q : Type} + +private theorem boundaryDecision_of_halted (M : MultiTapeTM k Bool Q) (w : List Bool) + (τ clock : ℕ) (hhalt : (M.runFrom (Cfg.init M.q₀ w) τ).Halted) (hle : τ ≤ clock) : + RootDecision.boundaryDecision M w clock = + some (decide ((M.runFrom (Cfg.init M.q₀ w) τ).output = [true])) := by + classical + unfold RootDecision.boundaryDecision + rw [MultiTapeTM.runFrom_eq_of_halt M _ hle hhalt] + have hstate : (M.runFrom (Cfg.init M.q₀ w) τ).state = none := hhalt + simp only [hstate, Option.isNone_none, ↓reduceIte] + +private theorem boundaryDecision_safe (M : MultiTapeTM k Bool Q) (w : List Bool) + (τ clock : ℕ) (hhalt : (M.runFrom (Cfg.init M.q₀ w) τ).Halted) : + RootDecision.boundaryDecision M w clock = none ∨ + RootDecision.boundaryDecision M w clock = + some (decide ((M.runFrom (Cfg.init M.q₀ w) τ).output = [true])) := by + classical + by_cases hle : τ ≤ clock + · exact Or.inr (boundaryDecision_of_halted M w τ clock hhalt hle) + · cases hv : RootDecision.boundaryDecision M w clock with + | none => exact Or.inl rfl + | some result => + have hclock : (M.runFrom (Cfg.init M.q₀ w) clock).Halted := by + dsimp only [RootDecision.boundaryDecision] at hv + split at hv + · rename_i hstate + exact Option.isNone_iff_eq_none.mp hstate + · cases hv + have hs := boundaryDecision_of_halted M w clock τ hclock (by omega) + have hc := boundaryDecision_of_halted M w clock clock hclock le_rfl + have ht := boundaryDecision_of_halted M w τ τ hhalt le_rfl + rw [hv] at hc + exact Or.inr (hc.trans (hs.symm.trans ht)) + +/-- Every candidate's schedule search reaches a first halt with its exact bounded decision. -/ +theorem exists_first_candidate [Finite Q] (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (guess : ℕ) : + ∃ T, + (∀ t < T, (ScheduleSearch.machine M e).halted + ((ScheduleSearch.machine M e).runFrom w + (ScheduleSearch.initial (w.length, BalancedParameters.blockWidth guess, + BalancedParameters.horizon guess)) t) = false) ∧ + (ScheduleSearch.machine M e).halted ((ScheduleSearch.machine M e).runFrom w + (ScheduleSearch.initial (w.length, BalancedParameters.blockWidth guess, + BalancedParameters.horizon guess)) T) = true ∧ + ScheduleSearch.answer ((ScheduleSearch.machine M e).runFrom w + (ScheduleSearch.initial (w.length, BalancedParameters.blockWidth guess, + BalancedParameters.horizon guess)) T) = RootDecision.boundaryDecision M w + (BalancedParameters.horizon guess * BalancedParameters.blockWidth guess) ∧ + ∀ t ≤ T, (ScheduleSearch.encState ((ScheduleSearch.machine M e).runFrom w + (ScheduleSearch.initial (w.length, BalancedParameters.blockWidth guess, + BalancedParameters.horizon guess)) t)).length ≤ ScheduleSearch.space k s Q w.length + (BalancedParameters.blockWidth guess) (BalancedParameters.horizon guess) := by + have : NeZero (BalancedParameters.blockWidth guess) := + ⟨(BalancedParameters.blockWidth_pos guess).ne'⟩ + obtain ⟨U, hhalt, hans, hspace⟩ := ScheduleSearch.exists_runFrom_boundaryDecision M e w + (BalancedParameters.blockWidth guess) (BalancedParameters.horizon guess) + obtain ⟨T, hle, hT, hpre, hstate⟩ := QueryMachine.exists_first_halt_le + (ScheduleSearch.machine M e) w _ U hhalt + exact ⟨T, hpre, hT, by rw [hstate]; exact hans, + fun t ht => hspace t (ht.trans hle)⟩ + +/-- A candidate call consumes its first-halt search clocks plus one entry and one return. -/ +theorem runFrom_one (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (guess T : ℕ) + (hpre : ∀ t < T, (ScheduleSearch.machine M e).halted + ((ScheduleSearch.machine M e).runFrom w + (ScheduleSearch.initial (w.length, BalancedParameters.blockWidth guess, + BalancedParameters.horizon guess)) t) = false) + (hhalt : (ScheduleSearch.machine M e).halted ((ScheduleSearch.machine M e).runFrom w + (ScheduleSearch.initial (w.length, BalancedParameters.blockWidth guess, + BalancedParameters.horizon guess)) T) = true) : + (machine M e).runFrom w (.inl (w.length, guess, none)) (T + 2) = + .inl (resume (w.length, guess, none) ((ScheduleSearch.machine M e).runFrom w + (ScheduleSearch.initial (w.length, BalancedParameters.blockWidth guess, + BalancedParameters.horizon guess)) T)) := by + apply QueryMachine.withCalls_runFrom_call + · rfl + · rfl + · exact hpre + · exact hhalt + +/-- If the source machine halts, increasing native time guesses reaches exactly its Boolean +output decision at a first halt. The completed guess never exceeds `max (input.length+1) τ`. -/ +theorem exists_first_decision [Finite Q] (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) + (w : List Bool) (τ : ℕ) (hhalt : (M.runFrom (Cfg.init M.q₀ w) τ).Halted) : + ∃ T guess, guess ≤ max (w.length + 1) τ ∧ + (∀ t < T, (machine M e).halted ((machine M e).runFrom w (initial w.length) t) = false) ∧ + (machine M e).runFrom w (initial w.length) T = + .inl (w.length, guess, some (decide ((M.runFrom (Cfg.init M.q₀ w) τ).output = [true]))) ∧ + (machine M e).halted ((machine M e).runFrom w (initial w.length) T) = true ∧ + ∀ t ≤ T, (encState ((machine M e).runFrom w (initial w.length) t)).length ≤ + space k s Q w.length (max (w.length + 1) τ) := by + classical + let result := decide ((M.runFrom (Cfg.init M.q₀ w) τ).output = [true]) + let bound := max (w.length + 1) τ + let value := fun guess => RootDecision.boundaryDecision M w + (BalancedParameters.horizon guess * BalancedParameters.blockWidth guess) + have hsafe (guess : ℕ) : value guess = none ∨ value guess = some result := + boundaryDecision_safe M w τ _ hhalt + have hbound : value bound = some result := boundaryDecision_of_halted M w τ _ hhalt + ((Nat.le_max_right _ _).trans (BalancedParameters.coverage bound)) + have hstep (guess : ℕ) (hguess : guess ≤ bound) : ∃ T, + (machine M e).runFrom w (.inl (w.length, guess, none)) T = + .inl (match value guess with + | none => (w.length, guess + 1, none) + | some answer => (w.length, guess, some answer)) ∧ + ∀ t ≤ T, (encState ((machine M e).runFrom w (.inl (w.length, guess, none)) t)).length ≤ + space k s Q w.length bound := by + obtain ⟨T, hpre, hT, hans, hsize⟩ := exists_first_candidate M e w guess + have hrun := runFrom_one M e w guess T hpre hT + have hnext : (encCaller (resume (w.length, guess, none) + ((ScheduleSearch.machine M e).runFrom w (ScheduleSearch.initial + (w.length, BalancedParameters.blockWidth guess, + BalancedParameters.horizon guess)) T))).length ≤ + callerSpace w.length bound := by + change (encCaller (match ScheduleSearch.answer _ with + | none => (w.length, guess + 1, none) + | some answer => (w.length, guess, some answer))).length ≤ _ + rw [hans] + change (encCaller (match value guess with + | none => (w.length, guess + 1, none) + | some answer => (w.length, guess, some answer))).length ≤ callerSpace w.length bound + cases hv : value guess with + | none => + have hlt : guess < bound := by + by_contra h + have heq : guess = bound := by omega + rw [heq, hbound] at hv + cases hv + exact caller_encoding_le w.length bound (guess + 1) none (by omega) + | some answer => exact caller_encoding_le w.length bound guess (some answer) hguess + refine ⟨T + 2, ?_, ?_⟩ + · have hresume : resume (w.length, guess, none) + ((ScheduleSearch.machine M e).runFrom w (ScheduleSearch.initial + (w.length, BalancedParameters.blockWidth guess, BalancedParameters.horizon guess)) T) = + (match value guess with + | none => (w.length, guess + 1, none) + | some answer => (w.length, guess, some answer)) := by + change (match ScheduleSearch.answer _ with + | none => (w.length, guess + 1, none) + | some answer => (w.length, guess, some answer)) = _ + rw [hans] + exact hrun.trans (congrArg Sum.inl hresume) + · exact runFrom_one_encoding_le M e w.length guess bound none w T rfl hguess hpre hT + (fun t ht => (hsize t ht).trans + (candidate_space_le k s Q w.length bound guess hguess)) hnext + have haux (n : ℕ) : ∀ guess, guess + n = bound → + ∃ T final, final ≤ bound ∧ (machine M e).runFrom w (.inl (w.length, guess, none)) T = + .inl (w.length, final, some result) ∧ + ∀ t ≤ T, (encState ((machine M e).runFrom w (.inl (w.length, guess, none)) t)).length ≤ + space k s Q w.length bound := by + induction n with + | zero => + intro guess heq + have heq' : guess = bound := by omega + obtain ⟨T, hT, hspace⟩ := hstep guess (by omega) + refine ⟨T, guess, by omega, ?_, hspace⟩ + simpa only [heq', hbound] using hT + | succ n ih => + intro guess heq + obtain ⟨T, hT, hspace⟩ := hstep guess (by omega) + cases hv : value guess with + | none => + obtain ⟨U, final, hfinal, hU, hUspace⟩ := ih (guess + 1) (by omega) + refine ⟨T + U, final, hfinal, ?_, ?_⟩ + · rw [QueryMachine.runFrom_add, hT, hv, hU] + · intro t ht + by_cases hbefore : t ≤ T + · exact hspace t hbefore + · obtain ⟨u, rfl⟩ := Nat.exists_eq_add_of_le (show T ≤ t by omega) + rw [QueryMachine.runFrom_add, hT, hv] + exact hUspace u (by omega) + | some answer => + have hans : some answer = some result := by + rcases hsafe guess with hnone | hans + · rw [hv] at hnone + cases hnone + · exact hv.symm.trans hans + refine ⟨T, guess, by omega, ?_, hspace⟩ + rw [hv] at hT + rw [← hans] + exact hT + obtain ⟨U, guess, hguess, hrun, hspace⟩ := haux (bound - (w.length + 1)) (w.length + 1) + (Nat.add_sub_of_le (Nat.le_max_left _ _)) + have hU : (machine M e).halted ((machine M e).runFrom w (initial w.length) U) = true := by + change (machine M e).halted ((machine M e).runFrom w + (.inl (w.length, w.length + 1, none)) U) = true + rw [hrun] + rfl + obtain ⟨T, hle, hT, hpre, hstate⟩ := QueryMachine.exists_first_halt_le + (machine M e) w (initial w.length) U hU + exact ⟨T, guess, hguess, hpre, hstate.trans hrun, hT, + fun t ht => hspace t (ht.trans hle)⟩ + +end Turing.MultiTapePTM.BlockComputation.OuterSearch diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/OuterSearchSpace.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/OuterSearchSpace.lean new file mode 100644 index 0000000000..721dff41c9 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/OuterSearchSpace.lean @@ -0,0 +1,122 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Mathlib.Data.Finset.Lattice.Fold +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.OuterSearch +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleSearchSpace +import Complexity.Computability.Machines.Turing.MultiTape.Internal.BinarySizeFacts + +/-! +# Physical space for bounded increasing time guesses + +One outer caller is retained beside one schedule search. A finite supremum bounds candidate +storage without adding a factor for the number of guesses. The supremum is a mathematical +allowance; no list of guesses or candidate spaces is constructed by the machine. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.OuterSearch + +open MultiTapeTM + +/-- Actual binary input length and largest time guess, including the optional decision. -/ +@[expose] noncomputable def callerSpace (N A : ℕ) : ℕ := + 8 * ((encNat N).length + (encNat A).length + 1) + +/-- Maximum storage of one candidate search through the largest permitted guess. -/ +@[expose] noncomputable def candidateSpace (k s : ℕ) (Q : Type) (N A : ℕ) : ℕ := + (Finset.range (A + 1)).sup fun guess => ScheduleSearch.space k s Q N + (BalancedParameters.blockWidth guess) (BalancedParameters.horizon guess) + +/-- One suspended caller beside one candidate, with complete sum and product framing. -/ +@[expose] noncomputable def space (k s : ℕ) (Q : Type) (N A : ℕ) : ℕ := + 2 * callerSpace N A + candidateSpace k s Q N A + 2 + +/-- Any candidate through the bounded guess fits the same supremum allowance. -/ +theorem candidate_space_le (k s : ℕ) (Q : Type) (N A guess : ℕ) (hguess : guess ≤ A) : + ScheduleSearch.space k s Q N (BalancedParameters.blockWidth guess) + (BalancedParameters.horizon guess) ≤ candidateSpace k s Q N A := + Finset.le_sup (f := fun guess => ScheduleSearch.space k s Q N + (BalancedParameters.blockWidth guess) (BalancedParameters.horizon guess)) + (Finset.mem_range.mpr (by omega)) + +/-- Binary caller storage is uniform through the largest permitted time guess. -/ +theorem caller_encoding_le (N A guess : ℕ) (result : Option Bool) (hguess : guess ≤ A) : + (encCaller (N, guess, result)).length ≤ callerSpace N A := by + have hg := length_encNat_mono hguess + have hr : (encOption encBool result).length ≤ 2 := by + cases result <;> simp only [encOption_none, encOption_some, List.length_nil, + List.length_cons, encBool_apply] <;> omega + simp only [encCaller, length_encPair, callerSpace] + omega + +/-- Every physical clock of a bounded candidate call charges its suspended caller and callee. -/ +theorem runFrom_one_encoding_le {k s : ℕ} {Q : Type} [Finite Q] + (M : MultiTapeTM k Bool Q) (e : Q ≃ Fin s) (N guess A : ℕ) (result : Option Bool) + (w : List Bool) (T : ℕ) (hpending : pending (N, guess, result) = true) + (hguess : guess ≤ A) + (hpre : ∀ u < T, (ScheduleSearch.machine M e).halted + ((ScheduleSearch.machine M e).runFrom w (ScheduleSearch.initial + (N, BalancedParameters.blockWidth guess, BalancedParameters.horizon guess)) u) = false) + (hhalt : (ScheduleSearch.machine M e).halted ((ScheduleSearch.machine M e).runFrom w + (ScheduleSearch.initial + (N, BalancedParameters.blockWidth guess, BalancedParameters.horizon guess)) T) = true) + (hsize : ∀ u ≤ T, (ScheduleSearch.encState ((ScheduleSearch.machine M e).runFrom w + (ScheduleSearch.initial + (N, BalancedParameters.blockWidth guess, BalancedParameters.horizon guess)) u)).length ≤ + candidateSpace k s Q N A) + (hnext : (encCaller (resume (N, guess, result) ((ScheduleSearch.machine M e).runFrom w + (ScheduleSearch.initial + (N, BalancedParameters.blockWidth guess, BalancedParameters.horizon guess)) T))).length ≤ + callerSpace N A) + (t : ℕ) (ht : t ≤ T + 2) : + (encState ((machine M e).runFrom w (.inl (N, guess, result)) t)).length ≤ + space k s Q N A := by + let c : CallerState := (N, guess, result) + let start := ScheduleSearch.initial (k := k) (Q := Q) + (N, BalancedParameters.blockWidth guess, BalancedParameters.horizon guess) + have hcaller := caller_encoding_le N A guess result hguess + have hreq : request (k := k) (Q := Q) c = some start := by + simp only [request, c, hpending, ↓reduceIte, start, parameters] + have hlive : caller.halted c = false := by + simp only [caller, c, hpending, Bool.not_true] + by_cases hzero : t = 0 + · subst t + change (encCaller c).length + 1 ≤ _ + dsimp only [space, c, start] at hnext ⊢ + omega + by_cases hfinal : t = T + 2 + · subst t + rw [show (machine M e).runFrom w (.inl c) (T + 2) = + .inl (resume c ((ScheduleSearch.machine M e).runFrom w start T)) from + QueryMachine.withCalls_runFrom_call caller (ScheduleSearch.machine M e) + request resume w c start T hlive hreq hpre hhalt] + change (encCaller (resume c + ((ScheduleSearch.machine M e).runFrom w start T))).length + 1 ≤ _ + dsimp only [space, c, start] at hnext ⊢ + omega + have hentry : (machine M e).runFrom w (.inl c) 1 = .inr (c, start) := + QueryMachine.withCalls_step_call caller (ScheduleSearch.machine M e) + request resume w c start hlive hreq + rw [show t = 1 + (t - 1) by omega, QueryMachine.runFrom_add, hentry] + have hpref := QueryMachine.withCalls_runFrom_subroutine_prefix caller + (ScheduleSearch.machine M e) request resume w c start (t - 1) + (fun u hu => hpre u (by omega)) + rw [show (machine M e).runFrom w (.inr (c, start)) (t - 1) = + .inr (c, (ScheduleSearch.machine M e).runFrom w start (t - 1)) from hpref] + have hcallee := hsize (t - 1) (by omega) + change (encSum encCaller (encPair encCaller ScheduleSearch.encState) + (.inr (c, (ScheduleSearch.machine M e).runFrom w start (t - 1)))).length ≤ _ + rw [QueryMachine.withCalls_encoding_inr_length] + dsimp only [space, c, start] at hcallee ⊢ + omega + + +end Turing.MultiTapePTM.BlockComputation.OuterSearch diff --git a/ComplexityTests/Machines/Turing/MultiTape/OuterSearch.lean b/ComplexityTests/Machines/Turing/MultiTape/OuterSearch.lean new file mode 100644 index 0000000000..99fe703efa --- /dev/null +++ b/ComplexityTests/Machines/Turing/MultiTape/OuterSearch.lean @@ -0,0 +1,74 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of native increasing-guess simulation) + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.DriverCorrect + +/-! +# Balanced parameters and increasing-guess regressions + +These tests exercise actual parameter values, both Boolean returns, live resumption, the empty +input starting guess and the fixed scanner phase. General semantic proofs cover arbitrary inputs. +-/ + +public section + +namespace ComplexityTests.OuterSearch + +open Turing MultiTapeTM MultiTapePTM.BlockComputation + +example : BalancedParameters.blockWidth 0 = 1 := by decide +example : BalancedParameters.horizon 0 = 0 := by decide +example : BalancedParameters.blockWidth 1 = 2 := by decide +example : BalancedParameters.horizon 1 = 1 := by decide +example : BalancedParameters.blockWidth 16 = 16 := by decide +example : BalancedParameters.horizon 16 = 1 := by decide +example : BalancedParameters.blockWidth 17 = 16 := by decide +example : BalancedParameters.horizon 17 = 2 := by decide +example : BalancedParameters.blockWidth 1024 = 128 := by decide +example : BalancedParameters.horizon 1024 = 8 := by decide + +example : OuterSearch.initial (k := 0) (Q := Fin 1) 0 = .inl (0, 1, none) := rfl +example : OuterSearch.resume (k := 0) (Q := Fin 1) (3, 4, none) + (.inl ((3, 4, 1), 0, some false)) = (3, 4, some false) := rfl +example : OuterSearch.resume (k := 0) (Q := Fin 1) (3, 4, none) + (.inl ((3, 4, 1), 0, some true)) = (3, 4, some true) := rfl +example : OuterSearch.resume (k := 0) (Q := Fin 1) (3, 4, none) + (.inl ((3, 4, 1), 0, none)) = (3, 5, none) := rfl +example : OuterSearch.caller.halted (3, 4, some false) = true := rfl +example : OuterSearch.caller.halted (3, 4, none) = false := rfl +example : OuterSearch.caller.finish (3, 4, some false) = [false] := rfl +example : OuterSearch.caller.finish (3, 4, some true) = [true] := rfl + +/-- A source that halts on its first step without accepting. -/ +def stop : MultiTapeTM 0 Bool (Fin 1) := MultiTapeTM.ofTr 0 fun _ _ _ => + { inputTape := .zero, workTapes := Fin.elim0, output := none, state := none } + +example : (Driver.machine stop (Equiv.refl _)).run [] 1 = .inl (0, true) := rfl +example : (Driver.machine stop (Equiv.refl _)).run [] 2 = + .inr (OuterSearch.initial 0) := rfl +example : (Driver.machine stop (Equiv.refl _)).run [true, false] 2 = + .inl (2, false) := rfl +example : (Driver.machine stop (Equiv.refl _)).run [true, false] 4 = + .inr (OuterSearch.initial 2) := rfl + +/-- The complete driver correctness theorem has a concrete rejecting source instance. -/ +example : ∃ T, (Driver.machine stop (Equiv.refl _)).halted + ((Driver.machine stop (Equiv.refl _)).run [] T) = true ∧ + (Driver.machine stop (Equiv.refl _)).output [] T = [false] := by + have hsource : (stop.runFrom (Cfg.init stop.q₀ []) 1).Halted := by + rw [MultiTapeTM.runFrom_one, MultiTapeTM.step_of_state (q := 0) rfl] + simp [stop, Action.apply] + have houtput : (stop.runFrom (Cfg.init stop.q₀ []) 1).output = [] := by + rw [MultiTapeTM.runFrom_one, MultiTapeTM.step_of_state (q := 0) rfl] + simp [stop, Action.apply] + obtain ⟨T, _, hhalt, hout, _⟩ := Driver.exists_first_decision stop (Equiv.refl _) [] 1 hsource + rw [houtput] at hout + exact ⟨T, hhalt, hout⟩ + +end ComplexityTests.OuterSearch diff --git a/ROADMAP.md b/ROADMAP.md index 5b0f082155..aa6b7d22e6 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -194,9 +194,12 @@ index links to canonical definitions rather than keeping a second set. - **Reingold:** a symmetric-machine model of SL, and the edge-list encoding of USTCON by a logspace reduction. - **Williams's `TIME[t] ⊆ SPACE[√(t log t)]`** ([#888]): implicit Tree Evaluation with the - fan-in-`d` space bound, subroutine calls for implicit gates and leaves, and padding of - non-perfect trees. For Cook–Mertz itself: malformed-input handling and the refined - `O(ℓ + H log(ℓ + 2))` parameter bound. + fan-in-`d` space bound and the native block-tree evaluator are developed in the stacked + work tracked by the issue. The fixed driver scans its original input, searches schedules + and increases balanced time guesses without a time-constructibility premise. Remaining: + assemble its physical-space allowances into `O(√(t log t))`, prove the standard class + inclusion, and finish review/integration. For Cook–Mertz itself: malformed-input handling + and the refined `O(ℓ + H log(ℓ + 2))` parameter bound. - Alternating time and space classes and bounded-alternation characterizations of PH; sharp bounds for sublinear space, distinct from polynomial-space simulation. - **Deeper consequences of a Boolean-hierarchy collapse:** Kadin's sparse `S` with From 528ff34ff37d7f0abc09707da3eff989ccfa9a2c Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 06:03:36 +0000 Subject: [PATCH 35/36] feat(complexity): prove Williams square-root-space theorem --- Complexity.lean | 3 + .../Classes/TimeSpace/Williams.lean | 157 ++++++++ .../BlockComputation/DriverBounds.lean | 104 ++++++ .../ScheduleSearchBounds.lean | 341 ++++++++++++++++++ ComplexityTests/Williams.lean | 67 ++++ ROADMAP.md | 10 +- 6 files changed, 678 insertions(+), 4 deletions(-) create mode 100644 Complexity/Computability/Complexity/Classes/TimeSpace/Williams.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/DriverBounds.lean create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleSearchBounds.lean create mode 100644 ComplexityTests/Williams.lean diff --git a/Complexity.lean b/Complexity.lean index 9156d660ee..2087bbc25e 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1362,6 +1362,7 @@ public import Complexity.Computability.Complexity.Classes.Time.AcceptCount public import Complexity.Computability.Complexity.Classes.Time.BoundedExists public import Complexity.Computability.Complexity.Classes.Time.Polynomial public import Complexity.Computability.Complexity.Classes.Time.Verifier +public import Complexity.Computability.Complexity.Classes.TimeSpace.Williams public import Complexity.Computability.Complexity.Classes.Toda public import Complexity.Computability.Complexity.Classes.Toda.Counting public import Complexity.Computability.Complexity.Classes.Toda.Hierarchy @@ -1698,6 +1699,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteSupplierCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ConcreteSupplierSpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.Driver +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.DriverBounds public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.DriverComputable public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.DriverCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.EncodedTree @@ -1776,6 +1778,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputatio public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadSpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleReadWindow public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleSearch +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleSearchBounds public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleSearchCorrect public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleSearchSpace public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputationTree diff --git a/Complexity/Computability/Complexity/Classes/TimeSpace/Williams.lean b/Complexity/Computability/Complexity/Classes/TimeSpace/Williams.lean new file mode 100644 index 0000000000..9a87de41a7 --- /dev/null +++ b/Complexity/Computability/Complexity/Classes/TimeSpace/Williams.lean @@ -0,0 +1,157 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Complexity.Classes.Time +public import Complexity.Computability.Complexity.Classes.Space.Basic +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.DriverComputable +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.DriverBounds +import Complexity.Computability.Machines.Turing.MultiTape.Run +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.NatLength +import Mathlib.Data.Nat.Sqrt + +/-! +# Williams's square-root-space simulation of deterministic time + +Theorem 1.1 of [Williams2025], version 1 (February 25, 2025), states that every multitape time +bound at least the input length admits a deterministic square-root-time-log-time space +simulation. Here both classes use binary multitape machines and arbitrary injective input +encodings. Their bounds allow fixed multiplicative and additive constants. The displayed natural +bound uses `Nat.sqrt (t n * (Nat.log2 (t n) + 1))`, with the added one making the small-input +convention explicit. The input is read-only; space counts visited work-tape cells. + +The simulator scans its input and guesses its own time bounds. No time-constructibility or +monotonicity assumption is made on `t`. The supplied source clock is only a proof witness. +Internally, the library uses a fixed-fan-in +three-window block tree and signed masking over a selected prime field; these implementation +choices do not restrict the deterministic source machines covered by the class inclusion. +-/ + +public section + +namespace Cslib.Complexity + +open _root_.Turing _root_.Turing.MultiTapeTM +open _root_.Turing.MultiTapePTM.BlockComputation + +private theorem size_succ_le (n : ℕ) : (n + 1).size ≤ n.size + 1 := by + apply Nat.size_le.mpr + rw [pow_succ] + have hn := Nat.lt_size_self n + have hp : 0 < 2 ^ n.size := Nat.pow_pos (by decide) + omega + +private theorem affine_max_product_le (c₁ c₂ : ℕ) : ∃ K, ∀ N x : ℕ, N ≤ x → + max (N + 1) (c₁ * x + c₂) * (max (N + 1) (c₁ * x + c₂)).size ≤ + K * (x * (Nat.log2 x + 1) + 1) := by + let a := c₁ + c₂ + 2 + refine ⟨2 * a * (a.size + 2), ?_⟩ + intro N x hN + let y := max (N + 1) (c₁ * x + c₂) + have hy : y ≤ a * (x + 1) := by + apply max_le <;> dsimp only [a] <;> nlinarith + have hsize := (Nat.size_le_size hy).trans (Nat.size_mul_le a (x + 1)) + have hsucc := size_succ_le x + have hxsize : x.size ≤ Nat.log2 x + 1 := by + simpa only [length_encNat_eq_size] using length_encNat_le_log2 (j := x) le_rfl + have hfactor : y.size ≤ (a.size + 2) * (Nat.log2 x + 1) := by + have hpos : 0 ≤ a.size * Nat.log2 x := Nat.zero_le _ + nlinarith + have hmul := Nat.mul_le_mul hy hfactor + have hround : (x + 1) * (Nat.log2 x + 1) ≤ 2 * (x * (Nat.log2 x + 1) + 1) := by + by_cases hx : x = 0 + · subst x; decide + · have hp : 1 ≤ x := Nat.one_le_iff_ne_zero.mpr hx + have h := Nat.mul_le_mul_right (Nat.log2 x + 1) (show x + 1 ≤ 2 * x by omega) + nlinarith + have hscale := Nat.mul_le_mul_left (a * (a.size + 2)) hround + dsimp only [y] at hmul ⊢ + nlinarith + +private theorem le_affine_sqrt_of_sq_le {x y C : ℕ} (h : x ^ 2 ≤ C * (y + 1)) : + x ≤ (C + 1) * Nat.sqrt y + (C + 1) := by + have hy : y + 1 ≤ (Nat.sqrt y + 1) ^ 2 := by + have h' := Nat.sqrt_le_add y + nlinarith + have hC : C ≤ (C + 1) ^ 2 := by nlinarith + have hmul := Nat.mul_le_mul hC hy + have hsq : x ^ 2 ≤ ((C + 1) * (Nat.sqrt y + 1)) ^ 2 := by + calc + _ ≤ C * (y + 1) := h + _ ≤ (C + 1) ^ 2 * (Nat.sqrt y + 1) ^ 2 := hmul + _ = _ := by ring + have hle := (Nat.pow_le_pow_iff_left (by decide : (2 : ℕ) ≠ 0)).mp hsq + calc + x ≤ (C + 1) * (Nat.sqrt y + 1) := hle + _ = _ := by ring + +/-- The complete driver absorbs an arbitrary affine source clock into the conventional +square-root-time-log-time resource, including its input-length scan. -/ +theorem williams_driver_space_le (k s : ℕ) (Q : Type) (c₁ c₂ : ℕ) : + ∃ C, ∀ N x : ℕ, N ≤ x → + Driver.space k s Q N (c₁ * x + c₂) ≤ + C * Nat.sqrt (x * (Nat.log2 x + 1)) + C := by + obtain ⟨c, hc⟩ := Driver.space_sq_le k s Q + obtain ⟨K, hK⟩ := affine_max_product_le c₁ c₂ + refine ⟨c * K + 1, ?_⟩ + intro N x hN + have h := (hc N (c₁ * x + c₂)).trans (Nat.mul_le_mul_left c (hK N x hN)) + rw [← Nat.mul_assoc] at h + exact le_affine_sqrt_of_sq_le h + +/-- Williams's deterministic time-to-space inclusion, for every injective input encoding. +This is Theorem 1.1 of [Williams2025], version 1, in the library's affine-bound multitape +classes. The binary logarithm and natural square root differ from the paper's asymptotic +notation only by fixed constants and the explicit small-input convention. -/ +theorem DTIME_subset_DSPACE_sqrt {α : Type*} (enc : α ↪ List Bool) (t : ℕ → ℕ) + (ht : ∀ n, n ≤ t n) : + DTIME enc t ⊆ DSPACE enc (fun n => Nat.sqrt (t n * (Nat.log2 (t n) + 1))) := by + classical + rintro A ⟨c₁, c₂, sourceSpace, k, Q, hfinite, M, hM⟩ + let : Finite Q := hfinite + let : Fintype Q := Fintype.ofFinite Q + let e : Q ≃ Fin (Fintype.card Q) := Fintype.equivFin Q + -- The range subtype is small even when the encoded instance type lives in a higher universe. + let Input := Set.range enc + let inputEncoding : Input ↪ List Bool := ⟨Subtype.val, Subtype.val_injective⟩ + let clock : Input → ℕ := fun word => c₁ * t word.val.length + c₂ + have hclock : ∀ word : Input, + (M.runFrom (Turing.Cfg.init M.q₀ (inputEncoding word)) (clock word)).Halted := by + intro word + obtain ⟨a, ha⟩ := word.property + have h := (hM a).runFrom_of_le le_rfl + change (M.runFrom (Turing.Cfg.init M.q₀ word.val) + (c₁ * t word.val.length + c₂)).Halted + rw [← ha] + simpa only [Turing.MultiTapeNTM.initCfg] using h.1 + obtain ⟨simTime, simFactor, k', Q', hfinite', simulator, hsim⟩ := + Driver.computableInSpace_decision M e inputEncoding clock hclock + obtain ⟨C, hC⟩ := williams_driver_space_le k (Fintype.card Q) Q c₁ c₂ + refine mem_DSPACE_iff.mpr ⟨simFactor * C, simFactor * C, ?_, k', Q', hfinite', simulator, ?_⟩ + · exact fun a => simTime ⟨enc a, ⟨a, rfl⟩⟩ + · intro a + let word : Input := ⟨enc a, ⟨a, rfl⟩⟩ + have h := hsim word + have hsource := (hM a).runFrom_of_le le_rfl + simp only [Turing.MultiTapeNTM.initCfg] at hsource + have hout : decide ((M.runFrom (Turing.Cfg.init M.q₀ (enc a)) + (c₁ * t (enc a).length + c₂)).output = [true]) = indicator A a := by + rw [hsource.2] + change decide ([indicator A a] = [true]) = indicator A a + cases indicator A a <;> rfl + have hresult := hC (enc a).length (t (enc a).length) (ht _) + have hbound := Nat.mul_le_mul_left simFactor hresult + rw [Nat.mul_add, ← Nat.mul_assoc] at hbound + have hcompute := h.1 + change simulator.Computes (enc a) + [decide ((M.runFrom (Turing.Cfg.init M.q₀ (enc a)) + (c₁ * t (enc a).length + c₂)).output = [true])] at hcompute + rw [hout] at hcompute + exact ⟨hcompute, h.2.1, h.2.2.mono hbound⟩ + +end Cslib.Complexity diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/DriverBounds.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/DriverBounds.lean new file mode 100644 index 0000000000..36ec0d3418 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/DriverBounds.lean @@ -0,0 +1,104 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.DriverCorrect +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleSearchBounds +import Complexity.Computability.Machines.Turing.MultiTape.Internal.BinarySizeFacts + +/-! +# Quantitative storage of the complete native driver + +The maximum over increasing guesses is bounded by the selected block width at the largest +guess. The complete driver, including its original-input scan, has squared storage at most a +fixed-machine constant times the largest guess multiplied by its binary length. No number of +guesses multiplies the candidate storage allowance. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.OuterSearch + +open MultiTapeTM + +/-- Taking a finite maximum of candidate allowances preserves the balanced storage shape. -/ +theorem space_le (k s : ℕ) (Q : Type) : ∃ C, ∀ N A : ℕ, + space k s Q N A ≤ C * (BalancedParameters.blockWidth A + N.size + 1) := by + obtain ⟨c, hc⟩ := ScheduleSearch.space_le k s Q + refine ⟨15 * c + 64, ?_⟩ + intro N A + have hsup : candidateSpace k s Q N A ≤ + c * (15 * BalancedParameters.blockWidth A + N.size + 1) := by + unfold candidateSpace + apply Finset.sup_le + intro guess hguess + have hguessA : guess ≤ A := by + have h := Finset.mem_range.mp hguess + omega + have hcost := hc N (BalancedParameters.blockWidth guess) + (BalancedParameters.horizon guess) (BalancedParameters.blockWidth_pos guess) + have hstorage := BalancedParameters.storage_le guess + have hmono := BalancedParameters.blockWidth_mono hguessA + have hterm : BalancedParameters.blockWidth guess + BalancedParameters.horizon guess * + ((BalancedParameters.blockWidth guess).size + 1) + N.size + 1 ≤ + 15 * BalancedParameters.blockWidth A + N.size + 1 := by nlinarith + exact hcost.trans (Nat.mul_le_mul_left c hterm) + have hsize := BalancedParameters.size_lt_twice_blockWidth A + simp only [space, callerSpace, length_encNat_eq_size] + nlinarith + +end Turing.MultiTapePTM.BlockComputation.OuterSearch + +namespace Turing.MultiTapePTM.BlockComputation.Driver + +/-- The input scan and all increasing-guess candidates use one balanced-width allowance. -/ +theorem space_le (k s : ℕ) (Q : Type) : ∃ C, ∀ N τ : ℕ, + space k s Q N τ ≤ C * + (BalancedParameters.blockWidth (max (N + 1) τ) + N.size + 1) := by + obtain ⟨c, hc⟩ := OuterSearch.space_le k s Q + refine ⟨c + 8, ?_⟩ + intro N τ + have houter := hc N (max (N + 1) τ) + have hlog : Nat.log2 N ≤ N.size := by + by_cases hz : N = 0 + · subst N + decide + · exact ((Nat.log2_lt hz).mpr (Nat.lt_size_self N)).le + dsimp only [space] + nlinarith + +/-- An integer square bound states the square-root storage rate without real-valued rounding. -/ +theorem space_sq_le (k s : ℕ) (Q : Type) : ∃ C, ∀ N τ : ℕ, + (space k s Q N τ) ^ 2 ≤ + C * (max (N + 1) τ * (max (N + 1) τ).size) := by + obtain ⟨c, hc⟩ := space_le k s Q + refine ⟨64 * c ^ 2, ?_⟩ + intro N τ + let A := max (N + 1) τ + have hA : 0 < A := by dsimp only [A]; omega + have hN : N ≤ A := by dsimp only [A]; omega + have hNsize := Nat.size_le_size hN + have hAsize := BalancedParameters.size_lt_twice_blockWidth A + have hblock := BalancedParameters.blockWidth_pos A + have hshape := hc N τ + have hscalar : space k s Q N τ ≤ 4 * c * BalancedParameters.blockWidth A := by + change space k s Q N τ ≤ c * + (BalancedParameters.blockWidth A + N.size + 1) at hshape + have hterm : BalancedParameters.blockWidth A + N.size + 1 ≤ + 4 * BalancedParameters.blockWidth A := by omega + calc + space k s Q N τ ≤ c * (BalancedParameters.blockWidth A + N.size + 1) := hshape + _ ≤ c * (4 * BalancedParameters.blockWidth A) := Nat.mul_le_mul_left c hterm + _ = 4 * c * BalancedParameters.blockWidth A := by ring + have hsquare := Nat.mul_le_mul hscalar hscalar + have hbalanced := BalancedParameters.blockWidth_sq_le hA + have hscaled := Nat.mul_le_mul_left (16 * c ^ 2) hbalanced + change (space k s Q N τ) ^ 2 ≤ 64 * c ^ 2 * (A * A.size) + nlinarith + +end Turing.MultiTapePTM.BlockComputation.Driver diff --git a/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleSearchBounds.lean b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleSearchBounds.lean new file mode 100644 index 0000000000..c3aa493ef5 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/BlockComputation/ScheduleSearchBounds.lean @@ -0,0 +1,341 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Machines.Turing.MultiTape.BlockComputation.ScheduleSearchSpace +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LinearSpaceArith + +/-! +# Quantitative selected-parameter bounds for schedule search + +These estimates separate linear bank and assignment-capacity storage from logarithmic metadata. +In particular, the chunk width used at each stack level is bounded by the binary block width, +not by the block width itself. This distinction prevents a spurious horizon-times-block term. +The final estimate assembles these bounds with one candidate and one suspended caller. +-/ + +public section + +namespace Turing.MultiTapePTM.BlockComputation.ScheduleSearch + +open MultiTapeTM Cslib.TreeEval.Ary.Packed +open QueryReplay MultiTapeNTM.ChoiceTable + +/-- A fixed-machine linear bound on the complete encoded block-value width. -/ +@[expose] def widthConstant (k s : ℕ) : ℕ := 7 * k + (s + 1).size + 4 + +/-- Positive blocks absorb the fixed header overhead into a fixed multiplicative constant. -/ +theorem width_le_mul (k s : ℕ) {b : ℕ} (hb : 0 < b) : + Encoding.width k s b ≤ widthConstant k s * b := by + have h := Encoding.width_le k s b + have hheader := Nat.le_mul_of_pos_right ((s + 1).size + 3) hb + dsimp only [widthConstant] + nlinarith + +/-- Measuring the encoded value width adds only a fixed number of bits to the block width. -/ +theorem width_size_le (k s : ℕ) {b : ℕ} (hb : 0 < b) : + (Encoding.width k s b).size ≤ (widthConstant k s).size + b.size := + (Nat.size_le_size (width_le_mul k s hb)).trans (Nat.size_mul_le _ _) + +/-- The selected chunk width is logarithmic in the block width, uniformly in the horizon. -/ +theorem selected_chunkWidth_le (k s : ℕ) {b : ℕ} (hb : 0 < b) : + chunkWidth (k * 3 + 1) (Encoding.width k s b) ≤ + (k * 3 + 1).size + (widthConstant k s).size + b.size := by + have h := Nat.size_mul_le (k * 3 + 1) (Encoding.width k s b) + have hw := width_size_le k s hb + change ((k * 3 + 1) * Encoding.width k s b).size ≤ _ + omega + +/-- This is the logarithmic estimate needed inside the horizon-dependent stack term. -/ +theorem horizon_chunkWidth_le (k s q : ℕ) {b : ℕ} (hb : 0 < b) : + q * (chunkWidth (k * 3 + 1) (Encoding.width k s b) + 1) ≤ + ((k * 3 + 1).size + (widthConstant k s).size + 1) * q * (b.size + 1) := by + have hm := selected_chunkWidth_le k s hb + have hscalar : chunkWidth (k * 3 + 1) (Encoding.width k s b) + 1 ≤ + ((k * 3 + 1).size + (widthConstant k s).size + 1) * (b.size + 1) := by + nlinarith + have h := Nat.mul_le_mul_left q hscalar + nlinarith + +/-- The selected raw evaluator contributes linear bank storage and logarithmic storage per level. +The context and supplier allowances are retained with their existing constant coefficients. -/ +theorem selected_queryStateBound_le (k s : ℕ) : ∃ C, ∀ contextBits supplierBits b q : ℕ, + 0 < b → + RawController.Query.queryStateBound contextBits + (fieldModulus (k * 3 + 1) (Encoding.width k s b)) (k * 3 + 1) + (chunkCount (k * 3 + 1) (Encoding.width k s b)) q supplierBits ≤ + 4 * contextBits + supplierBits + C * (b + q * (b.size + 1) + 1) := by + let d := k * 3 + 1 + let A := widthConstant k s + let L := d.size + A.size + 1 + refine ⟨200 * (d + 1) * (A + L + d.size + 1) + 4, ?_⟩ + intro contextBits supplierBits b q hb + have hraw := RawController.Query.queryStateBound_packed_le contextBits supplierBits + (d := d) (b := Encoding.width k s b) (H := q) (by dsimp only [d]; omega) + (Encoding.width_pos k s b) + have hwidth := width_le_mul k s hb + have hstack := horizon_chunkWidth_le k s q hb + have hinner : Encoding.width k s b + + q * (chunkWidth d (Encoding.width k s b) + 1) + d.size + 1 ≤ + (A + L + d.size + 1) * (b + q * (b.size + 1) + 1) := by + let R := b + q * (b.size + 1) + 1 + have hw : Encoding.width k s b ≤ A * R := + hwidth.trans (Nat.mul_le_mul_left A (by dsimp only [R]; omega)) + have hq : q * (chunkWidth d (Encoding.width k s b) + 1) ≤ L * R := by + calc + _ ≤ L * q * (b.size + 1) := hstack + _ = L * (q * (b.size + 1)) := Nat.mul_assoc _ _ _ + _ ≤ L * R := Nat.mul_le_mul_left L (by dsimp only [R]; omega) + have hc : d.size + 1 ≤ (d.size + 1) * R := + Nat.le_mul_of_pos_right _ (by dsimp only [R]; omega) + calc + _ ≤ A * R + L * R + (d.size + 1) * R := by omega + _ = _ := by dsimp only [R]; ring + have hmul := Nat.mul_le_mul_left (200 * (d + 1)) hinner + nlinarith + +/-- The selected assignment capacity is a power of two with its exact binary length charged. -/ +theorem assignment_limit_size (d B : ℕ) : + ((2 ^ chunkWidth d B) ^ (d * chunkCount d B)).size = + d * (chunkCount d B * chunkWidth d B) + 1 := by + rw [← pow_mul, Nat.size_pow] + congr 1 + ring + +/-- Capacity bits are linear in the value width; the number of assignments is never charged. -/ +theorem assignment_limit_size_le {d B : ℕ} (hd : 0 < d) (hB : 0 < B) : + ((2 ^ chunkWidth d B) ^ (d * chunkCount d B)).size ≤ + d * (B + chunkWidth d B) + 1 := by + rw [assignment_limit_size] + have h := Nat.mul_le_mul_left d (chunkCount_mul_lt hd hB).le + omega + +/-- A full interpolation point has linear field-cell storage at the selected parameters. -/ +theorem selected_point_cost_le {d B : ℕ} (hd : 0 < d) (hB : 0 < B) : + d * chunkCount d B * (2 * (fieldModulus d B).size + 1) ≤ + 17 * d * (B + chunkWidth d B) := by + have hp := chunkCount_mul_fieldModulus_size_le hd hB + have hr := chunkCount_le hd hB + have hscalar : chunkCount d B * (2 * (fieldModulus d B).size + 1) ≤ + 17 * (B + chunkWidth d B) := by nlinarith + have h := Nat.mul_le_mul_left d hscalar + nlinarith + +/-- Explicit output-count and alphabet/modulus storage also fit one linear bank allowance. -/ +theorem selected_bank_cost_le {d B : ℕ} (hd : 0 < d) (hB : 0 < B) : + chunkCount d B * ((fieldModulus d B).size + + (2 ^ chunkWidth d B).size + 1) ≤ 12 * (B + chunkWidth d B) := by + have halphabet := Nat.size_le_size (alphabetSize_lt hd hB).le + have hp : 0 < (fieldModulus d B).size := + Nat.size_pos.mpr (fieldModulus_prime d B).pos + have hregister := chunkCount_mul_fieldModulus_size_le hd hB + have hscalar : (fieldModulus d B).size + (2 ^ chunkWidth d B).size + 1 ≤ + 3 * (fieldModulus d B).size := by omega + have h := Nat.mul_le_mul_left (chunkCount d B) hscalar + nlinarith + +/-- Native parameter initialization already supplies a uniform binary metadata-size estimate. -/ +theorem prepared_metadata_le (k s : ℕ) : ∃ C, ∀ N b σ q : ℕ, + (Initializer.encMetadata ((Initializer.prepare k s (N, b, σ, q)).1)).length ≤ + C * (N.size + b.size + σ.size + q.size + 1) := by + obtain ⟨c, hc⟩ := MultiTapeTM.ComputableInSpace.length_le (Initializer.prepare_inSpace k s) + refine ⟨4 * c, ?_⟩ + intro N b σ q + have h := hc (N, b, σ, q) + simp only [Initializer.encState, Initializer.encArgs, Initializer.prepare, + length_encPair, length_encNat_eq_size] at h ⊢ + nlinarith + +/-- Binary selected interpolation metadata retains the assignment capacity only once. -/ +theorem selected_metadataSpace_le (N b σ q d B : ℕ) (hd : 0 < d) (hB : 0 < B) : + ConcreteSupplier.metadataSpace N b σ q (chunkWidth d B) (2 ^ chunkWidth d B) + ((2 ^ chunkWidth d B) ^ (d * chunkCount d B)) (fieldModulus d B) d + (chunkCount d B) ≤ + 100 * (d + 1) * (N.size + b.size + σ.size + q.size + B + chunkWidth d B + d.size + 1) := by + have hr := chunkCount_le hd hB + have hrsize := Nat.size_le_size hr + have hBsize := length_encNat_le_self B + have hmSize := length_encNat_le_self (chunkWidth d B) + have hstride := Nat.size_mul_le (chunkCount d B) (chunkWidth d B) + have hpoint := Nat.size_mul_le d (chunkCount d B) + have hp := fieldModulus_size_le hd hB + have hlimit := assignment_limit_size_le hd hB + simp only [length_encNat_eq_size] at hBsize hmSize + simp only [ConcreteSupplier.metadataSpace, QueryGate.encParams, Interpolation.encContext, + encNat3, length_encPair, length_encNat_eq_size, Nat.size_pow] + have hempty : (encList encNat []).length = 0 := rfl + rw [hempty] + nlinarith + +private theorem context_le_metadataSpace (N b σ q m alphabet limit p d r : ℕ) : + (ConcreteSupplier.encContext (N, b, σ, q, m, alphabet, limit)).length ≤ + ConcreteSupplier.metadataSpace N b σ q m alphabet limit p d r := by + simp only [ConcreteSupplier.encContext, ConcreteSupplier.metadataSpace, QueryGate.encParams, + Interpolation.encContext, encNat3, length_encPair] + omega + +private theorem log2_le_size (N : ℕ) : Nat.log2 N ≤ N.size := by + by_cases hz : N = 0 + · subst N; decide + · exact ((Nat.log2_lt hz).mpr (Nat.lt_size_self N)).le + +/-- One selected candidate has linear bank storage and logarithmic storage per tree level. -/ +theorem initialized_allowance_le (k s : ℕ) (Q : Type) : ∃ C, ∀ N b σ q : ℕ, 0 < b → + InitializedEvaluator.allowance k s Q N b σ q ≤ + C * (b + q * (b.size + 1) + N.size + σ.size + 1) := by + let d := k * 3 + 1 + let A := widthConstant k s + let M := d.size + A + let H := 100 * (d + 1) * (A + M + d.size + 2) + let P := 17 * d * (A + M) + let row := Fintype.card (Option (Fin k)) * (2 * Nat.card SignType + 1) + let replay := 4 * M + 3 * A + 8 + 2 * row + 10 + + 1024 * (k + 1) * (Nat.card (Option Q) + Nat.card OutputTrack + 3) + let reply := 48 * (H + 2 * P + 1) + 16 * A + 3 * P + replay + 14 + obtain ⟨ci, hci⟩ := prepared_metadata_le k s + obtain ⟨cr, hcr⟩ := selected_queryStateBound_le k s + let init := 16 * ci + 16 + 192 * (d + 1) * (2 * A + d.size) + refine ⟨init + 4 * H + reply + cr + 2, ?_⟩ + intro N b σ q hb + let B := Encoding.width k s b + let m := chunkWidth d B + let r := chunkCount d B + let p := fieldModulus d B + let alphabet := 2 ^ m + let limit := alphabet ^ (d * r) + let R := b + q * (b.size + 1) + N.size + σ.size + 1 + have hd : 0 < d := by dsimp only [d]; omega + have hB : 0 < B := Encoding.width_pos k s b + have hR : 0 < R := by dsimp only [R]; omega + have hbs : b.size ≤ b := by simpa only [length_encNat_eq_size] using length_encNat_le_self b + have hqs : q.size ≤ q := by simpa only [length_encNat_eq_size] using length_encNat_le_self q + have hqmul : q ≤ q * (b.size + 1) := Nat.le_mul_of_pos_right _ (by omega) + have hqR : q ≤ R := by dsimp only [R]; omega + have hargs : N.size + b.size + σ.size + q.size + 1 ≤ R := by + dsimp only [R]; omega + have hbR : b ≤ R := by dsimp only [R]; omega + have hNR : N.size ≤ R := by dsimp only [R]; omega + have hqsizeR : q.size ≤ R := hqs.trans hqR + have hBR : B ≤ A * R := (width_le_mul k s hb).trans (Nat.mul_le_mul_left A hbR) + have hmR : m ≤ M * R := by + have hm := chunkWidth_le d B + have hc := Nat.le_mul_of_pos_right d.size hR + dsimp only [M] + nlinarith only [hm, hc, hBR] + have hrB : r ≤ B := chunkCount_le hd hB + have hrs : r.size ≤ B := by + have h := length_encNat_le_self r + rw [length_encNat_eq_size] at h + omega + have hms : m.size ≤ m := by simpa only [length_encNat_eq_size] using length_encNat_le_self m + have hds : d.size ≤ d.size * R := Nat.le_mul_of_pos_right _ hR + have hR0 : N.size + b.size + σ.size + q.size + B + m + d.size + 1 ≤ + (A + M + d.size + 2) * R := by nlinarith only [hargs, hBR, hmR, hds] + have hmeta0 := selected_metadataSpace_le N b σ q d B hd hB + have hmeta1 := Nat.mul_le_mul_left (100 * (d + 1)) hR0 + have hmeta : ConcreteSupplier.metadataSpace N b σ q m alphabet limit p d r ≤ H * R := by + calc + _ ≤ 100 * (d + 1) * + (N.size + b.size + σ.size + q.size + B + m + d.size + 1) := hmeta0 + _ ≤ 100 * (d + 1) * ((A + M + d.size + 2) * R) := hmeta1 + _ = H * R := by dsimp only [H]; ring + have hcontext : (ConcreteSupplier.encContext (N, b, σ, q, m, alphabet, limit)).length ≤ H * R := + (context_le_metadataSpace _ _ _ _ _ _ _ _ _ _).trans hmeta + have hpoint0 := selected_point_cost_le hd hB + have hsum : B + m ≤ (A + M) * R := by nlinarith only [hBR, hmR] + have hpoint1 := Nat.mul_le_mul_left (17 * d) hsum + have hpoint : d * r * (2 * p.size + 1) ≤ P * R := by + calc + _ ≤ 17 * d * (B + m) := hpoint0 + _ ≤ 17 * d * ((A + M) * R) := hpoint1 + _ = P * R := by dsimp only [P]; ring + have hcell : r * (2 * p.size + 1) ≤ P * R := by + calc + _ ≤ d * (r * (2 * p.size + 1)) := Nat.le_mul_of_pos_left _ hd + _ = d * r * (2 * p.size + 1) := by ring + _ ≤ P * R := hpoint + have hlog := log2_le_size N + have hsmall : b + Nat.log2 N + Nat.card (Option Q) + Nat.card OutputTrack + 1 ≤ + (Nat.card (Option Q) + Nat.card OutputTrack + 3) * R := by + have hc := Nat.le_mul_of_pos_right + (Nat.card (Option Q) + Nat.card OutputTrack + 1) hR + have hbN : b + N.size ≤ R := by dsimp only [R]; omega + nlinarith only [hc, hbN, hlog] + have hreplay0 := Nat.mul_le_mul_left (1024 * (k + 1)) hsmall + have hreplay : ConcreteSupplier.replaySpace k s Q N b q m r ≤ replay * R := by + simp only [ConcreteSupplier.replaySpace, SupplierChunk.encContext, length_encPair, + length_encNat_eq_size] + change 2 * (2 * m.size + 1 + r.size) + 1 + (B + 1) + + (2 * (4 * q.size + row + 2) + 2 + + 1024 * (k + 1) * + (b + Nat.log2 N + Nat.card (Option Q) + Nat.card OutputTrack + 1)) ≤ replay * R + dsimp only [replay] + nlinarith only [hms, hrs, hmR, hBR, hqsizeR, hreplay0, hR] + have hreply : ConcreteSupplier.replySpace k s Q N b σ q m alphabet limit p d r ≤ reply * R := by + simp only [ConcreteSupplier.replySpace, ConcreteSupplier.gateSpace, length_encNat_eq_size] + dsimp only [reply] + nlinarith only [hmeta, hpoint, hcell, hreplay, hrs, hBR, hR] + have hmetadata := hci N b σ q + have hmetadataR : (Initializer.encMetadata + ((Initializer.prepare k s (N, b, σ, q)).1)).length ≤ ci * R := + hmetadata.trans (Nat.mul_le_mul_left ci hargs) + have hinit0 := Initializer.space_prepare_le k s N b σ q + have hinit1 := Nat.mul_le_mul_left (192 * (d + 1)) + (show 2 * B + d.size ≤ (2 * A + d.size) * R by + nlinarith only [hBR, hds]) + have hinit : Initializer.space k ((Initializer.prepare k s (N, b, σ, q)).1) ≤ init * R := by + change Initializer.space k ((Initializer.prepare k s (N, b, σ, q)).1) ≤ + 16 * ((Initializer.encMetadata ((Initializer.prepare k s (N, b, σ, q)).1)).length + 1) + + 192 * (d + 1) * (2 * B + d.size) at hinit0 + dsimp only [init] + nlinarith only [hinit0, hinit1, hmetadataR, hR] + have hraw := hcr (ConcreteSupplier.encContext (N, b, σ, q, m, alphabet, limit)).length + (ConcreteSupplier.replySpace k s Q N b σ q m alphabet limit p d r) b q hb + change RawController.Query.queryStateBound + (ConcreteSupplier.encContext (N, b, σ, q, m, alphabet, limit)).length p d r q + (ConcreteSupplier.replySpace k s Q N b σ q m alphabet limit p d r) ≤ + 4 * (ConcreteSupplier.encContext (N, b, σ, q, m, alphabet, limit)).length + + ConcreteSupplier.replySpace k s Q N b σ q m alphabet limit p d r + + cr * (b + q * (b.size + 1) + 1) at hraw + have hraw1 := Nat.mul_le_mul_left cr + (show b + q * (b.size + 1) + 1 ≤ R by dsimp only [R]; omega) + change Initializer.space k ((Initializer.prepare k s (N, b, σ, q)).1) + + RawController.Query.queryStateBound + (ConcreteSupplier.encContext (N, b, σ, q, m, alphabet, limit)).length p d r q + (ConcreteSupplier.replySpace k s Q N b σ q m alphabet limit p d r) + 2 ≤ _ + change _ ≤ (init + 4 * H + reply + cr + 2) * R + nlinarith only [hinit, hraw, hraw1, hcontext, hreply, hR] + +/-- The complete sequential search has the fixed-machine square-root-simulation storage shape. -/ +theorem space_le (k s : ℕ) (Q : Type) : ∃ C, ∀ N b q : ℕ, 0 < b → + space k s Q N b q ≤ C * (b + q * (b.size + 1) + N.size + 1) := by + obtain ⟨c, hc⟩ := initialized_allowance_le k s Q + refine ⟨c * (2 * (k + 1) + 2) + 64 * (k + 2) + 2, ?_⟩ + intro N b q hb + let R := b + q * (b.size + 1) + N.size + 1 + have hbs : b.size ≤ b := by simpa only [length_encNat_eq_size] using length_encNat_le_self b + have hqs : q.size ≤ q := by simpa only [length_encNat_eq_size] using length_encNat_le_self q + have hq : q ≤ q * (b.size + 1) := Nat.le_mul_of_pos_right _ (by omega) + have hcounter := counter_encoding_le k q (3 ^ ((k + 1) * q)) le_rfl + rw [length_encNat_eq_size] at hcounter + have hbound := hc N b (3 ^ ((k + 1) * q)) q hb + have harg : b + q * (b.size + 1) + N.size + (3 ^ ((k + 1) * q)).size + 1 ≤ + (2 * (k + 1) + 2) * R := by dsimp only [R]; nlinarith + have hmul := Nat.mul_le_mul_left c harg + have hcaller : 2 * callerSpace k (N, b, q) + 2 ≤ (64 * (k + 2) + 2) * R := by + simp only [callerSpace, encArgs, length_encPair, length_encNat_eq_size] + dsimp only [R] + nlinarith + have hinit := hbound.trans hmul + change 2 * callerSpace k (N, b, q) + + InitializedEvaluator.allowance k s Q N b (3 ^ ((k + 1) * q)) q + 2 ≤ _ + calc + _ ≤ (64 * (k + 2) + 2) * R + c * ((2 * (k + 1) + 2) * R) := by omega + _ = _ := by dsimp only [R]; ring + +end Turing.MultiTapePTM.BlockComputation.ScheduleSearch diff --git a/ComplexityTests/Williams.lean b/ComplexityTests/Williams.lean new file mode 100644 index 0000000000..2b22ecdcd6 --- /dev/null +++ b/ComplexityTests/Williams.lean @@ -0,0 +1,67 @@ +/- +Copyright (c) 2026 Christian Reitwiessner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Reitwiessner +-/ + +module +-- upstream: local-only (tests of Williams simulation and quantitative bounds) + +public import Complexity.Computability.Complexity.Classes.TimeSpace.Williams +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Streaming + +/-! +# Square-root-space theorem clients + +The parity language exercises the final inclusion with a concrete linear-time source and both +accepting and rejecting words. A lifted instance type checks the arbitrary-universe encoding +bridge on a satisfiable constant-machine example. Capacity examples exercise exact binary +storage rather than the exponentially many assignments. +-/ + +public section + +namespace ComplexityTests.Williams + +open Cslib.Complexity +open _root_.Turing.MultiTapeTM +open _root_.Turing.MultiTapePTM.BlockComputation +open Cslib.TreeEval.Ary.Packed + +/-- Parity of the number of true input bits. -/ +def oddOnes (word : List Bool) : Bool := word.foldl (fun b x => b ^^ x) false + +example : oddOnes [true, false, true, true] = true ∧ oddOnes [true, true] = false := by decide + +private theorem oddOnes_mem_DTIME : {w | oddOnes w = true} ∈ DTIME encBits id := + setOf_mem_DTIME_of_computableInO + (ComputableTidilyInO.foldl_bits_finite encBool (fun b x => b ^^ x) false).computableInO + (fun _ => le_rfl) + +example : {w | oddOnes w = true} ∈ + DSPACE encBits (fun n => Nat.sqrt (n * (Nat.log2 n + 1))) := + DTIME_subset_DSPACE_sqrt encBits id (fun _ => le_rfl) oddOnes_mem_DTIME + +/-- This instance type deliberately lives above the machine-state universe. -/ +private def liftedEncoding : ULift.{1} (List Bool) ↪ List Bool := + ⟨ULift.down, ULift.down_injective⟩ + +private theorem lifted_univ_mem_DTIME : (Set.univ : Set (ULift.{1} (List Bool))) ∈ + DTIME liftedEncoding id := by + obtain ⟨c, h⟩ := computableInTimeAndSpace_of_const (encIn := liftedEncoding) + (encOut := encBool) true + exact ⟨0, c, fun _ => 0, decidableInTimeAndSpace_iff.mpr + ⟨Function.const (ULift.{1} (List Bool)) true, + by simpa only [Nat.zero_mul, Nat.zero_add] using h, + fun _ => by simp⟩⟩ + +example : (Set.univ : Set (ULift.{1} (List Bool))) ∈ + DSPACE liftedEncoding (fun n => Nat.sqrt (n * (Nat.log2 n + 1))) := + DTIME_subset_DSPACE_sqrt liftedEncoding id (fun _ => le_rfl) lifted_univ_mem_DTIME + +example : (((2 ^ chunkWidth 1 6) ^ (1 * chunkCount 1 6)) : ℕ).size = 7 := by decide +example : (((2 ^ chunkWidth 3 17) ^ (3 * chunkCount 3 17)) : ℕ).size = 55 := by decide +example : BalancedParameters.blockWidth 1024 + BalancedParameters.horizon 1024 * + ((BalancedParameters.blockWidth 1024).size + 1) = 200 := by decide + +end ComplexityTests.Williams diff --git a/ROADMAP.md b/ROADMAP.md index aa6b7d22e6..778c965c6d 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -195,10 +195,12 @@ index links to canonical definitions rather than keeping a second set. logspace reduction. - **Williams's `TIME[t] ⊆ SPACE[√(t log t)]`** ([#888]): implicit Tree Evaluation with the fan-in-`d` space bound and the native block-tree evaluator are developed in the stacked - work tracked by the issue. The fixed driver scans its original input, searches schedules - and increases balanced time guesses without a time-constructibility premise. Remaining: - assemble its physical-space allowances into `O(√(t log t))`, prove the standard class - inclusion, and finish review/integration. For Cook–Mertz itself: malformed-input handling + work tracked by the issue. The full `DTIME_subset_DSPACE_sqrt` inclusion is proved in + `Complexity/Computability/Complexity/Classes/TimeSpace/Williams.lean` for arbitrary + injective input encodings and every `t(n) ≥ n`, without time constructibility. Its fixed + driver scans the original input, searches schedules and increases balanced time guesses. + The stacked development still needs final CI and integration; the broad issue remains + open until it lands. For Cook–Mertz itself: malformed-input handling and the refined `O(ℓ + H log(ℓ + 2))` parameter bound. - Alternating time and space classes and bounded-alternation characterizations of PH; sharp bounds for sublinear space, distinct from polynomial-space simulation. From 9d31d5ce4bc9df574039db63002e5407c99a0afc Mon Sep 17 00:00:00 2001 From: chris-ai1 Date: Sun, 1 Nov 2026 06:10:58 +0000 Subject: [PATCH 36/36] doc(results): register Williams square-root-space theorem --- ComplexityResults/Entries.lean | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ComplexityResults/Entries.lean b/ComplexityResults/Entries.lean index 821ead1aae..5f40e5cd5c 100644 --- a/ComplexityResults/Entries.lean +++ b/ComplexityResults/Entries.lean @@ -9,6 +9,7 @@ import ComplexityResults.Registry import Complexity.Computability.Complexity.Classes.LogSpace.FirstOrder import Complexity.Computability.Complexity.Classes.LogSpace.PolyTime import Complexity.Computability.Complexity.Classes.SharpP.Lowness +import Complexity.Computability.Complexity.Classes.TimeSpace.Williams import Complexity.Computability.Complexity.FineGrained.OrthogonalVectors import Complexity.Computability.Complexity.FineGrained.PPZ import Complexity.Computability.Complexity.FineGrained.Schoening @@ -145,3 +146,17 @@ register_result "native-sparsification" where encCNF and polynomial encoded-length overhead; no new sharp sparsification \ constant." +register_result "williams-square-root-space" where + title := "Williams's square-root-space simulation of deterministic time" + topic := "space complexity" + sources := [ + { paper := Williams2025, theorem := "Theorem 1.1", + locator := "arXiv:2502.17779v1, Section 3" } + ] + theorems := [ + Cslib.Complexity.DTIME_subset_DSPACE_sqrt + ] + scope := "For every time bound t(n) >= n and arbitrary injective input encoding, \ + DTIME(t) is contained in DSPACE(Nat.sqrt(t * (Nat.log2(t) + 1))). Finite binary \ + multitape machines, read-only input and visited-work-cell space; classes absorb \ + affine constants. No time-constructibility or monotonicity hypothesis."