From 6094ec9cc975f8e361f8ea49146ac4bea36aa115 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 05:13:43 -0400 Subject: [PATCH 01/73] feat(exactTriangle): relate lopsided queries and matrix products Adapt the finite counting interface and signed digit expansion from anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5 for milestone #1438. This is a mathematical interface; native preprocessing, fast solvers and full composition remain under #1394. --- Complexity.lean | 2 + .../Algorithms/ExactTriangle/Lopsided.lean | 265 ++++++++++++++++++ .../Foundations/Data/Matrix/IntegerBits.lean | 112 ++++++++ ComplexityTests/Lopsided.lean | 163 +++++++++++ NOTICE | 5 +- ROADMAP.md | 8 +- UPSTREAM.md | 11 + 7 files changed, 561 insertions(+), 5 deletions(-) create mode 100644 Complexity/Algorithms/ExactTriangle/Lopsided.lean create mode 100644 Complexity/Foundations/Data/Matrix/IntegerBits.lean create mode 100644 ComplexityTests/Lopsided.lean diff --git a/Complexity.lean b/Complexity.lean index bcc3d2303..d661ead1c 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1,6 +1,7 @@ module -- shake: keep-all --deprecated_module: ignore public import Complexity.Algorithms.ExactTriangle.Defs +public import Complexity.Algorithms.ExactTriangle.Lopsided public import Complexity.Algorithms.Lean.FusionTree.Capacity public import Complexity.Algorithms.Lean.FusionTree.Compression public import Complexity.Algorithms.Lean.FusionTree.Correction @@ -2084,6 +2085,7 @@ public import Complexity.Foundations.Data.List.OfFn public import Complexity.Foundations.Data.List.Range public import Complexity.Foundations.Data.List.SelectDistinct public import Complexity.Foundations.Data.Matrix.Binary +public import Complexity.Foundations.Data.Matrix.IntegerBits public import Complexity.Foundations.Data.Matrix.Permanent public import Complexity.Foundations.Data.Matrix.Reindex public import Complexity.Foundations.Data.Matrix.TotallyRegular diff --git a/Complexity/Algorithms/ExactTriangle/Lopsided.lean b/Complexity/Algorithms/ExactTriangle/Lopsided.lean new file mode 100644 index 000000000..d52e77943 --- /dev/null +++ b/Complexity/Algorithms/ExactTriangle/Lopsided.lean @@ -0,0 +1,265 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Foundations.Data.Matrix.IntegerBits +public import Mathlib.Data.Fintype.EquivFin +import Mathlib.Tactic.Ring + +/-! +# Finite lopsided triangle queries as matrix products + +Two outer parts have `n` vertices each; the finite middle part embeds into `Fin D`. +The matrices count common neighbors at every outer pair. Only pairs in `W` are graph edges, +so only those counts are triangle counts. Answers outside `W` are unconstrained. + +This mathematical interface gives no native program, preprocessing cost, sparsity bound, +fast solver or subquadratic running time. + +## Main definitions + +* `LopsidedGraph`: two adjacency relations, a finite middle part and queried outer edges. +* `LopsidedGraph.matX`, `LopsidedGraph.matY`: padded biadjacency matrices. +* `LopsidedGraph.signedBitInstance`: four sign choices for each pair of binary digits. + +## Main results + +* `LopsidedGraph.matX_mul_matY_apply`: entries count common middle neighbors. +* `LopsidedGraph.matX_mul_matY_apply_ne_zero_iff`: nonzero entries detect common neighbors. +* `LopsidedGraph.zero_one`: the converse for zero/one matrices. +* `LopsidedGraph.entries_from_counts`: bounded signed products from `4 * β²` counts. + +## References + +Adapted from [Anthropic's MatrixLanguage.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec3/MatrixLanguage.lean) +and the lopsided definitions in [PaperStatements.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/PaperStatements.lean#L798-L852). +The adaptation uses `Set.EqOn`, pointwise signed parts, and direct finite cardinalities. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.ExactTriangle + +/-- An unweighted tripartite graph whose outer parts each have `n` vertices. -/ +structure LopsidedGraph (n : ℕ) where + /-- The middle vertices. -/ + M : Type + /-- The middle part is finite. -/ + [fintypeM : Fintype M] + /-- Edges from the first outer part to the middle. -/ + adjA : Fin n → M → Prop + /-- Edges from the middle to the second outer part. -/ + adjB : M → Fin n → Prop + /-- Queried outer edges. -/ + W : Finset (Fin n × Fin n) + +attribute [instance] LopsidedGraph.fintypeM + +namespace LopsidedGraph + +/-- On a pair in `W`, a common middle neighbor closes a triangle. +For a pair outside `W`, this predicate does not assert an outer edge. -/ +def HasCommonNeighbor {n : ℕ} (I : LopsidedGraph n) (a b : Fin n) : Prop := + ∃ v : I.M, I.adjA a v ∧ I.adjB v b + +open Classical in +/-- Number of common middle neighbors. At pairs in `W` this is the triangle count. -/ +noncomputable def commonNeighborCount {n : ℕ} (I : LopsidedGraph n) (a b : Fin n) : ℕ := + (Finset.univ.filter fun v : I.M => I.adjA a v ∧ I.adjB v b).card + +/-- Two thin matrices describe adjacency where their entries equal one. +The counting theorem `zero_one` requires all entries to be zero or one. -/ +def ofMatrices {n D : ℕ} (X : Matrix (Fin n) (Fin D) ℤ) + (Y : Matrix (Fin D) (Fin n) ℤ) (W : Finset (Fin n × Fin n)) : LopsidedGraph n where + M := Fin D + adjA a v := X a v = 1 + adjB v b := Y v b = 1 + W := W + +open Classical in +/-- The count for a matrix-defined graph uses the ordinary finite middle indices. -/ +theorem commonNeighborCount_ofMatrices {n D : ℕ} (X : Matrix (Fin n) (Fin D) ℤ) + (Y : Matrix (Fin D) (Fin n) ℤ) (W : Finset (Fin n × Fin n)) (a b : Fin n) : + (ofMatrices X Y W).commonNeighborCount a b = + (Finset.univ.filter fun v : Fin D => X a v = 1 ∧ Y v b = 1).card := by + unfold ofMatrices commonNeighborCount + congr 1 + ext v + simp + +open Classical in +/-- The first biadjacency matrix, with zero columns outside the injection range. -/ +noncomputable def matX {n D : ℕ} (I : LopsidedGraph n) (e : I.M ↪ Fin D) : + Matrix (Fin n) (Fin D) ℤ := + Matrix.of fun a j => if ∃ v : I.M, e v = j ∧ I.adjA a v then 1 else 0 + +open Classical in +/-- The second biadjacency matrix, with zero rows outside the injection range. -/ +noncomputable def matY {n D : ℕ} (I : LopsidedGraph n) (e : I.M ↪ Fin D) : + Matrix (Fin D) (Fin n) ℤ := + Matrix.of fun j b => if ∃ v : I.M, e v = j ∧ I.adjB v b then 1 else 0 + +variable {n D : ℕ} (I : LopsidedGraph n) (e : I.M ↪ Fin D) + +/-- Every first biadjacency entry is zero or one. -/ +theorem matX_eq_zero_or_one (a : Fin n) (j : Fin D) : + I.matX e a j = 0 ∨ I.matX e a j = 1 := by + classical + simp only [matX, Matrix.of_apply] + split_ifs <;> simp + +/-- Every second biadjacency entry is zero or one. -/ +theorem matY_eq_zero_or_one (j : Fin D) (b : Fin n) : + I.matY e j b = 0 ∨ I.matY e j b = 1 := by + classical + simp only [matY, Matrix.of_apply] + split_ifs <;> simp + +/-- A middle part of cardinality at most `D` admits the required numbering. -/ +theorem nonempty_embedding (h : Fintype.card I.M ≤ D) : Nonempty (I.M ↪ Fin D) := + Function.Embedding.nonempty_of_card_le (by simpa using h) + +open Classical in +/-- At a middle vertex, the first matrix records its adjacency. -/ +theorem matX_apply_emb (a : Fin n) (v : I.M) : + I.matX e a (e v) = if I.adjA a v then 1 else 0 := by + simp [matX] + +open Classical in +/-- At a middle vertex, the second matrix records its adjacency. -/ +theorem matY_apply_emb (v : I.M) (b : Fin n) : + I.matY e (e v) b = if I.adjB v b then 1 else 0 := by + simp [matY] + +/-- An unused column is zero. -/ +theorem matX_apply_of_notMem_range (a : Fin n) {j : Fin D} (hj : j ∉ Set.range e) : + I.matX e a j = 0 := + ite_eq_right fun ⟨v, hv, _⟩ => hj ⟨v, hv⟩ + +/-- An unused row is zero. -/ +theorem matY_apply_of_notMem_range {j : Fin D} (hj : j ∉ Set.range e) (b : Fin n) : + I.matY e j b = 0 := + ite_eq_right fun ⟨v, hv, _⟩ => hj ⟨v, hv⟩ + +/-- A pair has a common neighbor if and only if its count is nonzero. -/ +theorem commonNeighborCount_ne_zero_iff (a b : Fin n) : + I.commonNeighborCount a b ≠ 0 ↔ I.HasCommonNeighbor a b := by + classical + rw [commonNeighborCount, Finset.card_ne_zero, Finset.filter_nonempty_iff] + simp [HasCommonNeighbor] + +/-- Every product entry counts common middle neighbors, independently of the numbering. -/ +theorem matX_mul_matY_apply (a b : Fin n) : + (I.matX e * I.matY e) a b = (I.commonNeighborCount a b : ℤ) := by + classical + rw [Matrix.mul_apply, commonNeighborCount, Finset.card_filter, Nat.cast_sum] + -- Only the columns of the middle vertices matter. + rw [← Finset.sum_subset (Finset.subset_univ (Finset.univ.map e)) fun j _ hj => by + rw [I.matX_apply_of_notMem_range e a (by simpa using hj), zero_mul], Finset.sum_map] + refine Finset.sum_congr rfl fun v _ => ?_ + rw [I.matX_apply_emb, I.matY_apply_emb] + by_cases hA : I.adjA a v <;> by_cases hB : I.adjB v b <;> simp [hA, hB] + +/-- A nonzero product entry detects a common middle neighbor. -/ +theorem matX_mul_matY_apply_ne_zero_iff (a b : Fin n) : + (I.matX e * I.matY e) a b ≠ 0 ↔ I.HasCommonNeighbor a b := by + rw [matX_mul_matY_apply, Ne, Nat.cast_eq_zero] + exact I.commonNeighborCount_ne_zero_iff a b + +/-- On queried outer edges, correct natural counts are exactly the integer product entries. +Ordinary `Set.EqOn` leaves all unqueried entries unconstrained. -/ +theorem counting_eqOn_iff (ans : Fin n × Fin n → ℕ) : + Set.EqOn ans (fun q => I.commonNeighborCount q.1 q.2) (I.W : Set _) ↔ + Set.EqOn (fun q => (ans q : ℤ)) (fun q => (I.matX e * I.matY e) q.1 q.2) + (I.W : Set _) := by + simp only [Set.EqOn, matX_mul_matY_apply, Nat.cast_inj] + +/-- Counts on queried edges provide their detection answers. -/ +theorem detection_eqOn_of_counting (ans : Fin n × Fin n → ℕ) + (h : Set.EqOn ans (fun q => I.commonNeighborCount q.1 q.2) (I.W : Set _)) : + Set.EqOn (fun q => ans q ≠ 0) (fun q => I.HasCommonNeighbor q.1 q.2) (I.W : Set _) := by + intro q hq + change (ans q ≠ 0) = I.HasCommonNeighbor q.1 q.2 + apply propext + rw [h hq, commonNeighborCount_ne_zero_iff] + +/-- For zero/one matrices, each product entry is the count in the graph they describe. -/ +theorem zero_one {n D : ℕ} (X : Matrix (Fin n) (Fin D) ℤ) (Y : Matrix (Fin D) (Fin n) ℤ) + (W : Finset (Fin n × Fin n)) (hX : ∀ a j, X a j = 0 ∨ X a j = 1) + (hY : ∀ j b, Y j b = 0 ∨ Y j b = 1) (a b : Fin n) : + (X * Y) a b = ((ofMatrices X Y W).commonNeighborCount a b : ℤ) := by + classical + rw [Matrix.mul_apply, commonNeighborCount, Finset.card_filter, Nat.cast_sum] + refine Finset.sum_congr rfl fun j _ => ?_ + rcases hX a j with hx | hx <;> rcases hY j b with hy | hy <;> + simp [ofMatrices, hx, hy] + +/-- One of four sign choices for a pair of binary digits; `true` selects the negative part. +Every instance has exactly `D` middle vertices and the original queried edges `W`. -/ +def signedBitInstance (X : Matrix (Fin n) (Fin D) ℤ) (Y : Matrix (Fin D) (Fin n) ℤ) + (W : Finset (Fin n × Fin n)) (negativeX negativeY : Bool) (i j : ℕ) : LopsidedGraph n := + ofMatrices (IntMatrix.bit (if negativeX then (X.map negPart) else (X.map posPart)) i) + (IntMatrix.bit (if negativeY then (Y.map negPart) else (Y.map posPart)) j) W + +/-- The finite family of sign/digit instances for a magnitude width `β`. +Different slots may describe the same graph. -/ +def signedBitInstances (X : Matrix (Fin n) (Fin D) ℤ) (Y : Matrix (Fin D) (Fin n) ℤ) + (W : Finset (Fin n × Fin n)) (β : ℕ) : + (Bool × Bool × Fin β × Fin β) → LopsidedGraph n := + fun q => signedBitInstance X Y W q.1 q.2.1 q.2.2.1.val q.2.2.2.val + +/-- The indexed family has `4 * β²` slots, allowing repeated or zero instances. -/ +theorem card_signedBit_indices (β : ℕ) : + Fintype.card (Bool × Bool × Fin β × Fin β) = 4 * β ^ 2 := by + simp [pow_two, Nat.mul_assoc, Nat.mul_left_comm, Nat.mul_comm] + +/-- Each nonnegative product entry is a weighted sum of digit-graph counts. -/ +private theorem mul_apply_eq_sum_counts {n D : ℕ} (X : Matrix (Fin n) (Fin D) ℤ) + (Y : Matrix (Fin D) (Fin n) ℤ) (W : Finset (Fin n × Fin n)) (β : ℕ) + (hX : ∀ a j, 0 ≤ X a j ∧ X a j < 2 ^ β) (hY : ∀ j b, 0 ≤ Y j b ∧ Y j b < 2 ^ β) (a b : Fin n) : + (X * Y) a b = ∑ i ∈ Finset.range β, ∑ j ∈ Finset.range β, (2 : ℤ) ^ (i + j) * + ((ofMatrices (IntMatrix.bit X i) (IntMatrix.bit Y j) W).commonNeighborCount a b : ℤ) := by + rw [IntMatrix.product X Y β hX hY, Matrix.sum_apply] + refine Finset.sum_congr rfl fun i _ => ?_ + rw [Matrix.sum_apply] + refine Finset.sum_congr rfl fun j _ => ?_ + rw [Matrix.smul_apply, smul_eq_mul, zero_one _ _ W (IntMatrix.bit_eq_zero_or_one X i) + (IntMatrix.bit_eq_zero_or_one Y j)] + +/-- Signed bounded product entries are weighted sums of four count instances per digit +pair, with signs `+, −, −, +`. Only counts at the same queried edge are needed. The absolute +bounds are strict; `β = 0` therefore permits only zero entries. -/ +theorem entries_from_counts {n D : ℕ} (X : Matrix (Fin n) (Fin D) ℤ) + (Y : Matrix (Fin D) (Fin n) ℤ) (W : Finset (Fin n × Fin n)) (β : ℕ) + (hX : ∀ a j, |X a j| < 2 ^ β) (hY : ∀ j b, |Y j b| < 2 ^ β) (a b : Fin n) : + (X * Y) a b = ∑ i ∈ Finset.range β, ∑ j ∈ Finset.range β, (2 : ℤ) ^ (i + j) * + (((signedBitInstance X Y W false false i j).commonNeighborCount + a b : ℤ) + - ((signedBitInstance X Y W false true i j).commonNeighborCount + a b : ℤ) + - ((signedBitInstance X Y W true false i j).commonNeighborCount + a b : ℤ) + + ((signedBitInstance X Y W true true i j).commonNeighborCount + a b : ℤ)) := by + simp only [signedBitInstance, Bool.false_eq_true, ↓reduceIte] + obtain ⟨hXpos, hXneg⟩ := IntMatrix.parts_lt X hX + obtain ⟨hYpos, hYneg⟩ := IntMatrix.parts_lt Y hY + -- `XY` is a signed sum of the four products of a part of `X` and a part of `Y`. + conv_lhs => rw [IntMatrix.parts X, IntMatrix.parts Y] + rw [Matrix.sub_mul, Matrix.mul_sub, Matrix.mul_sub] + simp only [Matrix.sub_apply] + rw [mul_apply_eq_sum_counts _ _ W β hXpos hYpos, + mul_apply_eq_sum_counts _ _ W β hXpos hYneg, + mul_apply_eq_sum_counts _ _ W β hXneg hYpos, + mul_apply_eq_sum_counts _ _ W β hXneg hYneg] + simp only [mul_sub, mul_add, Finset.sum_sub_distrib, Finset.sum_add_distrib] + ring + +end LopsidedGraph + +end Cslib.Algorithms.ExactTriangle diff --git a/Complexity/Foundations/Data/Matrix/IntegerBits.lean b/Complexity/Foundations/Data/Matrix/IntegerBits.lean new file mode 100644 index 000000000..5083b77db --- /dev/null +++ b/Complexity/Foundations/Data/Matrix/IntegerBits.lean @@ -0,0 +1,112 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Algebra.Order.Group.PosPart +public import Mathlib.Data.Matrix.Basic +public import Mathlib.Data.Nat.Log +import Mathlib.Tactic.Positivity +import Mathlib.Tactic.Ring + +/-! +# Binary digits of integer matrices + +Finite binary expansions and their products, using Mathlib's pointwise positive and negative +parts. These are algebraic identities, with no algorithm or running-time assertion. + +## Main definitions + +* `IntMatrix.bit`: the binary digit matrix of a given weight. + +## Main results + +* `IntMatrix.binary`: expansion of a nonnegative bounded matrix. +* `IntMatrix.product`: expansion of a product into products of digit matrices. +* `IntMatrix.abs_lt_two_pow_log`: an explicit integer bit bound, including zero. + +## References + +Adapted from [Anthropic's MatrixLanguage.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec3/MatrixLanguage.lean), +`matBit` and `Footnote8.binary/product`. General index types and the integer bit bound are original +adaptation code. +-/ + +@[expose] public section + +namespace Cslib.IntMatrix + +/-- The binary digit of weight `2^i`, entry by entry. For negative entries this is the +Euclidean remainder of the quotient; reconstruction below assumes nonnegative entries. -/ +def bit {m n : Type*} (X : Matrix m n ℤ) (i : ℕ) : Matrix m n ℤ := + Matrix.of fun a j => X a j / 2 ^ i % 2 + +/-- `x mod 2^β` is the sum of the last `β` binary digits of `x`, with their weights. -/ +private theorem emod_two_pow_eq_sum_digits (x : ℤ) (β : ℕ) : + x % 2 ^ β = ∑ i ∈ Finset.range β, 2 ^ i * (x / 2 ^ i % 2) := by + induction β with + | zero => simp + | succ β ih => + have h : x / (2 ^ β * 2) = x / 2 ^ β / 2 := (Int.ediv_ediv_of_nonneg (by positivity)).symm + rw [Finset.sum_range_succ, ← ih, pow_succ, Int.emod_def, Int.emod_def x (2 ^ β), + Int.emod_def (x / 2 ^ β), h] + ring + +/-- A matrix with entries in `[0, 2^β)` is the weighted sum of its first `β` digit matrices. -/ +theorem binary {m n : Type*} (X : Matrix m n ℤ) (β : ℕ) + (hX : ∀ a j, 0 ≤ X a j ∧ X a j < 2 ^ β) : + X = ∑ i ∈ Finset.range β, (2 : ℤ) ^ i • bit X i := by + ext a j + rw [Matrix.sum_apply] + simp only [Matrix.smul_apply, bit, Matrix.of_apply, smul_eq_mul] + rw [← emod_two_pow_eq_sum_digits, Int.emod_eq_of_lt (hX a j).1 (hX a j).2] + +/-- Every digit matrix has entries zero or one, even for signed input. -/ +theorem bit_eq_zero_or_one {m n : Type*} (X : Matrix m n ℤ) (i : ℕ) (a : m) + (j : n) : bit X i a j = 0 ∨ bit X i a j = 1 := by + simp only [bit, Matrix.of_apply] + omega + +/-- Expand a product of nonnegative bounded matrices into products of binary digits. -/ +theorem product {m d n : Type*} [Fintype d] (X : Matrix m d ℤ) (Y : Matrix d n ℤ) + (β : ℕ) (hX : ∀ a j, 0 ≤ X a j ∧ X a j < 2 ^ β) (hY : ∀ j b, 0 ≤ Y j b ∧ Y j b < 2 ^ β) : + X * Y = ∑ i ∈ Finset.range β, ∑ j ∈ Finset.range β, + (2 : ℤ) ^ (i + j) • (bit X i * bit Y j) := by + conv_lhs => rw [binary X β hX, binary Y β hY] + rw [Matrix.sum_mul] + refine Finset.sum_congr rfl fun i _ => ?_ + rw [Matrix.mul_sum] + refine Finset.sum_congr rfl fun j _ => ?_ + rw [Matrix.smul_mul, Matrix.mul_smul, smul_smul, pow_add] + +/-- Entrywise positive and negative parts reconstruct an integer matrix. +`Matrix.map` reuses scalar signed parts without adding an ordering instance to matrices. -/ +theorem parts {m n : Type*} (X : Matrix m n ℤ) : + X = X.map posPart - X.map negPart := by + ext a j + exact (posPart_sub_negPart (X a j)).symm + +/-- Both pointwise signed parts of an absolutely bounded matrix satisfy the same nonnegative +bound. This uses the existing ordered-group operations, without introducing matrix variants. -/ +theorem parts_lt {m n : Type*} (X : Matrix m n ℤ) {β : ℕ} + (hX : ∀ a j, |X a j| < 2 ^ β) : + (∀ a j, 0 ≤ (X.map posPart) a j ∧ (X.map posPart) a j < 2 ^ β) ∧ + ∀ a j, 0 ≤ (X.map negPart) a j ∧ (X.map negPart) a j < 2 ^ β := by + have hpow : (0 : ℤ) < 2 ^ β := by positivity + exact ⟨fun a j => ⟨le_max_right _ _, max_lt ((le_abs_self _).trans_lt (hX a j)) hpow⟩, + fun a j => ⟨le_max_right _ _, max_lt ((neg_le_abs _).trans_lt (hX a j)) hpow⟩⟩ + +/-- An integer bounded in magnitude by `U` needs at most `log₂ U + 1` magnitude bits. +This also holds for `U = 0`; no real logarithms or asymptotic claim are involved. -/ +theorem abs_lt_two_pow_log {U : ℕ} (x : ℤ) (hx : |x| ≤ U) : + |x| < 2 ^ (Nat.log 2 U + 1) := by + have h := Nat.lt_pow_succ_log_self (by decide : 1 < 2) U + have hcast : (U : ℤ) < 2 ^ (Nat.log 2 U + 1) := by exact_mod_cast h + exact hx.trans_lt hcast + +end Cslib.IntMatrix diff --git a/ComplexityTests/Lopsided.lean b/ComplexityTests/Lopsided.lean new file mode 100644 index 000000000..737cbf386 --- /dev/null +++ b/ComplexityTests/Lopsided.lean @@ -0,0 +1,163 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +import Complexity.Algorithms.ExactTriangle.Lopsided +import Mathlib.LinearAlgebra.Matrix.Notation + +/-! Concrete tests for padding, counts, queried answers and signed reconstruction. -/ + +open Cslib.Algorithms.ExactTriangle +open LopsidedGraph + +private abbrev twoNeighbors : LopsidedGraph 2 where + M := Fin 2 + adjA a _ := a = 0 + adjB _ b := b = 1 + W := {(0, 1)} + +private def padded : Fin 2 ↪ Fin 3 := ⟨fun v => ⟨v.val, by omega⟩, + fun _ _ h => Fin.ext (show _ from congrArg (fun v : Fin 3 => v.val) h)⟩ + +example : twoNeighbors.commonNeighborCount 0 1 = 2 := by + norm_num [commonNeighborCount, twoNeighbors, Finset.univ_fin2] + rfl + +example : twoNeighbors.HasCommonNeighbor 0 1 := ⟨0, rfl, rfl⟩ + +example : ¬twoNeighbors.HasCommonNeighbor 1 1 := by + simp [HasCommonNeighbor, twoNeighbors] + +example : twoNeighbors.matX padded 0 2 = 0 ∧ twoNeighbors.matY padded 2 1 = 0 := by + have h : (2 : Fin 3) ∉ Set.range padded := by + rintro ⟨v, hv⟩ + have hv' : v.val = 2 := congrArg (fun w : Fin 3 => w.val) hv + have := v.isLt + omega + exact ⟨matX_apply_of_notMem_range _ _ _ h, matY_apply_of_notMem_range _ _ h _⟩ + +example : (twoNeighbors.matX padded * twoNeighbors.matY padded) 0 1 = 2 := by + rw [matX_mul_matY_apply] + norm_num [commonNeighborCount, twoNeighbors, Finset.univ_fin2] + rfl + +-- Values away from the sole query are deliberately incorrect and still allowed. +example : Set.EqOn (fun q : Fin 2 × Fin 2 => if q = (0, 1) then 2 else 99) + (fun q => twoNeighbors.commonNeighborCount q.1 q.2) (twoNeighbors.W : Set _) := by + intro q hq + have h : q = (0, 1) := by simpa [twoNeighbors] using hq + subst q + norm_num [commonNeighborCount, twoNeighbors, Finset.univ_fin2] + rfl + +private abbrev emptyMiddle : LopsidedGraph 1 where + M := Fin 0 + adjA _ _ := True + adjB _ _ := True + W := {(0, 0)} + +example : emptyMiddle.commonNeighborCount 0 0 = 0 := by + simp [commonNeighborCount, emptyMiddle] + +example : ¬emptyMiddle.HasCommonNeighbor 0 0 := by + simp [HasCommonNeighbor, emptyMiddle] + +example : (emptyMiddle.matX (Function.Embedding.refl _) * + emptyMiddle.matY (Function.Embedding.refl _)) 0 0 = 0 := by + rw [matX_mul_matY_apply] + simp [commonNeighborCount, emptyMiddle] + +private def signedX : Matrix (Fin 1) (Fin 2) ℤ := !![-3, 2] +private def signedY : Matrix (Fin 2) (Fin 1) ℤ := !![2; -1] + +example : (signedX * signedY) 0 0 = -8 := by + norm_num [signedX, signedY, Matrix.mul_apply, Fin.sum_univ_two] + +example : (signedBitInstance signedX signedY {(0, 0)} true false 0 1).commonNeighborCount + 0 0 = 1 := by + classical + simp only [signedBitInstance, commonNeighborCount_ofMatrices, ↓reduceIte] + norm_num [signedX, signedY, + Cslib.IntMatrix.bit, posPart_def, negPart_def, Finset.univ_fin2, + Finset.filter_insert, Finset.filter_singleton] + +example : (signedBitInstance signedX signedY {(0, 0)} false true 1 0).commonNeighborCount + 0 0 = 1 := by + classical + simp only [signedBitInstance, commonNeighborCount_ofMatrices, ↓reduceIte] + norm_num [signedX, signedY, + Cslib.IntMatrix.bit, posPart_def, negPart_def, Finset.univ_fin2, + Finset.filter_insert, Finset.filter_singleton] + +-- Apply the signed reconstruction theorem to an actual product, including all four signs. +example : (∑ i ∈ Finset.range 2, ∑ j ∈ Finset.range 2, (2 : ℤ) ^ (i + j) * + (((signedBitInstance signedX signedY {(0, 0)} false false i j).commonNeighborCount 0 0 : ℤ) + - ((signedBitInstance signedX signedY {(0, 0)} false true i j).commonNeighborCount 0 0 : ℤ) + - ((signedBitInstance signedX signedY {(0, 0)} true false i j).commonNeighborCount 0 0 : ℤ) + + ((signedBitInstance signedX signedY {(0, 0)} true true i j).commonNeighborCount 0 0 : ℤ))) + = -8 := by + rw [← entries_from_counts signedX signedY {(0, 0)} 2 + (by intro a j; fin_cases a; fin_cases j <;> norm_num [signedX]) + (by intro j b; fin_cases j <;> fin_cases b <;> norm_num [signedY])] + norm_num [signedX, signedY, Matrix.mul_apply, Fin.sum_univ_two] + +example : Cslib.IntMatrix.bit (!![-1, 0, 3] : Matrix (Fin 1) (Fin 3) ℤ) 1 0 0 = 1 := by + norm_num [Cslib.IntMatrix.bit] + +example : |(-7 : ℤ)| < 2 ^ (Nat.log 2 7 + 1) := by + exact Cslib.IntMatrix.abs_lt_two_pow_log (-7) (by norm_num) + +example : |(0 : ℤ)| < 2 ^ (Nat.log 2 0 + 1) := by + exact Cslib.IntMatrix.abs_lt_two_pow_log 0 (by norm_num) + +example : (signedX * (-signedY)) 0 0 = 8 := by + norm_num [signedX, signedY, Matrix.mul_apply, Fin.sum_univ_two] + +example : (ofMatrices (!![1, 1] : Matrix (Fin 1) (Fin 2) ℤ) !![1; 1] + {(0, 0)}).commonNeighborCount 0 0 = 2 := by + have h := zero_one (!![1, 1] : Matrix (Fin 1) (Fin 2) ℤ) !![1; 1] {(0, 0)} + (by intro a j; fin_cases a; fin_cases j <;> simp) + (by intro j b; fin_cases j <;> fin_cases b <;> simp) 0 0 + norm_num [Matrix.mul_apply, Fin.sum_univ_two] at h + exact_mod_cast h.symm + +example : (signedBitInstance signedX (-signedY) {(0, 0)} true true 0 1).commonNeighborCount + 0 0 = 1 := by + classical + simp only [signedBitInstance, commonNeighborCount_ofMatrices, ↓reduceIte] + norm_num [signedX, signedY, + Cslib.IntMatrix.bit, posPart_def, negPart_def, Finset.univ_fin2, + Finset.filter_insert, Finset.filter_singleton] + +example : (signedBitInstances signedX signedY {(0, 0)} 2 (true, false, 0, 1)).W = + {(0, 0)} := rfl + +example : Fintype.card (Bool × Bool × Fin 2 × Fin 2) = 16 := by decide + +-- Empty outer parts with a genuinely nonempty middle part still give an empty product. +example : (0 : Matrix (Fin 0) (Fin 3) ℤ) * (0 : Matrix (Fin 3) (Fin 0) ℤ) = 0 := by + ext a b + exact Fin.elim0 a + +example : Set.EqOn (fun _ : Fin 0 × Fin 0 => (99 : ℕ)) + (fun q => + (ofMatrices (0 : Matrix (Fin 0) (Fin 3) ℤ) 0 ∅).commonNeighborCount q.1 q.2) + (∅ : Set (Fin 0 × Fin 0)) := by + simp + +-- With beta zero, strict magnitude bounds are satisfied by zero matrices of nonempty shape. +example : ((0 : Matrix (Fin 1) (Fin 2) ℤ) * (0 : Matrix (Fin 2) (Fin 1) ℤ)) 0 0 = 0 := by + have h := entries_from_counts (0 : Matrix (Fin 1) (Fin 2) ℤ) 0 {(0, 0)} 0 + (by simp) (by simp) 0 0 + simpa only [Finset.range_zero, Finset.sum_empty] using h + +-- A queried edge with disjoint middle neighborhoods is a negative triangle query. +example : let I := ofMatrices (!![1, 0] : Matrix (Fin 1) (Fin 2) ℤ) !![0; 1] {(0, 0)} + (0, 0) ∈ I.W ∧ I.commonNeighborCount 0 0 = 0 := by + dsimp only + constructor + · simp [ofMatrices] + · rw [commonNeighborCount_ofMatrices] + norm_num [Finset.univ_fin2, Finset.filter_insert, Finset.filter_singleton] diff --git a/NOTICE b/NOTICE index 81584c241..3ab01a2df 100644 --- a/NOTICE +++ b/NOTICE @@ -1,8 +1,9 @@ The 3SUM/Exact Triangle reductions, their finite counting lemmas and native matrix builder in -Complexity/Algorithms/ExactTriangle/Defs.lean, +Complexity/Algorithms/ExactTriangle/{Defs,Lopsided}.lean, Complexity/Algorithms/ThreeSum/, Complexity/Algorithms/WordRAM/ThreeSum/, -Complexity/Foundations/Data/Finset/Average.lean and +Complexity/Foundations/Data/Finset/Average.lean, +Complexity/Foundations/Data/Matrix/IntegerBits.lean and Complexity/Foundations/NumberTheory/PrimeCounting.lean are adapted from anthropics/formal-math, 3sum-apsp, revision 16a5ce41304fcdaea4e0be7c132e1c361dc26da5. diff --git a/ROADMAP.md b/ROADMAP.md index 8d53f5250..5b3b23001 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -421,8 +421,10 @@ each statement against the numbered result. Organize modules by subject, not cha transfer of Anthropic's modular machine and 3SUM/APSP algorithms; direct 3SUM adaptation ([#1394]) has the Convolution-3SUM/Exact Triangle correctness reduction, a charged native single-instance matrix builder and the complete finite mathematical Chan–He reduction ([#1435]), including - ordinary distinct-position inputs and explicit size bounds. It still needs native Chan–He - preprocessing, the reduction host, the fast Exact Triangle solver and full input/time/width/ + ordinary distinct-position inputs and explicit size bounds. The finite lopsided matrix/counting + interface ([#1438]) adds padding, detection and signed digit reconstruction; it still needs + modular Exact Triangle hashing/false-positive splitting, tensor/sparse-query programs, native + Chan–He preprocessing, the reduction host, the fast Exact Triangle solver and full input/time/width/ asymptotic composition, then the improved Section 4 route to exponent 1.9992; size-based width selection with an input-fit guarantee and explicit overhead; expected-time rules for unbounded almost-surely terminating structured loops; random-tape equivalence and BPP/RP/ZPP simulation @@ -546,6 +548,6 @@ not planned, not as proved. [#1155]: https://github.com/cslib-community/complexitylib/issues/1155 [#1351]: https://github.com/cslib-community/complexitylib/issues/1351 [#1394]: https://github.com/cslib-community/complexitylib/issues/1394 - [#1435]: https://github.com/cslib-community/complexitylib/issues/1435 +[#1438]: https://github.com/cslib-community/complexitylib/issues/1438 [#1449]: https://github.com/cslib-community/complexitylib/issues/1449 diff --git a/UPSTREAM.md b/UPSTREAM.md index f465fed04..87d9cb528 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -165,6 +165,17 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he length, count and magnitude bounds; `scanPairs` counts candidate-prime/element pairs and is not a charged running time. Generic finite-average and prime-counting lemmas live in Foundations. See milestone [#1435](https://github.com/cslib-community/complexitylib/issues/1435). + `Algorithms/ExactTriangle/Lopsided.lean` adapts the finite unweighted lopsided query/counting + interface from the same pin's `Sec3/MatrixLanguage.lean`: arbitrary-injection zero padding, + common-neighbor counts as product entries, detection, the zero/one converse, and signed products + from `4 * β²` count instances with the original queried edges. Its digit identities live in + `Foundations/Data/Matrix/IntegerBits.lean`, reuse Mathlib's pointwise positive/negative parts, + and give an explicit `Nat.log 2 U + 1` magnitude-bit bound including zero. Query answers use + `Set.EqOn`; no additional weighted-triangle predicate or source machine is introduced. + This is the mathematical interface milestone + [#1438](https://github.com/cslib-community/complexitylib/issues/1438), with no sparsity assumption, + preprocessing/time claim or solver. Modular hashing/false-positive splitting and tensor/sparse + query programs remain in #1394, alongside the native and asymptotic composition below. The existing signed and unsigned APIs were reused; no source machine or separate run semantics were copied. The full reduction host, native Chan–He preprocessing, fast Exact Triangle and full input/time/width/asymptotic composition remain in From dec963a5dd581ae8acecb846e7b87b1bf7e5c78e Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 06:02:25 -0400 Subject: [PATCH 02/73] feat(exactTriangle): prove modular counts and the selected-prime bound --- Complexity.lean | 4 + .../Algorithms/ExactTriangle/Hashing.lean | 482 ++++++++++++++++++ .../Algorithms/ExactTriangle/Weights.lean | 61 +++ .../Foundations/NumberTheory/PrimeWindow.lean | 346 +++++++++++++ .../Foundations/RingTheory/CyclicIntRing.lean | 84 +++ ComplexityTests/ExactTriangleHashing.lean | 115 +++++ NOTICE | 9 +- ROADMAP.md | 6 +- UPSTREAM.md | 12 +- 9 files changed, 1113 insertions(+), 6 deletions(-) create mode 100644 Complexity/Algorithms/ExactTriangle/Hashing.lean create mode 100644 Complexity/Algorithms/ExactTriangle/Weights.lean create mode 100644 Complexity/Foundations/NumberTheory/PrimeWindow.lean create mode 100644 Complexity/Foundations/RingTheory/CyclicIntRing.lean create mode 100644 ComplexityTests/ExactTriangleHashing.lean diff --git a/Complexity.lean b/Complexity.lean index d661ead1c..e6ac94554 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1,7 +1,9 @@ module -- shake: keep-all --deprecated_module: ignore public import Complexity.Algorithms.ExactTriangle.Defs +public import Complexity.Algorithms.ExactTriangle.Hashing public import Complexity.Algorithms.ExactTriangle.Lopsided +public import Complexity.Algorithms.ExactTriangle.Weights public import Complexity.Algorithms.Lean.FusionTree.Capacity public import Complexity.Algorithms.Lean.FusionTree.Compression public import Complexity.Algorithms.Lean.FusionTree.Correction @@ -2190,6 +2192,7 @@ public import Complexity.Foundations.NumberTheory.AKS.Introspective public import Complexity.Foundations.NumberTheory.AKS.SmallR public import Complexity.Foundations.NumberTheory.Harmonic public import Complexity.Foundations.NumberTheory.PrimeCounting +public import Complexity.Foundations.NumberTheory.PrimeWindow public import Complexity.Foundations.Order.BooleanSubalgebra public import Complexity.Foundations.Order.Changes public import Complexity.Foundations.Order.UpperLower @@ -2200,6 +2203,7 @@ public import Complexity.Foundations.Probability.SecondMoment public import Complexity.Foundations.Quantum.Measurement public import Complexity.Foundations.Relation.InductiveCounting public import Complexity.Foundations.Relation.RelatesWithinSteps +public import Complexity.Foundations.RingTheory.CyclicIntRing public import Complexity.Foundations.Semantics.Stateful.Amortized public import Complexity.Foundations.Semantics.Stateful.Basic public import Complexity.Foundations.Semantics.Stateful.Composition diff --git a/Complexity/Algorithms/ExactTriangle/Hashing.lean b/Complexity/Algorithms/ExactTriangle/Hashing.lean new file mode 100644 index 000000000..6799e79fc --- /dev/null +++ b/Complexity/Algorithms/ExactTriangle/Hashing.lean @@ -0,0 +1,482 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.ExactTriangle.Weights +public import Complexity.Foundations.NumberTheory.PrimeWindow +public import Complexity.Foundations.RingTheory.CyclicIntRing +public import Mathlib.Data.Matrix.Mul + +/-! +# Modular counts and prime selection for integer Exact Triangle + +Let `S(a,b,c)` denote `weightSum`, `F(p)` the false-positive count and `Z₀` the zero count. A +minimum-count prime in `[sqrt D / 2, sqrt D)` also minimises false positives. For weights +bounded by `n^κ`, with `16 ≤ D ≤ n` and `1 ≤ κ`, a universal constant bounds its false positives +by `C * κ * n^3 * log n / sqrt D`. The prime count used here is proved for every admitted `D`. + +## Main definitions + +* `countZeroMod`, `falsePositiveCount`, `zeroCount`: counts of finite triples. +* `IsSelectedPrime`: a minimum-count prime in the specified window. +* `matP`, `matQ`: cyclic polynomial matrices encoding edge residues. + +## Main results + +* `countZeroMod_eq`: modular count is the sum of false positives and true zeros. +* `coeff_matP_mul_matQ`, `falsePositiveCount_add_zeroCount_eq_sum_coeff`: coefficient + identities. +* `exists_isSelectedPrime`: the minimum exists. +* `Hashing.falsePositiveCount_le_const_mul`: the uniform false-positive bound. + +This is a mathematical reduction step. No native program or charged running-time bound is +asserted. The supplied three matrices retain the existing `HasZeroTriangle` semantics. + +## References + +Direct adaptation of [Anthropic's +Hashing.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec3/Theorem17/Hashing.lean), +with the minimal count/selection definitions extracted from its `PaperStatements.lean`. +Source-derived prose below is paraphrased. The source pin is +`anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.ExactTriangle + +open Classical in +/-- Primes in the half-open window `[sqrt D / 2, sqrt D)`. -/ +noncomputable def primesInRange (D : ℕ) : Finset ℕ := + (Finset.range D).filter fun p => p.Prime ∧ Real.sqrt D / 2 ≤ (p : ℝ) ∧ (p : ℝ) < Real.sqrt D + +/-- Number of triangles whose sum vanishes modulo `p`. -/ +def countZeroMod {n : ℕ} (wAB wBC wAC : Fin n → Fin n → ℤ) (p : ℕ) : ℕ := + (Finset.univ.filter fun t : Fin n × Fin n × Fin n => + weightSum wAB wBC wAC t.1 t.2.1 t.2.2 ≡ 0 [ZMOD (p : ℤ)]).card + +/-- A window prime minimises the modular count over all window primes. -/ +def IsSelectedPrime {n : ℕ} (wAB wBC wAC : Fin n → Fin n → ℤ) (D p : ℕ) : Prop := + p ∈ primesInRange D ∧ ∀ q ∈ primesInRange D, + countZeroMod wAB wBC wAC p ≤ countZeroMod wAB wBC wAC q + +private theorem four_le_sqrt {D : ℕ} (hD : 16 ≤ D) : 4 ≤ Real.sqrt (D : ℝ) := + Real.le_sqrt_of_sq_le (by norm_num; exact_mod_cast hD) + +private theorem natCast_toNat_emod {p : ℕ} (hp : 0 < p) (u : ℤ) : + ((u % (p : ℤ)).toNat : ℤ) = u % (p : ℤ) := + Int.toNat_of_nonneg (Int.emod_nonneg _ (by omega)) + +private theorem toNat_emod_lt {p : ℕ} (hp : 0 < p) (u : ℤ) : (u % (p : ℤ)).toNat < p := by + have := Int.emod_nonneg u (by omega : (p : ℤ) ≠ 0) + have := Int.emod_lt_of_pos u (by omega : (0 : ℤ) < p) + omega + +/-! +### The primes in the range -/ + +/-- Proof of Theorem 17, a prime p ∈ [√D/2, √D): the primes in the range are the primes `p` with +`√D/2 ≤ p < √D`. (The bound `p < D` in the definition excludes none of them.) +-/ +theorem mem_primesInRange {D p : ℕ} : + p ∈ primesInRange D ↔ p.Prime ∧ Real.sqrt D / 2 ≤ (p : ℝ) ∧ (p : ℝ) < Real.sqrt D := by + unfold primesInRange + rw [Finset.mem_filter, Finset.mem_range] + refine ⟨fun h => h.2, fun h => ⟨?_, h⟩⟩ + -- From `p < √D` we get `p ≤ p² < D`. + have hsq : p ^ 2 < D := by exact_mod_cast (Real.lt_sqrt (Nat.cast_nonneg p)).1 h.2.2 + exact (Nat.le_self_pow two_ne_zero p).trans_lt hsq + +/-- Window membership is an exact integer-square test, suitable for native enumeration. -/ +theorem mem_primesInRange_iff_sq {D p : ℕ} : + p ∈ primesInRange D ↔ p.Prime ∧ D ≤ 4 * p ^ 2 ∧ p ^ 2 < D := by + rw [mem_primesInRange] + have hp0 : (0 : ℝ) ≤ p := Nat.cast_nonneg p + have hsqrt0 : 0 ≤ Real.sqrt (D : ℝ) := Real.sqrt_nonneg _ + have hsqrt : (Real.sqrt (D : ℝ)) ^ 2 = D := Real.sq_sqrt (Nat.cast_nonneg D) + constructor + · rintro ⟨hp, hlo, hhi⟩ + refine ⟨hp, ?_, ?_⟩ + · have h : (D : ℝ) ≤ 4 * (p : ℝ) ^ 2 := by nlinarith + exact_mod_cast h + · exact_mod_cast (Real.lt_sqrt hp0).1 hhi + · rintro ⟨hp, hlo, hhi⟩ + refine ⟨hp, ?_, (Real.lt_sqrt hp0).2 (by exact_mod_cast hhi)⟩ + have h : (D : ℝ) ≤ 4 * (p : ℝ) ^ 2 := by exact_mod_cast hlo + nlinarith + +/-- A computable finite enumeration of exactly the same half-open prime window. -/ +theorem primesInRange_eq_filter (D : ℕ) : + primesInRange D = (Finset.range D).filter fun p => p.Prime ∧ D ≤ 4 * p ^ 2 ∧ p ^ 2 < D := by + ext p + rw [mem_primesInRange_iff_sq, Finset.mem_filter, Finset.mem_range] + exact ⟨fun h => ⟨(Nat.le_self_pow two_ne_zero p).trans_lt h.2.2, h⟩, fun h => h.2⟩ + +/-- A number `r < p` is the sum of the residues of `u` and `v` in `{0, …, p − 1}`, reduced modulo +`p`, exactly if `u + v ≡ r`. +-/ +private theorem eq_add_toNat_emod_iff {p r : ℕ} (hp : p ≠ 0) (hr : r < p) (u v : ℤ) : + r = ((u % (p : ℤ)).toNat + (v % (p : ℤ)).toNat) % p ↔ u + v ≡ (r : ℤ) [ZMOD (p : ℤ)] := by + rw [← Int.natCast_inj, Int.ModEq, Int.emod_eq_of_lt (Int.natCast_nonneg r) (by exact_mod_cast hr), + eq_comm] + push_cast + rw [natCast_toNat_emod (Nat.pos_of_ne_zero hp), + natCast_toNat_emod (Nat.pos_of_ne_zero hp), + ← Int.add_emod] + +/-! +### Counting the triples with `S(a,b,c) ≡ 0 (mod p)` -/ + +section Counting + +variable {n : ℕ} (wAB wBC wAC : Fin n → Fin n → ℤ) + +/-- A false positive of p is a triple (a,b,c) with S(a,b,c) ≠ 0 and p ∣ S(a,b,c). -/ +abbrev IsFalsePositive (p : ℕ) (t : Fin n × Fin n × Fin n) : Prop := + weightSum wAB wBC wAC t.1 t.2.1 t.2.2 ≠ 0 ∧ (p : ℤ) ∣ weightSum wAB wBC wAC t.1 t.2.1 t.2.2 + +/-- let falsePositiveCount(p) denote the number of false positives of p. -/ +def falsePositiveCount (p : ℕ) : ℕ := + (Finset.univ.filter fun t : Fin n × Fin n × Fin n => IsFalsePositive wAB wBC wAC p t).card + +/-- zeroCount, the number of zero triangles. -/ +def zeroCount : ℕ := + (Finset.univ.filter fun t : Fin n × Fin n × Fin n => weightSum wAB wBC wAC t.1 t.2.1 t.2.2 = + 0).card + +/-- A positive zero count is exactly the existing Exact Triangle decision predicate. -/ +theorem zeroCount_pos_iff : 0 < zeroCount wAB wBC wAC ↔ HasZeroTriangle wAB wBC wAC := by + classical + simp only [zeroCount, Finset.card_pos, HasZeroTriangle] + constructor + · rintro ⟨⟨a, b, c⟩, h⟩ + exact ⟨a, b, c, (Finset.mem_filter.mp h).2⟩ + · rintro ⟨a, b, c, h⟩ + exact ⟨(a, b, c), Finset.mem_filter.mpr ⟨Finset.mem_univ _, h⟩⟩ + +/-- This number is the number of false positives falsePositiveCount(p) plus zeroCount, the number of +zero triangles, which does not depend on p. +-/ +theorem countZeroMod_eq (p : ℕ) : (countZeroMod wAB wBC wAC) p = (falsePositiveCount wAB wBC + wAC) p + (zeroCount wAB wBC wAC) := by + classical + simp only [countZeroMod, falsePositiveCount, zeroCount, Finset.card_filter, ← + Finset.sum_add_distrib] + -- A triple with `p ∣ S(a,b,c)` is a zero triangle or a false positive, and not both. + refine Finset.sum_congr rfl fun t _ => ?_ + by_cases h0 : weightSum wAB wBC wAC t.1 t.2.1 t.2.2 = 0 <;> + simp [IsFalsePositive, Int.modEq_zero_iff_dvd, h0] + +/-- let P[a,c] := x^{w(a,c) mod p}, a matrix over `ℤ[x]/(x^p − 1)`. `w mod p` is the residue in +`{0, …, p − 1}`. +-/ +noncomputable def matP (p : ℕ) : Matrix (Fin n) (Fin n) (CyclicIntRing p) := + Matrix.of fun a c => CyclicIntRing.x p ^ (wAC a c % (p : ℤ)).toNat + +/-- and Q[c,b] := x^{w(b,c) mod p}. -/ +noncomputable def matQ (p : ℕ) : Matrix (Fin n) (Fin n) (CyclicIntRing p) := + Matrix.of fun c b => CyclicIntRing.x p ^ (wBC b c % (p : ℤ)).toNat + +/-- the coefficient of x^r in (PQ)[a,b] is the number of c ∈ C with w(a,c) + w(b,c) ≡ r (mod p). -/ +theorem coeff_matP_mul_matQ {p : ℕ} (hp : p ≠ 0) (a b : Fin n) (r : ℕ) (hr : r < p) : + CyclicIntRing.coeff hp r ((matP wAC p * matQ wBC p) a b) = + ((Finset.univ.filter fun c : Fin n => + wAC a c + wBC b c ≡ (r : ℤ) [ZMOD (p : ℤ)]).card : ℤ) := by + classical + -- `(PQ)[a,b]` is the sum over `c` of `x^(w(a,c) mod p + w(b,c) mod p)`, and the coefficient of + -- `x^r` in each term is 1 or 0. + rw [Matrix.mul_apply] + simp only [matP, matQ, Matrix.of_apply, ← pow_add] + rw [map_sum] + simp only [CyclicIntRing.coeff_x_pow] + rw [Finset.card_filter] + push_cast + refine Finset.sum_congr rfl fun c _ => ?_ + simp only [eq_add_toNat_emod_iff hp hr] + +/-- then falsePositiveCount(p) + zeroCount is the sum over the pairs (a,b) ∈ A × B of the +coefficient of x^{−w(a,b) mod p} in (PQ)[a,b]. +-/ +theorem falsePositiveCount_add_zeroCount_eq_sum_coeff {p : ℕ} (hp : p ≠ 0) : + (((falsePositiveCount wAB wBC wAC) p + (zeroCount wAB wBC wAC) : ℕ) : ℤ) = + ∑ a : Fin n, ∑ b : Fin n, + CyclicIntRing.coeff hp ((-wAB a b) % (p : ℤ)).toNat ((matP wAC p * matQ wBC p) a b) := by + classical + rw [← countZeroMod_eq] + unfold countZeroMod + rw [Finset.card_filter, Fintype.sum_prod_type] + push_cast + refine Finset.sum_congr rfl fun a _ => ?_ + rw [Fintype.sum_prod_type] + refine Finset.sum_congr rfl fun b _ => ?_ + have hcast := natCast_toNat_emod (Nat.pos_of_ne_zero hp) (-wAB a b) + rw [(coeff_matP_mul_matQ wBC wAC) hp a b _ (toNat_emod_lt (Nat.pos_of_ne_zero hp) _), + Finset.card_filter] + push_cast + refine Finset.sum_congr rfl fun c _ => ?_ + -- `S(a,b,c) ≡ 0` says the same as `w(a,c) + w(b,c) ≡ -w(a,b)`, and `-w(a,b)` is congruent to its + -- residue. + have hres : (-wAB a b) % (p : ℤ) ≡ -wAB a b [ZMOD (p : ℤ)] := Int.mod_modEq _ _ + have hiff : weightSum wAB wBC wAC a b c ≡ 0 [ZMOD (p : ℤ)] ↔ + wAC a c + wBC b c ≡ ((((-wAB a b) % (p : ℤ)).toNat : ℕ) : ℤ) [ZMOD (p : ℤ)] := by + rw [hcast, show wAC a c + wBC b c ≡ (-wAB a b) % (p : ℤ) [ZMOD (p : ℤ)] ↔ + wAC a c + wBC b c ≡ -wAB a b [ZMOD (p : ℤ)] from + ⟨fun h => h.trans hres, fun h => h.trans hres.symm⟩, + Int.modEq_iff_dvd, Int.modEq_iff_dvd, + show 0 - weightSum wAB wBC wAC a b c = -wAB a b - (wAC a c + wBC b c) by + simp only [weightSum]; ring] + simp only [hiff] + +end Counting + +/-- the fewer than √D primes in the range. -/ +theorem card_primesInRange_lt (D : ℕ) (hD : 16 ≤ D) : + ((primesInRange D).card : ℝ) < Real.sqrt D := by + have hpos : 0 < Real.sqrt D := by linarith [four_le_sqrt hD] + -- The primes in the range are among the numbers `1, …, ⌈√D⌉ - 1`. + have hsub : primesInRange D ⊆ Finset.Ico 1 ⌈Real.sqrt D⌉₊ := by + intro p hp + obtain ⟨hprime, -, hlt⟩ := mem_primesInRange.1 hp + exact Finset.mem_Ico.2 ⟨hprime.one_lt.le, Nat.lt_ceil.2 hlt⟩ + have hcard : (primesInRange D).card + 1 ≤ ⌈Real.sqrt D⌉₊ := by + have hle := Finset.card_le_card hsub + have hone : 1 ≤ ⌈Real.sqrt D⌉₊ := Nat.one_le_ceil_iff.2 hpos + rw [Nat.card_Ico] at hle + omega + have hcast : ((primesInRange D).card : ℝ) + 1 ≤ (⌈Real.sqrt D⌉₊ : ℝ) := by exact_mod_cast hcard + linarith [Nat.ceil_lt_add_one hpos.le] + +/-! +### Selecting the prime -/ + +section Selection + +variable {n D p : ℕ} {κ : ℝ} (wAB wBC wAC : Fin n → Fin n → ℤ) + +/-- There is a prime to select. -/ +theorem exists_isSelectedPrime (D : ℕ) (hD : 16 ≤ D) : ∃ p, IsSelectedPrime wAB wBC wAC D p := by + obtain ⟨q, hq⟩ := + Nat.exists_prime_half_le_and_lt (Real.sqrt D) (four_le_sqrt hD) + exact Finset.exists_min_image (primesInRange D) (countZeroMod wAB wBC wAC) ⟨q, + mem_primesInRange.2 hq⟩ + +/-- We select the prime with the smallest count, which is also the prime with the fewest false +positives. +-/ +theorem IsSelectedPrime.falsePositiveCount_le {wAB wBC wAC : Fin n → Fin n → ℤ} (hp : + IsSelectedPrime wAB wBC wAC D p) : + ∀ q ∈ primesInRange D, (falsePositiveCount wAB wBC wAC) p ≤ (falsePositiveCount wAB wBC wAC) + q := by + intro q hq + have h := hp.2 q hq + rw [countZeroMod_eq, countZeroMod_eq] at h + omega + +/-! +### The bound on the number of false positives of the selected prime -/ + +open Classical in +/-- a triple with S(a,b,c) ≠ 0 is a false positive exactly of the primes in the range that divide +S(a,b,c). Since 0 < |S(a,b,c)| ≤ 3n^ν and these primes are at least √D/2, there are at most +log_{√D/2}(3n^ν) of them. +-/ +theorem card_falsePositive_primes_le (hD : 16 ≤ D) (hT : WeightsBoundedBy wAB wBC wAC ((n : ℝ) ^ κ)) + (t : Fin n × Fin n × Fin n) (ht : weightSum wAB wBC wAC t.1 t.2.1 t.2.2 ≠ 0) : + (((primesInRange D).filter fun p => IsFalsePositive wAB wBC wAC p t).card : ℝ) + ≤ Real.logb (Real.sqrt D / 2) (3 * (n : ℝ) ^ κ) := by + have hsqrt := four_le_sqrt hD + set divisors := (primesInRange D).filter fun p => IsFalsePositive wAB wBC wAC p t + have hmem : ∀ p ∈ divisors, + p.Prime ∧ Real.sqrt D / 2 ≤ (p : ℝ) ∧ (p : ℤ) ∣ weightSum wAB wBC wAC t.1 t.2.1 t.2.2 := by + intro p hp + obtain ⟨hrange, -, hdvd⟩ := Finset.mem_filter.1 hp + obtain ⟨hprime, hge, -⟩ := mem_primesInRange.1 hrange + exact ⟨hprime, hge, hdvd⟩ + -- The product of these primes divides `S(a,b,c)`, so it is at most `|S(a,b,c)| ≤ 3n^κ`. + have hdvd : ∏ p ∈ divisors, p ∣ (weightSum wAB wBC wAC t.1 t.2.1 t.2.2).natAbs := + Finset.prod_primes_dvd _ (fun p hp => (hmem p hp).1.prime) + fun p hp => Int.natCast_dvd.1 (hmem p hp).2.2 + have hprod : ∏ p ∈ divisors, (p : ℝ) ≤ 3 * (n : ℝ) ^ κ := + calc ∏ p ∈ divisors, (p : ℝ) = ((∏ p ∈ divisors, p : ℕ) : ℝ) := (Nat.cast_prod _ _).symm + _ ≤ ((weightSum wAB wBC wAC t.1 t.2.1 t.2.2).natAbs : ℝ) := by + exact_mod_cast Nat.le_of_dvd (Int.natAbs_pos.2 ht) hdvd + _ = ((|weightSum wAB wBC wAC t.1 t.2.1 t.2.2| : ℤ) : ℝ) := Nat.cast_natAbs _ + _ ≤ 3 * (n : ℝ) ^ κ := (abs_weightSum_le wAB wBC wAC) hT _ _ _ + -- Each of these primes is at least `√D/2`. + have hpow : (Real.sqrt D / 2) ^ divisors.card ≤ ∏ p ∈ divisors, (p : ℝ) := by + rw [← Finset.prod_const] + exact Finset.prod_le_prod₀ (fun _ _ => by linarith) fun p hp => (hmem p hp).2.1 + have hY : 0 < 3 * (n : ℝ) ^ κ := lt_of_lt_of_le (by positivity) (hpow.trans hprod) + rw [Real.le_logb_iff_rpow_le (by linarith) hY, Real.rpow_natCast] + exact hpow.trans hprod + +/-- Hence the numbers of false positives of all the primes in the range add up to at most n³ +log_{√D/2}(3n^ν). +-/ +theorem sum_falsePositiveCount_le (hD : 16 ≤ D) (hκ : 1 ≤ κ) (hT : WeightsBoundedBy wAB wBC wAC + ((n : ℝ) ^ κ)) : + ((∑ p ∈ primesInRange D, (falsePositiveCount wAB wBC wAC) p : ℕ) : ℝ) + ≤ (n : ℝ) ^ 3 * Real.logb (Real.sqrt D / 2) (3 * (n : ℝ) ^ κ) := by + classical + have hsqrt := four_le_sqrt hD + -- Count the pairs (prime, false positive of that prime) triple by triple. + have hswap : ∑ p ∈ primesInRange D, (falsePositiveCount wAB wBC wAC) p = ∑ t : Fin n × Fin n × + Fin n, + ((primesInRange D).filter fun p => IsFalsePositive wAB wBC wAC p t).card := by + simp only [falsePositiveCount, Finset.card_filter] + exact Finset.sum_comm + have hterm : ∀ t : Fin n × Fin n × Fin n, + (((primesInRange D).filter fun p => IsFalsePositive wAB wBC wAC p t).card : ℝ) + ≤ Real.logb (Real.sqrt D / 2) (3 * (n : ℝ) ^ κ) := by + intro t + by_cases ht : weightSum wAB wBC wAC t.1 t.2.1 t.2.2 = 0 + · -- A zero triangle is a false positive of no prime. + rw [Finset.filter_eq_empty_iff.2 fun p _ h => h.1 ht, Finset.card_empty, Nat.cast_zero] + have hn : (1 : ℝ) ≤ n := by exact_mod_cast Fin.pos t.1 + have hpow : (1 : ℝ) ≤ (n : ℝ) ^ κ := Real.one_le_rpow hn (by linarith) + exact Real.logb_nonneg (by linarith) (by linarith) + · -- Otherwise this is the bound of `card_falsePositive_primes_le` for this triple. + convert (card_falsePositive_primes_le wAB wBC wAC) hD hT t ht + rw [hswap, Nat.cast_sum] + calc ∑ t : Fin n × Fin n × Fin n, + (((primesInRange D).filter fun p => IsFalsePositive wAB wBC wAC p t).card : ℝ) + ≤ ∑ _t : Fin n × Fin n × Fin n, Real.logb (Real.sqrt D / 2) (3 * (n : ℝ) ^ κ) := + Finset.sum_le_sum fun t _ => hterm t + _ = (n : ℝ) ^ 3 * Real.logb (Real.sqrt D / 2) (3 * (n : ℝ) ^ κ) := by + rw [Finset.sum_const, Finset.card_univ, Fintype.card_prod, Fintype.card_prod, + Fintype.card_fin, nsmul_eq_mul] + push_cast + ring + +end Selection + +/-- By the prime number theorem there are Ω(√D/log D) primes in the range. + +NOTE. The modular reduction needs this for every `D ≥ 16`, not only for large `D`, and so it is +stated. It follows from a counting form of Bertrand's postulate; the prime number theorem is +not needed. +-/ +theorem exists_le_card_primesInRange : + ∃ c : ℝ, 0 < c ∧ ∀ D : ℕ, 16 ≤ D → + c * (Real.sqrt D / Real.log D) ≤ ((primesInRange D).card : ℝ) := by + obtain ⟨c, hc, hcount⟩ := Nat.exists_forall_mul_sqrt_div_log_le_card_primes + exact ⟨c, hc, fun D hD => hcount D hD _ + fun _ hp hge hlt => mem_primesInRange.2 ⟨hp, hge, hlt⟩⟩ + +/-- falsePositiveCount(p) is at most the average over them, multiplied out. -/ +theorem IsSelectedPrime.falsePositiveCount_mul_card_le_sum {n D p : ℕ} {wAB wBC wAC : Fin n → + Fin n → ℤ} + (hp : IsSelectedPrime wAB wBC wAC D p) : + (falsePositiveCount wAB wBC wAC) p * (primesInRange D).card ≤ ∑ q ∈ primesInRange D, + (falsePositiveCount wAB wBC wAC) q := by + rw [mul_comm, ← smul_eq_mul] + exact Finset.card_nsmul_le_sum _ _ _ hp.falsePositiveCount_le + +/-- log D = O(log(√D/2)) for D ≥ 16. -/ +theorem log_le_four_mul_log_sqrt_div_two (D : ℕ) (hD : 16 ≤ D) : + Real.log D ≤ 4 * Real.log (Real.sqrt D / 2) := by + have hD16 : (16 : ℝ) ≤ D := by exact_mod_cast hD + have hsqrt : 0 < Real.sqrt D := by linarith [four_le_sqrt hD] + -- `log(√D/2) = (log D)/2 - log 2`, and `4 log 2 = log 16 ≤ log D`. + have hlog16 : 4 * Real.log 2 ≤ Real.log D := by + have h := Real.log_le_log (by norm_num) (show (2 : ℝ) ^ 4 ≤ D by linarith) + rw [Real.log_pow] at h + exact_mod_cast h + rw [Real.log_div hsqrt.ne' (by norm_num), Real.log_sqrt (by linarith)] + linarith [hlog16] + +/-- falsePositiveCount(p) is at most the average over them, so [...] falsePositiveCount(p) = O(n³ +log(3n^ν)/√D): if there are at least `c √D/log D` primes in the range, then +`falsePositiveCount(p) ≤ (4/c) n³ log(3n^κ)/√D`. +-/ +theorem falsePositiveCount_le_of_le_card_primesInRange {n D p : ℕ} {κ c : ℝ} + (wAB wBC wAC : Fin n → Fin n → ℤ) (hc : 0 < c) + (hcard : c * (Real.sqrt D / Real.log D) ≤ ((primesInRange D).card : ℝ)) (hD : 16 ≤ D) + (hDn : D ≤ n) (hκ : 1 ≤ κ) (hT : WeightsBoundedBy wAB wBC wAC ((n : ℝ) ^ κ)) (hp : + IsSelectedPrime wAB wBC wAC D p) : + ((falsePositiveCount wAB wBC wAC) p : ℝ) ≤ 4 / c * ((n : ℝ) ^ 3 * Real.log (3 * (n : ℝ) ^ κ) + / Real.sqrt D) := by + -- The quantities of the paper's sentence: `r = √D`, `Y = 3n^κ`, and the number of primes in the + -- range. + set r := Real.sqrt D + set Y := 3 * (n : ℝ) ^ κ with hY + set numPrimes := (primesInRange D).card + have hr : 4 ≤ r := four_le_sqrt hD + have hn : (16 : ℝ) ≤ n := by exact_mod_cast hD.trans hDn + have hlogD_pos : 0 < Real.log D := Real.log_pos (by exact_mod_cast (by omega : 1 < D)) + have hlogr_pos : 0 < Real.log (r / 2) := Real.log_pos (by linarith) + have hlogY_nonneg : 0 ≤ Real.log Y := by + have : 1 ≤ (n : ℝ) ^ κ := Real.one_le_rpow (by linarith) (by linarith) + exact Real.log_nonneg (by rw [hY]; linarith) + -- The selected prime is at most average, after multiplying by the number of primes. + -- False positives of all primes in the + -- range add up to at most `n³ log_{r/2} Y`. + have average : ((falsePositiveCount wAB wBC wAC) p : ℝ) * numPrimes ≤ (n : ℝ) ^ 3 * Real.logb + (r / 2) Y := + le_trans (by exact_mod_cast hp.falsePositiveCount_mul_card_le_sum) + ((sum_falsePositiveCount_le wAB wBC wAC) hD hκ hT) + -- The base of the logarithm changes: `log_{r/2} Y ≤ 4 log Y / log D`. + have hlogD_le : Real.log D ≤ 4 * Real.log (r / 2) := log_le_four_mul_log_sqrt_div_two D hD + have changeBase : Real.logb (r / 2) Y ≤ 4 * Real.log Y / Real.log D := by + rw [Real.logb, div_le_div_iff₀ hlogr_pos hlogD_pos] + linarith [mul_le_mul_of_nonneg_left hlogD_le hlogY_nonneg] + rw [show 4 / c * ((n : ℝ) ^ 3 * Real.log Y / r) = 4 * (n : ℝ) ^ 3 * Real.log Y / (c * r) by + field_simp, le_div_iff₀ (by positivity)] + calc ((falsePositiveCount wAB wBC wAC) p : ℝ) * (c * r) = (falsePositiveCount wAB wBC wAC) p * + (c * (r / Real.log D)) * Real.log D := by field_simp + _ ≤ (falsePositiveCount wAB wBC wAC) p * numPrimes * Real.log D := by gcongr + _ ≤ (n : ℝ) ^ 3 * Real.logb (r / 2) Y * Real.log D := by gcongr + _ ≤ (n : ℝ) ^ 3 * (4 * Real.log Y / Real.log D) * Real.log D := by gcongr + _ = 4 * (n : ℝ) ^ 3 * Real.log Y := by field_simp + +/-- falsePositiveCount(p) = O(n³ log(3n^ν)/√D) = O(ν n³ log n/√D). -/ +theorem exists_falsePositiveCount_le : + ∃ C : ℝ, 1 ≤ C ∧ ∀ {n D p : ℕ} {κ : ℝ}, 16 ≤ D → D ≤ n → 1 ≤ κ → ∀ wAB wBC wAC : Fin n → Fin + n → ℤ, + WeightsBoundedBy wAB wBC wAC ((n : ℝ) ^ κ) → IsSelectedPrime wAB wBC wAC D p → + ((falsePositiveCount wAB wBC wAC) p : ℝ) ≤ C * (κ * (n : ℝ) ^ 3 * Real.log n / Real.sqrt + D) := by + obtain ⟨c, hc, hcard⟩ := exists_le_card_primesInRange + refine ⟨max 1 (8 / c), le_max_left _ _, fun {n D p κ} hD hDn hκ wAB wBC wAC hT hp => ?_⟩ + have hn : (16 : ℝ) ≤ n := by exact_mod_cast hD.trans hDn + have hlogn : 0 ≤ Real.log n := Real.log_nonneg (by linarith) + -- `log(3n^κ) = log 3 + κ log n ≤ 2κ log n`, because `3 ≤ n` and `κ ≥ 1`. + have hlog : Real.log (3 * (n : ℝ) ^ κ) ≤ 2 * κ * Real.log n := by + have hlog3 : Real.log 3 ≤ κ * Real.log n := + (Real.log_le_log (by norm_num) (by linarith)).trans (le_mul_of_one_le_left hlogn hκ) + rw [Real.log_mul (by norm_num) (by positivity), Real.log_rpow (by linarith)] + linarith [hlog3] + calc ((falsePositiveCount wAB wBC wAC) p : ℝ) ≤ 4 / c * ((n : ℝ) ^ 3 * Real.log (3 * (n : ℝ) ^ + κ) / Real.sqrt D) := + (falsePositiveCount_le_of_le_card_primesInRange wAB wBC wAC) hc (hcard D hD) hD hDn hκ hT hp + _ ≤ 4 / c * ((n : ℝ) ^ 3 * (2 * κ * Real.log n) / Real.sqrt D) := by gcongr + _ = 8 / c * (κ * (n : ℝ) ^ 3 * Real.log n / Real.sqrt D) := by ring + _ ≤ max 1 (8 / c) * (κ * (n : ℝ) ^ 3 * Real.log n / Real.sqrt D) := by + gcongr + exact le_max_right _ _ + +/-- A name for the constant in falsePositiveCount(p) = [...] = O(ν n³ log n/√D), for the bounds on +running times that are built on this one. +-/ +noncomputable def Hashing.falsePositiveConst : ℝ := Classical.choose exists_falsePositiveCount_le + +/-- The constant is at least 1. -/ +theorem Hashing.one_le_falsePositiveConst : 1 ≤ Hashing.falsePositiveConst := + (Classical.choose_spec exists_falsePositiveCount_le).1 + +/-- falsePositiveCount(p) = [...] = O(ν n³ log n/√D), with `Hashing.falsePositiveConst` as the +constant. +-/ +theorem Hashing.falsePositiveCount_le_const_mul {n D p : ℕ} {κ : ℝ} (hD : 16 ≤ D) (hDn : D ≤ n) + (hκ : 1 ≤ κ) (wAB wBC wAC : Fin n → Fin n → ℤ) (hT : WeightsBoundedBy wAB wBC wAC ((n : ℝ) ^ κ)) + (hp : IsSelectedPrime wAB wBC wAC D p) : + ((falsePositiveCount wAB wBC wAC) p : ℝ) ≤ Hashing.falsePositiveConst * (κ * (n : ℝ) ^ 3 * + Real.log n / Real.sqrt D) := + (Classical.choose_spec exists_falsePositiveCount_le).2 hD hDn hκ wAB wBC wAC hT hp + +end Cslib.Algorithms.ExactTriangle diff --git a/Complexity/Algorithms/ExactTriangle/Weights.lean b/Complexity/Algorithms/ExactTriangle/Weights.lean new file mode 100644 index 000000000..d358d0cd8 --- /dev/null +++ b/Complexity/Algorithms/ExactTriangle/Weights.lean @@ -0,0 +1,61 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.ExactTriangle.Defs +public import Mathlib.Algebra.Order.Group.Abs +public import Mathlib.Basic.Real.Basic +import Mathlib.Tactic.Linarith + +/-! +# Integer triangle sums and magnitude bounds + +## Main definitions + +* `weightSum`: the sum of the three supplied integer edge weights. +* `WeightsBoundedBy`: a shared real bound on their absolute values. + +## Main results + +* `abs_weightSum_le`: a triangle's magnitude is at most three times the edge bound. + +## References + +Adapted from the weight sum and bound definitions in [Anthropic's +PaperStatements.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/PaperStatements.lean#L893-L914) +and the triangle inequality in its +[Hashing.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec3/Theorem17/Hashing.lean#L246-L255). +The existing `HasZeroTriangle` remains the decision predicate. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.ExactTriangle + +variable {n : ℕ} (wAB wBC wAC : Fin n → Fin n → ℤ) + +/-- Sum of the A–B, B–C and A–C weights at a triangle. -/ +def weightSum (a b c : Fin n) : ℤ := wAB a b + wBC b c + wAC a c + +/-- Every supplied edge weight has absolute value at most `U`. -/ +def WeightsBoundedBy (U : ℝ) : Prop := + (∀ a b, ((|wAB a b| : ℤ) : ℝ) ≤ U) ∧ + (∀ b c, ((|wBC b c| : ℤ) : ℝ) ≤ U) ∧ ∀ a c, ((|wAC a c| : ℤ) : ℝ) ≤ U + +/-- The three edge bounds imply a threefold bound on every triangle sum. -/ +theorem abs_weightSum_le {U : ℝ} (h : WeightsBoundedBy wAB wBC wAC U) (a b c : Fin n) : + ((|weightSum wAB wBC wAC a b c| : ℤ) : ℝ) ≤ 3 * U := by + obtain ⟨hAB, hBC, hAC⟩ := h + have habs : |weightSum wAB wBC wAC a b c| ≤ |wAB a b| + |wBC b c| + |wAC a c| := + abs_add_three _ _ _ + have hcast : ((|weightSum wAB wBC wAC a b c| : ℤ) : ℝ) ≤ + ((|wAB a b| : ℤ) : ℝ) + ((|wBC b c| : ℤ) : ℝ) + ((|wAC a c| : ℤ) : ℝ) := by + exact_mod_cast habs + linarith [hAB a b, hBC b c, hAC a c] + +end Cslib.Algorithms.ExactTriangle diff --git a/Complexity/Foundations/NumberTheory/PrimeWindow.lean b/Complexity/Foundations/NumberTheory/PrimeWindow.lean new file mode 100644 index 000000000..dbeb6390e --- /dev/null +++ b/Complexity/Foundations/NumberTheory/PrimeWindow.lean @@ -0,0 +1,346 @@ +/- +Copyright (c) 2020 Patrick Stevens. All rights reserved. +Copyright (c) 2026 Anthropic, PBC. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Patrick Stevens, Bolton Bailey, Anthropic, PBC +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Analysis.SpecialFunctions.Log.Base +public import Mathlib.Analysis.SpecialFunctions.Pow.Real +public import Mathlib.NumberTheory.PrimeCounting +import Mathlib.Analysis.Complex.ExponentialBounds +import Mathlib.Data.Nat.Prime.Factorial +import Mathlib.NumberTheory.Bertrand +import Mathlib.NumberTheory.Primorial +import Mathlib.Analysis.Convex.SpecificFunctions.Deriv + +/-! +# Explicit prime counts in a window of ratio two + +For every real `x ≥ 4`, the half-open window `[x/2, x)` contains at least `c * x / log x` primes +for one universal positive constant `c`. The proof counts prime factors of a central binomial +coefficient, so no prime-number-theorem hypothesis is assumed. + +## Main results + +* `Nat.exists_prime_half_le_and_lt`: a prime exists in every such window. +* `Nat.exists_forall_mul_div_log_le_card_primes`: the uniform counting bound. +* `Nat.exists_forall_mul_sqrt_div_log_le_card_primes`: the bound at `x = sqrt D`, for all `D ≥ + 16`. + +## References + +Adapted from [Anthropic's +PrimesInWindow.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Util/PrimesInWindow.lean). +Its central-binomial and concavity estimates adapt `Mathlib/NumberTheory/Bertrand.lean`, +originally by Patrick Stevens and Bolton Bailey; that source credit is retained. +-/ + +@[expose] public section + +open Finset + +namespace Nat + +private theorem one_le_log_of_three_le {x : ℝ} (hx : 3 ≤ x) : 1 ≤ Real.log x := + (Real.le_log_iff_exp_le (by linarith)).2 (Real.exp_one_lt_three.le.trans hx) + + +/-- The primes `p` with `n < p ≤ 2n`: the primes that Bertrand's postulate is about. -/ +private def bertrandPrimes (n : ℕ) : Finset ℕ := (Ioc n (2 * n)).filter Nat.Prime + +/-- Membership in `bertrandPrimes`, spelled out. -/ +private theorem mem_bertrandPrimes {n p : ℕ} : + p ∈ bertrandPrimes n ↔ (n < p ∧ p ≤ 2 * n) ∧ p.Prime := by + simp only [bertrandPrimes, mem_filter, mem_Ioc] + +/-! +## 1. Erdős's upper bound for the central binomial coefficient -/ + +/-- Every prime power in `C(2n, n)` is at most `2n`, so `|s|` of them multiply to at most +`(2n)^|s|`. +-/ +private theorem prod_pow_factorization_le {n : ℕ} (hn : 0 < n) (s : Finset ℕ) : + ∏ p ∈ s, p ^ n.centralBinom.factorization p ≤ (2 * n) ^ s.card := + prod_le_pow_card _ _ _ fun _ _ => Nat.pow_factorization_choose_le (by omega) + +/-- The primes in `(√(2n), n]` contribute at most `4^(2n/3)` to `C(2n, n)`: their exponent is at +most 1, it is 0 above `2n/3`, and the product of the primes up to `2n/3` is at most `4^(2n/3)`. +Adapted from Mathlib, Mathlib/NumberTheory/Bertrand.lean, Apache 2.0. +-/ +private theorem prod_middle_le {n : ℕ} (hn : 2 < n) (s : Finset ℕ) + (hs : ∀ p ∈ s, p.Prime ∧ Nat.sqrt (2 * n) < p ∧ p ≤ n) : + ∏ p ∈ s, p ^ n.centralBinom.factorization p ≤ 4 ^ (2 * n / 3) := + calc ∏ p ∈ s, p ^ n.centralBinom.factorization p + ≤ ∏ p ∈ s, (if p ≤ 2 * n / 3 then p else 1) := by + refine prod_le_prod fun p hp => ?_ + obtain ⟨hprime, hsqrt, hpn⟩ := hs p hp + split_ifs with h3 + · exact (pow_right_mono₀ hprime.one_lt.le + (Nat.factorization_choose_le_one (Nat.sqrt_lt'.mp hsqrt))).trans (pow_one p).le + · rw [Nat.factorization_centralBinom_of_two_mul_self_lt_three_mul hn hpn (by omega), + pow_zero] + _ = ∏ p ∈ s.filter (· ≤ 2 * n / 3), p := (prod_filter _ _).symm + _ ≤ primorial (2 * n / 3) := by + refine prod_le_prod_of_subset_of_one_le (fun p hp => ?_) + fun p hp _ => (mem_filter.1 hp).2.one_lt.le + obtain ⟨hps, hp3⟩ := mem_filter.1 hp + exact mem_filter.2 ⟨mem_range.2 (by omega), (hs p hps).1⟩ + _ ≤ 4 ^ (2 * n / 3) := primorial_le_four_pow _ + +/-- Erdős's bound, keeping the primes of `(n, 2n]`: `C(2n, n) ≤ (2n)^√(2n) · 4^(2n/3) · (2n)^k`, +where `k` is the number of primes in `(n, 2n]`. Mathlib proves the case `k = 0` on the way to +Bertrand's postulate (`centralBinom_le_of_no_bertrand_prime`). Adapted from Mathlib, +Mathlib/NumberTheory/Bertrand.lean, Apache 2.0. +-/ +private theorem centralBinom_le (n : ℕ) (hn : 2 < n) : + n.centralBinom ≤ + (2 * n) ^ Nat.sqrt (2 * n) * 4 ^ (2 * n / 3) * (2 * n) ^ (bertrandPrimes n).card := by + -- `C(2n, n)` is the product of its prime powers over the set `T` of the primes up to `2n`. + set T := (range (2 * n + 1)).filter Nat.Prime with hT + have hprod : ∏ p ∈ T, p ^ n.centralBinom.factorization p = n.centralBinom := + (prod_filter_of_ne fun p _ h => by + contrapose h + rw [Nat.factorization_eq_zero_of_not_prime _ h, pow_zero]).trans + n.prod_pow_factorization_centralBinom + -- Split the primes at `n`, and those up to `n` at `√(2n)`. + rw [← hprod, ← prod_filter_mul_prod_filter_not T (· ≤ n), + ← prod_filter_mul_prod_filter_not (T.filter (· ≤ n)) (· ≤ Nat.sqrt (2 * n))] + refine mul_le_mul' (mul_le_mul' ?_ ?_) ?_ + · -- There are at most `√(2n)` primes up to `√(2n)`. + have hsub : (T.filter (· ≤ n)).filter (· ≤ Nat.sqrt (2 * n)) ⊆ Icc 1 (Nat.sqrt (2 * n)) := by + intro p hp + simp only [hT, mem_filter, mem_range] at hp + obtain ⟨⟨⟨-, hprime⟩, -⟩, hsqrt⟩ := hp + exact mem_Icc.mpr ⟨hprime.one_lt.le, hsqrt⟩ + exact (prod_pow_factorization_le (by omega) _).trans (pow_right_mono₀ (by omega) + ((card_le_card hsub).trans_eq (by rw [Nat.card_Icc, Nat.add_sub_cancel]))) + · -- The primes in `(√(2n), n]`. + refine prod_middle_le hn _ fun p hp => ?_ + simp only [hT, mem_filter, mem_range, not_le] at hp + obtain ⟨⟨⟨-, hprime⟩, hpn⟩, hsqrt⟩ := hp + exact ⟨hprime, hsqrt, hpn⟩ + · -- The primes above `n` are those of `(n, 2n]`. + refine (prod_pow_factorization_le (by omega) _).trans + (pow_right_mono₀ (by omega) (card_le_card fun p hp => ?_)) + simp only [hT, mem_filter, mem_range] at hp + obtain ⟨⟨hp2n, hprime⟩, hnp⟩ := hp + exact mem_bertrandPrimes.2 ⟨⟨by omega, by omega⟩, hprime⟩ + +/-! +## 2. The elementary estimate `n^6 · (2n)^(6√(2n)) ≤ 2^n` for `n ≥ 16562` -/ + +/-- For `a ≥ 0` the function `log x + √(2x) · log (2x) - a x` is concave for `x > 1/2`. Adapted from +Mathlib, Mathlib/NumberTheory/Bertrand.lean, Apache 2.0 (there inside +`Bertrand.real_main_inequality`, for `a = log 4 / 3`). +-/ +private theorem concaveOn_log_add_sqrt_mul_log_sub (a : ℝ) (ha : 0 ≤ a) : + ConcaveOn ℝ (Set.Ioi (1 / 2 : ℝ)) + (fun x : ℝ => Real.log x + √(2 * x) * Real.log (2 * x) - a * x) := by + apply ConcaveOn.sub + · apply ConcaveOn.add + · exact strictConcaveOn_log_Ioi.concaveOn.subset + (Set.Ioi_subset_Ioi (by norm_num)) (convex_Ioi _) + · have h := strictConcaveOn_sqrt_mul_log_Ioi.concaveOn.comp_linearMap + ((2 : ℝ) • (LinearMap.id : ℝ →ₗ[ℝ] ℝ)) + have hset : (⇑((2 : ℝ) • (LinearMap.id : ℝ →ₗ[ℝ] ℝ))) ⁻¹' Set.Ioi 1 = Set.Ioi (1 / 2) := by + ext x + simp only [Set.mem_Ioi, Set.mem_preimage, LinearMap.smul_apply, LinearMap.id_coe, id_eq, + smul_eq_mul] + constructor <;> intro h <;> linarith + rw [hset] at h + exact h + · exact ConvexOn.smul ha (convexOn_id (convex_Ioi _)) + +/-- `log x + √(2x) · log (2x) ≤ (log 2 / 6) · x` for `x ≥ 16562`. The difference of the two sides +is concave, it is nonnegative at `x = 2` and nonpositive at `x = 16562 = 2 · 91²` (where `√(2x) += 182`; the only numerical input is `91^13 ≤ 2^85`), so it is nonpositive from there on. Adapted +from Mathlib, Mathlib/NumberTheory/Bertrand.lean, Apache 2.0 (the same argument proves +`Bertrand.real_main_inequality` there, with other numbers). +-/ +private theorem log_add_sqrt_mul_log_le {x : ℝ} (hx : 16562 ≤ x) : + Real.log x + √(2 * x) * Real.log (2 * x) ≤ Real.log 2 / 6 * x := by + have hlog2 : 0 < Real.log 2 := Real.log_pos (by norm_num) + have hat2 : (0 : ℝ) ≤ + Real.log 2 + √(2 * 2) * Real.log (2 * 2) - Real.log 2 / 6 * 2 := by + rw [Real.sqrt_mul_self (by norm_num), Real.log_mul (by norm_num) (by norm_num)] + linarith + have hat16562 : Real.log 16562 + √(2 * 16562) * Real.log (2 * 16562) + - Real.log 2 / 6 * 16562 ≤ 0 := by + have hlog91 : 13 * Real.log 91 ≤ 85 * Real.log 2 := by + have h := Real.log_le_log (by positivity) (show (91 : ℝ) ^ 13 ≤ 2 ^ 85 by norm_num) + rw [Real.log_pow, Real.log_pow] at h + exact_mod_cast h + have hlogx : Real.log 16562 = Real.log 2 + 2 * Real.log 91 := by + rw [show (16562 : ℝ) = 2 * 91 ^ 2 by norm_num, Real.log_mul (by norm_num) (by norm_num), + Real.log_pow] + norm_num + have hlog2x : Real.log (182 * 182) = 2 * Real.log 2 + 2 * Real.log 91 := by + rw [show (182 * 182 : ℝ) = 2 ^ 2 * 91 ^ 2 by norm_num, + Real.log_mul (by norm_num) (by norm_num), Real.log_pow, Real.log_pow] + norm_num + rw [show (2 * 16562 : ℝ) = 182 * 182 by norm_num, Real.sqrt_mul_self (by norm_num), hlogx, + hlog2x] + -- `366 log 91 ≤ 366 · (85/13) log 2 < (16562/6 - 365) log 2` + linarith only [hlog91, hlog2] + have hconcave := concaveOn_log_add_sqrt_mul_log_sub (Real.log 2 / 6) (by positivity) + have hatx := hconcave.right_le_of_le_left'' + (x := 2) (y := 16562) (z := x) (by norm_num [Set.mem_Ioi]) (by rw [Set.mem_Ioi]; linarith) + (by norm_num) hx (hat16562.trans hat2) + linarith only [hatx, hat16562] + +/-- The same estimate for natural numbers, without logarithms. -/ +private theorem pow_mul_pow_sqrt_le_two_pow {n : ℕ} (hn : 16562 ≤ n) : + n ^ 6 * (2 * n) ^ (6 * Nat.sqrt (2 * n)) ≤ 2 ^ n := by + have hx : (16562 : ℝ) ≤ n := by exact_mod_cast hn + have hreal := log_add_sqrt_mul_log_le hx + have hsqrt : (Nat.sqrt (2 * n) : ℝ) ≤ √(2 * n) := by + exact_mod_cast Real.nat_sqrt_le_real_sqrt (a := 2 * n) + have hlog : 0 ≤ Real.log (2 * n) := Real.log_nonneg (by linarith) + have hpos : (0 : ℝ) < n := by linarith + rw [← Nat.cast_le (α := ℝ)] + push_cast + rw [← Real.log_le_log_iff (by positivity) (by positivity), + Real.log_mul (by positivity) (by positivity), Real.log_pow, Real.log_pow, Real.log_pow] + push_cast + -- `6 log n + 6 ⌊√(2n)⌋ log(2n) ≤ 6 (log n + √(2n) log(2n)) ≤ n log 2` + linarith [hreal, mul_le_mul_of_nonneg_right hsqrt hlog] + +/-! +## 3. The count for `n ≥ 16562` -/ + +/-- `2^n ≤ (2n)^(2k)` for `n ≥ 16562`. If it failed, the sixth power of `4^n < n · (2n)^√(2n) · +4^(2n/3) · (2n)^k` would give `2^(12n) < (n^6 (2n)^(6√(2n))) · 2^(8n) · 2^(3n) ≤ 2^(12n)`. +-/ +private theorem two_pow_le_of_large {n : ℕ} (hn : 16562 ≤ n) : + 2 ^ n ≤ (2 * n) ^ (2 * (bertrandPrimes n).card) := by + by_contra hcon + have hfew : (2 * n) ^ (2 * (bertrandPrimes n).card) ≤ 2 ^ n := (not_le.mp hcon).le + have herdos : 4 ^ n < n * ((2 * n) ^ Nat.sqrt (2 * n) * 4 ^ (2 * n / 3) + * (2 * n) ^ (bertrandPrimes n).card) := + (Nat.four_pow_lt_mul_centralBinom n (by omega)).trans_le + (Nat.mul_le_mul_left _ (centralBinom_le n (by omega))) + have hsmall := pow_mul_pow_sqrt_le_two_pow hn + have hmiddle : 4 ^ (6 * (2 * n / 3)) ≤ 4 ^ (4 * n) := + Nat.pow_le_pow_right (by norm_num) (by omega) + refine lt_irrefl ((4 ^ n) ^ 6) ?_ + calc (4 ^ n) ^ 6 + < (n * ((2 * n) ^ Nat.sqrt (2 * n) * 4 ^ (2 * n / 3) + * (2 * n) ^ (bertrandPrimes n).card)) ^ 6 := Nat.pow_lt_pow_left herdos (by norm_num) + _ = (n ^ 6 * (2 * n) ^ (6 * Nat.sqrt (2 * n))) * 4 ^ (6 * (2 * n / 3)) + * ((2 * n) ^ (2 * (bertrandPrimes n).card)) ^ 3 := by ring + _ ≤ 2 ^ n * 4 ^ (4 * n) * (2 ^ n) ^ 3 := by gcongr + _ = (4 ^ n) ^ 6 := by + rw [mul_comm 4 n, pow_mul 4 n 4, show (4 : ℕ) ^ n = 2 ^ n * 2 ^ n by rw [← mul_pow]; rfl] + ring + +/-- The count of step 3 with natural logarithms: `n log 2 ≤ 2 k log (2n)`. -/ +private theorem mul_log_two_le_card_bertrandPrimes_mul_log {n : ℕ} (hn : 16562 ≤ n) : + (n : ℝ) * Real.log 2 ≤ 2 * (bertrandPrimes n).card * Real.log (2 * n) := by + have hpow : (2 : ℝ) ^ n ≤ (2 * (n : ℝ)) ^ (2 * (bertrandPrimes n).card) := by + exact_mod_cast two_pow_le_of_large hn + have hlog := Real.log_le_log (by positivity) hpow + rw [Real.log_pow, Real.log_pow] at hlog + exact_mod_cast hlog + +/-! +## 4. Half-open windows `[x/2, x)` with real endpoints -/ + +/-- For a real `x ≥ 4` the natural number `n = ⌈x/2⌉ - 1`, written below as `(⌈x⌉ - 1) / 2`, +satisfies `n ≥ 1`, `n ≥ x/4` and `2n < x`, and every natural number in `(n, 2n]` lies in `[x/2, +x)`. +-/ +private theorem exists_nat_window {x : ℝ} (hx : 4 ≤ x) : + ∃ n : ℕ, 1 ≤ n ∧ x / 4 ≤ (n : ℝ) ∧ 2 * (n : ℝ) < x ∧ + ∀ p : ℕ, n < p → p ≤ 2 * n → x / 2 ≤ (p : ℝ) ∧ (p : ℝ) < x := by + have hle : x ≤ (⌈x⌉₊ : ℝ) := Nat.le_ceil x + have hlt : (⌈x⌉₊ : ℝ) < x + 1 := Nat.ceil_lt_add_one (by linarith) + have hceil : 4 ≤ ⌈x⌉₊ := by exact_mod_cast hx.trans hle + obtain ⟨n, hn⟩ : ∃ n, n = (⌈x⌉₊ - 1) / 2 := ⟨_, rfl⟩ + have hlow : (⌈x⌉₊ : ℝ) ≤ 2 * n + 2 := by exact_mod_cast (by omega : ⌈x⌉₊ ≤ 2 * n + 2) + have hhigh : (2 * n + 1 : ℝ) ≤ (⌈x⌉₊ : ℝ) := by exact_mod_cast (by omega : 2 * n + 1 ≤ ⌈x⌉₊) + refine ⟨n, by omega, by linarith, by linarith, fun p hnp hp2n => ?_⟩ + have hnp' : (n : ℝ) + 1 ≤ p := by exact_mod_cast hnp + have hp2n' : (p : ℝ) ≤ 2 * n := by exact_mod_cast hp2n + exact ⟨by linarith, by linarith⟩ + +/-- For every real `x ≥ 4` there is a prime `p` with `x/2 ≤ p < x`, by Bertrand's postulate. -/ +theorem exists_prime_half_le_and_lt (x : ℝ) (hx : 4 ≤ x) : + ∃ p : ℕ, p.Prime ∧ x / 2 ≤ (p : ℝ) ∧ (p : ℝ) < x := by + obtain ⟨n, hn1, -, -, hwindow⟩ := exists_nat_window hx + obtain ⟨p, hprime, hnp, hp2n⟩ := Nat.exists_prime_lt_and_le_two_mul n (by omega) + exact ⟨p, hprime, hwindow p hnp hp2n⟩ + +/-- If `(n, 2n]` lies in the window and `n ≥ x/4` is large, the window has at least `x / (12 log x)` +primes: `(x/4) log 2 ≤ n log 2 ≤ 2 k log (2n) ≤ 2 k log x`, and `log 2 ≥ 2/3`. +-/ +private theorem div_log_le_card_of_large {x : ℝ} {n : ℕ} {S : Finset ℕ} (hx : 4 ≤ x) + (hn : 16562 ≤ n) (hn4 : x / 4 ≤ (n : ℝ)) (h2n : 2 * (n : ℝ) < x) (hsub : bertrandPrimes n ⊆ S) : + x / (12 * Real.log x) ≤ (S.card : ℝ) := by + have hcard : ((bertrandPrimes n).card : ℝ) ≤ S.card := by exact_mod_cast card_le_card hsub + have hn1 : (1 : ℝ) ≤ n := by exact_mod_cast (by omega : 1 ≤ n) + have hlog : Real.log (2 * n) ≤ Real.log x := Real.log_le_log (by linarith) h2n.le + have hlog0 : 0 ≤ Real.log (2 * n) := Real.log_nonneg (by linarith) + have hlog2 : 2 / 3 ≤ Real.log 2 := by linarith [Real.log_two_gt_d9] + rw [div_le_iff₀ (mul_pos (by norm_num) (Real.log_pos (by linarith)))] + calc x = 6 * (x / 4 * (2 / 3)) := by ring + _ ≤ 6 * (n * Real.log 2) := by gcongr + _ ≤ 6 * (2 * (bertrandPrimes n).card * Real.log (2 * n)) := + mul_le_mul_of_nonneg_left (mul_log_two_le_card_bertrandPrimes_mul_log hn) (by norm_num) + _ ≤ 6 * (2 * S.card * Real.log x) := by gcongr + _ = S.card * (12 * Real.log x) := by ring + +/-- There is a `c > 0` such that, for every real `x ≥ 4`, every finite set of natural numbers that +contains all primes `p` with `x/2 ≤ p < x` has at least `c · x / log x` elements. (This form +applies to the set of those primes however it is written down.) +-/ +theorem exists_forall_mul_div_log_le_card_primes : + ∃ c : ℝ, 0 < c ∧ ∀ x : ℝ, 4 ≤ x → ∀ S : Finset ℕ, + (∀ p : ℕ, p.Prime → x / 2 ≤ (p : ℝ) → (p : ℝ) < x → p ∈ S) → + c * (x / Real.log x) ≤ (S.card : ℝ) := by + -- `66248 = 4 · 16562`: for `x` below it one prime is enough + refine ⟨1 / 66248, by norm_num, fun x hx S hS => ?_⟩ + obtain ⟨n, hn1, hn4, h2n, hwindow⟩ := exists_nat_window hx + have hlogx : 1 ≤ Real.log x := one_le_log_of_three_le (by linarith) + rcases le_or_gt 16562 n with hlarge | hsmall + · -- The primes of `(n, 2n]` lie in the window. + have hsub : bertrandPrimes n ⊆ S := fun p hp => by + obtain ⟨⟨hnp, hp2n⟩, hprime⟩ := mem_bertrandPrimes.1 hp + exact hS p hprime (hwindow p hnp hp2n).1 (hwindow p hnp hp2n).2 + calc 1 / 66248 * (x / Real.log x) ≤ 1 / 12 * (x / Real.log x) := by gcongr; norm_num + _ = x / (12 * Real.log x) := by field_simp + _ ≤ (S.card : ℝ) := div_log_le_card_of_large hx hlarge hn4 h2n hsub + · -- Here `x ≤ 4n < 66248`, and one prime is enough. + obtain ⟨p, hprime, hlow, hhigh⟩ := exists_prime_half_le_and_lt x hx + have hone : (1 : ℝ) ≤ S.card := by + exact_mod_cast Finset.card_pos.mpr ⟨p, hS p hprime hlow hhigh⟩ + have hn : (n : ℝ) ≤ 16562 := by exact_mod_cast hsmall.le + calc 1 / 66248 * (x / Real.log x) ≤ 1 / 66248 * (x / 1) := by gcongr + _ ≤ 1 := by linarith + _ ≤ (S.card : ℝ) := hone + +/-! +## 5. The window `[√D/2, √D)` used in the modular reduction -/ + +/-- There is a `c > 0` such that, for every natural number `D ≥ 16`, every finite set of natural +numbers that contains all primes `p` with `√D/2 ≤ p < √D` has at least `c · √D / log D` +elements. +-/ +theorem exists_forall_mul_sqrt_div_log_le_card_primes : + ∃ c : ℝ, 0 < c ∧ ∀ D : ℕ, 16 ≤ D → ∀ S : Finset ℕ, + (∀ p : ℕ, p.Prime → √D / 2 ≤ (p : ℝ) → (p : ℝ) < √D → p ∈ S) → + c * (√D / Real.log D) ≤ (S.card : ℝ) := by + obtain ⟨c, hc, hcount⟩ := exists_forall_mul_div_log_le_card_primes + refine ⟨c, hc, fun D hD S hS => ?_⟩ + have hlog : 0 < Real.log D := Real.log_pos (by exact_mod_cast (by omega : 1 < D)) + calc c * (√D / Real.log D) ≤ c * (√D / (Real.log D / 2)) := by + gcongr + linarith + _ = c * (√D / Real.log (√D)) := by rw [Real.log_sqrt (Nat.cast_nonneg D)] + _ ≤ (S.card : ℝ) := hcount _ ((Real.le_sqrt_of_sq_le (by norm_num; exact_mod_cast hD))) S hS + +end Nat diff --git a/Complexity/Foundations/RingTheory/CyclicIntRing.lean b/Complexity/Foundations/RingTheory/CyclicIntRing.lean new file mode 100644 index 000000000..94ae6d738 --- /dev/null +++ b/Complexity/Foundations/RingTheory/CyclicIntRing.lean @@ -0,0 +1,84 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.RingTheory.AdjoinRoot + +/-! +# Integer polynomials modulo `X^p - 1` + +## Main definitions + +* `CyclicIntRing`: the integer polynomial quotient. +* `CyclicIntRing.coeff`: coefficients in the unique representative of degree less than `p`. + +## Main results + +* `CyclicIntRing.coeff_x_pow`: powers have a single coefficient at the residue of their + exponent. + +## References + +Adapted from [Anthropic's +Hashing.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec3/Theorem17/Hashing.lean#L57-L101). +-/ + +@[expose] public section + +namespace Cslib + +open Polynomial in +/-- The quotient ring `ℤ[X]/(X^p - 1)`. -/ +abbrev CyclicIntRing (p : ℕ) : Type := AdjoinRoot ((X : ℤ[X]) ^ p - C 1) + +namespace CyclicIntRing + +open Polynomial + +variable {p : ℕ} + +/-- The element `x` of `ℤ[x]/(x^p − 1)`. -/ +noncomputable def x (p : ℕ) : CyclicIntRing p := AdjoinRoot.root _ + +/-- The coefficient of degree `r` in an element `z` of the quotient, as a linear map. It is the +coefficient of `x^r` in the unique polynomial of degree less than `p` that represents `z` +(Mathlib's `AdjoinRoot.modByMonicHom` gives that polynomial; it asks for the fact that `x^p − 1` +is monic, which holds as `p ≠ 0`). +-/ +noncomputable def coeff (hp : p ≠ 0) (r : ℕ) : CyclicIntRing p →ₗ[ℤ] ℤ := + lcoeff ℤ r ∘ₗ AdjoinRoot.modByMonicHom (monic_X_pow_sub_C (1 : ℤ) hp) + +/-- The coefficient, in terms of the representing polynomial. -/ +theorem coeff_apply (hp : p ≠ 0) (r : ℕ) (z : CyclicIntRing p) : + coeff hp r z = (AdjoinRoot.modByMonicHom (monic_X_pow_sub_C (1 : ℤ) hp) z).coeff r := + rfl + +/-- In `ℤ[x]/(x^p − 1)` we have `x^p = 1`. -/ +private theorem x_pow_self (p : ℕ) : x p ^ p = 1 := by + have h : AdjoinRoot.mk ((X : ℤ[X]) ^ p - C 1) ((X : ℤ[X]) ^ p - C 1) = 0 := AdjoinRoot.mk_self + rw [map_sub, map_pow, AdjoinRoot.mk_X, C_1, map_one] at h + exact sub_eq_zero.1 h + +/-- The coefficient of `x^r` in the power `x^k` of `ℤ[x]/(x^p − 1)` is 1 if `r = k mod p` and 0 +otherwise. +-/ +theorem coeff_x_pow (hp : p ≠ 0) (k r : ℕ) : + coeff hp r (x p ^ k) = if r = k % p then 1 else 0 := by + have hred : x p ^ k = x p ^ (k % p) := by + conv_lhs => rw [← Nat.div_add_mod k p, pow_add, pow_mul, x_pow_self, one_pow, one_mul] + have hdeg : ((X : ℤ[X]) ^ (k % p)).degree < ((X : ℤ[X]) ^ p - C 1).degree := by + rw [degree_X_pow, degree_X_pow_sub_C (Nat.pos_of_ne_zero hp)] + exact_mod_cast Nat.mod_lt k (Nat.pos_of_ne_zero hp) + rw [coeff_apply, hred, x, ← AdjoinRoot.mk_X, ← map_pow, AdjoinRoot.modByMonicHom_mk, + (modByMonic_eq_self_iff (monic_X_pow_sub_C (1 : ℤ) hp)).2 hdeg, coeff_X_pow] + +end CyclicIntRing + + +end Cslib diff --git a/ComplexityTests/ExactTriangleHashing.lean b/ComplexityTests/ExactTriangleHashing.lean new file mode 100644 index 000000000..41df09a87 --- /dev/null +++ b/ComplexityTests/ExactTriangleHashing.lean @@ -0,0 +1,115 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +import Complexity.Algorithms.ExactTriangle.Hashing + +/-! +# Modular Exact Triangle counting and prime-selection regressions + +Concrete zeros, false positives, coefficient wraparound, minimum selection, the integer window +boundary and an admitted instance of the universal bound. These assert no native running time. +-/ + +namespace ComplexityTests.ExactTriangleHashing + +open Cslib Cslib.Algorithms.ExactTriangle + +private def ab (a _ : Fin 2) : ℤ := if a = 0 then 0 else 2 +private def z (_ _ : Fin 2) : ℤ := 0 + +private theorem window16 : primesInRange 16 = {2, 3} := by + rw [primesInRange_eq_filter] + decide + +-- Both endpoints matter: prime 2 is admitted at the lower endpoint for D=16; +-- prime 5 is excluded at the upper endpoint for D=25, and prime 3 is admitted there. +example : 2 ∈ primesInRange 16 ∧ 5 ∉ primesInRange 25 ∧ 3 ∈ primesInRange 25 := by + simp only [mem_primesInRange_iff_sq] + decide + +example : 2 ∉ primesInRange 17 := by rw [mem_primesInRange_iff_sq]; decide + +example : countZeroMod ab z z 2 = 8 ∧ countZeroMod ab z z 3 = 4 := by + unfold countZeroMod weightSum ab z + decide + +example : zeroCount ab z z = 4 ∧ falsePositiveCount ab z z 2 = 4 ∧ + falsePositiveCount ab z z 3 = 0 := by + unfold zeroCount falsePositiveCount IsFalsePositive weightSum ab z + decide + +example : countZeroMod ab z z 0 = 4 ∧ falsePositiveCount ab z z 0 = 0 := by + unfold countZeroMod falsePositiveCount IsFalsePositive weightSum ab z + decide + +-- Counting distinguishes the two candidate primes; the minimum is 3. +example : IsSelectedPrime ab z z 16 3 ∧ ¬IsSelectedPrime ab z z 16 2 := by + simp only [IsSelectedPrime, window16] + unfold countZeroMod weightSum ab z + decide + +-- A tie permits either minimum prime; the predicate does not impose a tie-break. +example : IsSelectedPrime (fun _ _ : Fin 1 => 0) (fun _ _ => 0) (fun _ _ => 0) 16 2 ∧ + IsSelectedPrime (fun _ _ : Fin 1 => 0) (fun _ _ => 0) (fun _ _ => 0) 16 3 := by + constructor <;> refine ⟨by rw [window16]; decide, ?_⟩ <;> intro q _ <;> + simp [countZeroMod, weightSum] + +example : HasZeroTriangle ab z z := ⟨0, 0, 0, rfl⟩ +example : 0 < zeroCount ab z z := (zeroCount_pos_iff ab z z).mpr ⟨0, 0, 0, rfl⟩ + +private def one (_ _ : Fin 2) : ℤ := 1 + +example : ¬HasZeroTriangle one one one := by + rintro ⟨a, b, c, h⟩ + norm_num [one] at h + +example : zeroCount one one one = 0 ∧ countZeroMod one one one 3 = 8 ∧ + falsePositiveCount one one one 3 = 8 := by + unfold zeroCount countZeroMod falsePositiveCount IsFalsePositive weightSum one + decide + +-- A negative residue and a positive residue wrap to exponent zero modulo 3. +example : CyclicIntRing.coeff (by decide : 3 ≠ 0) 0 + ((matP (fun _ _ : Fin 2 => (-1 : ℤ)) 3 * + matQ (fun _ _ : Fin 2 => (1 : ℤ)) 3) 0 0) = 2 := by + rw [coeff_matP_mul_matQ (fun _ _ : Fin 2 => (1 : ℤ)) + (fun _ _ : Fin 2 => (-1 : ℤ)) (by decide) 0 0 0 (by decide)] + decide + +-- These matrices are asymmetric: transposing either input changes the selected count. +private def ac (a c : Fin 2) : ℤ := if a = 0 then if c = 0 then -1 else 2 else 0 +private def bc (b c : Fin 2) : ℤ := if b = 0 ∧ c = 1 then 1 else 0 + +example : CyclicIntRing.coeff (by decide : 3 ≠ 0) 0 ((matP ac 3 * matQ bc 3) 0 0) = 1 := by + rw [coeff_matP_mul_matQ bc ac (by decide) 0 0 0 (by decide)] + unfold ac bc + decide + +example : CyclicIntRing.coeff (by decide : 2 ≠ 0) 1 (CyclicIntRing.x 2 ^ 5) = 1 := by + rw [CyclicIntRing.coeff_x_pow] + norm_num + +example : CyclicIntRing.coeff (by decide : 2 ≠ 0) 3 (CyclicIntRing.x 2 ^ 5) = 0 := by + rw [CyclicIntRing.coeff_x_pow] + norm_num + +example : zeroCount (fun _ _ : Fin 0 => 0) (fun _ _ => 0) (fun _ _ => 0) = 0 := by + simp [zeroCount] + +example : ¬WeightsBoundedBy (fun _ _ : Fin 1 => (0 : ℤ)) (fun _ _ => 0) (fun _ _ => 0) (-1) := by + norm_num [WeightsBoundedBy] + +-- The full bound's n,D,kappa hypotheses and input bound have a concrete witness. +example : (falsePositiveCount (fun _ _ : Fin 16 => 0) (fun _ _ => 0) (fun _ _ => 0) 2 : ℝ) ≤ + Hashing.falsePositiveConst * (1 * (16 : ℝ) ^ 3 * Real.log 16 / Real.sqrt 16) := by + apply Hashing.falsePositiveCount_le_const_mul (by decide : 16 ≤ 16) (by decide : 16 ≤ 16) + (by norm_num : (1 : ℝ) ≤ 1) + · norm_num [WeightsBoundedBy] + · refine ⟨by rw [window16]; decide, ?_⟩ + intro q _ + simp [countZeroMod, weightSum] + +end ComplexityTests.ExactTriangleHashing diff --git a/NOTICE b/NOTICE index 3ab01a2df..95b9c8d57 100644 --- a/NOTICE +++ b/NOTICE @@ -1,10 +1,11 @@ The 3SUM/Exact Triangle reductions, their finite counting lemmas and native matrix builder in -Complexity/Algorithms/ExactTriangle/{Defs,Lopsided}.lean, +Complexity/Algorithms/ExactTriangle/{Defs,Weights,Lopsided,Hashing}.lean, Complexity/Algorithms/ThreeSum/, Complexity/Algorithms/WordRAM/ThreeSum/, Complexity/Foundations/Data/Finset/Average.lean, Complexity/Foundations/Data/Matrix/IntegerBits.lean and -Complexity/Foundations/NumberTheory/PrimeCounting.lean are adapted from +Complexity/Foundations/NumberTheory/{PrimeCounting,PrimeWindow}.lean and +Complexity/Foundations/RingTheory/CyclicIntRing.lean are adapted from anthropics/formal-math, 3sum-apsp, revision 16a5ce41304fcdaea4e0be7c132e1c361dc26da5. @@ -13,3 +14,7 @@ Sparse Lopsided Graphs" Copyright 2026 Anthropic, PBC This product is licensed under the Apache License, Version 2.0 (see LICENSE). + +The prime-window proof also adapts Mathlib/NumberTheory/Bertrand.lean, +Copyright (c) 2020 Patrick Stevens; authors Patrick Stevens, Bolton Bailey, +under the Apache License, Version 2.0. diff --git a/ROADMAP.md b/ROADMAP.md index 5b3b23001..d31b9ede1 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -422,8 +422,9 @@ each statement against the numbered result. Organize modules by subject, not cha has the Convolution-3SUM/Exact Triangle correctness reduction, a charged native single-instance matrix builder and the complete finite mathematical Chan–He reduction ([#1435]), including ordinary distinct-position inputs and explicit size bounds. The finite lopsided matrix/counting - interface ([#1438]) adds padding, detection and signed digit reconstruction; it still needs - modular Exact Triangle hashing/false-positive splitting, tensor/sparse-query programs, native + interface ([#1438]) adds padding, detection and signed digit reconstruction. Modular counting + and the selected-prime false-positive bound ([#1442]) are proved mathematically; it still needs + residue/chunk/piece splitting and witness recovery, tensor/sparse-query programs, native Chan–He preprocessing, the reduction host, the fast Exact Triangle solver and full input/time/width/ asymptotic composition, then the improved Section 4 route to exponent 1.9992; size-based width selection with an input-fit guarantee and explicit overhead; expected-time rules for unbounded @@ -550,4 +551,5 @@ not planned, not as proved. [#1394]: https://github.com/cslib-community/complexitylib/issues/1394 [#1435]: https://github.com/cslib-community/complexitylib/issues/1435 [#1438]: https://github.com/cslib-community/complexitylib/issues/1438 +[#1442]: https://github.com/cslib-community/complexitylib/issues/1442 [#1449]: https://github.com/cslib-community/complexitylib/issues/1449 diff --git a/UPSTREAM.md b/UPSTREAM.md index 87d9cb528..2bc29e783 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -174,8 +174,16 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he `Set.EqOn`; no additional weighted-triangle predicate or source machine is introduced. This is the mathematical interface milestone [#1438](https://github.com/cslib-community/complexitylib/issues/1438), with no sparsity assumption, - preprocessing/time claim or solver. Modular hashing/false-positive splitting and tensor/sparse - query programs remain in #1394, alongside the native and asymptotic composition below. + preprocessing/time claim or solver. `Algorithms/ExactTriangle/{Weights,Hashing}.lean` now adapts + the modular counting/coefficient identities, minimum-count prime and uniform false-positive + bound from `Sec3/Theorem17/Hashing.lean`, using supplied matrices and the existing decision + predicate. Its integer-square window characterisation supports later native enumeration. + The quotient/coefficient API lives in `Foundations/RingTheory/CyclicIntRing.lean`; the uniform + all-window prime-count bound lives in `Foundations/NumberTheory/PrimeWindow.lean`, retaining + the source credit to Mathlib, Patrick Stevens and Bolton Bailey. These are mathematical + statements, not native instructions or an assumed solver. See + [#1442](https://github.com/cslib-community/complexitylib/issues/1442). Residue/chunk/piece splitting, + witness recovery and tensor/sparse query programs remain in #1394, alongside native composition. The existing signed and unsigned APIs were reused; no source machine or separate run semantics were copied. The full reduction host, native Chan–He preprocessing, fast Exact Triangle and full input/time/width/asymptotic composition remain in From 0d8da3e78edceaa2e11fa3c31a1b44fce10dbdbd Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 06:09:24 -0400 Subject: [PATCH 03/73] doc(threeSum): cite the lopsided triangle paper and clarify adaptation scope --- Complexity/Algorithms/ExactTriangle/Hashing.lean | 1 + Complexity/Algorithms/ExactTriangle/Lopsided.lean | 1 + UPSTREAM.md | 13 +++++++------ references.bib | 12 ++++++++++++ 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Complexity/Algorithms/ExactTriangle/Hashing.lean b/Complexity/Algorithms/ExactTriangle/Hashing.lean index 6799e79fc..affaadd2e 100644 --- a/Complexity/Algorithms/ExactTriangle/Hashing.lean +++ b/Complexity/Algorithms/ExactTriangle/Hashing.lean @@ -39,6 +39,7 @@ asserted. The supplied three matrices retain the existing `HasZeroTriangle` sema ## References +The modular reduction is part of [AlmanWilliams2026, Section 3]. Direct adaptation of [Anthropic's Hashing.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec3/Theorem17/Hashing.lean), with the minimal count/selection definitions extracted from its `PaperStatements.lean`. diff --git a/Complexity/Algorithms/ExactTriangle/Lopsided.lean b/Complexity/Algorithms/ExactTriangle/Lopsided.lean index d52e77943..84694440f 100644 --- a/Complexity/Algorithms/ExactTriangle/Lopsided.lean +++ b/Complexity/Algorithms/ExactTriangle/Lopsided.lean @@ -36,6 +36,7 @@ fast solver or subquadratic running time. ## References +The lopsided matrix interface is used in [AlmanWilliams2026, Section 3]. Adapted from [Anthropic's MatrixLanguage.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec3/MatrixLanguage.lean) and the lopsided definitions in [PaperStatements.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/PaperStatements.lean#L798-L852). The adaptation uses `Set.EqOn`, pointwise signed parts, and direct finite cardinalities. diff --git a/UPSTREAM.md b/UPSTREAM.md index 2bc29e783..8b49547da 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -158,6 +158,13 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he Given its arguments in registers, it writes one instance in at most `131*t² + 5` instructions at width `w + 2`, excluding the halt; its theorem identifies the three encoded matrices and preserves the input. Layout, signed index/filler bounds and target address bounds are explicit. + The existing signed and unsigned APIs were reused; no source machine or separate run semantics + were copied. The full reduction host, native Chan–He preprocessing, fast Exact Triangle and full + input/time/width/asymptotic composition remain in + [#1394](https://github.com/cslib-community/complexitylib/issues/1394), including the improved + Section 4 route needed for the published exponent 1.9992. Anthropic's copyright and attribution + are retained; the native proofs and representation choices are local. + `Algorithms/ThreeSum/ThreeSets.lean` and `ArrayPacking.lean` provide the generic distinct-position frontend and three-array packing. `Algorithms/ThreeSum/ChanHe/` now adapts the core mathematical reduction: minimum-prime searches, collision/heavy-element bounds, light-element arrays, bounded @@ -184,12 +191,6 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he statements, not native instructions or an assumed solver. See [#1442](https://github.com/cslib-community/complexitylib/issues/1442). Residue/chunk/piece splitting, witness recovery and tensor/sparse query programs remain in #1394, alongside native composition. - The existing signed and unsigned APIs were reused; no source machine or separate run semantics - were copied. The full reduction host, native Chan–He preprocessing, fast Exact Triangle and full - input/time/width/asymptotic composition remain in - [#1394](https://github.com/cslib-community/complexitylib/issues/1394), including the improved - Section 4 route needed for the published exponent 1.9992. Anthropic's copyright and attribution - are retained; the native proofs and representation choices are local. * **Typed RAM upstream core.** [CSLib #1158](https://github.com/leanprover/cslib/pull/1158) at `beea60a15eddc91d93e92fa5af9fb965c351d0d5` proposes `Cslib/Computability/Machines/RAM/Basic.lean`, in namespace `Cslib.RAM.Typed`. diff --git a/references.bib b/references.bib index 8cf6daceb..f42e7901a 100644 --- a/references.bib +++ b/references.bib @@ -103,6 +103,18 @@ @misc{Algolean2026 url = {https://github.com/Shreyas4991/Algolean} } +@misc{AlmanWilliams2026, + author = {Alman, Josh and Vassilevska Williams, Virginia}, + title = {Truly Subquadratic {3SUM} and Truly Subcubic {APSP} via Triangles in + Sparse Lopsided Graphs}, + year = {2026}, + eprint = {2610.06783}, + archivePrefix = {arXiv}, + primaryClass = {cs.DS}, + doi = {10.48550/arXiv.2610.06783}, + url = {https://arxiv.org/abs/2610.06783v1} +} + @article{AllenderBuhrmanKouckyVanMelkebeekRonneburger2006, author = {Allender, Eric and Buhrman, Harry and Kouck{\'y}, Michal and van Melkebeek, Dieter and Ronneburger, Detlef}, title = {Power from Random Strings}, From 8d80ecce623c8ab4a08742ed6944892ebe9f9b21 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 06:09:49 -0400 Subject: [PATCH 04/73] doc(bibliography): sort the new paper entry --- references.bib | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/references.bib b/references.bib index f42e7901a..af7ca6f2c 100644 --- a/references.bib +++ b/references.bib @@ -103,18 +103,6 @@ @misc{Algolean2026 url = {https://github.com/Shreyas4991/Algolean} } -@misc{AlmanWilliams2026, - author = {Alman, Josh and Vassilevska Williams, Virginia}, - title = {Truly Subquadratic {3SUM} and Truly Subcubic {APSP} via Triangles in - Sparse Lopsided Graphs}, - year = {2026}, - eprint = {2610.06783}, - archivePrefix = {arXiv}, - primaryClass = {cs.DS}, - doi = {10.48550/arXiv.2610.06783}, - url = {https://arxiv.org/abs/2610.06783v1} -} - @article{AllenderBuhrmanKouckyVanMelkebeekRonneburger2006, author = {Allender, Eric and Buhrman, Harry and Kouck{\'y}, Michal and van Melkebeek, Dieter and Ronneburger, Detlef}, title = {Power from Random Strings}, @@ -137,6 +125,18 @@ @article{AllenderRubinstein1988 doi = {10.1137/0217075} } +@misc{AlmanWilliams2026, + author = {Alman, Josh and Vassilevska Williams, Virginia}, + title = {Truly Subquadratic {3SUM} and Truly Subcubic {APSP} via Triangles in + Sparse Lopsided Graphs}, + year = {2026}, + eprint = {2610.06783}, + archivePrefix = {arXiv}, + primaryClass = {cs.DS}, + doi = {10.48550/arXiv.2610.06783}, + url = {https://arxiv.org/abs/2610.06783v1} +} + @article{AlonBoppana1987, author = {Alon, Noga and Boppana, Ravi B.}, title = {The Monotone Circuit Complexity of {B}oolean Functions}, From fd69be7bc459d83cab3863f6f6e2f59239b21595 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 09:02:59 -0400 Subject: [PATCH 05/73] fix(exactTriangle): repair the prime-window header and hashing documentation --- .../Algorithms/ExactTriangle/Hashing.lean | 115 +++++++++++------- .../Foundations/NumberTheory/PrimeWindow.lean | 4 +- .../Foundations/RingTheory/CyclicIntRing.lean | 1 - 3 files changed, 71 insertions(+), 49 deletions(-) diff --git a/Complexity/Algorithms/ExactTriangle/Hashing.lean b/Complexity/Algorithms/ExactTriangle/Hashing.lean index affaadd2e..fbb2834b6 100644 --- a/Complexity/Algorithms/ExactTriangle/Hashing.lean +++ b/Complexity/Algorithms/ExactTriangle/Hashing.lean @@ -43,7 +43,8 @@ The modular reduction is part of [AlmanWilliams2026, Section 3]. Direct adaptation of [Anthropic's Hashing.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec3/Theorem17/Hashing.lean), with the minimal count/selection definitions extracted from its `PaperStatements.lean`. -Source-derived prose below is paraphrased. The source pin is +The declaration docstrings describe the Lean definitions and statements in local notation. +The source pin is `anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. -/ @@ -52,16 +53,20 @@ Source-derived prose below is paraphrased. The source pin is namespace Cslib.Algorithms.ExactTriangle open Classical in -/-- Primes in the half-open window `[sqrt D / 2, sqrt D)`. -/ +/-- Prime numbers in the half-open real interval `[sqrt D / 2, sqrt D)`. +-/ noncomputable def primesInRange (D : ℕ) : Finset ℕ := (Finset.range D).filter fun p => p.Prime ∧ Real.sqrt D / 2 ≤ (p : ℝ) ∧ (p : ℝ) < Real.sqrt D -/-- Number of triangles whose sum vanishes modulo `p`. -/ +/-- Count vertex triples whose three edge weights have sum congruent to zero modulo `p`. +-/ def countZeroMod {n : ℕ} (wAB wBC wAC : Fin n → Fin n → ℤ) (p : ℕ) : ℕ := (Finset.univ.filter fun t : Fin n × Fin n × Fin n => weightSum wAB wBC wAC t.1 t.2.1 t.2.2 ≡ 0 [ZMOD (p : ℤ)]).card -/-- A window prime minimises the modular count over all window primes. -/ +/-- A prime in `primesInRange D` whose modular-zero count is minimal among all primes in that +window. Tied minima all satisfy this predicate. +-/ def IsSelectedPrime {n : ℕ} (wAB wBC wAC : Fin n → Fin n → ℤ) (D p : ℕ) : Prop := p ∈ primesInRange D ∧ ∀ q ∈ primesInRange D, countZeroMod wAB wBC wAC p ≤ countZeroMod wAB wBC wAC q @@ -81,8 +86,8 @@ private theorem toNat_emod_lt {p : ℕ} (hp : 0 < p) (u : ℤ) : (u % (p : ℤ)) /-! ### The primes in the range -/ -/-- Proof of Theorem 17, a prime p ∈ [√D/2, √D): the primes in the range are the primes `p` with -`√D/2 ≤ p < √D`. (The bound `p < D` in the definition excludes none of them.) +/-- Window membership is equivalent to primality and the two real endpoint inequalities. The +finite enumeration bound in `primesInRange` excludes no member of the interval. -/ theorem mem_primesInRange {D p : ℕ} : p ∈ primesInRange D ↔ p.Prime ∧ Real.sqrt D / 2 ≤ (p : ℝ) ∧ (p : ℝ) < Real.sqrt D := by @@ -93,7 +98,9 @@ theorem mem_primesInRange {D p : ℕ} : have hsq : p ^ 2 < D := by exact_mod_cast (Real.lt_sqrt (Nat.cast_nonneg p)).1 h.2.2 exact (Nat.le_self_pow two_ne_zero p).trans_lt hsq -/-- Window membership is an exact integer-square test, suitable for native enumeration. -/ +/-- Characterize window membership using primality and integer-square inequalities, including the +closed lower and open upper endpoints. +-/ theorem mem_primesInRange_iff_sq {D p : ℕ} : p ∈ primesInRange D ↔ p.Prime ∧ D ≤ 4 * p ^ 2 ∧ p ^ 2 < D := by rw [mem_primesInRange] @@ -111,7 +118,8 @@ theorem mem_primesInRange_iff_sq {D p : ℕ} : have h : (D : ℝ) ≤ 4 * (p : ℝ) ^ 2 := by exact_mod_cast hlo nlinarith -/-- A computable finite enumeration of exactly the same half-open prime window. -/ +/-- Enumerate the real prime window using a decidable filter of the integers below `D`. +-/ theorem primesInRange_eq_filter (D : ℕ) : primesInRange D = (Finset.range D).filter fun p => p.Prime ∧ D ≤ 4 * p ^ 2 ∧ p ^ 2 < D := by ext p @@ -137,20 +145,26 @@ section Counting variable {n : ℕ} (wAB wBC wAC : Fin n → Fin n → ℤ) -/-- A false positive of p is a triple (a,b,c) with S(a,b,c) ≠ 0 and p ∣ S(a,b,c). -/ +/-- A vertex triple has a nonzero integer weight sum that is congruent to zero modulo `p`. +-/ abbrev IsFalsePositive (p : ℕ) (t : Fin n × Fin n × Fin n) : Prop := weightSum wAB wBC wAC t.1 t.2.1 t.2.2 ≠ 0 ∧ (p : ℤ) ∣ weightSum wAB wBC wAC t.1 t.2.1 t.2.2 -/-- let falsePositiveCount(p) denote the number of false positives of p. -/ +/-- Count vertex triples satisfying `IsFalsePositive` for the supplied three weight matrices and +modulus. +-/ def falsePositiveCount (p : ℕ) : ℕ := (Finset.univ.filter fun t : Fin n × Fin n × Fin n => IsFalsePositive wAB wBC wAC p t).card -/-- zeroCount, the number of zero triangles. -/ +/-- Count vertex triples whose three supplied edge weights sum to zero as an integer. +-/ def zeroCount : ℕ := (Finset.univ.filter fun t : Fin n × Fin n × Fin n => weightSum wAB wBC wAC t.1 t.2.1 t.2.2 = 0).card -/-- A positive zero count is exactly the existing Exact Triangle decision predicate. -/ +/-- The zero-triple count is positive exactly when the supplied matrices satisfy +`HasZeroTriangle`. +-/ theorem zeroCount_pos_iff : 0 < zeroCount wAB wBC wAC ↔ HasZeroTriangle wAB wBC wAC := by classical simp only [zeroCount, Finset.card_pos, HasZeroTriangle] @@ -160,8 +174,8 @@ theorem zeroCount_pos_iff : 0 < zeroCount wAB wBC wAC ↔ HasZeroTriangle wAB wB · rintro ⟨a, b, c, h⟩ exact ⟨(a, b, c), Finset.mem_filter.mpr ⟨Finset.mem_univ _, h⟩⟩ -/-- This number is the number of false positives falsePositiveCount(p) plus zeroCount, the number of -zero triangles, which does not depend on p. +/-- Partition the modular-zero triples into false positives and exact zero-sum triples. The +exact-zero count is independent of the modulus. -/ theorem countZeroMod_eq (p : ℕ) : (countZeroMod wAB wBC wAC) p = (falsePositiveCount wAB wBC wAC) p + (zeroCount wAB wBC wAC) := by @@ -173,17 +187,21 @@ theorem countZeroMod_eq (p : ℕ) : (countZeroMod wAB wBC wAC) p = (falsePositiv by_cases h0 : weightSum wAB wBC wAC t.1 t.2.1 t.2.2 = 0 <;> simp [IsFalsePositive, Int.modEq_zero_iff_dvd, h0] -/-- let P[a,c] := x^{w(a,c) mod p}, a matrix over `ℤ[x]/(x^p − 1)`. `w mod p` is the residue in -`{0, …, p − 1}`. +/-- Encode the A–C weight at `(a,c)` as a power of the cyclic polynomial generator, with exponent +equal to its nonnegative residue modulo `p`. -/ noncomputable def matP (p : ℕ) : Matrix (Fin n) (Fin n) (CyclicIntRing p) := Matrix.of fun a c => CyclicIntRing.x p ^ (wAC a c % (p : ℤ)).toNat -/-- and Q[c,b] := x^{w(b,c) mod p}. -/ +/-- Encode the B–C weight at `(b,c)` in matrix position `(c,b)`. This orientation makes the +product sum over the middle index `c`. +-/ noncomputable def matQ (p : ℕ) : Matrix (Fin n) (Fin n) (CyclicIntRing p) := Matrix.of fun c b => CyclicIntRing.x p ^ (wBC b c % (p : ℤ)).toNat -/-- the coefficient of x^r in (PQ)[a,b] is the number of c ∈ C with w(a,c) + w(b,c) ≡ r (mod p). -/ +/-- For a nonzero modulus and a residue `r < p`, the coefficient of degree `r` in the cyclic +matrix product counts middle vertices whose two incident weights sum to `r` modulo `p`. +-/ theorem coeff_matP_mul_matQ {p : ℕ} (hp : p ≠ 0) (a b : Fin n) (r : ℕ) (hr : r < p) : CyclicIntRing.coeff hp r ((matP wAC p * matQ wBC p) a b) = ((Finset.univ.filter fun c : Fin n => @@ -200,8 +218,8 @@ theorem coeff_matP_mul_matQ {p : ℕ} (hp : p ≠ 0) (a b : Fin n) (r : ℕ) (hr refine Finset.sum_congr rfl fun c _ => ?_ simp only [eq_add_toNat_emod_iff hp hr] -/-- then falsePositiveCount(p) + zeroCount is the sum over the pairs (a,b) ∈ A × B of the -coefficient of x^{−w(a,b) mod p} in (PQ)[a,b]. +/-- Recover the total modular-zero count from the cyclic matrix product by summing, over `(a,b)`, +the coefficient at the residue of `-wAB a b`. -/ theorem falsePositiveCount_add_zeroCount_eq_sum_coeff {p : ℕ} (hp : p ≠ 0) : (((falsePositiveCount wAB wBC wAC) p + (zeroCount wAB wBC wAC) : ℕ) : ℤ) = @@ -235,7 +253,8 @@ theorem falsePositiveCount_add_zeroCount_eq_sum_coeff {p : ℕ} (hp : p ≠ 0) : end Counting -/-- the fewer than √D primes in the range. -/ +/-- For `D ≥ 16`, the number of primes in `primesInRange D` is strictly less than `sqrt D`. +-/ theorem card_primesInRange_lt (D : ℕ) (hD : 16 ≤ D) : ((primesInRange D).card : ℝ) < Real.sqrt D := by have hpos : 0 < Real.sqrt D := by linarith [four_le_sqrt hD] @@ -259,15 +278,17 @@ section Selection variable {n D p : ℕ} {κ : ℝ} (wAB wBC wAC : Fin n → Fin n → ℤ) -/-- There is a prime to select. -/ +/-- For every `D ≥ 16` and every three supplied weight matrices, the prime window contains a +minimum of the modular-zero count. +-/ theorem exists_isSelectedPrime (D : ℕ) (hD : 16 ≤ D) : ∃ p, IsSelectedPrime wAB wBC wAC D p := by obtain ⟨q, hq⟩ := Nat.exists_prime_half_le_and_lt (Real.sqrt D) (four_le_sqrt hD) exact Finset.exists_min_image (primesInRange D) (countZeroMod wAB wBC wAC) ⟨q, mem_primesInRange.2 hq⟩ -/-- We select the prime with the smallest count, which is also the prime with the fewest false -positives. +/-- A selected minimum of the modular-zero count also minimizes the false-positive count over the +same prime window. -/ theorem IsSelectedPrime.falsePositiveCount_le {wAB wBC wAC : Fin n → Fin n → ℤ} (hp : IsSelectedPrime wAB wBC wAC D p) : @@ -282,9 +303,8 @@ theorem IsSelectedPrime.falsePositiveCount_le {wAB wBC wAC : Fin n → Fin n → ### The bound on the number of false positives of the selected prime -/ open Classical in -/-- a triple with S(a,b,c) ≠ 0 is a false positive exactly of the primes in the range that divide -S(a,b,c). Since 0 < |S(a,b,c)| ≤ 3n^ν and these primes are at least √D/2, there are at most -log_{√D/2}(3n^ν) of them. +/-- For a fixed nonzero-sum triple and edge weights bounded by `n^κ`, bound the number of window +primes accepting that triple by the base-`sqrt D / 2` logarithm of `3*n^κ`. -/ theorem card_falsePositive_primes_le (hD : 16 ≤ D) (hT : WeightsBoundedBy wAB wBC wAC ((n : ℝ) ^ κ)) (t : Fin n × Fin n × Fin n) (ht : weightSum wAB wBC wAC t.1 t.2.1 t.2.2 ≠ 0) : @@ -316,8 +336,9 @@ theorem card_falsePositive_primes_le (hD : 16 ≤ D) (hT : WeightsBoundedBy wAB rw [Real.le_logb_iff_rpow_le (by linarith) hY, Real.rpow_natCast] exact hpow.trans hprod -/-- Hence the numbers of false positives of all the primes in the range add up to at most n³ -log_{√D/2}(3n^ν). +/-- Sum the per-triple prime-divisibility bound over all `n^3` triples. For `D ≥ 16`, `κ ≥ 1` and +edge weights bounded by `n^κ`, the sum of false-positive counts is at most `n^3 * logb (sqrt D / +2) (3*n^κ)`. -/ theorem sum_falsePositiveCount_le (hD : 16 ≤ D) (hκ : 1 ≤ κ) (hT : WeightsBoundedBy wAB wBC wAC ((n : ℝ) ^ κ)) : @@ -356,11 +377,9 @@ theorem sum_falsePositiveCount_le (hD : 16 ≤ D) (hκ : 1 ≤ κ) (hT : Weights end Selection -/-- By the prime number theorem there are Ω(√D/log D) primes in the range. - -NOTE. The modular reduction needs this for every `D ≥ 16`, not only for large `D`, and so it is -stated. It follows from a counting form of Bertrand's postulate; the prime number theorem is -not needed. +/-- There is a universal positive `c` such that every window with `D ≥ 16` contains at least `c * +sqrt D / log D` primes. The proof uses the all-window counting form of Bertrand’s postulate from +`PrimeWindow`, with no prime-number-theorem assumption. -/ theorem exists_le_card_primesInRange : ∃ c : ℝ, 0 < c ∧ ∀ D : ℕ, 16 ≤ D → @@ -369,7 +388,9 @@ theorem exists_le_card_primesInRange : exact ⟨c, hc, fun D hD => hcount D hD _ fun _ hp hge hlt => mem_primesInRange.2 ⟨hp, hge, hlt⟩⟩ -/-- falsePositiveCount(p) is at most the average over them, multiplied out. -/ +/-- Multiply the selected prime’s minimum false-positive count by the number of window primes to +obtain a bound by the sum of their false-positive counts. +-/ theorem IsSelectedPrime.falsePositiveCount_mul_card_le_sum {n D p : ℕ} {wAB wBC wAC : Fin n → Fin n → ℤ} (hp : IsSelectedPrime wAB wBC wAC D p) : @@ -378,7 +399,8 @@ theorem IsSelectedPrime.falsePositiveCount_mul_card_le_sum {n D p : ℕ} {wAB wB rw [mul_comm, ← smul_eq_mul] exact Finset.card_nsmul_le_sum _ _ _ hp.falsePositiveCount_le -/-- log D = O(log(√D/2)) for D ≥ 16. -/ +/-- For `D ≥ 16`, compare `log D` with four times `log (sqrt D / 2)`. +-/ theorem log_le_four_mul_log_sqrt_div_two (D : ℕ) (hD : 16 ≤ D) : Real.log D ≤ 4 * Real.log (Real.sqrt D / 2) := by have hD16 : (16 : ℝ) ≤ D := by exact_mod_cast hD @@ -391,9 +413,9 @@ theorem log_le_four_mul_log_sqrt_div_two (D : ℕ) (hD : 16 ≤ D) : rw [Real.log_div hsqrt.ne' (by norm_num), Real.log_sqrt (by linarith)] linarith [hlog16] -/-- falsePositiveCount(p) is at most the average over them, so [...] falsePositiveCount(p) = O(n³ -log(3n^ν)/√D): if there are at least `c √D/log D` primes in the range, then -`falsePositiveCount(p) ≤ (4/c) n³ log(3n^κ)/√D`. +/-- For `16 ≤ D ≤ n`, `κ ≥ 1` and edge weights bounded by `n^κ`, a positive pointwise prime-count +lower bound `c * sqrt D / log D` gives every selected prime at most `(4/c) * n^3 * log (3*n^κ) / +sqrt D` false positives. -/ theorem falsePositiveCount_le_of_le_card_primesInRange {n D p : ℕ} {κ c : ℝ} (wAB wBC wAC : Fin n → Fin n → ℤ) (hc : 0 < c) @@ -435,7 +457,9 @@ theorem falsePositiveCount_le_of_le_card_primesInRange {n D p : ℕ} {κ c : ℝ _ ≤ (n : ℝ) ^ 3 * (4 * Real.log Y / Real.log D) * Real.log D := by gcongr _ = 4 * (n : ℝ) ^ 3 * Real.log Y := by field_simp -/-- falsePositiveCount(p) = O(n³ log(3n^ν)/√D) = O(ν n³ log n/√D). -/ +/-- There is a universal `C ≥ 1` such that, for `16 ≤ D ≤ n`, `κ ≥ 1` and edge weights bounded by +`n^κ`, every selected prime has at most `C * κ * n^3 * log n / sqrt D` false positives. +-/ theorem exists_falsePositiveCount_le : ∃ C : ℝ, 1 ≤ C ∧ ∀ {n D p : ℕ} {κ : ℝ}, 16 ≤ D → D ≤ n → 1 ≤ κ → ∀ wAB wBC wAC : Fin n → Fin n → ℤ, @@ -461,17 +485,18 @@ theorem exists_falsePositiveCount_le : gcongr exact le_max_right _ _ -/-- A name for the constant in falsePositiveCount(p) = [...] = O(ν n³ log n/√D), for the bounds on -running times that are built on this one. +/-- A universal real constant witnessing `exists_falsePositiveCount_le`. It depends on none of the +input dimensions, weight matrices, exponent or selected prime, and is at least one. -/ noncomputable def Hashing.falsePositiveConst : ℝ := Classical.choose exists_falsePositiveCount_le -/-- The constant is at least 1. -/ +/-- The universal false-positive constant `Hashing.falsePositiveConst` is at least one. +-/ theorem Hashing.one_le_falsePositiveConst : 1 ≤ Hashing.falsePositiveConst := (Classical.choose_spec exists_falsePositiveCount_le).1 -/-- falsePositiveCount(p) = [...] = O(ν n³ log n/√D), with `Hashing.falsePositiveConst` as the -constant. +/-- For `16 ≤ D ≤ n`, `κ ≥ 1` and edge weights bounded by `n^κ`, bound every selected prime’s +false-positive count by `Hashing.falsePositiveConst * κ * n^3 * log n / sqrt D`. -/ theorem Hashing.falsePositiveCount_le_const_mul {n D p : ℕ} {κ : ℝ} (hD : 16 ≤ D) (hDn : D ≤ n) (hκ : 1 ≤ κ) (wAB wBC wAC : Fin n → Fin n → ℤ) (hT : WeightsBoundedBy wAB wBC wAC ((n : ℝ) ^ κ)) diff --git a/Complexity/Foundations/NumberTheory/PrimeWindow.lean b/Complexity/Foundations/NumberTheory/PrimeWindow.lean index dbeb6390e..9693a5302 100644 --- a/Complexity/Foundations/NumberTheory/PrimeWindow.lean +++ b/Complexity/Foundations/NumberTheory/PrimeWindow.lean @@ -1,6 +1,5 @@ /- -Copyright (c) 2020 Patrick Stevens. All rights reserved. -Copyright (c) 2026 Anthropic, PBC. All rights reserved. +Copyright (c) 2020 Patrick Stevens, 2026 Anthropic, PBC. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Patrick Stevens, Bolton Bailey, Anthropic, PBC -/ @@ -49,7 +48,6 @@ namespace Nat private theorem one_le_log_of_three_le {x : ℝ} (hx : 3 ≤ x) : 1 ≤ Real.log x := (Real.le_log_iff_exp_le (by linarith)).2 (Real.exp_one_lt_three.le.trans hx) - /-- The primes `p` with `n < p ≤ 2n`: the primes that Bertrand's postulate is about. -/ private def bertrandPrimes (n : ℕ) : Finset ℕ := (Ioc n (2 * n)).filter Nat.Prime diff --git a/Complexity/Foundations/RingTheory/CyclicIntRing.lean b/Complexity/Foundations/RingTheory/CyclicIntRing.lean index 94ae6d738..d15f95b19 100644 --- a/Complexity/Foundations/RingTheory/CyclicIntRing.lean +++ b/Complexity/Foundations/RingTheory/CyclicIntRing.lean @@ -80,5 +80,4 @@ theorem coeff_x_pow (hp : p ≠ 0) (k r : ℕ) : end CyclicIntRing - end Cslib From ce646c75bd6af9c6ace7fadc00bd4fde0c673f72 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 07:13:16 -0400 Subject: [PATCH 06/73] feat(exactTriangle): split modular queries and recover witnesses Adapt the complete finite Theorem 17 splitting and recovery component, using the existing three weight matrices, lopsided graphs, modular counts and EqOn answer interface. Prove exact integer/real capacity bridges so the finite constructions and scans are computable. Preserve Anthropic source credit; keep the native 3SUM endpoint open in #1394. Validation: all six gates in scripts/check.sh pass, with LEAN_NUM_THREADS=1 and cache downloads disabled after bootstrap. Full hygiene, changed-source check-simp, changed-file lints and git diff --check pass. Curated abstracts and rendering are unaffected. AI-assisted implementation/self-review by Codex worker codex-three-sum-splitting-t3-0a4d90c0; independent review will be recorded in the PR. --- Complexity.lean | 7 + .../Algorithms/ExactTriangle/Hashing.lean | 18 +- .../Algorithms/ExactTriangle/Splitting.lean | 534 ++++++++++++++++++ .../Algorithms/ExactTriangle/Witnesses.lean | 398 +++++++++++++ .../Foundations/Data/Finset/Quotient.lean | 44 ++ Complexity/Foundations/Data/Int/Residue.lean | 41 ++ .../Foundations/Data/Matrix/PairChunks.lean | 114 ++++ Complexity/Foundations/Data/Nat/CeilDiv.lean | 55 ++ .../Foundations/Data/Nat/SqrtRatio.lean | 47 ++ ComplexityTests/ExactTriangleSplitting.lean | 158 ++++++ NOTICE | 8 +- 11 files changed, 1408 insertions(+), 16 deletions(-) create mode 100644 Complexity/Algorithms/ExactTriangle/Splitting.lean create mode 100644 Complexity/Algorithms/ExactTriangle/Witnesses.lean create mode 100644 Complexity/Foundations/Data/Finset/Quotient.lean create mode 100644 Complexity/Foundations/Data/Int/Residue.lean create mode 100644 Complexity/Foundations/Data/Matrix/PairChunks.lean create mode 100644 Complexity/Foundations/Data/Nat/CeilDiv.lean create mode 100644 Complexity/Foundations/Data/Nat/SqrtRatio.lean create mode 100644 ComplexityTests/ExactTriangleSplitting.lean diff --git a/Complexity.lean b/Complexity.lean index e6ac94554..0f88b5283 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -3,7 +3,9 @@ module -- shake: keep-all --deprecated_module: ignore public import Complexity.Algorithms.ExactTriangle.Defs public import Complexity.Algorithms.ExactTriangle.Hashing public import Complexity.Algorithms.ExactTriangle.Lopsided +public import Complexity.Algorithms.ExactTriangle.Splitting public import Complexity.Algorithms.ExactTriangle.Weights +public import Complexity.Algorithms.ExactTriangle.Witnesses public import Complexity.Algorithms.Lean.FusionTree.Capacity public import Complexity.Algorithms.Lean.FusionTree.Compression public import Complexity.Algorithms.Lean.FusionTree.Correction @@ -2067,12 +2069,14 @@ public import Complexity.Foundations.Data.Finset.Average public import Complexity.Foundations.Data.Finset.Expect public import Complexity.Foundations.Data.Finset.Markov public import Complexity.Foundations.Data.Finset.Quantile +public import Complexity.Foundations.Data.Finset.Quotient public import Complexity.Foundations.Data.Finset.Sampling public import Complexity.Foundations.Data.Finset.SubsetSum public import Complexity.Foundations.Data.Finset.SymmDiff public import Complexity.Foundations.Data.Hamming public import Complexity.Foundations.Data.Hamming.Expansion public import Complexity.Foundations.Data.Int.Order +public import Complexity.Foundations.Data.Int.Residue public import Complexity.Foundations.Data.List.Append public import Complexity.Foundations.Data.List.BalancedFold public import Complexity.Foundations.Data.List.Basic @@ -2088,6 +2092,7 @@ public import Complexity.Foundations.Data.List.Range public import Complexity.Foundations.Data.List.SelectDistinct public import Complexity.Foundations.Data.Matrix.Binary public import Complexity.Foundations.Data.Matrix.IntegerBits +public import Complexity.Foundations.Data.Matrix.PairChunks public import Complexity.Foundations.Data.Matrix.Permanent public import Complexity.Foundations.Data.Matrix.Reindex public import Complexity.Foundations.Data.Matrix.TotallyRegular @@ -2109,6 +2114,7 @@ public import Complexity.Foundations.Data.Nat.Bitwise.Projection public import Complexity.Foundations.Data.Nat.Bitwise.Sparse public import Complexity.Foundations.Data.Nat.Bitwise.SumTree public import Complexity.Foundations.Data.Nat.Card +public import Complexity.Foundations.Data.Nat.CeilDiv public import Complexity.Foundations.Data.Nat.Choose public import Complexity.Foundations.Data.Nat.Choose.Real public import Complexity.Foundations.Data.Nat.Div @@ -2123,6 +2129,7 @@ public import Complexity.Foundations.Data.Nat.Prime public import Complexity.Foundations.Data.Nat.Primrec public import Complexity.Foundations.Data.Nat.Radix public import Complexity.Foundations.Data.Nat.Radix.Measurement +public import Complexity.Foundations.Data.Nat.SqrtRatio public import Complexity.Foundations.Data.Option public import Complexity.Foundations.Data.OrderedSet public import Complexity.Foundations.Data.OrderedSet.Spec diff --git a/Complexity/Algorithms/ExactTriangle/Hashing.lean b/Complexity/Algorithms/ExactTriangle/Hashing.lean index fbb2834b6..86aba7824 100644 --- a/Complexity/Algorithms/ExactTriangle/Hashing.lean +++ b/Complexity/Algorithms/ExactTriangle/Hashing.lean @@ -8,6 +8,7 @@ module -- upstream: candidate public import Complexity.Algorithms.ExactTriangle.Weights +public import Complexity.Foundations.Data.Int.Residue public import Complexity.Foundations.NumberTheory.PrimeWindow public import Complexity.Foundations.RingTheory.CyclicIntRing public import Mathlib.Data.Matrix.Mul @@ -74,15 +75,6 @@ def IsSelectedPrime {n : ℕ} (wAB wBC wAC : Fin n → Fin n → ℤ) (D p : ℕ private theorem four_le_sqrt {D : ℕ} (hD : 16 ≤ D) : 4 ≤ Real.sqrt (D : ℝ) := Real.le_sqrt_of_sq_le (by norm_num; exact_mod_cast hD) -private theorem natCast_toNat_emod {p : ℕ} (hp : 0 < p) (u : ℤ) : - ((u % (p : ℤ)).toNat : ℤ) = u % (p : ℤ) := - Int.toNat_of_nonneg (Int.emod_nonneg _ (by omega)) - -private theorem toNat_emod_lt {p : ℕ} (hp : 0 < p) (u : ℤ) : (u % (p : ℤ)).toNat < p := by - have := Int.emod_nonneg u (by omega : (p : ℤ) ≠ 0) - have := Int.emod_lt_of_pos u (by omega : (0 : ℤ) < p) - omega - /-! ### The primes in the range -/ @@ -134,8 +126,8 @@ private theorem eq_add_toNat_emod_iff {p r : ℕ} (hp : p ≠ 0) (hr : r < p) (u rw [← Int.natCast_inj, Int.ModEq, Int.emod_eq_of_lt (Int.natCast_nonneg r) (by exact_mod_cast hr), eq_comm] push_cast - rw [natCast_toNat_emod (Nat.pos_of_ne_zero hp), - natCast_toNat_emod (Nat.pos_of_ne_zero hp), + rw [Int.natCast_toNat_emod (Nat.pos_of_ne_zero hp), + Int.natCast_toNat_emod (Nat.pos_of_ne_zero hp), ← Int.add_emod] /-! @@ -233,8 +225,8 @@ theorem falsePositiveCount_add_zeroCount_eq_sum_coeff {p : ℕ} (hp : p ≠ 0) : refine Finset.sum_congr rfl fun a _ => ?_ rw [Fintype.sum_prod_type] refine Finset.sum_congr rfl fun b _ => ?_ - have hcast := natCast_toNat_emod (Nat.pos_of_ne_zero hp) (-wAB a b) - rw [(coeff_matP_mul_matQ wBC wAC) hp a b _ (toNat_emod_lt (Nat.pos_of_ne_zero hp) _), + have hcast := Int.natCast_toNat_emod (Nat.pos_of_ne_zero hp) (-wAB a b) + rw [(coeff_matP_mul_matQ wBC wAC) hp a b _ (Int.toNat_emod_lt (Nat.pos_of_ne_zero hp) _), Finset.card_filter] push_cast refine Finset.sum_congr rfl fun c _ => ?_ diff --git a/Complexity/Algorithms/ExactTriangle/Splitting.lean b/Complexity/Algorithms/ExactTriangle/Splitting.lean new file mode 100644 index 000000000..5015daf5e --- /dev/null +++ b/Complexity/Algorithms/ExactTriangle/Splitting.lean @@ -0,0 +1,534 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.ExactTriangle.Hashing +public import Complexity.Algorithms.ExactTriangle.Lopsided +public import Complexity.Foundations.Data.Matrix.PairChunks +public import Complexity.Foundations.Data.Nat.SqrtRatio +import Mathlib.Tactic.GCongr +import Mathlib.Tactic.FieldSimp + +/-! +# Modular splitting of Exact Triangle into finite lopsided queries + +Pieces of the third part and chunks of A–B residue classes define lopsided graphs with a +middle vertex `(c, label)`. The three input matrices retain their original orientations. +All bounds below use `16 ≤ D ≤ n` and `1 ≤ g`; the sharper degree bound also uses `g ≤ sqrt D`. +No native program, fast solver or charged instruction bound is asserted. + +## Main definitions + +* `piece`, `residueClass`, `chunkOf`: vertex blocks and residue query chunks. +* `lopInstance`, `instanceIndices`, `totalChunks`: lopsided graphs and their finite indices. + +## Main results + +* `existsUnique_mem_piece`, `card_piece_le`: unique piece coverage and size. +* `totalChunks_le`, `card_instanceIndices_le`: chunk and instance bounds. +* `card_middle_lopInstance`, `hasCommonNeighbor_lopInstance_iff`: size and modular correctness. +* `ncard_nbr_lopInstance_le`: degree bound. + +## References + +The reduction is part of [AlmanWilliams2026, Section 3]. Direct adaptation of +[Instances.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec3/Theorem17/Instances.lean), with only required definitions from +`PaperStatements.lean`, [Chunks.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec3/Chunks.lean) and [Parameters.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec3/Parameters.lean), at +`anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. +Source-derived prose below is paraphrased; quotations used for review are in the PR record. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.ExactTriangle + +open Cslib.Matrix + +/-- The largest number of query pairs that is "at most n²/√D" (Corollary 15 and Theorem 17; +Theorem 5 has "at most N²/√D"): `⌊n²/√D⌋`. -/ +def queryCap (n D : ℕ) : ℕ := Nat.sqrt ((n ^ 2) ^ 2 / D) + +/-- The integer capacity is exactly the real-valued floor used in the reduction. -/ +theorem queryCap_eq_floor (n D : ℕ) : queryCap n D = ⌊(n : ℝ) ^ 2 / Real.sqrt D⌋₊ := by + rw [queryCap, Nat.sqrt_div_eq_floor_div_sqrt] + norm_cast + +/-- Proof of Theorem 17: "let s := ⌊√D⌋". -/ +def sOf (D : ℕ) : ℕ := Nat.sqrt D + +/-- Proof of Theorem 17: the bound "⌈s/g⌉" on the number of vertices of a piece. -/ +def pieceSize (D g : ℕ) : ℕ := sOf D ⌈/⌉ g + +/-- The integer block size equals the real ceiling, also at the zero-divisor convention. -/ +theorem pieceSize_eq_ceil (D g : ℕ) : pieceSize D g = ⌈(sOf D : ℝ) / (g : ℝ)⌉₊ := by + by_cases hg : g = 0 + · simp [pieceSize, hg] + exact (Nat.ceil_div_eq_ceilDiv _ (Nat.pos_of_ne_zero hg)).symm + +/-- Proof of Theorem 17: "split C into pieces C₁, …, C_h of at most ⌈s/g⌉ vertices each". The paper +does not say how; we take blocks of `⌈s/g⌉` consecutive vertices. `piece n D g k` is the paper's +`C_{k+1}`. -/ +def piece (n D g k : ℕ) : Finset (Fin n) := + Finset.univ.filter fun c : Fin n => c.val / pieceSize D g = k + +/-- Proof of Theorem 17: the number `h` of pieces, `⌈n / ⌈s/g⌉⌉` for the blocks of `piece` +(meaningful when `⌈s/g⌉ ≥ 1`). -/ +def numPieces (n D g : ℕ) : ℕ := n ⌈/⌉ pieceSize D g + +/-- The number of blocks is the ceiling of the real quotient by the block size. -/ +theorem numPieces_eq_ceil (n D g : ℕ) : + numPieces n D g = ⌈(n : ℝ) / (pieceSize D g : ℝ)⌉₊ := by + by_cases h : pieceSize D g = 0 + · simp [numPieces, h] + exact (Nat.ceil_div_eq_ceilDiv _ (Nat.pos_of_ne_zero h)).symm + +/-- The index of an instance built by the reduction of Theorem 17: a residue `ϱ ∈ ℤ_p`, the number +`j` of a chunk of `W_ϱ`, and the number `k` of a piece of `C`. -/ +abbrev InstanceIndex (p : ℕ) : Type := Fin p × ℕ × ℕ + + +/-- Proof of Theorem 17: "For ϱ ∈ ℤ_p let W_ϱ be the set of edges (a,b) ∈ A × B with w(a,b) ≡ ϱ (mod +p)". -/ +def residueClass {n : ℕ} (wAB : Fin n → Fin n → ℤ) (p : ℕ) (ϱ : Fin p) : Finset (Fin n × Fin n) := + Finset.univ.filter fun q : Fin n × Fin n => wAB q.1 q.2 ≡ ((ϱ : ℕ) : ℤ) [ZMOD (p : ℤ)] + +/-- Proof of Theorem 17: "and cut it into chunks of at most n²/√D query pairs". The `j`-th chunk of +`W_ϱ`. -/ +def chunkOf {n : ℕ} (wAB : Fin n → Fin n → ℤ) (D p : ℕ) (ϱ : Fin p) (j : ℕ) : + Finset (Fin n × Fin n) := + chunk (residueClass wAB p ϱ) (queryCap n D) j + +/-- Proof of Theorem 17: "For each chunk 𝒬 ⊆ W_ϱ and each piece C_k form the instance of +Lop-AE-SparseTri(n, D) with query pairs W := 𝒬 and middle part C_k × ℤ_p [...], whose middle +vertices are pairs (vertex, label) [...], with a ∼ (c, σ) ⟺ σ ≡ w(a,c) + ϱ, (c, σ) ∼ b ⟺ σ ≡ +−w(b,c) (mod p)." -/ +def lopInstance {n : ℕ} (wAB wBC wAC : Fin n → Fin n → ℤ) (D g p : ℕ) : + InstanceIndex p → LopsidedGraph n + | (ϱ, j, k) => + { M := {c : Fin n // c ∈ piece n D g k} × Fin p + adjA := fun a (c, σ) => ((σ : ℕ) : ℤ) ≡ wAC a c + ((ϱ : ℕ) : ℤ) [ZMOD (p : ℤ)] + adjB := fun (c, σ) b => ((σ : ℕ) : ℤ) ≡ -wBC b c [ZMOD (p : ℤ)] + W := chunkOf wAB D p ϱ j } + +/-- Proof of Theorem 17: "For each chunk 𝒬 ⊆ W_ϱ and each piece C_k" there is an instance; these +are the indices of all the instances. This set depends only on the Exact Triangle instance and on +`D`, `g`, `p`, not on any answer of the oracle. -/ +def instanceIndices {n : ℕ} (wAB : Fin n → Fin n → ℤ) (D g p : ℕ) : + Finset (InstanceIndex p) := + Finset.univ.biUnion fun ϱ : Fin p => + ((Finset.range (numChunks (residueClass wAB p ϱ) (queryCap n D))) ×ˢ + (Finset.range (numPieces n D g))).image fun jk => (ϱ, jk) + +/-- Total number of query chunks, summed over residues. -/ +def totalChunks {n : ℕ} (wAB : Fin n → Fin n → ℤ) (D p : ℕ) : ℕ := + ∑ ϱ : Fin p, numChunks (residueClass wAB p ϱ) (queryCap n D) + +variable {n : ℕ} + +/-- `√D ≤ D ≤ n`. -/ +theorem sqrt_le_natCast {D : ℕ} (hD : 1 ≤ D) (hDn : D ≤ n) : Real.sqrt D ≤ n := + calc Real.sqrt D ≤ Real.sqrt D * Real.sqrt D := + le_mul_of_one_le_left (Real.sqrt_nonneg _) (Real.one_le_sqrt.mpr (by exact_mod_cast hD)) + _ = D := Real.mul_self_sqrt (Nat.cast_nonneg D) + _ ≤ n := by exact_mod_cast hDn + +/-- The bound `⌊n²/√D⌋` on the size of a chunk is at least 1, because `√D ≤ n ≤ n²`. -/ +theorem one_le_queryCap {D : ℕ} (hD : 1 ≤ D) (hDn : D ≤ n) : 1 ≤ queryCap n D := by + rw [queryCap_eq_floor] + have hn : (1 : ℝ) ≤ n := by exact_mod_cast hD.trans hDn + refine Nat.le_floor ?_ + rw [Nat.cast_one, le_div_iff₀ (Real.sqrt_pos.mpr (by exact_mod_cast hD)), one_mul] + exact (sqrt_le_natCast hD hDn).trans (le_self_pow₀ hn two_ne_zero) + +private theorem four_le_sqrt {D : ℕ} (hD : 16 ≤ D) : 4 ≤ Real.sqrt (D : ℝ) := + Real.le_sqrt_of_sq_le (by norm_num; exact_mod_cast hD) + +variable {n D g p : ℕ} + +/-! ### The pieces -/ + +/-- `s = ⌊√D⌋ ≤ √D`. -/ +theorem sOf_le_sqrt (D : ℕ) : (sOf D : ℝ) ≤ Real.sqrt D := + Real.nat_sqrt_le_real_sqrt + +/-- `√D < s + 1`. -/ +theorem sqrt_lt_sOf_add_one (D : ℕ) : Real.sqrt D < (sOf D : ℝ) + 1 := + Real.real_sqrt_lt_nat_sqrt_succ + +/-- `s > 0` because `D ≥ 16`. -/ +private theorem sOf_pos (hD : 16 ≤ D) : (0 : ℝ) < sOf D := by + linarith [four_le_sqrt hD, sqrt_lt_sOf_add_one D] + +/-- The bound `⌈s/g⌉` on the size of a piece is at least 1. -/ +theorem pieceSize_pos (hD : 16 ≤ D) (hg1 : 1 ≤ g) : 0 < pieceSize D g := by + rw [pieceSize_eq_ceil] + exact Nat.ceil_pos.mpr (div_pos (sOf_pos hD) (by exact_mod_cast hg1)) + +/-- `⌈s/g⌉ ≤ s` because `g ≥ 1`. -/ +theorem pieceSize_le_sOf (hg1 : 1 ≤ g) : pieceSize D g ≤ sOf D := by + rw [pieceSize_eq_ceil] + exact Nat.ceil_le.mpr (div_le_self (Nat.cast_nonneg _) (by exact_mod_cast hg1)) + +/-- A vertex lies in the piece number `k` exactly if its number divided by `⌈s/g⌉` is `k`. -/ +theorem mem_piece {k : ℕ} (c : Fin n) : c ∈ piece n D g k ↔ c.val / pieceSize D g = k := by + simp [piece] + +/-- Proof of Theorem 17: "split C into pieces C₁, …, C_h of at most ⌈s/g⌉ vertices each": every +vertex of `C` lies in exactly one of the `h` pieces. -/ +theorem existsUnique_mem_piece (hD : 16 ≤ D) (hg1 : 1 ≤ g) (c : Fin n) : + ∃! k, k < numPieces n D g ∧ c ∈ piece n D g k := by + refine ⟨c.val / pieceSize D g, ⟨?_, (mem_piece c).mpr rfl⟩, + fun k hk => ((mem_piece c).mp hk.2).symm⟩ + rw [numPieces_eq_ceil] + refine Nat.lt_ceil.mpr (lt_of_le_of_lt Nat.cast_div_le ?_) + exact div_lt_div_of_pos_right (by exact_mod_cast c.isLt) + (by exact_mod_cast pieceSize_pos hD hg1) + +/-- Proof of Theorem 17, "pieces [...] of at most ⌈s/g⌉ vertices each": `|C_k| ≤ ⌈s/g⌉`, because the +numbers of the vertices of the piece number `k` lie in `[k ⌈s/g⌉, (k + 1) ⌈s/g⌉)`. -/ +theorem card_piece_le (hD : 16 ≤ D) (hg1 : 1 ≤ g) (n k : ℕ) : + (piece n D g k).card ≤ pieceSize D g := + Finset.univ.card_filter_div_eq_le (fun _ _ _ _ h => Fin.ext h) (pieceSize_pos hD hg1) k + +/-- Proof of Theorem 17: "so that h ≤ ⌈ng/s⌉". -/ +theorem numPieces_le (hD : 16 ≤ D) (hg1 : 1 ≤ g) : + numPieces n D g ≤ ⌈(n : ℝ) * (g : ℝ) / (sOf D : ℝ)⌉₊ := by + have hsize : (0 : ℝ) < pieceSize D g := by exact_mod_cast pieceSize_pos hD hg1 + have hg : (0 : ℝ) < g := by exact_mod_cast hg1 + -- `s ≤ ⌈s/g⌉ g` + have hs' : (sOf D : ℝ) / (g : ℝ) ≤ pieceSize D g := by + rw [pieceSize_eq_ceil] + exact Nat.le_ceil _ + have hs := (div_le_iff₀ hg).mp hs' + rw [numPieces_eq_ceil] + refine Nat.ceil_mono ?_ + rw [div_le_div_iff₀ hsize (sOf_pos hD)] + calc (n : ℝ) * (sOf D : ℝ) ≤ (n : ℝ) * ((pieceSize D g : ℝ) * (g : ℝ)) := by gcongr + _ = (n : ℝ) * (g : ℝ) * (pieceSize D g : ℝ) := by ring + +/-! ### Residues -/ + +/-- The residue of the integer `x` modulo `p`, as an element of `Fin p`. -/ +def resFin (hp : p ≠ 0) (x : ℤ) : Fin p := + ⟨(x % (p : ℤ)).toNat, Int.toNat_emod_lt (Nat.pos_of_ne_zero hp) x⟩ + +/-- The residue of `x` is congruent to `x`. -/ +theorem resFin_modEq (hp : p ≠ 0) (x : ℤ) : ((resFin hp x : ℕ) : ℤ) ≡ x [ZMOD (p : ℤ)] := by + rw [resFin, Int.natCast_toNat_emod (Nat.pos_of_ne_zero hp)] + exact Int.mod_modEq x p + +/-- An element of `Fin p` is congruent to `x` modulo `p` exactly if it is the residue of `x`. -/ +private theorem modEq_iff_eq_resFin (hp : p ≠ 0) (σ : Fin p) (x : ℤ) : + ((σ : ℕ) : ℤ) ≡ x [ZMOD (p : ℤ)] ↔ σ = resFin hp x := by + refine ⟨fun h => ?_, fun h => h ▸ resFin_modEq hp x⟩ + -- Two congruent numbers in `{0, …, p − 1}` are equal. + have hmod : (σ : ℕ) ≡ (resFin hp x : ℕ) [MOD p] := + (Int.natCast_modEq_iff).mp (h.trans (resFin_modEq hp x).symm) + exact Fin.ext (Nat.ModEq.eq_of_lt_of_lt hmod σ.isLt (resFin hp x).isLt) + +/-! ### The arithmetic behind the two counts -/ + +/-- The number `⌈|S|/cap⌉` of chunks of `S` satisfies `⌈|S|/cap⌉ cap ≤ |S| + cap − 1`. -/ +private theorem numChunks_mul_le (S : Finset (Fin n × Fin n)) (cap : ℕ) (hcap : 1 ≤ cap) : + numChunks S cap * cap + 1 ≤ S.card + cap := by + rw [numChunks] + exact Nat.ceilDiv_mul_lt hcap + +/-- `s + 1` exceeds `√D` by at least `1/(2√D + 1)`: since `(s + 1)²` and `D` are integers, +`1 ≤ (s + 1)² − D = (s + 1 − √D)(s + 1 + √D)`. -/ +private theorem one_le_gap_mul (D : ℕ) : + 1 ≤ ((sOf D : ℝ) + 1 - Real.sqrt D) * (2 * Real.sqrt D + 1) := by + have hsq : Real.sqrt D ^ 2 = D := Real.sq_sqrt (Nat.cast_nonneg D) + have hle := sOf_le_sqrt D + have hlt := sqrt_lt_sOf_add_one D + have hinteger : (D : ℝ) + 1 ≤ ((sOf D : ℝ) + 1) ^ 2 := by + have hreal : (D : ℝ) < ((sOf D : ℝ) + 1) ^ 2 := + hsq ▸ pow_lt_pow_left₀ hlt (Real.sqrt_nonneg _) two_ne_zero + have hnat : D < (sOf D + 1) ^ 2 := by exact_mod_cast hreal + exact_mod_cast hnat + calc (1 : ℝ) ≤ ((sOf D : ℝ) + 1) ^ 2 - Real.sqrt D ^ 2 := by linarith [hinteger, hsq] + _ = ((sOf D : ℝ) + 1 - Real.sqrt D) * ((sOf D : ℝ) + 1 + Real.sqrt D) := by ring + _ ≤ ((sOf D : ℝ) + 1 - Real.sqrt D) * (2 * Real.sqrt D + 1) := + mul_le_mul_of_nonneg_left (by linarith) (by linarith) + +/-- The inequality behind "at most p + √D [...] chunks": `n² < (s + 1) ⌊n²/√D⌋ + p`. + +Write `r = √D`, `x = n²/r` and `δ = s + 1 − r`. Then +`(s + 1)⌊x⌋ > (s + 1)(x − 1) = n² + xδ − (s + 1)`, so it is enough that `xδ ≥ r/2 + 1 ≥ s + 1 − p`. +This holds because `x ≥ r³` (as `D ≤ n`), `δ ≥ 1/(2r + 1)` (as `D` is an integer) and `r ≥ 4`. -/ +private theorem sq_lt_mul_queryCap_add (hD : 16 ≤ D) (hDn : D ≤ n) + (hp : Real.sqrt D / 2 ≤ (p : ℝ)) : n ^ 2 < (sOf D + 1) * queryCap n D + p := by + have hgap := one_le_gap_mul D + have hs := sOf_le_sqrt D + set r := Real.sqrt D with hr + set δ := (sOf D : ℝ) + 1 - r with hδ + set x := (n : ℝ) ^ 2 / r with hx + have hr4 : 4 ≤ r := four_le_sqrt hD + have hxr : x * r = (n : ℝ) ^ 2 := div_mul_cancel₀ _ (by linarith) + have hx3 : r ^ 3 ≤ x := by + rw [hx, le_div_iff₀ (by linarith)] + calc r ^ 3 * r = (r ^ 2) ^ 2 := by ring + _ = (D : ℝ) ^ 2 := by rw [hr, Real.sq_sqrt (Nat.cast_nonneg D)] + _ ≤ (n : ℝ) ^ 2 := by gcongr + have hxδ : r / 2 + 1 ≤ x * δ := by + refine le_of_mul_le_mul_right ?_ (show 0 < 2 * r + 1 by linarith) + calc (r / 2 + 1) * (2 * r + 1) ≤ r ^ 3 := by + -- `r³ ≥ 4r²` and `r² ≥ 4r` + linarith [mul_le_mul_of_nonneg_right hr4 (sq_nonneg r), + mul_le_mul_of_nonneg_right hr4 (show 0 ≤ r by linarith)] + _ ≤ x * 1 := by rw [mul_one]; exact hx3 + _ ≤ x * (δ * (2 * r + 1)) := + mul_le_mul_of_nonneg_left hgap (le_trans (by positivity) hx3) + _ = x * δ * (2 * r + 1) := by ring + have hfloor : x < (queryCap n D : ℝ) + 1 := by + rw [queryCap_eq_floor] + exact Nat.lt_floor_add_one _ + have hmain : (n : ℝ) ^ 2 < ((sOf D : ℝ) + 1) * (queryCap n D : ℝ) + p := + calc (n : ℝ) ^ 2 = x * r := hxr.symm + _ ≤ x * r + x * δ - ((sOf D : ℝ) + 1) + r / 2 := by + -- `s + 1 ≤ r + 1 ≤ xδ + r/2` + linarith [hxδ, hs] + _ = ((sOf D : ℝ) + 1) * (x - 1) + r / 2 := by rw [hδ]; ring + _ < ((sOf D : ℝ) + 1) * (queryCap n D : ℝ) + p := + add_lt_add_of_lt_of_le (mul_lt_mul_of_pos_left (by linarith [hfloor]) (by positivity)) hp + exact_mod_cast hmain + +/-- Proof of Theorem 17: the two facts used for the count of the instances, "s ≥ √D − 1 ≥ 3√D/4 +because D ≥ 16, and √D ≤ √n ≤ 2n/3 because D ≤ n and we may assume n ≥ 3". -/ +theorem le_sOf_and_sqrt_le (hD : 16 ≤ D) (hDn : D ≤ n) : + 3 * Real.sqrt D / 4 ≤ (sOf D : ℝ) ∧ Real.sqrt D ≤ 2 * (n : ℝ) / 3 := by + have hsqrtD := four_le_sqrt hD + have hsqrtn := four_le_sqrt (hD.trans hDn) + constructor + · calc 3 * Real.sqrt D / 4 ≤ Real.sqrt D - 1 := by linarith + _ ≤ (sOf D : ℝ) := by linarith [sqrt_lt_sOf_add_one D] + · calc Real.sqrt D ≤ Real.sqrt n := Real.sqrt_le_sqrt (by exact_mod_cast hDn) + _ ≤ 2 * (n : ℝ) / 3 := by + -- `n = √n √n ≥ 4√n` + linarith [mul_le_mul_of_nonneg_right hsqrtn (Real.sqrt_nonneg n), + Real.mul_self_sqrt (Nat.cast_nonneg n : (0 : ℝ) ≤ n)] + +section Matrices + +variable (wAB wBC wAC : Fin n → Fin n → ℤ) + +/-! ### The sets `W_ϱ` -/ + +/-- A pair lies in `W_ϱ` exactly if `ϱ` is the residue of its weight. -/ +theorem mem_residueClass (hp : p ≠ 0) (ϱ : Fin p) (q : Fin n × Fin n) : + q ∈ residueClass wAB p ϱ ↔ ϱ = resFin hp (wAB q.1 q.2) := by + rw [← modEq_iff_eq_resFin] + simp only [residueClass, Finset.mem_filter, Finset.mem_univ, true_and] + exact ⟨Int.ModEq.symm, Int.ModEq.symm⟩ + +/-- Proof of Theorem 17, the sets `W_ϱ` for `ϱ ∈ ℤ_p`: every pair `(a,b) ∈ A × B` lies in exactly +one of them. -/ +theorem existsUnique_mem_residueClass (hp : p ≠ 0) (q : Fin n × Fin n) : + ∃! ϱ : Fin p, q ∈ residueClass wAB p ϱ := + ⟨resFin hp (wAB q.1 q.2), (mem_residueClass wAB hp _ q).mpr rfl, + fun ϱ hϱ => (mem_residueClass wAB hp ϱ q).mp hϱ⟩ + +/-- The sets `W_ϱ` have `n²` pairs in all. -/ +private theorem sum_card_residueClass (hp : p ≠ 0) : + ∑ ϱ : Fin p, (residueClass wAB p ϱ).card = n ^ 2 := by + calc ∑ ϱ : Fin p, (residueClass wAB p ϱ).card + = ∑ ϱ : Fin p, ((Finset.univ : Finset (Fin n × Fin n)).filter + fun q => resFin hp (wAB q.1 q.2) = ϱ).card := by + refine Finset.sum_congr rfl fun ϱ _ => congrArg Finset.card ?_ + ext q + rw [mem_residueClass wAB hp] + simp [eq_comm] + _ = (Finset.univ : Finset (Fin n × Fin n)).card := + (Finset.card_eq_sum_card_fiberwise fun _ _ => Finset.mem_coe.mpr (Finset.mem_univ _)).symm + _ = n ^ 2 := by simp [sq] + +/-! ### The chunks -/ + +/-- Proof of Theorem 17: "cut it into chunks of at most n²/√D query pairs". (The chunks of `W_ϱ` +cover it: `biUnion_chunk`.) -/ +theorem card_chunkOf_le (hD : 1 ≤ D) (hDn : D ≤ n) (ϱ : Fin p) (j : ℕ) : + ((chunkOf wAB D p ϱ j).card : ℝ) ≤ (n : ℝ) ^ 2 / Real.sqrt D := + calc ((chunkOf wAB D p ϱ j).card : ℝ) ≤ (queryCap n D : ℝ) := by + exact_mod_cast card_chunk_le _ (one_le_queryCap hD hDn) j + _ ≤ (n : ℝ) ^ 2 / Real.sqrt D := by + rw [queryCap_eq_floor] + exact Nat.floor_le (by positivity) + +/-- There are at most `p + s` chunks in all. Each `W_ϱ` has at most `(|W_ϱ| + cap − 1)/cap` chunks, +where `cap = ⌊n²/√D⌋`, so `p + s + 1` chunks or more would need `n² ≥ (s + 1) cap + p` pairs. -/ +private theorem totalChunks_le_add_sOf (hD : 16 ≤ D) (hDn : D ≤ n) (hp0 : p ≠ 0) + (hp : Real.sqrt D / 2 ≤ (p : ℝ)) : totalChunks wAB D p ≤ p + sOf D := by + have hsum : totalChunks wAB D p * queryCap n D + p ≤ n ^ 2 + p * queryCap n D := by + have h := Finset.sum_le_sum fun ϱ (_ : ϱ ∈ (Finset.univ : Finset (Fin p))) => + numChunks_mul_le (residueClass wAB p ϱ) (queryCap n D) (one_le_queryCap (by omega) hDn) + simp only [Finset.sum_add_distrib, ← Finset.sum_mul, Finset.sum_const, Finset.card_univ, + Fintype.card_fin, smul_eq_mul, mul_one] at h + rwa [sum_card_residueClass wAB hp0] at h + have hpairs := sq_lt_mul_queryCap_add hD hDn hp + by_contra hmany + have hmul : (p + sOf D + 1) * queryCap n D ≤ totalChunks wAB D p * queryCap n D := + Nat.mul_le_mul_right _ (by omega) + rw [show (p + sOf D + 1) * queryCap n D = p * queryCap n D + (sOf D + 1) * queryCap n D by + ring] at hmul + -- `hsum` and `hmul` give `(s + 1) cap + p ≤ n²`, against `hpairs`. + omega + +/-- Proof of Theorem 17: "There are at most p + √D ≤ 2√D chunks in all." + +NOTE. A chunk holds a whole number of pairs, at most `⌊n²/√D⌋`, so the count is at most +`p + (n² − p)/⌊n²/√D⌋`, and the second term can exceed `√D` slightly. The printed bound is still +true, because the count is an integer, `D` is an integer and `D ≤ n` (`sq_lt_mul_queryCap_add`). -/ +theorem totalChunks_le (hD : 16 ≤ D) (hDn : D ≤ n) (hp : p ∈ primesInRange D) : + (totalChunks wAB D p : ℝ) ≤ (p : ℝ) + Real.sqrt D ∧ + (p : ℝ) + Real.sqrt D ≤ 2 * Real.sqrt D := by + obtain ⟨hprime, hge, hlt⟩ := mem_primesInRange.mp hp + have hnat : (totalChunks wAB D p : ℝ) ≤ (p : ℝ) + (sOf D : ℝ) := by + exact_mod_cast totalChunks_le_add_sOf wAB hD hDn hprime.ne_zero hge + exact ⟨by linarith [sOf_le_sqrt D], by linarith⟩ + +/-! ### The instances -/ + +/-- Proof of Theorem 17: "middle part C_k × ℤ_p, of size at most sp ≤ D". So each instance is an +instance of Lop-AE-SparseTri(n, D). -/ +theorem card_middle_lopInstance (hD : 16 ≤ D) (hg1 : 1 ≤ g) (hp : p ∈ primesInRange D) + (ι : InstanceIndex p) : Fintype.card (lopInstance wAB wBC wAC D g p ι).M ≤ D := by + obtain ⟨-, -, hlt⟩ := mem_primesInRange.mp hp + have hsp : (sOf D : ℝ) * (p : ℝ) ≤ D := + calc (sOf D : ℝ) * (p : ℝ) ≤ Real.sqrt D * Real.sqrt D := + mul_le_mul (sOf_le_sqrt D) hlt.le (Nat.cast_nonneg p) (Real.sqrt_nonneg _) + _ = D := Real.mul_self_sqrt (Nat.cast_nonneg D) + calc Fintype.card ({c : Fin n // c ∈ piece n D g ι.2.2} × Fin p) + = (piece n D g ι.2.2).card * p := by + rw [Fintype.card_prod, Fintype.card_coe, Fintype.card_fin] + _ ≤ sOf D * p := Nat.mul_le_mul_right p + ((card_piece_le hD hg1 n ι.2.2).trans (pieceSize_le_sOf hg1)) + _ ≤ D := by exact_mod_cast hsp + +/-- A query pair of the instance with index `(ϱ, j, k)` lies in `W_ϱ`, and `j` is the number of its +chunk. -/ +theorem mem_lopInstance_W {wAB wBC wAC : Fin n → Fin n → ℤ} + {ι : InstanceIndex p} {q : Fin n × Fin n} + (hq : q ∈ (lopInstance wAB wBC wAC D g p ι).W) : + q ∈ residueClass wAB p ι.1 ∧ rankIn (residueClass wAB p ι.1) q / queryCap n D = ι.2.1 := + Finset.mem_filter.mp hq + +/-- Proof of Theorem 17: "Within the chunk, the condition S(a,b,c) ≡ 0 (mod p) has become the +equality w(a,c) + ϱ ≡ −w(b,c) of a label of (a,c) and a label of (b,c)". -/ +theorem weightSum_modEq_zero_iff {p ϱ : ℤ} {a b : Fin n} (hϱ : wAB a b ≡ ϱ [ZMOD p]) (c : Fin n) : + weightSum wAB wBC wAC a b c ≡ 0 [ZMOD p] ↔ wAC a c + ϱ ≡ -wBC b c [ZMOD p] := by + rw [Int.modEq_iff_dvd, Int.modEq_iff_dvd, + show 0 - weightSum wAB wBC wAC a b c = -wBC b c - (wAC a c + ϱ) + (ϱ - wAB a b) by + simp only [weightSum]; ring] + exact dvd_add_left (Int.modEq_iff_dvd.mp hϱ) + +/-- Proof of Theorem 17: "a query pair (a,b) ∈ 𝒬 has a common neighbor if and only if some c ∈ C_k +has S(a,b,c) ≡ 0 (mod p)". -/ +theorem hasCommonNeighbor_lopInstance_iff (hp : p ≠ 0) + {ι : InstanceIndex p} {q : Fin n × Fin n} + (hq : q ∈ (lopInstance wAB wBC wAC D g p ι).W) : + (lopInstance wAB wBC wAC D g p ι).HasCommonNeighbor q.1 q.2 ↔ + ∃ c ∈ piece n D g ι.2.2, weightSum wAB wBC wAC q.1 q.2 c ≡ 0 [ZMOD (p : ℤ)] := by + have hϱ : wAB q.1 q.2 ≡ ((ι.1 : ℕ) : ℤ) [ZMOD (p : ℤ)] := + (Finset.mem_filter.mp (mem_lopInstance_W hq).1).2 + constructor + · -- A common neighbor `(c, σ)` carries the label `σ` of `(a,c)` and of `(b,c)`. + rintro ⟨⟨c, σ⟩, hA, hB⟩ + exact ⟨c, c.2, (weightSum_modEq_zero_iff wAB wBC wAC hϱ c).mpr (hA.symm.trans hB)⟩ + · -- If the two labels agree, the vertex `c` with that label is a common neighbor. + rintro ⟨c, hc, hS⟩ + have hB := resFin_modEq hp (-wBC q.2 c) + exact ⟨(⟨c, hc⟩, resFin hp (-wBC q.2 c)), + hB.trans ((weightSum_modEq_zero_iff wAB wBC wAC hϱ c).mp hS).symm, hB⟩ + +/-- Proof of Theorem 17, "For each chunk 𝒬 ⊆ W_ϱ and each piece C_k": `(ϱ, j, k)` is the index of an +instance exactly if `j` is the number of a chunk of `W_ϱ` and `k` the number of a piece. -/ +theorem mem_instanceIndices (ι : InstanceIndex p) : + ι ∈ instanceIndices wAB D g p ↔ + ι.2.1 < numChunks (residueClass wAB p ι.1) (queryCap n D) ∧ ι.2.2 < numPieces n D g := by + obtain ⟨ϱ, j, k⟩ := ι + simp only [instanceIndices, Finset.mem_biUnion, Finset.mem_univ, true_and, + Finset.mem_image, Finset.mem_product, Finset.mem_range, Prod.mk.injEq, Prod.exists] + exact ⟨fun ⟨_, _, _, h, h1, h2, h3⟩ => by subst h1 h2 h3; exact h, + fun h => ⟨ϱ, j, k, h, rfl, rfl, rfl⟩⟩ + +/-- There are as many instances as chunks times pieces. -/ +theorem card_instanceIndices (D g p : ℕ) : + (instanceIndices wAB D g p).card = totalChunks wAB D p * numPieces n D g := by + unfold instanceIndices totalChunks + rw [Finset.card_biUnion, Finset.sum_mul] + · refine Finset.sum_congr rfl fun ϱ _ => ?_ + rw [Finset.card_image_of_injective _ (Prod.mk_right_injective ϱ), Finset.card_product, + Finset.card_range, Finset.card_range] + · intro ϱ _ ϱ' _ hne + rw [Function.onFun, Finset.disjoint_left] + intro x hx hx' + obtain ⟨_, _, rfl⟩ := Finset.mem_image.mp hx + obtain ⟨_, _, h⟩ := Finset.mem_image.mp hx' + exact hne (congrArg Prod.fst h).symm + +/-- Proof of Theorem 17: "There are at most 2√D h ≤ 2√D(ng/s + 1) ≤ 4ng instances". -/ +theorem card_instanceIndices_le (hD : 16 ≤ D) (hDn : D ≤ n) (hg1 : 1 ≤ g) + (hp : p ∈ primesInRange D) : + ((instanceIndices wAB D g p).card : ℝ) ≤ 4 * (n : ℝ) * (g : ℝ) := by + obtain ⟨hs, hsqrt⟩ := le_sOf_and_sqrt_le hD hDn + obtain ⟨hchunks, hchunks'⟩ := totalChunks_le wAB hD hDn hp + have hs0 := sOf_pos hD + have hpieces : (numPieces n D g : ℝ) ≤ (n : ℝ) * (g : ℝ) / (sOf D : ℝ) + 1 := + calc (numPieces n D g : ℝ) ≤ (⌈(n : ℝ) * (g : ℝ) / (sOf D : ℝ)⌉₊ : ℝ) := by + exact_mod_cast numPieces_le hD hg1 + _ ≤ (n : ℝ) * (g : ℝ) / (sOf D : ℝ) + 1 := (Nat.ceil_lt_add_one (by positivity)).le + -- `2√D · ng/s ≤ 8ng/3` because `s ≥ 3√D/4`, and `2√D ≤ 4n/3 ≤ 4ng/3`. + have hmain : + Real.sqrt D * ((n : ℝ) * (g : ℝ) / (sOf D : ℝ)) ≤ 4 / 3 * ((n : ℝ) * (g : ℝ)) := by + rw [← mul_div_assoc, div_le_iff₀ hs0] + calc Real.sqrt D * ((n : ℝ) * (g : ℝ)) + = 4 / 3 * ((n : ℝ) * (g : ℝ)) * (3 * Real.sqrt D / 4) := by ring + _ ≤ 4 / 3 * ((n : ℝ) * (g : ℝ)) * (sOf D : ℝ) := by gcongr + have hng : (n : ℝ) ≤ (n : ℝ) * (g : ℝ) := + le_mul_of_one_le_right (Nat.cast_nonneg n) (by exact_mod_cast hg1) + calc ((instanceIndices wAB D g p).card : ℝ) + = (totalChunks wAB D p : ℝ) * (numPieces n D g : ℝ) := by + rw [card_instanceIndices, Nat.cast_mul] + _ ≤ 2 * Real.sqrt D * (numPieces n D g : ℝ) := by gcongr; exact hchunks.trans hchunks' + _ ≤ 2 * Real.sqrt D * ((n : ℝ) * (g : ℝ) / (sOf D : ℝ) + 1) := by gcongr + _ ≤ 4 * (n : ℝ) * (g : ℝ) := by linarith [hmain, hng, hsqrt] + +end Matrices + +/-! ### The neighborhoods in an instance -/ + +/-- The graph of a function into `Fin p` has as many points as the domain. -/ +private theorem ncard_graph {α : Type} [Fintype α] (f : α → Fin p) : + {v : α × Fin p | v.2 = f v.1}.ncard = Fintype.card α := by + have h : {v : α × Fin p | v.2 = f v.1} = Set.univ.graphOn f := by + ext v + simp [Set.mem_graphOn, eq_comm] + rw [h, Set.ncard_graphOn, Set.ncard_univ, Nat.card_eq_fintype_card] + +/-- Section 3.1, "The reductions below [...] produce at most n²/√D query pairs and sets of at most +√D elements", and Remark 20, "every vertex of A has O(√D/g) neighbors in the middle part": in an +instance of the reduction, a vertex of `A` or of `B` has exactly one neighbor `(c, σ)` for every +`c ∈ C_k`, hence at most `⌈s/g⌉` neighbors. (And `⌈s/g⌉ ≤ s ≤ √D`; for `g ≤ √D` also +`⌈s/g⌉ ≤ 2√D/g`, `pieceSize_le_two_mul_sqrt_div`.) -/ +theorem ncard_nbr_lopInstance_le (wAB wBC wAC : Fin n → Fin n → ℤ) (hD : 16 ≤ D) + (hg1 : 1 ≤ g) (hp : p ≠ 0) (ι : InstanceIndex p) : + (∀ a, {v | (lopInstance wAB wBC wAC D g p ι).adjA a v}.ncard ≤ pieceSize D g) ∧ + (∀ b, {v | (lopInstance wAB wBC wAC D g p ι).adjB v b}.ncard ≤ pieceSize D g) := by + -- The neighbors of a vertex are the graph of the function that sends `c` to its label. + have hgraph : ∀ (f : {c : Fin n // c ∈ piece n D g ι.2.2} → ℤ), + {v : {c : Fin n // c ∈ piece n D g ι.2.2} × Fin p | + ((v.2 : ℕ) : ℤ) ≡ f v.1 [ZMOD (p : ℤ)]}.ncard ≤ pieceSize D g := by + intro f + refine le_trans (le_of_eq ?_) (card_piece_le hD hg1 n ι.2.2) + rw [← Fintype.card_coe, ← ncard_graph fun c => resFin hp (f c)] + congr 1 + ext v + exact modEq_iff_eq_resFin hp v.2 _ + exact ⟨fun a => hgraph fun c => wAC a c + ((ι.1 : ℕ) : ℤ), fun b => hgraph fun c => -wBC b c⟩ + +end Cslib.Algorithms.ExactTriangle diff --git a/Complexity/Algorithms/ExactTriangle/Witnesses.lean b/Complexity/Algorithms/ExactTriangle/Witnesses.lean new file mode 100644 index 000000000..0b5e39cb9 --- /dev/null +++ b/Complexity/Algorithms/ExactTriangle/Witnesses.lean @@ -0,0 +1,398 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.ExactTriangle.Splitting +public import Mathlib.Combinatorics.Enumerative.DoubleCounting +import Mathlib.Analysis.Complex.ExponentialBounds + +/-! +# Finite witness recovery from modular lopsided answers + +Correct finite answers are ordinary `Set.EqOn` premises on the actual lopsided graphs. +The recovery scans each accepted piece until it finds an exact zero. Distinct failed scans +contain distinct modular false positives. No solver, native program or time oracle is assumed. + +## Main definitions + +* `scanPiece`, `runScans`: finite witness search and number of scans. +* `AnswersCorrect`, `IsScanOrder`: correctness on queried pairs and a duplicate-free scan order. + +## Main results + +* `recovery_correct`: sound and complete recovery with at most `falsePositiveCount + 1` scans. +* `card_failedScans_le`: false-positive accounting. +* `falsePositiveCount_add_one_mul_pieceSize_le`: bound on the number of vertices scanned. + +## References + +Adapted from [Witnesses.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec3/Theorem17/Witnesses.lean) and the minimal scan definitions in +`PaperStatements.lean` at `anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. +Context: [AlmanWilliams2026, Section 3]. Source-derived prose is paraphrased. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.ExactTriangle + +open Cslib.Matrix + +/-- A finite search result returns a zero triangle, or certifies that no zero triangle exists. +This specifies mathematical correctness only. -/ +def IsSearchAnswer {n : ℕ} (wAB wBC wAC : Fin n → Fin n → ℤ) + (r : Option (Fin n × Fin n × Fin n)) : Prop := + (∀ a b c, r = some (a, b, c) → weightSum wAB wBC wAC a b c = 0) ∧ + (r = none → ¬ HasZeroTriangle wAB wBC wAC) + +/-- Proof of Theorem 17: "scan the piece C_k of its instance for a c with S(a,b,c) = 0". The +vertices of the piece are tried in increasing order; the result is the first `c` found, or `none` if +the scan fails. -/ +def scanPiece {n : ℕ} (wAB wBC wAC : Fin n → Fin n → ℤ) (D g k : ℕ) (a b : Fin n) : + Option (Fin n) := + ((List.finRange n).filter fun c => c ∈ piece n D g k).find? fun c => + weightSum wAB wBC wAC a b c = 0 + +/-- Proof of Theorem 17: "For every query pair that the oracle accepts". `ans ι` is the oracle's +answer to the instance with index `ι`. The result is the set of all (instance, query pair of that +instance) that the oracle accepts; each of them calls for one scan. -/ +def acceptedPairs {n : ℕ} (wAB wBC wAC : Fin n → Fin n → ℤ) (D g p : ℕ) + (ans : InstanceIndex p → Fin n × Fin n → Bool) : Finset (InstanceIndex p × (Fin n × Fin n)) := + ((instanceIndices wAB D g p) ×ˢ Finset.univ).filter fun (ι, q) => + q ∈ (lopInstance wAB wBC wAC D g p ι).W ∧ ans ι q = true + +/-- Proof of Theorem 17: the scans, one after the other, over a list of accepted pairs: "We stop as +soon as a zero triangle is found." The result is the triangle found (or `none`) together with the +number of scans that were carried out. -/ +def runScans {n : ℕ} (wAB wBC wAC : Fin n → Fin n → ℤ) (D g : ℕ) {p : ℕ} : + List (InstanceIndex p × (Fin n × Fin n)) → Option (Fin n × Fin n × Fin n) × ℕ + | [] => (none, 0) + | ((_, _, k), a, b) :: rest => + match scanPiece wAB wBC wAC D g k a b with + | some c => (some (a, b, c), 1) + | none => ((runScans wAB wBC wAC D g rest).1, (runScans wAB wBC wAC D g rest).2 + 1) + +/-- Proof of Theorem 17: "For every query pair that the oracle accepts, scan [...]". The paper does +not say in which order. `L` is an order of the scans: a list in which every accepted pair occurs +exactly once. -/ +def IsScanOrder {n : ℕ} (wAB wBC wAC : Fin n → Fin n → ℤ) (D g p : ℕ) + (ans : InstanceIndex p → Fin n × Fin n → Bool) (L : List (InstanceIndex p × (Fin n × Fin n))) : + Prop := + L.Nodup ∧ ∀ x, x ∈ L ↔ x ∈ acceptedPairs wAB wBC wAC D g p ans + + +variable {n D g p : ℕ} + +section Matrices + +variable {wAB wBC wAC : Fin n → Fin n → ℤ} + +/-! ### Scans and accepted pairs -/ + +/-- Proof of Theorem 17, "scan the piece C_k of its instance for a c with S(a,b,c) = 0": a `c` that +a scan returns lies in the piece and has `S(a,b,c) = 0`. -/ +theorem scanPiece_eq_some {k : ℕ} {a b c : Fin n} (h : scanPiece wAB wBC wAC D g k a b = some c) : + c ∈ piece n D g k ∧ weightSum wAB wBC wAC a b c = 0 := by + have hmem := List.mem_of_find?_eq_some h + rw [List.mem_filter] at hmem + exact ⟨by simpa using hmem.2, by simpa using List.find?_some h⟩ + +/-- A scan fails exactly if the piece has no `c` with `S(a,b,c) = 0`. -/ +theorem scanPiece_eq_none_iff {k : ℕ} {a b : Fin n} : + scanPiece wAB wBC wAC D g k a b = none ↔ + ∀ c ∈ piece n D g k, weightSum wAB wBC wAC a b c ≠ 0 := by + unfold scanPiece + rw [List.find?_eq_none] + simp [List.mem_filter] + +variable (wAB wBC wAC) (D g) + +/-- The scan that belongs to `x = ((ϱ, j, k), (a, b))`, a query pair `(a, b)` of the instance with +index `(ϱ, j, k)`: the piece `C_k` is scanned for the pair `(a, b)`. -/ +abbrev scanOf (wAB wBC wAC : Fin n → Fin n → ℤ) (D g : ℕ) + (x : InstanceIndex p × (Fin n × Fin n)) : Option (Fin n) := + scanPiece wAB wBC wAC D g x.1.2.2 x.2.1 x.2.2 + +/-- What the scans over a list of pairs return: a triangle that is returned is a zero triangle; if +none is returned, then every scan failed; and all the scans that are carried out, except possibly +the last one, fail. -/ +private theorem runScans_spec (wAB wBC wAC : Fin n → Fin n → ℤ) (D g : ℕ) + (l : List (InstanceIndex p × (Fin n × Fin n))) : + (∀ a b c, (runScans wAB wBC wAC D g l).1 = some (a, b, c) → weightSum wAB wBC wAC a b c = 0) ∧ + ((runScans wAB wBC wAC D g l).1 = none → ∀ x ∈ l, scanOf wAB wBC wAC D g x = none) ∧ + (runScans wAB wBC wAC D g l).2 ≤ + (l.filter fun x => decide (scanOf wAB wBC wAC D g x = none)).length + 1 := by + induction l with + | nil => simp [runScans] + | cons x rest ih => + obtain ⟨ihzero, ihnone, ihcount⟩ := ih + cases hscan : scanOf wAB wBC wAC D g x with + | some c => + -- The first scan finds `c`, and the scans stop. + have hrun : runScans wAB wBC wAC D g (x :: rest) = (some (x.2.1, x.2.2, c), 1) := by + simp only [runScans, hscan] + rw [hrun] + refine ⟨fun a b c' h => ?_, fun h => by simp at h, by simp⟩ + simp only [Option.some.injEq, Prod.mk.injEq] at h + obtain ⟨rfl, rfl, rfl⟩ := h + exact (scanPiece_eq_some hscan).2 + | none => + -- The first scan fails, and the scans go on with the rest of the list. + have hrun : runScans wAB wBC wAC D g (x :: rest) + = ((runScans wAB wBC wAC D g rest).1, (runScans wAB wBC wAC D g rest).2 + 1) := by + simp only [runScans, hscan] + rw [hrun, List.filter_cons_of_pos (by simpa using hscan), List.length_cons] + refine ⟨ihzero, fun h y hy => ?_, by omega⟩ + rcases List.mem_cons.mp hy with rfl | hy + · exact hscan + · exact ihnone h y hy + +variable (p) (ans : InstanceIndex p → Fin n × Fin n → Bool) + +/-- The hypothesis on the oracle: `ans ι` is a correct answer to the instance with index `ι`, for +every instance of the reduction. -/ +def AnswersCorrect (wAB wBC wAC : Fin n → Fin n → ℤ) (D g p : ℕ) + (ans : InstanceIndex p → Fin n × Fin n → Bool) : Prop := + ∀ ι ∈ instanceIndices wAB D g p, Set.EqOn (fun q => ans ι q = true) + (fun q => (lopInstance wAB wBC wAC D g p ι).HasCommonNeighbor q.1 q.2) + ((lopInstance wAB wBC wAC D g p ι).W : Set _) + +/-- Proof of Theorem 17: a "failed scan, of a piece C_k for a pair (a,b)": the oracle accepted the +pair in the instance, and the piece contains no `c` with `S(a,b,c) = 0`. -/ +def failedScans (wAB wBC wAC : Fin n → Fin n → ℤ) (D g p : ℕ) + (ans : InstanceIndex p → Fin n × Fin n → Bool) : Finset (InstanceIndex p × (Fin n × Fin n)) := + (acceptedPairs wAB wBC wAC D g p ans).filter fun x => scanOf wAB wBC wAC D g x = none + +variable {wAB wBC wAC D g p ans} + +/-- The accepted pairs: an index of an instance, a query pair of that instance, and the answer +"yes". -/ +theorem mem_acceptedPairs {ι : InstanceIndex p} {q : Fin n × Fin n} : + (ι, q) ∈ acceptedPairs wAB wBC wAC D g p ans ↔ + ι ∈ instanceIndices wAB D g p ∧ q ∈ (lopInstance wAB wBC wAC D g p ι).W ∧ ans ι q = true := by + simp [acceptedPairs] + +/-! ### The sentences of the paragraph "Witnesses" -/ + +/-- Proof of Theorem 17: "A zero triangle is always found, since its c lies in some piece and makes +the oracle accept its pair." -/ +theorem exists_mem_acceptedPairs (hD : 16 ≤ D) (hDn : D ≤ n) (hg1 : 1 ≤ g) (hp : p ≠ 0) + (hans : AnswersCorrect wAB wBC wAC D g p ans) {a b c : Fin n} + (h0 : weightSum wAB wBC wAC a b c = 0) : + ∃ ϱ j k, ((ϱ, j, k), (a, b)) ∈ acceptedPairs wAB wBC wAC D g p ans ∧ c ∈ piece n D g k := by + -- The piece of `c`, the residue class of `(a, b)` and the chunk of `(a, b)` in it. + obtain ⟨k, ⟨hk, hck⟩, -⟩ := existsUnique_mem_piece hD hg1 c + obtain ⟨ϱ, hab, -⟩ := existsUnique_mem_residueClass wAB hp (a, b) + obtain ⟨j, hj, hW⟩ := Finset.mem_biUnion.mp + ((biUnion_chunk (residueClass wAB p ϱ) (one_le_queryCap (by omega) hDn)).symm ▸ hab) + have hι : ((ϱ, j, k) : InstanceIndex p) ∈ instanceIndices wAB D g p := + (mem_instanceIndices wAB _).mpr ⟨Finset.mem_range.mp hj, hk⟩ + have hquery : (a, b) ∈ (lopInstance wAB wBC wAC D g p (ϱ, j, k)).W := hW + refine ⟨ϱ, j, k, mem_acceptedPairs.mpr ⟨hι, hquery, ?_⟩, hck⟩ + -- The vertex `c` makes the oracle accept the pair. + apply (Iff.of_eq (hans _ hι hquery)).mpr + rw [hasCommonNeighbor_lopInstance_iff wAB wBC wAC hp hquery] + exact ⟨c, hck, by rw [show weightSum wAB wBC wAC a b c = 0 from h0]⟩ + +/-- Proof of Theorem 17: "A failed scan, of a piece C_k for a pair (a,b), contains a c ∈ C_k with +S(a,b,c) ≡ 0 (mod p) but S(a,b,c) ≠ 0, that is, a false positive (a,b,c) of p". -/ +theorem exists_isFalsePositive_of_mem_failedScans (hp : p ≠ 0) + (hans : AnswersCorrect wAB wBC wAC D g p ans) + {ϱ : Fin p} {j k : ℕ} {a b : Fin n} + (hx : ((ϱ, j, k), (a, b)) ∈ failedScans wAB wBC wAC D g p ans) : + ∃ c ∈ piece n D g k, IsFalsePositive wAB wBC wAC p (a, b, c) := by + obtain ⟨hacc, hnone⟩ := Finset.mem_filter.mp hx + obtain ⟨hι, hquery, htrue⟩ := mem_acceptedPairs.mp hacc + obtain ⟨c, hc, hS⟩ := (hasCommonNeighbor_lopInstance_iff wAB wBC wAC hp hquery).mp + ((Iff.of_eq (hans _ hι hquery)).mp htrue) + exact ⟨c, hc, scanPiece_eq_none_iff.mp hnone c hc, Int.modEq_zero_iff_dvd.mp hS⟩ + +/-- Proof of Theorem 17: "distinct scans contain distinct false positives": a triple `(a,b,c)` +belongs to the scan of at most one accepted pair. -/ +theorem eq_of_mem_acceptedPairs {ϱ ϱ' : Fin p} {j j' k k' : ℕ} {q : Fin n × Fin n} + (hx : ((ϱ, j, k), q) ∈ acceptedPairs wAB wBC wAC D g p ans) + (hy : ((ϱ', j', k'), q) ∈ acceptedPairs wAB wBC wAC D g p ans) {c : Fin n} + (hc : c ∈ piece n D g k) + (hc' : c ∈ piece n D g k') : (ϱ, j, k) = (ϱ', j', k') := by + have hp : p ≠ 0 := by + rintro rfl + exact ϱ.elim0 + obtain ⟨hclass, hchunk⟩ := mem_lopInstance_W (mem_acceptedPairs.mp hx).2.1 + obtain ⟨hclass', hchunk'⟩ := mem_lopInstance_W (mem_acceptedPairs.mp hy).2.1 + -- The pair determines its residue class, then its chunk; the vertex `c` determines the piece. + obtain rfl : ϱ = ϱ' := (existsUnique_mem_residueClass wAB hp q).unique hclass hclass' + obtain rfl : j = j' := hchunk.symm.trans hchunk' + obtain rfl : k = k' := ((mem_piece c).mp hc).symm.trans ((mem_piece c).mp hc') + rfl + +/-- Proof of Theorem 17: "distinct scans contain distinct false positives, so there are at most F(p) +failed scans", for any finite family `s` of scans and any notion "the scan `x` contains the triple +`t`". This applies to the accepted-pair scans in `card_failedScans_le`. -/ +theorem card_le_falsePositiveCount_of_distinct_scans + (wAB wBC wAC : Fin n → Fin n → ℤ) {σ : Type*} (s : Finset σ) + (contains : σ → Fin n × Fin n × Fin n → Prop) + (hfalse : ∀ x ∈ s, ∃ t, contains x t ∧ IsFalsePositive wAB wBC wAC p t) + (hdistinct : ∀ t, ∀ x ∈ s, ∀ y ∈ s, contains x t → contains y t → x = y) : + s.card ≤ falsePositiveCount wAB wBC wAC p := by + classical + refine Finset.card_le_card_of_forall_subsingleton contains (fun x hx => ?_) + fun t _ x hx y hy => hdistinct t x hx.1 y hy.1 hx.2 hy.2 + obtain ⟨t, ht, hfp⟩ := hfalse x hx + exact ⟨t, Finset.mem_filter.mpr ⟨Finset.mem_univ _, hfp⟩, ht⟩ + +/-- Proof of Theorem 17: "so there are at most F(p) failed scans". -/ +theorem card_failedScans_le (hp : p ≠ 0) + (hans : AnswersCorrect wAB wBC wAC D g p ans) : + (failedScans wAB wBC wAC D g p ans).card ≤ falsePositiveCount wAB wBC wAC p := by + -- The scan of the piece `C_k` for the pair `(a, b)` contains the triples `(a, b, c)`, `c ∈ C_k`. + refine card_le_falsePositiveCount_of_distinct_scans wAB wBC wAC _ + (fun x t => x.2 = (t.1, t.2.1) ∧ t.2.2 ∈ piece n D g x.1.2.2) ?_ ?_ + · rintro ⟨⟨ϱ, j, k⟩, a, b⟩ hx + obtain ⟨c, hc, hfalse⟩ := exists_isFalsePositive_of_mem_failedScans hp hans hx + exact ⟨(a, b, c), ⟨rfl, hc⟩, hfalse⟩ + · rintro ⟨a, b, c⟩ ⟨⟨ϱ, j, k⟩, q⟩ hx ⟨⟨ϱ', j', k'⟩, q'⟩ hy ⟨rfl, hc⟩ ⟨rfl, hc'⟩ + rw [eq_of_mem_acceptedPairs (Finset.mem_filter.mp hx).1 (Finset.mem_filter.mp hy).1 hc hc'] + +end Matrices + +/-- Proof of Theorem 17: the length of a scan, the `O(√D/g)` in "the scans cost O((F(p) + 1)√D/g)": +a piece has at most `⌈s/g⌉ ≤ 2√D/g` vertices. -/ +theorem pieceSize_le_two_mul_sqrt_div (hg1 : 1 ≤ g) (hg : (g : ℝ) ≤ Real.sqrt D) : + (pieceSize D g : ℝ) ≤ 2 * Real.sqrt D / (g : ℝ) := by + have hg0 : (0 : ℝ) < g := by exact_mod_cast hg1 + calc (pieceSize D g : ℝ) ≤ (sOf D : ℝ) / (g : ℝ) + 1 := by + rw [pieceSize_eq_ceil] + exact (Nat.ceil_lt_add_one (by positivity)).le + _ ≤ Real.sqrt D / (g : ℝ) + Real.sqrt D / (g : ℝ) := + add_le_add (div_le_div_of_nonneg_right (sOf_le_sqrt D) hg0.le) + ((one_le_div hg0).mpr hg) + _ = 2 * Real.sqrt D / (g : ℝ) := by ring + +/-- Proof of Theorem 17: "the scans cost O((F(p) + 1)√D/g) = O(ν n³ log n/g)". -/ +theorem falsePositiveCount_add_one_mul_pieceSize_le {κ : ℝ} (wAB wBC wAC : Fin n → Fin n → ℤ) + (hD : 16 ≤ D) (hDn : D ≤ n) (hg1 : 1 ≤ g) (hg : (g : ℝ) ≤ Real.sqrt D) (hκ : 1 ≤ κ) + (hT : WeightsBoundedBy wAB wBC wAC ((n : ℝ) ^ κ)) (hp : IsSelectedPrime wAB wBC wAC D p) : + ((falsePositiveCount wAB wBC wAC p : ℝ) + 1) * (pieceSize D g : ℝ) + ≤ (2 * Hashing.falsePositiveConst + 2) * (κ * (n : ℝ) ^ 3 * Real.log n / (g : ℝ)) := by + have hsqrt : 4 ≤ Real.sqrt (D : ℝ) := + Real.le_sqrt_of_sq_le (by norm_num; exact_mod_cast hD) + have hn : (16 : ℝ) ≤ n := by exact_mod_cast hD.trans hDn + have hlog : 1 ≤ Real.log n := + (Real.le_log_iff_exp_le (by linarith)).2 (Real.exp_one_lt_three.le.trans (by linarith)) + set B := κ * (n : ℝ) ^ 3 * Real.log n with hB + -- The two terms: `F(p) √D = O(B)` by the bound on `F(p)`, and `√D ≤ n ≤ n³ ≤ B`. + have hfalse : (falsePositiveCount wAB wBC wAC p : ℝ) * Real.sqrt D ≤ + Hashing.falsePositiveConst * B := by + have hF := Hashing.falsePositiveCount_le_const_mul hD hDn hκ wAB wBC wAC hT hp + rwa [← mul_div_assoc, le_div_iff₀ (by linarith)] at hF + have hone : Real.sqrt D ≤ B := + calc Real.sqrt D ≤ n := sqrt_le_natCast (by omega) hDn + _ ≤ (n : ℝ) ^ 3 := le_self_pow₀ (by linarith) (by norm_num) + _ ≤ κ * (n : ℝ) ^ 3 := le_mul_of_one_le_left (by positivity) hκ + _ ≤ B := le_mul_of_one_le_right (by positivity) hlog + calc ((falsePositiveCount wAB wBC wAC p : ℝ) + 1) * (pieceSize D g : ℝ) + ≤ ((falsePositiveCount wAB wBC wAC p : ℝ) + 1) * (2 * Real.sqrt D / (g : ℝ)) := by + gcongr + exact pieceSize_le_two_mul_sqrt_div hg1 hg + _ = (2 * ((falsePositiveCount wAB wBC wAC p : ℝ) * Real.sqrt D) + + 2 * Real.sqrt D) / (g : ℝ) := by ring + _ ≤ (2 * (Hashing.falsePositiveConst * B) + 2 * B) / (g : ℝ) := by gcongr + _ = (2 * Hashing.falsePositiveConst + 2) * (B / (g : ℝ)) := by ring + +/-! ### The output of the reduction -/ + +/-- **Theorem 17, correctness.** Given any correct answers of an oracle for +Lop-AE-SparseTri to the instances of the reduction, and whatever the order `L` of the scans, the +reduction's output is correct: it is a zero triangle if one exists, and `none` otherwise. It +carries out at most `F(p) + 1` scans ("We stop as soon as a zero triangle is found"). -/ +theorem recovery_correct (hD : 16 ≤ D) (hDn : D ≤ n) (hg1 : 1 ≤ g) + (wAB wBC wAC : Fin n → Fin n → ℤ) (hp : IsSelectedPrime wAB wBC wAC D p) + {ans : InstanceIndex p → Fin n × Fin n → Bool} (hans : AnswersCorrect wAB wBC wAC D g p ans) + {L : List (InstanceIndex p × (Fin n × Fin n))} (hL : IsScanOrder wAB wBC wAC D g p ans L) : + IsSearchAnswer wAB wBC wAC (runScans wAB wBC wAC D g L).1 ∧ + (runScans wAB wBC wAC D g L).2 ≤ falsePositiveCount wAB wBC wAC p + 1 := by + have hp0 : p ≠ 0 := (mem_primesInRange.mp hp.1).1.ne_zero + obtain ⟨hzero, hnone, hcount⟩ := runScans_spec wAB wBC wAC D g L + refine ⟨⟨hzero, fun hout ⟨a, b, c, h0⟩ => ?_⟩, ?_⟩ + · -- "A zero triangle is always found": its scan cannot have failed. + obtain ⟨ϱ, j, k, hx, hc⟩ := exists_mem_acceptedPairs hD hDn hg1 hp0 hans h0 + exact scanPiece_eq_none_iff.mp (hnone hout _ ((hL.2 _).mpr hx)) c hc h0 + · -- All the scans except possibly the last one fail, and there are at most `F(p)` failed scans. + have hfailed : (L.filter fun x => decide (scanOf wAB wBC wAC D g x = none)).length + ≤ (failedScans wAB wBC wAC D g p ans).card := by + rw [← List.toFinset_card_of_nodup (hL.1.filter _)] + refine Finset.card_le_card fun x hx => ?_ + obtain ⟨hxL, hxnone⟩ := List.mem_filter.mp (List.mem_toFinset.mp hx) + exact Finset.mem_filter.mpr ⟨(hL.2 x).mp hxL, by simpa using hxnone⟩ + have hF := card_failedScans_le hp0 hans + omega + + +/-- Every finite set of accepted queries admits a duplicate-free scan order. -/ +theorem exists_isScanOrder (wAB wBC wAC : Fin n → Fin n → ℤ) (D g p : ℕ) + (ans : InstanceIndex p → Fin n × Fin n → Bool) : + ∃ L, IsScanOrder wAB wBC wAC D g p ans L := + ⟨(acceptedPairs wAB wBC wAC D g p ans).toList, + Finset.nodup_toList _, fun _ => Finset.mem_toList⟩ + +/-- Correct finite answer tables exist. This is a finite specification, with no solver or +complexity claim. Values outside the queried edges remain unconstrained. -/ +theorem exists_answersCorrect (wAB wBC wAC : Fin n → Fin n → ℤ) (D g p : ℕ) : + ∃ ans, AnswersCorrect wAB wBC wAC D g p ans := by + classical + refine ⟨fun ι q => decide ((lopInstance wAB wBC wAC D g p ι).HasCommonNeighbor q.1 q.2), ?_⟩ + intro ι _ q hq + simp + +/-- Correct finite common-neighbor counts supply the Boolean answers used by recovery. +This connects directly to the lopsided matrix/counting interface. -/ +theorem answersCorrect_of_counting (wAB wBC wAC : Fin n → Fin n → ℤ) (D g p : ℕ) + (counts : InstanceIndex p → Fin n × Fin n → ℕ) + (hcounts : ∀ ι ∈ instanceIndices wAB D g p, + Set.EqOn (counts ι) (fun q => (lopInstance wAB wBC wAC D g p ι).commonNeighborCount q.1 q.2) + ((lopInstance wAB wBC wAC D g p ι).W : Set _)) : + AnswersCorrect wAB wBC wAC D g p (fun ι q => decide (counts ι q ≠ 0)) := by + intro ι hι q hq + apply propext + simp only [decide_eq_true_eq] + exact Iff.of_eq ((lopInstance wAB wBC wAC D g p ι).detection_eqOn_of_counting + (counts ι) (hcounts ι hι) hq) + +/-- The recovery scan examines at most the uniform false-positive bound times the piece size. +This counts candidate vertices only; it is not a charged native running-time theorem. -/ +theorem recovery_vertexBound {κ : ℝ} (hD : 16 ≤ D) (hDn : D ≤ n) (hg1 : 1 ≤ g) + (hg : (g : ℝ) ≤ Real.sqrt D) (hκ : 1 ≤ κ) + (wAB wBC wAC : Fin n → Fin n → ℤ) + (hw : WeightsBoundedBy wAB wBC wAC ((n : ℝ) ^ κ)) + (hp : IsSelectedPrime wAB wBC wAC D p) + {ans : InstanceIndex p → Fin n × Fin n → Bool} (hans : AnswersCorrect wAB wBC wAC D g p ans) + {L : List (InstanceIndex p × (Fin n × Fin n))} (hL : IsScanOrder wAB wBC wAC D g p ans L) : + IsSearchAnswer wAB wBC wAC (runScans wAB wBC wAC D g L).1 ∧ + ((runScans wAB wBC wAC D g L).2 : ℝ) * pieceSize D g ≤ + (2 * Hashing.falsePositiveConst + 2) * (κ * (n : ℝ) ^ 3 * Real.log n / (g : ℝ)) := by + obtain ⟨hcorrect, hcount⟩ := recovery_correct hD hDn hg1 wAB wBC wAC hp hans hL + refine ⟨hcorrect, le_trans ?_ (falsePositiveCount_add_one_mul_pieceSize_le + wAB wBC wAC hD hDn hg1 hg hκ hw hp)⟩ + gcongr + exact_mod_cast hcount + +/-- Finite reduction equivalence: scanning all accepted queries returns `none` exactly when the +original three matrices have no zero triangle. Any duplicate-free scan order works. -/ +theorem recovery_eq_none_iff (hD : 16 ≤ D) (hDn : D ≤ n) (hg1 : 1 ≤ g) + (wAB wBC wAC : Fin n → Fin n → ℤ) (hp : IsSelectedPrime wAB wBC wAC D p) + {ans : InstanceIndex p → Fin n × Fin n → Bool} (hans : AnswersCorrect wAB wBC wAC D g p ans) + {L : List (InstanceIndex p × (Fin n × Fin n))} (hL : IsScanOrder wAB wBC wAC D g p ans L) : + (runScans wAB wBC wAC D g L).1 = none ↔ ¬ HasZeroTriangle wAB wBC wAC := by + obtain ⟨hsound, hcomplete⟩ := (recovery_correct hD hDn hg1 wAB wBC wAC hp hans hL).1 + refine ⟨hcomplete, fun hn => ?_⟩ + cases h : (runScans wAB wBC wAC D g L).1 with + | none => rfl + | some t => + obtain ⟨a, b, c⟩ := t + exact False.elim (hn ⟨a, b, c, hsound a b c h⟩) + +end Cslib.Algorithms.ExactTriangle diff --git a/Complexity/Foundations/Data/Finset/Quotient.lean b/Complexity/Foundations/Data/Finset/Quotient.lean new file mode 100644 index 000000000..875f7e52d --- /dev/null +++ b/Complexity/Foundations/Data/Finset/Quotient.lean @@ -0,0 +1,44 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Order.Interval.Finset.Nat + +/-! +# Cardinality of a quotient fiber + +## Main results + +* `Finset.card_filter_div_eq_le`: an injectively indexed quotient fiber has at most the divisor + many members. + +## References + +Adapted from [Counting.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Util/Counting.lean) at +`anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. +-/ + +@[expose] public section + +namespace Finset + +/-- At most `c` elements `x` of `s` have `⌊f x / c⌋ = k`, if `f` is injective on `s`: their values +lie in `[kc, kc + c)`. -/ +theorem card_filter_div_eq_le {α : Type*} (s : Finset α) {f : α → ℕ} (hf : Set.InjOn f s) + {c : ℕ} (hc : 0 < c) (k : ℕ) : (s.filter fun x => f x / c = k).card ≤ c := by + have hmaps : ∀ x ∈ s.filter fun x => f x / c = k, f x ∈ Finset.Ico (k * c) (k * c + c) := by + intro x hx + obtain ⟨-, rfl⟩ := Finset.mem_filter.mp hx + rw [Finset.mem_Ico, ← Nat.succ_mul] + exact ⟨Nat.div_mul_le_self _ _, (Nat.div_lt_iff_lt_mul hc).mp (Nat.lt_succ_self _)⟩ + calc (s.filter fun x => f x / c = k).card ≤ (Finset.Ico (k * c) (k * c + c)).card := + Finset.card_le_card_of_injOn f hmaps (hf.mono fun x hx => (Finset.mem_filter.mp hx).1) + _ = c := by rw [Nat.card_Ico, Nat.add_sub_cancel_left] + +end Finset diff --git a/Complexity/Foundations/Data/Int/Residue.lean b/Complexity/Foundations/Data/Int/Residue.lean new file mode 100644 index 000000000..f6622c6a7 --- /dev/null +++ b/Complexity/Foundations/Data/Int/Residue.lean @@ -0,0 +1,41 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Algebra.Order.Ring.Int + +/-! +# Integer residues as natural numbers + +## Main results + +* `Int.natCast_toNat_emod`: the nonnegative integer residue survives a round trip through `Nat`. +* `Int.toNat_emod_lt`: the natural residue is below a positive modulus. + +## References + +Adapted from [Index.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Util/Index.lean) at +`anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. +The existing Exact Triangle hashing client now uses these shared facts. +-/ + +@[expose] public section + +namespace Int + +theorem natCast_toNat_emod {p : ℕ} (hp : 0 < p) (u : ℤ) : + ((u % (p : ℤ)).toNat : ℤ) = u % (p : ℤ) := + Int.toNat_of_nonneg (Int.emod_nonneg _ (by omega)) + +theorem toNat_emod_lt {p : ℕ} (hp : 0 < p) (u : ℤ) : (u % (p : ℤ)).toNat < p := by + have := Int.emod_nonneg u (by omega : (p : ℤ) ≠ 0) + have := Int.emod_lt_of_pos u (by omega : (0 : ℤ) < p) + omega + +end Int diff --git a/Complexity/Foundations/Data/Matrix/PairChunks.lean b/Complexity/Foundations/Data/Matrix/PairChunks.lean new file mode 100644 index 000000000..3f3c74fee --- /dev/null +++ b/Complexity/Foundations/Data/Matrix/PairChunks.lean @@ -0,0 +1,114 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Foundations.Data.Nat.CeilDiv +public import Complexity.Foundations.Data.Finset.Quotient +public import Mathlib.Logic.Equiv.Fin.Basic +public import Mathlib.Data.Fintype.Prod +import Mathlib.Order.Interval.Finset.Fin + +/-! +# Chunks of matrix index pairs + +## Main definitions + +* `pairIndex`, `rankIn`: row-major index and rank in a finite set. +* `chunk`, `numChunks`: consecutive chunks and their number. + +## Main results + +* `card_chunk_le`: each chunk has at most the capacity many pairs. +* `biUnion_chunk`: all chunks cover the input finite set. + +## References + +Adapted from [Chunks.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec3/Chunks.lean) and the minimal definitions in +`PaperStatements.lean` at `anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. +There are no graph or weight assumptions. +-/ + +@[expose] public section + +namespace Cslib.Matrix + +/-- The position of the pair `(a, b)` in the row-major order of `A × B`. -/ +def pairIndex {n : ℕ} (q : Fin n × Fin n) : ℕ := q.1.val * n + q.2.val + +/-- The number of pairs of `S` that come before `q` in row-major order. -/ +def rankIn {n : ℕ} (S : Finset (Fin n × Fin n)) (q : Fin n × Fin n) : ℕ := + (S.filter fun r => pairIndex r < pairIndex q).card + +/-- The `j`-th chunk (`j = 0, 1, …`) of `S` when `S` is cut, in row-major order, into chunks of +`cap` pairs each (the last one may be smaller). -/ +def chunk {n : ℕ} (S : Finset (Fin n × Fin n)) (cap j : ℕ) : Finset (Fin n × Fin n) := + S.filter fun q => rankIn S q / cap = j + +/-- The number `⌈|S| / cap⌉` of nonempty chunks of `S` (for `cap ≥ 1`). -/ +def numChunks {n : ℕ} (S : Finset (Fin n × Fin n)) (cap : ℕ) : ℕ := + S.card ⌈/⌉ cap + +variable {n : ℕ} {S : Finset (Fin n × Fin n)} {cap : ℕ} + +/-! ### Ranks -/ + +/-- The position in row-major order determines the pair. -/ +theorem pairIndex_injective : Function.Injective (pairIndex (n := n)) := fun q r h => + finProdFinEquiv.injective (Fin.ext (by simpa [pairIndex, add_comm, mul_comm] using h)) + +/-- Among the pairs of `S`, a pair that comes earlier in row-major order has a smaller rank. -/ +theorem rankIn_lt_rankIn {q r : Fin n × Fin n} (hq : q ∈ S) + (h : pairIndex q < pairIndex r) : rankIn S q < rankIn S r := by + refine Finset.card_lt_card ⟨fun x hx => ?_, fun hsub => ?_⟩ + · rw [Finset.mem_filter] at hx ⊢ + exact ⟨hx.1, hx.2.trans h⟩ + · exact lt_irrefl _ (Finset.mem_filter.mp (hsub (Finset.mem_filter.mpr ⟨hq, h⟩))).2 + +/-- Different pairs of `S` have different ranks. -/ +theorem rankIn_injOn (S : Finset (Fin n × Fin n)) : Set.InjOn (rankIn S) S := by + intro q hq r hr h + rcases lt_trichotomy (pairIndex q) (pairIndex r) with hlt | heq | hgt + · exact absurd h (rankIn_lt_rankIn hq hlt).ne + · exact pairIndex_injective heq + · exact absurd h (rankIn_lt_rankIn hr hgt).ne' + +/-- The rank of a pair of `S` is less than the number of pairs of `S`. -/ +theorem rankIn_lt_card {q : Fin n × Fin n} (hq : q ∈ S) : rankIn S q < S.card := + Finset.card_lt_card ⟨Finset.filter_subset _ _, + fun hsub => lt_irrefl _ (Finset.mem_filter.mp (hsub hq)).2⟩ + +/-- In the full matrix, row-major rank is the row-major index itself. This avoids enumerating +all preceding pairs when applying the chunk API to a full matrix. -/ +theorem rankIn_univ (q : Fin n × Fin n) : + rankIn (Finset.univ : Finset (Fin n × Fin n)) q = pairIndex q := by + calc rankIn (Finset.univ : Finset (Fin n × Fin n)) q + = (Finset.Iio (finProdFinEquiv q)).card := by + apply Finset.card_equiv finProdFinEquiv + intro r + simp [pairIndex, finProdFinEquiv, add_comm, mul_comm, Fin.lt_def] + _ = pairIndex q := by simp [pairIndex, finProdFinEquiv, add_comm, mul_comm] + +/-! ### Chunks -/ + +/-- A chunk has at most `cap` pairs: their ranks are different numbers in `[j cap, (j + 1) cap)`. -/ +theorem card_chunk_le (S : Finset (Fin n × Fin n)) (hcap : 1 ≤ cap) (j : ℕ) : + (chunk S cap j).card ≤ cap := + S.card_filter_div_eq_le (rankIn_injOn S) hcap j + +/-- The first `⌈|S|/cap⌉` chunks cover `S`, because `|S| ≤ ⌈|S|/cap⌉ cap`. -/ +theorem biUnion_chunk (S : Finset (Fin n × Fin n)) (hcap : 1 ≤ cap) : + (Finset.range (numChunks S cap)).biUnion (fun j => chunk S cap j) = S := by + have hcard : S.card ≤ numChunks S cap * cap := by + rw [numChunks] + exact Nat.le_ceilDiv_mul hcap + ext q + simp only [Finset.mem_biUnion, Finset.mem_range, chunk, Finset.mem_filter] + refine ⟨fun ⟨_, _, hq, _⟩ => hq, fun hq => ⟨rankIn S q / cap, ?_, hq, rfl⟩⟩ + exact (Nat.div_lt_iff_lt_mul hcap).mpr ((rankIn_lt_card hq).trans_le hcard) + +end Cslib.Matrix diff --git a/Complexity/Foundations/Data/Nat/CeilDiv.lean b/Complexity/Foundations/Data/Nat/CeilDiv.lean new file mode 100644 index 000000000..c88317bc1 --- /dev/null +++ b/Complexity/Foundations/Data/Nat/CeilDiv.lean @@ -0,0 +1,55 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Algebra.Order.Floor.Div +public import Mathlib.Algebra.Order.Archimedean.Real.Basic + +/-! +# Natural ceilings of quotients + +## Main results + +* `Nat.ceil_div_eq_ceilDiv`: real quotient ceilings equal natural ceiling division. +* `Nat.le_ceilDiv_mul`, `Nat.ceilDiv_mul_lt`: covering and overshoot bounds. + +## References + +Adapted from [Ceil.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Util/Ceil.lean) at +`anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. +-/ + +@[expose] public section + +namespace Nat + +/-! ## The ceiling of a quotient of natural numbers -/ + +/-- `a ⌈/⌉ b ≤ k` says that `k` pieces of size `b` cover `a`. Mathlib's `ceilDiv_le_iff_le_mul` has +`b * k` on the right. -/ +theorem ceilDiv_le_iff {a b k : ℕ} (hb : 0 < b) : a ⌈/⌉ b ≤ k ↔ a ≤ k * b := by + rw [ceilDiv_le_iff_le_mul hb, Nat.mul_comm] + +/-- `a ⌈/⌉ b` pieces of size `b` cover `a`. -/ +theorem le_ceilDiv_mul {a b : ℕ} (hb : 0 < b) : a ≤ a ⌈/⌉ b * b := + (ceilDiv_le_iff hb).1 le_rfl + +/-- `a ⌈/⌉ b` pieces of size `b` overshoot `a` by less than one piece. -/ +theorem ceilDiv_mul_lt {a b : ℕ} (hb : 0 < b) : a ⌈/⌉ b * b < a + b := by + have := Nat.div_mul_le_self (a + b - 1) b + rw [Nat.ceilDiv_eq_add_pred_div] + omega + +/-- The ceiling of the real quotient of two natural numbers, in natural numbers. -/ +theorem ceil_div_eq_ceilDiv (a : ℕ) {b : ℕ} (hb : 0 < b) : ⌈(a : ℝ) / (b : ℝ)⌉₊ = a ⌈/⌉ b := by + refine eq_of_forall_ge_iff fun k => ?_ + rw [Nat.ceil_le, div_le_iff₀ (Nat.cast_pos.2 hb), ceilDiv_le_iff hb] + exact_mod_cast Iff.rfl + +end Nat diff --git a/Complexity/Foundations/Data/Nat/SqrtRatio.lean b/Complexity/Foundations/Data/Nat/SqrtRatio.lean new file mode 100644 index 000000000..602c73737 --- /dev/null +++ b/Complexity/Foundations/Data/Nat/SqrtRatio.lean @@ -0,0 +1,47 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Analysis.Real.Sqrt +import Mathlib.Tactic.Positivity +import Mathlib.Tactic.NormNum + +/-! +# Computing a floor involving a square root + +## Main results + +* `Nat.sqrt_div_eq_floor_div_sqrt`: the floor of `a / sqrt b` is the natural square root + of `a² / b`, including the zero-denominator convention. + +This elementary arithmetic bridge is original adaptation code. It lets finite algorithms use +integer arithmetic while retaining their real-valued capacity specifications. +-/ + +public section + +namespace Nat + +/-- A computable integer formula for the floor of a quotient by a square root. -/ +theorem sqrt_div_eq_floor_div_sqrt (a b : ℕ) : + Nat.sqrt (a ^ 2 / b) = ⌊(a : ℝ) / Real.sqrt b⌋₊ := by + by_cases hb : b = 0 + · simp [hb] + have hb0 : 0 < b := Nat.pos_of_ne_zero hb + have hs : 0 < Real.sqrt (b : ℝ) := Real.sqrt_pos.mpr (by exact_mod_cast hb0) + apply eq_of_forall_le_iff + intro k + rw [Nat.le_sqrt', Nat.le_div_iff_mul_le hb0, Nat.le_floor_iff (by positivity), + le_div_iff₀ hs] + have hcast : k ^ 2 * b ≤ a ^ 2 ↔ (k : ℝ) ^ 2 * (b : ℝ) ≤ (a : ℝ) ^ 2 := by + norm_cast + rw [hcast, ← sq_le_sq₀ (show (0 : ℝ) ≤ k * Real.sqrt b by positivity) (Nat.cast_nonneg a)] + rw [mul_pow, Real.sq_sqrt (Nat.cast_nonneg b)] + +end Nat diff --git a/ComplexityTests/ExactTriangleSplitting.lean b/ComplexityTests/ExactTriangleSplitting.lean new file mode 100644 index 000000000..1768d67f6 --- /dev/null +++ b/ComplexityTests/ExactTriangleSplitting.lean @@ -0,0 +1,158 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +import Complexity.Algorithms.ExactTriangle.Witnesses +import Mathlib.Tactic.NormNum.NatSqrt + +open Cslib.Algorithms.ExactTriangle Cslib.Matrix + +-- Row-major chunks, including the short last chunk and the first empty chunk. +example : chunk (Finset.univ : Finset (Fin 2 × Fin 2)) 3 0 = + {(0, 0), (0, 1), (1, 0)} := by decide +example : chunk (Finset.univ : Finset (Fin 2 × Fin 2)) 3 1 = {(1, 1)} := by decide +example : chunk (Finset.univ : Finset (Fin 2 × Fin 2)) 3 2 = ∅ := by decide +example : chunk (∅ : Finset (Fin 2 × Fin 2)) 3 0 = ∅ := by decide +example : numChunks (Finset.univ : Finset (Fin 2 × Fin 2)) 3 = 2 := by + rw [numChunks, Nat.ceilDiv_eq_add_pred_div] + decide + +private theorem pieceSize16 : pieceSize 16 2 = 2 := by + rw [pieceSize, sOf] + norm_num [Nat.ceilDiv_eq_add_pred_div] +private theorem queryCap16 : queryCap 16 16 = 64 := by norm_num [queryCap] +example : queryCap 17 17 = 70 := by norm_num [queryCap] +example : queryCap 0 16 = 0 := by norm_num [queryCap] +example : queryCap 16 0 = 0 := by norm_num [queryCap] + +-- Boundary indices belong to adjacent pieces; out-of-range pieces are empty. +example : (1 : Fin 16) ∈ piece 16 16 2 0 ∧ (2 : Fin 16) ∈ piece 16 16 2 1 := by + simp [piece, pieceSize16] +example : piece 16 16 2 8 = ∅ := by + ext c + simp only [piece, pieceSize16, Finset.mem_filter, Finset.mem_univ, true_and, + Finset.notMem_empty, iff_false] + omega +example : piece 0 16 2 0 = ∅ := by simp [piece] +example : numPieces 16 16 2 = 8 := by + rw [numPieces, pieceSize16, Nat.ceilDiv_eq_add_pred_div] + +private def zeroWeight : Fin 16 → Fin 16 → ℤ := fun _ _ => 0 +private def falseWeight : Fin 16 → Fin 16 → ℤ := fun _ _ => 6 + +-- The prime window admits p = 3 at the smallest supported D. +private theorem prime3 : 3 ∈ primesInRange 16 := by + rw [mem_primesInRange_iff_sq] + decide + +-- All-zero matrices supply an admitted, nonempty splitting with the promised bounds. +example : (totalChunks zeroWeight 16 3 : ℝ) ≤ 3 + Real.sqrt 16 := + (totalChunks_le zeroWeight (by decide) (by decide) prime3).1 +example : ((instanceIndices zeroWeight 16 2 3).card : ℝ) ≤ 4 * 16 * 2 := + card_instanceIndices_le zeroWeight (by decide) (by decide) (by decide) prime3 +example : Fintype.card (lopInstance zeroWeight zeroWeight zeroWeight 16 2 3 (0, 0, 0)).M ≤ 16 := + card_middle_lopInstance zeroWeight zeroWeight zeroWeight (by decide) (by decide) prime3 _ + +-- Residues include negative weights, and are independent of exact zero sums. +example : (0, 0) ∈ residueClass (fun _ _ : Fin 1 => -1) 3 2 := by decide +example : IsFalsePositive falseWeight zeroWeight zeroWeight 3 (0, 0, 0) := by decide +example : ¬ HasZeroTriangle falseWeight zeroWeight zeroWeight := by + simp [HasZeroTriangle, falseWeight, zeroWeight] +example : HasZeroTriangle zeroWeight zeroWeight zeroWeight := ⟨0, 0, 0, rfl⟩ + +-- Recovery rejects a modular false positive; a true zero returns a witness on its first scan. +example : scanPiece falseWeight zeroWeight zeroWeight 16 2 0 0 0 = none := by + rw [scanPiece_eq_none_iff] + intro c _ + norm_num [weightSum, falseWeight, zeroWeight] +example : scanPiece zeroWeight zeroWeight zeroWeight 16 2 0 0 0 = some 0 := by + simp [scanPiece, piece, pieceSize16, weightSum, zeroWeight, List.finRange_succ] +example : runScans falseWeight zeroWeight zeroWeight 16 2 + [(((0 : Fin 3), 0, 0), (0, 0))] = (none, 1) := by + have hscan : scanPiece falseWeight zeroWeight zeroWeight 16 2 0 0 0 = none := by + rw [scanPiece_eq_none_iff] + intro c _ + norm_num [weightSum, falseWeight, zeroWeight] + simp [runScans, hscan] +example : runScans zeroWeight zeroWeight zeroWeight 16 2 + [(((0 : Fin 3), 0, 0), (0, 0)), ((0, 0, 1), (1, 1))] = (some (0, 0, 0), 1) := by + simp [runScans, scanPiece, piece, pieceSize16, weightSum, zeroWeight, List.finRange_succ] + +-- The query at rank 63 ends chunk 0; rank 64 starts chunk 1. +example : (3, 15) ∈ chunkOf zeroWeight 16 3 0 0 := by + have hclass : residueClass zeroWeight 3 0 = Finset.univ := by + ext q + simp [residueClass, zeroWeight] + simp [chunkOf, queryCap16, hclass, chunk, rankIn_univ, pairIndex] +example : (4, 0) ∈ chunkOf zeroWeight 16 3 0 1 := by + have hclass : residueClass zeroWeight 3 0 = Finset.univ := by + ext q + simp [residueClass, zeroWeight] + simp [chunkOf, queryCap16, hclass, chunk, rankIn_univ, pairIndex] + +private theorem falseQuery : (0, 0) ∈ + (lopInstance falseWeight zeroWeight zeroWeight 16 2 3 (0, 0, 0)).W := by + change (0, 0) ∈ chunkOf falseWeight 16 3 0 0 + have hclass : residueClass falseWeight 3 0 = Finset.univ := by + ext q + norm_num [residueClass, falseWeight, Int.ModEq] + simp [chunkOf, queryCap16, hclass, chunk, rankIn_univ, pairIndex] + +-- A modular query really is accepted despite the absence of any exact zero triangle. +example : + (lopInstance falseWeight zeroWeight zeroWeight 16 2 3 (0, 0, 0)).HasCommonNeighbor 0 0 := by + rw [hasCommonNeighbor_lopInstance_iff falseWeight zeroWeight zeroWeight (by decide) falseQuery] + refine ⟨0, ?_, ?_⟩ + · simp [piece, pieceSize16] + · decide + +-- Supply the actual finite modular answers, rather than an abstract unconstrained answer premise. +private def modularAnswers (wAB wBC wAC : Fin 16 → Fin 16 → ℤ) + (ι : InstanceIndex 3) (q : Fin 16 × Fin 16) : Bool := + decide (∃ c ∈ piece 16 16 2 ι.2.2, weightSum wAB wBC wAC q.1 q.2 c ≡ 0 [ZMOD (3 : ℤ)]) + +private theorem modularAnswers_correct (wAB wBC wAC : Fin 16 → Fin 16 → ℤ) : + AnswersCorrect wAB wBC wAC 16 2 3 (modularAnswers wAB wBC wAC) := by + intro ι _ q hq + apply propext + simp only [modularAnswers, decide_eq_true_eq] + exact (hasCommonNeighbor_lopInstance_iff wAB wBC wAC (by decide) hq).symm + +private theorem zeroSelected : IsSelectedPrime zeroWeight zeroWeight zeroWeight 16 3 := by + refine ⟨prime3, fun q _ => ?_⟩ + simp [countZeroMod, weightSum, zeroWeight] + +-- The complete recovery theorem is inhabited at its smallest admitted n and D. +example : ∃ L, IsScanOrder zeroWeight zeroWeight zeroWeight 16 2 3 + (modularAnswers zeroWeight zeroWeight zeroWeight) L ∧ + (runScans zeroWeight zeroWeight zeroWeight 16 2 L).1 ≠ none ∧ + (runScans zeroWeight zeroWeight zeroWeight 16 2 L).2 ≤ 1 := by + obtain ⟨L, hL⟩ := exists_isScanOrder zeroWeight zeroWeight zeroWeight 16 2 3 + (modularAnswers zeroWeight zeroWeight zeroWeight) + have hc := modularAnswers_correct zeroWeight zeroWeight zeroWeight + obtain ⟨hout, hcount⟩ := recovery_correct (by decide) (by decide) (by decide) + zeroWeight zeroWeight zeroWeight zeroSelected hc hL + refine ⟨L, hL, fun hn => hout.2 hn ⟨0, 0, 0, rfl⟩, ?_⟩ + simpa [falsePositiveCount, IsFalsePositive, weightSum, zeroWeight] using hcount + +private theorem falseSelected : IsSelectedPrime falseWeight zeroWeight zeroWeight 16 3 := by + refine ⟨prime3, fun q hq => ?_⟩ + obtain ⟨hprime, -, hsq⟩ := mem_primesInRange_iff_sq.mp hq + have hq : q = 2 ∨ q = 3 := by + have := hprime.two_le + have : q < 4 := by nlinarith + omega + rcases hq with rfl | rfl <;> + norm_num [countZeroMod, weightSum, falseWeight, zeroWeight, Int.ModEq] + +example : ∃ L, IsScanOrder falseWeight zeroWeight zeroWeight 16 2 3 + (modularAnswers falseWeight zeroWeight zeroWeight) L ∧ + (runScans falseWeight zeroWeight zeroWeight 16 2 L).1 = none := by + obtain ⟨L, hL⟩ := exists_isScanOrder falseWeight zeroWeight zeroWeight 16 2 3 + (modularAnswers falseWeight zeroWeight zeroWeight) + refine ⟨L, hL, (recovery_eq_none_iff (by decide) (by decide) (by decide) + falseWeight zeroWeight zeroWeight falseSelected + (modularAnswers_correct falseWeight zeroWeight zeroWeight) hL).mpr ?_⟩ + simp [HasZeroTriangle, falseWeight, zeroWeight] diff --git a/NOTICE b/NOTICE index 95b9c8d57..e48c2ecb6 100644 --- a/NOTICE +++ b/NOTICE @@ -1,9 +1,11 @@ The 3SUM/Exact Triangle reductions, their finite counting lemmas and native matrix builder in -Complexity/Algorithms/ExactTriangle/{Defs,Weights,Lopsided,Hashing}.lean, +Complexity/Algorithms/ExactTriangle/{Defs,Weights,Lopsided,Hashing,Splitting,Witnesses}.lean, Complexity/Algorithms/ThreeSum/, Complexity/Algorithms/WordRAM/ThreeSum/, -Complexity/Foundations/Data/Finset/Average.lean, -Complexity/Foundations/Data/Matrix/IntegerBits.lean and +Complexity/Foundations/Data/Finset/{Average,Quotient}.lean, +Complexity/Foundations/Data/Int/Residue.lean, +Complexity/Foundations/Data/Nat/CeilDiv.lean, +Complexity/Foundations/Data/Matrix/{IntegerBits,PairChunks}.lean and Complexity/Foundations/NumberTheory/{PrimeCounting,PrimeWindow}.lean and Complexity/Foundations/RingTheory/CyclicIntRing.lean are adapted from anthropics/formal-math, 3sum-apsp, revision From 2cae26c953e11a21b980198ab983f6c52a5a5062 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 07:21:36 -0400 Subject: [PATCH 07/73] doc(threeSum): record finite splitting and recovery milestone Address the nonblocking review finding on #1455: finite residue/chunk/piece splitting and witness recovery are implemented. Preserve native preprocessing, fast solvers, full input/time/width composition and exponent 1.9992 as open work under #1394. Documentation only. Lean and test files are byte-identical to independently reviewed e41e1dda62a10c8f89917e2ccab54fbbedad1ad4, whose six strict gates pass. Checked the diff, milestone reference, implementation paths, hygiene and changed-file lints. Abstracts and rendering are unaffected. --- ROADMAP.md | 10 ++++++---- UPSTREAM.md | 8 ++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index d31b9ede1..044ec8d47 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -423,10 +423,11 @@ each statement against the numbered result. Organize modules by subject, not cha matrix builder and the complete finite mathematical Chan–He reduction ([#1435]), including ordinary distinct-position inputs and explicit size bounds. The finite lopsided matrix/counting interface ([#1438]) adds padding, detection and signed digit reconstruction. Modular counting - and the selected-prime false-positive bound ([#1442]) are proved mathematically; it still needs - residue/chunk/piece splitting and witness recovery, tensor/sparse-query programs, native - Chan–He preprocessing, the reduction host, the fast Exact Triangle solver and full input/time/width/ - asymptotic composition, then the improved Section 4 route to exponent 1.9992; size-based width + and the selected-prime false-positive bound ([#1442]), finite residue/chunk/piece splitting and + witness recovery ([#1447]) are proved mathematically. It still needs tensor/sparse-query programs, + native Chan–He preprocessing, the reduction host, the fast Exact Triangle solver and full + input/time/width/asymptotic composition, then the improved Section 4 route to exponent 1.9992; + size-based width selection with an input-fit guarantee and explicit overhead; expected-time rules for unbounded almost-surely terminating structured loops; random-tape equivalence and BPP/RP/ZPP simulation bridges for the randomized word RAM ([#1124]); transfer of cell-probe lower bounds; and the @@ -552,4 +553,5 @@ not planned, not as proved. [#1435]: https://github.com/cslib-community/complexitylib/issues/1435 [#1438]: https://github.com/cslib-community/complexitylib/issues/1438 [#1442]: https://github.com/cslib-community/complexitylib/issues/1442 +[#1447]: https://github.com/cslib-community/complexitylib/issues/1447 [#1449]: https://github.com/cslib-community/complexitylib/issues/1449 diff --git a/UPSTREAM.md b/UPSTREAM.md index 8b49547da..abb96bb7a 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -189,8 +189,12 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he all-window prime-count bound lives in `Foundations/NumberTheory/PrimeWindow.lean`, retaining the source credit to Mathlib, Patrick Stevens and Bolton Bailey. These are mathematical statements, not native instructions or an assumed solver. See - [#1442](https://github.com/cslib-community/complexitylib/issues/1442). Residue/chunk/piece splitting, - witness recovery and tensor/sparse query programs remain in #1394, alongside native composition. + [#1442](https://github.com/cslib-community/complexitylib/issues/1442). + `Algorithms/ExactTriangle/{Splitting,Witnesses}.lean` adapts the complete finite splitting and + witness recovery component ([#1447](https://github.com/cslib-community/complexitylib/issues/1447)), + using the existing matrices, lopsided graph and `Set.EqOn` answer API. Computable integer parameters + retain the source's exact real floor/ceiling specifications through checked bridges. Tensor/sparse + query programs, native preprocessing/solvers and full input/time/width composition remain in #1394. * **Typed RAM upstream core.** [CSLib #1158](https://github.com/leanprover/cslib/pull/1158) at `beea60a15eddc91d93e92fa5af9fb965c351d0d5` proposes `Cslib/Computability/Machines/RAM/Basic.lean`, in namespace `Cslib.RAM.Typed`. From dc4206a314c9623894f6db04178ea0ecbb019bdf Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 09:23:13 -0400 Subject: [PATCH 08/73] refactor(exactTriangle): reuse the natural residue bound and clarify source prose --- Complexity/Algorithms/ExactTriangle/Hashing.lean | 3 ++- Complexity/Algorithms/ExactTriangle/Splitting.lean | 8 +++++--- Complexity/Algorithms/ExactTriangle/Witnesses.lean | 8 ++++++-- Complexity/Foundations/Data/Int/Residue.lean | 10 ++++------ 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/Complexity/Algorithms/ExactTriangle/Hashing.lean b/Complexity/Algorithms/ExactTriangle/Hashing.lean index 86aba7824..17ee53686 100644 --- a/Complexity/Algorithms/ExactTriangle/Hashing.lean +++ b/Complexity/Algorithms/ExactTriangle/Hashing.lean @@ -226,7 +226,8 @@ theorem falsePositiveCount_add_zeroCount_eq_sum_coeff {p : ℕ} (hp : p ≠ 0) : rw [Fintype.sum_prod_type] refine Finset.sum_congr rfl fun b _ => ?_ have hcast := Int.natCast_toNat_emod (Nat.pos_of_ne_zero hp) (-wAB a b) - rw [(coeff_matP_mul_matQ wBC wAC) hp a b _ (Int.toNat_emod_lt (Nat.pos_of_ne_zero hp) _), + have hr : ((-wAB a b) % (p : ℤ)).toNat < p := Int.natMod_lt (m := -wAB a b) hp + rw [(coeff_matP_mul_matQ wBC wAC) hp a b _ hr, Finset.card_filter] push_cast refine Finset.sum_congr rfl fun c _ => ?_ diff --git a/Complexity/Algorithms/ExactTriangle/Splitting.lean b/Complexity/Algorithms/ExactTriangle/Splitting.lean index 5015daf5e..4f6f435c3 100644 --- a/Complexity/Algorithms/ExactTriangle/Splitting.lean +++ b/Complexity/Algorithms/ExactTriangle/Splitting.lean @@ -40,7 +40,8 @@ The reduction is part of [AlmanWilliams2026, Section 3]. Direct adaptation of [Instances.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec3/Theorem17/Instances.lean), with only required definitions from `PaperStatements.lean`, [Chunks.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec3/Chunks.lean) and [Parameters.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec3/Parameters.lean), at `anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. -Source-derived prose below is paraphrased; quotations used for review are in the PR record. +Quoted docstring fragments reproduce comments in the pinned formal source; other prose describes +the local Lean API. -/ @[expose] public section @@ -215,11 +216,12 @@ theorem numPieces_le (hD : 16 ≤ D) (hg1 : 1 ≤ g) : /-- The residue of the integer `x` modulo `p`, as an element of `Fin p`. -/ def resFin (hp : p ≠ 0) (x : ℤ) : Fin p := - ⟨(x % (p : ℤ)).toNat, Int.toNat_emod_lt (Nat.pos_of_ne_zero hp) x⟩ + ⟨x.natMod p, Int.natMod_lt hp⟩ /-- The residue of `x` is congruent to `x`. -/ theorem resFin_modEq (hp : p ≠ 0) (x : ℤ) : ((resFin hp x : ℕ) : ℤ) ≡ x [ZMOD (p : ℤ)] := by - rw [resFin, Int.natCast_toNat_emod (Nat.pos_of_ne_zero hp)] + change ((x % (p : ℤ)).toNat : ℤ) ≡ x [ZMOD (p : ℤ)] + rw [Int.natCast_toNat_emod (Nat.pos_of_ne_zero hp)] exact Int.mod_modEq x p /-- An element of `Fin p` is congruent to `x` modulo `p` exactly if it is the residue of `x`. -/ diff --git a/Complexity/Algorithms/ExactTriangle/Witnesses.lean b/Complexity/Algorithms/ExactTriangle/Witnesses.lean index 0b5e39cb9..dfaeebb6a 100644 --- a/Complexity/Algorithms/ExactTriangle/Witnesses.lean +++ b/Complexity/Algorithms/ExactTriangle/Witnesses.lean @@ -33,7 +33,8 @@ contain distinct modular false positives. No solver, native program or time orac Adapted from [Witnesses.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec3/Theorem17/Witnesses.lean) and the minimal scan definitions in `PaperStatements.lean` at `anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. -Context: [AlmanWilliams2026, Section 3]. Source-derived prose is paraphrased. +Context: [AlmanWilliams2026, Section 3]. Quoted docstring fragments reproduce comments in the pinned +formal source; other prose describes the local Lean API. -/ @[expose] public section @@ -272,7 +273,10 @@ theorem pieceSize_le_two_mul_sqrt_div (hg1 : 1 ≤ g) (hg : (g : ℝ) ≤ Real.s ((one_le_div hg0).mpr hg) _ = 2 * Real.sqrt D / (g : ℝ) := by ring -/-- Proof of Theorem 17: "the scans cost O((F(p) + 1)√D/g) = O(ν n³ log n/g)". -/ +/-- For `16 ≤ D ≤ n`, `1 ≤ g ≤ sqrt D`, `κ ≥ 1` and edge weights bounded by `n^κ`, bound +`(falsePositiveCount + 1) * pieceSize` at any selected prime by +`(2 * Hashing.falsePositiveConst + 2) * κ * n^3 * log n / g`. +This arithmetic estimate counts candidate vertices rather than native instructions. -/ theorem falsePositiveCount_add_one_mul_pieceSize_le {κ : ℝ} (wAB wBC wAC : Fin n → Fin n → ℤ) (hD : 16 ≤ D) (hDn : D ≤ n) (hg1 : 1 ≤ g) (hg : (g : ℝ) ≤ Real.sqrt D) (hκ : 1 ≤ κ) (hT : WeightsBoundedBy wAB wBC wAC ((n : ℝ) ^ κ)) (hp : IsSelectedPrime wAB wBC wAC D p) : diff --git a/Complexity/Foundations/Data/Int/Residue.lean b/Complexity/Foundations/Data/Int/Residue.lean index f6622c6a7..eee6215f9 100644 --- a/Complexity/Foundations/Data/Int/Residue.lean +++ b/Complexity/Foundations/Data/Int/Residue.lean @@ -16,7 +16,8 @@ public import Mathlib.Algebra.Order.Ring.Int ## Main results * `Int.natCast_toNat_emod`: the nonnegative integer residue survives a round trip through `Nat`. -* `Int.toNat_emod_lt`: the natural residue is below a positive modulus. + +Bounds on natural residues use Mathlib's existing `Int.natMod_lt`. ## References @@ -29,13 +30,10 @@ The existing Exact Triangle hashing client now uses these shared facts. namespace Int +/-- Casting the natural Euclidean residue back to `Int` preserves its value for a positive +modulus. -/ theorem natCast_toNat_emod {p : ℕ} (hp : 0 < p) (u : ℤ) : ((u % (p : ℤ)).toNat : ℤ) = u % (p : ℤ) := Int.toNat_of_nonneg (Int.emod_nonneg _ (by omega)) -theorem toNat_emod_lt {p : ℕ} (hp : 0 < p) (u : ℤ) : (u % (p : ℤ)).toNat < p := by - have := Int.emod_nonneg u (by omega : (p : ℤ) ≠ 0) - have := Int.emod_lt_of_pos u (by omega : (0 : ℤ) < p) - omega - end Int From 2434f4716afc9c69ce742e87bec05d60a91078df Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 07:49:36 -0400 Subject: [PATCH 09/73] feat(matrix): prove the tensor recursion and pruned evaluation core --- Complexity.lean | 4 + .../Schonhage/Alphabets.lean | 269 +++++++ .../MatrixMultiplication/Schonhage/Full.lean | 374 +++++++++ .../Schonhage/Identity.lean | 201 +++++ .../Schonhage/Pruned.lean | 751 ++++++++++++++++++ ComplexityTests/Schonhage.lean | 100 +++ NOTICE | 3 +- ROADMAP.md | 7 +- UPSTREAM.md | 11 +- 9 files changed, 1715 insertions(+), 5 deletions(-) create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/Alphabets.lean create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/Full.lean create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/Identity.lean create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/Pruned.lean create mode 100644 ComplexityTests/Schonhage.lean diff --git a/Complexity.lean b/Complexity.lean index 0f88b5283..58c9b1d11 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -29,6 +29,10 @@ public import Complexity.Algorithms.Lean.VanEmdeBoas.Elements public import Complexity.Algorithms.Lean.VanEmdeBoas.Insert public import Complexity.Algorithms.Lean.VanEmdeBoas.Layout public import Complexity.Algorithms.Lean.VanEmdeBoas.Successor +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Alphabets +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Full +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Identity +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Pruned public import Complexity.Algorithms.Reachability.BreadthFirst public import Complexity.Algorithms.Reachability.InductiveCounting public import Complexity.Algorithms.Reachability.InductiveCounting.Bounds diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Alphabets.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Alphabets.lean new file mode 100644 index 000000000..d9d6a76f9 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Alphabets.lean @@ -0,0 +1,269 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.LinearAlgebra.Matrix.Notation +public import Mathlib.Tactic.DeriveFintype + +/-! +# The alphabets and coefficient forms of Schönhage's tensor + +Seven left variables, seven right variables, ten output variables and ten multiplication +terms are represented explicitly. Indices start at zero. Linear forms are coefficient +functions over integers; no arithmetic operation is treated as a native machine instruction. + +## Main definitions + +* `LeftVar`, `RightVar`, `OutVar`, `Term`: the four finite alphabets. +* `phi`, `psi`, `chi`: the left, right and output coefficient forms. +* `Term.Contributes`: occurrence in an output form. + +## Main results + +* `card_leftVar`, `card_rightVar`, `card_term`: alphabet sizes. +* `Term.contributes_iff`, `chi_eq_ite`: output incidence and its coefficient. + +## References + +Part of [AlmanWilliams2026, Section 2]. Direct mathematical adaptation of +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Definitions are selectively extracted from that pin's `PaperStatements.lean`; no machine +semantics or running-time statement is imported. Source-derived prose is paraphrased below. +-/ + +@[expose] public section + +open Finset + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Section 2.2: We call x₁, x₂, x₃, p₁₁, p₁₂, p₂₁, p₂₂ the seven left variables. +-/ +inductive LeftVar : Type + | x (i : Fin 3) + | p (i j : Fin 2) + deriving DecidableEq, Fintype + +/-- Section 2.2: and y₁, y₂, y₃, q₁₁, q₁₂, q₂₁, q₂₂ the seven right variables. +-/ +inductive RightVar : Type + | y (j : Fin 3) + | q (i j : Fin 2) + deriving DecidableEq, Fintype + +/-- Section 2.2: We will also use ten output variables z_ij (for 1 ≤ i, j ≤ 3) and z₀. +-/ +inductive OutVar : Type + | z (i j : Fin 3) + | z0 + deriving DecidableEq, Fintype + +/-- Section 2.2: Schönhage's identity has ten terms, which we name P_ij (for 1 ≤ i, j ≤ 3, +corresponding to the entries of p̂ and q̂) and P₀. +-/ +inductive Term : Type + | P (i j : Fin 3) + | P0 + deriving DecidableEq, Fintype + +/-- The linear form consisting of the single left variable `x_i`. +-/ +def xForm (i : Fin 3) : LeftVar → ℤ := Pi.single (LeftVar.x i) 1 +/-- The linear form consisting of the single left variable `p_ij`. +-/ +def pForm (i j : Fin 2) : LeftVar → ℤ := Pi.single (LeftVar.p i j) 1 +/-- The linear form consisting of the single right variable `y_j`. +-/ +def yForm (j : Fin 3) : RightVar → ℤ := Pi.single (RightVar.y j) 1 +/-- The linear form consisting of the single right variable `q_ij`. +-/ +def qForm (i j : Fin 2) : RightVar → ℤ := Pi.single (RightVar.q i j) 1 +/-- The linear form consisting of the single output variable `z_ij`. +-/ +def zForm (i j : Fin 3) : OutVar → ℤ := Pi.single (OutVar.z i j) 1 +/-- The linear form consisting of the single output variable `z₀`. +-/ +def z0Form : OutVar → ℤ := Pi.single OutVar.z0 1 + +/-- Section 2.2: the 3 × 3 matrix `p̂` of linear forms, p̂ = (p₁₁, p₁₂, 0; p₂₁, p₂₂, 0; -p₁₁-p₂₁, +-p₁₂-p₂₂, 0). +-/ +def pHat : Matrix (Fin 3) (Fin 3) (LeftVar → ℤ) := + !![pForm 0 0, pForm 0 1, 0; + pForm 1 0, pForm 1 1, 0; + -pForm 0 0 - pForm 1 0, -pForm 0 1 - pForm 1 1, 0] + +/-- Section 2.2: the 3 × 3 matrix `q̂` of linear forms, q̂ = (q₁₁, q₁₂, -q₁₁-q₁₂; q₂₁, q₂₂, +-q₂₁-q₂₂; 0, 0, 0). +-/ +def qHat : Matrix (Fin 3) (Fin 3) (RightVar → ℤ) := + !![qForm 0 0, qForm 0 1, -qForm 0 0 - qForm 0 1; + qForm 1 0, qForm 1 1, -qForm 1 0 - qForm 1 1; + 0, 0, 0] + +/-- Section 2.2: the linear form `φ_λ` in the left variables, φ_{P_ij} = x_i + p̂_ij and φ_{P₀} = +-(x₁ + x₂ + x₃). Section 2.3.1: let φ_λ(s) ∈ {0, ±1} denote the coefficient of s in φ_λ; this is +`phi lam s`. +-/ +def phi : Term → LeftVar → ℤ + | .P i j => xForm i + pHat i j + | .P0 => -(xForm 0 + xForm 1 + xForm 2) + +/-- Section 2.2: the linear form `ψ_λ` in the right variables, ψ_{P_ij} = y_j + q̂_ij and ψ_{P₀} = +y₁ + y₂ + y₃. Section 2.3.1: `ψ_λ(t)`, the coefficient of `t` in `ψ_λ`, is `psi lam t`. +-/ +def psi : Term → RightVar → ℤ + | .P i j => yForm j + qHat i j + | .P0 => yForm 0 + yForm 1 + yForm 2 + +/-- Section 2.2: the linear form `χ_λ` in the output variables, χ_{P_ij} = z_ij + z₀ and χ_{P₀} = +z₀. +-/ +def chi : Term → OutVar → ℤ + | .P i j => zForm i j + z0Form + | .P0 => z0Form + +/-- Section 2.2: the output variable `z₀` is inner, the `z_ij` are outer. +-/ +def OutVar.IsInner : OutVar → Prop + | .z _ _ => False + | .z0 => True + +instance OutVar.instDecidablePredIsInner : DecidablePred OutVar.IsInner + | .z _ _ => isFalse fun h => h + | .z0 => isTrue trivial + +/-- Section 2.2: We say that a term λ contributes to an output variable z if z appears in χ_λ. +-/ +def Term.Contributes (lam : Term) (z : OutVar) : Prop := chi lam z ≠ 0 + +instance Term.instDecidableContributes (lam : Term) (z : OutVar) : Decidable (lam.Contributes z) := + inferInstanceAs (Decidable (chi lam z ≠ 0)) + +/-- The term that the private leaf has at a level where the output string has the variable `z`: +`P₀` for `z₀` and `P_ij` for `z_ij`. +-/ +def OutVar.privateTerm : OutVar → Term + | .z i j => .P i j + | .z0 => .P0 + +/-- Section 2.3.1: A left string of length L is a string u = u₁ u₂ ⋯ u_L of L left variables, and +we call u_ℓ its variable at level ℓ. +-/ +abbrev LeftStr (L : ℕ) : Type := Fin L → LeftVar +/-- Section 2.3.1: right strings of length `L`. +-/ +abbrev RightStr (L : ℕ) : Type := Fin L → RightVar +/-- Section 2.3.1: output strings of length `L`. +-/ +abbrev OutStr (L : ℕ) : Type := Fin L → OutVar +/-- Section 2.3.1: We refer to the call reached by choosing the terms τ₁, …, τ_k at levels 1, …, k +as the vertex τ₁ ⋯ τ_k, a string of k terms. It is at depth k. +-/ +abbrev Vertex (k : ℕ) : Type := Fin k → Term +/-- Section 2.3.1: the leaves, the vertices τ = τ₁ ⋯ τ_L at depth L. +-/ +abbrev Leaf (L : ℕ) : Type := Vertex L + +/-! ### The alphabets -/ + +/-- The left variables, listed: `x i` is `.inl i`, and `p i j` is `.inr (i, j)`. +-/ +def LeftVar.equiv : LeftVar ≃ Fin 3 ⊕ Fin 2 × Fin 2 where + toFun + | .x i => .inl i + | .p i j => .inr (i, j) + invFun + | .inl i => .x i + | .inr (i, j) => .p i j + left_inv s := by cases s <;> rfl + right_inv s := by rcases s with _ | ⟨_, _⟩ <;> rfl + +/-- The right variables, listed: `y j` is `.inl j`, and `q i j` is `.inr (i, j)`. +-/ +def RightVar.equiv : RightVar ≃ Fin 3 ⊕ Fin 2 × Fin 2 where + toFun + | .y j => .inl j + | .q i j => .inr (i, j) + invFun + | .inl j => .y j + | .inr (i, j) => .q i j + left_inv t := by cases t <;> rfl + right_inv t := by rcases t with _ | ⟨_, _⟩ <;> rfl + +/-- The terms, listed: `P i j` is `some (i, j)`, and `P0` is `none`. +-/ +def Term.equiv : Term ≃ Option (Fin 3 × Fin 3) where + toFun + | .P i j => some (i, j) + | .P0 => none + invFun + | some (i, j) => .P i j + | none => .P0 + left_inv t := by cases t <;> rfl + right_inv t := by rcases t with _ | ⟨_, _⟩ <;> rfl + +/-- Section 2.2: there are seven left variables. +-/ +theorem card_leftVar : Fintype.card LeftVar = 7 := by + simp [Fintype.card_congr LeftVar.equiv] + +/-- Section 2.2: there are seven right variables. +-/ +theorem card_rightVar : Fintype.card RightVar = 7 := by + simp [Fintype.card_congr RightVar.equiv] + +/-- Section 2.2: Schönhage's identity has ten terms. +-/ +theorem card_term : Fintype.card Term = 10 := by + simp [Fintype.card_congr Term.equiv] + +/-! ### Which terms contribute to which output variables -/ + +/-- Section 2.2, second observation: every term contributes to z₀. +-/ +@[simp] +theorem Term.contributes_z0 (lam : Term) : lam.Contributes .z0 := by + cases lam <;> simp [Term.Contributes, chi, zForm, z0Form] + +/-- Section 2.2, second observation: only P_ij contributes to z_ij. +-/ +@[simp] +theorem Term.contributes_z_iff (lam : Term) (i j : Fin 3) : + lam.Contributes (.z i j) ↔ lam = .P i j := by + cases lam <;> simp [Term.Contributes, chi, zForm, z0Form, Pi.single_apply, eq_comm] + +/-- The second observation in one statement: a term contributes to `z` if and only if `z` is +inner, that is `z₀`, or the term is `z.privateTerm`, which is `P_ij` for `z = z_ij`. (This is the +term that the private leaf of Section 2.4.3 has at a level with the variable `z`.) +-/ +theorem Term.contributes_iff (lam : Term) (z : OutVar) : + lam.Contributes z ↔ z.IsInner ∨ lam = z.privateTerm := by + decide +revert + +/-- A sum over the terms contributing to `z` has the one summand `P_ij` for `z = z_ij`, and runs +over all terms for `z = z₀`. This is how step (4) of the recursions assembles the slice at `z`. +-/ +theorem Term.sum_contributes {A : Type*} [AddCommMonoid A] (c : Term → A) (z : OutVar) : + ∑ lam : Term with lam.Contributes z, c lam + = match z with + | .z i j => c (.P i j) + | .z0 => ∑ lam, c lam := by + cases z with + | z i j => simp [filter_eq'] + | z0 => simp + +/-- Section 2.2: the χ_λ forms are all sums of output variables (without any minus signs or other +coefficients). +-/ +theorem chi_eq_ite (lam : Term) (z : OutVar) : + chi lam z = if lam.Contributes z then 1 else 0 := by + decide +revert + +end Cslib.Algorithms.MatrixMultiplication.Schonhage diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Full.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Full.lean new file mode 100644 index 000000000..a1c8d82b6 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Full.lean @@ -0,0 +1,374 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Alphabets +public import Mathlib.Algebra.BigOperators.Fin +public import Mathlib.Data.Fintype.BigOperators +public import Mathlib.Data.Finset.Pi +import Mathlib.Tactic.Ring + +/-! +# Full tensor recursion and shared encodings + +The recursion works on mathematical arrays indexed by finite strings. Its multiplication +record identifies the two encoded integers at each leaf. The returned array equals the +bilinear tensor expansion. These recursive values and combinatorial counts carry no native +instruction-cost interpretation. + +## Main definitions + +* `Full.run`, `Full.result`, `Full.multipliedAt`: full evaluation and leaf operands. +* `encodeLeft`, `encodeRight`, `encodingL`, `encodingR`: shared leaf encodings. +* `bilinearProduct`, `coefficient`, `leaves`: the tensor expansion and contributing leaves. + +## Main results + +* `full_spec`: full evaluation returns the bilinear product and uses the stated encodings. +* `tensor_expansion`: expansion into products of the one-level tensor coefficients. +* `abs_phi_le_one`, `abs_psi_le_one`: bounded encoding coefficients. + +## References + +Part of [AlmanWilliams2026, Section 2]. Direct mathematical adaptation of +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Definitions are selectively extracted from that pin's `PaperStatements.lean`; no machine +semantics or running-time statement is imported. Source-derived prose is paraphrased below. +-/ + +@[expose] public section + +open Finset + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Section 2.3.1: An array a on the left strings of length L assigns an integer a[u] to each u +that is a left string of length L. So such an array is a function `LeftStr L → ℤ`, and likewise +for right strings, output strings and leaves. + +the slice of a at a left variable s is the array a_s on the strings of length L - 1 given by +a_s[u'] := a[s u']; the same definition is used for every alphabet. +-/ +def sliceAt {α β : Type} {L : ℕ} (a : (Fin (L + 1) → α) → β) (s : α) : (Fin L → α) → β := + fun u' => a (Fin.cons s u') + +/-- Section 2.3.1, step (2) of `Full.result`: A_λ := ∑_s φ_λ(s) a_s. +-/ +def encodeStepL {L : ℕ} (lam : Term) (a : LeftStr (L + 1) → ℤ) : LeftStr L → ℤ := + fun u' => ∑ s, phi lam s * sliceAt a s u' + +/-- Section 2.3.1, step (2) of `Full.result`: B_λ := ∑_t ψ_λ(t) b_t. +-/ +def encodeStepR {L : ℕ} (lam : Term) (b : RightStr (L + 1) → ℤ) : RightStr L → ℤ := + fun v' => ∑ t, psi lam t * sliceAt b t v' + +/-- Section 2.3.1: The recursion Full.result(a, b), for arrays a, b on the left and right strings +of length L, returns an array on the output strings of length L: (1) If L = 0, return the number +ab. (2) For each term λ of Schönhage's identity, form A_λ := ∑_s φ_λ(s) a_s and B_λ := ∑_t ψ_λ(t) +b_t. (3) For each term λ, recursively compute C_λ := Full.result(A_λ, B_λ). (4) Return the array c +whose slices are c_{z_ij} := C_{P_ij} and c_{z₀} := ∑_λ C_λ. + +`Full.run` is this recursion, and it also keeps a record of the multiplications. Its first +component is the array that is returned. Its second component gives, for each leaf `τ`, the two +numbers that step (1) multiplies at `τ`: the leaf `λ τ'` of a call is the leaf `τ'` of its +recursive call for `λ`. + +An array on the strings of length 0 has one entry, at the empty string `Fin.elim0`; that entry is +the number. +-/ +def Full.run : (L : ℕ) → (LeftStr L → ℤ) → (RightStr L → ℤ) → (OutStr L → ℤ) × (Leaf L → ℤ × ℤ) + | 0, a, b => (fun _ => a Fin.elim0 * b Fin.elim0, fun _ => (a Fin.elim0, b Fin.elim0)) + | L + 1, a, b => + let R : Term → (OutStr L → ℤ) × (Leaf L → ℤ × ℤ) := fun lam => + Full.run L (encodeStepL lam a) (encodeStepR lam b) + (fun w => + match w 0 with + | .z i j => (R (.P i j)).1 (Fin.tail w) + | .z0 => ∑ lam, (R lam).1 (Fin.tail w), + fun τ => (R (τ 0)).2 (Fin.tail τ)) + +/-- The array that `Full.result(a, b)` returns. +-/ +def Full.result (L : ℕ) (a : LeftStr L → ℤ) (b : RightStr L → ℤ) : OutStr L → ℤ := + (Full.run L a b).1 + +/-- The two numbers that `Full.result(a, b)` multiplies at the leaf `τ`, the left one first. +-/ +def Full.multipliedAt (L : ℕ) (a : LeftStr L → ℤ) (b : RightStr L → ℤ) (τ : Leaf L) : ℤ × ℤ := + (Full.run L a b).2 τ + +/-! ### 2.3.2 Unraveling the recursion computation -/ + +/-- Section 2.3.2: Φ_τ(a) := ∑_u a[u] ∏_{ℓ=1}^{L} φ_{τ_ℓ}(u_ℓ), the sum over all left strings `u`. +-/ +def encodeLeft {L : ℕ} (τ : Leaf L) (a : LeftStr L → ℤ) : ℤ := ∑ u, a u * ∏ ℓ, phi (τ ℓ) (u ℓ) + +/-- Section 2.3.2: Ψ_τ(b) := ∑_v b[v] ∏_{ℓ=1}^{L} ψ_{τ_ℓ}(v_ℓ), the sum over all right strings +`v`. +-/ +def encodeRight {L : ℕ} (τ : Leaf L) (b : RightStr L → ℤ) : ℤ := ∑ v, b v * ∏ ℓ, psi (τ ℓ) (v ℓ) + +/-- Section 2.3.2: a vertex τ₁ ⋯ τ_k contributes to an output string w if τ_ℓ contributes to w_ℓ +at every level ℓ ≤ k, for a leaf, that is, for `k = L`: the leaf `τ` contributes to `w` if `τ_ℓ` +contributes to `w_ℓ` at every level `ℓ`. +-/ +def Leaf.Contributes {L : ℕ} (τ : Leaf L) (w : OutStr L) : Prop := + ∀ ℓ, (τ ℓ).Contributes (w ℓ) + +instance Leaf.instDecidableContributes {L : ℕ} (τ : Leaf L) (w : OutStr L) : + Decidable (Leaf.Contributes τ w) := + inferInstanceAs (Decidable (∀ ℓ, (τ ℓ).Contributes (w ℓ))) + +/-- Equation (2): bilinearProduct(a, b)[w] := ∑_{τ contributing to w} Φ_τ(a) Ψ_τ(b). +-/ +def bilinearProduct {L : ℕ} (a : LeftStr L → ℤ) (b : RightStr L → ℤ) : OutStr L → ℤ := + fun w => ∑ τ : Leaf L with Leaf.Contributes τ w, encodeLeft τ a * encodeRight τ b + +/-- Equation (3): γ(s, t, z) := ∑_{λ contributing to z} φ_λ(s) ψ_λ(t). +-/ +def coefficient (s : LeftVar) (t : RightVar) (z : OutVar) : ℤ := + ∑ lam : Term with lam.Contributes z, phi lam s * psi lam t + +/-- Section 2.4.1: The encoding of a is the array of the 10^L numbers Φ_τ(a), indexed by the +leaves τ. +-/ +def encodingL {L : ℕ} (a : LeftStr L → ℤ) : Leaf L → ℤ := fun τ => encodeLeft τ a + +/-- Section 2.4.1: The encoding of b, the array of the numbers Ψ_τ(b). +-/ +def encodingR {L : ℕ} (b : RightStr L → ℤ) : Leaf L → ℤ := fun τ => encodeRight τ b + +/-- Section 2.4.2: For a set U of output strings of length L, we write leaves(U) for the set of +leaves that contribute to some output string of U. +-/ +def leaves {L : ℕ} (U : Finset (OutStr L)) : Finset (Leaf L) := + univ.filter fun τ => ∃ w ∈ U, Leaf.Contributes τ w + +/-- Section 2.3.1: an array on the left strings of length `L` has 7^L entries in total. +-/ +theorem card_leftStr (L : ℕ) : Fintype.card (LeftStr L) = 7 ^ L := by + rw [Fintype.card_fun, card_leftVar, Fintype.card_fin] + +/-- Section 2.3.1: an array on the right strings of length `L` has `7^L` entries. +-/ +theorem card_rightStr (L : ℕ) : Fintype.card (RightStr L) = 7 ^ L := by + rw [Fintype.card_fun, card_rightVar, Fintype.card_fin] + +/-- Section 2.3.1: At depth k there are 10^k vertices; in particular there are `10^L` leaves. +-/ +theorem card_vertex (k : ℕ) : Fintype.card (Vertex k) = 10 ^ k := by + rw [Fintype.card_fun, card_term, Fintype.card_fin] + +/-- Section 2.3.1: φ_λ(s) ∈ {0, ±1}. +-/ +theorem abs_phi_le_one (lam : Term) (s : LeftVar) : |phi lam s| ≤ 1 := by + decide +revert + +/-- Section 2.3.1: `ψ_λ(t) ∈ {0, ±1}`. +-/ +theorem abs_psi_le_one (lam : Term) (t : RightVar) : |psi lam t| ≤ 1 := by + decide +revert + +/-- Section 2.3.1: for each term λ, at most three of the seven values φ_λ(s) are nonzero. +-/ +theorem card_support_phi_le (lam : Term) : (univ.filter fun s => phi lam s ≠ 0).card ≤ 3 := by + decide +revert + +/-- Section 2.3.1: and similarly for the seven values ψ_λ(t). +-/ +theorem card_support_psi_le (lam : Term) : (univ.filter fun t => psi lam t ≠ 0).card ≤ 3 := by + decide +revert + +/-! ### Which leaves contribute to an output string -/ + +/-- In the proof of Lemma 7: the leaves contributing to zw' are the λτ' with λ contributing to z +and τ' contributing to w'. Here `λ = τ 0`, `τ' = Fin.tail τ`, `z = w 0` and `w' = Fin.tail w`. +-/ +theorem Leaf.contributes_succ {L : ℕ} {τ : Leaf (L + 1)} {w : OutStr (L + 1)} : + Leaf.Contributes τ w + ↔ (τ 0).Contributes (w 0) ∧ Leaf.Contributes (Fin.tail τ) (Fin.tail w) := by + simp only [Leaf.Contributes, Fin.forall_fin_succ, Fin.tail] + +/-- A sum over the strings of length `L + 1` is a sum over the first variable `s` and the rest +`u'` of the string `s u'`. +-/ +private theorem sum_strings_succ {α : Type*} [Fintype α] {L : ℕ} (f : (Fin (L + 1) → α) → ℤ) : + ∑ u, f u = ∑ s, ∑ u', f (Fin.cons s u') := by + rw [← Fintype.sum_prod_type'] + exact (Fintype.sum_equiv (Fin.consEquiv fun _ => α) _ _ fun _ => rfl).symm + +/-- A sum over the leaves contributing to `w`, split by the first term of the leaf. +-/ +theorem Leaf.sum_contributes_succ {L : ℕ} (w : OutStr (L + 1)) (f : Leaf (L + 1) → ℤ) : + ∑ τ : Leaf (L + 1) with Leaf.Contributes τ w, f τ + = ∑ lam : Term with lam.Contributes (w 0), + ∑ τ' : Leaf L with Leaf.Contributes τ' (Fin.tail w), f (Fin.cons lam τ') := by + simp only [sum_filter, sum_strings_succ, Leaf.contributes_succ, Fin.cons_zero, + Fin.tail_cons] + refine sum_congr rfl fun lam _ => ?_ + by_cases h : lam.Contributes (w 0) <;> simp [h] + +/-- In the proof of Lemma 8: the leaves contributing to `w` are the leaves whose term at level ℓ +contributes to w_ℓ, independently for each ℓ. +-/ +theorem Leaf.filter_contributes_eq_piFinset {L : ℕ} (w : OutStr L) : + (univ.filter fun τ : Leaf L => Leaf.Contributes τ w) + = Fintype.piFinset fun ℓ => univ.filter fun lam : Term => lam.Contributes (w ℓ) := by + ext τ + simp only [mem_filter, mem_univ, true_and, Fintype.mem_piFinset] + rfl + +/-- Membership in `leaves(U)`. +-/ +@[simp] +theorem mem_leaves {L : ℕ} {U : Finset (OutStr L)} {τ : Leaf L} : + τ ∈ leaves U ↔ ∃ w ∈ U, Leaf.Contributes τ w := by + simp [leaves] + +/-! ### The two encodings at once -/ + +section encodeWith + +variable {α : Type} [Fintype α] (c : Term → α → ℤ) {L : ℕ} + +/-- The number `∑_u a[u] ∏_ℓ c_{τ_ℓ}(u_ℓ)`, for an array `a` on the strings over a finite alphabet +and the coefficients `c` of a family of linear forms, one for each term. By definition it is +`Φ_τ(a)` for `c = φ` and `Ψ_τ(b)` for `c = ψ`. +-/ +def encodeWith (τ : Leaf L) (a : (Fin L → α) → ℤ) : ℤ := ∑ u, a u * ∏ ℓ, c (τ ℓ) (u ℓ) + +/-- For `L = 0` the only string is empty, and so are the products over the levels. +-/ +theorem encodeWith_zero (τ : Leaf 0) (a : (Fin 0 → α) → ℤ) : encodeWith c τ a = a Fin.elim0 := by + simp [encodeWith, Subsingleton.elim (default : Fin 0 → α) Fin.elim0] + +/-- The number at the leaf `λτ'` for `a` is the number at the leaf `τ'` for `∑_s c_λ(s) a_s`. +-/ +theorem encodeWith_cons (lam : Term) (τ' : Leaf L) (a : (Fin (L + 1) → α) → ℤ) : + encodeWith c (Fin.cons lam τ' : Leaf (L + 1)) a + = encodeWith c τ' fun u' => ∑ s, c lam s * sliceAt a s u' := by + simp only [encodeWith, sum_strings_succ, Fin.prod_univ_succ, Fin.cons_zero, Fin.cons_succ, + sliceAt, sum_mul] + rw [sum_comm] + exact sum_congr rfl fun u' _ => sum_congr rfl fun s _ => by ring + +end encodeWith + +/-! ### Lemma 7 -/ + +/-- In the proof of Lemma 7: for every leaf λτ' we have Φ_{λτ'}(a) = ∑_s φ_λ(s) Φ_{τ'}(a_s) = +Φ_{τ'}(A_λ). +-/ +theorem encodeLeft_cons {L : ℕ} (lam : Term) (τ' : Leaf L) (a : LeftStr (L + 1) → ℤ) : + encodeLeft (Fin.cons lam τ' : Leaf (L + 1)) a = encodeLeft τ' (encodeStepL lam a) := + encodeWith_cons phi lam τ' a + +/-- In the proof of Lemma 7: and similarly Ψ_{λτ'}(b) = Ψ_{τ'}(B_λ). +-/ +theorem encodeRight_cons {L : ℕ} (lam : Term) (τ' : Leaf L) (b : RightStr (L + 1) → ℤ) : + encodeRight (Fin.cons lam τ' : Leaf (L + 1)) b = encodeRight τ' (encodeStepR lam b) := + encodeWith_cons psi lam τ' b + +/-- For L = 0, the only leaf is the empty string τ, and Φ_τ(a) = a […], as the products over the +levels are empty (proof of Lemma 7). +-/ +private theorem encodeLeft_zero (τ : Leaf 0) (a : LeftStr 0 → ℤ) : encodeLeft τ a = a Fin.elim0 := + encodeWith_zero phi τ a + +/-- and Ψ_τ(b) = b (proof of Lemma 7). +-/ +private theorem encodeRight_zero (τ : Leaf 0) (b : RightStr 0 → ℤ) : + encodeRight τ b = b Fin.elim0 := + encodeWith_zero psi τ b + +/-- **Lemma 7**, first half. At the leaf τ, Full.result(a, b) multiplies Φ_τ(a) by Ψ_τ(b). +-/ +theorem Full.multipliedAt_eq_encoding {L : ℕ} (a : LeftStr L → ℤ) (b : RightStr L → ℤ) + (τ : Leaf L) : + Full.multipliedAt L a b τ = (encodeLeft τ a, encodeRight τ b) := by + induction L with + | zero => + rw [encodeLeft_zero, encodeRight_zero] + rfl + | succ L ih => + -- The leaf `τ` is `λ τ'` with `λ = τ 0` and `τ' = Fin.tail τ`. By steps (2) and (3) it is the + -- leaf `τ'` of `Full.result(A_λ, B_λ)`. + have hstep : Full.multipliedAt (L + 1) a b τ + = Full.multipliedAt L (encodeStepL (τ 0) a) (encodeStepR (τ 0) b) (Fin.tail τ) := rfl + rw [hstep, ih, ← encodeLeft_cons, ← encodeRight_cons, Fin.cons_self_tail] + +/-- Step (4) of `Full.result`, as it is read in the proof of Lemma 7: `c[z w'] = ∑_{λ contributing +to z} C_λ[w']`. +-/ +theorem Full.result_succ {L : ℕ} (a : LeftStr (L + 1) → ℤ) (b : RightStr (L + 1) → ℤ) + (w : OutStr (L + 1)) : + Full.result (L + 1) a b w = ∑ lam : Term with lam.Contributes (w 0), + Full.result L (encodeStepL lam a) (encodeStepR lam b) (Fin.tail w) := by + rw [Term.sum_contributes] + rfl + +/-- In the proof of Lemma 7: `bilinearProduct(a, b)[z w'] = ∑_{λ contributing to z} +bilinearProduct(A_λ, B_λ)[w']`. +-/ +theorem bilinearProduct_succ {L : ℕ} (a : LeftStr (L + 1) → ℤ) (b : RightStr (L + 1) → ℤ) + (w : OutStr (L + 1)) : + bilinearProduct a b w = ∑ lam : Term with lam.Contributes (w 0), + bilinearProduct (encodeStepL lam a) (encodeStepR lam b) (Fin.tail w) := by + simp only [bilinearProduct, Leaf.sum_contributes_succ, encodeLeft_cons, encodeRight_cons] + +/-- **Lemma 7**, second half. and it returns bilinearProduct(a, b). +-/ +theorem Full.result_eq_bilinearProduct {L : ℕ} (a : LeftStr L → ℤ) (b : RightStr L → ℤ) : + Full.result L a b = bilinearProduct a b := by + induction L with + | zero => + funext w + simp [Full.result, Full.run, bilinearProduct, Leaf.Contributes, encodeLeft_zero, + encodeRight_zero] + | succ L ih => + funext w + simp only [Full.result_succ, bilinearProduct_succ, ih] + +/-- **Lemma 7**. At the leaf τ, Full.result(a, b) multiplies Φ_τ(a) by Ψ_τ(b), and it returns +bilinearProduct(a, b). +-/ +theorem full_spec {L : ℕ} (a : LeftStr L → ℤ) (b : RightStr L → ℤ) : + (∀ τ : Leaf L, Full.multipliedAt L a b τ = (encodeLeft τ a, encodeRight τ b)) ∧ + Full.result L a b = bilinearProduct a b := + ⟨Full.multipliedAt_eq_encoding a b, Full.result_eq_bilinearProduct a b⟩ + +/-! ### Lemma 8 -/ + +/-- The display in the proof of Lemma 8: the sum, over the leaves `τ` contributing to `w`, of `∏_ℓ +φ_{τ_ℓ}(u_ℓ) ψ_{τ_ℓ}(v_ℓ)` is the product of sums ∏_ℓ ∑_{τ_ℓ contributing to w_ℓ} φ_{τ_ℓ}(u_ℓ) +ψ_{τ_ℓ}(v_ℓ) = ∏_ℓ γ(u_ℓ, v_ℓ, w_ℓ) by (3). +-/ +theorem prod_gamma_eq_sum_contributes {L : ℕ} (u : LeftStr L) (v : RightStr L) (w : OutStr L) : + ∏ ℓ, coefficient (u ℓ) (v ℓ) (w ℓ) + = ∑ τ : Leaf L with Leaf.Contributes τ w, (∏ ℓ, phi (τ ℓ) (u ℓ)) * ∏ ℓ, psi (τ ℓ) (v ℓ) := by + simp only [coefficient] + rw [prod_univ_sum, Leaf.filter_contributes_eq_piFinset] + exact sum_congr rfl fun τ _ => prod_mul_distrib + +/-- **Lemma 8**. For all arrays a, b and every output string w, bilinearProduct(a, b)[w] = +∑_{u,v} γ(u₁, v₁, w₁) γ(u₂, v₂, w₂) ⋯ γ(u_L, v_L, w_L) a[u] b[v], the sum over all left strings u +and right strings v. +-/ +theorem tensor_expansion {L : ℕ} (a : LeftStr L → ℤ) (b : RightStr L → ℤ) (w : OutStr L) : + bilinearProduct a b w = ∑ u, ∑ v, (∏ ℓ, coefficient (u ℓ) (v ℓ) (w ℓ)) * a u * b v := by + -- "the coefficient of a[u] b[v] in (2) is ∑_τ ∏_ℓ φ_{τ_ℓ}(u_ℓ) ψ_{τ_ℓ}(v_ℓ), summed over the + -- leaves τ contributing to w": expand `Φ_τ(a) Ψ_τ(b)` and sum over `τ` last. + simp only [bilinearProduct, encodeLeft, encodeRight, sum_mul_sum] + rw [sum_comm] + refine sum_congr rfl fun u _ => ?_ + rw [sum_comm] + refine sum_congr rfl fun v _ => ?_ + rw [prod_gamma_eq_sum_contributes, sum_mul, sum_mul] + exact sum_congr rfl fun τ _ => by ring + +end Cslib.Algorithms.MatrixMultiplication.Schonhage diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Identity.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Identity.lean new file mode 100644 index 000000000..15b7942f4 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Identity.lean @@ -0,0 +1,201 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Alphabets +public import Mathlib.Algebra.MvPolynomial.CommRing +import Mathlib.LinearAlgebra.Finsupp.LinearCombination +import Mathlib.Tactic.LinearCombination + +/-! +# Schönhage's ten-term polynomial identity + +The ten products of linear forms equal the wanted inner/outer product polynomial plus +an explicit error polynomial. This is an identity over integers in 24 formal variables. +The error term is retained; it cannot be discarded for unrestricted inputs. + +## Main definitions + +* `TriPoly`, `formL`, `formR`, `formO`: the polynomial interpretation of the forms. +* `G`, `E`: the wanted polynomial and the error polynomial. + +## Main results + +* `identity`: the complete ten-term polynomial identity, including its error. + +## References + +Part of [AlmanWilliams2026, Section 2]. Direct mathematical adaptation of +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Definitions are selectively extracted from that pin's `PaperStatements.lean`; no machine +semantics or running-time statement is imported. Source-derived prose is paraphrased below. +-/ + +@[expose] public section + +open Finset + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- All 24 variables of the identity: the left, the right and the output variables. +-/ +abbrev Var : Type := LeftVar ⊕ RightVar ⊕ OutVar + +/-- The polynomials in which Schönhage's identity is an identity. +-/ +abbrev TriPoly : Type := MvPolynomial Var ℤ + +/-- A left variable, as a polynomial. +-/ +noncomputable def polyL (s : LeftVar) : TriPoly := MvPolynomial.X (Sum.inl s) +/-- A right variable, as a polynomial. +-/ +noncomputable def polyR (t : RightVar) : TriPoly := MvPolynomial.X (Sum.inr (Sum.inl t)) +/-- An output variable, as a polynomial. +-/ +noncomputable def polyO (z : OutVar) : TriPoly := MvPolynomial.X (Sum.inr (Sum.inr z)) + +/-- A linear form in the left variables, as a polynomial. +-/ +noncomputable def formL (f : LeftVar → ℤ) : TriPoly := ∑ s, MvPolynomial.C (f s) * polyL s +/-- A linear form in the right variables, as a polynomial. +-/ +noncomputable def formR (f : RightVar → ℤ) : TriPoly := ∑ t, MvPolynomial.C (f t) * polyR t +/-- A linear form in the output variables, as a polynomial. +-/ +noncomputable def formO (f : OutVar → ℤ) : TriPoly := ∑ z, MvPolynomial.C (f z) * polyO z + +/-- Section 2.2: G := ∑_{i,j=1}^{3} x_i y_j z_ij + (∑_{i,j=1}^{2} p_ij q_ij) z₀. +-/ +noncomputable def G : TriPoly := + (∑ i : Fin 3, ∑ j : Fin 3, polyL (.x i) * polyR (.y j) * polyO (.z i j)) + + (∑ i : Fin 2, ∑ j : Fin 2, polyL (.p i j) * polyR (.q i j)) * polyO .z0 + +/-- Lemma 6: E := ∑_{i,j=1}^{3} (x_i q̂_ij + p̂_ij y_j + p̂_ij q̂_ij) z_ij. +-/ +noncomputable def E : TriPoly := + ∑ i : Fin 3, ∑ j : Fin 3, + (polyL (.x i) * formR (qHat i j) + formL (pHat i j) * polyR (.y j) + + formL (pHat i j) * formR (qHat i j)) * polyO (.z i j) + +/-- A sum over the ten terms, split into the `P_ij` and `P₀`. +-/ +private theorem sum_term {A : Type*} [AddCommMonoid A] (f : Term → A) : + ∑ lam, f lam = ∑ i, ∑ j, f (.P i j) + f .P0 := by + rw [← Term.equiv.symm.sum_comp f, Fintype.sum_option, Fintype.sum_prod_type, add_comm] + rfl + +/-! ### Linear forms as polynomials -/ + +/-- `formL` is the linear map that sends the form consisting of the single variable `s` to the +polynomial `s`. +-/ +theorem formL_eq_linearCombination (f : LeftVar → ℤ) : + formL f = Fintype.linearCombination ℤ polyL f := by + simp only [formL, Fintype.linearCombination_apply, MvPolynomial.smul_eq_C_mul] + +/-- `formR` is the linear map that sends the form consisting of the single variable `t` to the +polynomial `t`. +-/ +theorem formR_eq_linearCombination (f : RightVar → ℤ) : + formR f = Fintype.linearCombination ℤ polyR f := by + simp only [formR, Fintype.linearCombination_apply, MvPolynomial.smul_eq_C_mul] + +/-- `formO` is the linear map that sends the form consisting of the single variable `z` to the +polynomial `z`. +-/ +theorem formO_eq_linearCombination (f : OutVar → ℤ) : + formO f = Fintype.linearCombination ℤ polyO f := by + simp only [formO, Fintype.linearCombination_apply, MvPolynomial.smul_eq_C_mul] + +/-- The form consisting of a single left variable is that variable. +-/ +theorem formL_single (s : LeftVar) : formL (Pi.single s 1) = polyL s := by + simp [formL_eq_linearCombination] + +/-- The form consisting of a single right variable is that variable. +-/ +theorem formR_single (t : RightVar) : formR (Pi.single t 1) = polyR t := by + simp [formR_eq_linearCombination] + +/-- The form consisting of a single output variable is that variable. +-/ +theorem formO_single (z : OutVar) : formO (Pi.single z 1) = polyO z := by + simp [formO_eq_linearCombination] + +/-- Section 2.2, the display of the linear forms: `φ_{P_ij} = x_i + p̂_ij`. +-/ +theorem formL_phi_P (i j : Fin 3) : formL (phi (.P i j)) = polyL (.x i) + formL (pHat i j) := by + simp [phi, xForm, formL_eq_linearCombination] + +/-- Section 2.2, the display of the linear forms: `ψ_{P_ij} = y_j + q̂_ij`. +-/ +theorem formR_psi_P (i j : Fin 3) : formR (psi (.P i j)) = polyR (.y j) + formR (qHat i j) := by + simp [psi, yForm, formR_eq_linearCombination] + +/-- Section 2.2, the display of the linear forms: `χ_{P_ij} = z_ij + z₀`. +-/ +theorem formO_chi_P (i j : Fin 3) : formO (chi (.P i j)) = polyO (.z i j) + polyO .z0 := by + simp [chi, zForm, z0Form, formO_eq_linearCombination] + +/-- Section 2.2, the display of the linear forms: `φ_{P₀} = -(x₁ + x₂ + x₃)`. +-/ +theorem formL_phi_P0 : formL (phi .P0) = -(polyL (.x 0) + polyL (.x 1) + polyL (.x 2)) := by + simp [phi, xForm, formL_eq_linearCombination] + +/-- Section 2.2, the display of the linear forms: `ψ_{P₀} = y₁ + y₂ + y₃`. +-/ +theorem formR_psi_P0 : formR (psi .P0) = polyR (.y 0) + polyR (.y 1) + polyR (.y 2) := by + simp [psi, yForm, formR_eq_linearCombination] + +/-- Section 2.2, the display of the linear forms: `χ_{P₀} = z₀`. +-/ +theorem formO_chi_P0 : formO (chi .P0) = polyO .z0 := + formO_single .z0 + +/-! ### Lemma 6 -/ + +/-- Section 2.2: every column of p̂ […] sums to zero. +-/ +theorem pHat_column_sum (j : Fin 3) : ∑ i, pHat i j = 0 := by + fin_cases j <;> simp [Fin.sum_univ_three, pHat] + +/-- Section 2.2: every row of q̂ sums to zero. +-/ +theorem qHat_row_sum (i : Fin 3) : ∑ j, qHat i j = 0 := by + fin_cases i <;> simp [Fin.sum_univ_three, qHat] + +/-- Section 2.2: Notice that ∑_{i,j=1}^{3} p̂_ij q̂_ij = ∑_{i,j=1}^{2} p_ij q_ij is the desired +inner product. +-/ +theorem sum_pHat_mul_qHat : + ∑ i : Fin 3, ∑ j : Fin 3, formL (pHat i j) * formR (qHat i j) + = ∑ i : Fin 2, ∑ j : Fin 2, polyL (.p i j) * polyR (.q i j) := by + simp [Fin.sum_univ_three, Fin.sum_univ_two, pHat, qHat, pForm, qForm, + formL_eq_linearCombination, formR_eq_linearCombination] + +/-- **Lemma 6**. Summing over the ten terms, ∑_λ φ_λ ψ_λ χ_λ = G + E, where E := ∑_{i,j=1}^{3} +(x_i q̂_ij + p̂_ij y_j + p̂_ij q̂_ij) z_ij. An identity of polynomials with integer coefficients +in the 24 variables. +-/ +theorem identity : ∑ lam, formL (phi lam) * formR (psi lam) * formO (chi lam) = G + E := by + -- The column sums of p̂ and the row sums of q̂, as identities of polynomials. + have hcol : ∀ j, ∑ i, formL (pHat i j) = 0 := fun j => by + simp only [formL_eq_linearCombination, ← map_sum, pHat_column_sum, map_zero] + have hrow : ∀ i, ∑ j, formR (qHat i j) = 0 := fun i => by + simp only [formR_eq_linearCombination, ← map_sum, qHat_row_sum, map_zero] + simp only [Fin.sum_univ_three] at hcol hrow + -- Write out the ten terms, and write the inner product in G as ∑ p̂_ij q̂_ij. + simp only [sum_term, formL_phi_P, formR_psi_P, formO_chi_P, formL_phi_P0, formR_psi_P0, + formO_chi_P0, G, E, ← sum_pHat_mul_qHat, Fin.sum_univ_three] + -- The coefficients of the z_ij agree term by term. In the coefficient of z₀ the products x_i y_j + -- cancel, and the cross terms are ∑_i x_i ∑_j q̂_ij + ∑_j y_j ∑_i p̂_ij = 0. + linear_combination polyO .z0 * (polyL (.x 0) * hrow 0 + polyL (.x 1) * hrow 1 + + polyL (.x 2) * hrow 2 + polyR (.y 0) * hcol 0 + polyR (.y 1) * hcol 1 + polyR (.y 2) * hcol 2) + +end Cslib.Algorithms.MatrixMultiplication.Schonhage diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Pruned.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Pruned.lean new file mode 100644 index 000000000..30043faf4 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Pruned.lean @@ -0,0 +1,751 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Full +public import Mathlib.Data.Multiset.Bind +public import Mathlib.Data.Finset.Piecewise + +/-! +# Pruned tensor evaluation and the calls it makes + +The recursion skips children with no requested output strings, reading only precomputed +leaf encodings. Its values agree with the full recursion on the requested set and vanish +elsewhere. A call trace records finite recursive calls and passed sets; it is not a machine +execution or an instruction counter. + +The root is always called. At depth zero with an empty request set it is itself a visited +leaf, although the set of contributing leaves is empty. The leaf characterization therefore +requires positive depth or a nonempty request set. The value and total-set-size statements +hold without that qualification. + +## Main definitions + +* `Pruned.run`, `Pruned.result`, `Pruned.calls`: pruned evaluation and its call trace. +* `Pruned.Visits`, `Pruned.totalSize`: visited leaves and total size of passed sets. +* `computeEncoding`: recursive computation of either input encoding. + +## Main results + +* `Pruned.result_eq_piecewise_full`: agreement on requested strings and zero elsewhere. +* `Pruned.visits_iff_mem_leaves`: exact visited-leaf characterization with the root exception. +* `Pruned.totalSize_le`: passed-set total bounded by depth plus one times leaf count. +* `pruned_spec`: all three properties together for the two input encodings. + +## References + +Part of [AlmanWilliams2026, Section 2]. Direct mathematical adaptation of +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Definitions are selectively extracted from that pin's `PaperStatements.lean`; no machine +semantics or running-time statement is imported. Source-derived prose is paraphrased below. +-/ + +@[expose] public section + +open Finset + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Section 2.4.2: for a set S of output strings and an output variable z, the slice S_z is the +set of output strings w' with z w' ∈ S. +-/ +def sliceSet {n : ℕ} (S : Finset (OutStr (n + 1))) (z : OutVar) : Finset (OutStr n) := + univ.filter fun w' => (Fin.cons z w' : OutStr (n + 1)) ∈ S + +/-- Section 2.4.2, step (2) of `Pruned.result`: let S_λ be the union of the slices S_z over the z +to which λ contributes. +-/ +def childSet {n : ℕ} (S : Finset (OutStr (n + 1))) (lam : Term) : Finset (OutStr n) := + (univ.filter fun z => lam.Contributes z).biUnion fun z => sliceSet S z + +/-- A call of `Pruned.result`. `vertex` is the vertex `τ₁⋯τ_k` at which it is made, as the pair +of its depth `k` and the string of terms. `passed` is the set that is passed to it, as the pair +of the length of its strings and the set. +-/ +structure PrunedCall where + /-- The call depth and the terms identifying its vertex. +-/ + vertex : Σ k, Vertex k + /-- The remaining string length and requested output strings. +-/ + passed : Σ n, Finset (OutStr n) + +/-- The same call, seen from one level higher: the vertex `τ₁⋯τ_k` below the child `λ` is the +vertex `λ τ₁⋯τ_k`. +-/ +def PrunedCall.under (lam : Term) (c : PrunedCall) : PrunedCall where + vertex := ⟨c.vertex.1 + 1, (Fin.cons lam c.vertex.2 : Vertex (c.vertex.1 + 1))⟩ + passed := c.passed + +/-- Section 2.4.2: Pruned_{τ₁⋯τ_k}(S), for a set S of output strings of length L - k, returns the +array returned by the vertex τ₁⋯τ_k of Full.result, restricted to the strings in S: (1) If k = L, +then look up Φ_τ(a) and Ψ_τ(b) from the precomputed encodings (Section 2.4.1) and return Φ_τ(a) +Ψ_τ(b) for the leaf τ = τ₁⋯τ_L. (2) For each term λ of Schönhage's identity, let S_λ be the union +of the slices S_z over the z to which λ contributes, i.e., S_{P_ij} := S_{z_ij} ∪ S_{z₀} and +S_{P₀} := S_{z₀}. (3) For each term λ of Schönhage's identity with S_λ ≠ ∅, compute C_λ := +Pruned_{τ₁⋯τ_k λ}(S_λ), an array on S_λ. (4) Return the array c on S whose slices are c_{z_ij} := +C_{P_ij} and c_{z₀} := ∑_λ C_λ, restricted to S_{z_ij} and to S_{z₀}. + +`Pruned.run` is this recursion, and it also keeps a record of the calls. Its first component is +the array that is returned. Its second component lists all the calls that are made, this one +included, each with the set passed to it. Vertices are named relative to the call we start from, +so for the call at the root they are the vertices of the paper. + +Here `n = L - k`. The vertex `τ₁⋯τ_k` enters only through the look-ups of step (1), so it is +passed as the parts of the two encodings that lie below it: `encA τ'` is the entry of the encoding +of `a` at the leaf `τ₁⋯τ_k τ'`, and likewise `encB`. At the root these are the two encodings +themselves, and the child `λ` receives their slices at `λ`. For a term `λ` with `S_λ = ∅` no call +is made: nothing is recorded, and `C_λ` is written as the zero array; step (4) does not read it, +because then `S_{z₀}`, and `S_{z_ij}` if `λ = P_ij`, are empty. In step (1) the number is +returned as an array on `S`, like every other result; `S` then consists of the empty string, +except when `Pruned.result` is called at the root with `L = 0` and `S = ∅`. +-/ +def Pruned.run : (n : ℕ) → (Leaf n → ℤ) → (Leaf n → ℤ) → Finset (OutStr n) → + (OutStr n → ℤ) × Multiset PrunedCall + | 0, encA, encB, S => + (S.piecewise (fun _ => encA Fin.elim0 * encB Fin.elim0) 0, {⟨⟨0, Fin.elim0⟩, ⟨0, S⟩⟩}) + | n + 1, encA, encB, S => + let R : Term → (OutStr n → ℤ) × Multiset PrunedCall := fun lam => + if (childSet S lam).Nonempty then + Pruned.run n (sliceAt encA lam) (sliceAt encB lam) (childSet S lam) + else (0, 0) + (S.piecewise (fun w => + match w 0 with + | .z i j => (R (.P i j)).1 (Fin.tail w) + | .z0 => ∑ lam, (R lam).1 (Fin.tail w)) 0, + ⟨⟨0, Fin.elim0⟩, ⟨n + 1, S⟩⟩ ::ₘ + univ.val.bind fun lam => ((R lam).2).map (PrunedCall.under lam)) + +/-- The array that `Pruned.result(S)` returns. +-/ +def Pruned.result (n : ℕ) (encA encB : Leaf n → ℤ) (S : Finset (OutStr n)) : OutStr n → ℤ := + (Pruned.run n encA encB S).1 + +/-- All the calls that `Pruned.result(S)` makes, itself included, each with the set passed to it. +-/ +def Pruned.calls (n : ℕ) (encA encB : Leaf n → ℤ) (S : Finset (OutStr n)) : Multiset PrunedCall := + (Pruned.run n encA encB S).2 + +/-- The leaves that `Pruned.result(U)`, called at the root, visits: the vertices at depth `L` +among its calls. +-/ +def Pruned.Visits {L : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (OutStr L)) (τ : Leaf L) : Prop := + ∃ c ∈ Pruned.calls L encA encB U, c.vertex = ⟨L, τ⟩ + +/-- The total size of the sets passed to the calls that `Pruned.result(U)` makes. +-/ +def Pruned.totalSize {L : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (OutStr L)) : ℕ := + ((Pruned.calls L encA encB U).map fun c => c.passed.2.card).sum + +/-- Section 2.4.3: the leaves contributing to an output string with inner set Q are the 10^m +leaves that choose any term at each of the m levels of Q, but P_ij at every level outside Q where +the output string has z_ij. We call the one that chooses P₀ at every level of Q the private leaf +of the output string. +-/ +def privateLeaf {L : ℕ} (w : OutStr L) : Leaf L := fun ℓ => (w ℓ).privateTerm + +/-! ### Section 2.4.1: the encodings -/ + +/-- Section 2.4.1, the procedure that computes the encoding of `a`: we run Full.result with b left +out and without step (4): […] the left number at the leaf τ is Φ_τ(a), and the leaf stores it. +What is left of `Full.result` is step (2) for `a`, the recursive calls of step (3), and at depth +`L` the number that has been reached. The encoding of b […] is computed in the same way, so the +procedure is written once, for the coefficients `c` of any family of linear forms: `c = φ` for +`a`, and `c = ψ` for `b`. +-/ +def computeEncoding {α : Type} [Fintype α] (c : Term → α → ℤ) : + (L : ℕ) → ((Fin L → α) → ℤ) → Leaf L → ℤ + | 0, a, _ => a Fin.elim0 + | L + 1, a, τ => computeEncoding c L (fun u' => ∑ s, c (τ 0) s * sliceAt a s u') (Fin.tail τ) + +/-- At the leaf `τ` the procedure of Section 2.4.1 stores `∑_u a[u] ∏_ℓ c_{τ_ℓ}(u_ℓ)`. +-/ +theorem computeEncoding_eq_encodeWith {α : Type} [Fintype α] (c : Term → α → ℤ) {L : ℕ} + (a : (Fin L → α) → ℤ) (τ : Leaf L) : computeEncoding c L a τ = encodeWith c τ a := by + induction L with + | zero => rw [computeEncoding, encodeWith_zero] + | succ n ih => rw [computeEncoding, ih, ← encodeWith_cons, Fin.cons_self_tail] + +/-- The procedure of Section 2.4.1 computes the encoding of `a`. +-/ +theorem computeEncoding_phi {L : ℕ} (a : LeftStr L → ℤ) : computeEncoding phi L a = encodingL a := + funext (computeEncoding_eq_encodeWith phi a) + +/-- The procedure of Section 2.4.1 computes the encoding of `b`. +-/ +theorem computeEncoding_psi {L : ℕ} (b : RightStr L → ℤ) : computeEncoding psi L b = encodingR b := + funext (computeEncoding_eq_encodeWith psi b) + +/-! ### Section 2.4.2: the sets `S_λ` of step (2) -/ + +/-- Membership in a slice of a set of output strings. +-/ +@[simp] +theorem mem_sliceSet {n : ℕ} (S : Finset (OutStr (n + 1))) (z : OutVar) (w' : OutStr n) : + w' ∈ sliceSet S z ↔ (Fin.cons z w' : OutStr (n + 1)) ∈ S := by + simp [sliceSet] + +/-- Section 2.4.2, step (2): i.e., S_{P_ij} := S_{z_ij} ∪ S_{z₀}. +-/ +theorem childSet_P {n : ℕ} (S : Finset (OutStr (n + 1))) (i j : Fin 3) : + childSet S (.P i j) = sliceSet S (.z i j) ∪ sliceSet S .z0 := by + have hz : (univ.filter fun z => (Term.P i j).Contributes z) = {.z i j, .z0} := by + decide +revert + rw [childSet, hz, biUnion_insert, singleton_biUnion] + +/-- Section 2.4.2, step (2): and S_{P₀} := S_{z₀}. +-/ +theorem childSet_P0 {n : ℕ} (S : Finset (OutStr (n + 1))) : + childSet S .P0 = sliceSet S .z0 := by + have hz : (univ.filter fun z => Term.P0.Contributes z) = {.z0} := by decide + rw [childSet, hz, singleton_biUnion] + +/-- Section 2.4.2, step (4): These restrictions make sense, since S_{z_ij} ⊆ S_{P_ij} and S_{z₀} ⊆ +S_λ for every λ, by step (2): `S_z ⊆ S_λ` whenever `λ` contributes to `z`. +-/ +theorem sliceSet_subset_childSet {n : ℕ} (S : Finset (OutStr (n + 1))) {lam : Term} {z : OutVar} + (h : lam.Contributes z) : sliceSet S z ⊆ childSet S lam := + subset_biUnion_of_mem (fun z => sliceSet S z) (mem_filter.mpr ⟨mem_univ z, h⟩) + +/-- In the proof of Lemma 10: S_λ consists of the last L-k-1 variables of each string in S such +that λ contributes to the first variable of that string. +-/ +theorem mem_childSet {n : ℕ} {S : Finset (OutStr (n + 1))} {lam : Term} {w' : OutStr n} : + w' ∈ childSet S lam ↔ ∃ w ∈ S, lam.Contributes (w 0) ∧ Fin.tail w = w' := by + simp only [childSet, mem_biUnion, mem_filter, mem_univ, true_and, mem_sliceSet] + constructor + · rintro ⟨z, hz, hw⟩ + exact ⟨_, hw, by simpa using hz, by simp⟩ + · rintro ⟨w, hw, hz, rfl⟩ + exact ⟨w 0, hz, by rwa [Fin.cons_self_tail]⟩ + +/-! ### Lemma 10, first claim: the values -/ + +/-- The array `C_λ` of step (3) of `Pruned.result`; it is the zero array if `S_λ = ∅`. +-/ +def Pruned.childResult {n : ℕ} (encA encB : Leaf (n + 1) → ℤ) (S : Finset (OutStr (n + 1))) + (lam : Term) : OutStr n → ℤ := + if (childSet S lam).Nonempty then + Pruned.result n (sliceAt encA lam) (sliceAt encB lam) (childSet S lam) + else 0 + +/-- Steps (2) to (4) of `Pruned.result`, written out. +-/ +theorem Pruned.result_succ {n : ℕ} (encA encB : Leaf (n + 1) → ℤ) (S : Finset (OutStr (n + 1))) : + Pruned.result (n + 1) encA encB S = S.piecewise (fun w => + match w 0 with + | .z i j => Pruned.childResult encA encB S (.P i j) (Fin.tail w) + | .z0 => ∑ lam, Pruned.childResult encA encB S lam (Fin.tail w)) 0 := by + simp only [Pruned.result, Pruned.run, Pruned.childResult, apply_ite Prod.fst] + +/-- Step (4) of `Pruned.result` at one string: the entry at `w ∈ S` is the sum of the `C_λ`, at +the rest of `w`, over the terms `λ` that contribute to the first variable of `w`. +-/ +theorem Pruned.result_succ_apply {n : ℕ} (encA encB : Leaf (n + 1) → ℤ) + (S : Finset (OutStr (n + 1))) (w : OutStr (n + 1)) : + Pruned.result (n + 1) encA encB S w = if w ∈ S then + ∑ lam : Term with lam.Contributes (w 0), Pruned.childResult encA encB S lam (Fin.tail w) + else 0 := by + rw [Pruned.result_succ, Finset.piecewise, Term.sum_contributes] + rfl + +/-- What `Pruned.result` returns, for arbitrary arrays in place of the two encodings: at each +string of `S`, the sum, over the leaves contributing to it, of the product of the two numbers +looked up at the leaf in step (1). +-/ +theorem Pruned.result_eq_piecewise_sum {n : ℕ} (encA encB : Leaf n → ℤ) (S : Finset (OutStr n)) : + Pruned.result n encA encB S + = S.piecewise (fun w => ∑ τ : Leaf n with Leaf.Contributes τ w, encA τ * encB τ) 0 := by + induction n with + | zero => + -- The only leaf is the empty string, and it contributes to the empty output string. + have hleaf (w : OutStr 0) : + (univ.filter fun τ : Leaf 0 => Leaf.Contributes τ w) = {Fin.elim0} := + eq_singleton_iff_unique_mem.mpr + ⟨mem_filter.mpr ⟨mem_univ _, fun ℓ => ℓ.elim0⟩, fun _ _ => Subsingleton.elim _ _⟩ + simp only [Pruned.result, Pruned.run, hleaf, sum_singleton] + | succ n ih => + -- The induction hypothesis describes each `C_λ`, also when `S_λ = ∅`. + have hC (lam : Term) : Pruned.childResult encA encB S lam + = (childSet S lam).piecewise (fun w' => + ∑ τ' : Leaf n with Leaf.Contributes τ' w', + encA (Fin.cons lam τ') * encB (Fin.cons lam τ')) 0 := by + unfold Pruned.childResult + split_ifs with hne + · exact ih _ _ _ + · rw [not_nonempty_iff_eq_empty.mp hne, Finset.piecewise_empty] + -- Step (4) reads `C_λ` only at strings of `S_λ`, and a leaf is its first term and the rest. + funext w + rw [Pruned.result_succ_apply, Finset.piecewise, Leaf.sum_contributes_succ] + split_ifs with hw + · refine sum_congr rfl fun lam hlam => ?_ + have hmem : Fin.tail w ∈ childSet S lam := + mem_childSet.mpr ⟨w, hw, (mem_filter.mp hlam).2, rfl⟩ + rw [hC, Finset.piecewise_eq_of_mem _ _ _ hmem] + · rfl + +/-- **Lemma 10**, first claim. Let a and b be input arrays, and let U be a set of output strings +of length L. Called at the root, Pruned.result(U) returns bilinearProduct(a, b) restricted to U. +-/ +theorem Pruned.result_eq_piecewise_bilinearProduct {L : ℕ} (a : LeftStr L → ℤ) + (b : RightStr L → ℤ) (U : Finset (OutStr L)) : + Pruned.result L (encodingL a) (encodingR b) U = U.piecewise (bilinearProduct a b) 0 := + -- Sum the products of the encoded numbers over the contributing leaves. + Pruned.result_eq_piecewise_sum _ _ U + +/-- Section 2.4.2, the specification of `Pruned.result`: it returns the array returned by the +vertex τ₁⋯τ_k of Full.result, restricted to the strings in S. Here `a` and `b` are the input +arrays of that vertex: by `encodeLeft_cons` and `encodeRight_cons`, applied once for each level, +the parts of the two encodings below a vertex are the encodings of its input arrays. +-/ +theorem Pruned.result_eq_piecewise_full {n : ℕ} (a : LeftStr n → ℤ) (b : RightStr n → ℤ) + (S : Finset (OutStr n)) : + Pruned.result n (encodingL a) (encodingR b) S = S.piecewise (Full.result n a b) 0 := by + rw [Pruned.result_eq_piecewise_bilinearProduct, Full.result_eq_bilinearProduct] + +/-! ### Lemma 10, second claim: the calls that are made and the sets passed to them -/ + +/-- Section 2.3.2: a vertex τ₁ ⋯ τ_k contributes to an output string w if τ_ℓ contributes to w_ℓ +at every level ℓ ≤ k. Only `k ≤ L` occurs; the hypothesis `h` is there so that `w_ℓ` makes sense. +-/ +def Vertex.Contributes {k L : ℕ} (τ : Vertex k) (w : OutStr L) : Prop := + ∀ (ℓ : Fin k) (h : (ℓ : ℕ) < L), (τ ℓ).Contributes (w ⟨ℓ, h⟩) + +/-- Section 2.3.2: for a vertex at depth `L`, that is, a leaf, contributes to means that `τ_ℓ` +contributes to `w_ℓ` at every level. +-/ +theorem Vertex.contributes_iff_leaf {L : ℕ} (τ : Leaf L) (w : OutStr L) : + Vertex.Contributes τ w ↔ Leaf.Contributes τ w := + ⟨fun h ℓ => h ℓ ℓ.2, fun h ℓ _ => h ℓ⟩ + +/-- The vertex `λ τ₁⋯τ_k` contributes to `w` if and only if `λ` contributes to the first variable +of `w` and `τ₁⋯τ_k` contributes to the rest of `w`. +-/ +theorem Vertex.contributes_cons {k L : ℕ} {lam : Term} {τ : Vertex k} {w : OutStr (L + 1)} : + Vertex.Contributes (Fin.cons lam τ : Vertex (k + 1)) w + ↔ lam.Contributes (w 0) ∧ Vertex.Contributes τ (Fin.tail w) := by + constructor + · intro h + exact ⟨h 0 (Nat.succ_pos L), fun ℓ hℓ => h ℓ.succ (Nat.succ_lt_succ hℓ)⟩ + · rintro ⟨h0, h1⟩ ℓ h + induction ℓ using Fin.cases with + | zero => exact h0 + | succ i => exact h1 i (Nat.lt_of_succ_lt_succ h) + +/-- The calls made by the child `λ` of `Pruned.result(S)`: none if `S_λ = ∅`. +-/ +def Pruned.childCalls {n : ℕ} (encA encB : Leaf (n + 1) → ℤ) (S : Finset (OutStr (n + 1))) + (lam : Term) : Multiset PrunedCall := + if (childSet S lam).Nonempty then + Pruned.calls n (sliceAt encA lam) (sliceAt encB lam) (childSet S lam) + else 0 + +/-- The calls made by `Pruned.result(S)`: the call itself, and the calls made by the children `λ` +with `S_λ ≠ ∅`. +-/ +theorem Pruned.calls_succ {n : ℕ} (encA encB : Leaf (n + 1) → ℤ) (S : Finset (OutStr (n + 1))) : + Pruned.calls (n + 1) encA encB S = ⟨⟨0, Fin.elim0⟩, ⟨n + 1, S⟩⟩ ::ₘ + univ.val.bind fun lam => (Pruned.childCalls encA encB S lam).map (.under lam) := by + simp only [Pruned.calls, Pruned.run, Pruned.childCalls, apply_ite Prod.snd] + +/-- The calls of `Pruned.result(S)`, one level unfolded (steps (2) and (3)): the call itself, and +for each term `λ` with `S_λ ≠ ∅` the calls of `Pruned_λ(S_λ)`. +-/ +theorem Pruned.mem_calls_succ {n : ℕ} {encA encB : Leaf (n + 1) → ℤ} + {S : Finset (OutStr (n + 1))} {c : PrunedCall} : + c ∈ Pruned.calls (n + 1) encA encB S ↔ + c = ⟨⟨0, Fin.elim0⟩, ⟨n + 1, S⟩⟩ + ∨ ∃ lam : Term, (childSet S lam).Nonempty + ∧ ∃ c' ∈ Pruned.calls n (sliceAt encA lam) (sliceAt encB lam) (childSet S lam), + c = c'.under lam := by + rw [Pruned.calls_succ, Multiset.mem_cons, Multiset.mem_bind] + refine or_congr Iff.rfl ⟨?_, ?_⟩ + · rintro ⟨lam, -, h⟩ + obtain ⟨c', hc', rfl⟩ := Multiset.mem_map.mp h + unfold Pruned.childCalls at hc' + split_ifs at hc' with hne + · exact ⟨lam, hne, c', hc', rfl⟩ + · simp at hc' + · rintro ⟨lam, hne, c', hc', rfl⟩ + refine ⟨lam, mem_univ lam, Multiset.mem_map.mpr ⟨c', ?_, rfl⟩⟩ + rwa [Pruned.childCalls, ite_eq_left hne] + +/-- A call below the child `λ` is at the vertex `τ` of depth `k + 1` exactly if `λ` is the first +term of `τ` and, seen from the child, the call is at the rest of `τ`. +-/ +private theorem PrunedCall.vertex_under_eq_iff {lam : Term} {c : PrunedCall} {k : ℕ} + {τ : Vertex (k + 1)} : + (c.under lam).vertex = ⟨k + 1, τ⟩ ↔ lam = τ 0 ∧ c.vertex = ⟨k, Fin.tail τ⟩ := by + obtain ⟨⟨k', τ'⟩, T⟩ := c + constructor + · intro h + simp only [PrunedCall.under, Sigma.mk.injEq, Nat.add_right_cancel_iff] at h + obtain ⟨rfl, h⟩ := h + obtain rfl := eq_of_heq h + simp + · rintro ⟨rfl, h⟩ + simp only [Sigma.mk.injEq] at h + obtain ⟨rfl, h⟩ := h + obtain rfl := eq_of_heq h + simp [PrunedCall.under] + +/-- `Pruned.result(U)`, called at the root, calls the vertex `τ`. For a leaf this is +`Pruned.Visits`. +-/ +def Pruned.Called {L k : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (OutStr L)) (τ : Vertex k) : + Prop := + ∃ c ∈ Pruned.calls L encA encB U, c.vertex = ⟨k, τ⟩ + +/-- The root is called. +-/ +theorem Pruned.called_root {L : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (OutStr L)) + (τ : Vertex 0) : Pruned.Called encA encB U τ := by + obtain rfl : τ = Fin.elim0 := Subsingleton.elim _ _ + cases L with + | zero => exact ⟨_, Multiset.mem_singleton_self _, rfl⟩ + | succ n => exact ⟨_, Pruned.mem_calls_succ.mpr (.inl rfl), rfl⟩ + +/-- Step (3): a vertex `λ τ'` below the root is called exactly if `S_λ ≠ ∅` and the child `λ` +calls `τ'`. +-/ +theorem Pruned.called_succ {n k : ℕ} (encA encB : Leaf (n + 1) → ℤ) (S : Finset (OutStr (n + 1))) + (τ : Vertex (k + 1)) : + Pruned.Called encA encB S τ ↔ (childSet S (τ 0)).Nonempty ∧ + Pruned.Called (sliceAt encA (τ 0)) (sliceAt encB (τ 0)) (childSet S (τ 0)) (Fin.tail τ) := by + constructor + · rintro ⟨c, hc, hcτ⟩ + rcases Pruned.mem_calls_succ.mp hc with rfl | ⟨lam, hne, c', hc', rfl⟩ + · exact absurd (congrArg Sigma.fst hcτ) (by simp) + · obtain ⟨rfl, hc'τ⟩ := PrunedCall.vertex_under_eq_iff.mp hcτ + exact ⟨hne, c', hc', hc'τ⟩ + · rintro ⟨hne, c', hc', hc'τ⟩ + exact ⟨c'.under (τ 0), Pruned.mem_calls_succ.mpr (.inr ⟨τ 0, hne, c', hc', rfl⟩), + PrunedCall.vertex_under_eq_iff.mpr ⟨rfl, hc'τ⟩⟩ + +/-- The vertex `λ τ'` contributes to a string of `S` exactly if `τ'` contributes to a string of +`S_λ`. +-/ +private theorem exists_contributes_childSet {n k : ℕ} (S : Finset (OutStr (n + 1))) + (τ : Vertex (k + 1)) : + (∃ w' ∈ childSet S (τ 0), Vertex.Contributes (Fin.tail τ) w') + ↔ ∃ w ∈ S, Vertex.Contributes τ w := by + have hcons (w : OutStr (n + 1)) : Vertex.Contributes τ w + ↔ (τ 0).Contributes (w 0) ∧ Vertex.Contributes (Fin.tail τ) (Fin.tail w) := by + rw [← Vertex.contributes_cons, Fin.cons_self_tail] + constructor + · rintro ⟨w', hw', hτ'⟩ + obtain ⟨w, hw, hz, rfl⟩ := mem_childSet.mp hw' + exact ⟨w, hw, (hcons w).mpr ⟨hz, hτ'⟩⟩ + · rintro ⟨w, hw, hτ⟩ + exact ⟨_, mem_childSet.mpr ⟨w, hw, ((hcons w).mp hτ).1, rfl⟩, ((hcons w).mp hτ).2⟩ + +/-- A vertex at depth `k ≤ L` is called by `Pruned.result(U)` if and only if it is the root or it +contributes to some string of `U` (proof of Lemma 10). The induction follows the recursion: the +vertex `λ τ'` is called by `Pruned.result(U)` exactly if `τ'` is called by `Pruned.result(U_λ)`. +-/ +theorem Pruned.called_iff_root_or {L k : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (OutStr L)) + (hkL : k ≤ L) (τ : Vertex k) : + Pruned.Called encA encB U τ ↔ k = 0 ∨ ∃ w ∈ U, Vertex.Contributes τ w := by + induction L generalizing k with + | zero => + obtain rfl : k = 0 := by omega + exact iff_of_true (Pruned.called_root encA encB U τ) (.inl rfl) + | succ n ih => + cases k with + | zero => exact iff_of_true (Pruned.called_root encA encB U τ) (.inl rfl) + | succ k => + have ihτ := ih (sliceAt encA (τ 0)) (sliceAt encB (τ 0)) (childSet U (τ 0)) + (Nat.le_of_succ_le_succ hkL) (Fin.tail τ) + rw [Pruned.called_succ, ← exists_contributes_childSet] + constructor + · rintro ⟨⟨w', hw'⟩, hcalled⟩ + rcases ihτ.mp hcalled with rfl | hτ' + · -- The child `λ` itself, as a vertex at depth 0, contributes to every string of `U_λ`. + exact .inr ⟨w', hw', fun ℓ => ℓ.elim0⟩ + · exact .inr hτ' + · rintro (hk | ⟨w', hw', hτ'⟩) + · exact absurd hk (Nat.succ_ne_zero k) + · exact ⟨⟨w', hw'⟩, ihτ.mpr (.inr ⟨w', hw', hτ'⟩)⟩ + +/-- In the proof of Lemma 10: the vertex is called if and only if this set is nonempty, that is, +if and only if the vertex contributes to some string of `U`. + +NOTE. As printed this fails for the root when `U = ∅` (the root is always called). The +hypothesis `hk` excludes only that case. `hkL` says that `τ` is a vertex of the recursion tree, +whose depth is at most `L` (Section 2.3.1); the type `Vertex k` exists for every `k`. +-/ +theorem Pruned.called_iff {L k : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (OutStr L)) + (hk : 1 ≤ k ∨ U.Nonempty) (hkL : k ≤ L) (τ : Vertex k) : + Pruned.Called encA encB U τ ↔ ∃ w ∈ U, Vertex.Contributes τ w := by + rw [Pruned.called_iff_root_or _ _ U hkL τ] + constructor + · rintro (rfl | h) + · obtain ⟨w, hw⟩ := hk.resolve_left (by omega) + exact ⟨w, hw, fun ℓ => ℓ.elim0⟩ + · exact h + · exact Or.inr + +/-- A call at depth `k` is passed strings of length `L - k`. +-/ +theorem Pruned.depth_add_length {L : ℕ} {encA encB : Leaf L → ℤ} {U : Finset (OutStr L)} + {c : PrunedCall} (hc : c ∈ Pruned.calls L encA encB U) : c.vertex.1 + c.passed.1 = L := by + induction L generalizing c with + | zero => + obtain rfl := Multiset.mem_singleton.mp hc + rfl + | succ n ih => + rcases Pruned.mem_calls_succ.mp hc with rfl | ⟨lam, -, c', hc', rfl⟩ + · exact Nat.zero_add _ + · have hlen := ih hc' + change c'.vertex.1 + 1 + c'.passed.1 = n + 1 + omega + +/-- `w'` is the suffix of `w` that is left when the first `k` variables are removed. +-/ +def IsSuffixFrom {n L : ℕ} (k : ℕ) (w' : OutStr n) (w : OutStr L) : Prop := + ∀ (i : Fin n) (hi : k + (i : ℕ) < L), w' i = w ⟨k + i, hi⟩ + +/-- Removing no variable leaves the string as it is. +-/ +private theorem isSuffixFrom_zero {n : ℕ} (w' w : OutStr n) : IsSuffixFrom 0 w' w ↔ w' = w := by + have hidx (i : Fin n) (hi : 0 + (i : ℕ) < n) : (⟨0 + i, hi⟩ : Fin n) = i := + Fin.ext (Nat.zero_add _) + constructor + · intro h + funext i + rw [h i (by simp), hidx] + · rintro rfl i hi + rw [hidx] + +/-- Removing `k + 1` variables is removing the first variable and then `k` more. +-/ +private theorem isSuffixFrom_succ {n L k : ℕ} {w' : OutStr n} {w : OutStr (L + 1)} : + IsSuffixFrom (k + 1) w' w ↔ IsSuffixFrom k w' (Fin.tail w) := by + have hidx (i : Fin n) (h₁ : k + 1 + (i : ℕ) < L + 1) (h₂ : k + (i : ℕ) < L) : + w ⟨k + 1 + i, h₁⟩ = Fin.tail w ⟨k + i, h₂⟩ := + congrArg w (Fin.ext (by simp only [Fin.val_succ]; omega)) + exact ⟨fun h i hi => (h i (by omega)).trans (hidx i _ hi), + fun h i hi => (h i (by omega)).trans (hidx i hi _).symm⟩ + +/-- The set passed to the root is `U`, and the root contributes to every string. +-/ +private theorem mem_iff_root {L : ℕ} (U : Finset (OutStr L)) (w' : OutStr L) : + w' ∈ U ↔ ∃ w ∈ U, Vertex.Contributes (Fin.elim0 : Vertex 0) w ∧ IsSuffixFrom 0 w' w := by + simp only [isSuffixFrom_zero] + exact ⟨fun h => ⟨w', h, fun ℓ => ℓ.elim0, rfl⟩, fun ⟨_, hw, _, h⟩ => h ▸ hw⟩ + +/-- Proof of Lemma 10 (announced in Section 2.4.2): the set passed to the vertex τ₁⋯τ_k consists +of the suffixes w_{k+1}⋯w_L of the strings w of U to which the vertex contributes. For a call +`c`, `c.vertex.1` is the depth `k`, `c.vertex.2` the vertex, `c.passed.1` the length `L - k` of +the strings passed, and `c.passed.2` the set passed. +-/ +theorem Pruned.mem_passed_iff {L : ℕ} {encA encB : Leaf L → ℤ} {U : Finset (OutStr L)} + {c : PrunedCall} (hc : c ∈ Pruned.calls L encA encB U) (w' : OutStr c.passed.1) : + w' ∈ c.passed.2 ↔ + ∃ w ∈ U, Vertex.Contributes c.vertex.2 w ∧ IsSuffixFrom c.vertex.1 w' w := by + induction L generalizing c with + | zero => + obtain rfl := Multiset.mem_singleton.mp hc + exact mem_iff_root U w' + | succ n ih => + rcases Pruned.mem_calls_succ.mp hc with rfl | ⟨lam, -, c', hc', rfl⟩ + · exact mem_iff_root U w' + · -- A vertex below the child `λ`: the induction hypothesis for `S_λ`, whose strings are the + -- rests of the strings of `U` to whose first variable `λ` contributes. + refine (ih hc' w').trans ⟨?_, ?_⟩ + · rintro ⟨_, hw₁, hτ, hsuffix⟩ + obtain ⟨w, hw, hz, rfl⟩ := mem_childSet.mp hw₁ + exact ⟨w, hw, Vertex.contributes_cons.mpr ⟨hz, hτ⟩, isSuffixFrom_succ.mpr hsuffix⟩ + · rintro ⟨w, hw, hτ, hsuffix⟩ + obtain ⟨hz, hτ'⟩ := Vertex.contributes_cons.mp hτ + exact ⟨Fin.tail w, mem_childSet.mpr ⟨w, hw, hz, rfl⟩, hτ', isSuffixFrom_succ.mp hsuffix⟩ + +/-- A leaf is visited exactly when it is the zero-depth root or contributes to a request. This +includes the empty request set without an exceptional hypothesis. +-/ +theorem Pruned.visits_iff_root_or_mem_leaves {L : ℕ} (encA encB : Leaf L → ℤ) + (U : Finset (OutStr L)) (τ : Leaf L) : + Pruned.Visits encA encB U τ ↔ L = 0 ∨ τ ∈ leaves U := by + change Pruned.Called encA encB U τ ↔ _ + simpa only [mem_leaves, Vertex.contributes_iff_leaf] using + Pruned.called_iff_root_or encA encB U (le_refl L) τ + +/-- **Lemma 10**, second claim. The leaves it visits are exactly those of leaves(U). The +hypothesis `0 < L ∨ U.Nonempty` is explained at `pruned_spec`. +-/ +theorem Pruned.visits_iff_mem_leaves {L : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (OutStr L)) + (h : 0 < L ∨ U.Nonempty) (τ : Leaf L) : Pruned.Visits encA encB U τ ↔ τ ∈ leaves U := + -- A leaf is a vertex at depth `L`. + (Pruned.called_iff encA encB U h le_rfl τ).trans + (by simp only [mem_leaves, Vertex.contributes_iff_leaf]) + +/-! ### Lemma 10, third claim: the total size of the sets passed -/ + +/-- The leaf `λ τ'` is in `leaves(S)` if and only if `τ'` is in `leaves(S_λ)`. +-/ +theorem mem_leaves_succ {n : ℕ} {S : Finset (OutStr (n + 1))} {τ : Leaf (n + 1)} : + τ ∈ leaves S ↔ Fin.tail τ ∈ leaves (childSet S (τ 0)) := by + rw [mem_leaves, mem_leaves] + constructor + · rintro ⟨w, hw, hcon⟩ + obtain ⟨h0, h1⟩ := Leaf.contributes_succ.mp hcon + exact ⟨Fin.tail w, mem_childSet.mpr ⟨w, hw, h0, rfl⟩, h1⟩ + · rintro ⟨w', hw', h1⟩ + obtain ⟨w, hw, h0, rfl⟩ := mem_childSet.mp hw' + exact ⟨w, hw, Leaf.contributes_succ.mpr ⟨h0, h1⟩⟩ + +/-- `leaves(S)` is the disjoint union, over the terms `λ`, of the leaves `λ τ'` with `τ'` in +`leaves(S_λ)`. +-/ +theorem card_leaves_succ {n : ℕ} (S : Finset (OutStr (n + 1))) : + (leaves S).card = ∑ lam, (leaves (childSet S lam)).card := by + rw [card_eq_sum_card_fiberwise (f := fun τ : Leaf (n + 1) => τ 0) (t := univ) + (fun _ _ => mem_coe.mpr (mem_univ _))] + refine sum_congr rfl fun lam _ => ?_ + rw [← card_image_of_injective (leaves (childSet S lam)) + (Fin.cons_right_injective (α := fun _ : Fin (n + 1) => Term) lam)] + congr 1 + ext τ + simp only [mem_filter, mem_image] + constructor + · rintro ⟨hτ, rfl⟩ + exact ⟨Fin.tail τ, mem_leaves_succ.mp hτ, Fin.cons_self_tail τ⟩ + · rintro ⟨τ', hτ', rfl⟩ + refine ⟨mem_leaves_succ.mpr ?_, Fin.cons_zero _ _⟩ + simpa using hτ' + +/-- Different output strings have different private leaves. +-/ +theorem privateLeaf_injective {L : ℕ} : Function.Injective (privateLeaf (L := L)) := + have hterm : Function.Injective OutVar.privateTerm := by decide + fun _ _ h => funext fun ℓ => hterm (congrFun h ℓ) + +/-- The map of the proof of Lemma 10, at the root: an output string goes to the leaf that picks +`P_ij` where the string has `z_ij` and `P₀` where it has `z₀`. The leaf contributes to the string +and determines it, so a set of output strings is no larger than its set of leaves. +-/ +theorem card_le_card_leaves {n : ℕ} (S : Finset (OutStr n)) : S.card ≤ (leaves S).card := by + refine card_le_card_of_injOn privateLeaf (fun w hw => ?_) privateLeaf_injective.injOn + exact mem_coe.mpr (mem_leaves.mpr + ⟨w, hw, fun _ => (Term.contributes_iff _ _).mpr (.inr rfl)⟩) + +/-- The total size of the sets passed to the calls at depth `k` in a list of calls. +-/ +private def sizeAt (s : Multiset PrunedCall) (k : ℕ) : ℕ := + ((s.filter fun c => c.vertex.1 = k).map fun c => c.passed.2.card).sum + +/-- The size at depth `k` of a list of calls with one more call. +-/ +private theorem sizeAt_cons (c : PrunedCall) (s : Multiset PrunedCall) (k : ℕ) : + sizeAt (c ::ₘ s) k + = (if c.vertex.1 = k then c.passed.2.card else 0) + sizeAt s k := by + unfold sizeAt + rw [Multiset.filter_cons] + split_ifs <;> simp + +/-- The size at depth `k` of the lists of calls of the ten children together. +-/ +private theorem sizeAt_bind (f : Term → Multiset PrunedCall) (k : ℕ) : + sizeAt (univ.val.bind f) k = ∑ lam, sizeAt (f lam) k := by + unfold sizeAt + rw [Multiset.filter_bind, Multiset.map_bind, Multiset.sum_bind] + rfl + +/-- Seen from one level higher, the calls at depth `k` are at depth `k + 1`. +-/ +private theorem sizeAt_under_succ (lam : Term) (s : Multiset PrunedCall) (k : ℕ) : + sizeAt (s.map (PrunedCall.under lam)) (k + 1) = sizeAt s k := by + unfold sizeAt + rw [Multiset.filter_map, Multiset.map_map] + rw [Multiset.filter_congr (q := fun c => c.vertex.1 = k) (by simp [PrunedCall.under])] + rfl + +/-- Seen from one level higher, no call is at depth `0`. +-/ +private theorem sizeAt_under_zero (lam : Term) (s : Multiset PrunedCall) : + sizeAt (s.map (PrunedCall.under lam)) 0 = 0 := by + unfold sizeAt + rw [Multiset.filter_map, Multiset.filter_eq_nil.mpr (by simp [PrunedCall.under])] + rfl + +/-- The total size of the sets passed is the sum, over the depths, of the sizes at each depth. +-/ +private theorem sum_sizeAt (s : Multiset PrunedCall) (N : ℕ) (h : ∀ c ∈ s, c.vertex.1 < N) : + (s.map fun c => c.passed.2.card).sum = ∑ k ∈ range N, sizeAt s k := by + induction s using Multiset.induction_on with + | empty => simp [sizeAt] + | cons c s ih => + simp only [sizeAt_cons, Multiset.map_cons, Multiset.sum_cons, sum_add_distrib] + rw [ih fun c' hc' => h c' (Multiset.mem_cons_of_mem hc'), sum_ite_eq, + ite_eq_left (mem_range.mpr (h c (Multiset.mem_cons_self c s)))] + +/-- End of the proof of Lemma 10: the sets passed to the vertices at each of the L+1 depths have +total size at most |leaves(U)|. +-/ +theorem Pruned.sum_card_passed_le {L : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (OutStr L)) (k : ℕ) : + (((Pruned.calls L encA encB U).filter fun c => c.vertex.1 = k).map fun c => c.passed.2.card).sum + ≤ (leaves U).card := by + -- The paper's injection (vertex, suffix) ↦ leaf, organised as an induction on `L`: the strings + -- passed to the root inject into `leaves(U)`, and the calls at depth `k + 1` are the calls at + -- depth `k` of the children `λ`, whose leaves, with `λ` put in front, are pairwise disjoint parts + -- of `leaves(U)`. + change sizeAt (Pruned.calls L encA encB U) k ≤ _ + induction L generalizing k with + | zero => + have hcalls : Pruned.calls 0 encA encB U = ⟨⟨0, Fin.elim0⟩, ⟨0, U⟩⟩ ::ₘ 0 := rfl + rw [hcalls, sizeAt_cons] + split_ifs + · simpa [sizeAt] using card_le_card_leaves U + · simp [sizeAt] + | succ n ih => + rw [Pruned.calls_succ, sizeAt_cons, sizeAt_bind] + cases k with + | zero => + simp only [sizeAt_under_zero, sum_const_zero, ite_true, add_zero] + exact card_le_card_leaves U + | succ k => + simp only [sizeAt_under_succ] + rw [ite_eq_right (by simp), zero_add, card_leaves_succ] + refine sum_le_sum fun lam _ => ?_ + unfold Pruned.childCalls + split_ifs + · exact ih _ _ _ _ + · simp [sizeAt] + +/-- **Lemma 10**, third claim. the sets passed to the calls it makes have total size at most +(L+1) |leaves(U)|. +-/ +theorem Pruned.totalSize_le {L : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (OutStr L)) : + Pruned.totalSize encA encB U ≤ (L + 1) * (leaves U).card := by + have hdepth : ∀ c ∈ Pruned.calls L encA encB U, c.vertex.1 < L + 1 := + fun c hc => by have := Pruned.depth_add_length hc; omega + rw [Pruned.totalSize, sum_sizeAt _ (L + 1) hdepth] + calc ∑ k ∈ range (L + 1), sizeAt (Pruned.calls L encA encB U) k + ≤ ∑ _k ∈ range (L + 1), (leaves U).card := + sum_le_sum fun k _ => Pruned.sum_card_passed_le _ _ U k + _ = (L + 1) * (leaves U).card := by simp + +/-- **Lemma 10**, its three claims together. Let a and b be input arrays, and let U be a set of +output strings of length L. Called at the root, Pruned.result(U) returns bilinearProduct(a, b) +restricted to U. The leaves it visits are exactly those of leaves(U), and the sets passed to the +calls it makes have total size at most (L+1) |leaves(U)|. + +NOTE. The hypothesis `0 < L ∨ U.Nonempty` of the second claim is not in the paper. It excludes +only the case `L = 0`, `U = ∅`, in which the root is itself a leaf, so that the call at the root +visits it, while `leaves(∅)` is empty. In the paper `L ≥ m ≥ 1`, and `Pruned.result` is run only +on nonempty sets (Section 2.4.4). +-/ +theorem pruned_spec {L : ℕ} (a : LeftStr L → ℤ) (b : RightStr L → ℤ) (U : Finset (OutStr L)) : + Pruned.result L (encodingL a) (encodingR b) U = U.piecewise (bilinearProduct a b) 0 + ∧ (0 < L ∨ U.Nonempty → + ∀ τ : Leaf L, Pruned.Visits (encodingL a) (encodingR b) U τ ↔ τ ∈ leaves U) + ∧ Pruned.totalSize (encodingL a) (encodingR b) U ≤ (L + 1) * (leaves U).card := + ⟨Pruned.result_eq_piecewise_bilinearProduct a b U, Pruned.visits_iff_mem_leaves _ _ U, + Pruned.totalSize_le _ _ U⟩ + +end Cslib.Algorithms.MatrixMultiplication.Schonhage diff --git a/ComplexityTests/Schonhage.lean b/ComplexityTests/Schonhage.lean new file mode 100644 index 000000000..62f3fb1d8 --- /dev/null +++ b/ComplexityTests/Schonhage.lean @@ -0,0 +1,100 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +import Complexity.Algorithms.MatrixMultiplication.Schonhage.Identity +import Complexity.Algorithms.MatrixMultiplication.Schonhage.Pruned + +/-! Concrete tensor evaluations, signed cancellation and pruning boundary cases. -/ + +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +namespace SchonhageTests + +private def left : LeftStr 1 → ℤ := fun u => + match u 0 with + | .x i => ![1, 2, 3] i + | .p i j => !![4, 5; 6, 7] i j + +private def right : RightStr 1 → ℤ := fun v => + match v 0 with + | .y j => ![8, 9, 10] j + | .q i j => !![11, 12; 13, 14] i j + +-- The negative tenth term cancels the unwanted outer products in the inner output. +example : Full.multipliedAt 1 left right ![.P0] = (-6, 27) := by decide + +example : Full.result 1 left right ![.z0] = 280 := by decide + +example : Full.result 1 left right ![.z 0 0] = 95 := by decide + +example : Full.result 1 left right ![.z 2 2] = 30 := by decide + +-- Unrestricted outer outputs include error terms: 95 is not the wanted product 1 * 8. +example : Full.result 1 left right ![.z 0 0] ≠ 1 * 8 := by decide + +example : coefficient (.x 0) (.y 0) .z0 = 0 := by decide + +example : coefficient (.p 0 1) (.q 0 1) .z0 = 1 := by decide + +example : coefficient (.p 0 1) (.q 1 0) .z0 = 0 := by decide + +example : coefficient (.x 0) (.q 0 0) (.z 0 0) = 1 := by decide + +private def outerQuery : Finset (OutStr 1) := {![.z 0 0]} + +private def innerQuery : Finset (OutStr 1) := {![.z0]} + +example : Pruned.result 1 (encodingL left) (encodingR right) outerQuery ![.z 0 0] = 95 := by + decide + +example : Pruned.result 1 (encodingL left) (encodingR right) outerQuery ![.z0] = 0 := by + decide + +example : (leaves outerQuery).card = 1 := by decide + +example : (Pruned.calls 1 (encodingL left) (encodingR right) outerQuery).card = 2 := by + decide + +example : Pruned.totalSize (encodingL left) (encodingR right) outerQuery = 2 := by decide + +example : (leaves innerQuery).card = 10 := by decide + +example : Pruned.totalSize (encodingL left) (encodingR right) innerQuery = 11 := by decide + +-- The shared private leaf of these two outputs is included only once. +private def mixedQuery : Finset (OutStr 1) := {![.z 0 0], ![.z0]} + +example : (leaves mixedQuery).card = 10 := by decide + +example : (Pruned.calls 1 (encodingL left) (encodingR right) mixedQuery).card = 11 := by + decide + +example : Pruned.totalSize (encodingL left) (encodingR right) mixedQuery = 12 := by decide + +example : Pruned.result 1 (encodingL left) (encodingR right) ∅ ![.z0] = 0 := by decide + +example : (Pruned.calls 1 (encodingL left) (encodingR right) ∅).card = 1 := by decide + +example : Pruned.totalSize (encodingL left) (encodingR right) ∅ = 0 := by decide + +-- Below a two-level query, the outer level keeps one child and the inner one keeps ten. +example : Pruned.result 2 (fun _ => 3) (fun _ => -4) {![.z 1 2, .z0]} + ![.z 1 2, .z0] = -120 := by decide + +example : Pruned.totalSize (fun _ : Leaf 2 => 3) (fun _ => -4) {![.z 1 2, .z0]} = 12 := by + decide + +example : Pruned.result 0 (fun _ => -3) (fun _ => 7) {Fin.elim0} Fin.elim0 = -21 := by + decide + +-- The unqualified visited-leaf equivalence fails at the empty, zero-depth root. +example : Pruned.Visits (fun _ : Leaf 0 => 1) (fun _ => 1) ∅ Fin.elim0 ∧ + Fin.elim0 ∉ leaves (∅ : Finset (OutStr 0)) := by + constructor + · exact (Pruned.visits_iff_root_or_mem_leaves _ _ ∅ Fin.elim0).mpr (.inl rfl) + · simp [leaves] + +end SchonhageTests diff --git a/NOTICE b/NOTICE index e48c2ecb6..5fb9b6c14 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,6 @@ -The 3SUM/Exact Triangle reductions, their finite counting lemmas and native matrix builder in +The 3SUM/Exact Triangle reductions, finite counting/tensor lemmas and native matrix builder in Complexity/Algorithms/ExactTriangle/{Defs,Weights,Lopsided,Hashing,Splitting,Witnesses}.lean, +Complexity/Algorithms/MatrixMultiplication/Schonhage/, Complexity/Algorithms/ThreeSum/, Complexity/Algorithms/WordRAM/ThreeSum/, Complexity/Foundations/Data/Finset/{Average,Quotient}.lean, diff --git a/ROADMAP.md b/ROADMAP.md index 044ec8d47..999c5e63c 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -424,8 +424,11 @@ each statement against the numbered result. Organize modules by subject, not cha ordinary distinct-position inputs and explicit size bounds. The finite lopsided matrix/counting interface ([#1438]) adds padding, detection and signed digit reconstruction. Modular counting and the selected-prime false-positive bound ([#1442]), finite residue/chunk/piece splitting and - witness recovery ([#1447]) are proved mathematically. It still needs tensor/sparse-query programs, - native Chan–He preprocessing, the reduction host, the fast Exact Triangle solver and full + witness recovery ([#1447]) are proved mathematically. The tensor identity, full recursion, + shared encodings and pruned-evaluation core ([#1459]) include exact visited-leaf and passed-set + bounds, with the empty zero-depth root handled explicitly. It still needs tiling and sparse + leaf bounds, native tensor/sparse-query programs, native Chan–He preprocessing, the reduction + host, the fast Exact Triangle solver and full input/time/width/asymptotic composition, then the improved Section 4 route to exponent 1.9992; size-based width selection with an input-fit guarantee and explicit overhead; expected-time rules for unbounded diff --git a/UPSTREAM.md b/UPSTREAM.md index abb96bb7a..bfa84a869 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -193,8 +193,15 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he `Algorithms/ExactTriangle/{Splitting,Witnesses}.lean` adapts the complete finite splitting and witness recovery component ([#1447](https://github.com/cslib-community/complexitylib/issues/1447)), using the existing matrices, lopsided graph and `Set.EqOn` answer API. Computable integer parameters - retain the source's exact real floor/ceiling specifications through checked bridges. Tensor/sparse - query programs, native preprocessing/solvers and full input/time/width composition remain in #1394. + retain the source's exact real floor/ceiling specifications through checked bridges. + `Algorithms/MatrixMultiplication/Schonhage/` adapts the tensor-recursion core from the same pin's + `Sec2/{Lemma6,Recursion,Lemma7_8,Lemma10}.lean`: the ten-term polynomial identity with explicit + error, full evaluation, shared encodings, pruned values, visited leaves and passed-set totals. + Its pure finite recursion traces are not native machine semantics or charged instruction costs. + The zero-depth empty-query root exception is explicit, with an unconditional root-or-leaf API. + See [#1459](https://github.com/cslib-community/complexitylib/issues/1459). Tiling/sparse leaf bounds, + native tensor/sparse-query programs, native preprocessing/solvers and full input/time/width + composition remain in #1394. * **Typed RAM upstream core.** [CSLib #1158](https://github.com/leanprover/cslib/pull/1158) at `beea60a15eddc91d93e92fa5af9fb965c351d0d5` proposes `Cslib/Computability/Machines/RAM/Basic.lean`, in namespace `Cslib.RAM.Typed`. From 62dc601e6139cc7146373caa7628f16130156a9a Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 07:55:40 -0400 Subject: [PATCH 10/73] doc(matrix): clarify the overlapping contributing leaf --- ComplexityTests/Schonhage.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ComplexityTests/Schonhage.lean b/ComplexityTests/Schonhage.lean index 62f3fb1d8..a70da4e16 100644 --- a/ComplexityTests/Schonhage.lean +++ b/ComplexityTests/Schonhage.lean @@ -64,7 +64,7 @@ example : (leaves innerQuery).card = 10 := by decide example : Pruned.totalSize (encodingL left) (encodingR right) innerQuery = 11 := by decide --- The shared private leaf of these two outputs is included only once. +-- The shared contributing leaf of these two outputs is included only once. private def mixedQuery : Finset (OutStr 1) := {![.z 0 0], ![.z0]} example : (leaves mixedQuery).card = 10 := by decide From 4f3226cd0db2b993905987a32864fa2c9a48d774 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 09:49:23 -0400 Subject: [PATCH 11/73] doc(matrix): make tensor and pruning explanations standalone --- .../MatrixMultiplication/Schonhage/Full.lean | 35 ++++------ .../Schonhage/Identity.lean | 5 +- .../Schonhage/Pruned.lean | 64 ++++++------------- 3 files changed, 34 insertions(+), 70 deletions(-) diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Full.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Full.lean index a1c8d82b6..ed1316900 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Full.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Full.lean @@ -67,19 +67,11 @@ def encodeStepL {L : ℕ} (lam : Term) (a : LeftStr (L + 1) → ℤ) : LeftStr L def encodeStepR {L : ℕ} (lam : Term) (b : RightStr (L + 1) → ℤ) : RightStr L → ℤ := fun v' => ∑ t, psi lam t * sliceAt b t v' -/-- Section 2.3.1: The recursion Full.result(a, b), for arrays a, b on the left and right strings -of length L, returns an array on the output strings of length L: (1) If L = 0, return the number -ab. (2) For each term λ of Schönhage's identity, form A_λ := ∑_s φ_λ(s) a_s and B_λ := ∑_t ψ_λ(t) -b_t. (3) For each term λ, recursively compute C_λ := Full.result(A_λ, B_λ). (4) Return the array c -whose slices are c_{z_ij} := C_{P_ij} and c_{z₀} := ∑_λ C_λ. - -`Full.run` is this recursion, and it also keeps a record of the multiplications. Its first -component is the array that is returned. Its second component gives, for each leaf `τ`, the two -numbers that step (1) multiplies at `τ`: the leaf `λ τ'` of a call is the leaf `τ'` of its -recursive call for `λ`. - -An array on the strings of length 0 has one entry, at the empty string `Fin.elim0`; that entry is -the number. +/-- Evaluate the two arrays and retain the operands used at each leaf. At depth zero, multiply +their unique entries. At positive depth, apply each term’s left and right coefficient forms, +recurse, and decode the child results into an output array. The second component maps each term +string to its pair of encoded input values. This is a finite integer recursion, with no +instruction counter. -/ def Full.run : (L : ℕ) → (LeftStr L → ℤ) → (RightStr L → ℤ) → (OutStr L → ℤ) × (Leaf L → ℤ × ℤ) | 0, a, b => (fun _ => a Fin.elim0 * b Fin.elim0, fun _ => (a Fin.elim0, b Fin.elim0)) @@ -280,7 +272,7 @@ levels are empty (proof of Lemma 7). private theorem encodeLeft_zero (τ : Leaf 0) (a : LeftStr 0 → ℤ) : encodeLeft τ a = a Fin.elim0 := encodeWith_zero phi τ a -/-- and Ψ_τ(b) = b (proof of Lemma 7). +/-- At depth zero, the right encoding equals the input entry at the unique empty string. -/ private theorem encodeRight_zero (τ : Leaf 0) (b : RightStr 0 → ℤ) : encodeRight τ b = b Fin.elim0 := @@ -321,7 +313,7 @@ theorem bilinearProduct_succ {L : ℕ} (a : LeftStr (L + 1) → ℤ) (b : RightS bilinearProduct (encodeStepL lam a) (encodeStepR lam b) (Fin.tail w) := by simp only [bilinearProduct, Leaf.sum_contributes_succ, encodeLeft_cons, encodeRight_cons] -/-- **Lemma 7**, second half. and it returns bilinearProduct(a, b). +/-- Full evaluation computes the bilinear tensor expansion `bilinearProduct`. -/ theorem Full.result_eq_bilinearProduct {L : ℕ} (a : LeftStr L → ℤ) (b : RightStr L → ℤ) : Full.result L a b = bilinearProduct a b := by @@ -334,8 +326,8 @@ theorem Full.result_eq_bilinearProduct {L : ℕ} (a : LeftStr L → ℤ) (b : Ri funext w simp only [Full.result_succ, bilinearProduct_succ, ih] -/-- **Lemma 7**. At the leaf τ, Full.result(a, b) multiplies Φ_τ(a) by Ψ_τ(b), and it returns -bilinearProduct(a, b). +/-- The complete value specification of full evaluation: its result is `bilinearProduct`, and each +leaf’s operands are the corresponding left and right encodings. -/ theorem full_spec {L : ℕ} (a : LeftStr L → ℤ) (b : RightStr L → ℤ) : (∀ τ : Leaf L, Full.multipliedAt L a b τ = (encodeLeft τ a, encodeRight τ b)) ∧ @@ -355,14 +347,13 @@ theorem prod_gamma_eq_sum_contributes {L : ℕ} (u : LeftStr L) (v : RightStr L) rw [prod_univ_sum, Leaf.filter_contributes_eq_piFinset] exact sum_congr rfl fun τ _ => prod_mul_distrib -/-- **Lemma 8**. For all arrays a, b and every output string w, bilinearProduct(a, b)[w] = -∑_{u,v} γ(u₁, v₁, w₁) γ(u₂, v₂, w₂) ⋯ γ(u_L, v_L, w_L) a[u] b[v], the sum over all left strings u -and right strings v. +/-- Expand `bilinearProduct` as a double sum of input-entry products. The coefficient of each pair +is the product, over string positions, of the one-level coefficients. -/ theorem tensor_expansion {L : ℕ} (a : LeftStr L → ℤ) (b : RightStr L → ℤ) (w : OutStr L) : bilinearProduct a b w = ∑ u, ∑ v, (∏ ℓ, coefficient (u ℓ) (v ℓ) (w ℓ)) * a u * b v := by - -- "the coefficient of a[u] b[v] in (2) is ∑_τ ∏_ℓ φ_{τ_ℓ}(u_ℓ) ψ_{τ_ℓ}(v_ℓ), summed over the - -- leaves τ contributing to w": expand `Φ_τ(a) Ψ_τ(b)` and sum over `τ` last. + -- Expand the encoded factors and reorder the finite sums. The coefficient of each input + -- product is the product of its one-level coefficients. simp only [bilinearProduct, encodeLeft, encodeRight, sum_mul_sum] rw [sum_comm] refine sum_congr rfl fun u _ => ?_ diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Identity.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Identity.lean index 15b7942f4..dea69212a 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Identity.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Identity.lean @@ -179,9 +179,8 @@ theorem sum_pHat_mul_qHat : simp [Fin.sum_univ_three, Fin.sum_univ_two, pHat, qHat, pForm, qForm, formL_eq_linearCombination, formR_eq_linearCombination] -/-- **Lemma 6**. Summing over the ten terms, ∑_λ φ_λ ψ_λ χ_λ = G + E, where E := ∑_{i,j=1}^{3} -(x_i q̂_ij + p̂_ij y_j + p̂_ij q̂_ij) z_ij. An identity of polynomials with integer coefficients -in the 24 variables. +/-- The ten products of the coefficient forms equal the wanted polynomial `G` plus the explicit +error polynomial `E`. -/ theorem identity : ∑ lam, formL (phi lam) * formR (psi lam) * formO (chi lam) = G + E := by -- The column sums of p̂ and the row sums of q̂, as identities of polynomials. diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Pruned.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Pruned.lean index 30043faf4..e581c0e20 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Pruned.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Pruned.lean @@ -82,28 +82,12 @@ def PrunedCall.under (lam : Term) (c : PrunedCall) : PrunedCall where vertex := ⟨c.vertex.1 + 1, (Fin.cons lam c.vertex.2 : Vertex (c.vertex.1 + 1))⟩ passed := c.passed -/-- Section 2.4.2: Pruned_{τ₁⋯τ_k}(S), for a set S of output strings of length L - k, returns the -array returned by the vertex τ₁⋯τ_k of Full.result, restricted to the strings in S: (1) If k = L, -then look up Φ_τ(a) and Ψ_τ(b) from the precomputed encodings (Section 2.4.1) and return Φ_τ(a) -Ψ_τ(b) for the leaf τ = τ₁⋯τ_L. (2) For each term λ of Schönhage's identity, let S_λ be the union -of the slices S_z over the z to which λ contributes, i.e., S_{P_ij} := S_{z_ij} ∪ S_{z₀} and -S_{P₀} := S_{z₀}. (3) For each term λ of Schönhage's identity with S_λ ≠ ∅, compute C_λ := -Pruned_{τ₁⋯τ_k λ}(S_λ), an array on S_λ. (4) Return the array c on S whose slices are c_{z_ij} := -C_{P_ij} and c_{z₀} := ∑_λ C_λ, restricted to S_{z_ij} and to S_{z₀}. - -`Pruned.run` is this recursion, and it also keeps a record of the calls. Its first component is -the array that is returned. Its second component lists all the calls that are made, this one -included, each with the set passed to it. Vertices are named relative to the call we start from, -so for the call at the root they are the vertices of the paper. - -Here `n = L - k`. The vertex `τ₁⋯τ_k` enters only through the look-ups of step (1), so it is -passed as the parts of the two encodings that lie below it: `encA τ'` is the entry of the encoding -of `a` at the leaf `τ₁⋯τ_k τ'`, and likewise `encB`. At the root these are the two encodings -themselves, and the child `λ` receives their slices at `λ`. For a term `λ` with `S_λ = ∅` no call -is made: nothing is recorded, and `C_λ` is written as the zero array; step (4) does not read it, -because then `S_{z₀}`, and `S_{z_ij}` if `λ = P_ij`, are empty. In step (1) the number is -returned as an array on `S`, like every other result; `S` then consists of the empty string, -except when `Pruned.result` is called at the root with `L = 0` and `S = ∅`. +/-- Evaluate requested outputs from the two supplied leaf-encoding arrays. For each term, form its +child request set and recurse only when that set is nonempty. Decode the child values and set +unrequested outputs to zero. The second component records recursive calls and their requested sets +as a multiset. Each child receives the corresponding slices of the encoding arrays. The root call +is always recorded, including at depth zero with an empty request set. These are mathematical +values and call records, without a native execution cost. -/ def Pruned.run : (n : ℕ) → (Leaf n → ℤ) → (Leaf n → ℤ) → Finset (OutStr n) → (OutStr n → ℤ) × Multiset PrunedCall @@ -151,12 +135,9 @@ def privateLeaf {L : ℕ} (w : OutStr L) : Leaf L := fun ℓ => (w ℓ).privateT /-! ### Section 2.4.1: the encodings -/ -/-- Section 2.4.1, the procedure that computes the encoding of `a`: we run Full.result with b left -out and without step (4): […] the left number at the leaf τ is Φ_τ(a), and the leaf stores it. -What is left of `Full.result` is step (2) for `a`, the recursive calls of step (3), and at depth -`L` the number that has been reached. The encoding of b […] is computed in the same way, so the -procedure is written once, for the coefficients `c` of any family of linear forms: `c = φ` for -`a`, and `c = ψ` for `b`. +/-- Recursively apply the coefficient family `c` along a term string. Each level replaces the +input array by a weighted sum of its first-letter slices; depth zero returns its unique entry. +Choosing `phi` or `psi` computes the corresponding shared input encoding. -/ def computeEncoding {α : Type} [Fintype α] (c : Term → α → ℤ) : (L : ℕ) → ((Fin L → α) → ℤ) → Leaf L → ℤ @@ -289,8 +270,8 @@ theorem Pruned.result_eq_piecewise_sum {n : ℕ} (encA encB : Leaf n → ℤ) (S rw [hC, Finset.piecewise_eq_of_mem _ _ _ hmem] · rfl -/-- **Lemma 10**, first claim. Let a and b be input arrays, and let U be a set of output strings -of length L. Called at the root, Pruned.result(U) returns bilinearProduct(a, b) restricted to U. +/-- Pruning the two input encodings yields `bilinearProduct` on the requested strings and zero +elsewhere. -/ theorem Pruned.result_eq_piecewise_bilinearProduct {L : ℕ} (a : LeftStr L → ℤ) (b : RightStr L → ℤ) (U : Finset (OutStr L)) : @@ -298,10 +279,8 @@ theorem Pruned.result_eq_piecewise_bilinearProduct {L : ℕ} (a : LeftStr L → -- Sum the products of the encoded numbers over the contributing leaves. Pruned.result_eq_piecewise_sum _ _ U -/-- Section 2.4.2, the specification of `Pruned.result`: it returns the array returned by the -vertex τ₁⋯τ_k of Full.result, restricted to the strings in S. Here `a` and `b` are the input -arrays of that vertex: by `encodeLeft_cons` and `encodeRight_cons`, applied once for each level, -the parts of the two encodings below a vertex are the encodings of its input arrays. +/-- Pruning precomputed input encodings agrees with full evaluation at every requested string and +is zero elsewhere. -/ theorem Pruned.result_eq_piecewise_full {n : ℕ} (a : LeftStr n → ℤ) (b : RightStr n → ℤ) (S : Finset (OutStr n)) : @@ -717,8 +696,8 @@ theorem Pruned.sum_card_passed_le {L : ℕ} (encA encB : Leaf L → ℤ) (U : Fi · exact ih _ _ _ _ · simp [sizeAt] -/-- **Lemma 10**, third claim. the sets passed to the calls it makes have total size at most -(L+1) |leaves(U)|. +/-- The sum of the cardinalities of all passed request sets is at most `(L + 1) * (leaves +U).card`. -/ theorem Pruned.totalSize_le {L : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (OutStr L)) : Pruned.totalSize encA encB U ≤ (L + 1) * (leaves U).card := by @@ -730,15 +709,10 @@ theorem Pruned.totalSize_le {L : ℕ} (encA encB : Leaf L → ℤ) (U : Finset ( sum_le_sum fun k _ => Pruned.sum_card_passed_le _ _ U k _ = (L + 1) * (leaves U).card := by simp -/-- **Lemma 10**, its three claims together. Let a and b be input arrays, and let U be a set of -output strings of length L. Called at the root, Pruned.result(U) returns bilinearProduct(a, b) -restricted to U. The leaves it visits are exactly those of leaves(U), and the sets passed to the -calls it makes have total size at most (L+1) |leaves(U)|. - -NOTE. The hypothesis `0 < L ∨ U.Nonempty` of the second claim is not in the paper. It excludes -only the case `L = 0`, `U = ∅`, in which the root is itself a leaf, so that the call at the root -visits it, while `leaves(∅)` is empty. In the paper `L ≥ m ≥ 1`, and `Pruned.result` is run only -on nonempty sets (Section 2.4.4). +/-- Combine value correctness, the visited-leaf characterization and the passed-set cardinality +bound for the two input encodings. Values and the set-size bound apply at every depth and request +set. The leaf equivalence excludes the empty zero-depth root; +`Pruned.visits_iff_root_or_mem_leaves` gives the unconditional alternative for that case. -/ theorem pruned_spec {L : ℕ} (a : LeftStr L → ℤ) (b : RightStr L → ℤ) (U : Finset (OutStr L)) : Pruned.result L (encodingL a) (encodingR b) U = U.piecewise (bilinearProduct a b) 0 From 19ee9423823818c8a69e038755ae99648c9a30dc Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 07:17:03 -0400 Subject: [PATCH 12/73] =?UTF-8?q?feat(wordram):=20count=20Chan=E2=80=93He?= =?UTF-8?q?=20collisions=20with=20native=20histograms?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement fixed native residue, histogram and composed collision-counting programs with explicit frames, local word-fit contracts, exact mathematical collision semantics and charged bound 34n + 5M + 18 (halt excluded). Implementation layer for #1454 under #1446/#1394, not full preprocessing. Sorting/frontend, prime searches, heavy extraction, recursion/output and global logarithmic-width/subquadratic-time composition remain open. Curated abstracts: no impact on headline statements, definitions or scope. Validated on base 8e182b091199b323c7e6271acebbfcee2e4cdd18 plus this exact diff: LEAN_NUM_THREADS=1 COMPLEXITYLIB_CACHE_AUTO=0 scripts/check.sh: all six gates passed, including strict test/global axiom audit. Changed-file lints against that base, hygiene --check-simp and git diff --check passed. Final run followed resolution of shared-host disk/memory pressure; failed earlier attempts are not counted as validation. Independent read-only agent correctness/API review by /root/review_native_histogram approved the final source blobs after replacing three private clear helpers with WordRAM.clear. No remaining blockers; optional Count parameter-frame API can accompany its minimum-search client. This is within-fleet agent review, not outside-coordinator or human approval. Outside coordinator review is required before integration. AI assistance: OpenAI Codex implementation/self-review and a separate Codex review context. Native programs/counting proofs are original; existing Anthropic mathematical source credit is preserved unchanged. --- Complexity.lean | 4 + Complexity/Algorithms/WordRAM/Histogram.lean | 402 ++++++++++++++++++ .../WordRAM/ThreeSum/ChanHe/Collisions.lean | 77 ++++ .../WordRAM/ThreeSum/ChanHe/Count.lean | 206 +++++++++ .../WordRAM/ThreeSum/ChanHe/Residues.lean | 211 +++++++++ ComplexityTests/ThreeSumHistogram.lean | 149 +++++++ 6 files changed, 1049 insertions(+) create mode 100644 Complexity/Algorithms/WordRAM/Histogram.lean create mode 100644 Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Collisions.lean create mode 100644 Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Count.lean create mode 100644 Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Residues.lean create mode 100644 ComplexityTests/ThreeSumHistogram.lean diff --git a/Complexity.lean b/Complexity.lean index e6ac94554..10dee5c44 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -88,6 +88,7 @@ public import Complexity.Algorithms.WordRAM.FusionTree.Query.Traversal public import Complexity.Algorithms.WordRAM.FusionTree.Rank public import Complexity.Algorithms.WordRAM.FusionTree.ReadHeader public import Complexity.Algorithms.WordRAM.FusionTree.Static +public import Complexity.Algorithms.WordRAM.Histogram public import Complexity.Algorithms.WordRAM.PrefixSums.Dynamic public import Complexity.Algorithms.WordRAM.PrefixSums.Query public import Complexity.Algorithms.WordRAM.PrefixSums.Tree @@ -100,6 +101,9 @@ public import Complexity.Algorithms.WordRAM.Quicksort public import Complexity.Algorithms.WordRAM.Stack public import Complexity.Algorithms.WordRAM.Stack.Controller public import Complexity.Algorithms.WordRAM.ThreeSum.Cell +public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.Collisions +public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.Count +public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.Residues public import Complexity.Algorithms.WordRAM.ThreeSum.Fill public import Complexity.Algorithms.WordRAM.ThreeSum.Memory public import Complexity.Algorithms.WordRAM.ThreeSum.Weight diff --git a/Complexity/Algorithms/WordRAM/Histogram.lean b/Complexity/Algorithms/WordRAM/Histogram.lean new file mode 100644 index 000000000..da7669c78 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/Histogram.lean @@ -0,0 +1,402 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Copy +public import Mathlib.Algebra.BigOperators.Group.Finset.Basic +public import Mathlib.Data.Finset.Prod +import Mathlib.Tactic.Linarith +import Mathlib.Tactic.NormNum + +/-! +# Charged word-RAM histograms + +A fixed program clears a histogram and inserts the keys of a disjoint input array. +An insertion into a bucket of size `c` creates `2c` ordered collisions. Every address +calculation, load, increment, store and loop test is charged. + +## Main definitions + +* `Histogram.count`: bucket sizes in an input prefix. +* `Histogram.pairs`: ordered collisions accumulated by prefix insertion. +* `Histogram.program`: clear the buckets, then insert the input keys. + +## Main results + +* `Histogram.exec`: histogram construction and collision counting with a linear instruction bound. + +## References + +* [T. M. Chan and Q. He, *Reducing 3SUM to Convolution-3SUM*][ChanHe2020] +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.Histogram + +open Cslib.WordRAM +open Finset + +/-- Number of occurrences of a key among the first `k` input positions. -/ +def count (keys : ℕ → ℕ) (k b : ℕ) : ℕ := #{i ∈ range k | keys i = b} + +@[simp] theorem count_zero (keys : ℕ → ℕ) (b : ℕ) : count keys 0 b = 0 := by + simp [count] + +/-- Inserting one key changes just its bucket. -/ +theorem count_succ (keys : ℕ → ℕ) (k b : ℕ) : + count keys (k + 1) b = count keys k b + if keys k = b then 1 else 0 := by + by_cases h : keys k = b <;> simp [count, range_add_one, filter_insert, h] + +/-- A bucket cannot contain more positions than the prefix. -/ +theorem count_le (keys : ℕ → ℕ) (k b : ℕ) : count keys k b ≤ k := + (card_filter_le _ _).trans_eq (card_range k) + +/-- Ordered collisions accumulated while inserting successive keys. -/ +def pairs (keys : ℕ → ℕ) : ℕ → ℕ + | 0 => 0 + | k + 1 => pairs keys k + 2 * count keys k (keys k) + +/-- The prefix collision counter is at most the square of the prefix length. -/ +theorem pairs_le_sq (keys : ℕ → ℕ) (k : ℕ) : pairs keys k ≤ k * k := by + induction k with + | zero => simp [pairs] + | succ k ih => + have := count_le keys k (keys k) + dsimp only [pairs] + nlinarith + +/-- The insertion counter counts exactly the ordered pairs of different positions +with the same key, including when equal keys occur at different input positions. -/ +theorem pairs_eq_card (keys : ℕ → ℕ) (k : ℕ) : + pairs keys k = #{p ∈ (range k).offDiag | keys p.1 = keys p.2} := by + induction k with + | zero => simp [pairs] + | succ k ih => + let S := range k + let A := {p ∈ S.offDiag | keys p.1 = keys p.2} + let B := {p ∈ {k} ×ˢ S | keys p.1 = keys p.2} + let C := {p ∈ S ×ˢ {k} | keys p.1 = keys p.2} + have hrow : B = {k} ×ˢ {i ∈ S | keys i = keys k} := by + ext p + simp only [B, mem_filter, mem_product, mem_singleton] + aesop + have hcol : C = {i ∈ S | keys i = keys k} ×ˢ {k} := by + ext p + simp only [C, mem_filter, mem_product, mem_singleton] + aesop + have hAB : Disjoint A B := by + rw [disjoint_left] + intro p hp hq + have hp' := (mem_offDiag.mp (mem_filter.mp hp).1).1 + have hq' := mem_singleton.mp (mem_product.mp (mem_filter.mp hq).1).1 + have : p.1 < k := mem_range.mp hp' + omega + have hAC : Disjoint (A ∪ B) C := by + rw [disjoint_left] + intro p hp hq + have hq' := mem_singleton.mp (mem_product.mp (mem_filter.mp hq).1).2 + rcases mem_union.mp hp with hp | hp + · have hp' := (mem_offDiag.mp (mem_filter.mp hp).1).2.1 + have : p.2 < k := mem_range.mp hp' + omega + · have hp' := (mem_product.mp (mem_filter.mp hp).1).2 + have : p.2 < k := mem_range.mp hp' + omega + have hsplit : {p ∈ (range (k + 1)).offDiag | keys p.1 = keys p.2} = A ∪ B ∪ C := by + rw [range_add_one, offDiag_insert (notMem_range_self : k ∉ range k), + filter_union, filter_union] + rw [hsplit, card_union_of_disjoint hAC, card_union_of_disjoint hAB, hrow, hcol, + card_product, card_product, card_singleton, one_mul, mul_one] + change pairs keys k + 2 * count keys k (keys k) = + #{p ∈ (range k).offDiag | keys p.1 = keys p.2} + count keys k (keys k) + + count keys k (keys k) + rw [ih] + omega + +/-- The original memory, with the histogram block replaced by prefix bucket counts. -/ +def memory (mem : ℕ → ℕ) (keys : ℕ → ℕ) (dst bins k : ℕ) : ℕ → ℕ := + fun a => if dst ≤ a ∧ a < dst + bins then count keys k (a - dst) else mem a + +/-- Reading a histogram bucket. -/ +theorem memory_read (mem : ℕ → ℕ) (keys : ℕ → ℕ) (dst bins k b : ℕ) + (hb : b < bins) : memory mem keys dst bins k (dst + b) = count keys k b := by + simp [memory, hb] + +/-- Histogram writes preserve all other cells. -/ +theorem memory_outside (mem : ℕ → ℕ) (keys : ℕ → ℕ) (dst bins k a : ℕ) + (ha : ¬(dst ≤ a ∧ a < dst + bins)) : memory mem keys dst bins k a = mem a := by + simp [memory, ha] + +/-- One insertion is a single histogram-cell update. -/ +theorem memory_succ (mem : ℕ → ℕ) (keys : ℕ → ℕ) (dst bins k : ℕ) + (hk : keys k < bins) : + memory mem keys dst bins (k + 1) = Function.update (memory mem keys dst bins k) + (dst + keys k) (count keys k (keys k) + 1) := by + funext a + by_cases ha : a = dst + keys k + · subst a + simp [memory_read, hk, count_succ] + · rw [Function.update_of_ne ha] + simp only [memory] + split_ifs with h + · have hne : keys k ≠ a - dst := by omega + simp [count_succ, hne] + · rfl + +/-- Input base. -/ +abbrev src : Fin 11 := 0 +/-- Histogram base. -/ +abbrev dst : Fin 11 := 1 +/-- Number of input keys. -/ +abbrev len : Fin 11 := 2 +/-- Number of buckets. -/ +abbrev bins : Fin 11 := 3 +/-- Prefix length. -/ +abbrev idx : Fin 11 := 4 +/-- Temporary address. -/ +abbrev addr : Fin 11 := 5 +/-- Current key. -/ +abbrev key : Fin 11 := 6 +/-- Current bucket count. -/ +abbrev bucketSize : Fin 11 := 7 +/-- Number of ordered collisions. -/ +abbrev total : Fin 11 := 8 +/-- Constant one. -/ +abbrev one : Fin 11 := 9 +/-- Constant zero. -/ +abbrev zero : Fin 11 := 10 + +/-- Clear one histogram cell and advance the cursor. -/ +def clearBody : Code .standard 11 := ram[ + addr ← dst + idx; *addr ← zero; idx ← idx + one ] + +/-- Insert one key and add twice its old bucket count. -/ +def body : Code .standard 11 := ram[ + addr ← src + idx; key ← *addr; addr ← dst + key; + bucketSize ← *addr; total ← total + bucketSize; total ← total + bucketSize; + bucketSize ← bucketSize + one; *addr ← bucketSize; idx ← idx + one ] + +/-- Clear the histogram, retaining the input and all four parameters. -/ +def clear : Code .standard 11 := + .seq (ram[ idx ← #0; one ← #1; zero ← #0 ]) (.loop (.lt idx bins) clearBody) + +/-- Insert all keys into a cleared histogram. -/ +def insert : Code .standard 11 := + .seq (ram[ idx ← #0; total ← #0 ]) (.loop (.lt idx len) body) + +/-- Fixed code, with all bases and lengths supplied in registers. -/ +def program : Code .standard 11 := .seq clear insert + +private theorem exec_clear {w : ℕ} (x : Data 11) (h1 : Fits w 1) + (hd : Fits w (x.regs dst + x.regs bins)) : + ∃ y, ExecWithin w clear x y (5 * x.regs bins + 5) ∧ + y.mem = Cslib.WordRAM.clear x.mem (x.regs dst) (x.regs bins) ∧ + (∀ i, i.val < 4 → y.regs i = x.regs i) ∧ y.regs one = 1 := by + let I (y : Data 11) : Prop := + (∀ i, i.val < 4 → y.regs i = x.regs i) ∧ y.regs one = 1 ∧ y.regs zero = 0 ∧ + y.regs idx ≤ x.regs bins ∧ y.mem = Cslib.WordRAM.clear x.mem (x.regs dst) (y.regs idx) + have hbody (y : Data 11) (hy : I y) (hc : (Cond.lt idx bins).Holds y) : + ∃ z, ExecWithin w clearBody y z 3 ∧ I z ∧ + x.regs bins - z.regs idx < x.regs bins - y.regs idx := by + obtain ⟨hr, ho, hz, hi, hm⟩ := hy + have hb := hr bins (by decide) + have hdst := hr dst (by decide) + have hlt : y.regs idx < x.regs bins := by simpa only [Cond.holds_lt, hb] using hc + have ha : Fits w (y.regs dst + y.regs idx) := hd.of_le (by rw [hdst]; omega) + have hn : Fits w (y.regs idx + 1) := hd.of_le (by omega) + refine ⟨?z, ?run, ?inv, ?decrease⟩ + case run => ram_eval [clearBody, ha, hn, ho] + case inv => + refine ⟨?_, ?_, ?_, ?_, ?_⟩ + · intro i hi + have hia : i ≠ addr := by intro h; subst i; norm_num [addr] at hi + have hii : i ≠ idx := by intro h; subst i; norm_num [idx] at hi + simpa only [Data.set_regs, Data.write_regs, Function.update_of_ne hia, + Function.update_of_ne hii] using hr i hi + · simpa +decide [Function.update_apply] using ho + · simpa +decide [Function.update_apply] using hz + · simpa [ho] using Nat.succ_le_of_lt hlt + · simp only [Data.set_mem, Data.write_mem, Data.set_regs, Function.update_apply] + rw [hz, hm, hdst, ← Cslib.WordRAM.clear_succ] + rfl + case decrease => simp only [Data.set_regs, Function.update_self]; omega + let x₀ := ((x.set idx 0).set one 1).set zero 0 + have hx₀ : I x₀ := by + refine ⟨?_, ?_, ?_, ?_, ?_⟩ + · intro i hi + have hiz : i ≠ zero := by intro h; subst i; norm_num [zero] at hi + have hio : i ≠ one := by intro h; subst i; norm_num [one] at hi + have hii : i ≠ idx := by intro h; subst i; norm_num [idx] at hi + simp only [x₀, Data.set_regs, Function.update_of_ne hiz, Function.update_of_ne hio, + Function.update_of_ne hii] + · simp +decide [x₀, one, zero, idx] + · simp [x₀] + · simp +decide [x₀, one, zero, idx] + · simp +decide [x₀, one, zero, idx, Cslib.WordRAM.clear_zero] + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I + (fun y => x.regs bins - y.regs idx) hbody hx₀ + obtain ⟨hr, ho, hz, hi, hm⟩ := hy + have hb := hr bins (by decide) + have hend : y.regs idx = x.regs bins := by + simp only [Cond.holds_lt, hb] at hc + omega + refine ⟨y, ?_, by simpa only [hend] using hm, hr, ho⟩ + have hinit : ExecWithin (B := .standard) w (ram[ idx ← #0; one ← #1; zero ← #0 ]) x x₀ 3 := by + ram_eval [x₀, h1] + convert hinit.seq he using 1 + · rfl + · simp +decide only [x₀, Data.set_regs, Function.update_apply, idx, one, zero, ↓reduceIte, + Nat.sub_zero] + omega + +private theorem exec_insert {w : ℕ} (x : Data 11) (keys : ℕ → ℕ) + (hs : Fits w (x.regs src + x.regs len)) + (hd : Fits w (x.regs dst + x.regs bins)) + (ht : Fits w (x.regs len * x.regs len + x.regs len + 1)) + (hdisj : x.regs src + x.regs len ≤ x.regs dst) + (hkeys : ∀ i < x.regs len, keys i < x.regs bins) + (hinput : ∀ i < x.regs len, x.mem (x.regs src + i) = keys i) + (hzero : ∀ b < x.regs bins, x.mem (x.regs dst + b) = 0) + (ho : x.regs one = 1) : + ∃ y, ExecWithin w insert x y (11 * x.regs len + 4) ∧ + y.mem = memory x.mem keys (x.regs dst) (x.regs bins) (x.regs len) ∧ + y.regs total = pairs keys (x.regs len) ∧ + (∀ i, i.val < 4 → y.regs i = x.regs i) ∧ y.regs one = 1 := by + let I (y : Data 11) : Prop := + (∀ i, i.val < 4 → y.regs i = x.regs i) ∧ y.regs one = 1 ∧ + y.regs idx ≤ x.regs len ∧ y.regs total = pairs keys (y.regs idx) ∧ + y.mem = memory x.mem keys (x.regs dst) (x.regs bins) (y.regs idx) + have hbody (y : Data 11) (hy : I y) (hc : (Cond.lt idx len).Holds y) : + ∃ z, ExecWithin w body y z 9 ∧ I z ∧ + x.regs len - z.regs idx < x.regs len - y.regs idx := by + obtain ⟨hr, ho, hi, hp, hm⟩ := hy + have hl := hr len (by decide) + have hsrc := hr src (by decide) + have hdst := hr dst (by decide) + have hlt : y.regs idx < x.regs len := by simpa only [Cond.holds_lt, hl] using hc + have hkey := hkeys _ hlt + have hread : y.mem (y.regs src + y.regs idx) = keys (y.regs idx) := by + rw [hm, hsrc, memory_outside] + · exact hinput _ hlt + · omega + have hbucket : y.mem (y.regs dst + keys (y.regs idx)) = + count keys (y.regs idx) (keys (y.regs idx)) := by + rw [hm, hdst, memory_read _ _ _ _ _ _ hkey] + have ha : Fits w (y.regs src + y.regs idx) := hs.of_le (by rw [hsrc]; omega) + have hb : Fits w (y.regs dst + keys (y.regs idx)) := hd.of_le (by rw [hdst]; omega) + have hn : Fits w (y.regs idx + 1) := ht.of_le (by omega) + have hc := count_le keys (y.regs idx) (keys (y.regs idx)) + have hpnext := pairs_le_sq keys (y.regs idx + 1) + have hsquare : (y.regs idx + 1) * (y.regs idx + 1) ≤ x.regs len * x.regs len := + Nat.mul_self_le_mul_self (by omega) + have ht₂ : Fits w (pairs keys (y.regs idx) + count keys (y.regs idx) (keys (y.regs idx)) + + count keys (y.regs idx) (keys (y.regs idx))) := ht.of_le (by + dsimp only [pairs] at hpnext + omega) + have ht₁ : Fits w (pairs keys (y.regs idx) + count keys (y.regs idx) (keys (y.regs idx))) := + ht₂.of_le (by omega) + have hcount : Fits w (count keys (y.regs idx) (keys (y.regs idx)) + 1) := + ht.of_le (by omega) + refine ⟨?z, ?run, ?inv, ?decrease⟩ + case run => ram_eval [body, ha, hb, hn, ho, hread, hbucket, hp, ht₁, ht₂, hcount] + case inv => + refine ⟨?_, ?_, ?_, ?_, ?_⟩ + · intro i hi + have hia : i ≠ addr := by intro h; subst i; norm_num [addr] at hi + have hik : i ≠ key := by intro h; subst i; norm_num [key] at hi + have hic : i ≠ bucketSize := by intro h; subst i; norm_num [bucketSize] at hi + have hit : i ≠ total := by intro h; subst i; norm_num [total] at hi + have hii : i ≠ idx := by intro h; subst i; norm_num [idx] at hi + simpa only [Data.set_regs, Data.write_regs, Function.update_of_ne hia, + Function.update_of_ne hik, Function.update_of_ne hic, Function.update_of_ne hit, + Function.update_of_ne hii] using hr i hi + · simpa +decide [Function.update_apply] using ho + · simpa [ho] using Nat.succ_le_of_lt hlt + · change pairs keys (y.regs idx) + count keys (y.regs idx) (keys (y.regs idx)) + + count keys (y.regs idx) (keys (y.regs idx)) = pairs keys (y.regs idx + 1) + simp only [pairs, two_mul, Nat.add_assoc] + · change Function.update y.mem (y.regs dst + keys (y.regs idx)) + (count keys (y.regs idx) (keys (y.regs idx)) + 1) = + memory x.mem keys (x.regs dst) (x.regs bins) (y.regs idx + 1) + rw [hm, hdst, ← memory_succ _ _ _ _ _ hkey] + case decrease => simp only [Data.set_regs, Function.update_self]; omega + let x₀ := (x.set idx 0).set total 0 + have hx₀ : I x₀ := by + refine ⟨?_, ?_, ?_, ?_, ?_⟩ + · intro i hi + have hit : i ≠ total := by intro h; subst i; norm_num [total] at hi + have hii : i ≠ idx := by intro h; subst i; norm_num [idx] at hi + simp only [x₀, Data.set_regs, Function.update_of_ne hit, Function.update_of_ne hii] + · simpa +decide [x₀, one, total, idx] using ho + · simp +decide [x₀, total, idx] + · simp +decide [x₀, total, idx, pairs] + · change x.mem = memory x.mem keys (x.regs dst) (x.regs bins) 0 + funext a + by_cases h : x.regs dst ≤ a ∧ a < x.regs dst + x.regs bins + · have ha : x.regs dst + (a - x.regs dst) = a := by omega + simpa [memory, h, ha] using hzero (a - x.regs dst) (by omega) + · simp only [memory, h, ↓reduceIte] + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I + (fun y => x.regs len - y.regs idx) hbody hx₀ + obtain ⟨hr, ho, hi, hp, hm⟩ := hy + have hl := hr len (by decide) + have hend : y.regs idx = x.regs len := by + simp only [Cond.holds_lt, hl] at hc + omega + refine ⟨y, ?_, by simpa only [hend] using hm, by simpa only [hend] using hp, hr, ho⟩ + have hinit : ExecWithin (B := .standard) w (ram[ idx ← #0; total ← #0 ]) x x₀ 2 := by + ram_eval [x₀] + convert hinit.seq he using 1 + · rfl + · simp +decide only [x₀, Data.set_regs, Function.update_apply, idx, total, ↓reduceIte, + Nat.sub_zero] + omega + +/-- Build a histogram and count ordered collisions within `5M + 11n + 9` native instructions. +The input keys lie in `[0,M)`. The input precedes the output block. Both one-past-end +addresses and the quadratic counter bound must fit. These are explicit local contracts; +constructing the keys and deriving a logarithmic width are separate client obligations. -/ +theorem exec {w : ℕ} (x : Data 11) (keys : ℕ → ℕ) (h1 : Fits w 1) + (hs : Fits w (x.regs src + x.regs len)) + (hd : Fits w (x.regs dst + x.regs bins)) + (ht : Fits w (x.regs len * x.regs len + x.regs len + 1)) + (hdisj : x.regs src + x.regs len ≤ x.regs dst) + (hkeys : ∀ i < x.regs len, keys i < x.regs bins) + (hinput : ∀ i < x.regs len, x.mem (x.regs src + i) = keys i) : + ∃ y, ExecWithin w program x y (5 * x.regs bins + 11 * x.regs len + 9) ∧ + y.mem = memory x.mem keys (x.regs dst) (x.regs bins) (x.regs len) ∧ + y.regs total = pairs keys (x.regs len) ∧ + (∀ i, i.val < 4 → y.regs i = x.regs i) := by + obtain ⟨y, he, hm, hr, ho⟩ := exec_clear x h1 hd + have hsrc := hr src (by decide) + have hdst := hr dst (by decide) + have hlen := hr len (by decide) + have hbins := hr bins (by decide) + obtain ⟨z, hz, hzm, hzt, hzr, _⟩ := exec_insert y keys + (by simpa only [hsrc, hlen] using hs) (by simpa only [hdst, hbins] using hd) + (by simpa only [hlen] using ht) (by simpa only [hsrc, hlen, hdst] using hdisj) + (by simpa only [hlen, hbins] using hkeys) (by + intro i hi + rw [hsrc, hm] + have hlt : i < x.regs len := by simpa only [hlen] using hi + simp only [Cslib.WordRAM.clear, show ¬(x.regs dst ≤ x.regs src + i ∧ + x.regs src + i < x.regs dst + x.regs bins) from by omega, ↓reduceIte] + exact hinput i hlt) (by + intro b hb + rw [hm, hdst] + have hb' : b < x.regs bins := by simpa only [hbins] using hb + simp [Cslib.WordRAM.clear, hb']) ho + refine ⟨z, ?_, ?_, by simpa only [hlen] using hzt, fun i hi => (hzr i hi).trans (hr i hi)⟩ + · exact (he.seq hz).mono (by rw [hlen]; omega) + · rw [hzm, hm, hdst, hbins, hlen] + funext a + simp only [memory, Cslib.WordRAM.clear] + split_ifs <;> rfl + +end Cslib.Algorithms.WordRAM.Histogram diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Collisions.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Collisions.lean new file mode 100644 index 000000000..6b18f5850 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Collisions.lean @@ -0,0 +1,77 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.Histogram +public import Complexity.Algorithms.ThreeSum.ChanHe.Collisions + +/-! +# Histogram collision counts for Chan–He + +The native histogram counts ordered pairs of different input positions. For an injective +array of integers and its Euclidean residue keys, this is exactly the existing Chan–He +collision count. Injectivity is required because the mathematical reduction operates on sets. +Equal input values must first be handled by the distinct-position frontend. + +## Main results + +* `coll_eq_histogramPairs`: the existing `coll` is the counter computed by the histogram. + +## References + +* [T. M. Chan and Q. He, *Reducing 3SUM to Convolution-3SUM*][ChanHe2020] +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.ThreeSum.ChanHe + +open Finset +open Cslib.Algorithms.ThreeSum.ChanHe + +/-- For distinct array values, histogramming Euclidean residues computes exactly the +ordered distinct-element collision count of the existing Chan–He reduction. -/ +theorem coll_eq_histogramPairs (values : ℕ → ℤ) (n M : ℕ) (hM : 0 < M) + (hinj : Set.InjOn values (↑(range n) : Set ℕ)) : + coll ((range n).image values) M = + Histogram.pairs (fun i => (values i % (M : ℤ)).toNat) n := by + rw [Histogram.pairs_eq_card] + symm + refine card_bij (fun p _ => (values p.1, values p.2)) ?_ ?_ ?_ + · intro p hp + obtain ⟨hp, heq⟩ := mem_filter.mp hp + obtain ⟨hp₁, hp₂, hne⟩ := mem_offDiag.mp hp + refine mem_filter.mpr ⟨mem_offDiag.mpr ⟨mem_image.mpr ⟨p.1, hp₁, rfl⟩, + mem_image.mpr ⟨p.2, hp₂, rfl⟩, fun h => hne (hinj hp₁ hp₂ h)⟩, ?_⟩ + apply (natCast_dvd_sub_iff_emod_eq M _ _).mpr + change values p.1 % (M : ℤ) = values p.2 % (M : ℤ) + have hpos : (0 : ℤ) < M := by exact_mod_cast hM + have h₁ := Int.emod_nonneg (values p.1) (ne_of_gt hpos) + have h₂ := Int.emod_nonneg (values p.2) (ne_of_gt hpos) + omega + · intro p hp q hq heq + have hp' := mem_offDiag.mp (mem_filter.mp hp).1 + have hq' := mem_offDiag.mp (mem_filter.mp hq).1 + exact Prod.ext (hinj hp'.1 hq'.1 (congrArg Prod.fst heq)) + (hinj hp'.2.1 hq'.2.1 (congrArg Prod.snd heq)) + · intro p hp + obtain ⟨hp, hdvd⟩ := mem_filter.mp hp + obtain ⟨hp₁, hp₂, hne⟩ := mem_offDiag.mp hp + obtain ⟨i, hi, hival⟩ := mem_image.mp hp₁ + obtain ⟨j, hj, hjval⟩ := mem_image.mp hp₂ + refine ⟨(i, j), mem_filter.mpr ⟨mem_offDiag.mpr ⟨hi, hj, ?_⟩, ?_⟩, ?_⟩ + · intro hij + change i = j at hij + apply hne + rw [← hival, ← hjval, hij] + · have heq := (natCast_dvd_sub_iff_emod_eq M _ _).mp hdvd + change (values i % (M : ℤ)).toNat = (values j % (M : ℤ)).toNat + rw [hival, hjval, heq] + · exact Prod.ext hival hjval + +end Cslib.Algorithms.WordRAM.ThreeSum.ChanHe diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Count.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Count.lean new file mode 100644 index 000000000..677b2c215 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Count.lean @@ -0,0 +1,206 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.Collisions +public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.Residues + +/-! +# Native Chan–He collision counting + +This fixed native program first decodes signed Euclidean residue keys, then builds a +histogram and counts ordered collisions. Register setup between the two loops is charged. +For injective input arrays its counter is exactly the existing Chan–He `coll`. + +## Main definitions + +* `Count.program`: the composed native residue/histogram program. +* `Count.memory`: the complete output memory, including the residue scratch array. + +## Main results + +* `Count.exec`: execution in at most `34n + 5M + 18` native instructions. +* `Count.exec_coll`: the counter equals the existing mathematical collision count. + +## References + +* [T. M. Chan and Q. He, *Reducing 3SUM to Convolution-3SUM*][ChanHe2020] +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Count + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Residue routine registers occupy the first sixteen registers. -/ +def residuesRegisters (i : Fin 16) : Fin 28 := ⟨i.val, by omega⟩ + +/-- Histogram registers occupy registers sixteen through twenty-six. -/ +def histogramRegisters (i : Fin 11) : Fin 28 := ⟨16 + i.val, by omega⟩ + +private theorem residues_injective : Function.Injective residuesRegisters := by + intro i j h + apply Fin.ext + exact congrArg (fun i : Fin 28 => i.val) h + +private theorem histogram_injective : Function.Injective histogramRegisters := by + intro i j h + apply Fin.ext + have := congrArg Fin.val h + dsimp [histogramRegisters] at this + omega + +/-- Encoded integer input base. -/ +abbrev src : Fin 28 := 9 +/-- Residue scratch base. -/ +abbrev scratch : Fin 28 := 10 +/-- Number of input values. -/ +abbrev len : Fin 28 := 11 +/-- Encoded positive modulus. -/ +abbrev modulus : Fin 28 := 12 +/-- Histogram destination base. -/ +abbrev dst : Fin 28 := 27 +/-- Output collision count, an ordinary unsigned word. -/ +abbrev total : Fin 28 := 24 + +/-- Histogram input base register. -/ +abbrev hsrc : Fin 28 := histogramRegisters Histogram.src +/-- Histogram output base register. -/ +abbrev hdst : Fin 28 := histogramRegisters Histogram.dst +/-- Histogram length register. -/ +abbrev hlen : Fin 28 := histogramRegisters Histogram.len +/-- Histogram modulus register. -/ +abbrev hbins : Fin 28 := histogramRegisters Histogram.bins +/-- Histogram constant register, also used to decode the encoded modulus. -/ +abbrev hone : Fin 28 := histogramRegisters Histogram.one + +/-- Set up the histogram parameters, explicitly decoding the positive modulus. -/ +def prepare : Code .standard 28 := ram[ + hsrc ← scratch; hdst ← dst; hlen ← len; hone ← #2; hbins ← modulus / hone ] + +/-- A single finite native program, independent of input length, modulus and word width. -/ +def program : Code .standard 28 := + .seq (Residues.program.rename residuesRegisters) + (.seq prepare (Histogram.program.rename histogramRegisters)) + +/-- The residue scratch array and the complete histogram, framed by the original memory. -/ +def memory (mem : ℕ → ℕ) (values : ℕ → ℤ) (n M scratch dst : ℕ) : ℕ → ℕ := + Histogram.memory (Residues.memory mem values M scratch n) + (fun i => (values i % (M : ℤ)).toNat) dst M n + +/-- Complete native collision counting, including residue computation, clearing the histogram +and register setup. Input, scratch and histogram blocks occur in that order and are disjoint. +The final halt is excluded. Fit hypotheses are local; a caller must derive them from its +input and width contract. -/ +theorem exec {w M : ℕ} (x : Data 28) (values : ℕ → ℤ) (hM : 0 < M) + (hmod : GenericRAM.Signed.Fits w (M : ℤ)) + (hmodreg : x.regs modulus = encode (M : ℤ)) + (hfit : ∀ i < x.regs len, GenericRAM.Signed.Fits w (values i)) + (hs : Fits (w + 2) (x.regs src + x.regs len)) + (hk : Fits (w + 2) (x.regs scratch + x.regs len)) + (hd : Fits (w + 2) (x.regs dst + M)) + (ht : Fits (w + 2) (x.regs len * x.regs len + x.regs len + 1)) + (hsk : x.regs src + x.regs len ≤ x.regs scratch) + (hkd : x.regs scratch + x.regs len ≤ x.regs dst) + (hinput : ∀ i < x.regs len, x.mem (x.regs src + i) = encode (values i)) : + ∃ y, ExecWithin (w + 2) program x y (34 * x.regs len + 5 * M + 18) ∧ + y.mem = memory x.mem values (x.regs len) M (x.regs scratch) (x.regs dst) ∧ + y.regs total = Histogram.pairs (fun i => (values i % (M : ℤ)).toNat) (x.regs len) := by + obtain ⟨a, ha, ham, har⟩ := Residues.exec (x.comap residuesRegisters) values hM hmod + hmodreg hfit hs hk hsk hinput + let y := a.extend residuesRegisters x + have hy : ExecWithin (w + 2) (Residues.program.rename residuesRegisters) x y + (23 * x.regs len + 4) := ha.rename residues_injective + have hyr (i : Fin 16) : y.regs (residuesRegisters i) = a.regs i := + residues_injective.extend_apply _ _ i + have hys : y.regs scratch = x.regs scratch := + (hyr Residues.dst).trans (har Residues.dst (by decide)) + have hyl : y.regs len = x.regs len := (hyr Residues.len).trans (har Residues.len (by decide)) + have hym : y.regs modulus = encode (M : ℤ) := + (hyr Residues.modulus).trans ((har Residues.modulus (by decide)).trans hmodreg) + have hyd : y.regs dst = x.regs dst := by + change Function.extend residuesRegisters a.regs x.regs dst = x.regs dst + apply Function.extend_apply' + rintro ⟨i, hi⟩ + have := congrArg Fin.val hi + dsimp [residuesRegisters, dst] at this + omega + let q := ((((y.set (histogramRegisters Histogram.src) (x.regs scratch)).set + (histogramRegisters Histogram.dst) (x.regs dst)).set + (histogramRegisters Histogram.len) (x.regs len)).set + (histogramRegisters Histogram.one) 2).set (histogramRegisters Histogram.bins) M + have htwo : Fits (w + 2) 2 := (show Fits 2 2 from by decide).mono (by omega) + have hraw : Fits (w + 2) M := + ((encode_fits hmod).mono (by omega)).of_le (by + simp only [encode, Int.natAbs_natCast] + omega) + have hprep : ExecWithin (w + 2) prepare y q 5 := by + ram_eval [prepare, q, hys, hyl, hym, hyd, htwo, hraw, histogramRegisters, + Histogram.src, Histogram.dst, Histogram.len, Histogram.one, Histogram.bins, + hsrc, hdst, hlen, hbins, hone, scratch, len, modulus, dst, Fin.mk.injEq, + encode_div_two, Int.natAbs_natCast] + have hqs : (q.comap histogramRegisters).regs Histogram.src = x.regs scratch := by + simp +decide [q, histogramRegisters, Histogram.src, Histogram.dst, Histogram.len, + Histogram.one, Histogram.bins] + have hqd : (q.comap histogramRegisters).regs Histogram.dst = x.regs dst := by + simp +decide [q, histogramRegisters, Histogram.src, Histogram.dst, Histogram.len, + Histogram.one, Histogram.bins] + have hql : (q.comap histogramRegisters).regs Histogram.len = x.regs len := by + simp +decide [q, histogramRegisters, Histogram.src, Histogram.dst, Histogram.len, + Histogram.one, Histogram.bins] + have hqb : (q.comap histogramRegisters).regs Histogram.bins = M := by + simp [q] + have hqm : q.mem = Residues.memory x.mem values M (x.regs scratch) (x.regs len) := ham + have h1 : Fits (w + 2) 1 := (show Fits 2 1 from by decide).mono (by omega) + obtain ⟨b, hb, hbm, hbt, _⟩ := Histogram.exec (q.comap histogramRegisters) + (fun i => (values i % (M : ℤ)).toNat) h1 + (by simpa only [hqs, hql] using hk) (by simpa only [hqd, hqb] using hd) + (by simpa only [hql] using ht) (by simpa only [hqs, hql, hqd] using hkd) + (by + intro i hi + rw [hqb] + have hnonneg := Int.emod_nonneg (values i) (show (M : ℤ) ≠ 0 by omega) + have hlt := Int.emod_lt_of_pos (values i) (show (0 : ℤ) < M by omega) + omega) + (by + intro i hi + have hi' : i < x.regs len := by simpa only [hql] using hi + rw [Data.comap_mem, hqm, hqs] + simp [Residues.memory, hi']) + refine ⟨b.extend histogramRegisters q, ?_, ?_, ?_⟩ + · exact (hy.seq (hprep.seq (hb.rename histogram_injective))).mono (by rw [hql, hqb]; omega) + · change b.mem = _ + rw [hbm, Data.comap_mem, hqm, hqd, hqb, hql] + rfl + · change Function.extend histogramRegisters b.regs q.regs + (histogramRegisters Histogram.total) = _ + rw [histogram_injective.extend_apply, hbt, hql] + +/-- For an injective integer array, the composed program returns exactly the collision +count used by the existing Chan–He prime searches. -/ +theorem exec_coll {w M : ℕ} (x : Data 28) (values : ℕ → ℤ) (hM : 0 < M) + (hmod : GenericRAM.Signed.Fits w (M : ℤ)) + (hmodreg : x.regs modulus = encode (M : ℤ)) + (hfit : ∀ i < x.regs len, GenericRAM.Signed.Fits w (values i)) + (hs : Fits (w + 2) (x.regs src + x.regs len)) + (hk : Fits (w + 2) (x.regs scratch + x.regs len)) + (hd : Fits (w + 2) (x.regs dst + M)) + (ht : Fits (w + 2) (x.regs len * x.regs len + x.regs len + 1)) + (hsk : x.regs src + x.regs len ≤ x.regs scratch) + (hkd : x.regs scratch + x.regs len ≤ x.regs dst) + (hinput : ∀ i < x.regs len, x.mem (x.regs src + i) = encode (values i)) + (hinj : Set.InjOn values (↑(Finset.range (x.regs len)) : Set ℕ)) : + ∃ y, ExecWithin (w + 2) program x y (34 * x.regs len + 5 * M + 18) ∧ + y.mem = memory x.mem values (x.regs len) M (x.regs scratch) (x.regs dst) ∧ + y.regs total = Cslib.Algorithms.ThreeSum.ChanHe.coll + ((Finset.range (x.regs len)).image values) M := by + obtain ⟨y, hy, hm, ht⟩ := exec x values hM hmod hmodreg hfit hs hk hd ht hsk hkd hinput + exact ⟨y, hy, hm, ht.trans (coll_eq_histogramPairs values _ M hM hinj).symm⟩ + +end Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Count diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Residues.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Residues.lean new file mode 100644 index 000000000..4cdbd3eef --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Residues.lean @@ -0,0 +1,211 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Signed.Arithmetic +public import Complexity.Computability.WordRAM.Copy +import Mathlib.Tactic.NormNum + +/-! +# Native Euclidean residue arrays + +The input holds signed encodings. The output holds ordinary unsigned residue keys, ready +for histogram construction. In particular a nonnegative encoded integer is explicitly decoded +by division by two before it is used as a histogram index. + +## Main definitions + +* `Residues.program`: a fixed loop producing Euclidean residue keys. + +## Main results + +* `Residues.exec`: charged execution preserving the input and all cells outside the output. + +## References + +* [T. M. Chan and Q. He, *Reducing 3SUM to Convolution-3SUM*][ChanHe2020] +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Residues + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Encoded integer input base. -/ +abbrev src : Fin 16 := 9 +/-- Unsigned residue output base. -/ +abbrev dst : Fin 16 := 10 +/-- Input length. -/ +abbrev len : Fin 16 := 11 +/-- Encoded modulus. -/ +abbrev modulus : Fin 16 := 12 +/-- Loop counter. -/ +abbrev idx : Fin 16 := 13 +/-- Address scratch. -/ +abbrev addr : Fin 16 := 14 +/-- Constant one. -/ +abbrev one : Fin 16 := 15 + +/-- The signed remainder block followed by explicit unsigned decoding and a store. -/ +def body : Code .standard 16 := + .seq (ram[ addr ← src + idx; left ← *addr; right ← modulus ]) + (.seq Signed.modulo (ram[ two ← #2; result ← result / two; + addr ← dst + idx; *addr ← result; idx ← idx + one ])) + +/-- All parameters are supplied at runtime; registers and code are independent +of length and width. -/ +def program : Code .standard 16 := + .seq (ram[ idx ← #0; one ← #1 ]) (.loop (.lt idx len) body) + +/-- Memory after writing a prefix of unsigned residue keys. -/ +def memory (mem : ℕ → ℕ) (values : ℕ → ℤ) (M dst k : ℕ) : ℕ → ℕ := + fun a => if dst ≤ a ∧ a < dst + k then (values (a - dst) % (M : ℤ)).toNat else mem a + +@[simp] theorem memory_zero (mem : ℕ → ℕ) (values : ℕ → ℤ) (M dst : ℕ) : + memory mem values M dst 0 = mem := by + funext a + simp [memory] + +private theorem memory_succ (mem : ℕ → ℕ) (values : ℕ → ℤ) (M dst k : ℕ) : + memory mem values M dst (k + 1) = Function.update (memory mem values M dst k) + (dst + k) (values k % (M : ℤ)).toNat := by + funext a + by_cases ha : a = dst + k + · subst a + simp [memory] + · rw [Function.update_of_ne ha] + simp only [memory] + have h : (dst ≤ a ∧ a < dst + (k + 1)) ↔ (dst ≤ a ∧ a < dst + k) := by omega + simp only [h] + +/-- Convert `n` encoded signed integers to unsigned Euclidean residue keys in at most +`23n + 4` native instructions. The modulus is positive and encoded in its parameter register; +addresses are ordinary unsigned words. The final halt is excluded. -/ +theorem exec {w M : ℕ} (x : Data 16) (values : ℕ → ℤ) (hM : 0 < M) + (hmod : GenericRAM.Signed.Fits w (M : ℤ)) + (hmodreg : x.regs modulus = encode (M : ℤ)) + (hfit : ∀ i < x.regs len, GenericRAM.Signed.Fits w (values i)) + (hs : Fits (w + 2) (x.regs src + x.regs len)) + (hd : Fits (w + 2) (x.regs dst + x.regs len)) + (hdisj : x.regs src + x.regs len ≤ x.regs dst) + (hinput : ∀ i < x.regs len, x.mem (x.regs src + i) = encode (values i)) : + ∃ y, ExecWithin (w + 2) program x y (23 * x.regs len + 4) ∧ + y.mem = memory x.mem values M (x.regs dst) (x.regs len) ∧ + (∀ i, 9 ≤ i.val ∧ i.val ≤ 12 → y.regs i = x.regs i) := by + let I (y : Data 16) : Prop := + (∀ i, 9 ≤ i.val ∧ i.val ≤ 12 → y.regs i = x.regs i) ∧ y.regs one = 1 ∧ + y.regs idx ≤ x.regs len ∧ + y.mem = memory x.mem values M (x.regs dst) (y.regs idx) + have hbody (y : Data 16) (hy : I y) (hc : (Cond.lt idx len).Holds y) : + ∃ z, ExecWithin (w + 2) body y z 21 ∧ I z ∧ + x.regs len - z.regs idx < x.regs len - y.regs idx := by + obtain ⟨hr, ho, hi, hm⟩ := hy + have hl := hr len (by decide) + have hsrc := hr src (by decide) + have hdst := hr dst (by decide) + have hmod' := hr modulus (by decide) + have hlt : y.regs idx < x.regs len := by simpa only [Cond.holds_lt, hl] using hc + have hread : y.mem (y.regs src + y.regs idx) = encode (values (y.regs idx)) := by + rw [hm, hsrc] + simp only [memory, show ¬(x.regs dst ≤ x.regs src + y.regs idx ∧ + x.regs src + y.regs idx < x.regs dst + y.regs idx) from by omega, ↓reduceIte] + exact hinput _ hlt + have ha : Fits (w + 2) (y.regs src + y.regs idx) := hs.of_le (by rw [hsrc]; omega) + let p := ((y.set addr (y.regs src + y.regs idx)).set left + (encode (values (y.regs idx)))).set right (encode (M : ℤ)) + have hp : ExecWithin (w + 2) + (ram[ addr ← src + idx; left ← *addr; right ← modulus ]) y p 3 := by + ram_eval [p, ha, hread, hmod', hmodreg, addr, src, idx, left, right, modulus, Fin.mk.injEq] + obtain ⟨z, hz, hzval, hzmem, hzreg⟩ := Signed.exec_modulo (hfit _ hlt) hmod p + (by simp +decide [p, left, right]) (by simp [p]) + have hzr (i : Fin 16) (hi : 9 ≤ i.val) : z.regs i = p.regs i := hzreg i (Or.inr hi) + have hzi : z.regs idx = y.regs idx := by + simpa +decide [p, idx, addr, left, right] using hzr idx (by decide) + have hzo : z.regs one = 1 := by + simpa +decide [p, one, addr, left, right] using (hzr one (by decide)).trans + (show p.regs one = 1 by simpa +decide [p, one, addr, left, right] using ho) + have hzd : z.regs dst = x.regs dst := by + simpa +decide [p, dst, addr, left, right] using (hzr dst (by decide)).trans + (show p.regs dst = x.regs dst by simpa +decide [p, dst, addr, left, right] using hdst) + have hnonneg := Int.emod_nonneg (values (y.regs idx)) (show (M : ℤ) ≠ 0 by omega) + have hdecode : z.regs result / 2 = (values (y.regs idx) % (M : ℤ)).toNat := by + rw [hzval, GenericRAM.Signed.Arithmetic.eval, encode_div_two] + omega + have hb : Fits (w + 2) (z.regs dst + z.regs idx) := hd.of_le (by rw [hzd, hzi]; omega) + have hn : Fits (w + 2) (z.regs idx + 1) := hs.of_le (by rw [hzi]; omega) + have htwo : Fits (w + 2) 2 := (show Fits 2 2 from by decide).mono (by omega) + have hrem : Fits (w + 2) (z.regs result / 2) := by + rw [hdecode] + have hlt' := Int.emod_lt_of_pos (values (y.regs idx)) (show (0 : ℤ) < M by omega) + exact ((encode_fits hmod).mono (by omega)).of_le (by + simp only [encode, Int.natAbs_natCast, show ¬(M : ℤ) < 0 from by omega, ↓reduceIte] + omega) + refine ⟨?q, ?run, ?inv, ?decrease⟩ + case run => + have htail : ExecWithin (w + 2) + (ram[ two ← #2; result ← result / two; addr ← dst + idx; + *addr ← result; idx ← idx + one ]) z ?q 5 := by + ram_eval [htwo, hrem, hb, hn, hzo, two, result, addr, dst, idx, one, Fin.mk.injEq] + exact (hp.seq (hz.seq htail)) + case inv => + refine ⟨?_, ?_, ?_, ?_⟩ + · intro i hi + have hip : p.regs i = y.regs i := by + have hia : i ≠ addr := by intro h; subst i; norm_num [addr] at hi + have hil : i ≠ left := by intro h; subst i; norm_num [left] at hi + have hir : i ≠ right := by intro h; subst i; norm_num [right] at hi + simp only [p, Data.set_regs, Function.update_of_ne hia, Function.update_of_ne hil, + Function.update_of_ne hir] + have hia : i ≠ addr := by intro h; subst i; norm_num [addr] at hi + have hii : i ≠ idx := by intro h; subst i; norm_num [idx] at hi + have hit : i ≠ two := by intro h; subst i; norm_num [two] at hi + have hir : i ≠ result := by intro h; subst i; norm_num [result] at hi + simpa only [Data.set_regs, Data.write_regs, Function.update_of_ne hia, + Function.update_of_ne hii, Function.update_of_ne hit, Function.update_of_ne hir] + using (hzr i hi.1).trans (hip.trans (hr i hi)) + · simpa +decide [Function.update_apply] using hzo + · simpa +decide [Function.update_apply, hzi, hzo] using Nat.succ_le_of_lt hlt + · simp only [Data.set_mem, Data.write_mem, Data.set_regs, Function.update_apply] + rw [hdecode, hzmem] + change Function.update y.mem (z.regs dst + z.regs idx) + (values (y.regs idx) % (M : ℤ)).toNat = _ + rw [hm, hzd, hzi, ← memory_succ] + rfl + case decrease => + change x.regs len - (z.regs idx + 1) < x.regs len - y.regs idx + rw [hzi] + omega + let x₀ := (x.set idx 0).set one 1 + have hx₀ : I x₀ := by + refine ⟨?_, ?_, ?_, ?_⟩ + · intro i hi + have hio : i ≠ one := by intro h; subst i; norm_num [one] at hi + have hii : i ≠ idx := by intro h; subst i; norm_num [idx] at hi + simp only [x₀, Data.set_regs, Function.update_of_ne hio, Function.update_of_ne hii] + · simp [x₀] + · simp +decide [x₀, idx, one] + · simp +decide [x₀, idx, one] + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I + (fun y => x.regs len - y.regs idx) hbody hx₀ + obtain ⟨hr, ho, hi, hm⟩ := hy + have hl := hr len (by decide) + have hend : y.regs idx = x.regs len := by + simp only [Cond.holds_lt, hl] at hc + omega + refine ⟨y, ?_, by simpa only [hend] using hm, hr⟩ + have h1 : Fits (w + 2) 1 := (show Fits 2 1 from by decide).mono (by omega) + have hinit : ExecWithin (B := .standard) (w + 2) (ram[ idx ← #0; one ← #1 ]) x x₀ 2 := by + ram_eval [x₀, h1] + convert hinit.seq he using 1 + · rfl + · simp +decide only [x₀, Data.set_regs, Function.update_apply, idx, one, ↓reduceIte, + Nat.sub_zero] + omega + +end Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Residues diff --git a/ComplexityTests/ThreeSumHistogram.lean b/ComplexityTests/ThreeSumHistogram.lean new file mode 100644 index 000000000..23081d441 --- /dev/null +++ b/ComplexityTests/ThreeSumHistogram.lean @@ -0,0 +1,149 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.Count + +/-! # Executed histogram and residue-array regressions -/ + +namespace ComplexityTests.ThreeSumHistogram + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.WordRAM + +private def keys (i : ℕ) : ℕ := [0, 1, 0, 0].getD i 0 + +private def input : Data 11 := + (((Data.init [0, 1, 0, 0]).set Histogram.src 1).set Histogram.dst 10).set Histogram.len 4 + |>.set Histogram.bins 3 + +/-- Clearing ignores the old contents, including nonzero words in the output block. -/ +example : (Code.eval 8 4 Histogram.program (input.write 10 77)).map + (fun yn => (yn.1.mem 10, yn.1.mem 11, yn.1.mem 12, yn.1.regs Histogram.total, yn.2)) = + some (3, 1, 0, 6, 68) := by decide + +/-- Read the same result through the generic charged theorem, checking both frames. -/ +example : ∃ y, ExecWithin 8 Histogram.program input y 68 ∧ + y.mem 10 = 3 ∧ y.mem 11 = 1 ∧ y.regs Histogram.total = 6 ∧ + y.mem 0 = 4 ∧ y.mem 13 = 0 := by + obtain ⟨y, hy, hm, ht, _⟩ := Histogram.exec (w := 8) input keys + (by decide) (by decide) (by decide) (by decide) (by decide) + (by + intro i hi + change i < 4 at hi + have : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 := by omega + rcases this with rfl | rfl | rfl | rfl <;> decide) + (by + intro i hi + change i < 4 at hi + have : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 := by omega + rcases this with rfl | rfl | rfl | rfl <;> decide) + refine ⟨y, hy, ?_, ?_, ?_, ?_, ?_⟩ + · rw [hm]; decide + · rw [hm]; decide + · rw [ht]; decide + · rw [hm]; decide + · rw [hm]; decide + +/-- Empty inputs and empty histograms pay only setup and two failed loop tests. -/ +example : (Code.eval 8 0 Histogram.program + ((input.set Histogram.len 0).set Histogram.bins 0)).map + (fun yn => (yn.1.regs Histogram.total, yn.2)) = some (0, 9) := by decide + +/-- A singleton has no collision; two equal keys at different positions create two. -/ +example : Histogram.pairs (fun _ => 0) 0 = 0 ∧ + Histogram.pairs (fun _ => 0) 1 = 0 ∧ Histogram.pairs (fun _ => 0) 2 = 2 := by decide + +/-- Different keys create no collision. -/ +example : Histogram.pairs id 4 = 0 := by decide + +private def values (i : ℕ) : ℤ := [-5, -2, 1, 4].getD i 0 + +private def signedInput : Data 16 := + (((Data.init ([-5, -2, 1, 4].map encode)).set ThreeSum.ChanHe.Residues.src 1).set + ThreeSum.ChanHe.Residues.dst 10).set ThreeSum.ChanHe.Residues.len 4 + |>.set ThreeSum.ChanHe.Residues.modulus (encode 3) + +/-- Negative dividends use Euclidean residues, and the outputs are decoded unsigned keys. -/ +example : (Code.eval 8 4 ThreeSum.ChanHe.Residues.program signedInput).map + (fun yn => (List.range 4).map fun i => yn.1.mem (10 + i)) = some [1, 1, 1, 1] := by decide + +/-- The signed-array contract has satisfiable hypotheses on the executed negative input. -/ +example : ∃ y, ExecWithin 8 ThreeSum.ChanHe.Residues.program signedInput y 96 ∧ + y.mem 10 = 1 ∧ y.mem 13 = 1 ∧ y.mem 1 = encode (-5) ∧ y.mem 14 = 0 := by + obtain ⟨y, hy, hm, _⟩ := ThreeSum.ChanHe.Residues.exec (w := 6) (M := 3) + signedInput values (by decide) (by decide) rfl + (by + intro i hi + change i < 4 at hi + have : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 := by omega + rcases this with rfl | rfl | rfl | rfl <;> decide) + (by decide) (by decide) (by decide) + (by + intro i hi + change i < 4 at hi + have : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 := by omega + rcases this with rfl | rfl | rfl | rfl <;> decide) + refine ⟨y, hy, ?_, ?_, ?_, ?_⟩ <;> rw [hm] <;> decide + +/-- The existing mathematical collision count agrees with the histogram's counter. -/ +example : Cslib.Algorithms.ThreeSum.ChanHe.coll {-5, -2, 1, 4} 3 = 12 ∧ + Histogram.pairs (fun i => (values i % 3).toNat) 4 = 12 := by decide + +private def countInput : Data 28 := + (((((Data.init ([-5, -2, 1, 4].map encode)).set ThreeSum.ChanHe.Count.src 1).set + ThreeSum.ChanHe.Count.scratch 10).set ThreeSum.ChanHe.Count.len 4).set + ThreeSum.ChanHe.Count.modulus (encode 3)).set ThreeSum.ChanHe.Count.dst 20 + +-- Evaluating the three renamed loops unfolds over 150 native instructions and their register maps. +set_option maxRecDepth 4096 in +/-- Execute the composed signed-input program and check its unsigned count and both arrays. -/ +example : (Code.eval 8 4 ThreeSum.ChanHe.Count.program countInput).map + (fun yn => [yn.1.mem 1, yn.1.mem 10, yn.1.mem 13, yn.1.mem 20, yn.1.mem 21, + yn.1.mem 22, yn.1.regs ThreeSum.ChanHe.Count.total]) = + some [encode (-5), 1, 1, 0, 4, 0, 12] := by decide + +/-- The full composed contract is admissible for the same negative-input example. -/ +example : ∃ y, ExecWithin 8 ThreeSum.ChanHe.Count.program countInput y 169 ∧ + y.regs ThreeSum.ChanHe.Count.total = 12 ∧ y.mem 21 = 4 ∧ y.mem 1 = encode (-5) := by + obtain ⟨y, hy, hm, ht⟩ := ThreeSum.ChanHe.Count.exec (w := 6) (M := 3) + countInput values (by decide) (by decide) rfl + (by + intro i hi + change i < 4 at hi + have : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 := by omega + rcases this with rfl | rfl | rfl | rfl <;> decide) + (by decide) (by decide) (by decide) (by decide) (by decide) (by decide) + (by + intro i hi + change i < 4 at hi + have : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 := by omega + rcases this with rfl | rfl | rfl | rfl <;> decide) + refine ⟨y, hy, ?_, ?_, ?_⟩ + · rw [ht]; decide + · rw [hm]; decide + · rw [hm]; decide + +-- The three complete program evaluations unfold the renamed loops and register maps. +set_option maxRecDepth 4096 in +/-- The composed program handles lengths zero, one and two, including an unused modulus block. -/ +example : ((Code.eval 8 3 ThreeSum.ChanHe.Count.program + (countInput.set ThreeSum.ChanHe.Count.len 0)).map + (fun yn => yn.1.regs ThreeSum.ChanHe.Count.total)) = some 0 ∧ + ((Code.eval 8 3 ThreeSum.ChanHe.Count.program + (countInput.set ThreeSum.ChanHe.Count.len 1)).map + (fun yn => yn.1.regs ThreeSum.ChanHe.Count.total)) = some 0 ∧ + ((Code.eval 8 3 ThreeSum.ChanHe.Count.program + (countInput.set ThreeSum.ChanHe.Count.len 2)).map + (fun yn => yn.1.regs ThreeSum.ChanHe.Count.total)) = some 2 := by decide + +/-- Equal integer values violate the set-enumeration premise, even at different positions. -/ +example : ¬Set.InjOn (fun _ : ℕ => (-1 : ℤ)) (↑(Finset.range 2) : Set ℕ) := by + intro h + have := h (by decide : 0 ∈ (↑(Finset.range 2) : Set ℕ)) + (by decide : 1 ∈ (↑(Finset.range 2) : Set ℕ)) rfl + omega + +end ComplexityTests.ThreeSumHistogram From a7122053713cab3e45b505306b5d03bcd01cc737 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 09:16:00 -0400 Subject: [PATCH 13/73] fix(wordram): preserve collision-counting parameters explicitly --- .../WordRAM/ThreeSum/ChanHe/Count.lean | 26 ++++++++++++++----- ComplexityTests/ThreeSumHistogram.lean | 8 +++--- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Count.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Count.lean index 677b2c215..5a8a69732 100644 --- a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Count.lean +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Count.lean @@ -9,6 +9,8 @@ module public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.Collisions public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.Residues +import Mathlib.Tactic.FinCases +import Mathlib.Tactic.NormNum /-! # Native Chan–He collision counting @@ -89,6 +91,12 @@ def program : Code .standard 28 := .seq (Residues.program.rename residuesRegisters) (.seq prepare (Histogram.program.rename histogramRegisters)) +/-- The complete counting program preserves its input base, scratch base, length, +encoded modulus and histogram base registers. -/ +theorem preserves_parameters (i : Fin 28) + (hi : (9 ≤ i.val ∧ i.val ≤ 12) ∨ i = dst) : program.Preserves i := by + fin_cases i <;> norm_num [dst] at hi <;> decide + /-- The residue scratch array and the complete histogram, framed by the original memory. -/ def memory (mem : ℕ → ℕ) (values : ℕ → ℤ) (n M scratch dst : ℕ) : ℕ → ℕ := Histogram.memory (Residues.memory mem values M scratch n) @@ -111,7 +119,8 @@ theorem exec {w M : ℕ} (x : Data 28) (values : ℕ → ℤ) (hM : 0 < M) (hinput : ∀ i < x.regs len, x.mem (x.regs src + i) = encode (values i)) : ∃ y, ExecWithin (w + 2) program x y (34 * x.regs len + 5 * M + 18) ∧ y.mem = memory x.mem values (x.regs len) M (x.regs scratch) (x.regs dst) ∧ - y.regs total = Histogram.pairs (fun i => (values i % (M : ℤ)).toNat) (x.regs len) := by + y.regs total = Histogram.pairs (fun i => (values i % (M : ℤ)).toNat) (x.regs len) ∧ + ∀ i, ((9 ≤ i.val ∧ i.val ≤ 12) ∨ i = dst) → y.regs i = x.regs i := by obtain ⟨a, ha, ham, har⟩ := Residues.exec (x.comap residuesRegisters) values hM hmod hmodreg hfit hs hk hsk hinput let y := a.extend residuesRegisters x @@ -173,8 +182,12 @@ theorem exec {w M : ℕ} (x : Data 28) (values : ℕ → ℤ) (hM : 0 < M) have hi' : i < x.regs len := by simpa only [hql] using hi rw [Data.comap_mem, hqm, hqs] simp [Residues.memory, hi']) - refine ⟨b.extend histogramRegisters q, ?_, ?_, ?_⟩ - · exact (hy.seq (hprep.seq (hb.rename histogram_injective))).mono (by rw [hql, hqb]; omega) + have he := (hy.seq (hprep.seq (hb.rename histogram_injective))).mono + (show (23 * x.regs len + 4) + (5 + (5 * (q.comap histogramRegisters).regs Histogram.bins + + 11 * (q.comap histogramRegisters).regs Histogram.len + 9)) ≤ + 34 * x.regs len + 5 * M + 18 by rw [hql, hqb]; omega) + refine ⟨b.extend histogramRegisters q, he, ?_, ?_, fun i hi => + ExecWithin.regs_eq he (preserves_parameters i hi)⟩ · change b.mem = _ rw [hbm, Data.comap_mem, hqm, hqd, hqb, hql] rfl @@ -199,8 +212,9 @@ theorem exec_coll {w M : ℕ} (x : Data 28) (values : ℕ → ℤ) (hM : 0 < M) ∃ y, ExecWithin (w + 2) program x y (34 * x.regs len + 5 * M + 18) ∧ y.mem = memory x.mem values (x.regs len) M (x.regs scratch) (x.regs dst) ∧ y.regs total = Cslib.Algorithms.ThreeSum.ChanHe.coll - ((Finset.range (x.regs len)).image values) M := by - obtain ⟨y, hy, hm, ht⟩ := exec x values hM hmod hmodreg hfit hs hk hd ht hsk hkd hinput - exact ⟨y, hy, hm, ht.trans (coll_eq_histogramPairs values _ M hM hinj).symm⟩ + ((Finset.range (x.regs len)).image values) M ∧ + ∀ i, ((9 ≤ i.val ∧ i.val ≤ 12) ∨ i = dst) → y.regs i = x.regs i := by + obtain ⟨y, hy, hm, ht, hr⟩ := exec x values hM hmod hmodreg hfit hs hk hd ht hsk hkd hinput + exact ⟨y, hy, hm, ht.trans (coll_eq_histogramPairs values _ M hM hinj).symm, hr⟩ end Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Count diff --git a/ComplexityTests/ThreeSumHistogram.lean b/ComplexityTests/ThreeSumHistogram.lean index 23081d441..d51a5340f 100644 --- a/ComplexityTests/ThreeSumHistogram.lean +++ b/ComplexityTests/ThreeSumHistogram.lean @@ -107,8 +107,10 @@ example : (Code.eval 8 4 ThreeSum.ChanHe.Count.program countInput).map /-- The full composed contract is admissible for the same negative-input example. -/ example : ∃ y, ExecWithin 8 ThreeSum.ChanHe.Count.program countInput y 169 ∧ - y.regs ThreeSum.ChanHe.Count.total = 12 ∧ y.mem 21 = 4 ∧ y.mem 1 = encode (-5) := by - obtain ⟨y, hy, hm, ht⟩ := ThreeSum.ChanHe.Count.exec (w := 6) (M := 3) + y.regs ThreeSum.ChanHe.Count.total = 12 ∧ y.mem 21 = 4 ∧ y.mem 1 = encode (-5) ∧ + ∀ i, ((9 ≤ i.val ∧ i.val ≤ 12) ∨ i = ThreeSum.ChanHe.Count.dst) → + y.regs i = countInput.regs i := by + obtain ⟨y, hy, hm, ht, hframe⟩ := ThreeSum.ChanHe.Count.exec (w := 6) (M := 3) countInput values (by decide) (by decide) rfl (by intro i hi @@ -121,7 +123,7 @@ example : ∃ y, ExecWithin 8 ThreeSum.ChanHe.Count.program countInput y 169 ∧ change i < 4 at hi have : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 := by omega rcases this with rfl | rfl | rfl | rfl <;> decide) - refine ⟨y, hy, ?_, ?_, ?_⟩ + refine ⟨y, hy, ?_, ?_, ?_, hframe⟩ · rw [ht]; decide · rw [hm]; decide · rw [hm]; decide From 844522a3dbb30980410ff9e07272ed0df0b9402c Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 08:44:21 -0400 Subject: [PATCH 14/73] feat(matrix): prove finite thin-product correctness and sparse bounds Implement the complete finite mathematical milestone of #1463 above #1464: valid layouts, batch-product correctness/error exclusion, arbitrary-N tiling, exact requested signed products, order/cardinality estimates, geometric tails and the derived sparse bounds. At D=4^m, L=19m, D^18<=N and |W|*2^m<=N^2, encoding entries are bounded by4*N^2/D^(1/18), contributing leaves by33 and passed-set cardinalities by(19m+1)*33 times that scale. Add finite magnitude and auxiliary object-count bounds. None are native instruction costs. All six final local gates pass: strict build, aggregate check, strict tests, environment lint, style lint and init-import check. Hygiene --check-simp, changed-file lints (14 library modules,1 new test) and diff --check pass. Tests kernel-check signed results3,20,0,-8, true padding, empty/sparse and negative-order boundaries, and instantiate all aggregate bounds with jointly satisfiable m=1 parameters and symbolic matrices without resource overrides. Read-only math/API review by /root/thin_product_review is clear after fixed doc references/magnitude wording/quotation hygiene. Focused final delta review by /root/thin_product_final_delta_review is clear for tree b021e2d9340b433a9fdb129882351e158e8c289f, reusing the prior mathematics review. These are internal agent reviews, not outside coordinator or human approval. Source: anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5, selective Section2 finite mathematics; Anthropic credit/NOTICE retained. Abstract assessment: no impact on selected headlines, catalogue or rendering. Full native tensor/sparse-product/Exact Triangle code, native Chan-He #1446, whole input/time/width composition and improved1.9992 remain under #1394. AI assistance: implementation and reviews with OpenAI Codex, worker session codex-thin-product-math-20261028. --- Complexity.lean | 14 + .../MatrixMultiplication/Schonhage/Batch.lean | 191 ++++++++++ .../Schonhage/Layout.lean | 150 ++++++++ .../Schonhage/Levels.lean | 334 ++++++++++++++++++ .../Schonhage/Magnitude.lean | 249 +++++++++++++ .../Schonhage/Orders.lean | 320 +++++++++++++++++ .../Schonhage/Parameters.lean | 134 +++++++ .../Schonhage/Sparse.lean | 311 ++++++++++++++++ .../Schonhage/ThinProduct.lean | 196 ++++++++++ .../Schonhage/Tiling.lean | 248 +++++++++++++ .../Foundations/Algebra/FiniteMagnitude.lean | 55 +++ .../Foundations/Algebra/GeometricTail.lean | 42 +++ .../Combinatorics/BinomialUpperBound.lean | 52 +++ .../Combinatorics/FunctionPlaces.lean | 141 ++++++++ .../Foundations/Data/Finset/OrderGlue.lean | 187 ++++++++++ ComplexityTests/SchonhageThinProduct.lean | 165 +++++++++ NOTICE | 4 +- ROADMAP.md | 6 +- UPSTREAM.md | 13 +- 19 files changed, 2806 insertions(+), 6 deletions(-) create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/Batch.lean create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/Layout.lean create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/Levels.lean create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/Magnitude.lean create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/Orders.lean create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/Parameters.lean create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/Sparse.lean create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/ThinProduct.lean create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/Tiling.lean create mode 100644 Complexity/Foundations/Algebra/FiniteMagnitude.lean create mode 100644 Complexity/Foundations/Algebra/GeometricTail.lean create mode 100644 Complexity/Foundations/Combinatorics/BinomialUpperBound.lean create mode 100644 Complexity/Foundations/Combinatorics/FunctionPlaces.lean create mode 100644 Complexity/Foundations/Data/Finset/OrderGlue.lean create mode 100644 ComplexityTests/SchonhageThinProduct.lean diff --git a/Complexity.lean b/Complexity.lean index 58c9b1d11..1cdd3b3c0 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -30,9 +30,18 @@ public import Complexity.Algorithms.Lean.VanEmdeBoas.Insert public import Complexity.Algorithms.Lean.VanEmdeBoas.Layout public import Complexity.Algorithms.Lean.VanEmdeBoas.Successor public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Alphabets +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Batch public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Full public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Identity +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Layout +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Levels +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Magnitude +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Orders +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Parameters public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Pruned +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Sparse +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.ThinProduct +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Tiling public import Complexity.Algorithms.Reachability.BreadthFirst public import Complexity.Algorithms.Reachability.InductiveCounting public import Complexity.Algorithms.Reachability.InductiveCounting.Bounds @@ -1862,6 +1871,8 @@ public import Complexity.Crypto.Protocols.SigmaProtocol.Lyubashevsky.NativeExtra public import Complexity.Crypto.Protocols.SigmaProtocol.SchnorrIdentification public import Complexity.Crypto.Protocols.SigmaProtocol.SchnorrSignature public import Complexity.Crypto.Protocols.Signature.RandomOracle +public import Complexity.Foundations.Algebra.FiniteMagnitude +public import Complexity.Foundations.Algebra.GeometricTail public import Complexity.Foundations.Analysis.BernoulliInequality public import Complexity.Foundations.Analysis.HarmonicMean public import Complexity.Foundations.Analysis.InnerProductSpace.Basic @@ -1926,6 +1937,7 @@ public import Complexity.Foundations.Combinatorics.AffineDisperser.IndependentSo public import Complexity.Foundations.Combinatorics.AffineDisperser.Threshold public import Complexity.Foundations.Combinatorics.BalancedSchedules public import Complexity.Foundations.Combinatorics.BinomialTail +public import Complexity.Foundations.Combinatorics.BinomialUpperBound public import Complexity.Foundations.Combinatorics.CentralBinomial public import Complexity.Foundations.Combinatorics.DepthReduction public import Complexity.Foundations.Combinatorics.Expander.AffineFamily @@ -1964,6 +1976,7 @@ public import Complexity.Foundations.Combinatorics.Expander.Walk public import Complexity.Foundations.Combinatorics.Expander.Zigzag public import Complexity.Foundations.Combinatorics.FiniteCover public import Complexity.Foundations.Combinatorics.FiniteCover.Basic +public import Complexity.Foundations.Combinatorics.FunctionPlaces public import Complexity.Foundations.Combinatorics.FunctionalGraph public import Complexity.Foundations.Combinatorics.FunctionalGraph.Active public import Complexity.Foundations.Combinatorics.FunctionalGraph.CheckpointForest @@ -2072,6 +2085,7 @@ public import Complexity.Foundations.Data.Fin.Tuple public import Complexity.Foundations.Data.Finset.Average public import Complexity.Foundations.Data.Finset.Expect public import Complexity.Foundations.Data.Finset.Markov +public import Complexity.Foundations.Data.Finset.OrderGlue public import Complexity.Foundations.Data.Finset.Quantile public import Complexity.Foundations.Data.Finset.Quotient public import Complexity.Foundations.Data.Finset.Sampling diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Batch.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Batch.lean new file mode 100644 index 000000000..9421e25bb --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Batch.lean @@ -0,0 +1,191 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Levels + +/-! +# Batch correctness of the tensor product + +## Main results + +* `bilinearProduct_arrays_eq_mul`: exact integer matrix multiplication at selected strings. +* `batch_product_spec`: arbitrary output strings with `m` inner levels. + +The tensor error has no effect: a nonzero summand must use the same inner levels on +both inputs as the output, and the remaining outer coefficients are Kronecker deltas. + +## References + +Adapted selectively from [AlmanWilliams2026, Section 2] and +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical +counts and identities; they give no native program or instruction-cost certificate. +Source-derived descriptions below are paraphrases. +-/ + +@[expose] public section + +open Finset + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-! ### The sizes of the matrices, and Figure 4 -/ + +/-- Section 2.3.3: there are `N₀` strings of `L - m` outer left (or right) variables. -/ +theorem card_outerStr (L m : ℕ) : Fintype.card (OuterStr L m) = N0 L m := by + simp [N0] + +/-- Section 2.3.3: there are `D` strings of `m` inner left (or right) variables. -/ +theorem card_innerStr (m : ℕ) : Fintype.card (InnerStr m) = D m := by + simp [D] + +/-- Section 2.3.3: there are `K = binom(L, m)` subsets `Q` of `{1, …, L}` of size `m`. -/ +theorem card_subsets_eq_K (L m : ℕ) : + (univ.filter fun Q : Finset (Fin L) => Q.card = m).card = K L m := by + rw [univ_filter_card_eq, card_powersetCard, card_univ, Fintype.card_fin, K] + +/-! ### The input arrays -/ + +/-- The entry of the left input array at the string with inner set `Q`, outer part `r` and inner +part `π` is `X_Q[r, π]`. -/ +@[simp] +theorem arrayL_leftStrOf {L m : ℕ} (X : Finset (Fin L) → LeftMat L m) (Q : Finset (Fin L)) + (hQ : Q.card = m) (r : OuterStr L m) (π : InnerStr m) : + arrayL m X (leftStrOf Q hQ r π) = X Q r π := by + have h : (innerSetL (leftStrOf Q hQ r π)).card = m := by rw [innerSetL_leftStrOf, hQ] + rw [arrayL, dite_eq_left h, outerPartL_leftStrOf, innerPartL_leftStrOf, innerSetL_leftStrOf] + +/-- The entry of the right input array at the string with inner set `Q`, inner part `π` and outer +part `c` is `Y_Q[π, c]`. -/ +@[simp] +theorem arrayR_rightStrOf {L m : ℕ} (Y : Finset (Fin L) → RightMat L m) (Q : Finset (Fin L)) + (hQ : Q.card = m) (π : InnerStr m) (c : OuterStr L m) : + arrayR m Y (rightStrOf Q hQ π c) = Y Q π c := by + have h : (innerSetR (rightStrOf Q hQ π c)).card = m := by rw [innerSetR_rightStrOf, hQ] + rw [arrayR, dite_eq_left h, innerPartR_rightStrOf, outerPartR_rightStrOf, innerSetR_rightStrOf] + +/-! ### The proof of Lemma 9 -/ + +/-- The inner-output coefficient selects equal inner index pairs (Section 2.3.3). +Checked directly on `coefficient`; no polynomial coefficient bridge is assumed. -/ +theorem coefficient_z0 (s : LeftVar) (t : RightVar) : + coefficient s t .z0 = if ∃ i j, s = .p i j ∧ t = .q i j then 1 else 0 := by + decide +revert + +/-- With two outer inputs, the outer-output coefficient selects exactly the matching row +and column (Section 2.3.3). -/ +theorem coefficient_x_y_z (i' j' i j : Fin 3) : + coefficient (.x i') (.y j') (.z i j) = if i' = i ∧ j' = j then 1 else 0 := by + decide +revert + +/-- The statement on `γ(x_i', y_j', z_ij)` as it is used: if `s` and `t` are outer and +`γ(s, t, z_ij) ≠ 0`, then `(s, t) = (x_i, y_j)`. -/ +private theorem eq_x_y_of_gamma_ne_zero {s : LeftVar} {t : RightVar} {i j : Fin 3} + (hs : ¬ s.IsInner) (ht : ¬ t.IsInner) (h : coefficient s t (.z i j) ≠ 0) : s = .x i ∧ t = .y j + := by + rcases s with i' | _ + · rcases t with j' | _ + · by_contra hne + exact h ((coefficient_x_y_z i' j' i j).trans + (ite_eq_right fun ⟨hi, hj⟩ => hne ⟨hi ▸ rfl, hj ▸ rfl⟩)) + · exact (ht trivial).elim + · exact (hs trivial).elim + +/-- The summands of Lemma 8 that can be nonzero. Let `w` be the output string with inner +set `Q`, row `r` and column `c`. If `γ(u_ℓ, v_ℓ, w_ℓ) ≠ 0` at every level and the inner sets of `u` +and `v` have `m` elements, then `u` has inner set `Q`, outer part `r` and some inner part `π`, and +`v` has inner set `Q`, outer part `c` and the inner part with the same indices. -/ +private theorem exists_of_summand_ne_zero {L m : ℕ} (Q : Finset (Fin L)) (hQ : Q.card = m) + (r c : OuterStr L m) {u : LeftStr L} {v : RightStr L} + (hγ : ∀ ℓ, coefficient (u ℓ) (v ℓ) (outStrOf Q hQ r c ℓ) ≠ 0) (hu : (innerSetL u).card = m) + (hv : (innerSetR v).card = m) : ∃ π, u = leftStrOf Q hQ r π ∧ v = rightStrOf Q hQ π c := by + simp only [outStrOf, forall_level_iff Q hQ, glue_innerLevel, glue_outerLevel] at hγ + obtain ⟨hγin, hγout⟩ := hγ + -- at a level ℓ ∈ Q we have w_ℓ = z₀ […] So (u_ℓ, v_ℓ) = (p_ij, q_ij) for some i, j. + have hin : ∀ k, ∃ ij : Fin 2 × Fin 2, + u (Finset.orderEmbOfFin Q hQ k) = .p ij.1 ij.2 ∧ v (Finset.orderEmbOfFin Q hQ k) = .q ij.1 + ij.2 := fun k => by + by_contra hne + exact hγin k ((coefficient_z0 _ _).trans (ite_eq_right fun ⟨i, j, h⟩ => hne ⟨(i, j), h⟩)) + choose π hπ using hin + -- Thus u and v have an inner variable at every level of Q, and their inner sets are therefore + -- exactly Q. So u_ℓ and v_ℓ are outer at the levels outside Q. + have huQ : innerSetL u = Q := + filter_eq_of_forall_innerLevel Q hQ (fun k => by rw [(hπ k).1]; trivial) hu + have hvQ : innerSetR v = Q := + filter_eq_of_forall_innerLevel Q hQ (fun k => by rw [(hπ k).2]; trivial) hv + have huout := ((filter_eq_iff_forall_level Q hQ).mp huQ).2 + have hvout := ((filter_eq_iff_forall_level Q hQ).mp hvQ).2 + -- at a level ℓ ∉ Q we have w_ℓ = z_ij for some i, j […] so (u_ℓ, v_ℓ) = (x_i, y_j). + have hout : ∀ k, u ((Qᶜ.orderEmbOfFin (card_compl_of_card_eq Q hQ)) k) = .x (r k) ∧ v + ((Qᶜ.orderEmbOfFin (card_compl_of_card_eq Q hQ)) k) = .y (c k) := + fun k => eq_x_y_of_gamma_ne_zero (huout k) (hvout k) (hγout k) + exact ⟨π, (eq_glue_iff Q hQ).mpr ⟨fun k => (hπ k).1, fun k => (hout k).1⟩, + (eq_glue_iff Q hQ).mpr ⟨fun k => (hπ k).2, fun k => (hout k).2⟩⟩ + +/-- In the proof of Lemma 9: the coefficient of the summand of `π` is 1. -/ +private theorem prod_gamma_eq_one {L m : ℕ} (Q : Finset (Fin L)) (hQ : Q.card = m) + (r c : OuterStr L m) (π : InnerStr m) : + ∏ ℓ, coefficient (leftStrOf Q hQ r π ℓ) (rightStrOf Q hQ π c ℓ) (outStrOf Q hQ r c ℓ) = 1 := by + have hone : ∀ ℓ, + coefficient (leftStrOf Q hQ r π ℓ) (rightStrOf Q hQ π c ℓ) (outStrOf Q hQ r c ℓ) = 1 := by + simp only [forall_level_iff Q hQ, leftStrOf, rightStrOf, outStrOf, glue_innerLevel, + glue_outerLevel] + exact ⟨fun _ => (coefficient_z0 _ _).trans (ite_eq_left ⟨_, _, rfl, rfl⟩), + fun _ => (coefficient_x_y_z _ _ _ _).trans (ite_eq_left ⟨rfl, rfl⟩)⟩ + exact prod_eq_one fun ℓ _ => hone ℓ + +/-- Lemma 9 for the output string with inner set `Q`, row `r` and column `c`: by Lemma 8 the entry +is a sum over all pairs `(u, v)`, and the summands that remain are those of the matrix product. -/ +theorem bilinearProduct_arrays_eq_mul {L m : ℕ} (X : Finset (Fin L) → LeftMat L m) + (Y : Finset (Fin L) → RightMat L m) (Q : Finset (Fin L)) (hQ : Q.card = m) + (r c : OuterStr L m) : + bilinearProduct (arrayL m X) (arrayR m Y) (outStrOf Q hQ r c) = (X Q * Y Q) r c := by + -- To show: `∑_{(u,v)} ∏_ℓ γ(u_ℓ, v_ℓ, w_ℓ) a[u] b[v] = ∑_π X_Q[r, π] Y_Q[π, c]`. The right-hand + -- side is the part of the left-hand side over the pairs `(u, v)` that belong to some `π`. + rw [tensor_expansion, Matrix.mul_apply, ← Fintype.sum_prod_type'] + symm + refine Fintype.sum_of_injective (fun π => (leftStrOf Q hQ r π, rightStrOf Q hQ π c)) ?_ _ _ ?_ ?_ + · -- There is one such summand for each of the D strings π. + exact fun π π' h => (leftStrOf_inj.mp (congrArg Prod.fst h)).2 + · -- Every other summand is zero. a[u] b[v] ≠ 0 requires the inner sets of u and v to have + -- exactly m elements. + rintro ⟨u, v⟩ hnot + by_contra hne + obtain ⟨⟨hγ, ha⟩, hb⟩ : ((∏ ℓ, coefficient (u ℓ) (v ℓ) (outStrOf Q hQ r c ℓ)) ≠ 0 + ∧ arrayL m X u ≠ 0) ∧ arrayR m Y v ≠ 0 := by + simpa only [mul_ne_zero_iff] using hne + obtain ⟨π, rfl, rfl⟩ := exists_of_summand_ne_zero Q hQ r c + (fun ℓ => prod_ne_zero_iff.mp hγ ℓ (mem_univ ℓ)) + (by_contra fun h => ha (dite_eq_right h)) (by_contra fun h => hb (dite_eq_right h)) + exact hnot ⟨π, rfl⟩ + · -- The summand of `π` is `1 · X_Q[r, π] · Y_Q[π, c]`. + intro π + rw [prod_gamma_eq_one, one_mul, arrayL_leftStrOf, arrayR_rightStrOf] + +/-- **Lemma 9**. For the input arrays a, b above and every output string w whose inner +set Q has exactly m elements, bilinearProduct(a, b)[w] = (X_Q Y_Q)[w]. (Equation (4).) -/ +theorem batch_product_spec {L m : ℕ} (X : Finset (Fin L) → LeftMat L m) (Y : Finset (Fin L) → + RightMat L m) + (w : OutStr L) (h : (innerSetO w).card = m) : + bilinearProduct (arrayL m X) (arrayR m Y) w + = (X (innerSetO w) * Y (innerSetO w)) (rowO w h) (colO w h) := by + -- `w` is the output string with the inner set, the row and the column of `w`. + conv_lhs => rw [outStr_eq_outStrOf h rfl h] + exact bilinearProduct_arrays_eq_mul X Y (innerSetO w) h (rowO w h) (colO w h) + +/-- Section 2.3.3: one run of Full […] computes all K of the products X_Q Y_Q. -/ +theorem Full.arrays_eq_mul {L m : ℕ} (X : Finset (Fin L) → LeftMat L m) + (Y : Finset (Fin L) → RightMat L m) (Q : Finset (Fin L)) (hQ : Q.card = m) + (r c : OuterStr L m) : + Full.result L (arrayL m X) (arrayR m Y) (outStrOf Q hQ r c) = (X Q * Y Q) r c := by + rw [Full.result_eq_bilinearProduct] + exact bilinearProduct_arrays_eq_mul X Y Q hQ r c + +end Cslib.Algorithms.MatrixMultiplication.Schonhage diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Layout.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Layout.lean new file mode 100644 index 000000000..354678f42 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Layout.lean @@ -0,0 +1,150 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Levels +public import Mathlib.Data.Nat.Sqrt + +/-! +# Tiling layout data + +## Main definitions + +* `Layout`: ordinary finite indexing choices, with no solver or bound certificate. +* `wantedStrings`, `tiles`: requested outputs grouped by tile. + +## References + +Adapted selectively from [AlmanWilliams2026, Section 2] and +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical +counts and identities; they give no native program or instruction-cost certificate. +Source-derived descriptions below are paraphrases. +-/ + +@[expose] public section + +open Finset + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Section 2.3.4: K₀ := ⌊√K⌋. -/ +def K0 (L m : ℕ) : ℕ := Nat.sqrt (K L m) + +/-- `N₀ = 3^{L-m}` is positive. -/ +theorem N0_pos (L m : ℕ) : 0 < N0 L m := Nat.pow_pos (by decide) + +/-- `K₀ = ⌊√binom(L, m)⌋` is positive when `m ≤ L`. -/ +theorem K0_pos {L m : ℕ} (h : m ≤ L) : 0 < K0 L m := Nat.sqrt_pos.mpr (Nat.choose_pos h) + +/-- The choices that the paper makes once and for all without naming them. + +* `hmL`: `m ≤ L`. The paper never states this; it follows from `L = 19m` (Section 2) and from + `L ≥ 10m` (Section 4). It makes `K₀ ≥ 1`. +* Section 2.3.3: we index its N₀ rows by the N₀ strings of L - m outer left variables, and its D + columns by the D strings of m inner left variables, and similarly for a `D × N₀` matrix. + `rowIdx`, `colIdx` and `innerIdx` say which string indexes which row or column, counted from 0. +* Section 2.3.4: We fix K₀² ≤ K distinct subsets of {1, …, L} of size m, one for each block product + of the grid, the same in every tile. `table (g, h)` is the subset of the block product of the + `g`-th row block of a row band and the `h`-th column block of a column band. + +Every statement about the tiling holds for every such choice. -/ +structure Layout (L m : ℕ) where + hmL : m ≤ L + /-- Index a block's rows by outer strings. -/ + rowIdx : Fin (N0 L m) ≃ OuterStr L m + /-- Index a block's columns by outer strings. -/ + colIdx : Fin (N0 L m) ≃ OuterStr L m + /-- Index the thin matrix dimension by strings of inner index pairs. -/ + innerIdx : Fin (D m) ≃ InnerStr m + /-- Assign a distinct size-`m` level set to each block product in a tile. -/ + table : Fin (K0 L m) × Fin (K0 L m) → Finset (Fin L) + table_card : ∀ gh, (table gh).card = m + table_injective : Function.Injective table + +/-- The number `K₀ N₀` of rows of a row band (Section 2.3.4: a band is `K₀` consecutive blocks of +`N₀` consecutive rows), which is also the number of columns of a column band. -/ +def bandSize (L m : ℕ) : ℕ := K0 L m * N0 L m + +/-- The number of row bands (and of column bands) after padding: `N / (K₀ N₀)` rounded up. -/ +def numBands (L m N : ℕ) : ℕ := (N + bandSize L m - 1) / bandSize L m + +/-- Section 2.3.4: we first pad N to a multiple of K₀ N₀. The padded size. -/ +def padN (L m N : ℕ) : ℕ := numBands L m N * bandSize L m + +/-- Section 2.3.4: `X` padded with zero rows. Rows are numbered from 0. -/ +def padRows {N D' : ℕ} (X : Matrix (Fin N) (Fin D') ℤ) (I : ℕ) (k : Fin D') : ℤ := + if h : I < N then X ⟨I, h⟩ k else 0 + +/-- Section 2.3.4: `Y` padded with zero columns. Columns are numbered from 0. -/ +def padCols {N D' : ℕ} (Y : Matrix (Fin D') (Fin N) ℤ) (k : Fin D') (J : ℕ) : ℤ := + if h : J < N then Y k ⟨J, h⟩ else 0 + +/-- Section 2.3.4: Cut X into row blocks of N₀ consecutive rows. The `B`-th row block (counted +from 0) consists of the rows `B N₀, …, B N₀ + N₀ - 1`. -/ +def rowBlock {L m N : ℕ} (lay : Layout L m) (X : Matrix (Fin N) (Fin (D m)) ℤ) (B : ℕ) : + LeftMat L m := + fun r π => padRows X (B * N0 L m + lay.rowIdx.symm r) (lay.innerIdx.symm π) + +/-- Section 2.3.4: and Y into column blocks of N₀ consecutive columns. -/ +def colBlock {L m N : ℕ} (lay : Layout L m) (Y : Matrix (Fin (D m)) (Fin N) ℤ) (B : ℕ) : + RightMat L m := + fun π c => padCols Y (lay.innerIdx.symm π) (B * N0 L m + lay.colIdx.symm c) + +/-- Section 2.3.4: In a tile, let X_Q and Y_Q be the row block and the column block of the block +product with subset Q, and let X_Q = Y_Q = 0 for the other subsets Q. The row band `β` (counted +from 0) consists of the row blocks `β K₀, …, β K₀ + K₀ - 1`. The subsets in the table are distinct, +so at most one summand is nonzero. -/ +def bandFamilyL {L m N : ℕ} (lay : Layout L m) (X : Matrix (Fin N) (Fin (D m)) ℤ) (β : ℕ) : + Finset (Fin L) → LeftMat L m := + fun Q => ∑ gh, if lay.table gh = Q then rowBlock lay X (β * K0 L m + gh.1) else 0 + +/-- The matrices `Y_Q` of a tile; they depend only on its column band `β`. -/ +def bandFamilyR {L m N : ℕ} (lay : Layout L m) (Y : Matrix (Fin (D m)) (Fin N) ℤ) (β : ℕ) : + Finset (Fin L) → RightMat L m := + fun Q => ∑ gh, if lay.table gh = Q then colBlock lay Y (β * K0 L m + gh.2) else 0 + +/-- Section 2.3.4: the left input array of a tile depends only on its row band. -/ +def bandArrayL {L m N : ℕ} (lay : Layout L m) (X : Matrix (Fin N) (Fin (D m)) ℤ) (β : ℕ) : + LeftStr L → ℤ := + arrayL m (bandFamilyL lay X β) + +/-- Section 2.3.4: and the right one only on its column band. -/ +def bandArrayR {L m N : ℕ} (lay : Layout L m) (Y : Matrix (Fin (D m)) (Fin N) ℤ) (β : ℕ) : + RightStr L → ℤ := + arrayR m (bandFamilyR lay Y β) + +/-- The band that contains row (or column) `I`. -/ +def bandOf (L m I : ℕ) : ℕ := I / bandSize L m + +/-- The position, within its band, of the block that contains row (or column) `I`. -/ +def blockOf {L m : ℕ} (lay : Layout L m) (I : ℕ) : Fin (K0 L m) := + ⟨I / N0 L m % K0 L m, Nat.mod_lt _ (K0_pos lay.hmL)⟩ + +/-- The position of row (or column) `I` within its block. -/ +def offsetOf (L m I : ℕ) : Fin (N0 L m) := ⟨I % N0 L m, Nat.mod_lt _ (N0_pos L m)⟩ + +/-- Section 2.4.4: Each wanted position (I, J) ∈ W lies in one tile T and is indexed by one output +string of T, the string whose inner set is the subset Q of the block product containing (I, J) and +whose row and column are those of (I, J) within that block product. The tile is the pair +`(bandOf L m I, bandOf L m J)`, and this is the output string. -/ +def outStrOfPos {L m : ℕ} (lay : Layout L m) (I J : ℕ) : OutStr L := + outStrOf (lay.table (blockOf lay I, blockOf lay J)) (lay.table_card _) + (lay.rowIdx (offsetOf L m I)) (lay.colIdx (offsetOf L m J)) + +/-- Section 2.4.4: For each tile T, let W_T be the set of the output strings of the wanted +positions in T. The tile `T` is given by its row band `β` and its column band `β'`. -/ +def wantedStrings {L m N : ℕ} (lay : Layout L m) (W : Finset (Fin N × Fin N)) (β β' : ℕ) : + Finset (OutStr L) := + (W.filter fun IJ => bandOf L m IJ.1 = β ∧ bandOf L m IJ.2 = β').image + fun IJ => outStrOfPos lay IJ.1 IJ.2 + +/-- All the tiles: pairs of a row band and a column band. -/ +def tiles (L m N : ℕ) : Finset (ℕ × ℕ) := range (numBands L m N) ×ˢ range (numBands L m N) + +end Cslib.Algorithms.MatrixMultiplication.Schonhage diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Levels.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Levels.lean new file mode 100644 index 000000000..38d7001a9 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Levels.lean @@ -0,0 +1,334 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Full +public import Complexity.Foundations.Data.Finset.OrderGlue + +/-! +# Matrix entries indexed by tensor strings + +## Main definitions + +* `arrayL`, `arrayR`: batch arrays with exactly `m` inner levels. +* `leftStrOf`, `rightStrOf`, `outStrOf`: entry strings. + +## Main results + +* `existsUnique_leftStr`, `existsUnique_rightStr`, `existsUnique_outStr`: nonvacuous entry layouts. + +## References + +Adapted selectively from [AlmanWilliams2026, Section 2] and +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical +counts and identities; they give no native program or instruction-cost certificate. +Source-derived descriptions below are paraphrases. +-/ + +@[expose] public section + +open Finset + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Section 2.2: We call the variables of the inner product, p, q, and z₀, inner. -/ +def LeftVar.IsInner : LeftVar → Prop + | .x _ => False + | .p _ _ => True +/-- Section 2.2: the right variables `q` are inner, the `y` are outer. -/ +def RightVar.IsInner : RightVar → Prop + | .y _ => False + | .q _ _ => True +instance LeftVar.instDecidablePredIsInner : DecidablePred LeftVar.IsInner + | .x _ => isFalse fun h => h + | .p _ _ => isTrue trivial +instance RightVar.instDecidablePredIsInner : DecidablePred RightVar.IsInner + | .y _ => isFalse fun h => h + | .q _ _ => isTrue trivial +/-- Section 2.3.3: The inner set of a left string is the set of levels at which it has a p (as +opposed to an x). -/ +def innerSetL {L : ℕ} (u : LeftStr L) : Finset (Fin L) := univ.filter fun ℓ => (u ℓ).IsInner +/-- Section 2.3.3: the inner set of a right string is the set of levels at which it has a q (as +opposed to a y). -/ +def innerSetR {L : ℕ} (v : RightStr L) : Finset (Fin L) := univ.filter fun ℓ => (v ℓ).IsInner +/-- Section 2.3.3: the inner set of an output string is the set of levels at which it has z₀ (as +opposed to a z_ij). -/ +def innerSetO {L : ℕ} (w : OutStr L) : Finset (Fin L) := univ.filter fun ℓ => (w ℓ).IsInner + +/-- Section 2.3.3: N₀ := 3^{L-m}. Meant for `m ≤ L`; for `m > L` the subtraction of natural +numbers is cut off at 0 and the value is 1. -/ +def N0 (L m : ℕ) : ℕ := 3 ^ (L - m) +/-- Section 2.3.3: D := 4^m. -/ +def D (m : ℕ) : ℕ := 4 ^ m +/-- Section 2.3.3: K := binom(L, m), the number of subsets of `{1, …, L}` of size `m`. -/ +def K (L m : ℕ) : ℕ := L.choose m + +/-- The index `i` of an outer left variable `x_i` (the value at a `p` is never used). -/ +def LeftVar.outerIndex : LeftVar → Fin 3 + | .x i => i + | .p _ _ => 0 +/-- The index pair `(i, j)` of an inner left variable `p_ij` (the value at an `x` is never used). -/ +def LeftVar.innerIndex : LeftVar → Fin 2 × Fin 2 + | .x _ => (0, 0) + | .p i j => (i, j) +/-- The index `j` of an outer right variable `y_j` (the value at a `q` is never used). -/ +def RightVar.outerIndex : RightVar → Fin 3 + | .y j => j + | .q _ _ => 0 +/-- The index pair `(i, j)` of an inner right variable `q_ij` (the value at a `y` is never used). -/ +def RightVar.innerIndex : RightVar → Fin 2 × Fin 2 + | .y _ => (0, 0) + | .q i j => (i, j) +/-- The first index `i` of an outer output variable `z_ij` (the value at `z₀` is never used). -/ +def OutVar.rowIndex : OutVar → Fin 3 + | .z i _ => i + | .z0 => 0 +/-- The second index `j` of an outer output variable `z_ij` (the value at `z₀` is never used). -/ +def OutVar.colIndex : OutVar → Fin 3 + | .z _ j => j + | .z0 => 0 + +/-- A string of `L - m` outer left variables `x_{i₁} ⋯ x_{i_{L-m}}`, written as the string of its +indices `i₁ ⋯ i_{L-m}`. The same type is used for strings `y_{j₁} ⋯ y_{j_{L-m}}` of outer right +variables. Section 2.3.3: these strings index the `N₀` rows of an `N₀ × D` matrix and the `N₀` +columns of a `D × N₀` matrix. -/ +abbrev OuterStr (L m : ℕ) : Type := Fin (L - m) → Fin 3 + +/-- A string of `m` inner left variables `π = p_{i₁j₁} ⋯ p_{i_m j_m}`, written as the string of its +index pairs. The same type is used for strings `π' = q_{i₁j₁} ⋯ q_{i_m j_m}` of inner right +variables, so that the paper's `π'`, with the same indices as `π`, is the same element of this +type as `π`. Section 2.3.3: these strings index the `D` columns of an `N₀ × D` matrix and the `D` +rows of a `D × N₀` matrix. With this convention the paper's (AB)[r, c] := ∑_π A[r, π] B[π', c] is +the usual matrix product. -/ +abbrev InnerStr (m : ℕ) : Type := Fin m → Fin 2 × Fin 2 + +/-- Section 2.3.3: its outer part is the string of its variables at the other levels, for a left +string whose inner set has `m` elements. -/ +def outerPartL {L m : ℕ} (u : LeftStr L) (h : (innerSetL u).card = m) : OuterStr L m := + fun k => (u (((innerSetL u)ᶜ.orderEmbOfFin (card_compl_of_card_eq (innerSetL u) h)) k)).outerIndex +/-- Section 2.3.3: The inner part of a string is the string of its variables at the levels of its +inner set, in the order of the levels, for a left string whose inner set has `m` elements. -/ +def innerPartL {L m : ℕ} (u : LeftStr L) (h : (innerSetL u).card = m) : InnerStr m := + fun k => (u (Finset.orderEmbOfFin (innerSetL u) h k)).innerIndex +/-- The outer part of a right string whose inner set has `m` elements. -/ +def outerPartR {L m : ℕ} (v : RightStr L) (h : (innerSetR v).card = m) : OuterStr L m := + fun k => (v (((innerSetR v)ᶜ.orderEmbOfFin (card_compl_of_card_eq (innerSetR v) h)) k)).outerIndex +/-- The inner part of a right string whose inner set has `m` elements. -/ +def innerPartR {L m : ℕ} (v : RightStr L) (h : (innerSetR v).card = m) : InnerStr m := + fun k => (v (Finset.orderEmbOfFin (innerSetR v) h k)).innerIndex + +/-- Section 2.3.3: if the variables of w at the levels outside Q are z_{i₁j₁}, …, z_{i_{L-m} +j_{L-m}}, in the order of the levels, then the row of w is the string x_{i₁} ⋯ x_{i_{L-m}}. -/ +def rowO {L m : ℕ} (w : OutStr L) (h : (innerSetO w).card = m) : OuterStr L m := + fun k => (w (((innerSetO w)ᶜ.orderEmbOfFin (card_compl_of_card_eq (innerSetO w) h)) k)).rowIndex +/-- Section 2.3.3: and its column is the string y_{j₁} ⋯ y_{j_{L-m}}. -/ +def colO {L m : ℕ} (w : OutStr L) (h : (innerSetO w).card = m) : OuterStr L m := + fun k => (w (((innerSetO w)ᶜ.orderEmbOfFin (card_compl_of_card_eq (innerSetO w) h)) k)).colIndex + +/-- An `N₀ × D` matrix, its rows and columns indexed by strings as in Section 2.3.3. -/ +abbrev LeftMat (L m : ℕ) : Type := Matrix (OuterStr L m) (InnerStr m) ℤ +/-- A `D × N₀` matrix, its rows and columns indexed by strings as in Section 2.3.3. -/ +abbrev RightMat (L m : ℕ) : Type := Matrix (InnerStr m) (OuterStr L m) ℤ + +/-- Section 2.3.3: a[u] := X_Q[u] […] for all the left strings u […] whose inner set Q has exactly +m elements, and a[u] := 0 […] at every other string, where X_Q[u] is the entry of `X_Q` at the +row and the column of u: the row is the outer part of `u` and the column is its inner part. The +family `X` gives a matrix `X Q` for every set `Q` of levels; only those with `|Q| = m` are used. -/ +def arrayL {L : ℕ} (m : ℕ) (X : Finset (Fin L) → LeftMat L m) : LeftStr L → ℤ := + fun u => + if h : (innerSetL u).card = m then X (innerSetL u) (outerPartL u h) (innerPartL u h) else 0 + +/-- Section 2.3.3: b[v] := Y_Q[v] for the right strings `v` whose inner set `Q` has exactly `m` +elements, the row given by the inner part of v and the column by the outer part, and `b[v] := 0` +at every other string. -/ +def arrayR {L : ℕ} (m : ℕ) (Y : Finset (Fin L) → RightMat L m) : RightStr L → ℤ := + fun v => + if h : (innerSetR v).card = m then Y (innerSetR v) (innerPartR v h) (outerPartR v h) else 0 + +/-! ### 2.3.4 Tiling the N × D × N product by products of shape N₀ × D × N₀: the number `M` -/ + +/-- Section 2.3.4: M := K N₀². -/ +def M (L m : ℕ) : ℕ := K L m * N0 L m ^ 2 + + +variable {L m : ℕ} (Q : Finset (Fin L)) (hQ : Q.card = m) + +/-! ### Left, right and output strings with a given inner set, outer part and inner part -/ + +/-- The left string with inner set `Q`, outer part `r` and inner part `π`. -/ +def leftStrOf (r : OuterStr L m) (π : InnerStr m) : LeftStr L := + glue Q hQ (fun k => .p (π k).1 (π k).2) (fun k => .x (r k)) + +/-- The right string with inner set `Q`, inner part `π` and outer part `c`. -/ +def rightStrOf (π : InnerStr m) (c : OuterStr L m) : RightStr L := + glue Q hQ (fun k => .q (π k).1 (π k).2) (fun k => .y (c k)) + +/-- The output string with inner set `Q`, row `r` and column `c` (Section 2.3.3: the output strings +with inner set Q index the entries of the product X_Q Y_Q): it has `z₀` at the levels of `Q`, and +at the `k`-th lowest level outside `Q` it has `z_{r_k c_k}`. -/ +def outStrOf (r c : OuterStr L m) : OutStr L := + glue Q hQ (fun _ => .z0) (fun k => .z (r k) (c k)) + +/-- The inner set of `leftStrOf Q hQ r π` is `Q`. -/ +@[simp] +theorem innerSetL_leftStrOf (r : OuterStr L m) (π : InnerStr m) : + innerSetL (leftStrOf Q hQ r π) = Q := + filter_glue Q hQ LeftVar.IsInner (fun _ => trivial) fun _ => id + +/-- The inner set of `rightStrOf Q hQ π c` is `Q`. -/ +@[simp] +theorem innerSetR_rightStrOf (π : InnerStr m) (c : OuterStr L m) : + innerSetR (rightStrOf Q hQ π c) = Q := + filter_glue Q hQ RightVar.IsInner (fun _ => trivial) fun _ => id + +/-- The inner set of `outStrOf Q hQ r c` is `Q`. -/ +@[simp] +theorem innerSetO_outStrOf (r c : OuterStr L m) : innerSetO (outStrOf Q hQ r c) = Q := + filter_glue Q hQ OutVar.IsInner (fun _ => trivial) fun _ => id + +/-- The outer part of `leftStrOf Q hQ r π` is `r`. -/ +@[simp] +theorem outerPartL_leftStrOf (r : OuterStr L m) (π : InnerStr m) + (h : (innerSetL (leftStrOf Q hQ r π)).card = m) : outerPartL (leftStrOf Q hQ r π) h = r := + funext fun k => congrArg LeftVar.outerIndex + (glue_outerLevel_of_eq Q hQ _ _ (innerSetL_leftStrOf Q hQ r π) h k) + +/-- The inner part of `leftStrOf Q hQ r π` is `π`. -/ +@[simp] +theorem innerPartL_leftStrOf (r : OuterStr L m) (π : InnerStr m) + (h : (innerSetL (leftStrOf Q hQ r π)).card = m) : innerPartL (leftStrOf Q hQ r π) h = π := + funext fun k => congrArg LeftVar.innerIndex + (glue_innerLevel_of_eq Q hQ _ _ (innerSetL_leftStrOf Q hQ r π) h k) + +/-- The inner part of `rightStrOf Q hQ π c` is `π`. -/ +@[simp] +theorem innerPartR_rightStrOf (π : InnerStr m) (c : OuterStr L m) + (h : (innerSetR (rightStrOf Q hQ π c)).card = m) : innerPartR (rightStrOf Q hQ π c) h = π := + funext fun k => congrArg RightVar.innerIndex + (glue_innerLevel_of_eq Q hQ _ _ (innerSetR_rightStrOf Q hQ π c) h k) + +/-- The outer part of `rightStrOf Q hQ π c` is `c`. -/ +@[simp] +theorem outerPartR_rightStrOf (π : InnerStr m) (c : OuterStr L m) + (h : (innerSetR (rightStrOf Q hQ π c)).card = m) : outerPartR (rightStrOf Q hQ π c) h = c := + funext fun k => congrArg RightVar.outerIndex + (glue_outerLevel_of_eq Q hQ _ _ (innerSetR_rightStrOf Q hQ π c) h k) + +/-- The variable of `outStrOf Q hQ r c` at the `k`-th lowest level outside its inner set. -/ +private theorem outStrOf_outerLevel (r c : OuterStr L m) + (h : (innerSetO (outStrOf Q hQ r c)).card = m) (k : Fin (L - m)) : + outStrOf Q hQ r c (((innerSetO (outStrOf Q hQ r c))ᶜ.orderEmbOfFin (card_compl_of_card_eq + (innerSetO (outStrOf Q hQ r c)) h)) k) = .z (r k) (c k) := by + rw [outerLevel_congr Q hQ (innerSetO_outStrOf Q hQ r c) h, outStrOf, glue_outerLevel] + +/-- The row of `outStrOf Q hQ r c` is `r`. -/ +@[simp] +theorem rowO_outStrOf (r c : OuterStr L m) (h : (innerSetO (outStrOf Q hQ r c)).card = m) : + rowO (outStrOf Q hQ r c) h = r := + funext fun k => congrArg OutVar.rowIndex (outStrOf_outerLevel Q hQ r c h k) + +/-- The column of `outStrOf Q hQ r c` is `c`. -/ +@[simp] +theorem colO_outStrOf (r c : OuterStr L m) (h : (innerSetO (outStrOf Q hQ r c)).card = m) : + colO (outStrOf Q hQ r c) h = c := + funext fun k => congrArg OutVar.colIndex (outStrOf_outerLevel Q hQ r c h k) + +variable {Q} + +/-- A left string with inner set `Q` is the string `leftStrOf` of its outer part and its inner +part. -/ +theorem leftStr_eq_leftStrOf {u : LeftStr L} (hu : innerSetL u = Q) (h : (innerSetL u).card = m) : + u = leftStrOf Q hQ (outerPartL u h) (innerPartL u h) := by + subst hu + refine glue_index _ h LeftVar.IsInner (inner := fun π : Fin 2 × Fin 2 => .p π.1 π.2) ?_ ?_ rfl + · rintro (i | ⟨i, j⟩) hs + exacts [hs.elim, rfl] + · rintro (i | ⟨i, j⟩) hs + exacts [rfl, (hs trivial).elim] + +/-- A right string with inner set `Q` is the string `rightStrOf` of its inner part and its outer +part. -/ +theorem rightStr_eq_rightStrOf {v : RightStr L} (hv : innerSetR v = Q) + (h : (innerSetR v).card = m) : v = rightStrOf Q hQ (innerPartR v h) (outerPartR v h) := by + subst hv + refine glue_index _ h RightVar.IsInner (inner := fun π : Fin 2 × Fin 2 => .q π.1 π.2) ?_ ?_ rfl + · rintro (j | ⟨i, j⟩) ht + exacts [ht.elim, rfl] + · rintro (j | ⟨i, j⟩) ht + exacts [rfl, (ht trivial).elim] + +/-- An output string with inner set `Q` is the string `outStrOf` of its row and its column. -/ +theorem outStr_eq_outStrOf {w : OutStr L} (hw : innerSetO w = Q) (h : (innerSetO w).card = m) : + w = outStrOf Q hQ (rowO w h) (colO w h) := by + subst hw + refine glue_index _ h OutVar.IsInner (inner := fun _ : Unit => .z0) + (outer := fun rc : Fin 3 × Fin 3 => .z rc.1 rc.2) (innerIndex := fun _ => ()) + (outerIndex := fun z => (z.rowIndex, z.colIndex)) ?_ ?_ rfl + · rintro (⟨i, j⟩ | _) hz + exacts [hz.elim, rfl] + · rintro (⟨i, j⟩ | _) hz + exacts [rfl, (hz trivial).elim] + +variable {hQ} + +/-- Left strings with the same inner set and different outer or inner parts are different. -/ +theorem leftStrOf_inj {r r' : OuterStr L m} {π π' : InnerStr m} : + leftStrOf Q hQ r π = leftStrOf Q hQ r' π' ↔ r = r' ∧ π = π' := by + refine ⟨fun h => ?_, by rintro ⟨rfl, rfl⟩; rfl⟩ + obtain ⟨hp, hx⟩ := (glue_inj Q hQ).mp h + exact ⟨funext fun k => LeftVar.x.inj (congrFun hx k), + funext fun k => Prod.ext (LeftVar.p.inj (congrFun hp k)).1 (LeftVar.p.inj (congrFun hp k)).2⟩ + +/-- Output strings with different inner sets, rows or columns are different. -/ +theorem outStrOf_inj {Q' : Finset (Fin L)} {hQ' : Q'.card = m} {r c r' c' : OuterStr L m} : + outStrOf Q hQ r c = outStrOf Q' hQ' r' c' ↔ Q = Q' ∧ r = r' ∧ c = c' := by + refine ⟨fun h => ?_, by rintro ⟨rfl, rfl, rfl⟩; rfl⟩ + obtain rfl : Q = Q' := by rw [← innerSetO_outStrOf Q hQ r c, h, innerSetO_outStrOf] + have hz := fun k => OutVar.z.inj (congrFun ((glue_inj Q hQ).mp h).2 k) + exact ⟨rfl, funext fun k => (hz k).1, funext fun k => (hz k).2⟩ + +variable (Q) (hQ) +include hQ + +/-- Section 2.3.3: A string is determined by its inner set, its outer part, and its inner part, +and the left strings with inner set Q index the entries of such a matrix: for each set `Q` of `m` +levels, each row `r` and each column `π` there is exactly one left string with inner set `Q`, outer +part `r` and inner part `π`. -/ +theorem existsUnique_leftStr (r : OuterStr L m) (π : InnerStr m) : + ∃! u : LeftStr L, innerSetL u = Q + ∧ ∃ h : (innerSetL u).card = m, outerPartL u h = r ∧ innerPartL u h = π := by + have hset := innerSetL_leftStrOf Q hQ r π + refine ⟨leftStrOf Q hQ r π, ⟨hset, hset.symm ▸ hQ, outerPartL_leftStrOf Q hQ r π _, + innerPartL_leftStrOf Q hQ r π _⟩, ?_⟩ + rintro u ⟨hu, h, rfl, rfl⟩ + exact leftStr_eq_leftStrOf hQ hu h + +/-- Section 2.3.3: Similarly, the right strings v with inner set Q index the entries of a D × N₀ +matrix. -/ +theorem existsUnique_rightStr (π : InnerStr m) (c : OuterStr L m) : + ∃! v : RightStr L, innerSetR v = Q + ∧ ∃ h : (innerSetR v).card = m, innerPartR v h = π ∧ outerPartR v h = c := by + have hset := innerSetR_rightStrOf Q hQ π c + refine ⟨rightStrOf Q hQ π c, ⟨hset, hset.symm ▸ hQ, + innerPartR_rightStrOf Q hQ π c _, outerPartR_rightStrOf Q hQ π c _⟩, ?_⟩ + rintro v ⟨hv, h, rfl, rfl⟩ + exact rightStr_eq_rightStrOf hQ hv h + +/-- Section 2.3.3: Finally, the output strings with inner set Q index the entries of the product +X_Q Y_Q. -/ +theorem existsUnique_outStr (r c : OuterStr L m) : + ∃! w : OutStr L, innerSetO w = Q + ∧ ∃ h : (innerSetO w).card = m, rowO w h = r ∧ colO w h = c := by + have hset := innerSetO_outStrOf Q hQ r c + refine ⟨outStrOf Q hQ r c, ⟨hset, hset.symm ▸ hQ, rowO_outStrOf Q hQ r c _, + colO_outStrOf Q hQ r c _⟩, ?_⟩ + rintro w ⟨hw, h, rfl, rfl⟩ + exact outStr_eq_outStrOf hQ hw h + +end Cslib.Algorithms.MatrixMultiplication.Schonhage diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Magnitude.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Magnitude.lean new file mode 100644 index 000000000..71e660aa6 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Magnitude.lean @@ -0,0 +1,249 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.ThinProduct +public import Complexity.Foundations.Algebra.FiniteMagnitude + +/-! +# Finite intermediate magnitudes and auxiliary counts + +## Main results + +* `abs_bandArrayL_le`, `abs_bandArrayR_le`: input entries are unchanged or zero. +* `abs_encodingL_le`, `abs_encodingR_le`, `Pruned.abs_result_le`: explicit integer magnitude bounds. +* `minutiae_total_le`: bound on layout subsets, tiles and wanted positions. + +These bounds do not certify a machine word width or native execution. + +## References + +Adapted selectively from [AlmanWilliams2026, Section 2] and +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical +counts and identities; they give no native program or instruction-cost certificate. +Source-derived descriptions below are paraphrases. +-/ + +@[expose] public section + +open Finset + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-! ### The remaining minutiae -/ + +/-- `2^{m/9} ≤ 2^m`. -/ +private theorem two_rpow_ninth_le (m : ℕ) : (2 : ℝ) ^ ((m : ℝ) / 9) ≤ (2 : ℝ) ^ m := by + rw [← Real.rpow_natCast] + have hm : (0 : ℝ) ≤ m := Nat.cast_nonneg m + exact Real.rpow_le_rpow_of_exponent_le (by norm_num) (by linarith) + +/-- A number that is at least `2^{m/9}` is at least 1 after multiplication by `2^{-m/9}`. -/ +private theorem one_le_saving_mul {m : ℕ} {x : ℝ} (hx : (2 : ℝ) ^ ((m : ℝ) / 9) ≤ x) : + 1 ≤ saving m * x := by + rw [← saving_mul_two_rpow m] + exact mul_le_mul_of_nonneg_left hx (saving_pos m).le + +/-- Section 2.4.4, in the remaining minutiae: K ≤ N. -/ +theorem K_le_of_D_pow_le (m N : ℕ) (hN : D m ^ 18 ≤ N) : K (19 * m) m ≤ N := + (Nat.le_mul_of_pos_right _ (N0_pos _ _)).trans (K_mul_N0_le m N hN) + +/-- Section 2.4.4, in the remaining minutiae: there are at most 4N²/N₀ tiles, where `N` is the +padded size. -/ +theorem card_tiles_mul_N0_le {L m : ℕ} (hmL : m ≤ L) (N : ℕ) : + (tiles L m N).card * N0 L m ≤ 4 * padN L m N ^ 2 := by + -- `N₀ ≤ K N₀² = M`, and there are at most `4N²/M` tiles. + refine le_trans (Nat.mul_le_mul_left _ ?_) (card_tiles_mul_M_le L m N) + calc N0 L m = 1 * N0 L m ^ 1 := by ring + _ ≤ K L m * N0 L m ^ 2 := + Nat.mul_le_mul (Nat.choose_pos hmL) (Nat.pow_le_pow_right (N0_pos L m) (by norm_num)) + +/-- Section 2.4.4, in the remaining minutiae: `N ≥ 2^{m/9}`. -/ +theorem two_rpow_le_of_D_pow_le (m N : ℕ) (hN : D m ^ 18 ≤ N) : + (2 : ℝ) ^ ((m : ℝ) / 9) ≤ (N : ℝ) := by + -- `2^{m/9} ≤ 2^m ≤ 4^{18m} = D^18 ≤ N`. + have hpow : 2 ^ m ≤ N := + calc 2 ^ m ≤ 4 ^ m := Nat.pow_le_pow_left (by norm_num) m + _ ≤ (4 ^ m) ^ 18 := Nat.le_self_pow (by norm_num) _ + _ ≤ N := hN + exact (two_rpow_ninth_le m).trans (by exact_mod_cast hpow) + +/-- Section 2.4.4, in the remaining minutiae: `N₀ ≥ 2^{m/9}`. -/ +theorem two_rpow_le_N0 (m : ℕ) : (2 : ℝ) ^ ((m : ℝ) / 9) ≤ (N0 (19 * m) m : ℝ) := by + -- `2^{m/9} ≤ 2^m ≤ 3^{18m} = N₀`. + have hpow : 2 ^ m ≤ N0 (19 * m) m := + calc 2 ^ m ≤ 3 ^ m := Nat.pow_le_pow_left (by norm_num) m + _ ≤ 3 ^ (19 * m - m) := Nat.pow_le_pow_right (by norm_num) (by omega) + exact (two_rpow_ninth_le m).trans (by exact_mod_cast hpow) + +/-- The three counts of the remaining minutiae (Section 2.4.4), one by one: the subsets in the +table, the tiles, and the positions of `W`. -/ +private theorem minutiae_parts (m N : ℕ) (W : Finset (Fin N × Fin N)) (hN : D m ^ 18 ≤ N) + (hW : W.card * 2 ^ m ≤ N ^ 2) : + (K (19 * m) m : ℝ) ≤ saving m * (N : ℝ) ^ 2 + ∧ ((tiles (19 * m) m N).card : ℝ) ≤ 4 * saving m * (padN (19 * m) m N : ℝ) ^ 2 + ∧ (W.card : ℝ) ≤ saving m * (N : ℝ) ^ 2 := by + have hc := saving_pos m + refine ⟨?_, ?_, ?_⟩ + · -- The subsets in the table: `K ≤ N ≤ 2^{-m/9} N²`. + calc (K (19 * m) m : ℝ) ≤ N := by exact_mod_cast K_le_of_D_pow_le m N hN + _ ≤ saving m * N * N := + le_mul_of_one_le_left N.cast_nonneg (one_le_saving_mul (two_rpow_le_of_D_pow_le m N hN)) + _ = saving m * (N : ℝ) ^ 2 := by ring + · -- The tiles: at most `4N²/N₀ ≤ 4 · 2^{-m/9} N²`. + have hT : ((tiles (19 * m) m N).card : ℝ) * N0 (19 * m) m + ≤ 4 * (padN (19 * m) m N : ℝ) ^ 2 := by + exact_mod_cast card_tiles_mul_N0_le (show m ≤ 19 * m by omega) N + calc ((tiles (19 * m) m N).card : ℝ) + ≤ (tiles (19 * m) m N).card * (saving m * N0 (19 * m) m) := + le_mul_of_one_le_right (Nat.cast_nonneg _) (one_le_saving_mul (two_rpow_le_N0 m)) + _ = saving m * ((tiles (19 * m) m N).card * N0 (19 * m) m) := by ring + _ ≤ saving m * (4 * (padN (19 * m) m N : ℝ) ^ 2) := by gcongr + _ = 4 * saving m * (padN (19 * m) m N : ℝ) ^ 2 := by ring + · -- The positions: `|W| ≤ 2^{-m} N² ≤ 2^{-m/9} N²`. + have hW' : (W.card : ℝ) * (2 : ℝ) ^ m ≤ (N : ℝ) ^ 2 := by exact_mod_cast hW + calc (W.card : ℝ) = saving m * (W.card * (2 : ℝ) ^ ((m : ℝ) / 9)) := by + rw [mul_left_comm, saving_mul_two_rpow, mul_one] + _ ≤ saving m * (W.card * (2 : ℝ) ^ m) := by gcongr; exact two_rpow_ninth_le m + _ ≤ saving m * (N : ℝ) ^ 2 := by gcongr + +/-- Section 2.4.4: O(L) operations per subset, per tile, and per position of W, which is +O(L 2^{-m/9} N²) in all: the number of these objects, with the `K₀²` subsets counted as at most +`K`, as the paper does (as K ≤ N). Here `N` is the size of the input. The constant is +`18 = 1 + 16 + 1`, because the padding at most doubles `N`. -/ +theorem minutiae_total_le (m N : ℕ) (W : Finset (Fin N × Fin N)) (hN : D m ^ 18 ≤ N) + (hW : W.card * 2 ^ m ≤ N ^ 2) : + ((K (19 * m) m + (tiles (19 * m) m N).card + W.card : ℕ) : ℝ) + ≤ 18 * (D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2 := by + obtain ⟨hK, hT, hW'⟩ := minutiae_parts m N W hN hW + have hc := saving_pos m + have hpad : (padN (19 * m) m N : ℝ) ≤ 2 * (N : ℝ) := by + exact_mod_cast padN_le_two_mul_of_D_pow_le m N hN + have hsq : saving m * (padN (19 * m) m N : ℝ) ^ 2 ≤ saving m * (2 * (N : ℝ)) ^ 2 := by gcongr + rw [← saving_eq_D_rpow] + push_cast + -- `K + |tiles| + |W| ≤ c N² + 4 c (2N)² + c N² = 18 c N²` with `c = 2^{-m/9}`. + linarith [hK, hT, hW', hsq] + +/-! ### The word size -/ + +/-- The subsets in the table are distinct (Section 2.3.4), so a sum over the table that selects the +subset `Q` is one of its terms or 0. -/ +private theorem sum_table_eq_zero_or {L m : ℕ} {A : Type*} [AddCommMonoid A] (lay : Layout L m) + (F : Fin (K0 L m) × Fin (K0 L m) → A) (Q : Finset (Fin L)) : + (∑ gh, if lay.table gh = Q then F gh else 0) = 0 + ∨ ∃ gh, (∑ gh', if lay.table gh' = Q then F gh' else 0) = F gh := by + by_cases hQ : ∃ gh, lay.table gh = Q + · obtain ⟨gh, rfl⟩ := hQ + exact .inr ⟨gh, by simp [lay.table_injective.eq_iff]⟩ + · exact .inl (sum_eq_zero fun gh _ => ite_eq_right (not_exists.mp hQ gh)) + +/-- Section 2.4.4, word size: The input entries are of this size, by assumption. Every entry of +the input array of a row band is an entry of `X` or 0. + +NOTE. `0 ≤ A` is needed only when `N = 0`. -/ +theorem abs_bandArrayL_le {L m N : ℕ} (lay : Layout L m) {X : Matrix (Fin N) (Fin (D m)) ℤ} {A : ℤ} + (hA : 0 ≤ A) (hX : ∀ I k, |X I k| ≤ A) (β : ℕ) (u : LeftStr L) : + |bandArrayL lay X β u| ≤ A := by + unfold bandArrayL arrayL bandFamilyL + split_ifs with h + · rcases sum_table_eq_zero_or lay (fun gh => rowBlock lay X (β * K0 L m + gh.1)) (innerSetL u) + with hzero | ⟨gh, hgh⟩ + · rw [hzero] + simpa using hA + · rw [hgh] + unfold rowBlock padRows + split_ifs + · exact hX _ _ + · simpa using hA + · simpa using hA + +/-- Section 2.4.4, word size: The input entries are of this size, by assumption. Every entry of +the input array of a column band is an entry of `Y` or 0. + +NOTE. `0 ≤ B` is needed only when `N = 0`. -/ +theorem abs_bandArrayR_le {L m N : ℕ} (lay : Layout L m) {Y : Matrix (Fin (D m)) (Fin N) ℤ} {B : ℤ} + (hB : 0 ≤ B) (hY : ∀ k J, |Y k J| ≤ B) (β : ℕ) (v : RightStr L) : + |bandArrayR lay Y β v| ≤ B := by + unfold bandArrayR arrayR bandFamilyR + split_ifs with h + · rcases sum_table_eq_zero_or lay (fun gh => colBlock lay Y (β * K0 L m + gh.2)) (innerSetR v) + with hzero | ⟨gh, hgh⟩ + · rw [hzero] + simpa using hB + · rw [hgh] + unfold colBlock padCols + split_ifs + · exact hY _ _ + · simpa using hB + · simpa using hB + +/-- A combination of the entries of an array, with a product of coefficients 0, 1 or -1 for each +entry, is at most the number of entries times the largest absolute value of an entry. -/ +theorem abs_encodeWith_le {α : Type} [Fintype α] {c : Term → α → ℤ} (hc : ∀ lam s, |c lam s| ≤ 1) + {L : ℕ} {a : (Fin L → α) → ℤ} {A : ℤ} (ha : ∀ u, |a u| ≤ A) (τ : Leaf L) : + |encodeWith c τ a| ≤ Fintype.card (Fin L → α) * A := by + have h := abs_sum_mul_le univ (c := fun u => ∏ ℓ, c (τ ℓ) (u ℓ)) + (fun u _ => abs_prod_le_one _ fun ℓ _ => hc _ _) fun u _ => ha u + simpa only [encodeWith, mul_comm, card_univ] using h + +/-- Section 2.4.4, word size: Every number in an encoding […] is a ±1 combination of at most 7^L +input entries, so its absolute value is at most `7^L` times the largest absolute value of an input +entry. For the encoding of `a`. -/ +theorem abs_encodingL_le {L : ℕ} {a : LeftStr L → ℤ} {A : ℤ} (ha : ∀ u, |a u| ≤ A) (τ : Leaf L) : + |encodingL a τ| ≤ 7 ^ L * A := by + have h := abs_encodeWith_le abs_phi_le_one ha τ + rw [card_leftStr] at h + exact_mod_cast h + +/-- Section 2.4.4, word size: Every number in an encoding […] is a ±1 combination of at most 7^L +input entries. For the encoding of `b`. -/ +theorem abs_encodingR_le {L : ℕ} {b : RightStr L → ℤ} {B : ℤ} (hb : ∀ v, |b v| ≤ B) (τ : Leaf L) : + |encodingR b τ| ≤ 7 ^ L * B := by + have h := abs_encodeWith_le abs_psi_le_one hb τ + rw [card_rightStr] at h + exact_mod_cast h + +/-- A returned pruned entry is a sum of at most `10^n` encoded products (Section 2.4.4). +This bounds the mathematical result, not native execution or machine width. -/ +theorem Pruned.abs_result_le {n : ℕ} (encA encB : Leaf n → ℤ) (A B : ℤ) (hA : ∀ τ, |encA τ| ≤ A) + (hB : ∀ τ, |encB τ| ≤ B) (S : Finset (OutStr n)) (w : OutStr n) : + |Pruned.result n encA encB S w| ≤ 10 ^ n * (A * B) := by + have hA0 : 0 ≤ A := (abs_nonneg _).trans (hA fun _ => Term.P0) + have hAB : 0 ≤ A * B := mul_nonneg hA0 ((abs_nonneg _).trans (hB fun _ => Term.P0)) + -- The value is 0, or the sum over some of the `10^n` leaves of the product of the two numbers + -- looked up there. + rw [Pruned.result_eq_piecewise_sum] + simp only [Finset.piecewise] + split_ifs + · calc |∑ τ : Leaf n with Leaf.Contributes τ w, encA τ * encB τ| + ≤ (univ.filter fun τ : Leaf n => Leaf.Contributes τ w).card * (A * B) := + abs_sum_le_card_mul _ fun τ _ => by + rw [abs_mul] + exact mul_le_mul (hA τ) (hB τ) (abs_nonneg _) hA0 + _ ≤ 10 ^ n * (A * B) := by + gcongr + exact_mod_cast (card_le_univ _).trans_eq (card_vertex n) + · simp only [Pi.zero_apply, abs_zero] + positivity + +/-- Section 2.4.4, word size: 10^L ≤ N² by (6), for `L = 19m` and `N ≥ D^18`. -/ +theorem ten_pow_le_sq (m N : ℕ) (hN : D m ^ 18 ≤ N) : 10 ^ (19 * m) ≤ N ^ 2 := by + -- `N₀ ≤ K N₀ ≤ N` and `2^{-m/9} ≤ 1`, so equation (6) gives `10^L ≤ N N₀ ≤ N²`. + have hN0 : (N0 (19 * m) m : ℝ) ≤ N := by + exact_mod_cast (Nat.le_mul_of_pos_left _ (Nat.choose_pos (by omega))).trans + (K_mul_N0_le m N hN) + have hc := saving_le_one m + have hreal : (10 : ℝ) ^ (19 * m) ≤ (N : ℝ) ^ 2 := + calc (10 : ℝ) ^ (19 * m) ≤ saving m * N * N0 (19 * m) m := encoding_size_le m N hN + _ ≤ 1 * N * N := by gcongr + _ = (N : ℝ) ^ 2 := by ring + exact_mod_cast hreal + +end Cslib.Algorithms.MatrixMultiplication.Schonhage diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Orders.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Orders.lean new file mode 100644 index 000000000..a11ba4e55 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Orders.lean @@ -0,0 +1,320 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Levels +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Pruned +public import Complexity.Foundations.Combinatorics.FunctionPlaces +public import Mathlib.Algebra.Order.Ring.Rat +import Mathlib.Tactic.Linarith +import Mathlib.Tactic.Ring +import Mathlib.Tactic.Positivity + +/-! +# Orders and counts of contributing leaves + +## Main definitions + +* `order`, `alpha`, `beta`: leaf order and its two counting distributions. + +## Main results + +* `card_contributing_of_order`, `card_filter_order_eq`, `card_outStr_of_leaf`: exact counts. +* `two_pow_mul_beta_le`: geometric decay when `L = 19m`. + +## References + +Adapted selectively from [AlmanWilliams2026, Section 2] and +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical +counts and identities; they give no native program or instruction-cost certificate. +Source-derived descriptions below are paraphrases. +-/ + +@[expose] public section + +open Finset + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The set of levels at which a leaf chooses `P₀`. -/ +def P0Levels {L : ℕ} (τ : Leaf L) : Finset (Fin L) := univ.filter fun ℓ => τ ℓ = Term.P0 + +/-- Section 2.4.3: we define the order of a leaf as m minus the number of levels at which it +chooses P₀. The order is an integer; it is negative for a leaf that chooses `P₀` at more than `m` +levels. -/ +def order {L : ℕ} (m : ℕ) (τ : Leaf L) : ℤ := (m : ℤ) - ((P0Levels τ).card : ℤ) + +/-- Section 2.4.3: α_d := binom(m, d) 9^d. -/ +def alpha (m d : ℕ) : ℕ := m.choose d * 9 ^ d + +/-- Section 2.4.3: β_d := binom(L, m-d) 9^{L-m+d}. Meant for `d ≤ m ≤ L`; outside this range the +subtractions of natural numbers are cut off at 0 and the value has no meaning. -/ +def beta (L m d : ℕ) : ℕ := L.choose (m - d) * 9 ^ (L - m + d) + +/-! ### One level: which terms contribute to which variables -/ + +/-- The private term of `z` is `P₀` exactly if `z = z₀`. -/ +theorem OutVar.privateTerm_eq_P0_iff (z : OutVar) : z.privateTerm = Term.P0 ↔ z.IsInner := by + cases z <;> simp [OutVar.IsInner, OutVar.privateTerm] + +/-- One term is `P₀`. -/ +private theorem card_term_P0 : (univ.filter fun lam : Term => lam = Term.P0).card = 1 := by decide + +/-- Nine terms are different from `P₀`. -/ +private theorem card_term_ne_P0 : (univ.filter fun lam : Term => ¬ lam = Term.P0).card = 9 := by + decide + +/-- Among the terms contributing to `z`, the number of those equal to `P₀`: one if `z = z₀`, none if +`z = z_ij`. -/ +private theorem card_contributing_P0 (z : OutVar) : + ((univ.filter fun lam : Term => lam.Contributes z).filter fun lam => lam = Term.P0).card + = if z.IsInner then 1 else 0 := by + decide +revert + +/-- Among the terms contributing to `z`, the number of those different from `P₀`: nine if `z = z₀`, +one if `z = z_ij`. -/ +private theorem card_contributing_ne_P0 (z : OutVar) : + ((univ.filter fun lam : Term => lam.Contributes z).filter fun lam => ¬ lam = Term.P0).card + = if z.IsInner then 9 else 1 := by + decide +revert + +/-- The number of terms contributing to `z`: ten if `z = z₀`, one if `z = z_ij`. -/ +private theorem card_contributing_term (z : OutVar) : + (univ.filter fun lam : Term => lam.Contributes z).card = if z.IsInner then 10 else 1 := by + decide +revert + +/-- Among the output variables to which `lam` contributes, exactly one is inner. -/ +private theorem card_contributed_inner (lam : Term) : + ((univ.filter fun z : OutVar => lam.Contributes z).filter fun z => z.IsInner).card = 1 := by + decide +revert + +/-- Among the output variables to which `lam` contributes, the number of outer ones: none if +`lam = P₀`, one if `lam = P_ij`. -/ +private theorem card_contributed_outer (lam : Term) : + ((univ.filter fun z : OutVar => lam.Contributes z).filter fun z => ¬ z.IsInner).card + = if lam = Term.P0 then 0 else 1 := by + decide +revert + +/-! ### The order of a leaf -/ + +/-- Membership in the set of levels at which a leaf chooses `P₀`. -/ +@[simp] +theorem mem_P0Levels {L : ℕ} {τ : Leaf L} {ℓ : Fin L} : ℓ ∈ P0Levels τ ↔ τ ℓ = Term.P0 := by + simp [P0Levels] + +/-- Membership in the inner set of an output string. -/ +@[simp] +theorem mem_innerSetO {L : ℕ} {η : OutStr L} {ℓ : Fin L} : ℓ ∈ innerSetO η ↔ (η ℓ).IsInner := by + simp [innerSetO] + +/-- Section 2.4.3: A leaf contributing to an output string chooses P₀ only at levels of the output +string's inner set. -/ +theorem P0Levels_subset {L : ℕ} {τ : Leaf L} {w : OutStr L} (h : Leaf.Contributes τ w) : + P0Levels τ ⊆ innerSetO w := by + intro ℓ hℓ + rw [mem_P0Levels] at hℓ + rw [mem_innerSetO] + rcases (Term.contributes_iff _ _).mp (h ℓ) with hinner | hprivate + · exact hinner + · exact (OutVar.privateTerm_eq_P0_iff _).mp (hprivate.symm.trans hℓ) + +/-- Section 2.4.3: so its order is at least 0. -/ +theorem order_nonneg {L m : ℕ} {w : OutStr L} (hw : (innerSetO w).card = m) {τ : Leaf L} + (h : Leaf.Contributes τ w) : 0 ≤ order m τ := by + have hc := card_le_card (P0Levels_subset h) + unfold order + omega + +/-- In the proof of Lemma 11: every leaf of leaves(U) has an order 0 ≤ d ≤ m. The upper +bound holds for every leaf. -/ +theorem order_le {L : ℕ} (m : ℕ) (τ : Leaf L) : order m τ ≤ m := by + unfold order + omega + +/-- A leaf has order `d` exactly if it chooses `P₀` at `m - d` levels. -/ +theorem order_eq_iff {L : ℕ} (m d : ℕ) (τ : Leaf L) : + order m τ = d ↔ (P0Levels τ).card + d = m := by + unfold order + omega + +/-! ### The counts -/ + +/-- Section 2.4.3: A single output string has 10^m […] leaves contributing to it. -/ +theorem card_filter_contributes {L m : ℕ} (w : OutStr L) (hw : (innerSetO w).card = m) : + (univ.filter fun τ : Leaf L => Leaf.Contributes τ w).card = 10 ^ m := by + -- Ten terms contribute to `z₀` and one to `z_ij`. + simp only [Leaf.filter_contributes_eq_piFinset, Fintype.card_piFinset, + card_contributing_term] + rw [prod_ite, prod_const, prod_const_one, mul_one, ← hw] + rfl + +/-- Section 2.4.3: exactly α_d := binom(m, d) 9^d leaves of order d contribute to an output +string. As everywhere in Section 2.4.3, the output strings are those whose inner sets have exactly +m elements. -/ +theorem card_contributing_of_order {L m : ℕ} (w : OutStr L) (hw : (innerSetO w).card = m) + (d : ℕ) : + (univ.filter fun τ : Leaf L => Leaf.Contributes τ w ∧ order m τ = d).card = alpha m d := by + by_cases hd : d ≤ m + · -- Sort the strings of terms that contribute to `w` level by level and have `P₀` at exactly + -- `m - d` levels by the set `S` of these levels. + have hcount := Finset.card_pi_places_card + (fun ℓ => univ.filter fun lam : Term => lam.Contributes (w ℓ)) (fun lam => lam = Term.P0) + (m - d) + simp only [card_contributing_P0, card_contributing_ne_P0] at hcount + -- Given `S`, there is no such string unless `S ⊆ Q`. Then there are nine choices at each of + -- the `|Q| - |S| = d` levels of `Q` outside `S`, and one choice at every other level. + have hgiven : ∀ S ∈ powersetCard (m - d) (univ : Finset (Fin L)), + (∏ ℓ, if ℓ ∈ S then (if (w ℓ).IsInner then 1 else 0) else (if (w ℓ).IsInner then 9 else 1)) + = if S ⊆ innerSetO w then 9 ^ d else 0 := by + intro S hS + have hQS := prod_ite_ite_subset (innerSetO w) S 9 + rw [hw, (mem_powersetCard.mp hS).2, Nat.sub_sub_self hd] at hQS + rw [← hQS] + exact prod_congr rfl fun ℓ _ => by simp + -- There are `binom(m, m - d) = binom(m, d)` sets `S ⊆ Q`. + rw [sum_congr rfl hgiven, sum_powersetCard_ite_subset, hw, Nat.choose_symm hd] at hcount + -- These strings are the leaves of order `d` that contribute to `w`. + rw [alpha, ← hcount] + refine congrArg card (Finset.ext fun τ => ?_) + simp only [mem_filter, mem_univ, true_and, order_eq_iff] + exact and_congr Iff.rfl (by unfold P0Levels; omega) + · -- No leaf has order more than `m`. + rw [alpha, Nat.choose_eq_zero_of_lt (by omega), zero_mul, card_eq_zero] + refine filter_eq_empty_iff.mpr fun τ _ h => ?_ + have hle := order_le m τ + omega + +/-- Section 2.4.3: The total number of leaves of order d in the entire recursion tree is +β_d := binom(L, m-d) 9^{L-m+d}. + +NOTE. `d ≤ m` and `m ≤ L` are left implicit in the paper. -/ +theorem card_filter_order_eq {L m : ℕ} (hmL : m ≤ L) (d : ℕ) (hd : d ≤ m) : + (univ.filter fun τ : Leaf L => order m τ = d).card = beta L m d := by + -- The leaves of order `d` are the strings of terms with `P₀` at exactly `m - d` levels. + have hsort : (univ.filter fun τ : Leaf L => order m τ = d) + = univ.filter fun τ : Leaf L => (univ.filter fun ℓ => τ ℓ = Term.P0).card = m - d := by + ext τ + simp only [mem_filter, mem_univ, true_and, order_eq_iff] + unfold P0Levels + omega + rw [hsort, Finset.card_places_card (fun lam : Term => lam = Term.P0) (m - d), card_term_P0, + card_term_ne_P0, one_pow, one_mul, Fintype.card_fin, beta, + show L - (m - d) = L - m + d by omega] + +/-- Section 2.4.3: In particular, β₀ = binom(L, m) 9^{L-m}. -/ +theorem beta_zero (L m : ℕ) : beta L m 0 = L.choose m * 9 ^ (L - m) := by + simp [beta] + +/-- Section 2.4.3: β₀ = binom(L, m) 9^{L-m} = M. -/ +theorem beta_zero_eq_M (L m : ℕ) : beta L m 0 = M L m := by + rw [beta_zero, M, K, N0, ← pow_mul, mul_comm (L - m) 2, pow_mul] + norm_num + +/-- Section 2.4.3: A leaf of order d contributes to binom(L-m+d, d) output strings, one for each +inner set of size m that contains the m-d levels at which it chooses P₀. Only the number is +stated. As everywhere in Section 2.4.3, the output strings are those whose inner sets have exactly +m elements. + +NOTE. `m ≤ L` is left implicit in the paper. -/ +theorem card_outStr_of_leaf {L m : ℕ} (hmL : m ≤ L) (d : ℕ) (τ : Leaf L) + (hτ : order m τ = d) : + (univ.filter fun w : OutStr L => (innerSetO w).card = m ∧ Leaf.Contributes τ w).card + = (L - m + d).choose d := by + have hP0 : (P0Levels τ).card + d = m := (order_eq_iff m d τ).mp hτ + -- Sort the output strings to which `τ` contributes level by level, with `z₀` at exactly `m` + -- levels, by the set `S` of these levels, which is their inner set. + have hcount := Finset.card_pi_places_card + (fun ℓ => univ.filter fun z : OutVar => (τ ℓ).Contributes z) (fun z => z.IsInner) m + simp only [card_contributed_inner, card_contributed_outer] at hcount + -- Given `S`, there is one such string if `S` contains the levels at which `τ` chooses `P₀`, and + -- none otherwise. + have hgiven : ∀ S ∈ powersetCard m (univ : Finset (Fin L)), + (∏ ℓ, if ℓ ∈ S then 1 else (if τ ℓ = Term.P0 then 0 else 1)) + = if P0Levels τ ⊆ S then 1 else 0 := by + intro S _ + rw [← prod_ite_ite_superset] + exact prod_congr rfl fun ℓ _ => by simp + -- A set of `m` levels that contains these `m - d` levels is given by the `L - m` levels outside + -- it, among the other `L - m + d` levels. + rw [sum_congr rfl hgiven, ← card_filter, + card_powersetCard_superset _ (by simpa using hmL), Fintype.card_fin, + show L - (P0Levels τ).card = L - m + d by omega, Nat.choose_symm_add] at hcount + rw [← hcount] + refine congrArg card (Finset.ext fun w => ?_) + simp only [mem_filter, mem_univ, true_and] + exact and_comm + +/-- `β_d` is positive when `m ≤ L`. -/ +theorem beta_pos {L m : ℕ} (hmL : m ≤ L) (d : ℕ) : 0 < beta L m d := + Nat.mul_pos (Nat.choose_pos (by omega)) (by positivity) + +/-- The equality in equation (5) without division: `β_d (L-m+d) = 9 (m-d+1) β_{d-1}` for +`1 ≤ d ≤ m ≤ L`. -/ +theorem Equation5.ratio_nat {L m : ℕ} (hmL : m ≤ L) (d : ℕ) (hd1 : 1 ≤ d) (hd : d ≤ m) : + beta L m d * (L - m + d) = 9 * (m - d + 1) * beta L m (d - 1) := by + -- With `k = m - d` this is `binom(L, k) (L - k) = binom(L, k + 1) (k + 1)`, times a power of 9. + have hchoose : L.choose (m - d + 1) * (m - d + 1) = L.choose (m - d) * (L - m + d) := by + rw [Nat.choose_succ_right_eq, show L - (m - d) = L - m + d by omega] + have hpow : 9 ^ (L - m + d) = 9 * 9 ^ (L - m + (d - 1)) := by + rw [show L - m + d = L - m + (d - 1) + 1 by omega, pow_succ, mul_comm] + rw [beta, beta, show m - (d - 1) = m - d + 1 by omega, hpow] + calc L.choose (m - d) * (9 * 9 ^ (L - m + (d - 1))) * (L - m + d) + = 9 * 9 ^ (L - m + (d - 1)) * (L.choose (m - d) * (L - m + d)) := by ring + _ = 9 * 9 ^ (L - m + (d - 1)) * (L.choose (m - d + 1) * (m - d + 1)) := by rw [hchoose] + _ = 9 * (m - d + 1) * (L.choose (m - d + 1) * 9 ^ (L - m + (d - 1))) := by ring + +/-- Section 2.4.3, the equality in equation (5), for every `L ≥ m` (it is used again as equation (7) +in Section 4.3): β_d / β_{d-1} = 9(m-d+1) / (L-m+d) for `1 ≤ d ≤ m`. + +NOTE. The paper prints the equality under `L = 19m` in (5) and under `L ≥ 10m` in (7); here it is +stated for every `L ≥ m`. -/ +theorem beta_ratio {L m : ℕ} (hmL : m ≤ L) (d : ℕ) (hd1 : 1 ≤ d) (hd : d ≤ m) : + (beta L m d : ℚ) / (beta L m (d - 1) : ℚ) + = 9 * ((m : ℚ) - (d : ℚ) + 1) / ((L : ℚ) - (m : ℚ) + (d : ℚ)) := by + have hm : (m : ℚ) ≤ L := by exact_mod_cast hmL + have hd0 : (1 : ℚ) ≤ d := by exact_mod_cast hd1 + have hbeta : (0 : ℚ) < beta L m (d - 1) := by exact_mod_cast beta_pos hmL (d - 1) + have hnat := congrArg (Nat.cast : ℕ → ℚ) (Equation5.ratio_nat hmL d hd1 hd) + push_cast [Nat.cast_sub hmL, Nat.cast_sub hd] at hnat + rw [div_eq_div_iff hbeta.ne' (by linarith), hnat] + +/-- Section 2.4.3, the inequalities in equation (5): if L = 19m, then for 1 ≤ d ≤ m we have +β_d / β_{d-1} = 9(m-d+1)/(L-m+d) ≤ 9m/(18m+1) < 1/2. -/ +theorem beta_ratio_lt_half (m d : ℕ) (hd1 : 1 ≤ d) (hd : d ≤ m) : + (beta (19 * m) m d : ℚ) / (beta (19 * m) m (d - 1) : ℚ) ≤ 9 * (m : ℚ) / (18 * (m : ℚ) + 1) + ∧ 9 * (m : ℚ) / (18 * (m : ℚ) + 1) < 1 / 2 := by + have hm : (1 : ℚ) ≤ m := by exact_mod_cast hd1.trans hd + have hd0 : (1 : ℚ) ≤ d := by exact_mod_cast hd1 + refine ⟨?_, ?_⟩ + · -- The numerator is at most `9m` and the denominator at least `18m + 1`, because `d ≥ 1`. + rw [beta_ratio (by omega) d hd1 hd] + push_cast + exact div_le_div₀ (by positivity) (by linarith) (by positivity) (by linarith) + · rw [div_lt_div_iff₀ (by positivity) (by norm_num)] + linarith + +/-- **Equation (5)**, conclusion: if `L = 19m` then β_d ≤ 2^{-d} M, for `0 ≤ d ≤ m`. +The inequality is multiplied out. -/ +theorem two_pow_mul_beta_le (m d : ℕ) (hd : d ≤ m) : 2 ^ d * beta (19 * m) m d ≤ M (19 * m) m := by + induction d with + | zero => simp [beta_zero_eq_M] + | succ d ih => + -- By the two inequalities of (5), `β_{d+1} < β_d / 2`. + obtain ⟨hle, hlt⟩ := beta_ratio_lt_half m (d + 1) (by omega) hd + rw [Nat.add_sub_cancel] at hle + have hbeta : (0 : ℚ) < beta (19 * m) m d := by exact_mod_cast beta_pos (by omega) d + have hhalf : 2 * beta (19 * m) m (d + 1) ≤ beta (19 * m) m d := by + have hq := (div_lt_iff₀ hbeta).mp (hle.trans_lt hlt) + exact_mod_cast (by linarith : (2 * beta (19 * m) m (d + 1) : ℚ) ≤ beta (19 * m) m d) + calc 2 ^ (d + 1) * beta (19 * m) m (d + 1) = 2 ^ d * (2 * beta (19 * m) m (d + 1)) := by ring + _ ≤ 2 ^ d * beta (19 * m) m d := Nat.mul_le_mul_left _ hhalf + _ ≤ M (19 * m) m := ih (by omega) + + +end Cslib.Algorithms.MatrixMultiplication.Schonhage diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Parameters.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Parameters.lean new file mode 100644 index 000000000..464b2a19c --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Parameters.lean @@ -0,0 +1,134 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Sparse +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Tiling +public import Complexity.Foundations.Combinatorics.BinomialUpperBound +public import Mathlib.Analysis.Complex.ExponentialBounds + +/-! +# Parameters of finite thin multiplication + +## Main results + +* `K_mul_N0_le_D_pow`: the band fits when `D^18 ≤ N`. +* `encoding_size_le`: the shared encoding length bound. + +## References + +Adapted selectively from [AlmanWilliams2026, Section 2] and +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical +counts and identities; they give no native program or instruction-cost certificate. +Source-derived descriptions below are paraphrases. +-/ + +@[expose] public section + +open Finset + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Section 2.4.4: using the general bound binom(L, m) ≤ (eL/m)^m, we have +K = binom(19m, m) ≤ (19e)^m. The general bound is `Nat.choose_le_exp_mul_div_pow`. + +NOTE. The printed bound divides by `m`. In Lean a quotient by 0 is 0 and `0^0 = 1`, so for `m = 0` +both sides are 1 and no hypothesis `m ≥ 1` is needed. -/ +theorem K_le_pow (m : ℕ) : (K (19 * m) m : ℝ) ≤ (19 * Real.exp 1) ^ m := by + rcases Nat.eq_zero_or_pos m with rfl | hm + · simp [K] + · have hm' : (m : ℝ) ≠ 0 := by exact_mod_cast hm.ne' + have hbase : Real.exp 1 * ((19 * m : ℕ) : ℝ) / (m : ℝ) = 19 * Real.exp 1 := by + push_cast + field_simp + exact hbase ▸ Nat.choose_le_exp_mul_div_pow (19 * m) m + +/-- Section 2.4.4: 19e · 3^18 < 4^18. -/ +theorem nineteen_mul_exp_mul_three_pow_lt : 19 * Real.exp 1 * (3 : ℝ) ^ 18 < (4 : ℝ) ^ 18 := by + calc 19 * Real.exp 1 * (3 : ℝ) ^ 18 < 19 * 2.7182818286 * (3 : ℝ) ^ 18 := by + gcongr + exact Real.exp_one_lt_d9 + _ < (4 : ℝ) ^ 18 := by norm_num + +/-- `4^{18m} = D^18`. -/ +private theorem four_pow_eq (m : ℕ) : ((4 : ℝ) ^ 18) ^ m = (D m : ℝ) ^ 18 := by + rw [D, Nat.cast_pow, Nat.cast_ofNat, ← pow_mul, ← pow_mul, mul_comm] + +/-- Section 2.4.4: so K N₀ ≤ (19e · 3^18)^m ≤ 4^{18m}, and `4^{18m} = D^18`. -/ +theorem K_mul_N0_le_D_pow (m : ℕ) : K (19 * m) m * N0 (19 * m) m ≤ D m ^ 18 := by + have hK := K_le_pow m + have key : (K (19 * m) m : ℝ) * (N0 (19 * m) m : ℝ) ≤ (D m : ℝ) ^ 18 := by + calc (K (19 * m) m : ℝ) * (N0 (19 * m) m : ℝ) + ≤ (19 * Real.exp 1) ^ m * ((3 : ℝ) ^ 18) ^ m := by + rw [N0_nineteen_mul m] + push_cast + rw [pow_mul] + exact mul_le_mul_of_nonneg_right hK (by positivity) + _ = (19 * Real.exp 1 * (3 : ℝ) ^ 18) ^ m := (mul_pow _ _ _).symm + _ ≤ ((4 : ℝ) ^ 18) ^ m := + pow_le_pow_left₀ (by positivity) nineteen_mul_exp_mul_three_pow_lt.le m + _ = (D m : ℝ) ^ 18 := four_pow_eq m + exact_mod_cast key + +/-- Section 2.4.4, the first consequence of `N ≥ D^18`: K N₀ ≤ D^18 ≤ N. -/ +theorem K_mul_N0_le (m N : ℕ) (hN : D m ^ 18 ≤ N) : K (19 * m) m * N0 (19 * m) m ≤ N := + (K_mul_N0_le_D_pow m).trans hN + +/-- Section 2.4.4, the first consequence of `N ≥ D^18`: and hence K₀ N₀ ≤ K N₀ ≤ D^18 ≤ N. -/ +theorem K0_mul_N0_le (m N : ℕ) (hN : D m ^ 18 ≤ N) : K0 (19 * m) m * N0 (19 * m) m ≤ N := + (Nat.mul_le_mul_right _ (Nat.sqrt_le_self _)).trans (K_mul_N0_le m N hN) + +/-- Section 2.4.4: This means the padding at most doubles N. -/ +theorem padN_le_two_mul_of_D_pow_le (m N : ℕ) (hN : D m ^ 18 ≤ N) : padN (19 * m) m N ≤ 2 * N := + padN_le_two_mul (19 * m) m N (K0_mul_N0_le m N hN) + +/-- Section 2.4.4: 10^19 · 2^{1/9} / 3^18 < 4^18. -/ +theorem ten_pow_mul_two_rpow_div_three_pow_lt : + (10 : ℝ) ^ 19 * (2 : ℝ) ^ (1 / 9 : ℝ) / (3 : ℝ) ^ 18 < (4 : ℝ) ^ 18 := by + have htwo : (2 : ℝ) ^ (1 / 9 : ℝ) ≤ 2 := by + calc (2 : ℝ) ^ (1 / 9 : ℝ) ≤ (2 : ℝ) ^ (1 : ℝ) := + Real.rpow_le_rpow_of_exponent_le (by norm_num) (by norm_num) + _ = 2 := Real.rpow_one 2 + rw [div_lt_iff₀ (by positivity)] + calc (10 : ℝ) ^ 19 * (2 : ℝ) ^ (1 / 9 : ℝ) ≤ (10 : ℝ) ^ 19 * 2 := + mul_le_mul_of_nonneg_left htwo (by positivity) + _ < (4 : ℝ) ^ 18 * (3 : ℝ) ^ 18 := by norm_num + +/-- Section 2.4.4: so 10^L · 2^{m/9} / N₀ ≤ D^18, for `L = 19m`. -/ +theorem ten_pow_div_N0_le (m : ℕ) : + (10 : ℝ) ^ (19 * m) * (2 : ℝ) ^ ((m : ℝ) / 9) / (N0 (19 * m) m : ℝ) ≤ (D m : ℝ) ^ 18 := by + have hroot : (2 : ℝ) ^ ((m : ℝ) / 9) = ((2 : ℝ) ^ (1 / 9 : ℝ)) ^ m := by + rw [← Real.rpow_natCast _ m, ← Real.rpow_mul (by norm_num)] + congr 1 + ring + calc (10 : ℝ) ^ (19 * m) * (2 : ℝ) ^ ((m : ℝ) / 9) / (N0 (19 * m) m : ℝ) + = ((10 : ℝ) ^ 19 * (2 : ℝ) ^ (1 / 9 : ℝ) / (3 : ℝ) ^ 18) ^ m := by + rw [N0_nineteen_mul m, hroot] + push_cast + rw [pow_mul, pow_mul, div_pow, mul_pow] + _ ≤ ((4 : ℝ) ^ 18) ^ m := pow_le_pow_left₀ (by positivity) + ten_pow_mul_two_rpow_div_three_pow_lt.le m + _ = (D m : ℝ) ^ 18 := four_pow_eq m + +/-- **Equation (6)**, the second consequence of `N ≥ D^18`: 10^L ≤ 2^{-m/9} N N₀, for +`L = 19m`. -/ +theorem encoding_size_le (m N : ℕ) (hN : D m ^ 18 ≤ N) : + (10 : ℝ) ^ (19 * m) ≤ (2 : ℝ) ^ (-(m : ℝ) / 9) * (N : ℝ) * (N0 (19 * m) m : ℝ) := by + have hN0 : (0 : ℝ) < (N0 (19 * m) m : ℝ) := by exact_mod_cast N0_pos _ _ + have hN' : (D m : ℝ) ^ 18 ≤ (N : ℝ) := by exact_mod_cast hN + have h := (ten_pow_div_N0_le m).trans hN' + rw [div_le_iff₀ hN0] at h + calc (10 : ℝ) ^ (19 * m) + = (10 : ℝ) ^ (19 * m) * (2 : ℝ) ^ ((m : ℝ) / 9) * saving m := by + rw [mul_assoc, mul_comm _ (saving m), saving_mul_two_rpow, mul_one] + _ ≤ (N : ℝ) * (N0 (19 * m) m : ℝ) * saving m := + mul_le_mul_of_nonneg_right h (saving_pos m).le + _ = saving m * (N : ℝ) * (N0 (19 * m) m : ℝ) := by ring + +end Cslib.Algorithms.MatrixMultiplication.Schonhage diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Sparse.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Sparse.lean new file mode 100644 index 000000000..9cd46e8f7 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Sparse.lean @@ -0,0 +1,311 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Orders +public import Complexity.Foundations.Algebra.GeometricTail +public import Mathlib.Analysis.SpecialFunctions.Pow.Real + +/-! +# Sparse query bounds + +## Main definitions + +* `saving`: the factor `D^(-1/18)` for `D = 4^m`. + +## Main results + +* `card_leaves_le_sum_min`: sum the two order-wise bounds. +* `card_leaves_le_saving`: explicit sparse leaf bound, with no assumed sparse solver. + +## References + +Adapted selectively from [AlmanWilliams2026, Section 2] and +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical +counts and identities; they give no native program or instruction-cost certificate. +Source-derived descriptions below are paraphrases. +-/ + +@[expose] public section + +open Finset + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-! ### The parameters from Section 2.4.3 on -/ + +/-- Section 2.4.3: From now on, we fix L = 19m, so that N₀ = 3^{18m}. -/ +theorem N0_nineteen_mul (m : ℕ) : N0 (19 * m) m = 3 ^ (18 * m) := by + unfold N0 + congr 1 + omega + +/-- `M` is positive when `m ≤ L`. -/ +theorem M_pos {L m : ℕ} (hmL : m ≤ L) : 0 < M L m := + Nat.mul_pos (Nat.choose_pos hmL) (Nat.pow_pos (Nat.pow_pos (by decide))) + +/-- `D = 4^m = 2^{2m}`. -/ +private theorem D_eq_two_pow (m : ℕ) : (D m : ℝ) = (2 : ℝ) ^ (2 * m) := by + rw [D, pow_mul] + norm_num + +/-- Section 2.4.3: Recall that D = 4^m, so 2^m = √D. -/ +theorem two_pow_eq_sqrt_D (m : ℕ) : (2 : ℝ) ^ m = Real.sqrt (D m) := by + rw [D_eq_two_pow, pow_mul', Real.sqrt_sq (by positivity)] + +/-- The factor `2^{-m/9}` of Sections 2.4.3 and 2.4.4, by which the costs of the algorithm of +Theorem 5 lie below `N²`. -/ +noncomputable def saving (m : ℕ) : ℝ := (2 : ℝ) ^ (-(m : ℝ) / 9) + +/-- `2^{-m/9}` is positive. -/ +theorem saving_pos (m : ℕ) : 0 < saving m := + Real.rpow_pos_of_pos two_pos _ + +/-- `2^{-m/9} ≤ 1`. -/ +theorem saving_le_one (m : ℕ) : saving m ≤ 1 := + Real.rpow_le_one_of_one_le_of_nonpos one_le_two + (div_nonpos_of_nonpos_of_nonneg (neg_nonpos.mpr m.cast_nonneg) (by norm_num)) + +/-- `2^{-m/9} · 2^{m/9} = 1`. -/ +theorem saving_mul_two_rpow (m : ℕ) : saving m * (2 : ℝ) ^ ((m : ℝ) / 9) = 1 := by + rw [saving, ← Real.rpow_add two_pos, show -(m : ℝ) / 9 + (m : ℝ) / 9 = 0 by ring, Real.rpow_zero] + +/-- Section 2.4.3: and 2^{-m/9} = D^{-1/18}. -/ +theorem saving_eq_D_rpow (m : ℕ) : saving m = (D m : ℝ) ^ (-(1 / 18 : ℝ)) := by + rw [saving, D_eq_two_pow, ← Real.rpow_natCast 2 (2 * m), ← Real.rpow_mul two_pos.le] + congr 1 + push_cast + ring + +/-! ### The first inequality -/ + +/-- In the proof of Lemma 11: We bound the number of leaves of order d in leaves(U) in two +ways: by |U| α_d, charging each output string of U for its leaves of order d, and by β_d, the number +of all leaves of order d. Quoted again in Section 4.3: At every order d, the pruned recursion of +Section 2.4 visits at most min{|U| α_d, β_d} leaves. -/ +theorem card_leaves_filter_order_le {L m : ℕ} (U : Finset (OutStr L)) + (hU : ∀ w ∈ U, (innerSetO w).card = m) (d : ℕ) : + ((leaves U).filter fun τ => order m τ = d).card ≤ min (U.card * alpha m d) (beta L m d) := by + refine le_min ?_ ?_ + · -- Charge each output string of U for its leaves of order d. + calc _ ≤ (U.biUnion fun w => + univ.filter fun τ : Leaf L => Leaf.Contributes τ w ∧ order m τ = d).card := by + refine card_le_card fun τ hτ => ?_ + obtain ⟨hτU, hτd⟩ := mem_filter.mp hτ + obtain ⟨w, hw, hc⟩ := mem_leaves.mp hτU + exact mem_biUnion.mpr ⟨w, hw, mem_filter.mpr ⟨mem_univ _, hc, hτd⟩⟩ + _ ≤ U.card * alpha m d := + card_biUnion_le_card_mul _ _ _ fun w hw => + (card_contributing_of_order w (hU w hw) d).le + · -- There are only β_d leaves of order d in all. + rcases U.eq_empty_or_nonempty with rfl | ⟨w₀, hw₀⟩ + · simp [leaves] + have hmL : m ≤ L := by + have h := card_le_univ (innerSetO w₀) + rwa [hU w₀ hw₀, Fintype.card_fin] at h + rcases le_or_gt d m with hd | hd + · rw [← card_filter_order_eq hmL d hd] + exact card_le_card (filter_subset_filter _ (subset_univ _)) + · -- No leaf has an order above m. + have hempty : ((leaves U).filter fun τ => order m τ = d) = ∅ := by + refine filter_false_of_mem fun τ _ h => ?_ + have hle := order_le m τ + omega + rw [hempty] + exact Nat.zero_le _ + +/-- **Lemma 11**, first inequality. For every set U of output strings whose inner sets have +exactly m elements, |leaves(U)| ≤ ∑_{d=0}^{m} min{|U| α_d, β_d}. -/ +theorem card_leaves_le_sum_min {L m : ℕ} (U : Finset (OutStr L)) + (hU : ∀ w ∈ U, (innerSetO w).card = m) : + (leaves U).card ≤ ∑ d ∈ range (m + 1), min (U.card * alpha m d) (beta L m d) := by + -- Every leaf of `leaves(U)` has an order between 0 and m. + have hord : ∀ τ ∈ leaves U, 0 ≤ order m τ := by + intro τ hτ + obtain ⟨w, hw, hc⟩ := mem_leaves.mp hτ + exact order_nonneg (hU w hw) hc + have hmaps : ∀ τ ∈ leaves U, (order m τ).toNat ∈ range (m + 1) := by + intro τ _ + have hle := order_le m τ + rw [mem_range] + omega + -- Sum the smaller of the two bounds over the orders. + rw [card_eq_sum_card_fiberwise hmaps] + refine sum_le_sum fun d _ => le_trans ?_ (card_leaves_filter_order_le U hU d) + refine card_le_card fun τ hτ => ?_ + obtain ⟨hτU, hτd⟩ := mem_filter.mp hτ + have hnonneg := hord τ hτU + exact mem_filter.mpr ⟨hτU, by omega⟩ + +/-! ### The second inequality -/ + +/-- Equation (5) over the reals: `β_d ≤ M 2^{-d}` for `0 ≤ d ≤ m` and `L = 19m`. -/ +private theorem beta_le_M_mul (m d : ℕ) (hd : d ≤ m) : + (beta (19 * m) m d : ℝ) ≤ (M (19 * m) m : ℝ) * (1 / 2 : ℝ) ^ d := by + have h : (2 : ℝ) ^ d * (beta (19 * m) m d : ℝ) ≤ (M (19 * m) m : ℝ) := by + exact_mod_cast two_pow_mul_beta_le m d hd + rw [one_div, inv_pow, ← div_eq_mul_inv, le_div_iff₀ (by positivity)] + linarith + +/-- By (5), the orders `d ≥ a` contribute `∑ β_d ≤ M ∑_{d ≥ a} 2^{-d} < 2 · 2^{-a} M`. -/ +private theorem sum_beta_lt_of_subset (m a : ℕ) (s : Finset ℕ) (hs : s ⊆ Ico a (m + 1)) : + ((∑ d ∈ s, beta (19 * m) m d : ℕ) : ℝ) < 2 * (1 / 2 : ℝ) ^ a * (M (19 * m) m : ℝ) := by + have hM : (0 : ℝ) < (M (19 * m) m : ℝ) := by exact_mod_cast M_pos (show m ≤ 19 * m by omega) + push_cast + calc ∑ d ∈ s, (beta (19 * m) m d : ℝ) + ≤ ∑ d ∈ s, (M (19 * m) m : ℝ) * (1 / 2 : ℝ) ^ d := + sum_le_sum fun d hd => + beta_le_M_mul m d (by have := mem_Ico.mp (hs hd); omega) + _ ≤ ∑ d ∈ Ico a (m + 1), (M (19 * m) m : ℝ) * (1 / 2 : ℝ) ^ d := + sum_le_sum_of_subset_of_nonneg hs fun _ _ _ => by positivity + _ = (M (19 * m) m : ℝ) * ∑ d ∈ Ico a (m + 1), (1 / 2 : ℝ) ^ d := (mul_sum _ _ _).symm + _ < (M (19 * m) m : ℝ) * (2 * (1 / 2 : ℝ) ^ a) := + mul_lt_mul_of_pos_left + ((geom_sum_Ico_lt_of_lt_one (by norm_num) (by norm_num) a _).trans_eq (by ring)) hM + _ = 2 * (1 / 2 : ℝ) ^ a * (M (19 * m) m : ℝ) := by ring + +/-- In the proof of Lemma 11: The orders d > m/9 contribute at most +∑_{d>m/9} β_d ≤ M ∑_{d>m/9} 2^{-d} < 2 · 2^{-m/9} M by (5). -/ +theorem sum_beta_large_orders_lt (m : ℕ) : + ((∑ d ∈ (range (m + 1)).filter (fun d => m < 9 * d), beta (19 * m) m d : ℕ) : ℝ) + < 2 * saving m * (M (19 * m) m : ℝ) := by + -- The orders d > m/9 are the orders d ≥ ⌊m/9⌋ + 1. + have hs : (range (m + 1)).filter (fun d => m < 9 * d) ⊆ Ico (m / 9 + 1) (m + 1) := by + intro d hd + obtain ⟨hdm, hd9⟩ := mem_filter.mp hd + rw [mem_range] at hdm + rw [mem_Ico] + omega + refine (sum_beta_lt_of_subset m (m / 9 + 1) _ hs).trans_le ?_ + have hpow : (1 / 2 : ℝ) ^ (m / 9 + 1) ≤ saving m := by + have hfloor : (m : ℝ) ≤ 9 * ((m / 9 + 1 : ℕ) : ℝ) := by + exact_mod_cast (by omega : m ≤ 9 * (m / 9 + 1)) + rw [show (1 / 2 : ℝ) ^ (m / 9 + 1) = (2 : ℝ) ^ (-((m / 9 + 1 : ℕ) : ℝ)) by + rw [Real.rpow_neg (by norm_num), Real.rpow_natCast, one_div, inv_pow]] + exact Real.rpow_le_rpow_of_exponent_le (by norm_num) (by linarith) + -- Multiply `hpow` by `2` and by `M`. + gcongr + +/-- One term of the display in the proof of Lemma 11: `binom(m, d) 9^d = binom(m, d) 72^d 8^{-d}`, +which is at most `72^{m/9} binom(m, d) 8^{-d}` for `d ≤ m/9`. The factor `1^{m-d}` makes the right +side a term of the expansion of `(1/8 + 1)^m`. -/ +private theorem alpha_le (m d : ℕ) (hd : 9 * d ≤ m) : + (alpha m d : ℝ) + ≤ (72 : ℝ) ^ ((m : ℝ) / 9) * ((1 / 8 : ℝ) ^ d * 1 ^ (m - d) * (m.choose d : ℝ)) := by + have hd' : (9 : ℝ) * d ≤ m := by exact_mod_cast hd + have hpow : (72 : ℝ) ^ d ≤ (72 : ℝ) ^ ((m : ℝ) / 9) := by + rw [← Real.rpow_natCast (72 : ℝ) d] + exact Real.rpow_le_rpow_of_exponent_le (by norm_num) (by linarith) + calc (alpha m d : ℝ) = (72 : ℝ) ^ d * ((1 / 8 : ℝ) ^ d * 1 ^ (m - d) * (m.choose d : ℝ)) := by + rw [alpha, one_pow, mul_one, ← mul_assoc, ← mul_pow] + push_cast + norm_num [mul_comm] + _ ≤ _ := by gcongr + +/-- The first two steps of the display in the proof of Lemma 11: +`∑_{d ≤ m/9} binom(m, d) 9^d ≤ 72^{m/9} ∑_{d=0}^{m} binom(m, d) 8^{-d} = 72^{m/9} (9/8)^m`. -/ +private theorem sum_alpha_small_orders_le_aux (m : ℕ) : + ((∑ d ∈ (range (m + 1)).filter (fun d => 9 * d ≤ m), alpha m d : ℕ) : ℝ) + ≤ (72 : ℝ) ^ ((m : ℝ) / 9) * (9 / 8 : ℝ) ^ m := by + push_cast + calc ∑ d ∈ (range (m + 1)).filter (fun d => 9 * d ≤ m), (alpha m d : ℝ) + ≤ ∑ d ∈ (range (m + 1)).filter (fun d => 9 * d ≤ m), + (72 : ℝ) ^ ((m : ℝ) / 9) * ((1 / 8 : ℝ) ^ d * 1 ^ (m - d) * (m.choose d : ℝ)) := + sum_le_sum fun d hd => alpha_le m d (mem_filter.mp hd).2 + _ ≤ ∑ d ∈ range (m + 1), + (72 : ℝ) ^ ((m : ℝ) / 9) * ((1 / 8 : ℝ) ^ d * 1 ^ (m - d) * (m.choose d : ℝ)) := + sum_le_sum_of_subset_of_nonneg (filter_subset _ _) fun _ _ _ => by positivity + _ = (72 : ℝ) ^ ((m : ℝ) / 9) * (9 / 8 : ℝ) ^ m := by + rw [← mul_sum, ← add_pow] + norm_num + +/-- The display in the proof of Lemma 11: ∑_{d ≤ m/9} binom(m, d) 9^d ≤ 72^{m/9} +∑_{d=0}^{m} binom(m, d) 8^{-d} = 72^{m/9} (9/8)^m = (72 · (9/8)^9)^{m/9} < 256^{m/9} = 2^m · +2^{-m/9}. + +NOTE. The strict inequality needs `m ≥ 1`, which is fixed in Section 2.3.3; for `m = 0` both ends +are 1. -/ +theorem sum_alpha_small_orders_lt (m : ℕ) (hm : 1 ≤ m) : + ((∑ d ∈ (range (m + 1)).filter (fun d => 9 * d ≤ m), alpha m d : ℕ) : ℝ) + < (2 : ℝ) ^ m * saving m := by + have hm9 : (0 : ℝ) < (m : ℝ) / 9 := by + have : (0 : ℝ) < (m : ℝ) := by exact_mod_cast hm + positivity + refine (sum_alpha_small_orders_le_aux m).trans_lt ?_ + -- 72^{m/9} (9/8)^m = (72 · (9/8)^9)^{m/9} + have hleft : (72 : ℝ) ^ ((m : ℝ) / 9) * (9 / 8 : ℝ) ^ m + = (72 * (9 / 8 : ℝ) ^ 9) ^ ((m : ℝ) / 9) := by + rw [Real.mul_rpow (by norm_num) (by positivity), ← Real.rpow_natCast (9 / 8 : ℝ) 9, + ← Real.rpow_mul (by norm_num), ← Real.rpow_natCast (9 / 8 : ℝ) m] + congr 2 + push_cast + ring + -- 256^{m/9} = 2^m · 2^{-m/9} + have hright : (256 : ℝ) ^ ((m : ℝ) / 9) = (2 : ℝ) ^ m * saving m := by + rw [saving, show (256 : ℝ) = (2 : ℝ) ^ ((8 : ℕ) : ℝ) by rw [Real.rpow_natCast]; norm_num, + ← Real.rpow_mul (by norm_num), ← Real.rpow_natCast (2 : ℝ) m, ← Real.rpow_add (by norm_num)] + congr 1 + push_cast + ring + rw [hleft, ← hright] + -- `72 · (9/8)^9 < 208 < 256`. + exact Real.rpow_lt_rpow (by positivity) (by norm_num) hm9 + +/-- The bound of `sum_alpha_small_orders_lt` with `≤`, which also holds for `m = 0`. -/ +private theorem sum_alpha_small_orders_le (m : ℕ) : + ((∑ d ∈ (range (m + 1)).filter (fun d => 9 * d ≤ m), alpha m d : ℕ) : ℝ) + ≤ (2 : ℝ) ^ m * saving m := by + rcases Nat.eq_zero_or_pos m with rfl | hm + · simp [alpha, saving] + · exact (sum_alpha_small_orders_lt m hm).le + +/-- In the proof of Lemma 11: We use |U| α_d for the orders d ≤ m/9 and β_d for the orders +d > m/9. -/ +private theorem card_leaves_le_split {L m : ℕ} (U : Finset (OutStr L)) + (hU : ∀ w ∈ U, (innerSetO w).card = m) : + (leaves U).card + ≤ U.card * ∑ d ∈ (range (m + 1)).filter (fun d => 9 * d ≤ m), alpha m d + + ∑ d ∈ (range (m + 1)).filter (fun d => m < 9 * d), beta L m d := by + refine (card_leaves_le_sum_min U hU).trans ?_ + rw [← sum_filter_add_sum_filter_not (range (m + 1)) (fun d => 9 * d ≤ m), mul_sum] + refine add_le_add (sum_le_sum fun d _ => min_le_left _ _) ?_ + rw [filter_congr fun d _ => (not_le (a := 9 * d) (b := m))] + exact sum_le_sum fun d _ => min_le_right _ _ + +/-- **Lemma 11**, second inequality. +and if L = 19m, then |leaves(U)| ≤ 2^{-m/9} (2^m |U| + 2M). -/ +theorem card_leaves_le_saving {m : ℕ} (U : Finset (OutStr (19 * m))) + (hU : ∀ w ∈ U, (innerSetO w).card = m) : + ((leaves U).card : ℝ) ≤ saving m * ((2 : ℝ) ^ m * (U.card : ℝ) + 2 * (M (19 * m) m : ℝ)) := by + have hsplit : ((leaves U).card : ℝ) + ≤ (U.card : ℝ) * ((∑ d ∈ (range (m + 1)).filter (fun d => 9 * d ≤ m), alpha m d : ℕ) : ℝ) + + ((∑ d ∈ (range (m + 1)).filter (fun d => m < 9 * d), beta (19 * m) m d : ℕ) : ℝ) := by + exact_mod_cast card_leaves_le_split U hU + have hsmall := + mul_le_mul_of_nonneg_left (sum_alpha_small_orders_le m) (Nat.cast_nonneg (α := ℝ) U.card) + have hlarge := (sum_beta_large_orders_lt m).le + calc ((leaves U).card : ℝ) ≤ _ := hsplit + _ ≤ (U.card : ℝ) * ((2 : ℝ) ^ m * saving m) + 2 * saving m * (M (19 * m) m : ℝ) := + add_le_add hsmall hlarge + _ = _ := by ring + +/-- **Lemma 11**, both inequalities together. For every set U of output strings whose inner +sets have exactly m elements, |leaves(U)| ≤ ∑_{d=0}^{m} min{|U| α_d, β_d}, and if L = 19m, +then |leaves(U)| ≤ 2^{-m/9} (2^m |U| + 2M). -/ +theorem sparse_leaf_bounds {L m : ℕ} (U : Finset (OutStr L)) (hU : ∀ w ∈ U, (innerSetO w).card = + m) : + (leaves U).card ≤ ∑ d ∈ range (m + 1), min (U.card * alpha m d) (beta L m d) + ∧ (L = 19 * m → + ((leaves U).card : ℝ) + ≤ (2 : ℝ) ^ (-(m : ℝ) / 9) * ((2 : ℝ) ^ m * (U.card : ℝ) + 2 * (M L m : ℝ))) := by + refine ⟨card_leaves_le_sum_min U hU, ?_⟩ + rintro rfl + exact card_leaves_le_saving U hU + +end Cslib.Algorithms.MatrixMultiplication.Schonhage diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/ThinProduct.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/ThinProduct.lean new file mode 100644 index 000000000..49c113b4a --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/ThinProduct.lean @@ -0,0 +1,196 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Parameters + +/-! +# Complete finite thin-product composition + +## Main results + +* `thin_product_spec`: exact queried entries for arbitrary signed integer matrices. +* `encodings_total_le`, `total_leaves_le`, `total_passed_sets_le`: explicit finite totals. + +For `D = 4^m`, `L = 19m`, `D^18 ≤ N` and `|W| 2^m ≤ N²`, encoding entries +and contributing leaves are bounded by constants times `N² / D^(1/18)`. Passed-set +cardinalities carry the additional factor `L + 1`. These are not instruction counts. + +## References + +Adapted selectively from [AlmanWilliams2026, Section 2] and +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical +counts and identities; they give no native program or instruction-cost certificate. +Source-derived descriptions below are paraphrases. +-/ + +@[expose] public section + +open Finset + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Section 2.4.4: Each wanted position (I, J) ∈ W lies in one tile T: the tile of a position is +one of the tiles. -/ +theorem tile_mem_tiles {L m N : ℕ} (hmL : m ≤ L) (I J : Fin N) : + (bandOf L m I, bandOf L m J) ∈ tiles L m N := + mem_product.mpr ⟨mem_range.mpr (bandOf_lt_numBands hmL N I I.2), + mem_range.mpr (bandOf_lt_numBands hmL N J J.2)⟩ + +/-- Section 2.4.4: a wanted position is indexed by one output string of T: its output string is in +the set `W_T` of its tile. -/ +theorem outStrOfPos_mem_wantedStrings {L m N : ℕ} (lay : Layout L m) (W : Finset (Fin N × Fin N)) + {I J : Fin N} (hIJ : (I, J) ∈ W) : + outStrOfPos lay I J ∈ wantedStrings lay W (bandOf L m I) (bandOf L m J) := + mem_image.mpr ⟨(I, J), mem_filter.mpr ⟨hIJ, rfl, rfl⟩, rfl⟩ + +/-- Section 2.4.3: the sets passed to `Pruned` consist only of […] output strings of length L whose +inner sets […] have exactly m elements. -/ +theorem card_innerSetO_of_mem_wantedStrings {L m N : ℕ} (lay : Layout L m) + (W : Finset (Fin N × Fin N)) (β β' : ℕ) (w : OutStr L) (hw : w ∈ wantedStrings lay W β β') : + (innerSetO w).card = m := by + obtain ⟨IJ, -, rfl⟩ := mem_image.mp hw + exact card_innerSetO_outStrOfPos lay IJ.1 IJ.2 + +/-- **Proof of Theorem 5**, correctness of the algorithm. for each tile T with W_T ≠ ∅, +we run Pruned(W_T), and report, for each (I, J) ∈ W, the value at its output string. This is correct +by Lemmas 9 and 10. The run on the tile of `(I, J)` reads the encodings of the input arrays of its +row band and its column band. This holds for all `m ≤ L`. -/ +theorem thin_product_spec {L m N : ℕ} (lay : Layout L m) (X : Matrix (Fin N) (Fin (D m)) ℤ) + (Y : Matrix (Fin (D m)) (Fin N) ℤ) (W : Finset (Fin N × Fin N)) (I J : Fin N) + (hIJ : (I, J) ∈ W) : + Pruned.result L (encodingL (bandArrayL lay X (bandOf L m I))) + (encodingR (bandArrayR lay Y (bandOf L m J))) + (wantedStrings lay W (bandOf L m I) (bandOf L m J)) (outStrOfPos lay I J) + = (X * Y) I J := by + -- Lemma 10, with Lemma 7: `Pruned` returns the output of `Full` restricted to the set passed to + -- it. + rw [Pruned.result_eq_piecewise_full, Finset.piecewise_eq_of_mem _ _ _ + (outStrOfPos_mem_wantedStrings lay W hIJ)] + -- Lemma 9, through the tiling of Section 2.3.4. + exact Full.bandArray_eq_mul lay X Y I J + +/-- Theorem 5 and Section 2.4.4: |W| ≤ N²/√D = 2^{-m} N². In the statements below this +hypothesis is written without a square root, as `|W| · 2^m ≤ N²`. -/ +theorem le_sq_div_sqrt_D_iff (m N w : ℕ) : + (w : ℝ) ≤ (N : ℝ) ^ 2 / Real.sqrt (D m) ↔ w * 2 ^ m ≤ N ^ 2 := by + rw [← two_pow_eq_sqrt_D, le_div_iff₀ (by positivity)] + exact_mod_cast Iff.rfl + +/-- The number of bands times `N₀` is at most the padded size, because `K₀ ≥ 1`. -/ +private theorem numBands_mul_N0_le {L m : ℕ} (hmL : m ≤ L) (N : ℕ) : + numBands L m N * N0 L m ≤ padN L m N := + Nat.mul_le_mul_left _ (Nat.le_mul_of_pos_left _ (K0_pos hmL)) + +/-- Section 2.4.4: By (6), the encodings cost (2N/K₀N₀) · O(10^L) = O(2^{-m/9} N²) operations: the +encodings of the input arrays of all row bands and all column bands have at most `4 N² / D^{1/18}` +entries in all. Here `N` is the size of the input, which the padding at most doubles. -/ +theorem encodings_total_le (m N : ℕ) (hN : D m ^ 18 ≤ N) : + ((2 * numBands (19 * m) m N * 10 ^ (19 * m) : ℕ) : ℝ) + ≤ 4 * (D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2 := by + have hbands : (numBands (19 * m) m N : ℝ) * N0 (19 * m) m ≤ 2 * N := by + exact_mod_cast (numBands_mul_N0_le (by omega) N).trans (padN_le_two_mul_of_D_pow_le m N hN) + have heq6 : (10 : ℝ) ^ (19 * m) ≤ saving m * N * N0 (19 * m) m := encoding_size_le m N hN + have hc := saving_pos m + rw [← saving_eq_D_rpow] + push_cast + calc 2 * (numBands (19 * m) m N : ℝ) * 10 ^ (19 * m) + ≤ 2 * numBands (19 * m) m N * (saving m * N * N0 (19 * m) m) := by gcongr + _ = 2 * saving m * (N * (numBands (19 * m) m N * N0 (19 * m) m)) := by ring + _ ≤ 2 * saving m * (N * (2 * N)) := by gcongr + _ = 4 * saving m * (N : ℝ) ^ 2 := by ring + +/-- Section 2.4.4: Each position of W lands in one set W_T, so ∑_T |W_T| ≤ |W|. -/ +theorem sum_card_wantedStrings_le {L m N : ℕ} (lay : Layout L m) (W : Finset (Fin N × Fin N)) : + ∑ T ∈ tiles L m N, (wantedStrings lay W T.1 T.2).card ≤ W.card := by + have hmap : ∀ IJ ∈ W, (bandOf L m IJ.1, bandOf L m IJ.2) ∈ tiles L m N := fun IJ _ => + tile_mem_tiles lay.hmL IJ.1 IJ.2 + rw [card_eq_sum_card_fiberwise hmap] + refine sum_le_sum fun T _ => ?_ + refine card_image_le.trans (le_of_eq ?_) + congr 1 + +/-- Section 2.4.4: there are at most 4N²/M tiles, so ∑_T M ≤ 4N², where `N` is the padded size. -/ +theorem sum_M_le (L m N : ℕ) : ∑ _T ∈ tiles L m N, M L m ≤ 4 * padN L m N ^ 2 := by + rw [sum_const, smul_eq_mul] + exact card_tiles_mul_M_le L m N + +/-- Section 2.4.4, the display: by Lemma 11 summed over the tiles, +`∑_T |leaves(W_T)| ≤ 2^{-m/9} (2^m |W| + 2 ∑_T M)`. -/ +theorem sum_card_leaves_le {m N : ℕ} (lay : Layout (19 * m) m) (W : Finset (Fin N × Fin N)) : + ((∑ T ∈ tiles (19 * m) m N, (leaves (wantedStrings lay W T.1 T.2)).card : ℕ) : ℝ) + ≤ saving m * ((2 : ℝ) ^ m * (W.card : ℝ) + + 2 * ((∑ _T ∈ tiles (19 * m) m N, M (19 * m) m : ℕ) : ℝ)) := by + have hc := saving_pos m + have hsum : ((∑ T ∈ tiles (19 * m) m N, (wantedStrings lay W T.1 T.2).card : ℕ) : ℝ) + ≤ (W.card : ℝ) := by + exact_mod_cast sum_card_wantedStrings_le lay W + push_cast at hsum ⊢ + calc ∑ T ∈ tiles (19 * m) m N, ((leaves (wantedStrings lay W T.1 T.2)).card : ℝ) + ≤ ∑ T ∈ tiles (19 * m) m N, saving m + * ((2 : ℝ) ^ m * ((wantedStrings lay W T.1 T.2).card : ℝ) + 2 * (M (19 * m) m : ℝ)) := + sum_le_sum fun T _ => + card_leaves_le_saving _ (card_innerSetO_of_mem_wantedStrings lay W T.1 T.2) + _ = saving m + * ((2 : ℝ) ^ m * ∑ T ∈ tiles (19 * m) m N, ((wantedStrings lay W T.1 T.2).card : ℝ) + + 2 * ∑ _T ∈ tiles (19 * m) m N, (M (19 * m) m : ℝ)) := by + rw [← mul_sum, sum_add_distrib, ← mul_sum, ← mul_sum] + _ ≤ _ := by gcongr + +/-- **Proof of Theorem 5**, the number of leaves in terms of the `N` and `D` of the theorem (`N` is +the size of the input, not the padded size). Since `N ≥ D^18 ≥ K₀ N₀`, the padding at most doubles +`N`, so `2^m |W| + 2 ∑_T M ≤ N² + 32 N²`. -/ +theorem total_leaves_le {m N : ℕ} (lay : Layout (19 * m) m) (W : Finset (Fin N × Fin N)) + (hN : D m ^ 18 ≤ N) (hW : W.card * 2 ^ m ≤ N ^ 2) : + ((∑ T ∈ tiles (19 * m) m N, (leaves (wantedStrings lay W T.1 T.2)).card : ℕ) : ℝ) + ≤ 33 * (D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2 := by + have hc := saving_pos m + have hWN : (2 : ℝ) ^ m * (W.card : ℝ) ≤ (N : ℝ) ^ 2 := by + rw [mul_comm] + exact_mod_cast hW + have hM : ((∑ _T ∈ tiles (19 * m) m N, M (19 * m) m : ℕ) : ℝ) ≤ 16 * (N : ℝ) ^ 2 := by + have hpad := Nat.pow_le_pow_left (padN_le_two_mul_of_D_pow_le m N hN) 2 + exact_mod_cast (sum_M_le (19 * m) m N).trans (by linarith) + refine (sum_card_leaves_le lay W).trans ?_ + rw [← saving_eq_D_rpow] + calc saving m + * ((2 : ℝ) ^ m * (W.card : ℝ) + 2 * ((∑ _T ∈ tiles (19 * m) m N, M (19 * m) m : ℕ) : ℝ)) + ≤ saving m * ((N : ℝ) ^ 2 + 2 * (16 * (N : ℝ) ^ 2)) := by gcongr + _ = 33 * saving m * (N : ℝ) ^ 2 := by ring + +/-- Lemma 10 summed over the tiles: the total size of the sets handled by the pruned recursions is +at most `L + 1` times the total number of leaves. -/ +private theorem sum_totalSize_le {m N : ℕ} (lay : Layout (19 * m) m) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (W : Finset (Fin N × Fin N)) : + ((∑ T ∈ tiles (19 * m) m N, + Pruned.totalSize (encodingL (bandArrayL lay X T.1)) (encodingR (bandArrayR lay Y T.2)) + (wantedStrings lay W T.1 T.2) : ℕ) : ℝ) + ≤ ((19 * m + 1 : ℕ) : ℝ) + * ((∑ T ∈ tiles (19 * m) m N, (leaves (wantedStrings lay W T.1 T.2)).card : ℕ) : ℝ) := by + have h : ∑ T ∈ tiles (19 * m) m N, + Pruned.totalSize (encodingL (bandArrayL lay X T.1)) (encodingR (bandArrayR lay Y T.2)) + (wantedStrings lay W T.1 T.2) + ≤ (19 * m + 1) * ∑ T ∈ tiles (19 * m) m N, (leaves (wantedStrings lay W T.1 T.2)).card := by + rw [mul_sum] + exact sum_le_sum fun T _ => Pruned.totalSize_le _ _ _ + exact_mod_cast h + +/-- **Proof of Theorem 5**, the total size of the sets, in the `N` and `D` of the theorem. -/ +theorem total_passed_sets_le {m N : ℕ} (lay : Layout (19 * m) m) (X : Matrix (Fin N) (Fin (D m)) ℤ) + (Y : Matrix (Fin (D m)) (Fin N) ℤ) (W : Finset (Fin N × Fin N)) (hN : D m ^ 18 ≤ N) + (hW : W.card * 2 ^ m ≤ N ^ 2) : + ((∑ T ∈ tiles (19 * m) m N, + Pruned.totalSize (encodingL (bandArrayL lay X T.1)) (encodingR (bandArrayR lay Y T.2)) + (wantedStrings lay W T.1 T.2) : ℕ) : ℝ) + ≤ ((19 * m + 1 : ℕ) : ℝ) * (33 * (D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2) := + (sum_totalSize_le lay X Y W).trans + (mul_le_mul_of_nonneg_left (total_leaves_le lay W hN hW) (Nat.cast_nonneg _)) + +end Cslib.Algorithms.MatrixMultiplication.Schonhage diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Tiling.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Tiling.lean new file mode 100644 index 000000000..dd34af063 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Tiling.lean @@ -0,0 +1,248 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Batch +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Layout +import Mathlib.Tactic.Ring + +/-! +# Padding and tiling correctness + +## Main results + +* `nonempty_layout`: valid layouts exist whenever `m ≤ L`. +* `Full.bandArray_eq_mul`: tiling computes every original integer product entry. +* `outStrOfPos_injective`: positions in a tile have distinct output strings. + +## References + +Adapted selectively from [AlmanWilliams2026, Section 2] and +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical +counts and identities; they give no native program or instruction-cost certificate. +Source-derived descriptions below are paraphrases. +-/ + +@[expose] public section + +open Finset + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-! ### The numbers -/ + +/-- Section 2.3.4: K₀² ≤ K. -/ +theorem K0_sq_le_K (L m : ℕ) : K0 L m ^ 2 ≤ K L m := + Nat.sqrt_le' _ + +/-- Section 2.3.4: K₀ ≥ √K / 2, squared. -/ +theorem K_le_four_mul_K0_sq (L m : ℕ) : K L m ≤ 4 * K0 L m ^ 2 := by + have hlt : K L m < (K0 L m + 1) ^ 2 := Nat.lt_succ_sqrt' _ + rcases Nat.eq_zero_or_pos (K0 L m) with h0 | h0 + · rw [h0] at hlt ⊢ + omega + · calc K L m ≤ (K0 L m + 1) ^ 2 := hlt.le + _ ≤ (2 * K0 L m) ^ 2 := Nat.pow_le_pow_left (by omega) 2 + _ = 4 * K0 L m ^ 2 := by ring + +/-- A band has at least one row. -/ +theorem bandSize_pos {L m : ℕ} (hmL : m ≤ L) : 0 < bandSize L m := + Nat.mul_pos (K0_pos hmL) (N0_pos L m) + +/-- Section 2.3.4: we first pad N to a multiple of K₀ N₀. -/ +theorem bandSize_dvd_padN (L m N : ℕ) : bandSize L m ∣ padN L m N := + Dvd.intro_left _ rfl + +/-- The padded size is at least `N`. + +NOTE. `m ≤ L` is left implicit in the paper. For `m > L` there is no subset of size `m` and +`K₀ = 0`. -/ +theorem le_padN {L m : ℕ} (hmL : m ≤ L) (N : ℕ) : N ≤ padN L m N := by + have h := Nat.lt_div_mul_add (a := N + bandSize L m - 1) (bandSize_pos hmL) + unfold padN numBands + omega + +/-- The padded size is the least multiple of `K₀ N₀` that is at least `N`: it is less than +`N + K₀ N₀`. + +NOTE. `m ≤ L` is left implicit in the paper. -/ +theorem padN_lt_add_bandSize {L m : ℕ} (hmL : m ≤ L) (N : ℕ) : + padN L m N < N + bandSize L m := by + have h := Nat.div_mul_le_self (N + bandSize L m - 1) (bandSize L m) + have hb := bandSize_pos hmL + unfold padN numBands + omega + +/-- Section 2.3.4: the padding at most doubles N since N ≥ K₀ N₀. -/ +theorem padN_le_two_mul (L m N : ℕ) (hN : K0 L m * N0 L m ≤ N) : padN L m N ≤ 2 * N := by + rcases Nat.lt_or_ge L m with hLm | hmL + · -- for `m > L` there are no subsets, `K₀ = 0`, and the padded size is 0 + have hK : K0 L m = 0 := by simp [K0, K, Nat.choose_eq_zero_of_lt hLm] + simp [padN, bandSize, hK] + · have h := padN_lt_add_bandSize hmL N + have hN' : bandSize L m ≤ N := hN + omega + +/-- The number of tiles is the square of the number of bands. -/ +theorem card_tiles (L m N : ℕ) : (tiles L m N).card = numBands L m N ^ 2 := by + simp [tiles, sq] + +/-- Section 2.3.4: there are (N / K₀N₀)² ≤ 4N²/M tiles, where `N` is the padded size. The +inequality is multiplied out. -/ +theorem card_tiles_mul_M_le (L m N : ℕ) : (tiles L m N).card * M L m ≤ 4 * padN L m N ^ 2 := by + rw [card_tiles] + calc numBands L m N ^ 2 * M L m + = numBands L m N ^ 2 * N0 L m ^ 2 * K L m := by unfold M; ring + _ ≤ numBands L m N ^ 2 * N0 L m ^ 2 * (4 * K0 L m ^ 2) := + Nat.mul_le_mul_left _ (K_le_four_mul_K0_sq L m) + _ = 4 * padN L m N ^ 2 := by unfold padN bandSize; ring + +/-! ### The table of subsets and the matrices of a tile -/ + +/-- Section 2.3.4: We fix K₀² ≤ K distinct subsets of {1, …, L} of size m: this can be done. + +NOTE. `m ≤ L` is left implicit in the paper; it is a field of `Layout`. -/ +theorem nonempty_layout {L m : ℕ} (hmL : m ≤ L) : Nonempty (Layout L m) := by + have hcard : Fintype.card (Fin (K0 L m) × Fin (K0 L m)) + ≤ Fintype.card {Q : Finset (Fin L) // Q.card = m} := by + rw [Fintype.card_prod, Fintype.card_fin, Fintype.card_subtype, card_subsets_eq_K, ← sq] + exact K0_sq_le_K L m + obtain ⟨e⟩ := Function.Embedding.nonempty_of_card_le hcard + exact ⟨{ hmL := hmL + rowIdx := (Fintype.equivFinOfCardEq (card_outerStr L m)).symm + colIdx := (Fintype.equivFinOfCardEq (card_outerStr L m)).symm + innerIdx := (Fintype.equivFinOfCardEq (card_innerStr m)).symm + table := fun gh => (e gh).1 + table_card := fun gh => (e gh).2 + table_injective := fun _ _ h => e.injective (Subtype.ext h) }⟩ + +/-- The subsets in the table are distinct, so a sum over the table that selects the subset of the +block product `gh` has one term. -/ +private theorem sum_table_eq {L m : ℕ} {A : Type*} [AddCommMonoid A] (lay : Layout L m) + (F : Fin (K0 L m) × Fin (K0 L m) → A) (gh : Fin (K0 L m) × Fin (K0 L m)) : + (∑ gh', if lay.table gh' = lay.table gh then F gh' else 0) = F gh := by + simp [lay.table_injective.eq_iff] + +/-- Section 2.3.4: In a tile, let X_Q and Y_Q be the row block and the column block of the block +product with subset Q. For `X_Q`: it is the `g`-th row block of the row band. -/ +@[simp] +theorem bandFamilyL_table {L m N : ℕ} (lay : Layout L m) (X : Matrix (Fin N) (Fin (D m)) ℤ) (β : ℕ) + (g h : Fin (K0 L m)) : + bandFamilyL lay X β (lay.table (g, h)) = rowBlock lay X (β * K0 L m + g) := + sum_table_eq lay _ (g, h) + +/-- The matrix `Y_Q` of the block product in position `(g, h)` is the `h`-th column block of the +column band. -/ +@[simp] +theorem bandFamilyR_table {L m N : ℕ} (lay : Layout L m) (Y : Matrix (Fin (D m)) (Fin N) ℤ) (β : ℕ) + (g h : Fin (K0 L m)) : + bandFamilyR lay Y β (lay.table (g, h)) = colBlock lay Y (β * K0 L m + h) := + sum_table_eq lay _ (g, h) + +/-- Section 2.3.4: and let X_Q = Y_Q = 0 for the other subsets Q. For `X_Q`. -/ +theorem bandFamilyL_eq_zero {L m N : ℕ} (lay : Layout L m) (X : Matrix (Fin N) (Fin (D m)) ℤ) + (β : ℕ) {Q : Finset (Fin L)} (hQ : ∀ gh, lay.table gh ≠ Q) : bandFamilyL lay X β Q = 0 := + sum_eq_zero fun gh _ => ite_eq_right (hQ gh) + +/-- Section 2.3.4: and let X_Q = Y_Q = 0 for the other subsets Q. For `Y_Q`. -/ +theorem bandFamilyR_eq_zero {L m N : ℕ} (lay : Layout L m) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (β : ℕ) {Q : Finset (Fin L)} (hQ : ∀ gh, lay.table gh ≠ Q) : bandFamilyR lay Y β Q = 0 := + sum_eq_zero fun gh _ => ite_eq_right (hQ gh) + +/-! ### One run of `Full` for each tile -/ + +/-- Section 2.3.4: Then one run of Full computes all K₀² block products of the tile (Lemma 9). +The tile is that of the row band `β` and the column band `β'`; the block product in position `(g, +h)` of its grid is the product of the row block `β K₀ + g` by the column block `β' K₀ + h`. -/ +theorem Full.bandArray_eq_block_mul {L m N : ℕ} (lay : Layout L m) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) (β β' : ℕ) + (g h : Fin (K0 L m)) (r c : OuterStr L m) : + Full.result L (bandArrayL lay X β) (bandArrayR lay Y β') + (outStrOf (lay.table (g, h)) (lay.table_card (g, h)) r c) + = (rowBlock lay X (β * K0 L m + g) * colBlock lay Y (β' * K0 L m + h)) r c := by + rw [bandArrayL, bandArrayR, Full.arrays_eq_mul, bandFamilyL_table, bandFamilyR_table] + +/-- A row (or column) is recovered from its band, the position of its block in the band and its +position in the block. -/ +private theorem position_decomp {L m : ℕ} (lay : Layout L m) (I : ℕ) : + (bandOf L m I * K0 L m + (blockOf lay I : ℕ)) * N0 L m + (offsetOf L m I : ℕ) = I := by + have hband : bandOf L m I = I / N0 L m / K0 L m := by + rw [bandOf, bandSize, Nat.mul_comm, Nat.div_div_eq_div_mul] + have hblock : bandOf L m I * K0 L m + (blockOf lay I : ℕ) = I / N0 L m := by + rw [hband] + exact Nat.div_add_mod' _ _ + rw [hblock] + exact Nat.div_add_mod' _ _ + +/-- Section 2.3.4: and running it on every tile computes all of XY. The entry `(XY)[I, J]` is the +output of the run on the tile of `(I, J)` at the output string of `(I, J)`. -/ +theorem Full.bandArray_eq_mul {L m N : ℕ} (lay : Layout L m) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) (I J : Fin N) : + Full.result L (bandArrayL lay X (bandOf L m I)) (bandArrayR lay Y (bandOf L m J)) (outStrOfPos + lay I J) + = (X * Y) I J := by + unfold outStrOfPos + rw [Full.bandArray_eq_block_mul, Matrix.mul_apply, Matrix.mul_apply, + ← Equiv.sum_comp lay.innerIdx] + -- Both sides are sums over the `D` inner indices; the summands agree because row `I` is recovered + -- from its band, block and place in the block, and likewise column `J`. + refine sum_congr rfl fun k _ => ?_ + simp only [rowBlock, colBlock, Equiv.symm_apply_apply, position_decomp, padRows, padCols, + I.isLt, J.isLt, dite_eq_left, Fin.eta] + +/-- The entry `(XY)[I, J]` as a value of `bilinearProduct`, that is, as a sum over leaves + (equation (2)). This +is the form in which Section 4 uses the tiling (by (2) and Lemma 9, (X_Q Y_Q)[w] is the sum of the +products at the leaves contributing to w). -/ +theorem bilinearProduct_bandArray_eq_mul {L m N : ℕ} (lay : Layout L m) (X : Matrix (Fin N) (Fin + (D m)) ℤ) + (Y : Matrix (Fin (D m)) (Fin N) ℤ) (I J : Fin N) : + bilinearProduct (bandArrayL lay X (bandOf L m I)) (bandArrayR lay Y (bandOf L m J)) + (outStrOfPos lay I J) + = (X * Y) I J := by + rw [← Full.result_eq_bilinearProduct] + exact Full.bandArray_eq_mul lay X Y I J + +/-- Every row and every column lies in one of the bands. + +NOTE. `m ≤ L` is left implicit in the paper. -/ +theorem bandOf_lt_numBands {L m : ℕ} (hmL : m ≤ L) (N I : ℕ) (hI : I < N) : + bandOf L m I < numBands L m N := by + rw [bandOf, Nat.div_lt_iff_lt_mul (bandSize_pos hmL)] + exact hI.trans_le (le_padN hmL N) + +/-! ### The output string of a position -/ + +/-- Section 2.4.4: the inner set of the output string of a position is the subset Q of the block +product containing (I, J). -/ +theorem innerSetO_outStrOfPos {L m : ℕ} (lay : Layout L m) (I J : ℕ) : + innerSetO (outStrOfPos lay I J) = lay.table (blockOf lay I, blockOf lay J) := + innerSetO_outStrOf _ _ _ _ + +/-- The inner set of the output string of a position has exactly `m` elements. -/ +theorem card_innerSetO_outStrOfPos {L m : ℕ} (lay : Layout L m) (I J : ℕ) : + (innerSetO (outStrOfPos lay I J)).card = m := + innerSetO_outStrOfPos lay I J ▸ lay.table_card _ + +/-- Used implicitly in Section 2.4.4 (is indexed by one output string of T, report […] the value +at its output string): different positions of the same tile have different output strings. -/ +theorem outStrOfPos_injective {L m : ℕ} (lay : Layout L m) (I J I' J' : ℕ) + (hI : bandOf L m I = bandOf L m I') (hJ : bandOf L m J = bandOf L m J') + (h : outStrOfPos lay I J = outStrOfPos lay I' J') : I = I' ∧ J = J' := by + -- The two strings have the same inner set, row and column; so the two positions lie in the same + -- block product and at the same place in it. + obtain ⟨hQ, hr, hc⟩ := outStrOf_inj.mp h + obtain ⟨hblockI, hblockJ⟩ := Prod.mk.inj (lay.table_injective hQ) + constructor + · rw [← position_decomp lay I, ← position_decomp lay I', hI, hblockI, + lay.rowIdx.injective hr] + · rw [← position_decomp lay J, ← position_decomp lay J', hJ, hblockJ, + lay.colIdx.injective hc] + +end Cslib.Algorithms.MatrixMultiplication.Schonhage diff --git a/Complexity/Foundations/Algebra/FiniteMagnitude.lean b/Complexity/Foundations/Algebra/FiniteMagnitude.lean new file mode 100644 index 000000000..99c8cad79 --- /dev/null +++ b/Complexity/Foundations/Algebra/FiniteMagnitude.lean @@ -0,0 +1,55 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Algebra.Order.BigOperators.Ring.Finset + +/-! +# Magnitudes of finite sums and products + +## Main results + +* `Finset.abs_sum_mul_le`: bounded signed linear combinations. +* `Finset.abs_prod_le_one`: products of coefficients bounded by one. + +## References + +Adapted selectively from [AlmanWilliams2026, Section 2] and +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical +counts and identities; they give no native program or instruction-cost certificate. +Source-derived descriptions below are paraphrases. +-/ + +@[expose] public section + +namespace Finset + +variable {ι R : Type*} + +/-- If `|f i| ≤ A` for all `i ∈ s`, then `|∑ i ∈ s, f i| ≤ s.card * A`. -/ +theorem abs_sum_le_card_mul [Ring R] [LinearOrder R] [IsOrderedRing R] (s : Finset ι) {f : ι → R} + {A : R} (h : ∀ i ∈ s, |f i| ≤ A) : |∑ i ∈ s, f i| ≤ s.card * A := + (abs_sum_le_sum_abs f s).trans ((sum_le_card_nsmul s _ A h).trans_eq (nsmul_eq_mul _ _)) + +/-- If `|c i| ≤ 1` and `|a i| ≤ A` for all `i ∈ s`, then `|∑ i ∈ s, c i * a i| ≤ s.card * A`. -/ +theorem abs_sum_mul_le [Ring R] [LinearOrder R] [IsOrderedRing R] (s : Finset ι) {c a : ι → R} + {A : R} (hc : ∀ i ∈ s, |c i| ≤ 1) (ha : ∀ i ∈ s, |a i| ≤ A) : + |∑ i ∈ s, c i * a i| ≤ s.card * A := + abs_sum_le_card_mul s fun i hi => + (abs_mul (c i) (a i)).trans_le + ((mul_le_of_le_one_left (abs_nonneg _) (hc i hi)).trans (ha i hi)) + +/-- If `|f i| ≤ 1` for all `i ∈ s`, then `|∏ i ∈ s, f i| ≤ 1`. -/ +theorem abs_prod_le_one [CommRing R] [LinearOrder R] [IsStrictOrderedRing R] (s : Finset ι) + {f : ι → R} (h : ∀ i ∈ s, |f i| ≤ 1) : |∏ i ∈ s, f i| ≤ 1 := + (abs_prod s f).trans_le (prod_le_one₀ (fun _ _ => abs_nonneg _) h) + + +end Finset diff --git a/Complexity/Foundations/Algebra/GeometricTail.lean b/Complexity/Foundations/Algebra/GeometricTail.lean new file mode 100644 index 000000000..fa60e6dd2 --- /dev/null +++ b/Complexity/Foundations/Algebra/GeometricTail.lean @@ -0,0 +1,42 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Algebra.Field.GeomSum +public import Mathlib.Algebra.Order.BigOperators.Ring.Finset + +/-! +# Strict bounds for finite geometric tails + +## Main results + +* `geom_sum_Ico_lt_of_lt_one`: every finite tail is strictly below the infinite tail. + +## References + +Adapted selectively from [AlmanWilliams2026, Section 2] and +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical +counts and identities; they give no native program or instruction-cost certificate. +Source-derived descriptions below are paraphrases. +-/ + +@[expose] public section + +/-- For `0 < x < 1`, `∑ i ∈ Ico m n, x ^ i < x ^ m / (1 - x)`. Mathlib's +`geom_sum_Ico_le_of_lt_one` has `≤`, for `0 ≤ x`. -/ +theorem geom_sum_Ico_lt_of_lt_one {K : Type*} [Field K] [LinearOrder K] [IsStrictOrderedRing K] + {x : K} (hx : 0 < x) (hx1 : x < 1) (m n : ℕ) : + ∑ i ∈ Finset.Ico m n, x ^ i < x ^ m / (1 - x) := by + have hpos : 0 < 1 - x := sub_pos.2 hx1 + rcases le_total m n with hmn | hnm + · rw [geom_sum_Ico' hx1.ne hmn] + exact div_lt_div_of_pos_right (sub_lt_self _ (pow_pos hx n)) hpos + · rw [Finset.Ico_eq_empty_of_le hnm, Finset.sum_empty] + exact div_pos (pow_pos hx m) hpos diff --git a/Complexity/Foundations/Combinatorics/BinomialUpperBound.lean b/Complexity/Foundations/Combinatorics/BinomialUpperBound.lean new file mode 100644 index 000000000..f47c81e9d --- /dev/null +++ b/Complexity/Foundations/Combinatorics/BinomialUpperBound.lean @@ -0,0 +1,52 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Analysis.Complex.Exponential +public import Mathlib.Data.Nat.Choose.Bounds + +/-! +# An exponential upper bound on binomial coefficients + +## Main results + +* `Nat.choose_le_exp_mul_div_pow`: the bound `(e n / k)^k`, including `k = 0`. + +## References + +Adapted selectively from [AlmanWilliams2026, Section 2] and +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical +counts and identities; they give no native program or instruction-cost certificate. +Source-derived descriptions below are paraphrases. +-/ + +@[expose] public section + +namespace Nat + +/-- `binom(n, k) ≤ (e n / k) ^ k`: indeed `binom(n, k) ≤ n ^ k / k!`, and +`k ^ k / k! ≤ e ^ k` is one term of the exponential series. For `k = 0` both sides are `1`. -/ +theorem choose_le_exp_mul_div_pow (n k : ℕ) : + (n.choose k : ℝ) ≤ (Real.exp 1 * (n : ℝ) / (k : ℝ)) ^ k := by + obtain rfl | hk := k.eq_zero_or_pos + · rw [choose_zero_right, pow_zero, cast_one] + have hk' : (0 : ℝ) < k := cast_pos.2 hk + have hfact : (0 : ℝ) < k.factorial := cast_pos.2 k.factorial_pos + have hexp : (k : ℝ) ^ k ≤ Real.exp k * k.factorial := + (div_le_iff₀ hfact).1 (Real.pow_div_factorial_le_exp _ hk'.le k) + rw [div_pow, mul_pow, ← Real.exp_nat_mul, mul_one] + refine (choose_le_pow_div k n).trans ?_ + rw [div_le_div_iff₀ hfact (pow_pos hk' k)] + calc (n : ℝ) ^ k * (k : ℝ) ^ k ≤ (n : ℝ) ^ k * (Real.exp k * k.factorial) := + mul_le_mul_of_nonneg_left hexp (pow_nonneg n.cast_nonneg k) + _ = Real.exp k * (n : ℝ) ^ k * k.factorial := by ring + + +end Nat diff --git a/Complexity/Foundations/Combinatorics/FunctionPlaces.lean b/Complexity/Foundations/Combinatorics/FunctionPlaces.lean new file mode 100644 index 000000000..05960b70c --- /dev/null +++ b/Complexity/Foundations/Combinatorics/FunctionPlaces.lean @@ -0,0 +1,141 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Algebra.Group.Action.Defs +public import Mathlib.Algebra.BigOperators.GroupWithZero.Finset +public import Mathlib.Data.Finset.Powerset +public import Mathlib.Data.Fintype.BigOperators + +/-! +# Counting functions by constrained positions + +## Main results + +* `Finset.card_pi_places_card`: count functions by the positions with a property. +* `Finset.card_places_card`: binomial count without pointwise constraints. + +## References + +Adapted selectively from [AlmanWilliams2026, Section 2] and +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical +counts and identities; they give no native program or instruction-cost certificate. +Source-derived descriptions below are paraphrases. +-/ + +@[expose] public section + +namespace Finset + +variable {ι : Type*} [Fintype ι] [DecidableEq ι] + +/-- A product that takes the value `a` on `S` and `b` elsewhere. -/ +theorem prod_ite_mem_const {M : Type*} [CommMonoid M] (S : Finset ι) (a b : M) : + (∏ i, if i ∈ S then a else b) = a ^ #S * b ^ (Fintype.card ι - #S) := by + rw [prod_ite, prod_const, prod_const, filter_mem_eq_inter, univ_inter, filter_not, + filter_mem_eq_inter, univ_inter, ← compl_eq_univ_sdiff, card_compl] + +/-- A product with the factors `1` on `S ∩ Q`, `0` on `S \ Q`, `c` on `Q \ S` and `1` elsewhere: it +is `0` unless `S ⊆ Q`. -/ +theorem prod_ite_ite_subset {M : Type*} [CommMonoidWithZero M] (Q S : Finset ι) (c : M) : + (∏ i, if i ∈ S then (if i ∈ Q then 1 else 0) else (if i ∈ Q then c else 1)) + = if S ⊆ Q then c ^ (#Q - #S) else 0 := by + split_ifs with h + · have hterm : ∀ i, (if i ∈ S then (if i ∈ Q then 1 else 0) else (if i ∈ Q then c else 1)) + = if i ∈ Q \ S then c else 1 := fun i => by + by_cases hS : i ∈ S + · simp [hS, h hS] + · simp [hS] + simp_rw [hterm] + rw [prod_ite_mem_const, one_pow, mul_one, card_sdiff_of_subset h] + · obtain ⟨i, hiS, hiQ⟩ := not_subset.mp h + exact prod_eq_zero (mem_univ i) (by simp [hiS, hiQ]) + +/-- A product with the factor `0` on `T \ S` and `1` elsewhere: it is `0` unless `T ⊆ S`. -/ +theorem prod_ite_ite_superset {M : Type*} [CommMonoidWithZero M] (T S : Finset ι) : + (∏ i, if i ∈ S then 1 else (if i ∈ T then 0 else 1)) = if T ⊆ S then (1 : M) else 0 := by + split_ifs with h + · refine prod_eq_one fun i _ => ?_ + by_cases hS : i ∈ S + · simp [hS] + · have hT : i ∉ T := fun hT => hS (h hT) + simp [hS, hT] + · obtain ⟨i, hiT, hiS⟩ := not_subset.mp h + exact prod_eq_zero (mem_univ i) (by simp [hiS, hiT]) + +/-- The number of sets of `k` elements inside `Q`, each counted `c` times. -/ +theorem sum_powersetCard_ite_subset (Q : Finset ι) (k c : ℕ) : + (∑ S ∈ powersetCard k (univ : Finset ι), if S ⊆ Q then c else 0) = (#Q).choose k * c := by + rw [← sum_filter, sum_const, smul_eq_mul, ← card_powersetCard] + congr 2 + ext S + simp [mem_powersetCard, and_comm] + +/-- The number of sets of `k` elements that contain `T`: choose the complement outside `T`. -/ +theorem card_powersetCard_superset (T : Finset ι) {k : ℕ} (hk : k ≤ Fintype.card ι) : + #{S ∈ powersetCard k (univ : Finset ι) | T ⊆ S} = + (Fintype.card ι - #T).choose (Fintype.card ι - k) := by + rw [← card_compl, ← card_powersetCard] + refine card_bij' (fun S _ => Sᶜ) (fun S _ => Sᶜ) ?_ ?_ (fun _ _ => compl_compl _) + (fun _ _ => compl_compl _) + · intro S hS + simp only [mem_filter, mem_powersetCard, subset_univ, true_and] at hS + simp [mem_powersetCard, hS.1, hS.2, card_compl] + · intro S hS + simp only [mem_powersetCard] at hS + simp only [mem_filter, mem_powersetCard, subset_univ, true_and, card_compl, hS.2] + exact ⟨by omega, by rw [← compl_subset_compl, compl_compl]; exact hS.1⟩ + +/-! ## Functions by the set of places with a property -/ + +variable {α : Type*} [Fintype α] + +/-- The number of functions `f` with `f i ∈ A i` whose set of places with `p` is exactly `S`: choose +a value with `p` at each place of `S` and one without `p` at every other place. -/ +theorem card_pi_places_eq [DecidableEq α] (A : ι → Finset α) (p : α → Prop) [DecidablePred p] + (S : Finset ι) : + #{f : ι → α | (∀ i, f i ∈ A i) ∧ ({i | p (f i)} : Finset ι) = S} = + ∏ i, if i ∈ S then #{a ∈ A i | p a} else #{a ∈ A i | ¬ p a} := by + have hmem (i : ι) (x : α) : (x ∈ if i ∈ S then {a ∈ A i | p a} else {a ∈ A i | ¬ p a}) ↔ + x ∈ A i ∧ (p x ↔ i ∈ S) := by + split_ifs with hi <;> simp [hi] + simp_rw [← apply_ite Finset.card, ← Fintype.card_piFinset] + congr 1 + ext f + simp only [mem_filter, mem_univ, true_and, Fintype.mem_piFinset, hmem, forall_and, + Finset.ext_iff] + +/-- The number of functions `f` with `f i ∈ A i` that have `p` at exactly `k` places, as a sum over +the possible sets of these places. -/ +theorem card_pi_places_card [DecidableEq α] (A : ι → Finset α) (p : α → Prop) [DecidablePred p] + (k : ℕ) : + #{f : ι → α | (∀ i, f i ∈ A i) ∧ #{i | p (f i)} = k} = + ∑ S ∈ powersetCard k (univ : Finset ι), + ∏ i, if i ∈ S then #{a ∈ A i | p a} else #{a ∈ A i | ¬ p a} := by + rw [card_eq_sum_card_fiberwise (f := fun f : ι → α => ({i | p (f i)} : Finset ι)) + (t := powersetCard k (univ : Finset ι)) + fun f hf => mem_coe.2 (mem_powersetCard.2 ⟨subset_univ _, (mem_filter.1 hf).2.2⟩)] + refine sum_congr rfl fun S hS => ?_ + rw [← card_pi_places_eq, filter_filter] + exact congrArg card (filter_congr fun f _ => + ⟨fun h => ⟨h.1.1, h.2⟩, fun h => ⟨⟨h.1, h.2 ▸ (mem_powersetCard.1 hS).2⟩, h.2⟩⟩) + +/-- The number of functions `ι → α` that have `p` at exactly `k` places. -/ +theorem card_places_card (p : α → Prop) [DecidablePred p] (k : ℕ) : + #{f : ι → α | #{i | p (f i)} = k} = + (Fintype.card ι).choose k * (#{a | p a} ^ k * #{a | ¬ p a} ^ (Fintype.card ι - k)) := by + classical + have h := card_pi_places_card (ι := ι) (fun _ => (univ : Finset α)) p k + simp only [mem_univ, implies_true, true_and] at h + rw [h, sum_congr rfl fun S hS => by + rw [prod_ite_mem_const, (mem_powersetCard.1 hS).2], sum_const, card_powersetCard, card_univ, + smul_eq_mul] + +end Finset diff --git a/Complexity/Foundations/Data/Finset/OrderGlue.lean b/Complexity/Foundations/Data/Finset/OrderGlue.lean new file mode 100644 index 000000000..02be86b3f --- /dev/null +++ b/Complexity/Foundations/Data/Finset/OrderGlue.lean @@ -0,0 +1,187 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Data.Finset.Sort + +/-! +# Gluing functions along ordered finite subsets + +## Main definitions + +* `Finset.glue`: join two strings along a subset and its complement. + +## Main results + +* `Finset.eq_glue_iff`, `Finset.glue_inj`: reconstruction and uniqueness. + +## References + +Adapted selectively from [AlmanWilliams2026, Section 2] and +[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical +counts and identities; they give no native program or instruction-cost certificate. +Source-derived descriptions below are paraphrases. +-/ + +@[expose] public section + +open Finset + +namespace Finset + +/-- The complement of a size-`m` subset of `Fin L` has size `L - m`. -/ +theorem card_compl_of_card_eq {L m : ℕ} (Q : Finset (Fin L)) (hQ : Q.card = m) : + Qᶜ.card = L - m := by + rw [Finset.card_compl, Fintype.card_fin, hQ] + +variable {L m : ℕ} (Q : Finset (Fin L)) (hQ : Q.card = m) + +/-! ### The levels of a set and of its complement, in order -/ + +/-- The `k`-th lowest level outside `Q` is not a level of `Q`. -/ +theorem outerLevel_notMem (k : Fin (L - m)) : (Qᶜ.orderEmbOfFin (card_compl_of_card_eq Q hQ)) k ∉ + Q := + mem_compl.mp (Qᶜ.orderEmbOfFin_mem (card_compl_of_card_eq Q hQ) k) + +/-- Every level of `Q` is the `k`-th lowest level of `Q` for some `k`. -/ +theorem exists_innerLevel {ℓ : Fin L} (hℓ : ℓ ∈ Q) : ∃ k, Finset.orderEmbOfFin Q hQ k = ℓ := + (Q.range_orderEmbOfFin hQ).ge hℓ + +/-- Every level outside `Q` is the `k`-th lowest level outside `Q` for some `k`. -/ +theorem exists_outerLevel {ℓ : Fin L} (hℓ : ℓ ∉ Q) : ∃ k, (Qᶜ.orderEmbOfFin (card_compl_of_card_eq + Q hQ)) k = ℓ := + (Qᶜ.range_orderEmbOfFin (card_compl_of_card_eq Q hQ)).ge (mem_coe.mpr (mem_compl.mpr hℓ)) + +/-- The levels of `Q` in order depend only on the set `Q`. -/ +theorem innerLevel_congr {Q' : Finset (Fin L)} (h : Q' = Q) (hQ' : Q'.card = m) : + Finset.orderEmbOfFin Q' hQ' = Finset.orderEmbOfFin Q hQ := by + subst h + rfl + +/-- The levels outside `Q` in order depend only on the set `Q`. -/ +theorem outerLevel_congr {Q' : Finset (Fin L)} (h : Q' = Q) (hQ' : Q'.card = m) : + (Q'ᶜ.orderEmbOfFin (card_compl_of_card_eq Q' hQ')) = (Qᶜ.orderEmbOfFin (card_compl_of_card_eq + Q hQ)) := by + subst h + rfl + +/-- A property holds at every level if and only if it holds at the levels of `Q` and at the levels +outside `Q`. -/ +theorem forall_level_iff {P : Fin L → Prop} : + (∀ ℓ, P ℓ) ↔ (∀ k, P (Finset.orderEmbOfFin Q hQ k)) ∧ ∀ k, P ((Qᶜ.orderEmbOfFin + (card_compl_of_card_eq Q hQ)) k) := by + refine ⟨fun h => ⟨fun _ => h _, fun _ => h _⟩, fun ⟨hin, hout⟩ ℓ => ?_⟩ + by_cases hℓ : ℓ ∈ Q + · obtain ⟨k, rfl⟩ := exists_innerLevel Q hQ hℓ + exact hin k + · obtain ⟨k, rfl⟩ := exists_outerLevel Q hQ hℓ + exact hout k + +/-- A property cuts out the set `Q` if and only if it holds at the levels of `Q` and fails at the +other levels. -/ +theorem filter_eq_iff_forall_level {P : Fin L → Prop} [DecidablePred P] : + univ.filter P = Q ↔ (∀ k, P (Finset.orderEmbOfFin Q hQ k)) ∧ ∀ k, ¬ P ((Qᶜ.orderEmbOfFin + (card_compl_of_card_eq Q hQ)) k) := by + simp only [Finset.ext_iff, forall_level_iff Q hQ, mem_filter, mem_univ, true_and, + Finset.orderEmbOfFin_mem, outerLevel_notMem Q hQ, iff_true, iff_false] + +/-- A property that holds at the `m` levels of `Q`, and at `m` levels in all, cuts out `Q`. -/ +theorem filter_eq_of_forall_innerLevel {P : Fin L → Prop} [DecidablePred P] + (hin : ∀ k, P (Finset.orderEmbOfFin Q hQ k)) (hcard : (univ.filter P).card = m) : univ.filter + P = Q := by + refine (eq_of_subset_of_card_le (fun ℓ hℓ => ?_) (hcard.trans hQ.symm).le).symm + obtain ⟨k, rfl⟩ := exists_innerLevel Q hQ hℓ + exact mem_filter.mpr ⟨mem_univ _, hin k⟩ + +/-! ### Gluing two strings along a set of levels -/ + +variable {α : Type*} + +/-- The string that has the letters of `f` at the levels of `Q` and the letters of `g` at the other +levels, both in the order of the levels. -/ +def glue (f : Fin m → α) (g : Fin (L - m) → α) : Fin L → α := + fun ℓ => + if hℓ : ℓ ∈ Q then f ((Q.orderIsoOfFin hQ).symm ⟨ℓ, hℓ⟩) + else g ((Qᶜ.orderIsoOfFin (card_compl_of_card_eq Q hQ)).symm ⟨ℓ, mem_compl.mpr hℓ⟩) + +/-- At the `k`-th lowest level of `Q`, the glued string has the `k`-th letter of `f`. -/ +@[simp] +theorem glue_innerLevel (f : Fin m → α) (g : Fin (L - m) → α) (k : Fin m) : + glue Q hQ f g (Finset.orderEmbOfFin Q hQ k) = f k := by + rw [glue, dite_eq_left (Finset.orderEmbOfFin_mem Q hQ k)] + exact congrArg f ((OrderIso.symm_apply_eq _).mpr (Subtype.ext rfl)) + +/-- At the `k`-th lowest level outside `Q`, the glued string has the `k`-th letter of `g`. -/ +@[simp] +theorem glue_outerLevel (f : Fin m → α) (g : Fin (L - m) → α) (k : Fin (L - m)) : + glue Q hQ f g ((Qᶜ.orderEmbOfFin (card_compl_of_card_eq Q hQ)) k) = g k := by + rw [glue, dite_eq_right (outerLevel_notMem Q hQ k)] + exact congrArg g ((OrderIso.symm_apply_eq _).mpr (Subtype.ext rfl)) + +/-- At the `k`-th lowest level of a set `Q'` that is equal to `Q`, the string glued along `Q` has +the `k`-th letter of `f`. -/ +theorem glue_innerLevel_of_eq (f : Fin m → α) (g : Fin (L - m) → α) {Q' : Finset (Fin L)} + (h : Q' = Q) (hQ' : Q'.card = m) (k : Fin m) : glue Q hQ f g (Finset.orderEmbOfFin Q' hQ' k) = + f k := by + rw [innerLevel_congr Q hQ h, glue_innerLevel] + +/-- At the `k`-th lowest level outside a set `Q'` that is equal to `Q`, the string glued along `Q` +has the `k`-th letter of `g`. -/ +theorem glue_outerLevel_of_eq (f : Fin m → α) (g : Fin (L - m) → α) {Q' : Finset (Fin L)} + (h : Q' = Q) (hQ' : Q'.card = m) (k : Fin (L - m)) : + glue Q hQ f g ((Q'ᶜ.orderEmbOfFin (card_compl_of_card_eq Q' hQ')) k) = g k := by + subst h + apply glue_outerLevel + +variable {f f' : Fin m → α} {g g' : Fin (L - m) → α} + +/-- The glued string is the only string with the letters of `f` at the levels of `Q` and the letters +of `g` at the other levels. -/ +theorem eq_glue_iff {u : Fin L → α} : + u = glue Q hQ f g + ↔ (∀ k, u (Finset.orderEmbOfFin Q hQ k) = f k) ∧ ∀ k, u ((Qᶜ.orderEmbOfFin + (card_compl_of_card_eq Q hQ)) k) = g k := by + simp only [funext_iff, forall_level_iff Q hQ, glue_innerLevel, glue_outerLevel] + +/-- The two strings can be read off the glued string. -/ +theorem glue_inj : glue Q hQ f g = glue Q hQ f' g' ↔ f = f' ∧ g = g' := by + rw [eq_glue_iff] + simp only [glue_innerLevel, glue_outerLevel, funext_iff] + +/-- A map applied to a glued string, letter by letter. -/ +theorem map_glue {β : Type*} (φ : α → β) : + (fun ℓ => φ (glue Q hQ f g ℓ)) = glue Q hQ (fun k => φ (f k)) (fun k => φ (g k)) := by + simp only [eq_glue_iff, glue_innerLevel, glue_outerLevel, implies_true, and_self] + +/-! ### Alphabets with inner and outer letters -/ + +variable (IsInner : α → Prop) [DecidablePred IsInner] + +/-- If the letters of `f` are inner and those of `g` are outer, then the glued string has its inner +letters exactly at the levels of `Q`. -/ +theorem filter_glue (hf : ∀ k, IsInner (f k)) (hg : ∀ k, ¬ IsInner (g k)) : + (univ.filter fun ℓ => IsInner (glue Q hQ f g ℓ)) = Q := by + simpa only [filter_eq_iff_forall_level Q hQ, glue_innerLevel, glue_outerLevel] using + And.intro hf hg + +/-- Let every inner letter `s` be `inner i` for its index `i = innerIndex s`, and every outer letter +be `outer o` for its index `o = outerIndex s`. Then a string whose inner letters are at the levels +of `Q` is rebuilt by gluing the letters `inner i` and `outer o` for the indices of its letters. -/ +theorem glue_index {I O : Type*} {inner : I → α} {outer : O → α} {innerIndex : α → I} + {outerIndex : α → O} (hI : ∀ s, IsInner s → inner (innerIndex s) = s) + (hO : ∀ s, ¬ IsInner s → outer (outerIndex s) = s) {u : Fin L → α} + (hu : (univ.filter fun ℓ => IsInner (u ℓ)) = Q) : + u = glue Q hQ (fun k => inner (innerIndex (u (Finset.orderEmbOfFin Q hQ k)))) + (fun k => outer (outerIndex (u ((Qᶜ.orderEmbOfFin (card_compl_of_card_eq Q hQ)) k)))) := by + obtain ⟨hin, hout⟩ := (filter_eq_iff_forall_level Q hQ).mp hu + exact (eq_glue_iff Q hQ).mpr ⟨fun k => (hI _ (hin k)).symm, fun k => (hO _ (hout k)).symm⟩ + + +end Finset diff --git a/ComplexityTests/SchonhageThinProduct.lean b/ComplexityTests/SchonhageThinProduct.lean new file mode 100644 index 000000000..3eff3864f --- /dev/null +++ b/ComplexityTests/SchonhageThinProduct.lean @@ -0,0 +1,165 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +import Complexity.Algorithms.MatrixMultiplication.Schonhage.Magnitude +import Complexity.Algorithms.MatrixMultiplication.Schonhage.Full +import Complexity.Algorithms.MatrixMultiplication.Schonhage.Pruned + +/-! +Regression clients for finite thin multiplication. Small computable layouts exercise actual signed +products and padding. The positive-depth parameter instance uses symbolic matrices so its large +outer dimension is never enumerated. No example claims a native instruction bound. +-/ + +open Finset Cslib.Algorithms.MatrixMultiplication.Schonhage + +namespace SchonhageThinProductTests + +/-- One outer level: blocks have three rows, and the inner dimension is one. -/ +private def outerLayout : Layout 1 0 where + hmL := by decide + rowIdx := (Equiv.funUnique (Fin 1) (Fin 3)).symm + colIdx := (Equiv.funUnique (Fin 1) (Fin 3)).symm + innerIdx := by + change Fin 1 ≃ (Fin 0 → Fin 2 × Fin 2) + exact Equiv.ofUnique _ _ + table := fun _ => ∅ + table_card := fun _ => rfl + table_injective := by + change Function.Injective (fun _ : Fin 1 × Fin 1 => _) + exact fun _ _ _ => Subsingleton.elim _ _ + +/-- One inner level: four inner indices and one row per block. -/ +private def innerLayout : Layout 1 1 where + hmL := by decide + rowIdx := by + change Fin 1 ≃ (Fin 0 → Fin 3) + exact Equiv.ofUnique _ _ + colIdx := by + change Fin 1 ≃ (Fin 0 → Fin 3) + exact Equiv.ofUnique _ _ + innerIdx := (finProdFinEquiv (m := 2) (n := 2)).symm.trans + (Equiv.funUnique (Fin 1) (Fin 2 × Fin 2)).symm + table := fun _ => {0} + table_card := fun _ => by decide + table_injective := by + change Function.Injective (fun _ : Fin 1 × Fin 1 => _) + exact fun _ _ _ => Subsingleton.elim _ _ + +private def X : Matrix (Fin 4) (Fin 1) ℤ := !![-3; 2; 0; 5] +private def Y : Matrix (Fin 1) (Fin 4) ℤ := !![2, -1, 4, 0] +private def W : Finset (Fin 4 × Fin 4) := {(0, 1), (3, 2), (2, 3)} + +-- N is not a multiple of the band size. The last two padded rows and columns are zero. +example : padN 1 0 4 = 6 := by decide +example : numBands 1 0 4 = 2 := by decide +example : padN 1 0 0 = 0 := by decide +kernel +example : padRows X 4 0 = 0 ∧ padCols Y 0 5 = 0 := by decide +kernel +example : padRows X 3 0 = 5 ∧ padCols Y 0 2 = 4 := by decide + +example : outStrOfPos outerLayout 0 1 = ![.z 0 1] := by decide +example : bandArrayL outerLayout X 0 ![.x 0] = -3 := by decide +kernel +-- These compute the finite recursion itself, including encodings and the wanted-output mapping. +example : Pruned.result 1 (encodingL (bandArrayL outerLayout X 0)) + (encodingR (bandArrayR outerLayout Y 0)) (wantedStrings outerLayout W 0 0) + (outStrOfPos outerLayout 0 1) = 3 := by decide +kernel + +example : Pruned.result 1 (encodingL (bandArrayL outerLayout X 1)) + (encodingR (bandArrayR outerLayout Y 0)) (wantedStrings outerLayout W 1 0) + (outStrOfPos outerLayout 3 2) = 20 := by decide +kernel + +example : Pruned.result 1 (encodingL (bandArrayL outerLayout X 0)) + (encodingR (bandArrayR outerLayout Y 1)) (wantedStrings outerLayout W 0 1) + (outStrOfPos outerLayout 2 3) = 0 := by decide +kernel + +-- Every queried product entry, through the general correctness theorem. +example (I J : Fin 4) (h : (I, J) ∈ W) : + Pruned.result 1 (encodingL (bandArrayL outerLayout X (bandOf 1 0 I))) + (encodingR (bandArrayR outerLayout Y (bandOf 1 0 J))) + (wantedStrings outerLayout W (bandOf 1 0 I) (bandOf 1 0 J)) + (outStrOfPos outerLayout I J) = (X * Y) I J := + thin_product_spec outerLayout X Y W I J h + +example : ∑ T ∈ tiles 1 0 4, (wantedStrings outerLayout W T.1 T.2).card = 3 := by decide +kernel +example : ∑ T ∈ tiles 1 0 4, (leaves (wantedStrings outerLayout W T.1 T.2)).card = 3 := by + decide +example : wantedStrings outerLayout W 1 1 = ∅ := by decide +example : (leaves (wantedStrings outerLayout W 1 1)).card = 0 := by decide +kernel + +-- Four signed summands at a genuine inner level; the tensor's error contributes nothing. +private def A : Matrix (Fin 1) (Fin 4) ℤ := !![-3, 2, 0, 5] +private def B : Matrix (Fin 4) (Fin 1) ℤ := !![2; -1; 4; 0] + +example : Full.result 1 (bandArrayL innerLayout A 0) (bandArrayR innerLayout B 0) + (outStrOfPos innerLayout 0 0) = -8 := by decide +kernel + +example : Pruned.result 1 (encodingL (bandArrayL innerLayout A 0)) + (encodingR (bandArrayR innerLayout B 0)) + (wantedStrings innerLayout ({(0, 0)} : Finset (Fin 1 × Fin 1)) 0 0) + (outStrOfPos innerLayout 0 0) = -8 := by decide +kernel + +example : (univ.filter fun τ : Leaf 1 => + Leaf.Contributes τ (fun _ => OutVar.z0) ∧ order 1 τ = 0).card = 1 := by decide +example : (univ.filter fun τ : Leaf 1 => + Leaf.Contributes τ (fun _ => OutVar.z0) ∧ order 1 τ = 1).card = 9 := by decide +example : (leaves ({fun _ => OutVar.z0} : Finset (OutStr 1))).card = 10 := by decide +example : order 0 (fun _ : Fin 1 => Term.P0) = -1 := by decide +example : (leaves (∅ : Finset (OutStr 0))).card = 0 := by decide +kernel + +example (β β' : ℕ) : wantedStrings outerLayout (∅ : Finset (Fin 4 × Fin 4)) β β' = ∅ := by + simp [wantedStrings] + +-- Existence at both the degenerate and positive-depth boundary, without assumptions on layouts. +example : Nonempty (Layout 0 0) := nonempty_layout (by decide) +example : Nonempty (Layout 19 1) := nonempty_layout (by decide) + +private abbrev admittedN : ℕ := 68719476736 +private noncomputable def admittedLayout : Layout 19 1 := + Classical.choice (nonempty_layout (by decide)) +private def singletonQuery : Finset (Fin admittedN × Fin admittedN) := {(0, 0)} + +-- All parameters of the complete composition are simultaneously satisfiable with m > 0. +example : D 1 ^ 18 ≤ admittedN ∧ singletonQuery.card * 2 ^ 1 ≤ admittedN ^ 2 := by + constructor + · rfl + · norm_num [singletonQuery, admittedN] + +example (X' : Matrix (Fin admittedN) (Fin (D 1)) ℤ) + (Y' : Matrix (Fin (D 1)) (Fin admittedN) ℤ) : + Pruned.result 19 (encodingL (bandArrayL admittedLayout X' (bandOf 19 1 0))) + (encodingR (bandArrayR admittedLayout Y' (bandOf 19 1 0))) + (wantedStrings admittedLayout singletonQuery (bandOf 19 1 0) (bandOf 19 1 0)) + (outStrOfPos admittedLayout 0 0) = (X' * Y') 0 0 := + thin_product_spec admittedLayout X' Y' singletonQuery 0 0 (by simp [singletonQuery]) + +example : + ((2 * numBands (19 * 1) 1 admittedN * 10 ^ (19 * 1) : ℕ) : ℝ) ≤ + 4 * (D 1 : ℝ) ^ (-(1 / 18 : ℝ)) * (admittedN : ℝ) ^ 2 := by + have hN : D 1 ^ 18 ≤ admittedN := by norm_num [D, admittedN] + exact encodings_total_le 1 admittedN hN + +example : + ((∑ T ∈ tiles (19 * 1) 1 admittedN, + (leaves (wantedStrings admittedLayout singletonQuery T.1 T.2)).card : ℕ) : ℝ) ≤ + 33 * (D 1 : ℝ) ^ (-(1 / 18 : ℝ)) * (admittedN : ℝ) ^ 2 := by + have hN : D 1 ^ 18 ≤ admittedN := by norm_num [D, admittedN] + have hW : singletonQuery.card * 2 ^ 1 ≤ admittedN ^ 2 := by + norm_num [singletonQuery, admittedN] + exact total_leaves_le (m := 1) (N := admittedN) admittedLayout singletonQuery hN hW + +example (X' : Matrix (Fin admittedN) (Fin (D 1)) ℤ) + (Y' : Matrix (Fin (D 1)) (Fin admittedN) ℤ) : + ((∑ T ∈ tiles (19 * 1) 1 admittedN, + Pruned.totalSize (encodingL (bandArrayL admittedLayout X' T.1)) + (encodingR (bandArrayR admittedLayout Y' T.2)) + (wantedStrings admittedLayout singletonQuery T.1 T.2) : ℕ) : ℝ) ≤ + ((19 * 1 + 1 : ℕ) : ℝ) * (33 * (D 1 : ℝ) ^ (-(1 / 18 : ℝ)) * (admittedN : ℝ) ^ 2) := by + have hN : D 1 ^ 18 ≤ admittedN := by norm_num [D, admittedN] + have hW : singletonQuery.card * 2 ^ 1 ≤ admittedN ^ 2 := by + norm_num [singletonQuery, admittedN] + exact total_passed_sets_le (m := 1) (N := admittedN) admittedLayout X' Y' singletonQuery hN hW + +end SchonhageThinProductTests diff --git a/NOTICE b/NOTICE index 5fb9b6c14..65eb16f1c 100644 --- a/NOTICE +++ b/NOTICE @@ -3,7 +3,9 @@ Complexity/Algorithms/ExactTriangle/{Defs,Weights,Lopsided,Hashing,Splitting,Wit Complexity/Algorithms/MatrixMultiplication/Schonhage/, Complexity/Algorithms/ThreeSum/, Complexity/Algorithms/WordRAM/ThreeSum/, -Complexity/Foundations/Data/Finset/{Average,Quotient}.lean, +Complexity/Foundations/Algebra/{FiniteMagnitude,GeometricTail}.lean, +Complexity/Foundations/Combinatorics/{BinomialUpperBound,FunctionPlaces}.lean, +Complexity/Foundations/Data/Finset/{Average,OrderGlue,Quotient}.lean, Complexity/Foundations/Data/Int/Residue.lean, Complexity/Foundations/Data/Nat/CeilDiv.lean, Complexity/Foundations/Data/Matrix/{IntegerBits,PairChunks}.lean and diff --git a/ROADMAP.md b/ROADMAP.md index 999c5e63c..5805e1216 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -426,8 +426,10 @@ each statement against the numbered result. Organize modules by subject, not cha and the selected-prime false-positive bound ([#1442]), finite residue/chunk/piece splitting and witness recovery ([#1447]) are proved mathematically. The tensor identity, full recursion, shared encodings and pruned-evaluation core ([#1459]) include exact visited-leaf and passed-set - bounds, with the empty zero-depth root handled explicitly. It still needs tiling and sparse - leaf bounds, native tensor/sparse-query programs, native Chan–He preprocessing, the reduction + bounds, with the empty zero-depth root handled explicitly. The finite thin-product component + ([#1463]) proves valid layouts, arbitrary-size padding/tiling, exact queried signed products and + sparse encoding/leaf/passed-set totals. These counts are not native instruction costs. It still + needs native tensor/sparse-query programs, native Chan–He preprocessing, the reduction host, the fast Exact Triangle solver and full input/time/width/asymptotic composition, then the improved Section 4 route to exponent 1.9992; size-based width diff --git a/UPSTREAM.md b/UPSTREAM.md index bfa84a869..dde42fa02 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -199,9 +199,16 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he error, full evaluation, shared encodings, pruned values, visited leaves and passed-set totals. Its pure finite recursion traces are not native machine semantics or charged instruction costs. The zero-depth empty-query root exception is explicit, with an unconditional root-or-leaf API. - See [#1459](https://github.com/cslib-community/complexitylib/issues/1459). Tiling/sparse leaf bounds, - native tensor/sparse-query programs, native preprocessing/solvers and full input/time/width - composition remain in #1394. + See [#1459](https://github.com/cslib-community/complexitylib/issues/1459). The complete finite + thin-product component ([#1463](https://github.com/cslib-community/complexitylib/issues/1463)) + adapts `Sec2/{Levels,Lemma9,Tiling,Orders,Lemma11,Theorem5}` and needed finite magnitude bounds. + Layouts contain only indexing choices, whose existence is proved; sparse bounds are derived + from exact contributing-leaf counts. Generic function-position counts, ordered gluing, binomial + and finite-sum bounds live in small Foundations modules. Mathlib `Finset.orderEmbOfFin` indexes + both a level set and its complement, and `Finset.piecewise` restricts answers. No source machine, + problem-statement universe or solver/time hypothesis is imported. Native tensor/sparse-query + programs, native preprocessing/solvers and full input/time/width composition, including the + improved Section 4 route to exponent 1.9992, remain in #1394. * **Typed RAM upstream core.** [CSLib #1158](https://github.com/leanprover/cslib/pull/1158) at `beea60a15eddc91d93e92fa5af9fb965c351d0d5` proposes `Cslib/Computability/Machines/RAM/Basic.lean`, in namespace `Cslib.RAM.Typed`. From 5cbb93d5ddc946364292740e2d85e334f4bfa649 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 08:21:35 -0400 Subject: [PATCH 15/73] =?UTF-8?q?feat(wordram):=20compute=20Chan=E2=80=93H?= =?UTF-8?q?e=20parameters=20from=20native=20input?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement fixed 24-register parameter code with charged numeric helpers, exact existing Chan–He outputs, preserved input memory, derived logarithmic width and subquadratic native time. Add executed boundary and input-init contract examples. Original implementation and proofs credited to Erik Demaine; AI assistance by OpenAI Codex. Part of #1446; independent complete parameter subtask #1458. --- Complexity.lean | 4 + Complexity/Algorithms/WordRAM/NatClog.lean | 116 +++ Complexity/Algorithms/WordRAM/NatLog.lean | 111 +++ Complexity/Algorithms/WordRAM/NatSqrt.lean | 129 +++ .../WordRAM/ThreeSum/ChanHe/Parameters.lean | 891 ++++++++++++++++++ ComplexityTests/WordRAMChanHeParameters.lean | 64 ++ 6 files changed, 1315 insertions(+) create mode 100644 Complexity/Algorithms/WordRAM/NatClog.lean create mode 100644 Complexity/Algorithms/WordRAM/NatLog.lean create mode 100644 Complexity/Algorithms/WordRAM/NatSqrt.lean create mode 100644 Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Parameters.lean create mode 100644 ComplexityTests/WordRAMChanHeParameters.lean diff --git a/Complexity.lean b/Complexity.lean index 10dee5c44..af010bc30 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -89,6 +89,9 @@ public import Complexity.Algorithms.WordRAM.FusionTree.Rank public import Complexity.Algorithms.WordRAM.FusionTree.ReadHeader public import Complexity.Algorithms.WordRAM.FusionTree.Static public import Complexity.Algorithms.WordRAM.Histogram +public import Complexity.Algorithms.WordRAM.NatClog +public import Complexity.Algorithms.WordRAM.NatLog +public import Complexity.Algorithms.WordRAM.NatSqrt public import Complexity.Algorithms.WordRAM.PrefixSums.Dynamic public import Complexity.Algorithms.WordRAM.PrefixSums.Query public import Complexity.Algorithms.WordRAM.PrefixSums.Tree @@ -103,6 +106,7 @@ public import Complexity.Algorithms.WordRAM.Stack.Controller public import Complexity.Algorithms.WordRAM.ThreeSum.Cell public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.Collisions public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.Count +public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.Parameters public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.Residues public import Complexity.Algorithms.WordRAM.ThreeSum.Fill public import Complexity.Algorithms.WordRAM.ThreeSum.Memory diff --git a/Complexity/Algorithms/WordRAM/NatClog.lean b/Complexity/Algorithms/WordRAM/NatClog.lean new file mode 100644 index 000000000..6feabd6b6 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/NatClog.lean @@ -0,0 +1,116 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Notation +public import Mathlib.Data.Nat.Log + +/-! +# A charged native binary logarithm + +## Main definitions + +* `NatClog.program`: successive ceiling division by two. + +## Main results + +* `NatClog.exec`: the ceiling logarithm with a logarithmic charged instruction bound. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.NatClog + +open Cslib.WordRAM + +/-- Quotient, initially the input. -/ +abbrev arg : Fin 4 := 0 +/-- Floor logarithm output. -/ +abbrev result : Fin 4 := 1 +/-- Constant one. -/ +abbrev one : Fin 4 := 2 +/-- Constant two. -/ +abbrev two : Fin 4 := 3 + +/-- Divide the quotient by two and increment the answer. -/ +def body : Code .standard 4 := ram[ arg ← arg + one; arg ← arg / two; result ← result + one ] + +/-- Fixed code for `Nat.clog 2`, including its value zero at input zero. -/ +def program : Code .standard 4 := + .seq (ram[ result ← #0; one ← #1; two ← #2 ]) (.loop (.lt one arg) body) + +/-- Compute the ceiling binary logarithm in at most `5 ⌈log₂ n⌉ + 5` instructions. +Memory is untouched and the input quotient is consumed. -/ +theorem exec {w : ℕ} (x : Data 4) (h2 : Fits w 2) (hn : Fits w (x.regs arg + 1)) + (hout : Fits w (Nat.clog 2 (x.regs arg) + 1)) : + ∃ y, ExecWithin w program x y (5 * Nat.clog 2 (x.regs arg) + 5) ∧ + y.regs result = Nat.clog 2 (x.regs arg) ∧ y.mem = x.mem ∧ + y.regs one = 1 ∧ y.regs two = 2 := by + let I (y : Data 4) : Prop := y.regs one = 1 ∧ y.regs two = 2 ∧ + y.regs arg ≤ x.regs arg ∧ + y.regs result + Nat.clog 2 (y.regs arg) = Nat.clog 2 (x.regs arg) ∧ y.mem = x.mem + have hbody (y : Data 4) (hy : I y) (hc : (Cond.lt one arg).Holds y) : + ∃ z, ExecWithin w body y z 3 ∧ I z ∧ + Nat.clog 2 (z.regs arg) < Nat.clog 2 (y.regs arg) := by + obtain ⟨ho, ht, harg, hsum, hm⟩ := hy + have hgt : 1 < y.regs arg := by simpa only [Cond.holds_lt, ho] using hc + have hpos : 0 < Nat.clog 2 (y.regs arg) := Nat.clog_pos (by decide) (by omega) + have hadd : Fits w (y.regs arg + 1) := hn.of_le (by omega) + have hdiv : Fits w ((y.regs arg + 1) / 2) := + hn.of_le ((Nat.div_le_self _ _).trans (by omega)) + have hinc : Fits w (y.regs result + 1) := hout.of_le (by omega) + refine ⟨?z, ?run, ?inv, ?decrease⟩ + case run => ram_eval [body, ho, ht, hadd, hdiv, hinc, arg, result, one, two, Fin.mk.injEq] + case inv => + refine ⟨?_, ?_, ?_, ?_, ?_⟩ + · simpa +decide [Function.update_apply] using ho + · simpa +decide [Function.update_apply] using ht + · simpa +decide [Function.update_apply] using + (show (y.regs arg + 1) / 2 ≤ x.regs arg from by omega) + · change y.regs result + 1 + Nat.clog 2 ((y.regs arg + 1) / 2) = Nat.clog 2 (x.regs arg) + have h := Nat.clog_of_two_le (b := 2) (by decide) (by omega : 2 ≤ y.regs arg) + simp only [show y.regs arg + 2 - 1 = y.regs arg + 1 by omega] at h + omega + · exact hm + case decrease => + change Nat.clog 2 ((y.regs arg + 1) / 2) < Nat.clog 2 (y.regs arg) + have h := Nat.clog_of_two_le (b := 2) (by decide) (by omega : 2 ≤ y.regs arg) + simp only [show y.regs arg + 2 - 1 = y.regs arg + 1 by omega] at h + omega + let x₀ := ((x.set result 0).set one 1).set two 2 + have hx₀ : I x₀ := by simp +decide [I, x₀, arg, result, one, two] + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I + (fun y => Nat.clog 2 (y.regs arg)) hbody hx₀ + obtain ⟨ho, ht, harg, hsum, hm⟩ := hy + have hzero : Nat.clog 2 (y.regs arg) = 0 := by + have hle : y.regs arg ≤ 1 := by simpa only [Cond.holds_lt, ho, not_lt] using hc + exact Nat.clog_of_right_le_one hle 2 + refine ⟨y, ?_, by omega, hm, ho, ht⟩ + have h1 : Fits w 1 := h2.of_le (by decide) + have hinit : ExecWithin (B := .standard) w (ram[ result ← #0; one ← #1; two ← #2 ]) x x₀ 3 := by + ram_eval [x₀, h1, h2] + convert hinit.seq he using 1 + · rfl + · simp +decide only [x₀, Data.set_regs, Function.update_apply, arg, result, one, two, + ↓reduceIte] + omega + +/-- Run the routine on any injectively chosen registers. -/ +theorem exec_rename {r w : ℕ} (f : Fin 4 → Fin r) (hf : f.Injective) (x : Data r) + (h2 : Fits w 2) (hn : Fits w (x.regs (f arg) + 1)) + (hout : Fits w (Nat.clog 2 (x.regs (f arg)) + 1)) : + ∃ y, ExecWithin w (program.rename f) x y (5 * Nat.clog 2 (x.regs (f arg)) + 5) ∧ + y.regs (f result) = Nat.clog 2 (x.regs (f arg)) ∧ y.mem = x.mem ∧ + y.regs (f one) = 1 ∧ y.regs (f two) = 2 := by + obtain ⟨a, ha, hr, hm, ho, ht⟩ := exec (x.comap f) h2 hn hout + refine ⟨a.extend f x, ha.rename hf, ?_, hm, ?_, ?_⟩ + · exact (hf.extend_apply _ _ result).trans hr + · exact (hf.extend_apply _ _ one).trans ho + · exact (hf.extend_apply _ _ two).trans ht + +end Cslib.Algorithms.WordRAM.NatClog diff --git a/Complexity/Algorithms/WordRAM/NatLog.lean b/Complexity/Algorithms/WordRAM/NatLog.lean new file mode 100644 index 000000000..eb229076e --- /dev/null +++ b/Complexity/Algorithms/WordRAM/NatLog.lean @@ -0,0 +1,111 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Notation +public import Mathlib.Data.Nat.Log + +/-! +# A charged native binary logarithm + +## Main definitions + +* `NatLog.program`: successive division by two. + +## Main results + +* `NatLog.exec`: the floor logarithm with a logarithmic charged instruction bound. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.NatLog + +open Cslib.WordRAM + +/-- Quotient, initially the input. -/ +abbrev arg : Fin 4 := 0 +/-- Floor logarithm output. -/ +abbrev result : Fin 4 := 1 +/-- Constant one. -/ +abbrev one : Fin 4 := 2 +/-- Constant two. -/ +abbrev two : Fin 4 := 3 + +/-- Divide the quotient by two and increment the answer. -/ +def body : Code .standard 4 := ram[ arg ← arg / two; result ← result + one ] + +/-- Fixed code for `Nat.log 2`, including its value zero at input zero. -/ +def program : Code .standard 4 := + .seq (ram[ result ← #0; one ← #1; two ← #2 ]) (.loop (.lt one arg) body) + +/-- Compute the floor binary logarithm in at most `4 log₂ n + 5` instructions. +Memory is untouched and the input quotient is consumed. -/ +theorem exec {w : ℕ} (x : Data 4) (h2 : Fits w 2) (hn : Fits w (x.regs arg)) + (hout : Fits w (Nat.log 2 (x.regs arg) + 1)) : + ∃ y, ExecWithin w program x y (4 * Nat.log 2 (x.regs arg) + 5) ∧ + y.regs result = Nat.log 2 (x.regs arg) ∧ y.mem = x.mem ∧ + y.regs one = 1 ∧ y.regs two = 2 := by + let I (y : Data 4) : Prop := y.regs one = 1 ∧ y.regs two = 2 ∧ + y.regs arg ≤ x.regs arg ∧ + y.regs result + Nat.log 2 (y.regs arg) = Nat.log 2 (x.regs arg) ∧ y.mem = x.mem + have hbody (y : Data 4) (hy : I y) (hc : (Cond.lt one arg).Holds y) : + ∃ z, ExecWithin w body y z 2 ∧ I z ∧ + Nat.log 2 (z.regs arg) < Nat.log 2 (y.regs arg) := by + obtain ⟨ho, ht, harg, hsum, hm⟩ := hy + have hgt : 1 < y.regs arg := by simpa only [Cond.holds_lt, ho] using hc + have hpos : 0 < Nat.log 2 (y.regs arg) := Nat.log_pos (by decide) (by omega) + have hdiv : Fits w (y.regs arg / 2) := hn.of_le ((Nat.div_le_self _ _).trans harg) + have hinc : Fits w (y.regs result + 1) := hout.of_le (by omega) + refine ⟨?z, ?run, ?inv, ?decrease⟩ + case run => ram_eval [body, ho, ht, hdiv, hinc, arg, result, one, two, Fin.mk.injEq] + case inv => + refine ⟨?_, ?_, ?_, ?_, ?_⟩ + · simpa +decide [Function.update_apply] using ho + · simpa +decide [Function.update_apply] using ht + · simpa +decide [Function.update_apply] using (Nat.div_le_self (y.regs arg) 2).trans harg + · change y.regs result + 1 + Nat.log 2 (y.regs arg / 2) = Nat.log 2 (x.regs arg) + rw [Nat.log_div_base] + omega + · exact hm + case decrease => + change Nat.log 2 (y.regs arg / 2) < Nat.log 2 (y.regs arg) + rw [Nat.log_div_base] + omega + let x₀ := ((x.set result 0).set one 1).set two 2 + have hx₀ : I x₀ := by simp +decide [I, x₀, arg, result, one, two] + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I + (fun y => Nat.log 2 (y.regs arg)) hbody hx₀ + obtain ⟨ho, ht, harg, hsum, hm⟩ := hy + have hzero : Nat.log 2 (y.regs arg) = 0 := by + have hle : y.regs arg ≤ 1 := by simpa only [Cond.holds_lt, ho, not_lt] using hc + exact Nat.log_eq_zero_iff.mpr (Or.inl (by omega)) + refine ⟨y, ?_, by omega, hm, ho, ht⟩ + have h1 : Fits w 1 := h2.of_le (by decide) + have hinit : ExecWithin (B := .standard) w (ram[ result ← #0; one ← #1; two ← #2 ]) x x₀ 3 := by + ram_eval [x₀, h1, h2] + convert hinit.seq he using 1 + · rfl + · simp +decide only [x₀, Data.set_regs, Function.update_apply, arg, result, one, two, + ↓reduceIte] + omega + +/-- Run the routine on any injectively chosen registers. -/ +theorem exec_rename {r w : ℕ} (f : Fin 4 → Fin r) (hf : f.Injective) (x : Data r) + (h2 : Fits w 2) (hn : Fits w (x.regs (f arg))) + (hout : Fits w (Nat.log 2 (x.regs (f arg)) + 1)) : + ∃ y, ExecWithin w (program.rename f) x y (4 * Nat.log 2 (x.regs (f arg)) + 5) ∧ + y.regs (f result) = Nat.log 2 (x.regs (f arg)) ∧ y.mem = x.mem ∧ + y.regs (f one) = 1 ∧ y.regs (f two) = 2 := by + obtain ⟨a, ha, hr, hm, ho, ht⟩ := exec (x.comap f) h2 hn hout + refine ⟨a.extend f x, ha.rename hf, ?_, hm, ?_, ?_⟩ + · exact (hf.extend_apply _ _ result).trans hr + · exact (hf.extend_apply _ _ one).trans ho + · exact (hf.extend_apply _ _ two).trans ht + +end Cslib.Algorithms.WordRAM.NatLog diff --git a/Complexity/Algorithms/WordRAM/NatSqrt.lean b/Complexity/Algorithms/WordRAM/NatSqrt.lean new file mode 100644 index 000000000..98b3ad67e --- /dev/null +++ b/Complexity/Algorithms/WordRAM/NatSqrt.lean @@ -0,0 +1,129 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Notation +public import Mathlib.Data.Nat.Sqrt +import Mathlib.Tactic.Linarith + +/-! +# A charged native integer square root + +## Main definitions + +* `NatSqrt.program`: successive squares. + +## Main results + +* `NatSqrt.exec`: integer square root in a square-root number of native instructions. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.NatSqrt + +open Cslib.WordRAM + +/-- Input integer, preserved. -/ +abbrev arg : Fin 6 := 0 +/-- Current root and output. -/ +abbrev root : Fin 6 := 1 +/-- Successor of the current root. -/ +abbrev next : Fin 6 := 2 +/-- Square of the successor. -/ +abbrev square : Fin 6 := 3 +/-- Constant one. -/ +abbrev one : Fin 6 := 4 +/-- Input plus one. -/ +abbrev bound : Fin 6 := 5 + +/-- Advance the root and compute the next square. -/ +def body : Code .standard 6 := ram[ + root ← next; next ← next + one; square ← next * next ] + +/-- A fixed program for the integer square root, with all square computations charged. -/ +def program : Code .standard 6 := + .seq (ram[ root ← #0; next ← #1; square ← #1; one ← #1; bound ← arg + one ]) + (.loop (.lt square bound) body) + +/-- Compute `Nat.sqrt n` in at most `5 sqrt(n) + 7` instructions, preserving input and memory. +The linear bound `4n + 4` covers the final unsuccessful square as well as all counters. -/ +theorem exec {w : ℕ} (x : Data 6) (hfit : Fits w (4 * x.regs arg + 4)) : + ∃ y, ExecWithin w program x y (5 * Nat.sqrt (x.regs arg) + 7) ∧ + y.regs root = Nat.sqrt (x.regs arg) ∧ y.regs arg = x.regs arg ∧ y.mem = x.mem ∧ + y.regs one = 1 := by + let n := x.regs arg + let I (y : Data 6) : Prop := y.regs arg = n ∧ y.regs one = 1 ∧ + y.regs bound = n + 1 ∧ y.regs next = y.regs root + 1 ∧ + y.regs square = y.regs next * y.regs next ∧ + y.regs root ≤ Nat.sqrt n ∧ y.mem = x.mem + have hbody (y : Data 6) (hy : I y) (hc : (Cond.lt square bound).Holds y) : + ∃ z, ExecWithin w body y z 3 ∧ I z ∧ + Nat.sqrt n - z.regs root < Nat.sqrt n - y.regs root := by + obtain ⟨ha, ho, hb, hn, hsq, hr, hm⟩ := hy + have hsmall : y.regs next * y.regs next ≤ n := by + simp only [Cond.holds_lt, hsq, hb] at hc + omega + have hnext : y.regs next ≤ Nat.sqrt n := Nat.le_sqrt.mpr hsmall + have hsl := Nat.sqrt_le n + have hsle := Nat.sqrt_le_self n + have hstep : Fits w (y.regs next + 1) := hfit.of_le (by dsimp [n] at *; omega) + have hsquare : Fits w ((y.regs next + 1) * (y.regs next + 1)) := hfit.of_le (by + change (y.regs next + 1) * (y.regs next + 1) ≤ 4 * n + 4 + have hnn : y.regs next ≤ n := hnext.trans hsle + nlinarith) + refine ⟨?z, ?run, ?inv, ?decrease⟩ + case run => ram_eval [body, ho, hstep, hsquare, root, next, square, one, Fin.mk.injEq] + case inv => + refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · simpa +decide [Function.update_apply] using ha + · simpa +decide [Function.update_apply] using ho + · simpa +decide [Function.update_apply] using hb + · rfl + · rfl + · exact hnext + · exact hm + case decrease => + change Nat.sqrt n - y.regs next < Nat.sqrt n - y.regs root + omega + let x₀ := ((((x.set root 0).set next 1).set square 1).set one 1).set bound (n + 1) + have hx₀ : I x₀ := by simp +decide [I, x₀, n, root, next, square, one, bound, arg] + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I + (fun y => Nat.sqrt n - y.regs root) hbody hx₀ + obtain ⟨ha, ho, hb, hn, hsq, hr, hm⟩ := hy + have hlarge : n < (y.regs root + 1) * (y.regs root + 1) := by + simp only [Cond.holds_lt, hsq, hn, hb] at hc + omega + have hout : y.regs root = Nat.sqrt n := by + have h := Nat.sqrt_lt.mpr hlarge + omega + have h1 : Fits w 1 := hfit.of_le (by omega) + have hbfit : Fits w (x.regs arg + 1) := hfit.of_le (by omega) + have hinit : ExecWithin (B := .standard) w + (ram[ root ← #0; next ← #1; square ← #1; one ← #1; bound ← arg + one ]) x x₀ 5 := by + ram_eval [x₀, n, h1, hbfit, root, next, square, one, bound, arg, Fin.mk.injEq] + refine ⟨y, ?_, hout, ha, hm, ho⟩ + convert hinit.seq he using 1 + · rfl + · simp +decide only [x₀, Data.set_regs, Function.update_apply, root, next, square, one, + bound, arg, n, ↓reduceIte, Nat.sub_zero] + omega + +/-- Run successive squares on any injectively chosen registers. -/ +theorem exec_rename {r w : ℕ} (f : Fin 6 → Fin r) (hf : f.Injective) (x : Data r) + (hfit : Fits w (4 * x.regs (f arg) + 4)) : + ∃ y, ExecWithin w (program.rename f) x y (5 * Nat.sqrt (x.regs (f arg)) + 7) ∧ + y.regs (f root) = Nat.sqrt (x.regs (f arg)) ∧ + y.regs (f arg) = x.regs (f arg) ∧ y.mem = x.mem ∧ y.regs (f one) = 1 := by + obtain ⟨a, ha, hr, hn, hm, ho⟩ := exec (x.comap f) hfit + refine ⟨a.extend f x, ha.rename hf, ?_, ?_, hm, ?_⟩ + · exact (hf.extend_apply _ _ root).trans hr + · exact (hf.extend_apply _ _ arg).trans hn + · exact (hf.extend_apply _ _ one).trans ho + +end Cslib.Algorithms.WordRAM.NatSqrt diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Parameters.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Parameters.lean new file mode 100644 index 000000000..3ddc4b7cc --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Parameters.lean @@ -0,0 +1,891 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.ThreeSum.ChanHe.Defs +public import Complexity.Algorithms.WordRAM.NatLog +public import Complexity.Algorithms.WordRAM.NatClog +public import Complexity.Algorithms.WordRAM.NatSqrt +public import Complexity.Computability.WordRAM.Compile +public import Complexity.Computability.WordRAM.Signed.Encoding +import Mathlib.Tactic.Linarith + +/-! +# Native parameters for Chan–He preprocessing + +Original native programs and proofs. The parameter meanings are the existing finite mathematical +Chan–He definitions, with magnitude bound `2U` after the distinct-position frontend. +All loops and arithmetic are instructions; the program is fixed for each exponent `κ`. + +## Main definitions + +* `Parameters.program`: read the input header and compute the exact parameters. +* `Parameters.Output`: identify the unsigned output registers. + +## Main results + +* `Parameters.runs_input`: native execution from signed-encoded input with derived fits. + +## References + +* [T. M. Chan and Q. He, *Reducing 3SUM to Convolution-3SUM*][ChanHe2020] +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Parameters + +open Cslib.WordRAM + +namespace Math +open Cslib.Algorithms.ThreeSum + +/-- A bound covering arithmetic, unsuccessful loop tests, and all output words. -/ +def budget (n U : ℕ) : ℕ := 8 * (ChanHe.mPar n (2 * U) + 1) ^ 2 + 8 * U + 4 * n + 64 + +private theorem want_le (n U : ℕ) : ChanHe.wPar n (2 * U) + 1 ≤ ChanHe.mPar n (2 * U) := + Nat.le_mul_of_pos_right _ (by omega) + +private theorem digits_le (n U : ℕ) : 5 * labelBits (2 * U) ≤ ChanHe.wPar n (2 * U) := + Nat.le_mul_of_pos_right _ (Nat.succ_pos _) + +private theorem factor_le (n U : ℕ) : + 2 * Nat.log 2 (ChanHe.wPar n (2 * U) + 1) + 4 ≤ ChanHe.mPar n (2 * U) := + Nat.le_mul_of_pos_left _ (Nat.succ_pos _) + +private theorem height_le (n : ℕ) : ChanHe.height n ≤ Nat.log 2 n + 2 := + Nat.clog_le_of_le_pow Nat.lt_two_pow_self.le + +end Math + +/-- Unsigned input length. -/ +abbrev len : Fin 24 := 0 +/-- Original magnitude bound U. -/ +abbrev magnitude : Fin 24 := 1 +/-- Label bits for magnitude bound 2U. -/ +abbrev digits : Fin 24 := 2 +/-- Recursion height. -/ +abbrev height : Fin 24 := 3 +/-- Recursion fuel. -/ +abbrev fuel : Fin 24 := 4 +/-- Number of desired primes. -/ +abbrev want : Fin 24 := 5 +/-- Upper bound on candidate primes. -/ +abbrev primeBound : Fin 24 := 6 +/-- Common packed convolution length, 8m². -/ +abbrev packedLen : Fin 24 := 7 +/-- Magnitude of the padding for the doubled frontend bound, 4U+1. -/ +abbrev padding : Fin 24 := 8 +/-- Constant one. -/ +abbrev one : Fin 24 := 9 +/-- Constant two. -/ +abbrev two : Fin 24 := 10 +/-- Constant three. -/ +abbrev three : Fin 24 := 11 +/-- Constant four. -/ +abbrev four : Fin 24 := 12 +/-- Constant five. -/ +abbrev five : Fin 24 := 13 +/-- Constant eight. -/ +abbrev eight : Fin 24 := 14 +/-- Input length plus two. -/ +abbrev base : Fin 24 := 15 +/-- Consumed logarithm argument. -/ +abbrev quotient : Fin 24 := 16 +/-- Floor logarithm of input length. -/ +abbrev logLen : Fin 24 := 17 +/-- Integer square root of input length. -/ +abbrev root : Fin 24 := 18 +/-- Number of desired primes plus one. -/ +abbrev wantSucc : Fin 24 := 19 +/-- Arithmetic scratch. -/ +abbrev temp : Fin 24 := 20 +/-- Successor square-root counter. -/ +abbrev nextRoot : Fin 24 := 21 +/-- Square of that counter. -/ +abbrev square : Fin 24 := 22 +/-- Square-root test bound. -/ +abbrev rootBound : Fin 24 := 23 + +/-- Exact mathematical outputs; every output is an ordinary unsigned word. -/ +structure Output (n U : ℕ) (x : Data 24) : Prop where + len : x.regs len = n + magnitude : x.regs magnitude = U + digits : x.regs digits = Cslib.Algorithms.ThreeSum.labelBits (2 * U) + height : x.regs height = Cslib.Algorithms.ThreeSum.ChanHe.height n + fuel : x.regs fuel = Cslib.Algorithms.ThreeSum.ChanHe.fuel n + want : x.regs want = Cslib.Algorithms.ThreeSum.ChanHe.wPar n (2 * U) + primeBound : x.regs primeBound = Cslib.Algorithms.ThreeSum.ChanHe.mPar n (2 * U) + packedLen : x.regs packedLen = 8 * Cslib.Algorithms.ThreeSum.ChanHe.mPar n (2 * U) ^ 2 + padding : x.regs padding = 4 * U + 1 + +/-- One header load, constants, and the polynomial base. -/ +def setup : Code .standard 24 := ram[ + quotient ← #0; len ← *quotient; one ← #1; two ← #2; three ← #3; + four ← #4; five ← #5; eight ← #8; base ← len + two ] + +/-- Unroll only the fixed exponent, never the input length or word width. -/ +def power : ℕ → Code .standard 24 + | 0 => ram[ magnitude ← #1 ] + | κ + 1 => .seq (power κ) (ram[ magnitude ← magnitude * base ]) + +/-- Register selection for a floor/ceiling logarithm with output `dst`. -/ +def logRegisters (dst : Fin 24) : Fin 4 → Fin 24 := ![quotient, dst, one, two] + +/-- Register selection for successive squares. -/ +def sqrtRegisters : Fin 6 → Fin 24 := ![len, root, nextRoot, square, one, rootBound] + +private theorem sqrt_injective : sqrtRegisters.Injective := by decide + +/-- The instruction sequence after the power computation. -/ +def calculate : Code .standard 24 := + .seq (ram[ quotient ← four * magnitude; padding ← quotient + one ]) + (.seq (NatLog.program.rename (logRegisters digits)) + (.seq (ram[ digits ← digits + one ]) + (.seq (NatSqrt.program.rename sqrtRegisters) + (.seq (ram[ temp ← root + one; want ← five * digits; want ← want * temp; + wantSucc ← want + one; quotient ← wantSucc ]) + (.seq (NatLog.program.rename (logRegisters temp)) + (.seq (ram[ temp ← two * temp; temp ← temp + four; primeBound ← wantSucc * temp ]) + (.seq (ram[ quotient ← len ]) + (.seq (NatLog.program.rename (logRegisters logLen)) + (.seq (ram[ quotient ← logLen + two ]) + (.seq (NatClog.program.rename (logRegisters height)) + (ram[ fuel ← three * height; fuel ← fuel - two; packedLen ← primeBound * primeBound; + packedLen ← eight * packedLen ]))))))))))) + +/-- Fixed 24-register native code for each fixed polynomial magnitude exponent. -/ +def program (κ : ℕ) : Code .standard 24 := .seq setup (.seq (power κ) calculate) + +private theorem exec_power {w : ℕ} (κ : ℕ) (x : Data 24) (hb : 1 ≤ x.regs base) + (hfit : Fits w (x.regs base ^ κ)) : + ExecWithin w (power κ) x (x.set magnitude (x.regs base ^ κ)) (κ + 1) := by + induction κ with + | zero => simpa only [power, pow_zero] using ExecWithin.const hfit + | succ κ ih => + have hprev : Fits w (x.regs base ^ κ) := + hfit.of_le (Nat.pow_le_pow_right hb (by omega)) + have he := ih hprev + have hm : ExecWithin w (ram[ magnitude ← magnitude * base ]) + (x.set magnitude (x.regs base ^ κ)) + (x.set magnitude (x.regs base ^ (κ + 1))) 1 := by + have hp : Fits w (x.regs base ^ κ * x.regs base) := by + simpa only [pow_succ] using hfit + apply ExecWithin.of_eval (fuel := 0) (n := 1) ?_ (by decide) + simp +decide [Code.eval, Op.eval_bin, Data.put, + BinOp.eval, hp, magnitude, base, pow_succ, Data.set, Function.update_idem] + exact (he.seq hm).mono (by omega) + +/-- An explicit charged bound, including all header reads, copies and setup. Halt adds one. -/ +def time (κ n U : ℕ) : ℕ := κ + 64 + 5 * Nat.sqrt n + 4 * Nat.log 2 (4 * U) + + 4 * Nat.log 2 (Cslib.Algorithms.ThreeSum.ChanHe.wPar n (2 * U) + 1) + + 4 * Nat.log 2 n + 5 * Cslib.Algorithms.ThreeSum.ChanHe.height n + +private theorem time_zero (n U : ℕ) : time 0 n U - 20 = 44 + 5 * Nat.sqrt n + + 4 * Nat.log 2 (4 * U) + + 4 * Nat.log 2 (Cslib.Algorithms.ThreeSum.ChanHe.wPar n (2 * U) + 1) + + 4 * Nat.log 2 n + 5 * Cslib.Algorithms.ThreeSum.ChanHe.height n := by + dsimp [time] + omega + +set_option maxHeartbeats 800000 in +-- Elaborating twelve native segments and their explicit register frames exceeds the default budget. +private theorem exec_calculate {w n U : ℕ} (x : Data 24) + (hlen : x.regs len = n) (hU : x.regs magnitude = U) + (h1 : x.regs one = 1) (h2 : x.regs two = 2) (h3 : x.regs three = 3) + (h4 : x.regs four = 4) (h5 : x.regs five = 5) (h8 : x.regs eight = 8) + (hfit : Fits w (Math.budget n U)) : + ∃ y, ExecWithin w calculate x y (time 0 n U - 20) ∧ Output n U y ∧ y.mem = x.mem := by + let Λ := Cslib.Algorithms.ThreeSum.labelBits (2 * U) + let h := Cslib.Algorithms.ThreeSum.ChanHe.height n + let wp := Cslib.Algorithms.ThreeSum.ChanHe.wPar n (2 * U) + let m := Cslib.Algorithms.ThreeSum.ChanHe.mPar n (2 * U) + have eΛ : Λ = Nat.log 2 (4 * U) + 1 := by + dsimp [Λ, Cslib.Algorithms.ThreeSum.labelBits] + congr 2 + omega + have ew : wp = 5 * Λ * (Nat.sqrt n + 1) := rfl + have em : m = (wp + 1) * (2 * Nat.log 2 (wp + 1) + 4) := rfl + have eh : Cslib.Algorithms.ThreeSum.ChanHe.fuel n = 3 * h - 2 := rfl + have hd : 5 * Λ ≤ wp := Math.digits_le n U + have hw : wp + 1 ≤ m := Math.want_le n U + have hf : 2 * Nat.log 2 (wp + 1) + 4 ≤ m := Math.factor_le n U + have hh : h ≤ Nat.log 2 n + 2 := Math.height_le n + have hl := Nat.log_le_self 2 n + have hroot := Nat.sqrt_le_self n + have hml : Fits w m := hfit.of_le (by dsimp [Math.budget]; nlinarith) + have fitm (v : ℕ) (hv : v ≤ m) : Fits w v := hml.of_le hv + have fitu (v : ℕ) (hv : v ≤ 8 * U + 64) : Fits w v := + hfit.of_le (by dsimp [Math.budget]; omega) + have fitn (v : ℕ) (hv : v ≤ 4 * n + 64) : Fits w v := + hfit.of_le (by dsimp [Math.budget]; omega) + have f1 : Fits w 1 := fitu _ (by omega) + have f2 : Fits w 2 := fitu _ (by omega) + have f4U : Fits w (4 * U) := fitu _ (by omega) + have fpad : Fits w (4 * U + 1) := fitu _ (by omega) + have fΛ : Fits w Λ := fitm _ (by omega) + have flogU : Fits w (Nat.log 2 (4 * U) + 1) := + fitu _ (by have := Nat.log_le_self 2 (4 * U); omega) + have fsqrt : Fits w (4 * n + 4) := fitn _ (by omega) + have fr : Fits w (Nat.sqrt n + 1) := fitn _ (by omega) + have f5Λ : Fits w (5 * Λ) := fitm _ (by omega) + have fwp : Fits w wp := fitm _ (by omega) + have fwps : Fits w (wp + 1) := fitm _ hw + have flogwp : Fits w (Nat.log 2 (wp + 1) + 1) := fitm _ (by omega) + have f2log : Fits w (2 * Nat.log 2 (wp + 1)) := fitm _ (by omega) + have ff : Fits w (2 * Nat.log 2 (wp + 1) + 4) := fitm _ hf + have fn : Fits w n := fitn _ (by omega) + have flogn : Fits w (Nat.log 2 n + 1) := fitn _ (by omega) + have flog2 : Fits w (Nat.log 2 n + 2) := fitn _ (by omega) + have flog3 : Fits w (Nat.log 2 n + 2 + 1) := fitn _ (by omega) + have fh : Fits w (h + 1) := fitn _ (by omega) + have f3h : Fits w (3 * h) := fitn _ (by omega) + have ffuel : Fits w (Cslib.Algorithms.ThreeSum.ChanHe.fuel n) := + fitn _ (by dsimp [Cslib.Algorithms.ThreeSum.ChanHe.fuel]; omega) + have fm2 : Fits w (m * m) := hfit.of_le (by dsimp [Math.budget]; nlinarith) + have fN : Fits w (8 * m ^ 2) := hfit.of_le (by dsimp [Math.budget]; nlinarith) + have fNm : Fits w (8 * (m * m)) := by simpa only [pow_two] using fN + let a := ((x.set quotient (4 * U)).set padding (4 * U + 1)) + have ea : ExecWithin w (ram[ quotient ← four * magnitude; padding ← quotient + one ]) x a 2 := by + ram_eval [a, h4, hU, h1, f4U, fpad, four, magnitude, one, quotient, padding, + Fin.mk.injEq] + have a_len : a.regs len = n := by + simpa +decide [a, quotient, padding, len] using hlen + have a_magnitude : a.regs magnitude = U := by + simpa +decide [a, quotient, padding, magnitude] using hU + have a_one : a.regs one = 1 := by + simpa +decide [a, quotient, padding, one] using h1 + have a_two : a.regs two = 2 := by + simpa +decide [a, quotient, padding, two] using h2 + have a_three : a.regs three = 3 := by + simpa +decide [a, quotient, padding, three] using h3 + have a_four : a.regs four = 4 := by + simpa +decide [a, quotient, padding, four] using h4 + have a_five : a.regs five = 5 := by + simpa +decide [a, quotient, padding, five] using h5 + have a_eight : a.regs eight = 8 := by + simpa +decide [a, quotient, padding, eight] using h8 + have a_quotient : a.regs quotient = 4 * U := by + simp +decide [a, quotient, padding] + have a_padding : a.regs padding = 4 * U + 1 := by + simp +decide [a, quotient, padding] + have a_mem : a.mem = x.mem := rfl + obtain ⟨b, eb, b_digits, b_mem, b_one, b_two⟩ := + NatLog.exec_rename (logRegisters digits) (by decide) a f2 + (by simpa only [logRegisters, NatLog.arg, Matrix.cons_val_zero, a_quotient] using f4U) + (by simpa only [logRegisters, NatLog.arg, Matrix.cons_val_zero, a_quotient, h] using flogU) + simp only [logRegisters, NatLog.arg, NatLog.result, NatLog.one, NatLog.two, + Matrix.cons_val_zero, Matrix.cons_val_one, Matrix.cons_val, + a_quotient] at eb b_digits b_one b_two + have b_len : b.regs len = n := (eb.regs_eq (by decide)).trans a_len + have b_magnitude : b.regs magnitude = U := (eb.regs_eq (by decide)).trans a_magnitude + have b_three : b.regs three = 3 := (eb.regs_eq (by decide)).trans a_three + have b_four : b.regs four = 4 := (eb.regs_eq (by decide)).trans a_four + have b_five : b.regs five = 5 := (eb.regs_eq (by decide)).trans a_five + have b_eight : b.regs eight = 8 := (eb.regs_eq (by decide)).trans a_eight + have b_padding : b.regs padding = 4 * U + 1 := (eb.regs_eq (by decide)).trans a_padding + have b_memory : b.mem = x.mem := b_mem.trans a_mem + let c := (b.set digits (Λ)) + have ec : ExecWithin w (ram[ digits ← digits + one ]) b c 1 := by + ram_eval [c, b_digits, b_one, ← eΛ, fΛ, digits, one, Fin.mk.injEq] + have c_len : c.regs len = n := by + simpa +decide [c, digits, len] using b_len + have c_magnitude : c.regs magnitude = U := by + simpa +decide [c, digits, magnitude] using b_magnitude + have c_three : c.regs three = 3 := by + simpa +decide [c, digits, three] using b_three + have c_four : c.regs four = 4 := by + simpa +decide [c, digits, four] using b_four + have c_five : c.regs five = 5 := by + simpa +decide [c, digits, five] using b_five + have c_eight : c.regs eight = 8 := by + simpa +decide [c, digits, eight] using b_eight + have c_padding : c.regs padding = 4 * U + 1 := by + simpa +decide [c, digits, padding] using b_padding + have c_digits : c.regs digits = Λ := by + simp +decide [c, digits] + have c_one : c.regs one = 1 := by + simpa +decide [c, digits, one] using b_one + have c_two : c.regs two = 2 := by + simpa +decide [c, digits, two] using b_two + have c_mem : c.mem = x.mem := b_memory + obtain ⟨d, ed, d_root, d_len, d_mem, d_one⟩ := NatSqrt.exec_rename + sqrtRegisters sqrt_injective c (by simpa [sqrtRegisters, NatSqrt.arg, c_len] using fsqrt) + simp only [sqrtRegisters, NatSqrt.arg, NatSqrt.root, NatSqrt.one, + Matrix.cons_val_zero, Matrix.cons_val_one, Matrix.cons_val, c_len] at ed d_root d_len d_one + have d_magnitude : d.regs magnitude = U := (ed.regs_eq (by decide)).trans c_magnitude + have d_three : d.regs three = 3 := (ed.regs_eq (by decide)).trans c_three + have d_four : d.regs four = 4 := (ed.regs_eq (by decide)).trans c_four + have d_five : d.regs five = 5 := (ed.regs_eq (by decide)).trans c_five + have d_eight : d.regs eight = 8 := (ed.regs_eq (by decide)).trans c_eight + have d_padding : d.regs padding = 4 * U + 1 := (ed.regs_eq (by decide)).trans c_padding + have d_digits : d.regs digits = Λ := (ed.regs_eq (by decide)).trans c_digits + have d_two : d.regs two = 2 := (ed.regs_eq (by decide)).trans c_two + have d_memory : d.mem = x.mem := d_mem.trans c_mem + let e := (((((d.set temp (Nat.sqrt n + 1)).set want (5 * Λ)).set want (wp)).set wantSucc + (wp + 1)).set quotient (wp + 1)) + have ee : ExecWithin w (ram[ temp ← root + one; want ← five * digits; want ← want * temp; + wantSucc ← want + one; quotient ← wantSucc ]) d e 5 := by + ram_eval [e, d_root, d_one, d_five, d_digits, ← ew, fr, f5Λ, fwp, fwps, temp, root, one, + want, five, digits, wantSucc, quotient, Fin.mk.injEq, Data.set, Function.update_idem] + have e_magnitude : e.regs magnitude = U := by + simpa +decide [e, temp, want, wantSucc, quotient, magnitude] using d_magnitude + have e_three : e.regs three = 3 := by + simpa +decide [e, temp, want, wantSucc, quotient, three] using d_three + have e_four : e.regs four = 4 := by + simpa +decide [e, temp, want, wantSucc, quotient, four] using d_four + have e_five : e.regs five = 5 := by + simpa +decide [e, temp, want, wantSucc, quotient, five] using d_five + have e_eight : e.regs eight = 8 := by + simpa +decide [e, temp, want, wantSucc, quotient, eight] using d_eight + have e_padding : e.regs padding = 4 * U + 1 := by + simpa +decide [e, temp, want, wantSucc, quotient, padding] using d_padding + have e_digits : e.regs digits = Λ := by + simpa +decide [e, temp, want, wantSucc, quotient, digits] using d_digits + have e_two : e.regs two = 2 := by + simpa +decide [e, temp, want, wantSucc, quotient, two] using d_two + have e_root : e.regs root = Nat.sqrt n := by + simpa +decide [e, temp, want, wantSucc, quotient, root] using d_root + have e_one : e.regs one = 1 := by + simpa +decide [e, temp, want, wantSucc, quotient, one] using d_one + have e_len : e.regs len = n := by + simpa +decide [e, temp, want, wantSucc, quotient, len] using d_len + have e_temp : e.regs temp = Nat.sqrt n + 1 := by + simp +decide [e, temp, want, wantSucc, quotient] + have e_want : e.regs want = wp := by + simp +decide [e, temp, want, wantSucc, quotient] + have e_wantSucc : e.regs wantSucc = wp + 1 := by + simp +decide [e, temp, want, wantSucc, quotient] + have e_quotient : e.regs quotient = wp + 1 := by + simp +decide [e, temp, want, wantSucc, quotient] + have e_mem : e.mem = x.mem := d_memory + obtain ⟨f, ef, f_temp, f_mem, f_one, f_two⟩ := + NatLog.exec_rename (logRegisters temp) (by decide) e f2 + (by simpa only [logRegisters, NatLog.arg, Matrix.cons_val_zero, e_quotient] using fwps) + (by simpa only [logRegisters, NatLog.arg, Matrix.cons_val_zero, e_quotient, h] using flogwp) + simp only [logRegisters, NatLog.arg, NatLog.result, NatLog.one, NatLog.two, + Matrix.cons_val_zero, Matrix.cons_val_one, Matrix.cons_val, e_quotient] at ef f_temp f_one f_two + have f_magnitude : f.regs magnitude = U := (ef.regs_eq (by decide)).trans e_magnitude + have f_three : f.regs three = 3 := (ef.regs_eq (by decide)).trans e_three + have f_four : f.regs four = 4 := (ef.regs_eq (by decide)).trans e_four + have f_five : f.regs five = 5 := (ef.regs_eq (by decide)).trans e_five + have f_eight : f.regs eight = 8 := (ef.regs_eq (by decide)).trans e_eight + have f_padding : f.regs padding = 4 * U + 1 := (ef.regs_eq (by decide)).trans e_padding + have f_digits : f.regs digits = Λ := (ef.regs_eq (by decide)).trans e_digits + have f_root : f.regs root = Nat.sqrt n := (ef.regs_eq (by decide)).trans e_root + have f_len : f.regs len = n := (ef.regs_eq (by decide)).trans e_len + have f_want : f.regs want = wp := (ef.regs_eq (by decide)).trans e_want + have f_wantSucc : f.regs wantSucc = wp + 1 := (ef.regs_eq (by decide)).trans e_wantSucc + have f_memory : f.mem = x.mem := f_mem.trans e_mem + let g := (((f.set temp (2 * Nat.log 2 (wp + 1))).set temp + (2 * Nat.log 2 (wp + 1) + 4)).set primeBound m) + have eg : ExecWithin w (ram[ temp ← two * temp; temp ← temp + four; + primeBound ← wantSucc * temp ]) f g 3 := by + ram_eval [g, f_two, f_temp, f_four, f_wantSucc, ← em, f2log, ff, hml, temp, two, four, + primeBound, wantSucc, Fin.mk.injEq, Data.set, Function.update_idem] + have g_magnitude : g.regs magnitude = U := by + simpa +decide [g, temp, primeBound, magnitude] using f_magnitude + have g_three : g.regs three = 3 := by + simpa +decide [g, temp, primeBound, three] using f_three + have g_four : g.regs four = 4 := by + simpa +decide [g, temp, primeBound, four] using f_four + have g_five : g.regs five = 5 := by + simpa +decide [g, temp, primeBound, five] using f_five + have g_eight : g.regs eight = 8 := by + simpa +decide [g, temp, primeBound, eight] using f_eight + have g_padding : g.regs padding = 4 * U + 1 := by + simpa +decide [g, temp, primeBound, padding] using f_padding + have g_digits : g.regs digits = Λ := by + simpa +decide [g, temp, primeBound, digits] using f_digits + have g_root : g.regs root = Nat.sqrt n := by + simpa +decide [g, temp, primeBound, root] using f_root + have g_len : g.regs len = n := by + simpa +decide [g, temp, primeBound, len] using f_len + have g_want : g.regs want = wp := by + simpa +decide [g, temp, primeBound, want] using f_want + have g_wantSucc : g.regs wantSucc = wp + 1 := by + simpa +decide [g, temp, primeBound, wantSucc] using f_wantSucc + have g_temp : g.regs temp = 2 * Nat.log 2 (wp + 1) + 4 := by + simp +decide [g, temp, primeBound] + have g_one : g.regs one = 1 := by + simpa +decide [g, temp, primeBound, one] using f_one + have g_two : g.regs two = 2 := by + simpa +decide [g, temp, primeBound, two] using f_two + have g_primeBound : g.regs primeBound = m := by + simp +decide [g, temp, primeBound] + have g_mem : g.mem = x.mem := f_memory + let j := (g.set quotient (n)) + have ej : ExecWithin (B := .standard) w (ram[ quotient ← len ]) g j 1 := by + ram_eval [j, g_len] + have j_magnitude : j.regs magnitude = U := by + simpa +decide [j, quotient, magnitude] using g_magnitude + have j_three : j.regs three = 3 := by + simpa +decide [j, quotient, three] using g_three + have j_four : j.regs four = 4 := by + simpa +decide [j, quotient, four] using g_four + have j_five : j.regs five = 5 := by + simpa +decide [j, quotient, five] using g_five + have j_eight : j.regs eight = 8 := by + simpa +decide [j, quotient, eight] using g_eight + have j_padding : j.regs padding = 4 * U + 1 := by + simpa +decide [j, quotient, padding] using g_padding + have j_digits : j.regs digits = Λ := by + simpa +decide [j, quotient, digits] using g_digits + have j_root : j.regs root = Nat.sqrt n := by + simpa +decide [j, quotient, root] using g_root + have j_len : j.regs len = n := by + simpa +decide [j, quotient, len] using g_len + have j_want : j.regs want = wp := by + simpa +decide [j, quotient, want] using g_want + have j_wantSucc : j.regs wantSucc = wp + 1 := by + simpa +decide [j, quotient, wantSucc] using g_wantSucc + have j_temp : j.regs temp = 2 * Nat.log 2 (wp + 1) + 4 := by + simpa +decide [j, quotient, temp] using g_temp + have j_one : j.regs one = 1 := by + simpa +decide [j, quotient, one] using g_one + have j_two : j.regs two = 2 := by + simpa +decide [j, quotient, two] using g_two + have j_primeBound : j.regs primeBound = m := by + simpa +decide [j, quotient, primeBound] using g_primeBound + have j_quotient : j.regs quotient = n := by + simp +decide [j, quotient] + have j_mem : j.mem = x.mem := g_mem + obtain ⟨k, ek, k_logLen, k_mem, k_one, k_two⟩ := + NatLog.exec_rename (logRegisters logLen) (by decide) j f2 + (by simpa only [logRegisters, NatLog.arg, Matrix.cons_val_zero, j_quotient] using fn) + (by simpa only [logRegisters, NatLog.arg, Matrix.cons_val_zero, j_quotient, h] using flogn) + simp only [logRegisters, NatLog.arg, NatLog.result, NatLog.one, NatLog.two, + Matrix.cons_val_zero, Matrix.cons_val_one, Matrix.cons_val, + j_quotient] at ek k_logLen k_one k_two + have k_magnitude : k.regs magnitude = U := (ek.regs_eq (by decide)).trans j_magnitude + have k_three : k.regs three = 3 := (ek.regs_eq (by decide)).trans j_three + have k_four : k.regs four = 4 := (ek.regs_eq (by decide)).trans j_four + have k_five : k.regs five = 5 := (ek.regs_eq (by decide)).trans j_five + have k_eight : k.regs eight = 8 := (ek.regs_eq (by decide)).trans j_eight + have k_padding : k.regs padding = 4 * U + 1 := (ek.regs_eq (by decide)).trans j_padding + have k_digits : k.regs digits = Λ := (ek.regs_eq (by decide)).trans j_digits + have k_root : k.regs root = Nat.sqrt n := (ek.regs_eq (by decide)).trans j_root + have k_len : k.regs len = n := (ek.regs_eq (by decide)).trans j_len + have k_want : k.regs want = wp := (ek.regs_eq (by decide)).trans j_want + have k_wantSucc : k.regs wantSucc = wp + 1 := (ek.regs_eq (by decide)).trans j_wantSucc + have k_temp : k.regs temp = 2 * Nat.log 2 (wp + 1) + 4 := (ek.regs_eq (by decide)).trans j_temp + have k_primeBound : k.regs primeBound = m := (ek.regs_eq (by decide)).trans j_primeBound + have k_memory : k.mem = x.mem := k_mem.trans j_mem + let l := (k.set quotient (Nat.log 2 n + 2)) + have el : ExecWithin w (ram[ quotient ← logLen + two ]) k l 1 := by + ram_eval [l, k_logLen, k_two, flog2, quotient, logLen, two, Fin.mk.injEq] + have l_magnitude : l.regs magnitude = U := by + simpa +decide [l, quotient, magnitude] using k_magnitude + have l_three : l.regs three = 3 := by + simpa +decide [l, quotient, three] using k_three + have l_four : l.regs four = 4 := by + simpa +decide [l, quotient, four] using k_four + have l_five : l.regs five = 5 := by + simpa +decide [l, quotient, five] using k_five + have l_eight : l.regs eight = 8 := by + simpa +decide [l, quotient, eight] using k_eight + have l_padding : l.regs padding = 4 * U + 1 := by + simpa +decide [l, quotient, padding] using k_padding + have l_digits : l.regs digits = Λ := by + simpa +decide [l, quotient, digits] using k_digits + have l_root : l.regs root = Nat.sqrt n := by + simpa +decide [l, quotient, root] using k_root + have l_len : l.regs len = n := by + simpa +decide [l, quotient, len] using k_len + have l_want : l.regs want = wp := by + simpa +decide [l, quotient, want] using k_want + have l_wantSucc : l.regs wantSucc = wp + 1 := by + simpa +decide [l, quotient, wantSucc] using k_wantSucc + have l_temp : l.regs temp = 2 * Nat.log 2 (wp + 1) + 4 := by + simpa +decide [l, quotient, temp] using k_temp + have l_primeBound : l.regs primeBound = m := by + simpa +decide [l, quotient, primeBound] using k_primeBound + have l_logLen : l.regs logLen = Nat.log 2 n := by + simpa +decide [l, quotient, logLen] using k_logLen + have l_one : l.regs one = 1 := by + simpa +decide [l, quotient, one] using k_one + have l_two : l.regs two = 2 := by + simpa +decide [l, quotient, two] using k_two + have l_quotient : l.regs quotient = Nat.log 2 n + 2 := by + simp +decide [l, quotient] + have l_mem : l.mem = x.mem := k_memory + obtain ⟨o, eo, o_height, o_mem, o_one, o_two⟩ := + NatClog.exec_rename (logRegisters height) (by decide) l f2 + (by simpa only [logRegisters, NatClog.arg, Matrix.cons_val_zero, l_quotient] using flog3) + (by simpa only [logRegisters, NatClog.arg, Matrix.cons_val_zero, l_quotient, h, + Cslib.Algorithms.ThreeSum.ChanHe.height] using fh) + simp only [logRegisters, NatClog.arg, NatClog.result, NatClog.one, NatClog.two, + Matrix.cons_val_zero, Matrix.cons_val_one, Matrix.cons_val, + l_quotient] at eo o_height o_one o_two + change o.regs height = h at o_height + change ExecWithin w _ l o (5 * h + 5) at eo + have o_magnitude : o.regs magnitude = U := (eo.regs_eq (by decide)).trans l_magnitude + have o_three : o.regs three = 3 := (eo.regs_eq (by decide)).trans l_three + have o_four : o.regs four = 4 := (eo.regs_eq (by decide)).trans l_four + have o_five : o.regs five = 5 := (eo.regs_eq (by decide)).trans l_five + have o_eight : o.regs eight = 8 := (eo.regs_eq (by decide)).trans l_eight + have o_padding : o.regs padding = 4 * U + 1 := (eo.regs_eq (by decide)).trans l_padding + have o_digits : o.regs digits = Λ := (eo.regs_eq (by decide)).trans l_digits + have o_root : o.regs root = Nat.sqrt n := (eo.regs_eq (by decide)).trans l_root + have o_len : o.regs len = n := (eo.regs_eq (by decide)).trans l_len + have o_want : o.regs want = wp := (eo.regs_eq (by decide)).trans l_want + have o_wantSucc : o.regs wantSucc = wp + 1 := (eo.regs_eq (by decide)).trans l_wantSucc + have o_temp : o.regs temp = 2 * Nat.log 2 (wp + 1) + 4 := (eo.regs_eq (by decide)).trans l_temp + have o_primeBound : o.regs primeBound = m := (eo.regs_eq (by decide)).trans l_primeBound + have o_logLen : o.regs logLen = Nat.log 2 n := (eo.regs_eq (by decide)).trans l_logLen + have o_memory : o.mem = x.mem := o_mem.trans l_mem + let p := ((((o.set fuel (3 * h)).set fuel (Cslib.Algorithms.ThreeSum.ChanHe.fuel n)).set packedLen + (m * m)).set packedLen (8 * m ^ 2)) + have ep : ExecWithin w (ram[ fuel ← three * height; fuel ← fuel - two; + packedLen ← primeBound * primeBound; packedLen ← eight * packedLen ]) o p 4 := by + ram_eval [p, o_three, o_height, o_two, o_primeBound, o_eight, ← eh, f3h, ffuel, fm2, fNm, + fuel, three, height, two, packedLen, primeBound, eight, Fin.mk.injEq, pow_two, Data.set, + Function.update_idem] + have p_magnitude : p.regs magnitude = U := by + simpa +decide [p, fuel, packedLen, magnitude] using o_magnitude + have p_three : p.regs three = 3 := by + simpa +decide [p, fuel, packedLen, three] using o_three + have p_four : p.regs four = 4 := by + simpa +decide [p, fuel, packedLen, four] using o_four + have p_five : p.regs five = 5 := by + simpa +decide [p, fuel, packedLen, five] using o_five + have p_eight : p.regs eight = 8 := by + simpa +decide [p, fuel, packedLen, eight] using o_eight + have p_padding : p.regs padding = 4 * U + 1 := by + simpa +decide [p, fuel, packedLen, padding] using o_padding + have p_digits : p.regs digits = Λ := by + simpa +decide [p, fuel, packedLen, digits] using o_digits + have p_root : p.regs root = Nat.sqrt n := by + simpa +decide [p, fuel, packedLen, root] using o_root + have p_len : p.regs len = n := by + simpa +decide [p, fuel, packedLen, len] using o_len + have p_want : p.regs want = wp := by + simpa +decide [p, fuel, packedLen, want] using o_want + have p_wantSucc : p.regs wantSucc = wp + 1 := by + simpa +decide [p, fuel, packedLen, wantSucc] using o_wantSucc + have p_temp : p.regs temp = 2 * Nat.log 2 (wp + 1) + 4 := by + simpa +decide [p, fuel, packedLen, temp] using o_temp + have p_primeBound : p.regs primeBound = m := by + simpa +decide [p, fuel, packedLen, primeBound] using o_primeBound + have p_logLen : p.regs logLen = Nat.log 2 n := by + simpa +decide [p, fuel, packedLen, logLen] using o_logLen + have p_height : p.regs height = h := by + simpa +decide [p, fuel, packedLen, height] using o_height + have p_one : p.regs one = 1 := by + simpa +decide [p, fuel, packedLen, one] using o_one + have p_two : p.regs two = 2 := by + simpa +decide [p, fuel, packedLen, two] using o_two + have p_fuel : p.regs fuel = Cslib.Algorithms.ThreeSum.ChanHe.fuel n := by + simp +decide [p, fuel, packedLen] + have p_packedLen : p.regs packedLen = 8 * m ^ 2 := by + simp +decide [p, fuel, packedLen] + have p_mem : p.mem = x.mem := o_memory + refine ⟨p, ?_, ?_, p_mem⟩ + · exact (ea.seq + (eb.seq + (ec.seq + (ed.seq + (ee.seq + (ef.seq + (eg.seq + (ej.seq + (ek.seq + (el.seq + (eo.seq + (ep)))))))))))).mono (by + rw [time_zero] + dsimp only [wp, h] + exact le_of_eq (by ring)) + · exact ⟨p_len, p_magnitude, p_digits, p_height, p_fuel, p_want, p_primeBound, p_packedLen, + p_padding⟩ + +/-- Read the native unsigned header and compute all parameters, preserving every memory cell. +This local theorem is discharged from a logarithmic width in `runs_input`. -/ +theorem exec {w : ℕ} (κ : ℕ) (x : Data 24) + (hfit : Fits w (Math.budget (x.mem 0) ((x.mem 0 + 2) ^ κ))) : + ∃ y, ExecWithin w (program κ) x y (time κ (x.mem 0) ((x.mem 0 + 2) ^ κ)) ∧ + Output (x.mem 0) ((x.mem 0 + 2) ^ κ) y ∧ y.mem = x.mem := by + let n := x.mem 0 + let U := (n + 2) ^ κ + have fit (v : ℕ) (hv : v ≤ 8 * U + 4 * n + 64) : Fits w v := + hfit.of_le (by change v ≤ Math.budget n U; dsimp [Math.budget]; omega) + have f1 : Fits w 1 := fit _ (by omega) + have f2 : Fits w 2 := fit _ (by omega) + have f3 : Fits w 3 := fit _ (by omega) + have f4 : Fits w 4 := fit _ (by omega) + have f5 : Fits w 5 := fit _ (by omega) + have f8 : Fits w 8 := fit _ (by omega) + have fb : Fits w (n + 2) := fit _ (by omega) + have fU : Fits w U := fit _ (by omega) + let a := (((((((((x.set quotient 0).set len n).set one 1).set two 2).set three 3).set + four 4).set five 5).set eight 8).set base (n + 2)) + have ha : ExecWithin w setup x a 9 := by + ram_eval [setup, a, n, f1, f2, f3, f4, f5, f8, fb, + quotient, len, one, two, three, four, five, eight, base, Fin.mk.injEq] + have hab : a.regs base = n + 2 := by simp [a] + have hp := exec_power κ a (by rw [hab]; omega) (by simpa only [hab] using fU) + let b := a.set magnitude U + have hpb : ExecWithin w (power κ) a b (κ + 1) := by simpa only [hab] using hp + obtain ⟨y, hy, hout, hm⟩ := exec_calculate b + (by simp +decide [b, a, n, magnitude, len, base, one, two, three, four, five, eight, quotient]) + (by simp [b, U, n]) + (by simp +decide [b, a, magnitude, one, base, two, three, four, five, eight]) + (by simp +decide [b, a, magnitude, two, base, three, four, five, eight]) + (by simp +decide [b, a, magnitude, three, base, four, five, eight]) + (by simp +decide [b, a, magnitude, four, base, five, eight]) + (by simp +decide [b, a, magnitude, five, base, eight]) + (by simp +decide [b, a, magnitude, eight, base]) hfit + refine ⟨y, (ha.seq (hpb.seq hy)).mono ?_, hout, hm⟩ + dsimp only [time] + omega + +namespace Math + +open Cslib.Algorithms.ThreeSum + +/-- A polynomial scale for bounding every parameter intermediate. -/ +def scale (n U : ℕ) : ℕ := 16 * (n + 2) * (U + 1) + +private theorem scale_bounds (n U : ℕ) : + 16 ≤ scale n U ∧ n + 2 ≤ scale n U ∧ 4 * U + 2 ≤ scale n U := by + have h := Nat.mul_le_mul_left 16 (Nat.mul_le_mul + (show 1 ≤ n + 2 by omega) (show 1 ≤ U + 1 by omega)) + have hn := Nat.le_mul_of_pos_right (n + 2) (show 0 < 16 * (U + 1) by positivity) + have hU := Nat.le_mul_of_pos_right (U + 1) (show 0 < n + 2 by omega) + dsimp [scale] + have hnu := Nat.zero_le (n * U) + refine ⟨?_, ?_, ?_⟩ <;> nlinarith + +private theorem scale_pow_mono {b : ℕ} (hb : 1 ≤ b) {i j : ℕ} (hij : i ≤ j) : + b ^ i ≤ b ^ j := Nat.pow_le_pow_right hb hij + +/-- A coarse polynomial bound; it also covers final unsuccessful root/log iterations. -/ +theorem budget_le_scale (n U : ℕ) : budget n U ≤ scale n U ^ 20 := by + let b := scale n U + obtain ⟨hb, hn, hU⟩ := scale_bounds n U + change 16 ≤ b at hb + change n + 2 ≤ b at hn + change 4 * U + 2 ≤ b at hU + have hb1 : 1 ≤ b := by omega + have hΛ : labelBits (2 * U) ≤ b := by + have hl := Nat.log_le_self 2 (4 * U) + dsimp [labelBits] + have he : 2 * (2 * U) = 4 * U := by omega + rw [he] + omega + have hr : Nat.sqrt n + 1 ≤ b := by have := Nat.sqrt_le_self n; omega + have hw : ChanHe.wPar n (2 * U) ≤ b ^ 3 := by + calc + _ ≤ b * b * b := by + dsimp [ChanHe.wPar] + exact Nat.mul_le_mul (Nat.mul_le_mul (by omega) hΛ) hr + _ = b ^ 3 := by ring + have hws : ChanHe.wPar n (2 * U) + 1 ≤ b ^ 4 := by + have hp : 1 ≤ b ^ 3 := Nat.one_le_pow _ _ hb1 + calc + _ ≤ 2 * b ^ 3 := by omega + _ ≤ b * b ^ 3 := Nat.mul_le_mul_right _ (by omega) + _ = b ^ 4 := by ring + have hfac : 2 * Nat.log 2 (ChanHe.wPar n (2 * U) + 1) + 4 ≤ b ^ 5 := by + have hl := Nat.log_le_self 2 (ChanHe.wPar n (2 * U) + 1) + have hp : 1 ≤ b ^ 4 := Nat.one_le_pow _ _ hb1 + calc + _ ≤ 6 * b ^ 4 := by omega + _ ≤ b * b ^ 4 := Nat.mul_le_mul_right _ (by omega) + _ = b ^ 5 := by ring + have hm : ChanHe.mPar n (2 * U) ≤ b ^ 9 := by + calc + _ ≤ b ^ 4 * b ^ 5 := Nat.mul_le_mul hws hfac + _ = b ^ 9 := by ring + have hms : ChanHe.mPar n (2 * U) + 1 ≤ 2 * b ^ 9 := by + have hp : 1 ≤ b ^ 9 := Nat.one_le_pow _ _ hb1 + omega + have hsq : (ChanHe.mPar n (2 * U) + 1) ^ 2 ≤ (2 * b ^ 9) ^ 2 := + Nat.pow_le_pow_left hms 2 + have hsmall : 8 * U + 4 * n + 64 ≤ 16 * b := by omega + have hp18 : b ≤ b ^ 18 := by + simpa only [pow_one] using scale_pow_mono hb1 (show 1 ≤ 18 by decide) + have hbudget : budget n U ≤ 48 * b ^ 18 := by + dsimp only [budget] + nlinarith + calc + _ ≤ 48 * b ^ 18 := hbudget + _ ≤ b ^ 2 * b ^ 18 := Nat.mul_le_mul_right _ (by nlinarith) + _ = b ^ 20 := by ring + +/-- A single polynomial bound, with exponent affine in the fixed magnitude exponent. -/ +theorem budget_polynomial (κ n : ℕ) : budget n ((n + 2) ^ κ) ≤ (n + 2) ^ (20 * κ + 120) := by + let a := n + 2 + have ha : 2 ≤ a := by dsimp [a]; omega + have hpow : 1 ≤ a ^ κ := Nat.one_le_pow _ _ (by omega) + have hs : scale n (a ^ κ) ≤ a ^ (κ + 6) := by + calc + _ = 16 * a * (a ^ κ + 1) := rfl + _ ≤ 32 * a * a ^ κ := by nlinarith + _ ≤ a ^ 5 * a * a ^ κ := by + exact Nat.mul_le_mul_right _ (Nat.mul_le_mul_right _ (by + have := Nat.pow_le_pow_left ha 5 + norm_num at this ⊢ + omega)) + _ = a ^ (κ + 6) := by ring + calc + _ ≤ scale n (a ^ κ) ^ 20 := budget_le_scale n (a ^ κ) + _ ≤ (a ^ (κ + 6)) ^ 20 := Nat.pow_le_pow_left hs 20 + _ = a ^ (20 * κ + 120) := by ring + +end Math +/-- Positive logarithmic word-width threshold for the complete parameter computation. -/ +def width (κ n : ℕ) : ℕ := (20 * κ + 121) * Nat.clog 2 (n + 2) + +/-- The width requirement is satisfiable even at empty input and exponent zero. -/ +theorem width_pos (κ n : ℕ) : 0 < width κ n := by + have := Nat.clog_pos (b := 2) (by decide) (show 1 < n + 2 by omega) + dsimp [width] + positivity + +private theorem budget_le_two_pow (κ n : ℕ) : + Math.budget n ((n + 2) ^ κ) ≤ 2 ^ ((20 * κ + 120) * Nat.clog 2 (n + 2)) := by + calc + _ ≤ (n + 2) ^ (20 * κ + 120) := Math.budget_polynomial κ n + _ ≤ (2 ^ Nat.clog 2 (n + 2)) ^ (20 * κ + 120) := + Nat.pow_le_pow_left (Nat.le_pow_clog (by decide) _) _ + _ = _ := by rw [← pow_mul]; congr 1; ring + +/-- Every local fit premise follows from the positive logarithmic width. -/ +theorem budget_fits {κ n w : ℕ} (hw : width κ n ≤ w) : + Fits w (Math.budget n ((n + 2) ^ κ)) := by + have hl := Nat.clog_pos (b := 2) (by decide) (show 1 < n + 2 by omega) + apply Fits.mono (w := width κ n) _ hw + apply (budget_le_two_pow κ n).trans_lt + apply Nat.pow_lt_pow_right (by decide) + dsimp [width] + nlinarith + +/-- Signed-encoded inputs and the unsigned length header fit at the same width. -/ +theorem inputFits {κ w : ℕ} (input : List ℤ) (hw : width κ input.length ≤ w) + (hvalues : ∀ z ∈ input, |z| ≤ ((input.length + 2) ^ κ : ℕ)) : + InputFits w (input.map Signed.encode) := by + have hf := budget_fits hw + refine ⟨?_, ?_⟩ + · simp only [List.length_map] + apply hf.of_le + dsimp [Math.budget] + omega + · intro v hv + obtain ⟨z, hz, rfl⟩ := List.mem_map.mp hv + have habs : z.natAbs ≤ (input.length + 2) ^ κ := by + have hc : (z.natAbs : ℤ) ≤ ((input.length + 2) ^ κ : ℕ) := by + simpa only [Int.natCast_natAbs] using hvalues z hz + exact_mod_cast hc + apply hf.of_le + dsimp [Math.budget, Signed.encode] + split_ifs <;> omega + +/-- Explicit `Oκ(√n + log(n+2))` instruction bound, valid also at `n = 0,1,2`. -/ +theorem time_bound (κ n : ℕ) : + time κ n ((n + 2) ^ κ) ≤ + 5 * Nat.sqrt n + (400 * κ + 3000) * Nat.clog 2 (n + 2) := by + let U := (n + 2) ^ κ + let L := Nat.clog 2 (n + 2) + let E := 20 * κ + 120 + have hL : 1 ≤ L := Nat.clog_pos (by decide) (show 1 < n + 2 by omega) + have hB : Nat.log 2 (Math.budget n U) ≤ E * L := by + calc + _ ≤ Nat.log 2 (2 ^ (E * L)) := Nat.log_mono_right (budget_le_two_pow κ n) + _ = _ := Nat.log_pow (by decide) _ + have hlogU : Nat.log 2 (4 * U) ≤ E * L := + (Nat.log_mono_right (show 4 * U ≤ Math.budget n U by dsimp [Math.budget]; omega)).trans hB + have hw := Math.want_le n U + have hlogw : Nat.log 2 (Cslib.Algorithms.ThreeSum.ChanHe.wPar n (2 * U) + 1) ≤ E * L := by + apply (Nat.log_mono_right ?_).trans hB + dsimp [Math.budget] + nlinarith + have hlogn : Nat.log 2 n ≤ E * L := + (Nat.log_mono_right (show n ≤ Math.budget n U by dsimp [Math.budget]; omega)).trans hB + have hh := Math.height_le n + change time κ n U ≤ _ + dsimp only [time] + dsimp only [E, L] at * + nlinarith + +/-- Native run from actual signed input initialization. No parameters or arithmetic routines +are supplied as advice. The returned state identifies all outputs and keeps *all* input memory. +The flat program has 24 registers and depends only on `κ`; halting adds one instruction. -/ +theorem runs_input {κ w : ℕ} (input : List ℤ) (hw : width κ input.length ≤ w) + (hvalues : ∀ z ∈ input, |z| ≤ ((input.length + 2) ^ κ : ℕ)) : + ∃ y t, t ≤ 5 * Nat.sqrt input.length + + (400 * κ + 3000) * Nat.clog 2 (input.length + 2) ∧ + Runs w ((program κ).program packedLen) (State.init (input.map Signed.encode)) + ⟨(program κ).size, y⟩ t ∧ + Output input.length ((input.length + 2) ^ κ) y ∧ + y.mem = (Data.init (input.map Signed.encode) : Data 24).mem ∧ y.Bounded w := by + have hi := inputFits input hw hvalues + obtain ⟨y, hy, ho, hm⟩ := exec κ (Data.init (input.map Signed.encode)) + (by simpa only [Data.init_mem_zero, List.length_map] using budget_fits hw) + simp only [Data.init_mem_zero, List.length_map] at hy ho + obtain ⟨t, ht, he⟩ := hy + have hr := he.runs (pc := 0) (p := (program κ).program packedLen) + (Program.Contains.self _).append_left + simp only [Nat.zero_add] at hr + refine ⟨y, t, ht.trans (time_bound κ input.length), hr, ho, hm, ?_⟩ + exact hr.bounded_init hi + +/-- The number of native instructions in the finite structured code is affine in `κ`. -/ +theorem size_program (κ : ℕ) : (program κ).size = κ + 71 := by + have hp : (power κ).size = κ + 1 := by + induction κ with + | zero => rfl + | succ κ ih => simp [power, ih] + simp [program, setup, calculate, hp, NatLog.program, NatLog.body, + NatClog.program, NatClog.body, NatSqrt.program, NatSqrt.body] + omega + +/-- The code is the same at every word width. -/ +theorem not_readsWidth (κ : ℕ) : ¬(program κ).ReadsWidth := by + have hs : ¬setup.ReadsWidth := by decide + have hc : ¬calculate.ReadsWidth := by decide + have hp : ¬(power κ).ReadsWidth := by + induction κ with + | zero => decide + | succ κ ih => + change ¬((power κ).ReadsWidth ∨ + (Code.op (Op.bin BinOp.mul magnitude magnitude base) : Code .standard 24).ReadsWidth) + exact not_or.mpr ⟨ih, by decide⟩ + simpa only [program, Code.ReadsWidth, not_or] using And.intro hs (And.intro hp hc) + +/-- Canonical-width executions use only polynomially bounded integers, including memory +addresses, loop counters, and final unsuccessful tests. -/ +theorem intermediates_polynomial {κ : ℕ} (input : List ℤ) + (hvalues : ∀ z ∈ input, |z| ≤ ((input.length + 2) ^ κ : ℕ)) + {s : State 24} {t : ℕ} + (hr : Runs (width κ input.length) ((program κ).program packedLen) + (State.init (input.map Signed.encode)) s t) : + (∀ i, s.data.regs i < (input.length + 2) ^ (40 * κ + 242)) ∧ + ∀ a, s.data.mem a < (input.length + 2) ^ (40 * κ + 242) := by + let n := input.length + let L := Nat.clog 2 (n + 2) + have hL : 0 < L := Nat.clog_pos (by decide) (by omega) + have hpred : 2 ^ (L - 1) < n + 2 := + Nat.pow_pred_clog_lt_self (b := 2) (x := n + 2) (by decide) (by omega) + have he : 2 ^ L = 2 * 2 ^ (L - 1) := by + conv_lhs => rw [show L = (L - 1) + 1 by omega] + rw [pow_succ, Nat.mul_comm] + have hceil : 2 ^ L ≤ (n + 2) ^ 2 := by rw [he]; nlinarith + have hpoly : 2 ^ width κ n ≤ (n + 2) ^ (40 * κ + 242) := by + calc + _ = (2 ^ L) ^ (20 * κ + 121) := by + dsimp only [width, L] + rw [← pow_mul] + congr 1 + ring + _ ≤ ((n + 2) ^ 2) ^ (20 * κ + 121) := Nat.pow_le_pow_left hceil _ + _ = _ := by rw [← pow_mul]; congr 1; omega + have hb := hr.bounded_init (inputFits input le_rfl hvalues) + exact ⟨fun i => (hb.1 i).trans_le hpoly, fun a => (hb.2 a).trans_le hpoly⟩ + +end Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Parameters diff --git a/ComplexityTests/WordRAMChanHeParameters.lean b/ComplexityTests/WordRAMChanHeParameters.lean new file mode 100644 index 000000000..f76dec9c1 --- /dev/null +++ b/ComplexityTests/WordRAMChanHeParameters.lean @@ -0,0 +1,64 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.Parameters + +/-! +# Executed boundary cases for native Chan–He parameters + +The observations include all nine outputs, the instruction count, and input memory cells. +The generic run clients below discharge the actual input magnitude and width hypotheses. +-/ + +-- This module contains only private execution checks. +set_option linter.privateModule false + +open Cslib.WordRAM +open Cslib.Algorithms.WordRAM.ThreeSum.ChanHe.Parameters + +private def observe (κ : ℕ) (input : List ℤ) : Option (List ℕ × ℕ × List ℕ) := + ((program κ).eval 64 32 (Data.init (input.map Signed.encode))).map fun (x, t) => + ([x.regs len, x.regs magnitude, x.regs digits, x.regs height, x.regs fuel, + x.regs want, x.regs primeBound, x.regs packedLen, x.regs padding], t, + [x.mem 0, x.mem 1, x.mem 2]) + +-- Empty input, including the fixed exponent zero. +example : observe 0 [] = some ([0, 1, 3, 1, 1, 15, 192, 294912, 5], 83, [0, 0, 0]) := by + decide + +-- One position; the signed encoding of -1 remains in memory. +example : observe 0 [-1] = some ([1, 1, 3, 1, 1, 30, 372, 1107072, 5], 88, [1, 3, 0]) := by + decide + +-- Two positions at exponent zero: the ceiling logarithm and fuel increase. +example : observe 0 [-1, 1] = + some ([2, 1, 3, 2, 4, 30, 372, 1107072, 5], 97, [2, 3, 2]) := by + decide + +-- Nonconstant polynomial magnitude, with both signed boundary values. +example : observe 1 [-4, 4] = + some ([2, 4, 5, 2, 4, 50, 714, 4078368, 17], 110, [2, 9, 8]) := by + decide + +-- A native contract client, including all width/magnitude hypotheses and all memory framing. +example : ∃ y t, t ≤ 5 * Nat.sqrt 2 + 3400 * Nat.clog 2 4 ∧ + Runs 512 ((program 1).program packedLen) (State.init ([(-4 : ℤ), 4].map Signed.encode)) + ⟨(program 1).size, y⟩ t ∧ Output 2 4 y ∧ + y.mem = (Data.init ([(-4 : ℤ), 4].map Signed.encode) : Data 24).mem ∧ y.Bounded 512 := by + apply runs_input (κ := 1) [(-4 : ℤ), 4] (by decide) + intro z hz + simp only [List.mem_cons, List.not_mem_nil, or_false] at hz + rcases hz with rfl | rfl <;> decide + +-- The empty-input width remains positive and the final input-init theorem is satisfiable. +example : 0 < width 0 0 ∧ InputFits 512 (([] : List ℤ).map Signed.encode) := by + constructor + · decide + · apply inputFits (κ := 0) [] (by decide) + simp From b4936ce28d8b498b364a7947d34f728c55f8b226 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 10:47:14 -0400 Subject: [PATCH 16/73] doc(matrix): explain finite thin-product guarantees in the Lean API Restack #1463 above repaired #1464 without changing the reviewed mathematics. Rewrite 149 documentation blocks in 14 modules as standalone descriptions; replace operation-cost and word-size narratives with the exact entry, leaf, passed-set and specified-value quantities proved. Preserve pinned Anthropic credit, source locators, statements, definitions, proof bodies and imports. An independent comparison with original ee288fb16 confirms that all code bytes outside comments are identical, including code whitespace. Validation of this final tree: all six gates pass together (strict build, aggregate check, strict tests, environment lint, style lint, init imports). Citation-aware full hygiene --check-simp, changed-file lints (14 library modules, one test) and git diff --check pass. Lean MCP FiniteMagnitude diagnostics finish without errors or warnings. Serial Smoke/Axioms warmups avoid the shared-host memory failure previously seen when these import-heavy clients run together. Tests and substantive math/API review evidence are retained from the original reviewed tree b021e2d9340b433a9fdb129882351e158e8c289f in ee288fb16. This change is documentation-only; /root/draft_thin_docs authored the scratch map and the continuation worker inspected/applied it. That drafting is not independent review. Earlier math reviews are inside-fleet agent reviews, not outside or human approval. Outside coordinator review will be requested on the final PR. No selected abstract, catalogue or rendering is affected. PR publication remains held while native dependent restacks are repaired, per root sequencing. #1394 remains open: native shared encodings/pruning/Exact Triangle, Chan-He #1446, complete Convolution/ordinary input/time/width composition and the improved Section4 route to exponent1.9992 are not supplied by these finite counts. AI assistance: OpenAI Codex workers codex-thin-product-math-20261028, codex-three-sum-continuation-t3-3dda8289 and /root/draft_thin_docs. --- .../MatrixMultiplication/Schonhage/Batch.lean | 29 ++--- .../Schonhage/Layout.lean | 67 +++++------ .../Schonhage/Levels.lean | 106 ++++++++---------- .../Schonhage/Magnitude.lean | 65 +++++------ .../Schonhage/Orders.lean | 69 +++++------- .../Schonhage/Parameters.lean | 29 +++-- .../Schonhage/Sparse.lean | 50 ++++----- .../Schonhage/ThinProduct.lean | 65 ++++++----- .../Schonhage/Tiling.lean | 69 +++++------- .../Foundations/Algebra/FiniteMagnitude.lean | 1 - .../Foundations/Algebra/GeometricTail.lean | 3 +- .../Combinatorics/BinomialUpperBound.lean | 1 - .../Combinatorics/FunctionPlaces.lean | 10 +- .../Foundations/Data/Finset/OrderGlue.lean | 8 +- 14 files changed, 270 insertions(+), 302 deletions(-) diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Batch.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Batch.lean index 9421e25bb..262f3f864 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Batch.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Batch.lean @@ -17,8 +17,9 @@ public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Levels * `bilinearProduct_arrays_eq_mul`: exact integer matrix multiplication at selected strings. * `batch_product_spec`: arbitrary output strings with `m` inner levels. -The tensor error has no effect: a nonzero summand must use the same inner levels on -both inputs as the output, and the remaining outer coefficients are Kronecker deltas. +The batch arrays vanish on strings with the wrong number of inner positions. +The coefficient rules select equal inner indices and the specified outer row and column, +yielding the usual product of integer matrices. ## References @@ -26,7 +27,6 @@ Adapted selectively from [AlmanWilliams2026, Section 2] and [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical counts and identities; they give no native program or instruction-cost certificate. -Source-derived descriptions below are paraphrases. -/ @[expose] public section @@ -35,17 +35,17 @@ open Finset namespace Cslib.Algorithms.MatrixMultiplication.Schonhage -/-! ### The sizes of the matrices, and Figure 4 -/ +/-! ### Cardinalities of index types -/ -/-- Section 2.3.3: there are `N₀` strings of `L - m` outer left (or right) variables. -/ +/-- The outer-index type has `N0 L m` elements. -/ theorem card_outerStr (L m : ℕ) : Fintype.card (OuterStr L m) = N0 L m := by simp [N0] -/-- Section 2.3.3: there are `D` strings of `m` inner left (or right) variables. -/ +/-- The inner-index type has `D m` elements. -/ theorem card_innerStr (m : ℕ) : Fintype.card (InnerStr m) = D m := by simp [D] -/-- Section 2.3.3: there are `K = binom(L, m)` subsets `Q` of `{1, …, L}` of size `m`. -/ +/-- There are `K L m` subsets of `Fin L` with cardinality `m`. -/ theorem card_subsets_eq_K (L m : ℕ) : (univ.filter fun Q : Finset (Fin L) => Q.card = m).card = K L m := by rw [univ_filter_card_eq, card_powersetCard, card_univ, Fintype.card_fin, K] @@ -70,7 +70,7 @@ theorem arrayR_rightStrOf {L m : ℕ} (Y : Finset (Fin L) → RightMat L m) (Q : have h : (innerSetR (rightStrOf Q hQ π c)).card = m := by rw [innerSetR_rightStrOf, hQ] rw [arrayR, dite_eq_left h, innerPartR_rightStrOf, outerPartR_rightStrOf, innerSetR_rightStrOf] -/-! ### The proof of Lemma 9 -/ +/-! ### Coefficients and batch correctness -/ /-- The inner-output coefficient selects equal inner index pairs (Section 2.3.3). Checked directly on `coefficient`; no polynomial coefficient bridge is assumed. -/ @@ -141,8 +141,9 @@ private theorem prod_gamma_eq_one {L m : ℕ} (Q : Finset (Fin L)) (hQ : Q.card fun _ => (coefficient_x_y_z _ _ _ _).trans (ite_eq_left ⟨rfl, rfl⟩)⟩ exact prod_eq_one fun ℓ _ => hone ℓ -/-- Lemma 9 for the output string with inner set `Q`, row `r` and column `c`: by Lemma 8 the entry -is a sum over all pairs `(u, v)`, and the summands that remain are those of the matrix product. -/ +/-- At the output string for a size-`m` set `Q` and indices `r`, `c`, the bilinear product of +the batch arrays equals `(X Q * Y Q) r c`. The matrices have arbitrary integer entries +(cf. [AlmanWilliams2026, Lemma 9]). -/ theorem bilinearProduct_arrays_eq_mul {L m : ℕ} (X : Finset (Fin L) → LeftMat L m) (Y : Finset (Fin L) → RightMat L m) (Q : Finset (Fin L)) (hQ : Q.card = m) (r c : OuterStr L m) : @@ -169,8 +170,9 @@ theorem bilinearProduct_arrays_eq_mul {L m : ℕ} (X : Finset (Fin L) → LeftMa intro π rw [prod_gamma_eq_one, one_mul, arrayL_leftStrOf, arrayR_rightStrOf] -/-- **Lemma 9**. For the input arrays a, b above and every output string w whose inner -set Q has exactly m elements, bilinearProduct(a, b)[w] = (X_Q Y_Q)[w]. (Equation (4).) -/ +/-- An output string with exactly `m` inner levels selects the product of the two matrices +indexed by its inner set, at its extracted row and column. This is an equality of integer +values for the arrays `arrayL m X` and `arrayR m Y` (cf. [AlmanWilliams2026, Lemma 9]). -/ theorem batch_product_spec {L m : ℕ} (X : Finset (Fin L) → LeftMat L m) (Y : Finset (Fin L) → RightMat L m) (w : OutStr L) (h : (innerSetO w).card = m) : @@ -180,7 +182,8 @@ theorem batch_product_spec {L m : ℕ} (X : Finset (Fin L) → LeftMat L m) (Y : conv_lhs => rw [outStr_eq_outStrOf h rfl h] exact bilinearProduct_arrays_eq_mul X Y (innerSetO w) h (rowO w h) (colO w h) -/-- Section 2.3.3: one run of Full […] computes all K of the products X_Q Y_Q. -/ +/-- `Full.result` on the batch arrays agrees with the matrix product for every size-`m` set +`Q` and every row and column index. -/ theorem Full.arrays_eq_mul {L m : ℕ} (X : Finset (Fin L) → LeftMat L m) (Y : Finset (Fin L) → RightMat L m) (Q : Finset (Fin L)) (hQ : Q.card = m) (r c : OuterStr L m) : diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Layout.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Layout.lean index 354678f42..684266bc9 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Layout.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Layout.lean @@ -15,7 +15,7 @@ public import Mathlib.Data.Nat.Sqrt ## Main definitions -* `Layout`: ordinary finite indexing choices, with no solver or bound certificate. +* `Layout`: index equivalences and an injective assignment of level sets to block pairs. * `wantedStrings`, `tiles`: requested outputs grouped by tile. ## References @@ -24,7 +24,6 @@ Adapted selectively from [AlmanWilliams2026, Section 2] and [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical counts and identities; they give no native program or instruction-cost certificate. -Source-derived descriptions below are paraphrases. -/ @[expose] public section @@ -33,7 +32,7 @@ open Finset namespace Cslib.Algorithms.MatrixMultiplication.Schonhage -/-- Section 2.3.4: K₀ := ⌊√K⌋. -/ +/-- The natural square root of the number `K L m` of size-`m` level sets. -/ def K0 (L m : ℕ) : ℕ := Nat.sqrt (K L m) /-- `N₀ = 3^{L-m}` is positive. -/ @@ -42,18 +41,13 @@ theorem N0_pos (L m : ℕ) : 0 < N0 L m := Nat.pow_pos (by decide) /-- `K₀ = ⌊√binom(L, m)⌋` is positive when `m ≤ L`. -/ theorem K0_pos {L m : ℕ} (h : m ≤ L) : 0 < K0 L m := Nat.sqrt_pos.mpr (Nat.choose_pos h) -/-- The choices that the paper makes once and for all without naming them. +/-- Indexing data for the block decomposition, under `m ≤ L`. -* `hmL`: `m ≤ L`. The paper never states this; it follows from `L = 19m` (Section 2) and from - `L ≥ 10m` (Section 4). It makes `K₀ ≥ 1`. -* Section 2.3.3: we index its N₀ rows by the N₀ strings of L - m outer left variables, and its D - columns by the D strings of m inner left variables, and similarly for a `D × N₀` matrix. - `rowIdx`, `colIdx` and `innerIdx` say which string indexes which row or column, counted from 0. -* Section 2.3.4: We fix K₀² ≤ K distinct subsets of {1, …, L} of size m, one for each block product - of the grid, the same in every tile. `table (g, h)` is the subset of the block product of the - `g`-th row block of a row band and the `h`-th column block of a column band. - -Every statement about the tiling holds for every such choice. -/ +The three equivalences identify block rows, block columns and the thin dimension with +their string indices. The injective `table` assigns a size-`m` subset of `Fin L` to each +pair of block indices in a tile. All tiles use this same table. The structure contains no +matrix values or solver certificate; the correctness results hold for any such indexing +choices. -/ structure Layout (L m : ℕ) where hmL : m ≤ L /-- Index a block's rows by outer strings. -/ @@ -67,54 +61,56 @@ structure Layout (L m : ℕ) where table_card : ∀ gh, (table gh).card = m table_injective : Function.Injective table -/-- The number `K₀ N₀` of rows of a row band (Section 2.3.4: a band is `K₀` consecutive blocks of -`N₀` consecutive rows), which is also the number of columns of a column band. -/ +/-- The common band dimension `K0 L m * N0 L m`: each band contains `K0 L m` consecutive +blocks of size `N0 L m`. -/ def bandSize (L m : ℕ) : ℕ := K0 L m * N0 L m -/-- The number of row bands (and of column bands) after padding: `N / (K₀ N₀)` rounded up. -/ +/-- The quotient `(N + bandSize L m - 1) / bandSize L m`. When `m ≤ L`, the band size is +positive and this is the number of bands needed to cover `N` positions, including `N = 0`. -/ def numBands (L m N : ℕ) : ℕ := (N + bandSize L m - 1) / bandSize L m -/-- Section 2.3.4: we first pad N to a multiple of K₀ N₀. The padded size. -/ +/-- The dimension covered by `numBands L m N` complete bands. -/ def padN (L m N : ℕ) : ℕ := numBands L m N * bandSize L m -/-- Section 2.3.4: `X` padded with zero rows. Rows are numbered from 0. -/ +/-- Extend `X` to natural-number row indices by returning zero at indices `I ≥ N`. -/ def padRows {N D' : ℕ} (X : Matrix (Fin N) (Fin D') ℤ) (I : ℕ) (k : Fin D') : ℤ := if h : I < N then X ⟨I, h⟩ k else 0 -/-- Section 2.3.4: `Y` padded with zero columns. Columns are numbered from 0. -/ +/-- Extend `Y` to natural-number column indices by returning zero at indices `J ≥ N`. -/ def padCols {N D' : ℕ} (Y : Matrix (Fin D') (Fin N) ℤ) (k : Fin D') (J : ℕ) : ℤ := if h : J < N then Y k ⟨J, h⟩ else 0 -/-- Section 2.3.4: Cut X into row blocks of N₀ consecutive rows. The `B`-th row block (counted -from 0) consists of the rows `B N₀, …, B N₀ + N₀ - 1`. -/ +/-- Extract the `N0 L m` rows starting at `B * N0 L m` from `padRows X`, using `lay.rowIdx` +and `lay.innerIdx` to index the resulting integer matrix by strings. -/ def rowBlock {L m N : ℕ} (lay : Layout L m) (X : Matrix (Fin N) (Fin (D m)) ℤ) (B : ℕ) : LeftMat L m := fun r π => padRows X (B * N0 L m + lay.rowIdx.symm r) (lay.innerIdx.symm π) -/-- Section 2.3.4: and Y into column blocks of N₀ consecutive columns. -/ +/-- Extract the `N0 L m` columns starting at `B * N0 L m` from `padCols Y`, using +`lay.innerIdx` and `lay.colIdx` to index the resulting integer matrix by strings. -/ def colBlock {L m N : ℕ} (lay : Layout L m) (Y : Matrix (Fin (D m)) (Fin N) ℤ) (B : ℕ) : RightMat L m := fun π c => padCols Y (lay.innerIdx.symm π) (B * N0 L m + lay.colIdx.symm c) -/-- Section 2.3.4: In a tile, let X_Q and Y_Q be the row block and the column block of the block -product with subset Q, and let X_Q = Y_Q = 0 for the other subsets Q. The row band `β` (counted -from 0) consists of the row blocks `β K₀, …, β K₀ + K₀ - 1`. The subsets in the table are distinct, -so at most one summand is nonzero. -/ +/-- For a table entry `Q = lay.table (g, h)`, select row block `β * K0 L m + g` of `X`; return +the zero matrix for sets outside the table's image. Injectivity of the table ensures that +the defining sum selects at most one matrix. -/ def bandFamilyL {L m N : ℕ} (lay : Layout L m) (X : Matrix (Fin N) (Fin (D m)) ℤ) (β : ℕ) : Finset (Fin L) → LeftMat L m := fun Q => ∑ gh, if lay.table gh = Q then rowBlock lay X (β * K0 L m + gh.1) else 0 -/-- The matrices `Y_Q` of a tile; they depend only on its column band `β`. -/ +/-- For a table entry `Q = lay.table (g, h)`, select column block `β * K0 L m + h` of `Y`; +return the zero matrix for sets outside the table's image. -/ def bandFamilyR {L m N : ℕ} (lay : Layout L m) (Y : Matrix (Fin (D m)) (Fin N) ℤ) (β : ℕ) : Finset (Fin L) → RightMat L m := fun Q => ∑ gh, if lay.table gh = Q then colBlock lay Y (β * K0 L m + gh.2) else 0 -/-- Section 2.3.4: the left input array of a tile depends only on its row band. -/ +/-- The left batch array formed from the block family for row band `β`. -/ def bandArrayL {L m N : ℕ} (lay : Layout L m) (X : Matrix (Fin N) (Fin (D m)) ℤ) (β : ℕ) : LeftStr L → ℤ := arrayL m (bandFamilyL lay X β) -/-- Section 2.3.4: and the right one only on its column band. -/ +/-- The right batch array formed from the block family for column band `β`. -/ def bandArrayR {L m N : ℕ} (lay : Layout L m) (Y : Matrix (Fin (D m)) (Fin N) ℤ) (β : ℕ) : RightStr L → ℤ := arrayR m (bandFamilyR lay Y β) @@ -129,16 +125,15 @@ def blockOf {L m : ℕ} (lay : Layout L m) (I : ℕ) : Fin (K0 L m) := /-- The position of row (or column) `I` within its block. -/ def offsetOf (L m I : ℕ) : Fin (N0 L m) := ⟨I % N0 L m, Nat.mod_lt _ (N0_pos L m)⟩ -/-- Section 2.4.4: Each wanted position (I, J) ∈ W lies in one tile T and is indexed by one output -string of T, the string whose inner set is the subset Q of the block product containing (I, J) and -whose row and column are those of (I, J) within that block product. The tile is the pair -`(bandOf L m I, bandOf L m J)`, and this is the output string. -/ +/-- Encode the within-tile position `(I, J)` as an output string. Its inner set comes from the +table at the two block indices, and its outer indices come from the two offsets within +those blocks. The tile itself is identified separately by `(bandOf L m I, bandOf L m J)`. -/ def outStrOfPos {L m : ℕ} (lay : Layout L m) (I J : ℕ) : OutStr L := outStrOf (lay.table (blockOf lay I, blockOf lay J)) (lay.table_card _) (lay.rowIdx (offsetOf L m I)) (lay.colIdx (offsetOf L m J)) -/-- Section 2.4.4: For each tile T, let W_T be the set of the output strings of the wanted -positions in T. The tile `T` is given by its row band `β` and its column band `β'`. -/ +/-- Output strings for the positions of `W` whose row band is `β` and column band is `β'`. +This is the image of the filtered position set under `outStrOfPos lay`. -/ def wantedStrings {L m N : ℕ} (lay : Layout L m) (W : Finset (Fin N × Fin N)) (β β' : ℕ) : Finset (OutStr L) := (W.filter fun IJ => bandOf L m IJ.1 = β ∧ bandOf L m IJ.2 = β').image diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Levels.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Levels.lean index 38d7001a9..da18d521e 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Levels.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Levels.lean @@ -20,7 +20,7 @@ public import Complexity.Foundations.Data.Finset.OrderGlue ## Main results -* `existsUnique_leftStr`, `existsUnique_rightStr`, `existsUnique_outStr`: nonvacuous entry layouts. +* `existsUnique_leftStr`, `existsUnique_rightStr`, `existsUnique_outStr`: unique entry strings. ## References @@ -28,7 +28,6 @@ Adapted selectively from [AlmanWilliams2026, Section 2] and [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical counts and identities; they give no native program or instruction-cost certificate. -Source-derived descriptions below are paraphrases. -/ @[expose] public section @@ -37,11 +36,11 @@ open Finset namespace Cslib.Algorithms.MatrixMultiplication.Schonhage -/-- Section 2.2: We call the variables of the inner product, p, q, and z₀, inner. -/ +/-- Recognize the constructors `.p i j` as inner left variables. -/ def LeftVar.IsInner : LeftVar → Prop | .x _ => False | .p _ _ => True -/-- Section 2.2: the right variables `q` are inner, the `y` are outer. -/ +/-- Recognize the constructors `.q i j` as inner right variables. -/ def RightVar.IsInner : RightVar → Prop | .y _ => False | .q _ _ => True @@ -51,69 +50,61 @@ instance LeftVar.instDecidablePredIsInner : DecidablePred LeftVar.IsInner instance RightVar.instDecidablePredIsInner : DecidablePred RightVar.IsInner | .y _ => isFalse fun h => h | .q _ _ => isTrue trivial -/-- Section 2.3.3: The inner set of a left string is the set of levels at which it has a p (as -opposed to an x). -/ +/-- Positions of the inner left variables in `u`. -/ def innerSetL {L : ℕ} (u : LeftStr L) : Finset (Fin L) := univ.filter fun ℓ => (u ℓ).IsInner -/-- Section 2.3.3: the inner set of a right string is the set of levels at which it has a q (as -opposed to a y). -/ +/-- Positions of the inner right variables in `v`. -/ def innerSetR {L : ℕ} (v : RightStr L) : Finset (Fin L) := univ.filter fun ℓ => (v ℓ).IsInner -/-- Section 2.3.3: the inner set of an output string is the set of levels at which it has z₀ (as -opposed to a z_ij). -/ +/-- Positions at which the output string is `.z0`. -/ def innerSetO {L : ℕ} (w : OutStr L) : Finset (Fin L) := univ.filter fun ℓ => (w ℓ).IsInner -/-- Section 2.3.3: N₀ := 3^{L-m}. Meant for `m ≤ L`; for `m > L` the subtraction of natural -numbers is cut off at 0 and the value is 1. -/ +/-- The outer dimension `3 ^ (L - m)`, with natural-number subtraction. In particular, this +definition gives `1` if `m > L`. -/ def N0 (L m : ℕ) : ℕ := 3 ^ (L - m) -/-- Section 2.3.3: D := 4^m. -/ +/-- The thin dimension `4 ^ m`. -/ def D (m : ℕ) : ℕ := 4 ^ m -/-- Section 2.3.3: K := binom(L, m), the number of subsets of `{1, …, L}` of size `m`. -/ +/-- The binomial coefficient `L.choose m`, counting size-`m` subsets of `Fin L`. -/ def K (L m : ℕ) : ℕ := L.choose m -/-- The index `i` of an outer left variable `x_i` (the value at a `p` is never used). -/ +/-- Extract `i` from `.x i`; return `0` on inner left variables. -/ def LeftVar.outerIndex : LeftVar → Fin 3 | .x i => i | .p _ _ => 0 -/-- The index pair `(i, j)` of an inner left variable `p_ij` (the value at an `x` is never used). -/ +/-- Extract `(i, j)` from `.p i j`; return `(0, 0)` on outer left variables. -/ def LeftVar.innerIndex : LeftVar → Fin 2 × Fin 2 | .x _ => (0, 0) | .p i j => (i, j) -/-- The index `j` of an outer right variable `y_j` (the value at a `q` is never used). -/ +/-- Extract `j` from `.y j`; return `0` on inner right variables. -/ def RightVar.outerIndex : RightVar → Fin 3 | .y j => j | .q _ _ => 0 -/-- The index pair `(i, j)` of an inner right variable `q_ij` (the value at a `y` is never used). -/ +/-- Extract `(i, j)` from `.q i j`; return `(0, 0)` on outer right variables. -/ def RightVar.innerIndex : RightVar → Fin 2 × Fin 2 | .y _ => (0, 0) | .q i j => (i, j) -/-- The first index `i` of an outer output variable `z_ij` (the value at `z₀` is never used). -/ +/-- Extract the first index from `.z i j`; return `0` on `.z0`. -/ def OutVar.rowIndex : OutVar → Fin 3 | .z i _ => i | .z0 => 0 -/-- The second index `j` of an outer output variable `z_ij` (the value at `z₀` is never used). -/ +/-- Extract the second index from `.z i j`; return `0` on `.z0`. -/ def OutVar.colIndex : OutVar → Fin 3 | .z _ j => j | .z0 => 0 -/-- A string of `L - m` outer left variables `x_{i₁} ⋯ x_{i_{L-m}}`, written as the string of its -indices `i₁ ⋯ i_{L-m}`. The same type is used for strings `y_{j₁} ⋯ y_{j_{L-m}}` of outer right -variables. Section 2.3.3: these strings index the `N₀` rows of an `N₀ × D` matrix and the `N₀` -columns of a `D × N₀` matrix. -/ +/-- Functions from `Fin (L - m)` to `Fin 3`, used as outer row and column indices. Each value +specifies the index of an outer left or right variable. -/ abbrev OuterStr (L m : ℕ) : Type := Fin (L - m) → Fin 3 -/-- A string of `m` inner left variables `π = p_{i₁j₁} ⋯ p_{i_m j_m}`, written as the string of its -index pairs. The same type is used for strings `π' = q_{i₁j₁} ⋯ q_{i_m j_m}` of inner right -variables, so that the paper's `π'`, with the same indices as `π`, is the same element of this -type as `π`. Section 2.3.3: these strings index the `D` columns of an `N₀ × D` matrix and the `D` -rows of a `D × N₀` matrix. With this convention the paper's (AB)[r, c] := ∑_π A[r, π] B[π', c] is -the usual matrix product. -/ +/-- Functions from `Fin m` to pairs of `Fin 2` indices, used for the common inner dimension of +the two matrices. Left and right inner variables with the same pair of indices share one +element of this type, so contraction over `InnerStr m` is ordinary matrix multiplication. -/ abbrev InnerStr (m : ℕ) : Type := Fin m → Fin 2 × Fin 2 -/-- Section 2.3.3: its outer part is the string of its variables at the other levels, for a left -string whose inner set has `m` elements. -/ +/-- Extract the outer left indices in increasing order of their positions, assuming `u` has +exactly `m` inner positions. -/ def outerPartL {L m : ℕ} (u : LeftStr L) (h : (innerSetL u).card = m) : OuterStr L m := fun k => (u (((innerSetL u)ᶜ.orderEmbOfFin (card_compl_of_card_eq (innerSetL u) h)) k)).outerIndex -/-- Section 2.3.3: The inner part of a string is the string of its variables at the levels of its -inner set, in the order of the levels, for a left string whose inner set has `m` elements. -/ +/-- Extract the inner left index pairs in increasing order of their positions, assuming `u` +has exactly `m` inner positions. -/ def innerPartL {L m : ℕ} (u : LeftStr L) (h : (innerSetL u).card = m) : InnerStr m := fun k => (u (Finset.orderEmbOfFin (innerSetL u) h k)).innerIndex /-- The outer part of a right string whose inner set has `m` elements. -/ @@ -123,37 +114,37 @@ def outerPartR {L m : ℕ} (v : RightStr L) (h : (innerSetR v).card = m) : Outer def innerPartR {L m : ℕ} (v : RightStr L) (h : (innerSetR v).card = m) : InnerStr m := fun k => (v (Finset.orderEmbOfFin (innerSetR v) h k)).innerIndex -/-- Section 2.3.3: if the variables of w at the levels outside Q are z_{i₁j₁}, …, z_{i_{L-m} -j_{L-m}}, in the order of the levels, then the row of w is the string x_{i₁} ⋯ x_{i_{L-m}}. -/ +/-- Read the first indices of the outer output variables in increasing position order, +assuming `w` has exactly `m` inner positions. -/ def rowO {L m : ℕ} (w : OutStr L) (h : (innerSetO w).card = m) : OuterStr L m := fun k => (w (((innerSetO w)ᶜ.orderEmbOfFin (card_compl_of_card_eq (innerSetO w) h)) k)).rowIndex -/-- Section 2.3.3: and its column is the string y_{j₁} ⋯ y_{j_{L-m}}. -/ +/-- Read the second indices of the outer output variables in increasing position order, +assuming `w` has exactly `m` inner positions. -/ def colO {L m : ℕ} (w : OutStr L) (h : (innerSetO w).card = m) : OuterStr L m := fun k => (w (((innerSetO w)ᶜ.orderEmbOfFin (card_compl_of_card_eq (innerSetO w) h)) k)).colIndex -/-- An `N₀ × D` matrix, its rows and columns indexed by strings as in Section 2.3.3. -/ +/-- Integer matrices with outer row indices and inner column indices. -/ abbrev LeftMat (L m : ℕ) : Type := Matrix (OuterStr L m) (InnerStr m) ℤ -/-- A `D × N₀` matrix, its rows and columns indexed by strings as in Section 2.3.3. -/ +/-- Integer matrices with inner row indices and outer column indices. -/ abbrev RightMat (L m : ℕ) : Type := Matrix (InnerStr m) (OuterStr L m) ℤ -/-- Section 2.3.3: a[u] := X_Q[u] […] for all the left strings u […] whose inner set Q has exactly -m elements, and a[u] := 0 […] at every other string, where X_Q[u] is the entry of `X_Q` at the -row and the column of u: the row is the outer part of `u` and the column is its inner part. The -family `X` gives a matrix `X Q` for every set `Q` of levels; only those with `|Q| = m` are used. -/ +/-- Encode a family of integer matrices as a left array. A string with exactly `m` inner +positions selects `X` at that set of positions, with row `outerPartL` and column +`innerPartL`; every other string has value zero. -/ def arrayL {L : ℕ} (m : ℕ) (X : Finset (Fin L) → LeftMat L m) : LeftStr L → ℤ := fun u => if h : (innerSetL u).card = m then X (innerSetL u) (outerPartL u h) (innerPartL u h) else 0 -/-- Section 2.3.3: b[v] := Y_Q[v] for the right strings `v` whose inner set `Q` has exactly `m` -elements, the row given by the inner part of v and the column by the outer part, and `b[v] := 0` -at every other string. -/ +/-- Encode a family of integer matrices as a right array. A string with exactly `m` inner +positions selects `Y` at that set of positions, with row `innerPartR` and column +`outerPartR`; every other string has value zero. -/ def arrayR {L : ℕ} (m : ℕ) (Y : Finset (Fin L) → RightMat L m) : RightStr L → ℤ := fun v => if h : (innerSetR v).card = m then Y (innerSetR v) (innerPartR v h) (outerPartR v h) else 0 -/-! ### 2.3.4 Tiling the N × D × N product by products of shape N₀ × D × N₀: the number `M` -/ +/-! ### The block-count parameter `M` -/ -/-- Section 2.3.4: M := K N₀². -/ +/-- The quantity `K L m * N0 L m ^ 2` used in the leaf and tile bounds. -/ def M (L m : ℕ) : ℕ := K L m * N0 L m ^ 2 @@ -169,9 +160,8 @@ def leftStrOf (r : OuterStr L m) (π : InnerStr m) : LeftStr L := def rightStrOf (π : InnerStr m) (c : OuterStr L m) : RightStr L := glue Q hQ (fun k => .q (π k).1 (π k).2) (fun k => .y (c k)) -/-- The output string with inner set `Q`, row `r` and column `c` (Section 2.3.3: the output strings -with inner set Q index the entries of the product X_Q Y_Q): it has `z₀` at the levels of `Q`, and -at the `k`-th lowest level outside `Q` it has `z_{r_k c_k}`. -/ +/-- Construct an output string with `.z0` at `Q` and `.z (r k) (c k)` at the `k`-th position +of its complement. Both sets of positions use increasing order. -/ def outStrOf (r c : OuterStr L m) : OutStr L := glue Q hQ (fun _ => .z0) (fun k => .z (r k) (c k)) @@ -296,10 +286,8 @@ theorem outStrOf_inj {Q' : Finset (Fin L)} {hQ' : Q'.card = m} {r c r' c' : Oute variable (Q) (hQ) include hQ -/-- Section 2.3.3: A string is determined by its inner set, its outer part, and its inner part, -and the left strings with inner set Q index the entries of such a matrix: for each set `Q` of `m` -levels, each row `r` and each column `π` there is exactly one left string with inner set `Q`, outer -part `r` and inner part `π`. -/ +/-- For a set `Q` of cardinality `m`, an outer index `r` and an inner index `π` determine a +unique left string with those extracted parts and inner set `Q`. -/ theorem existsUnique_leftStr (r : OuterStr L m) (π : InnerStr m) : ∃! u : LeftStr L, innerSetL u = Q ∧ ∃ h : (innerSetL u).card = m, outerPartL u h = r ∧ innerPartL u h = π := by @@ -309,8 +297,8 @@ theorem existsUnique_leftStr (r : OuterStr L m) (π : InnerStr m) : rintro u ⟨hu, h, rfl, rfl⟩ exact leftStr_eq_leftStrOf hQ hu h -/-- Section 2.3.3: Similarly, the right strings v with inner set Q index the entries of a D × N₀ -matrix. -/ +/-- For a set `Q` of cardinality `m`, an inner index `π` and an outer index `c` determine a +unique right string with those extracted parts and inner set `Q`. -/ theorem existsUnique_rightStr (π : InnerStr m) (c : OuterStr L m) : ∃! v : RightStr L, innerSetR v = Q ∧ ∃ h : (innerSetR v).card = m, innerPartR v h = π ∧ outerPartR v h = c := by @@ -320,8 +308,8 @@ theorem existsUnique_rightStr (π : InnerStr m) (c : OuterStr L m) : rintro v ⟨hv, h, rfl, rfl⟩ exact rightStr_eq_rightStrOf hQ hv h -/-- Section 2.3.3: Finally, the output strings with inner set Q index the entries of the product -X_Q Y_Q. -/ +/-- For a set `Q` of cardinality `m`, a row `r` and a column `c` determine a unique output +string with those extracted indices and inner set `Q`. -/ theorem existsUnique_outStr (r c : OuterStr L m) : ∃! w : OutStr L, innerSetO w = Q ∧ ∃ h : (innerSetO w).card = m, rowO w h = r ∧ colO w h = c := by diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Magnitude.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Magnitude.lean index 71e660aa6..7a932b680 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Magnitude.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Magnitude.lean @@ -11,7 +11,7 @@ public import Complexity.Algorithms.MatrixMultiplication.Schonhage.ThinProduct public import Complexity.Foundations.Algebra.FiniteMagnitude /-! -# Finite intermediate magnitudes and auxiliary counts +# Magnitudes of specified integer values and auxiliary counts ## Main results @@ -19,7 +19,9 @@ public import Complexity.Foundations.Algebra.FiniteMagnitude * `abs_encodingL_le`, `abs_encodingR_le`, `Pruned.abs_result_le`: explicit integer magnitude bounds. * `minutiae_total_le`: bound on layout subsets, tiles and wanted positions. -These bounds do not certify a machine word width or native execution. +The magnitude bounds concern band-array values, encoded values and returned pruned +values. They do not bound every intermediate state of a native implementation or certify +a machine word width. ## References @@ -27,7 +29,6 @@ Adapted selectively from [AlmanWilliams2026, Section 2] and [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical counts and identities; they give no native program or instruction-cost certificate. -Source-derived descriptions below are paraphrases. -/ @[expose] public section @@ -36,7 +37,7 @@ open Finset namespace Cslib.Algorithms.MatrixMultiplication.Schonhage -/-! ### The remaining minutiae -/ +/-! ### Auxiliary finite counts -/ /-- `2^{m/9} ≤ 2^m`. -/ private theorem two_rpow_ninth_le (m : ℕ) : (2 : ℝ) ^ ((m : ℝ) / 9) ≤ (2 : ℝ) ^ m := by @@ -50,12 +51,12 @@ private theorem one_le_saving_mul {m : ℕ} {x : ℝ} (hx : (2 : ℝ) ^ ((m : rw [← saving_mul_two_rpow m] exact mul_le_mul_of_nonneg_left hx (saving_pos m).le -/-- Section 2.4.4, in the remaining minutiae: K ≤ N. -/ +/-- When `D m ^ 18 ≤ N`, the number `K (19 * m) m` of size-`m` level sets is at most `N`. -/ theorem K_le_of_D_pow_le (m N : ℕ) (hN : D m ^ 18 ≤ N) : K (19 * m) m ≤ N := (Nat.le_mul_of_pos_right _ (N0_pos _ _)).trans (K_mul_N0_le m N hN) -/-- Section 2.4.4, in the remaining minutiae: there are at most 4N²/N₀ tiles, where `N` is the -padded size. -/ +/-- For `m ≤ L`, the tile count multiplied by `N0 L m` is at most four times the square of the +padded dimension `padN L m N`. -/ theorem card_tiles_mul_N0_le {L m : ℕ} (hmL : m ≤ L) (N : ℕ) : (tiles L m N).card * N0 L m ≤ 4 * padN L m N ^ 2 := by -- `N₀ ≤ K N₀² = M`, and there are at most `4N²/M` tiles. @@ -64,7 +65,7 @@ theorem card_tiles_mul_N0_le {L m : ℕ} (hmL : m ≤ L) (N : ℕ) : _ ≤ K L m * N0 L m ^ 2 := Nat.mul_le_mul (Nat.choose_pos hmL) (Nat.pow_le_pow_right (N0_pos L m) (by norm_num)) -/-- Section 2.4.4, in the remaining minutiae: `N ≥ 2^{m/9}`. -/ +/-- The size condition `D m ^ 18 ≤ N` implies the real bound `2 ^ (m / 9) ≤ N`. -/ theorem two_rpow_le_of_D_pow_le (m N : ℕ) (hN : D m ^ 18 ≤ N) : (2 : ℝ) ^ ((m : ℝ) / 9) ≤ (N : ℝ) := by -- `2^{m/9} ≤ 2^m ≤ 4^{18m} = D^18 ≤ N`. @@ -74,7 +75,7 @@ theorem two_rpow_le_of_D_pow_le (m N : ℕ) (hN : D m ^ 18 ≤ N) : _ ≤ N := hN exact (two_rpow_ninth_le m).trans (by exact_mod_cast hpow) -/-- Section 2.4.4, in the remaining minutiae: `N₀ ≥ 2^{m/9}`. -/ +/-- At `L = 19 * m`, the outer dimension is at least the real power `2 ^ (m / 9)`. -/ theorem two_rpow_le_N0 (m : ℕ) : (2 : ℝ) ^ ((m : ℝ) / 9) ≤ (N0 (19 * m) m : ℝ) := by -- `2^{m/9} ≤ 2^m ≤ 3^{18m} = N₀`. have hpow : 2 ^ m ≤ N0 (19 * m) m := @@ -113,10 +114,10 @@ private theorem minutiae_parts (m N : ℕ) (W : Finset (Fin N × Fin N)) (hN : D _ ≤ saving m * (W.card * (2 : ℝ) ^ m) := by gcongr; exact two_rpow_ninth_le m _ ≤ saving m * (N : ℝ) ^ 2 := by gcongr -/-- Section 2.4.4: O(L) operations per subset, per tile, and per position of W, which is -O(L 2^{-m/9} N²) in all: the number of these objects, with the `K₀²` subsets counted as at most -`K`, as the paper does (as K ≤ N). Here `N` is the size of the input. The constant is -`18 = 1 + 16 + 1`, because the padding at most doubles `N`. -/ +/-- At `L = 19 * m`, the sum of `K L m`, the tile count and `W.card` is at most `18 * +D^(-1/18) * N²`, where `D = D m` and `N` is the original matrix dimension. The hypotheses +are `D m ^ 18 ≤ N` and `W.card * 2 ^ m ≤ N ^ 2`. This counts finite objects; it does not +count operations on them. -/ theorem minutiae_total_le (m N : ℕ) (W : Finset (Fin N × Fin N)) (hN : D m ^ 18 ≤ N) (hW : W.card * 2 ^ m ≤ N ^ 2) : ((K (19 * m) m + (tiles (19 * m) m N).card + W.card : ℕ) : ℝ) @@ -131,7 +132,7 @@ theorem minutiae_total_le (m N : ℕ) (W : Finset (Fin N × Fin N)) (hN : D m ^ -- `K + |tiles| + |W| ≤ c N² + 4 c (2N)² + c N² = 18 c N²` with `c = 2^{-m/9}`. linarith [hK, hT, hW', hsq] -/-! ### The word size -/ +/-! ### Bounds on specified integer values -/ /-- The subsets in the table are distinct (Section 2.3.4), so a sum over the table that selects the subset `Q` is one of its terms or 0. -/ @@ -144,10 +145,9 @@ private theorem sum_table_eq_zero_or {L m : ℕ} {A : Type*} [AddCommMonoid A] ( exact .inr ⟨gh, by simp [lay.table_injective.eq_iff]⟩ · exact .inl (sum_eq_zero fun gh _ => ite_eq_right (not_exists.mp hQ gh)) -/-- Section 2.4.4, word size: The input entries are of this size, by assumption. Every entry of -the input array of a row band is an entry of `X` or 0. - -NOTE. `0 ≤ A` is needed only when `N = 0`. -/ +/-- A nonnegative bound `A` on the absolute values of all entries of `X` also bounds every +value of its left band arrays, for every band index. Zero padding and unused strings are +included; `0 ≤ A` remains required when `N = 0`. -/ theorem abs_bandArrayL_le {L m N : ℕ} (lay : Layout L m) {X : Matrix (Fin N) (Fin (D m)) ℤ} {A : ℤ} (hA : 0 ≤ A) (hX : ∀ I k, |X I k| ≤ A) (β : ℕ) (u : LeftStr L) : |bandArrayL lay X β u| ≤ A := by @@ -164,10 +164,9 @@ theorem abs_bandArrayL_le {L m N : ℕ} (lay : Layout L m) {X : Matrix (Fin N) ( · simpa using hA · simpa using hA -/-- Section 2.4.4, word size: The input entries are of this size, by assumption. Every entry of -the input array of a column band is an entry of `Y` or 0. - -NOTE. `0 ≤ B` is needed only when `N = 0`. -/ +/-- A nonnegative bound `B` on the absolute values of all entries of `Y` also bounds every +value of its right band arrays, for every band index. Zero padding and unused strings are +included; `0 ≤ B` remains required when `N = 0`. -/ theorem abs_bandArrayR_le {L m N : ℕ} (lay : Layout L m) {Y : Matrix (Fin (D m)) (Fin N) ℤ} {B : ℤ} (hB : 0 ≤ B) (hY : ∀ k J, |Y k J| ≤ B) (β : ℕ) (v : RightStr L) : |bandArrayR lay Y β v| ≤ B := by @@ -184,8 +183,9 @@ theorem abs_bandArrayR_le {L m N : ℕ} (lay : Layout L m) {Y : Matrix (Fin (D m · simpa using hB · simpa using hB -/-- A combination of the entries of an array, with a product of coefficients 0, 1 or -1 for each -entry, is at most the number of entries times the largest absolute value of an entry. -/ +/-- If every coefficient has absolute value at most one and every array entry has absolute +value at most `A`, then an encoded value has absolute value at most the array index type's +cardinality times `A`. This bounds the value of the finite sum defining `encodeWith`. -/ theorem abs_encodeWith_le {α : Type} [Fintype α] {c : Term → α → ℤ} (hc : ∀ lam s, |c lam s| ≤ 1) {L : ℕ} {a : (Fin L → α) → ℤ} {A : ℤ} (ha : ∀ u, |a u| ≤ A) (τ : Leaf L) : |encodeWith c τ a| ≤ Fintype.card (Fin L → α) * A := by @@ -193,25 +193,26 @@ theorem abs_encodeWith_le {α : Type} [Fintype α] {c : Term → α → ℤ} (hc (fun u _ => abs_prod_le_one _ fun ℓ _ => hc _ _) fun u _ => ha u simpa only [encodeWith, mul_comm, card_univ] using h -/-- Section 2.4.4, word size: Every number in an encoding […] is a ±1 combination of at most 7^L -input entries, so its absolute value is at most `7^L` times the largest absolute value of an input -entry. For the encoding of `a`. -/ +/-- An absolute bound `A` on the left array entries gives the bound `7 ^ L * A` on each value +of `encodingL`. -/ theorem abs_encodingL_le {L : ℕ} {a : LeftStr L → ℤ} {A : ℤ} (ha : ∀ u, |a u| ≤ A) (τ : Leaf L) : |encodingL a τ| ≤ 7 ^ L * A := by have h := abs_encodeWith_le abs_phi_le_one ha τ rw [card_leftStr] at h exact_mod_cast h -/-- Section 2.4.4, word size: Every number in an encoding […] is a ±1 combination of at most 7^L -input entries. For the encoding of `b`. -/ +/-- An absolute bound `B` on the right array entries gives the bound `7 ^ L * B` on each value +of `encodingR`. -/ theorem abs_encodingR_le {L : ℕ} {b : RightStr L → ℤ} {B : ℤ} (hb : ∀ v, |b v| ≤ B) (τ : Leaf L) : |encodingR b τ| ≤ 7 ^ L * B := by have h := abs_encodeWith_le abs_psi_le_one hb τ rw [card_rightStr] at h exact_mod_cast h -/-- A returned pruned entry is a sum of at most `10^n` encoded products (Section 2.4.4). -This bounds the mathematical result, not native execution or machine width. -/ +/-- If `encA` and `encB` are bounded in absolute value by `A` and `B`, every value returned by +`Pruned.result` is bounded by `10 ^ n * (A * B)`. This holds for every request set `S` and +output string `w`, including unrequested strings, whose returned value is zero. It bounds +the specified mathematical result, not native scratch states or machine word width. -/ theorem Pruned.abs_result_le {n : ℕ} (encA encB : Leaf n → ℤ) (A B : ℤ) (hA : ∀ τ, |encA τ| ≤ A) (hB : ∀ τ, |encB τ| ≤ B) (S : Finset (OutStr n)) (w : OutStr n) : |Pruned.result n encA encB S w| ≤ 10 ^ n * (A * B) := by @@ -233,7 +234,7 @@ theorem Pruned.abs_result_le {n : ℕ} (encA encB : Leaf n → ℤ) (A B : ℤ) · simp only [Pi.zero_apply, abs_zero] positivity -/-- Section 2.4.4, word size: 10^L ≤ N² by (6), for `L = 19m` and `N ≥ D^18`. -/ +/-- Under `D m ^ 18 ≤ N`, the number `10 ^ (19 * m)` of leaf indices is at most `N ^ 2`. -/ theorem ten_pow_le_sq (m N : ℕ) (hN : D m ^ 18 ≤ N) : 10 ^ (19 * m) ≤ N ^ 2 := by -- `N₀ ≤ K N₀ ≤ N` and `2^{-m/9} ≤ 1`, so equation (6) gives `10^L ≤ N N₀ ≤ N²`. have hN0 : (N0 (19 * m) m : ℝ) ≤ N := by diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Orders.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Orders.lean index a11ba4e55..facc0f50c 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Orders.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Orders.lean @@ -33,7 +33,6 @@ Adapted selectively from [AlmanWilliams2026, Section 2] and [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical counts and identities; they give no native program or instruction-cost certificate. -Source-derived descriptions below are paraphrases. -/ @[expose] public section @@ -45,16 +44,17 @@ namespace Cslib.Algorithms.MatrixMultiplication.Schonhage /-- The set of levels at which a leaf chooses `P₀`. -/ def P0Levels {L : ℕ} (τ : Leaf L) : Finset (Fin L) := univ.filter fun ℓ => τ ℓ = Term.P0 -/-- Section 2.4.3: we define the order of a leaf as m minus the number of levels at which it -chooses P₀. The order is an integer; it is negative for a leaf that chooses `P₀` at more than `m` -levels. -/ +/-- The integer difference between `m` and the number of `P0` positions of a leaf. It can be +negative for arbitrary leaves; nonnegativity is proved for leaves contributing to an +output with `m` inner positions. -/ def order {L : ℕ} (m : ℕ) (τ : Leaf L) : ℤ := (m : ℤ) - ((P0Levels τ).card : ℤ) -/-- Section 2.4.3: α_d := binom(m, d) 9^d. -/ +/-- The quantity `m.choose d * 9 ^ d`, counting order-`d` leaves contributing to one output +with `m` inner positions. -/ def alpha (m d : ℕ) : ℕ := m.choose d * 9 ^ d -/-- Section 2.4.3: β_d := binom(L, m-d) 9^{L-m+d}. Meant for `d ≤ m ≤ L`; outside this range the -subtractions of natural numbers are cut off at 0 and the value has no meaning. -/ +/-- The quantity `L.choose (m - d) * 9 ^ (L - m + d)`, using natural-number subtraction. Its +interpretation as the number of order-`d` leaves requires `d ≤ m ≤ L`. -/ def beta (L m d : ℕ) : ℕ := L.choose (m - d) * 9 ^ (L - m + d) /-! ### One level: which terms contribute to which variables -/ @@ -113,8 +113,7 @@ theorem mem_P0Levels {L : ℕ} {τ : Leaf L} {ℓ : Fin L} : ℓ ∈ P0Levels τ theorem mem_innerSetO {L : ℕ} {η : OutStr L} {ℓ : Fin L} : ℓ ∈ innerSetO η ↔ (η ℓ).IsInner := by simp [innerSetO] -/-- Section 2.4.3: A leaf contributing to an output string chooses P₀ only at levels of the output -string's inner set. -/ +/-- A contributing leaf can use `P0` only at an inner position of the output string. -/ theorem P0Levels_subset {L : ℕ} {τ : Leaf L} {w : OutStr L} (h : Leaf.Contributes τ w) : P0Levels τ ⊆ innerSetO w := by intro ℓ hℓ @@ -124,15 +123,16 @@ theorem P0Levels_subset {L : ℕ} {τ : Leaf L} {w : OutStr L} (h : Leaf.Contrib · exact hinner · exact (OutVar.privateTerm_eq_P0_iff _).mp (hprivate.symm.trans hℓ) -/-- Section 2.4.3: so its order is at least 0. -/ +/-- A leaf contributing to a string with exactly `m` inner positions has nonnegative order +relative to `m`. -/ theorem order_nonneg {L m : ℕ} {w : OutStr L} (hw : (innerSetO w).card = m) {τ : Leaf L} (h : Leaf.Contributes τ w) : 0 ≤ order m τ := by have hc := card_le_card (P0Levels_subset h) unfold order omega -/-- In the proof of Lemma 11: every leaf of leaves(U) has an order 0 ≤ d ≤ m. The upper -bound holds for every leaf. -/ +/-- The order relative to `m` of any leaf is at most `m`, regardless of which outputs it +contributes to. -/ theorem order_le {L : ℕ} (m : ℕ) (τ : Leaf L) : order m τ ≤ m := by unfold order omega @@ -145,7 +145,7 @@ theorem order_eq_iff {L : ℕ} (m d : ℕ) (τ : Leaf L) : /-! ### The counts -/ -/-- Section 2.4.3: A single output string has 10^m […] leaves contributing to it. -/ +/-- Exactly `10 ^ m` leaves contribute to an output string with `m` inner positions. -/ theorem card_filter_contributes {L m : ℕ} (w : OutStr L) (hw : (innerSetO w).card = m) : (univ.filter fun τ : Leaf L => Leaf.Contributes τ w).card = 10 ^ m := by -- Ten terms contribute to `z₀` and one to `z_ij`. @@ -154,9 +154,8 @@ theorem card_filter_contributes {L m : ℕ} (w : OutStr L) (hw : (innerSetO w).c rw [prod_ite, prod_const, prod_const_one, mul_one, ← hw] rfl -/-- Section 2.4.3: exactly α_d := binom(m, d) 9^d leaves of order d contribute to an output -string. As everywhere in Section 2.4.3, the output strings are those whose inner sets have exactly -m elements. -/ +/-- For a string with `m` inner positions, `alpha m d` leaves of order `d` contribute to it. +The natural parameter `d` is unrestricted; for `d > m` both sides are zero. -/ theorem card_contributing_of_order {L m : ℕ} (w : OutStr L) (hw : (innerSetO w).card = m) (d : ℕ) : (univ.filter fun τ : Leaf L => Leaf.Contributes τ w ∧ order m τ = d).card = alpha m d := by @@ -190,10 +189,8 @@ theorem card_contributing_of_order {L m : ℕ} (w : OutStr L) (hw : (innerSetO w have hle := order_le m τ omega -/-- Section 2.4.3: The total number of leaves of order d in the entire recursion tree is -β_d := binom(L, m-d) 9^{L-m+d}. - -NOTE. `d ≤ m` and `m ≤ L` are left implicit in the paper. -/ +/-- If `d ≤ m ≤ L`, the full leaf index type contains exactly `beta L m d` leaves of order +`d`. -/ theorem card_filter_order_eq {L m : ℕ} (hmL : m ≤ L) (d : ℕ) (hd : d ≤ m) : (univ.filter fun τ : Leaf L => order m τ = d).card = beta L m d := by -- The leaves of order `d` are the strings of terms with `P₀` at exactly `m - d` levels. @@ -207,21 +204,17 @@ theorem card_filter_order_eq {L m : ℕ} (hmL : m ≤ L) (d : ℕ) (hd : d ≤ m card_term_ne_P0, one_pow, one_mul, Fintype.card_fin, beta, show L - (m - d) = L - m + d by omega] -/-- Section 2.4.3: In particular, β₀ = binom(L, m) 9^{L-m}. -/ +/-- Evaluate `beta` at order zero. -/ theorem beta_zero (L m : ℕ) : beta L m 0 = L.choose m * 9 ^ (L - m) := by simp [beta] -/-- Section 2.4.3: β₀ = binom(L, m) 9^{L-m} = M. -/ +/-- The order-zero count formula equals `M L m`. -/ theorem beta_zero_eq_M (L m : ℕ) : beta L m 0 = M L m := by rw [beta_zero, M, K, N0, ← pow_mul, mul_comm (L - m) 2, pow_mul] norm_num -/-- Section 2.4.3: A leaf of order d contributes to binom(L-m+d, d) output strings, one for each -inner set of size m that contains the m-d levels at which it chooses P₀. Only the number is -stated. As everywhere in Section 2.4.3, the output strings are those whose inner sets have exactly -m elements. - -NOTE. `m ≤ L` is left implicit in the paper. -/ +/-- Assuming `m ≤ L`, a leaf of natural order `d` contributes to exactly `(L - m + d).choose +d` output strings with `m` inner positions. The order hypothesis forces `d ≤ m`. -/ theorem card_outStr_of_leaf {L m : ℕ} (hmL : m ≤ L) (d : ℕ) (τ : Leaf L) (hτ : order m τ = d) : (univ.filter fun w : OutStr L => (innerSetO w).card = m ∧ Leaf.Contributes τ w).card @@ -254,8 +247,8 @@ theorem card_outStr_of_leaf {L m : ℕ} (hmL : m ≤ L) (d : ℕ) (τ : Leaf L) theorem beta_pos {L m : ℕ} (hmL : m ≤ L) (d : ℕ) : 0 < beta L m d := Nat.mul_pos (Nat.choose_pos (by omega)) (by positivity) -/-- The equality in equation (5) without division: `β_d (L-m+d) = 9 (m-d+1) β_{d-1}` for -`1 ≤ d ≤ m ≤ L`. -/ +/-- For `1 ≤ d ≤ m ≤ L`, consecutive values of `beta` satisfy the displayed multiplication +identity, without division (cf. [AlmanWilliams2026, equation (5)]). -/ theorem Equation5.ratio_nat {L m : ℕ} (hmL : m ≤ L) (d : ℕ) (hd1 : 1 ≤ d) (hd : d ≤ m) : beta L m d * (L - m + d) = 9 * (m - d + 1) * beta L m (d - 1) := by -- With `k = m - d` this is `binom(L, k) (L - k) = binom(L, k + 1) (k + 1)`, times a power of 9. @@ -269,11 +262,9 @@ theorem Equation5.ratio_nat {L m : ℕ} (hmL : m ≤ L) (d : ℕ) (hd1 : 1 ≤ d _ = 9 * 9 ^ (L - m + (d - 1)) * (L.choose (m - d + 1) * (m - d + 1)) := by rw [hchoose] _ = 9 * (m - d + 1) * (L.choose (m - d + 1) * 9 ^ (L - m + (d - 1))) := by ring -/-- Section 2.4.3, the equality in equation (5), for every `L ≥ m` (it is used again as equation (7) -in Section 4.3): β_d / β_{d-1} = 9(m-d+1) / (L-m+d) for `1 ≤ d ≤ m`. - -NOTE. The paper prints the equality under `L = 19m` in (5) and under `L ≥ 10m` in (7); here it is -stated for every `L ≥ m`. -/ +/-- For `1 ≤ d ≤ m ≤ L`, the ratio of consecutive `beta` values is `9 * (m - d + 1) / (L - m + +d)`, with the ratio and subtractions interpreted in `ℚ`. This extends the parameter ranges +used in [AlmanWilliams2026, equations (5) and (7)]. -/ theorem beta_ratio {L m : ℕ} (hmL : m ≤ L) (d : ℕ) (hd1 : 1 ≤ d) (hd : d ≤ m) : (beta L m d : ℚ) / (beta L m (d - 1) : ℚ) = 9 * ((m : ℚ) - (d : ℚ) + 1) / ((L : ℚ) - (m : ℚ) + (d : ℚ)) := by @@ -284,8 +275,8 @@ theorem beta_ratio {L m : ℕ} (hmL : m ≤ L) (d : ℕ) (hd1 : 1 ≤ d) (hd : d push_cast [Nat.cast_sub hmL, Nat.cast_sub hd] at hnat rw [div_eq_div_iff hbeta.ne' (by linarith), hnat] -/-- Section 2.4.3, the inequalities in equation (5): if L = 19m, then for 1 ≤ d ≤ m we have -β_d / β_{d-1} = 9(m-d+1)/(L-m+d) ≤ 9m/(18m+1) < 1/2. -/ +/-- At `L = 19 * m` and `1 ≤ d ≤ m`, the ratio of consecutive `beta` values is bounded above +by `9 * m / (18 * m + 1)`, which is strictly below `1 / 2`. All divisions here are in `ℚ`. -/ theorem beta_ratio_lt_half (m d : ℕ) (hd1 : 1 ≤ d) (hd : d ≤ m) : (beta (19 * m) m d : ℚ) / (beta (19 * m) m (d - 1) : ℚ) ≤ 9 * (m : ℚ) / (18 * (m : ℚ) + 1) ∧ 9 * (m : ℚ) / (18 * (m : ℚ) + 1) < 1 / 2 := by @@ -299,8 +290,8 @@ theorem beta_ratio_lt_half (m d : ℕ) (hd1 : 1 ≤ d) (hd : d ≤ m) : · rw [div_lt_div_iff₀ (by positivity) (by norm_num)] linarith -/-- **Equation (5)**, conclusion: if `L = 19m` then β_d ≤ 2^{-d} M, for `0 ≤ d ≤ m`. -The inequality is multiplied out. -/ +/-- For `d ≤ m` and `L = 19 * m`, the order count satisfies `2 ^ d * beta L m d ≤ M L m`. This +is the division-free geometric bound from [AlmanWilliams2026, equation (5)]. -/ theorem two_pow_mul_beta_le (m d : ℕ) (hd : d ≤ m) : 2 ^ d * beta (19 * m) m d ≤ M (19 * m) m := by induction d with | zero => simp [beta_zero_eq_M] diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Parameters.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Parameters.lean index 464b2a19c..10b5d1679 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Parameters.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Parameters.lean @@ -17,7 +17,7 @@ public import Mathlib.Analysis.Complex.ExponentialBounds ## Main results -* `K_mul_N0_le_D_pow`: the band fits when `D^18 ≤ N`. +* `K_mul_N0_le_D_pow`: bound `K L m * N0 L m` by `D m ^ 18` at `L = 19 * m`. * `encoding_size_le`: the shared encoding length bound. ## References @@ -26,7 +26,6 @@ Adapted selectively from [AlmanWilliams2026, Section 2] and [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical counts and identities; they give no native program or instruction-cost certificate. -Source-derived descriptions below are paraphrases. -/ @[expose] public section @@ -35,11 +34,8 @@ open Finset namespace Cslib.Algorithms.MatrixMultiplication.Schonhage -/-- Section 2.4.4: using the general bound binom(L, m) ≤ (eL/m)^m, we have -K = binom(19m, m) ≤ (19e)^m. The general bound is `Nat.choose_le_exp_mul_div_pow`. - -NOTE. The printed bound divides by `m`. In Lean a quotient by 0 is 0 and `0^0 = 1`, so for `m = 0` -both sides are 1 and no hypothesis `m ≥ 1` is needed. -/ +/-- The binomial count at `L = 19 * m` is at most `(19 * Real.exp 1) ^ m`. This uses +`Nat.choose_le_exp_mul_div_pow` and includes `m = 0`, when both sides are one. -/ theorem K_le_pow (m : ℕ) : (K (19 * m) m : ℝ) ≤ (19 * Real.exp 1) ^ m := by rcases Nat.eq_zero_or_pos m with rfl | hm · simp [K] @@ -49,7 +45,7 @@ theorem K_le_pow (m : ℕ) : (K (19 * m) m : ℝ) ≤ (19 * Real.exp 1) ^ m := b field_simp exact hbase ▸ Nat.choose_le_exp_mul_div_pow (19 * m) m -/-- Section 2.4.4: 19e · 3^18 < 4^18. -/ +/-- The real constant `19 * exp 1 * 3 ^ 18` is strictly less than `4 ^ 18`. -/ theorem nineteen_mul_exp_mul_three_pow_lt : 19 * Real.exp 1 * (3 : ℝ) ^ 18 < (4 : ℝ) ^ 18 := by calc 19 * Real.exp 1 * (3 : ℝ) ^ 18 < 19 * 2.7182818286 * (3 : ℝ) ^ 18 := by gcongr @@ -60,7 +56,7 @@ theorem nineteen_mul_exp_mul_three_pow_lt : 19 * Real.exp 1 * (3 : ℝ) ^ 18 < ( private theorem four_pow_eq (m : ℕ) : ((4 : ℝ) ^ 18) ^ m = (D m : ℝ) ^ 18 := by rw [D, Nat.cast_pow, Nat.cast_ofNat, ← pow_mul, ← pow_mul, mul_comm] -/-- Section 2.4.4: so K N₀ ≤ (19e · 3^18)^m ≤ 4^{18m}, and `4^{18m} = D^18`. -/ +/-- At `L = 19 * m`, the product `K L m * N0 L m` is at most `D m ^ 18`. -/ theorem K_mul_N0_le_D_pow (m : ℕ) : K (19 * m) m * N0 (19 * m) m ≤ D m ^ 18 := by have hK := K_le_pow m have key : (K (19 * m) m : ℝ) * (N0 (19 * m) m : ℝ) ≤ (D m : ℝ) ^ 18 := by @@ -76,19 +72,20 @@ theorem K_mul_N0_le_D_pow (m : ℕ) : K (19 * m) m * N0 (19 * m) m ≤ D m ^ 18 _ = (D m : ℝ) ^ 18 := four_pow_eq m exact_mod_cast key -/-- Section 2.4.4, the first consequence of `N ≥ D^18`: K N₀ ≤ D^18 ≤ N. -/ +/-- The size condition `D m ^ 18 ≤ N` bounds `K (19 * m) m * N0 (19 * m) m` by `N`. -/ theorem K_mul_N0_le (m N : ℕ) (hN : D m ^ 18 ≤ N) : K (19 * m) m * N0 (19 * m) m ≤ N := (K_mul_N0_le_D_pow m).trans hN -/-- Section 2.4.4, the first consequence of `N ≥ D^18`: and hence K₀ N₀ ≤ K N₀ ≤ D^18 ≤ N. -/ +/-- The size condition `D m ^ 18 ≤ N` bounds the band dimension at `L = 19 * m` by `N`. -/ theorem K0_mul_N0_le (m N : ℕ) (hN : D m ^ 18 ≤ N) : K0 (19 * m) m * N0 (19 * m) m ≤ N := (Nat.mul_le_mul_right _ (Nat.sqrt_le_self _)).trans (K_mul_N0_le m N hN) -/-- Section 2.4.4: This means the padding at most doubles N. -/ +/-- Under `D m ^ 18 ≤ N`, padding to complete bands at `L = 19 * m` gives a dimension at most +`2 * N`. -/ theorem padN_le_two_mul_of_D_pow_le (m N : ℕ) (hN : D m ^ 18 ≤ N) : padN (19 * m) m N ≤ 2 * N := padN_le_two_mul (19 * m) m N (K0_mul_N0_le m N hN) -/-- Section 2.4.4: 10^19 · 2^{1/9} / 3^18 < 4^18. -/ +/-- The real constant `10 ^ 19 * 2 ^ (1 / 9) / 3 ^ 18` is strictly less than `4 ^ 18`. -/ theorem ten_pow_mul_two_rpow_div_three_pow_lt : (10 : ℝ) ^ 19 * (2 : ℝ) ^ (1 / 9 : ℝ) / (3 : ℝ) ^ 18 < (4 : ℝ) ^ 18 := by have htwo : (2 : ℝ) ^ (1 / 9 : ℝ) ≤ 2 := by @@ -100,7 +97,7 @@ theorem ten_pow_mul_two_rpow_div_three_pow_lt : mul_le_mul_of_nonneg_left htwo (by positivity) _ < (4 : ℝ) ^ 18 * (3 : ℝ) ^ 18 := by norm_num -/-- Section 2.4.4: so 10^L · 2^{m/9} / N₀ ≤ D^18, for `L = 19m`. -/ +/-- At `L = 19 * m`, the real quotient `10 ^ L * 2 ^ (m / 9) / N0 L m` is at most `D m ^ 18`. -/ theorem ten_pow_div_N0_le (m : ℕ) : (10 : ℝ) ^ (19 * m) * (2 : ℝ) ^ ((m : ℝ) / 9) / (N0 (19 * m) m : ℝ) ≤ (D m : ℝ) ^ 18 := by have hroot : (2 : ℝ) ^ ((m : ℝ) / 9) = ((2 : ℝ) ^ (1 / 9 : ℝ)) ^ m := by @@ -116,8 +113,8 @@ theorem ten_pow_div_N0_le (m : ℕ) : ten_pow_mul_two_rpow_div_three_pow_lt.le m _ = (D m : ℝ) ^ 18 := four_pow_eq m -/-- **Equation (6)**, the second consequence of `N ≥ D^18`: 10^L ≤ 2^{-m/9} N N₀, for -`L = 19m`. -/ +/-- For `L = 19 * m` and `D m ^ 18 ≤ N`, the encoding index count `10 ^ L` is at most `2 ^ (-m +/ 9) * N * N0 L m`, as a real inequality (cf. [AlmanWilliams2026, equation (6)]). -/ theorem encoding_size_le (m N : ℕ) (hN : D m ^ 18 ≤ N) : (10 : ℝ) ^ (19 * m) ≤ (2 : ℝ) ^ (-(m : ℝ) / 9) * (N : ℝ) * (N0 (19 * m) m : ℝ) := by have hN0 : (0 : ℝ) < (N0 (19 * m) m : ℝ) := by exact_mod_cast N0_pos _ _ diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Sparse.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Sparse.lean index 9cd46e8f7..b18c209f3 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Sparse.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Sparse.lean @@ -29,7 +29,6 @@ Adapted selectively from [AlmanWilliams2026, Section 2] and [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical counts and identities; they give no native program or instruction-cost certificate. -Source-derived descriptions below are paraphrases. -/ @[expose] public section @@ -38,9 +37,9 @@ open Finset namespace Cslib.Algorithms.MatrixMultiplication.Schonhage -/-! ### The parameters from Section 2.4.3 on -/ +/-! ### Specialization to `L = 19 * m` -/ -/-- Section 2.4.3: From now on, we fix L = 19m, so that N₀ = 3^{18m}. -/ +/-- At `L = 19 * m`, the outer dimension is `3 ^ (18 * m)`. -/ theorem N0_nineteen_mul (m : ℕ) : N0 (19 * m) m = 3 ^ (18 * m) := by unfold N0 congr 1 @@ -55,12 +54,12 @@ private theorem D_eq_two_pow (m : ℕ) : (D m : ℝ) = (2 : ℝ) ^ (2 * m) := by rw [D, pow_mul] norm_num -/-- Section 2.4.3: Recall that D = 4^m, so 2^m = √D. -/ +/-- The real square root of `D m` is `2 ^ m`. -/ theorem two_pow_eq_sqrt_D (m : ℕ) : (2 : ℝ) ^ m = Real.sqrt (D m) := by rw [D_eq_two_pow, pow_mul', Real.sqrt_sq (by positivity)] -/-- The factor `2^{-m/9}` of Sections 2.4.3 and 2.4.4, by which the costs of the algorithm of -Theorem 5 lie below `N²`. -/ +/-- The positive real factor `2 ^ (-m / 9)`, used in the finite leaf and encoding count +bounds. -/ noncomputable def saving (m : ℕ) : ℝ := (2 : ℝ) ^ (-(m : ℝ) / 9) /-- `2^{-m/9}` is positive. -/ @@ -76,7 +75,7 @@ theorem saving_le_one (m : ℕ) : saving m ≤ 1 := theorem saving_mul_two_rpow (m : ℕ) : saving m * (2 : ℝ) ^ ((m : ℝ) / 9) = 1 := by rw [saving, ← Real.rpow_add two_pos, show -(m : ℝ) / 9 + (m : ℝ) / 9 = 0 by ring, Real.rpow_zero] -/-- Section 2.4.3: and 2^{-m/9} = D^{-1/18}. -/ +/-- Express `saving m` as the real power `D^(-1/18)` for `D = D m`. -/ theorem saving_eq_D_rpow (m : ℕ) : saving m = (D m : ℝ) ^ (-(1 / 18 : ℝ)) := by rw [saving, D_eq_two_pow, ← Real.rpow_natCast 2 (2 * m), ← Real.rpow_mul two_pos.le] congr 1 @@ -85,10 +84,9 @@ theorem saving_eq_D_rpow (m : ℕ) : saving m = (D m : ℝ) ^ (-(1 / 18 : ℝ)) /-! ### The first inequality -/ -/-- In the proof of Lemma 11: We bound the number of leaves of order d in leaves(U) in two -ways: by |U| α_d, charging each output string of U for its leaves of order d, and by β_d, the number -of all leaves of order d. Quoted again in Section 4.3: At every order d, the pruned recursion of -Section 2.4 visits at most min{|U| α_d, β_d} leaves. -/ +/-- If every string in `U` has `m` inner positions, its contributing leaves of natural order +`d` are bounded both by `U.card * alpha m d` and by `beta L m d`. The minimum combines +these bounds without counting a shared leaf more than once. -/ theorem card_leaves_filter_order_le {L m : ℕ} (U : Finset (OutStr L)) (hU : ∀ w ∈ U, (innerSetO w).card = m) (d : ℕ) : ((leaves U).filter fun τ => order m τ = d).card ≤ min (U.card * alpha m d) (beta L m d) := by @@ -120,8 +118,9 @@ theorem card_leaves_filter_order_le {L m : ℕ} (U : Finset (OutStr L)) rw [hempty] exact Nat.zero_le _ -/-- **Lemma 11**, first inequality. For every set U of output strings whose inner sets have -exactly m elements, |leaves(U)| ≤ ∑_{d=0}^{m} min{|U| α_d, β_d}. -/ +/-- Bound the cardinality of `leaves U` by summing the smaller of the per-output bound and the +full leaf count over natural orders `0` through `m`. Every string in `U` must have exactly +`m` inner positions; the empty set is permitted (cf. [AlmanWilliams2026, Lemma 11]). -/ theorem card_leaves_le_sum_min {L m : ℕ} (U : Finset (OutStr L)) (hU : ∀ w ∈ U, (innerSetO w).card = m) : (leaves U).card ≤ ∑ d ∈ range (m + 1), min (U.card * alpha m d) (beta L m d) := by @@ -170,8 +169,8 @@ private theorem sum_beta_lt_of_subset (m a : ℕ) (s : Finset ℕ) (hs : s ⊆ I ((geom_sum_Ico_lt_of_lt_one (by norm_num) (by norm_num) a _).trans_eq (by ring)) hM _ = 2 * (1 / 2 : ℝ) ^ a * (M (19 * m) m : ℝ) := by ring -/-- In the proof of Lemma 11: The orders d > m/9 contribute at most -∑_{d>m/9} β_d ≤ M ∑_{d>m/9} 2^{-d} < 2 · 2^{-m/9} M by (5). -/ +/-- At `L = 19 * m`, the sum of `beta L m d` over `0 ≤ d ≤ m` with `m < 9 * d` is strictly +below `2 * saving m * M L m`. This includes `m = 0`, when the sum is empty. -/ theorem sum_beta_large_orders_lt (m : ℕ) : ((∑ d ∈ (range (m + 1)).filter (fun d => m < 9 * d), beta (19 * m) m d : ℕ) : ℝ) < 2 * saving m * (M (19 * m) m : ℝ) := by @@ -225,12 +224,9 @@ private theorem sum_alpha_small_orders_le_aux (m : ℕ) : rw [← mul_sum, ← add_pow] norm_num -/-- The display in the proof of Lemma 11: ∑_{d ≤ m/9} binom(m, d) 9^d ≤ 72^{m/9} -∑_{d=0}^{m} binom(m, d) 8^{-d} = 72^{m/9} (9/8)^m = (72 · (9/8)^9)^{m/9} < 256^{m/9} = 2^m · -2^{-m/9}. - -NOTE. The strict inequality needs `m ≥ 1`, which is fixed in Section 2.3.3; for `m = 0` both ends -are 1. -/ +/-- For `m ≥ 1`, the sum of `alpha m d` over `0 ≤ d ≤ m` with `9 * d ≤ m` is strictly below `2 +^ m * saving m`. For `m = 0`, the two quantities are equal, so the strict inequality needs +the stated positivity hypothesis. -/ theorem sum_alpha_small_orders_lt (m : ℕ) (hm : 1 ≤ m) : ((∑ d ∈ (range (m + 1)).filter (fun d => 9 * d ≤ m), alpha m d : ℕ) : ℝ) < (2 : ℝ) ^ m * saving m := by @@ -278,8 +274,10 @@ private theorem card_leaves_le_split {L m : ℕ} (U : Finset (OutStr L)) rw [filter_congr fun d _ => (not_le (a := 9 * d) (b := m))] exact sum_le_sum fun d _ => min_le_right _ _ -/-- **Lemma 11**, second inequality. -and if L = 19m, then |leaves(U)| ≤ 2^{-m/9} (2^m |U| + 2M). -/ +/-- For strings of length `19 * m`, each with `m` inner positions, the contributing leaf set +has cardinality at most `saving m * (2 ^ m * U.card + 2 * M (19 * m) m)`. This real bound +permits `m = 0` and `U = ∅`; it makes no execution-cost claim (cf. [AlmanWilliams2026, +Lemma 11]). -/ theorem card_leaves_le_saving {m : ℕ} (U : Finset (OutStr (19 * m))) (hU : ∀ w ∈ U, (innerSetO w).card = m) : ((leaves U).card : ℝ) ≤ saving m * ((2 : ℝ) ^ m * (U.card : ℝ) + 2 * (M (19 * m) m : ℝ)) := by @@ -295,9 +293,9 @@ theorem card_leaves_le_saving {m : ℕ} (U : Finset (OutStr (19 * m))) add_le_add hsmall hlarge _ = _ := by ring -/-- **Lemma 11**, both inequalities together. For every set U of output strings whose inner -sets have exactly m elements, |leaves(U)| ≤ ∑_{d=0}^{m} min{|U| α_d, β_d}, and if L = 19m, -then |leaves(U)| ≤ 2^{-m/9} (2^m |U| + 2M). -/ +/-- Combine the order-wise minimum bound on `leaves U` with the explicit real bound available +when `L = 19 * m`. Both conclusions assume only that each requested string has `m` inner +positions; the specialization is conditional on the displayed equality of parameters. -/ theorem sparse_leaf_bounds {L m : ℕ} (U : Finset (OutStr L)) (hU : ∀ w ∈ U, (innerSetO w).card = m) : (leaves U).card ≤ ∑ d ∈ range (m + 1), min (U.card * alpha m d) (beta L m d) diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/ThinProduct.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/ThinProduct.lean index 49c113b4a..cfe5b34c1 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/ThinProduct.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/ThinProduct.lean @@ -10,16 +10,20 @@ module public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Parameters /-! -# Complete finite thin-product composition +# Finite thin-product correctness and cardinality bounds ## Main results * `thin_product_spec`: exact queried entries for arbitrary signed integer matrices. * `encodings_total_le`, `total_leaves_le`, `total_passed_sets_le`: explicit finite totals. -For `D = 4^m`, `L = 19m`, `D^18 ≤ N` and `|W| 2^m ≤ N²`, encoding entries -and contributing leaves are bounded by constants times `N² / D^(1/18)`. Passed-set -cardinalities carry the additional factor `L + 1`. These are not instruction counts. +Correctness holds for arbitrary signed integer matrices and any layout. For `D = 4^m`, +`L = 19m`, `D^18 ≤ N` and `W.card * 2^m ≤ N²`, the encoding entry count is at most +`4 * D^(-1/18) * N²`, the sum of contributing-leaf cardinalities is at most +`33 * D^(-1/18) * N²`, and the sum of passed request-set cardinalities is at most +`(19m + 1) * 33 * D^(-1/18) * N²`. Here `N` is the original matrix dimension. +The definitions permit zero parameters and empty request sets; the bounds retain their +stated size and sparsity hypotheses. No native program or instruction bound is proved. ## References @@ -27,7 +31,6 @@ Adapted selectively from [AlmanWilliams2026, Section 2] and [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical counts and identities; they give no native program or instruction-cost certificate. -Source-derived descriptions below are paraphrases. -/ @[expose] public section @@ -36,32 +39,31 @@ open Finset namespace Cslib.Algorithms.MatrixMultiplication.Schonhage -/-- Section 2.4.4: Each wanted position (I, J) ∈ W lies in one tile T: the tile of a position is -one of the tiles. -/ +/-- For `m ≤ L`, the band pair of any position `(I, J) : Fin N × Fin N` belongs to the finite +tile set. -/ theorem tile_mem_tiles {L m N : ℕ} (hmL : m ≤ L) (I J : Fin N) : (bandOf L m I, bandOf L m J) ∈ tiles L m N := mem_product.mpr ⟨mem_range.mpr (bandOf_lt_numBands hmL N I I.2), mem_range.mpr (bandOf_lt_numBands hmL N J J.2)⟩ -/-- Section 2.4.4: a wanted position is indexed by one output string of T: its output string is in -the set `W_T` of its tile. -/ +/-- A requested position contributes its output string to `wantedStrings` for its band pair. -/ theorem outStrOfPos_mem_wantedStrings {L m N : ℕ} (lay : Layout L m) (W : Finset (Fin N × Fin N)) {I J : Fin N} (hIJ : (I, J) ∈ W) : outStrOfPos lay I J ∈ wantedStrings lay W (bandOf L m I) (bandOf L m J) := mem_image.mpr ⟨(I, J), mem_filter.mpr ⟨hIJ, rfl, rfl⟩, rfl⟩ -/-- Section 2.4.3: the sets passed to `Pruned` consist only of […] output strings of length L whose -inner sets […] have exactly m elements. -/ +/-- Every string requested in a tile has exactly `m` inner positions. -/ theorem card_innerSetO_of_mem_wantedStrings {L m N : ℕ} (lay : Layout L m) (W : Finset (Fin N × Fin N)) (β β' : ℕ) (w : OutStr L) (hw : w ∈ wantedStrings lay W β β') : (innerSetO w).card = m := by obtain ⟨IJ, -, rfl⟩ := mem_image.mp hw exact card_innerSetO_outStrOfPos lay IJ.1 IJ.2 -/-- **Proof of Theorem 5**, correctness of the algorithm. for each tile T with W_T ≠ ∅, -we run Pruned(W_T), and report, for each (I, J) ∈ W, the value at its output string. This is correct -by Lemmas 9 and 10. The run on the tile of `(I, J)` reads the encodings of the input arrays of its -row band and its column band. This holds for all `m ≤ L`. -/ +/-- For arbitrary signed integer matrices of shapes `N × D m` and `D m × N`, the pruned result +at any position in `W` equals the corresponding entry of `X * Y`. The encodings and +request set are those for that position's row and column bands. This holds for every +`Layout L m`, which includes `m ≤ L`, without the size or sparsity conditions used in the +counting bounds (cf. [AlmanWilliams2026, Theorem 5]). -/ theorem thin_product_spec {L m N : ℕ} (lay : Layout L m) (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) (W : Finset (Fin N × Fin N)) (I J : Fin N) (hIJ : (I, J) ∈ W) : @@ -76,8 +78,8 @@ theorem thin_product_spec {L m N : ℕ} (lay : Layout L m) (X : Matrix (Fin N) ( -- Lemma 9, through the tiling of Section 2.3.4. exact Full.bandArray_eq_mul lay X Y I J -/-- Theorem 5 and Section 2.4.4: |W| ≤ N²/√D = 2^{-m} N². In the statements below this -hypothesis is written without a square root, as `|W| · 2^m ≤ N²`. -/ +/-- The real inequality `w ≤ N² / sqrt (D m)` is equivalent to the natural-number inequality +`w * 2 ^ m ≤ N ^ 2`, including `m = 0`. -/ theorem le_sq_div_sqrt_D_iff (m N w : ℕ) : (w : ℝ) ≤ (N : ℝ) ^ 2 / Real.sqrt (D m) ↔ w * 2 ^ m ≤ N ^ 2 := by rw [← two_pow_eq_sqrt_D, le_div_iff₀ (by positivity)] @@ -88,9 +90,10 @@ private theorem numBands_mul_N0_le {L m : ℕ} (hmL : m ≤ L) (N : ℕ) : numBands L m N * N0 L m ≤ padN L m N := Nat.mul_le_mul_left _ (Nat.le_mul_of_pos_left _ (K0_pos hmL)) -/-- Section 2.4.4: By (6), the encodings cost (2N/K₀N₀) · O(10^L) = O(2^{-m/9} N²) operations: the -encodings of the input arrays of all row bands and all column bands have at most `4 N² / D^{1/18}` -entries in all. Here `N` is the size of the input, which the padding at most doubles. -/ +/-- At `L = 19 * m` with `D m ^ 18 ≤ N`, the encodings of all row-band and column-band arrays +have at most `4 * D^(-1/18) * N²` entries in total, where `D = D m`. The counted quantity +is `2 * numBands L m N * 10 ^ L`, and `N` is the original dimension. This is an entry +count, not a bound on instructions or encoding time. -/ theorem encodings_total_le (m N : ℕ) (hN : D m ^ 18 ≤ N) : ((2 * numBands (19 * m) m N * 10 ^ (19 * m) : ℕ) : ℝ) ≤ 4 * (D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2 := by @@ -106,7 +109,8 @@ theorem encodings_total_le (m N : ℕ) (hN : D m ^ 18 ≤ N) : _ ≤ 2 * saving m * (N * (2 * N)) := by gcongr _ = 4 * saving m * (N : ℝ) ^ 2 := by ring -/-- Section 2.4.4: Each position of W lands in one set W_T, so ∑_T |W_T| ≤ |W|. -/ +/-- Summing the cardinalities of the requested-string sets over all tiles gives at most +`W.card`. -/ theorem sum_card_wantedStrings_le {L m N : ℕ} (lay : Layout L m) (W : Finset (Fin N × Fin N)) : ∑ T ∈ tiles L m N, (wantedStrings lay W T.1 T.2).card ≤ W.card := by have hmap : ∀ IJ ∈ W, (bandOf L m IJ.1, bandOf L m IJ.2) ∈ tiles L m N := fun IJ _ => @@ -116,13 +120,14 @@ theorem sum_card_wantedStrings_le {L m N : ℕ} (lay : Layout L m) (W : Finset ( refine card_image_le.trans (le_of_eq ?_) congr 1 -/-- Section 2.4.4: there are at most 4N²/M tiles, so ∑_T M ≤ 4N², where `N` is the padded size. -/ +/-- The sum of `M L m` over the finite tile set is at most `4 * padN L m N ^ 2`. -/ theorem sum_M_le (L m N : ℕ) : ∑ _T ∈ tiles L m N, M L m ≤ 4 * padN L m N ^ 2 := by rw [sum_const, smul_eq_mul] exact card_tiles_mul_M_le L m N -/-- Section 2.4.4, the display: by Lemma 11 summed over the tiles, -`∑_T |leaves(W_T)| ≤ 2^{-m/9} (2^m |W| + 2 ∑_T M)`. -/ +/-- At `L = 19 * m`, bound the sum of contributing-leaf cardinalities for the tile request +sets by `saving m * (2 ^ m * W.card + 2 * ∑ T ∈ tiles L m N, M L m)`. A leaf used in +distinct tiles is counted once for each such tile. -/ theorem sum_card_leaves_le {m N : ℕ} (lay : Layout (19 * m) m) (W : Finset (Fin N × Fin N)) : ((∑ T ∈ tiles (19 * m) m N, (leaves (wantedStrings lay W T.1 T.2)).card : ℕ) : ℝ) ≤ saving m * ((2 : ℝ) ^ m * (W.card : ℝ) @@ -143,9 +148,10 @@ theorem sum_card_leaves_le {m N : ℕ} (lay : Layout (19 * m) m) (W : Finset (Fi rw [← mul_sum, sum_add_distrib, ← mul_sum, ← mul_sum] _ ≤ _ := by gcongr -/-- **Proof of Theorem 5**, the number of leaves in terms of the `N` and `D` of the theorem (`N` is -the size of the input, not the padded size). Since `N ≥ D^18 ≥ K₀ N₀`, the padding at most doubles -`N`, so `2^m |W| + 2 ∑_T M ≤ N² + 32 N²`. -/ +/-- At `L = 19 * m`, assume `D m ^ 18 ≤ N` and `W.card * 2 ^ m ≤ N ^ 2`. The sum of +contributing-leaf cardinalities over all tiles is then at most `33 * D^(-1/18) * N²`, +where `D = D m` and `N` is the original dimension. This counts leaves with their tile +multiplicities, not executed instructions. -/ theorem total_leaves_le {m N : ℕ} (lay : Layout (19 * m) m) (W : Finset (Fin N × Fin N)) (hN : D m ^ 18 ≤ N) (hW : W.card * 2 ^ m ≤ N ^ 2) : ((∑ T ∈ tiles (19 * m) m N, (leaves (wantedStrings lay W T.1 T.2)).card : ℕ) : ℝ) @@ -182,7 +188,10 @@ private theorem sum_totalSize_le {m N : ℕ} (lay : Layout (19 * m) m) exact sum_le_sum fun T _ => Pruned.totalSize_le _ _ _ exact_mod_cast h -/-- **Proof of Theorem 5**, the total size of the sets, in the `N` and `D` of the theorem. -/ +/-- At `L = 19 * m`, assume `D m ^ 18 ≤ N` and `W.card * 2 ^ m ≤ N ^ 2`. Summing +`Pruned.totalSize` over all tiles gives at most `(19 * m + 1) * (33 * D^(-1/18) * N²)`, +where `D = D m` and `N` is the original dimension. The quantity sums request-set +cardinalities through the pruned recursions; it is not an instruction count. -/ theorem total_passed_sets_le {m N : ℕ} (lay : Layout (19 * m) m) (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) (W : Finset (Fin N × Fin N)) (hN : D m ^ 18 ≤ N) (hW : W.card * 2 ^ m ≤ N ^ 2) : diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Tiling.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Tiling.lean index dd34af063..980e8f750 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Tiling.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Tiling.lean @@ -17,7 +17,7 @@ import Mathlib.Tactic.Ring ## Main results * `nonempty_layout`: valid layouts exist whenever `m ≤ L`. -* `Full.bandArray_eq_mul`: tiling computes every original integer product entry. +* `Full.bandArray_eq_mul`: product entries as `Full.result` values of band arrays. * `outStrOfPos_injective`: positions in a tile have distinct output strings. ## References @@ -26,7 +26,6 @@ Adapted selectively from [AlmanWilliams2026, Section 2] and [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical counts and identities; they give no native program or instruction-cost certificate. -Source-derived descriptions below are paraphrases. -/ @[expose] public section @@ -37,11 +36,11 @@ namespace Cslib.Algorithms.MatrixMultiplication.Schonhage /-! ### The numbers -/ -/-- Section 2.3.4: K₀² ≤ K. -/ +/-- The square of the natural square root `K0 L m` is at most `K L m`. -/ theorem K0_sq_le_K (L m : ℕ) : K0 L m ^ 2 ≤ K L m := Nat.sqrt_le' _ -/-- Section 2.3.4: K₀ ≥ √K / 2, squared. -/ +/-- The binomial count `K L m` is at most four times `K0 L m ^ 2`. -/ theorem K_le_four_mul_K0_sq (L m : ℕ) : K L m ≤ 4 * K0 L m ^ 2 := by have hlt : K L m < (K0 L m + 1) ^ 2 := Nat.lt_succ_sqrt' _ rcases Nat.eq_zero_or_pos (K0 L m) with h0 | h0 @@ -55,23 +54,19 @@ theorem K_le_four_mul_K0_sq (L m : ℕ) : K L m ≤ 4 * K0 L m ^ 2 := by theorem bandSize_pos {L m : ℕ} (hmL : m ≤ L) : 0 < bandSize L m := Nat.mul_pos (K0_pos hmL) (N0_pos L m) -/-- Section 2.3.4: we first pad N to a multiple of K₀ N₀. -/ +/-- The padded dimension is a multiple of the band size. -/ theorem bandSize_dvd_padN (L m N : ℕ) : bandSize L m ∣ padN L m N := Dvd.intro_left _ rfl -/-- The padded size is at least `N`. - -NOTE. `m ≤ L` is left implicit in the paper. For `m > L` there is no subset of size `m` and -`K₀ = 0`. -/ +/-- For `m ≤ L`, padding covers all `N` original positions. The parameter condition ensures a +positive band size. -/ theorem le_padN {L m : ℕ} (hmL : m ≤ L) (N : ℕ) : N ≤ padN L m N := by have h := Nat.lt_div_mul_add (a := N + bandSize L m - 1) (bandSize_pos hmL) unfold padN numBands omega -/-- The padded size is the least multiple of `K₀ N₀` that is at least `N`: it is less than -`N + K₀ N₀`. - -NOTE. `m ≤ L` is left implicit in the paper. -/ +/-- For `m ≤ L`, the padded dimension is strictly less than `N + bandSize L m`, including `N = +0`. -/ theorem padN_lt_add_bandSize {L m : ℕ} (hmL : m ≤ L) (N : ℕ) : padN L m N < N + bandSize L m := by have h := Nat.div_mul_le_self (N + bandSize L m - 1) (bandSize L m) @@ -79,7 +74,7 @@ theorem padN_lt_add_bandSize {L m : ℕ} (hmL : m ≤ L) (N : ℕ) : unfold padN numBands omega -/-- Section 2.3.4: the padding at most doubles N since N ≥ K₀ N₀. -/ +/-- If the band size is at most `N`, the padded dimension is at most `2 * N`. -/ theorem padN_le_two_mul (L m N : ℕ) (hN : K0 L m * N0 L m ≤ N) : padN L m N ≤ 2 * N := by rcases Nat.lt_or_ge L m with hLm | hmL · -- for `m > L` there are no subsets, `K₀ = 0`, and the padded size is 0 @@ -93,8 +88,8 @@ theorem padN_le_two_mul (L m N : ℕ) (hN : K0 L m * N0 L m ≤ N) : padN L m N theorem card_tiles (L m N : ℕ) : (tiles L m N).card = numBands L m N ^ 2 := by simp [tiles, sq] -/-- Section 2.3.4: there are (N / K₀N₀)² ≤ 4N²/M tiles, where `N` is the padded size. The -inequality is multiplied out. -/ +/-- The tile count multiplied by `M L m` is at most `4 * padN L m N ^ 2`. The bound is stated +without division and requires no positivity hypothesis. -/ theorem card_tiles_mul_M_le (L m N : ℕ) : (tiles L m N).card * M L m ≤ 4 * padN L m N ^ 2 := by rw [card_tiles] calc numBands L m N ^ 2 * M L m @@ -105,9 +100,7 @@ theorem card_tiles_mul_M_le (L m N : ℕ) : (tiles L m N).card * M L m ≤ 4 * p /-! ### The table of subsets and the matrices of a tile -/ -/-- Section 2.3.4: We fix K₀² ≤ K distinct subsets of {1, …, L} of size m: this can be done. - -NOTE. `m ≤ L` is left implicit in the paper; it is a field of `Layout`. -/ +/-- Indexing data `Layout L m` exist for every `m ≤ L`, including `m = 0`. -/ theorem nonempty_layout {L m : ℕ} (hmL : m ≤ L) : Nonempty (Layout L m) := by have hcard : Fintype.card (Fin (K0 L m) × Fin (K0 L m)) ≤ Fintype.card {Q : Finset (Fin L) // Q.card = m} := by @@ -129,8 +122,7 @@ private theorem sum_table_eq {L m : ℕ} {A : Type*} [AddCommMonoid A] (lay : La (∑ gh', if lay.table gh' = lay.table gh then F gh' else 0) = F gh := by simp [lay.table_injective.eq_iff] -/-- Section 2.3.4: In a tile, let X_Q and Y_Q be the row block and the column block of the block -product with subset Q. For `X_Q`: it is the `g`-th row block of the row band. -/ +/-- At the table entry `(g, h)`, the left family selects row block `β * K0 L m + g`. -/ @[simp] theorem bandFamilyL_table {L m N : ℕ} (lay : Layout L m) (X : Matrix (Fin N) (Fin (D m)) ℤ) (β : ℕ) (g h : Fin (K0 L m)) : @@ -145,21 +137,21 @@ theorem bandFamilyR_table {L m N : ℕ} (lay : Layout L m) (Y : Matrix (Fin (D m bandFamilyR lay Y β (lay.table (g, h)) = colBlock lay Y (β * K0 L m + h) := sum_table_eq lay _ (g, h) -/-- Section 2.3.4: and let X_Q = Y_Q = 0 for the other subsets Q. For `X_Q`. -/ +/-- The left family is zero at any level set outside the table image. -/ theorem bandFamilyL_eq_zero {L m N : ℕ} (lay : Layout L m) (X : Matrix (Fin N) (Fin (D m)) ℤ) (β : ℕ) {Q : Finset (Fin L)} (hQ : ∀ gh, lay.table gh ≠ Q) : bandFamilyL lay X β Q = 0 := sum_eq_zero fun gh _ => ite_eq_right (hQ gh) -/-- Section 2.3.4: and let X_Q = Y_Q = 0 for the other subsets Q. For `Y_Q`. -/ +/-- The right family is zero at any level set outside the table image. -/ theorem bandFamilyR_eq_zero {L m N : ℕ} (lay : Layout L m) (Y : Matrix (Fin (D m)) (Fin N) ℤ) (β : ℕ) {Q : Finset (Fin L)} (hQ : ∀ gh, lay.table gh ≠ Q) : bandFamilyR lay Y β Q = 0 := sum_eq_zero fun gh _ => ite_eq_right (hQ gh) -/-! ### One run of `Full` for each tile -/ +/-! ### Product identities for tile arrays -/ -/-- Section 2.3.4: Then one run of Full computes all K₀² block products of the tile (Lemma 9). -The tile is that of the row band `β` and the column band `β'`; the block product in position `(g, -h)` of its grid is the product of the row block `β K₀ + g` by the column block `β' K₀ + h`. -/ +/-- For the band arrays of tile `(β, β')`, `Full.result` at the output string for table entry +`(g, h)` equals the product entry of row block `β * K0 L m + g` and column block `β' * K0 +L m + h`. -/ theorem Full.bandArray_eq_block_mul {L m N : ℕ} (lay : Layout L m) (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) (β β' : ℕ) (g h : Fin (K0 L m)) (r c : OuterStr L m) : @@ -180,8 +172,9 @@ private theorem position_decomp {L m : ℕ} (lay : Layout L m) (I : ℕ) : rw [hblock] exact Nat.div_add_mod' _ _ -/-- Section 2.3.4: and running it on every tile computes all of XY. The entry `(XY)[I, J]` is the -output of the run on the tile of `(I, J)` at the output string of `(I, J)`. -/ +/-- The band arrays for the tile containing an original position `(I, J)` give `Full.result` +equal to `(X * Y) I J` at `outStrOfPos lay I J`. The matrices may have arbitrary signed +integer entries. -/ theorem Full.bandArray_eq_mul {L m N : ℕ} (lay : Layout L m) (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) (I J : Fin N) : Full.result L (bandArrayL lay X (bandOf L m I)) (bandArrayR lay Y (bandOf L m J)) (outStrOfPos @@ -196,10 +189,8 @@ theorem Full.bandArray_eq_mul {L m N : ℕ} (lay : Layout L m) simp only [rowBlock, colBlock, Equiv.symm_apply_apply, position_decomp, padRows, padCols, I.isLt, J.isLt, dite_eq_left, Fin.eta] -/-- The entry `(XY)[I, J]` as a value of `bilinearProduct`, that is, as a sum over leaves - (equation (2)). This -is the form in which Section 4 uses the tiling (by (2) and Lemma 9, (X_Q Y_Q)[w] is the sum of the -products at the leaves contributing to w). -/ +/-- At the output string of an original position `(I, J)`, the bilinear product of its two +band arrays equals `(X * Y) I J`. -/ theorem bilinearProduct_bandArray_eq_mul {L m N : ℕ} (lay : Layout L m) (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) (I J : Fin N) : @@ -209,9 +200,7 @@ theorem bilinearProduct_bandArray_eq_mul {L m N : ℕ} (lay : Layout L m) (X : M rw [← Full.result_eq_bilinearProduct] exact Full.bandArray_eq_mul lay X Y I J -/-- Every row and every column lies in one of the bands. - -NOTE. `m ≤ L` is left implicit in the paper. -/ +/-- For `m ≤ L`, any index `I < N` has band index below `numBands L m N`. -/ theorem bandOf_lt_numBands {L m : ℕ} (hmL : m ≤ L) (N I : ℕ) (hI : I < N) : bandOf L m I < numBands L m N := by rw [bandOf, Nat.div_lt_iff_lt_mul (bandSize_pos hmL)] @@ -219,8 +208,8 @@ theorem bandOf_lt_numBands {L m : ℕ} (hmL : m ≤ L) (N I : ℕ) (hI : I < N) /-! ### The output string of a position -/ -/-- Section 2.4.4: the inner set of the output string of a position is the subset Q of the block -product containing (I, J). -/ +/-- The inner set of a position's output string is the table entry indexed by its two +within-band block indices. -/ theorem innerSetO_outStrOfPos {L m : ℕ} (lay : Layout L m) (I J : ℕ) : innerSetO (outStrOfPos lay I J) = lay.table (blockOf lay I, blockOf lay J) := innerSetO_outStrOf _ _ _ _ @@ -230,8 +219,8 @@ theorem card_innerSetO_outStrOfPos {L m : ℕ} (lay : Layout L m) (I J : ℕ) : (innerSetO (outStrOfPos lay I J)).card = m := innerSetO_outStrOfPos lay I J ▸ lay.table_card _ -/-- Used implicitly in Section 2.4.4 (is indexed by one output string of T, report […] the value -at its output string): different positions of the same tile have different output strings. -/ +/-- Within a fixed pair of row and column bands, equality of output strings implies equality +of both natural-number position indices. -/ theorem outStrOfPos_injective {L m : ℕ} (lay : Layout L m) (I J I' J' : ℕ) (hI : bandOf L m I = bandOf L m I') (hJ : bandOf L m J = bandOf L m J') (h : outStrOfPos lay I J = outStrOfPos lay I' J') : I = I' ∧ J = J' := by diff --git a/Complexity/Foundations/Algebra/FiniteMagnitude.lean b/Complexity/Foundations/Algebra/FiniteMagnitude.lean index 99c8cad79..e18125684 100644 --- a/Complexity/Foundations/Algebra/FiniteMagnitude.lean +++ b/Complexity/Foundations/Algebra/FiniteMagnitude.lean @@ -24,7 +24,6 @@ Adapted selectively from [AlmanWilliams2026, Section 2] and [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical counts and identities; they give no native program or instruction-cost certificate. -Source-derived descriptions below are paraphrases. -/ @[expose] public section diff --git a/Complexity/Foundations/Algebra/GeometricTail.lean b/Complexity/Foundations/Algebra/GeometricTail.lean index fa60e6dd2..c9b5095dc 100644 --- a/Complexity/Foundations/Algebra/GeometricTail.lean +++ b/Complexity/Foundations/Algebra/GeometricTail.lean @@ -16,7 +16,7 @@ public import Mathlib.Algebra.Order.BigOperators.Ring.Finset ## Main results -* `geom_sum_Ico_lt_of_lt_one`: every finite tail is strictly below the infinite tail. +* `geom_sum_Ico_lt_of_lt_one`: the strict bound for `∑ i ∈ Ico m n, x ^ i` when `0 < x < 1`. ## References @@ -24,7 +24,6 @@ Adapted selectively from [AlmanWilliams2026, Section 2] and [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical counts and identities; they give no native program or instruction-cost certificate. -Source-derived descriptions below are paraphrases. -/ @[expose] public section diff --git a/Complexity/Foundations/Combinatorics/BinomialUpperBound.lean b/Complexity/Foundations/Combinatorics/BinomialUpperBound.lean index f47c81e9d..6036e4144 100644 --- a/Complexity/Foundations/Combinatorics/BinomialUpperBound.lean +++ b/Complexity/Foundations/Combinatorics/BinomialUpperBound.lean @@ -24,7 +24,6 @@ Adapted selectively from [AlmanWilliams2026, Section 2] and [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical counts and identities; they give no native program or instruction-cost certificate. -Source-derived descriptions below are paraphrases. -/ @[expose] public section diff --git a/Complexity/Foundations/Combinatorics/FunctionPlaces.lean b/Complexity/Foundations/Combinatorics/FunctionPlaces.lean index 05960b70c..e2ac74931 100644 --- a/Complexity/Foundations/Combinatorics/FunctionPlaces.lean +++ b/Complexity/Foundations/Combinatorics/FunctionPlaces.lean @@ -27,7 +27,6 @@ Adapted selectively from [AlmanWilliams2026, Section 2] and [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical counts and identities; they give no native program or instruction-cost certificate. -Source-derived descriptions below are paraphrases. -/ @[expose] public section @@ -97,8 +96,8 @@ theorem card_powersetCard_superset (T : Finset ι) {k : ℕ} (hk : k ≤ Fintype variable {α : Type*} [Fintype α] -/-- The number of functions `f` with `f i ∈ A i` whose set of places with `p` is exactly `S`: choose -a value with `p` at each place of `S` and one without `p` at every other place. -/ +/-- Count the functions satisfying `f i ∈ A i` and having property `p` exactly on `S` by +multiplying the permitted choices at each position. -/ theorem card_pi_places_eq [DecidableEq α] (A : ι → Finset α) (p : α → Prop) [DecidablePred p] (S : Finset ι) : #{f : ι → α | (∀ i, f i ∈ A i) ∧ ({i | p (f i)} : Finset ι) = S} = @@ -112,8 +111,9 @@ theorem card_pi_places_eq [DecidableEq α] (A : ι → Finset α) (p : α → Pr simp only [mem_filter, mem_univ, true_and, Fintype.mem_piFinset, hmem, forall_and, Finset.ext_iff] -/-- The number of functions `f` with `f i ∈ A i` that have `p` at exactly `k` places, as a sum over -the possible sets of these places. -/ +/-- Partition the functions satisfying `f i ∈ A i` and having property `p` at exactly `k` +positions by their size-`k` set of such positions, and sum the corresponding products of +choice counts. -/ theorem card_pi_places_card [DecidableEq α] (A : ι → Finset α) (p : α → Prop) [DecidablePred p] (k : ℕ) : #{f : ι → α | (∀ i, f i ∈ A i) ∧ #{i | p (f i)} = k} = diff --git a/Complexity/Foundations/Data/Finset/OrderGlue.lean b/Complexity/Foundations/Data/Finset/OrderGlue.lean index 02be86b3f..9a61f690c 100644 --- a/Complexity/Foundations/Data/Finset/OrderGlue.lean +++ b/Complexity/Foundations/Data/Finset/OrderGlue.lean @@ -27,7 +27,6 @@ Adapted selectively from [AlmanWilliams2026, Section 2] and [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical counts and identities; they give no native program or instruction-cost certificate. -Source-derived descriptions below are paraphrases. -/ @[expose] public section @@ -171,9 +170,10 @@ theorem filter_glue (hf : ∀ k, IsInner (f k)) (hg : ∀ k, ¬ IsInner (g k)) : simpa only [filter_eq_iff_forall_level Q hQ, glue_innerLevel, glue_outerLevel] using And.intro hf hg -/-- Let every inner letter `s` be `inner i` for its index `i = innerIndex s`, and every outer letter -be `outer o` for its index `o = outerIndex s`. Then a string whose inner letters are at the levels -of `Q` is rebuilt by gluing the letters `inner i` and `outer o` for the indices of its letters. -/ +/-- Reconstruct `u` by extracting and reinserting its inner and outer indices. The hypotheses +require that both index projections have the stated reconstruction property on their +respective classes of letters, and that the inner letters of `u` occur exactly on the +size-`m` set `Q`. -/ theorem glue_index {I O : Type*} {inner : I → α} {outer : O → α} {innerIndex : α → I} {outerIndex : α → O} (hI : ∀ s, IsInner s → inner (innerIndex s) = s) (hO : ∀ s, ¬ IsInner s → outer (outerIndex s) = s) {u : Fin L → α} From 0505439ad73e746828ba9e5ade7ec8bb4577456e Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 10:47:17 -0400 Subject: [PATCH 17/73] fix(wordram): repair parameter proof hygiene and ceiling-log docs --- Complexity/Algorithms/WordRAM/NatClog.lean | 4 ++-- .../Algorithms/WordRAM/ThreeSum/ChanHe/Parameters.lean | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Complexity/Algorithms/WordRAM/NatClog.lean b/Complexity/Algorithms/WordRAM/NatClog.lean index 6feabd6b6..215117b05 100644 --- a/Complexity/Algorithms/WordRAM/NatClog.lean +++ b/Complexity/Algorithms/WordRAM/NatClog.lean @@ -11,7 +11,7 @@ public import Complexity.Computability.WordRAM.Notation public import Mathlib.Data.Nat.Log /-! -# A charged native binary logarithm +# A charged native ceiling binary logarithm ## Main definitions @@ -30,7 +30,7 @@ open Cslib.WordRAM /-- Quotient, initially the input. -/ abbrev arg : Fin 4 := 0 -/-- Floor logarithm output. -/ +/-- Ceiling logarithm output. -/ abbrev result : Fin 4 := 1 /-- Constant one. -/ abbrev one : Fin 4 := 2 diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Parameters.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Parameters.lean index 3ddc4b7cc..f26ccbe18 100644 --- a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Parameters.lean +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Parameters.lean @@ -841,8 +841,9 @@ theorem size_program (κ : ℕ) : (program κ).size = κ + 71 := by induction κ with | zero => rfl | succ κ ih => simp [power, ih] - simp [program, setup, calculate, hp, NatLog.program, NatLog.body, - NatClog.program, NatClog.body, NatSqrt.program, NatSqrt.body] + simp only [program, setup, calculate, NatLog.program, NatLog.body, NatSqrt.program, + NatSqrt.body, NatClog.program, NatClog.body, Code.size.eq_2, Code.size, Nat.reduceAdd, hp, + Code.size_rename] omega /-- The code is the same at every word width. -/ From ad3e09735a116f9868e87cf7ae6af59e5cb6a285 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 12:45:55 -0400 Subject: [PATCH 18/73] feat(wordram): implement shared tensor encodings Complete the shared left/right encoder milestone #1493 above parent b4936ce28 (#1496). Fixed standard-WordRAM code with 32 registers computes every public encodingL/encodingR leaf from actual initialized flat signed inputs. The native bound is 240*(L+1)*10^L instructions plus halt, with explicit magnitude/address inequalities, two alternating buffers, charged initialization/copying and the complete outside frame. The full ordinary native 3SUM objective #1394 remains open; this component supplies no sparse-query or Exact Triangle solver. Validation of tree b598513f02cc064eaf50d3c680684c19289ffd71 against b4936ce28d8b498b364a7947d34f728c55f8b226: all six gates pass together (build --wfail --iofail, mk_all --check, test --wfail --iofail, lint, lint-style, checkInitImports), full citation-aware hygiene --check-simp, changed-file lints (13 library modules, one new test) and diff checks pass. Tests execute both encoders at depths zero, one and two, compare all 100 depth-two leaves with the unchanged public encodings, preserve input/frame sentinels, and cover adjacent word widths and all three copy block orders. Kernel-checked concrete flat executions discharge every width/magnitude premise. Selected abstracts, catalogue and rendering are unaffected. AI assistance: OpenAI Codex implementation and self-review by delegated worker codex-shared-encoding-t3-03b600bd. Independent correctness/API review is pending; this is not an approval or a claim that the complete 3SUM objective is done. --- Complexity.lean | 12 + .../Schonhage/CoordinateTransform.lean | 191 +++++++++++++ .../MatrixMultiplication/Schonhage/Cell.lean | 129 +++++++++ .../Schonhage/Controller.lean | 250 ++++++++++++++++++ .../MatrixMultiplication/Schonhage/Copy.lean | 48 ++++ .../Schonhage/Encoding.lean | 244 +++++++++++++++++ .../MatrixMultiplication/Schonhage/Exec.lean | 136 ++++++++++ .../MatrixMultiplication/Schonhage/Flat.lean | 88 ++++++ .../MatrixMultiplication/Schonhage/Forms.lean | 235 ++++++++++++++++ .../Schonhage/Header.lean | 174 ++++++++++++ .../MatrixMultiplication/Schonhage/Input.lean | 127 +++++++++ .../Schonhage/Meaning.lean | 96 +++++++ .../MatrixMultiplication/Schonhage/Stage.lean | 203 ++++++++++++++ Complexity/Computability/WordRAM/Copy.lean | 29 +- ComplexityTests/SchonhageEncoding.lean | 139 ++++++++++ NOTICE | 3 +- ROADMAP.md | 10 +- UPSTREAM.md | 18 +- 18 files changed, 2119 insertions(+), 13 deletions(-) create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/CoordinateTransform.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Cell.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Controller.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Copy.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Encoding.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Exec.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Flat.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Forms.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Header.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Input.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Meaning.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Stage.lean create mode 100644 ComplexityTests/SchonhageEncoding.lean diff --git a/Complexity.lean b/Complexity.lean index 1cdd3b3c0..04ec040e5 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -31,6 +31,7 @@ public import Complexity.Algorithms.Lean.VanEmdeBoas.Layout public import Complexity.Algorithms.Lean.VanEmdeBoas.Successor public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Alphabets public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Batch +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.CoordinateTransform public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Full public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Identity public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Layout @@ -103,6 +104,17 @@ public import Complexity.Algorithms.WordRAM.FusionTree.Query.Traversal public import Complexity.Algorithms.WordRAM.FusionTree.Rank public import Complexity.Algorithms.WordRAM.FusionTree.ReadHeader public import Complexity.Algorithms.WordRAM.FusionTree.Static +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Cell +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Controller +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Copy +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Exec +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Flat +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Forms +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Header +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Input +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Meaning +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Stage public import Complexity.Algorithms.WordRAM.PrefixSums.Dynamic public import Complexity.Algorithms.WordRAM.PrefixSums.Query public import Complexity.Algorithms.WordRAM.PrefixSums.Tree diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/CoordinateTransform.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/CoordinateTransform.lean new file mode 100644 index 000000000..eb1392c7d --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/CoordinateTransform.lean @@ -0,0 +1,191 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Full +public import Complexity.Foundations.Algebra.FiniteMagnitude +import Mathlib.Tactic.Ring +import Mathlib.Tactic.Linarith + +/-! +# Shared coordinate transforms in flat arrays + +After `j` coordinates have been transformed, the array has `10^j * 7^k` entries, where +`j + k = L`. Its entry at `(p, r)` sums the input entries with suffix `r`, weighting their +prefixes by the product of the selected coefficients. The next coordinate transform computes +each new entry from seven entries of this shared array. It does not independently enumerate +all input entries for every final leaf. + +## Main definitions + +* `CoordinateTransform.weight`: product of coefficients along the transformed prefix. +* `CoordinateTransform.value`: prefix expansion at a mixed-radix array position. + +## Main results + +* `CoordinateTransform.value_step`: the seven-to-ten shared coordinate update. +* `CoordinateTransform.stageSize_le`: every stage has at most `10^L` entries. + +## References + +An iterative implementation invariant for [AlmanWilliams2026, Section 2.4.1]. The coefficients +are those of the existing encoding API, adapted from Anthropic, PBC's formal source at +`16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. This module defines mathematical arrays and sums, +not a separate machine or an instruction-counting semantics. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage.CoordinateTransform + +open Finset + +/-- Product of `j` coefficients, reading prefix digits from least significant to most +significant. Values at noncanonical indices are harmless; clients use indices below the powers. -/ +def weight (c : ℕ → ℕ → ℤ) : ℕ → ℕ → ℕ → ℤ + | 0, _, _ => 1 + | j + 1, p, v => weight c j (p / 10) (v / 7) * c (p % 10) (v % 7) + +/-- Value at transformed prefix `p` and original suffix `r`. The remaining suffix has `k` +base-seven digits; the original input index is `v * 7^k + r`. -/ +def value (c : ℕ → ℕ → ℤ) (j k : ℕ) (a : ℕ → ℤ) (p r : ℕ) : ℤ := + ∑ v ∈ range (7 ^ j), weight c j p v * a (v * 7 ^ k + r) + +private theorem sum_blocks (m n : ℕ) (f : ℕ → ℤ) : + ∑ i ∈ range (m * n), f i = ∑ q ∈ range m, ∑ s ∈ range n, f (q * n + s) := by + induction m with + | zero => simp + | succ m ih => + rw [Nat.succ_mul, sum_range_add, ih, sum_range_succ] + +/-- At the initial stage no coordinate is transformed, so the entry is the input itself. -/ +theorem value_zero (c : ℕ → ℕ → ℤ) (k : ℕ) (a : ℕ → ℤ) (p r : ℕ) : + value c 0 k a p r = a r := by + simp [value, weight] + +/-- The next array entry uses seven already shared values. This is the exact algebraic +invariant of the mixed-radix native loop. -/ +theorem value_step (c : ℕ → ℕ → ℤ) (j k : ℕ) (a : ℕ → ℤ) (p r : ℕ) + {t : ℕ} (ht : t < 10) : + value c (j + 1) k a (p * 10 + t) r = + ∑ s ∈ range 7, c t s * value c j (k + 1) a p (s * 7 ^ k + r) := by + simp only [value, pow_succ, sum_blocks] + rw [sum_comm] + refine sum_congr rfl fun s hs => ?_ + have hs7 := mem_range.mp hs + simp only [mul_sum] + refine sum_congr rfl fun q _ => ?_ + have hp : (p * 10 + t) / 10 = p := by omega + have hq : (q * 7 + s) / 7 = q := by omega + simp only [weight, hp, hq, Nat.add_mod, Nat.mul_mod_left, Nat.mod_eq_of_lt ht, + Nat.mod_eq_of_lt hs7, zero_add] + have hidx : (q * 7 + s) * 7 ^ k + r = q * (7 ^ k * 7) + (s * 7 ^ k + r) := by ring + rw [hidx] + ring + +/-- Each prefix product has absolute value at most one. -/ +theorem abs_weight_le {c : ℕ → ℕ → ℤ} (hc : ∀ t < 10, ∀ s < 7, |c t s| ≤ 1) + (j p v : ℕ) : |weight c j p v| ≤ 1 := by + induction j generalizing p v with + | zero => simp [weight] + | succ j ih => + rw [weight, abs_mul] + exact (mul_le_mul (ih _ _) (hc _ (Nat.mod_lt _ (by decide)) _ + (Nat.mod_lt _ (by decide))) (abs_nonneg _) (by decide : (0 : ℤ) ≤ 1)).trans + (by norm_num) + +/-- Intermediate transformed values are bounded from the original input values. -/ +theorem abs_value_le {c : ℕ → ℕ → ℤ} (hc : ∀ t < 10, ∀ s < 7, |c t s| ≤ 1) + (j k p r : ℕ) {a : ℕ → ℤ} {A : ℤ} + (ha : ∀ v < 7 ^ j, |a (v * 7 ^ k + r)| ≤ A) : + |value c j k a p r| ≤ (7 ^ j : ℕ) * A := by + exact (abs_sum_mul_le (range (7 ^ j)) (fun v _ => abs_weight_le hc j p v) + (fun v hv => ha v (mem_range.mp hv))).trans_eq (by simp) + +/-- Input bounds suffice for every shared intermediate value, including all depths and all +prefixes. Only the original input segment is used in this estimate. -/ +theorem abs_value_le_of_input {c : ℕ → ℕ → ℤ} + (hc : ∀ t < 10, ∀ s < 7, |c t s| ≤ 1) {L j k : ℕ} (hjk : j + k = L) + (p r : ℕ) (hr : r < 7 ^ k) {a : ℕ → ℤ} {A : ℤ} + (ha : ∀ i < 7 ^ L, |a i| ≤ A) : |value c j k a p r| ≤ (7 ^ j : ℕ) * A := by + apply abs_value_le hc + intro v hv + have hprod := Nat.mul_le_mul_right (7 ^ k) (Nat.succ_le_of_lt hv) + have hidx : v * 7 ^ k + r < 7 ^ j * 7 ^ k := by nlinarith only [hprod, hr] + rw [← pow_add, hjk] at hidx + exact ha _ hidx + +private theorem index_cons {b L : ℕ} (s : Fin b) (u : Fin L → Fin b) : + (finFunctionFinEquiv (Fin.cons s u) : ℕ) = s.val + b * (finFunctionFinEquiv u : ℕ) := by + simp only [finFunctionFinEquiv_apply, Fin.sum_univ_succ, Fin.cons_zero, Fin.cons_succ, + Fin.val_zero, pow_zero, mul_one, Fin.val_succ, pow_succ, Finset.mul_sum] + congr 1 + apply sum_congr rfl + intro i _ + ring + +/-- Digitwise coefficients as a total flat table. Only the first ten rows and seven columns +are read by the transform. -/ +def table (c : Fin 10 → Fin 7 → ℤ) (t s : ℕ) : ℤ := + c ⟨t % 10, Nat.mod_lt _ (by decide)⟩ ⟨s % 7, Nat.mod_lt _ (by decide)⟩ + +/-- Flat prefix weights are exactly the product of the original coordinate coefficients. +`finFunctionFinEquiv` supplies the existing Mathlib digit numbering, least significant first. -/ +theorem weight_index (c : Fin 10 → Fin 7 → ℤ) {L : ℕ} + (τ : Fin L → Fin 10) (u : Fin L → Fin 7) : + weight (table c) L (finFunctionFinEquiv τ) (finFunctionFinEquiv u) = + ∏ i, c (τ i) (u i) := by + induction L with + | zero => simp [weight] + | succ L ih => + rw [← Fin.cons_self_tail τ, ← Fin.cons_self_tail u, index_cons, index_cons, weight] + have ht : ((τ 0).val + 10 * (finFunctionFinEquiv (Fin.tail τ) : ℕ)) / 10 = + (finFunctionFinEquiv (Fin.tail τ) : ℕ) := by omega + have hu : ((u 0).val + 7 * (finFunctionFinEquiv (Fin.tail u) : ℕ)) / 7 = + (finFunctionFinEquiv (Fin.tail u) : ℕ) := by omega + have htmod : ((τ 0).val + 10 * (finFunctionFinEquiv (Fin.tail τ) : ℕ)) % 10 = + (τ 0).val := by omega + have humod : ((u 0).val + 7 * (finFunctionFinEquiv (Fin.tail u) : ℕ)) % 7 = + (u 0).val := by omega + rw [ht, hu, htmod, humod, ih, Fin.prod_univ_succ] + simp only [table, Nat.mod_eq_of_lt (τ 0).isLt, Nat.mod_eq_of_lt (u 0).isLt, + Fin.cons_zero, Fin.cons_succ] + exact mul_comm _ _ + +/-- At the final stage the flat transform is the tensor encoding sum on digit strings. +The input array is supplied explicitly by its values at Mathlib's numbered string positions. -/ +theorem value_final (c : Fin 10 → Fin 7 → ℤ) (L : ℕ) + (a : (Fin L → Fin 7) → ℤ) (arr : ℕ → ℤ) + (harr : ∀ u, arr (finFunctionFinEquiv u) = a u) (τ : Fin L → Fin 10) : + value (table c) L 0 arr (finFunctionFinEquiv τ) 0 = + ∑ u, a u * ∏ i, c (τ i) (u i) := by + simp only [value, pow_zero, mul_one, add_zero] + rw [← Fin.sum_univ_eq_sum_range] + rw [← (finFunctionFinEquiv (m := 7) (n := L)).sum_comp] + apply sum_congr rfl + intro u _ + rw [weight_index, harr, mul_comm] + +/-- The mixed-radix stage fits into a buffer of `10^(j+k)` entries. -/ +theorem stageSize_le (j k : ℕ) : 10 ^ j * 7 ^ k ≤ 10 ^ (j + k) := by + rw [pow_add] + exact Nat.mul_le_mul_left _ (Nat.pow_le_pow_left (by decide : 7 ≤ 10) k) + +/-- Even charging every one of the `L` stages at the maximum array size gives only +`L * 10^L` cells. This is a combinatorial bound; native loop and arithmetic costs are separate. -/ +theorem total_stageSize_le (L : ℕ) : + ∑ j ∈ range L, 10 ^ (j + 1) * 7 ^ (L - (j + 1)) ≤ L * 10 ^ L := by + calc + _ ≤ ∑ _j ∈ range L, 10 ^ L := by + apply sum_le_sum + intro j hj + have hjL : j + 1 ≤ L := by have := mem_range.mp hj; omega + simpa only [Nat.add_sub_of_le hjL] using stageSize_le (j + 1) (L - (j + 1)) + _ = _ := by simp + +end Cslib.Algorithms.MatrixMultiplication.Schonhage.CoordinateTransform diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Cell.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Cell.lean new file mode 100644 index 000000000..d743deb96 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Cell.lean @@ -0,0 +1,129 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Forms +import Mathlib.Tactic.Ring + +/-! +# A native mixed-radix encoding cell + +## Main results + +* `Encoding.exec_cell`: charged index arithmetic, coefficient dispatch, signed linear form, + output store and counter increment for one cell of a shared coordinate transform. + +## References + +The linear forms are from [AlmanWilliams2026, Section 2.4.1]; see `Encoding` for provenance. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- The registers modified by a cell, apart from the nine signed-arithmetic scratch registers. -/ +def CellLocal (i : Fin 32) : Prop := + i = cell ∨ i = block ∨ i = term ∨ i = suffix ∨ i = address ∨ + i = accumulator ∨ i = group ∨ i = temporary + +instance (i : Fin 32) : Decidable (CellLocal i) := + inferInstanceAs (Decidable (i = cell ∨ i = block ∨ i = term ∨ i = suffix ∨ + i = address ∨ i = accumulator ∨ i = group ∨ i = temporary)) + +private theorem cell_preserves (side : Bool) (i : Fin 32) : + 9 ≤ i.val → ¬CellLocal i → (cellBody side).Preserves i := by + decide +kernel +revert + +set_option maxHeartbeats 2000000 in +-- Symbolic execution retains nine instructions' functional register updates; normalizing them +-- and proving the explicit nonlinear address inequalities exceeds the default heartbeat budget. +/-- One output entry, with all index arithmetic and the counter increment charged. A single +ordinary explicit natural-number bound covers the addresses and index products; no fit +condition is imposed on an unspecified intermediate machine state. -/ +theorem exec_cell {w : ℕ} (hw : 0 < w) (side : Bool) (x : Data 32) + (P n s d i : ℕ) (hP : 0 < P) (hnpos : 0 < n) (hi : i < 10 * P * n) + (hsrc : x.regs src = s) (hdst : x.regs dst = d) (hcell : x.regs cell = i) + (hn : x.regs stride = n) (hseven : x.regs seven = 7) + (hten : x.regs ten = 10) (hone : x.regs one = 1) + (v : Fin 7 → ℤ) (B : ℤ) (hB : 0 ≤ B) (hv : ∀ a, |v a| ≤ B) + (hmem : ∀ a, x.mem (s + (i / (10 * n) * 7 + a.val) * n + i % n) = encode (v a)) + (hval : 7 * B < (2 : ℤ) ^ (w - 1)) + (haddr : Fits (w + 2) (s + d + 64 + 64 * P * n)) : + ∃ y, ExecWithin (w + 2) (cellBody side) x y 205 ∧ + y.mem = Function.update x.mem (d + i) + (encode (∑ a, coefficients side ⟨i / n % 10, Nat.mod_lt _ (by decide)⟩ a * v a)) ∧ + y.regs cell = i + 1 ∧ + (∀ a, 9 ≤ a.val → ¬CellLocal a → y.regs a = x.regs a) := by + let p := i / (10 * n) + let t : Fin 10 := ⟨i / n % 10, Nat.mod_lt _ (by decide)⟩ + let r := i % n + have hp : p < P := by + apply (Nat.div_lt_iff_lt_mul (by positivity : 0 < 10 * n)).mpr + nlinarith [hi] + have hr : r < n := Nat.mod_lt _ hnpos + have hn1 : 1 ≤ n := hnpos + have hPn : n ≤ P * n := by nlinarith + have hmul : p * n < P * n := Nat.mul_lt_mul_of_pos_right hp hnpos + have h10n : Fits (w + 2) (10 * n) := haddr.of_le (by nlinarith) + have hpfit : Fits (w + 2) p := haddr.of_le (by nlinarith) + have hdiv : i / n ≤ i := Nat.div_le_self _ _ + have hdivfit : Fits (w + 2) (i / n) := haddr.of_le (by nlinarith) + have htfit : Fits (w + 2) t.val := haddr.of_le (by have := t.isLt; omega) + have hrfit : Fits (w + 2) r := haddr.of_le (by nlinarith) + have hp7 : Fits (w + 2) (p * 7) := haddr.of_le (by nlinarith) + have hp7n : Fits (w + 2) (p * 7 * n) := haddr.of_le (by nlinarith) + have hp7nr : Fits (w + 2) (p * 7 * n + r) := haddr.of_le (by nlinarith) + have hgroup : Fits (w + 2) (s + (p * 7 * n + r)) := haddr.of_le (by nlinarith) + let z := ((((((((x.set temporary (10 * n)).set block p).set term (i / n)).set term t.val).set + suffix r).set group (p * 7)).set group (p * 7 * n)).set group (p * 7 * n + r)).set + group (s + (p * 7 * n + r)) + have hz : ExecWithin (w + 2) cellSetup x z 9 := by + ram_eval [cellSetup, z, p, t, r, hsrc, hcell, hn, hseven, hten, + h10n, hpfit, hdivfit, htfit, hrfit, hp7, hp7n, hp7nr, hgroup, + temporary, block, term, suffix, group, src, cell, stride, seven, ten, Fin.mk.injEq] + have hzterm : z.regs term = t.val := by + simp [z, temporary, block, term, suffix, group] + have hzgroup : z.regs group = s + (p * 7 * n + r) := by simp [z] + have hzn : z.regs stride = n := by + simpa [z, temporary, block, term, suffix, group, stride, Fin.mk.injEq] using hn + have hzmem : ∀ a, z.mem (s + (p * 7 * n + r) + a.val * n) = encode (v a) := by + intro a + have hidx : s + (p * 7 * n + r) + a.val * n = s + (p * 7 + a.val) * n + r := by ring + simpa only [z, Data.set_mem, hidx] using hmem a + have hspan : Fits (w + 2) (s + (p * 7 * n + r) + 7 * n) := + haddr.of_le (by nlinarith) + obtain ⟨u, hu, huacc, humem, hureg⟩ := exec_dispatch hw side (List.finRange 10) t + (List.mem_finRange _) z B (s + (p * 7 * n + r)) n v hzterm hzgroup hzn hzmem + hB hv hval hspan (haddr.of_le (by omega)) + have zucell : u.regs cell = i := by + rw [hureg cell (by decide) (by decide) (by decide) (by decide)] + simpa [z, temporary, block, term, suffix, group, cell, Fin.mk.injEq] using hcell + have zudst : u.regs dst = d := by + rw [hureg dst (by decide) (by decide) (by decide) (by decide)] + simpa [z, temporary, block, term, suffix, group, dst, Fin.mk.injEq] using hdst + have zuone : u.regs one = 1 := by + rw [hureg one (by decide) (by decide) (by decide) (by decide)] + simpa [z, temporary, block, term, suffix, group, one, Fin.mk.injEq] using hone + have hdfit : Fits (w + 2) (d + i) := haddr.of_le (by nlinarith) + have hinc : Fits (w + 2) (i + 1) := haddr.of_le (by nlinarith) + let y := ((u.set address (d + i)).write (d + i) (u.regs accumulator)).set cell (i + 1) + have hy : ExecWithin (w + 2) cellFinish u y 3 := by + ram_eval [cellFinish, y, zucell, zudst, zuone, hdfit, hinc, + address, cell, accumulator, dst, one, Fin.mk.injEq] + have he : ExecWithin (w + 2) (cellBody side) x y 205 := by + simpa only [cellBody, List.length_finRange] using (hz.seq (hu.seq hy)).mono + (by decide : 9 + ((3 * 10 + 163) + 3) ≤ 205) + refine ⟨y, he, ?_, ?_, fun a ha hl => he.regs_eq (cell_preserves side a ha hl)⟩ + · simp only [y, Data.set_mem, Data.write_mem, humem, z, huacc] + rfl + · simp [y] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Controller.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Controller.lean new file mode 100644 index 000000000..04eabe5c3 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Controller.lean @@ -0,0 +1,250 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Header +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Meaning +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Copy +import Mathlib.Tactic.Ring + +/-! +# Alternating buffers for the shared native encoder + +## Main definitions + +* `Encoding.destination`, `Encoding.source`: the alternating buffer addresses. +* `Encoding.Progress`: the mixed-radix array and frame invariant after a stage. + +## References + +The shared encodings are from [AlmanWilliams2026, Section 2.4.1]; see `Encoding` for provenance. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open CoordinateTransform + +/-- Destination of the next coordinate transform; always one of the two fixed buffers. -/ +def destination (L : ℕ) : ℕ → ℕ + | 0 => outputBase L + | j + 1 => if destination L j = outputBase L then scratchBase L else outputBase L + +/-- Source of the next coordinate transform; initially the preserved input segment. -/ +def source (L : ℕ) : ℕ → ℕ + | 0 => 2 + | j + 1 => destination L j + +/-- A destination is either the fixed output block or the scratch block. -/ +theorem destination_cases (L j : ℕ) : + destination L j = outputBase L ∨ destination L j = scratchBase L := by + cases j with + | zero => exact Or.inl rfl + | succ j => unfold destination; split <;> simp + +/-- After the initial transform, the source is also one of the two buffers. -/ +theorem source_cases {L j : ℕ} (hj : 0 < j) : + source L j = outputBase L ∨ source L j = scratchBase L := by + cases j with + | zero => omega + | succ j => exact destination_cases L j + +/-- The two alternating buffer addresses differ at every stage. -/ +theorem source_ne_destination (L j : ℕ) : source L j ≠ destination L j := by + have hpos : 0 < 10 ^ L := by positivity + have h7pos : 0 < 7 ^ L := by positivity + cases j with + | zero => simp only [source, destination, outputBase]; omega + | succ j => + rcases destination_cases L j with h | h + · simp only [source, destination, h, ↓reduceIte] + unfold scratchBase + omega + · have hne : scratchBase L ≠ outputBase L := by unfold scratchBase; omega + simp only [source, destination, h, hne, ↓reduceIte] + exact hne + +/-- Both current addresses are bounded by the end of the second buffer. -/ +theorem buffer_address_le (L j : ℕ) : + source L j ≤ 2 + 2 * 10 ^ L ∧ destination L j ≤ 2 + 2 * 10 ^ L := by + have h7 := Nat.pow_le_pow_left (by decide : 7 ≤ 10) L + have hd := destination_cases L j + have hs : source L j = 2 ∨ source L j = outputBase L ∨ source L j = scratchBase L := by + cases j with + | zero => exact Or.inl rfl + | succ j => exact Or.inr (destination_cases L j) + simp only [scratchBase, outputBase] at hs hd + constructor <;> omega + +/-- Arrays stored at consecutive buffers are disjoint, in either order. The initial input +ends exactly at the first output address. -/ +theorem buffers_separated {L j k N : ℕ} (hjk : j + k = L) + (hN : N ≤ 10 ^ L) : + source L j + 10 ^ j * 7 ^ k ≤ destination L j ∨ + destination L j + N ≤ source L j := by + have hsize : 10 ^ j * 7 ^ k ≤ 10 ^ L := by simpa only [hjk] using stageSize_le j k + cases j with + | zero => + left + simp only [Nat.zero_add] at hjk + simp [source, destination, outputBase, hjk] + | succ j => + have hs := source_cases (L := L) (j := j + 1) (by omega) + have hd := destination_cases L (j + 1) + have hne := source_ne_destination L (j + 1) + unfold scratchBase at hs hd + omega + +/-- The shared-array invariant at depth `j`. All fields describe ordinary registers or +mathematical memory values. Execution, timing and width are proved separately. -/ +structure Progress (side : Bool) (L j : ℕ) (arr : ℕ → ℤ) (initial : ℕ → ℕ) + (y : Data 32) : Prop where + /-- Fixed parameters retained across coordinate stages. -/ + fixed : y.regs depth = L ∧ y.regs capacity = 10 ^ L ∧ + y.regs output = outputBase L ∧ y.regs scratch = scratchBase L ∧ + y.regs one = 1 ∧ y.regs seven = 7 ∧ y.regs ten = 10 + /-- Number of transformed coordinates. -/ + stage : y.regs Encoding.stage = j + /-- Number of transformed prefixes. -/ + prefixes : y.regs Encoding.prefixes = 10 ^ j + /-- Original suffix length in the current source. -/ + stride : y.regs Encoding.stride = 7 ^ (L - j) + /-- Current source address. -/ + src : y.regs Encoding.src = source L j + /-- Current destination address. -/ + dst : y.regs Encoding.dst = destination L j + /-- The source holds exactly the shared prefix sums. -/ + values : ∀ p < 10 ^ j, ∀ r < 7 ^ (L - j), + y.mem (source L j + p * 7 ^ (L - j) + r) = + encode (value (table (coefficients side)) j (L - j) arr p r) + /-- Input, headers and all cells outside both work buffers are preserved. -/ + frame : ∀ a, ¬(outputBase L ≤ a ∧ a < scratchBase L + 10 ^ L) → y.mem a = initial a + +/-- The actual header initialization establishes the zero-stage invariant from the original +flat input segment. No scratch values or preinitialized coefficient tables are required. -/ +theorem progress_zero (side : Bool) (L : ℕ) (arr : ℕ → ℤ) (x y : Data 32) + (hy : SetupPost L x y) (hinput : ∀ i < 7 ^ L, x.mem (2 + i) = encode (arr i)) : + Progress side L 0 arr x.mem y := by + refine ⟨⟨hy.depth, hy.capacity, hy.output, hy.scratch, hy.one, hy.seven, hy.ten⟩, + hy.stage, hy.prefixes, ?_, hy.src, hy.dst, ?_, ?_⟩ + · simpa only [Nat.sub_zero] using hy.stride + · intro p hp r hr + have hp0 : p = 0 := by simpa only [pow_zero] using Nat.eq_zero_of_le_zero (by omega : p ≤ 0) + subst p + rw [hy.mem, value_zero] + simpa only [source, Nat.sub_zero, Nat.zero_mul, Nat.add_zero] using hinput r hr + · intro a _ + rw [hy.mem] + +/-- One proved native stage advances the mixed-radix invariant. The signed magnitude bound +comes from the original input and the number of coordinates already transformed. -/ +theorem progress_step {w L j : ℕ} (hw : 0 < w) (hj : j < L) (side : Bool) + (arr : ℕ → ℤ) (initial : ℕ → ℕ) (x : Data 32) (hx : Progress side L j arr initial x) + (A : ℤ) (hA : 0 ≤ A) (ha : ∀ i < 7 ^ L, |arr i| ≤ A) + (hmag : (7 ^ L : ℕ) * A < (2 : ℤ) ^ (w - 1)) + (haddr : Fits (w + 2) (addressBound L)) : + ∃ y, ExecWithin (w + 2) (stageBody side) x y (207 * 10 ^ L + 12) ∧ + Progress side L (j + 1) arr initial y := by + let k := L - (j + 1) + let n := 7 ^ k + let P := 10 ^ j + have hn : 0 < n := by dsimp only [n]; positivity + have hP : 0 < P := by dsimp only [P]; positivity + have hk : L - j = k + 1 := by omega + have hjk : j + (k + 1) = L := by omega + have hjk' : j + 1 + k = L := by omega + have hstride : 7 ^ (L - j) = 7 * n := by rw [hk, pow_succ'] + have hN : 10 * P * n ≤ 10 ^ L := by + have h := stageSize_le (j + 1) k + simpa only [hjk', pow_succ', P, n] using h + have hcoeff : ∀ t < 10, ∀ s < 7, |table (coefficients side) t s| ≤ 1 := by + intro t _ s _ + unfold table coefficients + cases side + · exact abs_phi_le_one _ _ + · exact abs_psi_le_one _ _ + let v (i : ℕ) (a : Fin 7) : ℤ := value (table (coefficients side)) j (k + 1) arr + (i / (10 * n)) (a.val * n + i % n) + let B : ℤ := (7 ^ j : ℕ) * A + have hB : 0 ≤ B := mul_nonneg (by positivity) hA + have hsuffix (i : ℕ) (a : Fin 7) : a.val * n + i % n < 7 ^ (k + 1) := by + have hm := Nat.mul_le_mul_right n (Nat.succ_le_of_lt a.isLt) + have hr := Nat.mod_lt i hn + rw [pow_succ'] + change a.val * n + i % n < 7 * n + nlinarith only [hm, hr] + have hv : ∀ i < 10 * P * n, ∀ a, |v i a| ≤ B := by + intro i _ a + exact abs_value_le_of_input hcoeff hjk _ _ (hsuffix i a) ha + have hload : ∀ i < 10 * P * n, ∀ a, + x.mem (source L j + (i / (10 * n) * 7 + a.val) * n + i % n) = encode (v i a) := by + intro i hi a + have hp : i / (10 * n) < 10 ^ j := by + apply (Nat.div_lt_iff_lt_mul (by positivity : 0 < 10 * n)).mpr + dsimp only [P] at hi + nlinarith only [hi] + have hidx : source L j + (i / (10 * n) * 7 + a.val) * n + i % n = + source L j + i / (10 * n) * 7 ^ (L - j) + (a.val * n + i % n) := by + rw [hstride] + ring + rw [hidx, hx.values _ hp _ (by rw [hk]; exact hsuffix i a)] + rw [hk] + have hvalue : 7 * B < (2 : ℤ) ^ (w - 1) := by + have hpow : 7 ^ (j + 1) ≤ 7 ^ L := Nat.pow_le_pow_right (by decide) (by omega) + have hpowZ : ((7 ^ (j + 1) : ℕ) : ℤ) ≤ (7 ^ L : ℕ) := by exact_mod_cast hpow + have hprod := mul_le_mul_of_nonneg_right hpowZ hA + simpa only [Nat.pow_succ', Nat.cast_mul, Nat.cast_ofNat, mul_assoc, B] using + hprod.trans_lt hmag + have hbound := buffer_address_le L j + have hcap : 1 ≤ 10 ^ L := Nat.one_le_pow L 10 (by decide) + have hnative : Fits (w + 2) (source L j + destination L j + 64 + 64 * P * n) := + haddr.of_le (by unfold addressBound; nlinarith only [hbound.1, hbound.2, hN, hcap]) + have hseparate : source L j + 7 * P * n ≤ destination L j ∨ + destination L j + 10 * P * n ≤ source L j := by + have h := buffers_separated (k := L - j) (by omega : j + (L - j) = L) hN + simpa only [hstride, P, Nat.mul_assoc, Nat.mul_comm, Nat.mul_left_comm] using h + obtain ⟨y, he, hm, hf, hsrc, hdst, hstr, hp, hstage, hreg⟩ := exec_stageBody hw side x + P n (source L j) (destination L j) j (outputBase L) (scratchBase L) hP hn hx.src hx.dst + hx.stage (by simpa only [hstride] using hx.stride) hx.prefixes hx.fixed.2.2.2.2.2.1 + hx.fixed.2.2.2.2.2.2 hx.fixed.2.2.2.2.1 hx.fixed.2.2.1 hx.fixed.2.2.2.1 v B hB hv hload + hvalue hnative (haddr.of_le (by unfold addressBound; omega)) hseparate + refine ⟨y, he.mono (by omega), ?_⟩ + have hr (a : Fin 32) (ha : 9 ≤ a.val) (hl : ¬StageLocal a) : y.regs a = x.regs a := hreg a ha hl + refine ⟨?_, hstage, ?_, ?_, hsrc, hdst, ?_, ?_⟩ + · rcases hx.fixed with ⟨hd, hc, ho, hs, h1, h7, h10⟩ + exact ⟨(hr depth (by decide) (by decide)).trans hd, + (hr capacity (by decide) (by decide)).trans hc, + (hr output (by decide) (by decide)).trans ho, + (hr scratch (by decide) (by decide)).trans hs, + (hr one (by decide) (by decide)).trans h1, + (hr seven (by decide) (by decide)).trans h7, + (hr ten (by decide) (by decide)).trans h10⟩ + · simpa only [P, pow_succ'] using hp + · exact hstr + · intro p hpp r hrr + have hrr' : r < n := hrr + have hpp' : p < 10 * P := by simpa only [pow_succ', P] using hpp + have hprod := Nat.mul_le_mul_right n (Nat.succ_le_of_lt hpp') + have hi : p * n + r < 10 * P * n := by nlinarith only [hprod, hrr'] + have hdiv : (p * n + r) / n = p := by + rw [Nat.mul_comm p n, Nat.mul_add_div hn, Nat.div_eq_of_lt hrr', Nat.add_zero] + have hmod : (p * n + r) % n = r := Nat.mul_add_mod_of_lt hrr' + have hvout := hm (p * n + r) hi + rw [show stageResult side n v (p * n + r) = value (table (coefficients side)) + (j + 1) k arr ((p * n + r) / n) ((p * n + r) % n) from stageResult_value side j k _ arr, + hdiv, hmod] at hvout + simpa only [source, n, k, Nat.add_assoc] using hvout + · intro a haout + rw [hf a ?_] + · exact hx.frame a haout + · rcases destination_cases L j with hd | hd <;> rw [hd] <;> unfold scratchBase at * <;> omega + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Copy.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Copy.lean new file mode 100644 index 000000000..8ada70338 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Copy.lean @@ -0,0 +1,48 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding + +/-! +# Copying the final encoding to its fixed output block + +## Main results + +* `Encoding.exec_copyOut`: reuse the native array copy, including equal blocks and both + possible disjoint address orders, with its charged `7n+4` bound. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding + +open Cslib.WordRAM + +/-- The final copy uses seven instructions per output word plus four instructions of setup +and loop termination. Equal source/output blocks are permitted, so odd and even depths use +the same fixed program. All other registers retain their values. -/ +theorem exec_copyOut {w : ℕ} (x : Data 32) (h1 : Fits w 1) + (hs : Fits w (x.regs src + x.regs capacity)) + (hd : Fits w (x.regs output + x.regs capacity)) + (hdisj : x.regs src + x.regs capacity ≤ x.regs output ∨ + x.regs output + x.regs capacity ≤ x.regs src ∨ x.regs src = x.regs output) : + ∃ y, ExecWithin w copyOut x y (7 * x.regs capacity + 4) ∧ + y.mem = ArrayCopy.copyPrefix x.mem (x.regs src) (x.regs output) (x.regs capacity) ∧ + (∀ i, i ≠ cell → i ≠ address → i ≠ accumulator → i ≠ one → y.regs i = x.regs i) := by + obtain ⟨z, hz, hm, _, _, _⟩ := ArrayCopy.exec_of_separated_or_eq (x.comap copyRegisters) + h1 hs hd hdisj + have he := hz.rename copyRegisters_injective + refine ⟨z.extend copyRegisters x, he, hm, ?_⟩ + intro i hc ha hv ho + apply he.regs_eq + have hp : ∀ i : Fin 32, i ≠ cell → i ≠ address → i ≠ accumulator → i ≠ one → + copyOut.Preserves i := by decide + exact hp i hc ha hv ho + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Encoding.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Encoding.lean new file mode 100644 index 000000000..10afd12bb --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Encoding.lean @@ -0,0 +1,244 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Full +public import Complexity.Computability.WordRAM.Signed.Arithmetic +public import Complexity.Computability.WordRAM.Copy + +/-! +# Native shared tensor encoding + +The two fixed programs use unsigned addresses and counters, and sign-and-magnitude encoded +integer entries. Address 1 holds the depth; the `7^L` encoded entries begin at address 2. +The ordinary `State.init` length header is at address 0. Output and scratch are consecutive +blocks of `10^L` cells immediately after the input. Each coordinate transform reads the +previous stage and writes the other buffer. No initialization of unread scratch is needed. +A final charged copy puts the result in the first buffer, also at depth zero. + +The coordinate transform is a new iterative implementation of the linear forms in +[AlmanWilliams2026, Section 2.4.1]. The coefficients and mathematical encoding are selectively +adapted from Anthropic's formal-math revision `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`, +[`Step`](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Programs/Sec2/Theorem5/Encode/Step.lean) +and [`Recursion`](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Programs/Sec2/Theorem5/Encode/Recursion.lean). The source's +recursive machine and execution semantics are not used. + +## Main definitions + +* `Encoding.program`: fixed native code for either coefficient table. +* `Encoding.input`: concrete input for `State.init`, including the depth header. +* `Encoding.outputBase`, `Encoding.scratchBase`: disjoint output and scratch blocks. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Seven variable digits: the three outer variables, then the four inner variables. -/ +def leftAt (s : Fin 7) : LeftVar := + if h : s.val < 3 then .x ⟨s.val, h⟩ + else .p ⟨(s.val - 3) / 2, by omega⟩ ⟨(s.val - 3) % 2, Nat.mod_lt _ (by decide)⟩ + +/-- Right-variable digits use the same order as the left-variable digits. -/ +def rightAt (s : Fin 7) : RightVar := + if h : s.val < 3 then .y ⟨s.val, h⟩ + else .q ⟨(s.val - 3) / 2, by omega⟩ ⟨(s.val - 3) % 2, Nat.mod_lt _ (by decide)⟩ + +/-- Row-major order on the nine `P` terms, followed by `P0`. -/ +def termAt (t : Fin 10) : Term := + if h : t.val < 9 then .P ⟨t.val / 3, by omega⟩ ⟨t.val % 3, Nat.mod_lt _ (by decide)⟩ + else .P0 + +/-- Explicit numbering of the seven left variables. -/ +def leftDigits : Fin 7 ≃ LeftVar where + toFun := leftAt + invFun + | .x i => ⟨i.val, by omega⟩ + | .p i j => ⟨3 + 2 * i.val + j.val, by omega⟩ + left_inv := by decide + right_inv := by decide + +/-- Explicit numbering of the seven right variables. -/ +def rightDigits : Fin 7 ≃ RightVar where + toFun := rightAt + invFun + | .y j => ⟨j.val, by omega⟩ + | .q i j => ⟨3 + 2 * i.val + j.val, by omega⟩ + left_inv := by decide + right_inv := by decide + +/-- Explicit numbering of all ten terms. -/ +def termDigits : Fin 10 ≃ Term where + toFun := termAt + invFun + | .P i j => ⟨3 * i.val + j.val, by omega⟩ + | .P0 => 9 + left_inv := by decide + right_inv := by decide + +/-- The left and right coefficient tables, with digits made explicit. -/ +def coefficients (rightSide : Bool) (t : Fin 10) (s : Fin 7) : ℤ := + if rightSide then psi (termAt t) (rightAt s) else phi (termAt t) (leftAt s) + +/-- Every actual coefficient is zero, one or minus one. -/ +theorem coefficients_values (side : Bool) (t : Fin 10) (s : Fin 7) : + coefficients side t s = 0 ∨ coefficients side t s = 1 ∨ coefficients side t s = -1 := by + decide +revert + +/-- The input block ends where the output block begins. -/ +def outputBase (L : ℕ) : ℕ := 2 + 7 ^ L + +/-- The scratch block immediately follows the entire output block. -/ +def scratchBase (L : ℕ) : ℕ := outputBase L + 10 ^ L + +/-- Concrete input: depth header followed by the signed input entries in flat order. +The caller encodes each integer with `Signed.encode` before passing the list to `State.init`. +The native unsigned length header is installed by `State.init` itself. -/ +def input (L : ℕ) (a : Fin (7 ^ L) → ℤ) : List ℕ := + L :: List.ofFn (fun i => encode (a i)) + +/-- The concrete left-array input, using Mathlib's base-seven string numbering. -/ +def inputL {L : ℕ} (a : LeftStr L → ℤ) : List ℕ := + input L fun i => a fun ℓ => leftAt (finFunctionFinEquiv.symm i ℓ) + +/-- The concrete right-array input, in exactly the same flat memory layout. -/ +def inputR {L : ℕ} (a : RightStr L → ℤ) : List ℕ := + input L fun i => a fun ℓ => rightAt (finFunctionFinEquiv.symm i ℓ) + +/-- All thirty-two registers are fixed independently of depth and width. Registers 0–8 +are precisely the existing signed-arithmetic workspace. -/ +abbrev depth : Fin 32 := 9 +@[inherit_doc depth] abbrev inputSize : Fin 32 := 10 +@[inherit_doc depth] abbrev capacity : Fin 32 := 11 +@[inherit_doc depth] abbrev src : Fin 32 := 12 +@[inherit_doc depth] abbrev dst : Fin 32 := 13 +@[inherit_doc depth] abbrev output : Fin 32 := 14 +@[inherit_doc depth] abbrev scratch : Fin 32 := 15 +@[inherit_doc depth] abbrev stride : Fin 32 := 16 +@[inherit_doc depth] abbrev prefixes : Fin 32 := 17 +@[inherit_doc depth] abbrev stage : Fin 32 := 18 +@[inherit_doc depth] abbrev cell : Fin 32 := 19 +@[inherit_doc depth] abbrev block : Fin 32 := 20 +@[inherit_doc depth] abbrev term : Fin 32 := 21 +@[inherit_doc depth] abbrev suffix : Fin 32 := 22 +@[inherit_doc depth] abbrev address : Fin 32 := 23 +@[inherit_doc depth] abbrev accumulator : Fin 32 := 24 +@[inherit_doc depth] abbrev one : Fin 32 := 25 +@[inherit_doc depth] abbrev seven : Fin 32 := 26 +@[inherit_doc depth] abbrev ten : Fin 32 := 27 +@[inherit_doc depth] abbrev group : Fin 32 := 28 +@[inherit_doc depth] abbrev temporary : Fin 32 := 29 +@[inherit_doc depth] abbrev stageSize : Fin 32 := 30 + +/-- One coefficient in `{0, 1, -1}` times one array entry. Zero coefficients cause no load. +Nonzero coefficients use the proved native signed addition or subtraction block. -/ +def summand (s : Fin 7) (c : ℤ) : Code .standard 32 := + if c = 0 then ram[ accumulator ← accumulator ] + else .seq (ram[ + temporary ← #s.val; + address ← temporary * stride; address ← group + address; + right ← *address; left ← accumulator ]) + (.seq (if c = 1 then add else subtract) (ram[ accumulator ← result ])) + +/-- A finite unrolling of the seven coefficients. This recursion constructs code; it does +not recurse on the input depth. -/ +def sumCode : List (Fin 7 × ℤ) → Code .standard 32 + | [] => ram[ accumulator ← accumulator ] + | (s, c) :: rest => .seq (summand s c) (sumCode rest) + +/-- One linear form, including accumulator initialization. -/ +def form (side : Bool) (t : Fin 10) : Code .standard 32 := + .seq (ram[ accumulator ← #0 ]) + (sumCode (List.ofFn fun s => (s, coefficients side t s))) + +/-- A fixed finite dispatch on the ten possible term digits. -/ +def dispatch (side : Bool) : List (Fin 10) → Code .standard 32 + | [] => ram[ accumulator ← #0 ] + | t :: rest => ram[ + temporary ← #t.val; + if term = temporary then [ ⟪form side t⟫ ] else [ ⟪dispatch side rest⟫ ] ] + +/-- Decode one flat output index into its mixed-radix coordinates and source group. -/ +def cellSetup : Code .standard 32 := ram[ + temporary ← ten * stride; + block ← cell / temporary; + term ← cell / stride; term ← term % ten; + suffix ← cell % stride; + group ← block * seven; group ← group * stride; + group ← group + suffix; group ← src + group ] + +/-- Store the computed entry and advance the flat index. -/ +def cellFinish : Code .standard 32 := ram[ + address ← dst + cell; *address ← accumulator; + cell ← cell + one ] + +/-- One output cell. The flat index is `(prefix * 10 + term) * stride + suffix`; +its seven source addresses are `(prefix * 7 + s) * stride + suffix`. -/ +def cellBody (side : Bool) : Code .standard 32 := + .seq cellSetup (.seq (dispatch side (List.finRange 10)) cellFinish) + +/-- Divide the suffix stride and compute the next stage size. -/ +def stageHead : Code .standard 32 := ram[ + stride ← stride / seven; + stageSize ← prefixes * ten; stageSize ← stageSize * stride; + cell ← #0 ] + +/-- Alternate the two buffers and advance the prefix and stage counters. -/ +def stageTail : Code .standard 32 := ram[ + src ← dst; + if dst = output then [ dst ← scratch ] else [ dst ← output ]; + prefixes ← prefixes * ten; stage ← stage + one ] + +/-- Transform the next coordinate, then alternate the output buffers. -/ +def stageBody (side : Bool) : Code .standard 32 := + .seq stageHead (.seq (.loop (.lt cell stageSize) (cellBody side)) stageTail) + +/-- Load the actual depth header and initialize the power counters. -/ +def setupHead : Code .standard 32 := ram[ + one ← #1; seven ← #7; ten ← #10; + address ← #1; depth ← *address; + inputSize ← #1; capacity ← #1; stage ← #0 ] + +/-- One multiplication by each radix and one counter increment. -/ +def powerBody : Code .standard 32 := ram[ + inputSize ← inputSize * seven; capacity ← capacity * ten; stage ← stage + one ] + +/-- Compute the concrete buffer addresses from the powers and initialize the first stage. -/ +def setupTail : Code .standard 32 := ram[ + temporary ← #2; output ← temporary + inputSize; scratch ← output + capacity; + src ← #2; dst ← output; stride ← inputSize; prefixes ← #1; stage ← #0 ] + +/-- Read the depth and compute both powers in a charged native loop. -/ +def setup : Code .standard 32 := + .seq setupHead (.seq (.loop (.lt stage depth) powerBody) setupTail) + +/-- Reuse the existing seven-register array copy inside the encoder's register file. -/ +def copyRegisters : Fin 7 → Fin 32 := ![src, output, capacity, cell, address, accumulator, one] + +/-- The seven copy registers are distinct. -/ +theorem copyRegisters_injective : Function.Injective copyRegisters := by decide + +/-- A charged copy into the fixed output block, permitting disjoint blocks in either order +or identical source and destination. -/ +def copyOut : Code .standard 32 := ArrayCopy.program.rename copyRegisters + +/-- The complete fixed native encoder, excluding its final halt. The returned `output` +register identifies the first output cell; the `capacity` register holds its length. -/ +def program (side : Bool) : Code .standard 32 := ram[ + ⟪setup⟫; + while stage < depth do [ ⟪stageBody side⟫ ]; + ⟪copyOut⟫ ] + +/-- The code performs no word-width queries. -/ +theorem program_readsWidth (side : Bool) : ¬(program side).ReadsWidth := by + cases side <;> decide + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Exec.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Exec.lean new file mode 100644 index 000000000..1d80c56e6 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Exec.lean @@ -0,0 +1,136 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Controller +import Mathlib.Tactic.Ring + +/-! +# Complete charged execution of the shared encoder + +## Main results + +* `Encoding.exec_coordinates`: all shared coordinate transforms, with charged loop control. +* `Encoding.exec`: complete initialization, transforms and fixed-output copy with an explicit + `240 * (L+1) * 10^L` native instruction bound. + +The code is directly over the standard unsigned WordRAM. Its signed add/subtract blocks use +two extra target bits and respectively at most fifteen/seventeen actual instructions. There +is no whole-program signed compiler in this construction and no uncharged simulation premise. + +## References + +The encodings are from [AlmanWilliams2026, Section 2.4.1]; see `Encoding` for provenance. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open CoordinateTransform + +/-- Explicit total instruction bound, including header preparation, all index arithmetic, +stage and cell loop control, and the final copy. A final halt costs one additional instruction. -/ +def timeBound (L : ℕ) : ℕ := 240 * (L + 1) * 10 ^ L + +/-- Every coordinate stage is executed natively. Charging every stage at the maximum buffer +size gives `L * (207 * 10^L + 14) + 2` instructions, including both outer loop controls. -/ +theorem exec_coordinates {w L : ℕ} (hw : 0 < w) (side : Bool) (arr : ℕ → ℤ) + (initial : ℕ → ℕ) (x : Data 32) (hx : Progress side L 0 arr initial x) + (A : ℤ) (hA : 0 ≤ A) (ha : ∀ i < 7 ^ L, |arr i| ≤ A) + (hmag : (7 ^ L : ℕ) * A < (2 : ℤ) ^ (w - 1)) + (haddr : Fits (w + 2) (addressBound L)) : + ∃ y, ExecWithin (w + 2) (.loop (.lt stage depth) (stageBody side)) x y + (L * (207 * 10 ^ L + 14) + 2) ∧ Progress side L L arr initial y := by + let I (y : Data 32) : Prop := ∃ j, j ≤ L ∧ Progress side L j arr initial y + have hbody (y : Data 32) (hy : I y) (hc : (Cond.lt stage depth).Holds y) : + ∃ z, ExecWithin (w + 2) (stageBody side) y z (207 * 10 ^ L + 12) ∧ I z ∧ + L - z.regs stage < L - y.regs stage := by + obtain ⟨j, hj, hprogress⟩ := hy + have hjL : j < L := by simpa only [Cond.holds_lt, hprogress.stage, hprogress.fixed.1] using hc + obtain ⟨z, he, hz⟩ := progress_step hw hjL side arr initial y hprogress A hA ha hmag haddr + exact ⟨z, he, ⟨j + 1, by omega, hz⟩, by rw [hprogress.stage, hz.stage]; omega⟩ + obtain ⟨y, he, ⟨j, hj, hy⟩, hc⟩ := ExecWithin.loop_invariant I + (fun y => L - y.regs stage) hbody ⟨0, Nat.zero_le _, hx⟩ + have hjL : j = L := by + simp only [Cond.holds_lt, hy.stage, hy.fixed.1] at hc + omega + subst j + refine ⟨y, ?_, hy⟩ + simpa only [hx.stage, Nat.sub_zero, Nat.add_assoc] using he + +/-- The encoder runs from a concrete flat memory layout and preserves every cell outside +its output/scratch region. Only the original input magnitude and explicit address bounds are +assumed; every intermediate signed arithmetic fit is derived in the coordinate-stage proof. -/ +theorem exec {w L : ℕ} (hw : 0 < w) (side : Bool) (arr : ℕ → ℤ) (x : Data 32) + (hheader : x.mem 1 = L) (hinput : ∀ i < 7 ^ L, x.mem (2 + i) = encode (arr i)) + (A : ℤ) (hA : 0 ≤ A) (ha : ∀ i < 7 ^ L, |arr i| ≤ A) + (hmag : (7 ^ L : ℕ) * A < (2 : ℤ) ^ (w - 1)) + (haddr : Fits (w + 2) (addressBound L)) : + ∃ y, ExecWithin (w + 2) (program side) x y (timeBound L) ∧ + (∀ p < 10 ^ L, y.mem (outputBase L + p) = + encode (value (table (coefficients side)) L 0 arr p 0)) ∧ + (∀ a, ¬(outputBase L ≤ a ∧ a < scratchBase L + 10 ^ L) → y.mem a = x.mem a) ∧ + y.regs output = outputBase L ∧ y.regs capacity = 10 ^ L := by + obtain ⟨z, hz, hsetup⟩ := exec_setup L x hheader haddr + have hzero := progress_zero side L arr x z hsetup hinput + obtain ⟨u, hu, hp⟩ := exec_coordinates hw side arr x.mem z hzero A hA ha hmag haddr + have hsource := hp.src + have hout := hp.fixed.2.2.1 + have hcap := hp.fixed.2.1 + have hcap1 : 1 ≤ 10 ^ L := Nat.one_le_pow L 10 (by decide) + have hsfit : Fits (w + 2) (u.regs src + u.regs capacity) := by + rw [hsource, hcap] + apply haddr.of_le + have hb := (buffer_address_le L L).1 + unfold addressBound + omega + have hdfit : Fits (w + 2) (u.regs output + u.regs capacity) := by + rw [hout, hcap] + apply haddr.of_le + have h7 := Nat.pow_le_pow_left (by decide : 7 ≤ 10) L + unfold addressBound outputBase + omega + have hdisj : u.regs src + u.regs capacity ≤ u.regs output ∨ + u.regs output + u.regs capacity ≤ u.regs src ∨ u.regs src = u.regs output := by + rw [hsource, hout, hcap] + by_cases hL : L = 0 + · rcases hL with rfl + left + decide + · rcases source_cases (L := L) (j := L) (by omega) with h | h + · exact Or.inr (Or.inr h) + · right + left + rw [h] + rfl + obtain ⟨y, hy, hymem, hyr⟩ := exec_copyOut u (haddr.of_le (by unfold addressBound; omega)) + hsfit hdfit hdisj + have he : ExecWithin (w + 2) (program side) x y (timeBound L) := by + have hwhole := hz.seq (hu.seq hy) + apply hwhole.mono + rw [hcap] + unfold timeBound + have hLcap : L ≤ L * 10 ^ L := by nlinarith only [Nat.mul_le_mul_left L hcap1] + nlinarith only [hcap1, hLcap] + refine ⟨y, he, ?_, ?_, ?_, ?_⟩ + · intro p hpp + rw [hymem, hsource, hout, hcap, ArrayCopy.copyPrefix_read hpp] + have h := hp.values p hpp 0 (by simp) + simpa only [Nat.sub_self, pow_zero, Nat.mul_one, Nat.add_zero] using h + · intro a haout + rw [hymem, hsource, hout, hcap, ArrayCopy.copyPrefix_outside ?_] + · exact hp.frame a haout + · unfold scratchBase at haout + omega + · exact (hyr output (by decide) (by decide) (by decide) (by decide)).trans hout + · exact (hyr capacity (by decide) (by decide) (by decide) (by decide)).trans hcap + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Flat.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Flat.lean new file mode 100644 index 000000000..9ced15aa5 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Flat.lean @@ -0,0 +1,88 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Input + +/-! +# Flat standard-WordRAM clients for the complete encoders + +## Main results + +* `Encoding.runs_left`, `Encoding.runs_right`: actual flat runs from `State.init`, the complete + output memory block, preserved outside frame, and a charged final halt. +* `Encoding.program_length_le`: one finite code-size bound independent of depth and width. + +The final halt returns the output block's address. Its length is `10^L` and its cells contain +the canonical signed encodings of the public mathematical values. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +set_option maxRecDepth 4096 in +-- Kernel reduction flattens the finite instruction list, including each signed arithmetic block. +/-- A single code-size bound for both fixed encoders, including the final halt. -/ +theorem program_length_le (side : Bool) : ((program side).program output).length ≤ 4096 := by + cases side <;> decide + +/-- Every one of the `10^L` flat output cells corresponds to exactly one original leaf. -/ +theorem leafIndex_bijective (L : ℕ) : Function.Bijective (leafIndex (L := L)) := by + have h : Function.Bijective (fun τ : Leaf L => fun i => termDigits.symm (τ i)) := + (Equiv.piCongrRight fun _ : Fin L => termDigits.symm).bijective + change Function.Bijective (fun τ : Leaf L => + finFunctionFinEquiv (fun i => termDigits.symm (τ i))) + exact finFunctionFinEquiv.bijective.comp h + +/-- Actual flat native left-encoding execution. The prefix exposes every output memory cell; +the same run then halts at its fixed final instruction, charging one more instruction. -/ +theorem runs_left {w L : ℕ} (hw : 0 < w) (a : LeftStr L → ℤ) (A : ℤ) + (hA : 0 ≤ A) (ha : ∀ u, |a u| ≤ A) + (hmag : (7 ^ L : ℕ) * A < (2 : ℤ) ^ (w - 1)) + (haddr : Fits (w + 2) (addressBound L)) : + InputFits (w + 2) (inputL a) ∧ + ∃ y n, n ≤ timeBound L ∧ + Runs (w + 2) ((program false).program output) (State.init (inputL a)) + ⟨(program false).size, y⟩ n ∧ + Executes (w + 2) ((program false).program output) (State.init (inputL a)) + (outputBase L) (n + 1) ∧ + (∀ τ, y.mem (outputBase L + (leafIndex τ).val) = encode (encodingL a τ)) ∧ + (∀ b, ¬(outputBase L ≤ b ∧ b < scratchBase L + 10 ^ L) → + y.mem b = (State.init (inputL a) : State 32).data.mem b) := by + obtain ⟨hi, y, ⟨n, hn, he⟩, _, hv, hf, ho, _⟩ := exec_left hw a A hA ha hmag haddr + refine ⟨hi, y, n, hn, ?_, ?_, hv, hf⟩ + · simpa only [Nat.zero_add, State.init] using he.runs (pc := 0) + (p := (program false).program output) (Program.Contains.self _).append_left + · simpa only [ho, State.init] using he.executes output + +/-- Actual flat native right-encoding execution, with all output cells and the same charged +standard-WordRAM calling convention and final halt. -/ +theorem runs_right {w L : ℕ} (hw : 0 < w) (a : RightStr L → ℤ) (A : ℤ) + (hA : 0 ≤ A) (ha : ∀ u, |a u| ≤ A) + (hmag : (7 ^ L : ℕ) * A < (2 : ℤ) ^ (w - 1)) + (haddr : Fits (w + 2) (addressBound L)) : + InputFits (w + 2) (inputR a) ∧ + ∃ y n, n ≤ timeBound L ∧ + Runs (w + 2) ((program true).program output) (State.init (inputR a)) + ⟨(program true).size, y⟩ n ∧ + Executes (w + 2) ((program true).program output) (State.init (inputR a)) + (outputBase L) (n + 1) ∧ + (∀ τ, y.mem (outputBase L + (leafIndex τ).val) = encode (encodingR a τ)) ∧ + (∀ b, ¬(outputBase L ≤ b ∧ b < scratchBase L + 10 ^ L) → + y.mem b = (State.init (inputR a) : State 32).data.mem b) := by + obtain ⟨hi, y, ⟨n, hn, he⟩, _, hv, hf, ho, _⟩ := exec_right hw a A hA ha hmag haddr + refine ⟨hi, y, n, hn, ?_, ?_, hv, hf⟩ + · simpa only [Nat.zero_add, State.init] using he.runs (pc := 0) + (p := (program true).program output) (Program.Contains.self _).append_left + · simpa only [ho, State.init] using he.executes output + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Forms.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Forms.lean new file mode 100644 index 000000000..33ed8e881 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Forms.lean @@ -0,0 +1,235 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding +public import Complexity.Foundations.Algebra.FiniteMagnitude +import Mathlib.Tactic.Ring + +/-! +# Native execution of the encoding forms + +The form code reads seven strided entries and uses only the existing native signed arithmetic +blocks. These results account for those blocks' actual instructions and their scratch registers. + +## Main results + +* `Encoding.exec_summand`: a coefficient in `{0, 1, -1}` takes at most 23 instructions. + +## References + +The linear forms are from [AlmanWilliams2026, Section 2.4.1]; see `Encoding` for provenance. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Registers outside the signed workspace and the form's three local registers. -/ +def FormFrame (x y : Data 32) : Prop := + y.mem = x.mem ∧ ∀ i, 9 ≤ i.val → i ≠ address → i ≠ accumulator → i ≠ temporary → + y.regs i = x.regs i + +/-- Form frames compose without changing any memory cell. -/ +theorem FormFrame.trans {x y z : Data 32} (hxy : FormFrame x y) (hyz : FormFrame y z) : + FormFrame x z := + ⟨hyz.1.trans hxy.1, fun i hi ha hc ht => + (hyz.2 i hi ha hc ht).trans (hxy.2 i hi ha hc ht)⟩ + +private theorem fits_of_abs_lt {w : ℕ} (hw : 0 < w) {a : ℤ} + (ha : |a| < (2 : ℤ) ^ (w - 1)) : GenericRAM.Signed.Fits w a := by + have h := abs_lt.mp ha + exact ⟨hw, h.1.le, h.2⟩ + +/-- A single coefficient is executed by real loads and signed arithmetic. The seven address +computations fit whenever the one-past-end address fits. The two operands must fit the signed +source range; the sum itself may use the two extra target bits supplied by the arithmetic block. -/ +theorem exec_summand {w : ℕ} (x : Data 32) (s : Fin 7) (c a v : ℤ) (base n : ℕ) + (hc : c = 0 ∨ c = 1 ∨ c = -1) + (hacc : x.regs accumulator = encode a) (hbase : x.regs group = base) + (hn : x.regs stride = n) (hmem : x.mem (base + s.val * n) = encode v) + (ha : GenericRAM.Signed.Fits w a) (hv : GenericRAM.Signed.Fits w v) + (haddr : Fits (w + 2) (base + 7 * n)) (hseven : Fits (w + 2) 7) : + ∃ y, ExecWithin (w + 2) (summand s c) x y 23 ∧ + y.regs accumulator = encode (a + c * v) ∧ FormFrame x y := by + rcases hc with rfl | rfl | rfl + · refine ⟨x.set accumulator (x.regs accumulator), (ExecWithin.copy).mono (by decide), ?_, ?_⟩ + · simp [hacc] + · refine ⟨rfl, ?_⟩ + intro i _ _ hi _ + simp only [Data.set_regs, Function.update_of_ne hi] + all_goals + have hs : Fits (w + 2) s.val := hseven.of_le (by omega) + have hmul : Fits (w + 2) (s.val * n) := haddr.of_le (by nlinarith [s.isLt]) + have hsum : Fits (w + 2) (base + s.val * n) := haddr.of_le (by nlinarith [s.isLt]) + let z := ((((x.set temporary s.val).set address (s.val * n)).set address + (base + s.val * n)).set right (encode v)).set left (encode a) + have hz : ExecWithin (w + 2) + (ram[ temporary ← #s.val; address ← temporary * stride; + address ← group + address; right ← *address; left ← accumulator ]) x z 5 := by + ram_eval [z, hs, hmul, hsum, hn, hbase, hacc, hmem, + temporary, address, group, stride, right, left, accumulator, Fin.mk.injEq] + have hza : z.regs left = encode a := by simp [z] + have hzv : z.regs right = encode v := by + simp [z, left, right] + have hzframe : FormFrame x z := by + refine ⟨rfl, ?_⟩ + intro i hi hia hic hit + have hil : i ≠ left := by intro h; subst i; simp at hi + have hir : i ≠ right := by intro h; subst i; simp at hi + simp only [z, Data.set_regs, Function.update_of_ne hil, Function.update_of_ne hir, + Function.update_of_ne hia, Function.update_of_ne hit] + · obtain ⟨y, he, hyr, hym, hyreg⟩ := exec_add ha hv z hza hzv + refine ⟨y.set accumulator (y.regs result), ?_, ?_, ?_⟩ + · simpa only [summand, show ¬(1 : ℤ) = 0 by decide, ↓reduceIte] using + (hz.seq (he.seq (ExecWithin.copy (d := accumulator) (s := result)))).mono + (by decide : 5 + (15 + 1) ≤ 23) + · simp [GenericRAM.Signed.Arithmetic.eval, hyr] + · refine ⟨hym.trans hzframe.1, ?_⟩ + intro i hi hia hic hit + simpa only [Data.set_regs, Function.update_of_ne hic] using + (hyreg i (Or.inr hi)).trans (hzframe.2 i hi hia hic hit) + · obtain ⟨y, he, hyr, hym, hyreg⟩ := exec_subtract ha hv z hza hzv + refine ⟨y.set accumulator (y.regs result), ?_, ?_, ?_⟩ + · simpa only [summand, show ¬(-1 : ℤ) = 0 by decide, + show ¬(-1 : ℤ) = 1 by decide, ↓reduceIte] using + (hz.seq (he.seq (ExecWithin.copy (d := accumulator) (s := result)))).mono + (by decide : 5 + (17 + 1) ≤ 23) + · simp [GenericRAM.Signed.Arithmetic.eval, hyr, sub_eq_add_neg] + · refine ⟨hym.trans hzframe.1, ?_⟩ + intro i hi hia hic hit + simpa only [Data.set_regs, Function.update_of_ne hic] using + (hyreg i (Or.inr hi)).trans (hzframe.2 i hi hia hic hit) + +/-- Execute any finite list of the fixed digit/coefficients. Partial sums are bounded by +`|a| + length * B`; the theorem derives each arithmetic operand's fit from that inequality. -/ +theorem exec_sumCode {w : ℕ} (hw : 0 < w) (pairs : List (Fin 7 × ℤ)) + (x : Data 32) (a B : ℤ) (base n : ℕ) (v : Fin 7 → ℤ) + (hc : ∀ sc ∈ pairs, sc.2 = 0 ∨ sc.2 = 1 ∨ sc.2 = -1) + (hacc : x.regs accumulator = encode a) (hbase : x.regs group = base) + (hn : x.regs stride = n) (hmem : ∀ s, x.mem (base + s.val * n) = encode (v s)) + (hB : 0 ≤ B) (hv : ∀ s, |v s| ≤ B) (hfitB : B < (2 : ℤ) ^ (w - 1)) + (hfit : |a| + pairs.length * B < (2 : ℤ) ^ (w - 1)) + (haddr : Fits (w + 2) (base + 7 * n)) (hseven : Fits (w + 2) 7) : + ∃ y, ExecWithin (w + 2) (sumCode pairs) x y (23 * pairs.length + 1) ∧ + y.regs accumulator = encode (a + (pairs.map fun sc => sc.2 * v sc.1).sum) ∧ + FormFrame x y := by + induction pairs generalizing x a with + | nil => + refine ⟨x.set accumulator (x.regs accumulator), ExecWithin.copy, ?_, ?_⟩ + · simpa using hacc + · refine ⟨rfl, ?_⟩ + intro i _ _ hi _ + simp only [Data.set_regs, Function.update_of_ne hi] + | cons sc rest ih => + obtain ⟨s, c⟩ := sc + have hc0 := hc (s, c) (by simp) + have hcv : |c * v s| ≤ B := by + rcases hc0 with rfl | rfl | rfl + · simpa using hB + · simpa using hv s + · simpa using hv s + have ha : GenericRAM.Signed.Fits w a := + fits_of_abs_lt hw (by have := abs_nonneg a; simp only [List.length_cons, + Nat.cast_add, Nat.cast_one] at hfit; nlinarith [Nat.cast_nonneg (α := ℤ) rest.length]) + have hvs := fits_of_abs_lt hw ((hv s).trans_lt hfitB) + obtain ⟨y, he, hyacc, hyframe⟩ := exec_summand x s c a (v s) base n hc0 hacc + hbase hn (hmem s) ha hvs haddr hseven + have hybase : y.regs group = base := (hyframe.2 group (by decide) (by decide) + (by decide) (by decide)).trans hbase + have hyn : y.regs stride = n := (hyframe.2 stride (by decide) (by decide) + (by decide) (by decide)).trans hn + have hfit' : |a + c * v s| + rest.length * B < (2 : ℤ) ^ (w - 1) := by + have ht := abs_add_le a (c * v s) + simp only [List.length_cons, Nat.cast_add, Nat.cast_one] at hfit + nlinarith + obtain ⟨z, hz, hzacc, hzframe⟩ := ih y (a + c * v s) + (fun sc hsc => hc sc (by simp [hsc])) hyacc hybase hyn + (fun s => by rw [hyframe.1]; exact hmem s) hfit' + refine ⟨z, ?_, ?_, hyframe.trans hzframe⟩ + · simpa only [sumCode, List.length_cons, Nat.mul_add, Nat.mul_one, + Nat.add_assoc, Nat.add_comm, Nat.add_left_comm] using he.seq hz + · simpa only [List.map_cons, List.sum_cons, add_assoc] using hzacc + +/-- A complete coefficient row, including initialization, costs at most 163 instructions. +All seven entries have absolute value at most `B`, so every partial sum has magnitude at +most `7B`; this ordinary explicit width condition is sufficient for every signed operation. -/ +theorem exec_form {w : ℕ} (hw : 0 < w) (side : Bool) (t : Fin 10) + (x : Data 32) (B : ℤ) (base n : ℕ) (v : Fin 7 → ℤ) + (hbase : x.regs group = base) (hn : x.regs stride = n) + (hmem : ∀ s, x.mem (base + s.val * n) = encode (v s)) + (hB : 0 ≤ B) (hv : ∀ s, |v s| ≤ B) (hfit : 7 * B < (2 : ℤ) ^ (w - 1)) + (haddr : Fits (w + 2) (base + 7 * n)) (hseven : Fits (w + 2) 7) : + ∃ y, ExecWithin (w + 2) (form side t) x y 163 ∧ + y.regs accumulator = encode (∑ s, coefficients side t s * v s) ∧ FormFrame x y := by + let z := x.set accumulator 0 + have hz : FormFrame x z := by + refine ⟨rfl, ?_⟩ + intro i _ _ hi _ + simp only [z, Data.set_regs, Function.update_of_ne hi] + obtain ⟨y, he, hacc, hf⟩ := exec_sumCode hw + (List.ofFn fun s => (s, coefficients side t s)) z 0 B base n v + (by intro sc hsc; obtain ⟨s, rfl⟩ := List.mem_ofFn.mp hsc; exact coefficients_values side t s) + (by simp [z, encode]) + ((hz.2 group (by decide) (by decide) (by decide) (by decide)).trans hbase) + ((hz.2 stride (by decide) (by decide) (by decide) (by decide)).trans hn) + (by simpa only [z, Data.set_mem] using hmem) hB hv (by linarith) + (by simpa using hfit) haddr hseven + refine ⟨y, ?_, ?_, hz.trans hf⟩ + · simpa only [form, List.length_ofFn] using + (ExecWithin.const (fits_zero (w := w + 2))).seq he + · simpa only [List.map_ofFn, List.sum_ofFn, Function.comp_apply, zero_add] using hacc + +/-- The finite term dispatch costs at most three instructions per inspected row in addition +to the row's 163-instruction bound. All ten forms share the same native register file. -/ +theorem exec_dispatch {w : ℕ} (hw : 0 < w) (side : Bool) (rows : List (Fin 10)) + (t : Fin 10) (ht : t ∈ rows) (x : Data 32) (B : ℤ) (base n : ℕ) (v : Fin 7 → ℤ) + (hterm : x.regs term = t.val) (hbase : x.regs group = base) (hn : x.regs stride = n) + (hmem : ∀ s, x.mem (base + s.val * n) = encode (v s)) + (hB : 0 ≤ B) (hv : ∀ s, |v s| ≤ B) (hfit : 7 * B < (2 : ℤ) ^ (w - 1)) + (haddr : Fits (w + 2) (base + 7 * n)) (hten : Fits (w + 2) 10) : + ∃ y, ExecWithin (w + 2) (dispatch side rows) x y (3 * rows.length + 163) ∧ + y.regs accumulator = encode (∑ s, coefficients side t s * v s) ∧ FormFrame x y := by + induction rows generalizing x with + | nil => simp at ht + | cons q rest ih => + let z := x.set temporary q.val + have hz : ExecWithin (B := .standard) (w + 2) (ram[ temporary ← #q.val ]) x z 1 := + ExecWithin.const (hten.of_le (by omega)) + have hzf : FormFrame x z := by + refine ⟨rfl, ?_⟩ + intro i _ _ _ hi + simp only [z, Data.set_regs, Function.update_of_ne hi] + have hzt : z.regs term = t.val := by + simpa [z, term, temporary, Fin.mk.injEq] using hterm + have hzb : z.regs group = base := by + simpa [z, group, temporary, Fin.mk.injEq] using hbase + have hzn : z.regs stride = n := by + simpa [z, stride, temporary, Fin.mk.injEq] using hn + have hzm : ∀ s, z.mem (base + s.val * n) = encode (v s) := hmem + by_cases hqt : t = q + · subst t + obtain ⟨y, he, hacc, hf⟩ := exec_form hw side q z B base n v hzb hzn hzm hB hv + hfit haddr (hten.of_le (by decide)) + refine ⟨y, ?_, hacc, hzf.trans hf⟩ + apply (hz.seq (ExecWithin.branch_pos ?_ he)).mono + · simp only [List.length_cons] + omega + · simp only [Cond.holds_eq, hzt, z, Data.set_regs, Function.update_self] + · have htrest : t ∈ rest := (List.mem_cons.mp ht).resolve_left hqt + obtain ⟨y, he, hacc, hf⟩ := ih htrest z hzt hzb hzn hzm + refine ⟨y, ?_, hacc, hzf.trans hf⟩ + apply (hz.seq (ExecWithin.branch_neg ?_ he)).mono + · simp only [List.length_cons] + omega + · simp only [Cond.holds_eq, hzt, z, Data.set_regs, Function.update_self] + exact fun h => hqt (Fin.ext h) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Header.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Header.lean new file mode 100644 index 000000000..5beb51aaf --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Header.lean @@ -0,0 +1,174 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding + +/-! +# Charged initialization from the actual depth header + +## Main results + +* `Encoding.exec_setup`: load the header and compute both powers, buffer addresses and + loop parameters within `5L+18` native instructions, preserving every memory cell. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding + +open Cslib.WordRAM + +/-- An explicit bound for the encoder's unsigned addresses, index products and counters. -/ +def addressBound (L : ℕ) : ℕ := L + 64 + 64 * 10 ^ L + +/-- Registers and memory after header initialization. This is only a postcondition; it +contains no execution, resource or intermediate-fit assumption. -/ +structure SetupPost (L : ℕ) (x y : Data 32) : Prop where + /-- Memory is unchanged. -/ + mem : y.mem = x.mem + /-- Depth loaded from memory address one. -/ + depth : y.regs Encoding.depth = L + /-- Computed input size. -/ + inputSize : y.regs Encoding.inputSize = 7 ^ L + /-- Computed buffer capacity. -/ + capacity : y.regs Encoding.capacity = 10 ^ L + /-- First output address. -/ + output : y.regs Encoding.output = outputBase L + /-- First scratch address. -/ + scratch : y.regs Encoding.scratch = scratchBase L + /-- Original input base. -/ + src : y.regs Encoding.src = 2 + /-- First destination buffer. -/ + dst : y.regs Encoding.dst = outputBase L + /-- First source suffix length. -/ + stride : y.regs Encoding.stride = 7 ^ L + /-- The empty transformed prefix. -/ + prefixes : y.regs Encoding.prefixes = 1 + /-- No coordinates transformed yet. -/ + stage : y.regs Encoding.stage = 0 + /-- Counter increment. -/ + one : y.regs Encoding.one = 1 + /-- Input radix. -/ + seven : y.regs Encoding.seven = 7 + /-- Output radix. -/ + ten : y.regs Encoding.ten = 10 + +/-- Header loads, both power computations, address arithmetic and counter initialization +are included. The only fit premise is the explicit bound on ordinary natural numbers. -/ +theorem exec_setup {w : ℕ} (L : ℕ) (x : Data 32) (hheader : x.mem 1 = L) + (haddr : Fits w (addressBound L)) : + ∃ y, ExecWithin w setup x y (5 * L + 18) ∧ SetupPost L x y := by + have h1 : Fits w 1 := haddr.of_le (by unfold addressBound; omega) + have h2 : Fits w 2 := haddr.of_le (by unfold addressBound; omega) + have h7 : Fits w 7 := haddr.of_le (by unfold addressBound; omega) + have h10 : Fits w 10 := haddr.of_le (by unfold addressBound; omega) + have hpow (b j : ℕ) (hb : b ≤ 10) (hj : j ≤ L) : Fits w (b ^ j) := by + apply haddr.of_le + have hbj := Nat.pow_le_pow_left hb j + have hjL := Nat.pow_le_pow_right (by decide : 1 ≤ (10 : ℕ)) hj + unfold addressBound + omega + let I (y : Data 32) : Prop := + y.mem = x.mem ∧ y.regs depth = L ∧ y.regs inputSize = 7 ^ y.regs stage ∧ + y.regs capacity = 10 ^ y.regs stage ∧ y.regs stage ≤ L ∧ + y.regs one = 1 ∧ y.regs seven = 7 ∧ y.regs ten = 10 + have hbody (y : Data 32) (hy : I y) (hc : (Cond.lt stage depth).Holds y) : + ∃ z, ExecWithin w powerBody y z 3 ∧ I z ∧ L - z.regs stage < L - y.regs stage := by + obtain ⟨hm, hd, hi, hcap, hj, ho, hs, ht⟩ := hy + have hjL : y.regs stage < L := by simpa only [Cond.holds_lt, hd] using hc + have hpi : Fits w (y.regs inputSize * y.regs seven) := by + rw [hi, hs, ← pow_succ] + exact hpow 7 _ (by decide) (by omega) + have hpc : Fits w (y.regs capacity * y.regs ten) := by + rw [hcap, ht, ← pow_succ] + exact hpow 10 _ (by decide) (by omega) + have hjfit : Fits w (y.regs stage + y.regs one) := by + rw [ho] + exact haddr.of_le (by unfold addressBound; omega) + have hpi' : Fits w (y.regs inputSize * 7) := by simpa only [hs] using hpi + have hpc' : Fits w (y.regs capacity * 10) := by simpa only [ht] using hpc + have hjfit' : Fits w (y.regs stage + 1) := by simpa only [ho] using hjfit + let z := ((y.set inputSize (y.regs inputSize * 7)).set capacity + (y.regs capacity * 10)).set stage (y.regs stage + 1) + have he : ExecWithin w powerBody y z 3 := by + ram_eval [powerBody, z, ho, hs, ht, hpi', hpc', hjfit', + inputSize, capacity, stage, one, seven, ten, Fin.mk.injEq] + refine ⟨z, he, ?_, ?_⟩ + · refine ⟨hm, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · simpa +decide [z, depth, inputSize, capacity, stage, Function.update_apply] using hd + · simp +decide [z, inputSize, capacity, stage, pow_succ, hi] + · simp +decide [z, inputSize, capacity, stage, pow_succ, hcap] + · simpa [z] using Nat.succ_le_of_lt hjL + · simpa +decide [z, one, inputSize, capacity, stage, Function.update_apply] using ho + · simpa +decide [z, seven, inputSize, capacity, stage, Function.update_apply] using hs + · simpa +decide [z, ten, inputSize, capacity, stage, Function.update_apply] using ht + · simp only [z, Data.set_regs, Function.update_self] + omega + let x0 := (((((((x.set one 1).set seven 7).set ten 10).set address 1).set depth L).set + inputSize 1).set capacity 1).set stage 0 + have hx0 : I x0 := by + simp +decide [I, x0, depth, inputSize, capacity, stage, one, seven, ten, address] + have hxrun : ExecWithin w setupHead x x0 8 := by + ram_eval [setupHead, x0, hheader, h1, h7, h10, fits_zero, + one, seven, ten, address, depth, inputSize, capacity, stage, Fin.mk.injEq] + obtain ⟨u, hu, hi, hc⟩ := ExecWithin.loop_invariant I (fun y => L - y.regs stage) hbody hx0 + obtain ⟨hm, hd, hin, hcap, hj, ho, hs, ht⟩ := hi + have hend : u.regs stage = L := by + simp only [Cond.holds_lt, hd] at hc + omega + rw [hend] at hin hcap + have hout : Fits w (2 + 7 ^ L) := haddr.of_le (by + have := Nat.pow_le_pow_left (by decide : 7 ≤ 10) L + unfold addressBound + omega) + have hscratch : Fits w (2 + 7 ^ L + 10 ^ L) := haddr.of_le (by + have := Nat.pow_le_pow_left (by decide : 7 ≤ 10) L + unfold addressBound + omega) + let y := (((((((u.set temporary 2).set output (outputBase L)).set scratch (scratchBase L)).set + src 2).set dst (outputBase L)).set stride (7 ^ L)).set prefixes 1).set stage 0 + have hyrun : ExecWithin w setupTail u y 8 := by + ram_eval [setupTail, y, outputBase, scratchBase, hin, hcap, h2, h1, hout, hscratch, + temporary, output, scratch, src, dst, stride, prefixes, stage, inputSize, capacity, + Fin.mk.injEq] + refine ⟨y, ?_, ?_⟩ + · have hcost : 8 + ((L - x0.regs stage) * (3 + 2) + 2 + 8) ≤ 5 * L + 18 := by + simp only [x0, Data.set_regs, Function.update_self, Nat.sub_zero] + omega + exact (hxrun.seq (hu.seq hyrun)).mono hcost + · constructor + all_goals simp +decide [y, outputBase, scratchBase, temporary, output, scratch, src, dst, + stride, prefixes, stage, depth, inputSize, capacity, one, seven, ten, + hm, hd, hin, hcap, ho, hs, ht] + +/-- The actual flat input fits the target machine. Each signed input value uses the existing +canonical encoding; the depth and the ordinary unsigned length header are bounded separately. -/ +theorem input_fits {w L : ℕ} (hw : 0 < w) (a : Fin (7 ^ L) → ℤ) (A : ℤ) + (hA : 0 ≤ A) (ha : ∀ i, |a i| ≤ A) + (hmag : (7 ^ L : ℕ) * A < (2 : ℤ) ^ (w - 1)) + (haddr : Fits (w + 2) (addressBound L)) : InputFits (w + 2) (input L a) := by + have hpow : (1 : ℤ) ≤ (7 ^ L : ℕ) := by + exact_mod_cast Nat.one_le_pow L 7 (by decide) + have hAfit : A < (2 : ℤ) ^ (w - 1) := by nlinarith + refine ⟨?_, ?_⟩ + · simp only [input, List.length_cons, List.length_ofFn] + apply haddr.of_le + have := Nat.pow_le_pow_left (by decide : 7 ≤ 10) L + unfold addressBound + omega + · intro v hv + rcases List.mem_cons.mp hv with rfl | hv + · exact haddr.of_le (by unfold addressBound; omega) + · obtain ⟨i, rfl⟩ := List.mem_ofFn.mp hv + have hi : GenericRAM.Signed.Fits w (a i) := by + have h := abs_lt.mp ((ha i).trans_lt hAfit) + exact ⟨hw, h.1.le, h.2⟩ + exact (Cslib.WordRAM.Signed.encode_fits hi).mono (by omega) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Input.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Input.lean new file mode 100644 index 000000000..727d0f96f --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Input.lean @@ -0,0 +1,127 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Exec +import Mathlib.Data.List.GetD + +/-! +# Actual initialized-input clients for the shared encoders + +## Main results + +* `Encoding.exec_input`: flat signed array, concrete depth header and actual `State.init`. +* `Encoding.exec_left`, `Encoding.exec_right`: all public tensor encodings, at every depth. + +The two programs and their thirty-two registers are independent of depth and word width. +Inputs are signed words encoded with `WordRAM.Signed.encode`; the depth and the machine's +length header are ordinary unsigned words. All input and reachable data words fit the target +width. Output and scratch are disjoint concrete blocks immediately after the input. + +## References + +The encodings are from [AlmanWilliams2026, Section 2.4.1]; see `Encoding` for provenance. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open CoordinateTransform + +private theorem getD_ofFn {N : ℕ} (f : Fin N → ℤ) (i : Fin N) : + (List.ofFn f).getD i.val 0 = f i := by + rw [List.getD_eq_getElem _ _ (by simp)] + simp + +/-- The complete native encoder starts from the ordinary `State.init` convention with a +real depth header and encoded input words. It returns all flat shared-transform values, keeps +the input and every outside cell, and leaves only target-sized words in memory and registers. -/ +theorem exec_input {w L : ℕ} (hw : 0 < w) (side : Bool) (a : Fin (7 ^ L) → ℤ) + (A : ℤ) (hA : 0 ≤ A) (ha : ∀ i, |a i| ≤ A) + (hmag : (7 ^ L : ℕ) * A < (2 : ℤ) ^ (w - 1)) + (haddr : Fits (w + 2) (addressBound L)) : + InputFits (w + 2) (input L a) ∧ + ∃ y, ExecWithin (w + 2) (program side) (State.init (input L a)).data y (timeBound L) ∧ + y.Bounded (w + 2) ∧ + (∀ p < 10 ^ L, y.mem (outputBase L + p) = encode + (value (table (coefficients side)) L 0 (fun i => (List.ofFn a).getD i 0) p 0)) ∧ + (∀ b, ¬(outputBase L ≤ b ∧ b < scratchBase L + 10 ^ L) → + y.mem b = (State.init (input L a) : State 32).data.mem b) ∧ + y.regs output = outputBase L ∧ y.regs capacity = 10 ^ L := by + have hi := input_fits hw a A hA ha hmag haddr + let x : Data 32 := (State.init (input L a)).data + have hheader : x.mem 1 = L := by + simp [x, State.init, Data.init, input] + have hinput (i : ℕ) (hi : i < 7 ^ L) : + x.mem (2 + i) = encode ((List.ofFn a).getD i 0) := by + have hidx : 2 + i = (i + 1) + 1 := by omega + change (Data.init (input L a) : Data 32).mem (2 + i) = _ + rw [hidx, Data.init_mem_succ] + simp only [input, List.getD_cons_succ] + rw [List.getD_eq_getElem _ _ (by simpa only [List.length_ofFn] using hi)] + simpa using congrArg encode (getD_ofFn a ⟨i, hi⟩).symm + have ha' (i : ℕ) (hi : i < 7 ^ L) : |(List.ofFn a).getD i 0| ≤ A := by + rw [getD_ofFn a ⟨i, hi⟩] + exact ha _ + obtain ⟨y, he, hv, hf, ho, hc⟩ := exec hw side (fun i => (List.ofFn a).getD i 0) x + hheader hinput A hA ha' hmag haddr + exact ⟨hi, y, he, he.bounded (Data.Bounded.init hi), hv, hf, ho, hc⟩ + +/-- Complete left encoder on arbitrary signed arrays indexed by the original seven-symbol +strings. Every one of the `10^L` leaves receives the unchanged public `encodingL` value. -/ +theorem exec_left {w L : ℕ} (hw : 0 < w) (a : LeftStr L → ℤ) (A : ℤ) + (hA : 0 ≤ A) (ha : ∀ u, |a u| ≤ A) + (hmag : (7 ^ L : ℕ) * A < (2 : ℤ) ^ (w - 1)) + (haddr : Fits (w + 2) (addressBound L)) : + InputFits (w + 2) (inputL a) ∧ + ∃ y, ExecWithin (w + 2) (program false) (State.init (inputL a)).data y (timeBound L) ∧ + y.Bounded (w + 2) ∧ + (∀ τ, y.mem (outputBase L + (leafIndex τ).val) = encode (encodingL a τ)) ∧ + (∀ b, ¬(outputBase L ≤ b ∧ b < scratchBase L + 10 ^ L) → + y.mem b = (State.init (inputL a) : State 32).data.mem b) ∧ + y.regs output = outputBase L ∧ y.regs capacity = 10 ^ L := by + let f : Fin (7 ^ L) → ℤ := fun i => a (fun ℓ => leftAt (finFunctionFinEquiv.symm i ℓ)) + obtain ⟨hi, y, he, hb, hv, hf, ho, hc⟩ := exec_input hw false f A hA (fun _ => ha _) hmag haddr + refine ⟨hi, y, he, hb, ?_, hf, ho, hc⟩ + intro τ + have harr (u : Fin L → Fin 7) : (List.ofFn f).getD (finFunctionFinEquiv u).val 0 = + a (fun i => leftAt (u i)) := by + rw [getD_ofFn] + simp only [f, Equiv.symm_apply_apply] + have h := hv (leafIndex τ) (leafIndex τ).isLt + rw [value_final_left a _ harr τ] at h + exact h + +/-- Complete right encoder on arbitrary signed arrays with the original `encodingR` API. -/ +theorem exec_right {w L : ℕ} (hw : 0 < w) (a : RightStr L → ℤ) (A : ℤ) + (hA : 0 ≤ A) (ha : ∀ u, |a u| ≤ A) + (hmag : (7 ^ L : ℕ) * A < (2 : ℤ) ^ (w - 1)) + (haddr : Fits (w + 2) (addressBound L)) : + InputFits (w + 2) (inputR a) ∧ + ∃ y, ExecWithin (w + 2) (program true) (State.init (inputR a)).data y (timeBound L) ∧ + y.Bounded (w + 2) ∧ + (∀ τ, y.mem (outputBase L + (leafIndex τ).val) = encode (encodingR a τ)) ∧ + (∀ b, ¬(outputBase L ≤ b ∧ b < scratchBase L + 10 ^ L) → + y.mem b = (State.init (inputR a) : State 32).data.mem b) ∧ + y.regs output = outputBase L ∧ y.regs capacity = 10 ^ L := by + let f : Fin (7 ^ L) → ℤ := fun i => a (fun ℓ => rightAt (finFunctionFinEquiv.symm i ℓ)) + obtain ⟨hi, y, he, hb, hv, hf, ho, hc⟩ := exec_input hw true f A hA (fun _ => ha _) hmag haddr + refine ⟨hi, y, he, hb, ?_, hf, ho, hc⟩ + intro τ + have harr (u : Fin L → Fin 7) : (List.ofFn f).getD (finFunctionFinEquiv u).val 0 = + a (fun i => rightAt (u i)) := by + rw [getD_ofFn] + simp only [f, Equiv.symm_apply_apply] + have h := hv (leafIndex τ) (leafIndex τ).isLt + rw [value_final_right a _ harr τ] at h + exact h + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Meaning.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Meaning.lean new file mode 100644 index 000000000..a22fed233 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Meaning.lean @@ -0,0 +1,96 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Stage +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.CoordinateTransform + +/-! +# Agreement of the flat tensor transform with the public encodings + +The digit numbering is supplied by Mathlib's `finFunctionFinEquiv`. The explicit alphabet +numberings contain exactly the original seven variables and ten terms. The final mixed-radix +transform is `encodingL` or `encodingR` for every depth, including zero. + +## Main results + +* `Encoding.value_final_left`, `Encoding.value_final_right`: unchanged public encoding semantics. + +## References + +The original encodings and coefficients are from [AlmanWilliams2026, Section 2.4.1], selectively +adapted from Anthropic, PBC's formal source; see `Encoding` for the exact pin and paths. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding + +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open CoordinateTransform + +/-- The numbered output position of a term string. -/ +def leafIndex {L : ℕ} (τ : Leaf L) : Fin (10 ^ L) := + finFunctionFinEquiv fun i => termDigits.symm (τ i) + +private theorem value_final_alphabet {α : Type} [Fintype α] (e : Fin 7 ≃ α) + (c : Term → α → ℤ) {L : ℕ} (a : (Fin L → α) → ℤ) (arr : ℕ → ℤ) + (harr : ∀ u : Fin L → Fin 7, arr (finFunctionFinEquiv u) = a (fun i => e (u i))) + (τ : Leaf L) : + value (table (fun t s => c (termAt t) (e s))) L 0 arr (leafIndex τ) 0 = + encodeWith c τ a := by + have h := value_final (fun t s => c (termAt t) (e s)) L + (fun u => a (fun i => e (u i))) arr harr (fun i => termDigits.symm (τ i)) + change value (table (fun t s => c (termAt t) (e s))) L 0 arr + (finFunctionFinEquiv fun i => termDigits.symm (τ i)) 0 = _ + rw [h] + unfold encodeWith + refine Fintype.sum_equiv (Equiv.piCongrRight fun _ : Fin L => e) _ _ ?_ + intro u + congr 1 + apply Finset.prod_congr rfl + intro i _ + change c (termDigits (termDigits.symm (τ i))) (e (u i)) = c (τ i) (e (u i)) + rw [Equiv.apply_symm_apply] + +/-- The flat shared left transform equals the existing `encodingL` on every leaf. -/ +theorem value_final_left {L : ℕ} (a : LeftStr L → ℤ) (arr : ℕ → ℤ) + (harr : ∀ u : Fin L → Fin 7, arr (finFunctionFinEquiv u) = a (fun i => leftAt (u i))) + (τ : Leaf L) : + value (table (coefficients false)) L 0 arr (leafIndex τ) 0 = encodingL a τ := + value_final_alphabet leftDigits phi a arr harr τ + +/-- The flat shared right transform equals the existing `encodingR` on every leaf. -/ +theorem value_final_right {L : ℕ} (a : RightStr L → ℤ) (arr : ℕ → ℤ) + (harr : ∀ u : Fin L → Fin 7, arr (finFunctionFinEquiv u) = a (fun i => rightAt (u i))) + (τ : Leaf L) : + value (table (coefficients true)) L 0 arr (leafIndex τ) 0 = encodingR a τ := + value_final_alphabet rightDigits psi a arr harr τ + +/-- The native stage's row-wise specification is the next shared-prefix value. This includes +the quotient/remainder decomposition of its actual flat loop counter. -/ +theorem stageResult_value (side : Bool) (j k i : ℕ) (arr : ℕ → ℤ) : + stageResult side (7 ^ k) + (fun i a => value (table (coefficients side)) j (k + 1) arr + (i / (10 * 7 ^ k)) (a.val * 7 ^ k + i % 7 ^ k)) i = + value (table (coefficients side)) (j + 1) k arr (i / 7 ^ k) (i % 7 ^ k) := by + have hdiv : i / 7 ^ k / 10 = i / (10 * 7 ^ k) := by + rw [Nat.div_div_eq_div_mul, Nat.mul_comm (7 ^ k) 10] + have hprefix : i / (10 * 7 ^ k) * 10 + i / 7 ^ k % 10 = i / 7 ^ k := by + simpa only [hdiv, Nat.mul_comm] using Nat.div_add_mod (i / 7 ^ k) 10 + have he := value_step (table (coefficients side)) j k arr (i / (10 * 7 ^ k)) + (i % 7 ^ k) (Nat.mod_lt (i / 7 ^ k) (by decide : 0 < 10)) + rw [hprefix] at he + unfold stageResult + rw [he, ← Fin.sum_univ_eq_sum_range] + apply Finset.sum_congr rfl + intro a _ + simp only [table, Nat.mod_mod, Nat.mod_eq_of_lt a.isLt] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Stage.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Stage.lean new file mode 100644 index 000000000..85c382fd0 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Stage.lean @@ -0,0 +1,203 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Cell + +/-! +# Charged shared-coordinate array loop + +## Main results + +* `Encoding.exec_cells`: all cells in one shared coordinate transform, preserving the input + and every memory cell outside the destination block. + +## References + +The forms are from [AlmanWilliams2026, Section 2.4.1]; see `Encoding` for provenance. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- The linear-form value at flat output position `i` of a stage with suffix stride `n`. -/ +def stageResult (side : Bool) (n : ℕ) (v : ℕ → Fin 7 → ℤ) (i : ℕ) : ℤ := + ∑ a, coefficients side ⟨i / n % 10, Nat.mod_lt _ (by decide)⟩ a * v i a + +/-- The complete cell loop has at most `207 * (10 * P * n) + 2` instructions. The seven +source entries for each output are shared values from the previous array, not separately +enumerated original input terms. Disjointness is permitted in either address order. -/ +theorem exec_cells {w : ℕ} (hw : 0 < w) (side : Bool) (x : Data 32) + (P n s d : ℕ) (hP : 0 < P) (hnpos : 0 < n) + (hsrc : x.regs src = s) (hdst : x.regs dst = d) (hcell : x.regs cell = 0) + (hn : x.regs stride = n) (hseven : x.regs seven = 7) + (hten : x.regs ten = 10) (hone : x.regs one = 1) + (hsize : x.regs stageSize = 10 * P * n) + (v : ℕ → Fin 7 → ℤ) (B : ℤ) (hB : 0 ≤ B) (hv : ∀ i < 10 * P * n, ∀ a, |v i a| ≤ B) + (hmem : ∀ i < 10 * P * n, ∀ a, + x.mem (s + (i / (10 * n) * 7 + a.val) * n + i % n) = encode (v i a)) + (hval : 7 * B < (2 : ℤ) ^ (w - 1)) + (haddr : Fits (w + 2) (s + d + 64 + 64 * P * n)) + (hdisj : s + 7 * P * n ≤ d ∨ d + 10 * P * n ≤ s) : + ∃ y, ExecWithin (w + 2) (.loop (.lt cell stageSize) (cellBody side)) x y + (207 * (10 * P * n) + 2) ∧ + (∀ i < 10 * P * n, y.mem (d + i) = encode (stageResult side n v i)) ∧ + (∀ a, ¬(d ≤ a ∧ a < d + 10 * P * n) → y.mem a = x.mem a) ∧ + (∀ a, 9 ≤ a.val → ¬CellLocal a → y.regs a = x.regs a) := by + let N := 10 * P * n + let I (y : Data 32) : Prop := + (∀ a, 9 ≤ a.val → ¬CellLocal a → y.regs a = x.regs a) ∧ y.regs cell ≤ N ∧ + (∀ i < y.regs cell, y.mem (d + i) = encode (stageResult side n v i)) ∧ + (∀ a, ¬(d ≤ a ∧ a < d + y.regs cell) → y.mem a = x.mem a) + have hbody (y : Data 32) (hy : I y) (hc : (Cond.lt cell stageSize).Holds y) : + ∃ z, ExecWithin (w + 2) (cellBody side) y z 205 ∧ I z ∧ + N - z.regs cell < N - y.regs cell := by + obtain ⟨hr, hiN, hm, hf⟩ := hy + have hys : y.regs src = s := (hr src (by decide) (by decide)).trans hsrc + have hyd : y.regs dst = d := (hr dst (by decide) (by decide)).trans hdst + have hyn : y.regs stride = n := (hr stride (by decide) (by decide)).trans hn + have hy7 : y.regs seven = 7 := (hr seven (by decide) (by decide)).trans hseven + have hy10 : y.regs ten = 10 := (hr ten (by decide) (by decide)).trans hten + have hy1 : y.regs one = 1 := (hr one (by decide) (by decide)).trans hone + have hysz : y.regs stageSize = N := (hr stageSize (by decide) (by decide)).trans hsize + have hi : y.regs cell < N := by simpa only [Cond.holds_lt, hysz] using hc + have hyinput (a : Fin 7) : + y.mem (s + (y.regs cell / (10 * n) * 7 + a.val) * n + y.regs cell % n) = + encode (v (y.regs cell) a) := by + have hp : y.regs cell / (10 * n) < P := by + apply (Nat.div_lt_iff_lt_mul (by positivity : 0 < 10 * n)).mpr + dsimp only [N] at hi + nlinarith + have hmod := Nat.mod_lt (y.regs cell) hnpos + have hprefix : y.regs cell / (10 * n) * 7 + a.val < P * 7 := by omega + have hproduct := Nat.mul_le_mul_right n (Nat.succ_le_of_lt hprefix) + have hoff : (y.regs cell / (10 * n) * 7 + a.val) * n + y.regs cell % n < + P * 7 * n := by nlinarith only [hproduct, hmod] + rw [Nat.mul_comm P 7] at hoff + rw [hf _ ?_] + · exact hmem _ hi a + · rcases hdisj with h | h <;> dsimp only [N] at hiN <;> omega + obtain ⟨z, he, hzmem, hzcell, hzreg⟩ := exec_cell hw side y P n s d (y.regs cell) + hP hnpos hi hys hyd rfl hyn hy7 hy10 hy1 (v (y.regs cell)) B hB (hv _ hi) + hyinput hval haddr + refine ⟨z, he, ?_, by rw [hzcell]; omega⟩ + refine ⟨fun a ha hl => (hzreg a ha hl).trans (hr a ha hl), by omega, ?_, ?_⟩ + · intro i hzi + rw [hzcell] at hzi + by_cases hic : i = y.regs cell + · subst i + simp only [hzmem, Function.update_self, stageResult] + · have hin : i < y.regs cell := by omega + rw [hzmem, Function.update_of_ne (by omega : d + i ≠ d + y.regs cell)] + exact hm i hin + · intro a ha + rw [hzcell] at ha + have hne : a ≠ d + y.regs cell := by omega + rw [hzmem, Function.update_of_ne hne] + exact hf a (by omega) + have hx : I x := by + refine ⟨fun _ _ _ => rfl, by simp [hcell, N], ?_, ?_⟩ + · simp only [hcell, Nat.not_lt_zero, false_implies, implies_true] + · intro a _ + rfl + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I (fun y => N - y.regs cell) hbody hx + obtain ⟨hr, hi, hm, hf⟩ := hy + have hysz : y.regs stageSize = N := (hr stageSize (by decide) (by decide)).trans hsize + have hend : y.regs cell = N := by + simp only [Cond.holds_lt, hysz] at hc + omega + refine ⟨y, ?_, ?_, ?_, hr⟩ + · simpa only [hcell, N, Nat.sub_zero, Nat.add_comm, Nat.mul_comm] using he + · simpa only [hend] using hm + · simpa only [hend] using hf + +/-- Registers changed by the stage controller or its cell loop. -/ +def StageLocal (i : Fin 32) : Prop := + CellLocal i ∨ i = stride ∨ i = stageSize ∨ i = src ∨ i = dst ∨ i = prefixes ∨ i = stage + +instance (i : Fin 32) : Decidable (StageLocal i) := + inferInstanceAs (Decidable (CellLocal i ∨ i = stride ∨ i = stageSize ∨ i = src ∨ + i = dst ∨ i = prefixes ∨ i = stage)) + +private theorem stage_preserves (side : Bool) (i : Fin 32) : + 9 ≤ i.val → ¬StageLocal i → (stageBody side).Preserves i := by + decide +kernel +revert + +/-- A complete coordinate stage, including stride division, stage-size arithmetic, both +cell-loop control instructions per entry, buffer alternation and the next-stage counters. -/ +theorem exec_stageBody {w : ℕ} (hw : 0 < w) (side : Bool) (x : Data 32) + (P n s d j o q : ℕ) (hP : 0 < P) (hnpos : 0 < n) + (hsrc : x.regs src = s) (hdst : x.regs dst = d) (hj : x.regs stage = j) + (hn : x.regs stride = 7 * n) (hp : x.regs prefixes = P) + (hseven : x.regs seven = 7) (hten : x.regs ten = 10) (hone : x.regs one = 1) + (hout : x.regs output = o) (hscratch : x.regs scratch = q) + (v : ℕ → Fin 7 → ℤ) (B : ℤ) (hB : 0 ≤ B) (hv : ∀ i < 10 * P * n, ∀ a, |v i a| ≤ B) + (hmem : ∀ i < 10 * P * n, ∀ a, + x.mem (s + (i / (10 * n) * 7 + a.val) * n + i % n) = encode (v i a)) + (hval : 7 * B < (2 : ℤ) ^ (w - 1)) + (haddr : Fits (w + 2) (s + d + 64 + 64 * P * n)) (hjfit : Fits (w + 2) (j + 1)) + (hdisj : s + 7 * P * n ≤ d ∨ d + 10 * P * n ≤ s) : + ∃ y, ExecWithin (w + 2) (stageBody side) x y (207 * (10 * P * n) + 12) ∧ + (∀ i < 10 * P * n, y.mem (d + i) = encode (stageResult side n v i)) ∧ + (∀ a, ¬(d ≤ a ∧ a < d + 10 * P * n) → y.mem a = x.mem a) ∧ + y.regs src = d ∧ y.regs dst = (if d = o then q else o) ∧ + y.regs stride = n ∧ y.regs prefixes = 10 * P ∧ y.regs stage = j + 1 ∧ + (∀ a, 9 ≤ a.val → ¬StageLocal a → y.regs a = x.regs a) := by + have hPn : n ≤ P * n := by nlinarith + have h10P : Fits (w + 2) (P * 10) := haddr.of_le (by nlinarith) + have hN : Fits (w + 2) (P * 10 * n) := haddr.of_le (by nlinarith) + have hnfit : Fits (w + 2) n := haddr.of_le (by nlinarith) + have hdiv : 7 * n / 7 = n := by omega + let z := (((x.set stride n).set stageSize (P * 10)).set stageSize (P * 10 * n)).set cell 0 + have hz : ExecWithin (w + 2) stageHead x z 4 := by + ram_eval [stageHead, z, hn, hseven, hp, hten, hdiv, hnfit, h10P, hN, + stride, prefixes, ten, seven, stageSize, cell, Fin.mk.injEq] + have hzr (a : Fin 32) (ha : a ≠ stride ∧ a ≠ stageSize ∧ a ≠ cell) : + z.regs a = x.regs a := by + simp only [z, Data.set_regs, Function.update_of_ne ha.1, Function.update_of_ne ha.2.1, + Function.update_of_ne ha.2.2] + obtain ⟨u, hu, hum, huf, hur⟩ := exec_cells hw side z P n s d hP hnpos + ((hzr src (by decide)).trans hsrc) ((hzr dst (by decide)).trans hdst) + (by simp [z]) (by simp [z, stride, stageSize, cell]) + ((hzr seven (by decide)).trans hseven) ((hzr ten (by decide)).trans hten) + ((hzr one (by decide)).trans hone) + (by simp [z, stageSize, cell, Nat.mul_comm]) v B hB hv hmem hval haddr hdisj + have ur (a : Fin 32) (ha : 9 ≤ a.val) (hl : ¬CellLocal a) + (hzl : a ≠ stride ∧ a ≠ stageSize ∧ a ≠ cell) : u.regs a = x.regs a := + (hur a ha hl).trans (hzr a hzl) + have us : u.regs dst = d := (ur dst (by decide) (by decide) (by decide)).trans hdst + have uo : u.regs output = o := (ur output (by decide) (by decide) (by decide)).trans hout + have uq : u.regs scratch = q := (ur scratch (by decide) (by decide) (by decide)).trans hscratch + have up : u.regs prefixes = P := (ur prefixes (by decide) (by decide) (by decide)).trans hp + have uj : u.regs stage = j := (ur stage (by decide) (by decide) (by decide)).trans hj + have ut : u.regs ten = 10 := (ur ten (by decide) (by decide) (by decide)).trans hten + have u1 : u.regs one = 1 := (ur one (by decide) (by decide) (by decide)).trans hone + have un : u.regs stride = n := by + rw [hur stride (by decide) (by decide)] + simp [z, stride, stageSize, cell] + let y := (((u.set src d).set dst (if d = o then q else o)).set prefixes (P * 10)).set + stage (j + 1) + have hy : ExecWithin (w + 2) stageTail u y 6 := by + by_cases hdo : d = o + all_goals ram_eval [stageTail, y, us, uo, uq, up, uj, ut, u1, hdo, h10P, hjfit, + src, dst, output, scratch, prefixes, stage, ten, one, Fin.mk.injEq] + have he : ExecWithin (w + 2) (stageBody side) x y (207 * (10 * P * n) + 12) := + (hz.seq (hu.seq hy)).mono (by omega) + refine ⟨y, he, hum, huf, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · simp [y, src, dst, prefixes, stage] + · simp [y, dst, prefixes, stage] + · simpa [y, src, dst, prefixes, stage, stride] using un + · simp [y, prefixes, stage, Nat.mul_comm] + · simp [y] + · intro a ha hl + exact he.regs_eq (stage_preserves side a ha hl) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding diff --git a/Complexity/Computability/WordRAM/Copy.lean b/Complexity/Computability/WordRAM/Copy.lean index 62e6600b3..1652067c0 100644 --- a/Complexity/Computability/WordRAM/Copy.lean +++ b/Complexity/Computability/WordRAM/Copy.lean @@ -1,7 +1,7 @@ /- -Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Copyright (c) 2026 Samuel Schlesinger, Erik Demaine. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Samuel Schlesinger +Authors: Samuel Schlesinger, Erik Demaine -/ module @@ -10,10 +10,10 @@ module public import Complexity.Computability.WordRAM.Allocation /-! -# Copying disjoint arrays on the word RAM +# Copying arrays on the word RAM The seven-register copy routine charges every address calculation, load, store, increment, -and loop test. Its source precedes its destination, as in a bump-allocated growing array. +and loop test. Source and destination may be disjoint in either order or equal. It is a fixed program: neither the length nor either address is baked into the code. ## Main definitions @@ -29,6 +29,8 @@ It is a fixed program: neither the length nor either address is baked into the c * `Cslib.WordRAM.ArrayCopy.exec`: copying `n` words within `7 * n + 4` instructions, preserving the source and all cells outside the destination. +* `Cslib.WordRAM.ArrayCopy.exec_of_separated_or_eq`: the same native program also supports + disjoint blocks in the reverse address order or identical source and destination. ## References @@ -39,7 +41,7 @@ It is a fixed program: neither the length nor either address is baked into the c namespace Cslib.WordRAM.ArrayCopy -/-- The memory after copying a prefix into a disjoint destination block. -/ +/-- The memory after copying a prefix into a destination block. -/ def copyPrefix (mem : ℕ → ℕ) (src dst k : ℕ) : ℕ → ℕ := fun a => if dst ≤ a ∧ a < dst + k then mem (src + (a - dst)) else mem a @@ -94,10 +96,11 @@ def program : Code .standard 7 := ram[ idx ← #0; one ← #1; while idx < len d /-- Low-level structured copying charges register setup and all loop instructions, excluding the final halt. The one-past-end addresses must fit a word. Boundedness of the input memory and registers is a separate premise when using `ExecWithin.bounded` to obtain bounded-word execution. -/ -theorem exec {w : ℕ} (x : Data 7) (h1 : Fits w 1) +theorem exec_of_separated_or_eq {w : ℕ} (x : Data 7) (h1 : Fits w 1) (hs : Fits w (x.regs src + x.regs len)) (hd : Fits w (x.regs dst + x.regs len)) - (hdisj : x.regs src + x.regs len ≤ x.regs dst) : + (hdisj : x.regs src + x.regs len ≤ x.regs dst ∨ + x.regs dst + x.regs len ≤ x.regs src ∨ x.regs src = x.regs dst) : ∃ y, ExecWithin w program x y (7 * x.regs len + 4) ∧ y.mem = copyPrefix x.mem (x.regs src) (x.regs dst) (x.regs len) ∧ y.regs src = x.regs src ∧ y.regs dst = x.regs dst ∧ y.regs len = x.regs len := by @@ -118,7 +121,7 @@ theorem exec {w : ℕ} (x : Data 7) (h1 : Fits w 1) have hread : y.mem (y.regs src + y.regs idx) = x.mem (x.regs src + y.regs idx) := by rw [hym, hys] apply copyPrefix_outside - omega + rcases hdisj with h | h | h <;> omega refine ⟨?z, ?run, ?inv, ?decrease⟩ case run => ram_eval [body, hsrc, hdst, hinc, hyo] case inv => @@ -150,6 +153,16 @@ theorem exec {w : ℕ} (x : Data 7) (h1 : Fits w 1) omega · simpa [hend] using hym +/-- Forward copying between disjoint arrays, retaining the original calling convention. -/ +theorem exec {w : ℕ} (x : Data 7) (h1 : Fits w 1) + (hs : Fits w (x.regs src + x.regs len)) + (hd : Fits w (x.regs dst + x.regs len)) + (hdisj : x.regs src + x.regs len ≤ x.regs dst) : + ∃ y, ExecWithin w program x y (7 * x.regs len + 4) ∧ + y.mem = copyPrefix x.mem (x.regs src) (x.regs dst) (x.regs len) ∧ + y.regs src = x.regs src ∧ y.regs dst = x.regs dst ∧ y.regs len = x.regs len := + exec_of_separated_or_eq x h1 hs hd (Or.inl hdisj) + end Cslib.WordRAM.ArrayCopy end diff --git a/ComplexityTests/SchonhageEncoding.lean b/ComplexityTests/SchonhageEncoding.lean new file mode 100644 index 000000000..e03bb9f01 --- /dev/null +++ b/ComplexityTests/SchonhageEncoding.lean @@ -0,0 +1,139 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Flat + +-- Full-program regressions intentionally evaluate the native interpreter at elaboration time. +set_option linter.hashCommand false + +/-! +# Executed native shared-encoding regressions + +These clients start with the actual unsigned input convention. The interpreter charges every +native arithmetic block, branch, loop and store. They compare observable memory after execution. +-/ + +namespace ComplexityTests.SchonhageEncoding + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding + +private def observe (side : Bool) (w L fuel : ℕ) (a : Fin (7 ^ L) → ℤ) : + Option ((List ℤ × Bool) × ℕ) := + ((program side).eval w fuel (State.init (input L a)).data).map fun (y, time) => + (((List.range (10 ^ L)).map fun i => decode (y.mem (outputBase L + i)), + decide (y.mem 0 = (input L a).length ∧ y.mem 1 = L ∧ + (∀ i : Fin (7 ^ L), y.mem (2 + i.val) = encode (a i)) ∧ + y.mem (scratchBase L + 10 ^ L) = 0)), time) + +-- Kernel reduction follows the finite form dispatch and all actual signed arithmetic blocks. +-- Its nested code and loop reductions exceed the default elaborator recursion depth. +set_option maxRecDepth 16384 in +example : observe false 8 0 2 (fun _ => -3) = some (([-3], true), 31) := by + decide +kernel + +-- These checks run the actual interpreter as regression tests; the general execution +-- theorems are kernel checked separately. VM evaluation avoids quadratic kernel reduction +-- through thousands of functional register updates in each concrete full-program test. +#guard (observe false 12 1 16 (fun i => (i.val : ℤ) + 1)).map Prod.fst == + some ([5, 6, 1, 8, 9, 2, -7, -9, 3, -6], true) + +#guard (observe true 12 1 16 (fun i => (i.val : ℤ) + 1)).map Prod.fst == + some ([5, 7, -6, 7, 9, -10, 1, 2, 3, 6], true) + +#guard (observe false 12 1 16 (fun i => -((i.val : ℤ) + 1))).map Prod.fst == + some ([-5, -6, -1, -8, -9, -2, 7, 9, -3, 6], true) + +-- At width four, the computed scratch address 19 overflows; width five is the first working +-- width for this zero-valued depth-one client. Every output and the adjacent frame are checked. +#guard observe false 4 1 16 (fun _ => 0) == none +#guard (observe false 5 1 16 (fun _ => 0)).map Prod.fst == + some (List.replicate 10 0, true) + +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +private def signedLeft (u : LeftStr 2) : ℤ := + if u 0 = .x 0 then -2 else if u 1 = .p 1 0 then 3 else 1 + +private def signedRight (u : RightStr 2) : ℤ := + if u 0 = .q 0 1 then -3 else if u 1 = .y 2 then 2 else 0 + +private def leafAt (i : Fin (10 ^ 2)) : Leaf 2 := + fun ℓ => termAt (finFunctionFinEquiv.symm i ℓ) + +-- Output addresses can be calculated by ordinary executable finite alphabet numberings. +#guard (leafIndex (fun _ : Fin 2 => Term.P0)).val == 99 +#guard (leafIndex (fun _ : Fin 2 => Term.P 0 0)).val == 0 +#guard (leftDigits.symm (.p 1 1)).val == 6 + +-- Two stages alternate buffers and then copy backward from scratch to output. The expected +-- values use the unchanged public mathematical encodings, including every one of the 100 leaves. +#guard (observe false 16 2 128 (fun i => signedLeft + (fun ℓ => leftAt (finFunctionFinEquiv.symm i ℓ)))).map Prod.fst == + some (List.ofFn (fun i => encodingL signedLeft (leafAt i)), true) + +#guard (observe true 16 2 128 (fun i => signedRight + (fun ℓ => rightAt (finFunctionFinEquiv.symm i ℓ)))).map Prod.fst == + some (List.ofFn (fun i => encodingR signedRight (leafAt i)), true) + +private def copyObserve (s d : ℕ) : Option (List ℕ × Bool × ℕ) := + let x : Data 7 := ⟨![s, d, 3, 0, 0, 0, 0], + fun a => if s ≤ a ∧ a < s + 3 then a - s + 1 else 99⟩ + (ArrayCopy.program.eval 8 4 x).map fun (y, time) => + (List.ofFn (fun i : Fin 3 => y.mem (d + i.val)), + decide ((∀ i : Fin 3, y.mem (s + i.val) = i.val + 1) ∧ + y.mem 0 = 99 ∧ y.mem 12 = 99), time) + +-- Execute the generalized helper in both disjoint orders and with equal blocks. The old +-- forward-copy theorem remains available; these also check nonzero words outside the frame. +#guard copyObserve 4 8 == some ([1, 2, 3], true, 25) +#guard copyObserve 8 4 == some ([1, 2, 3], true, 25) +#guard copyObserve 4 4 == some ([1, 2, 3], true, 25) + +-- Nonzero garbage immediately beyond the two work buffers survives the complete encoder. +#guard ((program false).eval 16 128 + ((State.init (input 2 (fun _ => -1)) : State 32).data.write + (scratchBase 2 + 10 ^ 2) 91)).map + (fun (y, _) => y.mem (scratchBase 2 + 10 ^ 2)) == some 91 + +#guard observe true 8 0 2 (fun _ => -4) == some (([-4], true), 31) + +/-- The explicit width condition itself has a checked boundary. -/ +example : ¬Fits 9 (addressBound 1) ∧ Fits 10 (addressBound 1) := by decide + +/-- A kernel-checked zero-depth flat execution with a negative value, derived from the +general theorem rather than from VM evaluation. All calling/width premises are discharged. -/ +example : ∃ y : Data 32, ∃ n, n ≤ 241 ∧ + Executes 8 ((program false).program output) + (State.init (inputL (fun _ : LeftStr 0 => -3))) 3 n ∧ y.mem 3 = 7 := by + obtain ⟨_, y, n, hn, _, he, hv, _⟩ := runs_left (w := 6) (by decide) + (fun _ : LeftStr 0 => -3) 3 (by decide) (by intro u; decide) (by decide) (by decide) + refine ⟨y, n + 1, ?_, he, ?_⟩ + · norm_num [timeBound] at hn + omega + · have hidx : (leafIndex (Fin.elim0 : Leaf 0)).val = 0 := by + have := (leafIndex (Fin.elim0 : Leaf 0)).isLt + norm_num at this ⊢ + have h := hv (Fin.elim0 : Leaf 0) + have hval : encodingL (fun _ : LeftStr 0 => -3) Fin.elim0 = -3 := + encodeWith_zero phi _ _ + have henc : encode (-3) = 7 := by decide + simpa only [outputBase, pow_zero, hidx, hval, henc, Nat.reduceAdd] using h + +/-- A nonvacuous two-stage client of the complete flat execution theorem. The checked signed +array has a zero `P0,P0` leaf through cancellation and also many nonzero leaves in the VM test. -/ +example : ∃ y : Data 32, ∃ n, n ≤ 72001 ∧ + Executes 18 ((program false).program output) (State.init (inputL signedLeft)) 51 n ∧ + y.mem (outputBase 2 + (leafIndex (fun _ : Fin 2 => Term.P0)).val) = 0 := by + obtain ⟨_, y, n, hn, _, he, hv, _⟩ := runs_left (w := 16) (by decide) + signedLeft 3 (by decide) (by decide +kernel) (by decide) (by decide) + refine ⟨y, n + 1, ?_, he, ?_⟩ + · norm_num [timeBound] at hn + omega + · have hmath : encodingL signedLeft (fun _ : Fin 2 => Term.P0) = 0 := by decide +kernel + simpa only [hmath, show encode 0 = 0 from rfl] using hv (fun _ : Fin 2 => Term.P0) + +end ComplexityTests.SchonhageEncoding diff --git a/NOTICE b/NOTICE index 65eb16f1c..a28ee14dd 100644 --- a/NOTICE +++ b/NOTICE @@ -1,8 +1,9 @@ -The 3SUM/Exact Triangle reductions, finite counting/tensor lemmas and native matrix builder in +The 3SUM/Exact Triangle reductions, finite counting/tensor lemmas and native programs in Complexity/Algorithms/ExactTriangle/{Defs,Weights,Lopsided,Hashing,Splitting,Witnesses}.lean, Complexity/Algorithms/MatrixMultiplication/Schonhage/, Complexity/Algorithms/ThreeSum/, Complexity/Algorithms/WordRAM/ThreeSum/, +Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/, Complexity/Foundations/Algebra/{FiniteMagnitude,GeometricTail}.lean, Complexity/Foundations/Combinatorics/{BinomialUpperBound,FunctionPlaces}.lean, Complexity/Foundations/Data/Finset/{Average,OrderGlue,Quotient}.lean, diff --git a/ROADMAP.md b/ROADMAP.md index 5805e1216..7e50f2a31 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -428,8 +428,11 @@ each statement against the numbered result. Organize modules by subject, not cha shared encodings and pruned-evaluation core ([#1459]) include exact visited-leaf and passed-set bounds, with the empty zero-depth root handled explicitly. The finite thin-product component ([#1463]) proves valid layouts, arbitrary-size padding/tiling, exact queried signed products and - sparse encoding/leaf/passed-set totals. These counts are not native instruction costs. It still - needs native tensor/sparse-query programs, native Chan–He preprocessing, the reduction + sparse encoding/leaf/passed-set totals. Those counts are not native instruction costs. + The shared native left/right encoders ([#1493]) compute all `10^L` leaf values from `7^L` + signed inputs within `240*(L+1)*10^L` standard WordRAM instructions, plus the final halt, + with fixed code/registers and concrete initialized inputs, frames and explicit width bounds. + Band/tile preparation, native pruned/sparse-query programs, native Chan–He preprocessing, the reduction host, the fast Exact Triangle solver and full input/time/width/asymptotic composition, then the improved Section 4 route to exponent 1.9992; size-based width @@ -560,3 +563,6 @@ not planned, not as proved. [#1442]: https://github.com/cslib-community/complexitylib/issues/1442 [#1447]: https://github.com/cslib-community/complexitylib/issues/1447 [#1449]: https://github.com/cslib-community/complexitylib/issues/1449 +[#1459]: https://github.com/cslib-community/complexitylib/issues/1459 +[#1463]: https://github.com/cslib-community/complexitylib/issues/1463 +[#1493]: https://github.com/cslib-community/complexitylib/issues/1493 diff --git a/UPSTREAM.md b/UPSTREAM.md index dde42fa02..87a5bc5c2 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -206,8 +206,22 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he from exact contributing-leaf counts. Generic function-position counts, ordered gluing, binomial and finite-sum bounds live in small Foundations modules. Mathlib `Finset.orderEmbOfFin` indexes both a level set and its complement, and `Finset.piecewise` restricts answers. No source machine, - problem-statement universe or solver/time hypothesis is imported. Native tensor/sparse-query - programs, native preprocessing/solvers and full input/time/width composition, including the + problem-statement universe or solver/time hypothesis is imported. + `Algorithms/WordRAM/MatrixMultiplication/Schonhage/` implements the complete shared left/right + encoding milestone ([#1493](https://github.com/cslib-community/complexitylib/issues/1493)). + Fixed standard-WordRAM code with 32 registers transforms one coordinate at a time between + two disjoint `10^L` buffers. It starts from `State.init` with a depth header and `7^L` + sign-and-magnitude words, computes all public `encodingL`/`encodingR` values, and preserves + the input and every cell outside the two buffers. Mathlib's `finFunctionFinEquiv` supplies + least-significant-first string numbering. All stages, powers, indices, address arithmetic, + signed add/subtract blocks and the final output copy cost at most `240*(L+1)*10^L` + instructions; halt adds one. Signed operands have magnitude bounded from the original + input by `7^L*A < 2^(w-1)`; the explicit natural bound `L+64+64*10^L` must fit target width + `w+2`. The two extra bits and actual arithmetic-block costs come from the existing native + signed kernels, without a copied source machine or a whole-program simulation hypothesis. + The existing array-copy interface is retained, with a narrow generalization for disjoint + blocks in either address order or identical blocks. Band/tile preparation, native pruned + and sparse-query programs, native preprocessing/solvers and full input/time/width composition, including the improved Section 4 route to exponent 1.9992, remain in #1394. * **Typed RAM upstream core.** [CSLib #1158](https://github.com/leanprover/cslib/pull/1158) at `beea60a15eddc91d93e92fa5af9fb965c351d0d5` proposes From 3b9234da6bc3c0daad2cd167aeb4fd8f9fc592b6 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 13:01:20 -0400 Subject: [PATCH 19/73] test(wordram): connect encoder witnesses to charged runs Connect the zero-depth and two-stage regression memory witnesses to the actual prefix Runs already proved by runs_left, rather than discarding that conjunct. Bound the prefixes by 240 and 72000 instructions and expose the same executions' charged halt at n+1. All library code is unchanged from ad3e09735a116f9868e87cf7ae6af59e5cb6a285 (tree b598513f02cc064eaf50d3c680684c19289ffd71). Validation of tree 8277017f2c63a5851b63a5d4b49775a9fb96d62d against actual parent b4936ce28d8b498b364a7947d34f728c55f8b226: all six gates pass together (build --wfail --iofail, mk_all --check, test --wfail --iofail, lint, lint-style, checkInitImports), full citation-aware hygiene --check-simp, fresh changed-file lints (13 library modules, one test), strict PR-draft declaration-name check and staged/unstaged diff checks pass. Independent nonauthor agent review 8034f3da approved exact ad3e09735/tree b598513f, covering full code/model/cost/width/source and ArrayCopy with no blockers. It is inside-fleet review, not outside approval. This test-only delta addresses its optional witness-connection finding; focused final test/source-mapping review remains pending through the parent coordinator. No selected abstract, catalogue or rendering impact. The full ordinary native 3SUM objective #1394 remains open; this implements encoder issue #1493. AI assistance: OpenAI Codex implementation/self-review by delegated worker codex-shared-encoding-t3-03b600bd. Publication remains on hold for focused review; this commit is not approval or a whole-3SUM completion claim. --- ComplexityTests/SchonhageEncoding.lean | 27 ++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/ComplexityTests/SchonhageEncoding.lean b/ComplexityTests/SchonhageEncoding.lean index e03bb9f01..6f9d10937 100644 --- a/ComplexityTests/SchonhageEncoding.lean +++ b/ComplexityTests/SchonhageEncoding.lean @@ -106,14 +106,15 @@ example : ¬Fits 9 (addressBound 1) ∧ Fits 10 (addressBound 1) := by decide /-- A kernel-checked zero-depth flat execution with a negative value, derived from the general theorem rather than from VM evaluation. All calling/width premises are discharged. -/ -example : ∃ y : Data 32, ∃ n, n ≤ 241 ∧ +example : ∃ y : Data 32, ∃ n, n ≤ 240 ∧ + Runs 8 ((program false).program output) + (State.init (inputL (fun _ : LeftStr 0 => -3))) ⟨(program false).size, y⟩ n ∧ Executes 8 ((program false).program output) - (State.init (inputL (fun _ : LeftStr 0 => -3))) 3 n ∧ y.mem 3 = 7 := by - obtain ⟨_, y, n, hn, _, he, hv, _⟩ := runs_left (w := 6) (by decide) + (State.init (inputL (fun _ : LeftStr 0 => -3))) 3 (n + 1) ∧ y.mem 3 = 7 := by + obtain ⟨_, y, n, hn, hr, he, hv, _⟩ := runs_left (w := 6) (by decide) (fun _ : LeftStr 0 => -3) 3 (by decide) (by intro u; decide) (by decide) (by decide) - refine ⟨y, n + 1, ?_, he, ?_⟩ - · norm_num [timeBound] at hn - omega + refine ⟨y, n, ?_, hr, he, ?_⟩ + · simpa only [timeBound, pow_zero, Nat.reduceAdd, Nat.mul_one] using hn · have hidx : (leafIndex (Fin.elim0 : Leaf 0)).val = 0 := by have := (leafIndex (Fin.elim0 : Leaf 0)).isLt norm_num at this ⊢ @@ -125,14 +126,16 @@ example : ∃ y : Data 32, ∃ n, n ≤ 241 ∧ /-- A nonvacuous two-stage client of the complete flat execution theorem. The checked signed array has a zero `P0,P0` leaf through cancellation and also many nonzero leaves in the VM test. -/ -example : ∃ y : Data 32, ∃ n, n ≤ 72001 ∧ - Executes 18 ((program false).program output) (State.init (inputL signedLeft)) 51 n ∧ +example : ∃ y : Data 32, ∃ n, n ≤ 72000 ∧ + Runs 18 ((program false).program output) (State.init (inputL signedLeft)) + ⟨(program false).size, y⟩ n ∧ + Executes 18 ((program false).program output) (State.init (inputL signedLeft)) 51 (n + 1) ∧ y.mem (outputBase 2 + (leafIndex (fun _ : Fin 2 => Term.P0)).val) = 0 := by - obtain ⟨_, y, n, hn, _, he, hv, _⟩ := runs_left (w := 16) (by decide) + obtain ⟨_, y, n, hn, hr, he, hv, _⟩ := runs_left (w := 16) (by decide) signedLeft 3 (by decide) (by decide +kernel) (by decide) (by decide) - refine ⟨y, n + 1, ?_, he, ?_⟩ - · norm_num [timeBound] at hn - omega + refine ⟨y, n, ?_, hr, he, ?_⟩ + · norm_num [timeBound] at hn ⊢ + exact hn · have hmath : encodingL signedLeft (fun _ : Fin 2 => Term.P0) = 0 := by decide +kernel simpa only [hmath, show encode 0 = 0 from rfl] using hv (fun _ : Fin 2 => Term.P0) From 46e9e72f1f9b3db16efbb5dc05a58b4b177ec590 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 15:00:55 -0400 Subject: [PATCH 20/73] feat(wordram): checkpoint native pruned-query controller proofs Work in progress for #1526. Fixed native controller and initialized-input interpreter regressions, numerical child-set bridge, call-count potential, charged power/scan/clear/frame/arithmetic/return kernels. Individual current Lean modules have clean MCP diagnostics; the earlier strict targeted scan/count/value/test checkpoint passed. The complete global controller invariant, native evaluator theorem, actual initialized flat-run clients and sparse charged bound remain unfinished. No full six-gate or independent review claim; no PR submission. Full #1394 endpoint remains open. AI-assisted implementation by codex-three-sum-continuation-t3-3dda8289. --- .../Schonhage/PrunedCounts.lean | 149 +++++++++ .../Schonhage/Pruned/Add.lean | 77 +++++ .../Schonhage/Pruned/Clear.lean | 153 +++++++++ .../Schonhage/Pruned/Codes.lean | 158 +++++++++ .../Schonhage/Pruned/Control.lean | 89 ++++++ .../Schonhage/Pruned/Emit.lean | 81 +++++ .../Schonhage/Pruned/Frame.lean | 139 ++++++++ .../Schonhage/Pruned/Leaf.lean | 68 ++++ .../Schonhage/Pruned/Memory.lean | 64 ++++ .../Schonhage/Pruned/Powers.lean | 117 +++++++ .../Schonhage/Pruned/Program.lean | 299 ++++++++++++++++++ .../Schonhage/Pruned/Scan.lean | 166 ++++++++++ .../Schonhage/Pruned/ScanInsert.lean | 90 ++++++ .../Schonhage/Pruned/ScanMemory.lean | 117 +++++++ .../Schonhage/Pruned/ScanSet.lean | 92 ++++++ .../Schonhage/Pruned/ScanStep.lean | 197 ++++++++++++ .../Schonhage/Pruned/Values.lean | 160 ++++++++++ .../Schonhage/Pruned/Weight.lean | 83 +++++ .../Computability/WordRAM/ArrayAppend.lean | 36 +++ ComplexityTests/SchonhagePruned.lean | 45 +++ 20 files changed, 2380 insertions(+) create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/PrunedCounts.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Add.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Clear.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Codes.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Control.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Emit.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Frame.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Leaf.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Memory.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Powers.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Scan.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanInsert.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanMemory.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanSet.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanStep.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Values.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Weight.lean create mode 100644 Complexity/Computability/WordRAM/ArrayAppend.lean create mode 100644 ComplexityTests/SchonhagePruned.lean diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/PrunedCounts.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/PrunedCounts.lean new file mode 100644 index 000000000..2fb1204b9 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/PrunedCounts.lean @@ -0,0 +1,149 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Pruned + +/-! +# Counts of nonempty calls in the finite pruned recursion + +## Main results + +* `Pruned.calls_irrel`: the call trace depends only on the requests, not the encoded values. +* `Pruned.card_calls_le_totalSize_add_one`: requested-set cardinalities pay for every call, + with one extra root for the empty-query boundary. + +These are combinatorial facts about the existing finite trace. They do not assign an instruction +cost to the recursion. Native execution bounds must separately charge their actual instructions. + +## References + +The trace is from [AlmanWilliams2026, Section 2.4.2] and Anthropic's formal-math revision +`16a5ce41304fcdaea4e0be7c132e1c361dc26da5`, [pinned source](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2/Lemma10.lean). +-/ + +@[expose] public section + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage + +open Finset + +/-- An empty request set has no child requests. -/ +@[simp] +theorem childSet_empty {n : ℕ} (lam : Term) : childSet (∅ : Finset (OutStr (n + 1))) lam = ∅ := by + ext w + simp [mem_childSet] + +/-- Values of the two encoded arrays do not affect which calls are made. -/ +theorem Pruned.calls_irrel {n : ℕ} (a b a' b' : Leaf n → ℤ) (S : Finset (OutStr n)) : + Pruned.calls n a b S = Pruned.calls n a' b' S := by + induction n with + | zero => rfl + | succ n ih => + rw [Pruned.calls_succ, Pruned.calls_succ] + apply congrArg (Multiset.cons (⟨⟨0, Fin.elim0⟩, ⟨n + 1, S⟩⟩ : PrunedCall)) + apply congrArg (fun f => univ.val.bind f) + funext lam + by_cases h : (childSet S lam).Nonempty + · simp only [Pruned.childCalls, h, ↓reduceIte] + exact congrArg (Multiset.map (PrunedCall.under lam)) (ih _ _ _ _ _) + · simp only [Pruned.childCalls, h, ↓reduceIte] + +/-- One root call and all nonempty child traces, counted with their multiplicities. -/ +theorem Pruned.card_calls_succ {n : ℕ} (a b : Leaf (n + 1) → ℤ) + (S : Finset (OutStr (n + 1))) : + (Pruned.calls (n + 1) a b S).card = 1 + ∑ lam, + if (childSet S lam).Nonempty then + (Pruned.calls n (sliceAt a lam) (sliceAt b lam) (childSet S lam)).card else 0 := by + rw [Pruned.calls_succ, Multiset.card_cons, Multiset.card_bind] + rw [Nat.add_comm] + apply congrArg (Nat.add 1) + apply sum_congr rfl + intro lam _ + simp only [Function.comp_apply, Multiset.card_map, Pruned.childCalls] + split_ifs <;> rfl + +/-- The passed-set total is the root size plus the totals of its nonempty children. -/ +theorem Pruned.totalSize_succ {n : ℕ} (a b : Leaf (n + 1) → ℤ) + (S : Finset (OutStr (n + 1))) : + Pruned.totalSize a b S = S.card + ∑ lam, + if (childSet S lam).Nonempty then + Pruned.totalSize (sliceAt a lam) (sliceAt b lam) (childSet S lam) else 0 := by + unfold Pruned.totalSize + rw [Pruned.calls_succ, Multiset.map_cons, Multiset.sum_cons, Multiset.map_bind, + Multiset.sum_bind] + apply congrArg (Nat.add S.card) + apply sum_congr rfl + intro lam _ + by_cases h : (childSet S lam).Nonempty <;> + simp only [Pruned.childCalls, h, ↓reduceIte, Multiset.map_map, + Multiset.map_zero, Multiset.sum_zero] + all_goals rfl + +/-- A nonempty call carries at least one query, so the passed-set total pays for every call. -/ +theorem Pruned.card_calls_le_totalSize {n : ℕ} (a b : Leaf n → ℤ) + (S : Finset (OutStr n)) (hS : S.Nonempty) : + (Pruned.calls n a b S).card ≤ Pruned.totalSize a b S := by + induction n with + | zero => simpa [Pruned.calls, Pruned.run, Pruned.totalSize] using one_le_card.mpr hS + | succ n ih => + rw [Pruned.card_calls_succ, Pruned.totalSize_succ] + apply Nat.add_le_add (one_le_card.mpr hS) + apply sum_le_sum + intro lam _ + split_ifs with h + · exact ih _ _ _ h + · exact le_rfl + +/-- Empty requests make only the root call, including at zero depth. -/ +theorem Pruned.card_calls_empty {n : ℕ} (a b : Leaf n → ℤ) : + (Pruned.calls n a b ∅).card = 1 := by + cases n with + | zero => rfl + | succ n => simp [Pruned.card_calls_succ] + +/-- Every call is paid for by a requested-set element, except possibly an empty root. -/ +theorem Pruned.card_calls_le_totalSize_add_one {n : ℕ} (a b : Leaf n → ℤ) + (S : Finset (OutStr n)) : + (Pruned.calls n a b S).card ≤ Pruned.totalSize a b S + 1 := by + rcases S.eq_empty_or_nonempty with rfl | hS + · rw [Pruned.card_calls_empty] + omega + · exact (Pruned.card_calls_le_totalSize a b S hS).trans (Nat.le_add_right _ _) + +/-- Encoded values do not affect the total size of the request sets. -/ +theorem Pruned.totalSize_irrel {n : ℕ} (a b a' b' : Leaf n → ℤ) (S : Finset (OutStr n)) : + Pruned.totalSize a b S = Pruned.totalSize a' b' S := by + unfold Pruned.totalSize + rw [Pruned.calls_irrel a b a' b' S] + +/-- Even without pruning, a ten-way depth-`n` tree has fewer than twice `10^n` nodes. +This ordinary count supplies a word-size bound for native timestamp generations. -/ +theorem Pruned.card_calls_add_one_le {n : ℕ} (a b : Leaf n → ℤ) + (S : Finset (OutStr n)) : + (Pruned.calls n a b S).card + 1 ≤ 2 * 10 ^ n := by + induction n with + | zero => rfl + | succ n ih => + let f : Term → ℕ := fun lam => if (childSet S lam).Nonempty then + (Pruned.calls n (sliceAt a lam) (sliceAt b lam) (childSet S lam)).card else 0 + have hpoint (lam : Term) : f lam + 1 ≤ 2 * 10 ^ n := by + dsimp only [f] + split_ifs + · exact ih _ _ _ + · have hp : 0 < (10 : ℕ) ^ n := Nat.pow_pos (by decide) + omega + have hsum := sum_le_sum (s := univ) (fun lam _ => hpoint lam) + have hten : Fintype.card Term = 10 := by decide + simp only [sum_add_distrib, sum_const, card_univ, hten, smul_eq_mul] at hsum + rw [Pruned.card_calls_succ] + change 1 + (∑ lam, f lam) + 1 ≤ _ + rw [pow_succ] + omega + +end Cslib.Algorithms.MatrixMultiplication.Schonhage diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Add.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Add.lean new file mode 100644 index 000000000..6f2a5333d --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Add.lean @@ -0,0 +1,77 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Memory + +/-! +# Charged consumption of a returned child value + +## Main results + +* `PrunedQuery.exec_addValue`: compute the child suffix address, load both signed words, call + the existing signed-add kernel, and store the sum at the parent query address. + +The instruction bound includes all indexed accesses and the native arithmetic kernel. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- The signed workspace and two address-calculation registers used by one accumulation. -/ +def AddValueLocal (i : Fin registers) : Prop := i.val < 9 ∨ i = address ∨ i = suffix + +instance (i : Fin registers) : Decidable (AddValueLocal i) := + inferInstanceAs (Decidable (i.val < 9 ∨ i = address ∨ i = suffix)) + +private theorem addValue_preserves (i : Fin registers) : + ¬AddValueLocal i → addValue.Preserves i := by decide +kernel +revert + +/-- All twenty-two instructions are native standard-basis operations. Both operands must fit +the source signed width; their sum is computed exactly by the existing two-extra-bit kernel. -/ +theorem exec_addValue {w : ℕ} {a b : ℤ} (ha : GenericRAM.Signed.Fits w a) + (hb : GenericRAM.Signed.Fits w b) (x : Data registers) (q : ℕ) + (hq : x.regs query = q) (h10 : x.regs ten = 10) + (hleft : x.mem (x.regs valueBase + q) = encode a) + (hright : x.mem (x.regs childValues + q / 10) = encode b) + (hp : Fits (w + 2) (x.regs valueBase + q)) + (hc : Fits (w + 2) (x.regs childValues + q / 10)) : + ∃ y, ExecWithin (w + 2) addValue x y 22 ∧ + y.mem = Function.update x.mem (x.regs valueBase + q) (encode (a + b)) ∧ + (∀ i, ¬AddValueLocal i → y.regs i = x.regs i) := by + let z := (((((x.set suffix (q / 10)).set address (x.regs valueBase + q)).set + left (encode a)).set address (x.regs childValues + q / 10)).set right (encode b)) + have hquot : Fits (w + 2) (q / 10) := hc.of_le (by omega) + have hload : ExecWithin (w + 2) addValueLoad x z 5 := by + ram_eval [addValueLoad, z, hq, h10, hleft, hright, hp, hc, hquot, suffix, address, + valueBase, query, ten, childValues, left, right, Fin.mk.injEq] + obtain ⟨u, hu, hsum, hframe⟩ := Signed.exec_add (r := 87) ha hb z + (by simp +decide [z, left, right, address]) (by simp [z]) + have hreg (i : Fin registers) (hi : 9 ≤ i.val) (hs : i ≠ suffix) (hd : i ≠ address) : + u.regs i = x.regs i := by + rw [hframe.2 i (Or.inr hi)] + have hl : i ≠ left := by intro he; subst he; exact Nat.not_le_of_lt (by decide) hi + have hr : i ≠ right := by intro he; subst he; exact Nat.not_le_of_lt (by decide) hi + simp only [z, Data.set_regs, Function.update_of_ne hr, Function.update_of_ne hd, + Function.update_of_ne hl, Function.update_of_ne hs] + have huv := hreg valueBase (by decide) (by decide) (by decide) + have huq := (hreg query (by decide) (by decide) (by decide)).trans hq + have hsum' : u.regs result = encode (a + b) := hsum + let y := (u.set address (x.regs valueBase + q)).write + (x.regs valueBase + q) (encode (a + b)) + have hstore : ExecWithin (w + 2) addValueStore u y 2 := by + ram_eval [addValueStore, y, huv, huq, hsum', hp, result, address, Fin.mk.injEq] + have he : ExecWithin (w + 2) addValue x y 22 := hload.seq (hu.seq hstore) + refine ⟨y, he, ?_, fun i hi => he.regs_eq (addValue_preserves i hi)⟩ + simpa only [y, Data.write_mem, Data.set_mem, z] using + congrArg (fun mem => Function.update mem (x.regs valueBase + q) (encode (a + b))) hframe.1 + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Clear.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Clear.lean new file mode 100644 index 000000000..9987b30f9 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Clear.lean @@ -0,0 +1,153 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Memory + +/-! +# Sparse initialization of requested accumulators + +## Main definitions + +* `PrunedQuery.zeroQueryPrefix`: the memory after zeroing a listed prefix of query addresses. + +## Main results + +* `PrunedQuery.exec_clearRequested`: actual indexed clearing within `7 N + 3` instructions. + +Repeated requests are permitted. Unlisted cells are preserved, and no dense value directory +is initialized by this routine. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Finset + +/-- Clear precisely the addresses named in the processed prefix of an explicit query list. -/ +def zeroQueryPrefix (mem : ℕ → ℕ) (base : ℕ) (input : ℕ → ℕ) (k : ℕ) : ℕ → ℕ := + fun a => if a ∈ (range k).image (fun i => base + input i) then 0 else mem a + +/-- Clearing no requests leaves memory unchanged. -/ +@[simp] +theorem zeroQueryPrefix_zero (mem : ℕ → ℕ) (base : ℕ) (input : ℕ → ℕ) : + zeroQueryPrefix mem base input 0 = mem := by ext a; simp [zeroQueryPrefix] + +/-- One additional listed request is exactly one memory store, including repeated requests. -/ +theorem zeroQueryPrefix_succ (mem : ℕ → ℕ) (base : ℕ) (input : ℕ → ℕ) (k : ℕ) : + zeroQueryPrefix mem base input (k + 1) = + Function.update (zeroQueryPrefix mem base input k) (base + input k) 0 := by + ext a + by_cases ha : a = base + input k <;> + simp [zeroQueryPrefix, range_add_one, image_insert, ha] + +/-- Every cell outside the concrete value directory is preserved by a bounded prefix. -/ +theorem zeroQueryPrefix_outside {mem : ℕ → ℕ} {base capacity k : ℕ} {input : ℕ → ℕ} + (hcode : ∀ i < k, input i < capacity) {a : ℕ} + (ha : ¬(Block.mk base capacity).Owns a) : zeroQueryPrefix mem base input k a = mem a := by + have hnot : a ∉ (range k).image (fun i => base + input i) := by + rintro h + obtain ⟨i, hi, rfl⟩ := mem_image.mp h + apply ha + change base ≤ base + input i ∧ base + input i < base + capacity + have := hcode i (mem_range.mp hi) + omega + simp only [zeroQueryPrefix, hnot, ↓reduceIte] + +/-- Registers modified by sparse clearing. -/ +def ClearLocal (i : Fin registers) : Prop := i = address ∨ i = query ∨ i = index + +instance (i : Fin registers) : Decidable (ClearLocal i) := + inferInstanceAs (Decidable (i = address ∨ i = query ∨ i = index)) + +private theorem clearStep_preserves (i : Fin registers) : + ¬ClearLocal i → clearValueStep.Preserves i := by decide +kernel +revert + +private theorem clear_preserves (i : Fin registers) : + ¬ClearLocal i → clearRequested.Preserves i := by decide +kernel +revert + +/-- Each requested word costs an indexed source load, indexed zero store and increment. -/ +theorem exec_clearValueStep {w : ℕ} (x : Data registers) (q : ℕ) + (hq : x.mem (x.regs listBase + x.regs index) = q) + (h0 : x.regs nil = 0) (h1 : x.regs one = 1) + (hr : Fits w (x.regs listBase + x.regs index)) + (hv : Fits w (x.regs valueBase + q)) (hi : Fits w (x.regs index + 1)) : + ∃ y, ExecWithin w clearValueStep x y 5 ∧ y.regs index = x.regs index + 1 ∧ + y.mem = Function.update x.mem (x.regs valueBase + q) 0 ∧ + (∀ i, ¬ClearLocal i → y.regs i = x.regs i) := by + let y := (((((x.set address (x.regs listBase + x.regs index)).set query q).set + address (x.regs valueBase + q)).write (x.regs valueBase + q) 0).set + index (x.regs index + 1)) + have he : ExecWithin w clearValueStep x y 5 := by + ram_eval [clearValueStep, y, hq, h0, h1, hr, hv, hi, address, query, index, listBase, + valueBase, nil, one, Fin.mk.injEq] + exact ⟨y, he, by simp [y], rfl, fun i hi => he.regs_eq (clearStep_preserves i hi)⟩ + +/-- The fixed native clearing loop visits only explicit requests. Width follows from concrete +source/destination endpoints and lengths, and source preservation follows from their separation. -/ +theorem exec_clearRequested {w N capacity : ℕ} (x : Data registers) (input : ℕ → ℕ) + (hN : x.regs count = N) + (hinput : ∀ i < N, x.mem (x.regs listBase + i) = input i) + (hcode : ∀ i < N, input i < capacity) + (hsep : x.regs listBase + N ≤ x.regs valueBase ∨ + x.regs valueBase + capacity ≤ x.regs listBase) + (h0 : x.regs nil = 0) (h1 : x.regs one = 1) + (hw : Fits w (x.regs listBase + N + x.regs valueBase + capacity + N + 2)) : + ∃ y, ExecWithin w clearRequested x y (7 * N + 3) ∧ y.regs index = N ∧ + y.mem = zeroQueryPrefix x.mem (x.regs valueBase) input N ∧ + (∀ i < N, y.mem (x.regs listBase + i) = input i) ∧ + (∀ i, ¬ClearLocal i → y.regs i = x.regs i) := by + let x0 := x.set index 0 + let I (y : Data registers) : Prop := y.regs index ≤ N ∧ + y.mem = zeroQueryPrefix x.mem (x.regs valueBase) input (y.regs index) ∧ + (∀ i, ¬ClearLocal i → y.regs i = x.regs i) + have hinit : I x0 := by + refine ⟨by simp [x0], by simp [x0], ?_⟩ + intro i hi + have hne : i ≠ index := by intro h; subst h; exact hi (Or.inr (Or.inr rfl)) + simp [x0, hne] + have hread (y : Data registers) (hy : I y) (i : ℕ) (hi : i < N) : + y.mem (x.regs listBase + i) = input i := by + rw [hy.2.1, zeroQueryPrefix_outside (fun j hj => hcode j (by omega))] + · exact hinput i hi + · change ¬(x.regs valueBase ≤ x.regs listBase + i ∧ + x.regs listBase + i < x.regs valueBase + capacity) + rcases hsep with h | h <;> omega + have hstep (y : Data registers) (hy : I y) (hc : (Cond.lt index count).Holds y) : + ∃ z, ExecWithin w clearValueStep y z 5 ∧ I z ∧ + N - z.regs index < N - y.regs index := by + obtain ⟨hle, hm, hf⟩ := hy + have hcount := (hf count (by decide)).trans hN + have hb := hf listBase (by decide) + have hv := hf valueBase (by decide) + have hnil := (hf nil (by decide)).trans h0 + have hone := (hf one (by decide)).trans h1 + have hk : y.regs index < N := by simpa only [Cond.holds_lt, hcount] using hc + have hq := hcode (y.regs index) hk + obtain ⟨z, hz, hi, hmem, hregs⟩ := exec_clearValueStep y (input (y.regs index)) + (by rw [hb]; exact hread y ⟨hle, hm, hf⟩ _ hk) hnil hone + (hw.of_le (by rw [hb]; omega)) (hw.of_le (by rw [hv]; omega)) (hw.of_le (by omega)) + refine ⟨z, hz, ⟨by omega, ?_, fun i hi => (hregs i hi).trans (hf i hi)⟩, by omega⟩ + rw [hmem, hm, hv, hi, zeroQueryPrefix_succ] + obtain ⟨y, hy, hIy, hstop⟩ := ExecWithin.loop_invariant I (fun y => N - y.regs index) + hstep hinit + have hcount := (hIy.2.2 count (by decide)).trans hN + have hindex : y.regs index = N := by + simp only [Cond.holds_lt, hcount, not_lt] at hstop + omega + have hsetup : ExecWithin w (ram[ index ← #0 ] : Code .standard registers) x x0 1 := by + ram_eval [x0] + have hloop : ExecWithin w (Code.loop (.lt index count) clearValueStep) x0 y (7 * N + 2) := by + simpa only [x0, Data.set_regs, Function.update_self, Nat.sub_zero, Nat.mul_comm] using hy + have he : ExecWithin w clearRequested x y (7 * N + 3) := + (hsetup.seq hloop).mono (by omega) + refine ⟨y, he, hindex, ?_, hread y hIy, fun i hi => he.regs_eq (clear_preserves i hi)⟩ + simpa only [hindex] using hIy.2.1 + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Codes.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Codes.lean new file mode 100644 index 000000000..dbaf0bada --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Codes.lean @@ -0,0 +1,158 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Pruned +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Meaning + +/-! +# Numerical query codes for native pruned evaluation + +The output variables use the existing term numbering through their private terms: the nine +outer outputs have digits zero through eight and the inner output has digit nine. The first +coordinate is the least significant digit, exactly as for the encoded leaf arrays. + +## Main definitions + +* `PrunedQuery.queryIndex`: the flat code of an output string. +* `PrunedQuery.childCodes`: the child request set, computed by digit tests and division. + +## Main results + +* `PrunedQuery.queryIndex_privateLeaf`: agreement with the existing leaf-array indexing. +* `PrunedQuery.queryIndex_cons`: the numerical first-digit decomposition. + +## References + +The child-set definition is adapted from [AlmanWilliams2026, Section 2.4.2] and Anthropic's +formal source at revision `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`, +[pinned source](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2/Lemma10.lean). These are finite coding identities, not native run claims. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Output digits follow their private terms in the existing encoder's numbering. -/ +def outputDigits : Fin 10 ≃ OutVar where + toFun t := match Encoding.termAt t with + | .P i j => .z i j + | .P0 => .z0 + invFun z := Encoding.termDigits.symm z.privateTerm + left_inv := by decide + right_inv := by decide + +/-- The least-significant-first flat code of an output string. -/ +def queryIndex {L : ℕ} (w : OutStr L) : Fin (10 ^ L) := + finFunctionFinEquiv fun i => outputDigits.symm (w i) + +/-- A query's code is the leaf-array address of its private leaf. -/ +theorem queryIndex_privateLeaf {L : ℕ} (w : OutStr L) : + queryIndex w = Encoding.leafIndex (privateLeaf w) := rfl + +/-- Every flat code denotes exactly one output string. -/ +theorem queryIndex_bijective (L : ℕ) : Function.Bijective (queryIndex (L := L)) := + finFunctionFinEquiv.bijective.comp + (Equiv.piCongrRight fun _ : Fin L => outputDigits.symm).bijective + +/-- The first query digit is its output-variable digit; the suffix is shifted by one place. -/ +theorem queryIndex_cons {L : ℕ} (z : OutVar) (w : OutStr L) : + (queryIndex (Fin.cons z w) : ℕ) = (outputDigits.symm z).val + 10 * (queryIndex w).val := by + simp only [queryIndex, finFunctionFinEquiv_apply, Fin.sum_univ_succ, Fin.cons_zero, + Fin.cons_succ, Fin.val_zero, pow_zero, mul_one, Fin.val_succ, pow_succ, Finset.mul_sum] + congr 1 + apply Finset.sum_congr rfl + intro i _ + ring + +/-- Taking the first numerical digit recovers the first output variable. -/ +theorem queryIndex_mod {L : ℕ} (w : OutStr (L + 1)) : + (queryIndex w).val % 10 = (outputDigits.symm (w 0)).val := by + have he := queryIndex_cons (w 0) (Fin.tail w) + rw [Fin.cons_self_tail] at he + rw [he] + have h := (outputDigits.symm (w 0)).isLt + omega + +/-- Integer division discards the first output coordinate. -/ +theorem queryIndex_div {L : ℕ} (w : OutStr (L + 1)) : + (queryIndex w).val / 10 = (queryIndex (Fin.tail w)).val := by + have he := queryIndex_cons (w 0) (Fin.tail w) + rw [Fin.cons_self_tail] at he + rw [he] + have h := (outputDigits.symm (w 0)).isLt + omega + +/-- A term contributes to the inner digit or to its own outer digit. -/ +theorem contributes_iff_digit (t : Fin 10) (z : OutVar) : + (Encoding.termDigits t).Contributes z ↔ + (outputDigits.symm z).val = 9 ∨ (outputDigits.symm z).val = t.val := by + decide +revert + +/-- Numerical child requests are selected by the actual digit tests, then shortened by division. +The finite-set image removes repeated suffixes. Native code will implement that deduplication. +-/ +def childCodes (S : Finset ℕ) (term : ℕ) : Finset ℕ := + (S.filter fun q => q % 10 = 9 ∨ q % 10 = term).image fun q => q / 10 + +/-- Child suffixes stay within the smaller numerical query space. -/ +theorem childCodes_bounded {n : ℕ} {S : Finset ℕ} (term : ℕ) + (hS : ∀ q ∈ S, q < 10 ^ (n + 1)) : + ∀ q ∈ childCodes S term, q < 10 ^ n := by + intro q hq + obtain ⟨p, hp, rfl⟩ := Finset.mem_image.mp hq + have h := hS p (Finset.mem_filter.mp hp).1 + apply (Nat.div_lt_iff_lt_mul (by decide : 0 < 10)).mpr + simpa only [pow_succ] using h + +/-- The numerical child requests agree exactly with the existing finite pruning definition. -/ +theorem mem_childCodes {L : ℕ} (S : Finset (OutStr (L + 1))) (t : Fin 10) (w : OutStr L) : + (queryIndex w).val ∈ childCodes (S.image fun v => (queryIndex v).val) t.val ↔ + w ∈ childSet S (Encoding.termDigits t) := by + constructor + · intro h + obtain ⟨q, hq, htail⟩ := Finset.mem_image.mp h + obtain ⟨hparent, hmatch⟩ := Finset.mem_filter.mp hq + obtain ⟨v, hv, rfl⟩ := Finset.mem_image.mp hparent + rw [queryIndex_div] at htail + have htw : Fin.tail v = w := (queryIndex_bijective L).injective (Fin.ext htail) + have hc : (Encoding.termDigits t).Contributes (v 0) := by + rw [contributes_iff_digit] + simpa only [queryIndex_mod] using hmatch + rw [mem_childSet] + exact ⟨v, hv, hc, htw⟩ + · intro h + obtain ⟨v, hv, hc, htail⟩ := mem_childSet.mp h + apply Finset.mem_image.mpr + refine ⟨(queryIndex v).val, Finset.mem_filter.mpr ⟨?_, ?_⟩, ?_⟩ + · exact Finset.mem_image.mpr ⟨v, hv, rfl⟩ + · rw [queryIndex_mod] + exact (contributes_iff_digit t (v 0)).mp hc + · rw [queryIndex_div, htail] + +/-- The whole native child-set specification is the numerical image of the existing child set. -/ +theorem childCodes_eq_image {L : ℕ} (S : Finset (OutStr (L + 1))) (t : Fin 10) : + childCodes (S.image fun w => (queryIndex w).val) t.val = + (childSet S (Encoding.termDigits t)).image (fun w => (queryIndex w).val) := by + ext q + constructor + · intro hq + have hbound := childCodes_bounded t.val (S := S.image fun w => (queryIndex w).val) + (fun p hp => by + obtain ⟨v, _, rfl⟩ := Finset.mem_image.mp hp + exact (queryIndex v).isLt) q hq + obtain ⟨w, hw⟩ := (queryIndex_bijective L).surjective ⟨q, hbound⟩ + have he : (queryIndex w).val = q := congrArg Fin.val hw + exact Finset.mem_image.mpr ⟨w, (mem_childCodes S t w).mp (he.symm ▸ hq), he⟩ + · intro hq + obtain ⟨w, hw, rfl⟩ := Finset.mem_image.mp hq + exact (mem_childCodes S t w).mpr hw + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Control.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Control.lean new file mode 100644 index 000000000..cd6e68387 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Control.lean @@ -0,0 +1,89 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Memory + +/-! +# Charged native return and term-advance transitions + +## Main results + +* `PrunedQuery.exec_pop`: return to the parent's accumulation phase or terminate the root. +* `PrunedQuery.exec_advance`: store the incremented term counter in the actual parent frame. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM + +/-- Registers changed by a return transition. -/ +def PopLocal (i : Fin registers) : Prop := i = active ∨ i = level ∨ i = mode + +instance (i : Fin registers) : Decidable (PopLocal i) := + inferInstanceAs (Decidable (i = active ∨ i = level ∨ i = mode)) + +private theorem pop_preserves (i : Fin registers) : ¬PopLocal i → pop.Preserves i := by + decide +kernel +revert + +/-- The return decision is the program's real comparison with zero. Both branches preserve +memory and all registers outside the three controller fields. -/ +theorem exec_pop {w : ℕ} (x : Data registers) (h0 : x.regs nil = 0) + (h1 : x.regs one = 1) (hw : Fits w (x.regs level + 2)) : + ∃ y, ExecWithin w pop x y 4 ∧ y.mem = x.mem ∧ + y.regs level = (if x.regs level = 0 then 0 else x.regs level - 1) ∧ + y.regs mode = (if x.regs level = 0 then x.regs mode else 2) ∧ + y.regs active = (if x.regs level = 0 then 0 else x.regs active) ∧ + (∀ i, ¬PopLocal i → y.regs i = x.regs i) := by + have h2 : Fits w 2 := hw.of_le (by omega) + have hd : Fits w (x.regs level - 1) := hw.of_le (by omega) + by_cases hroot : x.regs level = 0 + · let y := x.set active 0 + have he : ExecWithin w pop x y 2 := by + ram_eval [pop, y, hroot, h0] + refine ⟨y, he.mono (by decide), rfl, ?_, ?_, ?_, + fun i hi => he.regs_eq (pop_preserves i hi)⟩ + all_goals simp +decide [y, hroot, active, level, mode] + · let y := (x.set level (x.regs level - 1)).set mode 2 + have he : ExecWithin w pop x y 4 := by + ram_eval [pop, y, hroot, h0, h1, h2, hd, level, mode, one, Fin.mk.injEq] + refine ⟨y, he, rfl, ?_, ?_, ?_, fun i hi => he.regs_eq (pop_preserves i hi)⟩ + all_goals simp +decide [y, hroot, active, level, mode] + +/-- Registers changed by advancing and storing a term counter. -/ +def AdvanceLocal (i : Fin registers) : Prop := i = term ∨ i = address ∨ i = mode + +instance (i : Fin registers) : Decidable (AdvanceLocal i) := + inferInstanceAs (Decidable (i = term ∨ i = address ∨ i = mode)) + +private theorem advance_preserves (i : Fin registers) : + ¬AdvanceLocal i → advance.Preserves i := by decide +kernel +revert + +/-- The term increment, frame-word address computation, store and phase assignment take five +instructions. Only the fourth frame word changes in memory. -/ +theorem exec_advance {w : ℕ} (x : Data registers) (h1 : x.regs one = 1) + (h4 : x.regs frameWidth = 4) (ht : Fits w (x.regs term + 1)) + (hf : Fits w (x.regs frame + 4)) : + ∃ y, ExecWithin w advance x y 5 ∧ y.regs term = x.regs term + 1 ∧ + y.regs mode = 1 ∧ + y.mem = Function.update x.mem (x.regs frame + 3) (x.regs term + 1) ∧ + (∀ i, ¬AdvanceLocal i → y.regs i = x.regs i) := by + have hmode : Fits w 1 := ht.of_le (by omega) + have hadd : Fits w (x.regs frame + 3) := hf.of_le (by omega) + let y := (((((x.set term (x.regs term + 1)).set address (x.regs frame + 4)).set + address (x.regs frame + 3)).write (x.regs frame + 3) (x.regs term + 1)).set mode 1) + have hsub : x.regs frame + 4 - 1 = x.regs frame + 3 := by omega + have he : ExecWithin w advance x y 5 := by + ram_eval [advance, y, h1, h4, ht, hf, hmode, hadd, hsub, term, address, mode, one, + frame, frameWidth, Fin.mk.injEq] + refine ⟨y, he, ?_, by simp [y], rfl, fun i hi => he.regs_eq (advance_preserves i hi)⟩ + simp +decide [y, term, address, mode] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Emit.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Emit.lean new file mode 100644 index 000000000..75dcb9f0e --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Emit.lean @@ -0,0 +1,81 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Memory + +/-! +# Charged timestamp insertion for native child queries + +## Main results + +* `PrunedQuery.exec_emit`: one native membership test and optional unique suffix insertion, + within nine instructions, with the complete memory update and register frame. + +This kernel does not assume that a child request set was computed elsewhere. The surrounding +native scan supplies each tested suffix and uses the returned timestamp/list updates. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM + +/-- Exactly the registers a timestamp insertion can modify. -/ +def EmitLocal (i : Fin registers) : Prop := + i = address ∨ i = marker ∨ i = temporary ∨ i = childCount + +instance (i : Fin registers) : Decidable (EmitLocal i) := + inferInstanceAs (Decidable (i = address ∨ i = marker ∨ i = temporary ∨ i = childCount)) + +private theorem emit_preserves (i : Fin registers) : ¬EmitLocal i → emit.Preserves i := by + decide +kernel +revert + +/-- Timestamp insertion performs its real load, comparison, optional stores and increment. +The result states the exact memory update even before a caller supplies array separation. -/ +theorem exec_emit {w : ℕ} (x : Data registers) (h1 : x.regs one = 1) + (hs : Fits w (x.regs stamps + x.regs suffix)) + (hl : Fits w (x.regs childList + x.regs childCount)) + (hn : Fits w (x.regs childCount + 1)) : + ∃ y, ExecWithin w emit x y 9 ∧ + y.regs childCount = + (if x.mem (x.regs stamps + x.regs suffix) = x.regs epoch then + x.regs childCount else x.regs childCount + 1) ∧ + y.mem = + (if x.mem (x.regs stamps + x.regs suffix) = x.regs epoch then x.mem else + Function.update + (Function.update x.mem (x.regs childList + x.regs childCount) (x.regs suffix)) + (x.regs stamps + x.regs suffix) (x.regs epoch)) ∧ + (∀ i, ¬EmitLocal i → y.regs i = x.regs i) := by + let s := x.regs stamps + x.regs suffix + let z := (x.set address s).set marker (x.mem s) + by_cases h : x.mem s = x.regs epoch + · let y := z.set temporary (z.regs temporary) + have he : ExecWithin w emit x y 4 := by + ram_eval [emit, z, y, s, h, hs, address, stamps, suffix, marker, epoch, temporary, + childCount, childList, one, Fin.mk.injEq] + refine ⟨y, he.mono (by decide), ?_, ?_, fun i hi => he.regs_eq (emit_preserves i hi)⟩ + · simp only [s] at h + simp [y, z, h, childCount, address, marker, temporary] + · simp only [s] at h + simp [y, z, h] + · let y := + (((((z.set address (x.regs childList + x.regs childCount)).write + (x.regs childList + x.regs childCount) (x.regs suffix)).set address s).write + s (x.regs epoch)).set childCount (x.regs childCount + 1)) + have he : ExecWithin w emit x y 9 := by + ram_eval [emit, z, y, s, h, hs, hl, hn, h1, address, stamps, suffix, marker, epoch, + temporary, childCount, childList, one, Fin.mk.injEq] + refine ⟨y, he, ?_, ?_, fun i hi => he.regs_eq (emit_preserves i hi)⟩ + · simp only [s] at h + simp [y, h] + · simp only [s] at h + simp [y, z, h, s, Data.write_mem] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Frame.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Frame.lean new file mode 100644 index 000000000..816abb5b1 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Frame.lean @@ -0,0 +1,139 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Memory + +/-! +# Native stack layout and frame loading + +## Main results + +* `PrunedQuery.exec_locate`: compute the actual stack/list/value/timestamp region addresses. +* `PrunedQuery.exec_loadFrame`: load all four stored frame words and compute row addresses. + +Every index multiplication, pointer addition and load is an ordinary charged instruction. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM + +/-- Exactly the registers used to locate the concrete workspace regions. -/ +def LocateLocal (i : Fin registers) : Prop := + i = frames ∨ i = temporary ∨ i = temporary₂ ∨ i = lists ∨ i = values ∨ i = stamps + +instance (i : Fin registers) : Decidable (LocateLocal i) := + inferInstanceAs (Decidable + (i = frames ∨ i = temporary ∨ i = temporary₂ ∨ i = lists ∨ i = values ∨ i = stamps)) + +private theorem locate_preserves (i : Fin registers) : + ¬LocateLocal i → locate.Preserves i := by decide +kernel +revert + +/-- The seven actual instructions compute the workspace partition from depth and array size. -/ +theorem exec_locate {w : ℕ} (x : Data registers) (h1 : x.regs one = 1) + (h4 : x.regs frameWidth = 4) + (hw : Fits w (x.regs scratch + 4 * (x.regs inputDepth + 1) + + 2 * (x.regs inputDepth + 1) * x.regs span + x.regs inputDepth + 8)) : + ∃ y, ExecWithin w locate x y 7 ∧ y.mem = x.mem ∧ + y.regs frames = x.regs scratch ∧ + y.regs lists = x.regs scratch + 4 * (x.regs inputDepth + 1) ∧ + y.regs values = x.regs scratch + 4 * (x.regs inputDepth + 1) + + (x.regs inputDepth + 1) * x.regs span ∧ + y.regs stamps = x.regs scratch + 4 * (x.regs inputDepth + 1) + + 2 * (x.regs inputDepth + 1) * x.regs span ∧ + (∀ i, ¬LocateLocal i → y.regs i = x.regs i) := by + let d := x.regs inputDepth + 1 + let b := x.regs scratch + 4 * d + let v := b + d * x.regs span + let s := v + d * x.regs span + let y := (((((((x.set frames (x.regs scratch)).set temporary d).set temporary₂ (4 * d)).set + lists b).set temporary₂ (d * x.regs span)).set values v).set stamps s) + have hd : Fits w d := hw.of_le (by dsimp [d]; omega) + have harea : Fits w (4 * d) := hw.of_le (by dsimp [d]; omega) + have hb : Fits w b := hw.of_le (by dsimp [b, d]; omega) + have hds : Fits w (d * x.regs span) := hw.of_le (by dsimp [d]; nlinarith) + have hv : Fits w v := hw.of_le (by dsimp [v, b, d]; nlinarith) + have hs : Fits w s := hw.of_le (by dsimp [s, v, b, d]; nlinarith) + have he : ExecWithin w locate x y 7 := by + ram_eval [locate, y, d, b, v, s, h1, h4, hd, harea, hb, hds, hv, hs, + frames, temporary, temporary₂, lists, values, stamps, scratch, inputDepth, frameWidth, + span, one, Fin.mk.injEq, Nat.mul_comm] + refine ⟨y, he, rfl, ?_, ?_, ?_, ?_, fun i hi => he.regs_eq (locate_preserves i hi)⟩ + all_goals simp +decide [y, b, d, v, s, frames, temporary, temporary₂, lists, values, + stamps, two_mul, add_mul, Nat.add_assoc] + +/-- Registers written while loading one stack frame and computing the two depth rows. -/ +def LoadFrameLocal (i : Fin registers) : Prop := + i = frame ∨ i = address ∨ i = count ∨ i = leafPrefix ∨ i = stride ∨ i = term ∨ + i = temporary ∨ i = listBase ∨ i = valueBase ∨ i = nextLevel ∨ i = childList ∨ + i = childValues + +instance (i : Fin registers) : Decidable (LoadFrameLocal i) := + inferInstanceAs (Decidable + (i = frame ∨ i = address ∨ i = count ∨ i = leafPrefix ∨ i = stride ∨ i = term ∨ + i = temporary ∨ i = listBase ∨ i = valueBase ∨ i = nextLevel ∨ i = childList ∨ + i = childValues)) + +private theorem loadFrame_preserves (i : Fin registers) : + ¬LoadFrameLocal i → loadFrame.Preserves i := by decide +kernel +revert + +/-- Seventeen native instructions load the four words at the computed frame address and +locate the current and child rows. The memory words are read, not supplied as frame arguments. -/ +theorem exec_loadFrame {w : ℕ} (x : Data registers) (h1 : x.regs one = 1) + (h4 : x.regs frameWidth = 4) + (hw : Fits w (x.regs frames + 4 * (x.regs level + 1) + x.regs lists + + x.regs values + (x.regs level + 1) * x.regs span + x.regs level + 8)) : + ∃ y, ExecWithin w loadFrame x y 17 ∧ y.mem = x.mem ∧ + y.regs frame = x.regs frames + 4 * x.regs level ∧ + y.regs count = x.mem (x.regs frames + 4 * x.regs level) ∧ + y.regs leafPrefix = x.mem (x.regs frames + 4 * x.regs level + 1) ∧ + y.regs stride = x.mem (x.regs frames + 4 * x.regs level + 2) ∧ + y.regs term = x.mem (x.regs frames + 4 * x.regs level + 3) ∧ + y.regs listBase = x.regs lists + x.regs level * x.regs span ∧ + y.regs valueBase = x.regs values + x.regs level * x.regs span ∧ + y.regs nextLevel = x.regs level + 1 ∧ + y.regs childList = x.regs lists + (x.regs level + 1) * x.regs span ∧ + y.regs childValues = x.regs values + (x.regs level + 1) * x.regs span ∧ + (∀ i, ¬LoadFrameLocal i → y.regs i = x.regs i) := by + let f := x.regs frames + x.regs level * 4 + let j := x.regs level + 1 + let y := (((((((((((((((((x.set frame (x.regs level * 4)).set frame f).set address f).set + count (x.mem f)).set address (f + 1)).set leafPrefix (x.mem (f + 1))).set + address (f + 2)).set stride (x.mem (f + 2))).set address (f + 3)).set + term (x.mem (f + 3))).set temporary (x.regs level * x.regs span)).set + listBase (x.regs lists + x.regs level * x.regs span)).set + valueBase (x.regs values + x.regs level * x.regs span)).set nextLevel j).set + temporary (j * x.regs span)).set childList (x.regs lists + j * x.regs span)).set + childValues (x.regs values + j * x.regs span)) + have hlev : Fits w (x.regs level * 4) := hw.of_le (by omega) + have hf (k : ℕ) (hk : k ≤ 3) : Fits w (f + k) := hw.of_le (by dsimp [f]; omega) + have hf0 : Fits w (x.regs frames + x.regs level * 4) := by + simpa only [f, Nat.add_zero] using hf 0 (by decide) + have hj : Fits w (x.regs level + 1) := hw.of_le (by omega) + have hprod : Fits w (x.regs level * x.regs span) := hw.of_le (by nlinarith) + have hnextprod : Fits w ((x.regs level + 1) * x.regs span) := hw.of_le (by nlinarith) + have hl : Fits w (x.regs lists + x.regs level * x.regs span) := hw.of_le (by nlinarith) + have hv : Fits w (x.regs values + x.regs level * x.regs span) := hw.of_le (by nlinarith) + have hcl : Fits w (x.regs lists + (x.regs level + 1) * x.regs span) := hw.of_le (by nlinarith) + have hcv : Fits w (x.regs values + (x.regs level + 1) * x.regs span) := hw.of_le (by nlinarith) + have hexact : Exec w loadFrame x y 17 := by + ram_eval [loadFrame, y, f, j, h1, h4, hlev, hf0, hf 1 (by decide), + hf 2 (by decide), hf 3 (by decide), hj, hprod, hnextprod, hl, hv, hcl, hcv, + frame, address, count, leafPrefix, stride, term, temporary, listBase, valueBase, + nextLevel, childList, childValues, level, span, frames, lists, values, frameWidth, one, + Fin.mk.injEq, Nat.add_zero] + have he : ExecWithin w loadFrame x y 17 := ⟨17, le_rfl, hexact⟩ + refine ⟨y, he, rfl, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, + fun i hi => he.regs_eq (loadFrame_preserves i hi)⟩ + all_goals simp +decide [y, f, j, frame, address, count, leafPrefix, stride, term, temporary, + listBase, valueBase, nextLevel, childList, childValues, Nat.mul_comm] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Leaf.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Leaf.lean new file mode 100644 index 000000000..8cab8f631 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Leaf.lean @@ -0,0 +1,68 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Control + +/-! +# Actual native leaf multiplication + +## Main results + +* `PrunedQuery.exec_leafProduct`: load the two encoded leaf words and store their exact signed + product, within nineteen native instructions. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- The existing signed workspace and the indexed-load address register. -/ +def LeafLocal (i : Fin registers) : Prop := i.val < 9 ∨ i = address + +instance (i : Fin registers) : Decidable (LeafLocal i) := + inferInstanceAs (Decidable (i.val < 9 ∨ i = address)) + +private theorem leafProduct_preserves (i : Fin registers) : + ¬LeafLocal i → leafProduct.Preserves i := by decide +kernel +revert + +/-- Both loads, the existing fourteen-instruction signed multiplication kernel and the store +are charged. The ordinary mathematical product must fit the source signed width. -/ +theorem exec_leafProduct {w : ℕ} {a b : ℤ} (ha : GenericRAM.Signed.Fits w a) + (hb : GenericRAM.Signed.Fits w b) (hab : GenericRAM.Signed.Fits w (a * b)) + (x : Data registers) + (hleft : x.mem (x.regs arrayA + x.regs leafPrefix) = encode a) + (hright : x.mem (x.regs arrayB + x.regs leafPrefix) = encode b) + (hp : Fits (w + 2) (x.regs arrayA + x.regs leafPrefix)) + (hq : Fits (w + 2) (x.regs arrayB + x.regs leafPrefix)) : + ∃ y, ExecWithin (w + 2) leafProduct x y 19 ∧ + y.mem = Function.update x.mem (x.regs valueBase) (encode (a * b)) ∧ + (∀ i, ¬LeafLocal i → y.regs i = x.regs i) := by + let z := ((((x.set address (x.regs arrayA + x.regs leafPrefix)).set left (encode a)).set + address (x.regs arrayB + x.regs leafPrefix)).set right (encode b)) + have hload : ExecWithin (w + 2) leafLoad x z 4 := by + ram_eval [leafLoad, z, hleft, hright, hp, hq, address, arrayA, arrayB, leafPrefix, + left, right, Fin.mk.injEq] + obtain ⟨u, hu, hproduct, hframe⟩ := Signed.exec_multiply (r := 87) ha hb hab z + (by simp +decide [z, left, right, address]) (by simp [z]) + have hv : u.regs valueBase = x.regs valueBase := by + rw [hframe.2 valueBase (Or.inr (by decide))] + simp +decide [z, valueBase, address, left, right] + have hproduct' : u.regs result = encode (a * b) := hproduct + let y := u.write (x.regs valueBase) (encode (a * b)) + have hstore : ExecWithin (w + 2) + (ram[ *valueBase ← result ] : Code .standard registers) u y 1 := by + ram_eval [y, hv, hproduct'] + have he : ExecWithin (w + 2) leafProduct x y 19 := hload.seq (hu.seq hstore) + refine ⟨y, he, ?_, fun i hi => he.regs_eq (leafProduct_preserves i hi)⟩ + simpa only [y, Data.write_mem, z, Data.set_mem] using + congrArg (fun mem => Function.update mem (x.regs valueBase) (encode (a * b))) hframe.1 + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Memory.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Memory.lean new file mode 100644 index 000000000..87fcc591f --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Memory.lean @@ -0,0 +1,64 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Program + +/-! +# Concrete input and scratch layout for native pruned queries + +## Main definitions + +* `PrunedQuery.input`: two ordinary headers, two encoded leaf arrays and an explicit query list. +* `PrunedQuery.outputBase`, `PrunedQuery.scratchBase`, `PrunedQuery.memoryEnd`: the output and + the complete explicit-stack workspace. + +The standard `State.init` length cell precedes this list. Query codes and the two source arrays +are read-only. The timestamp region is initially zero because it lies beyond the supplied input. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Flat initialized input: depth, query count, both signed arrays, then explicit query codes. -/ +def input (L : ℕ) (a b : Fin (10 ^ L) → ℤ) (qs : List (Fin (10 ^ L))) : List ℕ := + [L, qs.length] ++ List.ofFn (fun i => encode (a i)) ++ + List.ofFn (fun i => encode (b i)) ++ qs.map Fin.val + +/-- Start of the returned values, in the original query-list order. -/ +def outputBase (L Q : ℕ) : ℕ := 3 + 2 * 10 ^ L + Q + +/-- First stack-frame cell, immediately after the returned values. -/ +def scratchBase (L Q : ℕ) : ℕ := outputBase L Q + Q + +/-- Query lists occupy one `10^L` block per possible stack depth. -/ +def listsBase (L Q : ℕ) : ℕ := scratchBase L Q + 4 * (L + 1) + +/-- Values occupy a separate `10^L` block per depth. -/ +def valuesBase (L Q : ℕ) : ℕ := listsBase L Q + (L + 1) * 10 ^ L + +/-- One shared timestamp directory suffices for all child-set constructions. -/ +def stampsBase (L Q : ℕ) : ℕ := valuesBase L Q + (L + 1) * 10 ^ L + +/-- One-past-end address of the complete output and scratch footprint. -/ +def memoryEnd (L Q : ℕ) : ℕ := stampsBase L Q + 10 ^ L + +/-- A scalar budget for the concrete addresses, counters, powers and timestamp generations. -/ +def addressBound (L Q : ℕ) : ℕ := 128 * (L + 1) * 10 ^ L + 8 * Q + 128 + +/-- The initialized payload includes no precomputed child requests or recursion certificate. -/ +theorem length_input (L : ℕ) (a b : Fin (10 ^ L) → ℤ) (qs : List (Fin (10 ^ L))) : + (input L a b qs).length = 2 + 2 * 10 ^ L + qs.length := by + simp only [input, List.length_append, List.length_cons, List.length_nil, List.length_ofFn, + List.length_map] + omega + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Powers.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Powers.lean new file mode 100644 index 000000000..93023bad4 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Powers.lean @@ -0,0 +1,117 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Memory + +/-! +# Charged initialization and encoding-array size + +## Main results + +* `PrunedQuery.exec_powers`: compute `10^L` using the actual native multiplication loop, + preserving all caller parameters and memory. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM + +/-- The scalar registers initialized or advanced by the power loop. -/ +def PowersLocal (i : Fin registers) : Prop := + i = nil ∨ i = one ∨ i = two ∨ i = ten ∨ i = nine ∨ i = frameWidth ∨ i = span ∨ i = index + +instance (i : Fin registers) : Decidable (PowersLocal i) := + inferInstanceAs (Decidable + (i = nil ∨ i = one ∨ i = two ∨ i = ten ∨ i = nine ∨ i = frameWidth ∨ i = span ∨ i = index)) + +private theorem powers_preserves (i : Fin registers) : ¬PowersLocal i → powers.Preserves i := by + decide +kernel +revert + +/-- The power loop charges its eight initializations, both body instructions, loop tests and +jumps. The only width premise is a bound on ordinary input-derived natural numbers. -/ +theorem exec_powers {w L Q : ℕ} (x : Data registers) (hL : x.regs inputDepth = L) + (hbound : Fits w (addressBound L Q)) : + ∃ y, ExecWithin w powers x y (4 * L + 10) ∧ y.mem = x.mem ∧ + y.regs inputDepth = L ∧ y.regs span = 10 ^ L ∧ y.regs index = L ∧ + y.regs nil = 0 ∧ y.regs one = 1 ∧ y.regs two = 2 ∧ y.regs ten = 10 ∧ + y.regs nine = 9 ∧ y.regs frameWidth = 4 ∧ + (∀ i, ¬PowersLocal i → y.regs i = x.regs i) := by + have hspan : 1 ≤ (10 : ℕ) ^ L := by + have hp : 0 < (10 : ℕ) ^ L := by positivity + omega + have hscalar : L + 128 + 128 * 10 ^ L ≤ addressBound L Q := by + have hm := Nat.mul_le_mul_left L hspan + unfold addressBound + nlinarith + have hbase := hbound.of_le hscalar + have h0 : Fits w 0 := fits_zero w + have h1 : Fits w 1 := hbase.of_le (by omega) + have h2 : Fits w 2 := hbase.of_le (by omega) + have h4 : Fits w 4 := hbase.of_le (by omega) + have h9 : Fits w 9 := hbase.of_le (by omega) + have h10 : Fits w 10 := hbase.of_le (by omega) + have hpow (j : ℕ) (hj : j ≤ L) : Fits w (10 ^ j) := by + have hle := Nat.pow_le_pow_right (by decide : 1 ≤ (10 : ℕ)) hj + exact hbase.of_le (by omega) + let I (y : Data registers) : Prop := + y.mem = x.mem ∧ y.regs inputDepth = L ∧ y.regs span = 10 ^ y.regs index ∧ + y.regs index ≤ L ∧ y.regs one = 1 ∧ y.regs ten = 10 + have hstep (y : Data registers) (hy : I y) (hc : (Cond.lt index inputDepth).Holds y) : + ∃ z, ExecWithin w powerStep y z 2 ∧ I z ∧ + L - z.regs index < L - y.regs index := by + obtain ⟨hm, hd, hp, hj, ho, ht⟩ := hy + have hlt : y.regs index < L := by simpa only [Cond.holds_lt, hd] using hc + have hmul : Fits w (y.regs span * 10) := by + rw [hp, ← pow_succ] + exact hpow _ (by omega) + have hinc : Fits w (y.regs index + 1) := hbase.of_le (by omega) + let z := (y.set span (y.regs span * 10)).set index (y.regs index + 1) + have he : ExecWithin w powerStep y z 2 := by + ram_eval [powerStep, z, ho, ht, hmul, hinc, span, index, ten, one, Fin.mk.injEq] + refine ⟨z, he, ?_, ?_⟩ + · refine ⟨hm, ?_, ?_, ?_, ?_, ?_⟩ + · simpa +decide [z, inputDepth, span, index, Function.update_apply] using hd + · simp +decide [z, span, index, hp, pow_succ] + · simpa [z] using Nat.succ_le_of_lt hlt + · simpa +decide [z, one, span, index, Function.update_apply] using ho + · simpa +decide [z, ten, span, index, Function.update_apply] using ht + · simp only [z, Data.set_regs, Function.update_self] + omega + let x0 := ((((((((x.set nil 0).set one 1).set two 2).set ten 10).set nine 9).set + frameWidth 4).set span 1).set index 0) + have hsetup : ExecWithin w powerSetup x x0 8 := by + ram_eval [powerSetup, x0, h0, h1, h2, h4, h9, h10] + have hI : I x0 := by + simp +decide [I, x0, hL, inputDepth, nil, one, two, ten, nine, frameWidth, span, index] + obtain ⟨y, hy, hIy, hstop⟩ := ExecWithin.loop_invariant I (fun y => L - y.regs index) + hstep hI + have he : ExecWithin w powers x y (4 * L + 10) := by + exact (hsetup.seq hy).mono (by simp [x0]; omega) + obtain ⟨hm, hd, hp, hj, ho, ht⟩ := hIy + have hidx : y.regs index = L := by + simp only [Cond.holds_lt, hd, not_lt] at hstop + omega + refine ⟨y, he, hm, hd, hp.trans (congrArg (fun n => 10 ^ n) hidx), hidx, + ?_, ho, ?_, ht, ?_, ?_, fun i hi => he.regs_eq (powers_preserves i hi)⟩ + · simpa +decide [x0, nil, one, two, ten, nine, frameWidth, span, index] using + hy.regs_eq (by decide : + (Code.loop (.lt index inputDepth) powerStep).Preserves nil) + · simpa +decide [x0, nil, one, two, ten, nine, frameWidth, span, index] using + hy.regs_eq (by decide : + (Code.loop (.lt index inputDepth) powerStep).Preserves two) + · simpa +decide [x0, nil, one, two, ten, nine, frameWidth, span, index] using + hy.regs_eq (by decide : + (Code.loop (.lt index inputDepth) powerStep).Preserves nine) + · simpa +decide [x0, nil, one, two, ten, nine, frameWidth, span, index] using + hy.regs_eq (by decide : + (Code.loop (.lt index inputDepth) powerStep).Preserves frameWidth) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean new file mode 100644 index 000000000..6afe673bf --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean @@ -0,0 +1,299 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Codes +public import Complexity.Computability.WordRAM.Signed.Arithmetic + +/-! +# Fixed native code for pruned tensor queries + +The controller uses an explicit depth-first stack. Query and value directories have one row +per depth. A shared timestamp directory deduplicates child suffixes without clearing a dense +array at every node. Encoded products and sums use the existing native signed kernels. + +## Main definitions + +* `PrunedQuery.kernel`: evaluator with explicit caller-supplied array and scratch addresses. +* `PrunedQuery.program`: initialized-input wrapper computing all addresses from the depth and + query-count headers. + +## References + +The query-set recursion is from [AlmanWilliams2026, Section 2.4.2], selectively adapted from +Anthropic's formal-math revision `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`, +[pinned source](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Programs/Sec2/Theorem5/Pruned/Recursion.lean). The explicit iterative stack and +timestamp implementation are new native code; the source machine is not imported. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- The program has a fixed register bank, including nine signed-arithmetic scratch registers. -/ +abbrev registers : ℕ := 96 + +abbrev inputDepth : Fin registers := 9 +abbrev span : Fin registers := 10 +abbrev arrayA : Fin registers := 11 +abbrev arrayB : Fin registers := 12 +abbrev queries : Fin registers := 13 +abbrev queryCount : Fin registers := 14 +abbrev output : Fin registers := 15 +abbrev scratch : Fin registers := 16 +abbrev epoch : Fin registers := 17 +abbrev frames : Fin registers := 18 +abbrev lists : Fin registers := 19 +abbrev values : Fin registers := 20 +abbrev stamps : Fin registers := 21 +abbrev level : Fin registers := 22 +abbrev mode : Fin registers := 23 +abbrev active : Fin registers := 24 +abbrev one : Fin registers := 25 +abbrev ten : Fin registers := 26 +abbrev nine : Fin registers := 27 +abbrev frameWidth : Fin registers := 28 +abbrev frame : Fin registers := 29 +abbrev count : Fin registers := 30 +abbrev leafPrefix : Fin registers := 31 +abbrev stride : Fin registers := 32 +abbrev term : Fin registers := 33 +abbrev index : Fin registers := 34 +abbrev childCount : Fin registers := 35 +abbrev query : Fin registers := 36 +abbrev digit : Fin registers := 37 +abbrev suffix : Fin registers := 38 +abbrev address : Fin registers := 39 +abbrev temporary : Fin registers := 40 +abbrev temporary₂ : Fin registers := 41 +abbrev temporary₃ : Fin registers := 42 +abbrev marker : Fin registers := 43 +abbrev listBase : Fin registers := 44 +abbrev valueBase : Fin registers := 45 +abbrev childList : Fin registers := 46 +abbrev childValues : Fin registers := 47 +abbrev nextLevel : Fin registers := 48 +abbrev childFrame : Fin registers := 49 +abbrev childPrefix : Fin registers := 50 +abbrev childStride : Fin registers := 51 +abbrev nil : Fin registers := 52 +abbrev two : Fin registers := 53 + +/-- Constant initialization, before the depth-controlled power loop. -/ +def powerSetup : Code .standard registers := ram[ + nil ← #0; one ← #1; two ← #2; ten ← #10; nine ← #9; frameWidth ← #4; + span ← #1; index ← #0 ] + +/-- One charged power multiplication and counter increment. -/ +def powerStep : Code .standard registers := ram[ + span ← span * ten; index ← index + one ] + +/-- Initialize constants and compute the actual encoding-array length `10^L`. -/ +def powers : Code .standard registers := ram[ + ⟪powerSetup⟫; while index < inputDepth do [ ⟪powerStep⟫ ] ] + +/-- Separate stack frames, query lists, value directories and membership timestamps. -/ +def locate : Code .standard registers := ram[ + frames ← scratch; + temporary ← inputDepth + one; temporary₂ ← temporary * frameWidth; + lists ← frames + temporary₂; + temporary₂ ← temporary * span; values ← lists + temporary₂; + stamps ← values + temporary₂ ] + +/-- Append one previously unseen suffix and mark it with the current epoch. -/ +def emit : Code .standard registers := ram[ + address ← stamps + suffix; marker ← *address; + if marker = epoch then [ temporary ← temporary ] else [ + address ← childList + childCount; *address ← suffix; + address ← stamps + suffix; *address ← epoch; + childCount ← childCount + one ] ] + +/-- The two fixed suffix kernels either divide a selected child query or copy a root query. -/ +def putSuffix (child : Bool) : Code .standard registers := + .seq (if child then ram[ suffix ← query / ten ] else ram[ suffix ← query ]) emit + +/-- A selected child suffix is computed by an actual unsigned division. -/ +def selectSuffix : Code .standard registers := putSuffix true + +/-- Load the next supplied query and compute its actual first digit. -/ +def scanLoad : Code .standard registers := ram[ + address ← listBase + index; query ← *address; digit ← query % ten ] + +/-- Root queries are copied directly; child queries are filtered by the native digit tests. -/ +def scanDispatch (child : Bool) : Code .standard registers := + if child then ram[ + if digit = nine then [ ⟪selectSuffix⟫ ] else [ + if digit = term then [ ⟪selectSuffix⟫ ] else [ temporary ← temporary ] ] ] + else putSuffix false + +/-- One deduplicating scan step, including input indexing, dispatch and counter increment. -/ +def scanStep (child : Bool) : Code .standard registers := ram[ + ⟪scanLoad⟫; ⟪scanDispatch child⟫; index ← index + one ] + +/-- Initialize one query scan with a genuinely fresh native epoch. -/ +def scanSetup : Code .standard registers := ram[ + epoch ← epoch + one; childCount ← #0; index ← #0 ] + +/-- Scan and deduplicate an explicit source list. -/ +def scan (child : Bool) : Code .standard registers := ram[ + ⟪scanSetup⟫; while index < count do [ ⟪scanStep child⟫ ] ] + +/-- Deduplicate supplied queries into the root list; the original list remains read-only. -/ +def rootList : Code .standard registers := ram[ + listBase ← queries; count ← queryCount; childList ← lists; ⟪scan false⟫; + address ← frames; *address ← childCount; + address ← address + one; *address ← nil; + address ← address + one; *address ← one; + address ← address + one; *address ← nil; + level ← #0; mode ← #0; active ← #1 ] + +/-- Load the current frame and compute its depth-specific list/value addresses. -/ +def loadFrame : Code .standard registers := ram[ + frame ← level * frameWidth; frame ← frames + frame; + address ← frame; count ← *address; + address ← address + one; leafPrefix ← *address; + address ← address + one; stride ← *address; + address ← address + one; term ← *address; + temporary ← level * span; listBase ← lists + temporary; + valueBase ← values + temporary; + nextLevel ← level + one; temporary ← nextLevel * span; + childList ← lists + temporary; childValues ← values + temporary ] + +/-- Return to the parent's accumulation phase, or finish the root. -/ +def pop : Code .standard registers := ram[ + if level = nil then [ active ← #0 ] else [ level ← level - one; mode ← #2 ] ] + +/-- Load both actual encoded words at the current leaf prefix. -/ +def leafLoad : Code .standard registers := ram[ + address ← arrayA + leafPrefix; left ← *address; + address ← arrayB + leafPrefix; right ← *address ] + +/-- One requested leaf product, using the existing native signed multiplication kernel. -/ +def leafProduct : Code .standard registers := ram[ + ⟪leafLoad⟫; ⟪multiply⟫; *valueBase ← result ] + +/-- Evaluate a requested leaf from the actual two encoded words. -/ +def leaf : Code .standard registers := ram[ + if nil < count then [ ⟪leafProduct⟫ ] else [ temporary ← temporary ]; + ⟪pop⟫ ] + +/-- One actual indexed store for an explicitly listed requested value. -/ +def clearValueStep : Code .standard registers := ram[ + address ← listBase + index; query ← *address; + address ← valueBase + query; *address ← nil; index ← index + one ] + +/-- Clear exactly the requested parent values, not the full dense directory. -/ +def clearRequested : Code .standard registers := ram[ + index ← #0; while index < count do [ ⟪clearValueStep⟫ ] ] + +/-- Initialize requested values and the frame's next-term counter. -/ +def clearValues : Code .standard registers := ram[ + ⟪clearRequested⟫; + term ← #0; address ← frame + frameWidth; address ← address - one; + *address ← term; mode ← #1 ] + +/-- Entry phase: multiply at a leaf; otherwise initialize the requested accumulators. -/ +def enter : Code .standard registers := ram[ + if level = inputDepth then [ ⟪leaf⟫ ] else [ ⟪clearValues⟫ ] ] + +/-- Form and deduplicate a child request set using actual tests, loads and stores. -/ +def buildChild : Code .standard registers := scan true + +/-- Advance the parent's term after an empty child or after accumulating its returned values. -/ +def advance : Code .standard registers := ram[ + term ← term + one; address ← frame + frameWidth; address ← address - one; + *address ← term; mode ← #1 ] + +/-- Push a nonempty child, with the exact least-significant-first leaf leafPrefix. -/ +def push : Code .standard registers := ram[ + childFrame ← nextLevel * frameWidth; childFrame ← frames + childFrame; + temporary₂ ← term * stride; childPrefix ← leafPrefix + temporary₂; + childStride ← stride * ten; + address ← childFrame; *address ← childCount; + address ← address + one; *address ← childPrefix; + address ← address + one; *address ← childStride; + address ← address + one; *address ← nil; + level ← nextLevel; mode ← #0 ] + +/-- Dispatch the next of the ten actual terms, recursing only on nonempty computed sets. -/ +def dispatch : Code .standard registers := ram[ + if term < ten then [ + ⟪buildChild⟫; + if nil < childCount then [ ⟪push⟫ ] else [ ⟪advance⟫ ] ] else [ ⟪pop⟫ ] ] + +/-- Load the two encoded operands at the actual parent query and child suffix addresses. -/ +def addValueLoad : Code .standard registers := ram[ + suffix ← query / ten; + address ← valueBase + query; left ← *address; + address ← childValues + suffix; right ← *address ] + +/-- Store the signed sum back at the computed parent query address. -/ +def addValueStore : Code .standard registers := ram[ + address ← valueBase + query; *address ← result ] + +/-- Add one returned child value to its parent's actual signed accumulator. -/ +def addValue : Code .standard registers := ram[ + ⟪addValueLoad⟫; ⟪add⟫; ⟪addValueStore⟫ ] + +/-- Traverse the parent requests once to consume a returned child's values. -/ +def accumulate : Code .standard registers := ram[ + index ← #0; + while index < count do [ + address ← listBase + index; query ← *address; digit ← query % ten; + if digit = nine then [ ⟪addValue⟫ ] else [ + if digit = term then [ ⟪addValue⟫ ] else [ temporary ← temporary ] ]; + index ← index + one ]; + ⟪advance⟫ ] + +/-- One explicit-stack controller step. Every branch and frame address is native work. -/ +def step : Code .standard registers := ram[ + ⟪loadFrame⟫; + if mode = nil then [ ⟪enter⟫ ] else [ + if mode = one then [ ⟪dispatch⟫ ] else [ ⟪accumulate⟫ ] ] ] + +/-- Report results in the original input query order, including repeated queries. -/ +def report : Code .standard registers := ram[ + index ← #0; + while index < queryCount do [ + address ← queries + index; query ← *address; + address ← values + query; temporary ← *address; + address ← output + index; *address ← temporary; index ← index + one ] ] + +/-- The common stack evaluator after powers and caller parameters have been initialized. -/ +def work : Code .standard registers := ram[ + ⟪locate⟫; ⟪rootList⟫; + while nil < active do [ ⟪step⟫ ]; ⟪report⟫ ] + +/-- Generic native evaluator with explicit pointer arguments and a fresh timestamp epoch. -/ +def kernel : Code .standard registers := ram[ ⟪powers⟫; ⟪work⟫ ] + +/-- Load the two ordinary scalar input headers. -/ +def header : Code .standard registers := ram[ + address ← #1; inputDepth ← *address; + address ← #2; queryCount ← *address ] + +/-- Compute all initial array/output/scratch pointers from the actual array and query lengths. -/ +def parameters : Code .standard registers := ram[ + arrayA ← #3; arrayB ← arrayA + span; queries ← arrayB + span; + output ← queries + queryCount; scratch ← output + queryCount; epoch ← #0 ] + +/-- One finite program reading the header and encoded arrays from ordinary initialized memory. -/ +def program : Code .standard registers := ram[ + ⟪header⟫; ⟪powers⟫; ⟪parameters⟫; ⟪work⟫ ] + +/-- The same program is used at every word width; it never reads the physical word size. -/ +theorem not_readsWidth : ¬program.ReadsWidth := by decide + +set_option maxRecDepth 4096 in +-- Flattening the fixed nested controller also expands its two signed arithmetic kernels. +/-- A fixed bound on both control code and all arithmetic blocks, including the final halt. -/ +theorem program_length_le : (program.program output).length ≤ 4096 := by decide + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Scan.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Scan.lean new file mode 100644 index 000000000..238c26412 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Scan.lean @@ -0,0 +1,166 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ScanInsert + +/-! +# Complete charged native query scans + +## Main results + +* `PrunedQuery.exec_scan`: the fixed native loop reads, filters and deduplicates its source + list in at most `20 N + 5` instructions, without clearing a dense directory. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM + +/-- Registers changed by the whole scan, including its fresh-epoch initialization. -/ +def ScanLocal (i : Fin registers) : Prop := ScanStepLocal i ∨ i = epoch + +instance (i : Fin registers) : Decidable (ScanLocal i) := + inferInstanceAs (Decidable (ScanStepLocal i ∨ i = epoch)) + +private theorem scan_preserves (child : Bool) (i : Fin registers) : + ¬ScanLocal i → (scan child).Preserves i := by decide +kernel +revert + +/-- The actual native scan constructs a distinct query list and its membership timestamps. +All width premises concern ordinary addresses, lengths and the incoming epoch. Source/output/ +directory separation is checked on concrete intervals. No child list is supplied in advance. -/ +theorem exec_scan {w N capacity : ℕ} (child : Bool) (x : Data registers) (input : ℕ → ℕ) + (hN : x.regs count = N) + (hinput : ∀ i < N, x.mem (x.regs listBase + i) = input i) + (hcodes : ∀ i < N, shortened child (input i) < capacity) + (hsep : x.regs childList + capacity ≤ x.regs stamps ∨ + x.regs stamps + capacity ≤ x.regs childList) + (hsrcOut : x.regs listBase + N ≤ x.regs childList ∨ + x.regs childList + capacity ≤ x.regs listBase) + (hsrcStamp : x.regs listBase + N ≤ x.regs stamps ∨ + x.regs stamps + capacity ≤ x.regs listBase) + (hmarks : ∀ q < capacity, x.mem (x.regs stamps + q) ≤ x.regs epoch) + (h1 : x.regs one = 1) (h10 : x.regs ten = 10) (h9 : x.regs nine = 9) + (hwidth : Fits w (x.regs listBase + N + x.regs childList + capacity + + x.regs stamps + capacity + N + capacity + x.regs epoch + 12)) : + ∃ y, ExecWithin w (scan child) x y (20 * N + 5) ∧ y.regs index = N ∧ + y.regs epoch = x.regs epoch + 1 ∧ + y.regs childCount = (scanSet child (x.regs term) input N).card ∧ + ScanMemory y.mem (x.regs childList) (x.regs stamps) (x.regs epoch + 1) capacity + (scanSet child (x.regs term) input N) ∧ + (∀ i < N, y.mem (x.regs listBase + i) = input i) ∧ + (∀ a, ¬(Block.mk (x.regs childList) capacity).Owns a → + ¬(Block.mk (x.regs stamps) capacity).Owns a → y.mem a = x.mem a) ∧ + (∀ i, ¬ScanLocal i → y.regs i = x.regs i) := by + let E := x.regs epoch + 1 + let x0 := ((x.set epoch E).set childCount 0).set index 0 + have h0 : Fits w 0 := fits_zero w + have hE : Fits w E := hwidth.of_le (by dsimp [E]; omega) + have hsetup : ExecWithin w scanSetup x x0 3 := by + ram_eval [scanSetup, x0, E, h1, hE, h0] + let I (y : Data registers) : Prop := + y.regs index ≤ N ∧ + y.regs childCount = (scanSet child (x.regs term) input (y.regs index)).card ∧ + ScanMemory y.mem (x.regs childList) (x.regs stamps) E capacity + (scanSet child (x.regs term) input (y.regs index)) ∧ + (∀ i < N, y.mem (x.regs listBase + i) = input i) ∧ + (∀ a, ¬(Block.mk (x.regs childList) capacity).Owns a → + ¬(Block.mk (x.regs stamps) capacity).Owns a → y.mem a = x.mem a) ∧ + (∀ i, ¬ScanStepLocal i → y.regs i = x0.regs i) + have hinit : I x0 := by + refine ⟨by simp [x0], by simp +decide [x0, childCount, index], ?_, hinput, ?_, + fun _ _ => rfl⟩ + · simpa only [x0, Data.set_mem, Data.set_regs, Function.update_self, scanSet_zero] using + (ScanMemory.empty (out := x.regs childList) (epoch := E) + (fun q hq => Nat.lt_succ_of_le (hmarks q hq))) + · exact fun _ _ _ => rfl + have hstep (y : Data registers) (hy : I y) (hc : (Cond.lt index count).Holds y) : + ∃ z, ExecWithin w (scanStep child) y z 18 ∧ I z ∧ + N - z.regs index < N - y.regs index := by + obtain ⟨hle, hcount, htable, hread, hframe, hfrozen⟩ := hy + have hfixed (i : Fin registers) (hi : ¬ScanStepLocal i) (he : i ≠ epoch) : + y.regs i = x.regs i := by + rw [hfrozen i hi] + simp only [x0, Data.set_regs] + have hic : i ≠ childCount := by intro h; subst h; exact hi (by decide) + have hii : i ≠ index := by intro h; subst h; exact hi (by decide) + simp only [Function.update_of_ne hii, Function.update_of_ne hic, + Function.update_of_ne he] + have hye : y.regs epoch = E := by + simpa +decide [x0, epoch, childCount, index] using hfrozen epoch (by decide) + have hyb := hfixed listBase (by decide) (by decide) + have hyo := hfixed childList (by decide) (by decide) + have hys := hfixed stamps (by decide) (by decide) + have hyt := hfixed term (by decide) (by decide) + have hyn := (hfixed count (by decide) (by decide)).trans hN + have hy1 := (hfixed one (by decide) (by decide)).trans h1 + have hy10 := (hfixed ten (by decide) (by decide)).trans h10 + have hy9 := (hfixed nine (by decide) (by decide)).trans h9 + have hk : y.regs index < N := by simpa only [Cond.holds_lt, hyn] using hc + have hq := hcodes (y.regs index) hk + have hcap := htable.card_le + have hw10 : Fits w 10 := hwidth.of_le (by omega) + have hwread : Fits w (y.regs listBase + y.regs index) := + hwidth.of_le (by rw [hyb]; omega) + have hwstamp : Fits w (y.regs stamps + shortened child (input (y.regs index))) := + hwidth.of_le (by rw [hys]; omega) + have hwout : Fits w (y.regs childList + y.regs childCount) := + hwidth.of_le (by rw [hyo, hcount]; omega) + have hwcount : Fits w (y.regs childCount + 1) := + hwidth.of_le (by rw [hcount]; omega) + have hwindex : Fits w (y.regs index + 1) := hwidth.of_le (by omega) + obtain ⟨z, hz, hidx, hcz, htz, hmf, hrf⟩ := exec_scanInsert child y + (scanSet child (x.regs term) input (y.regs index)) (input (y.regs index)) + (by rw [hyb]; exact hread _ hk) hcount + (by simpa only [hyo, hys, hye] using htable) hq + (by simpa only [hyo, hys] using hsep) hy1 hy10 hy9 + hw10 hwread hwstamp hwout hwcount hwindex + have hsource (i : ℕ) (hi : i < N) : z.mem (x.regs listBase + i) = input i := by + rw [hmf] + · exact hread i hi + · rw [hyo] + change ¬(x.regs childList ≤ x.regs listBase + i ∧ + x.regs listBase + i < x.regs childList + capacity) + rcases hsrcOut with h | h <;> omega + · rw [hys] + change ¬(x.regs stamps ≤ x.regs listBase + i ∧ + x.regs listBase + i < x.regs stamps + capacity) + rcases hsrcStamp with h | h <;> omega + refine ⟨z, hz, ?_, by omega⟩ + refine ⟨by omega, ?_, ?_, hsource, ?_, ?_⟩ + · simpa only [hyt, hidx, ← scanSet_succ] using hcz + · simpa only [hyo, hys, hye, hyt, hidx, ← scanSet_succ] using htz + · intro a hao has + rw [hmf a (by simpa only [hyo] using hao) (by simpa only [hys] using has)] + exact hframe a hao has + · exact fun i hi => (hrf i hi).trans (hfrozen i hi) + obtain ⟨y, hy, hIy, hstop⟩ := ExecWithin.loop_invariant I (fun y => N - y.regs index) + hstep hinit + obtain ⟨hle, hcount, htable, hread, hframe, hfrozen⟩ := hIy + have hyn : y.regs count = N := by + have := hfrozen count (by decide) + simpa +decide [x0, count, epoch, childCount, index] using this.trans (by + simpa +decide [x0, count, epoch, childCount, index] using hN) + have hindex : y.regs index = N := by + simp only [Cond.holds_lt, hyn, not_lt] at hstop + omega + have he : ExecWithin w (scan child) x y (20 * N + 5) := by + have hloop : ExecWithin w (Code.loop (.lt index count) (scanStep child)) x0 y + (20 * N + 2) := by + simpa only [x0, Data.set_regs, Function.update_self, Nat.sub_zero, + Nat.mul_comm] using hy + exact (hsetup.seq hloop).mono (by omega) + refine ⟨y, he, hindex, ?_, ?_, ?_, hread, hframe, + fun i hi => he.regs_eq (scan_preserves child i hi)⟩ + · simpa +decide [x0, E, epoch, childCount, index] using hfrozen epoch (by decide) + · simpa only [hindex] using hcount + · simpa only [hindex] using htable + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanInsert.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanInsert.lean new file mode 100644 index 000000000..4a78483c7 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanInsert.lean @@ -0,0 +1,90 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ScanMemory +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ScanStep + +/-! +# Query-set meaning of an actual native scan step + +## Main results + +* `PrunedQuery.exec_scanInsert`: the native filter and timestamp comparisons compute exactly + a finite-set insertion, preserving the list/directory invariant and the outside memory frame. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM + +/-- One charged native step computes its set insertion from the loaded source word and the +actual timestamp. `S` describes the current memory, rather than supplying a decision oracle. -/ +theorem exec_scanInsert {w capacity : ℕ} (child : Bool) (x : Data registers) (S : Finset ℕ) + (q : ℕ) (hq : x.mem (x.regs listBase + x.regs index) = q) + (hcount : x.regs childCount = S.card) + (htable : ScanMemory x.mem (x.regs childList) (x.regs stamps) (x.regs epoch) capacity S) + (hcode : shortened child q < capacity) + (hsep : x.regs childList + capacity ≤ x.regs stamps ∨ + x.regs stamps + capacity ≤ x.regs childList) + (h1 : x.regs one = 1) (h10 : x.regs ten = 10) (h9 : x.regs nine = 9) + (h10fit : Fits w 10) (hread : Fits w (x.regs listBase + x.regs index)) + (hs : Fits w (x.regs stamps + shortened child q)) + (hl : Fits w (x.regs childList + x.regs childCount)) + (hn : Fits w (x.regs childCount + 1)) (hi : Fits w (x.regs index + 1)) : + ∃ y, ExecWithin w (scanStep child) x y 18 ∧ y.regs index = x.regs index + 1 ∧ + y.regs childCount = (if selected child (x.regs term) q then + insert (shortened child q) S else S).card ∧ + ScanMemory y.mem (x.regs childList) (x.regs stamps) (x.regs epoch) capacity + (if selected child (x.regs term) q then insert (shortened child q) S else S) ∧ + (∀ a, ¬(Block.mk (x.regs childList) capacity).Owns a → + ¬(Block.mk (x.regs stamps) capacity).Owns a → y.mem a = x.mem a) ∧ + (∀ i, ¬ScanStepLocal i → y.regs i = x.regs i) := by + obtain ⟨y, he, hindex, hc, hm, hf⟩ := exec_scanStep child x q hq h1 h10 h9 + h10fit hread hs hl hn hi + have hseen := htable.marked (shortened child q) hcode + refine ⟨y, he, hindex, ?_, ?_, ?_, hf⟩ + · by_cases hsel : selected child (x.regs term) q + · by_cases hmem : shortened child q ∈ S + · simpa [hsel, hmem, hseen.mpr hmem, hcount] using hc + · have hmark : x.mem (x.regs stamps + shortened child q) ≠ x.regs epoch := + fun h => hmem (hseen.mp h) + simpa [hsel, hmem, hmark, hcount] using hc + · simpa [hsel, hcount] using hc + · by_cases hsel : selected child (x.regs term) q + · by_cases hmem : shortened child q ∈ S + · simpa [hm, hsel, hmem, hseen.mpr hmem] using htable + · have hmark : x.mem (x.regs stamps + shortened child q) ≠ x.regs epoch := + fun h => hmem (hseen.mp h) + simpa [hm, hsel, hmark, hcount] using htable.insert_new hcode hmem hsep + · simpa [hm, hsel] using htable + · intro a hout hstamp + rw [hm] + split_ifs with hnew + · have hmem : shortened child q ∉ S := fun h => hnew.2 (hseen.mpr h) + have hcapa := htable.card_lt_of_notMem hcode hmem + have hao : a ≠ x.regs childList + x.regs childCount := by + intro ha + apply hout + rw [ha, hcount] + change x.regs childList ≤ x.regs childList + S.card ∧ + x.regs childList + S.card < x.regs childList + capacity + exact ⟨by omega, by omega⟩ + have has : a ≠ x.regs stamps + shortened child q := by + intro ha + apply hstamp + rw [ha] + change x.regs stamps ≤ x.regs stamps + shortened child q ∧ + x.regs stamps + shortened child q < x.regs stamps + capacity + exact ⟨by omega, by omega⟩ + simp only [Function.update_of_ne has, Function.update_of_ne hao] + · rfl + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanMemory.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanMemory.lean new file mode 100644 index 000000000..ad0caa23a --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanMemory.lean @@ -0,0 +1,117 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ScanSet + +/-! +# Memory invariant of a timestamped query list + +## Main definitions + +* `PrunedQuery.ScanMemory`: a distinct finite list and its timestamp membership directory. + +## Main results + +* `PrunedQuery.ScanMemory.insert_new`: the two actual stores preserve the invariant on insertion. + +The invariant describes memory contents only. Execution and instruction charges are proved +separately for the native scan; no execution certificate is included here. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM + +/-- The occupied output prefix enumerates `S` once, and the timestamp table identifies exactly +its members. Older timestamps may remain elsewhere in the directory. -/ +structure ScanMemory (mem : ℕ → ℕ) (out stamps epoch capacity : ℕ) (S : Finset ℕ) : Prop where + /-- Each listed value is a valid directory index. -/ + bounded : ∀ q ∈ S, q < capacity + /-- Every timestamp is at most the current epoch. -/ + stamp_le : ∀ q < capacity, mem (stamps + q) ≤ epoch + /-- The current epoch is exactly the membership marker. -/ + marked : ∀ q < capacity, mem (stamps + q) = epoch ↔ q ∈ S + /-- The actual occupied prefix is an injective enumeration of the set. -/ + listed : ∃ v : Fin S.card → ℕ, Function.Injective v ∧ + Set.range v = (S : Set ℕ) ∧ (Block.mk out S.card).Realizes mem v + +/-- A realized distinct list cannot exceed its concrete directory capacity. -/ +theorem ScanMemory.card_le {mem : ℕ → ℕ} {out stamps epoch capacity : ℕ} {S : Finset ℕ} + (h : ScanMemory mem out stamps epoch capacity S) : S.card ≤ capacity := by + have hs : S ⊆ Finset.range capacity := fun q hq => Finset.mem_range.mpr (h.bounded q hq) + simpa using Finset.card_le_card hs + +/-- An unseen valid code leaves a free output cell for its insertion. -/ +theorem ScanMemory.card_lt_of_notMem {mem : ℕ → ℕ} {out stamps epoch capacity : ℕ} + {S : Finset ℕ} (h : ScanMemory mem out stamps epoch capacity S) {q : ℕ} + (hq : q < capacity) (hnew : q ∉ S) : S.card < capacity := by + have hs : insert q S ⊆ Finset.range capacity := by + intro a ha + rcases Finset.mem_insert.mp ha with rfl | ha + · exact Finset.mem_range.mpr hq + · exact Finset.mem_range.mpr (h.bounded a ha) + have hc := Finset.card_le_card hs + simp only [Finset.card_insert_of_notMem hnew, Finset.card_range] at hc + omega + +/-- A fresh epoch establishes the empty directory without clearing its cells. -/ +theorem ScanMemory.empty {mem : ℕ → ℕ} {out stamps epoch capacity : ℕ} + (h : ∀ q < capacity, mem (stamps + q) < epoch) : + ScanMemory mem out stamps epoch capacity ∅ := by + refine ⟨by simp, fun q hq => (h q hq).le, ?_, ?_⟩ + · intro q hq + simp only [Finset.notMem_empty, iff_false] + exact Nat.ne_of_lt (h q hq) + · refine ⟨Fin.elim0, (fun i => i.elim0), ?_, (fun i => i.elim0)⟩ + ext q + simp + +/-- Append one previously unseen code, then mark its directory entry. Disjointness of the +two concrete blocks ensures that neither store corrupts the other representation. -/ +theorem ScanMemory.insert_new {mem : ℕ → ℕ} {out stamps epoch capacity : ℕ} {S : Finset ℕ} + (h : ScanMemory mem out stamps epoch capacity S) {q : ℕ} (hq : q < capacity) + (hnew : q ∉ S) (hsep : out + capacity ≤ stamps ∨ stamps + capacity ≤ out) : + ScanMemory + (Function.update (Function.update mem (out + S.card) q) (stamps + q) epoch) + out stamps epoch capacity (insert q S) := by + have hcard := h.card_lt_of_notMem hq hnew + have haway (a : ℕ) (ha : a < capacity) : stamps + a ≠ out + S.card := by + rcases hsep with hs | hs <;> omega + refine ⟨?_, ?_, ?_, ?_⟩ + · intro a ha + rcases Finset.mem_insert.mp ha with rfl | ha + · exact hq + · exact h.bounded a ha + · intro a ha + by_cases he : a = q + · subst a + simp + · simpa only [Function.update_of_ne (by omega : stamps + a ≠ stamps + q), + Function.update_of_ne (haway a ha)] using h.stamp_le a ha + · intro a ha + by_cases he : a = q + · subst a + simp + · simpa only [Function.update_of_ne (by omega : stamps + a ≠ stamps + q), + Function.update_of_ne (haway a ha), Finset.mem_insert, he, false_or] using h.marked a ha + · obtain ⟨v, hv, hrange, hreal⟩ := h.listed + have hnot : q ∉ Set.range v := by simpa only [hrange, Finset.mem_coe] using hnew + have hlist := hreal.snoc q + have hown : ¬(Block.mk out (S.card + 1)).Owns (stamps + q) := by + change ¬(out ≤ stamps + q ∧ stamps + q < out + (S.card + 1)) + rcases hsep with hs | hs <;> omega + have hlist' := hlist.update_of_not_owns hown epoch + rw [Finset.card_insert_of_notMem hnew] + refine ⟨Fin.snoc v q, Fin.snoc_injective_iff.mpr ⟨hv, hnot⟩, ?_, hlist'⟩ + rw [Fin.range_snoc, hrange] + simp + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanSet.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanSet.lean new file mode 100644 index 000000000..88c30f215 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanSet.lean @@ -0,0 +1,92 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Codes +public import Complexity.Computability.WordRAM.ArrayAppend + +/-! +# Finite-set invariant of the native deduplicating scan + +## Main definitions + +* `PrunedQuery.scanSet`: the selected, shortened codes among a processed input prefix. + +## Main results + +* `PrunedQuery.scanSet_succ`: the exact insertion made by one scan step. +* `PrunedQuery.scanSet_child`: agreement of the child scan with `childCodes`. + +These describe the native loop's mathematical invariant; they do not assume its execution. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Finset + +/-- Root scans retain every query; child scans retain exactly contributing first digits. -/ +def selected (child : Bool) (term code : ℕ) : Prop := + if child then code % 10 = 9 ∨ code % 10 = term else True + +instance (child : Bool) (term code : ℕ) : Decidable (selected child term code) := + inferInstanceAs (Decidable (if child then code % 10 = 9 ∨ code % 10 = term else True)) + +/-- Child requests discard one digit; root requests keep the supplied complete code. -/ +def shortened (child : Bool) (code : ℕ) : ℕ := if child then code / 10 else code + +/-- The deduplicated codes selected from the first `k` actual input entries. -/ +def scanSet (child : Bool) (term : ℕ) (input : ℕ → ℕ) (k : ℕ) : Finset ℕ := + ((range k).filter fun i => selected child term (input i)).image + fun i => shortened child (input i) + +/-- No input positions have been visited initially. -/ +@[simp] +theorem scanSet_zero (child : Bool) (term : ℕ) (input : ℕ → ℕ) : + scanSet child term input 0 = ∅ := by simp [scanSet] + +/-- A scan step inserts one selected suffix, leaving any previous occurrence deduplicated. -/ +theorem scanSet_succ (child : Bool) (term : ℕ) (input : ℕ → ℕ) (k : ℕ) : + scanSet child term input (k + 1) = + if selected child term (input k) then + insert (shortened child (input k)) (scanSet child term input k) + else scanSet child term input k := by + by_cases h : selected child term (input k) <;> simp [scanSet, range_add_one, filter_insert, h] + +/-- At most one new distinct code is emitted for each processed input word. -/ +theorem card_scanSet_le (child : Bool) (term : ℕ) (input : ℕ → ℕ) (k : ℕ) : + (scanSet child term input k).card ≤ k := + (card_image_le.trans (card_filter_le _ _)).trans_eq (card_range k) + +/-- Root deduplication computes exactly the image of the input prefix. -/ +theorem scanSet_root (term : ℕ) (input : ℕ → ℕ) (k : ℕ) : + scanSet false term input k = (range k).image input := by + simp [scanSet, selected, shortened] + +/-- A child scan uses the same selected quotient image as the finite pruning specification. -/ +theorem scanSet_child (term : ℕ) (input : ℕ → ℕ) (k : ℕ) : + scanSet true term input k = childCodes ((range k).image input) term := by + ext q + simp only [scanSet, selected, ↓reduceIte, shortened, mem_image, + mem_filter, childCodes] + constructor + · rintro ⟨i, ⟨hi, hm⟩, he⟩ + exact ⟨input i, ⟨⟨i, hi, rfl⟩, hm⟩, he⟩ + · rintro ⟨v, ⟨⟨i, hi, rfl⟩, hm⟩, he⟩ + exact ⟨i, ⟨hi, hm⟩, he⟩ + +/-- Every stored code lies in the concrete destination capacity when each selected source does. -/ +theorem scanSet_bounded {child : Bool} {term k capacity : ℕ} {input : ℕ → ℕ} + (h : ∀ i < k, selected child term (input i) → shortened child (input i) < capacity) : + ∀ q ∈ scanSet child term input k, q < capacity := by + intro q hq + obtain ⟨i, hi, rfl⟩ := mem_image.mp hq + exact h i (mem_range.mp (mem_filter.mp hi).1) (mem_filter.mp hi).2 + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanStep.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanStep.lean new file mode 100644 index 000000000..dc933a6d9 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanStep.lean @@ -0,0 +1,197 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Emit +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ScanSet + +/-! +# Charged native suffix selection + +## Main results + +* `PrunedQuery.exec_suffix`: one real quotient computation and timestamp insertion, + within ten native instructions, with its complete memory and register frame. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM + +/-- Registers changed by shortening a query and attempting its insertion. -/ +def SuffixLocal (i : Fin registers) : Prop := EmitLocal i ∨ i = suffix + +instance (i : Fin registers) : Decidable (SuffixLocal i) := + inferInstanceAs (Decidable (EmitLocal i ∨ i = suffix)) + +private theorem suffix_preserves (child : Bool) (i : Fin registers) : + ¬SuffixLocal i → (putSuffix child).Preserves i := by decide +kernel +revert + +/-- The selected suffix is divided from the actual loaded query, then deduplicated by its +actual memory timestamp. No query-set computation is supplied as a hypothesis. -/ +theorem exec_suffix {w : ℕ} (child : Bool) (x : Data registers) (q : ℕ) + (hq : x.regs query = q) (h1 : x.regs one = 1) (h10 : x.regs ten = 10) + (hs : Fits w (x.regs stamps + shortened child q)) + (hl : Fits w (x.regs childList + x.regs childCount)) + (hn : Fits w (x.regs childCount + 1)) : + ∃ y, ExecWithin w (putSuffix child) x y 10 ∧ y.regs suffix = shortened child q ∧ + y.regs childCount = + (if x.mem (x.regs stamps + shortened child q) = x.regs epoch then + x.regs childCount else x.regs childCount + 1) ∧ + y.mem = + (if x.mem (x.regs stamps + shortened child q) = x.regs epoch then x.mem else + Function.update (Function.update x.mem + (x.regs childList + x.regs childCount) (shortened child q)) + (x.regs stamps + shortened child q) (x.regs epoch)) ∧ + (∀ i, ¬SuffixLocal i → y.regs i = x.regs i) := by + let z := x.set suffix (shortened child q) + have hqfit : Fits w (shortened child q) := hs.of_le (by omega) + have hpre : ExecWithin w + (if child then ram[ suffix ← query / ten ] else ram[ suffix ← query ]) x z 1 := by + cases child with + | false => + change ExecWithin w (ram[ suffix ← query ]) x z 1 + ram_eval [z, shortened, hq] + | true => + have hv : Fits w (q / 10) := hqfit + change ExecWithin w (ram[ suffix ← query / ten ]) x (x.set suffix (q / 10)) 1 + ram_eval [hq, h10, hv] + have hz1 : z.regs one = 1 := by simpa [z, one, suffix] using h1 + have hzs : Fits w (z.regs stamps + z.regs suffix) := by + simpa [z, stamps, suffix] using hs + have hzl : Fits w (z.regs childList + z.regs childCount) := by + simpa [z, childList, childCount, suffix] using hl + have hzn : Fits w (z.regs childCount + 1) := by + simpa [z, childCount, suffix] using hn + obtain ⟨y, hy, hc, hm, hf⟩ := exec_emit z hz1 hzs hzl hzn + have he : ExecWithin w (putSuffix child) x y 10 := hpre.seq hy + refine ⟨y, he, ?_, ?_, ?_, fun i hi => he.regs_eq (suffix_preserves child i hi)⟩ + · simpa [z] using hf suffix (by decide) + · simpa [z, stamps, suffix, childCount, epoch] using hc + · simpa [z, stamps, suffix, childList, childCount, epoch] using hm + +private theorem dispatch_preserves (child : Bool) (i : Fin registers) : + ¬SuffixLocal i → (scanDispatch child).Preserves i := by decide +kernel +revert + +/-- Execute the actual query filter and unique insertion. The numerical selection is proved +from the two real digit comparisons; it is not a supplied child-set certificate. -/ +theorem exec_scanDispatch {w : ℕ} (child : Bool) (x : Data registers) (q : ℕ) + (hq : x.regs query = q) (hdigit : x.regs digit = q % 10) + (h1 : x.regs one = 1) (h10 : x.regs ten = 10) (h9 : x.regs nine = 9) + (hs : Fits w (x.regs stamps + shortened child q)) + (hl : Fits w (x.regs childList + x.regs childCount)) + (hn : Fits w (x.regs childCount + 1)) : + ∃ y, ExecWithin w (scanDispatch child) x y 14 ∧ + y.regs childCount = + (if selected child (x.regs term) q ∧ + x.mem (x.regs stamps + shortened child q) ≠ x.regs epoch then + x.regs childCount + 1 else x.regs childCount) ∧ + y.mem = + (if selected child (x.regs term) q ∧ + x.mem (x.regs stamps + shortened child q) ≠ x.regs epoch then + Function.update (Function.update x.mem + (x.regs childList + x.regs childCount) (shortened child q)) + (x.regs stamps + shortened child q) (x.regs epoch) else x.mem) ∧ + (∀ i, ¬SuffixLocal i → y.regs i = x.regs i) := by + by_cases hsel : selected child (x.regs term) q + · obtain ⟨y, hy, _, hc, hm, _⟩ := exec_suffix child x q hq h1 h10 hs hl hn + have he : ExecWithin w (scanDispatch child) x y 14 := by + cases child with + | false => exact hy.mono (by decide) + | true => + have hm : q % 10 = 9 ∨ q % 10 = x.regs term := hsel + by_cases hfirst : q % 10 = 9 + · exact (ExecWithin.branch_pos (by + simpa only [Cond.holds_eq, hdigit, h9] using hfirst) hy).mono (by decide) + · have hsecond := hm.resolve_left hfirst + exact (ExecWithin.branch_neg (by + simpa only [Cond.holds_eq, hdigit, h9] using hfirst) + (ExecWithin.branch_pos (by simpa only [Cond.holds_eq, hdigit] using hsecond) hy)).mono + (by decide) + refine ⟨y, he, ?_, ?_, fun i hi => he.regs_eq (dispatch_preserves child i hi)⟩ + · by_cases hseen : x.mem (x.regs stamps + shortened child q) = x.regs epoch <;> + simpa [hsel, hseen] using hc + · by_cases hseen : x.mem (x.regs stamps + shortened child q) = x.regs epoch <;> + simpa [hsel, hseen] using hm + · have hchild : child = true := by + cases child <;> simp_all [selected] + subst hchild + have hfirst : q % 10 ≠ 9 := fun h => hsel (Or.inl h) + have hsecond : q % 10 ≠ x.regs term := fun h => hsel (Or.inr h) + let y := x.set temporary (x.regs temporary) + have he : ExecWithin w (scanDispatch true) x y 5 := + ExecWithin.branch_neg (by simpa only [Cond.holds_eq, hdigit, h9] using hfirst) + (ExecWithin.branch_neg (by simpa only [Cond.holds_eq, hdigit] using hsecond) + ExecWithin.copy) + refine ⟨y, he.mono (by decide), ?_, ?_, + fun i hi => he.regs_eq (dispatch_preserves true i hi)⟩ + · simp [hsel, y, childCount, temporary] + · simp [hsel, y] + +/-- All scan-step scratch registers; caller pointers, counts and the current epoch are kept. -/ +def ScanStepLocal (i : Fin registers) : Prop := + SuffixLocal i ∨ i = query ∨ i = digit ∨ i = index + +instance (i : Fin registers) : Decidable (ScanStepLocal i) := + inferInstanceAs (Decidable (SuffixLocal i ∨ i = query ∨ i = digit ∨ i = index)) + +private theorem scanStep_preserves (child : Bool) (i : Fin registers) : + ¬ScanStepLocal i → (scanStep child).Preserves i := by decide +kernel +revert + +/-- One complete native scan step includes source indexing, the actual load and digit +computation, conditional deduplicated insertion, and the loop-counter increment. -/ +theorem exec_scanStep {w : ℕ} (child : Bool) (x : Data registers) (q : ℕ) + (hq : x.mem (x.regs listBase + x.regs index) = q) + (h1 : x.regs one = 1) (h10 : x.regs ten = 10) (h9 : x.regs nine = 9) + (h10fit : Fits w 10) (hread : Fits w (x.regs listBase + x.regs index)) + (hs : Fits w (x.regs stamps + shortened child q)) + (hl : Fits w (x.regs childList + x.regs childCount)) + (hn : Fits w (x.regs childCount + 1)) (hi : Fits w (x.regs index + 1)) : + ∃ y, ExecWithin w (scanStep child) x y 18 ∧ y.regs index = x.regs index + 1 ∧ + y.regs childCount = + (if selected child (x.regs term) q ∧ + x.mem (x.regs stamps + shortened child q) ≠ x.regs epoch then + x.regs childCount + 1 else x.regs childCount) ∧ + y.mem = + (if selected child (x.regs term) q ∧ + x.mem (x.regs stamps + shortened child q) ≠ x.regs epoch then + Function.update (Function.update x.mem + (x.regs childList + x.regs childCount) (shortened child q)) + (x.regs stamps + shortened child q) (x.regs epoch) else x.mem) ∧ + (∀ i, ¬ScanStepLocal i → y.regs i = x.regs i) := by + let z := ((x.set address (x.regs listBase + x.regs index)).set query q).set digit (q % 10) + have hmod : Fits w (q % 10) := h10fit.of_le (Nat.le_of_lt (Nat.mod_lt _ (by decide))) + have hload : ExecWithin w scanLoad x z 3 := by + ram_eval [scanLoad, z, hq, h10, hread, hmod, address, listBase, index, query, digit, ten, + Fin.mk.injEq] + have hreg (i : Fin registers) (ha : i ≠ address) (hq' : i ≠ query) (hd : i ≠ digit) : + z.regs i = x.regs i := by simp [z, ha, hq', hd] + obtain ⟨u, hu, hc, hm, hf⟩ := exec_scanDispatch child z q + (by simp [z, query, digit]) (by simp [z]) + ((hreg one (by decide) (by decide) (by decide)).trans h1) + ((hreg ten (by decide) (by decide) (by decide)).trans h10) + ((hreg nine (by decide) (by decide) (by decide)).trans h9) + (by simpa [z, stamps, address, query, digit] using hs) + (by simpa [z, childList, childCount, address, query, digit] using hl) + (by simpa [z, childCount, address, query, digit] using hn) + have hui : u.regs index = x.regs index := + (hf index (by decide)).trans (hreg index (by decide) (by decide) (by decide)) + have huo : u.regs one = 1 := + (hf one (by decide)).trans ((hreg one (by decide) (by decide) (by decide)).trans h1) + let y := u.set index (x.regs index + 1) + have hinc : ExecWithin w (ram[ index ← index + one ]) u y 1 := by + ram_eval [y, hui, huo, hi] + have he : ExecWithin w (scanStep child) x y 18 := hload.seq (hu.seq hinc) + refine ⟨y, he, by simp [y], ?_, ?_, + fun i hi => he.regs_eq (scanStep_preserves child i hi)⟩ + · simpa [y, z, index, childCount, term, address, query, digit, stamps, epoch] using hc + · simpa [y, z, childList, childCount, term, address, query, digit, stamps, epoch] using hm + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Values.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Values.lean new file mode 100644 index 000000000..8a3f7d1a5 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Values.lean @@ -0,0 +1,160 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Codes +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Magnitude + +/-! +# Values and flat addresses of the native recursion + +## Main definitions + +* `PrunedQuery.partialValue`: the requested accumulator after a prefix of the ten terms. + +## Main results + +* `PrunedQuery.slice_strided`: flat strided leaf arrays agree with the existing `sliceAt`. +* `PrunedQuery.abs_partialValue_le`: every intermediate accumulator has an explicit bound. + +These are identities and magnitude bounds for the controller invariant. They do not assume +that a machine executes the recursion. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Finset + +/-- The existing leaf indexing has the same least-significant-first decomposition as queries. -/ +theorem leafIndex_cons {n : ℕ} (lam : Term) (v : Leaf n) : + (Encoding.leafIndex (Fin.cons lam v)).val = + (Encoding.termDigits.symm lam).val + 10 * (Encoding.leafIndex v).val := by + simp only [Encoding.leafIndex, finFunctionFinEquiv_apply, Fin.sum_univ_succ, + Fin.cons_zero, Fin.cons_succ, Fin.val_zero, pow_zero, mul_one, Fin.val_succ, + pow_succ, mul_sum] + congr 1 + apply sum_congr rfl + intro i _ + ring + +/-- The native prefix/stride update selects exactly the existing mathematical child slice. -/ +theorem slice_strided {n : ℕ} (a : ℕ → ℤ) (p s : ℕ) (lam : Term) : + sliceAt (fun v : Leaf (n + 1) => a (p + s * (Encoding.leafIndex v).val)) lam = + fun v : Leaf n => a (p + (Encoding.termDigits.symm lam).val * s + + (s * 10) * (Encoding.leafIndex v).val) := by + funext v + change a (p + s * (Encoding.leafIndex (Fin.cons lam v)).val) = _ + rw [leafIndex_cons] + congr 1 + ring + +/-- A frame's prefix and stride put every remaining leaf inside the original encoding array. -/ +theorem leafAddress_lt {n p s size : ℕ} (hp : p < s) (hs : s * 10 ^ n = size) + (v : Leaf n) : p + s * (Encoding.leafIndex v).val < size := by + have hi := (Encoding.leafIndex v).isLt + nlinarith + +/-- The mathematical value of a requested native accumulator after terms numbered below `k`. +Unrequested directory cells are deliberately unspecified by the native interface. -/ +def partialValue {n : ℕ} (a b : Leaf (n + 1) → ℤ) (S : Finset (OutStr (n + 1))) + (w : OutStr (n + 1)) (k : ℕ) : ℤ := + ∑ i : Fin 10 with i.val < k ∧ (Encoding.termDigits i).Contributes (w 0), + Pruned.childResult a b S (Encoding.termDigits i) (Fin.tail w) + +/-- Before the first child, the native accumulator is zero. -/ +@[simp] +theorem partialValue_zero {n : ℕ} (a b : Leaf (n + 1) → ℤ) + (S : Finset (OutStr (n + 1))) (w : OutStr (n + 1)) : partialValue a b S w 0 = 0 := by + simp [partialValue] + +/-- Processing the next term adds exactly its contributing child, or leaves the value unchanged. -/ +theorem partialValue_succ {n : ℕ} (a b : Leaf (n + 1) → ℤ) + (S : Finset (OutStr (n + 1))) (w : OutStr (n + 1)) (k : Fin 10) : + partialValue a b S w (k.val + 1) = partialValue a b S w k.val + + if (Encoding.termDigits k).Contributes (w 0) then + Pruned.childResult a b S (Encoding.termDigits k) (Fin.tail w) else 0 := by + have hfilter : + (univ.filter fun i : Fin 10 => i.val < k.val + 1 ∧ + (Encoding.termDigits i).Contributes (w 0)) = + if (Encoding.termDigits k).Contributes (w 0) then + insert k (univ.filter fun i : Fin 10 => i.val < k.val ∧ + (Encoding.termDigits i).Contributes (w 0)) else + univ.filter fun i : Fin 10 => i.val < k.val ∧ + (Encoding.termDigits i).Contributes (w 0) := by + ext i + by_cases hc : (Encoding.termDigits k).Contributes (w 0) + · simp only [mem_filter, mem_univ, true_and, hc, ↓reduceIte, mem_insert] + constructor + · rintro ⟨hi, hci⟩ + by_cases he : i = k + · exact Or.inl he + · exact Or.inr ⟨by have hne := Fin.val_ne_of_ne he; omega, hci⟩ + · rintro (rfl | ⟨hi, hci⟩) + · exact ⟨by omega, hc⟩ + · exact ⟨by omega, hci⟩ + · simp only [mem_filter, mem_univ, true_and, hc, ↓reduceIte] + constructor + · rintro ⟨hi, hci⟩ + have hne : i ≠ k := fun he => hc (he ▸ hci) + exact ⟨by have hv := Fin.val_ne_of_ne hne; omega, hci⟩ + · exact fun ⟨hi, hci⟩ => ⟨by omega, hci⟩ + unfold partialValue + rw [hfilter] + split_ifs + · rw [sum_insert (by simp)] + omega + · simp + +/-- After all ten terms, a requested accumulator is the existing pruned result. -/ +theorem partialValue_ten {n : ℕ} (a b : Leaf (n + 1) → ℤ) + (S : Finset (OutStr (n + 1))) (w : OutStr (n + 1)) (hw : w ∈ S) : + partialValue a b S w 10 = Pruned.result (n + 1) a b S w := by + rw [Pruned.result_succ_apply] + simp only [hw, ↓reduceIte] + unfold partialValue + apply sum_equiv Encoding.termDigits + · intro i + simp only [mem_filter, mem_univ, true_and, i.isLt] + · intro _ _ + rfl + +/-- Every child's mathematical result has the bound for its remaining depth, including a +skipped empty child whose value is zero. -/ +theorem abs_childResult_le {n : ℕ} (a b : Leaf (n + 1) → ℤ) (A B : ℤ) + (hA : ∀ v, |a v| ≤ A) (hB : ∀ v, |b v| ≤ B) + (S : Finset (OutStr (n + 1))) (lam : Term) (w : OutStr n) : + |Pruned.childResult a b S lam w| ≤ 10 ^ n * (A * B) := by + have hA0 : 0 ≤ A := (abs_nonneg _).trans (hA fun _ => .P0) + have hB0 : 0 ≤ B := (abs_nonneg _).trans (hB fun _ => .P0) + unfold Pruned.childResult + split_ifs + · exact Pruned.abs_result_le _ _ A B (fun v => hA (Fin.cons lam v)) + (fun v => hB (Fin.cons lam v)) _ _ + · simp only [Pi.zero_apply, abs_zero] + positivity + +/-- The bound covers all partial sums, not only the final recursively specified output. -/ +theorem abs_partialValue_le {n : ℕ} (a b : Leaf (n + 1) → ℤ) (A B : ℤ) + (hA : ∀ v, |a v| ≤ A) (hB : ∀ v, |b v| ≤ B) + (S : Finset (OutStr (n + 1))) (w : OutStr (n + 1)) (k : ℕ) : + |partialValue a b S w k| ≤ 10 ^ (n + 1) * (A * B) := by + have hA0 : 0 ≤ A := (abs_nonneg _).trans (hA fun _ => .P0) + have hB0 : 0 ≤ B := (abs_nonneg _).trans (hB fun _ => .P0) + let T := (univ : Finset (Fin 10)).filter fun i => + i.val < k ∧ (Encoding.termDigits i).Contributes (w 0) + have hcard : T.card ≤ 10 := (card_le_univ _).trans_eq (by simp) + calc + |partialValue a b S w k| ≤ (T.card : ℤ) * (10 ^ n * (A * B)) := + abs_sum_le_card_mul T (fun i _ => abs_childResult_le a b A B hA hB S _ _) + _ ≤ 10 * (10 ^ n * (A * B)) := by gcongr; exact_mod_cast hcard + _ = 10 ^ (n + 1) * (A * B) := by ring + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Weight.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Weight.lean new file mode 100644 index 000000000..50e804816 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Weight.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.PrunedCounts +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Codes + +/-! +# A finite trace weight for the native controller's potential + +## Main definitions + +* `PrunedQuery.traceWeight`: a weight on the already defined finite mathematical call trace. + +## Main results + +* `PrunedQuery.traceWeight_le`: a bound from contributing leaves and recursion depth. + +This weight is a mathematical potential, not an assumed execution-time certificate. A separate +native execution proof must show that actual instructions decrease it sufficiently. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Finset + +/-- Weighting each call by its request count plus one pays for scans and fixed controller work. -/ +def traceWeight {n : ℕ} (S : Finset (OutStr n)) : ℕ := + 4096 * (Pruned.totalSize (fun _ => 0) (fun _ => 0) S + + (Pruned.calls n (fun _ => 0) (fun _ => 0) S).card) + +/-- The weight of the zero-depth root, including the empty-query boundary. -/ +theorem traceWeight_zero (S : Finset (OutStr 0)) : traceWeight S = 4096 * (S.card + 1) := by + simp [traceWeight, Pruned.totalSize, Pruned.calls, Pruned.run] + +/-- The existing leaf/passed-set theorem bounds this potential without a runtime assumption. -/ +theorem traceWeight_le {n : ℕ} (S : Finset (OutStr n)) : + traceWeight S ≤ 4096 * (2 * (n + 1) * (leaves S).card + 1) := by + have hc := Pruned.card_calls_le_totalSize_add_one (fun _ => 0) (fun _ => 0) S + have hs := Pruned.totalSize_le (fun _ => 0) (fun _ => 0) S + unfold traceWeight + nlinarith + +/-- One root's scan budget and the weights of exactly its nonempty children. -/ +theorem traceWeight_succ {n : ℕ} (S : Finset (OutStr (n + 1))) : + traceWeight S = 4096 * (S.card + 1) + ∑ lam, + if (childSet S lam).Nonempty then traceWeight (childSet S lam) else 0 := by + unfold traceWeight + rw [Pruned.totalSize_succ, Pruned.card_calls_succ] + calc + _ = 4096 * (S.card + 1) + + 4096 * ((∑ lam, if (childSet S lam).Nonempty then + Pruned.totalSize (sliceAt (fun _ => 0) lam) (sliceAt (fun _ => 0) lam) + (childSet S lam) else 0) + + ∑ lam, if (childSet S lam).Nonempty then + (Pruned.calls n (sliceAt (fun _ => 0) lam) (sliceAt (fun _ => 0) lam) + (childSet S lam)).card else 0) := by ring + _ = 4096 * (S.card + 1) + ∑ lam, + 4096 * ((if (childSet S lam).Nonempty then + Pruned.totalSize (sliceAt (fun _ => 0) lam) (sliceAt (fun _ => 0) lam) + (childSet S lam) else 0) + + if (childSet S lam).Nonempty then + (Pruned.calls n (sliceAt (fun _ => 0) lam) (sliceAt (fun _ => 0) lam) + (childSet S lam)).card else 0) := by + rw [← sum_add_distrib, mul_sum] + _ = _ := by + apply congrArg (Nat.add (4096 * (S.card + 1))) + apply sum_congr rfl + intro lam _ + split_ifs + · rw [Pruned.totalSize_irrel _ _ (fun _ => 0) (fun _ => 0), + Pruned.calls_irrel _ _ (fun _ => 0) (fun _ => 0)] + · simp + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Computability/WordRAM/ArrayAppend.lean b/Complexity/Computability/WordRAM/ArrayAppend.lean new file mode 100644 index 000000000..18f04600e --- /dev/null +++ b/Complexity/Computability/WordRAM/ArrayAppend.lean @@ -0,0 +1,36 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Allocation +public import Mathlib.Data.Fin.Tuple.Basic + +/-! +# Appending one word to a realized memory block + +## Main results + +* `Block.Realizes.snoc`: the actual one-cell memory update realizes `Fin.snoc` of the prior + finite array. This is a memory identity; a caller must separately charge its store instruction. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.Block + +/-- Appending a value at the first cell after an array realizes the extended finite function. -/ +theorem Realizes.snoc {base n : ℕ} {mem : ℕ → ℕ} {v : Fin n → ℕ} + (h : (Block.mk base n).Realizes mem v) (a : ℕ) : + (Block.mk base (n + 1)).Realizes (Function.update mem (base + n) a) (Fin.snoc v a) := by + intro i + refine Fin.lastCases ?_ (fun j => ?_) i + · simp [read] + · have hne : base + j.val ≠ base + n := by have := j.isLt; omega + simpa only [read, Fin.val_castSucc, Function.update_of_ne hne, Fin.snoc_castSucc] using h j + +end Cslib.WordRAM.Block diff --git a/ComplexityTests/SchonhagePruned.lean b/ComplexityTests/SchonhagePruned.lean new file mode 100644 index 000000000..5bfd6dc20 --- /dev/null +++ b/ComplexityTests/SchonhagePruned.lean @@ -0,0 +1,45 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Memory + +-- Full controller regressions evaluate the actual native interpreter at elaboration time. +set_option linter.hashCommand false + +/-! Actual native controller regressions, including shared and repeated query suffixes. -/ + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +namespace ComplexityTests.SchonhagePruned + +private def observe (w L fuel : ℕ) (a b : Fin (10 ^ L) → ℤ) (qs : List (Fin (10 ^ L))) := + (program.eval w fuel (State.init (input L a b qs) : State registers).data).map fun (y, time) => + ((List.range qs.length).map fun i => decode (y.mem (outputBase L qs.length + i)), time) + +private def answers (w L fuel : ℕ) (a b : Fin (10 ^ L) → ℤ) (qs : List (Fin (10 ^ L))) := + (observe w L fuel a b qs).map Prod.fst + +-- One leaf is multiplied once and reported twice, including its negative sign. +#guard answers 16 0 64 (fun _ => -3) (fun _ => 2) [0, 0] == some [-6, -6] +#guard answers 16 0 64 (fun _ => -3) (fun _ => 2) [] == some [] + +-- Digit nine is the inner wildcard; the outer query uses exactly its private leaf. +#guard answers 20 1 128 (fun i => i.val + 1) (fun _ => 1) [9, 0, 9] == some [55, 1, 55] +#guard answers 20 1 128 (fun i => -(i.val + 1 : ℤ)) (fun _ => 1) [9, 0, 9] == + some [-55, -1, -55] +#guard answers 20 1 128 (fun _ => 5) (fun _ => 0) [] == some [] + +-- Mixed suffixes share work. The two wildcard positions exercise both digit orders. +#guard answers 24 2 2048 (fun i => i.val + 1) (fun _ => 1) [99, 90, 9, 0, 99] == + some [5050, 460, 55, 1, 5050] + +-- A nonzero word at the one-past-end address survives the complete controller. +#guard ((program.eval 20 128 + (((State.init (input 1 (fun _ => -2) (fun _ => 3) [9, 0]) : State registers).data).write + (memoryEnd 1 2) 91)).map fun (y, _) => y.mem (memoryEnd 1 2)) == some 91 + +end ComplexityTests.SchonhagePruned From 68bbe40e3ae2e1164fb7586b5948fe65a2db9da8 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 15:22:45 -0400 Subject: [PATCH 21/73] refactor(schonhage): reuse binomial bounds and sharpen signed tests Address the bounded nonblocking review findings on #1496. Keep Nat.choose_le_exp_mul_div_pow unchanged while deriving it from Cslib.sum_choose_le_exp_one_mul_div_pow in the existing real binomial module; remove the redundant module and migrate its import. Preserve Anthropic, Samuel Schlesinger, OpenAI and Erik Demaine credit. Kernel-check an actual mixed inner/outer L2,m1 signed pruned result of -8, remove two signature-only tests and retain positive admitted-parameter checks. Paraphrase the identified source-derived sentence, shorten generic source boilerplate and record the generic naming follow-up. No public mathematical statement or definition changes meaning; no selected abstract or endpoint promotion. Final validation/source/review evidence is in PR #1496. AI-assisted implementation by codex-three-sum-continuation-t3-3dda8289. --- Complexity.lean | 1 - .../Schonhage/Parameters.lean | 2 +- .../Schonhage/Sparse.lean | 4 +- .../Foundations/Algebra/FiniteMagnitude.lean | 5 +- .../Foundations/Algebra/GeometricTail.lean | 5 +- .../Combinatorics/BinomialUpperBound.lean | 51 ------------------- .../Combinatorics/FunctionPlaces.lean | 5 +- .../Foundations/Data/Finset/OrderGlue.lean | 5 +- .../Foundations/Data/Nat/Choose/Real.lean | 32 +++++++++++- ComplexityTests/SchonhageThinProduct.lean | 28 ++++------ NOTICE | 6 ++- UPSTREAM.md | 1 + 12 files changed, 58 insertions(+), 87 deletions(-) delete mode 100644 Complexity/Foundations/Combinatorics/BinomialUpperBound.lean diff --git a/Complexity.lean b/Complexity.lean index 1cdd3b3c0..9674671a2 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1937,7 +1937,6 @@ public import Complexity.Foundations.Combinatorics.AffineDisperser.IndependentSo public import Complexity.Foundations.Combinatorics.AffineDisperser.Threshold public import Complexity.Foundations.Combinatorics.BalancedSchedules public import Complexity.Foundations.Combinatorics.BinomialTail -public import Complexity.Foundations.Combinatorics.BinomialUpperBound public import Complexity.Foundations.Combinatorics.CentralBinomial public import Complexity.Foundations.Combinatorics.DepthReduction public import Complexity.Foundations.Combinatorics.Expander.AffineFamily diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Parameters.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Parameters.lean index 10b5d1679..38a936be5 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Parameters.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Parameters.lean @@ -9,7 +9,7 @@ module public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Sparse public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Tiling -public import Complexity.Foundations.Combinatorics.BinomialUpperBound +public import Complexity.Foundations.Data.Nat.Choose.Real public import Mathlib.Analysis.Complex.ExponentialBounds /-! diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Sparse.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Sparse.lean index b18c209f3..1b2cab585 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Sparse.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Sparse.lean @@ -261,8 +261,8 @@ private theorem sum_alpha_small_orders_le (m : ℕ) : · simp [alpha, saving] · exact (sum_alpha_small_orders_lt m hm).le -/-- In the proof of Lemma 11: We use |U| α_d for the orders d ≤ m/9 and β_d for the orders -d > m/9. -/ +/-- Split the order sum at `m / 9`, applying the query-dependent estimate below the cutoff +and the layout-dependent estimate above it. -/ private theorem card_leaves_le_split {L m : ℕ} (U : Finset (OutStr L)) (hU : ∀ w ∈ U, (innerSetO w).card = m) : (leaves U).card diff --git a/Complexity/Foundations/Algebra/FiniteMagnitude.lean b/Complexity/Foundations/Algebra/FiniteMagnitude.lean index e18125684..4bf60b86e 100644 --- a/Complexity/Foundations/Algebra/FiniteMagnitude.lean +++ b/Complexity/Foundations/Algebra/FiniteMagnitude.lean @@ -20,10 +20,9 @@ public import Mathlib.Algebra.Order.BigOperators.Ring.Finset ## References -Adapted selectively from [AlmanWilliams2026, Section 2] and +Adapted selectively from [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). -Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical -counts and identities; they give no native program or instruction-cost certificate. +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. -/ @[expose] public section diff --git a/Complexity/Foundations/Algebra/GeometricTail.lean b/Complexity/Foundations/Algebra/GeometricTail.lean index c9b5095dc..ca762bc79 100644 --- a/Complexity/Foundations/Algebra/GeometricTail.lean +++ b/Complexity/Foundations/Algebra/GeometricTail.lean @@ -20,10 +20,9 @@ public import Mathlib.Algebra.Order.BigOperators.Ring.Finset ## References -Adapted selectively from [AlmanWilliams2026, Section 2] and +Adapted selectively from [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). -Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical -counts and identities; they give no native program or instruction-cost certificate. +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. -/ @[expose] public section diff --git a/Complexity/Foundations/Combinatorics/BinomialUpperBound.lean b/Complexity/Foundations/Combinatorics/BinomialUpperBound.lean deleted file mode 100644 index 6036e4144..000000000 --- a/Complexity/Foundations/Combinatorics/BinomialUpperBound.lean +++ /dev/null @@ -1,51 +0,0 @@ -/- -Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Anthropic, PBC, Erik Demaine --/ - -module --- upstream: candidate - -public import Complexity.Init -public import Mathlib.Analysis.Complex.Exponential -public import Mathlib.Data.Nat.Choose.Bounds - -/-! -# An exponential upper bound on binomial coefficients - -## Main results - -* `Nat.choose_le_exp_mul_div_pow`: the bound `(e n / k)^k`, including `k = 0`. - -## References - -Adapted selectively from [AlmanWilliams2026, Section 2] and -[Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). -Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical -counts and identities; they give no native program or instruction-cost certificate. --/ - -@[expose] public section - -namespace Nat - -/-- `binom(n, k) ≤ (e n / k) ^ k`: indeed `binom(n, k) ≤ n ^ k / k!`, and -`k ^ k / k! ≤ e ^ k` is one term of the exponential series. For `k = 0` both sides are `1`. -/ -theorem choose_le_exp_mul_div_pow (n k : ℕ) : - (n.choose k : ℝ) ≤ (Real.exp 1 * (n : ℝ) / (k : ℝ)) ^ k := by - obtain rfl | hk := k.eq_zero_or_pos - · rw [choose_zero_right, pow_zero, cast_one] - have hk' : (0 : ℝ) < k := cast_pos.2 hk - have hfact : (0 : ℝ) < k.factorial := cast_pos.2 k.factorial_pos - have hexp : (k : ℝ) ^ k ≤ Real.exp k * k.factorial := - (div_le_iff₀ hfact).1 (Real.pow_div_factorial_le_exp _ hk'.le k) - rw [div_pow, mul_pow, ← Real.exp_nat_mul, mul_one] - refine (choose_le_pow_div k n).trans ?_ - rw [div_le_div_iff₀ hfact (pow_pos hk' k)] - calc (n : ℝ) ^ k * (k : ℝ) ^ k ≤ (n : ℝ) ^ k * (Real.exp k * k.factorial) := - mul_le_mul_of_nonneg_left hexp (pow_nonneg n.cast_nonneg k) - _ = Real.exp k * (n : ℝ) ^ k * k.factorial := by ring - - -end Nat diff --git a/Complexity/Foundations/Combinatorics/FunctionPlaces.lean b/Complexity/Foundations/Combinatorics/FunctionPlaces.lean index e2ac74931..206c61f3d 100644 --- a/Complexity/Foundations/Combinatorics/FunctionPlaces.lean +++ b/Complexity/Foundations/Combinatorics/FunctionPlaces.lean @@ -23,10 +23,9 @@ public import Mathlib.Data.Fintype.BigOperators ## References -Adapted selectively from [AlmanWilliams2026, Section 2] and +Adapted selectively from [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). -Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical -counts and identities; they give no native program or instruction-cost certificate. +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. -/ @[expose] public section diff --git a/Complexity/Foundations/Data/Finset/OrderGlue.lean b/Complexity/Foundations/Data/Finset/OrderGlue.lean index 9a61f690c..3cff17ec3 100644 --- a/Complexity/Foundations/Data/Finset/OrderGlue.lean +++ b/Complexity/Foundations/Data/Finset/OrderGlue.lean @@ -23,10 +23,9 @@ public import Mathlib.Data.Finset.Sort ## References -Adapted selectively from [AlmanWilliams2026, Section 2] and +Adapted selectively from [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). -Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical -counts and identities; they give no native program or instruction-cost certificate. +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. -/ @[expose] public section diff --git a/Complexity/Foundations/Data/Nat/Choose/Real.lean b/Complexity/Foundations/Data/Nat/Choose/Real.lean index 8265f9eab..b8e5e8f6a 100644 --- a/Complexity/Foundations/Data/Nat/Choose/Real.lean +++ b/Complexity/Foundations/Data/Nat/Choose/Real.lean @@ -1,7 +1,7 @@ /- -Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Copyright (c) 2026 Samuel Schlesinger, Anthropic, PBC, Erik Demaine. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Samuel Schlesinger, OpenAI +Authors: Samuel Schlesinger, OpenAI, Anthropic, PBC, Erik Demaine -/ module @@ -34,6 +34,7 @@ generating-function bound, motivated by the geometric thresholds in Appendix A.3 * `Cslib.sum_choose_le_exp_mul_pow`: a real-parameter generating-function bound. * `Cslib.sum_choose_le_exp_one_mul_div_pow`: the bound with parameter `q / e`. +* `Nat.choose_le_exp_mul_div_pow`: its single-binomial-coefficient consequence, for all `n, k`. * `Cslib.sum_choose_le_two_rpow_div`: the geometric tail under an exact integer budget test. * `Cslib.sum_choose_le_two_pow_div_add_one`: its natural-number rounded form. * `Cslib.le_mul_logb_of_two_pow_le_sum_choose_sq`: the logarithmic consequence. @@ -113,6 +114,33 @@ theorem sum_choose_le_exp_one_mul_div_pow {q e : ℕ} (heq : e ≤ q) : rw [mul_div_assoc, mul_pow, ← Real.exp_nat_mul] simp +end Cslib + +namespace Nat + +/-- A single binomial coefficient is at most `(exp 1 * n / k)^k`, including `k = 0`. +This follows from `Cslib.sum_choose_le_exp_one_mul_div_pow` when `k ≤ n`; otherwise the +coefficient vanishes. The statement is also present in +[Anthropic's formal source](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Util/Choose.lean#L52-L53); +this proof reuses the partial-sum bound. -/ +theorem choose_le_exp_mul_div_pow (n k : ℕ) : + (n.choose k : ℝ) ≤ (Real.exp 1 * (n : ℝ) / (k : ℝ)) ^ k := by + by_cases hkn : k ≤ n + · calc + (n.choose k : ℝ) ≤ ∑ j ∈ Finset.range (k + 1), (n.choose j : ℝ) := + Finset.single_le_sum (fun j _ => Nat.cast_nonneg (n.choose j)) + (Finset.mem_range.mpr (by omega)) + _ ≤ (Real.exp 1 * (n : ℝ) / (k : ℝ)) ^ k := + Cslib.sum_choose_le_exp_one_mul_div_pow hkn + · rw [choose_eq_zero_of_lt (by omega), cast_zero] + positivity + +end Nat + +namespace Cslib + +open Finset + /-- For any input size `N`, the tail with dimension `C * N` and radius `Γ * N / M` is at most `2 ^ (N / B)` when the exact integer test `B * Γ * ⌈log₂ (4 * C * M)⌉ ≤ M` holds. This is an original corollary of `sum_choose_le_exp_mul_pow`, motivated by Appendix A.3 of diff --git a/ComplexityTests/SchonhageThinProduct.lean b/ComplexityTests/SchonhageThinProduct.lean index 3eff3864f..9ab0067ef 100644 --- a/ComplexityTests/SchonhageThinProduct.lean +++ b/ComplexityTests/SchonhageThinProduct.lean @@ -75,27 +75,29 @@ example : Pruned.result 1 (encodingL (bandArrayL outerLayout X 0)) (encodingR (bandArrayR outerLayout Y 1)) (wantedStrings outerLayout W 0 1) (outStrOfPos outerLayout 2 3) = 0 := by decide +kernel --- Every queried product entry, through the general correctness theorem. -example (I J : Fin 4) (h : (I, J) ∈ W) : - Pruned.result 1 (encodingL (bandArrayL outerLayout X (bandOf 1 0 I))) - (encodingR (bandArrayR outerLayout Y (bandOf 1 0 J))) - (wantedStrings outerLayout W (bandOf 1 0 I) (bandOf 1 0 J)) - (outStrOfPos outerLayout I J) = (X * Y) I J := - thin_product_spec outerLayout X Y W I J h - example : ∑ T ∈ tiles 1 0 4, (wantedStrings outerLayout W T.1 T.2).card = 3 := by decide +kernel example : ∑ T ∈ tiles 1 0 4, (leaves (wantedStrings outerLayout W T.1 T.2)).card = 3 := by decide example : wantedStrings outerLayout W 1 1 = ∅ := by decide example : (leaves (wantedStrings outerLayout W 1 1)).card = 0 := by decide +kernel --- Four signed summands at a genuine inner level; the tensor's error contributes nothing. +-- Four signed summands with a single inner output; there is no outer error coefficient here. private def A : Matrix (Fin 1) (Fin 4) ℤ := !![-3, 2, 0, 5] private def B : Matrix (Fin 4) (Fin 1) ℤ := !![2; -1; 4; 0] example : Full.result 1 (bandArrayL innerLayout A 0) (bandArrayR innerLayout B 0) (outStrOfPos innerLayout 0 0) = -8 := by decide +kernel +-- A mixed inner/outer output checks the exact-inner-count zeroing that eliminates error terms. +private def mixedX (_Q : Finset (Fin 2)) : LeftMat 2 1 := + fun _ π => A 0 (finProdFinEquiv (π 0)) +private def mixedY (_Q : Finset (Fin 2)) : RightMat 2 1 := + fun π _ => B (finProdFinEquiv (π 0)) 0 +private def mixedOutput : OutStr 2 := ![.z0, .z 0 0] + +example : Pruned.result 2 (encodingL (arrayL 1 mixedX)) (encodingR (arrayR 1 mixedY)) + {mixedOutput} mixedOutput = -8 := by decide +kernel + example : Pruned.result 1 (encodingL (bandArrayL innerLayout A 0)) (encodingR (bandArrayR innerLayout B 0)) (wantedStrings innerLayout ({(0, 0)} : Finset (Fin 1 × Fin 1)) 0 0) @@ -127,14 +129,6 @@ example : D 1 ^ 18 ≤ admittedN ∧ singletonQuery.card * 2 ^ 1 ≤ admittedN ^ · rfl · norm_num [singletonQuery, admittedN] -example (X' : Matrix (Fin admittedN) (Fin (D 1)) ℤ) - (Y' : Matrix (Fin (D 1)) (Fin admittedN) ℤ) : - Pruned.result 19 (encodingL (bandArrayL admittedLayout X' (bandOf 19 1 0))) - (encodingR (bandArrayR admittedLayout Y' (bandOf 19 1 0))) - (wantedStrings admittedLayout singletonQuery (bandOf 19 1 0) (bandOf 19 1 0)) - (outStrOfPos admittedLayout 0 0) = (X' * Y') 0 0 := - thin_product_spec admittedLayout X' Y' singletonQuery 0 0 (by simp [singletonQuery]) - example : ((2 * numBands (19 * 1) 1 admittedN * 10 ^ (19 * 1) : ℕ) : ℝ) ≤ 4 * (D 1 : ℝ) ^ (-(1 / 18 : ℝ)) * (admittedN : ℝ) ^ 2 := by diff --git a/NOTICE b/NOTICE index 65eb16f1c..4f0b9376d 100644 --- a/NOTICE +++ b/NOTICE @@ -4,7 +4,7 @@ Complexity/Algorithms/MatrixMultiplication/Schonhage/, Complexity/Algorithms/ThreeSum/, Complexity/Algorithms/WordRAM/ThreeSum/, Complexity/Foundations/Algebra/{FiniteMagnitude,GeometricTail}.lean, -Complexity/Foundations/Combinatorics/{BinomialUpperBound,FunctionPlaces}.lean, +Complexity/Foundations/Combinatorics/FunctionPlaces.lean, Complexity/Foundations/Data/Finset/{Average,OrderGlue,Quotient}.lean, Complexity/Foundations/Data/Int/Residue.lean, Complexity/Foundations/Data/Nat/CeilDiv.lean, @@ -14,6 +14,10 @@ Complexity/Foundations/RingTheory/CyclicIntRing.lean are adapted from anthropics/formal-math, 3sum-apsp, revision 16a5ce41304fcdaea4e0be7c132e1c361dc26da5. +The single-coefficient binomial bound in Complexity/Foundations/Data/Nat/Choose/Real.lean +retains its source credit from that adaptation; its proof now derives the statement from the +library's existing partial-binomial-sum bound. + ThreeSumApsp: a Lean 4 formalization of "Truly Subquadratic 3SUM and Truly Subcubic APSP via Triangles in Sparse Lopsided Graphs" Copyright 2026 Anthropic, PBC diff --git a/UPSTREAM.md b/UPSTREAM.md index dde42fa02..3dfd78c52 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -287,6 +287,7 @@ agreement during upstream review. | `Foundations/Combinatorics/{FiniteCover,Hoeffding,Hypergeometric,PowersetTail,Sunflower}` | Consider Mathlib combinatorics; probabilistic-method lemmas belong near `Combinatorics/Probabilistic`. | | `Foundations/Data/Finset/Expect` | Product-type expectation helpers extracted from `Foundations/Combinatorics/Expander/Products` (#317); Mathlib candidates. | | `Foundations/Data/Finset/Markov` | Mathlib candidate: `Algebra/Order/BigOperators/Group/Finset` (`Finset.card_filter_mul_le_sum`, `Finset.mul_card_filter_le_sum`). | +| `Foundations/Data/Finset/OrderGlue` | Follow-up to #1496: rename the generic `forall_level_iff`, `exists_innerLevel`, `outerLevel_notMem` and `filter_glue` using order-embedding/complement terminology (for example `forall_iff_orderEmb_and_compl`), retaining this topic module. Migrate the concrete `Schonhage/Levels` and `Schonhage/Batch` clients together with the active native band-preparation client; defer that API migration until the latter is stable. | | `Foundations/Data/Matrix/Permanent` | Mathlib candidate: Laplace expansion of the permanent along row and column 0, beside `LinearAlgebra/Matrix/Permanent` (mirroring `Matrix.det_succ_row_zero`). | | `Foundations/LinearAlgebra/{Rank,Matrix/CutRank,Matrix/Lindsey}` | Mathlib candidates: `LinearAlgebra/Dimension/LinearMap` and `LinearAlgebra/Matrix/Rank`; placement of Lindsey's lemma needs agreement. | | `Probability/PairwiseIndependentHash/Shift` | Consider integration into `PairwiseIndependentHash`. | From cb261f6b95c87eacbba84611956af697a6a086cb Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 15:41:56 -0400 Subject: [PATCH 22/73] feat(WordRAM): prepare and encode every shared matrix band Implement the complete bounded #1523 native component under #1394. A fixed 96-register standard WordRAM program computes valid subset/inverse tables and a deterministic existing Layout, prepares all padded signed row/column bands, calls the unchanged #1520 encoders once per shared band, collects every leaf, and restores the entire initialized input prefix and outside frame. The State.init and flat Runs/Executes clients bind the actual output memory to public encodingL/R in the unchanged leafIndex order (P0 digit 9). All powers, mask/popcount/root/table loops, indices, guarded matrix addresses, initialization, copying, encoder calls, loop controls and halt are charged. The generic bound is 4096*(L+1)*(1+N*4^m+2^L+2*numBands*10^L), plus halt; at L=19m and N>=(4^m)^18, the full charged run is <=81920*(L+1)*N^2/(4^m)^(1/18). Width assumptions are explicit original-parameter inequalities: the wordBudget fits target w+2 and 7^L*A < 2^(w-1), w>0, A>=0. Actual signed kernels use at most 15/17 instructions for add/subtract, already included through the unchanged encoder's real cost. No all-state fit, callback, solver, assumed run/cost or source machine semantics is a hypothesis. Symbolic positive-depth thin native examples and concrete signed/padded/frame/width-boundary executions establish nonvacuity. Pinned source: anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5, 3sum-apsp/ThreeSumApsp/Programs/Sec2/Theorem5/Encode/AllBands.lean:230-239,262-271 and BandArrayFacts.lean:107-113. Verified checkout files equal their pinned Git blobs and all PR-draft quotations match those exact line slices. The source's row/column encoding predicates map to runs' quantified original matrices, all bands and all leaves on computed MaskTable.layout; Light limits/program maps, callee contracts and source time hypotheses are replaced by actual native code proofs, not imported. The block index is beta*K0+(side? h:g), and row is block*N0 plus the compressed outer offset. Shared tile pointers and retained table words are exposed by tileInputBases/tile_input_values/table_word_layout. Reuse unchanged WordRAM control/rename/copy/signed APIs, Layout/bandArray/encoding mathematics and Mathlib finite index/sorted-set APIs. New mask scans, dense inverse lookup, direct base-seven decoder and native execution/cost composition are original. Small native-power/ordered-filter/positional-digit/row-major lemmas live in narrow topic modules. Retain Anthropic, PBC source credit and NOTICE. Validation: PASS all six strict gates (build --wfail --iofail, mk_all --check, test --wfail --iofail, lint, lint-style, checkInitImports), serial Smoke/Axioms warmups and the global Axioms audit; full citation-aware hygiene --check-simp, fresh actual-base changed-file lints, strict PR-name and diff checks. Validated with LEAN_NUM_THREADS=1 and cache downloads disabled. Windows static metadata reads used PYTHONUTF8=1; no Lean/source change was needed. New Lean files use ordinary non-executable file modes. Exact payload tree bdfc5a1a33f0d712b90f4fe4bc9d6c27e2d8318e. Targeted Lean MCP endpoint/tests and pinned quote identity also pass. No PR has been opened; independent exact-revision correctness/API/source-mapping review is pending. Abstract assessment: no impact on selected abstracts, models or scope; no new headline or PDFs. Pruned queries, products, solvers and whole ordinary 3SUM remain open under #1394. Base #1520 remains unchanged at 3b9234da6bc3c0daad2cd167aeb4fd8f9fc592b6. AI assistance: Codex implementation/proofs, session codex-native-bandprep-t3-03b600bd, under delegating-worker coordination. Existing unchanged encoder outside-review evidence is PR1520#issuecomment-6477077023; final new-component review pending. --- Complexity.lean | 29 ++ .../Schonhage/BandBounds.lean | 102 +++++ .../Schonhage/MaskTable.lean | 315 ++++++++++++++++ .../Schonhage/SplitDigits.lean | 103 +++++ .../Schonhage/BandAll.lean | 171 +++++++++ .../Schonhage/BandCatalogue.lean | 70 ++++ .../Schonhage/BandCollect.lean | 119 ++++++ .../Schonhage/BandCopy.lean | 134 +++++++ .../Schonhage/BandCost.lean | 70 ++++ .../Schonhage/BandCount.lean | 112 ++++++ .../Schonhage/BandEncode.lean | 137 +++++++ .../Schonhage/BandExec.lean | 245 ++++++++++++ .../Schonhage/BandFlat.lean | 118 ++++++ .../Schonhage/BandHeader.lean | 278 ++++++++++++++ .../Schonhage/BandInput.lean | 149 ++++++++ .../Schonhage/BandMeaning.lean | 351 ++++++++++++++++++ .../Schonhage/BandMemory.lean | 124 +++++++ .../Schonhage/BandParameters.lean | 152 ++++++++ .../Schonhage/BandPopulation.lean | 114 ++++++ .../Schonhage/BandPrepare.lean | 182 +++++++++ .../Schonhage/BandProgram.lean | 322 ++++++++++++++++ .../Schonhage/BandRead.lean | 196 ++++++++++ .../Schonhage/BandRoot.lean | 101 +++++ .../Schonhage/BandSplit.lean | 247 ++++++++++++ .../Schonhage/BandTables.lean | 280 ++++++++++++++ .../Schonhage/BandThin.lean | 76 ++++ Complexity/Computability/WordRAM/Power.lean | 93 +++++ .../Data/Finset/OrderedFilter.lean | 42 +++ .../Foundations/Data/List/RowMajor.lean | 64 ++++ Complexity/Foundations/Data/Nat/OfDigits.lean | 46 +++ ComplexityTests/SchonhageBandPreparation.lean | 167 +++++++++ NOTICE | 4 + ROADMAP.md | 7 +- UPSTREAM.md | 19 +- 34 files changed, 4736 insertions(+), 3 deletions(-) create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/BandBounds.lean create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/MaskTable.lean create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/SplitDigits.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandAll.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCatalogue.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCollect.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCopy.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCost.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCount.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandEncode.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandExec.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandFlat.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandHeader.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandInput.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandMeaning.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandMemory.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandParameters.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandPopulation.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandPrepare.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandProgram.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandRead.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandRoot.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandSplit.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandTables.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandThin.lean create mode 100644 Complexity/Computability/WordRAM/Power.lean create mode 100644 Complexity/Foundations/Data/Finset/OrderedFilter.lean create mode 100644 Complexity/Foundations/Data/List/RowMajor.lean create mode 100644 Complexity/Foundations/Data/Nat/OfDigits.lean create mode 100644 ComplexityTests/SchonhageBandPreparation.lean diff --git a/Complexity.lean b/Complexity.lean index 04ec040e5..d530239c9 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -30,6 +30,7 @@ public import Complexity.Algorithms.Lean.VanEmdeBoas.Insert public import Complexity.Algorithms.Lean.VanEmdeBoas.Layout public import Complexity.Algorithms.Lean.VanEmdeBoas.Successor public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Alphabets +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.BandBounds public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Batch public import Complexity.Algorithms.MatrixMultiplication.Schonhage.CoordinateTransform public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Full @@ -37,10 +38,12 @@ public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Identity public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Layout public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Levels public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Magnitude +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.MaskTable public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Orders public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Parameters public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Pruned public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Sparse +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.SplitDigits public import Complexity.Algorithms.MatrixMultiplication.Schonhage.ThinProduct public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Tiling public import Complexity.Algorithms.Reachability.BreadthFirst @@ -104,6 +107,28 @@ public import Complexity.Algorithms.WordRAM.FusionTree.Query.Traversal public import Complexity.Algorithms.WordRAM.FusionTree.Rank public import Complexity.Algorithms.WordRAM.FusionTree.ReadHeader public import Complexity.Algorithms.WordRAM.FusionTree.Static +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandAll +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandCatalogue +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandCollect +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandCopy +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandCost +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandCount +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandEncode +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandExec +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandFlat +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandHeader +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandInput +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandMeaning +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandMemory +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandParameters +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPopulation +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPrepare +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandProgram +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandRead +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandRoot +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandSplit +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandTables +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandThin public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Cell public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Controller public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Copy @@ -1650,6 +1675,7 @@ public import Complexity.Computability.WordRAM.Multiword.Workspace public import Complexity.Computability.WordRAM.Native public import Complexity.Computability.WordRAM.Notation public import Complexity.Computability.WordRAM.PackedCount +public import Complexity.Computability.WordRAM.Power public import Complexity.Computability.WordRAM.RAM public import Complexity.Computability.WordRAM.RAMInput public import Complexity.Computability.WordRAM.Randomized.Defs @@ -2098,6 +2124,7 @@ public import Complexity.Foundations.Data.Finset.Average public import Complexity.Foundations.Data.Finset.Expect public import Complexity.Foundations.Data.Finset.Markov public import Complexity.Foundations.Data.Finset.OrderGlue +public import Complexity.Foundations.Data.Finset.OrderedFilter public import Complexity.Foundations.Data.Finset.Quantile public import Complexity.Foundations.Data.Finset.Quotient public import Complexity.Foundations.Data.Finset.Sampling @@ -2119,6 +2146,7 @@ public import Complexity.Foundations.Data.List.Lex public import Complexity.Foundations.Data.List.Lookup public import Complexity.Foundations.Data.List.OfFn public import Complexity.Foundations.Data.List.Range +public import Complexity.Foundations.Data.List.RowMajor public import Complexity.Foundations.Data.List.SelectDistinct public import Complexity.Foundations.Data.Matrix.Binary public import Complexity.Foundations.Data.Matrix.IntegerBits @@ -2151,6 +2179,7 @@ public import Complexity.Foundations.Data.Nat.Div public import Complexity.Foundations.Data.Nat.Factorial public import Complexity.Foundations.Data.Nat.IntermediateValue public import Complexity.Foundations.Data.Nat.Log +public import Complexity.Foundations.Data.Nat.OfDigits public import Complexity.Foundations.Data.Nat.PolynomialBound.Closure public import Complexity.Foundations.Data.Nat.PolynomialBound.Exponential public import Complexity.Foundations.Data.Nat.PolynomialBound.Log diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/BandBounds.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/BandBounds.lean new file mode 100644 index 000000000..4fc53bbe6 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/BandBounds.lean @@ -0,0 +1,102 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Layout +import Mathlib.Data.Nat.Choose.Bounds +import Complexity.Algorithms.MatrixMultiplication.Schonhage.Tiling + +/-! +# Bounds for guarded band addressing + +## Main results + +* `bandSize_le_seven_pow`: a complete band fits within the tensor input dimension. +* `band_position_bounds`: all padded row/column arithmetic stays below `N+7^L` before + the native guard. Row-major matrix addresses are computed only after that guard. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage + +private theorem mul_add_lt_mul {a b c d : ℕ} (ha : a < b) (hc : c < d) : + a * d + c < b * d := by + have h := Nat.mul_le_mul_right d (Nat.succ_le_of_lt ha) + rw [Nat.succ_mul] at h + omega + +/-- The entire band dimension is bounded by the flat tensor input dimension. -/ +theorem bandSize_le_seven_pow (L m : ℕ) : bandSize L m ≤ 7 ^ L := by + have hK : K0 L m ≤ 2 ^ L := (Nat.sqrt_le_self _).trans (Nat.choose_le_two_pow _ _) + have hN : N0 L m ≤ 3 ^ L := Nat.pow_le_pow_right (by decide) (Nat.sub_le _ _) + calc bandSize L m ≤ 2 ^ L * 3 ^ L := Nat.mul_le_mul hK hN + _ = 6 ^ L := by rw [← Nat.mul_pow] + _ ≤ 7 ^ L := Nat.pow_le_pow_left (by decide) _ + +/-- Even the last padded band has small block arithmetic. This bound precedes the `r finFunctionFinEquiv.symm a i = (1 : Fin 2) + +/-- The integer mask of a finite level set, with level zero least significant. -/ +def maskCode {L : ℕ} (Q : Finset (Fin L)) : Fin (2 ^ L) := + finFunctionFinEquiv fun i => if i ∈ Q then (1 : Fin 2) else 0 + +/-- Decoding the explicitly numbered mask recovers the original set. -/ +theorem maskSet_maskCode {L : ℕ} (Q : Finset (Fin L)) : maskSet (maskCode Q) = Q := by + ext i + simp [maskSet, maskCode] + +/-- Encoding a decoded bounded mask recovers the original integer. -/ +theorem maskCode_maskSet {L : ℕ} (a : Fin (2 ^ L)) : maskCode (maskSet a) = a := by + apply finFunctionFinEquiv.symm.injective + funext i + simp only [maskCode, Equiv.symm_apply_apply, maskSet, mem_filter, mem_univ, true_and] + have h := (finFunctionFinEquiv.symm a i).isLt + split <;> apply Fin.ext <;> simp_all only [Fin.ext_iff, Fin.val_one, Fin.val_zero] + omega + +/-- Binary-mask numbering is an explicit bijection, not a chosen enumeration. -/ +def maskEquiv (L : ℕ) : Fin (2 ^ L) ≃ Finset (Fin L) where + toFun := maskSet + invFun := maskCode + left_inv := maskCode_maskSet + right_inv := maskSet_maskCode + +/-- Number of one digits among exactly the first `L` binary digits. -/ +def population (L a : ℕ) : ℕ := ∑ i : Fin L, a / 2 ^ i.val % 2 + +/-- A zero-depth scan has population zero. -/ +theorem population_zero (a : ℕ) : population 0 a = 0 := by simp [population] + +/-- One native remainder/division step removes the lowest digit. -/ +theorem population_succ (L a : ℕ) : + population (L + 1) a = a % 2 + population L (a / 2) := by + simp only [population, Fin.sum_univ_succ, Fin.val_zero, pow_zero, Nat.div_one, + Fin.val_succ, pow_succ', ← Nat.div_div_eq_div_mul] + +/-- There are at most `L` one digits, even for an unbounded natural argument. -/ +theorem population_le (L a : ℕ) : population L a ≤ L := by + induction L generalizing a with + | zero => rw [population_zero] + | succ L ih => + rw [population_succ] + have hm := Nat.mod_lt a (by decide : 0 < 2) + have ht := ih (a / 2) + omega + +/-- The loop's digit sum is the cardinality of the represented level set. -/ +theorem population_eq_card {L : ℕ} (a : Fin (2 ^ L)) : + population L a.val = (maskSet a).card := by + rw [maskSet, card_eq_sum_ones, sum_filter] + apply sum_congr rfl + intro i _ + have h : (finFunctionFinEquiv.symm a i).val = a.val / 2 ^ i.val % 2 := rfl + have hb := (finFunctionFinEquiv.symm a i).isLt + split <;> simp_all only [Fin.ext_iff, Fin.val_one] + omega + +/-- All valid masks, in the ordinary linear order on bounded naturals. -/ +def validMasks (L m : ℕ) : Finset (Fin (2 ^ L)) := + univ.filter fun a => population L a.val = m + +/-- Every size-`m` subset occurs once in the bounded mask scan. -/ +theorem card_validMasks (L m : ℕ) : (validMasks L m).card = K L m := by + calc + (validMasks L m).card = ((univ : Finset (Fin L)).powersetCard m).card := by + apply card_bij (fun a _ => maskSet a) + · intro a ha + rw [mem_powersetCard_univ] + rw [← population_eq_card] + exact (mem_filter.mp ha).2 + · intro a _ b _ h + exact (maskEquiv L).injective h + · intro Q hQ + refine ⟨maskCode Q, ?_, maskSet_maskCode Q⟩ + simp only [validMasks, mem_filter, mem_univ, true_and, population_eq_card, + maskSet_maskCode] + exact mem_powersetCard_univ.mp hQ + _ = K L m := by rw [card_powersetCard, card_univ, Fintype.card_fin]; rfl + +/-- Number of valid masks strictly below the scan counter. -/ +def prefixCount (L m t : ℕ) : ℕ := ((range t).filter fun a => population L a = m).card + +/-- The scan begins without any accepted mask. -/ +theorem prefixCount_zero (L m : ℕ) : prefixCount L m 0 = 0 := by simp [prefixCount] + +/-- A scan accepts at most one mask in each iteration. -/ +theorem prefixCount_le (L m t : ℕ) : prefixCount L m t ≤ t := + (card_filter_le _ _).trans (by rw [card_range]) + +/-- The actual scalar counter update for one mask. -/ +theorem prefixCount_succ (L m t : ℕ) : + prefixCount L m (t + 1) = prefixCount L m t + if population L t = m then 1 else 0 := by + by_cases h : population L t = m <;> simp [prefixCount, range_add_one, filter_insert, h] + +/-- Extending a scan cannot decrease the accepted-mask counter. -/ +theorem prefixCount_mono (L m : ℕ) {a b : ℕ} (h : a ≤ b) : + prefixCount L m a ≤ prefixCount L m b := + card_le_card (filter_subset_filter _ (range_mono h)) + +/-- Earlier valid masks have strictly smaller accepted-mask ranks. -/ +theorem prefixCount_lt {L m a b : ℕ} (ha : population L a = m) (hab : a < b) : + prefixCount L m a < prefixCount L m b := by + have h := prefixCount_mono L m (show a + 1 ≤ b by omega) + rw [prefixCount_succ] at h + simp only [ha, ↓reduceIte] at h + omega + +/-- The finite complete scan has exactly the binomial count. -/ +theorem prefixCount_full (L m : ℕ) : prefixCount L m (2 ^ L) = K L m := by + rw [← card_validMasks] + apply card_bij (fun a ha => (⟨a, mem_range.mp (mem_filter.mp ha).1⟩ : Fin (2 ^ L))) + · intro a ha + exact mem_filter.mpr ⟨mem_univ _, (mem_filter.mp ha).2⟩ + · intro a _ b _ h + exact congrArg Fin.val h + · intro a ha + exact ⟨a.val, mem_filter.mpr ⟨mem_range.mpr a.isLt, (mem_filter.mp ha).2⟩, rfl⟩ + +private theorem card_below_orderEmb {α : Type*} [LinearOrder α] (s : Finset α) + {n : ℕ} (h : s.card = n) (i : Fin n) : + (s.filter fun a => a < s.orderEmbOfFin h i).card = i.val := by + rw [← card_range i.val] + symm + apply card_bij (fun j hj => s.orderEmbOfFin h + ⟨j, (mem_range.mp hj).trans i.isLt⟩) + · intro j hj + exact mem_filter.mpr ⟨s.orderEmbOfFin_mem h _, + (s.orderEmbOfFin h).strictMono (mem_range.mp hj)⟩ + · intro j hj k hk hjk + exact congrArg Fin.val ((s.orderEmbOfFin h).injective hjk) + · intro a ha + let j := (s.orderIsoOfFin h).symm ⟨a, (mem_filter.mp ha).1⟩ + have hj : s.orderEmbOfFin h j = a := + congrArg Subtype.val ((s.orderIsoOfFin h).apply_symm_apply ⟨a, (mem_filter.mp ha).1⟩) + have hji : j < i := (s.orderEmbOfFin h).lt_iff_lt.mp (hj ▸ (mem_filter.mp ha).2) + exact ⟨j.val, mem_range.mpr hji, hj⟩ + +/-- The scalar scan count equals the rank among the bounded valid masks. -/ +theorem prefixCount_eq_bounded (L m t : ℕ) (ht : t ≤ 2 ^ L) : + prefixCount L m t = ((validMasks L m).filter fun a => a.val < t).card := by + apply card_bij (fun a ha => + (⟨a, (mem_range.mp (mem_filter.mp ha).1).trans_le ht⟩ : Fin (2 ^ L))) + · intro a ha + exact mem_filter.mpr ⟨mem_filter.mpr ⟨mem_univ _, (mem_filter.mp ha).2⟩, + mem_range.mp (mem_filter.mp ha).1⟩ + · intro a _ b _ h + exact congrArg Fin.val h + · intro a ha + exact ⟨a.val, mem_filter.mpr ⟨mem_range.mpr (mem_filter.mp ha).2, + (mem_filter.mp (mem_filter.mp ha).1).2⟩, rfl⟩ + +/-- Row-major block-pair rank in the full list of size-`m` masks. -/ +def pairRank {L m : ℕ} (gh : Fin (K0 L m) × Fin (K0 L m)) : Fin (K L m) := + Fin.castLE (by simpa only [K0] using Nat.sqrt_le (K L m)) (finProdFinEquiv gh) + +/-- The selected mask at a block pair is the corresponding mask in increasing order. -/ +def selectedMask {L m : ℕ} (gh : Fin (K0 L m) × Fin (K0 L m)) : Fin (2 ^ L) := + (validMasks L m).orderEmbOfFin (card_validMasks L m) (pairRank gh) + +/-- The computed prefix counter at a selected mask is its row-major block-pair rank. -/ +theorem prefixCount_selectedMask {L m : ℕ} (gh : Fin (K0 L m) × Fin (K0 L m)) : + prefixCount L m (selectedMask gh).val = (pairRank gh).val := by + rw [prefixCount_eq_bounded _ _ _ (selectedMask gh).isLt.le] + exact card_below_orderEmb (validMasks L m) (card_validMasks L m) (pairRank gh) + +/-- The dense inverse-table word: zero for an unselected mask, otherwise rank plus one. -/ +def lookup (L m a : ℕ) : ℕ := + if a < 2 ^ L ∧ population L a = m ∧ prefixCount L m a < K0 L m * K0 L m + then prefixCount L m a + 1 else 0 + +/-- Exactly the selected valid masks have nonzero inverse-table words. -/ +theorem lookup_ne_zero_iff (L m a : ℕ) : lookup L m a ≠ 0 ↔ + a < 2 ^ L ∧ population L a = m ∧ prefixCount L m a < K0 L m * K0 L m := by + simp [lookup] + +/-- Every inverse-table word is bounded by its original mask plus one. -/ +theorem lookup_le (L m a : ℕ) : lookup L m a ≤ a + 1 := by + unfold lookup + split + · exact Nat.add_le_add_right (prefixCount_le L m a) 1 + · exact Nat.zero_le _ + +/-- The inverse-table word at every selected block pair has the promised rank. -/ +theorem lookup_selectedMask {L m : ℕ} (gh : Fin (K0 L m) × Fin (K0 L m)) : + lookup L m (selectedMask gh).val = (pairRank gh).val + 1 := by + have hm := (validMasks L m).orderEmbOfFin_mem (card_validMasks L m) (pairRank gh) + have hp := (mem_filter.mp hm).2 + change population L (selectedMask gh).val = m at hp + have hr : (pairRank gh).val < K0 L m * K0 L m := (finProdFinEquiv gh).isLt + simp only [lookup, (selectedMask gh).isLt, hp, prefixCount_selectedMask, + hr, and_self, ↓reduceIte] + +/-- A nonzero inverse-table word belongs to one of the selected block pairs. -/ +theorem exists_selectedMask_of_lookup {L m a : ℕ} (h : lookup L m a ≠ 0) : + ∃ gh : Fin (K0 L m) × Fin (K0 L m), (selectedMask gh).val = a := by + unfold lookup at h + split_ifs at h with hc + · obtain ⟨ha, hp, hr⟩ := hc + let aa : Fin (2 ^ L) := ⟨a, ha⟩ + have hmem : aa ∈ validMasks L m := mem_filter.mpr ⟨mem_univ _, hp⟩ + let j := (validMasks L m).orderIsoOfFin (card_validMasks L m) |>.symm ⟨aa, hmem⟩ + have hj : (validMasks L m).orderEmbOfFin (card_validMasks L m) j = aa := + congrArg Subtype.val ((validMasks L m).orderIsoOfFin (card_validMasks L m) + |>.apply_symm_apply ⟨aa, hmem⟩) + have hjrank : j.val = prefixCount L m a := by + rw [prefixCount_eq_bounded L m a ha.le] + have hbelow := card_below_orderEmb (validMasks L m) (card_validMasks L m) j + rw [hj] at hbelow + exact hbelow.symm + let gh := (@finProdFinEquiv (K0 L m) (K0 L m)).symm ⟨prefixCount L m a, hr⟩ + have hprval : (pairRank gh).val = prefixCount L m a := + congrArg Fin.val ((@finProdFinEquiv (K0 L m) (K0 L m)).apply_symm_apply + ⟨prefixCount L m a, hr⟩) + have hpr : pairRank gh = j := Fin.ext (hprval.trans hjrank.symm) + refine ⟨gh, ?_⟩ + change ((validMasks L m).orderEmbOfFin (card_validMasks L m) (pairRank gh)).val = a + rw [hpr, hj] + · exact (h rfl).elim + +/-- Inverse-table division and remainder recover exactly the two block indices. -/ +theorem lookup_blockPair {L m : ℕ} (gh : Fin (K0 L m) × Fin (K0 L m)) : + (lookup L m (selectedMask gh).val - 1) / K0 L m = gh.1.val ∧ + (lookup L m (selectedMask gh).val - 1) % K0 L m = gh.2.val := by + rw [lookup_selectedMask, Nat.add_sub_cancel] + have hpos : 0 < K0 L m := by have := gh.2.isLt; omega + change (gh.2.val + K0 L m * gh.1.val) / K0 L m = gh.1.val ∧ + (gh.2.val + K0 L m * gh.1.val) % K0 L m = gh.2.val + rw [Nat.add_mul_div_left _ _ hpos, Nat.div_eq_of_lt gh.2.isLt, Nat.zero_add, + Nat.add_mul_mod_self_left, Nat.mod_eq_of_lt gh.2.isLt] + exact ⟨rfl, rfl⟩ + +/-- Both decoded block indices are in range whenever the inverse-table word is nonzero. -/ +theorem lookup_block_lt {L m a : ℕ} (h : lookup L m a ≠ 0) (rightSide : Bool) : + (if rightSide then (lookup L m a - 1) % K0 L m + else (lookup L m a - 1) / K0 L m) < K0 L m := by + obtain ⟨gh, hs⟩ := exists_selectedMask_of_lookup h + have hp := lookup_blockPair gh + rw [hs] at hp + cases rightSide + · simpa only [Bool.false_eq_true, ↓reduceIte, hp.1] using gh.1.isLt + · simpa only [↓reduceIte, hp.2] using gh.2.isLt + +/-- Selected masks always have exactly the required cardinality. -/ +theorem card_selectedMask {L m : ℕ} (gh : Fin (K0 L m) × Fin (K0 L m)) : + (maskSet (selectedMask gh)).card = m := by + have h := (validMasks L m).orderEmbOfFin_mem (card_validMasks L m) (pairRank gh) + exact (population_eq_card _).symm.trans ((mem_filter.mp h).2) + +/-- Distinct block pairs receive distinct level sets. -/ +theorem selectedMask_injective (L m : ℕ) : + Function.Injective (fun gh : Fin (K0 L m) × Fin (K0 L m) => maskSet (selectedMask gh)) := by + intro gh gh' h + have hmask := (maskEquiv L).injective h + have hrank := (validMasks L m).orderEmbOfFin (card_validMasks L m) |>.injective hmask + apply finProdFinEquiv.injective + apply Fin.ext + exact congrArg (fun i : Fin (K L m) => i.val) hrank + +/-- The thin-dimension pair digit uses row-major order, as do the encoder's inner variables. -/ +def innerDigits : Fin 4 ≃ Fin 2 × Fin 2 := (@finProdFinEquiv 2 2).symm + +/-- Explicit base-three outer strings and base-four strings of index pairs, with the first +`K0²` binary masks in increasing order as the shared block table. -/ +def layout {L m : ℕ} (hmL : m ≤ L) : Layout L m where + hmL := hmL + rowIdx := finFunctionFinEquiv.symm + colIdx := finFunctionFinEquiv.symm + innerIdx := finFunctionFinEquiv.symm.trans (Equiv.piCongrRight fun _ => innerDigits) + table gh := maskSet (selectedMask gh) + table_card := card_selectedMask + table_injective := selectedMask_injective L m + +end Cslib.Algorithms.MatrixMultiplication.Schonhage.MaskTable diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/SplitDigits.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/SplitDigits.lean new file mode 100644 index 000000000..a610801d1 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/SplitDigits.lean @@ -0,0 +1,103 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.MaskTable +public import Complexity.Foundations.Data.Nat.OfDigits +public import Complexity.Foundations.Data.Finset.OrderedFilter + +/-! +# Splitting flat tensor digits into band indices + +Outer digits `0,1,2` contribute to a base-three offset. Inner digits `3,4,5,6` +contribute to a base-four offset and set the corresponding binary mask bit. All positions +are least significant first, in the encoder's existing numbering. + +## Main definitions + +* `BandDigits.parts`: the three indices computed by the native digit loop. +* `BandDigits.sevenDigits`: exactly `L` digits, retaining leading zeros. + +## Main results + +* `BandDigits.parts_bounds`: component bounds derived from the original depth. +* `BandDigits.parts_lists`: the compressed digit-list interpretation of all three indices. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage.BandDigits + +/-- Binary level mask and the two compressed offsets. -/ +structure Parts where + /-- Inner-level mask. -/ + mask : ℕ + /-- Base-three outer offset. -/ + outer : ℕ + /-- Base-four inner offset. -/ + inner : ℕ + deriving DecidableEq, Repr + +/-- Remove one base-seven digit, recursively interpreting the remaining higher levels. -/ +def parts : ℕ → ℕ → Parts + | 0, _ => ⟨0, 0, 0⟩ + | L + 1, a => + let tail := parts L (a / 7) + if a % 7 < 3 then ⟨2 * tail.mask, a % 7 + 3 * tail.outer, tail.inner⟩ + else ⟨1 + 2 * tail.mask, tail.outer, (a % 7 - 3) + 4 * tail.inner⟩ + +/-- Component fits depend only on the input depth, even for an unbounded argument. -/ +theorem parts_bounds (L a : ℕ) : + (parts L a).mask < 2 ^ L ∧ (parts L a).outer < 3 ^ L ∧ (parts L a).inner < 4 ^ L := by + induction L generalizing a with + | zero => simp [parts] + | succ L ih => + obtain ⟨hm, ho, hi⟩ := ih (a / 7) + have hd : a % 7 < 7 := Nat.mod_lt _ (by decide) + rw [parts] + split <;> dsimp only + · simp only [pow_succ] + constructor + · omega + constructor <;> omega + · simp only [pow_succ] + constructor + · omega + constructor <;> omega + +/-- A fixed number of digits, so that zero digits at the end are retained. -/ +def sevenDigits : ℕ → ℕ → List ℕ + | 0, _ => [] + | L + 1, a => a % 7 :: sevenDigits L (a / 7) + +/-- These are exactly the digits in Mathlib's finite-function numbering. -/ +theorem sevenDigits_eq_ofFn (L a : ℕ) : + sevenDigits L a = List.ofFn (fun i : Fin L => a / 7 ^ i.val % 7) := by + induction L generalizing a with + | zero => simp [sevenDigits] + | succ L ih => + rw [sevenDigits, ih, List.ofFn_succ] + simp only [Fin.val_zero, pow_zero, Nat.div_one, Fin.val_succ, pow_succ', + ← Nat.div_div_eq_div_mul] + +/-- The native indices are precisely the binary mask and the two compressed digit lists. -/ +theorem parts_lists (L a : ℕ) : + (parts L a).mask = Nat.ofDigits 2 ((sevenDigits L a).map fun d => if d < 3 then 0 else 1) ∧ + (parts L a).outer = Nat.ofDigits 3 ((sevenDigits L a).filter fun d => d < 3) ∧ + (parts L a).inner = Nat.ofDigits 4 + (((sevenDigits L a).filter fun d => ¬d < 3).map fun d => d - 3) := by + induction L generalizing a with + | zero => simp [parts, sevenDigits] + | succ L ih => + obtain ⟨hm, ho, hi⟩ := ih (a / 7) + by_cases hd : a % 7 < 3 + · simp [parts, sevenDigits, hd, Nat.ofDigits, hm, ho, hi] + · have hg : 3 ≤ a % 7 := by omega + simp [parts, sevenDigits, hd, hg, Nat.ofDigits, hm, ho, hi] + +end Cslib.Algorithms.MatrixMultiplication.Schonhage.BandDigits diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandAll.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandAll.lean new file mode 100644 index 000000000..1cea6cc66 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandAll.lean @@ -0,0 +1,171 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandEncode + +/-! +# Every shared band, computed once + +## Main results + +* `BandPreparation.exec_encodeBands`: the complete native loop prepares, encodes and collects + all bands of one matrix side. Catalogue cells retain the existing flat leaf numbering. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +-- Static preservation checks reduce the finite persistent-register set. +set_option maxRecDepth 4096 + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Catalogue blocks are ordered by band index, without duplicating them per tile. -/ +theorem catalogue_address_lt {L m N β γ p : ℕ} (side : Bool) (hβγ : β < γ) + (hp : p < 10 ^ L) : + catalogueAddress L m N side β p < catalogueAddress L m N side γ 0 := by + have ht : 0 < 10 ^ L := Nat.pow_pos (by decide) + unfold catalogueAddress + nlinarith only [hβγ, hp, ht] + +/-- The full one-side catalogue interval ends at the block immediately after its last band. -/ +theorem catalogue_block_inside {L m N β : ℕ} (side : Bool) (hβ : β < numBands L m N) : + catalogueAddress L m N side 0 0 ≤ catalogueAddress L m N side β 0 ∧ + catalogueAddress L m N side β 0 + 10 ^ L ≤ + catalogueAddress L m N side (numBands L m N) 0 := by + have ht : 0 < 10 ^ L := Nat.pow_pos (by decide) + unfold catalogueAddress + constructor <;> nlinarith only [hβ, ht, Nat.zero_le β] + +/-- All ordinary band counters fit from the original explicit allocation budget. -/ +theorem bands_fits {w L m N : ℕ} (hfit : Fits w (wordBudget L m N)) : + Fits w (numBands L m N) := by + apply hfit.of_le + unfold wordBudget + have ht : 0 < 10 ^ L := Nat.pow_pos (by decide) + nlinarith only [ht, Nat.zero_le L, Nat.zero_le N, Nat.zero_le (N * D m), + Nat.zero_le (2 ^ L), Nat.zero_le (numBands L m N)] + +/-- The actual native loop calls the complete encoder once for every shared band, charges +all loop control and counter increments, and preserves any outside caller memory. -/ +theorem exec_encodeBands {w L m N : ℕ} (hw : 0 < w) (side : Bool) (x : Data 96) + (hx : BandParameters L m N x) + (htable : ∀ t < 2 ^ L, x.mem (inverseBase L m N + t) = MaskTable.lookup L m t) + (matrix : ℕ → ℕ → ℤ) + (hmat : ∀ r < N, ∀ k < D m, + x.mem (matrixAddress L m N side r k) = encode (matrix r k)) + (A : ℤ) (hA : 0 ≤ A) (ha : ∀ r < N, ∀ k < D m, |matrix r k| ≤ A) + (hmag : (7 ^ L : ℕ) * A < (2 : ℤ) ^ (w - 1)) + (hfit : Fits (w + 2) (wordBudget L m N)) : + ∃ y, ExecWithin (w + 2) (encodeBands side) x y + (numBands L m N * (bandTime L + 3) + 3) ∧ + BandParameters L m N y ∧ + (∀ β < numBands L m N, ∀ p < 10 ^ L, + y.mem (catalogueAddress L m N side β p) = + encode (catalogueValue L m N side matrix β p)) ∧ + (∀ b, ¬(1 ≤ b ∧ b < Encoding.scratchBase L + 10 ^ L) → + ¬(catalogueAddress L m N side 0 0 ≤ b ∧ + b < catalogueAddress L m N side (numBands L m N) 0) → + y.mem b = x.mem b) := by + let start := x.set band 0 + have hs : ExecWithin (B := .standard) (w + 2) (ram[ band ← #0 ]) x start 1 := + ExecWithin.const (fits_zero _) + have hp : BandParameters L m N start := parameters_reframe hx (by + intro i hi + have hne : i ≠ band := by + have hp : ∀ i ∈ parameterRegisters, i ≠ band := by decide + exact hp i hi + simp only [start, Data.set_regs, Function.update_of_ne hne]) + let I (y : Data 96) : Prop := BandParameters L m N y ∧ + y.regs band ≤ numBands L m N ∧ + (∀ β < y.regs band, ∀ p < 10 ^ L, + y.mem (catalogueAddress L m N side β p) = + encode (catalogueValue L m N side matrix β p)) ∧ + (∀ b, ¬(1 ≤ b ∧ b < Encoding.scratchBase L + 10 ^ L) → + ¬(catalogueAddress L m N side 0 0 ≤ b ∧ + b < catalogueAddress L m N side (numBands L m N) 0) → + y.mem b = x.mem b) + have hstart : I start := by + refine ⟨hp, ?_, ?_, ?_⟩ + · simp [start] + · simp [start] + · intro b _ _; rfl + have hbody (y : Data 96) (hy : I y) (hc : (Cond.lt band bands).Holds y) : + ∃ z, ExecWithin (w + 2) (bandBody side) y z (bandTime L + 1) ∧ I z ∧ + numBands L m N - z.regs band < numBands L m N - y.regs band := by + let β := y.regs band + have hβ : β < numBands L m N := by simpa only [Cond.holds_lt, hy.1.bands] using hc + have hlow := workspace_le_snapshot L m N + have hcat := catalogue_le_address L m N 0 side + have htable' (t : ℕ) (ht : t < 2 ^ L) : + y.mem (inverseBase L m N + t) = MaskTable.lookup L m t := by + rw [hy.2.2.2 _ (by unfold inverseBase; omega) (by + unfold catalogueBase tableBase at hcat + omega)] + exact htable t ht + have hmat' (r : ℕ) (hr : r < N) (k : ℕ) (hk : k < D m) : + y.mem (matrixAddress L m N side r k) = encode (matrix r k) := by + have hbound := matrixAddress_lt_inverse (L := L) side hr hk + rw [hy.2.2.2 _ (by unfold matrixAddress; omega) (by + unfold catalogueBase tableBase at hcat + have := Nat.zero_le (2 ^ L) + omega)] + exact hmat r hr k hk + obtain ⟨t, he, htp, htb, hvalues, hframe⟩ := exec_band hw side β y hy.1 hβ rfl + htable' matrix hmat' A hA ha hmag hfit + let z := t.set band (β + 1) + have hz : ExecWithin (B := .standard) (w + 2) (ram[ band ← band + one ]) t z 1 := by + ram_eval [z, htb, htp.fixed.one, (bands_fits hfit).of_le (Nat.succ_le_of_lt hβ), + band, one, Fin.mk.injEq] + have hzb : z.regs band = β + 1 := by simp [z] + refine ⟨z, he.seq hz, ?_, ?_⟩ + · refine ⟨parameters_reframe htp ?_, ?_, ?_, ?_⟩ + · intro i hi + have hp : ∀ i ∈ parameterRegisters, i ≠ band := by decide + simp only [z, Data.set_regs, Function.update_of_ne (hp i hi)] + · rw [hzb]; omega + · intro γ hγ p hp + rw [hzb] at hγ + change t.mem _ = _ + by_cases hγβ : γ = β + · subst γ; exact hvalues p hp + · have hγlt : γ < β := by omega + have hprior := catalogue_address_lt (m := m) (N := N) side hγlt hp + rw [hframe _ ?_ (by omega)] + · exact hy.2.2.1 γ hγlt p hp + · have haddr := catalogue_le_address L m N γ side + unfold catalogueAddress at haddr + have hsep : Encoding.scratchBase L + 10 ^ L ≤ catalogueBase L m N := by + unfold catalogueBase tableBase inverseBase + have := Nat.zero_le (2 ^ L) + omega + unfold catalogueAddress + omega + · intro b hb hc + change t.mem b = x.mem b + have hinside := catalogue_block_inside side hβ + rw [hframe b hb (by omega)] + exact hy.2.2.2 b hb hc + · rw [hzb]; change numBands L m N - (β + 1) < numBands L m N - β; omega + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I + (fun y => numBands L m N - y.regs band) hbody hstart + have hb : y.regs band = numBands L m N := by + simp only [Cond.holds_lt, hy.1.bands] at hc + have := hy.2.1 + omega + refine ⟨y, ?_, hy.1, ?_, hy.2.2.2⟩ + · apply (hs.seq he).mono + have hz : start.regs band = 0 := by simp [start] + simp only [hz, Nat.sub_zero, Nat.add_assoc, Nat.reduceAdd] + omega + · simpa only [hb] using hy.2.2.1 + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCatalogue.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCatalogue.lean new file mode 100644 index 000000000..163d03c14 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCatalogue.lean @@ -0,0 +1,70 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandFlat + +/-! +# Shared catalogue inputs for an existing mathematical tile + +## Main definitions + +* `BandPreparation.tileInputBases`: the two shared band pointers of a tile in `tiles`. + +## Main results + +* `BandPreparation.tile_input_values`: both pointed arrays contain the corresponding + unchanged public band encodings, in `Encoding.leafIndex` order. +* `BandPreparation.table_word_layout`: the computed forward table encodes the actual + deterministic `Layout` table, rather than caller-supplied indexing data. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- A tile `(rowBand,columnBand)` reads these two shared `10^L` arrays. The left pointer +depends only on its row band, and the right pointer only on its column band. -/ +def tileInputBases (L m N : ℕ) (T : ℕ × ℕ) : ℕ × ℕ := + (catalogueAddress L m N false T.1 0, catalogueAddress L m N true T.2 0) + +/-- The catalogue results returned by `runs` directly supply both inputs of every original +mathematical tile. Repeated row/column bands refer to the same stored arrays. -/ +theorem tile_input_values {L m N : ℕ} (hm : m ≤ L) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) (y : Data 96) + (hleft : ∀ β < numBands L m N, ∀ τ, + y.mem (catalogueAddress L m N false β (Encoding.leafIndex τ).val) = + encode (encodingL (bandArrayL (MaskTable.layout hm) X β) τ)) + (hright : ∀ β < numBands L m N, ∀ τ, + y.mem (catalogueAddress L m N true β (Encoding.leafIndex τ).val) = + encode (encodingR (bandArrayR (MaskTable.layout hm) Y β) τ)) + (T : ℕ × ℕ) (hT : T ∈ tiles L m N) (τ : Leaf L) : + y.mem ((tileInputBases L m N T).1 + (Encoding.leafIndex τ).val) = + encode (encodingL (bandArrayL (MaskTable.layout hm) X T.1) τ) ∧ + y.mem ((tileInputBases L m N T).2 + (Encoding.leafIndex τ).val) = + encode (encodingR (bandArrayR (MaskTable.layout hm) Y T.2) τ) := by + have ht : T.1 < numBands L m N ∧ T.2 < numBands L m N := by + simpa only [tiles, Finset.mem_product, Finset.mem_range] using hT + simpa only [tileInputBases, catalogueAddress, Nat.add_zero] using + And.intro (hleft T.1 ht.1 τ) (hright T.2 ht.2 τ) + +/-- The retained native forward words encode exactly the computed mathematical layout. -/ +theorem table_word_layout {L m N : ℕ} (hm : m ≤ L) (y : Data 96) + (hforward : ∀ gh : Fin (K0 L m) × Fin (K0 L m), + y.mem (tableBase L m N + (MaskTable.pairRank gh).val) = + (MaskTable.selectedMask gh).val) + (gh : Fin (K0 L m) × Fin (K0 L m)) : + y.mem (tableBase L m N + (MaskTable.pairRank gh).val) = + (MaskTable.maskCode ((MaskTable.layout hm).table gh)).val := by + rw [hforward] + exact congrArg Fin.val (MaskTable.maskCode_maskSet (MaskTable.selectedMask gh)).symm + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCollect.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCollect.lean new file mode 100644 index 000000000..ba061da3e --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCollect.lean @@ -0,0 +1,119 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandCopy +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandParameters + +/-! +# Charged copies into the shared band catalogue + +## Main results + +* `BandPreparation.catalogue_block_le`: each band has one disjoint catalogue block. +* `BandPreparation.exec_collectBand`: the real native encoder-output copy, including + every address calculation and the unchanged copy kernel's instruction count. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +-- Static register-frame proofs reduce the fixed finite code and register bank. +set_option maxRecDepth 4096 + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Every complete band block lies in the catalogue allocation. -/ +theorem catalogue_block_le {L m N β : ℕ} (side : Bool) (hβ : β < numBands L m N) : + catalogueAddress L m N side β 0 + 10 ^ L ≤ memoryEnd L m N := by + have ht : 0 < 10 ^ L := Nat.pow_pos (by decide) + unfold catalogueAddress memoryEnd + cases side <;> simp only [Bool.false_eq_true, ↓reduceIte, Nat.zero_add, Nat.add_zero] + all_goals nlinarith only [hβ, ht] + +/-- No band output is placed in the protected snapshot, tables or native workspace. -/ +theorem catalogue_le_address (L m N β : ℕ) (side : Bool) : + catalogueBase L m N ≤ catalogueAddress L m N side β 0 := by + unfold catalogueAddress + omega + +/-- Address arithmetic and the full copy are charged. The copy's source and destination are +disjoint by the concrete allocation; arbitrary words outside its destination are retained. -/ +theorem exec_collectBand {w L m N : ℕ} (side : Bool) (β : ℕ) (x : Data 96) + (hx : BandParameters L m N x) (hβ : β < numBands L m N) (hb : x.regs band = β) + (hfit : Fits w (wordBudget L m N)) : + ∃ y, ExecWithin w (collectBand side) x y (7 * 10 ^ L + 10) ∧ + y.mem = ArrayCopy.copyPrefix x.mem (Encoding.outputBase L) + (catalogueAddress L m N side β 0) (10 ^ L) ∧ + BandParameters L m N y ∧ y.regs band = β := by + let b := (if side then numBands L m N else 0) + β + let dest := catalogueAddress L m N side β 0 + have hdest : Fits w (dest + 10 ^ L) := (memory_fits hfit).of_le + (catalogue_block_le side hβ) + have hd0 : Fits w dest := hdest.of_le (Nat.le_add_right _ _) + have hp : Fits w (b * 10 ^ L) := hd0.of_le (by + dsimp only [dest, catalogueAddress, b] + omega) + have hbfit : Fits w b := hp.of_le (Nat.le_mul_of_pos_right _ + (Nat.pow_pos (by decide))) + have hs : Fits w (7 ^ L + 2) := (encoder_fits hfit).of_le (by + have h7 := Nat.pow_le_pow_left (by decide : 7 ≤ 10) L + unfold Encoding.addressBound + nlinarith only [h7, Nat.zero_le L, Nat.zero_le (10 ^ L)]) + have hsrc : Fits w (Encoding.outputBase L + 10 ^ L) := + ((memory_fits hfit).of_le (snapshot_le_memoryEnd L m N)).of_le (by + have h := workspace_le_snapshot L m N + unfold Encoding.scratchBase at h + have := Nat.zero_le (10 ^ L) + omega) + let start := ((((((x.set copySrc (7 ^ L + 2)).set temporary b).set temporary + (b * 10 ^ L)).set temporary dest).set copyDst dest).set copyLen (10 ^ L)) + dsimp only [dest, catalogueAddress] at hd0 + have hhead : ExecWithin w (collectHead side) x start 6 := by + cases side <;> + simp only [b, Bool.false_eq_true, ↓reduceIte, Nat.zero_add, Nat.add_zero] at hbfit hp hd0 <;> + ram_eval [collectHead, start, dest, b, catalogueAddress, hx.cells, hx.leaves, + hx.catalogue, hx.bands, hx.fixed.two, hb, hs, hd0, hp, hbfit, + copySrc, copyDst, copyLen, temporary, cells, two, leaves, catalogue, bands, + band, Fin.mk.injEq, Nat.zero_add, Nat.add_zero] + obtain ⟨y, he, hmem, _⟩ := exec_copyKernel start (small_fits hfit (by decide)) + (by simpa [start, copySrc, copyLen, temporary, Encoding.outputBase, Nat.add_comm] using hsrc) + (by simpa [start, copyDst, copyLen, temporary] using hdest) + (Or.inl (by + have hlow := workspace_le_snapshot L m N + have hcat : snapshotBase L m N ≤ catalogueBase L m N := by + unfold catalogueBase tableBase inverseBase + have := Nat.zero_le (2 ^ L) + omega + have hsep : Encoding.outputBase L + 10 ^ L ≤ dest := by + have hn := Nat.zero_le (10 ^ L) + unfold Encoding.scratchBase at hlow + exact (by omega : Encoding.outputBase L + 10 ^ L ≤ snapshotBase L m N).trans + (hcat.trans (catalogue_le_address L m N β side)) + simpa [start, copySrc, copyDst, copyLen, temporary, Encoding.outputBase, Nat.add_comm] + using hsep)) + have hwhole : ExecWithin w (collectBand side) x y (7 * 10 ^ L + 10) := by + apply (hhead.seq he).mono + have hn : start.regs copyLen = 10 ^ L := by simp [start] + rw [hn] + omega + refine ⟨y, hwhole, ?_, parameters_reframe hx ?_, ?_⟩ + · simpa [start, copySrc, copyDst, copyLen, temporary, Encoding.outputBase, Nat.add_comm] + using hmem + · intro i hi + apply hwhole.regs_eq + have hp : ∀ side i, i ∈ parameterRegisters → (collectBand side).Preserves i := by + intro side + cases side <;> simp +decide only [] + exact hp side i hi + · exact (hwhole.regs_eq (by + cases side <;> simp +decide only [])).trans hb + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCopy.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCopy.lean new file mode 100644 index 000000000..151981f38 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCopy.lean @@ -0,0 +1,134 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandMemory +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandHeader + +/-! +# Native copies used by the band-preparation caller + +## Main results + +* `BandPreparation.exec_copyKernel`: reuse the actual array-copy program in its fixed low + register bank, preserving every persistent register and permitting both address orders. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +open Cslib.WordRAM + +/-- The seven low copy registers are embedded injectively. -/ +theorem copyRegisters_injective : Function.Injective copyRegisters := by + intro i j h + exact Fin.ext (congrArg (fun i : Fin 96 => i.val) h) + +/-- The unchanged encoder occupies precisely its original low register bank. -/ +theorem encoderRegisters_injective : Function.Injective encoderRegisters := by + intro i j h + exact Fin.ext (congrArg (fun i : Fin 96 => i.val) h) + +/-- Copying uses only the original seven-register routine and its actual native cost. +The persistent high register bank is untouched; arbitrary outside memory is preserved +by the explicit `copyPrefix` result. -/ +theorem exec_copyKernel {w : ℕ} (x : Data 96) (h1 : Fits w 1) + (hs : Fits w (x.regs copySrc + x.regs copyLen)) + (hd : Fits w (x.regs copyDst + x.regs copyLen)) + (hsep : x.regs copySrc + x.regs copyLen ≤ x.regs copyDst ∨ + x.regs copyDst + x.regs copyLen ≤ x.regs copySrc ∨ x.regs copySrc = x.regs copyDst) : + ∃ y, ExecWithin w (ArrayCopy.program.rename copyRegisters) x y (7 * x.regs copyLen + 4) ∧ + y.mem = ArrayCopy.copyPrefix x.mem (x.regs copySrc) (x.regs copyDst) (x.regs copyLen) ∧ + (∀ i, 7 ≤ i.val → y.regs i = x.regs i) := by + obtain ⟨z, hz, hm, _, _, _⟩ := ArrayCopy.exec_of_separated_or_eq (x.comap copyRegisters) + h1 hs hd hsep + have he := hz.rename copyRegisters_injective + refine ⟨z.extend copyRegisters x, he, hm, ?_⟩ + intro i hi + apply he.regs_eq + have hf : ∀ i : Fin 96, 7 ≤ i.val → (ArrayCopy.program.rename copyRegisters).Preserves i := by + simp only [ArrayCopy.program, ArrayCopy.body, Code.rename, Code.Preserves, + Op.rename, Op.bin, Op.dest, copyRegisters] + decide + exact hf i hi + +private theorem lowSetup_regs (x : Data 96) (s d n : ℕ) (i : Fin 96) (hi : 7 ≤ i.val) : + (((x.set copySrc s).set copyDst d).set copyLen n).regs i = x.regs i := by + have hs : i ≠ copySrc := by intro h; subst i; norm_num [copySrc] at hi + have hd : i ≠ copyDst := by intro h; subst i; norm_num [copyDst] at hi + have hn : i ≠ copyLen := by intro h; subst i; norm_num [copyLen] at hi + simp only [Data.set_regs, Function.update_of_ne hs, Function.update_of_ne hd, + Function.update_of_ne hn] + +/-- Protect the entire original initialized prefix, including header zero, with the actual +native copy. Source words and all memory outside the snapshot block remain untouched. -/ +theorem exec_saveInput {w L m N : ℕ} (x : Data 96) (hx : HeaderPost L m N x) + (hfit : Fits w (wordBudget L m N)) : + ∃ y, ExecWithin w saveInput x y (7 * inputEnd m N + 7) ∧ + y.mem = ArrayCopy.copyPrefix x.mem 0 (snapshotBase L m N) (inputEnd m N) ∧ + (∀ i, 7 ≤ i.val → y.regs i = x.regs i) := by + let start := ((x.set copySrc 0).set copyDst (snapshotBase L m N)).set copyLen (inputEnd m N) + have hb : Fits w (inputEnd m N) := (memory_fits hfit).of_le (input_le_memoryEnd L m N) + have hi : Fits w (inverseBase L m N) := + (memory_fits hfit).of_le (inverse_le_memoryEnd L m N) + obtain ⟨y, hy, hm, hf⟩ := exec_copyKernel start (small_fits hfit (by decide)) + (by simpa [start, copySrc, copyDst, copyLen] using hb) + (by simpa [start, copySrc, copyDst, copyLen, inverseBase] using hi) + (Or.inl (by simpa [start, copySrc, copyDst, copyLen] using input_le_snapshot L m N)) + have h0 : ExecWithin (B := .standard) w (.op (.const copySrc 0)) + x (x.set copySrc 0) 1 := ExecWithin.const (fits_zero w) + have h1 : ExecWithin (B := .standard) w (.op (.copy copyDst snapshot)) + (x.set copySrc 0) ((x.set copySrc 0).set copyDst (snapshotBase L m N)) 1 := by + ram_eval [hx.snapshot, copySrc, copyDst, snapshot, Fin.mk.injEq] + have h2 : ExecWithin (B := .standard) w (.op (.copy copyLen prefixSize)) + ((x.set copySrc 0).set copyDst (snapshotBase L m N)) start 1 := by + ram_eval [start, hx.prefixSize, copySrc, copyDst, copyLen, prefixSize, Fin.mk.injEq] + refine ⟨y, ?_, ?_, ?_⟩ + · apply (h0.seq (h1.seq (h2.seq hy))).mono + have hn : start.regs copyLen = inputEnd m N := by simp [start] + rw [hn] + omega + · simpa [start, copySrc, copyDst, copyLen] using hm + · intro i hi + exact (hf i hi).trans (lowSetup_regs x _ _ _ i hi) + +/-- Restore the complete original input using the reverse disjoint-copy case. This copies +the actual protected words rather than reconstructing or assuming the input is unchanged. -/ +theorem exec_restoreInput {w L m N : ℕ} (x : Data 96) (hx : HeaderPost L m N x) + (hfit : Fits w (wordBudget L m N)) : + ∃ y, ExecWithin w restoreInput x y (7 * inputEnd m N + 7) ∧ + y.mem = ArrayCopy.copyPrefix x.mem (snapshotBase L m N) 0 (inputEnd m N) ∧ + (∀ i, 7 ≤ i.val → y.regs i = x.regs i) := by + let start := ((x.set copySrc (snapshotBase L m N)).set copyDst 0).set copyLen (inputEnd m N) + have hb : Fits w (inputEnd m N) := (memory_fits hfit).of_le (input_le_memoryEnd L m N) + have hi : Fits w (inverseBase L m N) := + (memory_fits hfit).of_le (inverse_le_memoryEnd L m N) + obtain ⟨y, hy, hm, hf⟩ := exec_copyKernel start (small_fits hfit (by decide)) + (by simpa [start, copySrc, copyDst, copyLen, inverseBase] using hi) + (by simpa [start, copySrc, copyDst, copyLen] using hb) + (Or.inr (Or.inl (by + simpa [start, copySrc, copyDst, copyLen] using input_le_snapshot L m N))) + have h0 : ExecWithin (B := .standard) w (.op (.copy copySrc snapshot)) + x (x.set copySrc (snapshotBase L m N)) 1 := by ram_eval [hx.snapshot] + have h1 : ExecWithin (B := .standard) w (.op (.const copyDst 0)) + (x.set copySrc (snapshotBase L m N)) + ((x.set copySrc (snapshotBase L m N)).set copyDst 0) 1 := ExecWithin.const (fits_zero w) + have h2 : ExecWithin (B := .standard) w (.op (.copy copyLen prefixSize)) + ((x.set copySrc (snapshotBase L m N)).set copyDst 0) start 1 := by + ram_eval [start, hx.prefixSize, copySrc, copyDst, copyLen, prefixSize, Fin.mk.injEq] + refine ⟨y, ?_, ?_, ?_⟩ + · apply (h0.seq (h1.seq (h2.seq hy))).mono + have hn : start.regs copyLen = inputEnd m N := by simp [start] + rw [hn] + omega + · simpa [start, copySrc, copyDst, copyLen] using hm + · intro i hi + exact (hf i hi).trans (lowSetup_regs x _ _ _ i hi) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCost.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCost.lean new file mode 100644 index 000000000..f8c74f3fa --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCost.lean @@ -0,0 +1,70 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandProgram +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.BandBounds + +/-! +# Numerical cost bounds for shared band preparation + +## Main definitions + +* `BandPreparation.timeBound`: a uniform budget for all setup, tables, input copying, + band preparation, shared encoder calls and catalogue copies. + +## Main results + +* `BandPreparation.thin_timeBound`: its explicit finite thin-regime bound. This numerical + lemma is separate from the theorem connecting the budget to the actual native execution. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Generic numerical instruction budget; a final halt is charged separately. -/ +def timeBound (L m N : ℕ) : ℕ := + 4096 * (L + 1) * (1 + N * D m + 2 ^ L + 2 * numBands L m N * 10 ^ L) + +/-- At `L=19m`, all original input and table overhead is absorbed by the saving for shared +encodings. The displayed constants and polynomial exponent are fixed and positive. -/ +theorem thin_timeBound (m N : ℕ) (hN : D m ^ 18 ≤ N) : + (timeBound (19 * m) m N : ℝ) ≤ + 40960 * ((19 * m + 1 : ℕ) : ℝ) * (D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2 := by + have hNpos : 0 < N := (Nat.pow_pos (show 0 < D m from Nat.pow_pos (by decide))).trans_le hN + have hs := preparation_shape_le (show m ≤ 19 * m by omega) hNpos + have hsR : ((1 + N * D m + 2 ^ (19 * m) + + 2 * numBands (19 * m) m N * 10 ^ (19 * m) : ℕ) : ℝ) ≤ + 5 * ((numBands (19 * m) m N * 10 ^ (19 * m) : ℕ) : ℝ) := by exact_mod_cast hs + have he := encodings_total_le m N hN + have hshape : ((1 + N * D m + 2 ^ (19 * m) + + 2 * numBands (19 * m) m N * 10 ^ (19 * m) : ℕ) : ℝ) ≤ + 10 * (D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2 := by + push_cast at hsR he ⊢ + nlinarith only [hsR, he] + calc (timeBound (19 * m) m N : ℝ) + = 4096 * ((19 * m + 1 : ℕ) : ℝ) * ((1 + N * D m + 2 ^ (19 * m) + + 2 * numBands (19 * m) m N * 10 ^ (19 * m) : ℕ) : ℝ) := by + simp only [timeBound, Nat.cast_mul, Nat.cast_ofNat] + _ ≤ 4096 * ((19 * m + 1 : ℕ) : ℝ) * + (10 * (D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2) := + mul_le_mul_of_nonneg_left hshape (by positivity) + _ = _ := by ring + +/-- Equivalent division form of the thin-regime bound requested by the matrix algorithm. -/ +theorem thin_timeBound_div (m N : ℕ) (hN : D m ^ 18 ≤ N) : + (timeBound (19 * m) m N : ℝ) ≤ + 40960 * ((19 * m + 1 : ℕ) : ℝ) * (N : ℝ) ^ 2 / (D m : ℝ) ^ (1 / 18 : ℝ) := by + have h := thin_timeBound m N hN + rw [Real.rpow_neg (by positivity)] at h + simpa only [div_eq_mul_inv, mul_assoc, mul_left_comm, mul_comm] using h + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCount.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCount.lean new file mode 100644 index 000000000..6fb297b6e --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandCount.lean @@ -0,0 +1,112 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPopulation + +/-! +# Native counting of all valid subset masks + +## Main results + +* `BandPreparation.exec_countMasks`: the complete binary-mask scan computes the binomial + coefficient, with its inner population loops and all outer controls charged. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open MaskTable + +/-- Scanning all masks computes `K L m`; neither that count nor a valid table is supplied +as an input. Every mask-counter and accepted-counter fit follows from the scan range. -/ +theorem exec_countMasks {w : ℕ} (L m : ℕ) (x : Data 96) + (hL : x.regs levels = L) (hm : x.regs inner = m) (hM : x.regs masks = 2 ^ L) + (h1 : x.regs one = 1) (h2 : x.regs two = 2) (hfit : Fits w (2 ^ L + L + 4)) : + ∃ y, ExecWithin w countMasks x y ((6 * L + 11) * 2 ^ L + 4) ∧ + y.regs count = K L m ∧ y.regs mask = 2 ^ L ∧ y.mem = x.mem ∧ + (∀ i, i ≠ mask → i ≠ count → i ≠ residue → i ≠ population → i ≠ counter → + i ≠ digit → y.regs i = x.regs i) := by + have h0 : Fits w 0 := hfit.of_le (by omega) + let start := (x.set mask 0).set count 0 + have hs : ExecWithin (B := .standard) w (ram[ mask ← #0; count ← #0 ]) x start 2 := by + ram_eval [start, h0, mask, count, Fin.mk.injEq] + let I (y : Data 96) : Prop := + y.mem = x.mem ∧ y.regs levels = L ∧ y.regs inner = m ∧ y.regs masks = 2 ^ L ∧ + y.regs one = 1 ∧ y.regs two = 2 ∧ y.regs mask ≤ 2 ^ L ∧ + y.regs count = prefixCount L m (y.regs mask) + have hi : I start := by + simp [I, start, hL, hm, hM, h1, h2, levels, inner, masks, one, two, mask, count, + prefixCount_zero] + have hb (y : Data 96) (hy : I y) (hc : (Cond.lt mask masks).Holds y) : + ∃ z, ExecWithin w countBody y z (6 * L + 9) ∧ I z ∧ + 2 ^ L - z.regs mask < 2 ^ L - y.regs mask := by + obtain ⟨hmem, hL, hm, hM, h1, h2, ht, hcount⟩ := hy + have htlt : y.regs mask < 2 ^ L := by simpa only [Cond.holds_lt, hM] using hc + obtain ⟨u, hu, hpop, humem, huf⟩ := exec_population L (y.regs mask) y hL rfl h1 h2 + (hfit.of_le (by omega)) + have hul : u.regs levels = L := + (huf levels (by decide) (by decide) (by decide) (by decide)).trans hL + have hui : u.regs inner = m := + (huf inner (by decide) (by decide) (by decide) (by decide)).trans hm + have huM : u.regs masks = 2 ^ L := + (huf masks (by decide) (by decide) (by decide) (by decide)).trans hM + have hu1 : u.regs one = 1 := + (huf one (by decide) (by decide) (by decide) (by decide)).trans h1 + have hu2 : u.regs two = 2 := + (huf two (by decide) (by decide) (by decide) (by decide)).trans h2 + have hut : u.regs mask = y.regs mask := + huf mask (by decide) (by decide) (by decide) (by decide) + have huc : u.regs count = prefixCount L m (y.regs mask) := + (huf count (by decide) (by decide) (by decide) (by decide)).trans hcount + have hfnext : Fits w (y.regs mask + 1) := hfit.of_le (by omega) + have hfc : Fits w (prefixCount L m (y.regs mask) + 1) := + hfit.of_le (by have := prefixCount_le L m (y.regs mask); omega) + let z := (u.set count (prefixCount L m (y.regs mask + 1))).set mask (y.regs mask + 1) + have hz : ExecWithin w (ram[ + if population = inner then [ count ← count + one ] else [ count ← count ]; + mask ← mask + one ]) u z 4 := by + by_cases h : MaskTable.population L (y.regs mask) = m <;> + ram_eval [z, prefixCount_succ, h, hpop, hui, hu1, hut, huc, hfnext, hfc, + population, inner, count, one, mask, Fin.mk.injEq] + refine ⟨z, hu.seq hz, ?_, ?_⟩ + · simpa +decide [I, z, levels, inner, masks, one, two, mask, count, + Function.update_apply] using + (show u.mem = x.mem ∧ u.regs levels = L ∧ u.regs inner = m ∧ + u.regs masks = 2 ^ L ∧ u.regs one = 1 ∧ u.regs two = 2 ∧ + y.regs mask + 1 ≤ 2 ^ L from + ⟨humem.trans hmem, hul, hui, huM, hu1, hu2, by omega⟩) + · have hzt : z.regs mask = y.regs mask + 1 := by simp [z] + rw [hzt] + omega + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I + (fun y => 2 ^ L - y.regs mask) hb hi + have ht : y.regs mask = 2 ^ L := by + simp only [Cond.holds_lt, hy.2.2.2.1] at hc + have := hy.2.2.2.2.2.2.1 + omega + have hwhole : ExecWithin w countMasks x y ((6 * L + 11) * 2 ^ L + 4) := by + apply (hs.seq he).mono + have hs0 : start.regs mask = 0 := by simp [start, mask, count] + rw [hs0, Nat.sub_zero] + nlinarith + refine ⟨y, hwhole, ?_, ht, hy.1, ?_⟩ + · have h := hy.2.2.2.2.2.2.2 + simpa only [ht, prefixCount_full] using h + · intro i hm hc hr hp hj hd + apply hwhole.regs_eq + have hf : ∀ i : Fin 96, i ≠ mask → i ≠ count → i ≠ residue → i ≠ population → + i ≠ counter → i ≠ digit → countMasks.Preserves i := by + simp only [countMasks, countBody, populationCode, Code.Preserves, Op.dest, Op.bin] + decide + exact hf i hm hc hr hp hj hd + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandEncode.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandEncode.lean new file mode 100644 index 000000000..a3831dde5 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandEncode.lean @@ -0,0 +1,137 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPrepare +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandCollect + +/-! +# Actual native encoder calls on the prepared bands + +## Main definitions + +* `BandPreparation.catalogueValue`: flat shared-transform values, using the existing + coordinate transform and the computed, padded band array. + +## Main results + +* `BandPreparation.exec_band`: one complete native preparation, unchanged encoder call and + catalogue copy, with charged instructions and an explicit memory frame. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +-- The persistent-register checks reduce the fixed 96-register parameter bank. +set_option maxRecDepth 4096 + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The shared catalogue uses exactly the existing flat coordinate-transform values. +`Encoding.value_final_left/right` and `bandValue_left/right` identify these with the public +`encodingL/R` of the original mathematical `bandArrayL/R`. -/ +def catalogueValue (L m N : ℕ) (side : Bool) (matrix : ℕ → ℕ → ℤ) (β p : ℕ) : ℤ := + CoordinateTransform.value (CoordinateTransform.table (Encoding.coefficients side)) + L 0 (bandValue L m N β side matrix) p 0 + +/-- The guarded band only reads real bounded matrix entries; all other entries are zero. -/ +theorem abs_bandValue_le {L m N β : ℕ} (side : Bool) (matrix : ℕ → ℕ → ℤ) + (A : ℤ) (hA : 0 ≤ A) (hmat : ∀ r < N, ∀ k < D m, |matrix r k| ≤ A) + (a : Fin (7 ^ L)) : |bandValue L m N β side matrix a.val| ≤ A := by + cases side <;> unfold bandValue <;> simp only [Bool.false_eq_true, ↓reduceIte] + all_goals + split_ifs with hq hr + · simpa only [abs_zero] using hA + · exact hmat _ hr _ (selected_parts_bounds a hq).2 + · simpa only [abs_zero] using hA + +/-- A real call of the unchanged encoder in its low 32-register bank. Parameters in the +other registers, arbitrary input headers and arbitrary outside memory are preserved. -/ +theorem exec_encoderKernel {w L m N : ℕ} (hw : 0 < w) (side : Bool) + (arr : ℕ → ℤ) (x : Data 96) (hheader : x.mem 1 = L) + (hinput : ∀ i < 7 ^ L, x.mem (2 + i) = encode (arr i)) + (A : ℤ) (hA : 0 ≤ A) (ha : ∀ i < 7 ^ L, |arr i| ≤ A) + (hmag : (7 ^ L : ℕ) * A < (2 : ℤ) ^ (w - 1)) + (hfit : Fits (w + 2) (wordBudget L m N)) : + ∃ y, ExecWithin (w + 2) ((Encoding.program side).rename encoderRegisters) x y + (Encoding.timeBound L) ∧ + (∀ p < 10 ^ L, y.mem (Encoding.outputBase L + p) = encode + (CoordinateTransform.value (CoordinateTransform.table (Encoding.coefficients side)) + L 0 arr p 0)) ∧ + (∀ b, ¬(Encoding.outputBase L ≤ b ∧ b < Encoding.scratchBase L + 10 ^ L) → + y.mem b = x.mem b) ∧ (∀ i, 32 ≤ i.val → y.regs i = x.regs i) := by + obtain ⟨z, hz, hv, hf, _, _⟩ := Encoding.exec hw side arr (x.comap encoderRegisters) + hheader hinput A hA ha hmag (encoder_fits hfit) + refine ⟨z.extend encoderRegisters x, hz.rename encoderRegisters_injective, hv, hf, ?_⟩ + intro i hi + change Function.extend encoderRegisters z.regs x.regs i = x.regs i + apply Function.extend_apply' + rintro ⟨j, hj⟩ + have hv := congrArg (fun k : Fin 96 => k.val) hj + change j.val = i.val at hv + omega + +/-- Native cost of preparing, encoding and collecting one shared band. -/ +def bandTime (L : ℕ) : ℕ := + (13 * L + 39) * 7 ^ L + 5 + Encoding.timeBound L + (7 * 10 ^ L + 10) + +/-- Every band invokes the actual encoder code and copies its complete output. Only the low +workspace and this single shared catalogue block can change. Original matrix/table data and +arbitrary caller query regions outside those blocks remain untouched. -/ +theorem exec_band {w L m N : ℕ} (hw : 0 < w) (side : Bool) (β : ℕ) (x : Data 96) + (hx : BandParameters L m N x) (hβ : β < numBands L m N) (hb : x.regs band = β) + (htable : ∀ t < 2 ^ L, x.mem (inverseBase L m N + t) = MaskTable.lookup L m t) + (matrix : ℕ → ℕ → ℤ) + (hmat : ∀ r < N, ∀ k < D m, + x.mem (matrixAddress L m N side r k) = encode (matrix r k)) + (A : ℤ) (hA : 0 ≤ A) (ha : ∀ r < N, ∀ k < D m, |matrix r k| ≤ A) + (hmag : (7 ^ L : ℕ) * A < (2 : ℤ) ^ (w - 1)) + (hfit : Fits (w + 2) (wordBudget L m N)) : + ∃ y, ExecWithin (w + 2) + (.seq (prepareBand side) (.seq ((Encoding.program side).rename encoderRegisters) + (collectBand side))) x y (bandTime L) ∧ + BandParameters L m N y ∧ y.regs band = β ∧ + (∀ p < 10 ^ L, y.mem (catalogueAddress L m N side β p) = + encode (catalogueValue L m N side matrix β p)) ∧ + (∀ b, ¬(1 ≤ b ∧ b < Encoding.scratchBase L + 10 ^ L) → + ¬(catalogueAddress L m N side β 0 ≤ b ∧ + b < catalogueAddress L m N side β 0 + 10 ^ L) → y.mem b = x.mem b) := by + obtain ⟨p, hep, hpp, hpb, hheader, hinput, hpf⟩ := + exec_prepareBand side β x hx hβ hb htable matrix hmat hfit + obtain ⟨e, hee, hev, hef, her⟩ := exec_encoderKernel hw side + (bandValue L m N β side matrix) p hheader hinput A hA + (fun i hi => abs_bandValue_le side matrix A hA ha ⟨i, hi⟩) hmag hfit + have hep' : BandParameters L m N e := parameters_reframe hpp (by + intro i hi + apply her + have hp : ∀ i ∈ parameterRegisters, 32 ≤ i.val := by decide + exact hp i hi) + have heb : e.regs band = β := (her band (by decide)).trans hpb + obtain ⟨y, hec, hmem, hyp, hyb⟩ := exec_collectBand side β e hep' hβ heb hfit + refine ⟨y, ?_, hyp, hyb, ?_, ?_⟩ + · simpa only [bandTime, Nat.add_assoc] using hep.seq (hee.seq hec) + · intro p hp + have heq : catalogueAddress L m N side β p = + catalogueAddress L m N side β 0 + p := by simp [catalogueAddress] + rw [heq, hmem, ArrayCopy.copyPrefix_read hp] + exact hev p hp + · intro b hb hc + rw [hmem, ArrayCopy.copyPrefix_outside hc] + calc + e.mem b = p.mem b := hef b (by + unfold Encoding.outputBase + have := Nat.zero_le (7 ^ L) + omega) + _ = x.mem b := hpf b (by + unfold Encoding.scratchBase Encoding.outputBase at hb + have := Nat.zero_le (10 ^ L) + omega) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandExec.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandExec.lean new file mode 100644 index 000000000..c299842d7 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandExec.lean @@ -0,0 +1,245 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandAll +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandTables +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandCost + +/-! +# Complete native shared-band execution + +## Main results + +* `BandPreparation.exec`: compute both tables and every shared encoding, from real scalar + headers and matrix words, restoring the full original input prefix and every outside cell. + All setup, copies, loops and encoder calls contribute to the instruction bound. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +-- Static frame checks reduce the fixed parameter bank and finite native table code. +set_option maxRecDepth 4096 + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The two tables and the protected input all precede the output catalogue. -/ +theorem snapshot_le_catalogue (L m N : ℕ) : snapshotBase L m N ≤ catalogueBase L m N := by + unfold catalogueBase tableBase inverseBase + have := Nat.zero_le (2 ^ L) + omega + +/-- The one-side catalogue end is within the complete allocation. -/ +theorem catalogue_side_end_le (L m N : ℕ) (side : Bool) : + catalogueAddress L m N side (numBands L m N) 0 ≤ memoryEnd L m N := by + unfold catalogueAddress memoryEnd + cases side <;> simp only [Bool.false_eq_true, ↓reduceIte, Nat.zero_add, Nat.add_zero] + all_goals nlinarith only [Nat.zero_le (numBands L m N * 10 ^ L)] + +private theorem band_frame_protected {L m N : ℕ} {side : Bool} {μ ν : ℕ → ℕ} + (h : ∀ b, ¬(1 ≤ b ∧ b < Encoding.scratchBase L + 10 ^ L) → + ¬(catalogueAddress L m N side 0 0 ≤ b ∧ + b < catalogueAddress L m N side (numBands L m N) 0) → ν b = μ b) + (a : ℕ) (ha : snapshotBase L m N ≤ a ∧ a < catalogueBase L m N) : ν a = μ a := by + apply h + · have := workspace_le_snapshot L m N + omega + · have := catalogue_le_address L m N 0 side + omega + +private theorem band_frame_above {L m N : ℕ} {side : Bool} {μ ν : ℕ → ℕ} + (h : ∀ b, ¬(1 ≤ b ∧ b < Encoding.scratchBase L + 10 ^ L) → + ¬(catalogueAddress L m N side 0 0 ≤ b ∧ + b < catalogueAddress L m N side (numBands L m N) 0) → ν b = μ b) + (a : ℕ) (ha : memoryEnd L m N ≤ a) : ν a = μ a := by + apply h + · have := workspace_le_snapshot L m N + have := (snapshot_le_catalogue L m N).trans (catalogue_le_memoryEnd L m N) + omega + · have := catalogue_side_end_le L m N side + omega + +/-- A coarse uniform bound for one actual shared-band call and its outer loop control. -/ +theorem bandTime_le (L : ℕ) : bandTime L + 3 ≤ 512 * (L + 1) * 10 ^ L := by + have h7 : 7 ^ L ≤ 10 ^ L := Nat.pow_le_pow_left (by decide) _ + have ht : 1 ≤ 10 ^ L := Nat.one_le_pow L 10 (by decide) + have hs := Nat.mul_le_mul_left (13 * L + 39) h7 + unfold bandTime Encoding.timeBound + nlinarith only [hs, ht, Nat.zero_le L, Nat.zero_le (L * 10 ^ L)] + +/-- The sum of the previously proved native component charges fits the generic bound. -/ +theorem execution_time_le (L m N : ℕ) : + (16 * L + 49) + ((7 * inputEnd m N + 7) + + (((6 * L + 16) * 2 ^ L + 14) + (((6 * L + 17) * 2 ^ L + 4) + + ((numBands L m N * (bandTime L + 3) + 3) + + ((numBands L m N * (bandTime L + 3) + 3) + (7 * inputEnd m N + 7)))))) ≤ + timeBound L m N := by + have hb := Nat.mul_le_mul_left (numBands L m N) (bandTime_le L) + have h2 : 1 ≤ 2 ^ L := Nat.one_le_pow L 2 (by decide) + unfold timeBound inputEnd + nlinarith only [hb, h2, Nat.zero_le L, Nat.zero_le (N * D m), + Nat.zero_le (L * (N * D m)), Nat.zero_le (L * 2 ^ L), + Nat.zero_le (numBands L m N * 10 ^ L), + Nat.zero_le (L * (numBands L m N * 10 ^ L))] + +/-- The full fixed native program. The caller supplies only ordinary `L,m,N` headers and +signed matrix words; both tables and every padded/encoded band are computed. Header zero +and arbitrary outside memory need not be zero or describe a standalone input list. -/ +theorem exec {w L m N : ℕ} (hw : 0 < w) (hm : m ≤ L) (x : Data 96) + (hL : x.mem 1 = L) (hm' : x.mem 2 = m) (hN : x.mem 3 = N) + (matrix : Bool → ℕ → ℕ → ℤ) + (hmat : ∀ side r, r < N → ∀ k, k < D m → + x.mem (matrixOffset m N side r k + 4) = encode (matrix side r k)) + (A : ℤ) (hA : 0 ≤ A) (ha : ∀ side r, r < N → ∀ k, k < D m → |matrix side r k| ≤ A) + (hmag : (7 ^ L : ℕ) * A < (2 : ℤ) ^ (w - 1)) + (hfit : Fits (w + 2) (wordBudget L m N)) : + ∃ y, ExecWithin (w + 2) program x y (timeBound L m N) ∧ + BandParameters L m N y ∧ + (∀ side β, β < numBands L m N → ∀ p, p < 10 ^ L → + y.mem (catalogueAddress L m N side β p) = + encode (catalogueValue L m N side (matrix side) β p)) ∧ + (∀ a < 2 ^ L, y.mem (inverseBase L m N + a) = MaskTable.lookup L m a) ∧ + (∀ gh : Fin (K0 L m) × Fin (K0 L m), + y.mem (tableBase L m N + (MaskTable.pairRank gh).val) = + (MaskTable.selectedMask gh).val) ∧ + (∀ b, ¬(inputEnd m N ≤ b ∧ b < memoryEnd L m N) → y.mem b = x.mem b) := by + obtain ⟨h, heh, hhp, hhmem⟩ := exec_header hm x hL hm' hN hfit + obtain ⟨s, hes, hsmem, hsr⟩ := exec_saveInput h hhp hfit + have hsp : HeaderPost L m N s := headerPost_reframe hhp (by + intro i hi + have hp : ∀ i ∈ headerRegisters, 7 ≤ i.val := by decide + exact hsr i (hp i hi)) + obtain ⟨c, hec, hcp, hcmem⟩ := exec_countCode s hsp hfit + have htableParams : TableParameters L m (inverseBase L m N) (tableBase L m N) c := + ⟨hcp.fixed.levels, hcp.fixed.inner, hcp.masks, hcp.inverse, hcp.table, hcp.square, + hcp.fixed.zero, hcp.fixed.one, hcp.fixed.two⟩ + obtain ⟨t, het, _, hlookup, hforward, htframe⟩ := exec_tables L m + (inverseBase L m N) (tableBase L m N) c htableParams (Nat.le_refl _) + (tables_fit hfit) + have htp : BandParameters L m N t := parameters_reframe hcp (by + intro i hi + apply het.regs_eq + have hp : ∀ i ∈ parameterRegisters, tablesCode.Preserves i := by + simp +decide only [] + exact hp i hi) + have hinv : inverseBase L m N ≤ catalogueBase L m N := by + unfold catalogueBase tableBase + have := Nat.zero_le (2 ^ L) + omega + have hsnapshot (a : ℕ) (ha : a < inputEnd m N) : + t.mem (snapshotBase L m N + a) = x.mem a := by + rw [htframe _ (by unfold inverseBase; omega) (by unfold tableBase inverseBase; omega), + hcmem, hsmem, ArrayCopy.copyPrefix_read ha, hhmem, Nat.zero_add] + have hmatrix (side : Bool) (r : ℕ) (hr : r < N) (k : ℕ) (hk : k < D m) : + t.mem (matrixAddress L m N side r k) = encode (matrix side r k) := by + have hb := matrixAddress_lt_inverse (L := L) side hr hk + unfold matrixAddress inverseBase at hb + have ho : matrixOffset m N side r k + 4 < inputEnd m N := by omega + rw [matrixAddress, hsnapshot _ ho] + exact hmat side r hr k hk + obtain ⟨l, hel, hlp, hlvalues, hlframe⟩ := exec_encodeBands hw false t htp + hlookup (matrix false) (hmatrix false) A hA (ha false) hmag hfit + have hlprotected := band_frame_protected hlframe + have hinverse (a : ℕ) (ha : a < 2 ^ L) : + snapshotBase L m N ≤ inverseBase L m N + a ∧ + inverseBase L m N + a < catalogueBase L m N := by + have hs : snapshotBase L m N ≤ inverseBase L m N := Nat.le_add_right _ _ + have ht : inverseBase L m N + a < tableBase L m N := by + change inverseBase L m N + a < inverseBase L m N + 2 ^ L + omega + exact ⟨hs.trans (Nat.le_add_right _ _), ht.trans_le (Nat.le_add_right _ _)⟩ + have hlookup' (a : ℕ) (ha : a < 2 ^ L) : + l.mem (inverseBase L m N + a) = MaskTable.lookup L m a := by + rw [hlprotected _ (hinverse a ha)] + exact hlookup a ha + have hmatrix' (r : ℕ) (hr : r < N) (k : ℕ) (hk : k < D m) : + l.mem (matrixAddress L m N true r k) = encode (matrix true r k) := by + have hb := matrixAddress_lt_inverse (L := L) true hr hk + rw [hlprotected (matrixAddress L m N true r k) ⟨Nat.le_add_right _ _, hb.trans_le hinv⟩] + exact hmatrix true r hr k hk + obtain ⟨r, her, hrp, hrvalues, hrframe⟩ := exec_encodeBands hw true l hlp + hlookup' (matrix true) hmatrix' A hA (ha true) hmag hfit + have hrprotected := band_frame_protected hrframe + obtain ⟨y, hey, hymem, hyr⟩ := exec_restoreInput r hrp.toHeaderPost hfit + have hyp : BandParameters L m N y := parameters_reframe hrp (by + intro i hi + have hp : ∀ i ∈ parameterRegisters, 7 ≤ i.val := by decide + exact hyr i (hp i hi)) + have hrestore (a : ℕ) (ha : inputEnd m N ≤ a) : y.mem a = r.mem a := by + rw [hymem, ArrayCopy.copyPrefix_outside (by omega)] + have hprotected (a : ℕ) (ha : snapshotBase L m N ≤ a ∧ a < catalogueBase L m N) : + r.mem a = t.mem a := (hrprotected a ha).trans (hlprotected a ha) + have hcatlow : Encoding.scratchBase L + 10 ^ L ≤ catalogueBase L m N := + (workspace_le_snapshot L m N).trans (snapshot_le_catalogue L m N) + have hinputcat : inputEnd m N ≤ catalogueBase L m N := + (input_le_snapshot L m N).trans (snapshot_le_catalogue L m N) + refine ⟨y, ?_, hyp, ?_, ?_, ?_, ?_⟩ + · apply (heh.seq (hes.seq (hec.seq (het.seq (hel.seq (her.seq hey)))))).mono + exact execution_time_le L m N + · intro side β hβ p hp + have hlow : inputEnd m N ≤ catalogueAddress L m N side β p := by + have := catalogue_le_address L m N β side + unfold catalogueAddress at * + omega + rw [hrestore _ hlow] + cases side + · rw [hrframe _ ?_ ?_] + · exact hlvalues β hβ p hp + · unfold catalogueAddress + simp only [Bool.false_eq_true, ↓reduceIte] + omega + · have hlt : catalogueAddress L m N false β p < + catalogueAddress L m N true 0 0 := by + unfold catalogueAddress + simp only [Bool.false_eq_true, ↓reduceIte, Nat.zero_add, Nat.add_zero] + have ht : 0 < 10 ^ L := Nat.pow_pos (by decide) + nlinarith only [hβ, hp, ht] + omega + · exact hrvalues β hβ p hp + · intro a ha + have hb := hinverse a ha + rw [hrestore _ ((input_le_snapshot L m N).trans hb.1), hprotected _ hb] + exact hlookup a ha + · intro gh + have hK : K L m ≤ 2 ^ L := Nat.choose_le_two_pow _ _ + have hi := (MaskTable.pairRank gh).isLt + have hb : snapshotBase L m N ≤ tableBase L m N + (MaskTable.pairRank gh).val ∧ + tableBase L m N + (MaskTable.pairRank gh).val < catalogueBase L m N := by + unfold catalogueBase tableBase inverseBase + omega + rw [hrestore _ ((input_le_snapshot L m N).trans hb.1), hprotected _ hb] + exact hforward gh + · intro b hb + by_cases hbin : b < inputEnd m N + · have hread : y.mem b = r.mem (snapshotBase L m N + b) := by + rw [hymem] + simpa only [Nat.zero_add] using (ArrayCopy.copyPrefix_read + (mem := r.mem) (src := snapshotBase L m N) (dst := 0) hbin) + rw [hread, hprotected _ (by + unfold catalogueBase tableBase inverseBase + have := Nat.zero_le (2 ^ L) + omega)] + exact hsnapshot b hbin + · have hbig : memoryEnd L m N ≤ b := by omega + rw [hrestore b (by omega), band_frame_above hrframe b hbig, + band_frame_above hlframe b hbig, htframe b ?_ ?_, hcmem, hsmem, + ArrayCopy.copyPrefix_outside ?_, hhmem] + · have hend := inverse_le_memoryEnd L m N + change snapshotBase L m N + inputEnd m N ≤ memoryEnd L m N at hend + omega + · have hend := table_le_memoryEnd L m N + change inverseBase L m N + 2 ^ L ≤ memoryEnd L m N at hend + omega + · have hend := catalogue_le_memoryEnd L m N + change tableBase L m N + 2 ^ L ≤ memoryEnd L m N at hend + omega + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandFlat.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandFlat.lean new file mode 100644 index 000000000..1b356a8f0 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandFlat.lean @@ -0,0 +1,118 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandInput + +/-! +# Flat native preparation of every public band encoding + +## Main results + +* `BandPreparation.runs`: actual flat `State.init` run, all public left/right encodings for + the computed deterministic layout, retained computed tables, input/outside frame and charged halt. +* `BandPreparation.program_length_le`: one fixed finite program, independent of every parameter. + +This is a bounded preparation component under the ordinary native 3SUM objective, not a +whole 3SUM algorithm or a pruned-query evaluation theorem. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The left catalogue retains the original mathematical band and leaf interfaces. -/ +theorem catalogueValue_left {L m N : ℕ} (hm : m ≤ L) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (matrix : ℕ → ℕ → ℤ) + (hX : ∀ i k, matrix i.val k.val = X i k) (β : ℕ) (τ : Leaf L) : + catalogueValue L m N false matrix β (Encoding.leafIndex τ).val = + encodingL (bandArrayL (MaskTable.layout hm) X β) τ := by + apply Encoding.value_final_left + intro u + simpa only [Equiv.symm_apply_apply] using + (bandValue_left hm X matrix hX β (finFunctionFinEquiv u)) + +/-- The right catalogue uses the same original flat leaf order. -/ +theorem catalogueValue_right {L m N : ℕ} (hm : m ≤ L) + (Y : Matrix (Fin (D m)) (Fin N) ℤ) (matrix : ℕ → ℕ → ℤ) + (hY : ∀ k i, matrix i.val k.val = Y k i) (β : ℕ) (τ : Leaf L) : + catalogueValue L m N true matrix β (Encoding.leafIndex τ).val = + encodingR (bandArrayR (MaskTable.layout hm) Y β) τ := by + apply Encoding.value_final_right + intro u + simpa only [Equiv.symm_apply_apply] using + (bandValue_right hm Y matrix (fun i k => hY k i) β (finFunctionFinEquiv u)) + +set_option maxRecDepth 4096 in +-- Static size arithmetic traverses the new finite blocks, reusing both encoder size bounds. +/-- One finite code-size bound for the 96-register program and its final halt. -/ +theorem program_length_le : (program.program catalogue).length ≤ 16384 := by + have hl := Encoding.program_length_le false + have hr := Encoding.program_length_le true + simp only [Code.program, List.length_append, Code.length_assemble, + List.length_cons, List.length_nil] at hl hr ⊢ + simp only [program, header, headerLoads, headerPowers, headerLayout, powerCode, + saveInput, countCode, countMasks, countBody, populationCode, rootCode, rootBody, + bandCount, tablesCode, tableBody, tableWrite, encodeBands, bandBody, prepareBand, + prepareHead, prepareBody, splitCode, splitHead, splitBody, readCell, collectBand, + collectHead, restoreInput, Code.size, Code.size_rename, Power.program, Power.body, + ArrayCopy.program, ArrayCopy.body, Bool.false_eq_true, ↓reduceIte, Nat.reduceAdd] + omega + +set_option maxRecDepth 4096 in +-- This finite structural check covers the same fixed instruction list, not an execution. +/-- The uniform program does not inspect its target word width. -/ +theorem not_readsWidth : ¬program.ReadsWidth := by decide + +/-- Every row and column band is encoded exactly once into its shared flat block. The +actual input contains only the three scalar parameters and signed matrices. `w` is the signed +source magnitude width and `w+2` is the target width used by the existing native kernels. +The prefix run exposes the actual final memory; its next instruction is the charged halt. +No unrelated caller memory or header is required to be zero in the general `exec` theorem. -/ +theorem runs {w L m N : ℕ} (hw : 0 < w) (hm : m ≤ L) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (A : ℤ) (hA : 0 ≤ A) (hX : ∀ i k, |X i k| ≤ A) (hY : ∀ k j, |Y k j| ≤ A) + (hmag : (7 ^ L : ℕ) * A < (2 : ℤ) ^ (w - 1)) + (hfit : Fits (w + 2) (wordBudget L m N)) : + InputFits (w + 2) (input L m N X Y) ∧ + ∃ y n, n ≤ timeBound L m N ∧ + Runs (w + 2) (program.program catalogue) (State.init (input L m N X Y)) + ⟨program.size, y⟩ n ∧ + Executes (w + 2) (program.program catalogue) (State.init (input L m N X Y)) + (catalogueBase L m N) (n + 1) ∧ + y.Bounded (w + 2) ∧ + (∀ β < numBands L m N, ∀ τ, + y.mem (catalogueAddress L m N false β (Encoding.leafIndex τ).val) = + encode (encodingL (bandArrayL (MaskTable.layout hm) X β) τ)) ∧ + (∀ β < numBands L m N, ∀ τ, + y.mem (catalogueAddress L m N true β (Encoding.leafIndex τ).val) = + encode (encodingR (bandArrayR (MaskTable.layout hm) Y β) τ)) ∧ + (∀ a < 2 ^ L, y.mem (inverseBase L m N + a) = MaskTable.lookup L m a) ∧ + (∀ gh : Fin (K0 L m) × Fin (K0 L m), + y.mem (tableBase L m N + (MaskTable.pairRank gh).val) = + (MaskTable.selectedMask gh).val) ∧ + (∀ b, ¬(inputEnd m N ≤ b ∧ b < memoryEnd L m N) → + y.mem b = (State.init (input L m N X Y) : State 96).data.mem b) := by + obtain ⟨hi, y, he, hb, hp, hv, ht, hf, hframe⟩ := + exec_input hw hm X Y A hA hX hY hmag hfit + obtain ⟨n, hn, he⟩ := he + refine ⟨hi, y, n, hn, ?_, ?_, hb, ?_, ?_, ht, hf, hframe⟩ + · simpa only [Nat.zero_add, State.init] using he.runs (pc := 0) + (p := program.program catalogue) (Program.Contains.self _).append_left + · simpa only [hp.catalogue, State.init] using he.executes catalogue + · intro β hβ τ + rw [hv false β hβ _ (Encoding.leafIndex τ).isLt, + catalogueValue_left hm X _ (by intro i k; simp [matrixValues])] + · intro β hβ τ + rw [hv true β hβ _ (Encoding.leafIndex τ).isLt, + catalogueValue_right hm Y _ (by intro k i; simp [matrixValues])] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandHeader.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandHeader.lean new file mode 100644 index 000000000..abc4fe7e9 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandHeader.lean @@ -0,0 +1,278 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandProgram + +/-! +# Actual header loads, native powers and concrete band addresses + +## Main results + +* `BandPreparation.exec_powerCode`: exact reuse of the five-register native power routine. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Renaming the actual power routine has no instruction overhead and changes only its +accumulator, counter and constant-one register. -/ +theorem exec_powerCode {w : ℕ} (v b n : Fin 96) + (hinj : Function.Injective (powerRegisters v b n)) (B M : ℕ) (hB : 1 ≤ B) + (x : Data 96) (hb : x.regs b = B) (hn : x.regs n = M) + (hfit : Fits w (B ^ M + M + 2)) : + ∃ y, ExecWithin w (powerCode v b n) x y (4 * M + 5) ∧ + y.regs v = B ^ M ∧ y.regs one = 1 ∧ y.mem = x.mem ∧ + (∀ i, i ≠ v → i ≠ counter → i ≠ one → y.regs i = x.regs i) := by + obtain ⟨z, hz, hv, _, ho, hm, _, _⟩ := + Power.exec B M hB (x.comap (powerRegisters v b n)) hb hn hfit + have he := hz.rename hinj + refine ⟨z.extend (powerRegisters v b n) x, he, ?_, ?_, hm, ?_⟩ + · have h := congrArg (fun d : Data 5 => d.regs Power.value) (Data.comap_extend hinj z x) + exact h.trans hv + · have h := congrArg (fun d : Data 5 => d.regs Power.one) (Data.comap_extend hinj z x) + exact h.trans ho + · intro i hv hc ho + apply he.regs_eq + simp [Power.program, Power.body, Code.rename, Code.Preserves, Op.rename, Op.dest, Op.bin, + powerRegisters, Power.value, Power.counter, Power.base, Power.exponent, Power.one, + Ne.symm hv, Ne.symm hc, Ne.symm ho] + +/-- Scalar parameters loaded from the three actual input headers and small constants. -/ +structure BasicParameters (L m N : ℕ) (x : Data 96) : Prop where + /-- Input depth. -/ + levels : x.regs levels = L + /-- Input inner cardinality. -/ + inner : x.regs inner = m + /-- Input matrix dimension. -/ + rows : x.regs rows = N + /-- Constant zero. -/ + zero : x.regs zero = 0 + /-- Constant one. -/ + one : x.regs one = 1 + /-- Constant two. -/ + two : x.regs two = 2 + /-- Constant three. -/ + three : x.regs three = 3 + /-- Constant four. -/ + four : x.regs four = 4 + /-- Constant seven. -/ + seven : x.regs seven = 7 + /-- Constant ten. -/ + ten : x.regs ten = 10 + +private def basicRegisters : Finset (Fin 96) := + {levels, inner, rows, zero, two, three, four, seven, ten} + +private theorem BasicParameters.of_frame {L m N : ℕ} {x y : Data 96} + (hx : BasicParameters L m N x) (hone : y.regs BandPreparation.one = 1) + (h : ∀ i ∈ basicRegisters, y.regs i = x.regs i) : BasicParameters L m N y := + ⟨(h _ (by decide)).trans hx.levels, (h _ (by decide)).trans hx.inner, + (h _ (by decide)).trans hx.rows, (h _ (by decide)).trans hx.zero, hone, + (h _ (by decide)).trans hx.two, (h _ (by decide)).trans hx.three, + (h _ (by decide)).trans hx.four, (h _ (by decide)).trans hx.seven, + (h _ (by decide)).trans hx.ten⟩ + +private theorem BasicParameters.after_power {L m N : ℕ} {v : Fin 96} {x y : Data 96} + (hx : BasicParameters L m N x) (hv : v ∉ basicRegisters) + (hone : y.regs BandPreparation.one = 1) + (hf : ∀ i, i ≠ v → i ≠ counter → i ≠ BandPreparation.one → y.regs i = x.regs i) : + BasicParameters L m N y := hx.of_frame hone (by + intro i hi + have hstatic : ∀ i ∈ basicRegisters, i ≠ counter ∧ i ≠ BandPreparation.one := by decide + exact hf i (fun heq => hv (heq ▸ hi)) (hstatic i hi).1 (hstatic i hi).2) + +/-- The global numerical budget bounds every final header power and its exponent. -/ +theorem power_fits {w L m N : ℕ} (hfit : Fits w (wordBudget L m N)) + (b n : ℕ) (hb : b ≤ 10) (hn : n ≤ L) : Fits w (b ^ n + n + 2) := by + have hp : b ^ n ≤ 10 ^ L := + (Nat.pow_le_pow_left hb _).trans (Nat.pow_le_pow_right (by decide) hn) + have hpos : 1 ≤ 10 ^ L := Nat.one_le_pow L 10 (by decide) + apply hfit.of_le + unfold wordBudget + nlinarith only [hp, hn, hpos, Nat.zero_le N, Nat.zero_le (N * D m), + Nat.zero_le (2 ^ L), Nat.zero_le (2 * numBands L m N * 10 ^ L)] + +/-- All header powers are computed by five actual native loops. Their register embeddings +are injective and their complete cost is linear in the original depth. -/ +theorem exec_headerPowers {w L m N : ℕ} (hm : m ≤ L) (x : Data 96) + (hx : BasicParameters L m N x) (hfit : Fits w (wordBudget L m N)) : + ∃ y, ExecWithin w headerPowers x y (16 * L + 26) ∧ BasicParameters L m N y ∧ + y.regs columns = D m ∧ y.regs outerSize = N0 L m ∧ y.regs masks = 2 ^ L ∧ + y.regs cells = 7 ^ L ∧ y.regs leaves = 10 ^ L ∧ y.mem = x.mem := by + obtain ⟨x1, h1, hD, ho1, hm1, hf1⟩ := exec_powerCode columns four inner (by decide) + 4 m (by decide) x hx.four hx.inner (power_fits hfit 4 m (by decide) hm) + have hp1 : BasicParameters L m N x1 := hx.after_power (by decide) ho1 hf1 + let x2 := x1.set temporary (L - m) + have hsub : ExecWithin w (ram[ temporary ← levels - inner ]) x1 x2 1 := by + have hL := power_fits hfit 1 L (by decide) le_rfl + have hs : Fits w (L - m) := hL.of_le (by simp only [one_pow]; omega) + ram_eval [x2, hp1.levels, hp1.inner, hs, temporary, levels, inner] + have ho2 : x2.regs one = 1 := by simpa [x2, temporary, one] using hp1.one + have hp2 : BasicParameters L m N x2 := hp1.of_frame ho2 (by + intro i hi + apply hsub.regs_eq + have hf : ∀ i ∈ basicRegisters, (ram[ temporary ← levels - inner ]).Preserves i := by + simp only [Code.Preserves, Op.dest_bin] + decide + exact hf i hi) + have htemp : x2.regs temporary = L - m := by simp [x2] + obtain ⟨x3, h3, hN0, ho3, hm3, hf3⟩ := exec_powerCode outerSize three temporary (by decide) + 3 (L - m) (by decide) x2 hp2.three htemp + (power_fits hfit 3 (L - m) (by decide) (Nat.sub_le _ _)) + have hp3 : BasicParameters L m N x3 := hp2.after_power (by decide) ho3 hf3 + obtain ⟨x4, h4, hS2, ho4, hm4, hf4⟩ := exec_powerCode masks two levels (by decide) + 2 L (by decide) x3 hp3.two hp3.levels (power_fits hfit 2 L (by decide) le_rfl) + have hp4 : BasicParameters L m N x4 := hp3.after_power (by decide) ho4 hf4 + obtain ⟨x5, h5, hS7, ho5, hm5, hf5⟩ := exec_powerCode cells seven levels (by decide) + 7 L (by decide) x4 hp4.seven hp4.levels (power_fits hfit 7 L (by decide) le_rfl) + have hp5 : BasicParameters L m N x5 := hp4.after_power (by decide) ho5 hf5 + obtain ⟨y, h6, hS10, ho6, hm6, hf6⟩ := exec_powerCode leaves ten levels (by decide) + 10 L (by decide) x5 hp5.ten hp5.levels (power_fits hfit 10 L (by decide) le_rfl) + refine ⟨y, ?_, hp5.after_power (by decide) ho6 hf6, ?_, ?_, ?_, ?_, hS10, ?_⟩ + · apply (h1.seq (hsub.seq (h3.seq (h4.seq (h5.seq h6))))).mono + omega + · have hD2 : x2.regs columns = 4 ^ m := by simpa [x2, temporary, columns] using hD + exact (hf6 columns (by decide) (by decide) (by decide)).trans + ((hf5 columns (by decide) (by decide) (by decide)).trans + ((hf4 columns (by decide) (by decide) (by decide)).trans + ((hf3 columns (by decide) (by decide) (by decide)).trans hD2))) + · exact (hf6 outerSize (by decide) (by decide) (by decide)).trans + ((hf5 outerSize (by decide) (by decide) (by decide)).trans + ((hf4 outerSize (by decide) (by decide) (by decide)).trans hN0)) + · exact (hf6 masks (by decide) (by decide) (by decide)).trans + ((hf5 masks (by decide) (by decide) (by decide)).trans hS2) + · exact (hf6 cells (by decide) (by decide) (by decide)).trans hS7 + · exact hm6.trans (hm5.trans (hm4.trans (hm3.trans hm1))) + +/-- Concrete computed powers and addresses after native header initialization. -/ +structure HeaderPost (L m N : ℕ) (x : Data 96) : Prop where + /-- Original scalar headers and constants. -/ + fixed : BasicParameters L m N x + /-- Thin dimension. -/ + columns : x.regs columns = D m + /-- Outer block dimension. -/ + outerSize : x.regs outerSize = N0 L m + /-- Complete mask scan range. -/ + masks : x.regs masks = 2 ^ L + /-- Flat input length of each band array. -/ + cells : x.regs cells = 7 ^ L + /-- Flat output length of each band encoding. -/ + leaves : x.regs leaves = 10 ^ L + /-- Original initialized prefix length. -/ + prefixSize : x.regs prefixSize = inputEnd m N + /-- Protected original-input copy address. -/ + snapshot : x.regs snapshot = snapshotBase L m N + /-- Dense inverse-table address. -/ + inverse : x.regs inverse = inverseBase L m N + /-- Selected-mask forward-table address. -/ + table : x.regs table = tableBase L m N + /-- Shared band-encoding output address. -/ + catalogue : x.regs catalogue = catalogueBase L m N + +/-- Header parameters are loaded from real memory, all five powers are computed natively, +and every concrete address calculation is charged. Header zero and all unrelated cells may +contain arbitrary words; initialization reads only addresses one through three. -/ +theorem exec_header {w L m N : ℕ} (hm : m ≤ L) (x : Data 96) + (hL : x.mem 1 = L) (hmemory : x.mem 2 = m) (hN : x.mem 3 = N) + (hfit : Fits w (wordBudget L m N)) : + ∃ y, ExecWithin w header x y (16 * L + 49) ∧ HeaderPost L m N y ∧ y.mem = x.mem := by + have h256 : 256 ≤ wordBudget L m N := Nat.le_mul_of_pos_right _ (Nat.succ_pos _) + have hsmall (v : ℕ) (hv : v ≤ 256) : Fits w v := hfit.of_le (hv.trans h256) + let x0 := (((((((((((((x.set address 1).set levels L).set address 2).set inner m).set + address 3).set rows N).set zero 0).set one 1).set two 2).set three 3).set four 4).set + seven 7).set ten 10) + have hs : ExecWithin w headerLoads x x0 13 := by + ram_eval [headerLoads, x0, hL, hmemory, hN, hsmall 0 (by decide), hsmall 1 (by decide), + hsmall 2 (by decide), hsmall 3 (by decide), hsmall 4 (by decide), + hsmall 7 (by decide), hsmall 10 (by decide), + address, levels, inner, rows, zero, one, two, three, four, seven, ten, Fin.mk.injEq] + have hp : BasicParameters L m N x0 := by + constructor <;> simp [x0, address, levels, inner, rows, zero, one, two, three, four, seven, ten] + obtain ⟨z, hz, hbasic, hD, hN0, hS2, hS7, hS10, hzmem⟩ := exec_headerPowers hm x0 hp hfit + have h7 : 7 ^ L ≤ 10 ^ L := Nat.pow_le_pow_left (by decide) _ + have h2 : 2 ^ L ≤ 10 ^ L := Nat.pow_le_pow_left (by decide) _ + have hlocal : Fits w (4 * (N * D m) + 8 * 10 ^ L + 16) := by + apply hfit.of_le + unfold wordBudget + nlinarith only [Nat.zero_le L, Nat.zero_le N, Nat.zero_le (N * D m), + Nat.zero_le (2 ^ L), Nat.zero_le (10 ^ L), + Nat.zero_le (2 * numBands L m N * 10 ^ L)] + let B := N * D m * 2 + 4 + let S := 10 ^ L * 2 + 7 ^ L + 2 + B + let I := S + B + let T := I + 2 ^ L + let C := T + 2 ^ L + have hnd : Fits w (N * D m) := hlocal.of_le (by omega) + have hnd2 : Fits w (N * D m * 2) := hlocal.of_le (by omega) + have hB : Fits w B := hlocal.of_le (by dsimp [B]; omega) + have ht2 : Fits w (10 ^ L * 2) := hlocal.of_le (by omega) + have hts : Fits w (10 ^ L * 2 + 7 ^ L) := hlocal.of_le (by omega) + have hts2 : Fits w (10 ^ L * 2 + 7 ^ L + 2) := hlocal.of_le (by omega) + have hS : Fits w S := hlocal.of_le (by dsimp [S, B]; omega) + have hI : Fits w I := hlocal.of_le (by dsimp [I, S, B]; omega) + have hT : Fits w T := hlocal.of_le (by dsimp [T, I, S, B]; omega) + have hC : Fits w C := hlocal.of_le (by dsimp [C, T, I, S, B]; omega) + let y := ((((((((((z.set prefixSize (N * D m)).set prefixSize (N * D m * 2)).set + prefixSize B).set snapshot (10 ^ L * 2)).set snapshot (10 ^ L * 2 + 7 ^ L)).set + snapshot (10 ^ L * 2 + 7 ^ L + 2)).set snapshot S).set inverse I).set table T).set catalogue C) + have hy : ExecWithin w headerLayout z y 10 := by + ram_eval [headerLayout, y, B, S, I, T, C, hbasic.rows, hbasic.two, hbasic.four, + hS2, hS7, hS10, hD, hnd, hnd2, hB, ht2, hts, hts2, hS, hI, hT, hC, + prefixSize, snapshot, inverse, table, catalogue, rows, columns, leaves, cells, + masks, two, four, Fin.mk.injEq, Function.update_idem] + have hfixed : BasicParameters L m N y := hbasic.of_frame (by + simpa [y, prefixSize, snapshot, inverse, table, catalogue, one] using hbasic.one) (by + intro i hi + apply hy.regs_eq + have hf : ∀ i ∈ basicRegisters, headerLayout.Preserves i := by + simp only [headerLayout, Code.Preserves, Op.dest_bin] + decide + exact hf i hi) + refine ⟨y, ?_, ?_, hzmem⟩ + · exact (hs.seq (hz.seq hy)).mono (by omega) + · constructor + · exact hfixed + · simpa [y, prefixSize, snapshot, inverse, table, catalogue, columns] using hD + · simpa [y, prefixSize, snapshot, inverse, table, catalogue, outerSize] using hN0 + · simpa [y, prefixSize, snapshot, inverse, table, catalogue, masks] using hS2 + · simpa [y, prefixSize, snapshot, inverse, table, catalogue, cells] using hS7 + · simpa [y, prefixSize, snapshot, inverse, table, catalogue, leaves] using hS10 + · have hb : y.regs prefixSize = B := by + simp +decide [y, prefixSize, snapshot, inverse, table, catalogue] + rw [hb] + dsimp only [B, inputEnd] + omega + · have hs : y.regs snapshot = S := by + simp +decide [y, prefixSize, snapshot, inverse, table, catalogue] + rw [hs] + dsimp only [S, B, snapshotBase, inputEnd, Encoding.scratchBase, Encoding.outputBase] + omega + · have hi : y.regs inverse = I := by + simp +decide [y, prefixSize, snapshot, inverse, table, catalogue] + rw [hi] + dsimp only [I, S, B, inverseBase, snapshotBase, inputEnd, + Encoding.scratchBase, Encoding.outputBase] + omega + · have ht : y.regs table = T := by + simp +decide [y, prefixSize, snapshot, inverse, table, catalogue] + rw [ht] + dsimp only [T, I, S, B, tableBase, inverseBase, snapshotBase, inputEnd, + Encoding.scratchBase, Encoding.outputBase] + omega + · have hc : y.regs catalogue = C := by simp [y] + rw [hc] + dsimp only [C, T, I, S, B, catalogueBase, tableBase, inverseBase, snapshotBase, + inputEnd, Encoding.scratchBase, Encoding.outputBase] + omega + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandInput.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandInput.lean new file mode 100644 index 000000000..5a60b0da5 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandInput.lean @@ -0,0 +1,149 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandExec +public import Complexity.Foundations.Data.List.RowMajor +import Mathlib.Data.List.GetD + +/-! +# Actual initialized header and signed-matrix inputs + +## Main definitions + +* `BandPreparation.matrixValues`: zero extension of the two original finite matrices for + stating the already proved native guarded-load specification. + +## Main results + +* `BandPreparation.input_fits`: the actual initialized list fits from original magnitude + and parameter bounds. +* `BandPreparation.exec_input`: complete charged native execution from `State.init`. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Original matrix entries, zero extended to natural indices. Right-side `r` denotes a +column of `Y`. This is only the mathematical memory specification; the program loads the +concrete row-major input words, after its actual bounds guard. -/ +def matrixValues {m N : ℕ} (X : Matrix (Fin N) (Fin (D m)) ℤ) + (Y : Matrix (Fin (D m)) (Fin N) ℤ) (side : Bool) (r k : ℕ) : ℤ := + if hr : r < N then if hk : k < D m then + if side then Y ⟨k, hk⟩ ⟨r, hr⟩ else X ⟨r, hr⟩ ⟨k, hk⟩ else 0 else 0 + +/-- The input prefix consists of the native length header, three scalar words and both +original matrices, with no powers, tables or precomputed outputs. -/ +theorem input_length {m N : ℕ} (L : ℕ) (X : Matrix (Fin N) (Fin (D m)) ℤ) + (Y : Matrix (Fin (D m)) (Fin N) ℤ) : (input L m N X Y).length + 1 = inputEnd m N := by + simp only [input, List.flatMap_ofFn_prod, List.length_append, List.length_ofFn, + List.length_cons, List.length_nil, inputEnd] + ring + +private theorem init_matrix {L m N : ℕ} (X : Matrix (Fin N) (Fin (D m)) ℤ) + (Y : Matrix (Fin (D m)) (Fin N) ℤ) (side : Bool) (r : ℕ) (hr : r < N) + (k : ℕ) (hk : k < D m) : + (State.init (input L m N X Y) : State 96).data.mem (matrixOffset m N side r k + 4) = + encode (matrixValues X Y side r k) := by + let xs := (List.finRange N).flatMap (fun i => List.ofFn fun k => encode (X i k)) + let ys := (List.finRange (D m)).flatMap (fun k => List.ofFn fun j => encode (Y k j)) + have hxs : xs.length = N * D m := by + simp only [xs, List.flatMap_ofFn_prod, List.length_ofFn] + change (Data.init (input L m N X Y) : Data 96).mem _ = _ + rw [show matrixOffset m N side r k + 4 = (matrixOffset m N side r k + 3) + 1 by omega, + Data.init_mem_succ] + change ((L :: m :: N :: (xs ++ ys)).getD (matrixOffset m N side r k + 3) 0) = _ + rw [show matrixOffset m N side r k + 3 = + ((matrixOffset m N side r k + 1) + 1) + 1 by omega] + simp only [List.getD_cons_succ] + cases side + · have hi : r * D m + k < N * D m := by + have h := (finProdFinEquiv (⟨r, hr⟩, ⟨k, hk⟩)).isLt + simpa only [finProdFinEquiv, Equiv.coe_fn_mk, Nat.add_comm, Nat.mul_comm] using h + simp only [matrixOffset, Bool.false_eq_true, ↓reduceIte] + rw [List.getD_append _ _ _ _ (by simpa only [hxs] using hi)] + simpa only [xs, matrixValues, dite_eq_left hr, dite_eq_left hk, + Bool.false_eq_true, ↓reduceIte] using + (List.getD_flatMap_ofFn (fun i k => encode (X i k)) 0 ⟨r, hr⟩ ⟨k, hk⟩) + · simp only [matrixOffset, ↓reduceIte] + rw [List.getD_append_right _ _ _ _ (by rw [hxs]; omega), hxs, + Nat.add_sub_cancel_right] + simpa only [ys, matrixValues, dite_eq_left hr, dite_eq_left hk, ↓reduceIte] using + (List.getD_flatMap_ofFn (fun k j => encode (Y k j)) 0 ⟨k, hk⟩ ⟨r, hr⟩) + +/-- The sign/magnitude input encoding uses at most `w+1` bits; the actual native program +uses `w+2` bits for its signed arithmetic kernels. Every input word is covered. -/ +theorem input_fits {w L m N : ℕ} (hw : 0 < w) (hm : m ≤ L) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (A : ℤ) (hA : 0 ≤ A) (hX : ∀ i k, |X i k| ≤ A) (hY : ∀ k j, |Y k j| ≤ A) + (hmag : (7 ^ L : ℕ) * A < (2 : ℤ) ^ (w - 1)) + (hfit : Fits (w + 2) (wordBudget L m N)) : InputFits (w + 2) (input L m N X Y) := by + have hscalar : Fits (w + 2) (L + N) := (local_fits hfit).of_le (by + nlinarith only [Nat.zero_le L, Nat.zero_le N, Nat.zero_le (N * D m), + Nat.zero_le (2 ^ L), Nat.zero_le (10 ^ L)]) + have hpow : (1 : ℤ) ≤ (7 ^ L : ℕ) := by + exact_mod_cast Nat.one_le_pow L 7 (by decide) + have hAfit : A < (2 : ℤ) ^ (w - 1) := by nlinarith only [hpow, hA, hmag] + have hvalue (z : ℤ) (hz : |z| ≤ A) : Fits (w + 2) (encode z) := by + have hb := abs_lt.mp (hz.trans_lt hAfit) + exact (encode_fits (show GenericRAM.Signed.Fits w z from ⟨hw, hb.1.le, hb.2⟩)).mono + (by omega) + refine ⟨?_, ?_⟩ + · apply ((memory_fits hfit).of_le (input_le_memoryEnd L m N)).of_le + have := input_length L X Y + omega + · intro v hv + simp only [input, List.mem_append, List.mem_cons, List.not_mem_nil, or_false] at hv + rcases hv with (hv | hv) | hv + · rcases hv with rfl | rfl | rfl <;> exact hscalar.of_le (by omega) + · obtain ⟨i, _, hv⟩ := List.mem_flatMap.mp hv + obtain ⟨k, rfl⟩ := List.mem_ofFn.mp hv + exact hvalue _ (hX i k) + · obtain ⟨k, _, hv⟩ := List.mem_flatMap.mp hv + obtain ⟨j, rfl⟩ := List.mem_ofFn.mp hv + exact hvalue _ (hY k j) + +/-- Complete actual `State.init` execution, with both encoded catalogues, computed tables, +preserved input and outside frame, bounded final state and the complete charged time bound. -/ +theorem exec_input {w L m N : ℕ} (hw : 0 < w) (hm : m ≤ L) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (A : ℤ) (hA : 0 ≤ A) (hX : ∀ i k, |X i k| ≤ A) (hY : ∀ k j, |Y k j| ≤ A) + (hmag : (7 ^ L : ℕ) * A < (2 : ℤ) ^ (w - 1)) + (hfit : Fits (w + 2) (wordBudget L m N)) : + InputFits (w + 2) (input L m N X Y) ∧ + ∃ y, ExecWithin (w + 2) program (State.init (input L m N X Y)).data y (timeBound L m N) ∧ + y.Bounded (w + 2) ∧ BandParameters L m N y ∧ + (∀ side β, β < numBands L m N → ∀ p, p < 10 ^ L → + y.mem (catalogueAddress L m N side β p) = + encode (catalogueValue L m N side (matrixValues X Y side) β p)) ∧ + (∀ a < 2 ^ L, y.mem (inverseBase L m N + a) = MaskTable.lookup L m a) ∧ + (∀ gh : Fin (K0 L m) × Fin (K0 L m), + y.mem (tableBase L m N + (MaskTable.pairRank gh).val) = + (MaskTable.selectedMask gh).val) ∧ + (∀ b, ¬(inputEnd m N ≤ b ∧ b < memoryEnd L m N) → + y.mem b = (State.init (input L m N X Y) : State 96).data.mem b) := by + have hi := input_fits hw hm X Y A hA hX hY hmag hfit + let x : Data 96 := (State.init (input L m N X Y)).data + have hL : x.mem 1 = L := by simp [x, State.init, Data.init, input] + have hm' : x.mem 2 = m := by simp [x, State.init, Data.init, input] + have hN : x.mem 3 = N := by simp [x, State.init, Data.init, input] + have hvalues (side : Bool) (r : ℕ) (hr : r < N) (k : ℕ) (hk : k < D m) : + |matrixValues X Y side r k| ≤ A := by + cases side <;> simp only [matrixValues, dite_eq_left hr, dite_eq_left hk, + Bool.false_eq_true, ↓reduceIte] + · exact hX _ _ + · exact hY _ _ + obtain ⟨y, he, hp, hv, ht, hf, hframe⟩ := exec hw hm x hL hm' hN + (matrixValues X Y) (init_matrix X Y) A hA hvalues hmag hfit + exact ⟨hi, y, he, he.bounded (Data.Bounded.init hi), hp, hv, ht, hf, hframe⟩ + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandMeaning.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandMeaning.lean new file mode 100644 index 000000000..67daa9a4e --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandMeaning.lean @@ -0,0 +1,351 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandProgram +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.SplitDigits + +/-! +# Meaning of the native band indices + +## Main results + +* `BandPreparation.leftAt_isInner`, `BandPreparation.rightAt_isInner`: the original + encoder numberings classify the same inner positions as the native decoder. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Encoding BandDigits + +/-- Digits three through six are exactly the inner left variables. -/ +theorem leftAt_isInner (d : Fin 7) : (leftAt d).IsInner ↔ ¬d.val < 3 := by decide +revert + +/-- The same four digits classify the inner right variables. -/ +theorem rightAt_isInner (d : Fin 7) : (rightAt d).IsInner ↔ ¬d.val < 3 := by decide +revert + +/-- An outer left digit is its ordinary base-three value. -/ +theorem leftAt_outer (d : Fin 7) (h : d.val < 3) : + (leftAt d).outerIndex.val = d.val := by decide +revert + +/-- An outer right digit is its ordinary base-three value. -/ +theorem rightAt_outer (d : Fin 7) (h : d.val < 3) : + (rightAt d).outerIndex.val = d.val := by decide +revert + +/-- The four inner left digits use the existing row-major pair numbering. -/ +theorem leftAt_inner (d : Fin 7) (h : ¬d.val < 3) : + (finProdFinEquiv (leftAt d).innerIndex).val = d.val - 3 := by decide +revert + +/-- The four inner right digits use the same pair numbering. -/ +theorem rightAt_inner (d : Fin 7) (h : ¬d.val < 3) : + (finProdFinEquiv (rightAt d).innerIndex).val = d.val - 3 := by decide +revert + +private theorem filter_outer {L : ℕ} (u : Fin L → Fin 7) + (Q : Finset (Fin L)) (hQ : ∀ ℓ, ℓ ∈ Q ↔ (u ℓ).val < 3) + (n : ℕ) (hn : Q.card = n) (f : Fin L → ℕ) + (hf : ∀ ℓ ∈ Q, f ℓ = (u ℓ).val) : + (List.ofFn fun ℓ => (u ℓ).val).filter (fun d => d < 3) = + List.ofFn (fun k => f (Q.orderEmbOfFin hn k)) := by + rw [Finset.ofFn_orderEmbOfFin, Finset.sort_eq_filter_finRange, + List.ofFn_eq_map, List.filter_map] + have hp : (fun ℓ : Fin L => decide ((u ℓ).val < 3)) = (fun ℓ => decide (ℓ ∈ Q)) := by + funext ℓ + simp only [hQ] + simp only [Function.comp_def, hp] + apply List.map_congr_left + intro ℓ hℓ + exact (hf ℓ (by simpa only [List.mem_filter, List.mem_finRange, true_and, + decide_eq_true_eq] using hℓ)).symm + +/-- Base-three compression follows the increasing outer positions used by `outerPartL`. -/ +theorem outer_digits_left {L m : ℕ} (u : Fin L → Fin 7) + (h : (innerSetL (fun ℓ => leftAt (u ℓ))).card = m) : + (List.ofFn fun ℓ => (u ℓ).val).filter (fun d => d < 3) = + List.ofFn (fun k => (outerPartL (fun ℓ => leftAt (u ℓ)) h k).val) := by + have hQ : ∀ ℓ, ℓ ∈ (innerSetL (fun ℓ => leftAt (u ℓ)))ᶜ ↔ (u ℓ).val < 3 := by + intro ℓ + simp [innerSetL, leftAt_isInner] + apply filter_outer u (innerSetL (fun ℓ => leftAt (u ℓ)))ᶜ hQ (L - m) + (Finset.card_compl_of_card_eq _ h) (fun ℓ => (leftAt (u ℓ)).outerIndex.val) + intro ℓ hℓ + exact leftAt_outer _ ((hQ ℓ).mp hℓ) + +/-- Base-three compression follows the increasing outer positions used by `outerPartR`. -/ +theorem outer_digits_right {L m : ℕ} (u : Fin L → Fin 7) + (h : (innerSetR (fun ℓ => rightAt (u ℓ))).card = m) : + (List.ofFn fun ℓ => (u ℓ).val).filter (fun d => d < 3) = + List.ofFn (fun k => (outerPartR (fun ℓ => rightAt (u ℓ)) h k).val) := by + have hQ : ∀ ℓ, ℓ ∈ (innerSetR (fun ℓ => rightAt (u ℓ)))ᶜ ↔ (u ℓ).val < 3 := by + intro ℓ + simp [innerSetR, rightAt_isInner] + apply filter_outer u (innerSetR (fun ℓ => rightAt (u ℓ)))ᶜ hQ (L - m) + (Finset.card_compl_of_card_eq _ h) (fun ℓ => (rightAt (u ℓ)).outerIndex.val) + intro ℓ hℓ + exact rightAt_outer _ ((hQ ℓ).mp hℓ) + +private theorem filter_inner {L : ℕ} (u : Fin L → Fin 7) + (Q : Finset (Fin L)) (hQ : ∀ ℓ, ℓ ∈ Q ↔ ¬(u ℓ).val < 3) + (n : ℕ) (hn : Q.card = n) (f : Fin L → ℕ) + (hf : ∀ ℓ ∈ Q, f ℓ = (u ℓ).val - 3) : + (((List.ofFn fun ℓ => (u ℓ).val).filter (fun d => ¬d < 3)).map fun d => d - 3) = + List.ofFn (fun k => f (Q.orderEmbOfFin hn k)) := by + rw [Finset.ofFn_orderEmbOfFin, Finset.sort_eq_filter_finRange, + List.ofFn_eq_map, List.filter_map, List.map_map] + have hp : (fun ℓ : Fin L => decide (¬(u ℓ).val < 3)) = (fun ℓ => decide (ℓ ∈ Q)) := by + funext ℓ + simp only [hQ] + simp only [Function.comp_def, hp] + apply List.map_congr_left + intro ℓ hℓ + exact (hf ℓ (by simpa only [List.mem_filter, List.mem_finRange, true_and, + decide_eq_true_eq] using hℓ)).symm + +/-- Base-four compression follows the increasing inner positions used by `innerPartL`. -/ +theorem inner_digits_left {L m : ℕ} (u : Fin L → Fin 7) + (h : (innerSetL (fun ℓ => leftAt (u ℓ))).card = m) : + (((List.ofFn fun ℓ => (u ℓ).val).filter (fun d => ¬d < 3)).map fun d => d - 3) = + List.ofFn (fun k => (finProdFinEquiv (innerPartL (fun ℓ => leftAt (u ℓ)) h k)).val) := by + have hQ : ∀ ℓ, ℓ ∈ innerSetL (fun ℓ => leftAt (u ℓ)) ↔ ¬(u ℓ).val < 3 := by + intro ℓ + simp [innerSetL, leftAt_isInner] + apply filter_inner u (innerSetL (fun ℓ => leftAt (u ℓ))) hQ m h + (fun ℓ => (finProdFinEquiv (leftAt (u ℓ)).innerIndex).val) + intro ℓ hℓ + exact leftAt_inner _ ((hQ ℓ).mp hℓ) + +/-- The right inner index uses the identical four pair digits. -/ +theorem inner_digits_right {L m : ℕ} (u : Fin L → Fin 7) + (h : (innerSetR (fun ℓ => rightAt (u ℓ))).card = m) : + (((List.ofFn fun ℓ => (u ℓ).val).filter (fun d => ¬d < 3)).map fun d => d - 3) = + List.ofFn (fun k => (finProdFinEquiv (innerPartR (fun ℓ => rightAt (u ℓ)) h k)).val) := by + have hQ : ∀ ℓ, ℓ ∈ innerSetR (fun ℓ => rightAt (u ℓ)) ↔ ¬(u ℓ).val < 3 := by + intro ℓ + simp [innerSetR, rightAt_isInner] + apply filter_inner u (innerSetR (fun ℓ => rightAt (u ℓ))) hQ m h + (fun ℓ => (finProdFinEquiv (rightAt (u ℓ)).innerIndex).val) + intro ℓ hℓ + exact rightAt_inner _ ((hQ ℓ).mp hℓ) + +/-- Native mask compression agrees with the exact inner level set of the original array. -/ +theorem parts_mask_left {L : ℕ} (a : Fin (7 ^ L)) : + (parts L a.val).mask = + (MaskTable.maskCode (innerSetL (fun ℓ => leftAt (finFunctionFinEquiv.symm a ℓ)))).val := by + rw [(parts_lists L a.val).1, sevenDigits_eq_ofFn] + change _ = (finFunctionFinEquiv (fun ℓ => + if ℓ ∈ innerSetL (fun ℓ => leftAt (finFunctionFinEquiv.symm a ℓ)) + then (1 : Fin 2) else 0)).val + rw [← Nat.ofDigits_finFunctionFinEquiv, List.map_ofFn] + congr 1 + apply congrArg List.ofFn + funext ℓ + change (if (finFunctionFinEquiv.symm a ℓ).val < 3 then 0 else 1) = _ + by_cases h : (finFunctionFinEquiv.symm a ℓ).val < 3 <;> + simp only [innerSetL, Finset.mem_filter, Finset.mem_univ, true_and, + leftAt_isInner, h, not_true_eq_false, not_false_eq_true, ↓reduceIte, + Fin.val_zero, Fin.val_one] + +/-- The right mask is classified identically, without changing the encoder numbering. -/ +theorem parts_mask_right {L : ℕ} (a : Fin (7 ^ L)) : + (parts L a.val).mask = + (MaskTable.maskCode (innerSetR (fun ℓ => rightAt (finFunctionFinEquiv.symm a ℓ)))).val := by + rw [(parts_lists L a.val).1, sevenDigits_eq_ofFn] + change _ = (finFunctionFinEquiv (fun ℓ => + if ℓ ∈ innerSetR (fun ℓ => rightAt (finFunctionFinEquiv.symm a ℓ)) + then (1 : Fin 2) else 0)).val + rw [← Nat.ofDigits_finFunctionFinEquiv, List.map_ofFn] + congr 1 + apply congrArg List.ofFn + funext ℓ + change (if (finFunctionFinEquiv.symm a ℓ).val < 3 then 0 else 1) = _ + by_cases h : (finFunctionFinEquiv.symm a ℓ).val < 3 <;> + simp only [innerSetR, Finset.mem_filter, Finset.mem_univ, true_and, + rightAt_isInner, h, not_true_eq_false, not_false_eq_true, ↓reduceIte, + Fin.val_zero, Fin.val_one] + +/-- Native left outer offset, with the exact public mathematical string interface. -/ +theorem parts_outer_left {L m : ℕ} (a : Fin (7 ^ L)) + (h : (innerSetL (fun ℓ => leftAt (finFunctionFinEquiv.symm a ℓ))).card = m) : + (parts L a.val).outer = + (finFunctionFinEquiv + (outerPartL (fun ℓ => leftAt (finFunctionFinEquiv.symm a ℓ)) h)).val := by + rw [(parts_lists L a.val).2.1, sevenDigits_eq_ofFn] + change Nat.ofDigits 3 ((List.ofFn fun ℓ => (finFunctionFinEquiv.symm a ℓ).val).filter + (fun d => d < 3)) = _ + rw [outer_digits_left _ h, Nat.ofDigits_finFunctionFinEquiv] + +/-- Native right outer offset in the same least-significant-first convention. -/ +theorem parts_outer_right {L m : ℕ} (a : Fin (7 ^ L)) + (h : (innerSetR (fun ℓ => rightAt (finFunctionFinEquiv.symm a ℓ))).card = m) : + (parts L a.val).outer = + (finFunctionFinEquiv + (outerPartR (fun ℓ => rightAt (finFunctionFinEquiv.symm a ℓ)) h)).val := by + rw [(parts_lists L a.val).2.1, sevenDigits_eq_ofFn] + change Nat.ofDigits 3 ((List.ofFn fun ℓ => (finFunctionFinEquiv.symm a ℓ).val).filter + (fun d => d < 3)) = _ + rw [outer_digits_right _ h, Nat.ofDigits_finFunctionFinEquiv] + +/-- Native left thin-dimension offset agrees with the row-major pair digits of `Layout`. -/ +theorem parts_inner_left {L m : ℕ} (a : Fin (7 ^ L)) + (h : (innerSetL (fun ℓ => leftAt (finFunctionFinEquiv.symm a ℓ))).card = m) : + (parts L a.val).inner = (finFunctionFinEquiv fun k => + finProdFinEquiv (innerPartL (fun ℓ => leftAt (finFunctionFinEquiv.symm a ℓ)) h k)).val := by + rw [(parts_lists L a.val).2.2, sevenDigits_eq_ofFn] + change Nat.ofDigits 4 (((List.ofFn fun ℓ => (finFunctionFinEquiv.symm a ℓ).val).filter + (fun d => ¬d < 3)).map fun d => d - 3) = _ + rw [inner_digits_left _ h, Nat.ofDigits_finFunctionFinEquiv] + +/-- Native right thin-dimension offset uses the same `Layout` equivalence. -/ +theorem parts_inner_right {L m : ℕ} (a : Fin (7 ^ L)) + (h : (innerSetR (fun ℓ => rightAt (finFunctionFinEquiv.symm a ℓ))).card = m) : + (parts L a.val).inner = (finFunctionFinEquiv fun k => + finProdFinEquiv (innerPartR (fun ℓ => rightAt (finFunctionFinEquiv.symm a ℓ)) h k)).val := by + rw [(parts_lists L a.val).2.2, sevenDigits_eq_ofFn] + change Nat.ofDigits 4 (((List.ofFn fun ℓ => (finFunctionFinEquiv.symm a ℓ).val).filter + (fun d => ¬d < 3)).map fun d => d - 3) = _ + rw [inner_digits_right _ h, Nat.ofDigits_finFunctionFinEquiv] + +/-- Numeric specification of one guarded band-array read. `matrix r k` denotes an original +matrix cell (or the transposed indexing of the right matrix); the native implementation +computes its actual row-major address and loads it. -/ +def bandValue (L m N β : ℕ) (rightSide : Bool) (matrix : ℕ → ℕ → ℤ) (a : ℕ) : ℤ := + let p := parts L a + let q := MaskTable.lookup L m p.mask + if q = 0 then 0 else + let b := if rightSide then (q - 1) % K0 L m else (q - 1) / K0 L m + let r := (β * K0 L m + b) * N0 L m + p.outer + if r < N then matrix r p.inner else 0 + +/-- The numeric left-band specification is the unchanged mathematical `bandArrayL` for +the computed deterministic layout, including all unselected strings and padding. -/ +theorem bandValue_left {L m N : ℕ} (hm : m ≤ L) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (matrix : ℕ → ℕ → ℤ) + (hX : ∀ i k, matrix i.val k.val = X i k) (β : ℕ) (a : Fin (7 ^ L)) : + bandValue L m N β false matrix a.val = + bandArrayL (MaskTable.layout hm) X β (fun ℓ => leftAt (finFunctionFinEquiv.symm a ℓ)) := by + let u : LeftStr L := fun ℓ => leftAt (finFunctionFinEquiv.symm a ℓ) + let lay := MaskTable.layout hm + have hmask : (parts L a.val).mask = (MaskTable.maskCode (innerSetL u)).val := + parts_mask_left a + by_cases hz : MaskTable.lookup L m (parts L a.val).mask = 0 + · have hmissing : ∀ gh, lay.table gh ≠ innerSetL u := by + intro gh heq + have hc := congrArg (fun Q => (MaskTable.maskCode Q).val) heq + change (MaskTable.maskCode (MaskTable.maskSet (MaskTable.selectedMask gh))).val = _ at hc + rw [MaskTable.maskCode_maskSet] at hc + have ha : (MaskTable.selectedMask gh).val = (parts L a.val).mask := hc.trans hmask.symm + have hne : MaskTable.lookup L m (MaskTable.selectedMask gh).val ≠ 0 := by + rw [MaskTable.lookup_selectedMask] + omega + exact hne (ha ▸ hz) + have hf := bandFamilyL_eq_zero lay X β hmissing + simp only [bandValue, ↓reduceIte, hz] + change 0 = (arrayL m (bandFamilyL lay X β)) u + unfold arrayL + split <;> simp only [hf, Matrix.zero_apply] + · obtain ⟨gh, hs⟩ := MaskTable.exists_selectedMask_of_lookup hz + have hcode : MaskTable.selectedMask gh = MaskTable.maskCode (innerSetL u) := + Fin.ext (hs.trans hmask) + have hset : lay.table gh = innerSetL u := by + change MaskTable.maskSet (MaskTable.selectedMask gh) = _ + rw [hcode, MaskTable.maskSet_maskCode] + have hcard : (innerSetL u).card = m := hset ▸ lay.table_card gh + have hf : bandFamilyL lay X β (innerSetL u) = rowBlock lay X (β * K0 L m + gh.1) := by + rw [← hset] + exact bandFamilyL_table lay X β gh.1 gh.2 + have ho : (parts L a.val).outer = (lay.rowIdx.symm (outerPartL u hcard)).val := + parts_outer_left a hcard + have hi : (parts L a.val).inner = (lay.innerIdx.symm (innerPartL u hcard)).val := by + change (parts L a.val).inner = + (finFunctionFinEquiv fun k => finProdFinEquiv (innerPartL u hcard k)).val + exact parts_inner_left a hcard + have hg : (MaskTable.lookup L m (parts L a.val).mask - 1) / K0 L m = gh.1.val := by + rw [← hs] + exact (MaskTable.lookup_blockPair gh).1 + change bandValue L m N β false matrix a.val = (arrayL m (bandFamilyL lay X β)) u + rw [arrayL, dite_eq_left hcard, hf] + simp only [bandValue, hz, ↓reduceIte, Bool.false_eq_true, hg, ho, hi, + rowBlock, padRows] + split_ifs with hr + · exact hX ⟨_, hr⟩ _ + · rfl + +/-- The numeric right-band specification is the unchanged mathematical `bandArrayR`. +Its natural row parameter refers to a column of the original right matrix. -/ +theorem bandValue_right {L m N : ℕ} (hm : m ≤ L) + (Y : Matrix (Fin (D m)) (Fin N) ℤ) (matrix : ℕ → ℕ → ℤ) + (hY : ∀ i k, matrix i.val k.val = Y k i) (β : ℕ) (a : Fin (7 ^ L)) : + bandValue L m N β true matrix a.val = + bandArrayR (MaskTable.layout hm) Y β (fun ℓ => rightAt (finFunctionFinEquiv.symm a ℓ)) := by + let u : RightStr L := fun ℓ => rightAt (finFunctionFinEquiv.symm a ℓ) + let lay := MaskTable.layout hm + have hmask : (parts L a.val).mask = (MaskTable.maskCode (innerSetR u)).val := + parts_mask_right a + by_cases hz : MaskTable.lookup L m (parts L a.val).mask = 0 + · have hmissing : ∀ gh, lay.table gh ≠ innerSetR u := by + intro gh heq + have hc := congrArg (fun Q => (MaskTable.maskCode Q).val) heq + change (MaskTable.maskCode (MaskTable.maskSet (MaskTable.selectedMask gh))).val = _ at hc + rw [MaskTable.maskCode_maskSet] at hc + have ha : (MaskTable.selectedMask gh).val = (parts L a.val).mask := hc.trans hmask.symm + have hne : MaskTable.lookup L m (MaskTable.selectedMask gh).val ≠ 0 := by + rw [MaskTable.lookup_selectedMask] + omega + exact hne (ha ▸ hz) + have hf := bandFamilyR_eq_zero lay Y β hmissing + simp only [bandValue, ↓reduceIte, hz] + change 0 = (arrayR m (bandFamilyR lay Y β)) u + unfold arrayR + split <;> simp only [hf, Matrix.zero_apply] + · obtain ⟨gh, hs⟩ := MaskTable.exists_selectedMask_of_lookup hz + have hcode : MaskTable.selectedMask gh = MaskTable.maskCode (innerSetR u) := + Fin.ext (hs.trans hmask) + have hset : lay.table gh = innerSetR u := by + change MaskTable.maskSet (MaskTable.selectedMask gh) = _ + rw [hcode, MaskTable.maskSet_maskCode] + have hcard : (innerSetR u).card = m := hset ▸ lay.table_card gh + have hf : bandFamilyR lay Y β (innerSetR u) = colBlock lay Y (β * K0 L m + gh.2) := by + rw [← hset] + exact bandFamilyR_table lay Y β gh.1 gh.2 + have ho : (parts L a.val).outer = (lay.colIdx.symm (outerPartR u hcard)).val := + parts_outer_right a hcard + have hi : (parts L a.val).inner = (lay.innerIdx.symm (innerPartR u hcard)).val := by + change (parts L a.val).inner = + (finFunctionFinEquiv fun k => finProdFinEquiv (innerPartR u hcard k)).val + exact parts_inner_right a hcard + have hg : (MaskTable.lookup L m (parts L a.val).mask - 1) % K0 L m = gh.2.val := by + rw [← hs] + exact (MaskTable.lookup_blockPair gh).2 + change bandValue L m N β true matrix a.val = (arrayR m (bandFamilyR lay Y β)) u + rw [arrayR, dite_eq_left hcard, hf] + simp only [bandValue, hz, ↓reduceIte, hg, ho, hi, colBlock, padCols] + split_ifs with hr + · exact hY ⟨_, hr⟩ _ + · rfl + +/-- A selected native mask ensures that its compressed offsets fit the actual matrix +dimensions, rather than merely the coarser depth-based bounds. -/ +theorem selected_parts_bounds {L m : ℕ} (a : Fin (7 ^ L)) + (hz : MaskTable.lookup L m (parts L a.val).mask ≠ 0) : + (parts L a.val).outer < N0 L m ∧ (parts L a.val).inner < D m := by + obtain ⟨gh, hs⟩ := MaskTable.exists_selectedMask_of_lookup hz + let u : LeftStr L := fun ℓ => leftAt (finFunctionFinEquiv.symm a ℓ) + have hc : MaskTable.selectedMask gh = MaskTable.maskCode (innerSetL u) := + Fin.ext (hs.trans (parts_mask_left a)) + have hset : MaskTable.maskSet (MaskTable.selectedMask gh) = innerSetL u := by + rw [hc, MaskTable.maskSet_maskCode] + have hcard : (innerSetL u).card = m := hset ▸ MaskTable.card_selectedMask gh + constructor + · rw [parts_outer_left a hcard] + exact (finFunctionFinEquiv (outerPartL u hcard)).isLt + · rw [parts_inner_left a hcard] + exact (finFunctionFinEquiv (fun k => finProdFinEquiv (innerPartL u hcard k))).isLt + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandMemory.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandMemory.lean new file mode 100644 index 000000000..44f7f1887 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandMemory.lean @@ -0,0 +1,124 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandProgram + +/-! +# Concrete disjoint memory regions and derived native fits + +## Main results + +* `BandPreparation.workspace_le_snapshot`: protected data are above the complete encoder. +* `BandPreparation.memory_fits`: the actual allocation end fits the explicit input budget. +* `BandPreparation.tables_fit`, `BandPreparation.split_fits`, `BandPreparation.encoder_fits`: + component inequalities derived from the same budget, without an intermediate-state oracle. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The native input prefix is disjoint from its protected copy. -/ +theorem input_le_snapshot (L m N : ℕ) : inputEnd m N ≤ snapshotBase L m N := by + unfold snapshotBase + exact (Nat.le_add_right (inputEnd m N) (Encoding.scratchBase L)).trans + (Nat.le_add_right _ (10 ^ L)) + +/-- The encoder's complete low workspace is below the protected original input. -/ +theorem workspace_le_snapshot (L m N : ℕ) : + Encoding.scratchBase L + 10 ^ L ≤ snapshotBase L m N := by + unfold snapshotBase + omega + +/-- The protected original-input copy ends exactly at the inverse table. -/ +theorem snapshot_end (L m N : ℕ) : + snapshotBase L m N + inputEnd m N = inverseBase L m N := rfl + +/-- The inverse table ends exactly at the selected-mask table. -/ +theorem inverse_end (L m N : ℕ) : inverseBase L m N + 2 ^ L = tableBase L m N := rfl + +/-- The selected-mask table ends exactly at the shared output catalogue. -/ +theorem table_end (L m N : ℕ) : tableBase L m N + 2 ^ L = catalogueBase L m N := rfl + +/-- The catalogue base is below the complete allocation end, including an empty catalogue. -/ +theorem catalogue_le_memoryEnd (L m N : ℕ) : catalogueBase L m N ≤ memoryEnd L m N := + Nat.le_add_right _ _ + +/-- Each table block precedes the complete allocation end. -/ +theorem table_le_memoryEnd (L m N : ℕ) : tableBase L m N ≤ memoryEnd L m N := + (Nat.le_add_right _ _ : tableBase L m N ≤ tableBase L m N + 2 ^ L).trans + (catalogue_le_memoryEnd L m N) + +/-- The protected prefix ends below every table/output allocation. -/ +theorem inverse_le_memoryEnd (L m N : ℕ) : inverseBase L m N ≤ memoryEnd L m N := + (Nat.le_add_right _ _ : inverseBase L m N ≤ inverseBase L m N + 2 ^ L).trans + (table_le_memoryEnd L m N) + +/-- The snapshot base is within the complete allocation. -/ +theorem snapshot_le_memoryEnd (L m N : ℕ) : snapshotBase L m N ≤ memoryEnd L m N := + (Nat.le_add_right _ _ : snapshotBase L m N ≤ snapshotBase L m N + inputEnd m N).trans + (inverse_le_memoryEnd L m N) + +/-- Every original input address also fits the complete allocation. -/ +theorem input_le_memoryEnd (L m N : ℕ) : inputEnd m N ≤ memoryEnd L m N := + (input_le_snapshot L m N).trans (snapshot_le_memoryEnd L m N) + +/-- The constant margin in the original budget covers all fixed program constants. -/ +theorem small_fits {w L m N v : ℕ} (hfit : Fits w (wordBudget L m N)) (hv : v ≤ 256) : + Fits w v := + hfit.of_le (hv.trans (Nat.le_mul_of_pos_right _ (Nat.succ_pos _))) + +/-- The entire real allocation, including both catalogue halves, fits the original numerical +word-budget inequality. The sizes of unreachable padded row-major products are irrelevant. -/ +theorem memory_fits {w L m N : ℕ} (hfit : Fits w (wordBudget L m N)) : + Fits w (memoryEnd L m N) := by + have h7 : 7 ^ L ≤ 10 ^ L := Nat.pow_le_pow_left (by decide) _ + apply hfit.of_le + unfold memoryEnd catalogueBase tableBase inverseBase snapshotBase inputEnd + Encoding.scratchBase Encoding.outputBase wordBudget + nlinarith only [h7, Nat.zero_le L, Nat.zero_le N, Nat.zero_le (N * D m), + Nat.zero_le (2 ^ L), Nat.zero_le (10 ^ L), + Nat.zero_le (2 * numBands L m N * 10 ^ L)] + +/-- Both table-store addresses and all population/count arithmetic fit before table execution. -/ +theorem tables_fit {w L m N : ℕ} (hfit : Fits w (wordBudget L m N)) : + Fits w (inverseBase L m N + tableBase L m N + 2 ^ L + L + 4) := by + have h7 : 7 ^ L ≤ 10 ^ L := Nat.pow_le_pow_left (by decide) _ + apply hfit.of_le + unfold tableBase inverseBase snapshotBase inputEnd Encoding.scratchBase Encoding.outputBase + wordBudget + nlinarith only [h7, Nat.zero_le L, Nat.zero_le N, Nat.zero_le (N * D m), + Nat.zero_le (2 ^ L), Nat.zero_le (10 ^ L), + Nat.zero_le (2 * numBands L m N * 10 ^ L)] + +/-- Every original band index admits the already proved native digit-loop fits. -/ +theorem split_fits {w L m N a : ℕ} (hfit : Fits w (wordBudget L m N)) (ha : a < 7 ^ L) : + Fits w (a + L + 16 * 10 ^ L + 16) := by + have h7 : 7 ^ L ≤ 10 ^ L := Nat.pow_le_pow_left (by decide) _ + apply hfit.of_le + unfold wordBudget + nlinarith only [ha, h7, Nat.zero_le L, Nat.zero_le N, Nat.zero_le (N * D m), + Nat.zero_le (2 ^ L), Nat.zero_le (10 ^ L), + Nat.zero_le (2 * numBands L m N * 10 ^ L)] + +/-- The unchanged native encoder's arithmetic/address premise follows from this client's +explicit original-parameter bound. Its signed magnitude premise remains separate. -/ +theorem encoder_fits {w L m N : ℕ} (hfit : Fits w (wordBudget L m N)) : + Fits w (Encoding.addressBound L) := by + apply hfit.of_le + unfold Encoding.addressBound wordBudget + have hpos : 1 ≤ 10 ^ L := Nat.one_le_pow L 10 (by decide) + nlinarith only [hpos, Nat.zero_le L, Nat.zero_le N, Nat.zero_le (N * D m), + Nat.zero_le (2 ^ L), Nat.zero_le (10 ^ L), + Nat.zero_le (2 * numBands L m N * 10 ^ L)] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandParameters.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandParameters.lean new file mode 100644 index 000000000..519cb3b84 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandParameters.lean @@ -0,0 +1,152 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandHeader +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandCount +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandRoot +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.BandBounds + +/-! +# Complete computed scalar parameters for native bands + +## Main results + +* `BandPreparation.exec_countCode`: compute binomial count, block count and band count + from actual scalar parameters, retaining the previously computed header layout. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Registers containing the persistent header and concrete layout. -/ +def headerRegisters : Finset (Fin 96) := + {levels, inner, rows, columns, outerSize, masks, cells, leaves, prefixSize, + snapshot, inverse, table, catalogue, zero, one, two, three, four, seven, ten} + +/-- The concrete layout survives any native execution that preserves its scalar registers. -/ +theorem headerPost_reframe {L m N : ℕ} {x y : Data 96} (hx : HeaderPost L m N x) + (h : ∀ i ∈ headerRegisters, y.regs i = x.regs i) : HeaderPost L m N y := by + refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · exact ⟨(h _ (by decide)).trans hx.fixed.levels, (h _ (by decide)).trans hx.fixed.inner, + (h _ (by decide)).trans hx.fixed.rows, (h _ (by decide)).trans hx.fixed.zero, + (h _ (by decide)).trans hx.fixed.one, (h _ (by decide)).trans hx.fixed.two, + (h _ (by decide)).trans hx.fixed.three, (h _ (by decide)).trans hx.fixed.four, + (h _ (by decide)).trans hx.fixed.seven, (h _ (by decide)).trans hx.fixed.ten⟩ + all_goals first + | exact (h _ (by decide)).trans hx.columns + | exact (h _ (by decide)).trans hx.outerSize + | exact (h _ (by decide)).trans hx.masks + | exact (h _ (by decide)).trans hx.cells + | exact (h _ (by decide)).trans hx.leaves + | exact (h _ (by decide)).trans hx.prefixSize + | exact (h _ (by decide)).trans hx.snapshot + | exact (h _ (by decide)).trans hx.inverse + | exact (h _ (by decide)).trans hx.table + | exact (h _ (by decide)).trans hx.catalogue + +/-- Persistent concrete layout and the additional scalar results actually computed by +the mask-count/root/band-count code. No fit or execution bound is bundled here. -/ +structure BandParameters (L m N : ℕ) (x : Data 96) : Prop extends HeaderPost L m N x where + /-- Computed floor square root of the binomial count. -/ + root : x.regs root = K0 L m + /-- Number of selected subset masks. -/ + square : x.regs square = K0 L m * K0 L m + /-- Computed number of shared bands per side. -/ + bands : x.regs bands = numBands L m N + +/-- All scalar parameters retained throughout preparation and encoder calls. -/ +def parameterRegisters : Finset (Fin 96) := headerRegisters ∪ {root, square, bands} + +/-- Preserve the computed parameters by preserving their actual registers. -/ +theorem parameters_reframe {L m N : ℕ} {x y : Data 96} (hx : BandParameters L m N x) + (h : ∀ i ∈ parameterRegisters, y.regs i = x.regs i) : BandParameters L m N y := by + refine ⟨headerPost_reframe hx.toHeaderPost (fun i hi => h i (Finset.mem_union_left _ hi)), + (h _ (by decide)).trans hx.root, (h _ (by decide)).trans hx.square, + (h _ (by decide)).trans hx.bands⟩ + +/-- A convenient local numerical bound, derived from the original global word budget. -/ +theorem local_fits {w L m N : ℕ} (hfit : Fits w (wordBudget L m N)) : + Fits w (16 * (L + N + N * D m + 2 ^ L + 10 ^ L + 1)) := by + apply hfit.of_le + unfold wordBudget + nlinarith only [Nat.zero_le L, Nat.zero_le N, Nat.zero_le (N * D m), + Nat.zero_le (2 ^ L), Nat.zero_le (10 ^ L), + Nat.zero_le (2 * numBands L m N * 10 ^ L)] + +/-- Compute the root and band counts with the actual native loops. -/ +theorem exec_countCode {w L m N : ℕ} (x : Data 96) + (hx : HeaderPost L m N x) (hfit : Fits w (wordBudget L m N)) : + ∃ y, ExecWithin w countCode x y ((6 * L + 16) * 2 ^ L + 14) ∧ + BandParameters L m N y ∧ y.mem = x.mem := by + have hl := local_fits hfit + have hK : K L m ≤ 2 ^ L := Nat.choose_le_two_pow _ _ + obtain ⟨c, hc, hcount, _, hcmem, _⟩ := exec_countMasks L m x hx.fixed.levels hx.fixed.inner + hx.masks hx.fixed.one hx.fixed.two (hl.of_le (by + nlinarith only [Nat.zero_le L, Nat.zero_le N, Nat.zero_le (N * D m), + Nat.zero_le (2 ^ L), Nat.zero_le (10 ^ L)])) + have hcp : HeaderPost L m N c := headerPost_reframe hx (by + intro i hi + apply hc.regs_eq + have hp : ∀ i ∈ headerRegisters, countMasks.Preserves i := by + simp only [countMasks, countBody, populationCode, Code.Preserves, Op.dest, Op.bin] + decide + exact hp i hi) + obtain ⟨r, hr, hroot, hsquare, hrmem, _⟩ := exec_root (K L m) c hcount hcp.fixed.one + (hl.of_le (by + nlinarith only [hK, Nat.zero_le L, Nat.zero_le N, + Nat.zero_le (N * D m), Nat.zero_le (2 ^ L), Nat.zero_le (10 ^ L)])) + have hrp : HeaderPost L m N r := headerPost_reframe hcp (by + intro i hi + apply hr.regs_eq + have hp : ∀ i ∈ headerRegisters, rootCode.Preserves i := by + simp only [rootCode, rootBody, Code.Preserves, Op.dest, Op.bin] + decide + exact hp i hi) + have hB : bandSize L m ≤ 7 ^ L := bandSize_le_seven_pow L m + have h7 : 7 ^ L ≤ 10 ^ L := Nat.pow_le_pow_left (by decide) _ + have hbfit : Fits w (bandSize L m) := hl.of_le (by + nlinarith only [hB, h7, Nat.zero_le L, Nat.zero_le N, + Nat.zero_le (N * D m), Nat.zero_le (2 ^ L), Nat.zero_le (10 ^ L)]) + have hnfit : Fits w (N + bandSize L m) := hl.of_le (by + nlinarith only [hB, h7, Nat.zero_le L, Nat.zero_le N, + Nat.zero_le (N * D m), Nat.zero_le (2 ^ L), Nat.zero_le (10 ^ L)]) + have hnsubfit : Fits w (N + bandSize L m - 1) := + hnfit.of_le (Nat.sub_le _ _) + have hdivfit : Fits w ((N + bandSize L m - 1) / bandSize L m) := + hnsubfit.of_le (Nat.div_le_self _ _) + let y := (((r.set temporary (bandSize L m)).set bands (N + bandSize L m)).set + bands (N + bandSize L m - 1)).set bands (numBands L m N) + have hroot0 : r.regs root = K0 L m := by simpa only [K0] using hroot + simp only [bandSize] at hbfit hnfit hnsubfit hdivfit + have hy : ExecWithin w bandCount r y 4 := by + ram_eval [bandCount, y, hroot0, hrp.outerSize, hrp.fixed.rows, hrp.fixed.one, + bandSize, numBands, hbfit, hnfit, hnsubfit, hdivfit, + temporary, root, outerSize, bands, rows, one, Fin.mk.injEq] + have he : ExecWithin w countCode x y ((6 * L + 16) * 2 ^ L + 14) := by + apply (hc.seq (hr.seq hy)).mono + have hsqrt : Nat.sqrt (K L m) ≤ 2 ^ L := (Nat.sqrt_le_self _).trans hK + nlinarith + have hyp : HeaderPost L m N y := headerPost_reframe hx (by + intro i hi + apply he.regs_eq + have hp : ∀ i ∈ headerRegisters, countCode.Preserves i := by + simp only [countCode, countMasks, countBody, populationCode, rootCode, rootBody, + bandCount, Code.Preserves, Op.dest, Op.bin] + decide + exact hp i hi) + refine ⟨y, he, ⟨hyp, ?_, ?_, ?_⟩, hrmem.trans hcmem⟩ + · simpa [y, temporary, bands, root] using hroot0 + · simpa [y, temporary, bands, square, K0] using hsquare + · simp [y] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandPopulation.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandPopulation.lean new file mode 100644 index 000000000..7e6315663 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandPopulation.lean @@ -0,0 +1,114 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandProgram +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.MaskTable + +/-! +# Charged native binary-mask population scans + +## Main results + +* `BandPreparation.exec_population`: the actual remainder/division loop computes the + population in `6L+5` instructions, preserves memory and changes only its four local registers. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.MatrixMultiplication.Schonhage.MaskTable + +/-- Popcount is a native loop, with a fit derived for each arithmetic operation from a +bound on its original mask and depth. No memory cell is touched. -/ +theorem exec_population {w : ℕ} (L a : ℕ) (x : Data 96) + (hl : x.regs levels = L) (ha : x.regs mask = a) + (ho : x.regs one = 1) (ht : x.regs two = 2) (hfit : Fits w (a + L + 2)) : + ∃ y, ExecWithin w populationCode x y (6 * L + 5) ∧ + y.regs population = MaskTable.population L a ∧ y.mem = x.mem ∧ + (∀ i, i ≠ residue → i ≠ population → i ≠ counter → i ≠ digit → + y.regs i = x.regs i) := by + have hzero : Fits w 0 := hfit.of_le (by omega) + let start := ((x.set residue a).set population 0).set counter 0 + let I (y : Data 96) : Prop := + y.mem = x.mem ∧ y.regs levels = L ∧ y.regs one = 1 ∧ y.regs two = 2 ∧ + y.regs counter ≤ L ∧ y.regs residue ≤ a ∧ + y.regs population + MaskTable.population (L - y.regs counter) (y.regs residue) = + MaskTable.population L a + have hi : I start := by + simp [I, start, hl, ho, ht, counter, residue, population, levels, one, two] + have hb (y : Data 96) (hy : I y) (hc : (Cond.lt counter levels).Holds y) : + ∃ z, ExecWithin w (ram[ + digit ← residue % two; population ← population + digit; + residue ← residue / two; counter ← counter + one ]) y z 4 ∧ + I z ∧ L - z.regs counter < L - y.regs counter := by + obtain ⟨hm, hL, h1, h2, hj, hr, hp⟩ := hy + have hjL : y.regs counter < L := by simpa only [Cond.holds_lt, hL] using hc + have hrec : MaskTable.population (L - y.regs counter) (y.regs residue) = + y.regs residue % 2 + + MaskTable.population (L - (y.regs counter + 1)) (y.regs residue / 2) := by + rw [show L - y.regs counter = (L - (y.regs counter + 1)) + 1 by omega, + population_succ] + have hpop := population_le L a + have hfpop : Fits w (y.regs population + y.regs residue % 2) := + hfit.of_le (by omega) + have hfmod : Fits w (y.regs residue % 2) := hfit.of_le (by omega) + have hfdiv : Fits w (y.regs residue / 2) := + hfit.of_le ((Nat.div_le_self _ _).trans (by omega)) + have hfnext : Fits w (y.regs counter + 1) := hfit.of_le (by omega) + let z := (((y.set digit (y.regs residue % 2)).set population + (y.regs population + y.regs residue % 2)).set residue (y.regs residue / 2)).set + counter (y.regs counter + 1) + have he : ExecWithin w (ram[ + digit ← residue % two; population ← population + digit; + residue ← residue / two; counter ← counter + one ]) y z 4 := by + ram_eval [z, hfpop, hfmod, hfdiv, hfnext, h1, h2, + digit, residue, population, counter, one, two, Fin.mk.injEq] + refine ⟨z, he, ?_, ?_⟩ + · have hsum : (y.regs population + y.regs residue % 2) + + MaskTable.population (L - (y.regs counter + 1)) (y.regs residue / 2) = + MaskTable.population L a := by omega + simpa +decide [I, z, levels, one, two, residue, population, counter, digit, + Function.update_apply] using + (show y.mem = x.mem ∧ y.regs levels = L ∧ y.regs one = 1 ∧ y.regs two = 2 ∧ + y.regs counter + 1 ≤ L ∧ y.regs residue / 2 ≤ a ∧ + (y.regs population + y.regs residue % 2) + + MaskTable.population (L - (y.regs counter + 1)) (y.regs residue / 2) = + MaskTable.population L a from + ⟨hm, hL, h1, h2, by omega, (Nat.div_le_self _ _).trans hr, hsum⟩) + · simp only [z, Data.set_regs, Function.update_self] + omega + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I + (fun y => L - y.regs counter) hb hi + have hj : y.regs counter = L := by + simp only [Cond.holds_lt, hy.2.1] at hc + have := hy.2.2.2.2.1 + omega + have hwhole : ExecWithin w populationCode x y (6 * L + 5) := by + have hs : ExecWithin (B := .standard) w (.op (.copy residue mask)) + x (x.set residue a) 1 := by + simpa only [ha] using (ExecWithin.copy (B := .standard) (w := w) + (x := x) (d := residue) (s := mask)) + have hwhole := hs.seq ((ExecWithin.const (d := population) hzero).seq + ((ExecWithin.const (d := counter) hzero).seq he)) + apply hwhole.mono + simp only [start, Data.set_regs, Function.update_self, Nat.sub_zero] + omega + refine ⟨y, hwhole, ?_, hy.1, ?_⟩ + · have h := hy.2.2.2.2.2.2 + simpa only [hj, Nat.sub_self, population_zero, Nat.add_zero] using h + · intro i hr hp hc hd + apply hwhole.regs_eq + have hframe : ∀ i : Fin 96, i ≠ residue → i ≠ population → i ≠ counter → i ≠ digit → + populationCode.Preserves i := by decide + exact hframe i hr hp hc hd + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandPrepare.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandPrepare.lean new file mode 100644 index 000000000..1364489d7 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandPrepare.lean @@ -0,0 +1,182 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandRead + +/-! +# Complete native padded-band preparation + +## Main results + +* `BandPreparation.exec_prepareBand`: initialize and fill every band-array cell by actual + native digit decoding, inverse-table lookup and guarded signed matrix loads. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +-- The static frame checks reduce finite programs over 96 registers and their parameter sets. +set_option maxRecDepth 4096 + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Every prepared-band cell is below the protected original input. -/ +theorem band_input_le_snapshot (L m N : ℕ) : 2 + 7 ^ L ≤ snapshotBase L m N := by + have h := workspace_le_snapshot L m N + unfold Encoding.scratchBase Encoding.outputBase at h + have := Nat.zero_le (10 ^ L) + omega + +private theorem split_preserves_parameters : + ∀ i ∈ parameterRegisters, splitCode.Preserves i := by + simp only [parameterRegisters, headerRegisters, splitCode, splitHead, splitBody, + Code.Preserves, Op.dest, Op.bin] + decide + +private theorem prepare_preserves_parameters (side : Bool) : + ∀ i ∈ parameterRegisters, (prepareBody side).Preserves i := by + cases side <;> simp +decide only [] + +/-- The entire input band is materialized with charged initialization, digit/index work, +padding and loop control. No cell outside the depth header and the band input is changed. -/ +theorem exec_prepareBand {w L m N : ℕ} (side : Bool) (β : ℕ) (x : Data 96) + (hx : BandParameters L m N x) (hβ : β < numBands L m N) (hb : x.regs band = β) + (htable : ∀ t < 2 ^ L, x.mem (inverseBase L m N + t) = MaskTable.lookup L m t) + (matrix : ℕ → ℕ → ℤ) + (hmat : ∀ r < N, ∀ k < D m, + x.mem (matrixAddress L m N side r k) = encode (matrix r k)) + (hfit : Fits w (wordBudget L m N)) : + ∃ y, ExecWithin w (prepareBand side) x y ((13 * L + 39) * 7 ^ L + 5) ∧ + BandParameters L m N y ∧ y.regs band = β ∧ y.mem 1 = L ∧ + (∀ a < 7 ^ L, y.mem (2 + a) = encode (bandValue L m N β side matrix a)) ∧ + (∀ b, ¬(1 ≤ b ∧ b < 2 + 7 ^ L) → y.mem b = x.mem b) := by + have hL : Fits w L := (local_fits hfit).of_le (by + nlinarith only [Nat.zero_le L, Nat.zero_le N, Nat.zero_le (N * D m), + Nat.zero_le (2 ^ L), Nat.zero_le (10 ^ L)]) + let start := ((x.set address 1).write 1 L).set index 0 + have hs : ExecWithin w prepareHead x start 3 := by + ram_eval [prepareHead, start, hx.fixed.levels, hL, + small_fits hfit (by decide : 1 ≤ 256), address, index, levels, Fin.mk.injEq] + have hsp : BandParameters L m N start := parameters_reframe hx (by + intro i hi + apply hs.regs_eq + have hp : ∀ i ∈ parameterRegisters, prepareHead.Preserves i := by + simp only [parameterRegisters, headerRegisters, prepareHead, Code.Preserves, Op.dest] + decide + exact hp i hi) + let I (y : Data 96) : Prop := BandParameters L m N y ∧ y.regs band = β ∧ + y.regs index ≤ 7 ^ L ∧ y.mem 1 = L ∧ + (∀ a < y.regs index, y.mem (2 + a) = encode (bandValue L m N β side matrix a)) ∧ + (∀ b, ¬(1 ≤ b ∧ b < 2 + 7 ^ L) → y.mem b = x.mem b) + have hstart : I start := by + refine ⟨hsp, ?_, ?_, ?_, ?_, ?_⟩ + · simpa [start, band, address, index] using hb + · simp [start] + · simp [start] + · simp [start] + · intro b hbb + have hne : b ≠ 1 := by + have := Nat.one_le_pow L 7 (by decide) + omega + simp [start, hne] + have hbody (y : Data 96) (hy : I y) (hc : (Cond.lt index cells).Holds y) : + ∃ z, ExecWithin w (prepareBody side) y z (13 * L + 37) ∧ I z ∧ + 7 ^ L - z.regs index < 7 ^ L - y.regs index := by + let i := y.regs index + have hi : i < 7 ^ L := by simpa only [Cond.holds_lt, hy.1.cells] using hc + have hsplit : SplitParameters L y := ⟨hy.1.fixed.levels, hy.1.fixed.one, + hy.1.fixed.two, hy.1.fixed.three, hy.1.fixed.four, hy.1.fixed.seven⟩ + obtain ⟨s, hes, _, hmask, houter, hinner, hsmem⟩ := + exec_split L i y hsplit rfl (split_fits hfit hi) + have hsp : BandParameters L m N s := parameters_reframe hy.1 + (fun j hj => hes.regs_eq (split_preserves_parameters j hj)) + have hsband : s.regs band = β := (hes.regs_eq (by + simp only [splitCode, splitHead, splitBody, Code.Preserves, Op.dest, Op.bin] + decide)).trans hy.2.1 + have hsindex : s.regs index = i := hes.regs_eq (by + simp only [splitCode, splitHead, splitBody, Code.Preserves, Op.dest, Op.bin] + decide) + have htable' (t : ℕ) (ht : t < 2 ^ L) : + s.mem (inverseBase L m N + t) = MaskTable.lookup L m t := by + rw [hsmem, hy.2.2.2.2.2 _ (by + have hlow := band_input_le_snapshot L m N + unfold inverseBase + omega)] + exact htable t ht + have hmat' (r : ℕ) (hr : r < N) (k : ℕ) (hk : k < D m) : + s.mem (matrixAddress L m N side r k) = encode (matrix r k) := by + rw [hsmem, hy.2.2.2.2.2 _ (by + have hlow := band_input_le_snapshot L m N + unfold matrixAddress + omega)] + exact hmat r hr k hk + obtain ⟨t, het, hv, htmem⟩ := exec_readCell side ⟨i, hi⟩ β s hsp hβ hsband + hmask houter hinner htable' matrix hmat' hfit + have htindex : t.regs index = i := (het.regs_eq (by + cases side <;> simp only [readCell, Code.Preserves, Op.dest, Op.bin] <;> decide)).trans + hsindex + have htone : t.regs one = 1 := (het.regs_eq (by + cases side <;> simp only [readCell, Code.Preserves, Op.dest, Op.bin] <;> decide)).trans + hsp.fixed.one + have httwo : t.regs two = 2 := (het.regs_eq (by + cases side <;> simp only [readCell, Code.Preserves, Op.dest, Op.bin] <;> decide)).trans + hsp.fixed.two + have hadd : Fits w (i + 2) := + ((memory_fits hfit).of_le (snapshot_le_memoryEnd L m N)).of_le (by + have hlow := band_input_le_snapshot L m N + omega) + have hinc : Fits w (i + 1) := hadd.of_le (by omega) + let z := ((t.set address (i + 2)).write (i + 2) + (encode (bandValue L m N β side matrix i))).set index (i + 1) + have hez : ExecWithin w (ram[ + address ← index + two; *address ← value; index ← index + one ]) t z 3 := by + ram_eval [z, htindex, htone, httwo, hv, hadd, hinc, + address, index, two, one, value, Fin.mk.injEq] + have he : ExecWithin w (prepareBody side) y z (13 * L + 37) := by + simpa only [prepareBody, Nat.add_assoc] using hes.seq (het.seq hez) + have hzindex : z.regs index = i + 1 := by simp [z] + have hzmem : z.mem = Function.update y.mem (i + 2) + (encode (bandValue L m N β side matrix i)) := by + simp only [z, Data.set_mem, Data.write_mem, htmem, hsmem] + refine ⟨z, he, ?_, ?_⟩ + · refine ⟨parameters_reframe hy.1 (fun j hj => + he.regs_eq (prepare_preserves_parameters side j hj)), ?_, ?_, ?_, ?_, ?_⟩ + · exact (he.regs_eq (by + cases side <;> + simp +decide only [])).trans hy.2.1 + · rw [hzindex]; omega + · rw [hzmem, Function.update_of_ne (by omega : 1 ≠ i + 2)] + exact hy.2.2.2.1 + · intro a ha + rw [hzindex] at ha + rw [hzmem] + by_cases hai : a = i + · subst a; rw [Nat.add_comm 2 i, Function.update_self] + · rw [Function.update_of_ne (by omega : 2 + a ≠ i + 2)] + exact hy.2.2.2.2.1 a (by omega) + · intro b hbb + rw [hzmem, Function.update_of_ne (by omega : b ≠ i + 2)] + exact hy.2.2.2.2.2 b hbb + · rw [hzindex]; change 7 ^ L - (i + 1) < 7 ^ L - i; omega + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I + (fun y => 7 ^ L - y.regs index) hbody hstart + have hindex : y.regs index = 7 ^ L := by + simp only [Cond.holds_lt, hy.1.cells] at hc + have := hy.2.2.1 + omega + refine ⟨y, ?_, hy.1, hy.2.1, hy.2.2.2.1, ?_, hy.2.2.2.2.2⟩ + · apply (hs.seq he).mono + have hzero : start.regs index = 0 := by simp [start] + rw [hzero, Nat.sub_zero] + nlinarith only [Nat.zero_le (7 ^ L)] + · simpa only [hindex] using hy.2.2.2.2.1 + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandProgram.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandProgram.lean new file mode 100644 index 000000000..127663f3c --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandProgram.lean @@ -0,0 +1,322 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Flat +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.ThinProduct +public import Complexity.Computability.WordRAM.Power + +/-! +# Native preparation and encoding of shared matrix bands + +The input is `L :: m :: N :: X ++ Y`, with both matrices in row-major order and their +signed entries encoded by `Signed.encode`. The native length header occupies address zero. +The program snapshots the complete initialized input before using the encoder's low workspace, +computes its subset tables, prepares and encodes each band once, and restores the input. + +The dense inverse table uses zero for an unselected mask and rank plus one for a selected mask. +The forward table lists the first `K0²` masks of cardinality `m` in increasing numeric order. +The shared output catalogue consists of all left bands followed by all right bands, with +`10^L` consecutive cells per band. A tile refers to its two band entries in this catalogue. + +`LeftVar.equiv` and `RightVar.equiv` identify the mathematical alphabets with +`Fin 3 ⊕ (Fin 2 × Fin 2)`, and `Term.equiv` identifies terms with `Option (Fin 3 × Fin 3)`. +The existing native `Encoding.leftDigits`, `Encoding.rightDigits` and `Encoding.termDigits` +also fix the executable flat `Fin 7`/`Fin 10` order: the nine product terms are row-major, +with digit `3*i+j`, and `P0` has digit 9. This preparation retains that numbering unchanged. + +## Main definitions + +* `BandPreparation.program`: fixed native code, including the actual shared encoders. +* `BandPreparation.input`: flat header and matrix input for `State.init`. +* `BandPreparation.catalogueAddress`: the location of a band's encoding leaf. + +## References + +Selective adaptation of the band preparation in [AlmanWilliams2026, Section 2.4.1] and +Anthropic's formal-math revision `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`, +[band-array preparation](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Programs/Sec2/Theorem5/Encode/BandArray.lean) +and [all-band encoding](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Programs/Sec2/Theorem5/Encode/AllBands.lean). +The mask scans, dense inverse table, native control and memory layout here are new; +the source's Light machine and execution semantics are not used. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Original initialized input prefix, including the native length header and three parameters. -/ +def inputEnd (m N : ℕ) : ℕ := 4 + 2 * (N * D m) + +/-- The encoder's low workspace and the original input are both below the snapshot. -/ +def snapshotBase (L m N : ℕ) : ℕ := inputEnd m N + Encoding.scratchBase L + 10 ^ L + +/-- Dense mask-to-rank inverse table. -/ +def inverseBase (L m N : ℕ) : ℕ := snapshotBase L m N + inputEnd m N + +/-- Forward selected-mask table; `2^L` cells are reserved. -/ +def tableBase (L m N : ℕ) : ℕ := inverseBase L m N + 2 ^ L + +/-- Shared encoding catalogue, after the snapshot and both tables. -/ +def catalogueBase (L m N : ℕ) : ℕ := tableBase L m N + 2 ^ L + +/-- Every left band, then every right band, occupies one block of `10^L` words. -/ +def catalogueAddress (L m N : ℕ) (rightSide : Bool) (β leaf : ℕ) : ℕ := + catalogueBase L m N + ((if rightSide then numBands L m N else 0) + β) * 10 ^ L + leaf + +/-- One past all allocated memory. -/ +def memoryEnd (L m N : ℕ) : ℕ := catalogueBase L m N + 2 * numBands L m N * 10 ^ L + +/-- Explicit input-parameter bound used to derive every unsigned arithmetic/address fit. +The signed magnitude condition is separate. This is an ordinary numerical inequality, +without quantification over executions or intermediate states. -/ +def wordBudget (L m N : ℕ) : ℕ := + 256 * (L + N + N * D m + 2 ^ L + 10 ^ L + 2 * numBands L m N * 10 ^ L + 1) + +/-- Row-major signed matrix input; no table, power or encoded band is supplied by the caller. -/ +def input (L m N : ℕ) (X : Matrix (Fin N) (Fin (D m)) ℤ) + (Y : Matrix (Fin (D m)) (Fin N) ℤ) : List ℕ := + [L, m, N] ++ + (List.finRange N).flatMap (fun i => List.ofFn fun k => Signed.encode (X i k)) ++ + (List.finRange (D m)).flatMap (fun k => List.ofFn fun j => Signed.encode (Y k j)) + +/-- Low registers are reserved for the existing encoder and copy routines. Persistent +parameters and preparation counters occupy registers 32 through 84 of a fixed bank of 96. -/ +abbrev levels : Fin 96 := 32 +@[inherit_doc levels] abbrev copySrc : Fin 96 := 0 +@[inherit_doc levels] abbrev copyDst : Fin 96 := 1 +@[inherit_doc levels] abbrev copyLen : Fin 96 := 2 +@[inherit_doc levels] abbrev inner : Fin 96 := 33 +@[inherit_doc levels] abbrev rows : Fin 96 := 34 +@[inherit_doc levels] abbrev columns : Fin 96 := 35 +@[inherit_doc levels] abbrev outerSize : Fin 96 := 36 +@[inherit_doc levels] abbrev masks : Fin 96 := 37 +@[inherit_doc levels] abbrev cells : Fin 96 := 38 +@[inherit_doc levels] abbrev leaves : Fin 96 := 39 +@[inherit_doc levels] abbrev prefixSize : Fin 96 := 40 +@[inherit_doc levels] abbrev snapshot : Fin 96 := 41 +@[inherit_doc levels] abbrev inverse : Fin 96 := 42 +@[inherit_doc levels] abbrev table : Fin 96 := 43 +@[inherit_doc levels] abbrev catalogue : Fin 96 := 44 +@[inherit_doc levels] abbrev bands : Fin 96 := 45 +@[inherit_doc levels] abbrev count : Fin 96 := 46 +@[inherit_doc levels] abbrev root : Fin 96 := 47 +@[inherit_doc levels] abbrev square : Fin 96 := 48 +@[inherit_doc levels] abbrev temporary : Fin 96 := 49 +@[inherit_doc levels] abbrev one : Fin 96 := 50 +@[inherit_doc levels] abbrev two : Fin 96 := 51 +@[inherit_doc levels] abbrev three : Fin 96 := 52 +@[inherit_doc levels] abbrev four : Fin 96 := 53 +@[inherit_doc levels] abbrev seven : Fin 96 := 54 +@[inherit_doc levels] abbrev ten : Fin 96 := 55 +@[inherit_doc levels] abbrev counter : Fin 96 := 56 +@[inherit_doc levels] abbrev mask : Fin 96 := 57 +@[inherit_doc levels] abbrev population : Fin 96 := 58 +@[inherit_doc levels] abbrev residue : Fin 96 := 59 +@[inherit_doc levels] abbrev digit : Fin 96 := 60 +@[inherit_doc levels] abbrev limit : Fin 96 := 61 +@[inherit_doc levels] abbrev rank : Fin 96 := 62 +@[inherit_doc levels] abbrev address : Fin 96 := 63 +@[inherit_doc levels] abbrev band : Fin 96 := 64 +@[inherit_doc levels] abbrev index : Fin 96 := 66 +@[inherit_doc levels] abbrev maskAcc : Fin 96 := 67 +@[inherit_doc levels] abbrev outerAcc : Fin 96 := 68 +@[inherit_doc levels] abbrev innerAcc : Fin 96 := 69 +@[inherit_doc levels] abbrev maskPower : Fin 96 := 70 +@[inherit_doc levels] abbrev outerPower : Fin 96 := 71 +@[inherit_doc levels] abbrev innerPower : Fin 96 := 72 +@[inherit_doc levels] abbrev level : Fin 96 := 73 +@[inherit_doc levels] abbrev block : Fin 96 := 75 +@[inherit_doc levels] abbrev row : Fin 96 := 77 +@[inherit_doc levels] abbrev source : Fin 96 := 79 +@[inherit_doc levels] abbrev value : Fin 96 := 80 +@[inherit_doc levels] abbrev zero : Fin 96 := 84 + +/-- Register embedding for the unchanged shared encoders. -/ +def encoderRegisters (i : Fin 32) : Fin 96 := ⟨i.val, by omega⟩ + +/-- Register embedding for the unchanged seven-register copy program. -/ +def copyRegisters (i : Fin 7) : Fin 96 := ⟨i.val, by omega⟩ + +/-- Embed the five-register native power routine in the caller's chosen scalar registers. -/ +def powerRegisters (v b n : Fin 96) : Fin 5 → Fin 96 := ![v, counter, b, n, one] + +/-- The actual native power routine, with register renaming only. -/ +def powerCode (v b n : Fin 96) : Code .standard 96 := + Power.program.rename (powerRegisters v b n) + +/-- Popcount is computed by an explicit fixed-depth loop of divisions and remainders. -/ +def populationCode : Code .standard 96 := ram[ + residue ← mask; population ← #0; counter ← #0; + while counter < levels do [ + digit ← residue % two; population ← population + digit; + residue ← residue / two; counter ← counter + one ] ] + +/-- Load the three actual header parameters and the small constants. -/ +def headerLoads : Code .standard 96 := ram[ + address ← #1; levels ← *address; address ← #2; inner ← *address; + address ← #3; rows ← *address; + zero ← #0; one ← #1; two ← #2; three ← #3; four ← #4; seven ← #7; ten ← #10 ] + +/-- Compute all five powers with actual renamed native routines. -/ +def headerPowers : Code .standard 96 := + .seq (powerCode columns four inner) (.seq (ram[ temporary ← levels - inner ]) + (.seq (powerCode outerSize three temporary) (.seq (powerCode masks two levels) + (.seq (powerCode cells seven levels) (powerCode leaves ten levels))))) + +/-- Compute concrete snapshot, table and catalogue addresses. -/ +def headerLayout : Code .standard 96 := ram[ + prefixSize ← rows * columns; prefixSize ← prefixSize * two; + prefixSize ← prefixSize + four; + snapshot ← leaves * two; snapshot ← snapshot + cells; snapshot ← snapshot + two; + snapshot ← snapshot + prefixSize; + inverse ← snapshot + prefixSize; table ← inverse + masks; catalogue ← table + masks ] + +/-- Compute the three header parameters, powers and addresses with charged native code. -/ +def header : Code .standard 96 := .seq headerLoads (.seq headerPowers headerLayout) + +/-- Save every original input word, including the actual length header. -/ +def saveInput : Code .standard 96 := ram[ + copySrc ← #0; copyDst ← snapshot; copyLen ← prefixSize; + ⟪ArrayCopy.program.rename copyRegisters⟫ ] + +/-- Visit one mask and update the number of accepted masks. -/ +def countBody : Code .standard 96 := + .seq populationCode (ram[ + if population = inner then [ count ← count + one ] else [ count ← count ]; + mask ← mask + one ]) + +/-- Count every size-`m` mask. -/ +def countMasks : Code .standard 96 := + .seq (ram[ mask ← #0; count ← #0 ]) (.loop (.lt mask masks) countBody) + +/-- One explicitly squared trial in the integer square-root search. -/ +def rootBody : Code .standard 96 := ram[ + root ← root + one; temporary ← root + one; square ← temporary * temporary ] + +/-- Compute the integer square root by squared comparisons. No square-root instruction +is added to the native basis. -/ +def rootCode : Code .standard 96 := + .seq (ram[ + root ← #0; square ← #1; limit ← count + one + ]) (.seq (.loop (.lt square limit) rootBody) (ram[ square ← root * root ])) + +/-- Compute the ceiling quotient giving the number of bands. -/ +def bandCount : Code .standard 96 := ram[ + temporary ← root * outerSize; + bands ← rows + temporary; bands ← bands - one; bands ← bands / temporary ] + +/-- Complete scalar band parameters from native mask counting and root computation. -/ +def countCode : Code .standard 96 := .seq countMasks (.seq rootCode bandCount) + +/-- Store the two table entries belonging to the current mask and advance both counters. -/ +def tableWrite : Code .standard 96 := ram[ + address ← inverse + mask; *address ← zero; + if population = inner then [ + if rank < square then [ + temporary ← rank + one; *address ← temporary; + address ← table + rank; *address ← mask ] else [ rank ← rank ]; + rank ← rank + one ] else [ rank ← rank ]; + mask ← mask + one ] + +/-- One complete table iteration, including the actual population scan. -/ +def tableBody : Code .standard 96 := .seq populationCode tableWrite + +/-- Materialize both tables, explicitly initializing every inverse-table cell. -/ +def tablesCode : Code .standard 96 := + .seq (ram[ mask ← #0; rank ← #0 ]) (.loop (.lt mask masks) tableBody) + +/-- Initialize the three digit accumulators and their positional weights. -/ +def splitHead : Code .standard 96 := ram[ + residue ← index; maskAcc ← #0; outerAcc ← #0; innerAcc ← #0; + maskPower ← #1; outerPower ← #1; innerPower ← #1; level ← #0 ] + +/-- One base-seven digit, with only ordinary native arithmetic and control. -/ +def splitBody : Code .standard 96 := ram[ + digit ← residue % seven; residue ← residue / seven; + if digit < three then [ + temporary ← digit * outerPower; outerAcc ← outerAcc + temporary; + outerPower ← outerPower * three ] else [ + digit ← digit - three; temporary ← digit * innerPower; + innerAcc ← innerAcc + temporary; innerPower ← innerPower * four; + maskAcc ← maskAcc + maskPower ]; + maskPower ← maskPower * two; level ← level + one ] + +/-- Decode a base-seven string into its inner mask and compressed base-three/base-four +offsets, in increasing level order. Every division, remainder, product and increment is native. -/ +def splitCode : Code .standard 96 := .seq splitHead (.loop (.lt level levels) splitBody) + +/-- Read a real matrix cell only after checking the padded row/column against `N`. +In particular, the potentially oversized row-major product is inside the guard. -/ +def readCell (rightSide : Bool) : Code .standard 96 := + .seq (ram[ + address ← inverse + maskAcc; rank ← *address; value ← #0 ]) + (.branch (.lt zero rank) + (.seq (ram[ rank ← rank - one ]) + (.seq (if rightSide then ram[ block ← rank % root ] else ram[ block ← rank / root ]) + (ram[ + temporary ← band * root; block ← temporary + block; + row ← block * outerSize; row ← row + outerAcc; + if row < rows then [ + ⟪if rightSide then ram[ + address ← innerAcc * rows; address ← address + row; + temporary ← rows * columns; address ← address + temporary ] + else ram[ address ← row * columns; address ← address + innerAcc ]⟫; + address ← address + four; address ← snapshot + address; value ← *address + ] else [ value ← value ] ]))) + (ram[ value ← value ])) + +/-- Actual header and counter initialization for each band. -/ +def prepareHead : Code .standard 96 := ram[ + address ← #1; *address ← levels; index ← #0 ] + +/-- Decode, guard, load and store one band-array entry. -/ +def prepareBody (rightSide : Bool) : Code .standard 96 := ram[ + ⟪splitCode⟫; ⟪readCell rightSide⟫; + address ← index + two; *address ← value; index ← index + one ] + +/-- Prepare every entry of one low-workspace band array, including all zero padding. -/ +def prepareBand (rightSide : Bool) : Code .standard 96 := + .seq prepareHead (.loop (.lt index cells) (prepareBody rightSide)) + +/-- Set up the actual encoder-output copy, including its catalogue address arithmetic. -/ +def collectHead (rightSide : Bool) : Code .standard 96 := ram[ + copySrc ← cells + two; + ⟪if rightSide then ram[ temporary ← bands + band ] else ram[ temporary ← band ]⟫; + temporary ← temporary * leaves; temporary ← catalogue + temporary; + copyDst ← temporary; copyLen ← leaves ] + +/-- Copy the actual encoder output to this band's shared catalogue entry. -/ +def collectBand (rightSide : Bool) : Code .standard 96 := + .seq (collectHead rightSide) (ArrayCopy.program.rename copyRegisters) + +/-- Prepare, encode and collect one shared band, then advance its counter. -/ +def bandBody (rightSide : Bool) : Code .standard 96 := + .seq (.seq (prepareBand rightSide) + (.seq ((Encoding.program rightSide).rename encoderRegisters) (collectBand rightSide))) + (ram[ band ← band + one ]) + +/-- Each row/column band calls the actual unchanged finite native encoder exactly once. -/ +def encodeBands (rightSide : Bool) : Code .standard 96 := + .seq (ram[ band ← #0 ]) (.loop (.lt band bands) (bandBody rightSide)) + +/-- Restore the complete original initialized input after all low-workspace calls. -/ +def restoreInput : Code .standard 96 := ram[ + copySrc ← snapshot; copyDst ← #0; copyLen ← prefixSize; + ⟪ArrayCopy.program.rename copyRegisters⟫ ] + +/-- One fixed finite native program for the entire band-preparation component. Halt returns +`catalogue`, the base address of the shared encodings; it is appended by `Code.program`. -/ +def program : Code .standard 96 := + .seq header (.seq saveInput (.seq countCode (.seq tablesCode + (.seq (encodeBands false) (.seq (encodeBands true) restoreInput))))) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandRead.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandRead.lean new file mode 100644 index 000000000..cc88b44cc --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandRead.lean @@ -0,0 +1,196 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandParameters +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandMemory +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandSplit + +/-! +# Guarded native matrix reads for band arrays + +## Main definitions + +* `BandPreparation.matrixAddress`: real row-major matrix addresses in the protected input. + +## Main results + +* `BandPreparation.exec_readCell`: the actual guarded read returns exactly one encoded + band value; padded rows are rejected before row-major multiplication. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open BandDigits + +/-- Offset in the concatenated original matrix inputs. The right matrix has `D` rows and +`N` columns, while its band specification labels a column by `r`. -/ +def matrixOffset (m N : ℕ) (rightSide : Bool) (r k : ℕ) : ℕ := + if rightSide then k * N + r + N * D m else r * D m + k + +/-- Address of an original signed input word in its protected snapshot. -/ +def matrixAddress (L m N : ℕ) (rightSide : Bool) (r k : ℕ) : ℕ := + snapshotBase L m N + (matrixOffset m N rightSide r k + 4) + +private theorem matrix_indices_lt {m N r k : ℕ} (hr : r < N) (hk : k < D m) : + r * D m + k < N * D m ∧ k * N + r < N * D m := by + have hleft := Nat.mul_le_mul_right (D m) (Nat.succ_le_of_lt hr) + have hright := Nat.mul_le_mul_right N (Nat.succ_le_of_lt hk) + rw [Nat.succ_mul] at hleft hright + rw [Nat.mul_comm (D m) N] at hright + constructor <;> omega + +/-- A real matrix cell lies strictly inside its protected snapshot. This uses the +real row/column guard, before constructing its flat address. -/ +theorem matrixAddress_lt_inverse {L m N r k : ℕ} (side : Bool) + (hr : r < N) (hk : k < D m) : + matrixAddress L m N side r k < inverseBase L m N := by + obtain ⟨hl, hr'⟩ := matrix_indices_lt hr hk + unfold matrixAddress matrixOffset inverseBase inputEnd + cases side <;> simp only [Bool.false_eq_true, ↓reduceIte] <;> omega + +/-- The complete guarded load uses at most twenty-four actual target instructions. +All intermediate products are bounded using the original parameters and the real `r + simp only [Bool.false_eq_true, ↓reduceIte] at hbfit hblockfit hrowfit hrfit hr <;> + simp only [Bool.false_eq_true, ↓reduceIte] at hleftMul hleftAdd hrightAdd + <;> simp only [Bool.false_eq_true, ↓reduceIte] at hrightOff hleftFour hrightFour + <;> simp only [Bool.false_eq_true, ↓reduceIte] at hleftAddress hrightAddress hcell <;> + ram_eval [readCell, y, v, u, b, r, hx.inverse, hx.snapshot, hx.root, hx.outerSize, + hx.columns, hx.fixed.zero, hx.fixed.one, hx.fixed.four, hx.fixed.rows, + hm, ho, hi, hb, hlookup, haddr, hqpos, hr, + small_fits hfit (by decide : 0 ≤ 256), hqfit, hbfit, hβfit, hblockfit, + hrowfit, hrfit, hnd, hleftMul, hleftAdd, hrightMul, hrightAdd, + hrightOff, hleftFour, hrightFour, hleftAddress, hrightAddress, + hcell, matrixAddress, matrixOffset, + inverse, snapshot, root, outerSize, columns, zero, one, four, rows, + maskAcc, outerAcc, innerAcc, band, address, rank, value, block, temporary, + row, Fin.mk.injEq] + · change y.regs value = encode (if q = 0 then 0 else + if r < N then matrix r p.inner else 0) + rw [ite_eq_right hq, ite_eq_left hr] + cases side <;> simp [y] + · cases side <;> rfl + · let y := v.set value 0 + dsimp only [b, r] at hbfit hblockfit hrowfit hrfit hr + refine ⟨y, ?_, ?_, rfl⟩ + · cases side <;> + simp only [Bool.false_eq_true, ↓reduceIte] at hbfit hblockfit hrowfit hrfit hr <;> + ram_eval [readCell, y, v, u, b, r, hx.inverse, hx.root, hx.outerSize, + hx.fixed.zero, hx.fixed.one, hx.fixed.rows, hm, ho, hi, hb, + hlookup, haddr, hqpos, hr, small_fits hfit (by decide : 0 ≤ 256), + hqfit, hbfit, hβfit, hblockfit, hrowfit, hrfit, + inverse, root, outerSize, zero, one, rows, maskAcc, outerAcc, band, + address, rank, value, block, temporary, row, Fin.mk.injEq] + · change y.regs value = encode (if q = 0 then 0 else + if r < N then matrix r p.inner else 0) + rw [ite_eq_right hq, ite_eq_right hr] + simp only [y, Data.set_regs, Function.update_self] + rfl + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandRoot.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandRoot.lean new file mode 100644 index 000000000..48ca52819 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandRoot.lean @@ -0,0 +1,101 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandProgram + +/-! +# Native integer square root for band parameters + +## Main results + +* `BandPreparation.exec_root`: successive squared comparisons compute the ordinary natural + square root within `5 * sqrt k + 6` instructions, with every trial square proved to fit. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +open Cslib.WordRAM + +/-- The root loop uses only increment, multiplication and unsigned comparison. The bound on +trial squares is derived from the original count, including the first failing trial. -/ +theorem exec_root {w : ℕ} (k : ℕ) (x : Data 96) + (hk : x.regs count = k) (ho : x.regs one = 1) (hfit : Fits w (4 * k + 4)) : + ∃ y, ExecWithin w rootCode x y (5 * Nat.sqrt k + 6) ∧ + y.regs root = Nat.sqrt k ∧ y.regs square = Nat.sqrt k * Nat.sqrt k ∧ + y.mem = x.mem ∧ + (∀ i, i ≠ root → i ≠ square → i ≠ temporary → i ≠ limit → y.regs i = x.regs i) := by + have h0 : Fits w 0 := hfit.of_le (by omega) + have h1 : Fits w 1 := hfit.of_le (by omega) + have hnext : Fits w (k + 1) := hfit.of_le (by omega) + let start := ((x.set root 0).set square 1).set limit (k + 1) + have hs : ExecWithin w (ram[ root ← #0; square ← #1; limit ← count + one ]) + x start 3 := by + ram_eval [start, hk, ho, h0, h1, hnext, root, square, limit, count, one, Fin.mk.injEq] + let I (y : Data 96) : Prop := + y.mem = x.mem ∧ y.regs one = 1 ∧ y.regs limit = k + 1 ∧ + y.regs root ≤ Nat.sqrt k ∧ + y.regs square = (y.regs root + 1) * (y.regs root + 1) + have hi : I start := by simp [I, start, ho, root, square, limit, one] + have hb (y : Data 96) (hy : I y) (hc : (Cond.lt square limit).Holds y) : + ∃ z, ExecWithin w rootBody y z 3 ∧ I z ∧ + Nat.sqrt k - z.regs root < Nat.sqrt k - y.regs root := by + obtain ⟨hm, h1, hlimit, hr, hsq⟩ := hy + have htrial : (y.regs root + 1) * (y.regs root + 1) ≤ k := by + simpa only [Cond.holds_lt, hlimit, hsq, Nat.lt_add_one_iff] using hc + have hrnext : y.regs root + 1 ≤ Nat.sqrt k := Nat.le_sqrt.mpr htrial + have hlarge : (y.regs root + 2) * (y.regs root + 2) ≤ 4 * k + 4 := by + nlinarith only [htrial] + have hrfit : Fits w (y.regs root + 1) := + hfit.of_le (by have := Nat.sqrt_le_self k; omega) + have htfit : Fits w (y.regs root + 2) := + hfit.of_le (by have := Nat.sqrt_le_self k; omega) + have hsfit : Fits w ((y.regs root + 2) * (y.regs root + 2)) := hfit.of_le hlarge + let z := ((y.set root (y.regs root + 1)).set temporary (y.regs root + 2)).set + square ((y.regs root + 2) * (y.regs root + 2)) + have he : ExecWithin w rootBody y z 3 := by + ram_eval [rootBody, z, h1, hrfit, htfit, hsfit, + root, temporary, square, one, Fin.mk.injEq, Nat.add_assoc] + refine ⟨z, he, ?_, ?_⟩ + · simpa +decide [I, z, root, temporary, square, one, limit, Function.update_apply, + Nat.add_assoc] using + (show y.mem = x.mem ∧ y.regs one = 1 ∧ y.regs limit = k + 1 ∧ + y.regs root + 1 ≤ Nat.sqrt k ∧ + (y.regs root + 2) * (y.regs root + 2) = + (y.regs root + 1 + 1) * (y.regs root + 1 + 1) from + ⟨hm, h1, hlimit, hrnext, by ring⟩) + · have hz : z.regs root = y.regs root + 1 := by simp [z, root, square, temporary] + rw [hz] + omega + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I + (fun y => Nat.sqrt k - y.regs root) hb hi + have hr : y.regs root = Nat.sqrt k := by + have hstop : k < (y.regs root + 1) * (y.regs root + 1) := by + simp only [Cond.holds_lt, hy.2.2.1, hy.2.2.2.2] at hc + omega + exact Nat.eq_sqrt.mpr ⟨(Nat.le_sqrt.mp hy.2.2.2.1), hstop⟩ + let z := y.set square (Nat.sqrt k * Nat.sqrt k) + have ht : ExecWithin w (ram[ square ← root * root ]) y z 1 := by + ram_eval [z, hr, hfit.of_le (Nat.sqrt_le k |>.trans (by omega)), square, root] + have hwhole : ExecWithin w rootCode x z (5 * Nat.sqrt k + 6) := by + apply (hs.seq (he.seq ht)).mono + have hsroot : start.regs root = 0 := by simp [start, root, square, limit] + rw [hsroot, Nat.sub_zero] + omega + refine ⟨z, hwhole, ?_, ?_, hy.1, ?_⟩ + · simpa [z, square, root] using hr + · simp [z] + · intro i hr hs ht hl + apply hwhole.regs_eq + have hf : ∀ i : Fin 96, i ≠ root → i ≠ square → i ≠ temporary → i ≠ limit → + rootCode.Preserves i := by decide + exact hf i hr hs ht hl + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandSplit.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandSplit.lean new file mode 100644 index 000000000..30463b689 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandSplit.lean @@ -0,0 +1,247 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandMeaning + +/-! +# Charged native splitting of tensor-string digits + +## Main results + +* `BandPreparation.exec_split`: the actual base-seven digit loop computes the binary mask + and both compressed offsets with every native arithmetic operation charged. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open BandDigits + +/-- Fixed scalar inputs to a digit loop, without execution or fit assumptions. -/ +structure SplitParameters (L : ℕ) (x : Data 96) : Prop where + /-- Original depth. -/ + levels : x.regs levels = L + /-- Constant one. -/ + one : x.regs one = 1 + /-- Constant two. -/ + two : x.regs two = 2 + /-- Constant three. -/ + three : x.regs three = 3 + /-- Constant four. -/ + four : x.regs four = 4 + /-- Constant seven. -/ + seven : x.regs seven = 7 + +private def splitParameterRegisters : Finset (Fin 96) := {levels, one, two, three, four, seven} + +private theorem SplitParameters.of_eq {L : ℕ} {x y : Data 96} (hx : SplitParameters L x) + (h : ∀ i ∈ splitParameterRegisters, y.regs i = x.regs i) : SplitParameters L y := + ⟨(h _ (by decide)).trans hx.levels, (h _ (by decide)).trans hx.one, + (h _ (by decide)).trans hx.two, (h _ (by decide)).trans hx.three, + (h _ (by decide)).trans hx.four, (h _ (by decide)).trans hx.seven⟩ + +/-- Remaining-digit equations and elementary counter bounds during the native scan. -/ +structure SplitProgress (L a : ℕ) (x : Data 96) : Prop where + /-- Parameters are retained. -/ + fixed : SplitParameters L x + /-- Number of consumed levels. -/ + level_le : x.regs level ≤ L + /-- Division only decreases the residual index. -/ + residue_le : x.regs residue ≤ a + /-- Binary place weight. -/ + mask_power : x.regs maskPower = 2 ^ x.regs level + /-- At most one outer place is consumed per level. -/ + outer_power : x.regs outerPower ≤ 3 ^ x.regs level + /-- At most one inner place is consumed per level. -/ + inner_power : x.regs innerPower ≤ 4 ^ x.regs level + /-- Partial binary value is below its next place. -/ + mask_acc : x.regs maskAcc < x.regs maskPower + /-- Partial base-three value is below its next place. -/ + outer_acc : x.regs outerAcc < x.regs outerPower + /-- Partial base-four value is below its next place. -/ + inner_acc : x.regs innerAcc < x.regs innerPower + /-- The binary accumulator and remaining digits give the complete mask. -/ + mask_value : x.regs maskAcc + x.regs maskPower * + (parts (L - x.regs level) (x.regs residue)).mask = (parts L a).mask + /-- The outer accumulator and remaining digits give the complete outer offset. -/ + outer_value : x.regs outerAcc + x.regs outerPower * + (parts (L - x.regs level) (x.regs residue)).outer = (parts L a).outer + /-- The inner accumulator and remaining digits give the complete inner offset. -/ + inner_value : x.regs innerAcc + x.regs innerPower * + (parts (L - x.regs level) (x.regs residue)).inner = (parts L a).inner + +/-- One native digit iteration, using only bounds derived from the original index/depth. -/ +theorem exec_splitBody {w : ℕ} (L a : ℕ) (x : Data 96) (hx : SplitProgress L a x) + (hc : (Cond.lt level levels).Holds x) (hfit : Fits w (a + L + 16 * 10 ^ L + 16)) : + ∃ y, ExecWithin w splitBody x y 11 ∧ SplitProgress L a y ∧ y.mem = x.mem ∧ + L - y.regs level < L - x.regs level := by + have hj : x.regs level < L := by simpa only [Cond.holds_lt, hx.fixed.levels] using hc + have hd : x.regs residue % 7 < 7 := Nat.mod_lt _ (by decide) + have hpow (b : ℕ) (hb : b ≤ 10) : b ^ x.regs level ≤ 10 ^ L := + (Nat.pow_le_pow_left hb _).trans (Nat.pow_le_pow_right (by decide) hx.level_le) + have hmp : x.regs maskPower ≤ 10 ^ L := by rw [hx.mask_power]; exact hpow 2 (by decide) + have hop : x.regs outerPower ≤ 10 ^ L := hx.outer_power.trans (hpow 3 (by decide)) + have hip : x.regs innerPower ≤ 10 ^ L := hx.inner_power.trans (hpow 4 (by decide)) + have hdFit : Fits w (x.regs residue % 7) := hfit.of_le (by omega) + have hrFit : Fits w (x.regs residue / 7) := + hfit.of_le ((Nat.div_le_self _ _).trans (by have := hx.residue_le; omega)) + have hmFit : Fits w (x.regs maskPower * 2) := hfit.of_le (by omega) + have hjFit : Fits w (x.regs level + 1) := hfit.of_le (by omega) + have hrest : L - x.regs level = L - (x.regs level + 1) + 1 := by omega + by_cases houter : x.regs residue % 7 < 3 + · have hmul : Fits w (x.regs residue % 7 * x.regs outerPower) := + hfit.of_le (by nlinarith) + have hadd : Fits w (x.regs outerAcc + x.regs residue % 7 * x.regs outerPower) := + hfit.of_le (by have := hx.outer_acc; nlinarith) + have hpower : Fits w (x.regs outerPower * 3) := hfit.of_le (by omega) + let y := (((((((x.set digit (x.regs residue % 7)).set residue (x.regs residue / 7)).set + temporary (x.regs residue % 7 * x.regs outerPower)).set outerAcc + (x.regs outerAcc + x.regs residue % 7 * x.regs outerPower)).set outerPower + (x.regs outerPower * 3)).set maskPower (x.regs maskPower * 2)).set + level (x.regs level + 1)) + have he : ExecWithin w splitBody x y 11 := by + ram_eval [splitBody, y, hx.fixed.one, hx.fixed.two, hx.fixed.three, hx.fixed.seven, + hdFit, hrFit, hmFit, hjFit, hmul, hadd, hpower, houter, + digit, residue, temporary, outerAcc, outerPower, maskPower, level, one, two, + three, seven, Fin.mk.injEq] + have hfixed : SplitParameters L y := hx.fixed.of_eq (by + intro i hi + apply he.regs_eq + have hf : ∀ i ∈ splitParameterRegisters, splitBody.Preserves i := by + simp only [splitBody, Code.Preserves, Op.dest, Op.bin] + decide + exact hf i hi) + have hmval := hx.mask_value + have hoval := hx.outer_value + have hival := hx.inner_value + rw [hrest, parts, ite_eq_left houter] at hmval hoval hival + refine ⟨y, he, ?_, rfl, ?_⟩ + · constructor + · exact hfixed + all_goals simp +decide only [y, Data.set_regs, Function.update_apply, ↓reduceIte] + · omega + · exact (Nat.div_le_self _ _).trans hx.residue_le + · simpa only [pow_succ] using congrArg (fun n => n * 2) hx.mask_power + · rw [pow_succ] + exact Nat.mul_le_mul_right 3 hx.outer_power + · exact hx.inner_power.trans (Nat.pow_le_pow_right (by decide) (by omega)) + · have := hx.mask_acc; omega + · have := hx.outer_acc; nlinarith only [this, houter] + · exact hx.inner_acc + · nlinarith only [hmval] + · nlinarith only [hoval] + · exact hival + · have hyl : y.regs level = x.regs level + 1 := by simp [y] + rw [hyl] + omega + · have hsub : Fits w (x.regs residue % 7 - 3) := hfit.of_le (by omega) + have hsuble : x.regs residue % 7 - 3 < 4 := by omega + have hmul : Fits w ((x.regs residue % 7 - 3) * x.regs innerPower) := + hfit.of_le (by nlinarith only [hip, hsuble]) + have hadd : Fits w (x.regs innerAcc + (x.regs residue % 7 - 3) * x.regs innerPower) := + hfit.of_le (by nlinarith only [hx.inner_acc, hip, hsuble]) + have hpower : Fits w (x.regs innerPower * 4) := hfit.of_le (by omega) + have hmask : Fits w (x.regs maskAcc + x.regs maskPower) := + hfit.of_le (by have := hx.mask_acc; omega) + let y := (((((((((x.set digit (x.regs residue % 7)).set residue (x.regs residue / 7)).set + digit (x.regs residue % 7 - 3)).set temporary + ((x.regs residue % 7 - 3) * x.regs innerPower)).set innerAcc + (x.regs innerAcc + (x.regs residue % 7 - 3) * x.regs innerPower)).set innerPower + (x.regs innerPower * 4)).set maskAcc (x.regs maskAcc + x.regs maskPower)).set + maskPower (x.regs maskPower * 2)).set level (x.regs level + 1)) + have he : ExecWithin w splitBody x y 11 := by + ram_eval [splitBody, y, hx.fixed.one, hx.fixed.two, hx.fixed.three, hx.fixed.four, + hx.fixed.seven, hdFit, hrFit, hmFit, hjFit, hsub, hmul, hadd, hpower, hmask, houter, + digit, residue, temporary, innerAcc, innerPower, maskAcc, maskPower, level, + one, two, three, four, seven, Fin.mk.injEq] + have hfixed : SplitParameters L y := hx.fixed.of_eq (by + intro i hi + apply he.regs_eq + have hf : ∀ i ∈ splitParameterRegisters, splitBody.Preserves i := by + simp only [splitBody, Code.Preserves, Op.dest, Op.bin] + decide + exact hf i hi) + have hmval := hx.mask_value + have hoval := hx.outer_value + have hival := hx.inner_value + rw [hrest, parts, ite_eq_right houter] at hmval hoval hival + refine ⟨y, he, ?_, rfl, ?_⟩ + · constructor + · exact hfixed + all_goals simp +decide only [y, Data.set_regs, Function.update_apply, ↓reduceIte] + · omega + · exact (Nat.div_le_self _ _).trans hx.residue_le + · simpa only [pow_succ] using congrArg (fun n => n * 2) hx.mask_power + · exact hx.outer_power.trans (Nat.pow_le_pow_right (by decide) (by omega)) + · rw [pow_succ] + exact Nat.mul_le_mul_right 4 hx.inner_power + · have := hx.mask_acc; omega + · exact hx.outer_acc + · nlinarith only [hx.inner_acc, hsuble] + · nlinarith only [hmval] + · exact hoval + · nlinarith only [hival] + · have hyl : y.regs level = x.regs level + 1 := by simp [y] + rw [hyl] + omega + +/-- All digits are split by the fixed native loop. Scratch need not start at zero: every +accumulator and place weight is explicitly initialized and its initialization is charged. -/ +theorem exec_split {w : ℕ} (L a : ℕ) (x : Data 96) (hx : SplitParameters L x) + (ha : x.regs index = a) (hfit : Fits w (a + L + 16 * 10 ^ L + 16)) : + ∃ y, ExecWithin w splitCode x y (13 * L + 10) ∧ SplitParameters L y ∧ + y.regs maskAcc = (parts L a).mask ∧ y.regs outerAcc = (parts L a).outer ∧ + y.regs innerAcc = (parts L a).inner ∧ y.mem = x.mem := by + have h0 : Fits w 0 := hfit.of_le (Nat.zero_le _) + have h1 : Fits w 1 := hfit.of_le (by omega) + let start := ((((((((x.set residue a).set maskAcc 0).set outerAcc 0).set innerAcc 0).set + maskPower 1).set outerPower 1).set innerPower 1).set level 0) + have hs : ExecWithin w splitHead x start 8 := by + ram_eval [splitHead, start, ha, h0, h1, residue, index, maskAcc, outerAcc, innerAcc, + maskPower, outerPower, innerPower, level, Fin.mk.injEq] + have hp : SplitParameters L start := hx.of_eq (by + intro i hi + apply hs.regs_eq + have hf : ∀ i ∈ splitParameterRegisters, splitHead.Preserves i := by + simp only [splitHead, Code.Preserves, Op.dest] + decide + exact hf i hi) + have hi : SplitProgress L a start := by + constructor + · exact hp + all_goals simp [start, residue, maskAcc, outerAcc, innerAcc, + maskPower, outerPower, innerPower, level] + let I (y : Data 96) : Prop := SplitProgress L a y ∧ y.mem = x.mem + have hb (y : Data 96) (hy : I y) (hc : (Cond.lt level levels).Holds y) : + ∃ z, ExecWithin w splitBody y z 11 ∧ I z ∧ L - z.regs level < L - y.regs level := by + obtain ⟨z, he, hz, hm, ht⟩ := exec_splitBody L a y hy.1 hc hfit + exact ⟨z, he, ⟨hz, hm.trans hy.2⟩, ht⟩ + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I + (fun y => L - y.regs level) hb ⟨hi, rfl⟩ + have hlevel : y.regs level = L := by + simp only [Cond.holds_lt, hy.1.fixed.levels] at hc + have := hy.1.level_le + omega + have hwhole : ExecWithin w splitCode x y (13 * L + 10) := by + apply (hs.seq he).mono + have hs0 : start.regs level = 0 := by simp [start] + rw [hs0, Nat.sub_zero] + omega + refine ⟨y, hwhole, hy.1.fixed, ?_, ?_, ?_, hy.2⟩ + · simpa only [hlevel, Nat.sub_self, parts, Nat.mul_zero, Nat.add_zero] + using hy.1.mask_value + · simpa only [hlevel, Nat.sub_self, parts, Nat.mul_zero, Nat.add_zero] + using hy.1.outer_value + · simpa only [hlevel, Nat.sub_self, parts, Nat.mul_zero, Nat.add_zero] + using hy.1.inner_value + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandTables.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandTables.lean new file mode 100644 index 000000000..41bf96539 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandTables.lean @@ -0,0 +1,280 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPopulation + +/-! +# Charged materialization of the subset tables + +## Main definitions + +* `BandPreparation.tableStep`: the exact memory writes made by one table iteration. + +## Main results + +* `BandPreparation.exec_tableWrite`: actual inverse/forward table stores and counter updates. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open MaskTable + +/-- The inverse word is initialized even for an unselected mask. Selected masks also write +one forward-table cell. This is only a mathematical description of the two native stores. -/ +def tableStep (L m inv tab t : ℕ) (μ : ℕ → ℕ) : ℕ → ℕ := + let μ' := Function.update μ (inv + t) (lookup L m t) + if lookup L m t = 0 then μ' else Function.update μ' (tab + prefixCount L m t) t + +/-- The part of both tables computed after visiting `t` masks. The outside frame permits +arbitrary caller memory, including future query inputs and scratch regions. -/ +structure TablesPrefix (L m inv tab t : ℕ) (initial current : ℕ → ℕ) : Prop where + /-- Every visited inverse word has been initialized. -/ + inverse : ∀ a < t, current (inv + a) = lookup L m a + /-- Every selected visited mask has been placed at its prefix rank. -/ + forward : ∀ a < t, lookup L m a ≠ 0 → current (tab + prefixCount L m a) = a + /-- Only the two explicitly reserved table blocks may change. -/ + frame : ∀ a, ¬(inv ≤ a ∧ a < inv + 2 ^ L) → ¬(tab ≤ a ∧ a < tab + 2 ^ L) → + current a = initial a + +/-- Empty scan prefix, with no initialized-memory assumption. -/ +theorem TablesPrefix.zero (L m inv tab : ℕ) (μ : ℕ → ℕ) : + TablesPrefix L m inv tab 0 μ μ := + ⟨fun _ h => (Nat.not_lt_zero _ h).elim, + fun _ h => (Nat.not_lt_zero _ h).elim, fun _ _ _ => rfl⟩ + +/-- The actual two stores extend the initialized table prefix by one mask. -/ +theorem TablesPrefix.step {L m inv tab t : ℕ} {μ μ' : ℕ → ℕ} + (h : TablesPrefix L m inv tab t μ μ') (ht : t < 2 ^ L) + (hsep : inv + 2 ^ L ≤ tab) : + TablesPrefix L m inv tab (t + 1) μ (tableStep L m inv tab t μ') := by + have hr : prefixCount L m t < 2 ^ L := (prefixCount_le L m t).trans_lt ht + constructor + · intro a ha + by_cases hat : a = t + · subst a + unfold tableStep + split + · exact Function.update_self _ _ _ + · rw [Function.update_of_ne (by omega), Function.update_self] + · have haold : a < t := by omega + unfold tableStep + split + · rw [Function.update_of_ne (by omega)] + exact h.inverse a haold + · rw [Function.update_of_ne (by omega), Function.update_of_ne (by omega)] + exact h.inverse a haold + · intro a ha hselected + by_cases hat : a = t + · subst a + simp only [tableStep, hselected, ↓reduceIte, Function.update_self] + · have haold : a < t := by omega + have hpop := (lookup_ne_zero_iff L m a).mp hselected |>.2.1 + have hless := prefixCount_lt hpop haold + unfold tableStep + split + · rw [Function.update_of_ne (by omega)] + exact h.forward a haold hselected + · rw [Function.update_of_ne (by omega), Function.update_of_ne (by omega)] + exact h.forward a haold hselected + · intro a hi hf + have hia : a ≠ inv + t := by omega + have hta : a ≠ tab + prefixCount L m t := by omega + unfold tableStep + split + · rw [Function.update_of_ne hia] + exact h.frame a hi hf + · rw [Function.update_of_ne hta, Function.update_of_ne hia] + exact h.frame a hi hf + +/-- Execute the stores after the population loop. The code neither searches a supplied +table nor receives a table-validity certificate. -/ +theorem exec_tableWrite {w : ℕ} (L m inv tab t : ℕ) (x : Data 96) + (ht : t < 2 ^ L) (hinv : x.regs inverse = inv) (htab : x.regs table = tab) + (hmask : x.regs mask = t) (hrank : x.regs rank = prefixCount L m t) + (hpop : x.regs population = MaskTable.population L t) (hm : x.regs inner = m) + (hsq : x.regs square = K0 L m * K0 L m) + (h0 : x.regs zero = 0) (h1 : x.regs one = 1) + (hfit : Fits w (inv + tab + 2 ^ L + 4)) : + ∃ y, ExecWithin w tableWrite x y 10 ∧ y.mem = tableStep L m inv tab t x.mem ∧ + y.regs mask = t + 1 ∧ y.regs rank = prefixCount L m (t + 1) ∧ + (∀ i, i ≠ address → i ≠ temporary → i ≠ mask → i ≠ rank → y.regs i = x.regs i) := by + have hbound := prefixCount_le L m t + have ha : Fits w (inv + t) := hfit.of_le (by omega) + have htaddr : Fits w (tab + prefixCount L m t) := hfit.of_le (by omega) + have hr : Fits w (prefixCount L m t + 1) := hfit.of_le (by omega) + have ht1 : Fits w (t + 1) := hfit.of_le (by omega) + by_cases hp : MaskTable.population L t = m + · by_cases hselected : prefixCount L m t < K0 L m * K0 L m + · let y := (((((((x.set address (inv + t)).write (inv + t) 0).set temporary + (prefixCount L m t + 1)).write (inv + t) (prefixCount L m t + 1)).set + address (tab + prefixCount L m t)).write (tab + prefixCount L m t) t).set + rank (prefixCount L m t + 1)).set mask (t + 1) + refine ⟨y, ?_, ?_, ?_, ?_, ?_⟩ + · ram_eval [tableWrite, y, hinv, htab, hmask, hrank, hpop, hm, hsq, h0, h1, + ha, htaddr, hr, ht1, hp, hselected, + inverse, table, mask, rank, population, inner, square, zero, one, address, + temporary, Fin.mk.injEq] + · simp [y, tableStep, lookup, ht, hp, hselected, Data.write, Data.set, + Function.update_idem] + · simp [y, Data.set, address, temporary, mask, rank] + · simp [y, Data.set, address, temporary, mask, rank, prefixCount_succ, hp] + · intro i ha ht hm hr + simp only [y, Data.set_regs, Data.write_regs, + Function.update_of_ne ha, Function.update_of_ne ht, + Function.update_of_ne hm, Function.update_of_ne hr] + · let y := ((((x.set address (inv + t)).write (inv + t) 0).set rank + (x.regs rank)).set rank (prefixCount L m t + 1)).set mask (t + 1) + refine ⟨y, ?_, ?_, ?_, ?_, ?_⟩ + · ram_eval [tableWrite, y, hinv, htab, hmask, hrank, hpop, hm, hsq, h0, h1, + ha, ht1, hr, hp, hselected, inverse, table, mask, rank, population, inner, + square, zero, one, address, temporary, Fin.mk.injEq] + · simp [y, tableStep, lookup, ht, hp, hselected, Data.write, Data.set] + · simp [y, Data.set, address, mask, rank] + · simp [y, Data.set, address, mask, rank, prefixCount_succ, hp] + · intro i ha _ hm hr + simp only [y, Data.set_regs, Data.write_regs, Function.update_of_ne ha, + Function.update_of_ne hm, Function.update_of_ne hr] + · let y := (((x.set address (inv + t)).write (inv + t) 0).set rank + (x.regs rank)).set mask (t + 1) + refine ⟨y, ?_, ?_, ?_, ?_, ?_⟩ + · ram_eval [tableWrite, y, hinv, htab, hmask, hrank, hpop, hm, hsq, h0, h1, + ha, ht1, hp, inverse, mask, rank, population, inner, zero, one, + address, Fin.mk.injEq] + · simp [y, tableStep, lookup, ht, hp, Data.write, Data.set] + · simp [y, Data.set, address, mask, rank] + · simp [y, Data.set, address, mask, rank, prefixCount_succ, hp, hrank] + · intro i ha _ hm hr + simp only [y, Data.set_regs, Data.write_regs, Function.update_of_ne ha, + Function.update_of_ne hm, Function.update_of_ne hr] + +/-- Persistent scalar parameters read by the table loops. These are register contents only; +there is no execution, fit or cost assumption in this postcondition. -/ +structure TableParameters (L m inv tab : ℕ) (x : Data 96) : Prop where + /-- Original depth. -/ + levels : x.regs levels = L + /-- Original inner cardinality. -/ + inner : x.regs inner = m + /-- Computed complete binary-mask range. -/ + masks : x.regs masks = 2 ^ L + /-- Inverse-table base. -/ + inverse : x.regs inverse = inv + /-- Forward-table base. -/ + table : x.regs table = tab + /-- Computed number of selected masks. -/ + square : x.regs square = K0 L m * K0 L m + /-- Constant zero. -/ + zero : x.regs zero = 0 + /-- Constant one. -/ + one : x.regs one = 1 + /-- Constant two. -/ + two : x.regs two = 2 + +private def tableParameterRegisters : Finset (Fin 96) := + {levels, inner, masks, inverse, table, square, zero, one, two} + +private theorem TableParameters.of_eq {L m inv tab : ℕ} {x y : Data 96} + (hx : TableParameters L m inv tab x) + (h : ∀ i ∈ tableParameterRegisters, y.regs i = x.regs i) : + TableParameters L m inv tab y := by + exact ⟨(h _ (by decide)).trans hx.levels, (h _ (by decide)).trans hx.inner, + (h _ (by decide)).trans hx.masks, (h _ (by decide)).trans hx.inverse, + (h _ (by decide)).trans hx.table, (h _ (by decide)).trans hx.square, + (h _ (by decide)).trans hx.zero, (h _ (by decide)).trans hx.one, + (h _ (by decide)).trans hx.two⟩ + +/-- The complete native scan computes both tables, with all inner population scans and +outer controls charged. Outside memory may contain arbitrary caller data. -/ +theorem exec_tables {w : ℕ} (L m inv tab : ℕ) (x : Data 96) + (hx : TableParameters L m inv tab x) (hsep : inv + 2 ^ L ≤ tab) + (hfit : Fits w (inv + tab + 2 ^ L + L + 4)) : + ∃ y, ExecWithin w tablesCode x y ((6 * L + 17) * 2 ^ L + 4) ∧ + TableParameters L m inv tab y ∧ + (∀ a < 2 ^ L, y.mem (inv + a) = lookup L m a) ∧ + (∀ gh : Fin (K0 L m) × Fin (K0 L m), + y.mem (tab + (pairRank gh).val) = (selectedMask gh).val) ∧ + (∀ a, ¬(inv ≤ a ∧ a < inv + 2 ^ L) → ¬(tab ≤ a ∧ a < tab + 2 ^ L) → + y.mem a = x.mem a) := by + have h0 : Fits w 0 := hfit.of_le (Nat.zero_le _) + let start := (x.set mask 0).set rank 0 + have hs : ExecWithin (B := .standard) w (ram[ mask ← #0; rank ← #0 ]) x start 2 := by + ram_eval [start, h0, mask, rank, Fin.mk.injEq] + have hstart : TableParameters L m inv tab start := hx.of_eq (by + intro i hi + have hf : ∀ i ∈ tableParameterRegisters, i ≠ mask ∧ i ≠ rank := by decide + simp only [start, Data.set_regs, Function.update_of_ne (hf i hi).1, + Function.update_of_ne (hf i hi).2]) + let I (y : Data 96) : Prop := + TableParameters L m inv tab y ∧ y.regs mask ≤ 2 ^ L ∧ + y.regs rank = prefixCount L m (y.regs mask) ∧ + TablesPrefix L m inv tab (y.regs mask) x.mem y.mem + have hi : I start := by + refine ⟨hstart, ?_, ?_, ?_⟩ + · simp [start, mask, rank] + · simp [start, mask, rank, prefixCount_zero] + · simpa [start, mask, rank] using TablesPrefix.zero L m inv tab x.mem + have hb (y : Data 96) (hy : I y) (hc : (Cond.lt mask masks).Holds y) : + ∃ z, ExecWithin w tableBody y z (6 * L + 15) ∧ I z ∧ + 2 ^ L - z.regs mask < 2 ^ L - y.regs mask := by + obtain ⟨hp, ht, hrank, hmem⟩ := hy + have htlt : y.regs mask < 2 ^ L := by simpa only [Cond.holds_lt, hp.masks] using hc + obtain ⟨u, hu, hpop, humem, huf⟩ := exec_population L (y.regs mask) y hp.levels rfl + hp.one hp.two (hfit.of_le (by omega)) + have hup : TableParameters L m inv tab u := hp.of_eq (by + intro i hi + have hf : ∀ i ∈ tableParameterRegisters, + i ≠ residue ∧ i ≠ population ∧ i ≠ counter ∧ i ≠ digit := by decide + exact huf i (hf i hi).1 (hf i hi).2.1 (hf i hi).2.2.1 (hf i hi).2.2.2) + have hut : u.regs mask = y.regs mask := + huf mask (by decide) (by decide) (by decide) (by decide) + have hur : u.regs rank = prefixCount L m (y.regs mask) := + (huf rank (by decide) (by decide) (by decide) (by decide)).trans hrank + obtain ⟨z, hz, hzmem, hzt, hzr, _⟩ := exec_tableWrite L m inv tab (y.regs mask) u + htlt hup.inverse hup.table hut hur hpop hup.inner hup.square hup.zero hup.one + (hfit.of_le (by omega)) + have he : ExecWithin w tableBody y z (6 * L + 15) := hu.seq hz + have hzp : TableParameters L m inv tab z := hp.of_eq (by + intro i hi + apply he.regs_eq + have hf : ∀ i ∈ tableParameterRegisters, tableBody.Preserves i := by + simp only [tableBody, tableWrite, populationCode, Code.Preserves, Op.dest, Op.bin] + decide + exact hf i hi) + refine ⟨z, he, ⟨hzp, ?_, ?_, ?_⟩, ?_⟩ + · rw [hzt] + omega + · simpa only [hzt] using hzr + · rw [hzt, hzmem, humem] + exact hmem.step htlt hsep + · rw [hzt] + omega + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I + (fun y => 2 ^ L - y.regs mask) hb hi + have ht : y.regs mask = 2 ^ L := by + simp only [Cond.holds_lt, hy.1.masks] at hc + have := hy.2.1 + omega + have hwhole : ExecWithin w tablesCode x y ((6 * L + 17) * 2 ^ L + 4) := by + apply (hs.seq he).mono + have hs0 : start.regs mask = 0 := by simp [start, mask, rank] + rw [hs0, Nat.sub_zero] + nlinarith + refine ⟨y, hwhole, hy.1, ?_, ?_, hy.2.2.2.frame⟩ + · intro a ha + exact hy.2.2.2.inverse a (ht ▸ ha) + · intro gh + have hlookup : lookup L m (selectedMask gh).val ≠ 0 := by rw [lookup_selectedMask]; omega + have hb : (selectedMask gh).val < y.regs mask := by rw [ht]; exact (selectedMask gh).isLt + have h := hy.2.2.2.forward (selectedMask gh).val hb hlookup + simpa only [prefixCount_selectedMask] using h + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandThin.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandThin.lean new file mode 100644 index 000000000..eb7c62200 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/BandThin.lean @@ -0,0 +1,76 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandFlat + +/-! +# Thin-regime native instruction bound + +## Main results + +* `BandPreparation.runs_thin`: actual initialized native execution, including the halt, + within `81920*(L+1)*N²/(4^m)^(1/18)` at `L=19m`, `N≥(4^m)^18`. +* `BandPreparation.width_exists`: original magnitude and parameter inequalities are jointly + satisfiable for every finite input bound, including positive thin parameters. + +The result concerns shared-band preparation and encoding only. Pruned queries, products and +the whole ordinary 3SUM endpoint remain separate obligations. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- All charged preparation and encoder instructions, including the final halt, satisfy +the finite thin-regime saving. Both the coefficient and polynomial exponent are fixed. -/ +theorem runs_thin {w m N : ℕ} (hw : 0 < w) (hN : D m ^ 18 ≤ N) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (A : ℤ) (hA : 0 ≤ A) (hX : ∀ i k, |X i k| ≤ A) (hY : ∀ k j, |Y k j| ≤ A) + (hmag : (7 ^ (19 * m) : ℕ) * A < (2 : ℤ) ^ (w - 1)) + (hfit : Fits (w + 2) (wordBudget (19 * m) m N)) : + ∃ n, Executes (w + 2) (program.program catalogue) + (State.init (input (19 * m) m N X Y)) (catalogueBase (19 * m) m N) n ∧ + (n : ℝ) ≤ 81920 * ((19 * m + 1 : ℕ) : ℝ) * (N : ℝ) ^ 2 / + (D m : ℝ) ^ (1 / 18 : ℝ) := by + obtain ⟨_, _, n, hn, _, he, _⟩ := runs hw (show m ≤ 19 * m by omega) + X Y A hA hX hY hmag hfit + refine ⟨n + 1, he, ?_⟩ + have hpos : 1 ≤ timeBound (19 * m) m N := by + apply Nat.succ_le_of_lt + unfold timeBound + positivity + have hc : n + 1 ≤ 2 * timeBound (19 * m) m N := by omega + have hcR : ((n + 1 : ℕ) : ℝ) ≤ 2 * (timeBound (19 * m) m N : ℝ) := by + exact_mod_cast hc + have ht := thin_timeBound_div m N hN + calc + ((n + 1 : ℕ) : ℝ) ≤ 2 * (timeBound (19 * m) m N : ℝ) := hcR + _ ≤ 2 * (40960 * ((19 * m + 1 : ℕ) : ℝ) * (N : ℝ) ^ 2 / + (D m : ℝ) ^ (1 / 18 : ℝ)) := mul_le_mul_of_nonneg_left ht (by positivity) + _ = _ := by ring + +/-- A concrete, deliberately generous width witnesses simultaneous signed-magnitude and +unsigned-address fits. This proves nonvacuity without enumerating a thin matrix. -/ +theorem width_exists (L m N : ℕ) (A : ℤ) (hA : 0 ≤ A) : + ∃ w, 0 < w ∧ (7 ^ L : ℕ) * A < (2 : ℤ) ^ (w - 1) ∧ + Fits (w + 2) (wordBudget L m N) := by + let q := wordBudget L m N + 7 ^ L * A.natAbs + 3 + refine ⟨q + 1, by omega, ?_, ?_⟩ + · have hnat : 7 ^ L * A.natAbs < 2 ^ q := + (Nat.lt_two_pow_self (n := 7 ^ L * A.natAbs)).trans_le + (Nat.pow_le_pow_right (by decide) (by dsimp [q]; omega)) + have hz : ((7 ^ L * A.natAbs : ℕ) : ℤ) < (2 : ℤ) ^ q := by exact_mod_cast hnat + simpa only [Nat.add_sub_cancel, Nat.cast_mul, Int.natCast_natAbs, + abs_of_nonneg hA] using hz + · exact (fits_self (wordBudget L m N)).mono (by dsimp [q]; omega) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandPreparation diff --git a/Complexity/Computability/WordRAM/Power.lean b/Complexity/Computability/WordRAM/Power.lean new file mode 100644 index 000000000..a49be46a7 --- /dev/null +++ b/Complexity/Computability/WordRAM/Power.lean @@ -0,0 +1,93 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Notation + +/-! +# Native natural powers + +## Main definitions + +* `Power.program`: repeated native multiplication, with base and exponent in registers. + +## Main results + +* `Power.exec`: `b^n` within `4n+5` instructions, with input-derived intermediate fits. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.Power + +/-- Result accumulator, initially one. -/ +abbrev value : Fin 5 := 0 +/-- Number of completed multiplications. -/ +abbrev counter : Fin 5 := 1 +/-- Input base. -/ +abbrev base : Fin 5 := 2 +/-- Input exponent. -/ +abbrev exponent : Fin 5 := 3 +/-- Constant one, initialized by the routine. -/ +abbrev one : Fin 5 := 4 + +/-- One multiplication and one counter increment. -/ +def body : Code .standard 5 := ram[ value ← value * base; counter ← counter + one ] + +/-- A fixed program for natural exponentiation by repeated multiplication. -/ +def program : Code .standard 5 := + .seq (ram[ value ← #1; counter ← #0; one ← #1 ]) (.loop (.lt counter exponent) body) + +/-- The routine is uniform in its arguments and preserves memory and both input registers. +Every multiplication fit follows from the final power and exponent bound. -/ +theorem exec {w : ℕ} (b n : ℕ) (hb : 1 ≤ b) (x : Data 5) + (hx : x.regs base = b) (hn : x.regs exponent = n) (hfit : Fits w (b ^ n + n + 2)) : + ∃ y, ExecWithin w program x y (4 * n + 5) ∧ y.regs value = b ^ n ∧ + y.regs counter = n ∧ y.regs one = 1 ∧ y.mem = x.mem ∧ + y.regs base = b ∧ y.regs exponent = n := by + have h0 : Fits w 0 := hfit.of_le (Nat.zero_le _) + have h1 : Fits w 1 := hfit.of_le (by omega) + let start := ((x.set value 1).set counter 0).set one 1 + have hs : ExecWithin (B := .standard) w + (ram[ value ← #1; counter ← #0; one ← #1 ]) x start 3 := by + ram_eval [start, h0, h1, value, counter, one, Fin.mk.injEq] + let I (y : Data 5) : Prop := + y.regs base = b ∧ y.regs exponent = n ∧ y.regs one = 1 ∧ y.regs counter ≤ n ∧ + y.regs value = b ^ y.regs counter ∧ y.mem = x.mem + have hi : I start := by simp [I, start, hx, hn, base, exponent, one, counter, value] + have hbody (y : Data 5) (hy : I y) (hc : (Cond.lt counter exponent).Holds y) : + ∃ z, ExecWithin w body y z 2 ∧ I z ∧ n - z.regs counter < n - y.regs counter := by + obtain ⟨hbv, hnv, ho, hc_le, hv, hm⟩ := hy + have hc_lt : y.regs counter < n := by simpa only [Cond.holds_lt, hnv] using hc + have hpn : b ^ (y.regs counter + 1) ≤ b ^ n := Nat.pow_le_pow_right hb (by omega) + have hp : Fits w (b ^ (y.regs counter + 1)) := hfit.of_le (hpn.trans (by omega)) + have hj : Fits w (y.regs counter + 1) := hfit.of_le (by omega) + let z := (y.set value (b ^ (y.regs counter + 1))).set counter (y.regs counter + 1) + have he : ExecWithin w body y z 2 := by + ram_eval [body, z, hbv, ho, hv, ← pow_succ, hp, hj, + value, counter, base, one, Fin.mk.injEq] + refine ⟨z, he, ?_, ?_⟩ + · simpa +decide [I, z, base, exponent, one, counter, value, Function.update_apply] using + (show y.regs base = b ∧ y.regs exponent = n ∧ y.regs one = 1 ∧ + y.regs counter + 1 ≤ n ∧ y.mem = x.mem from ⟨hbv, hnv, ho, by omega, hm⟩) + · simp only [z, Data.set_regs, Function.update_self] + omega + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I + (fun y => n - y.regs counter) hbody hi + have hj : y.regs counter = n := by + simp only [Cond.holds_lt, hy.2.1] at hc + have := hy.2.2.2.1 + omega + refine ⟨y, ?_, ?_, hj, hy.2.2.1, hy.2.2.2.2.2, hy.1, hy.2.1⟩ + · apply (hs.seq he).mono + have hs0 : start.regs counter = 0 := by simp [start, counter, one] + rw [hs0, Nat.sub_zero] + omega + · simpa only [hj] using hy.2.2.2.2.1 + +end Cslib.WordRAM.Power diff --git a/Complexity/Foundations/Data/Finset/OrderedFilter.lean b/Complexity/Foundations/Data/Finset/OrderedFilter.lean new file mode 100644 index 000000000..81ed3ec1f --- /dev/null +++ b/Complexity/Foundations/Data/Finset/OrderedFilter.lean @@ -0,0 +1,42 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Data.Finset.Sort +public import Mathlib.Data.List.OfFn + +/-! +# Ordered finite-set enumeration and filtering + +## Main results + +* `Finset.sort_eq_filter_finRange`: a level set in increasing order is the filtered level list. +* `Finset.ofFn_orderEmbOfFin`: values at its ordered positions form its sorted mapped list. +-/ + +@[expose] public section + +namespace Finset + +/-- Increasing level-set enumeration is ordinary filtering of all levels. -/ +theorem sort_eq_filter_finRange {L : ℕ} (s : Finset (Fin L)) : + s.sort = (List.finRange L).filter fun i => decide (i ∈ s) := by + apply s.sortedLT_sort.eq_of_mem_iff + ((List.sortedLT_finRange L).pairwise.filter _ |>.sortedLT) + intro a + simp + +/-- Applying a function to the ordered finite-set positions agrees with its sorted list. -/ +theorem ofFn_orderEmbOfFin {α β : Type*} [LinearOrder α] (s : Finset α) + {k : ℕ} (h : s.card = k) (f : α → β) : + List.ofFn (fun i => f (s.orderEmbOfFin h i)) = s.sort.map f := by + rw [← s.listMap_orderEmbOfFin_finRange h, List.map_map, List.ofFn_eq_map] + rfl + +end Finset diff --git a/Complexity/Foundations/Data/List/RowMajor.lean b/Complexity/Foundations/Data/List/RowMajor.lean new file mode 100644 index 000000000..c03ab4023 --- /dev/null +++ b/Complexity/Foundations/Data/List/RowMajor.lean @@ -0,0 +1,64 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Data.List.OfFn +public import Mathlib.Data.List.FinRange +public import Mathlib.Logic.Equiv.Fin.Basic +import Mathlib.Data.List.Basic +import Mathlib.Data.List.GetD + +/-! +# Row-major finite arrays as lists + +## Main results + +* `List.flatMap_ofFn_prod`: nested row-major enumeration agrees with the existing product + equivalence on finite indices. +* `List.getD_flatMap_ofFn`: read a cell at its ordinary flat row-major index. +-/ + +@[expose] public section + +namespace List + +/-- Flattening all rows enumerates the standard finite product equivalence. -/ +theorem flatMap_ofFn_prod {α : Type*} {n d : ℕ} (f : Fin n → Fin d → α) : + (finRange n).flatMap (fun i => ofFn (f i)) = + ofFn (fun p : Fin (n * d) => f (finProdFinEquiv.symm p).1 + (finProdFinEquiv.symm p).2) := by + rw [ofFn_mul] + simp only [ofFn_eq_map, ← flatMap_def] + apply flatMap_congr + intro i _ + apply map_congr_left + intro j _ + have heq : (⟨i.val * d + j.val, by + calc + i.val * d + j.val < i.val * d + d := Nat.add_lt_add_left j.isLt _ + _ = (i.val + 1) * d := by rw [Nat.add_mul, Nat.one_mul] + _ ≤ n * d := Nat.mul_le_mul_right d i.isLt⟩ : Fin (n * d)) = finProdFinEquiv (i, j) := by + apply Fin.ext + simp [finProdFinEquiv, Nat.mul_comm, Nat.add_comm] + simp only [heq, Equiv.symm_apply_apply] + +/-- Ordinary flat indexing reads the corresponding original row and column. -/ +theorem getD_flatMap_ofFn {α : Type*} {n d : ℕ} (f : Fin n → Fin d → α) + (default : α) (i : Fin n) (j : Fin d) : + ((finRange n).flatMap (fun i => ofFn (f i))).getD (i.val * d + j.val) default = + f i j := by + rw [flatMap_ofFn_prod] + have heq : i.val * d + j.val = (finProdFinEquiv (i, j)).val := by + simp [finProdFinEquiv, Nat.mul_comm, Nat.add_comm] + rw [heq, getD_eq_getElem _ _ (by + rw [length_ofFn] + exact (finProdFinEquiv (i, j)).isLt), List.getElem_ofFn] + exact congrArg (fun p => f p.1 p.2) (finProdFinEquiv.symm_apply_apply (i, j)) + +end List diff --git a/Complexity/Foundations/Data/Nat/OfDigits.lean b/Complexity/Foundations/Data/Nat/OfDigits.lean new file mode 100644 index 000000000..25ac68b3c --- /dev/null +++ b/Complexity/Foundations/Data/Nat/OfDigits.lean @@ -0,0 +1,46 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Data.Nat.Digits.Defs +public import Mathlib.Algebra.BigOperators.Fin + +/-! +# Finite-function digit numbering + +## Main results + +* `Nat.ofDigits_ofFn`: a digit list as a finite sum of weighted digits. +* `Nat.ofDigits_finFunctionFinEquiv`: agreement with Mathlib's finite-function numbering. +-/ + +@[expose] public section + +namespace Nat + +/-- Interpreting a fixed-length tuple as a least-significant-first digit list. -/ +theorem ofDigits_ofFn (b : ℕ) {L : ℕ} (f : Fin L → ℕ) : + ofDigits b (List.ofFn f) = ∑ i, f i * b ^ i.val := by + induction L with + | zero => simp + | succ L ih => + rw [List.ofFn_succ, ofDigits_cons, ih, Fin.sum_univ_succ] + simp only [Fin.val_zero, pow_zero, mul_one, Fin.val_succ, pow_succ'] + rw [Finset.mul_sum] + congr 1 + apply Finset.sum_congr rfl + intro i _ + ring + +/-- `finFunctionFinEquiv` uses exactly `ofDigits`'s least-significant-first convention. -/ +theorem ofDigits_finFunctionFinEquiv {b L : ℕ} (f : Fin L → Fin b) : + ofDigits b (List.ofFn fun i => (f i).val) = (finFunctionFinEquiv f).val := + ofDigits_ofFn b _ + +end Nat diff --git a/ComplexityTests/SchonhageBandPreparation.lean b/ComplexityTests/SchonhageBandPreparation.lean new file mode 100644 index 000000000..bd2d3af56 --- /dev/null +++ b/ComplexityTests/SchonhageBandPreparation.lean @@ -0,0 +1,167 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandThin +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandCatalogue +import Complexity.Algorithms.MatrixMultiplication.Schonhage.MaskTable + +-- These regressions evaluate actual native code; general execution proofs are checked separately. +set_option linter.hashCommand false + +namespace ComplexityTests.SchonhageBandPreparation + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage +open BandPreparation + +private def leafAt {L : ℕ} (i : Fin (10 ^ L)) : Leaf L := + fun ℓ => Encoding.termAt (finFunctionFinEquiv.symm i ℓ) + +private def expected {L m N : ℕ} (hm : m ≤ L) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) : List ℤ := + ((List.range (numBands L m N)).flatMap fun β => + List.ofFn fun i => encodingL (bandArrayL (MaskTable.layout hm) X β) (leafAt i)) ++ + ((List.range (numBands L m N)).flatMap fun β => + List.ofFn fun i => encodingR (bandArrayR (MaskTable.layout hm) Y β) (leafAt i)) + +private def observe (w L m N : ℕ) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) : + Option ((List ℤ × Bool) × List ℕ) := + let initial := ((State.init (input L m N X Y) : State 96).data.write + (memoryEnd L m N) 91) + (program.eval w 1024 initial).map fun (y, _) => + (((List.range (2 * numBands L m N * 10 ^ L)).map fun i => + decode (y.mem (catalogueBase L m N + i)), + decide ((∀ a < inputEnd m N, y.mem a = initial.mem a) ∧ + y.mem (memoryEnd L m N) = 91)), + (List.range (2 ^ L)).map fun i => y.mem (inverseBase L m N + i)) + +-- Distinct tiles share the existing row/column band blocks, with no per-tile duplication. +#guard tileInputBases 1 0 4 (0, 1) == (57, 87) +#guard tileInputBases 1 0 4 (1, 1) == (67, 87) + +-- A zero-dimensional matrix computes its tables and has no bands. Its input is restored. +#guard observe 8 0 0 0 (fun _ _ => 0) (fun _ _ => 0) == some (([], true), [1]) + +-- A genuine signed zero-depth band on each side calls both actual encoder programs. +#guard observe 8 0 0 1 (fun _ _ => -3) (fun _ _ => 2) == some (([-3, 2], true), [1]) + +-- All leaves of four distinct shared bands agree with the unchanged mathematical arrays. +#guard (observe 12 1 1 2 (fun i k => (i.val : ℤ) - k.val) + (fun k j => (k.val : ℤ) - 2 * j.val)).map (fun z => z.1) == + some (expected (N := 2) (by decide : 1 ≤ 1) (fun i k => (i.val : ℤ) - k.val) + (fun k j => (k.val : ℤ) - 2 * j.val), true) + +-- Two row and column bands with padding: the last band has only one real row/column. +#guard (observe 12 1 0 4 (fun i _ => (i.val : ℤ) - 2) + (fun _ j => 1 - (j.val : ℤ))).map (fun z => z.1) == + some (expected (N := 4) (by decide : 0 ≤ 1) (fun i _ => (i.val : ℤ) - 2) + (fun _ j => 1 - (j.val : ℤ)), true) + +-- The second size-one mask is unselected when K0² = 1; the first is mask one. +#guard (observe 12 2 1 1 (fun _ _ => -1) (fun _ _ => 1)).map Prod.snd == + some [0, 1, 0, 0] + +-- Address-width boundary for a padded, two-band call. The nonzero sentinel also survives. +#guard observe 6 1 0 4 (fun _ _ => 0) (fun _ _ => 0) == none +#guard (observe 7 1 0 4 (fun _ _ => 0) (fun _ _ => 0)).map (fun z => z.1.2) == some true + +/-- The original numerical width premise has a checked strict boundary. -/ +example : ¬Fits 13 (wordBudget 1 0 4) ∧ Fits 14 (wordBudget 1 0 4) := by decide +kernel + +/-- A negative zero-depth initialized client. The actual final memory belongs to the +charged prefix run, which is followed by its charged halt. -/ +example : ∃ y : Data 96, ∃ n, n ≤ timeBound 0 0 1 ∧ + Runs 11 (program.program catalogue) + (State.init (input 0 0 1 (fun _ _ => -3) (fun _ _ => 2))) ⟨program.size, y⟩ n ∧ + Executes 11 (program.program catalogue) + (State.init (input 0 0 1 (fun _ _ => -3) (fun _ _ => 2))) + (catalogueBase 0 0 1) (n + 1) ∧ + y.mem (catalogueAddress 0 0 1 false 0 0) = 7 ∧ + y.mem (catalogueAddress 0 0 1 true 0 0) = 4 := by + obtain ⟨_, y, n, hn, hr, he, _, hl, hright, _⟩ := runs (w := 9) (N := 1) (by decide) + (by decide : 0 ≤ 0) (fun _ _ => -3) (fun _ _ => 2) 3 (by decide) + (by intro i k; decide) (by intro k j; decide) (by decide) (by decide +kernel) + refine ⟨y, n, hn, hr, he, ?_, ?_⟩ + · have h := hl 0 (by decide +kernel) (Fin.elim0 : Leaf 0) + have hi : (Encoding.leafIndex (Fin.elim0 : Leaf 0)).val = 0 := by + have := (Encoding.leafIndex (Fin.elim0 : Leaf 0)).isLt + norm_num at this ⊢ + have hv : encodingL (bandArrayL (N := 1) (MaskTable.layout (by decide : 0 ≤ 0)) + (fun _ _ => -3) 0) Fin.elim0 = -3 := by decide +kernel + simpa only [hi, hv, show encode (-3) = 7 from rfl] using h + · have h := hright 0 (by decide +kernel) (Fin.elim0 : Leaf 0) + have hi : (Encoding.leafIndex (Fin.elim0 : Leaf 0)).val = 0 := by + have := (Encoding.leafIndex (Fin.elim0 : Leaf 0)).isLt + norm_num at this ⊢ + have hv : encodingR (bandArrayR (N := 1) (MaskTable.layout (by decide : 0 ≤ 0)) + (fun _ _ => 2) 0) Fin.elim0 = 2 := by decide +kernel + simpa only [hi, hv, show encode 2 = 4 from rfl] using h + +/-- A positive-depth, signed padded multi-band client of the general kernel-checked run. +The prefix is tied to its actual final memory, including restored input and outside frame. -/ +example : ∃ y : Data 96, ∃ n, n ≤ timeBound 1 0 4 ∧ + Runs 14 (program.program catalogue) + (State.init (input 1 0 4 (fun i _ => (i.val : ℤ) - 2) + (fun _ j => 1 - (j.val : ℤ)))) ⟨program.size, y⟩ n ∧ + Executes 14 (program.program catalogue) + (State.init (input 1 0 4 (fun i _ => (i.val : ℤ) - 2) + (fun _ j => 1 - (j.val : ℤ)))) (catalogueBase 1 0 4) (n + 1) ∧ + y.mem (catalogueAddress 1 0 4 false 0 + (Encoding.leafIndex (fun _ : Fin 1 => Term.P 0 0)).val) = encode (-2) ∧ + (∀ b, ¬(inputEnd 0 4 ≤ b ∧ b < memoryEnd 1 0 4) → + y.mem b = (State.init (input 1 0 4 (fun i _ => (i.val : ℤ) - 2) + (fun _ j => 1 - (j.val : ℤ))) : State 96).data.mem b) := by + obtain ⟨_, y, n, hn, hr, he, _, hl, _, _, _, hf⟩ := runs (w := 12) (N := 4) (by decide) + (by decide : 0 ≤ 1) (fun i _ => (i.val : ℤ) - 2) (fun _ j => 1 - (j.val : ℤ)) + 2 (by decide) (by decide +kernel) (by decide +kernel) (by decide) (by decide +kernel) + refine ⟨y, n, hn, hr, he, ?_, hf⟩ + have hv : encodingL (bandArrayL (N := 4) (MaskTable.layout (by decide : 0 ≤ 1)) + (fun i _ => (i.val : ℤ) - 2) 0) (fun _ : Fin 1 => Term.P 0 0) = -2 := by decide +kernel + simpa only [hv] using hl 0 (by decide +kernel) (fun _ : Fin 1 => Term.P 0 0) + +private def framedEmpty : Data 96 := + ((State.init (input 0 0 0 (fun _ _ => 0) (fun _ _ => 0)) : State 96).data.write + 0 77).write (memoryEnd 0 0 0) 91 + +/-- General-call framing allows an unrelated native header zero and a nonzero adjacent +query/scratch sentinel. The full original prefix, including header zero, is restored. -/ +example : ∃ y : Data 96, ∃ n, n ≤ timeBound 0 0 0 ∧ + Runs 10 (program.program catalogue) ⟨0, framedEmpty⟩ ⟨program.size, y⟩ n ∧ + y.mem 0 = 77 ∧ y.mem (memoryEnd 0 0 0) = 91 := by + obtain ⟨y, ⟨n, hn, he⟩, _, _, _, _, hf⟩ := exec (w := 8) (L := 0) (m := 0) + (N := 0) (by decide) (by decide) framedEmpty (by decide +kernel) + (by decide +kernel) (by decide +kernel) (fun _ _ _ => 0) + (by intro side r hr; omega) 0 (by decide) (by intro side r hr; omega) + (by decide) (by decide +kernel) + refine ⟨y, n, hn, ?_, ?_, ?_⟩ + · simpa only [Nat.zero_add] using he.runs (pc := 0) + (p := program.program catalogue) (Program.Contains.self _).append_left + · rw [hf 0 (by decide +kernel)] + decide +kernel + · rw [hf (memoryEnd 0 0 0) (by omega)] + rfl + +/-- Symbolic positive-depth thin parameters and nonzero signed matrices simultaneously +satisfy all width premises and the genuine charged native saving, without enumerating them. -/ +example (m : ℕ) : let M := m + 1 + ∃ w n, 0 < w ∧ 0 < M ∧ Fits (w + 2) (wordBudget (19 * M) M (D M ^ 18)) ∧ + (7 ^ (19 * M) : ℕ) * (1 : ℤ) < (2 : ℤ) ^ (w - 1) ∧ + Executes (w + 2) (program.program catalogue) + (State.init (input (19 * M) M (D M ^ 18) (fun _ _ => 1) (fun _ _ => -1))) + (catalogueBase (19 * M) M (D M ^ 18)) n ∧ + (n : ℝ) ≤ 81920 * ((19 * M + 1 : ℕ) : ℝ) * (D M ^ 18 : ℕ) ^ (2 : ℕ) / + (D M : ℝ) ^ (1 / 18 : ℝ) := by + dsimp only + obtain ⟨w, hw, hmag, hfit⟩ := width_exists (19 * (m + 1)) (m + 1) + (D (m + 1) ^ 18) 1 (by decide) + obtain ⟨n, he, ht⟩ := runs_thin (m := m + 1) (N := D (m + 1) ^ 18) hw (Nat.le_refl _) + (fun _ _ => 1) (fun _ _ => -1) 1 (by decide) + (by intro i k; decide) (by intro k j; decide) hmag hfit + exact ⟨w, n, hw, Nat.succ_pos _, hfit, hmag, he, ht⟩ + +end ComplexityTests.SchonhageBandPreparation diff --git a/NOTICE b/NOTICE index a28ee14dd..5b8c5c0c1 100644 --- a/NOTICE +++ b/NOTICE @@ -24,3 +24,7 @@ This product is licensed under the Apache License, Version 2.0 (see LICENSE). The prime-window proof also adapts Mathlib/NumberTheory/Bertrand.lean, Copyright (c) 2020 Patrick Stevens; authors Patrick Stevens, Bolton Bailey, under the Apache License, Version 2.0. + +The native shared-band preparation selectively adapts the band-array and all-band +organization from that same pinned source. Anthropic, PBC credit is retained in +BandProgram.lean; its native mask scans, dense inverse table and execution proofs are new. diff --git a/ROADMAP.md b/ROADMAP.md index 7e50f2a31..83a6eaaf8 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -432,7 +432,11 @@ each statement against the numbered result. Organize modules by subject, not cha The shared native left/right encoders ([#1493]) compute all `10^L` leaf values from `7^L` signed inputs within `240*(L+1)*10^L` standard WordRAM instructions, plus the final halt, with fixed code/registers and concrete initialized inputs, frames and explicit width bounds. - Band/tile preparation, native pruned/sparse-query programs, native Chan–He preprocessing, the reduction + Native shared-band preparation ([#1523]) computes deterministic subset tables and a valid layout, + prepares padded signed matrix bands, invokes both shared encoders for every band, and restores the + original initialized input. Fixed 96-register code has a complete charged bound + `4096*(L+1)*(1+N*4^m+2^L+2*numBands*10^L)`; at `L=19m`, `N≥(4^m)^18`, including halt, this is at most + `81920*(L+1)*N²/(4^m)^(1/18)`. Native pruned/sparse-query programs, native Chan–He preprocessing, the reduction host, the fast Exact Triangle solver and full input/time/width/asymptotic composition, then the improved Section 4 route to exponent 1.9992; size-based width @@ -566,3 +570,4 @@ not planned, not as proved. [#1459]: https://github.com/cslib-community/complexitylib/issues/1459 [#1463]: https://github.com/cslib-community/complexitylib/issues/1463 [#1493]: https://github.com/cslib-community/complexitylib/issues/1493 +[#1523]: https://github.com/cslib-community/complexitylib/issues/1523 diff --git a/UPSTREAM.md b/UPSTREAM.md index 87a5bc5c2..35d54dd00 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -220,8 +220,23 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he `w+2`. The two extra bits and actual arithmetic-block costs come from the existing native signed kernels, without a copied source machine or a whole-program simulation hypothesis. The existing array-copy interface is retained, with a narrow generalization for disjoint - blocks in either address order or identical blocks. Band/tile preparation, native pruned - and sparse-query programs, native preprocessing/solvers and full input/time/width composition, including the + blocks in either address order or identical blocks. + The native shared-band preparation component + ([#1523](https://github.com/cslib-community/complexitylib/issues/1523)) computes a deterministic + `Layout` and both subset tables by native mask/popcount scans, computes the square-root block + count by a charged loop, and decodes each band-array index into the original row/column and + thin-dimension coordinates. Real matrix bounds are checked before row-major multiplication. + Each shared row/column band calls the unchanged encoder once; tiles refer to its catalogue + block in the existing leaf order. The fixed 96-register standard code starts from actual + `State.init` scalar/matrix input, charges snapshot and restoration of the complete original + prefix, and preserves arbitrary outside memory. All counter/address fits follow from an + explicit original-parameter word budget; signed fits follow from the original entry bound. + Its complete instruction bound, including halt, is + `81920*(L+1)*N²/(4^m)^(1/18)` when `L=19m` and `N≥(4^m)^18`. Native power, ordered-filter, + finite-digit and row-major list lemmas live in small topic modules; the existing mathematical + layout, matrix and encoding interfaces are reused. No source machine or call/time oracle + is introduced. Native pruned and sparse-query programs, native preprocessing/solvers and + full input/time/width composition, including the improved Section 4 route to exponent 1.9992, remain in #1394. * **Typed RAM upstream core.** [CSLib #1158](https://github.com/leanprover/cslib/pull/1158) at `beea60a15eddc91d93e92fa5af9fb965c351d0d5` proposes From 87ff44c7a18b68539f44f0207aef6c54fdf9273b Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 15:57:25 -0400 Subject: [PATCH 23/73] feat(wordram): checkpoint native pruned accumulation and frame transitions Work in progress for #1526, not a submitted component. Complete native parent accumulation loop (31N+3), internal sparse initialization (7N+8), computed child push (15), shared actual frame-word store, executable query-code inverse/requested-set bridge and recursive timestamp allowance. These current modules have clean targeted Lean MCP diagnostics; actual interpreter and kernel digit/request regressions pass. Changed-scope hygiene --check-simp and diff check pass. The general node/controller continuation proof, actual initialized full evaluator theorem and final sparse charged bound remain unfinished; no full six-gate or independent review claim. AI-assisted implementation by codex-three-sum-continuation-t3-3dda8289; full #1394 remains open. Final current-tree checkpoint: strict build --wfail --iofail of Accumulate, Clear, Push, Frame, Powers, Leaf, Weight, Scan and SchonhagePruned tests passed together (3596 jobs); scope hygiene --check-simp and diff checks pass. This is a partial implementation checkpoint, not an all-six-validated PR or completed #1526 milestone. --- .../Schonhage/Pruned/Accumulate.lean | 169 ++++++++++++++++++ .../Schonhage/Pruned/AccumulateStep.lean | 135 ++++++++++++++ .../Schonhage/Pruned/Clear.lean | 67 ++++++- .../Schonhage/Pruned/Codes.lean | 30 ++++ .../Schonhage/Pruned/Control.lean | 60 +++++-- .../Schonhage/Pruned/Program.lean | 39 ++-- .../Schonhage/Pruned/Push.lean | 84 +++++++++ .../Schonhage/Pruned/Weight.lean | 26 +++ ComplexityTests/SchonhagePruned.lean | 4 + 9 files changed, 584 insertions(+), 30 deletions(-) create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Accumulate.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/AccumulateStep.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Push.lean diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Accumulate.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Accumulate.lean new file mode 100644 index 000000000..59707b311 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Accumulate.lean @@ -0,0 +1,169 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.AccumulateStep +public import Complexity.Computability.WordRAM.Heap + +/-! +# Complete native accumulation over a distinct parent list + +## Main results + +* `PrunedQuery.exec_accumulateLoop`: the native loop adds a returned child exactly once at + each contributing listed parent query, within `31 N + 3` instructions. + +The loop invariant uses Mathlib's existing `Finset.piecewise` for the updated memory cells. +The source list and child values are preserved by the concrete block separations. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed Finset + +private theorem accumulateLoop_preserves (i : Fin registers) : + ¬AccumulateStepLocal i → accumulateLoop.Preserves i := by decide +kernel +revert + +/-- This is the real indexed signed-add loop. The list's distinctness is its ordinary memory +property, established by the preceding native timestamp scan, not a query-decision oracle. -/ +theorem exec_accumulateLoop {w N capacity : ℕ} (x : Data registers) (input : ℕ → ℕ) + (V B : ℕ → ℤ) (hN : x.regs count = N) + (hinput : ∀ i < N, x.mem (x.regs listBase + i) = input i) + (hcode : ∀ i < N, input i < capacity) + (hinj : Set.InjOn input {i | i < N}) + (hparent : ∀ i < N, x.mem (x.regs valueBase + input i) = encode (V (input i))) + (hchild : ∀ i < N, selected true (x.regs term) (input i) → + x.mem (x.regs childValues + input i / 10) = encode (B (input i / 10))) + (hV : ∀ i < N, GenericRAM.Signed.Fits w (V (input i))) + (hB : ∀ i < N, GenericRAM.Signed.Fits w (B (input i / 10))) + (hsepSource : x.regs listBase + N ≤ x.regs valueBase ∨ + x.regs valueBase + capacity ≤ x.regs listBase) + (hsepChild : x.regs valueBase + capacity ≤ x.regs childValues ∨ + x.regs childValues + capacity ≤ x.regs valueBase) + (h1 : x.regs one = 1) (h9 : x.regs nine = 9) (h10 : x.regs ten = 10) + (hw : Fits (w + 2) (x.regs listBase + N + x.regs valueBase + capacity + + x.regs childValues + capacity + N + 16)) : + ∃ y, ExecWithin (w + 2) accumulateLoop x y (31 * N + 3) ∧ y.regs index = N ∧ + y.mem = (((range N).filter fun i => selected true (x.regs term) (input i)).image + fun i => x.regs valueBase + input i).piecewise + (fun a => encode (V (a - x.regs valueBase) + B ((a - x.regs valueBase) / 10))) x.mem ∧ + Modifies (Block.mk (x.regs valueBase) capacity).footprint x.mem y.mem ∧ + (∀ i < N, y.mem (x.regs listBase + i) = input i) ∧ + (∀ i, ¬AccumulateStepLocal i → y.regs i = x.regs i) := by + let written (k : ℕ) := (((range k).filter fun i => + selected true (x.regs term) (input i)).image fun i => x.regs valueBase + input i) + let newValue (a : ℕ) := + encode (V (a - x.regs valueBase) + B ((a - x.regs valueBase) / 10)) + let x0 := x.set index 0 + let I (y : Data registers) : Prop := y.regs index ≤ N ∧ + y.mem = (written (y.regs index)).piecewise newValue x.mem ∧ + (∀ i, ¬AccumulateStepLocal i → y.regs i = x.regs i) + have hinit : I x0 := by + refine ⟨by simp [x0], ?_, ?_⟩ + · funext a + simp [x0, written, Finset.piecewise] + · intro i hi + have hne : i ≠ index := by intro h; subst h; exact hi (by decide) + simp [x0, hne] + have houtside (y : Data registers) (hy : I y) (a : ℕ) + (ha : ¬(Block.mk (x.regs valueBase) capacity).Owns a) : y.mem a = x.mem a := by + have hnot : a ∉ written (y.regs index) := by + rintro h + obtain ⟨i, hi, rfl⟩ := mem_image.mp h + have hik := mem_range.mp (mem_filter.mp hi).1 + have hq := hcode i (lt_of_lt_of_le hik hy.1) + apply ha + change x.regs valueBase ≤ x.regs valueBase + input i ∧ + x.regs valueBase + input i < x.regs valueBase + capacity + omega + rw [hy.2.1] + simp only [Finset.piecewise, hnot, ↓reduceIte] + have hread (y : Data registers) (hy : I y) (i : ℕ) (hi : i < N) : + y.mem (x.regs listBase + i) = input i := by + rw [houtside y hy] + · exact hinput i hi + · change ¬(x.regs valueBase ≤ x.regs listBase + i ∧ + x.regs listBase + i < x.regs valueBase + capacity) + rcases hsepSource with h | h <;> omega + have hstep (y : Data registers) (hy : I y) (hc : (Cond.lt index count).Holds y) : + ∃ z, ExecWithin (w + 2) accumulateStep y z 29 ∧ I z ∧ + N - z.regs index < N - y.regs index := by + obtain ⟨hle, hmem, hf⟩ := hy + have hcount := (hf count (by decide)).trans hN + have hb := hf listBase (by decide) + have hv := hf valueBase (by decide) + have hcptr := hf childValues (by decide) + have ht := hf term (by decide) + have hk : y.regs index < N := by simpa only [Cond.holds_lt, hcount] using hc + have hq := hcode (y.regs index) hk + have hquot : input (y.regs index) / 10 < capacity := + lt_of_le_of_lt (Nat.div_le_self _ _) hq + have hnot : x.regs valueBase + input (y.regs index) ∉ written (y.regs index) := by + rintro h + obtain ⟨j, hj, he⟩ := mem_image.mp h + have hjk := mem_range.mp (mem_filter.mp hj).1 + have hji := hinj (lt_of_lt_of_le hjk hle) hk (Nat.add_left_cancel he) + omega + have hleft : y.mem (y.regs valueBase + input (y.regs index)) = + encode (V (input (y.regs index))) := by + rw [hv, hmem] + simp only [Finset.piecewise, hnot, ↓reduceIte] + exact hparent _ hk + have hright (hs : selected true (y.regs term) (input (y.regs index))) : + y.mem (y.regs childValues + input (y.regs index) / 10) = + encode (B (input (y.regs index) / 10)) := by + rw [hcptr, houtside y ⟨hle, hmem, hf⟩] + · exact hchild _ hk (by simpa only [ht] using hs) + · change ¬(x.regs valueBase ≤ x.regs childValues + input (y.regs index) / 10 ∧ + x.regs childValues + input (y.regs index) / 10 < x.regs valueBase + capacity) + rcases hsepChild with h | h <;> omega + obtain ⟨z, hz, hi, hm, hregs⟩ := exec_accumulateStep (hV _ hk) (hB _ hk) y + (input (y.regs index)) (by rw [hb]; exact hread y ⟨hle, hmem, hf⟩ _ hk) + ((hf one (by decide)).trans h1) ((hf nine (by decide)).trans h9) + ((hf ten (by decide)).trans h10) (fun _ => hleft) hright + (hw.of_le (by omega)) (hw.of_le (by rw [hb]; omega)) + (hw.of_le (by rw [hv]; omega)) (hw.of_le (by rw [hcptr]; omega)) (hw.of_le (by omega)) + refine ⟨z, hz, ⟨by omega, ?_, fun i hi => (hregs i hi).trans (hf i hi)⟩, by omega⟩ + have hnext : written (y.regs index + 1) = + if selected true (x.regs term) (input (y.regs index)) then + insert (x.regs valueBase + input (y.regs index)) (written (y.regs index)) + else written (y.regs index) := by + by_cases hs : selected true (x.regs term) (input (y.regs index)) <;> + simp [written, range_add_one, filter_insert, hs] + rw [hm, ht, hv, hmem, hi, hnext] + funext a + by_cases hs : selected true (x.regs term) (input (y.regs index)) + · by_cases he : a = x.regs valueBase + input (y.regs index) + · subst a + simp [hs, Finset.piecewise, newValue] + · simp [hs, Finset.piecewise, he, newValue] + · simp [hs, Finset.piecewise, newValue] + obtain ⟨y, hy, hIy, hstop⟩ := ExecWithin.loop_invariant I (fun y => N - y.regs index) + hstep hinit + have hcount := (hIy.2.2 count (by decide)).trans hN + have hindex : y.regs index = N := by + simp only [Cond.holds_lt, hcount, not_lt] at hstop + omega + have hsetup : ExecWithin (w + 2) (ram[ index ← #0 ] : Code .standard registers) x x0 1 := by + ram_eval [x0] + have hloop : ExecWithin (w + 2) (Code.loop (.lt index count) accumulateStep) x0 y + (31 * N + 2) := by + simpa only [x0, Data.set_regs, Function.update_self, Nat.sub_zero, Nat.mul_comm] using hy + have he : ExecWithin (w + 2) accumulateLoop x y (31 * N + 3) := + (hsetup.seq hloop).mono (by omega) + refine ⟨y, he, hindex, ?_, ?_, hread y hIy, + fun i hi => he.regs_eq (accumulateLoop_preserves i hi)⟩ + · change y.mem = (written N).piecewise newValue x.mem + rw [← hindex] + exact hIy.2.1 + · intro a ha + exact houtside y hIy a (by simpa only [Block.mem_footprint] using ha) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/AccumulateStep.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/AccumulateStep.lean new file mode 100644 index 000000000..0ac4a1719 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/AccumulateStep.lean @@ -0,0 +1,135 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Add +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ScanStep + +/-! +# Actual native selection and accumulation of one query + +## Main results + +* `PrunedQuery.exec_accumulateDispatch`: the real digit comparisons select a child's addition. +* `PrunedQuery.exec_accumulateStep`: include the source indexing, load, digit and counter work. + +The child-value memory premise is needed only for selected queries. Unselected child-directory +cells may contain arbitrary prior values and are never loaded by the program. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Registers changed by selected signed accumulation or its unselected no-op. -/ +def AccumulateDispatchLocal (i : Fin registers) : Prop := AddValueLocal i ∨ i = temporary + +instance (i : Fin registers) : Decidable (AccumulateDispatchLocal i) := + inferInstanceAs (Decidable (AddValueLocal i ∨ i = temporary)) + +private theorem accumulateDispatch_preserves (i : Fin registers) : + ¬AccumulateDispatchLocal i → accumulateDispatch.Preserves i := by decide +kernel +revert + +/-- Exactly the contributing parent queries load and add a child value. Selection is proved +from the actual comparisons with digit nine and the current term register. -/ +theorem exec_accumulateDispatch {w : ℕ} {a b : ℤ} + (ha : GenericRAM.Signed.Fits w a) (hb : GenericRAM.Signed.Fits w b) + (x : Data registers) (q : ℕ) (hq : x.regs query = q) (hd : x.regs digit = q % 10) + (h9 : x.regs nine = 9) (h10 : x.regs ten = 10) + (hleft : selected true (x.regs term) q → x.mem (x.regs valueBase + q) = encode a) + (hright : selected true (x.regs term) q → + x.mem (x.regs childValues + q / 10) = encode b) + (hp : Fits (w + 2) (x.regs valueBase + q)) + (hc : Fits (w + 2) (x.regs childValues + q / 10)) : + ∃ y, ExecWithin (w + 2) accumulateDispatch x y 25 ∧ + y.mem = (if selected true (x.regs term) q then + Function.update x.mem (x.regs valueBase + q) (encode (a + b)) else x.mem) ∧ + (∀ i, ¬AccumulateDispatchLocal i → y.regs i = x.regs i) := by + by_cases hsel : selected true (x.regs term) q + · obtain ⟨y, hy, hmem, _⟩ := exec_addValue ha hb x q hq h10 (hleft hsel) (hright hsel) hp hc + have hchoice : q % 10 = 9 ∨ q % 10 = x.regs term := hsel + have he : ExecWithin (w + 2) accumulateDispatch x y 25 := by + by_cases hfirst : q % 10 = 9 + · exact (ExecWithin.branch_pos (by + simpa only [Cond.holds_eq, hd, h9] using hfirst) hy).mono (by decide) + · have hsecond := hchoice.resolve_left hfirst + exact (ExecWithin.branch_neg (by + simpa only [Cond.holds_eq, hd, h9] using hfirst) + (ExecWithin.branch_pos (by simpa only [Cond.holds_eq, hd] using hsecond) hy)).mono + (by decide) + exact ⟨y, he, by simpa only [hsel, ↓reduceIte] using hmem, + fun i hi => he.regs_eq (accumulateDispatch_preserves i hi)⟩ + · have hfirst : q % 10 ≠ 9 := fun h => hsel (Or.inl h) + have hsecond : q % 10 ≠ x.regs term := fun h => hsel (Or.inr h) + let y := x.set temporary (x.regs temporary) + have he : ExecWithin (w + 2) accumulateDispatch x y 5 := + ExecWithin.branch_neg (by simpa only [Cond.holds_eq, hd, h9] using hfirst) + (ExecWithin.branch_neg (by simpa only [Cond.holds_eq, hd] using hsecond) ExecWithin.copy) + exact ⟨y, he.mono (by decide), by simp [hsel, y], + fun i hi => he.regs_eq (accumulateDispatch_preserves i hi)⟩ + +/-- Registers changed by loading, dispatching and advancing one parent query. -/ +def AccumulateStepLocal (i : Fin registers) : Prop := + AccumulateDispatchLocal i ∨ i = query ∨ i = digit ∨ i = index + +instance (i : Fin registers) : Decidable (AccumulateStepLocal i) := + inferInstanceAs (Decidable + (AccumulateDispatchLocal i ∨ i = query ∨ i = digit ∨ i = index)) + +private theorem accumulateStep_preserves (i : Fin registers) : + ¬AccumulateStepLocal i → accumulateStep.Preserves i := by decide +kernel +revert + +/-- One complete native parent-query iteration, including every index/digit/control operation. +The conditional memory hypotheses reflect only which child words are actually requested. -/ +theorem exec_accumulateStep {w : ℕ} {a b : ℤ} + (ha : GenericRAM.Signed.Fits w a) (hb : GenericRAM.Signed.Fits w b) + (x : Data registers) (q : ℕ) + (hq : x.mem (x.regs listBase + x.regs index) = q) + (h1 : x.regs one = 1) (h9 : x.regs nine = 9) (h10 : x.regs ten = 10) + (hleft : selected true (x.regs term) q → x.mem (x.regs valueBase + q) = encode a) + (hright : selected true (x.regs term) q → + x.mem (x.regs childValues + q / 10) = encode b) + (h10fit : Fits (w + 2) 10) + (hr : Fits (w + 2) (x.regs listBase + x.regs index)) + (hp : Fits (w + 2) (x.regs valueBase + q)) + (hc : Fits (w + 2) (x.regs childValues + q / 10)) + (hi : Fits (w + 2) (x.regs index + 1)) : + ∃ y, ExecWithin (w + 2) accumulateStep x y 29 ∧ y.regs index = x.regs index + 1 ∧ + y.mem = (if selected true (x.regs term) q then + Function.update x.mem (x.regs valueBase + q) (encode (a + b)) else x.mem) ∧ + (∀ i, ¬AccumulateStepLocal i → y.regs i = x.regs i) := by + let z := ((x.set address (x.regs listBase + x.regs index)).set query q).set digit (q % 10) + have hmod : Fits (w + 2) (q % 10) := + h10fit.of_le (Nat.le_of_lt (Nat.mod_lt _ (by decide))) + have hload : ExecWithin (w + 2) scanLoad x z 3 := by + ram_eval [scanLoad, z, hq, h10, hr, hmod, address, listBase, index, query, digit, ten, + Fin.mk.injEq] + have hzreg (i : Fin registers) (ha : i ≠ address) (hq' : i ≠ query) (hd : i ≠ digit) : + z.regs i = x.regs i := by simp [z, ha, hq', hd] + obtain ⟨u, hu, hm, hf⟩ := exec_accumulateDispatch ha hb z q + (by simp [z, query, digit]) (by simp [z]) + ((hzreg nine (by decide) (by decide) (by decide)).trans h9) + ((hzreg ten (by decide) (by decide) (by decide)).trans h10) + (by simpa +decide [z, term, address, query, digit, valueBase] using hleft) + (by simpa +decide [z, term, address, query, digit, childValues] using hright) + (by simpa +decide [z, address, query, digit, valueBase] using hp) + (by simpa +decide [z, address, query, digit, childValues] using hc) + have hui : u.regs index = x.regs index := + (hf index (by decide)).trans (hzreg index (by decide) (by decide) (by decide)) + have huo : u.regs one = 1 := + (hf one (by decide)).trans ((hzreg one (by decide) (by decide) (by decide)).trans h1) + let y := u.set index (x.regs index + 1) + have hinc : ExecWithin (w + 2) (ram[ index ← index + one ]) u y 1 := by + ram_eval [y, hui, huo, hi] + have he : ExecWithin (w + 2) accumulateStep x y 29 := hload.seq (hu.seq hinc) + refine ⟨y, he, by simp [y], ?_, fun i hi => he.regs_eq (accumulateStep_preserves i hi)⟩ + simpa +decide [y, z, term, address, query, digit, valueBase] using hm + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Clear.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Clear.lean index 9987b30f9..2ac85e964 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Clear.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Clear.lean @@ -8,6 +8,7 @@ module -- upstream: candidate public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Memory +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Control /-! # Sparse initialization of requested accumulators @@ -32,20 +33,22 @@ open Cslib.WordRAM Finset /-- Clear precisely the addresses named in the processed prefix of an explicit query list. -/ def zeroQueryPrefix (mem : ℕ → ℕ) (base : ℕ) (input : ℕ → ℕ) (k : ℕ) : ℕ → ℕ := - fun a => if a ∈ (range k).image (fun i => base + input i) then 0 else mem a + ((range k).image (fun i => base + input i)).piecewise (fun _ => 0) mem /-- Clearing no requests leaves memory unchanged. -/ @[simp] theorem zeroQueryPrefix_zero (mem : ℕ → ℕ) (base : ℕ) (input : ℕ → ℕ) : - zeroQueryPrefix mem base input 0 = mem := by ext a; simp [zeroQueryPrefix] + zeroQueryPrefix mem base input 0 = mem := by + funext a + simp only [zeroQueryPrefix, Finset.range_zero, Finset.image_empty, Finset.piecewise, + Finset.notMem_empty, ↓reduceIte] /-- One additional listed request is exactly one memory store, including repeated requests. -/ theorem zeroQueryPrefix_succ (mem : ℕ → ℕ) (base : ℕ) (input : ℕ → ℕ) (k : ℕ) : zeroQueryPrefix mem base input (k + 1) = Function.update (zeroQueryPrefix mem base input k) (base + input k) 0 := by - ext a - by_cases ha : a = base + input k <;> - simp [zeroQueryPrefix, range_add_one, image_insert, ha] + unfold zeroQueryPrefix + rw [Finset.range_add_one, Finset.image_insert, Finset.piecewise_insert] /-- Every cell outside the concrete value directory is preserved by a bounded prefix. -/ theorem zeroQueryPrefix_outside {mem : ℕ → ℕ} {base capacity k : ℕ} {input : ℕ → ℕ} @@ -58,7 +61,14 @@ theorem zeroQueryPrefix_outside {mem : ℕ → ℕ} {base capacity k : ℕ} {inp change base ≤ base + input i ∧ base + input i < base + capacity have := hcode i (mem_range.mp hi) omega - simp only [zeroQueryPrefix, hnot, ↓reduceIte] + simp only [zeroQueryPrefix, piecewise, hnot, ↓reduceIte] + +/-- Every requested value in the processed prefix is actually zero in the resulting memory. -/ +theorem zeroQueryPrefix_at {mem : ℕ → ℕ} {base k i : ℕ} {input : ℕ → ℕ} (hi : i < k) : + zeroQueryPrefix mem base input k (base + input i) = 0 := by + have hmem : base + input i ∈ (range k).image (fun j => base + input j) := + mem_image.mpr ⟨i, mem_range.mpr hi, rfl⟩ + simp only [zeroQueryPrefix, piecewise, hmem, ↓reduceIte] /-- Registers modified by sparse clearing. -/ def ClearLocal (i : Fin registers) : Prop := i = address ∨ i = query ∨ i = index @@ -150,4 +160,49 @@ theorem exec_clearRequested {w N capacity : ℕ} (x : Data registers) (input : refine ⟨y, he, hindex, ?_, hread y hIy, fun i hi => he.regs_eq (clear_preserves i hi)⟩ simpa only [hindex] using hIy.2.1 +/-- Registers changed by a complete internal-node initialization. -/ +def ClearValuesLocal (i : Fin registers) : Prop := ClearLocal i ∨ AdvanceLocal i + +instance (i : Fin registers) : Decidable (ClearValuesLocal i) := + inferInstanceAs (Decidable (ClearLocal i ∨ AdvanceLocal i)) + +private theorem clearValues_preserves (i : Fin registers) : + ¬ClearValuesLocal i → clearValues.Preserves i := by decide +kernel +revert + +/-- Sparse clearing and actual stored-frame initialization together cost `7 N + 8` instructions. +The source list is preserved; the only additional memory write is its frame's fourth word. -/ +theorem exec_clearValues {w N capacity : ℕ} (x : Data registers) (input : ℕ → ℕ) + (hN : x.regs count = N) + (hinput : ∀ i < N, x.mem (x.regs listBase + i) = input i) + (hcode : ∀ i < N, input i < capacity) + (hsep : x.regs listBase + N ≤ x.regs valueBase ∨ + x.regs valueBase + capacity ≤ x.regs listBase) + (hframeSep : x.regs frame + 4 ≤ x.regs listBase ∨ + x.regs listBase + N ≤ x.regs frame) + (h0 : x.regs nil = 0) (h1 : x.regs one = 1) (h4 : x.regs frameWidth = 4) + (hw : Fits w (x.regs listBase + N + x.regs valueBase + capacity + N + 2)) + (hf : Fits w (x.regs frame + 4)) : + ∃ y, ExecWithin w clearValues x y (7 * N + 8) ∧ y.regs index = N ∧ + y.regs term = 0 ∧ y.regs mode = 1 ∧ + y.mem = Function.update (zeroQueryPrefix x.mem (x.regs valueBase) input N) + (x.regs frame + 3) 0 ∧ + (∀ i < N, y.mem (x.regs listBase + i) = input i) ∧ + (∀ i, ¬ClearValuesLocal i → y.regs i = x.regs i) := by + obtain ⟨z, hz, hindex, hmem, hread, hregs⟩ := + exec_clearRequested x input hN hinput hcode hsep h0 h1 hw + have hframe := hregs frame (by decide) + obtain ⟨y, hy, hterm, hmode, hmem', hregs'⟩ := exec_clearValuesFinish z + ((hregs one (by decide)).trans h1) ((hregs frameWidth (by decide)).trans h4) + (by simpa only [hframe] using hf) + have he : ExecWithin w clearValues x y (7 * N + 8) := (hz.seq hy).mono (by omega) + have hm : y.mem = Function.update z.mem (x.regs frame + 3) 0 := by + simpa only [hframe] using hmem' + refine ⟨y, he, (hregs' index (by decide)).trans hindex, hterm, hmode, + by rw [hm, hmem], ?_, fun i hi => he.regs_eq (clearValues_preserves i hi)⟩ + intro i hi + have hne : x.regs listBase + i ≠ x.regs frame + 3 := by + rcases hframeSep with h | h <;> omega + rw [hm, Function.update_of_ne hne] + exact hread i hi + end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Codes.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Codes.lean index dbaf0bada..8f15ff940 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Codes.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Codes.lean @@ -53,6 +53,36 @@ def outputDigits : Fin 10 ≃ OutVar where def queryIndex {L : ℕ} (w : OutStr L) : Fin (10 ^ L) := finFunctionFinEquiv fun i => outputDigits.symm (w i) +/-- Decode a supplied finite query code with the existing executable digit enumeration. -/ +def queryAt {L : ℕ} (q : Fin (10 ^ L)) : OutStr L := + fun i => outputDigits ((finFunctionFinEquiv.symm q) i) + +/-- Encoding a decoded query returns the actual supplied code. -/ +@[simp] +theorem queryIndex_queryAt {L : ℕ} (q : Fin (10 ^ L)) : queryIndex (queryAt q) = q := by + simp only [queryIndex, queryAt, Equiv.symm_apply_apply, Equiv.apply_symm_apply] + +/-- Decoding a query's code returns every output coordinate unchanged. -/ +@[simp] +theorem queryAt_queryIndex {L : ℕ} (w : OutStr L) : queryAt (queryIndex w) = w := by + funext i + simp only [queryAt, queryIndex, Equiv.symm_apply_apply, Equiv.apply_symm_apply] + +/-- The requested output set is computed from the ordinary initialized list of query codes. +Repeated list entries are allowed and will be reported in their original order. -/ +def requested {L : ℕ} (qs : List (Fin (10 ^ L))) : Finset (OutStr L) := + qs.toFinset.image queryAt + +/-- The request set's numerical image is precisely the set of supplied numerical codes. -/ +theorem image_queryIndex_requested {L : ℕ} (qs : List (Fin (10 ^ L))) : + (requested qs).image (fun w => (queryIndex w).val) = qs.toFinset.image Fin.val := by + simp only [requested, Finset.image_image, Function.comp_def, queryIndex_queryAt] + +/-- Every supplied list entry is one of the requested mathematical outputs. -/ +theorem queryAt_mem_requested {L : ℕ} {qs : List (Fin (10 ^ L))} {q : Fin (10 ^ L)} + (hq : q ∈ qs) : queryAt q ∈ requested qs := + Finset.mem_image.mpr ⟨q, List.mem_toFinset.mpr hq, rfl⟩ + /-- A query's code is the leaf-array address of its private leaf. -/ theorem queryIndex_privateLeaf {L : ℕ} (w : OutStr L) : queryIndex w = Encoding.leafIndex (privateLeaf w) := rfl diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Control.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Control.lean index cd6e68387..057047440 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Control.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Control.lean @@ -66,6 +66,24 @@ instance (i : Fin registers) : Decidable (AdvanceLocal i) := private theorem advance_preserves (i : Fin registers) : ¬AdvanceLocal i → advance.Preserves i := by decide +kernel +revert +/-- Store a term already in its register and switch to dispatch, charging all four operations. -/ +theorem exec_storeTerm {w : ℕ} (x : Data registers) (h1 : x.regs one = 1) + (h4 : x.regs frameWidth = 4) (hf : Fits w (x.regs frame + 4)) : + ∃ y, ExecWithin w storeTerm x y 4 ∧ y.regs mode = 1 ∧ + y.mem = Function.update x.mem (x.regs frame + 3) (x.regs term) ∧ + (∀ i, i ≠ address → i ≠ mode → y.regs i = x.regs i) := by + have hmode : Fits w 1 := hf.of_le (by omega) + have hadd : Fits w (x.regs frame + 3) := hf.of_le (by omega) + let y := ((((x.set address (x.regs frame + 4)).set address (x.regs frame + 3)).write + (x.regs frame + 3) (x.regs term)).set mode 1) + have hsub : x.regs frame + 4 - 1 = x.regs frame + 3 := by omega + have he : ExecWithin w storeTerm x y 4 := by + ram_eval [storeTerm, y, h1, h4, hf, hmode, hadd, hsub, address, mode, one, + frame, frameWidth, term, Fin.mk.injEq] + refine ⟨y, he, by simp [y], rfl, ?_⟩ + intro i ha hm + exact he.regs_eq (by decide +kernel +revert) + /-- The term increment, frame-word address computation, store and phase assignment take five instructions. Only the fourth frame word changes in memory. -/ theorem exec_advance {w : ℕ} (x : Data registers) (h1 : x.regs one = 1) @@ -75,15 +93,37 @@ theorem exec_advance {w : ℕ} (x : Data registers) (h1 : x.regs one = 1) y.regs mode = 1 ∧ y.mem = Function.update x.mem (x.regs frame + 3) (x.regs term + 1) ∧ (∀ i, ¬AdvanceLocal i → y.regs i = x.regs i) := by - have hmode : Fits w 1 := ht.of_le (by omega) - have hadd : Fits w (x.regs frame + 3) := hf.of_le (by omega) - let y := (((((x.set term (x.regs term + 1)).set address (x.regs frame + 4)).set - address (x.regs frame + 3)).write (x.regs frame + 3) (x.regs term + 1)).set mode 1) - have hsub : x.regs frame + 4 - 1 = x.regs frame + 3 := by omega - have he : ExecWithin w advance x y 5 := by - ram_eval [advance, y, h1, h4, ht, hf, hmode, hadd, hsub, term, address, mode, one, - frame, frameWidth, Fin.mk.injEq] - refine ⟨y, he, ?_, by simp [y], rfl, fun i hi => he.regs_eq (advance_preserves i hi)⟩ - simp +decide [y, term, address, mode] + let z := x.set term (x.regs term + 1) + have hpre : ExecWithin w (ram[ term ← term + one ]) x z 1 := by + ram_eval [z, h1, ht] + obtain ⟨y, hy, hm, hmem, hreg⟩ := exec_storeTerm z + (by simpa +decide [z, one, term] using h1) + (by simpa +decide [z, frameWidth, term] using h4) + (by simpa +decide [z, frame, term] using hf) + have he : ExecWithin w advance x y 5 := hpre.seq hy + refine ⟨y, he, ?_, hm, ?_, fun i hi => he.regs_eq (advance_preserves i hi)⟩ + · simpa [z] using hreg term (by decide) (by decide) + · simpa +decide [z, frame, term] using hmem + +private theorem clearFinish_preserves (i : Fin registers) : + ¬AdvanceLocal i → clearValuesFinish.Preserves i := by decide +kernel +revert + +/-- Initialization uses the same actual frame store as advancement, with a zero term counter. -/ +theorem exec_clearValuesFinish {w : ℕ} (x : Data registers) (h1 : x.regs one = 1) + (h4 : x.regs frameWidth = 4) (hf : Fits w (x.regs frame + 4)) : + ∃ y, ExecWithin w clearValuesFinish x y 5 ∧ y.regs term = 0 ∧ y.regs mode = 1 ∧ + y.mem = Function.update x.mem (x.regs frame + 3) 0 ∧ + (∀ i, ¬AdvanceLocal i → y.regs i = x.regs i) := by + let z := x.set term 0 + have hpre : ExecWithin w (ram[ term ← #0 ] : Code .standard registers) x z 1 := by + ram_eval [z] + obtain ⟨y, hy, hm, hmem, hreg⟩ := exec_storeTerm z + (by simpa +decide [z, one, term] using h1) + (by simpa +decide [z, frameWidth, term] using h4) + (by simpa +decide [z, frame, term] using hf) + have he : ExecWithin w clearValuesFinish x y 5 := hpre.seq hy + refine ⟨y, he, ?_, hm, ?_, fun i hi => he.regs_eq (clearFinish_preserves i hi)⟩ + · simpa [z] using hreg term (by decide) (by decide) + · simpa +decide [z, frame, term] using hmem end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean index 6afe673bf..92630414a 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean @@ -193,11 +193,17 @@ def clearValueStep : Code .standard registers := ram[ def clearRequested : Code .standard registers := ram[ index ← #0; while index < count do [ ⟪clearValueStep⟫ ] ] +/-- Store the actual next-term counter and enter the frame's dispatch phase. -/ +def storeTerm : Code .standard registers := ram[ + address ← frame + frameWidth; address ← address - one; + *address ← term; mode ← #1 ] + +/-- Initialize the stored next-term counter after sparse accumulator clearing. -/ +def clearValuesFinish : Code .standard registers := ram[ term ← #0; ⟪storeTerm⟫ ] + /-- Initialize requested values and the frame's next-term counter. -/ def clearValues : Code .standard registers := ram[ - ⟪clearRequested⟫; - term ← #0; address ← frame + frameWidth; address ← address - one; - *address ← term; mode ← #1 ] + ⟪clearRequested⟫; ⟪clearValuesFinish⟫ ] /-- Entry phase: multiply at a leaf; otherwise initialize the requested accumulators. -/ def enter : Code .standard registers := ram[ @@ -208,8 +214,7 @@ def buildChild : Code .standard registers := scan true /-- Advance the parent's term after an empty child or after accumulating its returned values. -/ def advance : Code .standard registers := ram[ - term ← term + one; address ← frame + frameWidth; address ← address - one; - *address ← term; mode ← #1 ] + term ← term + one; ⟪storeTerm⟫ ] /-- Push a nonempty child, with the exact least-significant-first leaf leafPrefix. -/ def push : Code .standard registers := ram[ @@ -242,15 +247,21 @@ def addValueStore : Code .standard registers := ram[ def addValue : Code .standard registers := ram[ ⟪addValueLoad⟫; ⟪add⟫; ⟪addValueStore⟫ ] -/-- Traverse the parent requests once to consume a returned child's values. -/ -def accumulate : Code .standard registers := ram[ - index ← #0; - while index < count do [ - address ← listBase + index; query ← *address; digit ← query % ten; - if digit = nine then [ ⟪addValue⟫ ] else [ - if digit = term then [ ⟪addValue⟫ ] else [ temporary ← temporary ] ]; - index ← index + one ]; - ⟪advance⟫ ] +/-- Select the returned child by the same two real digit comparisons as child construction. -/ +def accumulateDispatch : Code .standard registers := ram[ + if digit = nine then [ ⟪addValue⟫ ] else [ + if digit = term then [ ⟪addValue⟫ ] else [ temporary ← temporary ] ] ] + +/-- Load, dispatch and advance one actual parent query. -/ +def accumulateStep : Code .standard registers := ram[ + ⟪scanLoad⟫; ⟪accumulateDispatch⟫; index ← index + one ] + +/-- Consume a returned child's values at the explicitly listed contributing parent queries. -/ +def accumulateLoop : Code .standard registers := ram[ + index ← #0; while index < count do [ ⟪accumulateStep⟫ ] ] + +/-- Traverse the parent requests once, then advance the stored term counter. -/ +def accumulate : Code .standard registers := ram[ ⟪accumulateLoop⟫; ⟪advance⟫ ] /-- One explicit-stack controller step. Every branch and frame address is native work. -/ def step : Code .standard registers := ram[ diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Push.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Push.lean new file mode 100644 index 000000000..ef5b11246 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Push.lean @@ -0,0 +1,84 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Control + +/-! +# Computed child-frame writes + +## Main results + +* `PrunedQuery.exec_push`: compute the next stack address, prefix and stride, store all four + child-frame words, and enter that frame in fifteen native instructions. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM + +/-- Registers changed while constructing and entering one child frame. -/ +def PushLocal (i : Fin registers) : Prop := + i = childFrame ∨ i = temporary₂ ∨ i = childPrefix ∨ i = childStride ∨ i = address ∨ + i = level ∨ i = mode + +instance (i : Fin registers) : Decidable (PushLocal i) := + inferInstanceAs (Decidable + (i = childFrame ∨ i = temporary₂ ∨ i = childPrefix ∨ i = childStride ∨ i = address ∨ + i = level ∨ i = mode)) + +private theorem push_preserves (i : Fin registers) : ¬PushLocal i → push.Preserves i := by + decide +kernel +revert + +/-- The child list's actual count is written to the computed frame, followed by the new leaf +prefix, stride and zero term counter. All scalar fit premises follow from the displayed budget. -/ +theorem exec_push {w : ℕ} (x : Data registers) (h0 : x.regs nil = 0) + (h1 : x.regs one = 1) (h4 : x.regs frameWidth = 4) (h10 : x.regs ten = 10) + (hw : Fits w (x.regs frames + x.regs nextLevel * 4 + x.regs leafPrefix + + x.regs term * x.regs stride + x.regs stride * 10 + 8)) : + ∃ y, ExecWithin w push x y 15 ∧ y.regs level = x.regs nextLevel ∧ y.regs mode = 0 ∧ + y.regs childFrame = x.regs frames + x.regs nextLevel * 4 ∧ + y.regs childPrefix = x.regs leafPrefix + x.regs term * x.regs stride ∧ + y.regs childStride = x.regs stride * 10 ∧ + y.mem = Function.update (Function.update (Function.update (Function.update x.mem + (x.regs frames + x.regs nextLevel * 4) (x.regs childCount)) + (x.regs frames + x.regs nextLevel * 4 + 1) + (x.regs leafPrefix + x.regs term * x.regs stride)) + (x.regs frames + x.regs nextLevel * 4 + 2) (x.regs stride * 10)) + (x.regs frames + x.regs nextLevel * 4 + 3) 0 ∧ + (∀ i, ¬PushLocal i → y.regs i = x.regs i) := by + let f := x.regs frames + x.regs nextLevel * 4 + let p := x.regs leafPrefix + x.regs term * x.regs stride + let s := x.regs stride * 10 + let y := (((((((((((((((x.set childFrame (x.regs nextLevel * 4)).set childFrame f).set + temporary₂ (x.regs term * x.regs stride)).set childPrefix p).set childStride s).set + address f).write f (x.regs childCount)).set address (f + 1)).write (f + 1) p).set + address (f + 2)).write (f + 2) s).set address (f + 3)).write (f + 3) 0).set + level (x.regs nextLevel)).set mode 0) + have hprod : Fits w (x.regs nextLevel * 4) := hw.of_le (by omega) + have hf (k : ℕ) (hk : k ≤ 3) : Fits w (x.regs frames + x.regs nextLevel * 4 + k) := + hw.of_le (by omega) + have hf0 : Fits w (x.regs frames + x.regs nextLevel * 4) := hw.of_le (by omega) + have ht : Fits w (x.regs term * x.regs stride) := hw.of_le (by omega) + have hp : Fits w (x.regs leafPrefix + x.regs term * x.regs stride) := hw.of_le (by omega) + have hs : Fits w (x.regs stride * 10) := hw.of_le (by omega) + have hexact : Exec w push x y 15 := by + ram_eval [push, y, f, p, s, h0, h1, h4, h10, hprod, hf0, + hf 1 (by decide), hf 2 (by decide), hf 3 (by decide), ht, hp, hs, + childFrame, temporary₂, childPrefix, childStride, address, level, mode, frames, + nextLevel, leafPrefix, term, stride, ten, one, frameWidth, childCount, nil, + Fin.mk.injEq, Nat.add_zero] + have he := hexact.within + refine ⟨y, he, ?_, by simp [y], ?_, ?_, ?_, rfl, + fun i hi => he.regs_eq (push_preserves i hi)⟩ + all_goals simp +decide [y, f, p, s, childFrame, temporary₂, childPrefix, childStride, + address, level, mode] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Weight.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Weight.lean index 50e804816..be346dda8 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Weight.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Weight.lean @@ -37,6 +37,32 @@ def traceWeight {n : ℕ} (S : Finset (OutStr n)) : ℕ := 4096 * (Pruned.totalSize (fun _ => 0) (fun _ => 0) S + (Pruned.calls n (fun _ => 0) (fun _ => 0) S).card) +/-- A timestamp budget for a node with `n` levels remaining. A leaf scans no children; +an internal node scans ten children, each of which may use its own recursive budget. -/ +def epochAllowance (n : ℕ) : ℕ := 2 * (10 ^ n - 1) + +/-- A leaf needs no new child-set timestamp. -/ +@[simp] +theorem epochAllowance_zero : epochAllowance 0 = 0 := rfl + +/-- Ten fresh child scans and all ten recursive budgets fit in the parent's allowance. -/ +theorem epochAllowance_succ (n : ℕ) : + 10 * (1 + epochAllowance n) ≤ epochAllowance (n + 1) := by + have hp : 1 ≤ (10 : ℕ) ^ n := Nat.one_le_pow n _ (by decide) + unfold epochAllowance + rw [pow_succ] + omega + +/-- Every partial traversal of the ten children fits the same concrete timestamp allowance. -/ +theorem mul_epochAllowance_le {n k : ℕ} (hk : k ≤ 10) : + k * (1 + epochAllowance n) ≤ epochAllowance (n + 1) := + (Nat.mul_le_mul_right _ hk).trans (epochAllowance_succ n) + +/-- The timestamp budget is bounded by twice the original leaf-array size. -/ +theorem epochAllowance_le (n : ℕ) : epochAllowance n ≤ 2 * 10 ^ n := by + unfold epochAllowance + omega + /-- The weight of the zero-depth root, including the empty-query boundary. -/ theorem traceWeight_zero (S : Finset (OutStr 0)) : traceWeight S = 4096 * (S.card + 1) := by simp [traceWeight, Pruned.totalSize, Pruned.calls, Pruned.run] diff --git a/ComplexityTests/SchonhagePruned.lean b/ComplexityTests/SchonhagePruned.lean index 5bfd6dc20..72bb67a7f 100644 --- a/ComplexityTests/SchonhagePruned.lean +++ b/ComplexityTests/SchonhagePruned.lean @@ -16,6 +16,10 @@ open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery namespace ComplexityTests.SchonhagePruned +-- Query codes use least-significant-first digits and retain the encoder's wildcard digit nine. +example : queryAt (L := 2) (90 : Fin 100) = ![.z 0 0, .z0] := by decide +kernel +example : (requested (L := 2) ([90, 9, 90] : List (Fin 100))).card = 2 := by decide +kernel + private def observe (w L fuel : ℕ) (a b : Fin (10 ^ L) → ℤ) (qs : List (Fin (10 ^ L))) := (program.eval w fuel (State.init (input L a b qs) : State registers).data).map fun (y, time) => ((List.range qs.length).map fun i => decode (y.mem (outputBase L qs.length + i)), time) From b3b8047e334b418bd0450a131c5a1eda905161cc Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 16:19:49 -0400 Subject: [PATCH 24/73] feat(wordram): checkpoint actual pruned-query root-frame stores WIP #1526. Factor root scan setup and root finish; prove the actual eleven-instruction root-frame initialization and exact memory/register frame. Current Root MCP diagnostics clean. Full node/controller composition remains unfinished; no PR or full-six claim. AI-assisted continuation by codex-three-sum-continuation-t3-3dda8289. --- .../Schonhage/Pruned/Program.lean | 13 +++- .../Schonhage/Pruned/Root.lean | 75 +++++++++++++++++++ 2 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Root.lean diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean index 92630414a..b99be382d 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean @@ -145,15 +145,22 @@ def scanSetup : Code .standard registers := ram[ def scan (child : Bool) : Code .standard registers := ram[ ⟪scanSetup⟫; while index < count do [ ⟪scanStep child⟫ ] ] -/-- Deduplicate supplied queries into the root list; the original list remains read-only. -/ -def rootList : Code .standard registers := ram[ - listBase ← queries; count ← queryCount; childList ← lists; ⟪scan false⟫; +/-- Point the deduplicating root scan at the actual supplied query list. -/ +def rootScanSetup : Code .standard registers := ram[ + listBase ← queries; count ← queryCount; childList ← lists ] + +/-- Store the four actual root-frame words and enter its first controller phase. -/ +def rootFinish : Code .standard registers := ram[ address ← frames; *address ← childCount; address ← address + one; *address ← nil; address ← address + one; *address ← one; address ← address + one; *address ← nil; level ← #0; mode ← #0; active ← #1 ] +/-- Deduplicate supplied queries into the root list; the original list remains read-only. -/ +def rootList : Code .standard registers := ram[ + ⟪rootScanSetup⟫; ⟪scan false⟫; ⟪rootFinish⟫ ] + /-- Load the current frame and compute its depth-specific list/value addresses. -/ def loadFrame : Code .standard registers := ram[ frame ← level * frameWidth; frame ← frames + frame; diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Root.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Root.lean new file mode 100644 index 000000000..0733ebf77 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Root.lean @@ -0,0 +1,75 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Scan +public import Complexity.Computability.WordRAM.Heap + +/-! +# Initialization of the actual root stack frame + +## Main results + +* `PrunedQuery.exec_rootFinish`: write the computed root count and enter its first phase. + +The count comes from the preceding native deduplication. The frame words are ordinary stores, +not an externally supplied traversal certificate. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM + +/-- Registers assigned after the actual root query list has been computed. -/ +def RootFinishLocal (i : Fin registers) : Prop := + i = address ∨ i = level ∨ i = mode ∨ i = active + +instance (i : Fin registers) : Decidable (RootFinishLocal i) := + inferInstanceAs (Decidable (i = address ∨ i = level ∨ i = mode ∨ i = active)) + +private theorem rootFinish_preserves (i : Fin registers) : + ¬RootFinishLocal i → rootFinish.Preserves i := by decide +kernel +revert + +/-- Eleven native instructions write the root's computed count, zero prefix, unit stride and +zero term, then set the controller to its entry phase. -/ +theorem exec_rootFinish {w : ℕ} (x : Data registers) (h0 : x.regs nil = 0) + (h1 : x.regs one = 1) (hw : Fits w (x.regs frames + 3)) : + ∃ y, ExecWithin w rootFinish x y 11 ∧ y.regs level = 0 ∧ y.regs mode = 0 ∧ + y.regs active = 1 ∧ + y.mem = Function.update (Function.update (Function.update (Function.update x.mem + (x.regs frames) (x.regs childCount)) (x.regs frames + 1) 0) + (x.regs frames + 2) 1) (x.regs frames + 3) 0 ∧ + Modifies (Block.mk (x.regs frames) 4).footprint x.mem y.mem ∧ + (∀ i, ¬RootFinishLocal i → y.regs i = x.regs i) := by + let f := x.regs frames + let y := (((((((((((x.set address f).write f (x.regs childCount)).set address (f + 1)).write + (f + 1) 0).set address (f + 2)).write (f + 2) 1).set address (f + 3)).write + (f + 3) 0).set level 0).set mode 0).set active 1) + have hconst : Fits w 1 := hw.of_le (by omega) + have hf1 : Fits w (x.regs frames + 1) := hw.of_le (by omega) + have hf2 : Fits w (x.regs frames + 2) := hw.of_le (by omega) + have he : ExecWithin w rootFinish x y 11 := by + ram_eval [rootFinish, y, f, h0, h1, hw, hconst, hf1, hf2, address, level, mode, active, + one, nil, childCount, frames, Fin.mk.injEq] + refine ⟨y, he, ?_, ?_, by simp [y], rfl, ?_, + fun i hi => he.regs_eq (rootFinish_preserves i hi)⟩ + · simp +decide [y, active, mode, level] + · simp +decide [y, active, mode] + · intro a ha + have hnot : ¬(f ≤ a ∧ a < f + 4) := by + simpa only [Block.mem_footprint, Block.Owns, f, Block.base, Block.length] using ha + have h0' : a ≠ f := by omega + have h1' : a ≠ f + 1 := by omega + have h2' : a ≠ f + 2 := by omega + have h3' : a ≠ f + 3 := by omega + simp only [y, Data.set_mem, Data.write_mem, Function.update_of_ne h3', + Function.update_of_ne h2', Function.update_of_ne h1', Function.update_of_ne h0'] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery From 66036150a12046b29986d8f108c0afaf49d01c61 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 16:35:30 -0400 Subject: [PATCH 25/73] fix(nat): reuse existing tuple digit identity in band preparation Address the sole P2 finding from the independent #1523 review of cb261f6b: remove duplicate Nat.ofDigits_ofFn and publicly import the existing radix module. The finite-function numbering bridge uses Nat.ofDigits_ofFn_eq_sum. Its statement is unchanged. Existing radix declarations, proofs and source credit are unchanged; no native program, endpoint statement or test changed. Validation on exact final tree 4807cb971cf8030f70df67e5e8fa13947dcbd202: PASS all six strict gates together (build --wfail --iofail, mk_all --check, test --wfail --iofail including global Axioms, lint, lint-style, checkInitImports), fresh actual-base changed-module lints (29 library/1 test), full citation-aware hygiene --check-simp, strict draft-name and diff checks. Serial LEAN_NUM_THREADS=1; optional cache downloads disabled. Lean MCP also reports clean diagnostics for the changed bridge. Logs are in .lake/codex-three-sum-continuation/band-review-six in the continuation checkout. Original native implementation and all full correctness/API/source-mapping review coverage retained. Review record: https://github.com/cslib-community/complexitylib/issues/1523#issuecomment-6479696830 Original implementation/validation handoff: https://github.com/cslib-community/complexitylib/issues/1523#issuecomment-6478943178 Focused final delta review pending before PR publication. No impact on selected abstracts, models or scope; no new headline. Complete #1394 native 1.9992 endpoint remains open. AI assistance: bounded reuse fix and validation by Codex session codex-three-sum-continuation-t3-3dda8289. Original author credit remains on cb261f6b. --- Complexity/Foundations/Data/Nat/OfDigits.lean | 24 ++++--------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/Complexity/Foundations/Data/Nat/OfDigits.lean b/Complexity/Foundations/Data/Nat/OfDigits.lean index 25ac68b3c..9fd7ecce8 100644 --- a/Complexity/Foundations/Data/Nat/OfDigits.lean +++ b/Complexity/Foundations/Data/Nat/OfDigits.lean @@ -7,40 +7,26 @@ Authors: Erik Demaine module -- upstream: candidate -public import Complexity.Init -public import Mathlib.Data.Nat.Digits.Defs -public import Mathlib.Algebra.BigOperators.Fin +public import Complexity.Foundations.Data.Nat.Radix /-! # Finite-function digit numbering ## Main results -* `Nat.ofDigits_ofFn`: a digit list as a finite sum of weighted digits. * `Nat.ofDigits_finFunctionFinEquiv`: agreement with Mathlib's finite-function numbering. + +The weighted-digit identity is the existing `Nat.ofDigits_ofFn_eq_sum` from fixed-length radix +arithmetic; this module supplies only its bridge to Mathlib's finite-function enumeration. -/ @[expose] public section namespace Nat -/-- Interpreting a fixed-length tuple as a least-significant-first digit list. -/ -theorem ofDigits_ofFn (b : ℕ) {L : ℕ} (f : Fin L → ℕ) : - ofDigits b (List.ofFn f) = ∑ i, f i * b ^ i.val := by - induction L with - | zero => simp - | succ L ih => - rw [List.ofFn_succ, ofDigits_cons, ih, Fin.sum_univ_succ] - simp only [Fin.val_zero, pow_zero, mul_one, Fin.val_succ, pow_succ'] - rw [Finset.mul_sum] - congr 1 - apply Finset.sum_congr rfl - intro i _ - ring - /-- `finFunctionFinEquiv` uses exactly `ofDigits`'s least-significant-first convention. -/ theorem ofDigits_finFunctionFinEquiv {b L : ℕ} (f : Fin L → Fin b) : ofDigits b (List.ofFn fun i => (f i).val) = (finFunctionFinEquiv f).val := - ofDigits_ofFn b _ + ofDigits_ofFn_eq_sum b _ end Nat From 79c9407a692ac738dc29e6153060419a4afdeeaa Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 17:17:02 -0400 Subject: [PATCH 26/73] feat(wordram): checkpoint complete native pruned-controller phases Continue #1526 implementation under #1394; this is explicitly partial WIP, not a complete component or PR submission. Full recursive node composition, initialized input/flat clients, derived global fits and total-time theorem remain open. No solver or all-state fit/execution/time certificate is assumed. Check full root setup/scan/frame (20Q+19), report (9Q+3), leaf/return (24), loaded leaf step (43), internal entry (7N+28), native nonempty child scan/push (20N+42), empty-child scan/advance (20N+33), returned-child accumulation (31N+29), and final return (26) against the actual standard fixed native Code. KernelRegs describes only constant/pointer register values. Node footprints preserve the ancestor rows. The zero-depth node matches existing Pruned.result and the finite traceWeight; its continuation uses the existing ExecWithin.loop_iter. No alternative machine or run semantics is introduced. The reporting-body/controller names are definitionally identical Code trees. ScanMemory.congr transports the existing representation across disjoint writes and removes duplicate root proof plumbing; push exposes its four-store frame. Validation: targeted Lean MCP diagnostics clean for every new/updated phase and ComplexityTests/SchonhagePruned.lean. Native interpreter regressions retain depths 0/1/2, both signs, repeated/empty/mixed queries and an outside sentinel. New kernel examples discharge the general node/register/address/signed premises, connect the same negative-product final memory to charged controller execution, and prove an empty leaf preserves the prior value cell. Diff whitespace passes. No aggregate/all-six validation claimed: root/native audit coordination hold is respected. Exact partial checkpoint tree 7fa4da67ea861a2bba3a6c430cd7e40996d0e794. Abstracts: no impact; no new headline or endpoint claim. Original 1.9992 objective and sparse-product/ET/final composition remain open under #1394. AI assistance: Codex implementation/proofs by continuation worker codex-three-sum-continuation-t3-3dda8289. --- .../Schonhage/Pruned/Child.lean | 186 ++++++++++++ .../Schonhage/Pruned/EmptyChild.lean | 165 ++++++++++ .../Schonhage/Pruned/Finish.lean | 69 +++++ .../Schonhage/Pruned/Leaf.lean | 64 ++++ .../Schonhage/Pruned/LeafStep.lean | 121 ++++++++ .../Schonhage/Pruned/Node.lean | 285 ++++++++++++++++++ .../Schonhage/Pruned/Program.lean | 19 +- .../Schonhage/Pruned/Push.lean | 14 +- .../Schonhage/Pruned/Report.lean | 148 +++++++++ .../Schonhage/Pruned/Return.lean | 177 +++++++++++ .../Schonhage/Pruned/Root.lean | 117 +++++++ .../Schonhage/Pruned/ScanMemory.lean | 17 ++ ComplexityTests/SchonhagePruned.lean | 61 ++++ 13 files changed, 1435 insertions(+), 8 deletions(-) create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Child.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/EmptyChild.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Finish.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/LeafStep.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Node.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Report.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Return.lean diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Child.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Child.lean new file mode 100644 index 000000000..3777af9de --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Child.lean @@ -0,0 +1,186 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Node +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Scan +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Push + +/-! +# Computed nonempty-child transitions of the native controller + +## Main results + +* `PrunedQuery.exec_node_child`: read the actual frame and requests, construct the child by + native digit tests/division/deduplication, and push its computed frame. + +The nonemptiness premise selects a case of the proof. The machine itself tests the count +that it has just computed; no query-selection or layout certificate is input to its code. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM + +/-- Only the next query row, timestamps and the next four-word frame may change during a +nonempty-child transition. In particular, parent queries, accumulators and frame survive. -/ +def childWrites (L f d : ℕ) : Finset ℕ := + (Block.mk (f + 4 * (L + 1) + (d + 1) * 10 ^ L) (10 ^ L)).footprint ∪ + (Block.mk (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L) (10 ^ L)).footprint ∪ + (Block.mk (f + 4 * (d + 1)) 4).footprint + +/-- This is the actual full controller transition, not a callee execution assumption. All +three controller comparisons, the complete native scan, four frame stores and index work +fit in `20 N + 42` instructions. The produced directory describes the same final memory. -/ +theorem exec_node_child {w L f a0 b0 d N p k : ℕ} (x : Data registers) + (hregs : KernelRegs L f a0 b0 x) (hd : x.regs level = d) (hdL : d < L) + (hm : x.regs mode = 1) + (hN : x.mem (f + 4 * d) = N) (hcap : N ≤ 10 ^ L) + (hpref : x.mem (f + 4 * d + 1) = p) (hp : p < 10 ^ d) + (hstride : x.mem (f + 4 * d + 2) = 10 ^ d) + (hterm : x.mem (f + 4 * d + 3) = k) (hk : k < 10) + (input : ℕ → ℕ) + (hinput : ∀ i < N, x.mem (f + 4 * (L + 1) + d * 10 ^ L + i) = input i) + (hcodes : ∀ i < N, input i < 10 ^ L) + (hmarks : ∀ q < 10 ^ L, + x.mem (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q) ≤ x.regs epoch) + (hne : (scanSet true k input N).Nonempty) + (hw : Fits w (8 * f + 64 * (L + 1) * 10 ^ L + x.regs epoch + 128)) : + ∃ y, ExecWithin w step x y (20 * N + 42) ∧ KernelRegs L f a0 b0 y ∧ + y.regs level = d + 1 ∧ y.regs mode = 0 ∧ y.regs active = x.regs active ∧ + y.regs epoch = x.regs epoch + 1 ∧ + ScanMemory y.mem (f + 4 * (L + 1) + (d + 1) * 10 ^ L) + (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L) (x.regs epoch + 1) (10 ^ L) + (scanSet true k input N) ∧ + y.mem (f + 4 * (d + 1)) = (scanSet true k input N).card ∧ + y.mem (f + 4 * (d + 1) + 1) = p + k * 10 ^ d ∧ + y.mem (f + 4 * (d + 1) + 2) = 10 ^ (d + 1) ∧ + y.mem (f + 4 * (d + 1) + 3) = 0 ∧ + Modifies (childWrites L f d) x.mem y.mem := by + have hpow : 1 ≤ (10 : ℕ) ^ L := Nat.one_le_pow L _ (by decide) + have hL : L + 1 ≤ (L + 1) * 10 ^ L := by nlinarith only [hpow] + have hmul : (d + 1) * 10 ^ L ≤ (L + 1) * 10 ^ L := + Nat.mul_le_mul_right _ (by omega) + have hnext : (10 : ℕ) ^ d * 10 ≤ 10 ^ L := by + simpa only [← pow_succ] using + Nat.pow_le_pow_right (by decide : 1 ≤ (10 : ℕ)) (by omega : d + 1 ≤ L) + have hnewp : p + k * 10 ^ d < 10 ^ L := by + nlinarith only [hp, hk, hnext] + obtain ⟨u, hu, humem, _, huc, hup, hus, hut, hul, _, hun, huch, _, hureg⟩ := + exec_loadFrame x hregs.one_eq hregs.frameWidth_eq (hw.of_le (by + rw [hregs.frames_eq, hregs.lists_eq, hregs.values_eq, hregs.span_eq, hd] + nlinarith only [hpow, hL, hmul])) + have fixed (i : Fin registers) (hi : ¬LoadFrameLocal i) : u.regs i = x.regs i := + hureg i hi + have hul' : u.regs listBase = f + 4 * (L + 1) + d * 10 ^ L := by + simpa only [hregs.lists_eq, hregs.span_eq, hd] using hul + have huch' : u.regs childList = f + 4 * (L + 1) + (d + 1) * 10 ^ L := by + simpa only [hregs.lists_eq, hregs.span_eq, hd] using huch + have hus' : u.regs stamps = f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L := + (fixed stamps (by decide)).trans hregs.stamps_eq + have huc' : u.regs count = N := by simpa only [hregs.frames_eq, hd, hN] using huc + have hup' : u.regs leafPrefix = p := by + simpa only [hregs.frames_eq, hd, hpref] using hup + have hstride' : u.regs stride = 10 ^ d := by + simpa only [hregs.frames_eq, hd, hstride] using hus + have hut' : u.regs term = k := by simpa only [hregs.frames_eq, hd, hterm] using hut + have hun' : u.regs nextLevel = d + 1 := by simpa only [hd] using hun + have hue : u.regs epoch = x.regs epoch := fixed epoch (by decide) + obtain ⟨v, hv, _, hve, hvc, htable, _, hscanFrame, hvreg⟩ := + exec_scan (N := N) (capacity := 10 ^ L) true u input huc' + (by simpa only [hul', humem] using hinput) + (fun i hi => (Nat.div_le_self _ _).trans_lt (hcodes i hi)) + (Or.inl (by rw [huch', hus']; nlinarith only [hmul, hpow])) + (Or.inl (by rw [hul', huch']; nlinarith only [hcap])) + (Or.inl (by rw [hul', hus']; nlinarith only [hcap, hmul, hpow])) + (by simpa only [hus', humem, hue] using hmarks) + ((fixed one (by decide)).trans hregs.one_eq) + ((fixed ten (by decide)).trans hregs.ten_eq) + ((fixed nine (by decide)).trans hregs.nine_eq) + (hw.of_le (by + rw [hul', huch', hus', hue] + nlinarith only [hcap, hmul, hpow, hL])) + have scanFixed (i : Fin registers) (hi : ¬ScanLocal i) : v.regs i = u.regs i := hvreg i hi + have hvf : v.regs frames = f := (scanFixed frames (by decide)).trans + ((fixed frames (by decide)).trans hregs.frames_eq) + have hvn : v.regs nextLevel = d + 1 := (scanFixed nextLevel (by decide)).trans hun' + have hvp : v.regs leafPrefix = p := (scanFixed leafPrefix (by decide)).trans hup' + have hvst : v.regs stride = 10 ^ d := (scanFixed stride (by decide)).trans hstride' + have hvt : v.regs term = k := (scanFixed term (by decide)).trans hut' + have hv0 := (scanFixed nil (by decide)).trans ((fixed nil (by decide)).trans hregs.nil_eq) + have hv1 := (scanFixed one (by decide)).trans ((fixed one (by decide)).trans hregs.one_eq) + have hv4 := (scanFixed frameWidth (by decide)).trans + ((fixed frameWidth (by decide)).trans hregs.frameWidth_eq) + have hv10 := (scanFixed ten (by decide)).trans ((fixed ten (by decide)).trans hregs.ten_eq) + have hvc' : v.regs childCount = (scanSet true k input N).card := by + simpa only [hut'] using hvc + obtain ⟨y, hy, hyl, hym, _, _, _, hmem, hpushFrame, _⟩ := exec_push v hv0 hv1 hv4 hv10 + (hw.of_le (by + rw [hvf, hvn, hvp, hvst, hvt] + nlinarith only [hcap, hpow, hL, hmul, hnext, hnewp])) + have hnonempty : (Cond.lt nil childCount).Holds v := by + simpa only [Cond.holds_lt, hv0, hvc'] using Finset.card_pos.mpr hne + have hcall : ExecWithin w dispatch u y (20 * N + 22) := by + have hpushBranch : ExecWithin w + (ram[ if nil < childCount then [ ⟪push⟫ ] else [ ⟪advance⟫ ] ]) v y 16 := + hy.branch_pos hnonempty + exact (hv.seq hpushBranch).branch_pos + (by simpa only [Cond.holds_lt, hut', (fixed ten (by decide)).trans hregs.ten_eq] using hk) + have hum : u.regs mode = 1 := (fixed mode (by decide)).trans hm + have hu0 := (fixed nil (by decide)).trans hregs.nil_eq + have hu1 := (fixed one (by decide)).trans hregs.one_eq + have hbranch : ExecWithin w + (ram[ if mode = nil then [ ⟪enter⟫ ] else [ + if mode = one then [ ⟪dispatch⟫ ] else [ ⟪accumulate⟫ ] ] ]) u y (20 * N + 25) := + (hcall.branch_pos (by simp only [Cond.holds_eq, hum, hu1])).branch_neg + (by simp only [Cond.holds_eq, hum, hu0]; decide) + have he : ExecWithin w step x y (20 * N + 42) := (hu.seq hbranch).mono (by omega) + have hfbase : v.regs frames + v.regs nextLevel * 4 = f + 4 * (d + 1) := by + rw [hvf, hvn] + omega + have hchildFrame : Modifies (Block.mk (f + 4 * (d + 1)) 4).footprint v.mem y.mem := by + simpa only [hfbase] using hpushFrame + have hlistAway (q : ℕ) : ¬(Block.mk (f + 4 * (d + 1)) 4).Owns + (f + 4 * (L + 1) + (d + 1) * 10 ^ L + q) := by + change ¬(_ ≤ _ ∧ _ < f + 4 * (d + 1) + 4) + omega + have hstampAway (q : ℕ) : ¬(Block.mk (f + 4 * (d + 1)) 4).Owns + (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q) := by + change ¬(_ ≤ _ ∧ _ < f + 4 * (d + 1) + 4) + omega + have htable' : ScanMemory v.mem (f + 4 * (L + 1) + (d + 1) * 10 ^ L) + (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L) (x.regs epoch + 1) (10 ^ L) + (scanSet true k input N) := by + simpa only [huch', hus', hue, hut'] using htable + refine ⟨y, he, hregs.of_step he, hyl.trans hvn, hym, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · exact (hy.regs_eq (by decide : push.Preserves active)).trans + ((scanFixed active (by decide)).trans (fixed active (by decide))) + · exact (hy.regs_eq (by decide : push.Preserves epoch)).trans (by + simpa only [hue] using hve) + · exact htable'.congr + (fun i _ => hchildFrame _ (by simpa only [Block.mem_footprint] using hlistAway i)) + (fun q _ => hchildFrame _ (by simpa only [Block.mem_footprint] using hstampAway q)) + · rw [hmem, hfbase, hvc'] + simp + · rw [hmem, hfbase, hvp, hvt, hvst] + simp + · rw [hmem, hfbase, hvst] + simp [pow_succ] + · rw [hmem, hfbase] + simp + · intro c hc + have hn := Finset.notMem_union.mp hc + have hs := Finset.notMem_union.mp hn.1 + rw [hchildFrame c hn.2, hscanFrame] + · exact congrFun humem c + · simpa only [Block.mem_footprint, huch'] using hs.1 + · simpa only [Block.mem_footprint, hus'] using hs.2 + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/EmptyChild.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/EmptyChild.lean new file mode 100644 index 000000000..0401bf21e --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/EmptyChild.lean @@ -0,0 +1,165 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Child + +/-! +# Actual empty-child transitions + +## Main results + +* `PrunedQuery.exec_node_emptyChild`: compute an empty child by real native scanning, + advance the parent term, and preserve its request list and partial values. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM + +/-- An empty-child transition may change the temporary child list, shared stamps and the +parent frame. Its first three frame words are also explicitly preserved below. -/ +def emptyChildWrites (L f d : ℕ) : Finset ℕ := + (Block.mk (f + 4 * (L + 1) + (d + 1) * 10 ^ L) (10 ^ L)).footprint ∪ + (Block.mk (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L) (10 ^ L)).footprint ∪ + (Block.mk (f + 4 * d) 4).footprint + +/-- The program itself observes its computed zero child count and skips the recursive push. +Every scan, comparison, increment and stored counter is charged. -/ +theorem exec_node_emptyChild {w L f a0 b0 d N k : ℕ} (x : Data registers) + (hregs : KernelRegs L f a0 b0 x) (hd : x.regs level = d) (hdL : d < L) + (hm : x.regs mode = 1) + (hN : x.mem (f + 4 * d) = N) (hcap : N ≤ 10 ^ L) + (hterm : x.mem (f + 4 * d + 3) = k) (hk : k < 10) + (input : ℕ → ℕ) + (hinput : ∀ i < N, x.mem (f + 4 * (L + 1) + d * 10 ^ L + i) = input i) + (hcodes : ∀ i < N, input i < 10 ^ L) + (hmarks : ∀ q < 10 ^ L, + x.mem (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q) ≤ x.regs epoch) + (hempty : scanSet true k input N = ∅) + (hw : Fits w (8 * f + 64 * (L + 1) * 10 ^ L + x.regs epoch + 128)) : + ∃ y, ExecWithin w step x y (20 * N + 33) ∧ KernelRegs L f a0 b0 y ∧ + y.regs level = d ∧ y.regs mode = 1 ∧ y.regs active = x.regs active ∧ + y.regs epoch = x.regs epoch + 1 ∧ + ScanMemory y.mem (f + 4 * (L + 1) + (d + 1) * 10 ^ L) + (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L) (x.regs epoch + 1) (10 ^ L) ∅ ∧ + (∀ j < 3, y.mem (f + 4 * d + j) = x.mem (f + 4 * d + j)) ∧ + y.mem (f + 4 * d + 3) = k + 1 ∧ + Modifies (emptyChildWrites L f d) x.mem y.mem := by + have hpow : 1 ≤ (10 : ℕ) ^ L := Nat.one_le_pow L _ (by decide) + have hL : L + 1 ≤ (L + 1) * 10 ^ L := by nlinarith only [hpow] + have hmul : (d + 1) * 10 ^ L ≤ (L + 1) * 10 ^ L := + Nat.mul_le_mul_right _ (by omega) + obtain ⟨u, hu, humem, huf, huc, _, _, hut, hul, _, hun, huch, _, hureg⟩ := + exec_loadFrame x hregs.one_eq hregs.frameWidth_eq (hw.of_le (by + rw [hregs.frames_eq, hregs.lists_eq, hregs.values_eq, hregs.span_eq, hd] + nlinarith only [hpow, hL, hmul])) + have fixed (i : Fin registers) (hi : ¬LoadFrameLocal i) : u.regs i = x.regs i := + hureg i hi + have hul' : u.regs listBase = f + 4 * (L + 1) + d * 10 ^ L := by + simpa only [hregs.lists_eq, hregs.span_eq, hd] using hul + have huch' : u.regs childList = f + 4 * (L + 1) + (d + 1) * 10 ^ L := by + simpa only [hregs.lists_eq, hregs.span_eq, hd] using huch + have hus' : u.regs stamps = f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L := + (fixed stamps (by decide)).trans hregs.stamps_eq + have huc' : u.regs count = N := by simpa only [hregs.frames_eq, hd, hN] using huc + have hut' : u.regs term = k := by simpa only [hregs.frames_eq, hd, hterm] using hut + have hue : u.regs epoch = x.regs epoch := fixed epoch (by decide) + obtain ⟨v, hv, _, hve, hvc, htable, _, hscanFrame, hvreg⟩ := + exec_scan (N := N) (capacity := 10 ^ L) true u input huc' + (by simpa only [hul', humem] using hinput) + (fun i hi => (Nat.div_le_self _ _).trans_lt (hcodes i hi)) + (Or.inl (by rw [huch', hus']; nlinarith only [hmul, hpow])) + (Or.inl (by rw [hul', huch']; nlinarith only [hcap])) + (Or.inl (by rw [hul', hus']; nlinarith only [hcap, hmul, hpow])) + (by simpa only [hus', humem, hue] using hmarks) + ((fixed one (by decide)).trans hregs.one_eq) + ((fixed ten (by decide)).trans hregs.ten_eq) + ((fixed nine (by decide)).trans hregs.nine_eq) + (hw.of_le (by + rw [hul', huch', hus', hue] + nlinarith only [hcap, hmul, hpow, hL])) + have scanFixed (i : Fin registers) (hi : ¬ScanLocal i) : v.regs i = u.regs i := hvreg i hi + have huf' : u.regs frame = f + 4 * d := by + simpa only [hregs.frames_eq, hd] using huf + have hvf : v.regs frame = f + 4 * d := (scanFixed frame (by decide)).trans huf' + have hvt : v.regs term = k := (scanFixed term (by decide)).trans hut' + have hv0 := (scanFixed nil (by decide)).trans ((fixed nil (by decide)).trans hregs.nil_eq) + have hv1 := (scanFixed one (by decide)).trans ((fixed one (by decide)).trans hregs.one_eq) + have hv4 := (scanFixed frameWidth (by decide)).trans + ((fixed frameWidth (by decide)).trans hregs.frameWidth_eq) + have hvc' : v.regs childCount = 0 := by + simpa only [hut', hempty, Finset.card_empty] using hvc + obtain ⟨y, hy, _, hym, hmem, _⟩ := exec_advance v hv1 hv4 + (hw.of_le (by rw [hvt]; omega)) (hw.of_le (by rw [hvf]; nlinarith only [hL, hdL])) + have hcall : ExecWithin w dispatch u y (20 * N + 13) := by + have hskip : ExecWithin w + (ram[ if nil < childCount then [ ⟪push⟫ ] else [ ⟪advance⟫ ] ]) v y 7 := + hy.branch_neg (by simp only [Cond.holds_lt, hv0, hvc', lt_self_iff_false, + not_false_eq_true]) + exact (hv.seq hskip).branch_pos + (by simpa only [Cond.holds_lt, hut', (fixed ten (by decide)).trans hregs.ten_eq] using hk) + have hum : u.regs mode = 1 := (fixed mode (by decide)).trans hm + have hu0 := (fixed nil (by decide)).trans hregs.nil_eq + have hu1 := (fixed one (by decide)).trans hregs.one_eq + have hbranch : ExecWithin w + (ram[ if mode = nil then [ ⟪enter⟫ ] else [ + if mode = one then [ ⟪dispatch⟫ ] else [ ⟪accumulate⟫ ] ] ]) u y (20 * N + 16) := + (hcall.branch_pos (by simp only [Cond.holds_eq, hum, hu1])).branch_neg + (by simp only [Cond.holds_eq, hum, hu0]; decide) + have he : ExecWithin w step x y (20 * N + 33) := (hu.seq hbranch).mono (by omega) + have hmem' : y.mem = Function.update v.mem (f + 4 * d + 3) (k + 1) := by + simpa only [hvf, hvt] using hmem + have hframe : Modifies (Block.mk (f + 4 * d) 4).footprint v.mem y.mem := by + rw [hmem'] + exact Modifies.write_of_owns (by constructor <;> omega) _ + have hlistAway (q : ℕ) : ¬(Block.mk (f + 4 * d) 4).Owns + (f + 4 * (L + 1) + (d + 1) * 10 ^ L + q) := by + change ¬(_ ≤ _ ∧ _ < f + 4 * d + 4) + omega + have hstampAway (q : ℕ) : ¬(Block.mk (f + 4 * d) 4).Owns + (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q) := by + change ¬(_ ≤ _ ∧ _ < f + 4 * d + 4) + omega + have htable' : ScanMemory v.mem (f + 4 * (L + 1) + (d + 1) * 10 ^ L) + (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L) (x.regs epoch + 1) (10 ^ L) ∅ := by + simpa only [huch', hus', hue, hut', hempty] using htable + refine ⟨y, he, hregs.of_step he, ?_, hym, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · exact (hy.regs_eq (by decide : advance.Preserves level)).trans + ((scanFixed level (by decide)).trans ((fixed level (by decide)).trans hd)) + · exact (hy.regs_eq (by decide : advance.Preserves active)).trans + ((scanFixed active (by decide)).trans (fixed active (by decide))) + · exact (hy.regs_eq (by decide : advance.Preserves epoch)).trans (by + simpa only [hue] using hve) + · exact htable'.congr + (fun i _ => hframe _ (by simpa only [Block.mem_footprint] using hlistAway i)) + (fun q _ => hframe _ (by simpa only [Block.mem_footprint] using hstampAway q)) + · intro j hj + rw [hmem', Function.update_of_ne (by omega : f + 4 * d + j ≠ f + 4 * d + 3), + hscanFrame] + · exact congrFun humem _ + · rw [huch'] + change ¬(f + 4 * (L + 1) + (d + 1) * 10 ^ L ≤ f + 4 * d + j ∧ + f + 4 * d + j < f + 4 * (L + 1) + (d + 1) * 10 ^ L + 10 ^ L) + omega + · rw [hus'] + change ¬(f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L ≤ f + 4 * d + j ∧ + f + 4 * d + j < f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + 10 ^ L) + omega + · simp only [hmem', Function.update_self] + · intro c hc + have hn := Finset.notMem_union.mp hc + have hs := Finset.notMem_union.mp hn.1 + rw [hframe c hn.2, hscanFrame] + · exact congrFun humem c + · simpa only [Block.mem_footprint, huch'] using hs.1 + · simpa only [Block.mem_footprint, hus'] using hs.2 + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Finish.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Finish.lean new file mode 100644 index 000000000..37e644600 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Finish.lean @@ -0,0 +1,69 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Node + +/-! +# Actual return after all ten child terms + +## Main results + +* `PrunedQuery.exec_node_finish`: read the completed frame and return through the real + controller comparisons, preserving all memory. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM + +/-- The stored term counter is read and compared with ten. The controller then either +terminates the root or selects the parent's accumulation phase, within 26 instructions. -/ +theorem exec_node_finish {w L f a0 b0 d : ℕ} (x : Data registers) + (hregs : KernelRegs L f a0 b0 x) (hd : x.regs level = d) (hdL : d ≤ L) + (hm : x.regs mode = 1) (hterm : x.mem (f + 4 * d + 3) = 10) + (hw : Fits w (8 * f + 64 * (L + 1) * 10 ^ L + 128)) : + ∃ y, ExecWithin w step x y 26 ∧ KernelRegs L f a0 b0 y ∧ y.mem = x.mem ∧ + y.regs level = (if d = 0 then 0 else d - 1) ∧ + y.regs mode = (if d = 0 then 1 else 2) ∧ + y.regs active = (if d = 0 then 0 else x.regs active) ∧ + y.regs epoch = x.regs epoch := by + have hpow : 1 ≤ (10 : ℕ) ^ L := Nat.one_le_pow L _ (by decide) + have hL : L + 1 ≤ (L + 1) * 10 ^ L := by nlinarith only [hpow] + have hmul : (d + 1) * 10 ^ L ≤ (L + 1) * 10 ^ L := + Nat.mul_le_mul_right _ (by omega) + obtain ⟨u, hu, humem, _, _, _, _, hut, _, _, _, _, _, hureg⟩ := + exec_loadFrame x hregs.one_eq hregs.frameWidth_eq (hw.of_le (by + rw [hregs.frames_eq, hregs.lists_eq, hregs.values_eq, hregs.span_eq, hd] + nlinarith only [hpow, hL, hmul])) + have fixed (i : Fin registers) (hi : ¬LoadFrameLocal i) : u.regs i = x.regs i := + hureg i hi + have hut' : u.regs term = 10 := by simpa only [hregs.frames_eq, hd, hterm] using hut + have hu0 := (fixed nil (by decide)).trans hregs.nil_eq + have hu1 := (fixed one (by decide)).trans hregs.one_eq + have hu10 := (fixed ten (by decide)).trans hregs.ten_eq + have hum : u.regs mode = 1 := (fixed mode (by decide)).trans hm + have hud : u.regs level = d := (fixed level (by decide)).trans hd + obtain ⟨y, hy, hymem, hyl, hym, hya, _⟩ := exec_pop u hu0 hu1 + (hw.of_le (by rw [hud]; nlinarith only [hL, hdL])) + have hcall : ExecWithin w dispatch u y 6 := hy.branch_neg + (by simp only [Cond.holds_lt, hut', hu10, lt_self_iff_false, not_false_eq_true]) + have hbranch : ExecWithin w + (ram[ if mode = nil then [ ⟪enter⟫ ] else [ + if mode = one then [ ⟪dispatch⟫ ] else [ ⟪accumulate⟫ ] ] ]) u y 9 := + (hcall.branch_pos (by simp only [Cond.holds_eq, hum, hu1])).branch_neg + (by simp only [Cond.holds_eq, hum, hu0]; decide) + have he : ExecWithin w step x y 26 := hu.seq hbranch + exact ⟨y, he, hregs.of_step he, hymem.trans humem, + by simpa only [hud] using hyl, by simpa only [hud, hum] using hym, + by simpa only [hud, fixed active (by decide)] using hya, + (hy.regs_eq (by decide : pop.Preserves epoch)).trans (fixed epoch (by decide))⟩ + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Leaf.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Leaf.lean index 8cab8f631..e21c6c9e6 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Leaf.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Leaf.lean @@ -16,6 +16,7 @@ public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Prune * `PrunedQuery.exec_leafProduct`: load the two encoded leaf words and store their exact signed product, within nineteen native instructions. +* `PrunedQuery.exec_leaf`: take the actual empty/nonempty branch and return to the parent. -/ @[expose] public section @@ -65,4 +66,67 @@ theorem exec_leafProduct {w : ℕ} {a b : ℤ} (ha : GenericRAM.Signed.Fits w a) simpa only [y, Data.write_mem, z, Data.set_mem] using congrArg (fun mem => Function.update mem (x.regs valueBase) (encode (a * b))) hframe.1 +/-- Registers used by the full leaf phase, including its actual parent-return decision. -/ +def LeafReturnLocal (i : Fin registers) : Prop := + LeafLocal i ∨ PopLocal i ∨ i = temporary + +instance (i : Fin registers) : Decidable (LeafReturnLocal i) := + inferInstanceAs (Decidable (LeafLocal i ∨ PopLocal i ∨ i = temporary)) + +private theorem leaf_preserves (i : Fin registers) : + ¬LeafReturnLocal i → leaf.Preserves i := by decide +kernel +revert + +/-- The empty leaf performs no operand loads or multiplication. A requested leaf computes +its signed product. Both execute the real return branch; root termination and parent +accumulation are decided by the stored depth register, not by an external call rule. -/ +theorem exec_leaf {w : ℕ} {a b : ℤ} (ha : GenericRAM.Signed.Fits w a) + (hb : GenericRAM.Signed.Fits w b) (hab : GenericRAM.Signed.Fits w (a * b)) + (x : Data registers) (h0 : x.regs nil = 0) (h1 : x.regs one = 1) + (hleft : x.mem (x.regs arrayA + x.regs leafPrefix) = encode a) + (hright : x.mem (x.regs arrayB + x.regs leafPrefix) = encode b) + (hp : Fits (w + 2) (x.regs arrayA + x.regs leafPrefix)) + (hq : Fits (w + 2) (x.regs arrayB + x.regs leafPrefix)) + (hd : Fits (w + 2) (x.regs level + 2)) : + ∃ y, ExecWithin (w + 2) leaf x y 24 ∧ + y.mem = (if x.regs count = 0 then x.mem else + Function.update x.mem (x.regs valueBase) (encode (a * b))) ∧ + y.regs level = (if x.regs level = 0 then 0 else x.regs level - 1) ∧ + y.regs mode = (if x.regs level = 0 then x.regs mode else 2) ∧ + y.regs active = (if x.regs level = 0 then 0 else x.regs active) ∧ + (∀ i, ¬LeafReturnLocal i → y.regs i = x.regs i) := by + have hpre : ∃ v, ExecWithin (w + 2) + (ram[ if nil < count then [ ⟪leafProduct⟫ ] else [ temporary ← temporary ] ]) + x v 20 ∧ + v.mem = (if x.regs count = 0 then x.mem else + Function.update x.mem (x.regs valueBase) (encode (a * b))) ∧ + (∀ i, ¬LeafLocal i → v.regs i = x.regs i) := by + by_cases hc : x.regs count = 0 + · have hcopy : ExecWithin (w + 2) + (ram[ temporary ← temporary ] : Code .standard registers) x x 1 := by + simpa only [Data.set, Function.update_eq_self] using + (Exec.copy (w := w + 2) (B := .standard) (x := x) + (d := temporary) (s := temporary)).within + have he : ExecWithin (w + 2) + (ram[ if nil < count then [ ⟪leafProduct⟫ ] else [ temporary ← temporary ] ]) + x x 3 := by + exact hcopy.branch_neg (by simp only [Cond.holds_lt, h0, hc, lt_self_iff_false, + not_false_eq_true]) + exact ⟨x, he.mono (by decide), by simp only [hc, ↓reduceIte], fun _ _ => rfl⟩ + · obtain ⟨v, hv, hmem, hreg⟩ := exec_leafProduct ha hb hab x hleft hright hp hq + have hcond : (Cond.lt nil count).Holds x := by + simpa only [Cond.holds_lt, h0] using Nat.pos_of_ne_zero hc + exact ⟨v, hv.branch_pos hcond, by simpa only [hc, ↓reduceIte] using hmem, hreg⟩ + obtain ⟨v, hv, hmem, hreg⟩ := hpre + have hv0 := (hreg nil (by decide)).trans h0 + have hv1 := (hreg one (by decide)).trans h1 + have hvd := hreg level (by decide) + have hvm := hreg mode (by decide) + have hva := hreg active (by decide) + obtain ⟨y, hy, hymem, hyl, hym, hya, _⟩ := exec_pop v hv0 hv1 + (by simpa only [hvd] using hd) + have he : ExecWithin (w + 2) leaf x y 24 := hv.seq hy + exact ⟨y, he, hymem.trans hmem, by simpa only [hvd] using hyl, + by simpa only [hvd, hvm] using hym, by simpa only [hvd, hva] using hya, + fun i hi => he.regs_eq (leaf_preserves i hi)⟩ + end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/LeafStep.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/LeafStep.lean new file mode 100644 index 000000000..014e4c5e7 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/LeafStep.lean @@ -0,0 +1,121 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Frame +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Leaf + +/-! +# The terminal case of the actual pruned-query controller + +## Main results + +* `PrunedQuery.exec_leafStep`: load the stored frame, select the leaf phase and return. +* `PrunedQuery.exec_controller_leaf`: prepend this real iteration to the controller's + remaining execution, with its charged loop test. + +These use the existing native `ExecWithin` semantics throughout. The continuation is a +conclusion of the execution proof, rather than a hypothesis supplying a solver or its cost. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Registers written by frame loading and a terminal controller phase. -/ +def LeafStepLocal (i : Fin registers) : Prop := LoadFrameLocal i ∨ LeafReturnLocal i + +instance (i : Fin registers) : Decidable (LeafStepLocal i) := + inferInstanceAs (Decidable (LoadFrameLocal i ∨ LeafReturnLocal i)) + +/-- The actual controller reads its frame and takes both real entry/leaf comparisons. +The only possible store is the requested product at this depth's value-row base. -/ +theorem exec_leafStep {w p : ℕ} {a b : ℤ} (ha : GenericRAM.Signed.Fits w a) + (hb : GenericRAM.Signed.Fits w b) (hab : GenericRAM.Signed.Fits w (a * b)) + (x : Data registers) (h0 : x.regs nil = 0) (h1 : x.regs one = 1) + (h4 : x.regs frameWidth = 4) (hm : x.regs mode = 0) + (hd : x.regs level = x.regs inputDepth) + (hpref : x.mem (x.regs frames + 4 * x.regs level + 1) = p) + (hleft : x.mem (x.regs arrayA + p) = encode a) + (hright : x.mem (x.regs arrayB + p) = encode b) + (hw : Fits (w + 2) (x.regs frames + 4 * (x.regs level + 1) + x.regs lists + + x.regs values + (x.regs level + 1) * x.regs span + x.regs level + + x.regs arrayA + p + x.regs arrayB + p + 12)) : + ∃ y, ExecWithin (w + 2) step x y 43 ∧ + y.mem = (if x.mem (x.regs frames + 4 * x.regs level) = 0 then x.mem else + Function.update x.mem (x.regs values + x.regs level * x.regs span) + (encode (a * b))) ∧ + y.regs level = (if x.regs level = 0 then 0 else x.regs level - 1) ∧ + y.regs mode = (if x.regs level = 0 then 0 else 2) ∧ + y.regs active = (if x.regs level = 0 then 0 else x.regs active) ∧ + (∀ i, ¬LeafStepLocal i → y.regs i = x.regs i) := by + obtain ⟨u, hu, humem, _, huc, hup, _, _, _, huv, _, _, _, hureg⟩ := + exec_loadFrame x h1 h4 (hw.of_le (by omega)) + have fixed (i : Fin registers) (hi : ¬LoadFrameLocal i) : u.regs i = x.regs i := + hureg i hi + have hu0 := (fixed nil (by decide)).trans h0 + have hu1 := (fixed one (by decide)).trans h1 + have hud := fixed level (by decide) + have hum := (fixed mode (by decide)).trans hm + have hua := fixed active (by decide) + have huA := fixed arrayA (by decide) + have huB := fixed arrayB (by decide) + have hup' : u.regs leafPrefix = p := hup.trans hpref + obtain ⟨y, hy, hymem, hyl, hym, hya, hyreg⟩ := exec_leaf ha hb hab u hu0 hu1 + (by simpa only [humem, huA, hup'] using hleft) + (by simpa only [humem, huB, hup'] using hright) + (hw.of_le (by rw [huA, hup']; omega)) + (hw.of_le (by rw [huB, hup']; omega)) + (hw.of_le (by rw [hud]; omega)) + have hentry : ExecWithin (w + 2) enter u y 25 := + hy.branch_pos (by simpa only [Cond.holds_eq, hud, fixed inputDepth (by decide)] using hd) + have hbranch : ExecWithin (w + 2) + (ram[ if mode = nil then [ ⟪enter⟫ ] else [ + if mode = one then [ ⟪dispatch⟫ ] else [ ⟪accumulate⟫ ] ] ]) u y 26 := + hentry.branch_pos (by simp only [Cond.holds_eq, hum, hu0]) + refine ⟨y, hu.seq hbranch, ?_, ?_, ?_, ?_, ?_⟩ + · simpa only [huc, huv, humem] using hymem + · simpa only [hud] using hyl + · simpa only [hud, hum] using hym + · simpa only [hud, hua] using hya + · intro i hi + exact (hyreg i (fun h => hi (Or.inr h))).trans + (hureg i (fun h => hi (Or.inl h))) + +/-- A terminal node is one actual iteration of the existing native loop. Its 43 body +instructions plus the two loop instructions are charged before any continuation. -/ +theorem exec_controller_leaf {w p : ℕ} {a b : ℤ} (ha : GenericRAM.Signed.Fits w a) + (hb : GenericRAM.Signed.Fits w b) (hab : GenericRAM.Signed.Fits w (a * b)) + (x : Data registers) (h0 : x.regs nil = 0) (h1 : x.regs one = 1) + (h4 : x.regs frameWidth = 4) (hm : x.regs mode = 0) + (hd : x.regs level = x.regs inputDepth) (hactive : x.regs active = 1) + (hpref : x.mem (x.regs frames + 4 * x.regs level + 1) = p) + (hleft : x.mem (x.regs arrayA + p) = encode a) + (hright : x.mem (x.regs arrayB + p) = encode b) + (hw : Fits (w + 2) (x.regs frames + 4 * (x.regs level + 1) + x.regs lists + + x.regs values + (x.regs level + 1) * x.regs span + x.regs level + + x.regs arrayA + p + x.regs arrayB + p + 12)) : + ∃ y, y.mem = (if x.mem (x.regs frames + 4 * x.regs level) = 0 then x.mem else + Function.update x.mem (x.regs values + x.regs level * x.regs span) + (encode (a * b))) ∧ + y.regs level = (if x.regs level = 0 then 0 else x.regs level - 1) ∧ + y.regs mode = (if x.regs level = 0 then 0 else 2) ∧ + y.regs active = (if x.regs level = 0 then 0 else 1) ∧ + (∀ i, ¬LeafStepLocal i → y.regs i = x.regs i) ∧ + ∀ z U, ExecWithin (w + 2) controller y z U → + ExecWithin (w + 2) controller x z (45 + U) := by + obtain ⟨y, hy, hmem, hyl, hym, hya, hreg⟩ := + exec_leafStep ha hb hab x h0 h1 h4 hm hd hpref hleft hright hw + refine ⟨y, hmem, hyl, hym, by simpa only [hactive] using hya, hreg, ?_⟩ + intro z U hz + have he := ExecWithin.loop_iter (by simp only [Cond.holds_lt, h0, hactive]; decide) hy hz + exact he.mono (by omega) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Node.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Node.lean new file mode 100644 index 000000000..35ef19f19 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Node.lean @@ -0,0 +1,285 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.LeafStep +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Clear +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ScanMemory +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Weight +public import Complexity.Computability.WordRAM.Heap + +/-! +# Memory and register invariants for actual controller nodes + +## Main definitions + +* `PrunedQuery.KernelRegs`: the unchanged native parameters and computed workspace pointers. +* `PrunedQuery.nodeWrites`: the descendant rows that one node may change. + +## Main results + +* `PrunedQuery.exec_node_zero`: the terminal node case, with the existing mathematical result, + concrete frame and timestamp preservation, and charged native continuation. + +The invariants describe ordinary register and memory values. They contain no execution, +solver, time-bound or all-state-fit field. Execution is proved separately in native semantics. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Fixed caller parameters and the explicit workspace partition computed by `locate`. +This is a register-value assertion, with no assumed execution or resource guarantee. -/ +structure KernelRegs (L f a b : ℕ) (x : Data registers) : Prop where + /-- Original recursion depth. -/ + depth_eq : x.regs inputDepth = L + /-- Actual source-array and row capacity. -/ + span_eq : x.regs span = 10 ^ L + /-- Left input-array pointer. -/ + left_eq : x.regs arrayA = a + /-- Right input-array pointer. -/ + right_eq : x.regs arrayB = b + /-- First stored frame. -/ + frames_eq : x.regs frames = f + /-- First query row, after the four-word frames. -/ + lists_eq : x.regs lists = f + 4 * (L + 1) + /-- First value row, after all query rows. -/ + values_eq : x.regs values = f + 4 * (L + 1) + (L + 1) * 10 ^ L + /-- Shared timestamp directory, after all value rows. -/ + stamps_eq : x.regs stamps = f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + /-- Constant zero. -/ + nil_eq : x.regs nil = 0 + /-- Constant one. -/ + one_eq : x.regs one = 1 + /-- Constant ten. -/ + ten_eq : x.regs ten = 10 + /-- Constant inner-output digit. -/ + nine_eq : x.regs nine = 9 + /-- Four ordinary words per frame. -/ + frameWidth_eq : x.regs frameWidth = 4 + +/-- The exact permitted workspace rows for a node at depth `d`. Its own query row is read-only; +descendant query rows, current/descendant value rows, frame suffix and timestamps may change. -/ +def nodeWrites (L f d : ℕ) : Finset ℕ := + (Block.mk (f + 4 * d) (4 * (L + 1 - d))).footprint ∪ + (Block.mk (f + 4 * (L + 1) + (d + 1) * 10 ^ L) ((L - d) * 10 ^ L)).footprint ∪ + (Block.mk (f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L) + ((L + 1 - d) * 10 ^ L)).footprint ∪ + (Block.mk (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L) (10 ^ L)).footprint + +private theorem KernelRegs.of_leafStep {L f a b : ℕ} {x y : Data registers} + (h : KernelRegs L f a b x) + (hy : ∀ i, ¬LeafStepLocal i → y.regs i = x.regs i) : KernelRegs L f a b y := by + constructor + · exact (hy inputDepth (by decide)).trans h.depth_eq + · exact (hy span (by decide)).trans h.span_eq + · exact (hy arrayA (by decide)).trans h.left_eq + · exact (hy arrayB (by decide)).trans h.right_eq + · exact (hy frames (by decide)).trans h.frames_eq + · exact (hy lists (by decide)).trans h.lists_eq + · exact (hy values (by decide)).trans h.values_eq + · exact (hy stamps (by decide)).trans h.stamps_eq + · exact (hy nil (by decide)).trans h.nil_eq + · exact (hy one (by decide)).trans h.one_eq + · exact (hy ten (by decide)).trans h.ten_eq + · exact (hy nine (by decide)).trans h.nine_eq + · exact (hy frameWidth (by decide)).trans h.frameWidth_eq + +/-- Every branch of the actual controller preserves the fixed caller parameters. -/ +theorem KernelRegs.of_step {w T L f a b : ℕ} {x y : Data registers} + (h : KernelRegs L f a b x) (he : ExecWithin w step x y T) : KernelRegs L f a b y := by + constructor + · exact (he.regs_eq (by decide : step.Preserves inputDepth)).trans h.depth_eq + · exact (he.regs_eq (by decide : step.Preserves span)).trans h.span_eq + · exact (he.regs_eq (by decide : step.Preserves arrayA)).trans h.left_eq + · exact (he.regs_eq (by decide : step.Preserves arrayB)).trans h.right_eq + · exact (he.regs_eq (by decide : step.Preserves frames)).trans h.frames_eq + · exact (he.regs_eq (by decide : step.Preserves lists)).trans h.lists_eq + · exact (he.regs_eq (by decide : step.Preserves values)).trans h.values_eq + · exact (he.regs_eq (by decide : step.Preserves stamps)).trans h.stamps_eq + · exact (he.regs_eq (by decide : step.Preserves nil)).trans h.nil_eq + · exact (he.regs_eq (by decide : step.Preserves one)).trans h.one_eq + · exact (he.regs_eq (by decide : step.Preserves ten)).trans h.ten_eq + · exact (he.regs_eq (by decide : step.Preserves nine)).trans h.nine_eq + · exact (he.regs_eq (by decide : step.Preserves frameWidth)).trans h.frameWidth_eq + +/-- The terminal induction case is a proved actual controller iteration. The requested values +are the existing `Pruned.result`; an empty set leaves the value row untouched. The memory +frame and timestamp bound hold at the same final state as the charged continuation. -/ +theorem exec_node_zero {w L f a0 b0 p E : ℕ} {a b : ℤ} + (ha : GenericRAM.Signed.Fits w a) (hb : GenericRAM.Signed.Fits w b) + (hab : GenericRAM.Signed.Fits w (a * b)) + (S : Finset (OutStr 0)) (x : Data registers) (hregs : KernelRegs L f a0 b0 x) + (hd : x.regs level = L) (hm : x.regs mode = 0) (hactive : x.regs active = 1) + (hcount : x.mem (f + 4 * L) = S.card) (hpref : x.mem (f + 4 * L + 1) = p) + (hleft : x.mem (a0 + p) = encode a) (hright : x.mem (b0 + p) = encode b) + (hE : x.regs epoch = E) + (hmarks : ∀ q < 10 ^ L, + x.mem (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q) ≤ E) + (hw : Fits (w + 2) (f + 4 * (L + 1) + (f + 4 * (L + 1)) + + (f + 4 * (L + 1) + (L + 1) * 10 ^ L) + (L + 1) * 10 ^ L + L + + a0 + p + b0 + p + 12)) : + ∃ y, KernelRegs L f a0 b0 y ∧ + y.regs level = (if L = 0 then 0 else L - 1) ∧ + y.regs mode = (if L = 0 then 0 else 2) ∧ + y.regs active = (if L = 0 then 0 else 1) ∧ y.regs epoch = E ∧ + (∀ q < 10 ^ L, + y.mem (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q) ≤ E) ∧ + (∀ q ∈ S, y.mem (f + 4 * (L + 1) + (L + 1) * 10 ^ L + L * 10 ^ L + + (queryIndex q).val) = encode (Pruned.result 0 (fun _ => a) (fun _ => b) S q)) ∧ + Modifies (nodeWrites L f L) x.mem y.mem ∧ + (∀ i, ¬LeafStepLocal i → y.regs i = x.regs i) ∧ + ∀ z U, ExecWithin (w + 2) controller y z U → + ExecWithin (w + 2) controller x z (traceWeight S + U) := by + obtain ⟨y, hmem, hyl, hym, hya, hreg, hrun⟩ := exec_controller_leaf ha hb hab x + hregs.nil_eq hregs.one_eq hregs.frameWidth_eq hm (hd.trans hregs.depth_eq.symm) + hactive + (by simpa only [hregs.frames_eq, hd] using hpref) + (by simpa only [hregs.left_eq] using hleft) + (by simpa only [hregs.right_eq] using hright) + (by simpa only [hregs.frames_eq, hregs.lists_eq, hregs.values_eq, hregs.span_eq, + hregs.left_eq, hregs.right_eq, hd] using hw) + have hm' : y.mem = if S.card = 0 then x.mem else + Function.update x.mem (f + 4 * (L + 1) + (L + 1) * 10 ^ L + L * 10 ^ L) + (encode (a * b)) := by + simpa only [hregs.frames_eq, hd, hcount, hregs.values_eq, hregs.span_eq] using hmem + have hpow : 0 < (10 : ℕ) ^ L := by positivity + have haway (q : ℕ) : f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q ≠ + f + 4 * (L + 1) + (L + 1) * 10 ^ L + L * 10 ^ L := by nlinarith + refine ⟨y, hregs.of_leafStep hreg, ?_, ?_, ?_, ?_, ?_, ?_, ?_, hreg, ?_⟩ + · simpa only [hd] using hyl + · simpa only [hd] using hym + · simpa only [hd] using hya + · exact (hreg epoch (by decide)).trans hE + · intro q hq + rw [hm'] + split_ifs + · exact hmarks q hq + · rw [Function.update_of_ne (haway q)] + exact hmarks q hq + · intro q hq + have hc : S.card ≠ 0 := Finset.card_ne_zero.mpr ⟨q, hq⟩ + have hq0 : (queryIndex q).val = 0 := by have := (queryIndex q).isLt; omega + simp only [hm', hc, ↓reduceIte, hq0, Nat.add_zero, Function.update_self, + Pruned.result, Pruned.run, Finset.piecewise, hq] + · intro c hc + rw [hm'] + split_ifs + · rfl + · apply Function.update_of_ne + intro he + apply hc + apply Finset.mem_union_left + apply Finset.mem_union_right + rw [Block.mem_footprint] + change _ ≤ c ∧ c < _ + subst c + simp only [Nat.add_sub_cancel_left] + constructor <;> omega + · intro z U hz + exact (hrun z U hz).mono (by rw [traceWeight_zero]; omega) + +/-- Enter an internal node by clearing only its listed accumulators and storing the actual +zero term counter. This is a complete controller step, including frame loads and comparisons. +Its displayed address budget contains only ordinary workspace parameters and list length. -/ +theorem exec_node_enter {w L f a0 b0 d N : ℕ} (x : Data registers) + (hregs : KernelRegs L f a0 b0 x) (hd : x.regs level = d) (hdL : d < L) + (hm : x.regs mode = 0) (hN : x.mem (f + 4 * d) = N) (hcap : N ≤ 10 ^ L) + (input : ℕ → ℕ) + (hinput : ∀ i < N, x.mem (f + 4 * (L + 1) + d * 10 ^ L + i) = input i) + (hcodes : ∀ i < N, input i < 10 ^ L) + (hw : Fits w (8 * f + 64 * (L + 1) * 10 ^ L + 128)) : + ∃ y, ExecWithin w step x y (7 * N + 28) ∧ KernelRegs L f a0 b0 y ∧ + y.regs level = d ∧ y.regs mode = 1 ∧ y.regs epoch = x.regs epoch ∧ + y.regs active = x.regs active ∧ + y.mem = Function.update + (zeroQueryPrefix x.mem (f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L) + input N) (f + 4 * d + 3) 0 ∧ + (∀ i < N, y.mem (f + 4 * (L + 1) + d * 10 ^ L + i) = input i) ∧ + Modifies (nodeWrites L f d) x.mem y.mem := by + have hpow : 1 ≤ (10 : ℕ) ^ L := Nat.one_le_pow L _ (by decide) + have hmul : (d + 1) * 10 ^ L ≤ (L + 1) * 10 ^ L := + Nat.mul_le_mul_right _ (by omega) + have hL : L + 1 ≤ (L + 1) * 10 ^ L := by nlinarith only [hpow] + obtain ⟨u, hu, humem, huf, huc, _, _, _, hul, huv, _, _, _, hureg⟩ := + exec_loadFrame x hregs.one_eq hregs.frameWidth_eq (hw.of_le (by + rw [hregs.frames_eq, hregs.lists_eq, hregs.values_eq, hregs.span_eq, hd] + nlinarith only [hpow, hmul, hL, hdL, hcap])) + have fixed (i : Fin registers) (hi : ¬LoadFrameLocal i) : u.regs i = x.regs i := + hureg i hi + have huf' : u.regs frame = f + 4 * d := by simpa only [hregs.frames_eq, hd] using huf + have hul' : u.regs listBase = f + 4 * (L + 1) + d * 10 ^ L := by + simpa only [hregs.lists_eq, hregs.span_eq, hd] using hul + have huv' : u.regs valueBase = f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L := by + simpa only [hregs.values_eq, hregs.span_eq, hd] using huv + have hc : u.regs count = N := by simpa only [hregs.frames_eq, hd, hN] using huc + have hu0 := (fixed nil (by decide)).trans hregs.nil_eq + have hu1 := (fixed one (by decide)).trans hregs.one_eq + have hu4 := (fixed frameWidth (by decide)).trans hregs.frameWidth_eq + obtain ⟨y, hy, _, _, hym, hymem, hyread, _⟩ := exec_clearValues u input hc + (by simpa only [hul', humem] using hinput) hcodes + (Or.inl (by rw [hul', huv']; nlinarith only [hpow, hmul, hL, hdL, hcap])) + (Or.inl (by rw [huf', hul']; omega)) hu0 hu1 hu4 + (hw.of_le (by rw [hul', huv']; nlinarith only [hpow, hmul, hL, hdL, hcap])) + (hw.of_le (by rw [huf']; nlinarith only [hpow, hmul, hL, hdL, hcap])) + have hnotLeaf : ¬(Cond.eq level inputDepth).Holds u := by + simp only [Cond.holds_eq, fixed level (by decide), fixed inputDepth (by decide), + hd, hregs.depth_eq] + omega + have hentry : ExecWithin w enter u y (7 * N + 10) := hy.branch_neg hnotLeaf + have hbranch : ExecWithin w + (ram[ if mode = nil then [ ⟪enter⟫ ] else [ + if mode = one then [ ⟪dispatch⟫ ] else [ ⟪accumulate⟫ ] ] ]) u y (7 * N + 11) := + hentry.branch_pos (by simp only [Cond.holds_eq, fixed mode (by decide), + fixed nil (by decide), hm, hregs.nil_eq]) + have he : ExecWithin w step x y (7 * N + 28) := (hu.seq hbranch).mono (by omega) + have hmem : y.mem = Function.update + (zeroQueryPrefix x.mem (f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L) + input N) (f + 4 * d + 3) 0 := by + simpa only [humem, huv', huf'] using hymem + refine ⟨y, he, hregs.of_step he, ?_, hym, ?_, ?_, hmem, ?_, ?_⟩ + · exact ((hy.regs_eq (by decide : clearValues.Preserves level)).trans + (fixed level (by decide))).trans hd + · exact (hy.regs_eq (by decide : clearValues.Preserves epoch)).trans + (fixed epoch (by decide)) + · exact (hy.regs_eq (by decide : clearValues.Preserves active)).trans + (fixed active (by decide)) + · simpa only [hul'] using hyread + · intro c hc' + have hf : ¬(Block.mk (f + 4 * d) (4 * (L + 1 - d))).Owns c := by + intro h + exact hc' (Finset.mem_union_left _ (Finset.mem_union_left _ + (Finset.mem_union_left _ (Block.mem_footprint.mpr h)))) + have hv : ¬(Block.mk + (f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L) (10 ^ L)).Owns c := by + intro h + apply hc' + apply Finset.mem_union_left + apply Finset.mem_union_right + rw [Block.mem_footprint] + change f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L ≤ c ∧ + c < f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L + + (L + 1 - d) * 10 ^ L + change f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L ≤ c ∧ + c < f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L + 10 ^ L at h + have hrem : 1 ≤ L + 1 - d := by omega + have hm' := Nat.mul_le_mul_right (10 ^ L) hrem + exact ⟨h.1, by omega⟩ + rw [hmem, Function.update_of_ne (by + intro heq + apply hf + change f + 4 * d ≤ c ∧ c < f + 4 * d + 4 * (L + 1 - d) + have hrem : 1 ≤ L + 1 - d := by omega + omega)] + exact zeroQueryPrefix_outside hcodes hv + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean index b99be382d..5575404e7 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean @@ -276,18 +276,23 @@ def step : Code .standard registers := ram[ if mode = nil then [ ⟪enter⟫ ] else [ if mode = one then [ ⟪dispatch⟫ ] else [ ⟪accumulate⟫ ] ] ] +/-- One actual lookup and indexed output store, including the reporting counter. -/ +def reportStep : Code .standard registers := ram[ + address ← queries + index; query ← *address; + address ← values + query; temporary ← *address; + address ← output + index; *address ← temporary; index ← index + one ] + /-- Report results in the original input query order, including repeated queries. -/ def report : Code .standard registers := ram[ - index ← #0; - while index < queryCount do [ - address ← queries + index; query ← *address; - address ← values + query; temporary ← *address; - address ← output + index; *address ← temporary; index ← index + one ] ] + index ← #0; while index < queryCount do [ ⟪reportStep⟫ ] ] /-- The common stack evaluator after powers and caller parameters have been initialized. -/ +def controller : Code .standard registers := ram[ + while nil < active do [ ⟪step⟫ ] ] + +/-- Evaluate the computed root, then report the original query list. -/ def work : Code .standard registers := ram[ - ⟪locate⟫; ⟪rootList⟫; - while nil < active do [ ⟪step⟫ ]; ⟪report⟫ ] + ⟪locate⟫; ⟪rootList⟫; ⟪controller⟫; ⟪report⟫ ] /-- Generic native evaluator with explicit pointer arguments and a fresh timestamp epoch. -/ def kernel : Code .standard registers := ram[ ⟪powers⟫; ⟪work⟫ ] diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Push.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Push.lean index ef5b11246..252435cd0 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Push.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Push.lean @@ -8,6 +8,7 @@ module -- upstream: candidate public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Control +public import Complexity.Computability.WordRAM.Heap /-! # Computed child-frame writes @@ -53,6 +54,7 @@ theorem exec_push {w : ℕ} (x : Data registers) (h0 : x.regs nil = 0) (x.regs leafPrefix + x.regs term * x.regs stride)) (x.regs frames + x.regs nextLevel * 4 + 2) (x.regs stride * 10)) (x.regs frames + x.regs nextLevel * 4 + 3) 0 ∧ + Modifies (Block.mk (x.regs frames + x.regs nextLevel * 4) 4).footprint x.mem y.mem ∧ (∀ i, ¬PushLocal i → y.regs i = x.regs i) := by let f := x.regs frames + x.regs nextLevel * 4 let p := x.regs leafPrefix + x.regs term * x.regs stride @@ -76,7 +78,17 @@ theorem exec_push {w : ℕ} (x : Data registers) (h0 : x.regs nil = 0) nextLevel, leafPrefix, term, stride, ten, one, frameWidth, childCount, nil, Fin.mk.injEq, Nat.add_zero] have he := hexact.within - refine ⟨y, he, ?_, by simp [y], ?_, ?_, ?_, rfl, + have hmod : Modifies (Block.mk f 4).footprint x.mem y.mem := by + intro c hc + have hnot : ¬(f ≤ c ∧ c < f + 4) := by + simpa only [Block.mem_footprint, Block.Owns, Block.base, Block.length] using hc + have h0' : c ≠ f := by omega + have h1' : c ≠ f + 1 := by omega + have h2' : c ≠ f + 2 := by omega + have h3' : c ≠ f + 3 := by omega + simp only [y, Data.set_mem, Data.write_mem, Function.update_of_ne h3', + Function.update_of_ne h2', Function.update_of_ne h1', Function.update_of_ne h0'] + refine ⟨y, he, ?_, by simp [y], ?_, ?_, ?_, rfl, hmod, fun i hi => he.regs_eq (push_preserves i hi)⟩ all_goals simp +decide [y, f, p, s, childFrame, temporary₂, childPrefix, childStride, address, level, mode] diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Report.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Report.lean new file mode 100644 index 000000000..20563c2d6 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Report.lean @@ -0,0 +1,148 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Memory +public import Complexity.Computability.WordRAM.Heap + +/-! +# Returning queried values in the actual input order + +## Main results + +* `PrunedQuery.exec_report`: the native reporting loop preserves duplicate requests and writes + their corresponding values in order, charging every load, address, store and loop test. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM + +/-- Registers used while reporting the explicitly supplied query list. -/ +def ReportLocal (i : Fin registers) : Prop := + i = index ∨ i = address ∨ i = query ∨ i = temporary + +instance (i : Fin registers) : Decidable (ReportLocal i) := + inferInstanceAs (Decidable (i = index ∨ i = address ∨ i = query ∨ i = temporary)) + +private theorem reportStep_preserves (i : Fin registers) : + ¬ReportLocal i → reportStep.Preserves i := by decide +kernel +revert + +private theorem report_preserves (i : Fin registers) : + ¬ReportLocal i → report.Preserves i := by decide +kernel +revert + +/-- The finite native loop reads the original requests, including duplicates. Its only writes +are the `N` output cells; the value directory and original requests remain unchanged. -/ +theorem exec_report {w N capacity : ℕ} (x : Data registers) (codes : ℕ → ℕ) + (hN : x.regs queryCount = N) + (hinput : ∀ i < N, x.mem (x.regs queries + i) = codes i) + (hcodes : ∀ i < N, codes i < capacity) + (hsrc : x.regs queries + N ≤ x.regs output ∨ + x.regs output + N ≤ x.regs queries) + (hvals : x.regs values + capacity ≤ x.regs output ∨ + x.regs output + N ≤ x.regs values) + (h1 : x.regs one = 1) + (hw : Fits w (x.regs queries + N + x.regs values + capacity + + x.regs output + N + N + 12)) : + ∃ y, ExecWithin w report x y (9 * N + 3) ∧ y.regs index = N ∧ + (∀ i < N, y.mem (x.regs output + i) = x.mem (x.regs values + codes i)) ∧ + Modifies (Block.mk (x.regs output) N).footprint x.mem y.mem ∧ + (∀ i, ¬ReportLocal i → y.regs i = x.regs i) := by + let u := x.set index 0 + have hsetup : ExecWithin w (ram[ index ← #0 ] : Code .standard registers) x u 1 := by + ram_eval [u] + let I (y : Data registers) : Prop := + y.regs index ≤ N ∧ + (∀ i < y.regs index, y.mem (x.regs output + i) = x.mem (x.regs values + codes i)) ∧ + (∀ i < N, y.mem (x.regs queries + i) = codes i) ∧ + (∀ q < capacity, y.mem (x.regs values + q) = x.mem (x.regs values + q)) ∧ + Modifies (Block.mk (x.regs output) N).footprint x.mem y.mem ∧ + (∀ i, ¬ReportLocal i → y.regs i = x.regs i) + have hinit : I u := by + refine ⟨by simp [u], ?_, hinput, fun _ _ => rfl, Modifies.refl _ _, ?_⟩ + · simp only [u, Data.set_regs, Function.update_self, not_lt_zero, false_implies, + implies_true] + · intro i hi + exact Function.update_of_ne (by intro he; exact hi (Or.inl he)) _ _ + have hstep (y : Data registers) (hy : I y) + (hc : (Cond.lt index queryCount).Holds y) : + ∃ z, ExecWithin w reportStep y z 7 ∧ I z ∧ N - z.regs index < N - y.regs index := by + obtain ⟨hle, hdone, hread, hvalue, hframe, hfrozen⟩ := hy + have hq := hfrozen queries (by decide) + have hv := hfrozen values (by decide) + have ho := hfrozen output (by decide) + have hn := (hfrozen queryCount (by decide)).trans hN + have h1' := (hfrozen one (by decide)).trans h1 + have hk : y.regs index < N := by simpa only [Cond.holds_lt, hn] using hc + let k := y.regs index + let value := x.mem (x.regs values + codes k) + let z := (((((((y.set address (y.regs queries + k)).set query (codes k)).set + address (y.regs values + codes k)).set temporary value).set + address (y.regs output + k)).write (y.regs output + k) value).set index (k + 1)) + have hread' : y.mem (y.regs queries + k) = codes k := by rw [hq]; exact hread k hk + have hval' : y.mem (y.regs values + codes k) = value := by + rw [hv] + exact hvalue (codes k) (hcodes k hk) + have hqa : Fits w (y.regs queries + k) := hw.of_le (by rw [hq]; dsimp [k]; omega) + have hva : Fits w (y.regs values + codes k) := + hw.of_le (by rw [hv]; have := hcodes k hk; omega) + have hoa : Fits w (y.regs output + k) := hw.of_le (by rw [ho]; dsimp [k]; omega) + have hki : Fits w (k + 1) := hw.of_le (by dsimp [k]; omega) + have hz : ExecWithin w reportStep y z 7 := by + ram_eval [reportStep, z, k, value, hread', hval', h1', hqa, hva, hoa, hki, + index, address, query, temporary, queries, values, output, one, Fin.mk.injEq] + have hmem : z.mem = Function.update y.mem (x.regs output + k) value := by + simp only [z, Data.set_mem, Data.write_mem, ho] + have hidx : z.regs index = k + 1 := by simp [z] + have hawaySrc (i : ℕ) (hi : i < N) : x.regs queries + i ≠ x.regs output + k := by + rcases hsrc with h | h <;> dsimp [k] <;> omega + have hawayVal (q : ℕ) (hq' : q < capacity) : + x.regs values + q ≠ x.regs output + k := by + rcases hvals with h | h <;> dsimp [k] <;> omega + refine ⟨z, hz, ?_, by dsimp [k] at hidx; omega⟩ + refine ⟨by dsimp [k] at hidx; omega, ?_, ?_, ?_, ?_, ?_⟩ + · intro i hi + rw [hidx] at hi + by_cases he : i = k + · subst i + simp only [hmem, Function.update_self, value] + · rw [hmem, Function.update_of_ne (by omega : x.regs output + i ≠ x.regs output + k)] + exact hdone i (by dsimp [k] at hi; omega) + · intro i hi + rw [hmem, Function.update_of_ne (hawaySrc i hi)] + exact hread i hi + · intro q hq' + rw [hmem, Function.update_of_ne (hawayVal q hq')] + exact hvalue q hq' + · intro a ha + have hnot : a ≠ x.regs output + k := by + intro he + apply ha + rw [Block.mem_footprint] + change x.regs output ≤ a ∧ a < x.regs output + N + dsimp [k] at he + omega + rw [hmem, Function.update_of_ne hnot] + exact hframe a ha + · exact fun i hi => (hz.regs_eq (reportStep_preserves i hi)).trans (hfrozen i hi) + obtain ⟨y, hy, hIy, hstop⟩ := ExecWithin.loop_invariant I (fun y => N - y.regs index) + hstep hinit + obtain ⟨hle, hdone, _, _, hframe, hfrozen⟩ := hIy + have hn := (hfrozen queryCount (by decide)).trans hN + have hidx : y.regs index = N := by + simp only [Cond.holds_lt, hn, not_lt] at hstop + omega + have hloop : ExecWithin w (.loop (.lt index queryCount) reportStep) u y (9 * N + 2) := by + simpa only [u, Data.set_regs, Function.update_self, Nat.sub_zero, Nat.mul_comm] using hy + have he : ExecWithin w report x y (9 * N + 3) := (hsetup.seq hloop).mono (by omega) + exact ⟨y, he, hidx, by simpa only [hidx] using hdone, hframe, + fun i hi => he.regs_eq (report_preserves i hi)⟩ + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Return.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Return.lean new file mode 100644 index 000000000..0ecd8f1d4 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Return.lean @@ -0,0 +1,177 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Node +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Accumulate + +/-! +# The actual controller phase after a child returns + +## Main results + +* `PrunedQuery.exec_node_return`: load the parent frame, add the returned child only at + contributing requests, and advance the stored term counter. + +The value-directory hypotheses describe ordinary memory established by the child proof. +They do not assume a child execution or its cost. All native accumulation and controller +instructions are proved and charged here. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed Finset + +/-- The complete parent-return phase, including its real frame loads, two mode comparisons, +signed indexed-add loop and stored counter increment. Unselected child cells are not read. -/ +theorem exec_node_return {w L f a0 b0 d N k : ℕ} (x : Data registers) + (hregs : KernelRegs L f a0 b0 x) (hd : x.regs level = d) (hdL : d < L) + (hm : x.regs mode = 2) (hN : x.mem (f + 4 * d) = N) (hcap : N ≤ 10 ^ L) + (hterm : x.mem (f + 4 * d + 3) = k) (hk : k < 10) + (input : ℕ → ℕ) (V B : ℕ → ℤ) + (hinput : ∀ i < N, x.mem (f + 4 * (L + 1) + d * 10 ^ L + i) = input i) + (hcodes : ∀ i < N, input i < 10 ^ L) (hinj : Set.InjOn input {i | i < N}) + (hparent : ∀ i < N, + x.mem (f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L + input i) = + encode (V (input i))) + (hchild : ∀ i < N, selected true k (input i) → + x.mem (f + 4 * (L + 1) + (L + 1) * 10 ^ L + (d + 1) * 10 ^ L + input i / 10) = + encode (B (input i / 10))) + (hV : ∀ i < N, GenericRAM.Signed.Fits w (V (input i))) + (hB : ∀ i < N, GenericRAM.Signed.Fits w (B (input i / 10))) + (hw : Fits (w + 2) (8 * f + 64 * (L + 1) * 10 ^ L + 128)) : + ∃ y, ExecWithin (w + 2) step x y (31 * N + 29) ∧ KernelRegs L f a0 b0 y ∧ + y.regs level = d ∧ y.regs mode = 1 ∧ y.regs active = x.regs active ∧ + y.regs epoch = x.regs epoch ∧ + (∀ i < N, + y.mem (f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L + input i) = + encode (V (input i) + if selected true k (input i) then B (input i / 10) else 0)) ∧ + (∀ j < 3, y.mem (f + 4 * d + j) = x.mem (f + 4 * d + j)) ∧ + y.mem (f + 4 * d + 3) = k + 1 ∧ + Modifies (nodeWrites L f d) x.mem y.mem := by + have hpow : 1 ≤ (10 : ℕ) ^ L := Nat.one_le_pow L _ (by decide) + have hL : L + 1 ≤ (L + 1) * 10 ^ L := by nlinarith only [hpow] + have hmul : (d + 1) * 10 ^ L ≤ (L + 1) * 10 ^ L := + Nat.mul_le_mul_right _ (by omega) + obtain ⟨u, hu, humem, huf, huc, _, _, hut, hul, huv, _, _, huch, hureg⟩ := + exec_loadFrame x hregs.one_eq hregs.frameWidth_eq (hw.of_le (by + rw [hregs.frames_eq, hregs.lists_eq, hregs.values_eq, hregs.span_eq, hd] + nlinarith only [hpow, hL, hmul])) + have fixed (i : Fin registers) (hi : ¬LoadFrameLocal i) : u.regs i = x.regs i := + hureg i hi + have huf' : u.regs frame = f + 4 * d := by simpa only [hregs.frames_eq, hd] using huf + have hul' : u.regs listBase = f + 4 * (L + 1) + d * 10 ^ L := by + simpa only [hregs.lists_eq, hregs.span_eq, hd] using hul + let vb := f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L + have huv' : u.regs valueBase = vb := by + simpa only [hregs.values_eq, hregs.span_eq, hd] using huv + have huch' : u.regs childValues = f + 4 * (L + 1) + (L + 1) * 10 ^ L + + (d + 1) * 10 ^ L := by + simpa only [hregs.values_eq, hregs.span_eq, hd] using huch + have huc' : u.regs count = N := by simpa only [hregs.frames_eq, hd, hN] using huc + have hut' : u.regs term = k := by simpa only [hregs.frames_eq, hd, hterm] using hut + obtain ⟨v, hv, _, hvmem, hvFrame, _, hvreg⟩ := exec_accumulateLoop u input V B huc' + (by simpa only [hul', humem] using hinput) hcodes hinj + (by simpa only [huv', humem] using hparent) + (by simpa only [huch', humem, hut'] using hchild) hV hB + (Or.inl (by rw [hul', huv']; dsimp [vb]; nlinarith only [hcap, hpow])) + (Or.inl (by rw [huv', huch']; dsimp [vb]; nlinarith only [])) + ((fixed one (by decide)).trans hregs.one_eq) + ((fixed nine (by decide)).trans hregs.nine_eq) + ((fixed ten (by decide)).trans hregs.ten_eq) + (hw.of_le (by + rw [hul', huv', huch'] + dsimp [vb] + nlinarith only [hcap, hpow, hmul, hL])) + have hvf : v.regs frame = f + 4 * d := (hvreg frame (by decide)).trans huf' + have hvt : v.regs term = k := (hvreg term (by decide)).trans hut' + obtain ⟨y, hy, _, hym, hymem, _⟩ := exec_advance v + ((hvreg one (by decide)).trans ((fixed one (by decide)).trans hregs.one_eq)) + ((hvreg frameWidth (by decide)).trans + ((fixed frameWidth (by decide)).trans hregs.frameWidth_eq)) + (hw.of_le (by rw [hvt]; omega)) (hw.of_le (by rw [hvf]; nlinarith only [hL, hdL])) + have hacc : ExecWithin (w + 2) accumulate u y (31 * N + 8) := (hv.seq hy).mono (by omega) + have hum : u.regs mode = 2 := (fixed mode (by decide)).trans hm + have hu0 := (fixed nil (by decide)).trans hregs.nil_eq + have hu1 := (fixed one (by decide)).trans hregs.one_eq + have hbranch : ExecWithin (w + 2) + (ram[ if mode = nil then [ ⟪enter⟫ ] else [ + if mode = one then [ ⟪dispatch⟫ ] else [ ⟪accumulate⟫ ] ] ]) u y (31 * N + 12) := + (hacc.branch_neg (by simp only [Cond.holds_eq, hum, hu1]; decide)).branch_neg + (by simp only [Cond.holds_eq, hum, hu0]; decide) + have he : ExecWithin (w + 2) step x y (31 * N + 29) := (hu.seq hbranch).mono (by omega) + let written := ((range N).filter fun i => selected true k (input i)).image + fun i => vb + input i + have hvmem' : v.mem = written.piecewise + (fun a => encode (V (a - vb) + B ((a - vb) / 10))) x.mem := by + rw [huv', hut', humem] at hvmem + exact hvmem + have hyframe : Modifies (Block.mk (f + 4 * d) 4).footprint v.mem y.mem := by + rw [hymem, hvf] + exact Modifies.write_of_owns (by constructor <;> omega) _ + have hvframe : Modifies (Block.mk vb (10 ^ L)).footprint x.mem v.mem := by + simpa only [huv', humem] using hvFrame + have frameBeforeValue : f + 4 * d + 4 ≤ vb := by dsimp [vb]; omega + refine ⟨y, he, hregs.of_step he, ?_, hym, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · exact (hy.regs_eq (by decide : advance.Preserves level)).trans + ((hvreg level (by decide)).trans ((fixed level (by decide)).trans hd)) + · exact (hy.regs_eq (by decide : advance.Preserves active)).trans + ((hvreg active (by decide)).trans (fixed active (by decide))) + · exact (hy.regs_eq (by decide : advance.Preserves epoch)).trans + ((hvreg epoch (by decide)).trans (fixed epoch (by decide))) + · intro i hi + change y.mem (vb + input i) = _ + have haway : ¬(Block.mk (f + 4 * d) 4).Owns (vb + input i) := by + change ¬(_ ≤ _ ∧ _ < f + 4 * d + 4) + omega + rw [hyframe _ (by simpa only [Block.mem_footprint] using haway), hvmem'] + by_cases hsel : selected true k (input i) + · have hmem : vb + input i ∈ written := mem_image.mpr + ⟨i, mem_filter.mpr ⟨mem_range.mpr hi, hsel⟩, rfl⟩ + simp only [piecewise, hmem, hsel, ↓reduceIte, Nat.add_sub_cancel_left] + · have hmem : vb + input i ∉ written := by + rintro h + obtain ⟨j, hj, hji⟩ := mem_image.mp h + have heq : input j = input i := Nat.add_left_cancel hji + exact hsel (heq ▸ (mem_filter.mp hj).2) + simpa only [piecewise, hmem, hsel, ↓reduceIte, add_zero] using hparent i hi + · intro j hj + rw [hymem, hvf, Function.update_of_ne (by omega : f + 4 * d + j ≠ f + 4 * d + 3)] + apply hvframe + rw [Block.mem_footprint] + change ¬(vb ≤ f + 4 * d + j ∧ _) + omega + · simp only [hymem, hvf, hvt, Function.update_self] + · intro c hc + have hnotFrame : c ∉ (Block.mk (f + 4 * d) 4).footprint := by + intro hf + apply hc + apply mem_union_left + apply mem_union_left + apply mem_union_left + rw [Block.mem_footprint] at hf ⊢ + change f + 4 * d ≤ c ∧ c < f + 4 * d + 4 * (L + 1 - d) + change f + 4 * d ≤ c ∧ c < f + 4 * d + 4 at hf + have hrem : 1 ≤ L + 1 - d := by omega + omega + have hnotValue : c ∉ (Block.mk vb (10 ^ L)).footprint := by + intro hc' + apply hc + apply mem_union_left + apply mem_union_right + rw [Block.mem_footprint] at hc' ⊢ + change vb ≤ c ∧ c < vb + (L + 1 - d) * 10 ^ L + change vb ≤ c ∧ c < vb + 10 ^ L at hc' + have hrem : 1 ≤ L + 1 - d := by omega + have hlen := Nat.mul_le_mul_right (10 ^ L) hrem + exact ⟨hc'.1, by omega⟩ + exact (hyframe c hnotFrame).trans (hvframe c hnotValue) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Root.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Root.lean index 0733ebf77..e1852d3ac 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Root.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Root.lean @@ -72,4 +72,121 @@ theorem exec_rootFinish {w : ℕ} (x : Data registers) (h0 : x.regs nil = 0) simp only [y, Data.set_mem, Data.write_mem, Function.update_of_ne h3', Function.update_of_ne h2', Function.update_of_ne h1', Function.update_of_ne h0'] +/-- Registers written by root-list construction and its first frame initialization. -/ +def RootLocal (i : Fin registers) : Prop := + ScanLocal i ∨ RootFinishLocal i ∨ i = listBase ∨ i = count ∨ i = childList + +instance (i : Fin registers) : Decidable (RootLocal i) := + inferInstanceAs (Decidable + (ScanLocal i ∨ RootFinishLocal i ∨ i = listBase ∨ i = count ∨ i = childList)) + +private theorem rootList_preserves (i : Fin registers) : + ¬RootLocal i → rootList.Preserves i := by decide +kernel +revert + +/-- Construct the actual distinct root list and frame. Repeated input queries are read and +removed by the native timestamp scan, rather than supplied as a mathematical set. The frame, +list and timestamp intervals are the only possible memory writes. -/ +theorem exec_rootList {w N capacity : ℕ} (x : Data registers) (input : ℕ → ℕ) + (hN : x.regs queryCount = N) + (hinput : ∀ i < N, x.mem (x.regs queries + i) = input i) + (hcodes : ∀ i < N, input i < capacity) + (hsep : x.regs lists + capacity ≤ x.regs stamps) + (hsrcOut : x.regs queries + N ≤ x.regs lists) + (hframeList : x.regs frames + 4 ≤ x.regs lists) + (hsrcFrame : x.regs queries + N ≤ x.regs frames) + (hmarks : ∀ q < capacity, x.mem (x.regs stamps + q) ≤ x.regs epoch) + (h0 : x.regs nil = 0) (h1 : x.regs one = 1) + (h10 : x.regs ten = 10) (h9 : x.regs nine = 9) + (hw : Fits w (x.regs queries + N + x.regs lists + capacity + + x.regs stamps + capacity + N + capacity + x.regs epoch + x.regs frames + 16)) : + ∃ y, ExecWithin w rootList x y (20 * N + 19) ∧ + y.regs level = 0 ∧ y.regs mode = 0 ∧ y.regs active = 1 ∧ + y.regs epoch = x.regs epoch + 1 ∧ + ScanMemory y.mem (x.regs lists) (x.regs stamps) (x.regs epoch + 1) capacity + ((Finset.range N).image input) ∧ + y.mem (x.regs frames) = ((Finset.range N).image input).card ∧ + y.mem (x.regs frames + 1) = 0 ∧ y.mem (x.regs frames + 2) = 1 ∧ + y.mem (x.regs frames + 3) = 0 ∧ + (∀ i < N, y.mem (x.regs queries + i) = input i) ∧ + (∀ a, ¬(Block.mk (x.regs lists) capacity).Owns a → + ¬(Block.mk (x.regs stamps) capacity).Owns a → + ¬(Block.mk (x.regs frames) 4).Owns a → y.mem a = x.mem a) ∧ + (∀ i, ¬RootLocal i → y.regs i = x.regs i) := by + let u := ((x.set listBase (x.regs queries)).set count N).set childList (x.regs lists) + have hu : ExecWithin w rootScanSetup x u 3 := by + ram_eval [rootScanSetup, u, hN, listBase, count, childList, queryCount, queries, lists, + Fin.mk.injEq] + obtain ⟨v, hv, _, hve, hvc, htable, _, hscanFrame, hvreg⟩ := exec_scan (N := N) + (capacity := capacity) false u input + (by simp +decide [u, count, childList]) + (by simpa +decide [u, listBase, count, childList] using hinput) + (by simpa only [shortened, Bool.false_eq_true, ↓reduceIte] using hcodes) + (Or.inl (by simpa +decide [u, childList, stamps, count, listBase] using hsep)) + (Or.inl (by simpa +decide [u, childList, listBase, count] using hsrcOut)) + (Or.inl (by simpa +decide [u, stamps, listBase, count, childList] using + hsrcOut.trans (Nat.le_trans (Nat.le_add_right _ _) hsep))) + (by simpa +decide [u, stamps, epoch, childList, count, listBase] using hmarks) + (by simpa +decide [u, one, childList, count, listBase] using h1) + (by simpa +decide [u, ten, childList, count, listBase] using h10) + (by simpa +decide [u, nine, childList, count, listBase] using h9) + (hw.of_le (by simp +decide [u, listBase, count, childList, stamps, epoch]; omega)) + have fixed (i : Fin registers) (hi : ¬ScanLocal i) + (hb : i ≠ listBase) (hc : i ≠ count) (ho : i ≠ childList) : + v.regs i = x.regs i := by + rw [hvreg i hi] + simp only [u, Data.set_regs, Function.update_of_ne ho, Function.update_of_ne hc, + Function.update_of_ne hb] + have hvf := fixed frames (by decide) (by decide) (by decide) (by decide) + have hvs := fixed stamps (by decide) (by decide) (by decide) (by decide) + obtain ⟨y, hy, hyl, hym, hya, hmem, hfinishFrame, _⟩ := exec_rootFinish v + ((fixed nil (by decide) (by decide) (by decide) (by decide)).trans h0) + ((fixed one (by decide) (by decide) (by decide) (by decide)).trans h1) + (hw.of_le (by rw [hvf]; omega)) + have he : ExecWithin w rootList x y (20 * N + 19) := by + exact (hu.seq (hv.seq hy)).mono (by omega) + have hyoutside (a : ℕ) (ha : ¬(Block.mk (x.regs frames) 4).Owns a) : + y.mem a = v.mem a := hfinishFrame a (by simpa only [Block.mem_footprint, hvf] using ha) + have hawayList (i : ℕ) : ¬(Block.mk (x.regs frames) 4).Owns (x.regs lists + i) := by + change ¬(x.regs frames ≤ _ ∧ _ < x.regs frames + 4) + omega + have hawayStamp (i : ℕ) : ¬(Block.mk (x.regs frames) 4).Owns (x.regs stamps + i) := by + change ¬(x.regs frames ≤ _ ∧ _ < x.regs frames + 4) + omega + have htable' : ScanMemory v.mem (x.regs lists) (x.regs stamps) (x.regs epoch + 1) + capacity ((Finset.range N).image input) := by + simpa +decide [u, childList, stamps, epoch, listBase, count, term, scanSet_root] using htable + refine ⟨y, he, hyl, hym, hya, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, + fun i hi => he.regs_eq (rootList_preserves i hi)⟩ + · simpa +decide [hy.regs_eq (by decide : rootFinish.Preserves epoch), u, epoch, + childList, count, listBase] using hve + · exact htable'.congr (fun i _ => hyoutside _ (hawayList i)) + (fun q _ => hyoutside _ (hawayStamp q)) + · rw [hmem, hvf] + simpa +decide [Function.update_apply, u, childList, count, listBase, term, scanSet_root] + using hvc + · rw [hmem, hvf] + simp + · rw [hmem, hvf] + simp + · rw [hmem, hvf] + simp + · intro i hi + rw [hyoutside _ (by change ¬(x.regs frames ≤ _ ∧ _ < x.regs frames + 4); omega)] + rw [hscanFrame] + · exact hinput i hi + · change ¬(u.regs childList ≤ _ ∧ _ < u.regs childList + capacity) + simp +decide only [u, Data.set_regs, childList, count, listBase, + Function.update_apply, ↓reduceIte] + omega + · change ¬(u.regs stamps ≤ _ ∧ _ < u.regs stamps + capacity) + have hus : u.regs stamps = x.regs stamps := by + simp +decide [u, stamps, childList, count, listBase] + rw [hus] + omega + · intro a hao has haf + rw [hyoutside a haf, hscanFrame] + · rfl + · simpa +decide [u, childList, count, listBase] using hao + · simpa +decide [u, stamps, childList, count, listBase] using has + end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanMemory.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanMemory.lean index ad0caa23a..227cecfbf 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanMemory.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanMemory.lean @@ -49,6 +49,23 @@ theorem ScanMemory.card_le {mem : ℕ → ℕ} {out stamps epoch capacity : ℕ} have hs : S ⊆ Finset.range capacity := fun q hq => Finset.mem_range.mpr (h.bounded q hq) simpa using Finset.card_le_card hs +/-- Agreement on the occupied list and timestamp directory preserves the scan invariant. +Unoccupied list cells have no specified meaning and need not agree. -/ +theorem ScanMemory.congr {mem mem' : ℕ → ℕ} {out stamps epoch capacity : ℕ} {S : Finset ℕ} + (h : ScanMemory mem out stamps epoch capacity S) + (hlist : ∀ i < S.card, mem' (out + i) = mem (out + i)) + (hstamp : ∀ q < capacity, mem' (stamps + q) = mem (stamps + q)) : + ScanMemory mem' out stamps epoch capacity S := by + refine ⟨h.bounded, ?_, ?_, ?_⟩ + · intro q hq + rw [hstamp q hq] + exact h.stamp_le q hq + · intro q hq + rw [hstamp q hq] + exact h.marked q hq + · obtain ⟨v, hv, hrange, hreal⟩ := h.listed + exact ⟨v, hv, hrange, fun i => (hlist i i.isLt).trans (hreal i)⟩ + /-- An unseen valid code leaves a free output cell for its insertion. -/ theorem ScanMemory.card_lt_of_notMem {mem : ℕ → ℕ} {out stamps epoch capacity : ℕ} {S : Finset ℕ} (h : ScanMemory mem out stamps epoch capacity S) {q : ℕ} diff --git a/ComplexityTests/SchonhagePruned.lean b/ComplexityTests/SchonhagePruned.lean index 72bb67a7f..7224cb954 100644 --- a/ComplexityTests/SchonhagePruned.lean +++ b/ComplexityTests/SchonhagePruned.lean @@ -5,6 +5,11 @@ Authors: Erik Demaine -/ import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Memory +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Root +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Report +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.EmptyChild +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Finish +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Return -- Full controller regressions evaluate the actual native interpreter at elaboration time. set_option linter.hashCommand false @@ -16,6 +21,62 @@ open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery namespace ComplexityTests.SchonhagePruned +private def leafData : Data registers where + regs i := match i.val with + | 10 => 1 | 11 => 3 | 12 => 4 | 18 => 8 | 19 => 12 | 20 => 13 | 21 => 14 + | 24 => 1 | 25 => 1 | 26 => 10 | 27 => 9 | 28 => 4 | _ => 0 + mem a := if a = 3 then encode (-3) else if a = 4 then encode 2 else + if a = 8 then 1 else if a = 10 then 1 else if a = 13 then 41 else 0 + +private theorem leafData_parameters : KernelRegs 0 8 3 4 leafData := by + constructor <;> rfl + +-- A concrete negative-product node discharges the general invariant, signed and address +-- premises. Its returned memory is attached to the same charged native loop execution. +example : ∃ y, ExecWithin 7 controller leafData y 8194 ∧ y.mem 13 = encode (-6) ∧ + y.mem 100 = 0 := by + let S : Finset (Cslib.Algorithms.MatrixMultiplication.Schonhage.OutStr 0) := {Fin.elim0} + obtain ⟨y, hregs, _, _, hactive, _, _, hvalue, hframe, _, hrun⟩ := + exec_node_zero (w := 5) (p := 0) (E := 0) (a := -3) (b := 2) + (by decide) (by decide) (by decide) S leafData leafData_parameters + rfl rfl rfl (by simp [leafData, S]) rfl rfl rfl rfl + (by + intro q hq + have he : q = 0 := by omega + subst q + decide) (by decide) + have hexit : ExecWithin 7 controller y y 2 := + ExecWithin.loop_exit (by simp only [Cond.holds_lt, hregs.nil_eq, hactive]; decide) + refine ⟨y, ?_, ?_, ?_⟩ + · simpa only [traceWeight_zero, S, Finset.card_singleton] using hrun y 2 hexit + · have hv := hvalue Fin.elim0 (Finset.mem_singleton_self _) + simpa [Cslib.Algorithms.MatrixMultiplication.Schonhage.Pruned.result, + Cslib.Algorithms.MatrixMultiplication.Schonhage.Pruned.run, + Finset.piecewise, S] using hv + · exact (hframe 100 (by decide)).trans rfl + +-- An empty leaf performs its real controller return without overwriting the old value cell. +example : ∃ y, ExecWithin 7 controller (leafData.write 8 0) y 47 ∧ y.mem 13 = 41 := by + let x := leafData.write 8 0 + obtain ⟨y, hy, hmem, _, _, hactive, hreg⟩ := exec_leafStep (w := 5) (p := 0) + (a := -3) (b := 2) (by decide) (by decide) (by decide) x + rfl rfl rfl rfl rfl rfl rfl rfl (by decide) + have hy0 : y.regs nil = 0 := (hreg nil (by decide)).trans rfl + have he : ExecWithin 7 controller x y 47 := ExecWithin.loop_once + (by decide) hy (by simp only [Cond.holds_lt, hy0, hactive]; decide) + refine ⟨y, he, ?_⟩ + rw [hmem] + rfl + +-- Naming the reporting body and controller loop changes no native control-code tree. +example : report = ram[ + index ← #0; while index < queryCount do [ + address ← queries + index; query ← *address; + address ← values + query; temporary ← *address; + address ← output + index; *address ← temporary; index ← index + one ] ] := rfl + +example : controller = ram[ while nil < active do [ ⟪step⟫ ] ] := rfl + -- Query codes use least-significant-first digits and retain the encoder's wildcard digit nine. example : queryAt (L := 2) (90 : Fin 100) = ![.z 0 0, .z0] := by decide +kernel example : (requested (L := 2) ([90, 9, 90] : List (Fin 100))).card = 2 := by decide +kernel From 99b8a1be735b890c0084e258ea3b17e33bb99c71 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 17:32:42 -0400 Subject: [PATCH 27/73] doc(matrix): replace copied source prose with API descriptions Resolve the documentation-only requested changes on #1464: https://github.com/cslib-community/complexitylib/pull/1464#issuecomment-6477111132 Rewrite source-quotation fragments and related explanations in Alphabets, Identity, Full and Pruned for the local Lean declarations. Preserve the mathematical attribution, bibliography keys, source pin and copyright. A comment-aware comparison with 4f3226cd proves every noncomment byte is identical in all four files. No declaration, proof, definition, program, import or test changes. Existing checked source-statement quotations and notation/quantifier/model/resource mapping in the PR remain unchanged. The final source-comment screen compares against quoted fragments in the actual pinned Sec2 and Statements/PaperStatements files, including the known local renames; no substantial quoted fragment remains unmarked. Validation is recorded for this exact tree in PR #1464. Source checkpoint tree 8c3b87bb870d4c4e2202917357d19a7a139296e8. No abstract/catalogue/model/scope or rendering impact; no new headline. The full native 1.9992 endpoint remains open under #1394. Ancestor and descendant remotes are unchanged. AI assistance: documentation repair and validation by Codex continuation worker codex-three-sum-continuation-t3-3dda8289. Existing independent mathematical/API and checked-source review coverage is retained; outside review of the bounded documentation delta is requested, with no new review agent commissioned. --- .../Schonhage/Alphabets.lean | 92 +++++------ .../MatrixMultiplication/Schonhage/Full.lean | 119 +++++--------- .../Schonhage/Identity.lean | 37 ++--- .../Schonhage/Pruned.lean | 152 +++++++----------- 4 files changed, 162 insertions(+), 238 deletions(-) diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Alphabets.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Alphabets.lean index d9d6a76f9..34bcd43e1 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Alphabets.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Alphabets.lean @@ -34,7 +34,8 @@ functions over integers; no arithmetic operation is treated as a native machine Part of [AlmanWilliams2026, Section 2]. Direct mathematical adaptation of [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Definitions are selectively extracted from that pin's `PaperStatements.lean`; no machine -semantics or running-time statement is imported. Source-derived prose is paraphrased below. +semantics or running-time statement is imported. The docstrings below explain the local +declarations in our own words. -/ @[expose] public section @@ -43,29 +44,28 @@ open Finset namespace Cslib.Algorithms.MatrixMultiplication.Schonhage -/-- Section 2.2: We call x₁, x₂, x₃, p₁₁, p₁₂, p₂₁, p₂₂ the seven left variables. --/ +/-- Left input coordinates: three `x` coordinates and a two-by-two family of `p` coordinates. -/ inductive LeftVar : Type | x (i : Fin 3) | p (i j : Fin 2) deriving DecidableEq, Fintype -/-- Section 2.2: and y₁, y₂, y₃, q₁₁, q₁₂, q₂₁, q₂₂ the seven right variables. --/ +/-- Right input coordinates: three `y` coordinates and a two-by-two family of `q` coordinates. -/ inductive RightVar : Type | y (j : Fin 3) | q (i j : Fin 2) deriving DecidableEq, Fintype -/-- Section 2.2: We will also use ten output variables z_ij (for 1 ≤ i, j ≤ 3) and z₀. +/-- Output coordinates comprise a three-by-three outer family and one distinguished inner +coordinate. -/ inductive OutVar : Type | z (i j : Fin 3) | z0 deriving DecidableEq, Fintype -/-- Section 2.2: Schönhage's identity has ten terms, which we name P_ij (for 1 ≤ i, j ≤ 3, -corresponding to the entries of p̂ and q̂) and P₀. +/-- The multiplication labels consist of nine indexed labels `P i j` and the additional label +`P0`. -/ inductive Term : Type | P (i j : Fin 3) @@ -91,46 +91,44 @@ def zForm (i j : Fin 3) : OutVar → ℤ := Pi.single (OutVar.z i j) 1 -/ def z0Form : OutVar → ℤ := Pi.single OutVar.z0 1 -/-- Section 2.2: the 3 × 3 matrix `p̂` of linear forms, p̂ = (p₁₁, p₁₂, 0; p₂₁, p₂₂, 0; -p₁₁-p₂₁, --p₁₂-p₂₂, 0). +/-- Extend the two-by-two `p` family to three rows by cancelling each column sum; the third column +is zero. -/ def pHat : Matrix (Fin 3) (Fin 3) (LeftVar → ℤ) := !![pForm 0 0, pForm 0 1, 0; pForm 1 0, pForm 1 1, 0; -pForm 0 0 - pForm 1 0, -pForm 0 1 - pForm 1 1, 0] -/-- Section 2.2: the 3 × 3 matrix `q̂` of linear forms, q̂ = (q₁₁, q₁₂, -q₁₁-q₁₂; q₂₁, q₂₂, --q₂₁-q₂₂; 0, 0, 0). +/-- Extend the two-by-two `q` family to three columns by cancelling each row sum; the third row is +zero. -/ def qHat : Matrix (Fin 3) (Fin 3) (RightVar → ℤ) := !![qForm 0 0, qForm 0 1, -qForm 0 0 - qForm 0 1; qForm 1 0, qForm 1 1, -qForm 1 0 - qForm 1 1; 0, 0, 0] -/-- Section 2.2: the linear form `φ_λ` in the left variables, φ_{P_ij} = x_i + p̂_ij and φ_{P₀} = --(x₁ + x₂ + x₃). Section 2.3.1: let φ_λ(s) ∈ {0, ±1} denote the coefficient of s in φ_λ; this is -`phi lam s`. +/-- Left coefficients for each multiplication label. An indexed label adds its `x` coordinate to +the corresponding `pHat` form; `P0` negates the sum of the three `x` coordinates. -/ def phi : Term → LeftVar → ℤ | .P i j => xForm i + pHat i j | .P0 => -(xForm 0 + xForm 1 + xForm 2) -/-- Section 2.2: the linear form `ψ_λ` in the right variables, ψ_{P_ij} = y_j + q̂_ij and ψ_{P₀} = -y₁ + y₂ + y₃. Section 2.3.1: `ψ_λ(t)`, the coefficient of `t` in `ψ_λ`, is `psi lam t`. +/-- Right coefficients for each multiplication label. Indexed labels use `y` plus `qHat`, whereas +`P0` uses the sum of the three `y` coordinates. -/ def psi : Term → RightVar → ℤ | .P i j => yForm j + qHat i j | .P0 => yForm 0 + yForm 1 + yForm 2 -/-- Section 2.2: the linear form `χ_λ` in the output variables, χ_{P_ij} = z_ij + z₀ and χ_{P₀} = -z₀. +/-- Output coefficients: each indexed label occurs in its own outer coordinate and in `z0`; `P0` +occurs only in `z0`. -/ def chi : Term → OutVar → ℤ | .P i j => zForm i j + z0Form | .P0 => z0Form -/-- Section 2.2: the output variable `z₀` is inner, the `z_ij` are outer. --/ +/-- Select the distinguished output coordinate, excluding the indexed outer coordinates. -/ def OutVar.IsInner : OutVar → Prop | .z _ _ => False | .z0 => True @@ -139,36 +137,28 @@ instance OutVar.instDecidablePredIsInner : DecidablePred OutVar.IsInner | .z _ _ => isFalse fun h => h | .z0 => isTrue trivial -/-- Section 2.2: We say that a term λ contributes to an output variable z if z appears in χ_λ. --/ +/-- A label is relevant to an output coordinate when its output coefficient is nonzero. -/ def Term.Contributes (lam : Term) (z : OutVar) : Prop := chi lam z ≠ 0 instance Term.instDecidableContributes (lam : Term) (z : OutVar) : Decidable (lam.Contributes z) := inferInstanceAs (Decidable (chi lam z ≠ 0)) -/-- The term that the private leaf has at a level where the output string has the variable `z`: -`P₀` for `z₀` and `P_ij` for `z_ij`. +/-- Choose the indexed label for an outer coordinate and `P0` for the inner coordinate. Applying +this choice coordinatewise defines a private leaf. -/ def OutVar.privateTerm : OutVar → Term | .z i j => .P i j | .z0 => .P0 -/-- Section 2.3.1: A left string of length L is a string u = u₁ u₂ ⋯ u_L of L left variables, and -we call u_ℓ its variable at level ℓ. --/ +/-- A length-`L` tuple of left coordinates, indexed from zero. -/ abbrev LeftStr (L : ℕ) : Type := Fin L → LeftVar -/-- Section 2.3.1: right strings of length `L`. --/ +/-- A length-`L` tuple with entries in `RightVar`. -/ abbrev RightStr (L : ℕ) : Type := Fin L → RightVar -/-- Section 2.3.1: output strings of length `L`. --/ +/-- A length-`L` tuple with entries in `OutVar`. -/ abbrev OutStr (L : ℕ) : Type := Fin L → OutVar -/-- Section 2.3.1: We refer to the call reached by choosing the terms τ₁, …, τ_k at levels 1, …, k -as the vertex τ₁ ⋯ τ_k, a string of k terms. It is at depth k. --/ +/-- The sequence of multiplication labels identifying a depth-`k` recursion vertex. -/ abbrev Vertex (k : ℕ) : Type := Fin k → Term -/-- Section 2.3.1: the leaves, the vertices τ = τ₁ ⋯ τ_L at depth L. --/ +/-- A terminal vertex, whose label sequence has the full recursion depth. -/ abbrev Leaf (L : ℕ) : Type := Vertex L /-! ### The alphabets -/ @@ -209,46 +199,40 @@ def Term.equiv : Term ≃ Option (Fin 3 × Fin 3) where left_inv t := by cases t <;> rfl right_inv t := by rcases t with _ | ⟨_, _⟩ <;> rfl -/-- Section 2.2: there are seven left variables. --/ +/-- The left alphabet has cardinality seven. -/ theorem card_leftVar : Fintype.card LeftVar = 7 := by simp [Fintype.card_congr LeftVar.equiv] -/-- Section 2.2: there are seven right variables. --/ +/-- The right alphabet has cardinality seven. -/ theorem card_rightVar : Fintype.card RightVar = 7 := by simp [Fintype.card_congr RightVar.equiv] -/-- Section 2.2: Schönhage's identity has ten terms. --/ +/-- There are ten multiplication labels. -/ theorem card_term : Fintype.card Term = 10 := by simp [Fintype.card_congr Term.equiv] /-! ### Which terms contribute to which output variables -/ -/-- Section 2.2, second observation: every term contributes to z₀. --/ +/-- The inner coordinate receives a contribution from every multiplication label. -/ @[simp] theorem Term.contributes_z0 (lam : Term) : lam.Contributes .z0 := by cases lam <;> simp [Term.Contributes, chi, zForm, z0Form] -/-- Section 2.2, second observation: only P_ij contributes to z_ij. --/ +/-- For an outer coordinate, relevance determines both indices of the multiplication label. -/ @[simp] theorem Term.contributes_z_iff (lam : Term) (i j : Fin 3) : lam.Contributes (.z i j) ↔ lam = .P i j := by cases lam <;> simp [Term.Contributes, chi, zForm, z0Form, Pi.single_apply, eq_comm] -/-- The second observation in one statement: a term contributes to `z` if and only if `z` is -inner, that is `z₀`, or the term is `z.privateTerm`, which is `P_ij` for `z = z_ij`. (This is the -term that the private leaf of Section 2.4.3 has at a level with the variable `z`.) +/-- Relevance has two possibilities: the output is inner, or the label is the private label +selected by that output. -/ theorem Term.contributes_iff (lam : Term) (z : OutVar) : lam.Contributes z ↔ z.IsInner ∨ lam = z.privateTerm := by decide +revert -/-- A sum over the terms contributing to `z` has the one summand `P_ij` for `z = z_ij`, and runs -over all terms for `z = z₀`. This is how step (4) of the recursions assembles the slice at `z`. +/-- Separate decoding into its two cases: an outer output selects one summand; the inner output +sums the whole label family. -/ theorem Term.sum_contributes {A : Type*} [AddCommMonoid A] (c : Term → A) (z : OutVar) : ∑ lam : Term with lam.Contributes z, c lam @@ -259,9 +243,7 @@ theorem Term.sum_contributes {A : Type*} [AddCommMonoid A] (c : Term → A) (z : | z i j => simp [filter_eq'] | z0 => simp -/-- Section 2.2: the χ_λ forms are all sums of output variables (without any minus signs or other -coefficients). --/ +/-- Each output coefficient is the zero-or-one indicator of `Term.Contributes`. -/ theorem chi_eq_ite (lam : Term) (z : OutVar) : chi lam z = if lam.Contributes z then 1 else 0 := by decide +revert diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Full.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Full.lean index ed1316900..801a2b80b 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Full.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Full.lean @@ -38,7 +38,8 @@ instruction-cost interpretation. Part of [AlmanWilliams2026, Section 2]. Direct mathematical adaptation of [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Definitions are selectively extracted from that pin's `PaperStatements.lean`; no machine -semantics or running-time statement is imported. Source-derived prose is paraphrased below. +semantics or running-time statement is imported. The docstrings below explain the local +declarations in our own words. -/ @[expose] public section @@ -47,23 +48,17 @@ open Finset namespace Cslib.Algorithms.MatrixMultiplication.Schonhage -/-- Section 2.3.1: An array a on the left strings of length L assigns an integer a[u] to each u -that is a left string of length L. So such an array is a function `LeftStr L → ℤ`, and likewise -for right strings, output strings and leaves. - -the slice of a at a left variable s is the array a_s on the strings of length L - 1 given by -a_s[u'] := a[s u']; the same definition is used for every alphabet. +/-- Fix the first coordinate of a tuple-indexed array and retain the shorter tuple as its +argument. The construction works for any coordinate alphabet and value type. -/ def sliceAt {α β : Type} {L : ℕ} (a : (Fin (L + 1) → α) → β) (s : α) : (Fin L → α) → β := fun u' => a (Fin.cons s u') -/-- Section 2.3.1, step (2) of `Full.result`: A_λ := ∑_s φ_λ(s) a_s. --/ +/-- Apply the left coefficient form for `lam` to all first-coordinate slices of the input array. -/ def encodeStepL {L : ℕ} (lam : Term) (a : LeftStr (L + 1) → ℤ) : LeftStr L → ℤ := fun u' => ∑ s, phi lam s * sliceAt a s u' -/-- Section 2.3.1, step (2) of `Full.result`: B_λ := ∑_t ψ_λ(t) b_t. --/ +/-- Apply the right coefficient form for `lam` to all first-coordinate slices of the input array. -/ def encodeStepR {L : ℕ} (lam : Term) (b : RightStr (L + 1) → ℤ) : RightStr L → ℤ := fun v' => ∑ t, psi lam t * sliceAt b t v' @@ -84,31 +79,27 @@ def Full.run : (L : ℕ) → (LeftStr L → ℤ) → (RightStr L → ℤ) → (O | .z0 => ∑ lam, (R lam).1 (Fin.tail w), fun τ => (R (τ 0)).2 (Fin.tail τ)) -/-- The array that `Full.result(a, b)` returns. --/ +/-- Project the output array from full evaluation, discarding its record of leaf operands. -/ def Full.result (L : ℕ) (a : LeftStr L → ℤ) (b : RightStr L → ℤ) : OutStr L → ℤ := (Full.run L a b).1 -/-- The two numbers that `Full.result(a, b)` multiplies at the leaf `τ`, the left one first. --/ +/-- Read the ordered pair of leaf operands recorded by `Full.run`. -/ def Full.multipliedAt (L : ℕ) (a : LeftStr L → ℤ) (b : RightStr L → ℤ) (τ : Leaf L) : ℤ × ℤ := (Full.run L a b).2 τ /-! ### 2.3.2 Unraveling the recursion computation -/ -/-- Section 2.3.2: Φ_τ(a) := ∑_u a[u] ∏_{ℓ=1}^{L} φ_{τ_ℓ}(u_ℓ), the sum over all left strings `u`. +/-- Evaluate the left encoding at one leaf by weighting each input entry with the product of its +coordinate coefficients. -/ def encodeLeft {L : ℕ} (τ : Leaf L) (a : LeftStr L → ℤ) : ℤ := ∑ u, a u * ∏ ℓ, phi (τ ℓ) (u ℓ) -/-- Section 2.3.2: Ψ_τ(b) := ∑_v b[v] ∏_{ℓ=1}^{L} ψ_{τ_ℓ}(v_ℓ), the sum over all right strings -`v`. +/-- Evaluate the right encoding at one leaf using the corresponding products of right +coefficients. -/ def encodeRight {L : ℕ} (τ : Leaf L) (b : RightStr L → ℤ) : ℤ := ∑ v, b v * ∏ ℓ, psi (τ ℓ) (v ℓ) -/-- Section 2.3.2: a vertex τ₁ ⋯ τ_k contributes to an output string w if τ_ℓ contributes to w_ℓ -at every level ℓ ≤ k, for a leaf, that is, for `k = L`: the leaf `τ` contributes to `w` if `τ_ℓ` -contributes to `w_ℓ` at every level `ℓ`. --/ +/-- A leaf is relevant to an output tuple when its labels are relevant at every coordinate. -/ def Leaf.Contributes {L : ℕ} (τ : Leaf L) (w : OutStr L) : Prop := ∀ ℓ, (τ ℓ).Contributes (w ℓ) @@ -116,71 +107,57 @@ instance Leaf.instDecidableContributes {L : ℕ} (τ : Leaf L) (w : OutStr L) : Decidable (Leaf.Contributes τ w) := inferInstanceAs (Decidable (∀ ℓ, (τ ℓ).Contributes (w ℓ))) -/-- Equation (2): bilinearProduct(a, b)[w] := ∑_{τ contributing to w} Φ_τ(a) Ψ_τ(b). --/ +/-- For each output tuple, sum the products of left and right encodings over its relevant leaves. -/ def bilinearProduct {L : ℕ} (a : LeftStr L → ℤ) (b : RightStr L → ℤ) : OutStr L → ℤ := fun w => ∑ τ : Leaf L with Leaf.Contributes τ w, encodeLeft τ a * encodeRight τ b -/-- Equation (3): γ(s, t, z) := ∑_{λ contributing to z} φ_λ(s) ψ_λ(t). +/-- The coefficient linking one left, right and output coordinate, obtained by summing products of +the relevant label coefficients. -/ def coefficient (s : LeftVar) (t : RightVar) (z : OutVar) : ℤ := ∑ lam : Term with lam.Contributes z, phi lam s * psi lam t -/-- Section 2.4.1: The encoding of a is the array of the 10^L numbers Φ_τ(a), indexed by the -leaves τ. --/ +/-- Package all left-encoding values into an array indexed by leaves. -/ def encodingL {L : ℕ} (a : LeftStr L → ℤ) : Leaf L → ℤ := fun τ => encodeLeft τ a -/-- Section 2.4.1: The encoding of b, the array of the numbers Ψ_τ(b). --/ +/-- Package all right-encoding values into an array indexed by leaves. -/ def encodingR {L : ℕ} (b : RightStr L → ℤ) : Leaf L → ℤ := fun τ => encodeRight τ b -/-- Section 2.4.2: For a set U of output strings of length L, we write leaves(U) for the set of -leaves that contribute to some output string of U. --/ +/-- Take the union of the relevant-leaf sets of all requested output tuples. -/ def leaves {L : ℕ} (U : Finset (OutStr L)) : Finset (Leaf L) := univ.filter fun τ => ∃ w ∈ U, Leaf.Contributes τ w -/-- Section 2.3.1: an array on the left strings of length `L` has 7^L entries in total. --/ +/-- Counting length-`L` left tuples gives `7^L`. -/ theorem card_leftStr (L : ℕ) : Fintype.card (LeftStr L) = 7 ^ L := by rw [Fintype.card_fun, card_leftVar, Fintype.card_fin] -/-- Section 2.3.1: an array on the right strings of length `L` has `7^L` entries. --/ +/-- Counting length-`L` right tuples gives `7^L`. -/ theorem card_rightStr (L : ℕ) : Fintype.card (RightStr L) = 7 ^ L := by rw [Fintype.card_fun, card_rightVar, Fintype.card_fin] -/-- Section 2.3.1: At depth k there are 10^k vertices; in particular there are `10^L` leaves. --/ +/-- A depth-`k` label sequence has ten independent choices at each position. -/ theorem card_vertex (k : ℕ) : Fintype.card (Vertex k) = 10 ^ k := by rw [Fintype.card_fun, card_term, Fintype.card_fin] -/-- Section 2.3.1: φ_λ(s) ∈ {0, ±1}. --/ +/-- Every left coefficient has absolute value at most one. -/ theorem abs_phi_le_one (lam : Term) (s : LeftVar) : |phi lam s| ≤ 1 := by decide +revert -/-- Section 2.3.1: `ψ_λ(t) ∈ {0, ±1}`. --/ +/-- Every right coefficient has absolute value at most one. -/ theorem abs_psi_le_one (lam : Term) (t : RightVar) : |psi lam t| ≤ 1 := by decide +revert -/-- Section 2.3.1: for each term λ, at most three of the seven values φ_λ(s) are nonzero. --/ +/-- The support of any left coefficient form has cardinality at most three. -/ theorem card_support_phi_le (lam : Term) : (univ.filter fun s => phi lam s ≠ 0).card ≤ 3 := by decide +revert -/-- Section 2.3.1: and similarly for the seven values ψ_λ(t). --/ +/-- The support of any right coefficient form has cardinality at most three. -/ theorem card_support_psi_le (lam : Term) : (univ.filter fun t => psi lam t ≠ 0).card ≤ 3 := by decide +revert /-! ### Which leaves contribute to an output string -/ -/-- In the proof of Lemma 7: the leaves contributing to zw' are the λτ' with λ contributing to z -and τ' contributing to w'. Here `λ = τ 0`, `τ' = Fin.tail τ`, `z = w 0` and `w' = Fin.tail w`. --/ +/-- Split coordinatewise leaf relevance into the first label and the remaining label tuple. -/ theorem Leaf.contributes_succ {L : ℕ} {τ : Leaf (L + 1)} {w : OutStr (L + 1)} : Leaf.Contributes τ w ↔ (τ 0).Contributes (w 0) ∧ Leaf.Contributes (Fin.tail τ) (Fin.tail w) := by @@ -205,9 +182,7 @@ theorem Leaf.sum_contributes_succ {L : ℕ} (w : OutStr (L + 1)) (f : Leaf (L + refine sum_congr rfl fun lam _ => ?_ by_cases h : lam.Contributes (w 0) <;> simp [h] -/-- In the proof of Lemma 8: the leaves contributing to `w` are the leaves whose term at level ℓ -contributes to w_ℓ, independently for each ℓ. --/ +/-- The relevant-leaf filter is a product of the allowed label sets at individual coordinates. -/ theorem Leaf.filter_contributes_eq_piFinset {L : ℕ} (w : OutStr L) : (univ.filter fun τ : Leaf L => Leaf.Contributes τ w) = Fintype.piFinset fun ℓ => univ.filter fun lam : Term => lam.Contributes (w ℓ) := by @@ -228,18 +203,17 @@ section encodeWith variable {α : Type} [Fintype α] (c : Term → α → ℤ) {L : ℕ} -/-- The number `∑_u a[u] ∏_ℓ c_{τ_ℓ}(u_ℓ)`, for an array `a` on the strings over a finite alphabet -and the coefficients `c` of a family of linear forms, one for each term. By definition it is -`Φ_τ(a)` for `c = φ` and `Ψ_τ(b)` for `c = ψ`. +/-- Encoding for an arbitrary coefficient family: weight each array entry by the product of +coefficients selected by the leaf. This specializes to both input encodings. -/ def encodeWith (τ : Leaf L) (a : (Fin L → α) → ℤ) : ℤ := ∑ u, a u * ∏ ℓ, c (τ ℓ) (u ℓ) -/-- For `L = 0` the only string is empty, and so are the products over the levels. --/ +/-- With no coordinates, the encoding evaluates the array at its unique empty argument. -/ theorem encodeWith_zero (τ : Leaf 0) (a : (Fin 0 → α) → ℤ) : encodeWith c τ a = a Fin.elim0 := by simp [encodeWith, Subsingleton.elim (default : Fin 0 → α) Fin.elim0] -/-- The number at the leaf `λτ'` for `a` is the number at the leaf `τ'` for `∑_s c_λ(s) a_s`. +/-- Process the first label of an encoding by combining the first-coordinate slices, then encode +the resulting shorter array. -/ theorem encodeWith_cons (lam : Term) (τ' : Leaf L) (a : (Fin (L + 1) → α) → ℤ) : encodeWith c (Fin.cons lam τ' : Leaf (L + 1)) a @@ -253,22 +227,19 @@ end encodeWith /-! ### Lemma 7 -/ -/-- In the proof of Lemma 7: for every leaf λτ' we have Φ_{λτ'}(a) = ∑_s φ_λ(s) Φ_{τ'}(a_s) = -Φ_{τ'}(A_λ). --/ +/-- The left encoding at a cons leaf is the tail encoding of the array produced by `encodeStepL`. -/ theorem encodeLeft_cons {L : ℕ} (lam : Term) (τ' : Leaf L) (a : LeftStr (L + 1) → ℤ) : encodeLeft (Fin.cons lam τ' : Leaf (L + 1)) a = encodeLeft τ' (encodeStepL lam a) := encodeWith_cons phi lam τ' a -/-- In the proof of Lemma 7: and similarly Ψ_{λτ'}(b) = Ψ_{τ'}(B_λ). +/-- The right encoding at a cons leaf is the tail encoding of the array produced by +`encodeStepR`. -/ theorem encodeRight_cons {L : ℕ} (lam : Term) (τ' : Leaf L) (b : RightStr (L + 1) → ℤ) : encodeRight (Fin.cons lam τ' : Leaf (L + 1)) b = encodeRight τ' (encodeStepR lam b) := encodeWith_cons psi lam τ' b -/-- For L = 0, the only leaf is the empty string τ, and Φ_τ(a) = a […], as the products over the -levels are empty (proof of Lemma 7). --/ +/-- The zero-depth left encoding reads the array at the empty tuple. -/ private theorem encodeLeft_zero (τ : Leaf 0) (a : LeftStr 0 → ℤ) : encodeLeft τ a = a Fin.elim0 := encodeWith_zero phi τ a @@ -278,7 +249,8 @@ private theorem encodeRight_zero (τ : Leaf 0) (b : RightStr 0 → ℤ) : encodeRight τ b = b Fin.elim0 := encodeWith_zero psi τ b -/-- **Lemma 7**, first half. At the leaf τ, Full.result(a, b) multiplies Φ_τ(a) by Ψ_τ(b). +/-- The operand record of full evaluation agrees with `encodeLeft` and `encodeRight` at every +leaf. -/ theorem Full.multipliedAt_eq_encoding {L : ℕ} (a : LeftStr L → ℤ) (b : RightStr L → ℤ) (τ : Leaf L) : @@ -294,8 +266,8 @@ theorem Full.multipliedAt_eq_encoding {L : ℕ} (a : LeftStr L → ℤ) (b : Rig = Full.multipliedAt L (encodeStepL (τ 0) a) (encodeStepR (τ 0) b) (Fin.tail τ) := rfl rw [hstep, ih, ← encodeLeft_cons, ← encodeRight_cons, Fin.cons_self_tail] -/-- Step (4) of `Full.result`, as it is read in the proof of Lemma 7: `c[z w'] = ∑_{λ contributing -to z} C_λ[w']`. +/-- Unfold full evaluation once and express decoding as a sum over labels relevant to the leading +output coordinate. -/ theorem Full.result_succ {L : ℕ} (a : LeftStr (L + 1) → ℤ) (b : RightStr (L + 1) → ℤ) (w : OutStr (L + 1)) : @@ -304,9 +276,7 @@ theorem Full.result_succ {L : ℕ} (a : LeftStr (L + 1) → ℤ) (b : RightStr ( rw [Term.sum_contributes] rfl -/-- In the proof of Lemma 7: `bilinearProduct(a, b)[z w'] = ∑_{λ contributing to z} -bilinearProduct(A_λ, B_λ)[w']`. --/ +/-- The bilinear product obeys the same first-coordinate decoding recurrence as full evaluation. -/ theorem bilinearProduct_succ {L : ℕ} (a : LeftStr (L + 1) → ℤ) (b : RightStr (L + 1) → ℤ) (w : OutStr (L + 1)) : bilinearProduct a b w = ∑ lam : Term with lam.Contributes (w 0), @@ -336,9 +306,8 @@ theorem full_spec {L : ℕ} (a : LeftStr L → ℤ) (b : RightStr L → ℤ) : /-! ### Lemma 8 -/ -/-- The display in the proof of Lemma 8: the sum, over the leaves `τ` contributing to `w`, of `∏_ℓ -φ_{τ_ℓ}(u_ℓ) ψ_{τ_ℓ}(v_ℓ)` is the product of sums ∏_ℓ ∑_{τ_ℓ contributing to w_ℓ} φ_{τ_ℓ}(u_ℓ) -ψ_{τ_ℓ}(v_ℓ) = ∏_ℓ γ(u_ℓ, v_ℓ, w_ℓ) by (3). +/-- Expand the product of one-coordinate coefficients as a sum of coefficient products indexed by +relevant leaves. -/ theorem prod_gamma_eq_sum_contributes {L : ℕ} (u : LeftStr L) (v : RightStr L) (w : OutStr L) : ∏ ℓ, coefficient (u ℓ) (v ℓ) (w ℓ) diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Identity.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Identity.lean index dea69212a..27995291a 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Identity.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Identity.lean @@ -33,7 +33,8 @@ The error term is retained; it cannot be discarded for unrestricted inputs. Part of [AlmanWilliams2026, Section 2]. Direct mathematical adaptation of [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Definitions are selectively extracted from that pin's `PaperStatements.lean`; no machine -semantics or running-time statement is imported. Source-derived prose is paraphrased below. +semantics or running-time statement is imported. The docstrings below explain the local +declarations in our own words. -/ @[expose] public section @@ -70,13 +71,15 @@ noncomputable def formR (f : RightVar → ℤ) : TriPoly := ∑ t, MvPolynomial. -/ noncomputable def formO (f : OutVar → ℤ) : TriPoly := ∑ z, MvPolynomial.C (f z) * polyO z -/-- Section 2.2: G := ∑_{i,j=1}^{3} x_i y_j z_ij + (∑_{i,j=1}^{2} p_ij q_ij) z₀. +/-- The target polynomial combines the nine outer `x`-`y` products with the four `p`-`q` products +attached to the inner coordinate. -/ noncomputable def G : TriPoly := (∑ i : Fin 3, ∑ j : Fin 3, polyL (.x i) * polyR (.y j) * polyO (.z i j)) + (∑ i : Fin 2, ∑ j : Fin 2, polyL (.p i j) * polyR (.q i j)) * polyO .z0 -/-- Lemma 6: E := ∑_{i,j=1}^{3} (x_i q̂_ij + p̂_ij y_j + p̂_ij q̂_ij) z_ij. +/-- The error polynomial collects the additional outer-coordinate terms involving `pHat` or +`qHat`. It has no inner-coordinate summand. -/ noncomputable def E : TriPoly := ∑ i : Fin 3, ∑ j : Fin 3, @@ -128,50 +131,48 @@ theorem formR_single (t : RightVar) : formR (Pi.single t 1) = polyR t := by theorem formO_single (z : OutVar) : formO (Pi.single z 1) = polyO z := by simp [formO_eq_linearCombination] -/-- Section 2.2, the display of the linear forms: `φ_{P_ij} = x_i + p̂_ij`. +/-- Interpret an indexed left coefficient form as the sum of its `x` variable and its extended `p` +form. -/ theorem formL_phi_P (i j : Fin 3) : formL (phi (.P i j)) = polyL (.x i) + formL (pHat i j) := by simp [phi, xForm, formL_eq_linearCombination] -/-- Section 2.2, the display of the linear forms: `ψ_{P_ij} = y_j + q̂_ij`. +/-- Interpret an indexed right coefficient form as the sum of its `y` variable and its extended +`q` form. -/ theorem formR_psi_P (i j : Fin 3) : formR (psi (.P i j)) = polyR (.y j) + formR (qHat i j) := by simp [psi, yForm, formR_eq_linearCombination] -/-- Section 2.2, the display of the linear forms: `χ_{P_ij} = z_ij + z₀`. +/-- Interpret an indexed output form as the sum of the corresponding outer variable and the inner +variable. -/ theorem formO_chi_P (i j : Fin 3) : formO (chi (.P i j)) = polyO (.z i j) + polyO .z0 := by simp [chi, zForm, z0Form, formO_eq_linearCombination] -/-- Section 2.2, the display of the linear forms: `φ_{P₀} = -(x₁ + x₂ + x₃)`. --/ +/-- The distinguished left form is the negative sum of the three `x` polynomials. -/ theorem formL_phi_P0 : formL (phi .P0) = -(polyL (.x 0) + polyL (.x 1) + polyL (.x 2)) := by simp [phi, xForm, formL_eq_linearCombination] -/-- Section 2.2, the display of the linear forms: `ψ_{P₀} = y₁ + y₂ + y₃`. --/ +/-- The distinguished right form is the sum of the three `y` polynomials. -/ theorem formR_psi_P0 : formR (psi .P0) = polyR (.y 0) + polyR (.y 1) + polyR (.y 2) := by simp [psi, yForm, formR_eq_linearCombination] -/-- Section 2.2, the display of the linear forms: `χ_{P₀} = z₀`. --/ +/-- The distinguished output form denotes the inner polynomial variable alone. -/ theorem formO_chi_P0 : formO (chi .P0) = polyO .z0 := formO_single .z0 /-! ### Lemma 6 -/ -/-- Section 2.2: every column of p̂ […] sums to zero. --/ +/-- Summing `pHat` over the row index gives the zero coefficient function. -/ theorem pHat_column_sum (j : Fin 3) : ∑ i, pHat i j = 0 := by fin_cases j <;> simp [Fin.sum_univ_three, pHat] -/-- Section 2.2: every row of q̂ sums to zero. --/ +/-- Summing `qHat` over the column index gives the zero coefficient function. -/ theorem qHat_row_sum (i : Fin 3) : ∑ j, qHat i j = 0 := by fin_cases i <;> simp [Fin.sum_univ_three, qHat] -/-- Section 2.2: Notice that ∑_{i,j=1}^{3} p̂_ij q̂_ij = ∑_{i,j=1}^{2} p_ij q_ij is the desired -inner product. +/-- The product sum for the extended arrays reduces to the four products from their original +two-by-two arrays. -/ theorem sum_pHat_mul_qHat : ∑ i : Fin 3, ∑ j : Fin 3, formL (pHat i j) * formR (qHat i j) diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Pruned.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Pruned.lean index e581c0e20..ac6930015 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Pruned.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Pruned.lean @@ -42,7 +42,8 @@ hold without that qualification. Part of [AlmanWilliams2026, Section 2]. Direct mathematical adaptation of [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). Definitions are selectively extracted from that pin's `PaperStatements.lean`; no machine -semantics or running-time statement is imported. Source-derived prose is paraphrased below. +semantics or running-time statement is imported. The docstrings below explain the local +declarations in our own words. -/ @[expose] public section @@ -51,21 +52,18 @@ open Finset namespace Cslib.Algorithms.MatrixMultiplication.Schonhage -/-- Section 2.4.2: for a set S of output strings and an output variable z, the slice S_z is the -set of output strings w' with z w' ∈ S. --/ +/-- Retain the tails of requested tuples whose first coordinate equals `z`. -/ def sliceSet {n : ℕ} (S : Finset (OutStr (n + 1))) (z : OutVar) : Finset (OutStr n) := univ.filter fun w' => (Fin.cons z w' : OutStr (n + 1)) ∈ S -/-- Section 2.4.2, step (2) of `Pruned.result`: let S_λ be the union of the slices S_z over the z -to which λ contributes. +/-- The requests for a child label are the union of slices for output coordinates to which that +label contributes. -/ def childSet {n : ℕ} (S : Finset (OutStr (n + 1))) (lam : Term) : Finset (OutStr n) := (univ.filter fun z => lam.Contributes z).biUnion fun z => sliceSet S z -/-- A call of `Pruned.result`. `vertex` is the vertex `τ₁⋯τ_k` at which it is made, as the pair -of its depth `k` and the string of terms. `passed` is the set that is passed to it, as the pair -of the length of its strings and the set. +/-- One mathematical call record, pairing its label-sequence position with the request set +supplied there. The dependent pairs retain the two tuple lengths. -/ structure PrunedCall where /-- The call depth and the terms identifying its vertex. @@ -75,9 +73,7 @@ structure PrunedCall where -/ passed : Σ n, Finset (OutStr n) -/-- The same call, seen from one level higher: the vertex `τ₁⋯τ_k` below the child `λ` is the -vertex `λ τ₁⋯τ_k`. --/ +/-- Embed a child call into its parent by prepending the child label to its recorded vertex. -/ def PrunedCall.under (lam : Term) (c : PrunedCall) : PrunedCall where vertex := ⟨c.vertex.1 + 1, (Fin.cons lam c.vertex.2 : Vertex (c.vertex.1 + 1))⟩ passed := c.passed @@ -105,31 +101,24 @@ def Pruned.run : (n : ℕ) → (Leaf n → ℤ) → (Leaf n → ℤ) → Finset ⟨⟨0, Fin.elim0⟩, ⟨n + 1, S⟩⟩ ::ₘ univ.val.bind fun lam => ((R lam).2).map (PrunedCall.under lam)) -/-- The array that `Pruned.result(S)` returns. --/ +/-- Project the output values from the pruned recursion and discard the call multiset. -/ def Pruned.result (n : ℕ) (encA encB : Leaf n → ℤ) (S : Finset (OutStr n)) : OutStr n → ℤ := (Pruned.run n encA encB S).1 -/-- All the calls that `Pruned.result(S)` makes, itself included, each with the set passed to it. --/ +/-- Project the multiset of call records, retaining repetitions and the root record. -/ def Pruned.calls (n : ℕ) (encA encB : Leaf n → ℤ) (S : Finset (OutStr n)) : Multiset PrunedCall := (Pruned.run n encA encB S).2 -/-- The leaves that `Pruned.result(U)`, called at the root, visits: the vertices at depth `L` -among its calls. --/ +/-- A leaf is visited when some recorded call has that full-depth vertex. -/ def Pruned.Visits {L : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (OutStr L)) (τ : Leaf L) : Prop := ∃ c ∈ Pruned.calls L encA encB U, c.vertex = ⟨L, τ⟩ -/-- The total size of the sets passed to the calls that `Pruned.result(U)` makes. --/ +/-- Add the cardinalities of the request sets carried by all call records, with multiplicity. -/ def Pruned.totalSize {L : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (OutStr L)) : ℕ := ((Pruned.calls L encA encB U).map fun c => c.passed.2.card).sum -/-- Section 2.4.3: the leaves contributing to an output string with inner set Q are the 10^m -leaves that choose any term at each of the m levels of Q, but P_ij at every level outside Q where -the output string has z_ij. We call the one that chooses P₀ at every level of Q the private leaf -of the output string. +/-- Choose the private label at every output coordinate. The resulting leaf is relevant to that +output tuple and distinguishes it from every other output tuple. -/ def privateLeaf {L : ℕ} (w : OutStr L) : Leaf L := fun ℓ => (w ℓ).privateTerm @@ -144,21 +133,18 @@ def computeEncoding {α : Type} [Fintype α] (c : Term → α → ℤ) : | 0, a, _ => a Fin.elim0 | L + 1, a, τ => computeEncoding c L (fun u' => ∑ s, c (τ 0) s * sliceAt a s u') (Fin.tail τ) -/-- At the leaf `τ` the procedure of Section 2.4.1 stores `∑_u a[u] ∏_ℓ c_{τ_ℓ}(u_ℓ)`. --/ +/-- Recursive encoding agrees with the closed coefficient-product formula `encodeWith`. -/ theorem computeEncoding_eq_encodeWith {α : Type} [Fintype α] (c : Term → α → ℤ) {L : ℕ} (a : (Fin L → α) → ℤ) (τ : Leaf L) : computeEncoding c L a τ = encodeWith c τ a := by induction L with | zero => rw [computeEncoding, encodeWith_zero] | succ n ih => rw [computeEncoding, ih, ← encodeWith_cons, Fin.cons_self_tail] -/-- The procedure of Section 2.4.1 computes the encoding of `a`. --/ +/-- Specializing recursive encoding to `phi` gives the existing left-encoding array. -/ theorem computeEncoding_phi {L : ℕ} (a : LeftStr L → ℤ) : computeEncoding phi L a = encodingL a := funext (computeEncoding_eq_encodeWith phi a) -/-- The procedure of Section 2.4.1 computes the encoding of `b`. --/ +/-- Specializing recursive encoding to `psi` gives the existing right-encoding array. -/ theorem computeEncoding_psi {L : ℕ} (b : RightStr L → ℤ) : computeEncoding psi L b = encodingR b := funext (computeEncoding_eq_encodeWith psi b) @@ -171,30 +157,28 @@ theorem mem_sliceSet {n : ℕ} (S : Finset (OutStr (n + 1))) (z : OutVar) (w' : w' ∈ sliceSet S z ↔ (Fin.cons z w' : OutStr (n + 1)) ∈ S := by simp [sliceSet] -/-- Section 2.4.2, step (2): i.e., S_{P_ij} := S_{z_ij} ∪ S_{z₀}. --/ +/-- An indexed child combines the matching outer slice with the inner slice of the request set. -/ theorem childSet_P {n : ℕ} (S : Finset (OutStr (n + 1))) (i j : Fin 3) : childSet S (.P i j) = sliceSet S (.z i j) ∪ sliceSet S .z0 := by have hz : (univ.filter fun z => (Term.P i j).Contributes z) = {.z i j, .z0} := by decide +revert rw [childSet, hz, biUnion_insert, singleton_biUnion] -/-- Section 2.4.2, step (2): and S_{P₀} := S_{z₀}. --/ +/-- The distinguished child needs only the inner slice of the request set. -/ theorem childSet_P0 {n : ℕ} (S : Finset (OutStr (n + 1))) : childSet S .P0 = sliceSet S .z0 := by have hz : (univ.filter fun z => Term.P0.Contributes z) = {.z0} := by decide rw [childSet, hz, singleton_biUnion] -/-- Section 2.4.2, step (4): These restrictions make sense, since S_{z_ij} ⊆ S_{P_ij} and S_{z₀} ⊆ -S_λ for every λ, by step (2): `S_z ⊆ S_λ` whenever `λ` contributes to `z`. +/-- Every requested suffix in a relevant output slice belongs to the corresponding child request +set. -/ theorem sliceSet_subset_childSet {n : ℕ} (S : Finset (OutStr (n + 1))) {lam : Term} {z : OutVar} (h : lam.Contributes z) : sliceSet S z ⊆ childSet S lam := subset_biUnion_of_mem (fun z => sliceSet S z) (mem_filter.mpr ⟨mem_univ z, h⟩) -/-- In the proof of Lemma 10: S_λ consists of the last L-k-1 variables of each string in S such -that λ contributes to the first variable of that string. +/-- A suffix belongs to a child request set precisely when it comes from a requested parent tuple +whose leading coordinate is relevant to that child label. -/ theorem mem_childSet {n : ℕ} {S : Finset (OutStr (n + 1))} {lam : Term} {w' : OutStr n} : w' ∈ childSet S lam ↔ ∃ w ∈ S, lam.Contributes (w 0) ∧ Fin.tail w = w' := by @@ -207,7 +191,8 @@ theorem mem_childSet {n : ℕ} {S : Finset (OutStr (n + 1))} {lam : Term} {w' : /-! ### Lemma 10, first claim: the values -/ -/-- The array `C_λ` of step (3) of `Pruned.result`; it is the zero array if `S_λ = ∅`. +/-- Evaluate a child on its requested suffixes, using the zero array when that child has no +requests. -/ def Pruned.childResult {n : ℕ} (encA encB : Leaf (n + 1) → ℤ) (S : Finset (OutStr (n + 1))) (lam : Term) : OutStr n → ℤ := @@ -215,8 +200,7 @@ def Pruned.childResult {n : ℕ} (encA encB : Leaf (n + 1) → ℤ) (S : Finset Pruned.result n (sliceAt encA lam) (sliceAt encB lam) (childSet S lam) else 0 -/-- Steps (2) to (4) of `Pruned.result`, written out. --/ +/-- Unfold the pruned values once, exposing the child arrays used for decoding. -/ theorem Pruned.result_succ {n : ℕ} (encA encB : Leaf (n + 1) → ℤ) (S : Finset (OutStr (n + 1))) : Pruned.result (n + 1) encA encB S = S.piecewise (fun w => match w 0 with @@ -224,8 +208,8 @@ theorem Pruned.result_succ {n : ℕ} (encA encB : Leaf (n + 1) → ℤ) (S : Fin | .z0 => ∑ lam, Pruned.childResult encA encB S lam (Fin.tail w)) 0 := by simp only [Pruned.result, Pruned.run, Pruned.childResult, apply_ite Prod.fst] -/-- Step (4) of `Pruned.result` at one string: the entry at `w ∈ S` is the sum of the `C_λ`, at -the rest of `w`, over the terms `λ` that contribute to the first variable of `w`. +/-- At a requested tuple, decoding sums child values at its tail over labels relevant to its +leading coordinate; every unrequested tuple has value zero. -/ theorem Pruned.result_succ_apply {n : ℕ} (encA encB : Leaf (n + 1) → ℤ) (S : Finset (OutStr (n + 1))) (w : OutStr (n + 1)) : @@ -235,9 +219,8 @@ theorem Pruned.result_succ_apply {n : ℕ} (encA encB : Leaf (n + 1) → ℤ) rw [Pruned.result_succ, Finset.piecewise, Term.sum_contributes] rfl -/-- What `Pruned.result` returns, for arbitrary arrays in place of the two encodings: at each -string of `S`, the sum, over the leaves contributing to it, of the product of the two numbers -looked up at the leaf in step (1). +/-- For arbitrary supplied leaf arrays, the pruned result is zero off the request set and is a sum +of relevant leaf products on that set. -/ theorem Pruned.result_eq_piecewise_sum {n : ℕ} (encA encB : Leaf n → ℤ) (S : Finset (OutStr n)) : Pruned.result n encA encB S @@ -289,21 +272,19 @@ theorem Pruned.result_eq_piecewise_full {n : ℕ} (a : LeftStr n → ℤ) (b : R /-! ### Lemma 10, second claim: the calls that are made and the sets passed to them -/ -/-- Section 2.3.2: a vertex τ₁ ⋯ τ_k contributes to an output string w if τ_ℓ contributes to w_ℓ -at every level ℓ ≤ k. Only `k ≤ L` occurs; the hypothesis `h` is there so that `w_ℓ` makes sense. +/-- Test relevance for an initial label sequence against the matching prefix of an output tuple. +Longer label sequences fail the depth condition. -/ def Vertex.Contributes {k L : ℕ} (τ : Vertex k) (w : OutStr L) : Prop := ∀ (ℓ : Fin k) (h : (ℓ : ℕ) < L), (τ ℓ).Contributes (w ⟨ℓ, h⟩) -/-- Section 2.3.2: for a vertex at depth `L`, that is, a leaf, contributes to means that `τ_ℓ` -contributes to `w_ℓ` at every level. --/ +/-- At full depth, vertex relevance coincides with the existing leaf-relevance predicate. -/ theorem Vertex.contributes_iff_leaf {L : ℕ} (τ : Leaf L) (w : OutStr L) : Vertex.Contributes τ w ↔ Leaf.Contributes τ w := ⟨fun h ℓ => h ℓ ℓ.2, fun h ℓ _ => h ℓ⟩ -/-- The vertex `λ τ₁⋯τ_k` contributes to `w` if and only if `λ` contributes to the first variable -of `w` and `τ₁⋯τ_k` contributes to the rest of `w`. +/-- Decompose relevance of a nonempty vertex into relevance of its head and relevance of its +tail. -/ theorem Vertex.contributes_cons {k L : ℕ} {lam : Term} {τ : Vertex k} {w : OutStr (L + 1)} : Vertex.Contributes (Fin.cons lam τ : Vertex (k + 1)) w @@ -316,7 +297,8 @@ theorem Vertex.contributes_cons {k L : ℕ} {lam : Term} {τ : Vertex k} {w : Ou | zero => exact h0 | succ i => exact h1 i (Nat.lt_of_succ_lt_succ h) -/-- The calls made by the child `λ` of `Pruned.result(S)`: none if `S_λ = ∅`. +/-- Obtain a child call multiset from its recursion, or the empty multiset when its request set is +empty. -/ def Pruned.childCalls {n : ℕ} (encA encB : Leaf (n + 1) → ℤ) (S : Finset (OutStr (n + 1))) (lam : Term) : Multiset PrunedCall := @@ -324,16 +306,14 @@ def Pruned.childCalls {n : ℕ} (encA encB : Leaf (n + 1) → ℤ) (S : Finset ( Pruned.calls n (sliceAt encA lam) (sliceAt encB lam) (childSet S lam) else 0 -/-- The calls made by `Pruned.result(S)`: the call itself, and the calls made by the children `λ` -with `S_λ ≠ ∅`. --/ +/-- Unfold call recording into the parent record and the embedded records from nonempty children. -/ theorem Pruned.calls_succ {n : ℕ} (encA encB : Leaf (n + 1) → ℤ) (S : Finset (OutStr (n + 1))) : Pruned.calls (n + 1) encA encB S = ⟨⟨0, Fin.elim0⟩, ⟨n + 1, S⟩⟩ ::ₘ univ.val.bind fun lam => (Pruned.childCalls encA encB S lam).map (.under lam) := by simp only [Pruned.calls, Pruned.run, Pruned.childCalls, apply_ite Prod.snd] -/-- The calls of `Pruned.result(S)`, one level unfolded (steps (2) and (3)): the call itself, and -for each term `λ` with `S_λ ≠ ∅` the calls of `Pruned_λ(S_λ)`. +/-- Membership in the positive-depth call multiset means being the root record or an embedded call +of some nonempty child. -/ theorem Pruned.mem_calls_succ {n : ℕ} {encA encB : Leaf (n + 1) → ℤ} {S : Finset (OutStr (n + 1))} {c : PrunedCall} : @@ -373,9 +353,7 @@ private theorem PrunedCall.vertex_under_eq_iff {lam : Term} {c : PrunedCall} {k obtain rfl := eq_of_heq h simp [PrunedCall.under] -/-- `Pruned.result(U)`, called at the root, calls the vertex `τ`. For a leaf this is -`Pruned.Visits`. --/ +/-- A vertex is called when its depth and label tuple occur in the recorded call multiset. -/ def Pruned.Called {L k : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (OutStr L)) (τ : Vertex k) : Prop := ∃ c ∈ Pruned.calls L encA encB U, c.vertex = ⟨k, τ⟩ @@ -389,8 +367,8 @@ theorem Pruned.called_root {L : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (O | zero => exact ⟨_, Multiset.mem_singleton_self _, rfl⟩ | succ n => exact ⟨_, Pruned.mem_calls_succ.mpr (.inl rfl), rfl⟩ -/-- Step (3): a vertex `λ τ'` below the root is called exactly if `S_λ ≠ ∅` and the child `λ` -calls `τ'`. +/-- A non-root vertex is called exactly when its leading child has requests and the tail vertex is +called within that child. -/ theorem Pruned.called_succ {n k : ℕ} (encA encB : Leaf (n + 1) → ℤ) (S : Finset (OutStr (n + 1))) (τ : Vertex (k + 1)) : @@ -406,8 +384,8 @@ theorem Pruned.called_succ {n k : ℕ} (encA encB : Leaf (n + 1) → ℤ) (S : F exact ⟨c'.under (τ 0), Pruned.mem_calls_succ.mpr (.inr ⟨τ 0, hne, c', hc', rfl⟩), PrunedCall.vertex_under_eq_iff.mpr ⟨rfl, hc'τ⟩⟩ -/-- The vertex `λ τ'` contributes to a string of `S` exactly if `τ'` contributes to a string of -`S_λ`. +/-- Existence of a requested tuple relevant to a cons vertex reduces to the same question for the +tail vertex and the child request set. -/ private theorem exists_contributes_childSet {n k : ℕ} (S : Finset (OutStr (n + 1))) (τ : Vertex (k + 1)) : @@ -423,9 +401,8 @@ private theorem exists_contributes_childSet {n k : ℕ} (S : Finset (OutStr (n + · rintro ⟨w, hw, hτ⟩ exact ⟨_, mem_childSet.mpr ⟨w, hw, ((hcons w).mp hτ).1, rfl⟩, ((hcons w).mp hτ).2⟩ -/-- A vertex at depth `k ≤ L` is called by `Pruned.result(U)` if and only if it is the root or it -contributes to some string of `U` (proof of Lemma 10). The induction follows the recursion: the -vertex `λ τ'` is called by `Pruned.result(U)` exactly if `τ'` is called by `Pruned.result(U_λ)`. +/-- For vertices within the recursion depth, being called is equivalent to being the root or being +relevant to at least one requested tuple. -/ theorem Pruned.called_iff_root_or {L k : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (OutStr L)) (hkL : k ≤ L) (τ : Vertex k) : @@ -451,12 +428,9 @@ theorem Pruned.called_iff_root_or {L k : ℕ} (encA encB : Leaf L → ℤ) (U : · exact absurd hk (Nat.succ_ne_zero k) · exact ⟨⟨w', hw'⟩, ihτ.mpr (.inr ⟨w', hw', hτ'⟩)⟩ -/-- In the proof of Lemma 10: the vertex is called if and only if this set is nonempty, that is, -if and only if the vertex contributes to some string of `U`. - -NOTE. As printed this fails for the root when `U = ∅` (the root is always called). The -hypothesis `hk` excludes only that case. `hkL` says that `τ` is a vertex of the recursion tree, -whose depth is at most `L` (Section 2.3.1); the type `Vertex k` exists for every `k`. +/-- Away from the empty-request root exception, a vertex within the recursion depth is called +exactly when some requested tuple is relevant to it. The hypothesis `hk` admits every +positive-depth vertex and also admits the root when requests are nonempty. -/ theorem Pruned.called_iff {L k : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (OutStr L)) (hk : 1 ≤ k ∨ U.Nonempty) (hkL : k ≤ L) (τ : Vertex k) : @@ -518,10 +492,8 @@ private theorem mem_iff_root {L : ℕ} (U : Finset (OutStr L)) (w' : OutStr L) : simp only [isSuffixFrom_zero] exact ⟨fun h => ⟨w', h, fun ℓ => ℓ.elim0, rfl⟩, fun ⟨_, hw, _, h⟩ => h ▸ hw⟩ -/-- Proof of Lemma 10 (announced in Section 2.4.2): the set passed to the vertex τ₁⋯τ_k consists -of the suffixes w_{k+1}⋯w_L of the strings w of U to which the vertex contributes. For a call -`c`, `c.vertex.1` is the depth `k`, `c.vertex.2` the vertex, `c.passed.1` the length `L - k` of -the strings passed, and `c.passed.2` the set passed. +/-- A recorded call receives exactly the suffixes of original requests relevant to its vertex. Its +recorded remaining length and depth identify the suffix position. -/ theorem Pruned.mem_passed_iff {L : ℕ} {encA encB : Leaf L → ℤ} {U : Finset (OutStr L)} {c : PrunedCall} (hc : c ∈ Pruned.calls L encA encB U) (w' : OutStr c.passed.1) : @@ -554,8 +526,8 @@ theorem Pruned.visits_iff_root_or_mem_leaves {L : ℕ} (encA encB : Leaf L → simpa only [mem_leaves, Vertex.contributes_iff_leaf] using Pruned.called_iff_root_or encA encB U (le_refl L) τ -/-- **Lemma 10**, second claim. The leaves it visits are exactly those of leaves(U). The -hypothesis `0 < L ∨ U.Nonempty` is explained at `pruned_spec`. +/-- With positive depth or nonempty requests, the visited-leaf set is exactly `leaves U`. The +unconditional root case is given separately by `Pruned.visits_iff_root_or_mem_leaves`. -/ theorem Pruned.visits_iff_mem_leaves {L : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (OutStr L)) (h : 0 < L ∨ U.Nonempty) (τ : Leaf L) : Pruned.Visits encA encB U τ ↔ τ ∈ leaves U := @@ -565,7 +537,8 @@ theorem Pruned.visits_iff_mem_leaves {L : ℕ} (encA encB : Leaf L → ℤ) (U : /-! ### Lemma 10, third claim: the total size of the sets passed -/ -/-- The leaf `λ τ'` is in `leaves(S)` if and only if `τ'` is in `leaves(S_λ)`. +/-- Membership in a positive-depth relevant-leaf set reduces to membership of the tail in the +child relevant-leaf set. -/ theorem mem_leaves_succ {n : ℕ} {S : Finset (OutStr (n + 1))} {τ : Leaf (n + 1)} : τ ∈ leaves S ↔ Fin.tail τ ∈ leaves (childSet S (τ 0)) := by @@ -578,8 +551,8 @@ theorem mem_leaves_succ {n : ℕ} {S : Finset (OutStr (n + 1))} {τ : Leaf (n + obtain ⟨w, hw, h0, rfl⟩ := mem_childSet.mp hw' exact ⟨w, hw, Leaf.contributes_succ.mpr ⟨h0, h1⟩⟩ -/-- `leaves(S)` is the disjoint union, over the terms `λ`, of the leaves `λ τ'` with `τ'` in -`leaves(S_λ)`. +/-- Count relevant leaves by summing the relevant-leaf counts of all children; distinct leading +labels keep these families disjoint. -/ theorem card_leaves_succ {n : ℕ} (S : Finset (OutStr (n + 1))) : (leaves S).card = ∑ lam, (leaves (childSet S lam)).card := by @@ -604,9 +577,8 @@ theorem privateLeaf_injective {L : ℕ} : Function.Injective (privateLeaf (L := have hterm : Function.Injective OutVar.privateTerm := by decide fun _ _ h => funext fun ℓ => hterm (congrFun h ℓ) -/-- The map of the proof of Lemma 10, at the root: an output string goes to the leaf that picks -`P_ij` where the string has `z_ij` and `P₀` where it has `z₀`. The leaf contributes to the string -and determines it, so a set of output strings is no larger than its set of leaves. +/-- The private-leaf injection bounds the number of requested tuples by the number of their +relevant leaves. -/ theorem card_le_card_leaves {n : ℕ} (S : Finset (OutStr n)) : S.card ≤ (leaves S).card := by refine card_le_card_of_injOn privateLeaf (fun w hw => ?_) privateLeaf_injective.injOn @@ -663,8 +635,8 @@ private theorem sum_sizeAt (s : Multiset PrunedCall) (N : ℕ) (h : ∀ c ∈ s, rw [ih fun c' hc' => h c' (Multiset.mem_cons_of_mem hc'), sum_ite_eq, ite_eq_left (mem_range.mpr (h c (Multiset.mem_cons_self c s)))] -/-- End of the proof of Lemma 10: the sets passed to the vertices at each of the L+1 depths have -total size at most |leaves(U)|. +/-- At any fixed depth, the sum of all passed request-set cardinalities is bounded by the root +request set's relevant-leaf count. -/ theorem Pruned.sum_card_passed_le {L : ℕ} (encA encB : Leaf L → ℤ) (U : Finset (OutStr L)) (k : ℕ) : (((Pruned.calls L encA encB U).filter fun c => c.vertex.1 = k).map fun c => c.passed.2.card).sum From 12abeee84a0dc63313321e97e6628512f8d32f71 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 18:12:52 -0400 Subject: [PATCH 28/73] refactor(finset): use order-embedding names in OrderGlue Finish bounded #1545 from exact #1536 head66036150a. Replace generic level/inner/outer API vocabulary with order-embedding/subset/complement names. Remove five redundant membership/range/congruence wrappers, using Mathlib's orderEmbOfFin_mem/range_orderEmbOfFin and ordinary substitution directly. Migrate every actual Levels/Batch proof client with no deprecated aliases. Keep filter_glue's generic name and rename its predicate parameter to P. The retained generic statement headers are identical after the documented renaming and binder alpha-renaming. All 48 Levels and 13 Batch declaration statements and all definition bodies remain unchanged. The entire native Encoding module is token-identical after comment removal; every other native library file is unchanged. Source credit and pin are retained. Thus existing State.init, Signed.encode, width/frame/cost and encodingL/R meanings remain. Put the sum/option-versus-flat-digit explanation at the actual leftDigits, rightDigits and termDigits hover docs, preserving their enumeration bodies and order (row-major P terms, P0 digit9). Update the UPSTREAM naming follow-up. Add generic nonconsecutive signed/zero/Option reconstruction clients using Mathlib's orderEmbOfFin_unique, plus the routed optional #1536 asymmetric native L4,m1,N29 prefix tests. Those compute headers/count/tables/snapshot and decode/read ranks1 and2 with K0=2, distinguishing quotient/remainder blocks and signed values without a full10^4-leaf encoder run or native body edits. Reuse unchanged outside coverage: PR1496#issuecomment-6477111644 atb4936ce28, PR1496#issuecomment-6480746578 at68bbe40e3, and PR1536#issuecomment-6480852097 at66036150a12046b29986d8f108c0afaf49d01c61. These are agent reviews, not human approval. No new cited mathematical result or machine model is introduced. Validation: PASS build --wfail --iofail, mk_all --check, test --wfail --iofail, lint, lint-style and checkInitImports, with serial Smoke/global Axioms warmups. Full citation-aware hygiene --check-simp, fresh changed-file lints against actual base66036150a, strict PR-name and diff checks, and source/native identity checks PASS. One thread, no cache downloads, PYTHONUTF8=1 on Windows. Targeted MCP checks, full hygiene --check-simp and source/native identity checks pass. Final source tree: 0c21b608cc5ed234527c73a3853a26132f52da0d. No source/ancestor merges or restacks. Abstract assessment: no impact; no selected theorem/model/meaning/scope or PDF change. This is generic ergonomics/testing only; full #1394 remains open. AI-assisted implementation by codex-native-bandprep-t3-03b600bd, under parent continuation-worker coordination. No new review agent commissioned; ordinary outside coordinator review is requested when the bounded PR is ready. --- .../MatrixMultiplication/Schonhage/Batch.lean | 15 +- .../Schonhage/Levels.lean | 23 +-- .../Schonhage/Encoding.lean | 12 +- .../Foundations/Data/Finset/OrderGlue.lean | 160 ++++++++---------- ComplexityTests/FinsetOrderGlue.lean | 68 ++++++++ ComplexityTests/SchonhageBandPreparation.lean | 21 +++ UPSTREAM.md | 1 + 7 files changed, 191 insertions(+), 109 deletions(-) create mode 100644 ComplexityTests/FinsetOrderGlue.lean diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Batch.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Batch.lean index 262f3f864..e8a05a06a 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Batch.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Batch.lean @@ -105,7 +105,8 @@ private theorem exists_of_summand_ne_zero {L m : ℕ} (Q : Finset (Fin L)) (hQ : (r c : OuterStr L m) {u : LeftStr L} {v : RightStr L} (hγ : ∀ ℓ, coefficient (u ℓ) (v ℓ) (outStrOf Q hQ r c ℓ) ≠ 0) (hu : (innerSetL u).card = m) (hv : (innerSetR v).card = m) : ∃ π, u = leftStrOf Q hQ r π ∧ v = rightStrOf Q hQ π c := by - simp only [outStrOf, forall_level_iff Q hQ, glue_innerLevel, glue_outerLevel] at hγ + simp only [outStrOf, forall_iff_orderEmbOfFin_and_compl Q hQ, glue_orderEmbOfFin, + glue_orderEmbOfFin_compl] at hγ obtain ⟨hγin, hγout⟩ := hγ -- at a level ℓ ∈ Q we have w_ℓ = z₀ […] So (u_ℓ, v_ℓ) = (p_ij, q_ij) for some i, j. have hin : ∀ k, ∃ ij : Fin 2 × Fin 2, @@ -117,11 +118,11 @@ private theorem exists_of_summand_ne_zero {L m : ℕ} (Q : Finset (Fin L)) (hQ : -- Thus u and v have an inner variable at every level of Q, and their inner sets are therefore -- exactly Q. So u_ℓ and v_ℓ are outer at the levels outside Q. have huQ : innerSetL u = Q := - filter_eq_of_forall_innerLevel Q hQ (fun k => by rw [(hπ k).1]; trivial) hu + filter_eq_of_forall_orderEmbOfFin Q hQ (fun k => by rw [(hπ k).1]; trivial) hu have hvQ : innerSetR v = Q := - filter_eq_of_forall_innerLevel Q hQ (fun k => by rw [(hπ k).2]; trivial) hv - have huout := ((filter_eq_iff_forall_level Q hQ).mp huQ).2 - have hvout := ((filter_eq_iff_forall_level Q hQ).mp hvQ).2 + filter_eq_of_forall_orderEmbOfFin Q hQ (fun k => by rw [(hπ k).2]; trivial) hv + have huout := ((filter_eq_iff_orderEmbOfFin_and_compl Q hQ).mp huQ).2 + have hvout := ((filter_eq_iff_orderEmbOfFin_and_compl Q hQ).mp hvQ).2 -- at a level ℓ ∉ Q we have w_ℓ = z_ij for some i, j […] so (u_ℓ, v_ℓ) = (x_i, y_j). have hout : ∀ k, u ((Qᶜ.orderEmbOfFin (card_compl_of_card_eq Q hQ)) k) = .x (r k) ∧ v ((Qᶜ.orderEmbOfFin (card_compl_of_card_eq Q hQ)) k) = .y (c k) := @@ -135,8 +136,8 @@ private theorem prod_gamma_eq_one {L m : ℕ} (Q : Finset (Fin L)) (hQ : Q.card ∏ ℓ, coefficient (leftStrOf Q hQ r π ℓ) (rightStrOf Q hQ π c ℓ) (outStrOf Q hQ r c ℓ) = 1 := by have hone : ∀ ℓ, coefficient (leftStrOf Q hQ r π ℓ) (rightStrOf Q hQ π c ℓ) (outStrOf Q hQ r c ℓ) = 1 := by - simp only [forall_level_iff Q hQ, leftStrOf, rightStrOf, outStrOf, glue_innerLevel, - glue_outerLevel] + simp only [forall_iff_orderEmbOfFin_and_compl Q hQ, leftStrOf, rightStrOf, outStrOf, + glue_orderEmbOfFin, glue_orderEmbOfFin_compl] exact ⟨fun _ => (coefficient_z0 _ _).trans (ite_eq_left ⟨_, _, rfl, rfl⟩), fun _ => (coefficient_x_y_z _ _ _ _).trans (ite_eq_left ⟨rfl, rfl⟩)⟩ exact prod_eq_one fun ℓ _ => hone ℓ diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Levels.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Levels.lean index da18d521e..74bb5a6a3 100644 --- a/Complexity/Algorithms/MatrixMultiplication/Schonhage/Levels.lean +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/Levels.lean @@ -187,35 +187,36 @@ theorem innerSetO_outStrOf (r c : OuterStr L m) : innerSetO (outStrOf Q hQ r c) theorem outerPartL_leftStrOf (r : OuterStr L m) (π : InnerStr m) (h : (innerSetL (leftStrOf Q hQ r π)).card = m) : outerPartL (leftStrOf Q hQ r π) h = r := funext fun k => congrArg LeftVar.outerIndex - (glue_outerLevel_of_eq Q hQ _ _ (innerSetL_leftStrOf Q hQ r π) h k) + (glue_orderEmbOfFin_compl_of_eq Q hQ _ _ (innerSetL_leftStrOf Q hQ r π) h k) /-- The inner part of `leftStrOf Q hQ r π` is `π`. -/ @[simp] theorem innerPartL_leftStrOf (r : OuterStr L m) (π : InnerStr m) (h : (innerSetL (leftStrOf Q hQ r π)).card = m) : innerPartL (leftStrOf Q hQ r π) h = π := funext fun k => congrArg LeftVar.innerIndex - (glue_innerLevel_of_eq Q hQ _ _ (innerSetL_leftStrOf Q hQ r π) h k) + (glue_orderEmbOfFin_of_eq Q hQ _ _ (innerSetL_leftStrOf Q hQ r π) h k) /-- The inner part of `rightStrOf Q hQ π c` is `π`. -/ @[simp] theorem innerPartR_rightStrOf (π : InnerStr m) (c : OuterStr L m) (h : (innerSetR (rightStrOf Q hQ π c)).card = m) : innerPartR (rightStrOf Q hQ π c) h = π := funext fun k => congrArg RightVar.innerIndex - (glue_innerLevel_of_eq Q hQ _ _ (innerSetR_rightStrOf Q hQ π c) h k) + (glue_orderEmbOfFin_of_eq Q hQ _ _ (innerSetR_rightStrOf Q hQ π c) h k) /-- The outer part of `rightStrOf Q hQ π c` is `c`. -/ @[simp] theorem outerPartR_rightStrOf (π : InnerStr m) (c : OuterStr L m) (h : (innerSetR (rightStrOf Q hQ π c)).card = m) : outerPartR (rightStrOf Q hQ π c) h = c := funext fun k => congrArg RightVar.outerIndex - (glue_outerLevel_of_eq Q hQ _ _ (innerSetR_rightStrOf Q hQ π c) h k) + (glue_orderEmbOfFin_compl_of_eq Q hQ _ _ (innerSetR_rightStrOf Q hQ π c) h k) /-- The variable of `outStrOf Q hQ r c` at the `k`-th lowest level outside its inner set. -/ private theorem outStrOf_outerLevel (r c : OuterStr L m) (h : (innerSetO (outStrOf Q hQ r c)).card = m) (k : Fin (L - m)) : outStrOf Q hQ r c (((innerSetO (outStrOf Q hQ r c))ᶜ.orderEmbOfFin (card_compl_of_card_eq (innerSetO (outStrOf Q hQ r c)) h)) k) = .z (r k) (c k) := by - rw [outerLevel_congr Q hQ (innerSetO_outStrOf Q hQ r c) h, outStrOf, glue_outerLevel] + simp only [innerSetO_outStrOf] at h ⊢ + exact glue_orderEmbOfFin_compl Q hQ _ _ k /-- The row of `outStrOf Q hQ r c` is `r`. -/ @[simp] @@ -236,7 +237,8 @@ part. -/ theorem leftStr_eq_leftStrOf {u : LeftStr L} (hu : innerSetL u = Q) (h : (innerSetL u).card = m) : u = leftStrOf Q hQ (outerPartL u h) (innerPartL u h) := by subst hu - refine glue_index _ h LeftVar.IsInner (inner := fun π : Fin 2 × Fin 2 => .p π.1 π.2) ?_ ?_ rfl + refine eq_glue_of_reconstruct _ h LeftVar.IsInner + (fromSubset := fun π : Fin 2 × Fin 2 => .p π.1 π.2) ?_ ?_ rfl · rintro (i | ⟨i, j⟩) hs exacts [hs.elim, rfl] · rintro (i | ⟨i, j⟩) hs @@ -247,7 +249,8 @@ part. -/ theorem rightStr_eq_rightStrOf {v : RightStr L} (hv : innerSetR v = Q) (h : (innerSetR v).card = m) : v = rightStrOf Q hQ (innerPartR v h) (outerPartR v h) := by subst hv - refine glue_index _ h RightVar.IsInner (inner := fun π : Fin 2 × Fin 2 => .q π.1 π.2) ?_ ?_ rfl + refine eq_glue_of_reconstruct _ h RightVar.IsInner + (fromSubset := fun π : Fin 2 × Fin 2 => .q π.1 π.2) ?_ ?_ rfl · rintro (j | ⟨i, j⟩) ht exacts [ht.elim, rfl] · rintro (j | ⟨i, j⟩) ht @@ -257,9 +260,9 @@ theorem rightStr_eq_rightStrOf {v : RightStr L} (hv : innerSetR v = Q) theorem outStr_eq_outStrOf {w : OutStr L} (hw : innerSetO w = Q) (h : (innerSetO w).card = m) : w = outStrOf Q hQ (rowO w h) (colO w h) := by subst hw - refine glue_index _ h OutVar.IsInner (inner := fun _ : Unit => .z0) - (outer := fun rc : Fin 3 × Fin 3 => .z rc.1 rc.2) (innerIndex := fun _ => ()) - (outerIndex := fun z => (z.rowIndex, z.colIndex)) ?_ ?_ rfl + refine eq_glue_of_reconstruct _ h OutVar.IsInner (fromSubset := fun _ : Unit => .z0) + (fromCompl := fun rc : Fin 3 × Fin 3 => .z rc.1 rc.2) (toSubset := fun _ => ()) + (toCompl := fun z => (z.rowIndex, z.colIndex)) ?_ ?_ rfl · rintro (⟨i, j⟩ | _) hz exacts [hz.elim, rfl] · rintro (⟨i, j⟩ | _) hz diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Encoding.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Encoding.lean index 10afd12bb..d9cf2eb32 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Encoding.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Encoding.lean @@ -57,7 +57,9 @@ def termAt (t : Fin 10) : Term := if h : t.val < 9 then .P ⟨t.val / 3, by omega⟩ ⟨t.val % 3, Nat.mod_lt _ (by decide)⟩ else .P0 -/-- Explicit numbering of the seven left variables. -/ +/-- Executable flat address digits for the seven left variables. `LeftVar.equiv` instead +exposes their mathematical sum carrier `Fin 3 ⊕ (Fin 2 × Fin 2)`. This dictionary gives a +single `Fin 7` digit for each variable, for the native array layout. -/ def leftDigits : Fin 7 ≃ LeftVar where toFun := leftAt invFun @@ -66,7 +68,9 @@ def leftDigits : Fin 7 ≃ LeftVar where left_inv := by decide right_inv := by decide -/-- Explicit numbering of the seven right variables. -/ +/-- Executable flat address digits for the seven right variables. `RightVar.equiv` instead +exposes their mathematical sum carrier `Fin 3 ⊕ (Fin 2 × Fin 2)`. This dictionary gives a +single `Fin 7` digit for each variable, in the same order as `leftDigits`. -/ def rightDigits : Fin 7 ≃ RightVar where toFun := rightAt invFun @@ -75,7 +79,9 @@ def rightDigits : Fin 7 ≃ RightVar where left_inv := by decide right_inv := by decide -/-- Explicit numbering of all ten terms. -/ +/-- Executable flat address digits for all ten terms. `Term.equiv` instead exposes their +mathematical carrier `Option (Fin 3 × Fin 3)`. The native dictionary uses `Fin 10`, with +row-major `P i j` at digit `3*i+j` and `P0` at digit 9. -/ def termDigits : Fin 10 ≃ Term where toFun := termAt invFun diff --git a/Complexity/Foundations/Data/Finset/OrderGlue.lean b/Complexity/Foundations/Data/Finset/OrderGlue.lean index 9a61f690c..252b1e6b3 100644 --- a/Complexity/Foundations/Data/Finset/OrderGlue.lean +++ b/Complexity/Foundations/Data/Finset/OrderGlue.lean @@ -15,18 +15,20 @@ public import Mathlib.Data.Finset.Sort ## Main definitions -* `Finset.glue`: join two strings along a subset and its complement. +* `Finset.glue`: join two functions along a subset and its complement, each enumerated + by Mathlib's `Finset.orderEmbOfFin`. ## Main results +* `Finset.forall_iff_orderEmbOfFin_and_compl`: split a predicate over the two enumerations. * `Finset.eq_glue_iff`, `Finset.glue_inj`: reconstruction and uniqueness. +* `Finset.eq_glue_of_reconstruct`: reconstruct values through maps on the two parts. ## References -Adapted selectively from [AlmanWilliams2026, Section 2] and +Adapted selectively from [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). -Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical -counts and identities; they give no native program or instruction-cost certificate. +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. -/ @[expose] public section @@ -42,146 +44,126 @@ theorem card_compl_of_card_eq {L m : ℕ} (Q : Finset (Fin L)) (hQ : Q.card = m) variable {L m : ℕ} (Q : Finset (Fin L)) (hQ : Q.card = m) -/-! ### The levels of a set and of its complement, in order -/ +/-! ### Predicates on a subset and its complement -/ -/-- The `k`-th lowest level outside `Q` is not a level of `Q`. -/ -theorem outerLevel_notMem (k : Fin (L - m)) : (Qᶜ.orderEmbOfFin (card_compl_of_card_eq Q hQ)) k ∉ - Q := - mem_compl.mp (Qᶜ.orderEmbOfFin_mem (card_compl_of_card_eq Q hQ) k) - -/-- Every level of `Q` is the `k`-th lowest level of `Q` for some `k`. -/ -theorem exists_innerLevel {ℓ : Fin L} (hℓ : ℓ ∈ Q) : ∃ k, Finset.orderEmbOfFin Q hQ k = ℓ := - (Q.range_orderEmbOfFin hQ).ge hℓ - -/-- Every level outside `Q` is the `k`-th lowest level outside `Q` for some `k`. -/ -theorem exists_outerLevel {ℓ : Fin L} (hℓ : ℓ ∉ Q) : ∃ k, (Qᶜ.orderEmbOfFin (card_compl_of_card_eq - Q hQ)) k = ℓ := - (Qᶜ.range_orderEmbOfFin (card_compl_of_card_eq Q hQ)).ge (mem_coe.mpr (mem_compl.mpr hℓ)) - -/-- The levels of `Q` in order depend only on the set `Q`. -/ -theorem innerLevel_congr {Q' : Finset (Fin L)} (h : Q' = Q) (hQ' : Q'.card = m) : - Finset.orderEmbOfFin Q' hQ' = Finset.orderEmbOfFin Q hQ := by - subst h - rfl - -/-- The levels outside `Q` in order depend only on the set `Q`. -/ -theorem outerLevel_congr {Q' : Finset (Fin L)} (h : Q' = Q) (hQ' : Q'.card = m) : - (Q'ᶜ.orderEmbOfFin (card_compl_of_card_eq Q' hQ')) = (Qᶜ.orderEmbOfFin (card_compl_of_card_eq - Q hQ)) := by - subst h - rfl - -/-- A property holds at every level if and only if it holds at the levels of `Q` and at the levels -outside `Q`. -/ -theorem forall_level_iff {P : Fin L → Prop} : +/-- A predicate holds everywhere if and only if it holds on the ordered enumerations of +`Q` and its complement. -/ +theorem forall_iff_orderEmbOfFin_and_compl {P : Fin L → Prop} : (∀ ℓ, P ℓ) ↔ (∀ k, P (Finset.orderEmbOfFin Q hQ k)) ∧ ∀ k, P ((Qᶜ.orderEmbOfFin (card_compl_of_card_eq Q hQ)) k) := by refine ⟨fun h => ⟨fun _ => h _, fun _ => h _⟩, fun ⟨hin, hout⟩ ℓ => ?_⟩ by_cases hℓ : ℓ ∈ Q - · obtain ⟨k, rfl⟩ := exists_innerLevel Q hQ hℓ + · obtain ⟨k, rfl⟩ := (Q.range_orderEmbOfFin hQ).ge hℓ exact hin k - · obtain ⟨k, rfl⟩ := exists_outerLevel Q hQ hℓ + · obtain ⟨k, rfl⟩ := (Qᶜ.range_orderEmbOfFin (card_compl_of_card_eq Q hQ)).ge + (mem_coe.mpr (mem_compl.mpr hℓ)) exact hout k -/-- A property cuts out the set `Q` if and only if it holds at the levels of `Q` and fails at the -other levels. -/ -theorem filter_eq_iff_forall_level {P : Fin L → Prop} [DecidablePred P] : +/-- A predicate cuts out `Q` if and only if it holds on its ordered enumeration and fails +on the ordered enumeration of its complement. -/ +theorem filter_eq_iff_orderEmbOfFin_and_compl {P : Fin L → Prop} [DecidablePred P] : univ.filter P = Q ↔ (∀ k, P (Finset.orderEmbOfFin Q hQ k)) ∧ ∀ k, ¬ P ((Qᶜ.orderEmbOfFin (card_compl_of_card_eq Q hQ)) k) := by - simp only [Finset.ext_iff, forall_level_iff Q hQ, mem_filter, mem_univ, true_and, - Finset.orderEmbOfFin_mem, outerLevel_notMem Q hQ, iff_true, iff_false] - -/-- A property that holds at the `m` levels of `Q`, and at `m` levels in all, cuts out `Q`. -/ -theorem filter_eq_of_forall_innerLevel {P : Fin L → Prop} [DecidablePred P] + have hcompl (k : Fin (L - m)) : + Qᶜ.orderEmbOfFin (card_compl_of_card_eq Q hQ) k ∉ Q := + mem_compl.mp (Qᶜ.orderEmbOfFin_mem (card_compl_of_card_eq Q hQ) k) + simp only [Finset.ext_iff, forall_iff_orderEmbOfFin_and_compl Q hQ, mem_filter, mem_univ, + true_and, Finset.orderEmbOfFin_mem, hcompl, iff_true, iff_false] + +/-- A predicate holding on every enumerated element of `Q`, and on `m` elements in all, +cuts out `Q`. -/ +theorem filter_eq_of_forall_orderEmbOfFin {P : Fin L → Prop} [DecidablePred P] (hin : ∀ k, P (Finset.orderEmbOfFin Q hQ k)) (hcard : (univ.filter P).card = m) : univ.filter P = Q := by refine (eq_of_subset_of_card_le (fun ℓ hℓ => ?_) (hcard.trans hQ.symm).le).symm - obtain ⟨k, rfl⟩ := exists_innerLevel Q hQ hℓ + obtain ⟨k, rfl⟩ := (Q.range_orderEmbOfFin hQ).ge hℓ exact mem_filter.mpr ⟨mem_univ _, hin k⟩ -/-! ### Gluing two strings along a set of levels -/ +/-! ### Gluing functions along an ordered subset -/ variable {α : Type*} -/-- The string that has the letters of `f` at the levels of `Q` and the letters of `g` at the other -levels, both in the order of the levels. -/ +/-- The function taking the values of `f` on `Q` and of `g` on its complement, with each +part enumerated in increasing order. -/ def glue (f : Fin m → α) (g : Fin (L - m) → α) : Fin L → α := fun ℓ => if hℓ : ℓ ∈ Q then f ((Q.orderIsoOfFin hQ).symm ⟨ℓ, hℓ⟩) else g ((Qᶜ.orderIsoOfFin (card_compl_of_card_eq Q hQ)).symm ⟨ℓ, mem_compl.mpr hℓ⟩) -/-- At the `k`-th lowest level of `Q`, the glued string has the `k`-th letter of `f`. -/ +/-- Evaluating a glued function on the ordered enumeration of `Q` reads `f`. -/ @[simp] -theorem glue_innerLevel (f : Fin m → α) (g : Fin (L - m) → α) (k : Fin m) : +theorem glue_orderEmbOfFin (f : Fin m → α) (g : Fin (L - m) → α) (k : Fin m) : glue Q hQ f g (Finset.orderEmbOfFin Q hQ k) = f k := by rw [glue, dite_eq_left (Finset.orderEmbOfFin_mem Q hQ k)] exact congrArg f ((OrderIso.symm_apply_eq _).mpr (Subtype.ext rfl)) -/-- At the `k`-th lowest level outside `Q`, the glued string has the `k`-th letter of `g`. -/ +/-- Evaluating a glued function on the ordered enumeration of the complement reads `g`. -/ @[simp] -theorem glue_outerLevel (f : Fin m → α) (g : Fin (L - m) → α) (k : Fin (L - m)) : +theorem glue_orderEmbOfFin_compl (f : Fin m → α) (g : Fin (L - m) → α) (k : Fin (L - m)) : glue Q hQ f g ((Qᶜ.orderEmbOfFin (card_compl_of_card_eq Q hQ)) k) = g k := by - rw [glue, dite_eq_right (outerLevel_notMem Q hQ k)] + rw [glue, dite_eq_right (mem_compl.mp + (Qᶜ.orderEmbOfFin_mem (card_compl_of_card_eq Q hQ) k))] exact congrArg g ((OrderIso.symm_apply_eq _).mpr (Subtype.ext rfl)) -/-- At the `k`-th lowest level of a set `Q'` that is equal to `Q`, the string glued along `Q` has -the `k`-th letter of `f`. -/ -theorem glue_innerLevel_of_eq (f : Fin m → α) (g : Fin (L - m) → α) {Q' : Finset (Fin L)} +/-- Enumerating a subset equal to `Q` reads the same values from a glued function. -/ +theorem glue_orderEmbOfFin_of_eq (f : Fin m → α) (g : Fin (L - m) → α) {Q' : Finset (Fin L)} (h : Q' = Q) (hQ' : Q'.card = m) (k : Fin m) : glue Q hQ f g (Finset.orderEmbOfFin Q' hQ' k) = f k := by - rw [innerLevel_congr Q hQ h, glue_innerLevel] + subst h + apply glue_orderEmbOfFin -/-- At the `k`-th lowest level outside a set `Q'` that is equal to `Q`, the string glued along `Q` -has the `k`-th letter of `g`. -/ -theorem glue_outerLevel_of_eq (f : Fin m → α) (g : Fin (L - m) → α) {Q' : Finset (Fin L)} +/-- Enumerating the complement of a subset equal to `Q` reads the same values from a +function glued along `Q`. -/ +theorem glue_orderEmbOfFin_compl_of_eq (f : Fin m → α) (g : Fin (L - m) → α) {Q' : Finset (Fin L)} (h : Q' = Q) (hQ' : Q'.card = m) (k : Fin (L - m)) : glue Q hQ f g ((Q'ᶜ.orderEmbOfFin (card_compl_of_card_eq Q' hQ')) k) = g k := by subst h - apply glue_outerLevel + apply glue_orderEmbOfFin_compl variable {f f' : Fin m → α} {g g' : Fin (L - m) → α} -/-- The glued string is the only string with the letters of `f` at the levels of `Q` and the letters -of `g` at the other levels. -/ +/-- A function is the glued function exactly when its restrictions to both ordered +parts have the stated values. -/ theorem eq_glue_iff {u : Fin L → α} : u = glue Q hQ f g ↔ (∀ k, u (Finset.orderEmbOfFin Q hQ k) = f k) ∧ ∀ k, u ((Qᶜ.orderEmbOfFin (card_compl_of_card_eq Q hQ)) k) = g k := by - simp only [funext_iff, forall_level_iff Q hQ, glue_innerLevel, glue_outerLevel] + simp only [funext_iff, forall_iff_orderEmbOfFin_and_compl Q hQ, glue_orderEmbOfFin, + glue_orderEmbOfFin_compl] -/-- The two strings can be read off the glued string. -/ +/-- Both component functions can be read off their glued function. -/ theorem glue_inj : glue Q hQ f g = glue Q hQ f' g' ↔ f = f' ∧ g = g' := by rw [eq_glue_iff] - simp only [glue_innerLevel, glue_outerLevel, funext_iff] + simp only [glue_orderEmbOfFin, glue_orderEmbOfFin_compl, funext_iff] -/-- A map applied to a glued string, letter by letter. -/ +/-- Applying a map to a glued function maps both of its components. -/ theorem map_glue {β : Type*} (φ : α → β) : (fun ℓ => φ (glue Q hQ f g ℓ)) = glue Q hQ (fun k => φ (f k)) (fun k => φ (g k)) := by - simp only [eq_glue_iff, glue_innerLevel, glue_outerLevel, implies_true, and_self] + simp only [eq_glue_iff, glue_orderEmbOfFin, glue_orderEmbOfFin_compl, implies_true, and_self] -/-! ### Alphabets with inner and outer letters -/ +/-! ### Predicates and reconstruction on the two parts -/ -variable (IsInner : α → Prop) [DecidablePred IsInner] +variable (P : α → Prop) [DecidablePred P] -/-- If the letters of `f` are inner and those of `g` are outer, then the glued string has its inner -letters exactly at the levels of `Q`. -/ -theorem filter_glue (hf : ∀ k, IsInner (f k)) (hg : ∀ k, ¬ IsInner (g k)) : - (univ.filter fun ℓ => IsInner (glue Q hQ f g ℓ)) = Q := by - simpa only [filter_eq_iff_forall_level Q hQ, glue_innerLevel, glue_outerLevel] using +/-- If `P` holds on the values of `f` and fails on the values of `g`, it holds on the +glued function exactly at the elements of `Q`. -/ +theorem filter_glue (hf : ∀ k, P (f k)) (hg : ∀ k, ¬ P (g k)) : + (univ.filter fun ℓ => P (glue Q hQ f g ℓ)) = Q := by + simpa only [filter_eq_iff_orderEmbOfFin_and_compl Q hQ, glue_orderEmbOfFin, + glue_orderEmbOfFin_compl] using And.intro hf hg -/-- Reconstruct `u` by extracting and reinserting its inner and outer indices. The hypotheses -require that both index projections have the stated reconstruction property on their -respective classes of letters, and that the inner letters of `u` occur exactly on the -size-`m` set `Q`. -/ -theorem glue_index {I O : Type*} {inner : I → α} {outer : O → α} {innerIndex : α → I} - {outerIndex : α → O} (hI : ∀ s, IsInner s → inner (innerIndex s) = s) - (hO : ∀ s, ¬ IsInner s → outer (outerIndex s) = s) {u : Fin L → α} - (hu : (univ.filter fun ℓ => IsInner (u ℓ)) = Q) : - u = glue Q hQ (fun k => inner (innerIndex (u (Finset.orderEmbOfFin Q hQ k)))) - (fun k => outer (outerIndex (u ((Qᶜ.orderEmbOfFin (card_compl_of_card_eq Q hQ)) k)))) := by - obtain ⟨hin, hout⟩ := (filter_eq_iff_forall_level Q hQ).mp hu - exact (eq_glue_iff Q hQ).mpr ⟨fun k => (hI _ (hin k)).symm, fun k => (hO _ (hout k)).symm⟩ - +/-- Reconstruct `u` from maps that recover its values on the subset defined by `P` and +on its complement. The two reconstruction hypotheses apply on their respective parts. -/ +theorem eq_glue_of_reconstruct {β γ : Type*} {fromSubset : β → α} {fromCompl : γ → α} + {toSubset : α → β} {toCompl : α → γ} + (hsubset : ∀ a, P a → fromSubset (toSubset a) = a) + (hcompl : ∀ a, ¬ P a → fromCompl (toCompl a) = a) {u : Fin L → α} + (hu : (univ.filter fun ℓ => P (u ℓ)) = Q) : + u = glue Q hQ (fun k => fromSubset (toSubset (u (Finset.orderEmbOfFin Q hQ k)))) + (fun k => fromCompl (toCompl (u ((Qᶜ.orderEmbOfFin (card_compl_of_card_eq Q hQ)) k)))) := by + obtain ⟨hin, hout⟩ := (filter_eq_iff_orderEmbOfFin_and_compl Q hQ).mp hu + exact (eq_glue_iff Q hQ).mpr + ⟨fun k => (hsubset _ (hin k)).symm, fun k => (hcompl _ (hout k)).symm⟩ end Finset diff --git a/ComplexityTests/FinsetOrderGlue.lean b/ComplexityTests/FinsetOrderGlue.lean new file mode 100644 index 000000000..6f1f7e941 --- /dev/null +++ b/ComplexityTests/FinsetOrderGlue.lean @@ -0,0 +1,68 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +import Complexity.Foundations.Data.Finset.OrderGlue +import Mathlib.Data.Fin.VecNotation + +namespace ComplexityTests.FinsetOrderGlue + +open Finset + +private def selected : Finset (Fin 5) := {1, 3} + +private theorem card_selected : selected.card = 2 := by decide + +private def values : Fin 2 → ℤ := ![-3, -7] + +private def otherValues : Fin 3 → ℤ := ![11, 0, 5] + +/-- Nonconsecutive positions and their complement are both enumerated in increasing order. -/ +example : glue selected card_selected values otherValues = ![11, -3, 0, -7, 5] := by + symm + apply (eq_glue_iff selected card_selected).mpr + have hs : (![1, 3] : Fin 2 → Fin 5) = selected.orderEmbOfFin card_selected := + orderEmbOfFin_unique card_selected (by decide +kernel) (by decide +kernel) + have hc : (![0, 2, 4] : Fin 3 → Fin 5) = + selectedᶜ.orderEmbOfFin (card_compl_of_card_eq selected card_selected) := + orderEmbOfFin_unique _ (by decide +kernel) (by decide +kernel) + constructor + · rw [← hs] + decide +kernel + · rw [← hc] + decide +kernel + +/-- Generic predicate recognition includes a zero value in the complement. -/ +example : (univ.filter fun i => glue selected card_selected values otherValues i < 0) = + selected := by + apply filter_glue selected card_selected (fun z : ℤ => z < 0) + · decide +kernel + · decide +kernel + +/-- The named evaluation rules recover values on both enumerations. -/ +example : glue selected card_selected values otherValues (selected.orderEmbOfFin card_selected 1) + = -7 ∧ + glue selected card_selected values otherValues + (selectedᶜ.orderEmbOfFin (card_compl_of_card_eq selected card_selected) 2) = 5 := by + simp only [glue_orderEmbOfFin, glue_orderEmbOfFin_compl] + exact ⟨rfl, rfl⟩ + +private def optionalValues : Fin 5 → Option ℤ := ![none, some (-3), none, some 7, none] + +/-- Reconstruction applies to a generic optional value, without tensor-alphabet vocabulary. -/ +example : optionalValues = glue selected card_selected + (fun k => some ((optionalValues (selected.orderEmbOfFin card_selected k)).getD 0)) + (fun _ => none) := by + refine eq_glue_of_reconstruct selected card_selected (fun a : Option ℤ => a ≠ none) + (fromSubset := some) (fromCompl := fun _ : Unit => none) + (toSubset := fun a => a.getD 0) (toCompl := fun _ => ()) ?_ ?_ ?_ + · rintro (_ | a) ha + · exact (ha rfl).elim + · rfl + · intro a ha + exact (not_not.mp ha).symm + · decide +kernel + +end ComplexityTests.FinsetOrderGlue diff --git a/ComplexityTests/SchonhageBandPreparation.lean b/ComplexityTests/SchonhageBandPreparation.lean index bd2d3af56..6ee815d44 100644 --- a/ComplexityTests/SchonhageBandPreparation.lean +++ b/ComplexityTests/SchonhageBandPreparation.lean @@ -66,6 +66,27 @@ private def observe (w L m N : ℕ) #guard (observe 12 2 1 1 (fun _ _ => -1) (fun _ _ => 1)).map Prod.snd == some [0, 1, 0, 0] +private def asymmetricRead (side : Bool) (a : ℕ) : Code .standard 96 := + .seq header (.seq saveInput (.seq countCode (.seq tablesCode + (.seq (.op (.const band 0)) (.seq (.op (.const index a)) + (.seq splitCode (readCell side))))))) + +private def observeAsymmetricRead (side : Bool) (a : ℕ) : + Option (ℕ × ℕ × ℕ × ℕ × ℤ) := + let data : Data 96 := (State.init (input 4 1 29 + (fun i k => ((100 * i.val + k.val : ℕ) : ℤ) - 1000) + (fun k j => -((1000 * k.val + j.val + 7 : ℕ) : ℤ)))).data + ((asymmetricRead side a).eval 24 256 data).map fun (y, _) => + (y.regs root, y.regs maskAcc, y.regs block, y.regs row, decode (y.regs value)) + +-- At L=4,m=1, the computed root is 2. Masks 2 and 4 have ranks 1 and 2, so division +-- and remainder select different blocks. Both guarded rows (1 and 28) are real at N=29. +-- These runs compute headers/tables/snapshot and decode/load only, without an encoder run. +#guard observeAsymmetricRead false 36 == some (2, 2, 0, 1, -898) +#guard observeAsymmetricRead true 36 == some (2, 2, 1, 28, -2035) +#guard observeAsymmetricRead false 246 == some (2, 4, 1, 28, 1802) +#guard observeAsymmetricRead true 246 == some (2, 4, 0, 1, -2008) + -- Address-width boundary for a padded, two-band call. The nonzero sentinel also survives. #guard observe 6 1 0 4 (fun _ _ => 0) (fun _ _ => 0) == none #guard (observe 7 1 0 4 (fun _ _ => 0) (fun _ _ => 0)).map (fun z => z.1.2) == some true diff --git a/UPSTREAM.md b/UPSTREAM.md index 35d54dd00..177552c5a 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -316,6 +316,7 @@ agreement during upstream review. | `Foundations/Combinatorics/{FiniteCover,Hoeffding,Hypergeometric,PowersetTail,Sunflower}` | Consider Mathlib combinatorics; probabilistic-method lemmas belong near `Combinatorics/Probabilistic`. | | `Foundations/Data/Finset/Expect` | Product-type expectation helpers extracted from `Foundations/Combinatorics/Expander/Products` (#317); Mathlib candidates. | | `Foundations/Data/Finset/Markov` | Mathlib candidate: `Algebra/Order/BigOperators/Group/Finset` (`Finset.card_filter_mul_le_sum`, `Finset.mul_card_filter_le_sum`). | +| `Foundations/Data/Finset/OrderGlue` | #1545 resolves the generic naming follow-up from #1496 after native bands stabilized in #1536: order-embedding/subset/complement terminology, Mathlib's `orderEmbOfFin_mem`/`range_orderEmbOfFin` and ordinary substitution in place of five redundant wrappers, and migration of finite tensor clients with checked native band clients. Retain this focused topic module. | | `Foundations/Data/Matrix/Permanent` | Mathlib candidate: Laplace expansion of the permanent along row and column 0, beside `LinearAlgebra/Matrix/Permanent` (mirroring `Matrix.det_succ_row_zero`). | | `Foundations/LinearAlgebra/{Rank,Matrix/CutRank,Matrix/Lindsey}` | Mathlib candidates: `LinearAlgebra/Dimension/LinearMap` and `LinearAlgebra/Matrix/Rank`; placement of Lindsey's lemma needs agreement. | | `Probability/PairwiseIndependentHash/Shift` | Consider integration into `PairwiseIndependentHash`. | From 829613fbcead562b0a6d8913e576ca3d3909e6a0 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 18:49:48 -0400 Subject: [PATCH 29/73] feat(WordRAM): prove all-depth pruned controller execution WIP checkpoint for #1526; no PR and no completed component claim. The actual fixed native controller now has a depth-inductive execution proof with original Pruned.result values, stored queries/frames, concrete spatial preservation, input-derived signed fits and traceWeight charged continuation. Root loop exit discharges the continuation. All new library modules through Recursive.exec_ready have clean targeted Lean MCP diagnostics. New concrete entry/result client reports no diagnostics, but the complete existing test file is still elaborating its VM regression commands; no final test-pass or all-six claim. Initialization/root-list/report/flat State.init composition, global width/fit clients, full component validation and independent review remain open. Aggregate checks are held for the coordinated root validation slot. GenericRAM/Signed/Fits.lean is the unchanged blob from independently owned PR #1552, commit f276db1174333d3e6253b93878be8b95c1fcc713; credit retained. No native program body changed in this checkpoint. All proofs and invariant definitions are original native work; existing mathematical pruning/encodings retain their source credit. No selected-abstract impact or whole-3SUM endpoint claim. AI-assisted implementation by codex-three-sum-continuation-t3-3dda8289. --- .../Schonhage/Pruned/Bounds.lean | 137 +++++++++++++ .../Schonhage/Pruned/Codes.lean | 6 + .../Schonhage/Pruned/Memory.lean | 2 +- .../Schonhage/Pruned/NodeFrame.lean | 132 +++++++++++++ .../Schonhage/Pruned/Progress.lean | 152 +++++++++++++++ .../Schonhage/Pruned/ProgressChild.lean | 108 +++++++++++ .../Schonhage/Pruned/ProgressEmpty.lean | 96 ++++++++++ .../Schonhage/Pruned/ProgressFinish.lean | 56 ++++++ .../Schonhage/Pruned/ProgressReturn.lean | 180 ++++++++++++++++++ .../Schonhage/Pruned/Ready.lean | 179 +++++++++++++++++ .../Schonhage/Pruned/Recursive.lean | 123 ++++++++++++ .../Schonhage/Pruned/Return.lean | 21 +- .../Schonhage/Pruned/ScanMemory.lean | 30 +++ .../Schonhage/Pruned/Weight.lean | 34 ++++ .../Computability/GenericRAM/Signed/Fits.lean | 39 ++++ ComplexityTests/SchonhagePruned.lean | 53 ++++++ 16 files changed, 1346 insertions(+), 2 deletions(-) create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Bounds.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/NodeFrame.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Progress.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ProgressChild.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ProgressEmpty.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ProgressFinish.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ProgressReturn.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Ready.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Recursive.lean create mode 100644 Complexity/Computability/GenericRAM/Signed/Fits.lean diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Bounds.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Bounds.lean new file mode 100755 index 000000000..a4edc68b3 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Bounds.lean @@ -0,0 +1,137 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Values +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Weight +public import Complexity.Computability.GenericRAM.Signed.Fits +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Memory + +/-! +# Input-derived bounds for native pruned evaluation + +## Main definitions + +* `PrunedQuery.nodeBudget`: a scalar address/counter budget, including remaining epochs. +* `PrunedQuery.magnitudeBound`: covers both operands and every partial sum. + +## Main results + +* `PrunedQuery.nodeBudget_input_le`: the concrete input-derived bound covers the root budget. +* `PrunedQuery.fits_partialValue`: the original magnitude bound derives partial accumulator fits. + +The only fit assumptions are numerical inequalities on original parameters. No state-by-state +fit assertion or execution/time certificate is supplied. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- A node budget for ordinary pointers, powers, counters and all remaining fresh epochs. -/ +def nodeBudget (L f a b E n : ℕ) : ℕ := + 8 * f + 64 * (L + 1) * 10 ^ L + a + b + 2 * 10 ^ L + E + epochAllowance n + 128 + +/-- The larger concrete standalone bound covers initialization and every root parameter. -/ +theorem nodeBudget_input_le (L Q : ℕ) : + nodeBudget L (scratchBase L Q) 3 (3 + 10 ^ L) 1 L ≤ addressBound L Q := by + have he := epochAllowance_le L + have hp : 1 ≤ (10 : ℕ) ^ L := Nat.one_le_pow L _ (by decide) + unfold nodeBudget scratchBase outputBase addressBound + nlinarith only [he, hp] + +/-- Epoch allocation to a child is paid for by the remaining parent allowance. -/ +theorem nodeBudget_child_le {L f a b E E' n : ℕ} + (hE : E' + epochAllowance n ≤ E + epochAllowance (n + 1)) : + nodeBudget L f a b E' n ≤ nodeBudget L f a b E (n + 1) := by + unfold nodeBudget + omega + +/-- A single original-parameter envelope covers the two inputs and all tensor partial sums. -/ +def magnitudeBound (L A B : ℕ) : ℕ := A + B + 10 ^ L * (A * B) + +/-- The positive-endpoint inequality makes the nonnegative envelope a signed fitting value. -/ +theorem magnitudeBound_fits {w L A B : ℕ} (hw : 0 < w) + (hfit : magnitudeBound L A B < 2 ^ (w - 1)) : + GenericRAM.Signed.Fits w (magnitudeBound L A B : ℤ) := by + refine ⟨hw, ?_, ?_⟩ + · have hp : (0 : ℤ) ≤ 2 ^ (w - 1) := by positivity + have hm : (0 : ℤ) ≤ (magnitudeBound L A B : ℕ) := by positivity + omega + · exact_mod_cast hfit + +/-- The original bound on one input entry derives its signed fit through the common envelope. -/ +theorem fits_left {w L A B : ℕ} {z : ℤ} + (hfit : GenericRAM.Signed.Fits w (magnitudeBound L A B : ℤ)) (hz : |z| ≤ (A : ℤ)) : + GenericRAM.Signed.Fits w z := by + apply hfit.of_natAbs_le + have hz' : z.natAbs ≤ A := by + rw [← Int.natCast_natAbs] at hz + exact_mod_cast hz + exact hz'.trans (by unfold magnitudeBound; omega) + +/-- The right operand uses the same envelope, including the zero-input boundary. -/ +theorem fits_right {w L A B : ℕ} {z : ℤ} + (hfit : GenericRAM.Signed.Fits w (magnitudeBound L A B : ℤ)) (hz : |z| ≤ (B : ℤ)) : + GenericRAM.Signed.Fits w z := by + apply hfit.of_natAbs_le + have hz' : z.natAbs ≤ B := by + rw [← Int.natCast_natAbs] at hz + exact_mod_cast hz + exact hz'.trans (by unfold magnitudeBound; omega) + +/-- The leaf product fits the same envelope, including when either input bound is zero. -/ +theorem fits_product {w L A B : ℕ} {a b : ℤ} + (hfit : GenericRAM.Signed.Fits w (magnitudeBound L A B : ℤ)) + (ha : |a| ≤ (A : ℤ)) (hb : |b| ≤ (B : ℤ)) : GenericRAM.Signed.Fits w (a * b) := by + apply hfit.of_natAbs_le + have ha' : a.natAbs ≤ A := by + rw [← Int.natCast_natAbs] at ha + exact_mod_cast ha + have hb' : b.natAbs ≤ B := by + rw [← Int.natCast_natAbs] at hb + exact_mod_cast hb + rw [Int.natAbs_mul] + have hp : A * B ≤ 10 ^ L * (A * B) := + Nat.le_mul_of_pos_left _ (by positivity : 0 < (10 : ℕ) ^ L) + exact (Nat.mul_le_mul ha' hb').trans (hp.trans (by unfold magnitudeBound; omega)) + +/-- A partial sum at any remaining depth fits by the original global magnitude inequality. -/ +theorem fits_partialValue {w L n A B : ℕ} (hn : n + 1 ≤ L) + (hfit : GenericRAM.Signed.Fits w (magnitudeBound L A B : ℤ)) + (a b : Leaf (n + 1) → ℤ) (ha : ∀ v, |a v| ≤ (A : ℤ)) + (hb : ∀ v, |b v| ≤ (B : ℤ)) (S : Finset (OutStr (n + 1))) + (q : OutStr (n + 1)) (k : ℕ) : GenericRAM.Signed.Fits w (partialValue a b S q k) := by + apply hfit.of_natAbs_le + have h := abs_partialValue_le a b A B ha hb S q k + have hnat : (partialValue a b S q k).natAbs ≤ 10 ^ (n + 1) * (A * B) := by + rw [← Int.natCast_natAbs] at h + exact_mod_cast h + have hp := Nat.pow_le_pow_right (by decide : 1 ≤ (10 : ℕ)) hn + have hm := Nat.mul_le_mul_right (A * B) hp + exact hnat.trans (hm.trans (by unfold magnitudeBound; omega)) + +/-- Every returned child result fits, including an unselected or skipped zero value. -/ +theorem fits_childResult {w L n A B : ℕ} (hn : n ≤ L) + (hfit : GenericRAM.Signed.Fits w (magnitudeBound L A B : ℤ)) + (a b : Leaf (n + 1) → ℤ) (ha : ∀ v, |a v| ≤ (A : ℤ)) + (hb : ∀ v, |b v| ≤ (B : ℤ)) (S : Finset (OutStr (n + 1))) + (lam : Term) (q : OutStr n) : GenericRAM.Signed.Fits w (Pruned.childResult a b S lam q) := by + apply hfit.of_natAbs_le + have h := abs_childResult_le a b A B ha hb S lam q + have hnat : (Pruned.childResult a b S lam q).natAbs ≤ 10 ^ n * (A * B) := by + rw [← Int.natCast_natAbs] at h + exact_mod_cast h + have hp := Nat.pow_le_pow_right (by decide : 1 ≤ (10 : ℕ)) hn + have hm := Nat.mul_le_mul_right (A * B) hp + exact hnat.trans (hm.trans (by unfold magnitudeBound; omega)) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Codes.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Codes.lean index 8f15ff940..2574de042 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Codes.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Codes.lean @@ -92,6 +92,12 @@ theorem queryIndex_bijective (L : ℕ) : Function.Bijective (queryIndex (L := L) finFunctionFinEquiv.bijective.comp (Equiv.piCongrRight fun _ : Fin L => outputDigits.symm).bijective +/-- Replacing output strings by their numerical codes preserves a request-set cardinality. -/ +theorem card_image_queryIndex {L : ℕ} (S : Finset (OutStr L)) : + (S.image (fun w => (queryIndex w).val)).card = S.card := + Finset.card_image_of_injective S (fun _ _ he => + (queryIndex_bijective L).injective (Fin.ext he)) + /-- The first query digit is its output-variable digit; the suffix is shifted by one place. -/ theorem queryIndex_cons {L : ℕ} (z : OutVar) (w : OutStr L) : (queryIndex (Fin.cons z w) : ℕ) = (outputDigits.symm z).val + 10 * (queryIndex w).val := by diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Memory.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Memory.lean index 87fcc591f..2bf6c5df7 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Memory.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Memory.lean @@ -52,7 +52,7 @@ def stampsBase (L Q : ℕ) : ℕ := valuesBase L Q + (L + 1) * 10 ^ L def memoryEnd (L Q : ℕ) : ℕ := stampsBase L Q + 10 ^ L /-- A scalar budget for the concrete addresses, counters, powers and timestamp generations. -/ -def addressBound (L Q : ℕ) : ℕ := 128 * (L + 1) * 10 ^ L + 8 * Q + 128 +def addressBound (L Q : ℕ) : ℕ := 256 * (L + 1) * 10 ^ L + 32 * Q + 512 /-- The initialized payload includes no precomputed child requests or recursion certificate. -/ theorem length_input (L : ℕ) (a b : Fin (10 ^ L) → ℤ) (qs : List (Fin (10 ^ L))) : diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/NodeFrame.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/NodeFrame.lean new file mode 100755 index 000000000..7237d2337 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/NodeFrame.lean @@ -0,0 +1,132 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.EmptyChild + +/-! +# Frames for the recursive controller proof + +## Main results + +* `PrunedQuery.nodeWrites_child_subset`: a recursive child's writes stay in its parent's frame. +* `PrunedQuery.notMem_nodeWrites_list`: a node's request list remains read-only. +* `PrunedQuery.notMem_nodeWrites_ancestor_values`: a child preserves its parent's accumulators. + +The footprints are the concrete intervals used by the native program. These facts impose no +execution or fit hypotheses; they provide the spatial part of the node induction. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM + +/-- Normalize every row interval to its fixed workspace endpoint. -/ +theorem mem_nodeWrites_iff {L f d c : ℕ} (hd : d ≤ L) : c ∈ nodeWrites L f d ↔ + (((f + 4 * d ≤ c ∧ c < f + 4 * (L + 1)) ∨ + (f + 4 * (L + 1) + (d + 1) * 10 ^ L ≤ c ∧ + c < f + 4 * (L + 1) + (L + 1) * 10 ^ L)) ∨ + (f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L ≤ c ∧ + c < f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L)) ∨ + (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L ≤ c ∧ + c < f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + 10 ^ L) := by + have hf : f + 4 * d + 4 * (L + 1 - d) = f + 4 * (L + 1) := by omega + have hl : f + 4 * (L + 1) + (d + 1) * 10 ^ L + (L - d) * 10 ^ L = + f + 4 * (L + 1) + (L + 1) * 10 ^ L := by + calc + _ = f + 4 * (L + 1) + (d + 1 + (L - d)) * 10 ^ L := by ring + _ = _ := by rw [show d + 1 + (L - d) = L + 1 by omega] + have hv : f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L + + (L + 1 - d) * 10 ^ L = f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L := by + calc + _ = f + 4 * (L + 1) + ((L + 1) + (d + (L + 1 - d))) * 10 ^ L := by ring + _ = _ := by rw [show d + (L + 1 - d) = L + 1 by omega]; ring + simp only [nodeWrites, Finset.mem_union, Block.mem_footprint, Block.Owns, hf, hl, hv] + +/-- The recursive child's full writable frame is contained in the parent's frame. -/ +theorem nodeWrites_child_subset {L f d : ℕ} (hd : d < L) : + nodeWrites L f (d + 1) ⊆ nodeWrites L f d := by + intro c hc + rw [mem_nodeWrites_iff (by omega : d + 1 ≤ L)] at hc + rw [mem_nodeWrites_iff hd.le] + have hv := Nat.mul_le_mul_right (10 ^ L) (by omega : d ≤ d + 1) + have hl := Nat.mul_le_mul_right (10 ^ L) (by omega : d + 1 ≤ d + 1 + 1) + omega + +/-- The scan-and-push transition changes only cells in the current node's frame. -/ +theorem childWrites_subset_nodeWrites {L f d : ℕ} (hd : d < L) : + childWrites L f d ⊆ nodeWrites L f d := by + intro c hc + simp only [childWrites, Finset.mem_union, Block.mem_footprint, Block.Owns] at hc + rw [mem_nodeWrites_iff hd.le] + have hprod := Nat.mul_le_mul_right (10 ^ L) (by omega : d + 2 ≤ L + 1) + have hstep : (d + 1) * 10 ^ L + 10 ^ L = (d + 2) * 10 ^ L := by ring + rcases hc with (hc | hc) | hc + · left; left; right + exact ⟨hc.1, by omega⟩ + · right + exact hc + · left; left; left + omega + +/-- The scan-and-skip transition also stays inside the current node's frame. -/ +theorem emptyChildWrites_subset_nodeWrites {L f d : ℕ} (hd : d < L) : + emptyChildWrites L f d ⊆ nodeWrites L f d := by + intro c hc + simp only [emptyChildWrites, Finset.mem_union, Block.mem_footprint, Block.Owns] at hc + rw [mem_nodeWrites_iff hd.le] + have hprod := Nat.mul_le_mul_right (10 ^ L) (by omega : d + 2 ≤ L + 1) + have hstep : (d + 1) * 10 ^ L + 10 ^ L = (d + 2) * 10 ^ L := by ring + rcases hc with (hc | hc) | hc + · left; left; right + exact ⟨hc.1, by omega⟩ + · right + exact hc + · left; left; left + omega + +/-- No node changes the original input or any other cell below the workspace. -/ +theorem notMem_nodeWrites_before {L f d c : ℕ} (hd : d ≤ L) (hc : c < f) : + c ∉ nodeWrites L f d := by + rw [mem_nodeWrites_iff hd] + omega + +/-- A node preserves every occupied or unoccupied cell of its own request row. -/ +theorem notMem_nodeWrites_list {L f d q : ℕ} (hd : d ≤ L) (hq : q < 10 ^ L) : + f + 4 * (L + 1) + d * 10 ^ L + q ∉ nodeWrites L f d := by + rw [mem_nodeWrites_iff hd] + have hprod := Nat.mul_le_mul_right (10 ^ L) (by omega : d + 1 ≤ L + 1) + have hnext : d * 10 ^ L + q < (d + 1) * 10 ^ L := by nlinarith only [hq] + have hvals : d * 10 ^ L + q < (L + 1) * 10 ^ L := hnext.trans_le hprod + have hsplit : (L + 1) * 10 ^ L ≤ 2 * (L + 1) * 10 ^ L := by + rw [Nat.mul_assoc] + omega + omega + +/-- Returning from a child preserves all four words of every ancestor frame. -/ +theorem notMem_nodeWrites_ancestor_frame {L f d e j : ℕ} (hd : d ≤ L) (he : e < d) + (hj : j < 4) : f + 4 * e + j ∉ nodeWrites L f d := by + rw [mem_nodeWrites_iff hd] + omega + +/-- Returning from a child preserves every value-directory cell in an ancestor's row. -/ +theorem notMem_nodeWrites_ancestor_values {L f d e q : ℕ} (hd : d ≤ L) (he : e < d) + (hq : q < 10 ^ L) : + f + 4 * (L + 1) + (L + 1) * 10 ^ L + e * 10 ^ L + q ∉ nodeWrites L f d := by + rw [mem_nodeWrites_iff hd] + have hprod := Nat.mul_le_mul_right (10 ^ L) (by omega : e + 1 ≤ d) + have hprodL := Nat.mul_le_mul_right (10 ^ L) (by omega : d ≤ L + 1) + have hnext : e * 10 ^ L + q < (e + 1) * 10 ^ L := by nlinarith only [hq] + have hlt : e * 10 ^ L + q < d * 10 ^ L := hnext.trans_le hprod + have hts : (L + 1) * 10 ^ L + e * 10 ^ L + q < 2 * (L + 1) * 10 ^ L := by + nlinarith only [hlt, hprodL] + omega + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Progress.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Progress.lean new file mode 100644 index 000000000..55f0c3ff8 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Progress.lean @@ -0,0 +1,152 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Ready +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.NodeFrame + +/-! +# Stored partial sums between native child calls + +## Main definitions + +* `PrunedQuery.NodeProgress`: the actual dispatch cursor, frame, directory and partial values. + +## Main results + +* `PrunedQuery.exec_ready_enter`: native entry establishes the zero-term invariant. + +This is an invariant of the existing finite controller. It contains ordinary register and memory +equalities, not assumed executions or resource bounds. The timestamp directory is shared by all +nodes, so the parent retains its list but does not retain a stale membership-marker assertion. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Progress after `k` of the ten actual child dispatches. The initial state is kept only to +express the concrete spatial frame and preservation of its occupied request list. -/ +structure NodeProgress (L f a0 b0 n d p E : ℕ) (S : Finset (OutStr (n + 1))) + (a b : Leaf (n + 1) → ℤ) (x : Data registers) (k : ℕ) (y : Data registers) : Prop where + /-- The constants and workspace pointers are unchanged. -/ + regs : KernelRegs L f a0 b0 y + /-- The cursor is at this internal node. -/ + level_eq : y.regs level = d + /-- The next controller step dispatches the stored term. -/ + mode_eq : y.regs mode = 1 + /-- A dispatching node is active. -/ + active_eq : y.regs active = 1 + /-- The first three frame words survive all child returns. -/ + frame : (Block.mk (f + 4 * d) 4).Realizes y.mem ![S.card, p, 10 ^ d, k] + /-- The parent's requests remain in the actual original row. -/ + requests : ∀ i < S.card, + y.mem (f + 4 * (L + 1) + d * 10 ^ L + i) = + x.mem (f + 4 * (L + 1) + d * 10 ^ L + i) + /-- Every requested accumulator holds the completed prefix of terms. -/ + values : ∀ q ∈ S, + y.mem (f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L + (queryIndex q).val) = + encode (partialValue a b S q k) + /-- Native scans only increase the generation. -/ + epoch_le : E ≤ y.regs epoch + /-- Each completed term pays for its scan and its possible descendant calls. -/ + epoch_upper : y.regs epoch ≤ E + k * (1 + epochAllowance n) + /-- All directory cells admit a further fresh generation. -/ + timestamps : ∀ q < 10 ^ L, + y.mem (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q) ≤ y.regs epoch + /-- Only the explicit descendant workspace may have changed. -/ + footprint : Modifies (nodeWrites L f d) x.mem y.mem + +/-- The native scan's quotient image is the existing mathematical child set, even though +the shared timestamp directory no longer marks the parent's membership. -/ +theorem NodeProgress.scan_eq {L f a0 b0 n d p E k : ℕ} {S : Finset (OutStr (n + 1))} + {a b : Leaf (n + 1) → ℤ} {x y : Data registers} + (hx : NodeReady L f a0 b0 (n + 1) d p E S x) + (hy : NodeProgress L f a0 b0 n d p E S a b x k y) (t : Fin 10) : + scanSet true t.val (fun i => y.mem (f + 4 * (L + 1) + d * 10 ^ L + i)) S.card = + (childSet S (Encoding.termDigits t)).image (fun q => (queryIndex q).val) := by + rw [scanSet_child] + have he : (Finset.range S.card).image + (fun i => y.mem (f + 4 * (L + 1) + d * 10 ^ L + i)) = + (Finset.range S.card).image + (fun i => x.mem (f + 4 * (L + 1) + d * 10 ^ L + i)) := + Finset.image_congr (fun i hi => hy.requests i (Finset.mem_range.mp hi)) + rw [he, hx.image_read, childCodes_eq_image] + +/-- Before a dispatch, the original scalar budget covers the fresh scan and every possible +descendant generation for that term. -/ +theorem NodeProgress.epoch_child_le {L f a0 b0 n d p E k : ℕ} + {S : Finset (OutStr (n + 1))} {a b : Leaf (n + 1) → ℤ} {x y : Data registers} + (hy : NodeProgress L f a0 b0 n d p E S a b x k y) (hk : k < 10) : + y.regs epoch + 1 + epochAllowance n ≤ E + epochAllowance (n + 1) := by + have hp := mul_epochAllowance_le (n := n) (by omega : k + 1 ≤ 10) + nlinarith only [hy.epoch_upper, hp] + +/-- Entry performs the actual frame loads, clear loop and term store. The same returned state +has zero partial sums and the original request list, including when that list is empty. -/ +theorem exec_ready_enter {w L f a0 b0 n d p E : ℕ} (S : Finset (OutStr (n + 1))) + (a b : Leaf (n + 1) → ℤ) (x : Data registers) + (hx : NodeReady L f a0 b0 (n + 1) d p E S x) + (hfit : Fits w (nodeBudget L f a0 b0 E (n + 1))) : + ∃ y, ExecWithin w step x y (7 * S.card + 28) ∧ + NodeProgress L f a0 b0 n d p E S a b x 0 y := by + have hd : d < L := by have := hx.depth_add; omega + have hpow : (10 : ℕ) ^ (n + 1) ≤ 10 ^ L := + Nat.pow_le_pow_right (by decide) (by have := hx.depth_add; omega) + let input := fun i => x.mem (f + 4 * (L + 1) + d * 10 ^ L + i) + have hcodes : ∀ i < S.card, input i < 10 ^ L := + fun i hi => (hx.read_lt hi).trans_le hpow + obtain ⟨y, hy, hregs, hlevel, hmode, hE, hactive, hmem, hread, hframe⟩ := + exec_node_enter (N := S.card) x hx.regs hx.level_eq hd hx.mode_eq (hx.frame 0) hx.card_le + input (fun _ _ => rfl) hcodes (hfit.of_le (by unfold nodeBudget; omega)) + have hrow (j : ℕ) (hj : j < 4) : + ¬(Block.mk (f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L) + (10 ^ L)).Owns (f + 4 * d + j) := by + dsimp only [Block.Owns] + omega + have hstamp (q : ℕ) : + ¬(Block.mk (f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L) + (10 ^ L)).Owns (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q) := by + dsimp only [Block.Owns] + have hm := Nat.mul_le_mul_right (10 ^ L) (by omega : d + 1 ≤ L + 1) + rintro ⟨_, hlt⟩ + nlinarith only [hm, hlt] + refine ⟨y, hy, ⟨hregs, hlevel, hmode, hactive.trans hx.active_eq, ?_, hread, ?_, + ?_, ?_, ?_, hframe⟩⟩ + · intro j + change y.mem (f + 4 * d + j.val) = ![S.card, p, 10 ^ d, 0] j + rw [hmem] + fin_cases j + · change Function.update (_ : ℕ → ℕ) (f + 4 * d + 3) 0 (f + 4 * d + 0) = S.card + rw [Function.update_of_ne (by omega), zeroQueryPrefix_outside hcodes (hrow 0 (by decide))] + exact hx.frame 0 + · change Function.update (_ : ℕ → ℕ) (f + 4 * d + 3) 0 (f + 4 * d + 1) = p + rw [Function.update_of_ne (by omega), zeroQueryPrefix_outside hcodes (hrow 1 (by decide))] + exact hx.frame 1 + · change Function.update (_ : ℕ → ℕ) (f + 4 * d + 3) 0 (f + 4 * d + 2) = 10 ^ d + rw [Function.update_of_ne (by omega), zeroQueryPrefix_outside hcodes (hrow 2 (by decide))] + exact hx.frame 2 + · exact Function.update_self _ _ _ + · intro q hq + obtain ⟨i, hi, hqi⟩ := hx.exists_read hq + have hval : f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L + + (queryIndex q).val ≠ f + 4 * d + 3 := by omega + rw [hmem, Function.update_of_ne hval, partialValue_zero, show encode 0 = 0 from rfl] + rw [← hqi] + exact zeroQueryPrefix_at hi + · rw [hE, hx.epoch_eq] + · simp only [Nat.zero_mul, Nat.add_zero, hE, hx.epoch_eq, le_refl] + · intro q hq + rw [hmem, Function.update_of_ne (by omega), zeroQueryPrefix_outside hcodes (hstamp q), + hE, hx.epoch_eq] + exact hx.requests.stamp_le q hq + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ProgressChild.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ProgressChild.lean new file mode 100644 index 000000000..f9504b89b --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ProgressChild.lean @@ -0,0 +1,108 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Progress + +/-! +# Child entry established by the native dispatch + +## Main results + +* `PrunedQuery.exec_progress_child`: an actual nonempty scan and push establishes the next + induction assertion, with its computed set and prefix. + +The finite nonemptiness test is a proof case split. The machine uses the count it computes by +scanning the parent's actual list; that count, its contents and the fresh directory are proved +at the same returned state as the execution. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- A pushed child's strided input cells are the existing `sliceAt`, rather than a supplied +child array. Both preceding native phases preserve the original source block below `f`. -/ +theorem NodeProgress.child_source {L f a0 b0 n d p E base : ℕ} + {S : Finset (OutStr (n + 1))} {a b c : Leaf (n + 1) → ℤ} + {x y u : Data registers} (hx : NodeReady L f a0 b0 (n + 1) d p E S x) (k : Fin 10) + (hy : NodeProgress L f a0 b0 n d p E S a b x k.val y) + (hpush : Modifies (childWrites L f d) y.mem u.mem) (hbase : base + 10 ^ L ≤ f) + (hc : ∀ v, x.mem (base + p + 10 ^ d * (Encoding.leafIndex v).val) = + Signed.encode (c v)) (v : Leaf n) : + u.mem (base + (p + k.val * 10 ^ d) + 10 ^ (d + 1) * (Encoding.leafIndex v).val) = + Signed.encode (sliceAt c (Encoding.termDigits k) v) := by + have hd : d < L := by have := hx.depth_add; omega + let t := Encoding.termDigits k + have haddr : base + (p + k.val * 10 ^ d) + 10 ^ (d + 1) * (Encoding.leafIndex v).val = + base + p + 10 ^ d * (Encoding.leafIndex (Fin.cons t v)).val := by + rw [leafIndex_cons, show Encoding.termDigits.symm t = k from + Encoding.termDigits.symm_apply_apply k, pow_succ] + ring + rw [haddr] + have hbefore := hx.source_before hbase (Fin.cons t v) + have hnot := notMem_nodeWrites_before hd.le hbefore + rw [hpush _ (fun h => hnot (childWrites_subset_nodeWrites hd h)), hy.footprint _ hnot] + exact hc (Fin.cons t v) + +/-- The computed child state satisfies the concrete node-entry assertion and has a budget +derived from the original parent parameters. No child execution is assumed here. -/ +theorem exec_progress_child {w L f a0 b0 n d p E : ℕ} + (S : Finset (OutStr (n + 1))) (a b : Leaf (n + 1) → ℤ) (x y : Data registers) + (hx : NodeReady L f a0 b0 (n + 1) d p E S x) (k : Fin 10) + (hy : NodeProgress L f a0 b0 n d p E S a b x k.val y) + (hne : (childSet S (Encoding.termDigits k)).Nonempty) + (hfit : Fits w (nodeBudget L f a0 b0 E (n + 1))) : + ∃ z, ExecWithin w step y z (20 * S.card + 42) ∧ + NodeReady L f a0 b0 n (d + 1) (p + k.val * 10 ^ d) (y.regs epoch + 1) + (childSet S (Encoding.termDigits k)) z ∧ + Modifies (childWrites L f d) y.mem z.mem ∧ + Fits w (nodeBudget L f a0 b0 (y.regs epoch + 1) n) := by + have hd : d < L := by have := hx.depth_add; omega + have hpow := Nat.pow_le_pow_right (by decide : 1 ≤ (10 : ℕ)) + (by have := hx.depth_add; omega : n + 1 ≤ L) + let input := fun i => y.mem (f + 4 * (L + 1) + d * 10 ^ L + i) + have hcodes : ∀ i < S.card, input i < 10 ^ L := by + intro i hi + exact (hy.requests i hi ▸ hx.read_lt hi).trans_le hpow + have hscan := hy.scan_eq hx k + have hnonempty : (scanSet true k.val input S.card).Nonempty := by + rw [hscan] + exact hne.image _ + have hE := hy.epoch_child_le k.isLt + obtain ⟨z, hz, hregs, hlevel, hmode, hactive, hepoch, hrequests, hcount, hpref, + hstride, hterm, hframe⟩ := exec_node_child (N := S.card) (p := p) (k := k.val) y + hy.regs hy.level_eq hd hy.mode_eq (hy.frame 0) hx.card_le (hy.frame 1) hx.prefix_lt + (hy.frame 2) (hy.frame 3) k.isLt input (fun _ _ => rfl) hcodes hy.timestamps + hnonempty (hfit.of_le (by unfold nodeBudget; omega)) + have hset : ScanMemory z.mem (f + 4 * (L + 1) + (d + 1) * 10 ^ L) + (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L) (y.regs epoch + 1) (10 ^ L) + ((childSet S (Encoding.termDigits k)).image (fun q => (queryIndex q).val)) := by + simpa only [input, hscan] using hrequests + refine ⟨z, hz, ⟨hregs, ?_, ?_, hlevel, hmode, hactive.trans hy.active_eq, hepoch, + ?_, hset⟩, hframe, hfit.of_le (nodeBudget_child_le hE)⟩ + · have := hx.depth_add + omega + · have hp := hx.prefix_lt + have hk := k.isLt + rw [pow_succ] + nlinarith only [hp, hk] + · intro j + change z.mem (f + 4 * (d + 1) + j.val) = + ![(childSet S (Encoding.termDigits k)).card, p + k.val * 10 ^ d, 10 ^ (d + 1), 0] j + fin_cases j + · change z.mem (f + 4 * (d + 1)) = (childSet S (Encoding.termDigits k)).card + simpa only [input, hscan, card_image_queryIndex] using hcount + · exact hpref + · exact hstride + · exact hterm + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ProgressEmpty.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ProgressEmpty.lean new file mode 100644 index 000000000..e779242cb --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ProgressEmpty.lean @@ -0,0 +1,96 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Progress + +/-! +# Skipping a computed empty child + +## Main results + +* `PrunedQuery.exec_progress_empty`: native scanning and its computed zero count advance the + partial-sum invariant without a recursive call or a value store. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +private theorem notMem_emptyChildWrites_values {L f d q : ℕ} (hd : d < L) + (hq : q < 10 ^ L) : + f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L + q ∉ emptyChildWrites L f d := by + simp only [emptyChildWrites, Finset.mem_union, Block.mem_footprint, Block.Owns] + have hm := Nat.mul_le_mul_right (10 ^ L) (by omega : d + 1 ≤ L + 1) + have hnext : d * 10 ^ L + q < (d + 1) * 10 ^ L := by nlinarith only [hq] + have hbound := hnext.trans_le hm + have hsep : (d + 1) * 10 ^ L + 10 ^ L ≤ (L + 1) * 10 ^ L := by + have := Nat.mul_le_mul_right (10 ^ L) (by omega : d + 2 ≤ L + 1) + nlinarith only [this] + rw [show 2 * (L + 1) * 10 ^ L = (L + 1) * 10 ^ L + (L + 1) * 10 ^ L by ring] + omega + +/-- The proof selects the empty mathematical case, and the actual native scan proves that +the machine takes its zero-count branch. Every directory update and loop test is charged. -/ +theorem exec_progress_empty {w L f a0 b0 n d p E : ℕ} + (S : Finset (OutStr (n + 1))) (a b : Leaf (n + 1) → ℤ) (x y : Data registers) + (hx : NodeReady L f a0 b0 (n + 1) d p E S x) (k : Fin 10) + (hy : NodeProgress L f a0 b0 n d p E S a b x k.val y) + (hempty : childSet S (Encoding.termDigits k) = ∅) + (hfit : Fits w (nodeBudget L f a0 b0 E (n + 1))) : + ∃ z, ExecWithin w step y z (20 * S.card + 33) ∧ + NodeProgress L f a0 b0 n d p E S a b x (k.val + 1) z := by + have hd : d < L := by have := hx.depth_add; omega + have hpow := Nat.pow_le_pow_right (by decide : 1 ≤ (10 : ℕ)) + (by have := hx.depth_add; omega : n + 1 ≤ L) + let input := fun i => y.mem (f + 4 * (L + 1) + d * 10 ^ L + i) + have hcodes : ∀ i < S.card, input i < 10 ^ L := by + intro i hi + exact (hy.requests i hi ▸ hx.read_lt hi).trans_le hpow + have hscan : scanSet true k.val input S.card = ∅ := by + rw [hy.scan_eq hx k, hempty, Finset.image_empty] + have hE := hy.epoch_child_le k.isLt + obtain ⟨z, hz, hregs, hlevel, hmode, hactive, hepoch, htable, hfirst, hterm, hframe⟩ := + exec_node_emptyChild (N := S.card) (k := k.val) y hy.regs hy.level_eq hd hy.mode_eq + (hy.frame 0) hx.card_le (hy.frame 3) k.isLt input (fun _ _ => rfl) hcodes + hy.timestamps hscan (hfit.of_le (by unfold nodeBudget; omega)) + have hmod : Modifies (nodeWrites L f d) y.mem z.mem := + fun c hc => hframe c (fun h => hc (emptyChildWrites_subset_nodeWrites hd h)) + refine ⟨z, hz, ⟨hregs, hlevel, hmode, hactive.trans hy.active_eq, ?_, ?_, ?_, + ?_, ?_, ?_, ?_⟩⟩ + · intro j + change z.mem (f + 4 * d + j.val) = ![S.card, p, 10 ^ d, k.val + 1] j + fin_cases j + · exact (hfirst 0 (by decide)).trans (hy.frame 0) + · exact (hfirst 1 (by decide)).trans (hy.frame 1) + · exact (hfirst 2 (by decide)).trans (hy.frame 2) + · exact hterm + · intro i hi + exact (hmod _ (notMem_nodeWrites_list hd.le (hi.trans_le hx.card_le))).trans + (hy.requests i hi) + · intro q hq + have hqbound := (queryIndex q).isLt.trans_le hpow + rw [hframe _ (notMem_emptyChildWrites_values hd hqbound), hy.values q hq, + partialValue_succ] + have hc : Pruned.childResult a b S (Encoding.termDigits k) (Fin.tail q) = 0 := by + simp [Pruned.childResult, hempty] + simp only [hc, ite_self, add_zero] + · rw [hepoch] + exact hy.epoch_le.trans (Nat.le_succ _) + · rw [hepoch] + have he := hy.epoch_upper + nlinarith only [he] + · intro q hq + rw [hepoch] + exact htable.stamp_le q hq + · simpa only [Finset.union_self] using hy.footprint.trans hmod + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ProgressFinish.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ProgressFinish.lean new file mode 100644 index 000000000..c137ad0a7 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ProgressFinish.lean @@ -0,0 +1,56 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Progress +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Finish + +/-! +# Returning from a completed native internal node + +## Main results + +* `PrunedQuery.exec_progress_finish`: the native exhausted-term branch returns the existing + pruned values and all frame, cursor and timestamp conclusions used by the parent. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- All ten stored partial sums give the existing `Pruned.result`. The native final dispatch +and pop change registers only, stopping at the root and resuming accumulation at a parent. -/ +theorem exec_progress_finish {w L f a0 b0 n d p E : ℕ} + (S : Finset (OutStr (n + 1))) (a b : Leaf (n + 1) → ℤ) (x y : Data registers) + (hx : NodeReady L f a0 b0 (n + 1) d p E S x) + (hy : NodeProgress L f a0 b0 n d p E S a b x 10 y) + (hfit : Fits w (nodeBudget L f a0 b0 E (n + 1))) : + ∃ z, ExecWithin w step y z 26 ∧ NodeResult L f a0 b0 (n + 1) d E S a b x z := by + have hd : d ≤ L := by have := hx.depth_add; omega + obtain ⟨z, hz, hregs, hmem, hlevel, hmode, hactive, hepoch⟩ := + exec_node_finish y hy.regs hy.level_eq hd hy.mode_eq (hy.frame 3) + (hfit.of_le (by unfold nodeBudget; omega)) + refine ⟨z, hz, ⟨hregs, hlevel, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩⟩ + · intro hpos + simpa only [ite_eq_right (by omega : d ≠ 0)] using hmode + · simpa only [hy.active_eq] using hactive + · rw [hepoch] + exact hy.epoch_le + · rw [hepoch] + exact hy.epoch_upper.trans (Nat.add_le_add_left (epochAllowance_succ n) E) + · intro q hq + rw [hmem, hepoch] + exact hy.timestamps q hq + · intro q hq + rw [hmem, hy.values q hq, partialValue_ten a b S q hq] + · simpa only [hmem] using hy.footprint + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ProgressReturn.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ProgressReturn.lean new file mode 100644 index 000000000..4a113cd1d --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ProgressReturn.lean @@ -0,0 +1,180 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Progress +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Return + +/-! +# Accumulation after a proved native child return + +## Main results + +* `PrunedQuery.exec_progress_return`: actual indexed signed accumulation establishes the next + partial sum, using the child's ordinary value and frame postconditions. + +Execution of the child is composed separately by the depth induction. This lemma proves the +parent's subsequent native instructions; no child execution or cost certificate is input here. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +private theorem notMem_childWrites_frame {L f d j : ℕ} (hd : d < L) (hj : j < 4) : + f + 4 * d + j ∉ childWrites L f d := by + simp only [childWrites, Finset.mem_union, Block.mem_footprint, Block.Owns] + omega + +private theorem notMem_childWrites_values {L f d q : ℕ} (hd : d < L) + (hq : q < 10 ^ L) : + f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L + q ∉ childWrites L f d := by + simp only [childWrites, Finset.mem_union, Block.mem_footprint, Block.Owns] + have hm := Nat.mul_le_mul_right (10 ^ L) (by omega : d + 2 ≤ L + 1) + have hnext : d * 10 ^ L + q < (d + 1) * 10 ^ L := by nlinarith only [hq] + have hbound := Nat.mul_le_mul_right (10 ^ L) (by omega : d + 1 ≤ L + 1) + rw [show 2 * (L + 1) * 10 ^ L = (L + 1) * 10 ^ L + (L + 1) * 10 ^ L by ring] + have hv := hnext.trans_le hbound + have hsep : (d + 1) * 10 ^ L + 10 ^ L ≤ (L + 1) * 10 ^ L := by + nlinarith only [hm] + omega + +/-- Ordinary child postconditions suffice to prove the actual parent accumulation step. +All signed fits follow from the common original-input magnitude envelope. -/ +theorem exec_progress_return {w L f a0 b0 n d p E A B : ℕ} + (S : Finset (OutStr (n + 1))) (a b : Leaf (n + 1) → ℤ) + (x y u z : Data registers) (hx : NodeReady L f a0 b0 (n + 1) d p E S x) (k : Fin 10) + (hy : NodeProgress L f a0 b0 n d p E S a b x k.val y) + (hne : (childSet S (Encoding.termDigits k)).Nonempty) + (hpush : Modifies (childWrites L f d) y.mem u.mem) + (hz : NodeResult L f a0 b0 n (d + 1) (y.regs epoch + 1) + (childSet S (Encoding.termDigits k)) + (sliceAt a (Encoding.termDigits k)) (sliceAt b (Encoding.termDigits k)) u z) + (ha : ∀ v, |a v| ≤ (A : ℤ)) (hb : ∀ v, |b v| ≤ (B : ℤ)) + (hw : 0 < w) (hmag : magnitudeBound L A B < 2 ^ (w - 1)) + (hfit : Fits (w + 2) (nodeBudget L f a0 b0 E (n + 1))) : + ∃ v, ExecWithin (w + 2) step z v (31 * S.card + 29) ∧ + NodeProgress L f a0 b0 n d p E S a b x (k.val + 1) v := by + have hd : d < L := by have := hx.depth_add; omega + have hdn : d + 1 ≤ L := by omega + have hn : n + 1 ≤ L := by have := hx.depth_add; omega + have hpow := Nat.pow_le_pow_right (by decide : 1 ≤ (10 : ℕ)) hn + have henv := magnitudeBound_fits hw hmag + have hsub := childWrites_subset_nodeWrites (f := f) hd + have hsub' := nodeWrites_child_subset (f := f) hd + have hmid : Modifies (nodeWrites L f d) y.mem z.mem := by + intro c hc + exact (hz.frame c (fun h => hc (hsub' h))).trans (hpush c (fun h => hc (hsub h))) + have hparent (j : ℕ) (hj : j < 4) : z.mem (f + 4 * d + j) = y.mem (f + 4 * d + j) := + (hz.frame _ (notMem_nodeWrites_ancestor_frame hdn (by omega) hj)).trans + (hpush _ (notMem_childWrites_frame hd hj)) + have hvalues (q : ℕ) (hq : q < 10 ^ L) : + z.mem (f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L + q) = + y.mem (f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L + q) := + (hz.frame _ (notMem_nodeWrites_ancestor_values hdn (by omega) hq)).trans + (hpush _ (notMem_childWrites_values hd hq)) + let input := fun i => x.mem (f + 4 * (L + 1) + d * 10 ^ L + i) + have hinput : ∀ i < S.card, + z.mem (f + 4 * (L + 1) + d * 10 ^ L + i) = input i := by + intro i hi + exact (hmid _ (notMem_nodeWrites_list hd.le (hi.trans_le hx.card_le))).trans + (hy.requests i hi) + have hquery : ∀ i < S.card, ∃ q ∈ S, (queryIndex q).val = input i := + fun i hi => hx.exists_of_read hi + have hcodes : ∀ i < S.card, input i < 10 ^ L := + fun i hi => (hx.read_lt hi).trans_le hpow + let V := fun c => if h : c < 10 ^ (n + 1) then + partialValue a b S (queryAt ⟨c, h⟩) k.val else 0 + let C := fun c => if h : c < 10 ^ n then + Pruned.childResult a b S (Encoding.termDigits k) (queryAt ⟨c, h⟩) else 0 + have hV (q : OutStr (n + 1)) : V (queryIndex q).val = partialValue a b S q k.val := by + dsimp only [V] + rw [dite_eq_left (queryIndex q).isLt] + change partialValue a b S (queryAt (queryIndex q)) k.val = _ + rw [queryAt_queryIndex] + have hC (q : OutStr n) : C (queryIndex q).val = + Pruned.childResult a b S (Encoding.termDigits k) q := by + dsimp only [C] + rw [dite_eq_left (queryIndex q).isLt] + change Pruned.childResult a b S (Encoding.termDigits k) (queryAt (queryIndex q)) = _ + rw [queryAt_queryIndex] + have hsel (q : OutStr (n + 1)) : selected true k.val (queryIndex q).val ↔ + (Encoding.termDigits k).Contributes (q 0) := by + simp only [selected, ↓reduceIte, queryIndex_mod, contributes_iff_digit] + have hlevel : z.regs level = d := by simpa only [ite_eq_right (by omega : d + 1 ≠ 0), + Nat.add_sub_cancel_right] using hz.level_eq + have hactive : z.regs active = 1 := by simpa only [ite_eq_right (by omega : d + 1 ≠ 0)] + using hz.active_eq + have hN : z.mem (f + 4 * d) = S.card := (hparent 0 (by decide)).trans (hy.frame 0) + have hterm : z.mem (f + 4 * d + 3) = k.val := (hparent 3 (by decide)).trans (hy.frame 3) + have hparentValues : ∀ i < S.card, + z.mem (f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L + input i) = + encode (V (input i)) := by + intro i hi + obtain ⟨q, hq, he⟩ := hquery i hi + rw [← he, hvalues _ ((queryIndex q).isLt.trans_le hpow), hy.values q hq, hV] + have hchildValues : ∀ i < S.card, selected true k.val (input i) → + z.mem (f + 4 * (L + 1) + (L + 1) * 10 ^ L + (d + 1) * 10 ^ L + input i / 10) = + encode (C (input i / 10)) := by + intro i hi hselected + obtain ⟨q, hq, he⟩ := hquery i hi + have hcontrib := (hsel q).mp (he ▸ hselected) + have htail : Fin.tail q ∈ childSet S (Encoding.termDigits k) := + mem_childSet.mpr ⟨q, hq, hcontrib, rfl⟩ + rw [← he, queryIndex_div, hC] + simpa only [Pruned.childResult, hne, ↓reduceIte] using hz.values (Fin.tail q) htail + obtain ⟨v, hv, hregs, hlevel', hmode, hactive', hepoch, hresult, hfirst, hterm', + hstamp, hframe⟩ := exec_node_return (N := S.card) (k := k.val) z hz.regs hlevel hd + (hz.mode_eq (by omega)) hN hx.card_le hterm k.isLt input V C hinput hcodes hx.injOn_read + hparentValues hchildValues + (by + intro i hi + obtain ⟨q, _, he⟩ := hquery i hi + rw [← he, hV] + exact fits_partialValue hn henv a b ha hb S q k.val) + (by + intro i hi + obtain ⟨q, _, he⟩ := hquery i hi + rw [← he, queryIndex_div, hC] + exact fits_childResult (by omega) henv a b ha hb S _ _) + (hfit.of_le (by unfold nodeBudget; omega)) + refine ⟨v, hv, ⟨hregs, hlevel', hmode, hactive'.trans hactive, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩⟩ + · intro j + change v.mem (f + 4 * d + j.val) = ![S.card, p, 10 ^ d, k.val + 1] j + fin_cases j + · exact (hfirst 0 (by decide)).trans hN + · exact ((hfirst 1 (by decide)).trans (hparent 1 (by decide))).trans (hy.frame 1) + · exact ((hfirst 2 (by decide)).trans (hparent 2 (by decide))).trans (hy.frame 2) + · exact hterm' + · intro i hi + exact ((hframe _ (notMem_nodeWrites_list hd.le (hi.trans_le hx.card_le))).trans + (hmid _ (notMem_nodeWrites_list hd.le (hi.trans_le hx.card_le)))).trans (hy.requests i hi) + · intro q hq + obtain ⟨i, hi, he⟩ := hx.exists_read hq + have hr := hresult i hi + change input i = (queryIndex q).val at he + rw [he, hV, queryIndex_div, hC] at hr + rw [hr, partialValue_succ] + simp only [hsel] + · rw [hepoch] + exact hy.epoch_le.trans ((Nat.le_succ _).trans hz.epoch_le) + · rw [hepoch] + have hzu := hz.epoch_upper + have hyu := hy.epoch_upper + nlinarith only [hzu, hyu] + · intro q hq + rw [hstamp q hq, hepoch] + exact hz.timestamps q hq + · have hmod : Modifies (nodeWrites L f d) x.mem z.mem := by + simpa only [Finset.union_self] using hy.footprint.trans hmid + simpa only [Finset.union_self] using hmod.trans hframe + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Ready.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Ready.lean new file mode 100755 index 000000000..a476936c0 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Ready.lean @@ -0,0 +1,179 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Bounds +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Node + +/-! +# Entry and return assertions for the native node induction + +## Main definitions + +* `PrunedQuery.NodeReady`: the actual stored frame and timestamped query representation. +* `PrunedQuery.NodeResult`: register, value and spatial conclusions after returning. + +## Main results + +* `PrunedQuery.exec_ready_zero`: the terminal induction case for arbitrary supplied arrays. + +Neither assertion contains execution, time or fit fields. Width and magnitude assumptions are +ordinary numerical input bounds, and execution is proved in the existing native semantics. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Entry to one node, as established by the native root scan or a native child push. -/ +structure NodeReady (L f a0 b0 n d p E : ℕ) (S : Finset (OutStr n)) + (x : Data registers) : Prop where + /-- The computed workspace pointers and constant registers. -/ + regs : KernelRegs L f a0 b0 x + /-- The two tuple lengths agree with the original depth. -/ + depth_add : d + n = L + /-- The leaf prefix is within its already processed digit space. -/ + prefix_lt : p < 10 ^ d + /-- The actual cursor points to this frame. -/ + level_eq : x.regs level = d + /-- This node has not begun its entry phase. -/ + mode_eq : x.regs mode = 0 + /-- The traversal is still active. -/ + active_eq : x.regs active = 1 + /-- Current actual timestamp generation. -/ + epoch_eq : x.regs epoch = E + /-- Four stored words: count, prefix, stride and first term. -/ + frame : (Block.mk (f + 4 * d) 4).Realizes x.mem ![S.card, p, 10 ^ d, 0] + /-- The actual list and directory represent exactly the requested numerical codes. -/ + requests : ScanMemory x.mem (f + 4 * (L + 1) + d * 10 ^ L) + (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L) E (10 ^ L) + (S.image fun q => (queryIndex q).val) + +/-- The spatial and mathematical result of one node. This is a postcondition on the actual +data, with no execution or time claim embedded in it. -/ +structure NodeResult (L f a0 b0 n d E : ℕ) (S : Finset (OutStr n)) + (a b : Leaf n → ℤ) (x y : Data registers) : Prop where + /-- Constant parameters remain unchanged. -/ + regs : KernelRegs L f a0 b0 y + /-- Return to the parent, keeping zero at the root. -/ + level_eq : y.regs level = if d = 0 then 0 else d - 1 + /-- A parent resumes accumulation after a non-root node. -/ + mode_eq : 0 < d → y.regs mode = 2 + /-- Only returning from the root stops traversal. -/ + active_eq : y.regs active = if d = 0 then 0 else 1 + /-- Timestamp generations do not go backwards. -/ + epoch_le : E ≤ y.regs epoch + /-- All fresh generations are covered by the remaining-depth allowance. -/ + epoch_upper : y.regs epoch ≤ E + epochAllowance n + /-- The directory is ready for the next fresh-generation scan. -/ + timestamps : ∀ q < 10 ^ L, + y.mem (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q) ≤ y.regs epoch + /-- Every requested cell holds the existing pruned result, in its original code position. -/ + values : ∀ q ∈ S, + y.mem (f + 4 * (L + 1) + (L + 1) * 10 ^ L + d * 10 ^ L + (queryIndex q).val) = + encode (Pruned.result n a b S q) + /-- Caller memory, including ancestor rows, is preserved outside the concrete node frame. -/ + frame : Modifies (nodeWrites L f d) x.mem y.mem + +/-- A ready frame's stored count cannot exceed its actual list row capacity. -/ +theorem NodeReady.card_le {L f a0 b0 n d p E : ℕ} {S : Finset (OutStr n)} + {x : Data registers} (h : NodeReady L f a0 b0 n d p E S x) : S.card ≤ 10 ^ L := by + simpa only [card_image_queryIndex] using h.requests.card_le + +/-- The occupied words of the actual row encode exactly the requested output strings. -/ +theorem NodeReady.image_read {L f a0 b0 n d p E : ℕ} {S : Finset (OutStr n)} + {x : Data registers} (h : NodeReady L f a0 b0 n d p E S x) : + (Finset.range S.card).image (fun i => + x.mem (f + 4 * (L + 1) + d * 10 ^ L + i)) = + S.image (fun q => (queryIndex q).val) := by + simpa only [card_image_queryIndex] using h.requests.image_read + +/-- Every occupied source-list word comes from one of the requested mathematical outputs. -/ +theorem NodeReady.exists_of_read {L f a0 b0 n d p E i : ℕ} {S : Finset (OutStr n)} + {x : Data registers} (h : NodeReady L f a0 b0 n d p E S x) (hi : i < S.card) : + ∃ q ∈ S, (queryIndex q).val = x.mem (f + 4 * (L + 1) + d * 10 ^ L + i) := by + apply Finset.mem_image.mp + rw [← h.image_read] + exact Finset.mem_image.mpr ⟨i, Finset.mem_range.mpr hi, rfl⟩ + +/-- Actual query words fit the remaining query space, not only the original workspace row. -/ +theorem NodeReady.read_lt {L f a0 b0 n d p E i : ℕ} {S : Finset (OutStr n)} + {x : Data registers} (h : NodeReady L f a0 b0 n d p E S x) (hi : i < S.card) : + x.mem (f + 4 * (L + 1) + d * 10 ^ L + i) < 10 ^ n := by + obtain ⟨q, _, hq⟩ := h.exists_of_read hi + exact hq ▸ (queryIndex q).isLt + +/-- Every requested output occurs at a concrete occupied position of the native list. -/ +theorem NodeReady.exists_read {L f a0 b0 n d p E : ℕ} {S : Finset (OutStr n)} + {x : Data registers} (h : NodeReady L f a0 b0 n d p E S x) {q : OutStr n} (hq : q ∈ S) : + ∃ i < S.card, x.mem (f + 4 * (L + 1) + d * 10 ^ L + i) = (queryIndex q).val := by + have hm : (queryIndex q).val ∈ S.image (fun v => (queryIndex v).val) := + Finset.mem_image.mpr ⟨q, hq, rfl⟩ + rw [← h.image_read] at hm + obtain ⟨i, hi, he⟩ := Finset.mem_image.mp hm + exact ⟨i, Finset.mem_range.mp hi, he⟩ + +/-- Distinct occupied positions cannot name the same parent query. -/ +theorem NodeReady.injOn_read {L f a0 b0 n d p E : ℕ} {S : Finset (OutStr n)} + {x : Data registers} (h : NodeReady L f a0 b0 n d p E S x) : + Set.InjOn (fun i => x.mem (f + 4 * (L + 1) + d * 10 ^ L + i)) {i | i < S.card} := by + simpa only [card_image_queryIndex] using h.requests.injOn_read + +/-- Each strided input address remains inside the original input block below the workspace. -/ +theorem NodeReady.source_before {L f a0 b0 n d p E base : ℕ} {S : Finset (OutStr n)} + {x : Data registers} (h : NodeReady L f a0 b0 n d p E S x) + (hbase : base + 10 ^ L ≤ f) (v : Leaf n) : + base + p + 10 ^ d * (Encoding.leafIndex v).val < f := by + have hs : (10 : ℕ) ^ d * 10 ^ n = 10 ^ L := by rw [← pow_add, h.depth_add] + have hv := leafAddress_lt h.prefix_lt hs v + omega + +/-- The terminal node proof obtains its signed fits from the common original magnitude +envelope. The same returned memory satisfies the result and the charged native continuation. -/ +theorem exec_ready_zero {w L f a0 b0 d p E A B : ℕ} (S : Finset (OutStr 0)) + (a b : Leaf 0 → ℤ) (x : Data registers) (hx : NodeReady L f a0 b0 0 d p E S x) + (hleft : ∀ v, x.mem (a0 + p + 10 ^ d * (Encoding.leafIndex v).val) = encode (a v)) + (hright : ∀ v, x.mem (b0 + p + 10 ^ d * (Encoding.leafIndex v).val) = encode (b v)) + (ha : ∀ v, |a v| ≤ (A : ℤ)) (hb : ∀ v, |b v| ≤ (B : ℤ)) + (hw : 0 < w) (hmag : magnitudeBound L A B < 2 ^ (w - 1)) + (hfit : Fits (w + 2) (nodeBudget L f a0 b0 E 0)) : + ∃ y, NodeResult L f a0 b0 0 d E S a b x y ∧ + ∀ z U, ExecWithin (w + 2) controller y z U → + ExecWithin (w + 2) controller x z (traceWeight S + U) := by + have hd : d = L := by have := hx.depth_add; omega + subst d + have henv := magnitudeBound_fits hw hmag + have hi : (Encoding.leafIndex (Fin.elim0 : Leaf 0)).val = 0 := by decide + have hcount : x.mem (f + 4 * L) = S.card := hx.frame 0 + have hpref : x.mem (f + 4 * L + 1) = p := hx.frame 1 + have hA : x.mem (a0 + p) = encode (a Fin.elim0) := by + simpa only [hi, Nat.mul_zero, Nat.add_zero] using hleft Fin.elim0 + have hB : x.mem (b0 + p) = encode (b Fin.elim0) := by + simpa only [hi, Nat.mul_zero, Nat.add_zero] using hright Fin.elim0 + have hpow : 1 ≤ (10 : ℕ) ^ L := Nat.one_le_pow L _ (by decide) + have hL : L + 1 ≤ (L + 1) * 10 ^ L := by nlinarith only [hpow] + obtain ⟨y, hregs, hyl, hym, hya, hye, hmarks, hvalue, hframe, _, hrun⟩ := + exec_node_zero (fits_left henv (ha Fin.elim0)) (fits_right henv (hb Fin.elim0)) + (fits_product henv (ha Fin.elim0) (hb Fin.elim0)) S x hx.regs hx.level_eq + hx.mode_eq hx.active_eq hcount hpref hA hB hx.epoch_eq hx.requests.stamp_le + (hfit.of_le (by + unfold nodeBudget + rw [epochAllowance_zero] + nlinarith only [hpow, hL, hx.prefix_lt])) + refine ⟨y, ⟨hregs, hyl, ?_, hya, ?_, ?_, ?_, hvalue, hframe⟩, hrun⟩ + · intro hLpos + simpa only [ite_eq_right (by omega : L ≠ 0)] using hym + · omega + · simp only [hye, epochAllowance_zero, Nat.add_zero, le_refl] + · intro q hq + simpa only [hye] using hmarks q hq + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Recursive.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Recursive.lean new file mode 100644 index 000000000..27f9c14ad --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Recursive.lean @@ -0,0 +1,123 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ProgressChild +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ProgressEmpty +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ProgressFinish +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ProgressReturn + +/-! +# All-depth execution of the native pruned controller + +## Main results + +* `PrunedQuery.exec_ready`: the fixed controller computes the existing pruned recursion, with + actual stored child lists, signed values, frames and a bound charged to its mathematical trace. + +The induction composes only `Code.ExecWithin` for the existing standard unsigned machine. Its +continuation form stops the inductive node at the parent return cursor; at the root the proved +zero active flag supplies the ordinary native loop-exit execution. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- An arbitrary-depth ready node returns the unchanged finite pruned result. The only width +premises concern original pointers, depths, epochs and input magnitudes; all intermediate fits +are derived. Every child is generated and executed by the same fixed native controller. -/ +theorem exec_ready {w L f a0 b0 A B : ℕ} (n : ℕ) {d p E : ℕ} + (S : Finset (OutStr n)) (a b : Leaf n → ℤ) (x : Data registers) + (hx : NodeReady L f a0 b0 n d p E S x) + (hleft : ∀ v, x.mem (a0 + p + 10 ^ d * (Encoding.leafIndex v).val) = encode (a v)) + (hright : ∀ v, x.mem (b0 + p + 10 ^ d * (Encoding.leafIndex v).val) = encode (b v)) + (hbeforeA : a0 + 10 ^ L ≤ f) (hbeforeB : b0 + 10 ^ L ≤ f) + (ha : ∀ v, |a v| ≤ (A : ℤ)) (hb : ∀ v, |b v| ≤ (B : ℤ)) + (hw : 0 < w) (hmag : magnitudeBound L A B < 2 ^ (w - 1)) + (hfit : Fits (w + 2) (nodeBudget L f a0 b0 E n)) : + ∃ y, NodeResult L f a0 b0 n d E S a b x y ∧ + ∀ z U, ExecWithin (w + 2) controller y z U → + ExecWithin (w + 2) controller x z (traceWeight S + U) := by + induction n generalizing d p E x with + | zero => exact exec_ready_zero S a b x hx hleft hright ha hb hw hmag hfit + | succ n ih => + obtain ⟨y, henter, hy⟩ := exec_ready_enter S a b x hx hfit + have hterms : ∀ k ≤ 10, ∃ u, NodeProgress L f a0 b0 n d p E S a b x k u ∧ + ∀ z U, ExecWithin (w + 2) controller u z U → + ExecWithin (w + 2) controller y z + (k * (51 * S.card + 75) + childWeightPrefix S k + U) := by + intro k + induction k with + | zero => + intro _ + refine ⟨y, hy, ?_⟩ + intro z U hz + simpa only [Nat.zero_mul, childWeightPrefix_zero, Nat.zero_add] using hz + | succ k ihk => + intro hk + obtain ⟨u, hu, hprevious⟩ := ihk (by omega) + let t : Fin 10 := ⟨k, by omega⟩ + by_cases hne : (childSet S (Encoding.termDigits t)).Nonempty + · obtain ⟨v, hpush, hv, hpushFrame, hvfit⟩ := exec_progress_child S a b x u hx t hu hne hfit + obtain ⟨z, hz, hchild⟩ := ih (childSet S (Encoding.termDigits t)) + (sliceAt a (Encoding.termDigits t)) (sliceAt b (Encoding.termDigits t)) v hv + (hu.child_source hx t hpushFrame hbeforeA hleft) + (hu.child_source hx t hpushFrame hbeforeB hright) + (fun v => ha (Fin.cons (Encoding.termDigits t) v)) + (fun v => hb (Fin.cons (Encoding.termDigits t) v)) hvfit + obtain ⟨v', hreturn, hv'⟩ := exec_progress_return S a b x u v z hx t hu hne + hpushFrame hz ha hb hw hmag hfit + refine ⟨v', hv', ?_⟩ + intro endState U hend + have hza : z.regs active = 1 := by + simpa only [ite_eq_right (by omega : d + 1 ≠ 0)] using hz.active_eq + have hreturnLoop := ExecWithin.loop_iter + (by simp only [Cond.holds_lt, hz.regs.nil_eq, hza]; decide) hreturn hend + have hchildLoop := hchild endState _ hreturnLoop + have hpushLoop := ExecWithin.loop_iter + (by simp only [Cond.holds_lt, hu.regs.nil_eq, hu.active_eq]; decide) hpush hchildLoop + have he := hprevious endState _ hpushLoop + apply he.mono + have hweight := childWeightPrefix_succ S t + simp only [hne, ↓reduceIte] at hweight + change childWeightPrefix S (k + 1) = childWeightPrefix S k + + traceWeight (childSet S (Encoding.termDigits t)) at hweight + simp only [Nat.add_mul, Nat.one_mul] + omega + · have hempty := Finset.not_nonempty_iff_eq_empty.mp hne + obtain ⟨v, hskip, hv⟩ := exec_progress_empty S a b x u hx t hu hempty hfit + refine ⟨v, hv, ?_⟩ + intro endState U hend + have hskipLoop := ExecWithin.loop_iter + (by simp only [Cond.holds_lt, hu.regs.nil_eq, hu.active_eq]; decide) hskip hend + have he := hprevious endState _ hskipLoop + apply he.mono + have hweight := childWeightPrefix_succ S t + simp only [hne, ↓reduceIte, Nat.add_zero] at hweight + change childWeightPrefix S (k + 1) = childWeightPrefix S k at hweight + simp only [Nat.add_mul, Nat.one_mul] + omega + obtain ⟨u, hu, hchildren⟩ := hterms 10 (by omega) + obtain ⟨z, hfinish, hz⟩ := exec_progress_finish S a b x u hx hu hfit + refine ⟨z, hz, ?_⟩ + intro endState U hend + have hfinishLoop := ExecWithin.loop_iter + (by simp only [Cond.holds_lt, hu.regs.nil_eq, hu.active_eq]; decide) hfinish hend + have hchildrenLoop := hchildren endState _ hfinishLoop + have he := ExecWithin.loop_iter + (by simp only [Cond.holds_lt, hx.regs.nil_eq, hx.active_eq]; decide) henter hchildrenLoop + apply he.mono + rw [traceWeight_succ_prefix] + have hc := node_local_cost_le S.card + omega + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Return.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Return.lean index 0ecd8f1d4..9b35198c4 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Return.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Return.lean @@ -55,6 +55,9 @@ theorem exec_node_return {w L f a0 b0 d N k : ℕ} (x : Data registers) encode (V (input i) + if selected true k (input i) then B (input i / 10) else 0)) ∧ (∀ j < 3, y.mem (f + 4 * d + j) = x.mem (f + 4 * d + j)) ∧ y.mem (f + 4 * d + 3) = k + 1 ∧ + (∀ q < 10 ^ L, + y.mem (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q) = + x.mem (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q)) ∧ Modifies (nodeWrites L f d) x.mem y.mem := by have hpow : 1 ≤ (10 : ℕ) ^ L := Nat.one_le_pow L _ (by decide) have hL : L + 1 ≤ (L + 1) * 10 ^ L := by nlinarith only [hpow] @@ -119,7 +122,7 @@ theorem exec_node_return {w L f a0 b0 d N k : ℕ} (x : Data registers) have hvframe : Modifies (Block.mk vb (10 ^ L)).footprint x.mem v.mem := by simpa only [huv', humem] using hvFrame have frameBeforeValue : f + 4 * d + 4 ≤ vb := by dsimp [vb]; omega - refine ⟨y, he, hregs.of_step he, ?_, hym, ?_, ?_, ?_, ?_, ?_, ?_⟩ + refine ⟨y, he, hregs.of_step he, ?_, hym, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ · exact (hy.regs_eq (by decide : advance.Preserves level)).trans ((hvreg level (by decide)).trans ((fixed level (by decide)).trans hd)) · exact (hy.regs_eq (by decide : advance.Preserves active)).trans @@ -149,6 +152,22 @@ theorem exec_node_return {w L f a0 b0 d N k : ℕ} (x : Data registers) change ¬(vb ≤ f + 4 * d + j ∧ _) omega · simp only [hymem, hvf, hvt, Function.update_self] + · intro q _ + have hafter : vb + 10 ^ L ≤ + f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q := by + dsimp only [vb] + nlinarith only [hmul] + have hf : f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q ∉ + (Block.mk (f + 4 * d) 4).footprint := by + rw [Block.mem_footprint] + dsimp only [Block.Owns] + omega + have hvf : f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q ∉ + (Block.mk vb (10 ^ L)).footprint := by + rw [Block.mem_footprint] + dsimp only [Block.Owns] + omega + exact (hyframe _ hf).trans (hvframe _ hvf) · intro c hc have hnotFrame : c ∉ (Block.mk (f + 4 * d) 4).footprint := by intro hf diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanMemory.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanMemory.lean index 227cecfbf..25d5184b1 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanMemory.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/ScanMemory.lean @@ -66,6 +66,36 @@ theorem ScanMemory.congr {mem mem' : ℕ → ℕ} {out stamps epoch capacity : · obtain ⟨v, hv, hrange, hreal⟩ := h.listed exact ⟨v, hv, hrange, fun i => (hlist i i.isLt).trans (hreal i)⟩ +/-- Reading the occupied native list gives exactly its represented finite set. -/ +theorem ScanMemory.image_read {mem : ℕ → ℕ} {out stamps epoch capacity : ℕ} {S : Finset ℕ} + (h : ScanMemory mem out stamps epoch capacity S) : + (Finset.range S.card).image (fun i => mem (out + i)) = S := by + obtain ⟨v, _, hrange, hreal⟩ := h.listed + ext q + constructor + · rintro hq + obtain ⟨i, hi, hq⟩ := Finset.mem_image.mp hq + have hi' : i < S.card := Finset.mem_range.mp hi + have hread : mem (out + i) = v ⟨i, hi'⟩ := hreal ⟨i, hi'⟩ + have hv : v ⟨i, hi'⟩ ∈ S := by + rw [← Finset.mem_coe, ← hrange] + exact ⟨⟨i, hi'⟩, rfl⟩ + exact (hread.symm.trans hq) ▸ hv + · intro hq + have hv : q ∈ Set.range v := by rw [hrange]; exact hq + obtain ⟨i, rfl⟩ := hv + exact Finset.mem_image.mpr ⟨i.val, Finset.mem_range.mpr i.isLt, hreal i⟩ + +/-- Occupied native list positions store different codes. -/ +theorem ScanMemory.injOn_read {mem : ℕ → ℕ} {out stamps epoch capacity : ℕ} {S : Finset ℕ} + (h : ScanMemory mem out stamps epoch capacity S) : + Set.InjOn (fun i => mem (out + i)) {i | i < S.card} := by + obtain ⟨v, hv, _, hreal⟩ := h.listed + intro i hi j hj he + have hreadi : mem (out + i) = v ⟨i, hi⟩ := hreal ⟨i, hi⟩ + have hreadj : mem (out + j) = v ⟨j, hj⟩ := hreal ⟨j, hj⟩ + exact congrArg Fin.val (hv (hreadi.symm.trans (he.trans hreadj))) + /-- An unseen valid code leaves a free output cell for its insertion. -/ theorem ScanMemory.card_lt_of_notMem {mem : ℕ → ℕ} {out stamps epoch capacity : ℕ} {S : Finset ℕ} (h : ScanMemory mem out stamps epoch capacity S) {q : ℕ} diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Weight.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Weight.lean index be346dda8..b65533733 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Weight.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Weight.lean @@ -106,4 +106,38 @@ theorem traceWeight_succ {n : ℕ} (S : Finset (OutStr (n + 1))) : Pruned.calls_irrel _ _ (fun _ => 0) (fun _ => 0)] · simp +/-- Potential contributed by the child subtrees with numerical labels below `k`. This is a +finite sum of mathematical trace weights, not an assumed execution-time bound. -/ +def childWeightPrefix {n : ℕ} (S : Finset (OutStr (n + 1))) (k : ℕ) : ℕ := + ∑ i ∈ range k, if hi : i < 10 then + if (childSet S (Encoding.termDigits ⟨i, hi⟩)).Nonempty then + traceWeight (childSet S (Encoding.termDigits ⟨i, hi⟩)) else 0 + else 0 + +/-- No child potential has been spent before the first term. -/ +@[simp] +theorem childWeightPrefix_zero {n : ℕ} (S : Finset (OutStr (n + 1))) : + childWeightPrefix S 0 = 0 := by simp [childWeightPrefix] + +/-- Advancing the native term counter adds exactly the next nonempty child's potential. -/ +theorem childWeightPrefix_succ {n : ℕ} (S : Finset (OutStr (n + 1))) (k : Fin 10) : + childWeightPrefix S (k.val + 1) = childWeightPrefix S k.val + + if (childSet S (Encoding.termDigits k)).Nonempty then + traceWeight (childSet S (Encoding.termDigits k)) else 0 := by + simp only [childWeightPrefix, sum_range_succ, dite_eq_left k.isLt] + +/-- After all ten terms, the child-prefix potential is exactly the recursive part of the +existing mathematical trace weight, in the program's unchanged numerical term order. -/ +theorem traceWeight_succ_prefix {n : ℕ} (S : Finset (OutStr (n + 1))) : + traceWeight S = 4096 * (S.card + 1) + childWeightPrefix S 10 := by + rw [traceWeight_succ] + congr 1 + +/-- The complete local controller work fits within the root summand of `traceWeight`. +The terms account for entry, ten scan/push/accumulate pairs, return, and all loop controls. +Empty children use less work than the displayed pair. Child recursive work is separate. -/ +theorem node_local_cost_le (N : ℕ) : + (7 * N + 30) + 10 * ((20 * N + 44) + (31 * N + 31)) + 28 ≤ + 4096 * (N + 1) := by omega + end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Computability/GenericRAM/Signed/Fits.lean b/Complexity/Computability/GenericRAM/Signed/Fits.lean new file mode 100644 index 000000000..aa069549a --- /dev/null +++ b/Complexity/Computability/GenericRAM/Signed/Fits.lean @@ -0,0 +1,39 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.GenericRAM.Signed +import Mathlib.Algebra.Order.Group.Abs + +/-! +# Signed fits from magnitude envelopes + +A representable nonnegative envelope covers every integer whose absolute value is at most it. +The envelope itself must fit: the asymmetric signed range still excludes its positive endpoint. + +## Main results + +* `Fits.of_natAbs_le`: derive signed fitting from a representable natural magnitude envelope. + +This elementary bound is original. +-/ + +@[expose] public section + +namespace Cslib.GenericRAM.Signed + +/-- A signed fit for a nonnegative envelope also covers every integer bounded by it. -/ +theorem Fits.of_natAbs_le {w B : ℕ} (hfit : Fits w (B : ℤ)) + {z : ℤ} (hz : z.natAbs ≤ B) : Fits w z := by + obtain ⟨hw, hlo, hhi⟩ := hfit + have habs := abs_le.mp (show |z| ≤ (B : ℤ) by + rw [← Int.natCast_natAbs] + exact_mod_cast hz) + exact ⟨hw, by omega, by omega⟩ + +end Cslib.GenericRAM.Signed diff --git a/ComplexityTests/SchonhagePruned.lean b/ComplexityTests/SchonhagePruned.lean index 7224cb954..63fc42956 100644 --- a/ComplexityTests/SchonhagePruned.lean +++ b/ComplexityTests/SchonhagePruned.lean @@ -10,6 +10,7 @@ import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Repor import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.EmptyChild import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Finish import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Return +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Ready -- Full controller regressions evaluate the actual native interpreter at elaboration time. set_option linter.hashCommand false @@ -31,6 +32,58 @@ private def leafData : Data registers where private theorem leafData_parameters : KernelRegs 0 8 3 4 leafData := by constructor <;> rfl +-- The general entry assertion is satisfied by concrete memory. Its timestamp directory has +-- one cell and its occupied list names the unique zero-depth query exactly once. +private theorem leafData_ready : NodeReady 0 8 3 4 0 0 0 0 {Fin.elim0} leafData := by + refine ⟨leafData_parameters, rfl, by decide, rfl, rfl, rfl, rfl, ?_, ?_⟩ + · intro i + fin_cases i <;> rfl + · change ScanMemory leafData.mem 12 14 0 1 {0} + refine ⟨?_, ?_, ?_, ?_⟩ + · intro q hq + have he := Finset.mem_singleton.mp hq + exact he ▸ (by decide : 0 < 1) + · intro q hq + have he : q = 0 := by omega + subst q + decide + · intro q hq + have he : q = 0 := by omega + subst q + decide + · refine ⟨fun _ => 0, ?_, ?_, ?_⟩ + · change Function.Injective (fun _ : Fin 1 => (0 : ℕ)) + intro i j _ + exact Subsingleton.elim i j + · ext q + constructor + · rintro ⟨i, rfl⟩ + exact Finset.mem_singleton_self _ + · intro hq + exact ⟨⟨0, by simp⟩, (Finset.mem_singleton.mp hq).symm⟩ + · intro i + change leafData.mem (12 + i.val) = 0 + have hi : i.val < 1 := i.isLt + have he : i.val = 0 := by omega + rw [he] + rfl + +-- The numerical magnitude/address hypotheses are derived from original input bounds, and the +-- value witness belongs to the same actual native execution (including the loop exit). +example : ∃ y, ExecWithin 9 controller leafData y 8194 ∧ y.mem 13 = encode (-6) := by + let S : Finset (Cslib.Algorithms.MatrixMultiplication.Schonhage.OutStr 0) := {Fin.elim0} + obtain ⟨y, hy, hrun⟩ := exec_ready_zero (w := 7) (A := 3) (B := 2) S + (fun _ => -3) (fun _ => 2) leafData leafData_ready + (by intro v; rfl) (by intro v; rfl) (by intro v; decide) (by intro v; decide) + (by decide) (by decide) (by decide) + have hexit : ExecWithin 9 controller y y 2 := ExecWithin.loop_exit + (by simp only [Cond.holds_lt, hy.regs.nil_eq, hy.active_eq]; decide) + refine ⟨y, ?_, ?_⟩ + · simpa only [traceWeight_zero, S, Finset.card_singleton] using hrun y 2 hexit + · have hv := hy.values Fin.elim0 (Finset.mem_singleton_self _) + simpa [Cslib.Algorithms.MatrixMultiplication.Schonhage.Pruned.result, + Cslib.Algorithms.MatrixMultiplication.Schonhage.Pruned.run, Finset.piecewise, S] using hv + -- A concrete negative-product node discharges the general invariant, signed and address -- premises. Its returned memory is attached to the same charged native loop execution. example : ∃ y, ExecWithin 7 controller leafData y 8194 ∧ y.mem 13 = encode (-6) ∧ From a93c0214ee4a621d612a8807ad6f15cd54674650 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 18:50:01 -0400 Subject: [PATCH 30/73] style(WordRAM): normalize native proof file modes --- .../WordRAM/MatrixMultiplication/Schonhage/Pruned/Bounds.lean | 0 .../WordRAM/MatrixMultiplication/Schonhage/Pruned/NodeFrame.lean | 0 .../WordRAM/MatrixMultiplication/Schonhage/Pruned/Ready.lean | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Bounds.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/NodeFrame.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Ready.lean diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Bounds.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Bounds.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/NodeFrame.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/NodeFrame.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Ready.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Ready.lean old mode 100755 new mode 100644 From 0c2e0ebea6199ac1fbe9b2e048ce24b9410f8d1a Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 19:55:22 -0400 Subject: [PATCH 31/73] feat(wordram): complete initialized sparse pruned-query execution Complete implementation candidate for #1526 above #1520 exact 3b9234da6bc3c0daad2cd167aeb4fd8f9fc592b6. Full ordinary native 3SUM and the published 1.9992 endpoint remain open under #1394. No PR yet; fresh exact-revision independent correctness/API/source review is pending before publication. One fixed standard native program: 96 registers, at most 4096 flat instructions including halt, no width reads. Complete State.init -> pre-halt Runs -> Executes uses the same answer memory. Every supplied query, including repeats and empty/zero-depth boundaries, equals existing Pruned.result; runs_bilinear specializes existing encoding contracts to bilinearProduct. exec_kernel computes its powers/layout and has a reusable pointer/epoch directory contract. All child lists, timestamp decisions, stack/control/index arithmetic, signed products/adds and reporting are actual charged code, proved by the all-depth exec_ready induction; its continuation is discharged by the actual root exit. Full charge including halt <= 8192*(L+Q+1+(L+1)*card(leaves(requested qs))). Ordinary source envelope M=A+B+10^L*A*B and address budget H=256*(L+1)*10^L+32*Q+512 derive input fit and execution at every physical W >= clog_2(M+H+1)+3. This is logarithmic in explicit component budgets; global 3SUM composition is not claimed. Inputs: raw L,Q then two canonical signed arrays of length10^L and unsigned query codes, after the actual standard length cell0. Returned output pointer is unsigned. All input/outside memory is preserved on initialized runs. Code/ExecWithin/State.init/Runs/Executes, signed kernels, Heap/Block APIs, existing mathematical pruning/encodings and Mathlib digit/log APIs are reused; GenericRAM/Signed/Fits is the unchanged blob from #1552 f276db1174333d3e6253b93878be8b95c1fcc713, with credit retained. Pinned source anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5. Checked Git-blob quotation locators: ThreeSumApsp/Sec2/Lemma10.lean:148-150 (Pruned_eq_restrictTo_sum) and:565-566 (Lemma10.total_size). Source Pruned -> existing Pruned.result; source restriction -> Finset.piecewise; source Leaves -> leaves; all natural depths and arbitrary integer arrays retained. Native indexing directly uses existing leafIndex(privateLeaf), first coordinate least significant, inner digit9. Child prefix/stride/division are proved against the finite childSet/sliceAt semantics. Native code/proofs and cost are original, not supplied by source Light semantics or its sorted-segment implementation. Full checked quotes and model/quantifier/layout/cost/reuse mapping are in the prepared UTF-8 PR draft. Copyright retained in source/NOTICE. Validation on tree bd6e74cce28dbd16c90291b847dac1a311402510: all SIX strict gates pass together, LEAN_NUM_THREADS=1, downloads disabled: build --wfail --iofail; mk_all --check; test --wfail --iofail (including Smoke/global Axioms); lint; lint-style; checkInitImports. Full citation-aware hygiene --check-simp; actual-base47-library/3-test changed-file and local lints; strict PR declaration-name audit; staged/unstaged diff checks pass. Register alias docs fixed the first lint finding; all native definition bytes are unchanged by that fix. Tests discharge real State.init positive/negative/empty/depth-two mixed/shared/repeated queries, sufficient-width family and failing fit boundary; callable signed wildcard with nonzero epoch and preserved input/outside sentinel also checks. No sorry, custom axiom or native_decide introduced. Selected abstracts/catalogue/rendering: no impact, no new headline. ROADMAP/UPSTREAM preserve the open sparse-product/ET and global1.9992 gaps. AI-assisted implementation by codex-three-sum-continuation-t3-3dda8289 (OpenAI Codex); original finite source credit to Anthropic, PBC. --- Complexity.lean | 47 +++++ .../Schonhage/Pruned/Bounds.lean | 19 ++ .../Schonhage/Pruned/Codes.lean | 13 +- .../Schonhage/Pruned/Evaluate.lean | 195 ++++++++++++++++++ .../Schonhage/Pruned/Flat.lean | 96 +++++++++ .../Schonhage/Pruned/Initialize.lean | 113 ++++++++++ .../Schonhage/Pruned/Input.lean | 168 +++++++++++++++ .../Schonhage/Pruned/Kernel.lean | 104 ++++++++++ .../Schonhage/Pruned/Program.lean | 45 +++- .../Schonhage/Pruned/Width.lean | 84 ++++++++ .../Computability/WordRAM/InputArray.lean | 40 ++++ ComplexityTests/SchonhagePruned.lean | 11 +- ComplexityTests/SchonhagePrunedInput.lean | 100 +++++++++ ComplexityTests/SchonhagePrunedKernel.lean | 70 +++++++ NOTICE | 5 + ROADMAP.md | 13 +- UPSTREAM.md | 18 +- 17 files changed, 1122 insertions(+), 19 deletions(-) create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Evaluate.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Flat.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Initialize.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Input.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Kernel.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Width.lean create mode 100644 Complexity/Computability/WordRAM/InputArray.lean create mode 100644 ComplexityTests/SchonhagePrunedInput.lean create mode 100644 ComplexityTests/SchonhagePrunedKernel.lean diff --git a/Complexity.lean b/Complexity.lean index 04ec040e5..5f56851e2 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -40,6 +40,7 @@ public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Magnitude public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Orders public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Parameters public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Pruned +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.PrunedCounts public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Sparse public import Complexity.Algorithms.MatrixMultiplication.Schonhage.ThinProduct public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Tiling @@ -114,6 +115,49 @@ public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Forms public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Header public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Input public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Meaning +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Accumulate +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.AccumulateStep +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Add +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Bounds +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Child +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Clear +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Codes +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Control +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Emit +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.EmptyChild +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Evaluate +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Finish +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Flat +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Frame +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Initialize +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Input +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Kernel +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Leaf +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.LeafStep +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Memory +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Node +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.NodeFrame +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Powers +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Program +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Progress +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ProgressChild +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ProgressEmpty +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ProgressFinish +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ProgressReturn +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Push +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Ready +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Recursive +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Report +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Return +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Root +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Scan +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ScanInsert +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ScanMemory +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ScanSet +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.ScanStep +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Values +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Weight +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Width public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Stage public import Complexity.Algorithms.WordRAM.PrefixSums.Dynamic public import Complexity.Algorithms.WordRAM.PrefixSums.Query @@ -1003,6 +1047,7 @@ public import Complexity.Computability.GenericRAM.Native.MeasurementFold public import Complexity.Computability.GenericRAM.Native.Nat public import Complexity.Computability.GenericRAM.Native.SubsetPairFold public import Complexity.Computability.GenericRAM.Signed +public import Complexity.Computability.GenericRAM.Signed.Fits public import Complexity.Computability.GenericRAM.Simulation.Finite public import Complexity.Computability.GenericRAM.Simulation.Run public import Complexity.Computability.GenericRAM.Simulation.Turing @@ -1621,6 +1666,7 @@ public import Complexity.Computability.USTCON.Reader.Encode public import Complexity.Computability.USTCON.Reader.Invariant public import Complexity.Computability.USTCON.Space public import Complexity.Computability.WordRAM.Allocation +public import Complexity.Computability.WordRAM.ArrayAppend public import Complexity.Computability.WordRAM.Basis public import Complexity.Computability.WordRAM.Compile public import Complexity.Computability.WordRAM.Controller @@ -1630,6 +1676,7 @@ public import Complexity.Computability.WordRAM.Dynamic public import Complexity.Computability.WordRAM.Function public import Complexity.Computability.WordRAM.Generic public import Complexity.Computability.WordRAM.Heap +public import Complexity.Computability.WordRAM.InputArray public import Complexity.Computability.WordRAM.MapOp public import Complexity.Computability.WordRAM.Multiword public import Complexity.Computability.WordRAM.Multiword.Arithmetic diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Bounds.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Bounds.lean index a4edc68b3..3248633e0 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Bounds.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Bounds.lean @@ -40,6 +40,25 @@ open Cslib.Algorithms.MatrixMultiplication.Schonhage def nodeBudget (L f a b E n : ℕ) : ℕ := 8 * f + 64 * (L + 1) * 10 ^ L + a + b + 2 * 10 ^ L + E + epochAllowance n + 128 +/-- Root deduplication and reporting additionally use the original query/output pointers and +the full supplied query count, which may exceed the distinct-set capacity. -/ +def evaluationBudget (L f a b q o Q E : ℕ) : ℕ := + nodeBudget L f a b (E + 1) L + 4 * (q + o + Q + 1) + +/-- The callable kernel also computes its own powers and workspace pointers. Its numeric +admission includes those setup bounds and permits arbitrary separated input/output addresses. -/ +def kernelBudget (L f a b q o Q E : ℕ) : ℕ := + addressBound L Q + evaluationBudget L f a b q o Q E + +/-- The ordinary initialized layout covers evaluation, including repeated supplied queries. -/ +theorem evaluationBudget_input_le (L Q : ℕ) : + evaluationBudget L (scratchBase L Q) 3 (3 + 10 ^ L) (3 + 2 * 10 ^ L) + (outputBase L Q) Q 0 ≤ addressBound L Q := by + have he := epochAllowance_le L + have hp : 1 ≤ (10 : ℕ) ^ L := Nat.one_le_pow L _ (by decide) + unfold evaluationBudget nodeBudget scratchBase outputBase addressBound + nlinarith only [he, hp] + /-- The larger concrete standalone bound covers initialization and every root parameter. -/ theorem nodeBudget_input_le (L Q : ℕ) : nodeBudget L (scratchBase L Q) 3 (3 + 10 ^ L) 1 L ≤ addressBound L Q := by diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Codes.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Codes.lean index 2574de042..0e9ed5be1 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Codes.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Codes.lean @@ -51,7 +51,7 @@ def outputDigits : Fin 10 ≃ OutVar where /-- The least-significant-first flat code of an output string. -/ def queryIndex {L : ℕ} (w : OutStr L) : Fin (10 ^ L) := - finFunctionFinEquiv fun i => outputDigits.symm (w i) + Encoding.leafIndex (privateLeaf w) /-- Decode a supplied finite query code with the existing executable digit enumeration. -/ def queryAt {L : ℕ} (q : Fin (10 ^ L)) : OutStr L := @@ -60,13 +60,17 @@ def queryAt {L : ℕ} (q : Fin (10 ^ L)) : OutStr L := /-- Encoding a decoded query returns the actual supplied code. -/ @[simp] theorem queryIndex_queryAt {L : ℕ} (q : Fin (10 ^ L)) : queryIndex (queryAt q) = q := by - simp only [queryIndex, queryAt, Equiv.symm_apply_apply, Equiv.apply_symm_apply] + change (finFunctionFinEquiv fun i => + outputDigits.symm (outputDigits (finFunctionFinEquiv.symm q i))) = q + simp only [Equiv.symm_apply_apply, Equiv.apply_symm_apply] /-- Decoding a query's code returns every output coordinate unchanged. -/ @[simp] theorem queryAt_queryIndex {L : ℕ} (w : OutStr L) : queryAt (queryIndex w) = w := by funext i - simp only [queryAt, queryIndex, Equiv.symm_apply_apply, Equiv.apply_symm_apply] + change outputDigits ((finFunctionFinEquiv.symm + (finFunctionFinEquiv fun j => outputDigits.symm (w j))) i) = w i + simp only [Equiv.symm_apply_apply, Equiv.apply_symm_apply] /-- The requested output set is computed from the ordinary initialized list of query codes. Repeated list entries are allowed and will be reported in their original order. -/ @@ -101,7 +105,8 @@ theorem card_image_queryIndex {L : ℕ} (S : Finset (OutStr L)) : /-- The first query digit is its output-variable digit; the suffix is shifted by one place. -/ theorem queryIndex_cons {L : ℕ} (z : OutVar) (w : OutStr L) : (queryIndex (Fin.cons z w) : ℕ) = (outputDigits.symm z).val + 10 * (queryIndex w).val := by - simp only [queryIndex, finFunctionFinEquiv_apply, Fin.sum_univ_succ, Fin.cons_zero, + simp only [queryIndex, Encoding.leafIndex, privateLeaf, finFunctionFinEquiv_apply, + Fin.sum_univ_succ, Fin.cons_zero, Fin.cons_succ, Fin.val_zero, pow_zero, mul_one, Fin.val_succ, pow_succ, Finset.mul_sum] congr 1 apply Finset.sum_congr rfl diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Evaluate.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Evaluate.lean new file mode 100644 index 000000000..37b9ed4eb --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Evaluate.lean @@ -0,0 +1,195 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Recursive +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Root +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Report + +/-! +# Complete native root evaluation and ordered reporting + +## Main results + +* `PrunedQuery.exec_evaluate`: actual root deduplication, all-depth native traversal and ordered + reporting, allowing duplicate input queries and arbitrary original memory outside the workspace. + +The query function specifies the words of the supplied input list. The machine computes its own +distinct set, every recursive child list and every reported index. All scratch addresses, signed +fits, fresh generations and instruction charges follow from the ordinary numerical premises. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Evaluate the actual supplied queries after workspace pointers have been computed. The result +uses the existing finite request-set semantics; no initialized directory or traversal certificate +is supplied. Only the original source cells and an upper bound on old timestamps are required. -/ +theorem exec_evaluate {w L f a0 b0 q0 o0 Q E A B : ℕ} + (a b : Leaf L → ℤ) (qs : Fin Q → OutStr L) (x : Data registers) + (hregs : KernelRegs L f a0 b0 x) (hQ : x.regs queryCount = Q) + (hq0 : x.regs queries = q0) (ho0 : x.regs output = o0) (hE : x.regs epoch = E) + (hinput : ∀ i, x.mem (q0 + i.val) = (queryIndex (qs i)).val) + (hleft : ∀ v, x.mem (a0 + (Encoding.leafIndex v).val) = encode (a v)) + (hright : ∀ v, x.mem (b0 + (Encoding.leafIndex v).val) = encode (b v)) + (hbeforeA : a0 + 10 ^ L ≤ f) (hbeforeB : b0 + 10 ^ L ≤ f) + (hqueryOut : q0 + Q ≤ o0) (houtFrame : o0 + Q ≤ f) + (hmarks : ∀ q < 10 ^ L, + x.mem (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q) ≤ E) + (ha : ∀ v, |a v| ≤ (A : ℤ)) (hb : ∀ v, |b v| ≤ (B : ℤ)) + (hw : 0 < w) (hmag : magnitudeBound L A B < 2 ^ (w - 1)) + (hfit : Fits (w + 2) (evaluationBudget L f a0 b0 q0 o0 Q E)) : + ∃ y, ExecWithin (w + 2) (ram[ ⟪rootList⟫; ⟪controller⟫; ⟪report⟫ ]) x y + (29 * Q + traceWeight (Finset.univ.image qs) + 24) ∧ + (∀ i, y.mem (o0 + i.val) = encode + (Pruned.result L a b (Finset.univ.image qs) (qs i))) ∧ + (∀ c, ¬(o0 ≤ c ∧ c < o0 + Q) → + ¬(f ≤ c ∧ c < f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + 10 ^ L) → + y.mem c = x.mem c) ∧ y.regs output = o0 ∧ + E + 1 ≤ y.regs epoch ∧ y.regs epoch ≤ E + 1 + epochAllowance L ∧ + (∀ q < 10 ^ L, + y.mem (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q) ≤ y.regs epoch) := by + let S : Finset (OutStr L) := Finset.univ.image qs + let codes := fun i => if hi : i < Q then (queryIndex (qs ⟨i, hi⟩)).val else 0 + have himage : (Finset.range Q).image codes = S.image (fun v => (queryIndex v).val) := by + ext c + simp only [Finset.mem_image, Finset.mem_range, S, Finset.mem_univ, true_and] + constructor + · rintro ⟨i, hi, he⟩ + exact ⟨qs ⟨i, hi⟩, ⟨⟨i, hi⟩, rfl⟩, by simpa only [codes, dite_eq_left hi] using he⟩ + · rintro ⟨v, ⟨i, rfl⟩, rfl⟩ + exact ⟨i.val, i.isLt, by simp only [codes, dite_eq_left i.isLt]⟩ + have hpow : 1 ≤ (10 : ℕ) ^ L := Nat.one_le_pow L _ (by decide) + have hL : L + 1 ≤ (L + 1) * 10 ^ L := by nlinarith only [hpow] + have hcodes (i : ℕ) (hi : i < Q) : codes i < 10 ^ L := by + simpa only [codes, dite_eq_left hi] using (queryIndex (qs ⟨i, hi⟩)).isLt + obtain ⟨u, hroot, hul, hum, hua, hue, hur, huc, hup, hus, hut, _, hrootFrame, _⟩ := + exec_rootList (N := Q) (capacity := 10 ^ L) x codes hQ + (fun i hi => by simpa only [hq0, codes, dite_eq_left hi] using hinput ⟨i, hi⟩) hcodes + (by rw [hregs.lists_eq, hregs.stamps_eq]; nlinarith only [hpow]) + (by rw [hq0, hregs.lists_eq]; omega) + (by rw [hregs.frames_eq, hregs.lists_eq]; omega) + (by rw [hq0, hregs.frames_eq]; omega) + (by simpa only [hregs.stamps_eq, hE] using hmarks) + hregs.nil_eq hregs.one_eq hregs.ten_eq hregs.nine_eq + (hfit.of_le (by + rw [hq0, hregs.lists_eq, hregs.stamps_eq, hE, hregs.frames_eq] + unfold evaluationBudget nodeBudget + nlinarith only [hpow, hL])) + have huregs : KernelRegs L f a0 b0 u := by + constructor + · exact (hroot.regs_eq (by decide : rootList.Preserves inputDepth)).trans hregs.depth_eq + · exact (hroot.regs_eq (by decide : rootList.Preserves span)).trans hregs.span_eq + · exact (hroot.regs_eq (by decide : rootList.Preserves arrayA)).trans hregs.left_eq + · exact (hroot.regs_eq (by decide : rootList.Preserves arrayB)).trans hregs.right_eq + · exact (hroot.regs_eq (by decide : rootList.Preserves frames)).trans hregs.frames_eq + · exact (hroot.regs_eq (by decide : rootList.Preserves lists)).trans hregs.lists_eq + · exact (hroot.regs_eq (by decide : rootList.Preserves values)).trans hregs.values_eq + · exact (hroot.regs_eq (by decide : rootList.Preserves stamps)).trans hregs.stamps_eq + · exact (hroot.regs_eq (by decide : rootList.Preserves nil)).trans hregs.nil_eq + · exact (hroot.regs_eq (by decide : rootList.Preserves one)).trans hregs.one_eq + · exact (hroot.regs_eq (by decide : rootList.Preserves ten)).trans hregs.ten_eq + · exact (hroot.regs_eq (by decide : rootList.Preserves nine)).trans hregs.nine_eq + · exact (hroot.regs_eq (by decide : rootList.Preserves frameWidth)).trans hregs.frameWidth_eq + have hu : NodeReady L f a0 b0 L 0 0 (E + 1) S u := by + refine ⟨huregs, by omega, by simp, hul, hum, hua, ?_, ?_, ?_⟩ + · simpa only [hE] using hue + · intro j + change u.mem (f + 4 * 0 + j.val) = ![S.card, 0, 10 ^ 0, 0] j + fin_cases j + · change u.mem (f) = S.card + simpa only [hregs.frames_eq, himage, card_image_queryIndex] using huc + · change u.mem (f + 1) = 0 + simpa only [hregs.frames_eq] using hup + · change u.mem (f + 2) = 1 + simpa only [hregs.frames_eq] using hus + · change u.mem (f + 3) = 0 + simpa only [hregs.frames_eq] using hut + · simpa only [hregs.lists_eq, hregs.stamps_eq, hE, himage, Nat.zero_mul, Nat.add_zero] + using hur + have hrootBefore (c : ℕ) (hc : c < f) : u.mem c = x.mem c := by + apply hrootFrame c + all_goals simp only [hregs.lists_eq, hregs.stamps_eq, hregs.frames_eq, Block.Owns] + all_goals omega + have hsourceA (v : Leaf L) : u.mem (a0 + 0 + 10 ^ 0 * (Encoding.leafIndex v).val) = + encode (a v) := by + simp only [Nat.add_zero, pow_zero, Nat.one_mul] + rw [hrootBefore _ (by have := (Encoding.leafIndex v).isLt; omega)] + exact hleft v + have hsourceB (v : Leaf L) : u.mem (b0 + 0 + 10 ^ 0 * (Encoding.leafIndex v).val) = + encode (b v) := by + simp only [Nat.add_zero, pow_zero, Nat.one_mul] + rw [hrootBefore _ (by have := (Encoding.leafIndex v).isLt; omega)] + exact hright v + obtain ⟨v, hv, hcontroller⟩ := exec_ready L S a b u hu hsourceA hsourceB hbeforeA hbeforeB + ha hb hw hmag (hfit.of_le (by unfold evaluationBudget; omega)) + have hexit : ExecWithin (w + 2) controller v v 2 := ExecWithin.loop_exit + (by simp only [Cond.holds_lt, hv.regs.nil_eq, hv.active_eq]; decide) + have hloop := hcontroller v 2 hexit + have hqv : v.regs queries = q0 := + (hloop.regs_eq (by decide : controller.Preserves queries)).trans + ((hroot.regs_eq (by decide : rootList.Preserves queries)).trans hq0) + have hov : v.regs output = o0 := + (hloop.regs_eq (by decide : controller.Preserves output)).trans + ((hroot.regs_eq (by decide : rootList.Preserves output)).trans ho0) + have hQv : v.regs queryCount = Q := + (hloop.regs_eq (by decide : controller.Preserves queryCount)).trans + ((hroot.regs_eq (by decide : rootList.Preserves queryCount)).trans hQ) + have hquery (i : ℕ) (hi : i < Q) : v.mem (v.regs queries + i) = codes i := by + rw [hqv, hv.frame _ (notMem_nodeWrites_before (Nat.zero_le _) (by omega)), + hrootBefore _ (by omega)] + simpa only [codes, dite_eq_left hi] using hinput ⟨i, hi⟩ + obtain ⟨y, hreport, _, hvalues, hreportFrame, _⟩ := exec_report (N := Q) + (capacity := 10 ^ L) v codes hQv hquery hcodes (Or.inl (by rw [hqv, hov]; omega)) + (Or.inr (by rw [hov, hv.regs.values_eq]; omega)) hv.regs.one_eq + (hfit.of_le (by + rw [hqv, hov, hv.regs.values_eq] + unfold evaluationBudget nodeBudget + nlinarith only [hpow, hL])) + have he : ExecWithin (w + 2) (ram[ ⟪rootList⟫; ⟪controller⟫; ⟪report⟫ ]) x y + (29 * Q + traceWeight S + 24) := (hroot.seq (hloop.seq hreport)).mono (by omega) + refine ⟨y, he, ?_, ?_, (hreport.regs_eq (by decide : report.Preserves output)).trans hov, + ?_, ?_, ?_⟩ + · intro i + have hr := hvalues i.val i.isLt + rw [hov, hv.regs.values_eq] at hr + have hqi : codes i.val = (queryIndex (qs i)).val := by + simp only [codes, dite_eq_left i.isLt] + rw [hqi] at hr + have hval := hv.values (qs i) (Finset.mem_image.mpr ⟨i, Finset.mem_univ _, rfl⟩) + simp only [Nat.zero_mul, Nat.add_zero] at hval + exact hr.trans hval + · intro c hout hscratch + have htwo : 2 * (L + 1) * 10 ^ L = (L + 1) * 10 ^ L + (L + 1) * 10 ^ L := by ring + rw [htwo] at hscratch + have hcap : 10 ^ L ≤ (L + 1) * 10 ^ L := Nat.le_mul_of_pos_left _ (by omega) + rw [hreportFrame c (by simpa only [hov, Block.mem_footprint, Block.Owns] using hout)] + have hnot : c ∉ nodeWrites L f 0 := by + rw [mem_nodeWrites_iff (Nat.zero_le _)] + simp only [Nat.zero_mul, Nat.add_zero, htwo] + omega + rw [hv.frame c hnot] + apply hrootFrame c + all_goals simp only [hregs.lists_eq, hregs.stamps_eq, hregs.frames_eq, Block.Owns, htwo] + all_goals omega + · rw [hreport.regs_eq (by decide : report.Preserves epoch)] + exact hv.epoch_le + · rw [hreport.regs_eq (by decide : report.Preserves epoch)] + exact hv.epoch_upper + · intro q hq + rw [hreportFrame _ (by + rw [Block.mem_footprint] + simp only [hov, Block.Owns] + omega), hreport.regs_eq (by decide : report.Preserves epoch)] + exact hv.timestamps q hq + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Flat.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Flat.lean new file mode 100644 index 000000000..deff0b518 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Flat.lean @@ -0,0 +1,96 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Input + +/-! +# Flat initialized executions of native pruned queries + +## Main results + +* `PrunedQuery.runs`: prefix `Runs` and charged halting `Executes` from actual `State.init`, + connected to the same final memory, existing finite pruned values and outside frame. +* `PrunedQuery.bounded_reachable`: every reachable prefix has target-sized data words. + +The single fixed program returns the unsigned pointer to its encoded answer list. Queries may +repeat and their order is preserved. Its 96 registers and code-size bound are independent of +depth, query count, input values and physical word width. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Actual initialized flat executions. The prefix witness holding all answers is precisely +the state at the final halt, and the halt is charged as one further target instruction. -/ +theorem runs {w L A B : ℕ} (hw : 0 < w) (a b : Fin (10 ^ L) → ℤ) + (qs : List (Fin (10 ^ L))) (ha : ∀ v, |a v| ≤ (A : ℤ)) (hb : ∀ v, |b v| ≤ (B : ℤ)) + (hmag : magnitudeBound L A B < 2 ^ (w - 1)) + (haddr : Fits (w + 2) (addressBound L qs.length)) : + InputFits (w + 2) (input L a b qs) ∧ + ∃ y n, n ≤ timeBound qs ∧ + Runs (w + 2) (program.program output) (State.init (input L a b qs)) + ⟨program.size, y⟩ n ∧ + Executes (w + 2) (program.program output) (State.init (input L a b qs)) + (outputBase L qs.length) (n + 1) ∧ y.Bounded (w + 2) ∧ + (∀ i : Fin qs.length, y.mem (outputBase L qs.length + i.val) = encode + (Pruned.result L (fun v => a (Encoding.leafIndex v)) + (fun v => b (Encoding.leafIndex v)) (requested qs) (queryAt (qs.get i)))) ∧ + (∀ c, ¬(outputBase L qs.length ≤ c ∧ c < memoryEnd L qs.length) → + y.mem c = (State.init (input L a b qs) : State registers).data.mem c) := by + obtain ⟨hi, y, ⟨n, hn, he⟩, hb, hv, hf, ho⟩ := exec_input hw a b qs ha hb hmag haddr + refine ⟨hi, y, n, hn, ?_, ?_, hb, hv, hf⟩ + · simpa only [Nat.zero_add, State.init] using he.runs (pc := 0) + (p := program.program output) (Program.Contains.self _).append_left + · simpa only [ho, State.init] using he.executes output + +/-- Ordinary native safety applies to every initialized reachable prefix, not merely the +returned witness. The admission hypotheses are the same numerical original-input bounds. -/ +theorem bounded_reachable {w L A B n : ℕ} (hw : 0 < w) (a b : Fin (10 ^ L) → ℤ) + (qs : List (Fin (10 ^ L))) (ha : ∀ v, |a v| ≤ (A : ℤ)) (hb : ∀ v, |b v| ≤ (B : ℤ)) + (hmag : magnitudeBound L A B < 2 ^ (w - 1)) + (haddr : Fits (w + 2) (addressBound L qs.length)) {y : State registers} + (hy : Runs (w + 2) (program.program output) (State.init (input L a b qs)) y n) : + y.data.Bounded (w + 2) := + hy.bounded_init (input_fits hw a b qs ha hb hmag haddr) + +/-- An encoder producer's existing leaf-value contract gives the original bilinear product at +every supplied query. This is the same actual initialized program and charged run as `runs`; +the mathematical specialization adds no machine operation or assumed producer execution. -/ +theorem runs_bilinear {w L A B : ℕ} (hw : 0 < w) (left right : Fin (10 ^ L) → ℤ) + (qs : List (Fin (10 ^ L))) (a : LeftStr L → ℤ) (b : RightStr L → ℤ) + (hleft : ∀ v, left (Encoding.leafIndex v) = encodingL a v) + (hright : ∀ v, right (Encoding.leafIndex v) = encodingR b v) + (ha : ∀ v, |left v| ≤ (A : ℤ)) (hb : ∀ v, |right v| ≤ (B : ℤ)) + (hmag : magnitudeBound L A B < 2 ^ (w - 1)) + (haddr : Fits (w + 2) (addressBound L qs.length)) : + InputFits (w + 2) (input L left right qs) ∧ + ∃ y n, n ≤ timeBound qs ∧ + Runs (w + 2) (program.program output) (State.init (input L left right qs)) + ⟨program.size, y⟩ n ∧ + Executes (w + 2) (program.program output) (State.init (input L left right qs)) + (outputBase L qs.length) (n + 1) ∧ y.Bounded (w + 2) ∧ + (∀ i : Fin qs.length, y.mem (outputBase L qs.length + i.val) = + encode (bilinearProduct a b (queryAt (qs.get i)))) ∧ + (∀ c, ¬(outputBase L qs.length ≤ c ∧ c < memoryEnd L qs.length) → + y.mem c = (State.init (input L left right qs) : State registers).data.mem c) := by + obtain ⟨hi, y, n, hn, hr, he, hy, hv, hf⟩ := runs hw left right qs ha hb hmag haddr + refine ⟨hi, y, n, hn, hr, he, hy, ?_, hf⟩ + intro i + have hvalue := hv i + rw [show (fun v => left (Encoding.leafIndex v)) = encodingL a from funext hleft, + show (fun v => right (Encoding.leafIndex v)) = encodingR b from funext hright, + Pruned.result_eq_piecewise_bilinearProduct, + Finset.piecewise_eq_of_mem _ _ _ (queryAt_mem_requested (List.get_mem qs i))] at hvalue + exact hvalue + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Initialize.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Initialize.lean new file mode 100644 index 000000000..ce7abfbd3 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Initialize.lean @@ -0,0 +1,113 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Powers +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Ready + +/-! +# Native headers, powers and evaluator pointers + +## Main results + +* `PrunedQuery.exec_initialize`: load both actual headers, compute the encoding-array length, + derive every evaluator pointer, and establish the ordinary register calling convention. + +The prefix uses `4L+27` instructions, including its power loop. Its continuation is the existing +structured sequence rule, so composing it with evaluation gives the original fixed program. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM + +/-- Initialization derives all pointers from the two ordinary scalar headers. Its output +assertion contains no execution or fit fields; the charged native composition is proved here. -/ +theorem exec_initialize {w L Q : ℕ} (x : Data registers) (hL : x.mem 1 = L) + (hQ : x.mem 2 = Q) (hfit : Fits w (addressBound L Q)) : + ∃ y, y.mem = x.mem ∧ KernelRegs L (scratchBase L Q) 3 (3 + 10 ^ L) y ∧ + y.regs queryCount = Q ∧ y.regs queries = 3 + 2 * 10 ^ L ∧ + y.regs output = outputBase L Q ∧ y.regs epoch = 0 ∧ + ∀ (tail : Code .standard registers) z T, ExecWithin w tail y z T → + ExecWithin w (ram[ ⟪header⟫; ⟪powers⟫; ⟪parameters⟫; ⟪locate⟫; ⟪tail⟫ ]) + x z (4 * L + 27 + T) := by + have hp : 1 ≤ (10 : ℕ) ^ L := Nat.one_le_pow L _ (by decide) + have hscalar : L + Q + 10 ^ L + 16 ≤ addressBound L Q := by + unfold addressBound + nlinarith only [hp] + have h1 : Fits w 1 := hfit.of_le (by omega) + have h2 : Fits w 2 := hfit.of_le (by omega) + have h3 : Fits w 3 := hfit.of_le (by omega) + let h := (((x.set address 1).set inputDepth L).set address 2).set queryCount Q + have hheader : ExecWithin w header x h 4 := by + ram_eval [header, h, hL, hQ, h1, h2, address, inputDepth, queryCount, Fin.mk.injEq] + have hdepth : h.regs inputDepth = L := by simp +decide [h, address, inputDepth, queryCount] + obtain ⟨p, hpower, hpmem, hpL, hspan, _, hnil, hone, _, hten, hnine, hfour, _⟩ := + exec_powers h hdepth hfit + have hpQ : p.regs queryCount = Q := + (hpower.regs_eq (by decide : powers.Preserves queryCount)).trans (by simp [h]) + let u := ((((((p.set arrayA 3).set arrayB (3 + 10 ^ L)).set queries (3 + 10 ^ L + 10 ^ L)).set + output (3 + 10 ^ L + 10 ^ L + Q)).set scratch (3 + 10 ^ L + 10 ^ L + Q + Q)).set epoch 0) + have hB : Fits w (3 + 10 ^ L) := hfit.of_le (by omega) + have hqueries : Fits w (3 + 10 ^ L + 10 ^ L) := + hfit.of_le (by unfold addressBound; nlinarith only [hp]) + have hout : Fits w (3 + 10 ^ L + 10 ^ L + Q) := + hfit.of_le (by unfold addressBound; nlinarith only [hp]) + have hs : Fits w (3 + 10 ^ L + 10 ^ L + Q + Q) := + hfit.of_le (by unfold addressBound; nlinarith only [hp]) + have hparameters : ExecWithin w parameters p u 6 := by + ram_eval [parameters, u, hspan, hpQ, h3, hB, hqueries, hout, hs, arrayA, arrayB, queries, + output, scratch, epoch, span, queryCount, Fin.mk.injEq] + have unchanged (i : Fin registers) (hi : parameters.Preserves i) : u.regs i = p.regs i := + hparameters.regs_eq hi + have huL : u.regs inputDepth = L := (unchanged _ (by decide)).trans hpL + have huspan : u.regs span = 10 ^ L := (unchanged _ (by decide)).trans hspan + have hu1 : u.regs one = 1 := (unchanged _ (by decide)).trans hone + have hu4 : u.regs frameWidth = 4 := (unchanged _ (by decide)).trans hfour + have hus : u.regs scratch = scratchBase L Q := by + simp +decide [u, scratch, epoch, scratchBase, outputBase, two_mul, Nat.add_assoc] + obtain ⟨y, hlocate, hymem, hyf, hyl, hyv, hys, _⟩ := exec_locate u hu1 hu4 + (hfit.of_le (by + rw [hus, huL, huspan] + unfold scratchBase outputBase addressBound + have hm : L ≤ L * 10 ^ L := Nat.le_mul_of_pos_right _ (by positivity) + nlinarith only [hp, hm])) + have hyconstant (i : Fin registers) (hi : locate.Preserves i) + (hi' : parameters.Preserves i) : y.regs i = p.regs i := + (hlocate.regs_eq hi).trans (unchanged i hi') + have hregs : KernelRegs L (scratchBase L Q) 3 (3 + 10 ^ L) y := by + constructor + · exact (hyconstant _ (by decide) (by decide)).trans hpL + · exact (hyconstant _ (by decide) (by decide)).trans hspan + · exact (hlocate.regs_eq (by decide : locate.Preserves arrayA)).trans (by + simp +decide [u, arrayA, arrayB, queries, output, scratch, epoch]) + · exact (hlocate.regs_eq (by decide : locate.Preserves arrayB)).trans (by + simp +decide [u, arrayA, arrayB, queries, output, scratch, epoch]) + · exact hyf.trans hus + · simpa only [hus, huL] using hyl + · simpa only [hus, huL, huspan] using hyv + · simpa only [hus, huL, huspan] using hys + · exact (hyconstant _ (by decide) (by decide)).trans hnil + · exact (hyconstant _ (by decide) (by decide)).trans hone + · exact (hyconstant _ (by decide) (by decide)).trans hten + · exact (hyconstant _ (by decide) (by decide)).trans hnine + · exact (hyconstant _ (by decide) (by decide)).trans hfour + refine ⟨y, ?_, hregs, ?_, ?_, ?_, ?_, ?_⟩ + · exact hymem.trans (show u.mem = p.mem from rfl) |>.trans hpmem + · exact (hyconstant _ (by decide) (by decide)).trans hpQ + · exact (hlocate.regs_eq (by decide : locate.Preserves queries)).trans (by + simp +decide [u, queries, output, scratch, epoch, two_mul, Nat.add_assoc]) + · exact (hlocate.regs_eq (by decide : locate.Preserves output)).trans (by + simp +decide [u, output, scratch, epoch, outputBase, two_mul, Nat.add_assoc]) + · exact (hlocate.regs_eq (by decide : locate.Preserves epoch)).trans (by simp [u]) + · intro tail z T htail + exact (hheader.seq (hpower.seq (hparameters.seq (hlocate.seq htail)))).mono (by omega) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Input.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Input.lean new file mode 100644 index 000000000..10e06c67b --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Input.lean @@ -0,0 +1,168 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Initialize +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Evaluate +public import Complexity.Computability.WordRAM.InputArray +import Mathlib.Data.List.GetD + +/-! +# Actual initialized-input execution of native pruned queries + +## Main definitions + +* `PrunedQuery.timeBound`: the full charged prefix, including setup and duplicate reporting. + +## Main results + +* `PrunedQuery.exec_input`: the fixed program starts at `State.init`, computes the existing + finite pruned values, preserves the input and outside memory, and leaves bounded native words. + +The input contains ordinary depth/count headers, two canonical signed arrays and query codes. +All child lists and directories are computed. No layout, execution or timing certificate is an +input word or an admission hypothesis. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Full prefix charge: actual header/power/pointer work, root deduplication, every recursive +controller instruction, loop exit and reporting in the original supplied order. -/ +def timeBound {L : ℕ} (qs : List (Fin (10 ^ L))) : ℕ := + 4 * L + 29 * qs.length + traceWeight (requested qs) + 51 + +/-- Pruning gives a bound in terms of the actual contributing leaf set, retaining the setup +and duplicate-query costs. This includes one additional final halt instruction. -/ +theorem timeBound_add_one_le {L : ℕ} (qs : List (Fin (10 ^ L))) : + timeBound qs + 1 ≤ + 8192 * (L + qs.length + 1 + (L + 1) * (leaves (requested qs)).card) := by + have ht := traceWeight_le (requested qs) + unfold timeBound + nlinarith only [ht] + +/-- Both scalar headers, both signed arrays, all query codes and the machine's unsigned length +header fit the same admitted target width. -/ +theorem input_fits {w L A B : ℕ} (hw : 0 < w) (a b : Fin (10 ^ L) → ℤ) + (qs : List (Fin (10 ^ L))) (ha : ∀ v, |a v| ≤ (A : ℤ)) (hb : ∀ v, |b v| ≤ (B : ℤ)) + (hmag : magnitudeBound L A B < 2 ^ (w - 1)) + (haddr : Fits (w + 2) (addressBound L qs.length)) : InputFits (w + 2) (input L a b qs) := by + have henv := magnitudeBound_fits hw hmag + have hp : 1 ≤ (10 : ℕ) ^ L := Nat.one_le_pow L _ (by decide) + have hsmall : L + qs.length + 10 ^ L + 16 ≤ addressBound L qs.length := by + unfold addressBound + nlinarith only [hp] + refine ⟨?_, ?_⟩ + · rw [length_input] + exact haddr.of_le (by unfold addressBound; nlinarith only [hp]) + · intro v hv + simp only [input, List.mem_append, List.mem_cons, List.not_mem_nil, or_false] at hv + rcases hv with (((rfl | rfl) | hv) | hv) | hv + · exact haddr.of_le (by omega) + · exact haddr.of_le (by omega) + · obtain ⟨i, rfl⟩ := List.mem_ofFn.mp hv + exact (encode_fits (fits_left henv (ha i))).mono (by omega) + · obtain ⟨i, rfl⟩ := List.mem_ofFn.mp hv + exact (encode_fits (fits_right henv (hb i))).mono (by omega) + · obtain ⟨q, _, rfl⟩ := List.mem_map.mp hv + exact haddr.of_le (by have := q.isLt; omega) + +/-- Execution of the complete native program on actual initialized memory. The final memory +witness is shared by execution, mathematical outputs, frame preservation and boundedness. -/ +theorem exec_input {w L A B : ℕ} (hw : 0 < w) (a b : Fin (10 ^ L) → ℤ) + (qs : List (Fin (10 ^ L))) (ha : ∀ v, |a v| ≤ (A : ℤ)) (hb : ∀ v, |b v| ≤ (B : ℤ)) + (hmag : magnitudeBound L A B < 2 ^ (w - 1)) + (haddr : Fits (w + 2) (addressBound L qs.length)) : + InputFits (w + 2) (input L a b qs) ∧ + ∃ y, ExecWithin (w + 2) program (State.init (input L a b qs)).data y (timeBound qs) ∧ + y.Bounded (w + 2) ∧ + (∀ i : Fin qs.length, y.mem (outputBase L qs.length + i.val) = encode + (Pruned.result L (fun v => a (Encoding.leafIndex v)) + (fun v => b (Encoding.leafIndex v)) (requested qs) (queryAt (qs.get i)))) ∧ + (∀ c, ¬(outputBase L qs.length ≤ c ∧ c < memoryEnd L qs.length) → + y.mem c = (State.init (input L a b qs) : State registers).data.mem c) ∧ + y.regs output = outputBase L qs.length := by + have hi := input_fits hw a b qs ha hb hmag haddr + let x : Data registers := (State.init (input L a b qs)).data + have hheaderL : x.mem 1 = L := by simp [x, State.init, Data.init, input] + have hheaderQ : x.mem 2 = qs.length := by simp [x, State.init, Data.init, input] + have hthree (n : ℕ) : 1 + (1 + (1 + n)) = 3 + n := by omega + have hleft (i : Fin (10 ^ L)) : x.mem (3 + i.val) = encode (a i) := by + have h := Data.init_realizes_append (r := registers) [L, qs.length] + (List.ofFn (fun j => encode (a j))) (List.ofFn (fun j => encode (b j)) ++ qs.map Fin.val) + have hr := h ⟨i.val, by simp⟩ + simpa [x, State.init, input, Block.read, List.append_assoc] using hr + have hright (i : Fin (10 ^ L)) : x.mem (3 + 10 ^ L + i.val) = encode (b i) := by + have h := Data.init_realizes_append (r := registers) + ([L, qs.length] ++ List.ofFn (fun j => encode (a j))) + (List.ofFn (fun j => encode (b j))) (qs.map Fin.val) + have hr := h ⟨i.val, by simp⟩ + simpa [x, State.init, input, Block.read, List.append_assoc, Nat.add_comm, + Nat.add_left_comm, Nat.add_assoc, hthree] using hr + have hqueries (i : Fin qs.length) : + x.mem (3 + 2 * 10 ^ L + i.val) = (qs.get i).val := by + have h := Data.init_realizes_append (r := registers) + ([L, qs.length] ++ List.ofFn (fun j => encode (a j)) ++ + List.ofFn (fun j => encode (b j))) (qs.map Fin.val) [] + have hr := h ⟨i.val, by simp⟩ + simpa [x, State.init, input, Block.read, List.append_assoc, two_mul, Nat.add_comm, + Nat.add_left_comm, Nat.add_assoc, hthree] using hr + have hzero (c : ℕ) (hc : (input L a b qs).length + 1 ≤ c) : x.mem c = 0 := by + have hc0 : c ≠ 0 := by omega + change (if c = 0 then (input L a b qs).length else (input L a b qs).getD (c - 1) 0) = 0 + rw [ite_eq_right hc0, List.getD_eq_default _ _ (by omega)] + obtain ⟨u, humem, hregs, hQ, hqueriesReg, hout, hE, hinitialize⟩ := + exec_initialize x hheaderL hheaderQ haddr + let queryList := fun i : Fin qs.length => queryAt (qs.get i) + have hset : Finset.univ.image queryList = requested qs := by + ext q + simp only [Finset.mem_image, Finset.mem_univ, true_and, requested, List.mem_toFinset] + constructor + · rintro ⟨i, rfl⟩ + exact ⟨qs.get i, List.get_mem _ i, rfl⟩ + · rintro ⟨c, hc, rfl⟩ + obtain ⟨i, rfl⟩ := List.mem_iff_get.mp hc + exact ⟨i, rfl⟩ + obtain ⟨y, hevaluate, hvalues, hframe, hreturn, _, _, _⟩ := exec_evaluate + (Q := qs.length) (q0 := 3 + 2 * 10 ^ L) (o0 := outputBase L qs.length) + (f := scratchBase L qs.length) + (a := fun v => a (Encoding.leafIndex v)) (b := fun v => b (Encoding.leafIndex v)) + queryList u hregs hQ hqueriesReg hout hE + (by intro i; rw [humem, queryIndex_queryAt]; exact hqueries i) + (by intro v; rw [humem]; exact hleft _) + (by intro v; rw [humem]; exact hright _) + (by unfold scratchBase outputBase; omega) (by unfold scratchBase outputBase; omega) + (by unfold outputBase; omega) (by unfold scratchBase; omega) + (by + intro q _ + rw [humem, hzero _ (by + rw [length_input] + unfold scratchBase outputBase + omega)]) + (fun v => ha _) (fun v => hb _) hw hmag + (haddr.of_le (evaluationBudget_input_le L qs.length)) + have he : ExecWithin (w + 2) program x y (timeBound qs) := by + have h := hinitialize _ y _ hevaluate + simp only [hset] at h + exact h.mono (by unfold timeBound; omega) + refine ⟨hi, y, he, he.bounded (Data.Bounded.init hi), ?_, ?_, hreturn⟩ + · intro i + simpa only [hset, queryList] using hvalues i + · intro c hc + rw [hframe c (by + unfold memoryEnd stampsBase valuesBase listsBase scratchBase at hc + omega) (by + simp only [memoryEnd, stampsBase, valuesBase, listsBase, scratchBase, + two_mul, Nat.add_mul, Nat.add_assoc] at hc ⊢ + omega), humem] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Kernel.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Kernel.lean new file mode 100644 index 000000000..72d1067f1 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Kernel.lean @@ -0,0 +1,104 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Evaluate +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Powers + +/-! +# Callable native pruned-query kernel + +## Main results + +* `PrunedQuery.exec_kernel`: compute powers/layout, deduplicate supplied queries, execute the + complete pruned traversal and report answers using ordinary caller pointers and a fresh epoch. + +Input arrays contain canonical `Signed.encode` words. Query codes, pointers, depth and count are +raw unsigned words. The caller supplies only the source arrays/list and old timestamp bound; +all frames, child lists and decisions are computed by the fixed code. The returned directory +bound permits reuse across tiles without a free dense-directory initialization per call. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The actual pointer-argument kernel, with computed powers and workspace partition, preserves +all memory outside its output and explicit scratch intervals. No initialized frame/list or child +execution premise is used. The original input magnitude envelope derives every signed fit. -/ +theorem exec_kernel {w L f a0 b0 q0 o0 Q E A B : ℕ} + (a b : Leaf L → ℤ) (qs : Fin Q → OutStr L) (x : Data registers) + (hL : x.regs inputDepth = L) (hA : x.regs arrayA = a0) (hB : x.regs arrayB = b0) + (hQ : x.regs queryCount = Q) (hq0 : x.regs queries = q0) + (ho0 : x.regs output = o0) (hf : x.regs scratch = f) (hE : x.regs epoch = E) + (hinput : ∀ i, x.mem (q0 + i.val) = (queryIndex (qs i)).val) + (hleft : ∀ v, x.mem (a0 + (Encoding.leafIndex v).val) = encode (a v)) + (hright : ∀ v, x.mem (b0 + (Encoding.leafIndex v).val) = encode (b v)) + (hbeforeA : a0 + 10 ^ L ≤ f) (hbeforeB : b0 + 10 ^ L ≤ f) + (hqueryOut : q0 + Q ≤ o0) (houtFrame : o0 + Q ≤ f) + (hmarks : ∀ q < 10 ^ L, + x.mem (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q) ≤ E) + (ha : ∀ v, |a v| ≤ (A : ℤ)) (hb : ∀ v, |b v| ≤ (B : ℤ)) + (hw : 0 < w) (hmag : magnitudeBound L A B < 2 ^ (w - 1)) + (hfit : Fits (w + 2) (kernelBudget L f a0 b0 q0 o0 Q E)) : + ∃ y, ExecWithin (w + 2) kernel x y + (4 * L + 29 * Q + traceWeight (Finset.univ.image qs) + 41) ∧ + (∀ i, y.mem (o0 + i.val) = encode + (Pruned.result L a b (Finset.univ.image qs) (qs i))) ∧ + (∀ c, ¬(o0 ≤ c ∧ c < o0 + Q) → + ¬(f ≤ c ∧ c < f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + 10 ^ L) → + y.mem c = x.mem c) ∧ y.regs output = o0 ∧ + E + 1 ≤ y.regs epoch ∧ y.regs epoch ≤ E + 1 + epochAllowance L ∧ + (∀ q < 10 ^ L, + y.mem (f + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + q) ≤ y.regs epoch) := by + obtain ⟨p, hp, hpmem, hpL, hspan, _, hnil, hone, _, hten, hnine, hfour, _⟩ := + exec_powers (Q := Q) x hL (hfit.of_le (by unfold kernelBudget; omega)) + have hpFrame : p.regs scratch = f := + (hp.regs_eq (by decide : powers.Preserves scratch)).trans hf + have hpow : 1 ≤ (10 : ℕ) ^ L := Nat.one_le_pow L _ (by decide) + have hLcap : L + 1 ≤ (L + 1) * 10 ^ L := by nlinarith only [hpow] + obtain ⟨u, hu, humem, huf, hul, huv, hus, _⟩ := exec_locate p hone hfour + (hfit.of_le (by + rw [hpFrame, hpL, hspan] + unfold kernelBudget evaluationBudget nodeBudget + nlinarith only [hpow, hLcap])) + have fixed (i : Fin registers) (hi : locate.Preserves i) (hi' : powers.Preserves i) : + u.regs i = x.regs i := (hu.regs_eq hi).trans (hp.regs_eq hi') + have hregs : KernelRegs L f a0 b0 u := by + constructor + · exact (hu.regs_eq (by decide : locate.Preserves inputDepth)).trans hpL + · exact (hu.regs_eq (by decide : locate.Preserves span)).trans hspan + · exact (fixed _ (by decide) (by decide)).trans hA + · exact (fixed _ (by decide) (by decide)).trans hB + · exact huf.trans hpFrame + · simpa only [hpFrame, hpL] using hul + · simpa only [hpFrame, hpL, hspan] using huv + · simpa only [hpFrame, hpL, hspan] using hus + · exact (hu.regs_eq (by decide : locate.Preserves nil)).trans hnil + · exact (hu.regs_eq (by decide : locate.Preserves one)).trans hone + · exact (hu.regs_eq (by decide : locate.Preserves ten)).trans hten + · exact (hu.regs_eq (by decide : locate.Preserves nine)).trans hnine + · exact (hu.regs_eq (by decide : locate.Preserves frameWidth)).trans hfour + have hmem : u.mem = x.mem := humem.trans hpmem + obtain ⟨y, hy, hvalues, hframe, hout, hel, heu, htable⟩ := exec_evaluate a b qs u hregs + ((fixed _ (by decide) (by decide)).trans hQ) + ((fixed _ (by decide) (by decide)).trans hq0) + ((fixed _ (by decide) (by decide)).trans ho0) + ((fixed _ (by decide) (by decide)).trans hE) + (by simpa only [hmem] using hinput) (by simpa only [hmem] using hleft) + (by simpa only [hmem] using hright) hbeforeA hbeforeB hqueryOut houtFrame + (by simpa only [hmem] using hmarks) ha hb hw hmag + (hfit.of_le (by unfold kernelBudget; omega)) + refine ⟨y, ?_, hvalues, ?_, hout, hel, heu, htable⟩ + · exact (hp.seq (hu.seq hy)).mono (by omega) + · simpa only [hmem] using hframe + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean index 5575404e7..51645b50e 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean @@ -40,50 +40,93 @@ open Cslib.WordRAM Cslib.WordRAM.Signed /-- The program has a fixed register bank, including nine signed-arithmetic scratch registers. -/ abbrev registers : ℕ := 96 +/-- Original tensor depth, loaded from the ordinary input header. -/ abbrev inputDepth : Fin registers := 9 +/-- Computed encoding-array length and per-depth directory capacity, `10^L`. -/ abbrev span : Fin registers := 10 +/-- Pointer to the left canonical signed encoding array. -/ abbrev arrayA : Fin registers := 11 +/-- Pointer to the right canonical signed encoding array. -/ abbrev arrayB : Fin registers := 12 +/-- Pointer to the original unsigned query-code list, including repeats. -/ abbrev queries : Fin registers := 13 +/-- Number of supplied queries, before native deduplication. -/ abbrev queryCount : Fin registers := 14 +/-- Pointer to the answer list, in the original query order. -/ abbrev output : Fin registers := 15 +/-- Caller-supplied start of the explicit stack and directory workspace. -/ abbrev scratch : Fin registers := 16 +/-- Current timestamp generation for native query deduplication. -/ abbrev epoch : Fin registers := 17 +/-- Pointer to the first four-word stack frame. -/ abbrev frames : Fin registers := 18 +/-- Pointer to the first per-depth distinct-query row. -/ abbrev lists : Fin registers := 19 +/-- Pointer to the first per-depth signed-value row. -/ abbrev values : Fin registers := 20 +/-- Pointer to the shared query-membership timestamp directory. -/ abbrev stamps : Fin registers := 21 +/-- Current stack depth, with zero denoting the root. -/ abbrev level : Fin registers := 22 +/-- Controller phase: zero for entry, one for dispatch, two for returned-child accumulation. -/ abbrev mode : Fin registers := 23 +/-- Traversal flag; returning from the root sets this to zero. -/ abbrev active : Fin registers := 24 +/-- Raw unsigned constant one for counters and offsets. -/ abbrev one : Fin registers := 25 +/-- Raw unsigned radix ten for the fixed term and query dictionaries. -/ abbrev ten : Fin registers := 26 +/-- Inner-output wildcard digit, in the existing term numbering. -/ abbrev nine : Fin registers := 27 +/-- Raw unsigned width of a stack frame: four words. -/ abbrev frameWidth : Fin registers := 28 +/-- Address of the currently loaded stack frame. -/ abbrev frame : Fin registers := 29 +/-- Number of distinct requests stored in the current frame. -/ abbrev count : Fin registers := 30 +/-- Processed least-significant leaf digits of the current node. -/ abbrev leafPrefix : Fin registers := 31 +/-- Place value of the next leaf digit, `10^level`. -/ abbrev stride : Fin registers := 32 +/-- Next term number to dispatch or accumulate. -/ abbrev term : Fin registers := 33 +/-- Loop counter for powers, scans, clearing, accumulation and reporting. -/ abbrev index : Fin registers := 34 +/-- Distinct child count computed by the actual timestamp scan. -/ abbrev childCount : Fin registers := 35 +/-- Current numerical output-string code loaded from a query list. -/ abbrev query : Fin registers := 36 +/-- First output digit computed by remainder modulo ten. -/ abbrev digit : Fin registers := 37 +/-- Remaining output digits computed by division by ten. -/ abbrev suffix : Fin registers := 38 +/-- Scratch address used by native indexed loads and stores. -/ abbrev address : Fin registers := 39 +/-- Scratch word for arithmetic, branch fallbacks and loaded values. -/ abbrev temporary : Fin registers := 40 +/-- Scratch word for row-size and address products. -/ abbrev temporary₂ : Fin registers := 41 -abbrev temporary₃ : Fin registers := 42 +/-- Timestamp loaded from the membership directory. -/ abbrev marker : Fin registers := 43 +/-- Address of the current depth's request row. -/ abbrev listBase : Fin registers := 44 +/-- Address of the current depth's accumulator row. -/ abbrev valueBase : Fin registers := 45 +/-- Address of the next depth's computed request row. -/ abbrev childList : Fin registers := 46 +/-- Address of the next depth's returned-value row. -/ abbrev childValues : Fin registers := 47 +/-- Computed child stack depth. -/ abbrev nextLevel : Fin registers := 48 +/-- Computed address of the frame about to be pushed. -/ abbrev childFrame : Fin registers := 49 +/-- Computed leaf prefix of the child about to be pushed. -/ abbrev childPrefix : Fin registers := 50 +/-- Computed leaf place value for the child about to be pushed. -/ abbrev childStride : Fin registers := 51 +/-- Raw unsigned constant zero. -/ abbrev nil : Fin registers := 52 +/-- Raw unsigned constant two, initialized with the fixed scalar constants. -/ abbrev two : Fin registers := 53 /-- Constant initialization, before the depth-controlled power loop. -/ diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Width.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Width.lean new file mode 100644 index 000000000..1a957b340 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Width.lean @@ -0,0 +1,84 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Flat +public import Mathlib.Data.Nat.Log + +/-! +# Sufficient physical width from original pruned-query inputs + +## Main definitions + +* `PrunedQuery.admissionWidth`: three bits beyond the binary ceiling logarithm of the combined + original magnitude and address budgets. + +## Main results + +* `PrunedQuery.runs_wide`: at every admitted physical width, the same finite program starts + from actual initialized input, returns every requested result and halts within the charged bound. + +Input fit is a conclusion, including both scalar headers and the machine's length word. This +component's threshold is logarithmic in its explicit budgets. Relating the encoding depth, query +count and magnitudes to a whole 3SUM input is separate composition work under #1394. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- A positive threshold for the physical target width, computed from original parameters. +The code does not inspect this number or the machine width. -/ +def admissionWidth (L Q A B : ℕ) : ℕ := + Nat.clog 2 (magnitudeBound L A B + addressBound L Q + 1) + 3 + +/-- The physical threshold is nonzero even for empty inputs and zero operands. -/ +theorem admissionWidth_pos (L Q A B : ℕ) : 0 < admissionWidth L Q A B := by + unfold admissionWidth + omega + +/-- Mathlib's ceiling logarithm derives both actual address-word fit and the signed source +envelope, retaining the two extra bits charged by the existing signed arithmetic kernels. -/ +theorem admission_bounds {W L Q A B : ℕ} (hW : admissionWidth L Q A B ≤ W) : + 0 < W - 2 ∧ magnitudeBound L A B < 2 ^ ((W - 2) - 1) ∧ Fits W (addressBound L Q) := by + have hlog := Nat.le_pow_clog (by decide : 1 < (2 : ℕ)) + (magnitudeBound L A B + addressBound L Q + 1) + have hs : Nat.clog 2 (magnitudeBound L A B + addressBound L Q + 1) ≤ (W - 2) - 1 := by + unfold admissionWidth at hW + omega + have hpower := Nat.pow_le_pow_right (by decide : 1 ≤ (2 : ℕ)) hs + refine ⟨by unfold admissionWidth at hW; omega, by omega, ?_⟩ + change addressBound L Q < 2 ^ W + have hpower' := Nat.pow_le_pow_right (by decide : 1 ≤ (2 : ℕ)) (by omega : + Nat.clog 2 (magnitudeBound L A B + addressBound L Q + 1) ≤ W) + omega + +/-- A complete initialized run at every sufficiently wide physical width, with guaranteed input +fit, a shared pre-halt memory witness, charged halt, all output values and outside preservation. -/ +theorem runs_wide {W L A B : ℕ} (a b : Fin (10 ^ L) → ℤ) + (qs : List (Fin (10 ^ L))) (ha : ∀ v, |a v| ≤ (A : ℤ)) (hb : ∀ v, |b v| ≤ (B : ℤ)) + (hW : admissionWidth L qs.length A B ≤ W) : + InputFits W (input L a b qs) ∧ + ∃ y n, n ≤ timeBound qs ∧ + Runs W (program.program output) (State.init (input L a b qs)) ⟨program.size, y⟩ n ∧ + Executes W (program.program output) (State.init (input L a b qs)) + (outputBase L qs.length) (n + 1) ∧ y.Bounded W ∧ + (∀ i : Fin qs.length, y.mem (outputBase L qs.length + i.val) = encode + (Pruned.result L (fun v => a (Encoding.leafIndex v)) + (fun v => b (Encoding.leafIndex v)) (requested qs) (queryAt (qs.get i)))) ∧ + (∀ c, ¬(outputBase L qs.length ≤ c ∧ c < memoryEnd L qs.length) → + y.mem c = (State.init (input L a b qs) : State registers).data.mem c) := by + obtain ⟨hw, hm, ha'⟩ := admission_bounds hW + have htwo : W - 2 + 2 = W := by unfold admissionWidth at hW; omega + have h := runs hw a b qs ha hb hm (by simpa only [htwo] using ha') + simpa only [htwo] using h + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery diff --git a/Complexity/Computability/WordRAM/InputArray.lean b/Complexity/Computability/WordRAM/InputArray.lean new file mode 100644 index 000000000..2bb63808c --- /dev/null +++ b/Complexity/Computability/WordRAM/InputArray.lean @@ -0,0 +1,40 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Allocation +import Mathlib.Data.List.GetD + +/-! +# Array blocks in ordinary initialized inputs + +## Main results + +* `WordRAM.Data.init_realizes_append`: a list segment appears at its actual initialized address, + including the machine's length header. + +This is a representation lemma for the existing `Data.init`, with no alternate input convention. +-/ + +@[expose] public section + +namespace Cslib.WordRAM + +/-- The payload between an arbitrary prefix and suffix occupies its corresponding actual +input block. The extra address word is the standard unsigned input-length header. -/ +theorem Data.init_realizes_append {r : ℕ} (pre payload post : List ℕ) : + (Block.mk (pre.length + 1) payload.length).Realizes + (Data.init (pre ++ (payload ++ post)) : Data r).mem payload.get := by + intro i + change (Data.init (pre ++ (payload ++ post)) : Data r).mem + (pre.length + 1 + i.val) = payload.get i + rw [show pre.length + 1 + i.val = (pre.length + i.val) + 1 by omega, + Data.init_mem_succ, List.getD_append_right _ _ _ _ (by omega), + Nat.add_sub_cancel_left, List.getD_append _ _ _ _ i.isLt, List.getD_eq_get] + +end Cslib.WordRAM diff --git a/ComplexityTests/SchonhagePruned.lean b/ComplexityTests/SchonhagePruned.lean index 63fc42956..ead284f2e 100644 --- a/ComplexityTests/SchonhagePruned.lean +++ b/ComplexityTests/SchonhagePruned.lean @@ -151,13 +151,8 @@ private def answers (w L fuel : ℕ) (a b : Fin (10 ^ L) → ℤ) (qs : List (Fi some [-55, -1, -55] #guard answers 20 1 128 (fun _ => 5) (fun _ => 0) [] == some [] --- Mixed suffixes share work. The two wildcard positions exercise both digit orders. -#guard answers 24 2 2048 (fun i => i.val + 1) (fun _ => 1) [99, 90, 9, 0, 99] == - some [5050, 460, 55, 1, 5050] - --- A nonzero word at the one-past-end address survives the complete controller. -#guard ((program.eval 20 128 - (((State.init (input 1 (fun _ => -2) (fun _ => 3) [9, 0]) : State registers).data).write - (memoryEnd 1 2) 91)).map fun (y, _) => y.mem (memoryEnd 1 2)) == some 91 +-- The depth-two mixed-query and outside-sentinel cases now have kernel-checked actual native +-- run witnesses in SchonhagePrunedInput and SchonhagePrunedKernel, respectively. The shallow +-- interpreter checks above remain independent executable sanity checks of the code. end ComplexityTests.SchonhagePruned diff --git a/ComplexityTests/SchonhagePrunedInput.lean b/ComplexityTests/SchonhagePrunedInput.lean new file mode 100644 index 000000000..0b8448cb1 --- /dev/null +++ b/ComplexityTests/SchonhagePrunedInput.lean @@ -0,0 +1,100 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Width + +/-! Kernel-checked initialized native runs with actual output witnesses and charged halt. -/ + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +namespace ComplexityTests.SchonhagePrunedInput + +-- A negative leaf is computed once and reported at both duplicate positions. The length +-- header, original operands and first outside cell belong to the same actual run witness. +example : ∃ y n, n ≤ timeBound (L := 0) ([0, 0] : List (Fin 1)) ∧ + Runs 10 (program.program output) (State.init (input 0 (fun _ => -3) (fun _ => 2) [0, 0])) + ⟨program.size, y⟩ n ∧ + Executes 10 (program.program output) (State.init (input 0 (fun _ => -3) (fun _ => 2) [0, 0])) + 7 (n + 1) ∧ y.mem 7 = encode (-6) ∧ y.mem 8 = encode (-6) ∧ + y.mem 0 = 6 ∧ y.mem 3 = encode (-3) ∧ y.mem (memoryEnd 0 2) = 0 := by + obtain ⟨_, y, n, hn, hr, he, _, hv, hf⟩ := runs (w := 8) (L := 0) (A := 3) (B := 2) + (by decide) (fun _ => -3) (fun _ => 2) ([0, 0] : List (Fin 1)) + (by intro i; decide) (by intro i; decide) (by decide) (by decide) + refine ⟨y, n, hn, hr, he, ?_, ?_, ?_, ?_, ?_⟩ + · simpa +decide [Cslib.Algorithms.MatrixMultiplication.Schonhage.Pruned.result, + Cslib.Algorithms.MatrixMultiplication.Schonhage.Pruned.run, Finset.piecewise, + requested, outputBase] using hv 0 + · simpa +decide [Cslib.Algorithms.MatrixMultiplication.Schonhage.Pruned.result, + Cslib.Algorithms.MatrixMultiplication.Schonhage.Pruned.run, Finset.piecewise, + requested, outputBase] using hv 1 + · exact (hf 0 (by decide)).trans rfl + · exact (hf 3 (by decide)).trans rfl + · rw [hf _ (by decide)] + rfl + +-- The conservative address admission is explicit and has a checked failing boundary. +example : Fits 10 (addressBound 0 2) ∧ ¬Fits 9 (addressBound 0 2) := by decide +kernel + +-- The input-derived physical threshold is positive and guarantees actual runs at every larger +-- width. It is a conservative sufficient threshold, rather than a claim of necessity. +example : admissionWidth 0 2 3 2 = 13 := by decide +kernel + +example (W : ℕ) (hW : 13 ≤ W) : + InputFits W (input 0 (fun _ => -3) (fun _ => 2) [0, 0]) ∧ + ∃ n, Executes W (program.program output) + (State.init (input 0 (fun _ => -3) (fun _ => 2) [0, 0])) 7 (n + 1) := by + have hadmit : admissionWidth 0 2 3 2 ≤ W := by + change 13 ≤ W + exact hW + obtain ⟨hi, _, n, _, _, he, _, _, _⟩ := runs_wide (W := W) (L := 0) (A := 3) (B := 2) + (fun _ => -3) (fun _ => 2) ([0, 0] : List (Fin 1)) + (by intro i; decide) (by intro i; decide) hadmit + exact ⟨hi, n, he⟩ + +-- An empty query list still executes setup, root deduplication and the real controller exit. +example : ∃ y n, n ≤ timeBound (L := 0) ([] : List (Fin 1)) ∧ + Runs 10 (program.program output) (State.init (input 0 (fun _ => -3) (fun _ => 2) [])) + ⟨program.size, y⟩ n ∧ + Executes 10 (program.program output) (State.init (input 0 (fun _ => -3) (fun _ => 2) [])) + 5 (n + 1) ∧ y.mem 3 = encode (-3) := by + obtain ⟨_, y, n, hn, hr, he, _, _, hf⟩ := runs (w := 8) (L := 0) (A := 3) (B := 2) + (by decide) (fun _ => -3) (fun _ => 2) ([] : List (Fin 1)) + (by intro i; decide) (by intro i; decide) (by decide) (by decide) + exact ⟨y, n, hn, hr, he, (hf 3 (by decide)).trans rfl⟩ + +private def left2 (i : Fin 100) : ℤ := i.val + 1 +private def queries2 : List (Fin 100) := [99, 90, 9, 0, 99] + +-- Both digit orders, shared wildcard leaves and duplicates are observed in a depth-two actual +-- flat run. Computing the small finite specification uses the kernel, not native_decide. +example : ∃ y n, n ≤ timeBound (L := 2) queries2 ∧ + Runs 17 (program.program output) (State.init (input 2 left2 (fun _ => 1) queries2)) + ⟨program.size, y⟩ n ∧ + Executes 17 (program.program output) (State.init (input 2 left2 (fun _ => 1) queries2)) + (outputBase 2 5) (n + 1) ∧ + ∀ i : Fin 5, y.mem (outputBase 2 5 + i.val) = encode (![5050, 460, 55, 1, 5050] i) := by + obtain ⟨_, y, n, hn, hr, he, _, hv, _⟩ := runs (w := 15) (L := 2) (A := 100) (B := 1) + (by decide) left2 (fun _ => 1) queries2 + (by + intro i + have := i.isLt + dsimp only [left2] + rw [abs_of_nonneg (by omega)] + omega) + (by intro i; decide) (by decide) (by decide) + refine ⟨y, n, hn, hr, he, ?_⟩ + intro i + have hvalue : Cslib.Algorithms.MatrixMultiplication.Schonhage.Pruned.result 2 + (fun v => left2 + (Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding.leafIndex v)) + (fun _ => 1) (requested (L := 2) queries2) (queryAt (queries2.get i)) = + ![5050, 460, 55, 1, 5050] i := by + fin_cases i <;> decide +kernel + rw [← hvalue] + exact hv i + +end ComplexityTests.SchonhagePrunedInput diff --git a/ComplexityTests/SchonhagePrunedKernel.lean b/ComplexityTests/SchonhagePrunedKernel.lean new file mode 100644 index 000000000..c54ab0854 --- /dev/null +++ b/ComplexityTests/SchonhagePrunedKernel.lean @@ -0,0 +1,70 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Kernel + +/-! A callable signed native instance with a nonzero epoch and preserved outside memory. -/ + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.PrunedQuery + +namespace ComplexityTests.SchonhagePrunedKernel + +private def caller : Data registers where + regs i := match i.val with + | 9 => 1 | 11 => 3 | 12 => 13 | 13 => 23 | 14 => 1 | 15 => 24 | 16 => 25 | 17 => 7 + | _ => 0 + mem c := if c = 1000 then 91 else if 3 ≤ c ∧ c < 13 then encode (-2) else + if 13 ≤ c ∧ c < 23 then encode 3 else if c = 23 then 9 else 0 + +private def wildcard : Fin 1 → Cslib.Algorithms.MatrixMultiplication.Schonhage.OutStr 1 := + fun _ => ![.z0] + +-- The kernel itself computes powers, layout, all ten contributing children and their stored +-- requests. The returned epoch/directory are ready for the next caller, without a dense clear. +example : ∃ y, ExecWithin 13 kernel caller y + (4 + 29 + traceWeight (Finset.univ.image wildcard) + 41) ∧ + y.Bounded 13 ∧ y.mem 24 = encode (-60) ∧ y.mem 3 = encode (-2) ∧ y.mem 1000 = 91 ∧ + 8 ≤ y.regs epoch ∧ y.regs epoch ≤ 26 ∧ ∀ q < 10, y.mem (73 + q) ≤ y.regs epoch := by + have hbounded : caller.Bounded 13 := by + constructor + · intro i + fin_cases i <;> decide + · intro c + dsimp only [caller] + split_ifs <;> decide + obtain ⟨y, hy, hv, hf, _, hel, heu, htable⟩ := exec_kernel (w := 11) (L := 1) + (f := 25) (a0 := 3) (b0 := 13) (q0 := 23) (o0 := 24) (Q := 1) (E := 7) + (A := 2) (B := 3) (fun _ => -2) (fun _ => 3) + wildcard caller rfl rfl rfl rfl rfl rfl rfl rfl + (by intro i; fin_cases i; decide +kernel) + (by + intro v + have h := (Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding.leafIndex v).isLt + dsimp only [caller] + split_ifs <;> first | rfl | omega) + (by + intro v + have h := (Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Encoding.leafIndex v).isLt + dsimp only [caller] + split_ifs <;> first | rfl | omega) + (by decide) (by decide) (by decide) (by decide) + (by + intro q hq + dsimp only [caller] + split_ifs <;> first | decide | omega) + (by intro v; decide) (by intro v; decide) (by decide) (by decide) (by decide) + refine ⟨y, hy, hy.bounded hbounded, ?_, ?_, ?_, hel, heu, htable⟩ + · have hr := hv (0 : Fin 1) + have hm : Cslib.Algorithms.MatrixMultiplication.Schonhage.Pruned.result 1 + (fun _ => -2) (fun _ => 3) + (Finset.univ.image wildcard) ![.z0] = -60 := by + decide +kernel + exact hr.trans (congrArg encode hm) + · exact (hf 3 (by decide) (by decide)).trans rfl + · exact (hf 1000 (by decide) (by decide)).trans rfl + +end ComplexityTests.SchonhagePrunedKernel diff --git a/NOTICE b/NOTICE index a28ee14dd..302c45086 100644 --- a/NOTICE +++ b/NOTICE @@ -24,3 +24,8 @@ This product is licensed under the Apache License, Version 2.0 (see LICENSE). The prime-window proof also adapts Mathlib/NumberTheory/Bertrand.lean, Copyright (c) 2020 Patrick Stevens; authors Patrick Stevens, Bolton Bailey, under the Apache License, Version 2.0. + +The shared native tensor encoders and explicit-stack pruned-query evaluator are original +implementations against Complexitylib's standard WordRAM APIs. Their finite tensor/pruning +specifications reuse the credited pinned source above; no source machine or instruction-cost +contract supplies the native execution proofs. diff --git a/ROADMAP.md b/ROADMAP.md index 7e50f2a31..b0bce8e7b 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -432,9 +432,16 @@ each statement against the numbered result. Organize modules by subject, not cha The shared native left/right encoders ([#1493]) compute all `10^L` leaf values from `7^L` signed inputs within `240*(L+1)*10^L` standard WordRAM instructions, plus the final halt, with fixed code/registers and concrete initialized inputs, frames and explicit width bounds. - Band/tile preparation, native pruned/sparse-query programs, native Chan–He preprocessing, the reduction - host, the fast Exact Triangle solver and full - input/time/width/asymptotic composition, then the improved Section 4 route to exponent 1.9992; + The native pruned-query evaluator ([#1526]) uses fixed 96-register code to compute its own + distinct root/child queries, stack traversal and signed accumulators. Actual initialized flat + runs preserve query order and duplicates, the input and outside frame. Setup, sparse clearing, + all control/index work and halt fit `8192*(L+Q+1+(L+1)*|leaves(requested queries)|)` instructions; + widths follow from explicit original magnitude/address bounds. A callable pointer/epoch kernel + permits directory reuse across tiles. This does not construct the product's tile query groups + or supply a sparse-product/Exact Triangle solver. Band/tile preparation ([#1523]) is a separate + producer; native Chan–He preprocessing and the reduction host are separate stack components. + Native sparse-product and Exact Triangle solving, full input/time/width/asymptotic composition, + and the improved Section 4 route to exponent 1.9992 remain under #1394; size-based width selection with an input-fit guarantee and explicit overhead; expected-time rules for unbounded almost-surely terminating structured loops; random-tape equivalence and BPP/RP/ZPP simulation diff --git a/UPSTREAM.md b/UPSTREAM.md index 87a5bc5c2..153ca9ea9 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -220,9 +220,21 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he `w+2`. The two extra bits and actual arithmetic-block costs come from the existing native signed kernels, without a copied source machine or a whole-program simulation hypothesis. The existing array-copy interface is retained, with a narrow generalization for disjoint - blocks in either address order or identical blocks. Band/tile preparation, native pruned - and sparse-query programs, native preprocessing/solvers and full input/time/width composition, including the - improved Section 4 route to exponent 1.9992, remain in #1394. + blocks in either address order or identical blocks. + The explicit-stack pruned-query evaluator ([#1526](https://github.com/cslib-community/complexitylib/issues/1526)) + is also original native code, reusing the finite `Pruned.result`, `childSet` and trace/leaf + quantities from `Sec2/Lemma10.lean`. `PrunedQuery.queryIndex` directly applies the existing + encoder `leafIndex` to `privateLeaf`; its executable output dictionary retains digit nine + for the inner wildcard. Fixed 96-register code computes child lists by actual digit tests, + division and timestamp deduplication, scans requested cells only, and charges the full stack, + signed kernels and reporting. Its complete initialized runs and callable pointer/epoch kernel + have explicit source-magnitude, address and outside-frame contracts. The actual bound including + halt is `8192*(L+Q+1+(L+1)*|leaves(requested queries)|)`. The source Light implementation's + semantics and instruction constants are not reused. Query grouping, sparse-product/Exact + Triangle solving and full input/time/width composition, including the improved Section 4 route + to exponent 1.9992, remain in #1394. The independent band producer is #1523; its catalogue + retains the same leaf ordering and canonical signed words. Raw 0/1 matrix producers must + actually double entries before copying into signed input arrays, with that work charged. * **Typed RAM upstream core.** [CSLib #1158](https://github.com/leanprover/cslib/pull/1158) at `beea60a15eddc91d93e92fa5af9fb965c351d0d5` proposes `Cslib/Computability/Machines/RAM/Basic.lean`, in namespace `Cslib.RAM.Typed`. From 06b3563e76d8fc9b1bf47ef4809441112fa4f823 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 20:38:50 -0400 Subject: [PATCH 32/73] feat(wordram): checkpoint computed wanted-query routing Partial implementation of #1561 by codex-three-sum-continuation-t3-3dda8289. Implements actual fixed standard 96-register routing/grouping code with computed valid-mask tables, tile counts, prefix offsets and stable scatter. General checked proofs cover the digit kernel (15L+10), one-query routing (15L+25), exact outStrOfPos/queryIndex mapping, computed header/count/relocated-table initialization (16L+(12L+33)*2^L+83), input-memory frame and original-input-derived fits. Generic finite stable-counting positions are injective and bijective. Targeted Lean MCP diagnostics clean for all nine new library modules and the regression suite. Actual initialized interpreter regressions pass for empty input, zero depth, duplicates and an asymmetric K0=2 three-band case; no encoded leaf array is materialized in routing tests. The 96-register program computes every routing/count/prefix/scatter decision. INCOMPLETE: general native counting, prefix and scatter execution invariants, initialized flat complete-run/width/frame/bound clients, final static hygiene and ALL SIX gates remain pending. This checkpoint is not a PR or acceptance claim. Mutable global audit slot remains with parent; no aggregate performed here. Full #1394 native sparse/ET solver and ordinary 1.9992 endpoint remain open. The child branch merges unchanged #1554 at 12abeee84 onto frozen #1562 base 0c2e0ebea. The local merge 60bec64398 only reconciled shared credit/roadmap/upstream prose; no ancestor branch was edited. Original implementation mathematics/code by Erik Demaine with AI assistance. Existing finite Schonhage and band/encoder/pruned interfaces are reused with their original credit retained. --- .../Schonhage/QueryDigits.lean | 55 +++++ Complexity/Algorithms/Sorting/Counting.lean | 167 +++++++++++++ .../Schonhage/Wanted/Meaning.lean | 127 ++++++++++ .../Schonhage/Wanted/Memory.lean | 123 ++++++++++ .../Schonhage/Wanted/Pack.lean | 222 ++++++++++++++++++ .../Schonhage/Wanted/PackProgram.lean | 83 +++++++ .../Schonhage/Wanted/Program.lean | 189 +++++++++++++++ .../Schonhage/Wanted/Route.lean | 186 +++++++++++++++ .../Schonhage/Wanted/Setup.lean | 167 +++++++++++++ ComplexityTests/SchonhageWanted.lean | 63 +++++ 10 files changed, 1382 insertions(+) create mode 100644 Complexity/Algorithms/MatrixMultiplication/Schonhage/QueryDigits.lean create mode 100644 Complexity/Algorithms/Sorting/Counting.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Meaning.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Memory.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Pack.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/PackProgram.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Program.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Route.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Setup.lean create mode 100644 ComplexityTests/SchonhageWanted.lean diff --git a/Complexity/Algorithms/MatrixMultiplication/Schonhage/QueryDigits.lean b/Complexity/Algorithms/MatrixMultiplication/Schonhage/QueryDigits.lean new file mode 100644 index 000000000..f54c776e6 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Schonhage/QueryDigits.lean @@ -0,0 +1,55 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.MaskTable + +/-! +# Packing a wanted output's digits + +The binary mask selects inner positions. At each remaining position, consume one digit +from each compressed base-three offset and emit their row-major base-ten digit. +All three numberings put the first coordinate in the least significant place. + +## Main definitions + +* `QueryDigits.pack`: the arithmetic recurrence computed by the native query encoder. + +## Main results + +* `QueryDigits.pack_lt`: the packed word occupies at most the original number of digits. + +This recurrence and its elementary bounds are original implementation mathematics. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.MatrixMultiplication.Schonhage.QueryDigits + +/-- Pack a binary level mask and two compressed base-three offsets into output digits. +Unused high bits and high offset digits are ignored after the specified depth. -/ +def pack : ℕ → ℕ → ℕ → ℕ → ℕ + | 0, _, _, _ => 0 + | L + 1, mask, row, col => + if mask % 2 = 0 then + 3 * (row % 3) + col % 3 + 10 * pack L (mask / 2) (row / 3) (col / 3) + else 9 + 10 * pack L (mask / 2) row col + +/-- Packing never needs a supplied bound on a partial accumulator or residual input. -/ +theorem pack_lt (L mask row col : ℕ) : pack L mask row col < 10 ^ L := by + induction L generalizing mask row col with + | zero => simp [pack] + | succ L ih => + have hr := Nat.mod_lt row (by decide : 0 < 3) + have hc := Nat.mod_lt col (by decide : 0 < 3) + have ho := ih (mask / 2) (row / 3) (col / 3) + have hi := ih (mask / 2) row col + simp only [pack, pow_succ] + split <;> omega + +end Cslib.Algorithms.MatrixMultiplication.Schonhage.QueryDigits diff --git a/Complexity/Algorithms/Sorting/Counting.lean b/Complexity/Algorithms/Sorting/Counting.lean new file mode 100644 index 000000000..381ff0bff --- /dev/null +++ b/Complexity/Algorithms/Sorting/Counting.lean @@ -0,0 +1,167 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Data.Finset.Card +public import Mathlib.Data.Finset.Range +public import Mathlib.Data.Fintype.EquivFin + +/-! +# Stable counting-sort positions + +## Main definitions + +* `CountingSort.count`: occurrences of a key in an original input prefix. +* `CountingSort.offset`: occurrences of all smaller keys in the complete input. +* `CountingSort.position`: stable scatter position of an original occurrence. + +## Main results + +* `CountingSort.offset_succ`: the prefix-sum update used by a native counting pass. +* `CountingSort.position_injective`: every original position has a distinct destination. +* `CountingSort.position_bijective`: scatter destinations form a permutation of the input. + +These finite counting identities are original supporting mathematics for the native router. +They assert no sorting program or execution cost. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.CountingSort + +open Finset + +/-- Number of occurrences of bucket `b` among the first `n` original positions. -/ +def count (key : ℕ → ℕ) (n b : ℕ) : ℕ := ((range n).filter fun i => key i = b).card + +/-- Starting position of bucket `b`, after all smaller keys in the complete input. -/ +def offset (key : ℕ → ℕ) (n b : ℕ) : ℕ := ((range n).filter fun i => key i < b).card + +/-- The stable destination of original position `i` in an input of length `n`. -/ +def position (key : ℕ → ℕ) (n i : ℕ) : ℕ := offset key n (key i) + count key i (key i) + +/-- Empty prefixes contain no occurrences. -/ +@[simp] +theorem count_zero (key : ℕ → ℕ) (b : ℕ) : count key 0 b = 0 := by simp [count] + +/-- Processing one original position increments just its bucket. -/ +theorem count_succ (key : ℕ → ℕ) (n b : ℕ) : + count key (n + 1) b = count key n b + if key n = b then 1 else 0 := by + by_cases h : key n = b <;> simp [count, range_add_one, filter_insert, h] + +/-- A prefix contains at most its original number of positions. -/ +theorem count_le (key : ℕ → ℕ) (n b : ℕ) : count key n b ≤ n := + (card_filter_le _ _).trans_eq (card_range n) + +/-- Extending the original prefix cannot remove an occurrence. -/ +theorem count_mono (key : ℕ → ℕ) {n n' : ℕ} (h : n ≤ n') (b : ℕ) : + count key n b ≤ count key n' b := + card_le_card (filter_subset_filter _ (range_mono h)) + +/-- The original occurrence at `i` has not yet been counted in its strict prefix. -/ +theorem count_lt_of_lt (key : ℕ → ℕ) {i n : ℕ} (h : i < n) : + count key i (key i) < count key n (key i) := by + have hs := count_succ key i (key i) + have hm := count_mono key (Nat.succ_le_of_lt h) (key i) + change count key (i + 1) (key i) ≤ count key n (key i) at hm + simp only [↓reduceIte] at hs + omega + +/-- The first bucket starts at zero. -/ +@[simp] +theorem offset_zero (key : ℕ → ℕ) (n : ℕ) : offset key n 0 = 0 := by simp [offset] + +/-- Every prefix offset lies inside the original input length. -/ +theorem offset_le (key : ℕ → ℕ) (n b : ℕ) : offset key n b ≤ n := + (card_filter_le _ _).trans_eq (card_range n) + +/-- Larger keys start no earlier. -/ +theorem offset_mono (key : ℕ → ℕ) (n : ℕ) {b c : ℕ} (h : b ≤ c) : + offset key n b ≤ offset key n c := by + apply card_le_card + intro i hi + obtain ⟨hi, hb⟩ := mem_filter.mp hi + exact mem_filter.mpr ⟨hi, hb.trans_le h⟩ + +/-- One prefix-sum step appends exactly the current bucket's cardinality. -/ +theorem offset_succ (key : ℕ → ℕ) (n b : ℕ) : + offset key n (b + 1) = offset key n b + count key n b := by + have he : ((range n).filter fun i => key i < b + 1) = + ((range n).filter fun i => key i < b) ∪ ((range n).filter fun i => key i = b) := by + ext i + simp only [mem_filter, mem_union, mem_range] + omega + unfold offset count + rw [he, card_union_of_disjoint] + exact disjoint_left.mpr (by + intro i hi hj + have := (mem_filter.mp hi).2 + have := (mem_filter.mp hj).2 + omega) + +/-- Once every admitted key is below `B`, its terminal offset is exactly the input length. -/ +theorem offset_full (key : ℕ → ℕ) (n B : ℕ) (h : ∀ i < n, key i < B) : + offset key n B = n := by + unfold offset + rw [filter_eq_self.mpr, card_range] + intro i hi + exact h i (mem_range.mp hi) + +/-- Each occurrence lands before the next bucket, and after its own bucket's start. -/ +theorem position_mem (key : ℕ → ℕ) {n i : ℕ} (hi : i < n) : + offset key n (key i) ≤ position key n i ∧ + position key n i < offset key n (key i + 1) := by + have := count_lt_of_lt key hi + rw [offset_succ] + exact ⟨Nat.le_add_right _ _, Nat.add_lt_add_left ‹_› _⟩ + +/-- Stable scatter destinations are valid original-length indices. -/ +theorem position_lt (key : ℕ → ℕ) {n i : ℕ} (hi : i < n) : position key n i < n := + (position_mem key hi).2.trans_le (offset_le key n (key i + 1)) + +/-- Original order is retained inside a bucket. -/ +theorem position_lt_same (key : ℕ → ℕ) (n : ℕ) {i j : ℕ} (hij : i < j) + (hkey : key i = key j) : position key n i < position key n j := by + have := count_lt_of_lt key hij + simp only [position, hkey] at * + omega + +/-- Every position in a smaller bucket precedes every position in a larger bucket. -/ +theorem position_lt_key (key : ℕ → ℕ) {n i j : ℕ} (hi : i < n) + (hkey : key i < key j) : position key n i < position key n j := + ((position_mem key hi).2.trans_le + (offset_mono key n (Nat.succ_le_of_lt hkey))).trans_le (Nat.le_add_right _ _) + +/-- Distinct original positions never overwrite one another during stable scatter. -/ +theorem position_injective (key : ℕ → ℕ) (n : ℕ) : + Function.Injective (fun i : Fin n => position key n i.val) := by + intro i j he + change position key n i.val = position key n j.val at he + rcases lt_trichotomy (key i) (key j) with hk | hk | hk + · have := position_lt_key key i.isLt hk + omega + · rcases lt_trichotomy i.val j.val with hij | hij | hij + · have := position_lt_same key n hij hk + omega + · exact Fin.ext hij + · have := position_lt_same key n hij hk.symm + omega + · have := position_lt_key key j.isLt hk + omega + +/-- All original occurrences, including equal keys, are a permutation of the output cells. -/ +theorem position_bijective (key : ℕ → ℕ) (n : ℕ) : + Function.Bijective (fun i : Fin n => + (⟨position key n i.val, position_lt key i.isLt⟩ : Fin n)) := by + have hi : Function.Injective (fun i : Fin n => + (⟨position key n i.val, position_lt key i.isLt⟩ : Fin n)) := + fun i j h => position_injective key n (congrArg (fun j : Fin n => j.val) h) + exact ⟨hi, Finite.surjective_of_injective hi⟩ + +end Cslib.Algorithms.CountingSort diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Meaning.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Meaning.lean new file mode 100644 index 000000000..af2e555cf --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Meaning.lean @@ -0,0 +1,127 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Pack +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Codes +public import Complexity.Foundations.Data.Nat.OfDigits +public import Complexity.Foundations.Data.Finset.OrderedFilter + +/-! +# Wanted query codes and the existing output strings + +## Main results + +* `Wanted.pack_output_list`: packing the inner mask and compressed outer digits recovers + the existing pruned evaluator's output numbering, including repeated zero digits. +* `Wanted.pack_outStrOfPos`: the computed mask and the ordinary row/column offsets give + exactly the original thin-product query, without a caller-supplied output string. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open QueryDigits PrunedQuery + +/-- The pruned evaluator uses row-major digits for ordinary outer outputs. -/ +theorem outputDigits_outer (i j : Fin 3) : + (outputDigits.symm (.z i j)).val = 3 * i.val + j.val := by decide +revert + +/-- Its inner output is the existing private term numbered nine. -/ +theorem outputDigits_inner : (outputDigits.symm .z0).val = 9 := by decide + +/-- The arithmetic recurrence reconstructs every output list from its inner flags and +the two outer-coordinate lists. It does not need a supplied cardinality certificate. -/ +theorem pack_output_list (zs : List OutVar) : + pack zs.length (Nat.ofDigits 2 (zs.map fun z => if z.IsInner then 1 else 0)) + (Nat.ofDigits 3 ((zs.filter fun z => ¬z.IsInner).map fun z => z.rowIndex.val)) + (Nat.ofDigits 3 ((zs.filter fun z => ¬z.IsInner).map fun z => z.colIndex.val)) = + Nat.ofDigits 10 (zs.map fun z => (outputDigits.symm z).val) := by + induction zs with + | nil => simp [pack] + | cons z zs ih => + cases z with + | z i j => + have hi : i.val < 3 := i.isLt + have hj : j.val < 3 := j.isLt + simpa [pack, OutVar.IsInner, OutVar.rowIndex, OutVar.colIndex, Nat.ofDigits, + Nat.add_mul_mod_self_left, Nat.add_mul_div_left, Nat.mod_eq_of_lt hi, + Nat.div_eq_of_lt hi, Nat.mod_eq_of_lt hj, Nat.div_eq_of_lt hj, + outputDigits_outer] using congrArg (fun t => 3 * i.val + j.val + 10 * t) ih + | z0 => + simpa [pack, OutVar.IsInner, Nat.ofDigits, Nat.add_mul_mod_self_left, + Nat.add_mul_div_left, outputDigits_inner] using ih + +private theorem outer_list {L m : ℕ} (w : OutStr L) (h : (innerSetO w).card = m) + (f : OutVar → Fin 3) : + ((List.ofFn w).filter fun z => ¬z.IsInner).map (fun z => (f z).val) = + List.ofFn (fun k => (f (w ((innerSetO w)ᶜ.orderEmbOfFin + (Finset.card_compl_of_card_eq _ h) k))).val) := by + rw [Finset.ofFn_orderEmbOfFin (innerSetO w)ᶜ + (Finset.card_compl_of_card_eq _ h) (fun i => (f (w i)).val), + Finset.sort_eq_filter_finRange, + List.ofFn_eq_map, List.filter_map, List.map_map] + have hp : (fun i : Fin L => decide (¬(w i).IsInner)) = + (fun i => decide (i ∈ (innerSetO w)ᶜ)) := by + funext i + simp [innerSetO] + simp only [Function.comp_def, hp] + +/-- A packed mask and the ordered outer coordinates recover every original output code. -/ +theorem pack_queryIndex {L m : ℕ} (w : OutStr L) (h : (innerSetO w).card = m) : + pack L (MaskTable.maskCode (innerSetO w)).val + (finFunctionFinEquiv (rowO w h)).val (finFunctionFinEquiv (colO w h)).val = + (queryIndex w).val := by + have hm : Nat.ofDigits 2 ((List.ofFn w).map fun z => if z.IsInner then 1 else 0) = + (MaskTable.maskCode (innerSetO w)).val := by + rw [List.map_ofFn] + calc + _ = Nat.ofDigits 2 (List.ofFn fun i => + ((if i ∈ innerSetO w then 1 else 0) : Fin 2).val) := by + congr 2 + funext i + by_cases hi : (w i).IsInner <;> simp [innerSetO, hi] + _ = _ := Nat.ofDigits_finFunctionFinEquiv _ + have hr : Nat.ofDigits 3 (((List.ofFn w).filter fun z => ¬z.IsInner).map + fun z => z.rowIndex.val) = (finFunctionFinEquiv (rowO w h)).val := by + rw [outer_list w h OutVar.rowIndex, Nat.ofDigits_finFunctionFinEquiv] + rfl + have hc : Nat.ofDigits 3 (((List.ofFn w).filter fun z => ¬z.IsInner).map + fun z => z.colIndex.val) = (finFunctionFinEquiv (colO w h)).val := by + rw [outer_list w h OutVar.colIndex, Nat.ofDigits_finFunctionFinEquiv] + rfl + have ho : Nat.ofDigits 10 ((List.ofFn w).map fun z => (outputDigits.symm z).val) = + (queryIndex w).val := by + rw [List.map_ofFn] + exact Nat.ofDigits_finFunctionFinEquiv (fun i => outputDigits.symm (w i)) + simpa only [List.length_ofFn, hm, hr, hc, ho] using pack_output_list (List.ofFn w) + +/-- The actual deterministic table mask and the original offsets compute precisely +`outStrOfPos`, in the unchanged pruned evaluator's least-significant-first numbering. -/ +theorem pack_outStrOfPos {L m : ℕ} (hm : m ≤ L) (I J : ℕ) : + pack L (MaskTable.selectedMask + (blockOf (MaskTable.layout hm) I, blockOf (MaskTable.layout hm) J)).val + (I % N0 L m) (J % N0 L m) = + (queryIndex (outStrOfPos (MaskTable.layout hm) I J)).val := by + let lay := MaskTable.layout hm + let Q := lay.table (blockOf lay I, blockOf lay J) + have hQ : Q.card = m := lay.table_card _ + have h := pack_queryIndex (outStrOfPos lay I J) + (by simpa only [outStrOfPos, innerSetO_outStrOf] using hQ) + unfold outStrOfPos at h + rw [rowO_outStrOf, colO_outStrOf, innerSetO_outStrOf] at h + change pack L (MaskTable.maskCode (MaskTable.maskSet + (MaskTable.selectedMask (blockOf lay I, blockOf lay J)))).val + (finFunctionFinEquiv (finFunctionFinEquiv.symm (offsetOf L m I))).val + (finFunctionFinEquiv (finFunctionFinEquiv.symm (offsetOf L m J))).val = _ at h + simpa only [MaskTable.maskCode_maskSet, Equiv.apply_symm_apply, offsetOf, N0, + outStrOfPos, lay] using h + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Memory.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Memory.lean new file mode 100644 index 000000000..1154f251c --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Memory.lean @@ -0,0 +1,123 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Route +public import Complexity.Algorithms.Sorting.Counting + +/-! +# Concrete memory layout of native wanted-query grouping + +## Main definitions + +* `Wanted.tableAddress`: computed selected-mask table after the readonly query input. +* `Wanted.Parameters`: scalar register equalities used by all three grouping passes. +* `Wanted.wordBudget`: an original-input-derived sufficient bound for unsigned words. + +All layout definitions are numeric specifications of actual address instructions. Neither +the layout nor the parameter record contains a run, time bound or assumed solver. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The original length word, four headers and two words per wanted position. -/ +def inputLimit (Q : ℕ) : ℕ := Q * 2 + 5 + +/-- The forward table follows a complete `2^L`-word inverse table. -/ +def tableAddress (L Q : ℕ) : ℕ := inputLimit Q + 2 ^ L + +/-- Tile counts follow both computed tables. -/ +def countAddress (L Q : ℕ) : ℕ := tableAddress L Q + 2 ^ L + +/-- One immutable prefix offset per tile, followed by one terminal offset. -/ +def offsetAddress (L m N Q : ℕ) : ℕ := countAddress L Q + numBands L m N ^ 2 + +/-- Mutable stable-scatter cursors follow the immutable prefix offsets. -/ +def cursorAddressBase (L m N Q : ℕ) : ℕ := offsetAddress L m N Q + numBands L m N ^ 2 + 1 + +/-- One actual query code per original occurrence. -/ +def codeAddress (L m N Q : ℕ) : ℕ := cursorAddressBase L m N Q + numBands L m N ^ 2 + +/-- Every code retains its original input position in the adjacent array. -/ +def originalAddress (L m N Q : ℕ) : ℕ := codeAddress L m N Q + Q + +/-- One past both grouped output arrays. -/ +def memoryLimit (L m N Q : ℕ) : ℕ := originalAddress L m N Q + Q + +/-- This sufficient budget covers the reused parameter kernels as well as all routing +and grouping arithmetic. It depends only on the original four scalar parameters. -/ +def wordBudget (L m N Q : ℕ) : ℕ := + 256 * (BandPreparation.wordBudget L m N + routeBudget L m N (tableAddress L Q) + + memoryLimit L m N Q + L + N + Q + numBands L m N ^ 2 + 10 ^ L + 2 ^ L + 16) + +/-- Persistent scalar register values after actual table relocation and allocation. -/ +structure Parameters (L m N Q : ℕ) (x : Data 96) : Prop where + /-- The reused table kernel's actual scalar contents. -/ + tables : BandPreparation.TableParameters L m (inputLimit Q) (tableAddress L Q) x + /-- The one-query router's actual scalar contents. -/ + route : RouteParameters L m N (tableAddress L Q) x + /-- Original query input starts after its four scalar headers. -/ + queries : x.regs queries = 5 + /-- Number of positions, retaining repetitions. -/ + queryCount : x.regs queryCount = Q + /-- Allocated tile counts. -/ + counts : x.regs counts = countAddress L Q + /-- Immutable prefix offsets. -/ + offsets : x.regs offsets = offsetAddress L m N Q + /-- Mutable cursor addresses. -/ + cursors : x.regs cursors = cursorAddressBase L m N Q + /-- Grouped query codes. -/ + codes : x.regs codes = codeAddress L m N Q + /-- Original-position permutation. -/ + originals : x.regs originals = originalAddress L m N Q + /-- Actual tile count. -/ + tileCount : x.regs tileCount = numBands L m N ^ 2 + /-- The entire readonly original prefix. -/ + inputEnd : x.regs inputEnd = inputLimit Q + /-- One past the allocated arrays. -/ + memoryEnd : x.regs memoryEnd = memoryLimit L m N Q + +/-- The exact scalar register footprint shared by the grouping passes. -/ +def parameterRegisters : Finset (Fin 96) := + {queries, queryCount, counts, offsets, cursors, codes, originals, tileCount, + bandWidth, inputEnd, memoryEnd, BandPreparation.levels, BandPreparation.inner, + BandPreparation.outerSize, BandPreparation.masks, BandPreparation.inverse, + BandPreparation.table, BandPreparation.root, BandPreparation.square, + BandPreparation.bands, BandPreparation.zero, BandPreparation.one, BandPreparation.two} + +/-- Persistent scalar contracts survive preservation of their concrete registers. -/ +theorem Parameters.reframe {L m N Q : ℕ} {x y : Data 96} (hx : Parameters L m N Q x) + (h : ∀ i ∈ parameterRegisters, y.regs i = x.regs i) : Parameters L m N Q y := by + refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · exact ⟨(h _ (by decide)).trans hx.tables.levels, + (h _ (by decide)).trans hx.tables.inner, (h _ (by decide)).trans hx.tables.masks, + (h _ (by decide)).trans hx.tables.inverse, (h _ (by decide)).trans hx.tables.table, + (h _ (by decide)).trans hx.tables.square, (h _ (by decide)).trans hx.tables.zero, + (h _ (by decide)).trans hx.tables.one, (h _ (by decide)).trans hx.tables.two⟩ + · exact ⟨(h _ (by decide)).trans hx.route.levels, + (h _ (by decide)).trans hx.route.outerSize, (h _ (by decide)).trans hx.route.root, + (h _ (by decide)).trans hx.route.bandWidth, (h _ (by decide)).trans hx.route.bands, + (h _ (by decide)).trans hx.route.table⟩ + all_goals first + | exact (h _ (by decide)).trans hx.queries + | exact (h _ (by decide)).trans hx.queryCount + | exact (h _ (by decide)).trans hx.counts + | exact (h _ (by decide)).trans hx.offsets + | exact (h _ (by decide)).trans hx.cursors + | exact (h _ (by decide)).trans hx.codes + | exact (h _ (by decide)).trans hx.originals + | exact (h _ (by decide)).trans hx.tileCount + | exact (h _ (by decide)).trans hx.inputEnd + | exact (h _ (by decide)).trans hx.memoryEnd + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Pack.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Pack.lean new file mode 100644 index 000000000..50c2f5a70 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Pack.lean @@ -0,0 +1,222 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.PackProgram +import Mathlib.Tactic.Linarith + +/-! +# Execution of the native wanted-output digit loop + +## Main results + +* `Wanted.exec_pack`: actual unsigned packing with an explicit linear instruction bound + and unchanged memory, for arbitrary initial scratch-register contents. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage.QueryDigits + +/-- Scalar contents retained throughout packing. These are only register equalities. -/ +structure PackParameters (L : ℕ) (x : Data 16) : Prop where + /-- Original depth. -/ + depth : x.regs depth = L + /-- Initialized unit. -/ + one : x.regs one = 1 + /-- Binary radix. -/ + two : x.regs two = 2 + /-- Compressed offset radix. -/ + three : x.regs three = 3 + /-- Output radix. -/ + ten : x.regs ten = 10 + /-- Inner output digit. -/ + nine : x.regs nine = 9 + +/-- A consumed prefix and the remaining arithmetic recurrence give the original output. -/ +structure PackProgress (L M R C : ℕ) (x : Data 16) : Prop where + /-- Persistent constants and depth. -/ + fixed : PackParameters L x + /-- A coordinate is visited at most once. -/ + level_le : x.regs level ≤ L + /-- Binary division decreases the mask. -/ + mask_le : x.regs mask ≤ M + /-- Ternary division decreases the row offset. -/ + row_le : x.regs row ≤ R + /-- Ternary division decreases the column offset. -/ + col_le : x.regs col ≤ C + /-- Actual next output place. -/ + power_eq : x.regs power = 10 ^ x.regs level + /-- Already emitted digits fit below the next place. -/ + code_lt : x.regs code < x.regs power + /-- Prefix and residual recurrence have the desired complete value. -/ + value : x.regs code + x.regs power * + pack (L - x.regs level) (x.regs mask) (x.regs row) (x.regs col) = pack L M R C + +private theorem parameters_after_body {L : ℕ} {x y : Data 16} + (hx : PackParameters L x) {w c : ℕ} (he : ExecWithin w packBody x y c) : + PackParameters L y := by + have hf (i : Fin 16) (hi : i ∈ ({depth, one, two, three, ten, nine} : Finset _)) : + y.regs i = x.regs i := by + apply he.regs_eq + have h : ∀ i ∈ ({depth, one, two, three, ten, nine} : Finset (Fin 16)), + packBody.Preserves i := by + simp only [packBody, Code.Preserves, Op.dest, Op.bin] + decide + exact h i hi + exact ⟨(hf _ (by decide)).trans hx.depth, (hf _ (by decide)).trans hx.one, + (hf _ (by decide)).trans hx.two, (hf _ (by decide)).trans hx.three, + (hf _ (by decide)).trans hx.ten, (hf _ (by decide)).trans hx.nine⟩ + +/-- One coordinate's actual test, arithmetic and register updates. The numeric budget is +derived from original inputs, rather than an assumed bound on intermediate states. -/ +theorem exec_packBody {w L M R C : ℕ} (x : Data 16) (hx : PackProgress L M R C x) + (hc : (Cond.lt level depth).Holds x) + (hfit : Fits w (M + R + C + L + 32 * 10 ^ L + 32)) : + ∃ y, ExecWithin w packBody x y 13 ∧ PackProgress L M R C y ∧ y.mem = x.mem ∧ + L - y.regs level < L - x.regs level := by + have hj : x.regs level < L := by simpa only [Cond.holds_lt, hx.fixed.depth] using hc + have hr := Nat.mod_lt (x.regs row) (by decide : 0 < 3) + have hcol := Nat.mod_lt (x.regs col) (by decide : 0 < 3) + have hp : x.regs power ≤ 10 ^ L := by + rw [hx.power_eq] + exact Nat.pow_le_pow_right (by decide) hx.level_le + have hmfit : Fits w (x.regs mask / 2) := hfit.of_le + ((Nat.div_le_self _ _).trans (by have := hx.mask_le; omega)) + have hbit : Fits w (x.regs mask % 2) := hfit.of_le (by + have := Nat.mod_lt (x.regs mask) (by decide : 0 < 2) + omega) + have hpfit : Fits w (x.regs power * 10) := hfit.of_le (by omega) + have hjfit : Fits w (x.regs level + 1) := hfit.of_le (by omega) + have hrest : L - x.regs level = L - (x.regs level + 1) + 1 := by omega + by_cases hb : x.regs mask % 2 = 0 + · let d := (x.regs row % 3) * 3 + x.regs col % 3 + have hd : d < 9 := by dsimp [d]; omega + have hrd : Fits w (x.regs row % 3) := hfit.of_le (by omega) + have hr3 : Fits w (x.regs row % 3 * 3) := hfit.of_le (by omega) + have hcd : Fits w (x.regs col % 3) := hfit.of_le (by omega) + have hdf : Fits w d := hfit.of_le (by omega) + have hrq : Fits w (x.regs row / 3) := hfit.of_le + ((Nat.div_le_self _ _).trans (by have := hx.row_le; omega)) + have hcq : Fits w (x.regs col / 3) := hfit.of_le + ((Nat.div_le_self _ _).trans (by have := hx.col_le; omega)) + have hmul : Fits w (d * x.regs power) := hfit.of_le (by nlinarith) + have hadd : Fits w (x.regs code + d * x.regs power) := hfit.of_le (by + have := hx.code_lt + nlinarith) + let y := ((((((((((((x.set bit 0).set mask (x.regs mask / 2)).set digit + (x.regs row % 3)).set digit (x.regs row % 3 * 3)).set temporary + (x.regs col % 3)).set digit d).set row (x.regs row / 3)).set + col (x.regs col / 3)).set temporary (d * x.regs power)).set code + (x.regs code + d * x.regs power)).set power (x.regs power * 10)).set + level (x.regs level + 1)) + have he : ExecWithin w packBody x y 13 := by + ram_eval [packBody, y, d, hx.fixed.two, hx.fixed.three, hx.fixed.one, + hx.fixed.ten, hb, hmfit, hbit, hpfit, hjfit, hrd, hr3, hcd, hdf, + hrq, hcq, hmul, hadd, Nat.lt_one_iff, + mask, bit, row, col, digit, temporary, code, power, level, two, three, one, ten, + Fin.mk.injEq] + refine ⟨y, he, ⟨parameters_after_body hx.fixed he, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩, + rfl, ?_⟩ + · simp only [y, Data.set_regs, Function.update_self]; omega + · simpa [y, mask, bit, digit, row, col, temporary, code, power, level] using + (Nat.div_le_self (x.regs mask) 2).trans hx.mask_le + · simpa [y, mask, bit, digit, row, col, temporary, code, power, level] using + (Nat.div_le_self (x.regs row) 3).trans hx.row_le + · simpa [y, mask, bit, digit, row, col, temporary, code, power, level] using + (Nat.div_le_self (x.regs col) 3).trans hx.col_le + · simp [y, power, level, hx.power_eq, pow_succ] + · have := hx.code_lt + simp only [y, Data.set_regs, Function.update_self, code, power, level, + Function.update_of_ne (by decide : (4 : Fin 16) ≠ 5), + Function.update_of_ne (by decide : (4 : Fin 16) ≠ 6), + Function.update_of_ne (by decide : (6 : Fin 16) ≠ 5)] + nlinarith + · have hv := hx.value + rw [hrest, pack, ite_eq_left hb] at hv + simp only [y, Data.set_regs, Function.update_apply] + simp only [mask, row, col, code, power, level, digit, temporary, + Fin.isValue, ↓reduceIte] + dsimp [d] at * + nlinarith only [hv] + · change L - (x.regs level + 1) < L - x.regs level + omega + · have h9 : Fits w 9 := hfit.of_le (by omega) + have hmul : Fits w (9 * x.regs power) := hfit.of_le (by omega) + have hadd : Fits w (x.regs code + 9 * x.regs power) := hfit.of_le (by + have := hx.code_lt + omega) + let y := (((((((x.set bit (x.regs mask % 2)).set mask (x.regs mask / 2)).set + digit 9).set temporary (9 * x.regs power)).set code + (x.regs code + 9 * x.regs power)).set power (x.regs power * 10)).set + level (x.regs level + 1)) + have he : ExecWithin w packBody x y 13 := by + ram_eval [packBody, y, hx.fixed.two, hx.fixed.nine, hx.fixed.one, hx.fixed.ten, + hb, hmfit, hbit, hpfit, hjfit, h9, hmul, hadd, Nat.lt_one_iff, + mask, bit, row, col, digit, temporary, code, power, level, two, three, one, ten, nine, + Fin.mk.injEq] + refine ⟨y, he, ⟨parameters_after_body hx.fixed he, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩, + rfl, ?_⟩ + · simp only [y, Data.set_regs, Function.update_self]; omega + · simpa [y, mask, bit, digit, row, col, temporary, code, power, level] using + (Nat.div_le_self (x.regs mask) 2).trans hx.mask_le + · simpa [y, mask, bit, digit, row, col, temporary, code, power, level] using hx.row_le + · simpa [y, mask, bit, digit, row, col, temporary, code, power, level] using hx.col_le + · simp [y, power, level, hx.power_eq, pow_succ] + · have := hx.code_lt + change x.regs code + 9 * x.regs power < x.regs power * 10 + omega + · have hv := hx.value + rw [hrest, pack, ite_eq_right hb] at hv + simp [y, mask, row, col, code, power, level, bit, digit, temporary] + nlinarith only [hv] + · change L - (x.regs level + 1) < L - x.regs level + omega + +/-- Execute the entire fixed packer from ordinary register inputs. Every branch, division, +remainder, index update and loop control is charged; memory and the depth are preserved. -/ +theorem exec_pack {w : ℕ} (L M R C : ℕ) (x : Data 16) + (hL : x.regs depth = L) (hM : x.regs mask = M) (hR : x.regs row = R) + (hC : x.regs col = C) (hfit : Fits w (M + R + C + L + 32 * 10 ^ L + 32)) : + ∃ y, ExecWithin w packCode x y (15 * L + 10) ∧ + y.regs code = pack L M R C ∧ y.regs depth = L ∧ y.mem = x.mem := by + have hf (v : ℕ) (hv : v ≤ 10) : Fits w v := hfit.of_le (by omega) + let start := ((((((((x.set one 1).set two 2).set three 3).set ten 10).set nine 9).set + code 0).set level 0).set power 1) + have hs : ExecWithin w packHead x start 8 := by + ram_eval [packHead, start, hf 0 (by decide), hf 1 (by decide), hf 2 (by decide), + hf 3 (by decide), hf 9 (by decide), hf 10 (by decide), + one, two, three, ten, nine, code, level, power, Fin.mk.injEq] + have hi : PackProgress L M R C start := by + refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · constructor <;> simp [start, depth, one, two, three, ten, nine, code, level, power, hL] + all_goals simp [start, one, two, three, ten, nine, code, level, power, + mask, row, col, hM, hR, hC] + let I (z : Data 16) : Prop := PackProgress L M R C z ∧ z.mem = x.mem + have hb (z : Data 16) (hz : I z) (hc : (Cond.lt level depth).Holds z) : + ∃ u, ExecWithin w packBody z u 13 ∧ I u ∧ + L - u.regs level < L - z.regs level := by + obtain ⟨u, he, hu, hm, ht⟩ := exec_packBody z hz.1 hc hfit + exact ⟨u, he, ⟨hu, hm.trans hz.2⟩, ht⟩ + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I + (fun z => L - z.regs level) hb ⟨hi, rfl⟩ + have hend : y.regs level = L := by + simp only [Cond.holds_lt, hy.1.fixed.depth] at hc + have := hy.1.level_le + omega + refine ⟨y, ?_, ?_, hy.1.fixed.depth, hy.2⟩ + · apply (hs.seq he).mono + have hstart : start.regs level = 0 := by simp [start, level, power] + rw [hstart, Nat.sub_zero] + omega + · simpa only [hend, Nat.sub_self, pack, Nat.mul_zero, Nat.add_zero] using hy.1.value + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/PackProgram.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/PackProgram.lean new file mode 100644 index 000000000..bc4010845 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/PackProgram.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Notation +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.QueryDigits + +/-! +# Native output-digit packing + +## Main definitions + +* `Wanted.packCode`: fixed unsigned code for a binary mask and compressed row/column offsets. + +The mask and both offsets are consumed in registers. No query string or dispatch table is +provided to this code, and no memory is read or written. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM + +/-- Register containing the original depth. -/ +abbrev depth : Fin 16 := 0 +/-- Remaining binary subset mask. -/ +abbrev mask : Fin 16 := 1 +/-- Remaining compressed row offset. -/ +abbrev row : Fin 16 := 2 +/-- Remaining compressed column offset. -/ +abbrev col : Fin 16 := 3 +/-- Accumulated output code. -/ +abbrev code : Fin 16 := 4 +/-- Number of consumed coordinates. -/ +abbrev level : Fin 16 := 5 +/-- Next base-ten place. -/ +abbrev power : Fin 16 := 6 +/-- Current binary mask digit. -/ +abbrev bit : Fin 16 := 7 +/-- Current output digit. -/ +abbrev digit : Fin 16 := 8 +/-- Arithmetic scratch register. -/ +abbrev temporary : Fin 16 := 9 +/-- Constant one. -/ +abbrev one : Fin 16 := 10 +/-- Constant two. -/ +abbrev two : Fin 16 := 11 +/-- Constant three. -/ +abbrev three : Fin 16 := 12 +/-- Constant ten. -/ +abbrev ten : Fin 16 := 13 +/-- Constant nine. -/ +abbrev nine : Fin 16 := 14 + +/-- Initialize loop counters and ordinary unsigned constants. -/ +def packHead : Code .standard 16 := ram[ + one ← #1; two ← #2; three ← #3; ten ← #10; nine ← #9; + code ← #0; level ← #0; power ← #1 +] + +/-- One actual digit decision, with row-major outer digits and inner digit nine. -/ +def packBody : Code .standard 16 := ram[ + bit ← mask % two; mask ← mask / two; + if bit < one then [ + digit ← row % three; digit ← digit * three; + temporary ← col % three; digit ← digit + temporary; + row ← row / three; col ← col / three + ] else [ digit ← nine ]; + temporary ← digit * power; code ← code + temporary; + power ← power * ten; level ← level + one +] + +/-- A single finite program handles every runtime depth and both compressed offsets. -/ +def packCode : Code .standard 16 := + .seq packHead (.loop (.lt level depth) packBody) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Program.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Program.lean new file mode 100644 index 000000000..cf90403ba --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Program.lean @@ -0,0 +1,189 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.PackProgram +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandTables +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandParameters + +/-! +# Native routing and stable grouping of wanted positions + +The initialized input contains only `L,m,N,Q` and `Q` ordinary row/column pairs. The code +computes the valid-mask tables, band parameters, tile counts, prefix offsets and scatter +positions. Each scatter writes the unchanged pruned-query code and its original position. +No sorted request list, layout certificate or dispatch decision is supplied as input. + +## Main definitions + +* `Wanted.routeCode`: compute one tile and one query code using the computed table. +* `Wanted.program`: computed tables followed by counting, prefix and stable scatter passes. + +These programs use only the existing standard unsigned instructions. Correctness, width, +frame and resource theorems are separate from the definitions. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Register embedding for the unchanged sixteen-register digit packer. -/ +def packRegisters (i : Fin 16) : Fin 96 := ⟨i.val, by omega⟩ + +/-- Original row of the current query. -/ +abbrev queryRow : Fin 96 := 16 +/-- Original column of the current query. -/ +abbrev queryCol : Fin 96 := 17 +/-- Computed row band. -/ +abbrev rowBand : Fin 96 := 18 +/-- Computed column band. -/ +abbrev colBand : Fin 96 := 19 +/-- Row-major tile key. -/ +abbrev tile : Fin 96 := 20 +/-- Address of the first original row/column pair. -/ +abbrev queries : Fin 96 := 21 +/-- Number of original query positions, including duplicates. -/ +abbrev queryCount : Fin 96 := 22 +/-- Current original query position or tile position. -/ +abbrev index : Fin 96 := 23 +/-- Tile-count array base. -/ +abbrev counts : Fin 96 := 24 +/-- Prefix-offset array base, with one additional terminal cell. -/ +abbrev offsets : Fin 96 := 25 +/-- Current stable-scatter cursor array base. -/ +abbrev cursors : Fin 96 := 26 +/-- Grouped query-code array base. -/ +abbrev codes : Fin 96 := 27 +/-- Grouped original-position array base. -/ +abbrev originals : Fin 96 := 28 +/-- Current tile's query count. -/ +abbrev count : Fin 96 := 29 +/-- Running prefix sum. -/ +abbrev total : Fin 96 := 30 +/-- Number of tiles. -/ +abbrev tileCount : Fin 96 := 31 +/-- Number of original positions in one band. -/ +abbrev bandWidth : Fin 96 := 85 +/-- One past the readonly initialized query input. -/ +abbrev inputEnd : Fin 96 := 86 +/-- One past the grouping arrays. -/ +abbrev memoryEnd : Fin 96 := 87 +/-- Address scratch. -/ +abbrev pointer : Fin 96 := 88 +/-- Saved address of the current scatter cursor. -/ +abbrev cursorAddress : Fin 96 := 89 +/-- Current grouped output position. -/ +abbrev position : Fin 96 := 90 + +/-- Compute just the row-major tile key. No memory is touched. -/ +def tileCode : Code .standard 96 := ram[ + rowBand ← queryRow / bandWidth; colBand ← queryCol / bandWidth; + tile ← rowBand * BandPreparation.bands; tile ← tile + colBand +] + +/-- Compute the selected subset rank and load its actual mask from the computed table. -/ +def routeHead : Code .standard 96 := ram[ + ⟪tileCode⟫; + ⟪.op (.copy (packRegisters depth) BandPreparation.levels)⟫; + ⟪.op (.bin .mod (packRegisters row) queryRow BandPreparation.outerSize)⟫; + ⟪.op (.bin .mod (packRegisters col) queryCol BandPreparation.outerSize)⟫; + ⟪.op (.bin .div (packRegisters mask) queryRow BandPreparation.outerSize)⟫; + ⟪.op (.bin .mod (packRegisters mask) (packRegisters mask) BandPreparation.root)⟫; + ⟪.op (.bin .mul (packRegisters mask) (packRegisters mask) BandPreparation.root)⟫; + ⟪.op (.bin .div (packRegisters temporary) queryCol BandPreparation.outerSize)⟫; + ⟪.op (.bin .mod (packRegisters temporary) (packRegisters temporary) BandPreparation.root)⟫; + ⟪.op (.bin .add (packRegisters mask) (packRegisters mask) (packRegisters temporary))⟫; + ⟪.op (.bin .add (packRegisters temporary) BandPreparation.table (packRegisters mask))⟫; + ⟪.op (.load (packRegisters mask) (packRegisters temporary))⟫ +] + +/-- Compute a tile key and its output code, with actual digit decisions and table load. -/ +def routeCode : Code .standard 96 := .seq routeHead (packCode.rename packRegisters) + +/-- Relocate the computed tables and every grouping array after the initialized input. -/ +def setup : Code .standard 96 := ram[ + pointer ← #4; queryCount ← *pointer; queries ← #5; + inputEnd ← queryCount * BandPreparation.two; inputEnd ← inputEnd + queries; + BandPreparation.inverse ← inputEnd; + BandPreparation.table ← BandPreparation.inverse + BandPreparation.masks; + counts ← BandPreparation.table + BandPreparation.masks; + bandWidth ← BandPreparation.root * BandPreparation.outerSize; + tileCount ← BandPreparation.bands * BandPreparation.bands; + offsets ← counts + tileCount; cursors ← offsets + tileCount; + cursors ← cursors + BandPreparation.one; codes ← cursors + tileCount; + originals ← codes + queryCount; memoryEnd ← originals + queryCount +] + +/-- Zero each tile count once. -/ +def clearCounts : Code .standard 96 := ram[ + index ← #0; + while index < tileCount do [ + pointer ← counts + index; *pointer ← BandPreparation.zero; + index ← index + BandPreparation.one + ] +] + +/-- Load one original pair without changing its input words. -/ +def loadQuery : Code .standard 96 := ram[ + pointer ← index * BandPreparation.two; pointer ← pointer + queries; + queryRow ← *pointer; pointer ← pointer + BandPreparation.one; queryCol ← *pointer +] + +/-- Count one input position in its actual tile, retaining duplicates. -/ +def countStep : Code .standard 96 := ram[ + ⟪loadQuery⟫; ⟪tileCode⟫; pointer ← counts + tile; count ← *pointer; + count ← count + BandPreparation.one; *pointer ← count; + index ← index + BandPreparation.one +] + +/-- Scan the original list once to obtain tile counts. -/ +def countQueries : Code .standard 96 := + .seq (ram[index ← #0]) (.loop (.lt index queryCount) countStep) + +/-- Store both the immutable prefix offset and the initial scatter cursor of one tile. -/ +def prefixStep : Code .standard 96 := ram[ + pointer ← counts + index; count ← *pointer; + pointer ← offsets + index; *pointer ← total; + pointer ← cursors + index; *pointer ← total; + total ← total + count; index ← index + BandPreparation.one +] + +/-- Compute exact tile spans, including the terminal offset after the last tile. -/ +def prefixCode : Code .standard 96 := ram[ + index ← #0; total ← #0; + while index < tileCount do [ ⟪prefixStep⟫ ]; + pointer ← offsets + tileCount; *pointer ← total +] + +/-- Append one code and original position to its tile, advancing the actual cursor. -/ +def scatterWrite : Code .standard 96 := ram[ + cursorAddress ← cursors + tile; position ← *cursorAddress; + pointer ← codes + position; ⟪.op (.store pointer (packRegisters code))⟫; + pointer ← originals + position; *pointer ← index; + position ← position + BandPreparation.one; *cursorAddress ← position; + index ← index + BandPreparation.one +] + +/-- Recompute one query's code and scatter it stably into its counted tile. -/ +def scatterStep : Code .standard 96 := + .seq loadQuery (.seq routeCode scatterWrite) + +/-- The second original-order scan retains every occurrence and its original position. -/ +def scatter : Code .standard 96 := + .seq (ram[index ← #0]) (.loop (.lt index queryCount) scatterStep) + +/-- Complete initialized native routing; no mathematical layout is an input word. -/ +def program : Code .standard 96 := + .seq BandPreparation.header (.seq BandPreparation.countCode (.seq setup + (.seq BandPreparation.tablesCode (.seq clearCounts + (.seq countQueries (.seq prefixCode scatter)))))) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Route.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Route.lean new file mode 100644 index 000000000..46624c569 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Route.lean @@ -0,0 +1,186 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Program +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Meaning +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Tiling + +/-! +# Native routing of one wanted position + +## Main results + +* `Wanted.exec_route`: actual divisions, selected-mask lookup and digit packing return + exactly the tile and `outStrOfPos` code for an original matrix position. + +The table contract is an ordinary memory equation supplied by the actual computed-table +routine. It supplies neither an output string nor an execution or time certificate. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Scalar values read by the one-query router. The table may be relocated by its caller. -/ +structure RouteParameters (L m N tab : ℕ) (x : Data 96) : Prop where + /-- Original depth. -/ + levels : x.regs BandPreparation.levels = L + /-- Compressed outer dimension. -/ + outerSize : x.regs BandPreparation.outerSize = N0 L m + /-- Computed root of the valid-subset count. -/ + root : x.regs BandPreparation.root = K0 L m + /-- Computed band width. -/ + bandWidth : x.regs bandWidth = bandSize L m + /-- Computed band count. -/ + bands : x.regs BandPreparation.bands = numBands L m N + /-- Actual forward-table base. -/ + table : x.regs BandPreparation.table = tab + +/-- Numeric bound for all arithmetic performed by one router and its digit kernel. -/ +def routeBudget (L m N tab : ℕ) : ℕ := + 64 * (L + N + tab + numBands L m N ^ 2 + 10 ^ L + 2 ^ L + 1) + +/-- Only the packer bank and three computed routing registers are written. -/ +def RouteLocal (i : Fin 96) : Prop := i.val < 16 ∨ i = rowBand ∨ i = colBand ∨ i = tile + +instance (i : Fin 96) : Decidable (RouteLocal i) := + inferInstanceAs (Decidable (i.val < 16 ∨ i = rowBand ∨ i = colBand ∨ i = tile)) + +private theorem route_preserves (i : Fin 96) (hi : ¬RouteLocal i) : routeCode.Preserves i := by + revert i + decide +kernel + +/-- The register map embeds the packer without identifying any pair of registers. -/ +theorem packRegisters_injective : Function.Injective packRegisters := by + intro i j h + exact Fin.ext (congrArg (fun i : Fin 96 => i.val) h) + +/-- Actual routing on one admitted original position. Original row/column words, all +persistent parameters and all memory survive; only the declared local registers change. -/ +theorem exec_route {w L m N tab : ℕ} (hm : m ≤ L) (I J : Fin N) (x : Data 96) + (hx : RouteParameters L m N tab x) + (hI : x.regs queryRow = I.val) (hJ : x.regs queryCol = J.val) + (htable : ∀ gh : Fin (K0 L m) × Fin (K0 L m), + x.mem (tab + (MaskTable.pairRank gh).val) = (MaskTable.selectedMask gh).val) + (hfit : Fits w (routeBudget L m N tab)) : + ∃ y, ExecWithin w routeCode x y (15 * L + 25) ∧ + y.regs rowBand = bandOf L m I.val ∧ y.regs colBand = bandOf L m J.val ∧ + y.regs tile = bandOf L m I.val * numBands L m N + bandOf L m J.val ∧ + y.regs (packRegisters code) = + (PrunedQuery.queryIndex (outStrOfPos (MaskTable.layout hm) I J)).val ∧ + y.mem = x.mem ∧ (∀ i, ¬RouteLocal i → y.regs i = x.regs i) := by + let lay := MaskTable.layout hm + let g := blockOf lay I + let h := blockOf lay J + let a := (MaskTable.selectedMask (g, h)).val + let r := I.val % N0 L m + let c := J.val % N0 L m + let β := bandOf L m I.val + let γ := bandOf L m J.val + let b := numBands L m N + let k := β * b + γ + let u := I.val / N0 L m + let v := J.val / N0 L m + let rank := g.val * K0 L m + h.val + have hβ : β < b := bandOf_lt_numBands hm N I I.isLt + have hγ : γ < b := bandOf_lt_numBands hm N J J.isLt + have hk : k < b ^ 2 := by + dsimp [k] + nlinarith only [Nat.mul_le_mul_right b (Nat.succ_le_of_lt hβ), hγ] + have hbb : b ≤ b ^ 2 := by nlinarith only [hβ] + have h2 : 2 ^ L ≤ 10 ^ L := Nat.pow_le_pow_left (by decide) _ + have hu : u ≤ N := (Nat.div_le_self _ _).trans I.isLt.le + have hv : v ≤ N := (Nat.div_le_self _ _).trans J.isLt.le + have hg : g.val < K0 L m := g.isLt + have hh : h.val < K0 L m := h.isLt + have hrank : rank < 2 ^ L := by + have hp : rank = (MaskTable.pairRank (g, h)).val := by + change g.val * K0 L m + h.val = h.val + K0 L m * g.val + ring + rw [hp] + exact (MaskTable.pairRank (g, h)).isLt.trans_le (Nat.choose_le_two_pow L m) + have hmul : g.val * K0 L m ≤ rank := by dsimp [rank]; omega + have ha : a < 2 ^ L := (MaskTable.selectedMask (g, h)).isLt + have hr : r ≤ N := (Nat.mod_le _ _).trans I.isLt.le + have hc : c ≤ N := (Nat.mod_le _ _).trans J.isLt.le + have hbfit (v : ℕ) (hv : v ≤ L + N + tab + b ^ 2 + 10 ^ L + 2 ^ L + 1) : + Fits w v := hfit.of_le (by + change v ≤ 64 * (L + N + tab + b ^ 2 + 10 ^ L + 2 ^ L + 1) + omega) + have hfβ : Fits w β := hbfit _ (by omega) + have hfγ : Fits w γ := hbfit _ (by omega) + have hfprod : Fits w (β * b) := hbfit _ (by dsimp [k] at hk; omega) + have hfk : Fits w k := hbfit _ (by omega) + have hfr : Fits w r := hbfit _ (by omega) + have hfc : Fits w c := hbfit _ (by omega) + have hfu : Fits w u := hbfit _ (by omega) + have hfv : Fits w v := hbfit _ (by omega) + have hfg : Fits w g.val := hbfit _ (by + have hle : g.val ≤ u := Nat.mod_le _ _ + omega) + have hfh : Fits w h.val := hbfit _ (by + have hle : h.val ≤ v := Nat.mod_le _ _ + omega) + have hfgmul : Fits w (g.val * K0 L m) := hbfit _ (by omega) + have hfrank : Fits w rank := hbfit _ (by omega) + have hfaddr : Fits w (tab + rank) := hbfit _ (by omega) + have hload : x.mem (tab + rank) = a := by + convert htable (g, h) using 1 + congr 1 + change tab + rank = tab + (h.val + K0 L m * g.val) + dsimp [rank] + ring + let y := (((((((((((((((x.set rowBand β).set colBand γ).set tile (β * b)).set + tile k).set (packRegisters depth) L).set (packRegisters row) r).set + (packRegisters col) c).set (packRegisters mask) u).set (packRegisters mask) g.val).set + (packRegisters mask) (g.val * K0 L m)).set (packRegisters temporary) v).set + (packRegisters temporary) h.val).set (packRegisters mask) rank).set + (packRegisters temporary) (tab + rank)).set (packRegisters mask) a) + have he : ExecWithin w routeHead x y 15 := by + dsimp only [β, γ, b, k, bandOf] at hfβ hfγ hfprod hfk + dsimp only [r, c, u, v, g, h, blockOf, lay] at hfr hfc hfu hfv hfg hfh + dsimp only [rank, g, h, blockOf, lay] at hfgmul hfrank hfaddr hload + ram_eval [routeHead, tileCode, y, hx.bandWidth, hx.bands, hx.levels, hx.outerSize, + hx.root, hx.table, hI, hJ, hfβ, hfγ, hfprod, hfk, hfr, hfc, hfu, hfv, hfg, hfh, + hfgmul, hfrank, hfaddr, hload, β, γ, b, k, r, c, u, v, rank, g, h, blockOf, + lay, bandOf, packRegisters, depth, mask, row, col, temporary, + queryRow, queryCol, rowBand, colBand, tile, BandPreparation.levels, + BandPreparation.outerSize, BandPreparation.root, BandPreparation.table, + BandPreparation.bands, bandWidth, Fin.reduceFinMk, Fin.mk.injEq] + have hpackfit : Fits w (a + r + c + L + 32 * 10 ^ L + 32) := hfit.of_le (by + change a + r + c + L + 32 * 10 ^ L + 32 ≤ + 64 * (L + N + tab + b ^ 2 + 10 ^ L + 2 ^ L + 1) + omega) + obtain ⟨z, hz, hcode, _, hmem⟩ := exec_pack L a r c (y.comap packRegisters) + (by simp [y, packRegisters, depth, row, col, mask, temporary, rowBand, colBand, tile]) + (by simp [y, packRegisters, mask]) + (by simp [y, packRegisters, row, col, mask, temporary, depth]) + (by simp [y, packRegisters, col, mask, temporary, depth]) hpackfit + have hrun : ExecWithin w routeCode x (z.extend packRegisters y) (15 * L + 25) := by + exact (he.seq (hz.rename packRegisters_injective)).mono (by omega) + refine ⟨z.extend packRegisters y, hrun, ?_, ?_, ?_, ?_, hmem, ?_⟩ + · have hp : (packCode.rename packRegisters).Preserves rowBand := by decide +kernel + exact ((hz.rename packRegisters_injective).regs_eq hp).trans (by simp [y, rowBand, + colBand, tile, packRegisters, depth, row, col, mask, temporary, β]) + · have hp : (packCode.rename packRegisters).Preserves colBand := by decide +kernel + exact ((hz.rename packRegisters_injective).regs_eq hp).trans (by simp [y, rowBand, + colBand, tile, packRegisters, depth, row, col, mask, temporary, γ]) + · have hp : (packCode.rename packRegisters).Preserves tile := by decide +kernel + exact ((hz.rename packRegisters_injective).regs_eq hp).trans (by simp [y, rowBand, + colBand, tile, packRegisters, depth, row, col, mask, temporary]; rfl) + · have hout := congrArg (fun q : Data 16 => q.regs code) + (Data.comap_extend packRegisters_injective z y) + exact hout.trans (hcode.trans (pack_outStrOfPos hm I J)) + · intro i hi + exact hrun.regs_eq (route_preserves i hi) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Setup.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Setup.lean new file mode 100644 index 000000000..7c3b293d2 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Setup.lean @@ -0,0 +1,167 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Memory +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.BandBounds + +/-! +# Actual initialization of wanted-query parameters + +## Main results + +* `Wanted.exec_setup`: read the query count and compute all relocated addresses natively. +* `Wanted.exec_tables`: scalar/header/count setup and actual valid-mask tables from the + original four input words, preserving every original query word. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Expanded allocated size, useful without hiding any arithmetic behind a layout record. -/ +theorem memoryLimit_eq (L m N Q : ℕ) : + memoryLimit L m N Q = 6 + 4 * Q + 2 * 2 ^ L + 3 * numBands L m N ^ 2 := by + simp only [memoryLimit, originalAddress, codeAddress, cursorAddressBase, + offsetAddress, countAddress, tableAddress, inputLimit] + omega + +/-- The original numerical word budget covers every scalar used by the grouping loops. -/ +theorem local_fits {w L m N Q : ℕ} (hfit : Fits w (wordBudget L m N Q)) : + Fits w (16 * (L + N + Q + memoryLimit L m N Q + 10 ^ L + 2 ^ L + + numBands L m N ^ 2 + 16)) := by + apply hfit.of_le + unfold wordBudget routeBudget + omega + +/-- Relocate the actual computed-table parameters after the input and allocate all +count/prefix/cursor/code/permutation arrays using charged native arithmetic. -/ +theorem exec_setup {w L m N Q : ℕ} (x : Data 96) + (hx : BandPreparation.BandParameters L m N x) (hQ : x.mem 4 = Q) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w setup x y 16 ∧ Parameters L m N Q y ∧ y.mem = x.mem := by + have hl := local_fits hfit + have hend := memoryLimit_eq L m N Q + have hf (v : ℕ) (hv : v ≤ memoryLimit L m N Q + 10 ^ L) : Fits w v := + hl.of_le (by omega) + have hB : bandSize L m ≤ 10 ^ L := + (bandSize_le_seven_pow L m).trans (Nat.pow_le_pow_left (by decide) _) + have h5 : Fits w 5 := hf _ (by omega) + have h4 : Fits w 4 := hf _ (by omega) + have hq2 : Fits w (Q * 2) := hf _ (by omega) + have hi : Fits w (inputLimit Q) := hf _ (by unfold inputLimit; omega) + have ht : Fits w (tableAddress L Q) := hf _ (by + unfold tableAddress inputLimit; omega) + have hc : Fits w (countAddress L Q) := hf _ (by + unfold countAddress tableAddress inputLimit; omega) + have hb : Fits w (bandSize L m) := hf _ (by omega) + have ht2 : Fits w (numBands L m N * numBands L m N) := hf _ (by + rw [← sq]; omega) + have ho : Fits w (offsetAddress L m N Q) := hf _ (by + unfold offsetAddress countAddress tableAddress inputLimit; omega) + have hcur0 : Fits w (offsetAddress L m N Q + numBands L m N ^ 2) := hf _ (by + unfold offsetAddress countAddress tableAddress inputLimit; omega) + have hcur : Fits w (cursorAddressBase L m N Q) := hf _ (by + unfold cursorAddressBase offsetAddress countAddress tableAddress inputLimit; omega) + have hco : Fits w (codeAddress L m N Q) := hf _ (by + unfold codeAddress cursorAddressBase offsetAddress countAddress tableAddress inputLimit; omega) + have horig : Fits w (originalAddress L m N Q) := hf _ (by + unfold originalAddress codeAddress cursorAddressBase offsetAddress countAddress + tableAddress inputLimit + omega) + have hefit : Fits w (memoryLimit L m N Q) := hf _ (by omega) + let y := ((((((((((((((((x.set pointer 4).set queryCount Q).set queries 5).set + inputEnd (Q * 2)).set inputEnd (inputLimit Q)).set BandPreparation.inverse + (inputLimit Q)).set BandPreparation.table (tableAddress L Q)).set counts + (countAddress L Q)).set bandWidth (bandSize L m)).set tileCount + (numBands L m N * numBands L m N)).set offsets (offsetAddress L m N Q)).set + cursors (offsetAddress L m N Q + numBands L m N ^ 2)).set cursors + (cursorAddressBase L m N Q)).set codes (codeAddress L m N Q)).set originals + (originalAddress L m N Q)).set memoryEnd (memoryLimit L m N Q)) + have he : ExecWithin w setup x y 16 := by + simp only [bandSize] at hb + simp only [inputLimit] at hi + simp only [tableAddress, inputLimit] at ht + simp only [countAddress, tableAddress, inputLimit] at hc + simp only [offsetAddress, countAddress, tableAddress, inputLimit] at ho hcur0 + simp only [cursorAddressBase, offsetAddress, countAddress, tableAddress, inputLimit] at hcur + simp only [codeAddress, cursorAddressBase, offsetAddress, countAddress, tableAddress, + inputLimit] at hco + simp only [originalAddress, codeAddress, cursorAddressBase, offsetAddress, countAddress, + tableAddress, inputLimit] at horig + simp only [memoryLimit, originalAddress, codeAddress, cursorAddressBase, offsetAddress, + countAddress, tableAddress, inputLimit] at hefit + simp only [pow_two] at ho hcur0 hcur hco horig hefit + ram_eval [setup, y, hQ, hx.fixed.two, hx.fixed.one, hx.masks, hx.root, hx.outerSize, + hx.bands, h4, h5, hq2, hi, ht, hc, hb, ht2, ho, hcur0, hcur, hco, horig, hefit, + memoryLimit, originalAddress, codeAddress, cursorAddressBase, offsetAddress, + countAddress, tableAddress, inputLimit, bandSize, pow_two, + pointer, queryCount, queries, inputEnd, counts, bandWidth, tileCount, offsets, + cursors, codes, originals, memoryEnd, BandPreparation.inverse, BandPreparation.table, + BandPreparation.root, BandPreparation.outerSize, BandPreparation.bands, + BandPreparation.masks, BandPreparation.two, BandPreparation.one, Fin.mk.injEq] + refine ⟨y, he, ?_, rfl⟩ + constructor + · constructor <;> simp [y, pointer, queryCount, queries, inputEnd, counts, bandWidth, + tileCount, offsets, cursors, codes, originals, memoryEnd, BandPreparation.levels, + BandPreparation.inner, BandPreparation.masks, BandPreparation.inverse, + BandPreparation.table, BandPreparation.square, BandPreparation.zero, + BandPreparation.one, BandPreparation.two, hx.fixed.levels, hx.fixed.inner, hx.masks, + hx.square, hx.fixed.zero, hx.fixed.one, hx.fixed.two] + · constructor <;> simp [y, pointer, queryCount, queries, inputEnd, counts, bandWidth, + tileCount, offsets, cursors, codes, originals, memoryEnd, BandPreparation.levels, + BandPreparation.outerSize, BandPreparation.root, BandPreparation.bands, + BandPreparation.inverse, BandPreparation.table, hx.fixed.levels, hx.outerSize, + hx.root, hx.bands] + all_goals simp [y, pointer, queryCount, queries, inputEnd, counts, bandWidth, + tileCount, offsets, cursors, codes, originals, memoryEnd, BandPreparation.inverse, + BandPreparation.table, pow_two] + +/-- The initialized producer of the tables used by routing, without invoking any encoders. +The only memory changes are the two relocated mask tables after the complete query input. -/ +theorem exec_tables {w L m N Q : ℕ} (hm : m ≤ L) (x : Data 96) + (hL : x.mem 1 = L) (hmemory : x.mem 2 = m) (hN : x.mem 3 = N) (hQ : x.mem 4 = Q) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w (.seq BandPreparation.header (.seq BandPreparation.countCode + (.seq setup BandPreparation.tablesCode))) x y + (16 * L + (12 * L + 33) * 2 ^ L + 83) ∧ Parameters L m N Q y ∧ + (∀ gh : Fin (K0 L m) × Fin (K0 L m), + y.mem (tableAddress L Q + (MaskTable.pairRank gh).val) = + (MaskTable.selectedMask gh).val) ∧ + (∀ a, a < inputLimit Q ∨ tableAddress L Q + 2 ^ L ≤ a → y.mem a = x.mem a) := by + have h10 := Nat.zero_le (10 ^ L) + have h2 := Nat.zero_le (2 ^ L) + have hpfit : Fits w (BandPreparation.wordBudget L m N) := hfit.of_le (by + unfold wordBudget; omega) + obtain ⟨h, hh, hparams, hmem⟩ := BandPreparation.exec_header hm x hL hmemory hN hpfit + obtain ⟨c, hc, cp, cm⟩ := BandPreparation.exec_countCode h hparams hpfit + obtain ⟨s, hs, sp, sm⟩ := exec_setup c cp (by rw [cm, hmem]; exact hQ) hfit + have htfit : Fits w (inputLimit Q + tableAddress L Q + 2 ^ L + L + 4) := by + apply (local_fits hfit).of_le + have := memoryLimit_eq L m N Q + unfold tableAddress inputLimit + omega + obtain ⟨y, hy, _, _, hforward, hf⟩ := BandPreparation.exec_tables L m + (inputLimit Q) (tableAddress L Q) s sp.tables (by rfl) htfit + have yp : Parameters L m N Q y := sp.reframe (by + intro i hi + apply hy.regs_eq + have hp : ∀ i ∈ parameterRegisters, BandPreparation.tablesCode.Preserves i := by + decide +kernel + exact hp i hi) + refine ⟨y, ?_, yp, hforward, ?_⟩ + · apply (hh.seq (hc.seq (hs.seq hy))).mono + nlinarith only [Nat.zero_le ((6 * L + 16) * 2 ^ L)] + · intro a ha + rw [hf a (by rcases ha with ha | ha <;> unfold tableAddress at * <;> omega) + (by rcases ha with ha | ha <;> unfold tableAddress at * <;> omega), sm, cm, hmem] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/ComplexityTests/SchonhageWanted.lean b/ComplexityTests/SchonhageWanted.lean new file mode 100644 index 000000000..297ddf5f1 --- /dev/null +++ b/ComplexityTests/SchonhageWanted.lean @@ -0,0 +1,63 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: local-only (tests) + +import Complexity.Init +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Route +import Complexity.Algorithms.MatrixMultiplication.Schonhage.MaskTable + +set_option linter.hashCommand false +set_option linter.privateModule false + +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +private def expectedCode {L m : ℕ} (hm : m ≤ L) (I J : ℕ) : ℕ := + (PrunedQuery.queryIndex (outStrOfPos (MaskTable.layout hm) I J)).val + +-- Distinct row/column block indices use rank 1 and rank 2, not their transposes. +#guard expectedCode (by decide : 1 ≤ 4) 2 28 == 97 +#guard expectedCode (by decide : 1 ≤ 4) 28 2 == 905 +example : QueryDigits.pack 4 2 2 1 ≠ QueryDigits.pack 4 4 1 2 := by decide +kernel + +private def packedInput : Data 16 where + regs i := if i = depth then 4 else if i = mask then 2 else + if i = row then 2 else if i = col then 1 else 81 + mem a := if a = 23 then 91 else 0 + +-- The arbitrary nonzero scratch bank is initialized by real code; the same returned state +-- carries the packed word and the unchanged memory assertion. +example : ∃ y, ExecWithin 19 packCode packedInput y 70 ∧ + y.regs code = 97 ∧ y.mem 23 = 91 := by + obtain ⟨y, he, hv, _, hf⟩ := exec_pack (w := 19) 4 2 2 1 packedInput + (by decide) (by decide) (by decide) (by decide) (by decide) + exact ⟨y, he, hv, congrFun hf 23⟩ + +private def routingInput (L m N : ℕ) (qs : List (ℕ × ℕ)) : List ℕ := + [L, m, N, qs.length] ++ qs.flatMap (fun q => [q.1, q.2]) + +private def observe (L m N : ℕ) (qs : List (ℕ × ℕ)) : + Option (List ℕ × List ℕ × List ℕ × Bool) := + let initial := (State.init (routingInput L m N qs) : State 96).data.write 100000 91 + (program.eval 25 1024 initial).map fun (y, _) => + ((List.range qs.length).map fun i => y.mem (y.regs codes + i), + (List.range qs.length).map fun i => y.mem (y.regs originals + i), + (List.range (y.regs tileCount + 1)).map fun i => y.mem (y.regs offsets + i), + decide ((∀ a < 5 + 2 * qs.length, y.mem a = initial.mem a) ∧ y.mem 100000 = 91)) + +-- Actual initialized empty and zero-depth runs, including the computed tables and prefix pass. +#guard observe 0 0 0 [] == some ([], [], [0], true) +#guard observe 0 0 2 [(1, 0), (0, 1), (1, 0)] == + some ([0, 0, 0], [1, 0, 2], [0, 0, 1, 3, 3], true) + +-- K0=2, three bands, asymmetric row/column digits and an original repeated query. +-- This exercises only routing; it never materializes a 10^4-leaf encoded array. +#guard observe 4 1 110 [(2, 28), (28, 2), (55, 109), (2, 28), (109, 55)] == + some ([97, 905, 97, 49, 49], [0, 1, 3, 2, 4], [0, 3, 3, 3, 3, 3, 4, 4, 5, 5], true) From 7772a3497017078b37d1e04004225fae28baddad Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 20:44:12 -0400 Subject: [PATCH 33/73] feat(wordram): checkpoint wanted count-pass proofs Continuation of #1561 from 06b3563e7. CountStep has clean targeted Lean MCP diagnostics, including exact 14-instruction native count update and memory frame. Dense zeroing and count-loop composition are implemented; Count.lean has one pending elementary nonnegativity/address-bound proof at line 152. This is an explicitly incomplete source checkpoint, not all-six validated or a PR. Work is paused solely to prioritize the bounded outside-requested #1562 repair. No program/public statement in #1562 changed. --- .../Schonhage/Wanted/Count.lean | 173 ++++++++++++++++++ .../Schonhage/Wanted/CountStep.lean | 131 +++++++++++++ 2 files changed, 304 insertions(+) create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Count.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/CountStep.lean diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Count.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Count.lean new file mode 100644 index 000000000..b04f8423c --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Count.lean @@ -0,0 +1,173 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.CountStep + +/-! +# Complete native count pass for wanted positions + +## Main results + +* `Wanted.exec_clearCounts`: initialize exactly the actual tile-count cells. +* `Wanted.exec_countQueries`: all original occurrences, including repetitions, are counted + by the actual coordinate-loading and tile-index code in linear charged time. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Actual dense initialization of tile counts. It touches no query or computed table. -/ +theorem exec_clearCounts {w L m N Q : ℕ} (x : Data 96) (hx : Parameters L m N Q x) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w clearCounts x y (5 * numBands L m N ^ 2 + 3) ∧ + Parameters L m N Q y ∧ + (∀ t < numBands L m N ^ 2, y.mem (countAddress L Q + t) = 0) ∧ + (∀ a, a < countAddress L Q ∨ countAddress L Q + numBands L m N ^ 2 ≤ a → + y.mem a = x.mem a) := by + let T := numBands L m N ^ 2 + let body : Code .standard 96 := ram[ + pointer ← counts + index; *pointer ← BandPreparation.zero; + index ← index + BandPreparation.one ] + let I (z : Data 96) : Prop := Parameters L m N Q z ∧ z.regs index ≤ T ∧ + (∀ t < z.regs index, z.mem (countAddress L Q + t) = 0) ∧ + (∀ a, a < countAddress L Q ∨ countAddress L Q + T ≤ a → z.mem a = x.mem a) + let start := x.set index 0 + have hs : ExecWithin w (ram[index ← #0] : Code .standard 96) x start 1 := by + ram_eval [start] + have hp : Parameters L m N Q start := hx.reframe (by + intro i hi + apply hs.regs_eq + have h : ∀ i ∈ parameterRegisters, (ram[index ← #0] : Code .standard 96).Preserves i := by + decide +kernel + exact h i hi) + have hstart : I start := ⟨hp, by simp [start], by simp [start], by simp [start]⟩ + have hb (z : Data 96) (hz : I z) (hc : (Cond.lt index tileCount).Holds z) : + ∃ u, ExecWithin w body z u 3 ∧ I u ∧ T - u.regs index < T - z.regs index := by + have hn : z.regs index < T := by + simpa only [Cond.holds_lt, hz.1.tileCount] using hc + have hl := local_fits hfit + have hend := memoryLimit_eq L m N Q + have h10 := Nat.zero_le (10 ^ L) + have hfaddr : Fits w (countAddress L Q + z.regs index) := hl.of_le (by + unfold countAddress tableAddress inputLimit + dsimp [T] at hn + omega) + have hnext : Fits w (z.regs index + 1) := hl.of_le (by dsimp [T] at hn; omega) + let u := ((z.set pointer (countAddress L Q + z.regs index)).write + (countAddress L Q + z.regs index) 0).set index (z.regs index + 1) + have he : ExecWithin w body z u 3 := by + ram_eval [body, u, hz.1.counts, hz.1.tables.zero, hz.1.tables.one, hfaddr, hnext, + pointer, counts, index, BandPreparation.zero, BandPreparation.one, Fin.mk.injEq] + have hu : Parameters L m N Q u := hz.1.reframe (by + intro i hi + apply he.regs_eq + have h : ∀ i ∈ parameterRegisters, body.Preserves i := by decide +kernel + exact h i hi) + have hi : u.regs index = z.regs index + 1 := by simp [u, index] + refine ⟨u, he, ⟨hu, by rw [hi]; omega, ?_, ?_⟩, by rw [hi]; omega⟩ + · intro t ht + rw [hi] at ht + change Function.update z.mem (countAddress L Q + z.regs index) 0 + (countAddress L Q + t) = 0 + by_cases h : t = z.regs index + · subst t + exact Function.update_self _ _ _ + · rw [Function.update_of_ne (by omega)] + exact hz.2.2.1 t (by omega) + · intro a ha + change Function.update z.mem (countAddress L Q + z.regs index) 0 a = x.mem a + rw [Function.update_of_ne (by rcases ha with ha | ha <;> omega)] + exact hz.2.2.2 a ha + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I (fun z => T - z.regs index) hb hstart + have hn : y.regs index = T := by + have hu := hy.2.1 + simp only [Cond.holds_lt, hy.1.tileCount] at hc + dsimp [T] at hu ⊢ + omega + refine ⟨y, ?_, hy.1, ?_, hy.2.2.2⟩ + · apply (hs.seq he).mono + have hzero : start.regs index = 0 := by simp [start] + rw [hzero, Nat.sub_zero] + dsimp [T] + omega + · intro t ht + exact hy.2.2.1 t (hn ▸ ht) + +/-- Every original query is loaded and its actual tile computed. Duplicate input pairs +increase counts independently; the readonly input and both computed tables are preserved. -/ +theorem exec_countQueries {w L m N Q : ℕ} (hm : m ≤ L) (pairs : ℕ → ℕ × ℕ) + (hpairs : ∀ i < Q, (pairs i).1 < N ∧ (pairs i).2 < N) (x : Data 96) + (hx : Parameters L m N Q x) + (hinput : ∀ i < Q, x.mem (5 + 2 * i) = (pairs i).1 ∧ + x.mem (5 + 2 * i + 1) = (pairs i).2) + (hzero : ∀ t < numBands L m N ^ 2, x.mem (countAddress L Q + t) = 0) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w countQueries x y (16 * Q + 3) ∧ Parameters L m N Q y ∧ + (∀ t < numBands L m N ^ 2, + y.mem (countAddress L Q + t) = CountingSort.count (tileKey L m N ∘ pairs) Q t) ∧ + (∀ a, a < countAddress L Q ∨ countAddress L Q + numBands L m N ^ 2 ≤ a → + y.mem a = x.mem a) := by + let key := tileKey L m N ∘ pairs + let T := numBands L m N ^ 2 + let I (z : Data 96) : Prop := Parameters L m N Q z ∧ z.regs index ≤ Q ∧ + (∀ i < Q, z.mem (5 + 2 * i) = (pairs i).1 ∧ + z.mem (5 + 2 * i + 1) = (pairs i).2) ∧ + (∀ t < T, z.mem (countAddress L Q + t) = CountingSort.count key (z.regs index) t) ∧ + (∀ a, a < countAddress L Q ∨ countAddress L Q + T ≤ a → z.mem a = x.mem a) + let start := x.set index 0 + have hs : ExecWithin w (ram[index ← #0] : Code .standard 96) x start 1 := by + ram_eval [start] + have hsp : Parameters L m N Q start := hx.reframe (by + intro i hi + apply hs.regs_eq + have h : ∀ i ∈ parameterRegisters, (ram[index ← #0] : Code .standard 96).Preserves i := by + decide +kernel + exact h i hi) + have hi : I start := by + refine ⟨hsp, by simp [start], hinput, ?_, by simp [start]⟩ + intro t ht + simpa only [start, Data.set_regs, Function.update_self, Data.set_mem, + CountingSort.count_zero] using hzero t ht + have hb (z : Data 96) (hz : I z) (hc : (Cond.lt index queryCount).Holds z) : + ∃ u, ExecWithin w countStep z u 14 ∧ I u ∧ + Q - u.regs index < Q - z.regs index := by + have hn : z.regs index < Q := by + simpa only [Cond.holds_lt, hz.1.queryCount] using hc + obtain ⟨u, he, hu, hidx, hcounts, hf⟩ := exec_countStep hm pairs hn (hpairs _ hn) + z hz.1 rfl (hz.2.2.1 _ hn) hz.2.2.2.1 hfit + refine ⟨u, he, ⟨hu, by rw [hidx]; omega, ?_, ?_, ?_⟩, by rw [hidx]; omega⟩ + · intro i hi + have ha : 5 + 2 * i + 1 < countAddress L Q := by + unfold countAddress tableAddress inputLimit + omega + rw [hf _ (Or.inl (by omega)), hf _ (Or.inl ha)] + exact hz.2.2.1 i hi + · intro t ht + rw [hidx] + exact hcounts t ht + · intro a ha + exact (hf a ha).trans (hz.2.2.2.2 a ha) + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I (fun z => Q - z.regs index) hb hi + have hn : y.regs index = Q := by + have hu := hy.2.1 + simp only [Cond.holds_lt, hy.1.queryCount] at hc + omega + refine ⟨y, ?_, hy.1, ?_, hy.2.2.2.2⟩ + · apply (hs.seq he).mono + have hz : start.regs index = 0 := by simp [start] + rw [hz, Nat.sub_zero] + omega + · intro t ht + simpa only [hn] using hy.2.2.2.1 t ht + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/CountStep.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/CountStep.lean new file mode 100644 index 000000000..adc763aff --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/CountStep.lean @@ -0,0 +1,131 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Setup + +/-! +# One charged native tile-count update + +## Main results + +* `Wanted.exec_countStep`: load the actual pair, compute its tile and increment exactly + that bucket while preserving original query memory and all other counts. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- A row-major tile key, computed from the two original coordinate words. -/ +def tileKey (L m N : ℕ) (q : ℕ × ℕ) : ℕ := + bandOf L m q.1 * numBands L m N + bandOf L m q.2 + +/-- Admitted original positions always name one of the actual tiles. -/ +theorem tileKey_lt {L m N : ℕ} (hm : m ≤ L) (q : ℕ × ℕ) + (hr : q.1 < N) (hc : q.2 < N) : tileKey L m N q < numBands L m N ^ 2 := by + have hβ := bandOf_lt_numBands hm N q.1 hr + have hγ := bandOf_lt_numBands hm N q.2 hc + unfold tileKey + nlinarith only [Nat.mul_le_mul_right (numBands L m N) (Nat.succ_le_of_lt hβ), hγ] + +private theorem countStep_parameters {w T L m N Q : ℕ} {x y : Data 96} + (hx : Parameters L m N Q x) (he : ExecWithin w countStep x y T) : + Parameters L m N Q y := hx.reframe (by + intro i hi + apply he.regs_eq + have hp : ∀ i ∈ parameterRegisters, countStep.Preserves i := by decide +kernel + exact hp i hi) + +/-- The full native count update, including loading both original coordinates. Its count +bound comes from the original prefix length, rather than a supplied intermediate-state fit. -/ +theorem exec_countStep {w L m N Q n : ℕ} (hm : m ≤ L) (pairs : ℕ → ℕ × ℕ) + (hn : n < Q) (hpair : (pairs n).1 < N ∧ (pairs n).2 < N) (x : Data 96) + (hx : Parameters L m N Q x) (hi : x.regs index = n) + (hinput : x.mem (5 + 2 * n) = (pairs n).1 ∧ + x.mem (5 + 2 * n + 1) = (pairs n).2) + (hcounts : ∀ t < numBands L m N ^ 2, + x.mem (countAddress L Q + t) = CountingSort.count (tileKey L m N ∘ pairs) n t) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w countStep x y 14 ∧ Parameters L m N Q y ∧ y.regs index = n + 1 ∧ + (∀ t < numBands L m N ^ 2, + y.mem (countAddress L Q + t) = + CountingSort.count (tileKey L m N ∘ pairs) (n + 1) t) ∧ + (∀ a, a < countAddress L Q ∨ countAddress L Q + numBands L m N ^ 2 ≤ a → + y.mem a = x.mem a) := by + let I := (pairs n).1 + let J := (pairs n).2 + let B := numBands L m N + let β := bandOf L m I + let γ := bandOf L m J + let key := tileKey L m N ∘ pairs + let c := CountingSort.count key n (key n) + have ht : key n < B ^ 2 := tileKey_lt hm _ hpair.1 hpair.2 + have hβ : β < B := bandOf_lt_numBands hm N I hpair.1 + have hγ : γ < B := bandOf_lt_numBands hm N J hpair.2 + have hB : B ≤ B ^ 2 := by nlinarith only [hβ] + have hBeq : B ^ 2 = numBands L m N ^ 2 := rfl + have h10 := Nat.zero_le (10 ^ L) + have hprod : β * B ≤ key n := by dsimp [key, tileKey, Function.comp_def, β, B, I]; omega + have hcle : c ≤ n := CountingSort.count_le _ _ _ + have hl := local_fits hfit + have hend := memoryLimit_eq L m N Q + have hf (v : ℕ) (hv : v ≤ memoryLimit L m N Q) : Fits w v := hl.of_le (by omega) + have hcn : Fits w (c + 1) := hf _ (by omega) + have hn1 : Fits w (n + 1) := hf _ (by omega) + have hn2 : Fits w (n * 2) := hf _ (by omega) + have ha : Fits w (n * 2 + 5) := hf _ (by omega) + have ha1 : Fits w (n * 2 + 5 + 1) := hf _ (by omega) + have hβf : Fits w β := hf _ (by omega) + have hγf : Fits w γ := hf _ (by omega) + have hpf : Fits w (β * B) := hf _ (by omega) + have hkf : Fits w (key n) := hf _ (by omega) + have hcf : Fits w (countAddress L Q + key n) := hf _ (by + unfold countAddress tableAddress inputLimit + omega) + have hload : x.mem (countAddress L Q + key n) = c := hcounts _ ht + have hI : x.mem (n * 2 + 5) = I := + (congrArg x.mem (by omega : n * 2 + 5 = 5 + 2 * n)).trans hinput.1 + have hJ : x.mem (n * 2 + 5 + 1) = J := + (congrArg x.mem (by omega : n * 2 + 5 + 1 = 5 + 2 * n + 1)).trans hinput.2 + let y := ((((((((((((((x.set pointer (n * 2)).set pointer (n * 2 + 5)).set queryRow I).set + pointer (n * 2 + 5 + 1)).set queryCol J).set rowBand β).set colBand γ).set + tile (β * B)).set tile (key n)).set pointer (countAddress L Q + key n)).set count c).set + count (c + 1)).write (countAddress L Q + key n) (c + 1)).set index (n + 1)) + have he : ExecWithin w countStep x y 14 := by + dsimp only [β, γ, B, I, J, bandOf] at hβf hγf hpf + dsimp only [I, J] at hI hJ + dsimp only [key, Function.comp_def, tileKey, bandOf] at hkf hcf hload + ram_eval [countStep, loadQuery, tileCode, y, hx.queries, hx.queryCount, hx.counts, + hx.route.bandWidth, hx.route.bands, hx.tables.one, hx.tables.two, hi, + hI, hJ, hload, hn2, ha, ha1, hβf, hγf, hpf, hkf, hcf, hcn, hn1, + I, J, β, γ, B, key, Function.comp_def, tileKey, bandOf, + pointer, index, queries, queryRow, queryCol, rowBand, colBand, tile, bandWidth, + counts, count, BandPreparation.bands, BandPreparation.one, BandPreparation.two, + Fin.mk.injEq] + refine ⟨y, he, countStep_parameters hx he, by simp [y, index], ?_, ?_⟩ + · intro t ht' + change y.mem (countAddress L Q + t) = CountingSort.count key (n + 1) t + rw [CountingSort.count_succ] + change (Function.update x.mem (countAddress L Q + key n) (c + 1)) + (countAddress L Q + t) = _ + by_cases h : key n = t + · subst t + simp only [Function.update_self, ↓reduceIte] + rfl + · have hc' : x.mem (countAddress L Q + t) = CountingSort.count key n t := hcounts t ht' + rw [Function.update_of_ne (by omega), hc', ite_eq_right h] + omega + · intro a ha + change Function.update x.mem (countAddress L Q + key n) (c + 1) a = x.mem a + rw [Function.update_of_ne (by rcases ha with ha | ha <;> omega)] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted From 702c14bcf9896655b17c9b2dd5e02e6f270c9966 Mon Sep 17 00:00:00 2001 From: Samuel Schlesinger Date: Thu, 29 Oct 2026 00:52:16 +0000 Subject: [PATCH 34/73] doc(finset): restore ordered-gluing source credit Restore the original AlmanWilliams2026 Section 2 citation and finite-scope sentence removed by the naming refactor. No Lean declarations or proof tokens change. AI-assisted coordinator fix by coordinator-codex-queue-20261028-ready-c. Original implementation and source authorship retained; independent delta review and new hosted CI requested on PR #1554. --- Complexity/Foundations/Data/Finset/OrderGlue.lean | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Complexity/Foundations/Data/Finset/OrderGlue.lean b/Complexity/Foundations/Data/Finset/OrderGlue.lean index 252b1e6b3..7d2253b09 100644 --- a/Complexity/Foundations/Data/Finset/OrderGlue.lean +++ b/Complexity/Foundations/Data/Finset/OrderGlue.lean @@ -26,9 +26,10 @@ public import Mathlib.Data.Finset.Sort ## References -Adapted selectively from +Adapted selectively from [AlmanWilliams2026, Section 2] and [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). -Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical +counts and identities; they give no native program or instruction-cost certificate. -/ @[expose] public section From ce77926c393ce34e7962759bb412172c4584a67c Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 20:57:26 -0400 Subject: [PATCH 35/73] fix(wordram): address pruned-query review findings Bounded documentation/test repair for #1562 / #1526 by codex-three-sum-continuation-t3-3dda8289, above exact 0c2e0ebea6199ac1fbe9b2e048ce24b9410f8d1a. Fix both ROADMAP reference definitions, distinguish client contracts from exported internal stage APIs, and document why the narrow ArrayAppend/InputArray topics avoid Allocation interface/dependency fanout. Extend existing post-integration issue #1553 with the exact pruned power register map, 4L+10 accounting, affected consumers, fused-encoder accounting and sibling integration order; no power program or ancestor is changed. Add one fully admitted signed runs_bilinear client: depth0, original arrays -3 and 2, actual encodingL/R premises discharged, original-input magnitude/address fits discharged, physical width10, same actual State.init prefix Runs and charged halt, and output memory at address6 equals Signed.encode(-6). Targeted Lean MCP diagnostics pass. Validation: all six strict gates passed together on final staged tree f4cfd4ae0d838c4b4bf4c43c924f400744450957, including Smoke/global Axioms; one thread/downloads disabled. Full citation-aware hygiene --check-simp, fresh actual-base 47-library/3-test module lint, changed-file audits, strict declaration-name check and staged/unstaged diff checks pass. Local machine-readable evidence: .lake/codex-three-sum-continuation/pruned-review-repair/{summary.json,static-summary.json,identity.json,*.log}. Source identity audit against 0c: all seven changed library files have identical comment-free tokens. Every public library statement, definition, native program and proof is unchanged. Every existing test byte is unchanged except the single inserted concrete client. Abstract/catalogue/rendering assessment: no impact. The prior inside-fleet technical review at issue1526#6484055995 and outside FINAL at PR1562#6484177608 cover unchanged native semantics/source mapping. Fresh focused outside review of this repair is requested; no new review agent was commissioned, and no outside/human/platform approval is claimed. All original Anthropic, PBC credit retained. AI-assisted implementation by OpenAI Codex. #1561 source is preserved separately at 7772a3497017078b37d1e04004225fae28baddad with its pending native grouping proof work explicit. Concrete sparse-product/Exact Triangle programs, full input/time/width composition and the original native 1.9992 endpoint remain open under #1394. --- .../Schonhage/Pruned/Evaluate.lean | 2 ++ .../Schonhage/Pruned/Flat.lean | 3 ++ .../Schonhage/Pruned/Initialize.lean | 2 ++ .../Schonhage/Pruned/Program.lean | 5 ++++ .../Schonhage/Pruned/Recursive.lean | 2 ++ .../Computability/WordRAM/ArrayAppend.lean | 4 +++ .../Computability/WordRAM/InputArray.lean | 3 ++ ComplexityTests/SchonhagePrunedInput.lean | 28 +++++++++++++++++++ ROADMAP.md | 2 ++ UPSTREAM.md | 9 ++++++ 10 files changed, 60 insertions(+) diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Evaluate.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Evaluate.lean index 37b9ed4eb..e3020ce65 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Evaluate.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Evaluate.lean @@ -22,6 +22,8 @@ public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Prune The query function specifies the words of the supplied input list. The machine computes its own distinct set, every recursive child list and every reported index. All scratch addresses, signed fits, fresh generations and instruction charges follow from the ordinary numerical premises. +This module is an internal implementation API used to prove the initialized and pointer-based +client interfaces in `Pruned.Flat`, `Pruned.Width` and `Pruned.Kernel`. -/ @[expose] public section diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Flat.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Flat.lean index deff0b518..03410e5ef 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Flat.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Flat.lean @@ -21,6 +21,9 @@ public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Prune The single fixed program returns the unsigned pointer to its encoded answer list. Queries may repeat and their order is preserved. Its 96 registers and code-size bound are independent of depth, query count, input values and physical word width. +These initialized-run theorems, `runs_wide` in `Pruned.Width` and `exec_kernel` in +`Pruned.Kernel` are the client interfaces. Stage `exec_*` lemmas in their dependencies are +internal implementation APIs exported for proof composition. -/ @[expose] public section diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Initialize.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Initialize.lean index ce7abfbd3..1f07cc5dd 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Initialize.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Initialize.lean @@ -20,6 +20,8 @@ public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Prune The prefix uses `4L+27` instructions, including its power loop. Its continuation is the existing structured sequence rule, so composing it with evaluation gives the original fixed program. +This continuation lemma is an internal implementation API for the complete initialized-run +theorems in `Pruned.Flat` and `Pruned.Width`. -/ @[expose] public section diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean index 51645b50e..37537e76f 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Program.lean @@ -23,6 +23,11 @@ array at every node. Encoded products and sums use the existing native signed ke * `PrunedQuery.program`: initialized-input wrapper computing all addresses from the depth and query-count headers. +Client execution interfaces are `exec_kernel` in `Pruned.Kernel`, `runs` and `runs_bilinear` +in `Pruned.Flat`, and `runs_wide` in `Pruned.Width`. Other stage `exec_*` lemmas and controller +invariants under `Pruned/` are internal implementation APIs. They are public to compose proofs +across the stage modules, rather than intended as independent caller contracts. + ## References The query-set recursion is from [AlmanWilliams2026, Section 2.4.2], selectively adapted from diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Recursive.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Recursive.lean index 27f9c14ad..c5989f0f3 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Recursive.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Pruned/Recursive.lean @@ -23,6 +23,8 @@ public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Prune The induction composes only `Code.ExecWithin` for the existing standard unsigned machine. Its continuation form stops the inductive node at the parent return cursor; at the root the proved zero active flag supplies the ordinary native loop-exit execution. +The stage execution lemmas and node invariants here are internal implementation APIs, +exported for proof composition across modules. Callers use `Pruned.Kernel` or `Pruned.Flat`. -/ @[expose] public section diff --git a/Complexity/Computability/WordRAM/ArrayAppend.lean b/Complexity/Computability/WordRAM/ArrayAppend.lean index 18f04600e..a2b04816b 100644 --- a/Complexity/Computability/WordRAM/ArrayAppend.lean +++ b/Complexity/Computability/WordRAM/ArrayAppend.lean @@ -17,6 +17,10 @@ public import Mathlib.Data.Fin.Tuple.Basic * `Block.Realizes.snoc`: the actual one-cell memory update realizes `Fin.snoc` of the prior finite array. This is a memory identity; a caller must separately charge its store instruction. + +This narrow topic module keeps the `Fin.snoc` dependency and append-specific proof out of +`Allocation`. Clients of core allocation then avoid an interface change and rebuild when the +append API changes; consumers import this module alongside `Block.Realizes.update` as needed. -/ @[expose] public section diff --git a/Complexity/Computability/WordRAM/InputArray.lean b/Complexity/Computability/WordRAM/InputArray.lean index 2bb63808c..2977deef4 100644 --- a/Complexity/Computability/WordRAM/InputArray.lean +++ b/Complexity/Computability/WordRAM/InputArray.lean @@ -19,6 +19,9 @@ import Mathlib.Data.List.GetD including the machine's length header. This is a representation lemma for the existing `Data.init`, with no alternate input convention. +It lives in a narrow input-array module to keep the list `getD` dependency and initialization +bridge out of `Allocation`. This avoids changing the allocation interface and rebuilding its +unrelated importers when the input representation API changes. -/ @[expose] public section diff --git a/ComplexityTests/SchonhagePrunedInput.lean b/ComplexityTests/SchonhagePrunedInput.lean index 0b8448cb1..1992f1381 100644 --- a/ComplexityTests/SchonhagePrunedInput.lean +++ b/ComplexityTests/SchonhagePrunedInput.lean @@ -39,6 +39,34 @@ example : ∃ y n, n ≤ timeBound (L := 0) ([0, 0] : List (Fin 1)) ∧ -- The conservative address admission is explicit and has a checked failing boundary. example : Fits 10 (addressBound 0 2) ∧ ¬Fits 9 (addressBound 0 2) := by decide +kernel +-- The bilinear specialization has an admitted signed client: the supplied leaf arrays are +-- proved to be the actual encodingL/R of the original zero-depth arrays. Its same returned +-- memory witness is reached from State.init and holds -3 * 2 = -6 after the charged run. +example : InputFits 10 (input 0 (fun _ => -3) (fun _ => 2) [0]) ∧ + ∃ y n, n ≤ timeBound (L := 0) ([0] : List (Fin 1)) ∧ + Runs 10 (program.program output) (State.init (input 0 (fun _ => -3) (fun _ => 2) [0])) + ⟨program.size, y⟩ n ∧ + Executes 10 (program.program output) + (State.init (input 0 (fun _ => -3) (fun _ => 2) [0])) 6 (n + 1) ∧ + y.mem 6 = encode (-6) := by + obtain ⟨hi, y, n, hn, hr, he, _, hv, _⟩ := + runs_bilinear (w := 8) (L := 0) (A := 3) (B := 2) (by decide) + (fun _ => -3) (fun _ => 2) ([0] : List (Fin 1)) (fun _ => -3) (fun _ => 2) + (by + intro v + simp [Cslib.Algorithms.MatrixMultiplication.Schonhage.encodingL, + Cslib.Algorithms.MatrixMultiplication.Schonhage.encodeLeft]) + (by + intro v + simp [Cslib.Algorithms.MatrixMultiplication.Schonhage.encodingR, + Cslib.Algorithms.MatrixMultiplication.Schonhage.encodeRight]) + (by intro i; decide) (by intro i; decide) (by decide) (by decide) + refine ⟨hi, y, n, hn, hr, he, ?_⟩ + simpa +decide [outputBase, Cslib.Algorithms.MatrixMultiplication.Schonhage.bilinearProduct, + Cslib.Algorithms.MatrixMultiplication.Schonhage.Leaf.Contributes, + Cslib.Algorithms.MatrixMultiplication.Schonhage.encodeLeft, + Cslib.Algorithms.MatrixMultiplication.Schonhage.encodeRight] using hv 0 + -- The input-derived physical threshold is positive and guarantees actual runs at every larger -- width. It is a conservative sufficient threshold, rather than a claim of necessity. example : admissionWidth 0 2 3 2 = 13 := by decide +kernel diff --git a/ROADMAP.md b/ROADMAP.md index b0bce8e7b..3b0d8f55c 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -573,3 +573,5 @@ not planned, not as proved. [#1459]: https://github.com/cslib-community/complexitylib/issues/1459 [#1463]: https://github.com/cslib-community/complexitylib/issues/1463 [#1493]: https://github.com/cslib-community/complexitylib/issues/1493 +[#1523]: https://github.com/cslib-community/complexitylib/issues/1523 +[#1526]: https://github.com/cslib-community/complexitylib/issues/1526 diff --git a/UPSTREAM.md b/UPSTREAM.md index 153ca9ea9..9627488c6 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -235,6 +235,15 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he to exponent 1.9992, remain in #1394. The independent band producer is #1523; its catalogue retains the same leaf ordering and canonical signed words. Raw 0/1 matrix producers must actually double entries before copying into signed input arrays, with that work charged. + Stage `exec_*` lemmas are internal implementation APIs exported across proof modules; + callers use the flat initialized runs, width theorem or pointer/epoch kernel. The narrow + `WordRAM.ArrayAppend` and `WordRAM.InputArray` topic modules keep append/input dependencies + and interface changes away from the widely imported `Allocation` core. + Post-integration [#1553](https://github.com/cslib-community/complexitylib/issues/1553) includes + replacing pruned `powers` with sibling #1536's generic `Power.program`, preserving the + `4L+10` budget via five retained constant initializations plus its `4L+5` run. Its exact + register map, affected execution/width/cost clients and sibling integration order are + recorded there. This documentation/test repair does not change either sibling's program. * **Typed RAM upstream core.** [CSLib #1158](https://github.com/leanprover/cslib/pull/1158) at `beea60a15eddc91d93e92fa5af9fb965c351d0d5` proposes `Cslib/Computability/Machines/RAM/Basic.lean`, in namespace `Cslib.RAM.Typed`. From 4f6de797203eb5dac02b2cadd9e8fa33e7427d57 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Wed, 28 Oct 2026 22:22:06 -0400 Subject: [PATCH 36/73] fix(imports): deduplicate aggregate after counting integration --- Complexity.lean | 1 - 1 file changed, 1 deletion(-) diff --git a/Complexity.lean b/Complexity.lean index 432ee4c7f..5c2c4eead 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -6,7 +6,6 @@ public import Complexity.Algorithms.ExactTriangle.Lopsided public import Complexity.Algorithms.ExactTriangle.Splitting public import Complexity.Algorithms.ExactTriangle.Weights public import Complexity.Algorithms.ExactTriangle.Witnesses -public import Complexity.Algorithms.ExactTriangle.Lopsided public import Complexity.Algorithms.Lean.FusionTree.Capacity public import Complexity.Algorithms.Lean.FusionTree.Compression public import Complexity.Algorithms.Lean.FusionTree.Correction From 02f1b495322d8cfee81969186795670e3574defa Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Wed, 28 Oct 2026 22:22:08 -0400 Subject: [PATCH 37/73] fix(imports): deduplicate aggregate after query-splitting integration --- Complexity.lean | 1 - 1 file changed, 1 deletion(-) diff --git a/Complexity.lean b/Complexity.lean index 69abff193..f12edfac1 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -6,7 +6,6 @@ public import Complexity.Algorithms.ExactTriangle.Lopsided public import Complexity.Algorithms.ExactTriangle.Splitting public import Complexity.Algorithms.ExactTriangle.Weights public import Complexity.Algorithms.ExactTriangle.Witnesses -public import Complexity.Algorithms.ExactTriangle.Lopsided public import Complexity.Algorithms.Lean.FusionTree.Capacity public import Complexity.Algorithms.Lean.FusionTree.Compression public import Complexity.Algorithms.Lean.FusionTree.Correction From b79ae8e407f9f0349d2c14beb0d3e1aac8187e04 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Wed, 28 Oct 2026 22:53:41 -0400 Subject: [PATCH 38/73] fix(imports): regenerate aggregate after Chan-He parameter integration --- Complexity.lean | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Complexity.lean b/Complexity.lean index c31f3baad..cd891a699 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -90,14 +90,14 @@ public import Complexity.Algorithms.WordRAM.FusionTree.Rank public import Complexity.Algorithms.WordRAM.FusionTree.ReadHeader public import Complexity.Algorithms.WordRAM.FusionTree.Static public import Complexity.Algorithms.WordRAM.Histogram -public import Complexity.Algorithms.WordRAM.NatClog -public import Complexity.Algorithms.WordRAM.NatLog -public import Complexity.Algorithms.WordRAM.NatSqrt public import Complexity.Algorithms.WordRAM.Karger.Contract public import Complexity.Algorithms.WordRAM.Karger.FastCut public import Complexity.Algorithms.WordRAM.Karger.Graph public import Complexity.Algorithms.WordRAM.KargerMinCut public import Complexity.Algorithms.WordRAM.KargerStein +public import Complexity.Algorithms.WordRAM.NatClog +public import Complexity.Algorithms.WordRAM.NatLog +public import Complexity.Algorithms.WordRAM.NatSqrt public import Complexity.Algorithms.WordRAM.PrefixSums.Dynamic public import Complexity.Algorithms.WordRAM.PrefixSums.Query public import Complexity.Algorithms.WordRAM.PrefixSums.Tree From a6e4005b238b0267619f18652fd4a5af64079d63 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 28 Oct 2026 23:09:21 -0400 Subject: [PATCH 39/73] feat(wordram): route and group wanted thin-product queries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement #1561's complete native routing/grouping component. State.init prepends the native length cell to [L,m,N,Q] followed by raw coordinate pairs. Actual finite unsigned standard Code computes tables, tile keys, codes, counts, prefix offsets and stable scatter. No layout/run/time/permutation certificate is supplied. Fixed 96 registers, <=4096 flat slots including halt, no width reads. Wanted.runs_grouped proves actual State.init prefix Runs and charged halt at the same final memory, exact per-occurrence code/original-position equations, exact wantedStrings tile spans, in-array span/code bounds, permutation, terminal Q, input/outside frame and final boundedness. All admitted physical widths are covered by the positive clog2(wordBudget+1)+1 threshold, with original input fit derived. All initialized reachable prefixes are bounded. Complete native charge including halt <=128*(L+1)*(Q+numBands^2+2^L+1). No 10^L scan per query/tile. Validation: final tree 6c8164db02df348a46ffe0cf5972415977c62939. All SIX strict gates passed together: build --wfail --iofail; mk_all --check; test --wfail --iofail (including Smoke/global Axioms); lint; lint-style; checkInitImports. One Lean thread, cache downloads disabled. Full citation-aware hygiene --check-simp, fresh actual-base 57-library/3-test lints, changed-file/name/staged+unstaged diff checks all pass. The initial test-only module-boundary VM evaluation failure was fixed by using the existing non-module native-test convention; every interpreter check remains. The corrected frozen tree passed the complete six-gate sequence again. Base: #1562 ce77926c393ce34e7962759bb412172c4584a67c. Preserve normal merges. Unchanged sibling prerequisite #1554/#1536 is retained at exact 702c14bcf9896655b17c9b2dd5e02e6f270c9966: all 33 library and two test blobs are byte-identical. Wanted contributes 24 library modules and one test. Existing unchanged sibling reviews are reusable; no ancestor branch or remote was edited. Pinned source: anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5, 3sum-apsp/ThreeSumApsp/Sec2/Theorem5.lean:44-46, checked against its Git blob: "outStrOfPos lay I J ∈ wantedStrings lay W (bandOf L m I) (bandOf L m J)". Tiling/Definitions.lean:128-131 defines that filter/image set; reuse it unchanged with W=qs.toFinset and separately preserve original list occurrences. Source Programs/Sec2/Theorem5/Wanted/Codes.lean:13-19 motivates routing. Actual native code uses the existing least-significant-first queryIndex/leafIndex dictionary, digit9 for the inner wildcard and 3*row+col otherwise. It proves agreement rather than identifying the Light program's separate numbering, semantics or costs. Native code/proofs/stable-scatter theory are original; primitive counts reuse Mathlib Nat.count directly, with zero/successor/bound/monotonicity APIs and the existing count_eq_card_filter_range identity. Redundant count/offset wrappers and their repeated elementary lemmas are removed. Every client is migrated; Program.lean and PackProgram.lean are byte-identical to checked checkpoint 2e2cc39675a30b024c3932bd23f06163a33c8925. Imported finite source credit is retained. All three source slices equal the pinned Git blobs. Fully admitted kernel clients cover empty/zero depth, repeated pairs, and the L4/m1/N110/K0=2 asymmetric family, tying answers to actual Runs/halt. Actual VM regressions cover all expected grouped words, original indices, offsets and input/outside frame. Fitting and failing physical-width premises are checked. No sorry, custom axiom or native_decide. No curated abstract/catalogue/PDF impact. Shared-band calls, actual buffer/header adaptation, pruned-kernel invocation, answer reconstruction, sparse-product/Exact Triangle and full native ordinary 3SUM composition/1.9992 remain open under #1394. This is a component milestone. Fresh nonauthoring correctness/API/source review is pending (root arranges); outside review remains required. No PR, integration or approval claim. AI-assisted implementation: OpenAI Codex, session codex-three-sum-continuation-t3-3dda8289. Author/source credit is preserved. --- Complexity.lean | 24 +++ Complexity/Algorithms/Sorting/Counting.lean | 135 +++++++------- .../Schonhage/Wanted/Cost.lean | 48 +++++ .../Schonhage/Wanted/Count.lean | 7 +- .../Schonhage/Wanted/CountStep.lean | 14 +- .../Schonhage/Wanted/Exec.lean | 118 ++++++++++++ .../Schonhage/Wanted/Flat.lean | 66 +++++++ .../Schonhage/Wanted/GroupedRuns.lean | 91 +++++++++ .../Schonhage/Wanted/Groups.lean | 154 ++++++++++++++++ .../Schonhage/Wanted/Input.lean | 148 +++++++++++++++ .../Schonhage/Wanted/Pack.lean | 4 +- .../Schonhage/Wanted/Prefix.lean | 174 ++++++++++++++++++ .../Schonhage/Wanted/PrefixStep.lean | 93 ++++++++++ .../Schonhage/Wanted/Program.lean | 18 +- .../Schonhage/Wanted/Scatter.lean | 171 +++++++++++++++++ .../Schonhage/Wanted/ScatterStep.lean | 124 +++++++++++++ .../Schonhage/Wanted/ScatterWrite.lean | 105 +++++++++++ .../Schonhage/Wanted/Width.lean | 83 +++++++++ .../Computability/WordRAM/Sequence.lean | 43 +++++ ComplexityTests/SchonhageWanted.lean | 83 ++++++++- ROADMAP.md | 12 +- UPSTREAM.md | 17 +- 22 files changed, 1641 insertions(+), 91 deletions(-) create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Cost.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Exec.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Flat.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/GroupedRuns.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Groups.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Input.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Prefix.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/PrefixStep.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Scatter.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/ScatterStep.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/ScatterWrite.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Width.lean create mode 100644 Complexity/Computability/WordRAM/Sequence.lean diff --git a/Complexity.lean b/Complexity.lean index 47c9aebaa..709d85185 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -43,6 +43,7 @@ public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Orders public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Parameters public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Pruned public import Complexity.Algorithms.MatrixMultiplication.Schonhage.PrunedCounts +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.QueryDigits public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Sparse public import Complexity.Algorithms.MatrixMultiplication.Schonhage.SplitDigits public import Complexity.Algorithms.MatrixMultiplication.Schonhage.ThinProduct @@ -56,6 +57,7 @@ public import Complexity.Algorithms.Reachability.InductiveCounting.Semantics public import Complexity.Algorithms.Reachability.Reingold public import Complexity.Algorithms.Reachability.ReingoldEval public import Complexity.Algorithms.Reachability.Savitch +public import Complexity.Algorithms.Sorting.Counting public import Complexity.Algorithms.Streaming.CountMin.Basic public import Complexity.Algorithms.Streaming.CountMin.LinearHash public import Complexity.Algorithms.Streaming.CountMin.Probability @@ -184,6 +186,27 @@ public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Prune public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Weight public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Width public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Stage +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Cost +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Count +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.CountStep +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Exec +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Flat +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.GroupedRuns +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Groups +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Input +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Meaning +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Memory +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Pack +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.PackProgram +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Prefix +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.PrefixStep +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Program +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Route +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Scatter +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.ScatterStep +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.ScatterWrite +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Setup +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Width public import Complexity.Algorithms.WordRAM.PrefixSums.Dynamic public import Complexity.Algorithms.WordRAM.PrefixSums.Query public import Complexity.Algorithms.WordRAM.PrefixSums.Tree @@ -1728,6 +1751,7 @@ public import Complexity.Computability.WordRAM.RAMInput public import Complexity.Computability.WordRAM.Randomized.Defs public import Complexity.Computability.WordRAM.Randomized.Expected public import Complexity.Computability.WordRAM.Randomized.Structured +public import Complexity.Computability.WordRAM.Sequence public import Complexity.Computability.WordRAM.Signed.Arithmetic public import Complexity.Computability.WordRAM.Signed.Comparison public import Complexity.Computability.WordRAM.Signed.Compile diff --git a/Complexity/Algorithms/Sorting/Counting.lean b/Complexity/Algorithms/Sorting/Counting.lean index 381ff0bff..4b9c4a737 100644 --- a/Complexity/Algorithms/Sorting/Counting.lean +++ b/Complexity/Algorithms/Sorting/Counting.lean @@ -10,25 +10,28 @@ module public import Complexity.Init public import Mathlib.Data.Finset.Card public import Mathlib.Data.Finset.Range +public import Mathlib.Data.Nat.Count public import Mathlib.Data.Fintype.EquivFin +public import Mathlib.Order.Interval.Finset.Nat /-! # Stable counting-sort positions ## Main definitions -* `CountingSort.count`: occurrences of a key in an original input prefix. -* `CountingSort.offset`: occurrences of all smaller keys in the complete input. * `CountingSort.position`: stable scatter position of an original occurrence. ## Main results -* `CountingSort.offset_succ`: the prefix-sum update used by a native counting pass. +* `CountingSort.count_lt_succ`: the prefix-sum update used by a native counting pass. * `CountingSort.position_injective`: every original position has a distinct destination. * `CountingSort.position_bijective`: scatter destinations form a permutation of the input. +* `CountingSort.scatter_span_image`: an actual scattered array's bucket span has exactly + the values whose original keys select that bucket. These finite counting identities are original supporting mathematics for the native router. -They assert no sorting program or execution cost. +They reuse Mathlib's `Nat.count` and its prefix-count APIs directly; only the stable-scatter +construction and cross-bucket theory are added. They assert no program or execution cost. -/ @[expose] public section @@ -37,67 +40,25 @@ namespace Cslib.Algorithms.CountingSort open Finset -/-- Number of occurrences of bucket `b` among the first `n` original positions. -/ -def count (key : ℕ → ℕ) (n b : ℕ) : ℕ := ((range n).filter fun i => key i = b).card - -/-- Starting position of bucket `b`, after all smaller keys in the complete input. -/ -def offset (key : ℕ → ℕ) (n b : ℕ) : ℕ := ((range n).filter fun i => key i < b).card - /-- The stable destination of original position `i` in an input of length `n`. -/ -def position (key : ℕ → ℕ) (n i : ℕ) : ℕ := offset key n (key i) + count key i (key i) - -/-- Empty prefixes contain no occurrences. -/ -@[simp] -theorem count_zero (key : ℕ → ℕ) (b : ℕ) : count key 0 b = 0 := by simp [count] - -/-- Processing one original position increments just its bucket. -/ -theorem count_succ (key : ℕ → ℕ) (n b : ℕ) : - count key (n + 1) b = count key n b + if key n = b then 1 else 0 := by - by_cases h : key n = b <;> simp [count, range_add_one, filter_insert, h] - -/-- A prefix contains at most its original number of positions. -/ -theorem count_le (key : ℕ → ℕ) (n b : ℕ) : count key n b ≤ n := - (card_filter_le _ _).trans_eq (card_range n) - -/-- Extending the original prefix cannot remove an occurrence. -/ -theorem count_mono (key : ℕ → ℕ) {n n' : ℕ} (h : n ≤ n') (b : ℕ) : - count key n b ≤ count key n' b := - card_le_card (filter_subset_filter _ (range_mono h)) - -/-- The original occurrence at `i` has not yet been counted in its strict prefix. -/ -theorem count_lt_of_lt (key : ℕ → ℕ) {i n : ℕ} (h : i < n) : - count key i (key i) < count key n (key i) := by - have hs := count_succ key i (key i) - have hm := count_mono key (Nat.succ_le_of_lt h) (key i) - change count key (i + 1) (key i) ≤ count key n (key i) at hm - simp only [↓reduceIte] at hs - omega - -/-- The first bucket starts at zero. -/ -@[simp] -theorem offset_zero (key : ℕ → ℕ) (n : ℕ) : offset key n 0 = 0 := by simp [offset] - -/-- Every prefix offset lies inside the original input length. -/ -theorem offset_le (key : ℕ → ℕ) (n b : ℕ) : offset key n b ≤ n := - (card_filter_le _ _).trans_eq (card_range n) +def position (key : ℕ → ℕ) (n i : ℕ) : ℕ := + Nat.count (fun k => key k < key i) n + Nat.count (fun k => key k = key i) i /-- Larger keys start no earlier. -/ -theorem offset_mono (key : ℕ → ℕ) (n : ℕ) {b c : ℕ} (h : b ≤ c) : - offset key n b ≤ offset key n c := by - apply card_le_card - intro i hi - obtain ⟨hi, hb⟩ := mem_filter.mp hi - exact mem_filter.mpr ⟨hi, hb.trans_le h⟩ +private theorem offset_mono (key : ℕ → ℕ) (n : ℕ) {b c : ℕ} (h : b ≤ c) : + Nat.count (fun k => key k < b) n ≤ Nat.count (fun k => key k < c) n := by + exact Nat.count_mono_left (fun _ _ hk => hk.trans_le h) /-- One prefix-sum step appends exactly the current bucket's cardinality. -/ -theorem offset_succ (key : ℕ → ℕ) (n b : ℕ) : - offset key n (b + 1) = offset key n b + count key n b := by +theorem count_lt_succ (key : ℕ → ℕ) (n b : ℕ) : + Nat.count (fun k => key k < b + 1) n = + Nat.count (fun k => key k < b) n + Nat.count (fun k => key k = b) n := by have he : ((range n).filter fun i => key i < b + 1) = ((range n).filter fun i => key i < b) ∪ ((range n).filter fun i => key i = b) := by ext i simp only [mem_filter, mem_union, mem_range] omega - unfold offset count + simp only [Nat.count_eq_card_filter_range] rw [he, card_union_of_disjoint] exact disjoint_left.mpr (by intro i hi hj @@ -105,30 +66,22 @@ theorem offset_succ (key : ℕ → ℕ) (n b : ℕ) : have := (mem_filter.mp hj).2 omega) -/-- Once every admitted key is below `B`, its terminal offset is exactly the input length. -/ -theorem offset_full (key : ℕ → ℕ) (n B : ℕ) (h : ∀ i < n, key i < B) : - offset key n B = n := by - unfold offset - rw [filter_eq_self.mpr, card_range] - intro i hi - exact h i (mem_range.mp hi) - /-- Each occurrence lands before the next bucket, and after its own bucket's start. -/ theorem position_mem (key : ℕ → ℕ) {n i : ℕ} (hi : i < n) : - offset key n (key i) ≤ position key n i ∧ - position key n i < offset key n (key i + 1) := by - have := count_lt_of_lt key hi - rw [offset_succ] + Nat.count (fun k => key k < key i) n ≤ position key n i ∧ + position key n i < Nat.count (fun k => key k < key i + 1) n := by + have := Nat.count_strict_mono (p := fun j => key j = key i) rfl hi + rw [count_lt_succ] exact ⟨Nat.le_add_right _ _, Nat.add_lt_add_left ‹_› _⟩ /-- Stable scatter destinations are valid original-length indices. -/ theorem position_lt (key : ℕ → ℕ) {n i : ℕ} (hi : i < n) : position key n i < n := - (position_mem key hi).2.trans_le (offset_le key n (key i + 1)) + (position_mem key hi).2.trans_le (Nat.count_le _) /-- Original order is retained inside a bucket. -/ theorem position_lt_same (key : ℕ → ℕ) (n : ℕ) {i j : ℕ} (hij : i < j) (hkey : key i = key j) : position key n i < position key n j := by - have := count_lt_of_lt key hij + have := Nat.count_strict_mono (p := fun j => key j = key i) rfl hij simp only [position, hkey] at * omega @@ -164,4 +117,48 @@ theorem position_bijective (key : ℕ → ℕ) (n : ℕ) : fun i j h => position_injective key n (congrArg (fun j : Fin n => j.val) h) exact ⟨hi, Finite.surjective_of_injective hi⟩ +/-- A stable destination belongs to a bucket's half-open span exactly when its key is that +bucket. Equal values or repeated original requests do not change the interval criterion. -/ +theorem position_mem_iff (key : ℕ → ℕ) {n i : ℕ} (hi : i < n) (b : ℕ) : + (Nat.count (fun k => key k < b) n ≤ position key n i ∧ + position key n i < Nat.count (fun k => key k < b + 1) n) ↔ + key i = b := by + constructor + · intro h + rcases lt_trichotomy (key i) b with hb | hb | hb + · have hp := (position_mem key hi).2.trans_le (offset_mono key n (Nat.succ_le_of_lt hb)) + omega + · exact hb + · have hp := (offset_mono key n (Nat.succ_le_of_lt hb)).trans (position_mem key hi).1 + change Nat.count (fun k => key k < b + 1) n ≤ position key n i at hp + omega + · intro h + subst b + exact position_mem key hi + +/-- A memory array satisfying the stable-scatter equations has precisely the original +values selected by each bucket. No sorted list or permutation certificate is a premise. -/ +theorem scatter_span_image {α : Type*} [DecidableEq α] (key : ℕ → ℕ) (n b : ℕ) + (stored values : ℕ → α) (h : ∀ i < n, stored (position key n i) = values i) : + (Ico (Nat.count (fun k => key k < b) n) (Nat.count (fun k => key k < b + 1) n)).image stored = + ((range n).filter fun i => key i = b).image values := by + ext v + constructor + · intro hv + obtain ⟨j, hj, hv⟩ := mem_image.mp hv + have hjn : j < n := (mem_Ico.mp hj).2.trans_le (Nat.count_le _) + obtain ⟨i, hi⟩ := (position_bijective key n).surjective ⟨j, hjn⟩ + have hpos : position key n i.val = j := congrArg (fun t : Fin n => t.val) hi + have hkey : key i.val = b := (position_mem_iff key i.isLt b).mp (by + rw [hpos] + exact mem_Ico.mp hj) + exact mem_image.mpr ⟨i.val, mem_filter.mpr ⟨mem_range.mpr i.isLt, hkey⟩, + (h i.val i.isLt).symm.trans (by simpa only [hpos] using hv)⟩ + · intro hv + obtain ⟨i, hi, hv⟩ := mem_image.mp hv + obtain ⟨hin, hik⟩ := mem_filter.mp hi + have hlt := mem_range.mp hin + exact mem_image.mpr ⟨position key n i, + mem_Ico.mpr ((position_mem_iff key hlt b).mpr hik), (h i hlt).trans hv⟩ + end Cslib.Algorithms.CountingSort diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Cost.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Cost.lean new file mode 100644 index 000000000..5bba08969 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Cost.lean @@ -0,0 +1,48 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Memory +import Mathlib.Tactic.Linarith + +/-! +# Charged bound for native wanted-query grouping + +## Main definitions + +* `Wanted.timeBound`: sum of actual parameter/table, zero/count, prefix and scatter charges. + +## Main results + +* `Wanted.timeBound_add_one_le`: a bound polynomial in depth times query, tile and mask counts. + +These are original instruction bounds for the native implementation. Actual executions are +proved separately; no execution or complexity certificate is an input to the program. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Prefix instruction budget, before the final flat halt. All initialization and loops count. -/ +def timeBound (L m N Q : ℕ) : ℕ := + 16 * L + (12 * L + 33) * 2 ^ L + 15 * numBands L m N ^ 2 + (15 * L + 57) * Q + 98 + +/-- Including halt, routing is polynomial in depth times the original query, tile and +subset-table sizes. In particular it does not scan `10^L` cells per query or tile. -/ +theorem timeBound_add_one_le (L m N Q : ℕ) : + timeBound L m N Q + 1 ≤ + 128 * (L + 1) * (Q + numBands L m N ^ 2 + 2 ^ L + 1) := by + unfold timeBound + nlinarith only [Nat.zero_le (L * Q), Nat.zero_le (L * numBands L m N ^ 2), + Nat.zero_le (L * 2 ^ L), Nat.zero_le Q, Nat.zero_le (numBands L m N ^ 2), + Nat.zero_le (2 ^ L), Nat.zero_le L] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Count.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Count.lean index b04f8423c..2d460459d 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Count.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Count.lean @@ -114,7 +114,7 @@ theorem exec_countQueries {w L m N Q : ℕ} (hm : m ≤ L) (pairs : ℕ → ℕ (hfit : Fits w (wordBudget L m N Q)) : ∃ y, ExecWithin w countQueries x y (16 * Q + 3) ∧ Parameters L m N Q y ∧ (∀ t < numBands L m N ^ 2, - y.mem (countAddress L Q + t) = CountingSort.count (tileKey L m N ∘ pairs) Q t) ∧ + y.mem (countAddress L Q + t) = Nat.count (fun k => (tileKey L m N ∘ pairs) k = t) Q) ∧ (∀ a, a < countAddress L Q ∨ countAddress L Q + numBands L m N ^ 2 ≤ a → y.mem a = x.mem a) := by let key := tileKey L m N ∘ pairs @@ -122,7 +122,7 @@ theorem exec_countQueries {w L m N Q : ℕ} (hm : m ≤ L) (pairs : ℕ → ℕ let I (z : Data 96) : Prop := Parameters L m N Q z ∧ z.regs index ≤ Q ∧ (∀ i < Q, z.mem (5 + 2 * i) = (pairs i).1 ∧ z.mem (5 + 2 * i + 1) = (pairs i).2) ∧ - (∀ t < T, z.mem (countAddress L Q + t) = CountingSort.count key (z.regs index) t) ∧ + (∀ t < T, z.mem (countAddress L Q + t) = Nat.count (fun k => key k = t) (z.regs index)) ∧ (∀ a, a < countAddress L Q ∨ countAddress L Q + T ≤ a → z.mem a = x.mem a) let start := x.set index 0 have hs : ExecWithin w (ram[index ← #0] : Code .standard 96) x start 1 := by @@ -137,7 +137,7 @@ theorem exec_countQueries {w L m N Q : ℕ} (hm : m ≤ L) (pairs : ℕ → ℕ refine ⟨hsp, by simp [start], hinput, ?_, by simp [start]⟩ intro t ht simpa only [start, Data.set_regs, Function.update_self, Data.set_mem, - CountingSort.count_zero] using hzero t ht + Nat.count_zero] using hzero t ht have hb (z : Data 96) (hz : I z) (hc : (Cond.lt index queryCount).Holds z) : ∃ u, ExecWithin w countStep z u 14 ∧ I u ∧ Q - u.regs index < Q - z.regs index := by @@ -148,6 +148,7 @@ theorem exec_countQueries {w L m N Q : ℕ} (hm : m ≤ L) (pairs : ℕ → ℕ refine ⟨u, he, ⟨hu, by rw [hidx]; omega, ?_, ?_, ?_⟩, by rw [hidx]; omega⟩ · intro i hi have ha : 5 + 2 * i + 1 < countAddress L Q := by + have h2 := Nat.zero_le (2 ^ L) unfold countAddress tableAddress inputLimit omega rw [hf _ (Or.inl (by omega)), hf _ (Or.inl ha)] diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/CountStep.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/CountStep.lean index adc763aff..4bf0ff97a 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/CountStep.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/CountStep.lean @@ -53,12 +53,12 @@ theorem exec_countStep {w L m N Q n : ℕ} (hm : m ≤ L) (pairs : ℕ → ℕ (hinput : x.mem (5 + 2 * n) = (pairs n).1 ∧ x.mem (5 + 2 * n + 1) = (pairs n).2) (hcounts : ∀ t < numBands L m N ^ 2, - x.mem (countAddress L Q + t) = CountingSort.count (tileKey L m N ∘ pairs) n t) + x.mem (countAddress L Q + t) = Nat.count (fun k => (tileKey L m N ∘ pairs) k = t) n) (hfit : Fits w (wordBudget L m N Q)) : ∃ y, ExecWithin w countStep x y 14 ∧ Parameters L m N Q y ∧ y.regs index = n + 1 ∧ (∀ t < numBands L m N ^ 2, y.mem (countAddress L Q + t) = - CountingSort.count (tileKey L m N ∘ pairs) (n + 1) t) ∧ + Nat.count (fun k => (tileKey L m N ∘ pairs) k = t) (n + 1)) ∧ (∀ a, a < countAddress L Q ∨ countAddress L Q + numBands L m N ^ 2 ≤ a → y.mem a = x.mem a) := by let I := (pairs n).1 @@ -67,7 +67,7 @@ theorem exec_countStep {w L m N Q n : ℕ} (hm : m ≤ L) (pairs : ℕ → ℕ let β := bandOf L m I let γ := bandOf L m J let key := tileKey L m N ∘ pairs - let c := CountingSort.count key n (key n) + let c := Nat.count (fun k => key k = (key n)) n have ht : key n < B ^ 2 := tileKey_lt hm _ hpair.1 hpair.2 have hβ : β < B := bandOf_lt_numBands hm N I hpair.1 have hγ : γ < B := bandOf_lt_numBands hm N J hpair.2 @@ -75,7 +75,7 @@ theorem exec_countStep {w L m N Q n : ℕ} (hm : m ≤ L) (pairs : ℕ → ℕ have hBeq : B ^ 2 = numBands L m N ^ 2 := rfl have h10 := Nat.zero_le (10 ^ L) have hprod : β * B ≤ key n := by dsimp [key, tileKey, Function.comp_def, β, B, I]; omega - have hcle : c ≤ n := CountingSort.count_le _ _ _ + have hcle : c ≤ n := Nat.count_le _ have hl := local_fits hfit have hend := memoryLimit_eq L m N Q have hf (v : ℕ) (hv : v ≤ memoryLimit L m N Q) : Fits w v := hl.of_le (by omega) @@ -113,15 +113,15 @@ theorem exec_countStep {w L m N Q n : ℕ} (hm : m ≤ L) (pairs : ℕ → ℕ Fin.mk.injEq] refine ⟨y, he, countStep_parameters hx he, by simp [y, index], ?_, ?_⟩ · intro t ht' - change y.mem (countAddress L Q + t) = CountingSort.count key (n + 1) t - rw [CountingSort.count_succ] + change y.mem (countAddress L Q + t) = Nat.count (fun k => key k = t) (n + 1) + rw [Nat.count_succ] change (Function.update x.mem (countAddress L Q + key n) (c + 1)) (countAddress L Q + t) = _ by_cases h : key n = t · subst t simp only [Function.update_self, ↓reduceIte] rfl - · have hc' : x.mem (countAddress L Q + t) = CountingSort.count key n t := hcounts t ht' + · have hc' : x.mem (countAddress L Q + t) = Nat.count (fun k => key k = t) n := hcounts t ht' rw [Function.update_of_ne (by omega), hc', ite_eq_right h] omega · intro a ha diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Exec.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Exec.lean new file mode 100644 index 000000000..edd2d0b07 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Exec.lean @@ -0,0 +1,118 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Count +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Prefix +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Scatter +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Cost + +/-! +# Complete native routing from original scalar and coordinate inputs + +## Main results + +* `Wanted.exec_program`: compute tables, counts, prefix offsets, query codes and stable + scatter positions in the fixed native program, retaining the original input and outside frame. + +All table and cursor premises of the internal stages are established by actual preceding code. +The caller supplies only the original scalar headers and the ordinary coordinate list. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The complete native program, with no table, query-code, prefix or permutation oracle. +Every original query occurrence has one computed stable destination and retains its index. -/ +theorem exec_program {w L m N Q : ℕ} (hm : m ≤ L) (pairs : ℕ → ℕ × ℕ) + (hpairs : ∀ i < Q, (pairs i).1 < N ∧ (pairs i).2 < N) (x : Data 96) + (hL : x.mem 1 = L) (hmemory : x.mem 2 = m) (hN : x.mem 3 = N) (hQ : x.mem 4 = Q) + (hinput : ∀ i < Q, x.mem (5 + 2 * i) = (pairs i).1 ∧ + x.mem (5 + 2 * i + 1) = (pairs i).2) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w program x y (timeBound L m N Q) ∧ Parameters L m N Q y ∧ + (∀ i < Q, + y.mem (codeAddress L m N Q + CountingSort.position (tileKey L m N ∘ pairs) Q i) = + (PrunedQuery.queryIndex + (outStrOfPos (MaskTable.layout hm) (pairs i).1 (pairs i).2)).val ∧ + y.mem (originalAddress L m N Q + CountingSort.position (tileKey L m N ∘ pairs) Q i) = i) ∧ + (∀ t ≤ numBands L m N ^ 2, + y.mem (offsetAddress L m N Q + t) = Nat.count (fun k => (tileKey L m N ∘ pairs) k < t) Q) ∧ + y.mem (offsetAddress L m N Q + numBands L m N ^ 2) = Q ∧ + (∀ a, a < inputLimit Q ∨ memoryLimit L m N Q ≤ a → y.mem a = x.mem a) := by + let key := tileKey L m N ∘ pairs + have h2 := Nat.zero_le (2 ^ L) + have hT := Nat.zero_le (numBands L m N ^ 2) + have horder : inputLimit Q ≤ countAddress L Q ∧ + countAddress L Q + numBands L m N ^ 2 = offsetAddress L m N Q ∧ + offsetAddress L m N Q ≤ cursorAddressBase L m N Q ∧ + cursorAddressBase L m N Q ≤ codeAddress L m N Q ∧ + codeAddress L m N Q ≤ memoryLimit L m N Q := by + simp only [countAddress, tableAddress, offsetAddress, cursorAddressBase, + codeAddress, memoryLimit, originalAddress] + exact ⟨by omega, trivial, by omega, by omega, by omega⟩ + obtain ⟨a, ha, ap, atable, af⟩ := exec_tables hm x hL hmemory hN hQ hfit + obtain ⟨b, hb, bp, bz, bf⟩ := exec_clearCounts a ap hfit + have bin (i : ℕ) (hi : i < Q) : b.mem (5 + 2 * i) = (pairs i).1 ∧ + b.mem (5 + 2 * i + 1) = (pairs i).2 := by + have hbound : 5 + 2 * i + 1 < inputLimit Q := by unfold inputLimit; omega + rw [bf _ (Or.inl (by omega)), af _ (Or.inl (by omega)), + bf _ (Or.inl (by omega)), af _ (Or.inl hbound)] + exact hinput i hi + obtain ⟨c, hc, cp, ccounts, cf⟩ := exec_countQueries hm pairs hpairs b bp bin bz hfit + have hkeys : ∀ i < Q, key i < numBands L m N ^ 2 := by + intro i hi + exact tileKey_lt hm _ (hpairs i hi).1 (hpairs i hi).2 + obtain ⟨p, hp, pp, poff, pcur, plast, pf⟩ := exec_prefix key hkeys c cp ccounts hfit + have pinput (addr : ℕ) (hi : addr < inputLimit Q) : p.mem addr = x.mem addr := by + rw [pf _ (Or.inl (by omega)), cf _ (Or.inl (by omega)), + bf _ (Or.inl (by omega)), af _ (Or.inl hi)] + have pin (i : ℕ) (hi : i < Q) : p.mem (5 + 2 * i) = (pairs i).1 ∧ + p.mem (5 + 2 * i + 1) = (pairs i).2 := by + have hbound : 5 + 2 * i + 1 < inputLimit Q := by unfold inputLimit; omega + rw [pinput _ (by omega), pinput _ hbound] + exact hinput i hi + have ptable (gh : Fin (K0 L m) × Fin (K0 L m)) : + p.mem (tableAddress L Q + (MaskTable.pairRank gh).val) = (MaskTable.selectedMask gh).val := by + have hr : (MaskTable.pairRank gh).val < 2 ^ L := + (MaskTable.pairRank gh).isLt.trans_le (Nat.choose_le_two_pow L m) + have htable : tableAddress L Q + (MaskTable.pairRank gh).val < countAddress L Q := by + unfold countAddress + omega + rw [pf _ (Or.inl (by omega)), cf _ (Or.inl htable), bf _ (Or.inl htable)] + exact atable gh + obtain ⟨y, hy, yp, yout, _, yf⟩ := exec_scatter hm pairs hpairs p pp pin ptable pcur hfit + refine ⟨y, ?_, yp, yout, ?_, ?_, ?_⟩ + · have hrest := hb.seq (hc.seq (hp.seq hy)) + have hfull := ha.seq hrest + apply hfull.mono + unfold timeBound + nlinarith + · intro t ht + have hlow : offsetAddress L m N Q + t < cursorAddressBase L m N Q := by + unfold cursorAddressBase + omega + rw [yf _ (Or.inl hlow)] + exact poff t ht + · rw [yf _ (Or.inl (by unfold cursorAddressBase; omega))] + exact plast + · intro addr hi + rw [yf _ (by rcases hi with hi | hi <;> omega), + pf _ (by rcases hi with hi | hi <;> omega), + cf _ (by rcases hi with hi | hi <;> omega), + bf _ (by rcases hi with hi | hi <;> omega)] + apply af addr + rcases hi with hi | hi + · exact Or.inl hi + · exact Or.inr (by unfold countAddress at horder; omega) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Flat.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Flat.lean new file mode 100644 index 000000000..b23cece5a --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Flat.lean @@ -0,0 +1,66 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Input + +/-! +# Initialized flat runs of native wanted-query grouping + +## Main results + +* `Wanted.runs`: one actual initialized native run returns grouped query codes, their + original-position permutation and exact tile spans; halt is charged on that same run. +* `Wanted.bounded_reachable`: every initialized reachable prefix has fitting data words. + +The returned unsigned pointer names the code array. Other addresses are explicit functions of +the original headers, and the complete original input and outside memory are preserved. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Actual initialized standard WordRAM runs, with the same final memory carrying all +answers, a charged halt, and the readonly-input/outside frame. -/ +theorem runs {w L m N : ℕ} (hm : m ≤ L) (qs : List (Fin N × Fin N)) + (hfit : Fits w (wordBudget L m N qs.length)) : + InputFits w (input L m qs) ∧ + ∃ y n, n ≤ timeBound L m N qs.length ∧ + Runs w (program.program codes) (State.init (input L m qs)) ⟨program.size, y⟩ n ∧ + Executes w (program.program codes) (State.init (input L m qs)) + (codeAddress L m N qs.length) (n + 1) ∧ y.Bounded w ∧ + (∀ i : Fin qs.length, + y.mem (codeAddress L m N qs.length + + CountingSort.position (tileKey L m N ∘ inputPairs qs) qs.length i.val) = + (PrunedQuery.queryIndex + (outStrOfPos (MaskTable.layout hm) (qs.get i).1 (qs.get i).2)).val ∧ + y.mem (originalAddress L m N qs.length + + CountingSort.position (tileKey L m N ∘ inputPairs qs) qs.length i.val) = i.val) ∧ + (∀ t ≤ numBands L m N ^ 2, y.mem (offsetAddress L m N qs.length + t) = + Nat.count (fun k => (tileKey L m N ∘ inputPairs qs) k < t) qs.length) ∧ + y.mem (offsetAddress L m N qs.length + numBands L m N ^ 2) = qs.length ∧ + (∀ a, a < inputLimit qs.length ∨ memoryLimit L m N qs.length ≤ a → + y.mem a = (State.init (input L m qs) : State 96).data.mem a) := by + obtain ⟨hi, y, ⟨n, hn, he⟩, hp, hb, hv, ho, ht, hf⟩ := exec_input hm qs hfit + refine ⟨hi, y, n, hn, ?_, ?_, hb, hv, ho, ht, hf⟩ + · simpa only [Nat.zero_add, State.init] using he.runs (pc := 0) + (p := program.program codes) (Program.Contains.self _).append_left + · simpa only [hp.codes, State.init] using he.executes codes + +/-- Ordinary initialized native safety holds at every reachable prefix, including the +parameter, table, count and scatter stages, from the same original-input fit guarantee. -/ +theorem bounded_reachable {w L m N n : ℕ} (hm : m ≤ L) (qs : List (Fin N × Fin N)) + (hfit : Fits w (wordBudget L m N qs.length)) {y : State 96} + (hy : Runs w (program.program codes) (State.init (input L m qs)) y n) : + y.data.Bounded w := hy.bounded_init (input_fits hm qs hfit) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/GroupedRuns.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/GroupedRuns.lean new file mode 100644 index 000000000..fa19a71a2 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/GroupedRuns.lean @@ -0,0 +1,91 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Groups +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Width + +/-! +# Initialized runs with the finite wanted-set interface + +## Main results + +* `Wanted.runs_grouped`: one actual initialized native run computes each tile's query-code + span and a permutation retaining all original positions, within the charged bound. + +This is the routing interface for subsequent shared-band and pruned-kernel composition. +It does not evaluate the product or reconstruct answers. Stage execution lemmas in the +supporting modules are internal implementation APIs; consumers use this theorem or `runs`. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Finset + +/-- Actual native grouping from the original raw coordinate list at every sufficiently +wide physical width. The same final memory supplies the tile spans, permutation and frame. +No layout, query-code, count or permutation certificate is supplied as input. -/ +theorem runs_grouped {W L m N : ℕ} (hm : m ≤ L) (qs : List (Fin N × Fin N)) + (hW : admissionWidth L m N qs.length ≤ W) : + InputFits W (input L m qs) ∧ + ∃ y n, n + 1 ≤ 128 * (L + 1) * + (qs.length + numBands L m N ^ 2 + 2 ^ L + 1) ∧ + Runs W (program.program codes) (State.init (input L m qs)) ⟨program.size, y⟩ n ∧ + Executes W (program.program codes) (State.init (input L m qs)) + (codeAddress L m N qs.length) (n + 1) ∧ y.Bounded W ∧ + (∀ i : Fin qs.length, + y.mem (codeAddress L m N qs.length + + CountingSort.position (tileKey L m N ∘ inputPairs qs) qs.length i.val) = + (PrunedQuery.queryIndex + (outStrOfPos (MaskTable.layout hm) (qs.get i).1 (qs.get i).2)).val ∧ + y.mem (originalAddress L m N qs.length + + CountingSort.position (tileKey L m N ∘ inputPairs qs) qs.length i.val) = i.val) ∧ + (∀ T ∈ tiles L m N, + (Ico (y.mem (offsetAddress L m N qs.length + (T.1 * numBands L m N + T.2))) + (y.mem (offsetAddress L m N qs.length + (T.1 * numBands L m N + T.2 + 1)))).image + (fun j => y.mem (codeAddress L m N qs.length + j)) = + (wantedStrings (MaskTable.layout hm) qs.toFinset T.1 T.2).image + (fun w => (PrunedQuery.queryIndex w).val)) ∧ + (∀ j < qs.length, y.mem (codeAddress L m N qs.length + j) < 10 ^ L) ∧ + (∀ t < numBands L m N ^ 2, + y.mem (offsetAddress L m N qs.length + t) ≤ + y.mem (offsetAddress L m N qs.length + (t + 1)) ∧ + y.mem (offsetAddress L m N qs.length + (t + 1)) ≤ qs.length) ∧ + (∀ j < qs.length, y.mem (originalAddress L m N qs.length + j) < qs.length) ∧ + Function.Injective + (fun j : Fin qs.length => y.mem (originalAddress L m N qs.length + j.val)) ∧ + (range qs.length).image (fun j => y.mem (originalAddress L m N qs.length + j)) = + range qs.length ∧ + y.mem (offsetAddress L m N qs.length + numBands L m N ^ 2) = qs.length ∧ + (∀ a, a < inputLimit qs.length ∨ memoryLimit L m N qs.length ≤ a → + y.mem a = (State.init (input L m qs) : State 96).data.mem a) := by + obtain ⟨hi, y, n, hn, hr, he, hb, hv, ho, ht, hf⟩ := runs_wide hm qs hW + obtain ⟨hperm, hinj, hcover⟩ := originals_permutation qs y.mem (fun i => (hv i).2) + refine ⟨hi, y, n, (Nat.succ_le_succ hn).trans (timeBound_add_one_le L m N qs.length), + hr, he, hb, hv, ?_, ?_, ?_, hperm, hinj, hcover, ht, hf⟩ + · intro T hT + exact code_span_eq_wantedStrings hm qs y.mem T hT (fun i => (hv i).1) ho + · intro j hj + obtain ⟨i, hi⟩ := + (CountingSort.position_bijective (tileKey L m N ∘ inputPairs qs) qs.length).surjective + ⟨j, hj⟩ + have hp := congrArg (fun k : Fin qs.length => k.val) hi + change CountingSort.position (tileKey L m N ∘ inputPairs qs) qs.length i.val = j at hp + have hvj := (hv i).1 + rw [hp] at hvj + rw [hvj] + exact (PrunedQuery.queryIndex _).isLt + · intro t ht + rw [ho t ht.le, ho (t + 1) (Nat.succ_le_of_lt ht)] + exact ⟨Nat.count_mono_left (fun _ _ hk => Nat.lt_succ_of_lt hk), Nat.count_le _⟩ + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Groups.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Groups.lean new file mode 100644 index 000000000..fcbfc7d29 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Groups.lean @@ -0,0 +1,154 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Flat + +/-! +# Consumer meaning of the computed query groups + +## Main results + +* `Wanted.code_span_eq_wantedStrings`: the actual stored offsets select exactly the + original finite `wantedStrings` codes of each tile. +* `Wanted.originals_permutation`: the actual original-position words form a permutation, + retaining all occurrences, including duplicate pairs. + +These representation lemmas consume the memory equations returned by the initialized run. +They neither replace the native grouping code nor supply any input certificate to it. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Finset + +private theorem tileKey_eq_iff {L m N : ℕ} (hm : m ≤ L) (i j : Fin N) (T : ℕ × ℕ) + (hT : T ∈ tiles L m N) : + tileKey L m N (i.val, j.val) = T.1 * numBands L m N + T.2 ↔ + bandOf L m i.val = T.1 ∧ bandOf L m j.val = T.2 := by + have hb : T.1 < numBands L m N ∧ T.2 < numBands L m N := by + simpa only [tiles, mem_product, mem_range] using hT + let u : Fin (numBands L m N) := ⟨bandOf L m i.val, bandOf_lt_numBands hm N i i.isLt⟩ + let v : Fin (numBands L m N) := ⟨bandOf L m j.val, bandOf_lt_numBands hm N j j.isLt⟩ + constructor + · intro hk + have he : finProdFinEquiv (u, v) = finProdFinEquiv + ((⟨T.1, hb.1⟩ : Fin (numBands L m N)), ⟨T.2, hb.2⟩) := by + apply Fin.ext + change bandOf L m j.val + numBands L m N * bandOf L m i.val = + T.2 + numBands L m N * T.1 + unfold tileKey at hk + nlinarith only [hk] + have hp := finProdFinEquiv.injective he + exact ⟨congrArg (fun p => p.1.val) hp, congrArg (fun p => p.2.val) hp⟩ + · rintro ⟨hi, hj⟩ + simp only [tileKey, hi, hj] + +/-- A tile's actual stored half-open span contains exactly the existing mathematical +`wantedStrings` codes. Duplicate list occurrences have the same set meaning and are +retained separately by the original-position permutation. -/ +theorem code_span_eq_wantedStrings {L m N : ℕ} (hm : m ≤ L) + (qs : List (Fin N × Fin N)) (μ : ℕ → ℕ) (T : ℕ × ℕ) (hT : T ∈ tiles L m N) + (hvalues : ∀ i : Fin qs.length, + μ (codeAddress L m N qs.length + + CountingSort.position (tileKey L m N ∘ inputPairs qs) qs.length i.val) = + (PrunedQuery.queryIndex + (outStrOfPos (MaskTable.layout hm) (qs.get i).1 (qs.get i).2)).val) + (hoffsets : ∀ t ≤ numBands L m N ^ 2, + μ (offsetAddress L m N qs.length + t) = + Nat.count (fun k => (tileKey L m N ∘ inputPairs qs) k < t) qs.length) : + (Ico (μ (offsetAddress L m N qs.length + (T.1 * numBands L m N + T.2))) + (μ (offsetAddress L m N qs.length + (T.1 * numBands L m N + T.2 + 1)))).image + (fun j => μ (codeAddress L m N qs.length + j)) = + (wantedStrings (MaskTable.layout hm) qs.toFinset T.1 T.2).image + (fun w => (PrunedQuery.queryIndex w).val) := by + let key := tileKey L m N ∘ inputPairs qs + let values := fun i => (PrunedQuery.queryIndex + (outStrOfPos (MaskTable.layout hm) (inputPairs qs i).1 (inputPairs qs i).2)).val + let t := T.1 * numBands L m N + T.2 + have hb : T.1 < numBands L m N ∧ T.2 < numBands L m N := by + simpa only [tiles, mem_product, mem_range] using hT + have ht : t < numBands L m N ^ 2 := by + dsimp [t] + nlinarith only [Nat.mul_le_mul_right (numBands L m N) (Nat.succ_le_of_lt hb.1), hb.2] + have hv (i : ℕ) (hi : i < qs.length) : + μ (codeAddress L m N qs.length + CountingSort.position key qs.length i) = values i := by + dsimp only [values] + rw [inputPairs_get qs ⟨i, hi⟩] + exact hvalues ⟨i, hi⟩ + rw [hoffsets _ ht.le, hoffsets _ (Nat.succ_le_of_lt ht)] + rw [CountingSort.scatter_span_image key qs.length t _ values hv] + simp only [wantedStrings, image_image, Function.comp_def] + ext code + constructor + · intro hc + obtain ⟨i, hi, hc⟩ := mem_image.mp hc + obtain ⟨hin, hik⟩ := mem_filter.mp hi + have hiQ := mem_range.mp hin + have hp : inputPairs qs i = ((qs.get ⟨i, hiQ⟩).1.val, (qs.get ⟨i, hiQ⟩).2.val) := + inputPairs_get qs ⟨i, hiQ⟩ + have hk : tileKey L m N ((qs.get ⟨i, hiQ⟩).1.val, (qs.get ⟨i, hiQ⟩).2.val) = t := by + simpa only [key, Function.comp_def, hp] using hik + have hband := (tileKey_eq_iff hm (qs.get ⟨i, hiQ⟩).1 (qs.get ⟨i, hiQ⟩).2 T hT).mp hk + exact mem_image.mpr ⟨qs.get ⟨i, hiQ⟩, + mem_filter.mpr ⟨List.mem_toFinset.mpr (List.get_mem _ _), hband⟩, + by simpa only [values, hp] using hc⟩ + · intro hc + obtain ⟨q, hq, hc⟩ := mem_image.mp hc + obtain ⟨hq, hband⟩ := mem_filter.mp hq + obtain ⟨i, rfl⟩ := List.mem_iff_get.mp (List.mem_toFinset.mp hq) + have hp := inputPairs_get qs i + have hk : key i.val = t := by + dsimp only [key, Function.comp_def] + rw [hp] + exact (tileKey_eq_iff hm (qs.get i).1 (qs.get i).2 T hT).mpr hband + exact mem_image.mpr ⟨i.val, mem_filter.mpr ⟨mem_range.mpr i.isLt, hk⟩, + by simpa only [values, hp] using hc⟩ + +/-- The actual stored original indices are in range, injective, and cover every input +position. Equal original coordinate pairs therefore retain their separate occurrences. -/ +theorem originals_permutation {L m N : ℕ} (qs : List (Fin N × Fin N)) (μ : ℕ → ℕ) + (h : ∀ i : Fin qs.length, + μ (originalAddress L m N qs.length + + CountingSort.position (tileKey L m N ∘ inputPairs qs) qs.length i.val) = i.val) : + (∀ j < qs.length, μ (originalAddress L m N qs.length + j) < qs.length) ∧ + Function.Injective (fun j : Fin qs.length => μ (originalAddress L m N qs.length + j.val)) ∧ + (range qs.length).image (fun j => μ (originalAddress L m N qs.length + j)) = + range qs.length := by + let key := tileKey L m N ∘ inputPairs qs + have inverse (j : Fin qs.length) : ∃ i : Fin qs.length, + CountingSort.position key qs.length i.val = j.val ∧ + μ (originalAddress L m N qs.length + j.val) = i.val := by + obtain ⟨i, hi⟩ := (CountingSort.position_bijective key qs.length).surjective j + have hp := congrArg (fun k : Fin qs.length => k.val) hi + exact ⟨i, hp, hp ▸ h i⟩ + have hrange (j : ℕ) (hj : j < qs.length) : + μ (originalAddress L m N qs.length + j) < qs.length := by + obtain ⟨i, _, hi⟩ := inverse ⟨j, hj⟩ + exact hi ▸ i.isLt + refine ⟨hrange, ?_, ?_⟩ + · intro i j hij + obtain ⟨a, ha, hia⟩ := inverse i + obtain ⟨b, hb, hjb⟩ := inverse j + have hab : a.val = b.val := hia.symm.trans (hij.trans hjb) + exact Fin.ext (ha.symm.trans ((congrArg (CountingSort.position key qs.length) hab).trans hb)) + · ext i + constructor + · intro hi + obtain ⟨j, hj, rfl⟩ := mem_image.mp hi + exact mem_range.mpr (hrange j (mem_range.mp hj)) + · intro hi + have hin := mem_range.mp hi + exact mem_image.mpr ⟨CountingSort.position key qs.length i, + mem_range.mpr (CountingSort.position_lt key hin), h ⟨i, hin⟩⟩ + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Input.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Input.lean new file mode 100644 index 000000000..9af69d9fc --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Input.lean @@ -0,0 +1,148 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Exec +public import Complexity.Foundations.Data.List.RowMajor +import Mathlib.Data.List.GetD + +/-! +# Ordinary initialized wanted-position inputs + +## Main definitions + +* `Wanted.input`: four raw headers followed by the ordinary interleaved coordinate words. +* `Wanted.inputPairs`: a zero extension of that original finite list for natural-index invariants. + +## Main results + +* `Wanted.input_fits`: the original header, length and coordinate words fit the admitted width. +* `Wanted.input_word`: initialized memory contains each original coordinate at its actual address. +* `Wanted.exec_input`: the fixed program computes the grouped arrays from actual `State.init`. + +No powers, valid masks, tile keys, offsets, query codes or permutation words occur in the input. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Four unsigned scalar headers followed by each row/column pair in original list order. +The standard `State.init` length word precedes this payload at address zero. -/ +def input {N : ℕ} (L m : ℕ) (qs : List (Fin N × Fin N)) : List ℕ := + [L, m, N, qs.length] ++ (List.finRange qs.length).flatMap (fun i => + List.ofFn (fun s : Fin 2 => if s = 0 then (qs.get i).1.val else (qs.get i).2.val)) + +/-- Ordinary input positions extended by zero outside the supplied list. This is a +mathematical view of original words, not a query-code or layout certificate. -/ +def inputPairs {N : ℕ} (qs : List (Fin N × Fin N)) (i : ℕ) : ℕ × ℕ := + if hi : i < qs.length then ((qs.get ⟨i, hi⟩).1.val, (qs.get ⟨i, hi⟩).2.val) else (0, 0) + +/-- Every in-range position is exactly its original pair. -/ +@[simp] +theorem inputPairs_get {N : ℕ} (qs : List (Fin N × Fin N)) (i : Fin qs.length) : + inputPairs qs i.val = ((qs.get i).1.val, (qs.get i).2.val) := by + simp [inputPairs, i.isLt] + +/-- The native length header and coordinate input end at the concrete allocation boundary. -/ +theorem input_length {N : ℕ} (L m : ℕ) (qs : List (Fin N × Fin N)) : + (input L m qs).length + 1 = inputLimit qs.length := by + simp only [input, List.flatMap_ofFn_prod, List.length_append, List.length_ofFn, + List.length_cons, List.length_nil, inputLimit] + omega + +/-- Both original coordinates are below the original matrix dimension. -/ +theorem inputPairs_lt {N : ℕ} (qs : List (Fin N × Fin N)) (i : ℕ) (hi : i < qs.length) : + (inputPairs qs i).1 < N ∧ (inputPairs qs i).2 < N := by + have he := inputPairs_get qs ⟨i, hi⟩ + rw [he] + exact ⟨(qs.get ⟨i, hi⟩).1.isLt, (qs.get ⟨i, hi⟩).2.isLt⟩ + +/-- The exact initialized row/column word, with address calculations in original list order. -/ +theorem input_word {N : ℕ} (L m : ℕ) (qs : List (Fin N × Fin N)) (i : Fin qs.length) + (s : Fin 2) : + (State.init (input L m qs) : State 96).data.mem (5 + 2 * i.val + s.val) = + if s = 0 then (qs.get i).1.val else (qs.get i).2.val := by + change (Data.init (input L m qs) : Data 96).mem _ = _ + rw [show 5 + 2 * i.val + s.val = (4 + (i.val * 2 + s.val)) + 1 by omega, + Data.init_mem_succ] + unfold input + rw [List.getD_append_right _ _ _ _ (by simp)] + simp only [List.length_cons, List.length_nil, Nat.reduceAdd, Nat.add_sub_cancel_left] + exact List.getD_flatMap_ofFn (fun j t => + if t = (0 : Fin 2) then (qs.get j).1.val else (qs.get j).2.val) 0 i s + +/-- Header, length and every coordinate word fit from the same original numerical budget. -/ +theorem input_fits {w L m N : ℕ} (hm : m ≤ L) (qs : List (Fin N × Fin N)) + (hfit : Fits w (wordBudget L m N qs.length)) : InputFits w (input L m qs) := by + have hl := local_fits hfit + have hend := memoryLimit_eq L m N qs.length + have h2 := Nat.zero_le (2 ^ L) + have h10 := Nat.zero_le (10 ^ L) + have hfN : Fits w N := hl.of_le (by omega) + refine ⟨?_, ?_⟩ + · have hi := input_length L m qs + apply hl.of_le + unfold inputLimit at hi + omega + · intro v hv + simp only [input, List.mem_append, List.mem_cons, List.not_mem_nil, or_false] at hv + rcases hv with (rfl | rfl | rfl | rfl) | hv + · exact hl.of_le (by omega) + · exact hl.of_le (by omega) + · exact hfN + · exact hl.of_le (by omega) + · obtain ⟨i, _, hv⟩ := List.mem_flatMap.mp hv + obtain ⟨s, rfl⟩ := List.mem_ofFn.mp hv + split + · exact hfN.of_le (qs.get i).1.isLt.le + · exact hfN.of_le (qs.get i).2.isLt.le + +/-- Complete execution from the actual initialized list, with guaranteed input fit and a +bounded returned state. All auxiliary tables, counts, offsets and codes are computed natively. -/ +theorem exec_input {w L m N : ℕ} (hm : m ≤ L) (qs : List (Fin N × Fin N)) + (hfit : Fits w (wordBudget L m N qs.length)) : + InputFits w (input L m qs) ∧ + ∃ y, ExecWithin w program (State.init (input L m qs) : State 96).data y + (timeBound L m N qs.length) ∧ Parameters L m N qs.length y ∧ y.Bounded w ∧ + (∀ i : Fin qs.length, + y.mem (codeAddress L m N qs.length + + CountingSort.position (tileKey L m N ∘ inputPairs qs) qs.length i.val) = + (PrunedQuery.queryIndex + (outStrOfPos (MaskTable.layout hm) (qs.get i).1 (qs.get i).2)).val ∧ + y.mem (originalAddress L m N qs.length + + CountingSort.position (tileKey L m N ∘ inputPairs qs) qs.length i.val) = i.val) ∧ + (∀ t ≤ numBands L m N ^ 2, y.mem (offsetAddress L m N qs.length + t) = + Nat.count (fun k => (tileKey L m N ∘ inputPairs qs) k < t) qs.length) ∧ + y.mem (offsetAddress L m N qs.length + numBands L m N ^ 2) = qs.length ∧ + (∀ a, a < inputLimit qs.length ∨ memoryLimit L m N qs.length ≤ a → + y.mem a = (State.init (input L m qs) : State 96).data.mem a) := by + have hfitInput := input_fits hm qs hfit + let x : Data 96 := (State.init (input L m qs)).data + have hL : x.mem 1 = L := by simp [x, State.init, Data.init, input] + have hM : x.mem 2 = m := by simp [x, State.init, Data.init, input] + have hN : x.mem 3 = N := by simp [x, State.init, Data.init, input] + have hQ : x.mem 4 = qs.length := by simp [x, State.init, Data.init, input] + have hwords (i : ℕ) (hi : i < qs.length) : + x.mem (5 + 2 * i) = (inputPairs qs i).1 ∧ + x.mem (5 + 2 * i + 1) = (inputPairs qs i).2 := by + have hr := input_word L m qs ⟨i, hi⟩ 0 + have hc := input_word L m qs ⟨i, hi⟩ 1 + rw [inputPairs_get qs ⟨i, hi⟩] + simpa only [Fin.val_zero, Fin.val_one, Nat.add_zero, + ↓reduceIte, Fin.isValue, Fin.reduceEq] using And.intro hr hc + obtain ⟨y, he, hp, hv, ho, hlast, hf⟩ := exec_program hm (inputPairs qs) + (inputPairs_lt qs) x hL hM hN hQ hwords hfit + refine ⟨hfitInput, y, he, hp, he.bounded (Data.Bounded.init hfitInput), ?_, ho, hlast, hf⟩ + intro i + simpa only [inputPairs_get] using hv i.val i.isLt + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Pack.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Pack.lean index 50c2f5a70..574eae785 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Pack.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Pack.lean @@ -176,7 +176,9 @@ theorem exec_packBody {w L M R C : ℕ} (x : Data 16) (hx : PackProgress L M R C omega · have hv := hx.value rw [hrest, pack, ite_eq_right hb] at hv - simp [y, mask, row, col, code, power, level, bit, digit, temporary] + simp only [bit, Fin.isValue, mask, digit, temporary, power, code, level, Data.set_regs, + ne_eq, Fin.reduceEq, not_false_eq_true, Function.update_of_ne, Function.update_self, + row, col, y] nlinarith only [hv] · change L - (x.regs level + 1) < L - x.regs level omega diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Prefix.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Prefix.lean new file mode 100644 index 000000000..a55e9bf18 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Prefix.lean @@ -0,0 +1,174 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.PrefixStep +public import Complexity.Computability.WordRAM.Sequence + +/-! +# Complete native tile-prefix pass + +## Main results + +* `Wanted.exec_prefix`: compute every exact tile span and initialize its scatter cursor, + including the terminal query-count offset, with native loads, stores and loop controls. + +This is an internal implementation API of the complete routing program. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The actual prefix loop initializes every immutable span and mutable cursor. Neither +the offsets nor the total input length are supplied as precomputed memory certificates. -/ +theorem exec_prefix {w L m N Q : ℕ} (key : ℕ → ℕ) + (hkey : ∀ i < Q, key i < numBands L m N ^ 2) (x : Data 96) + (hx : Parameters L m N Q x) + (hcounts : ∀ t < numBands L m N ^ 2, + x.mem (countAddress L Q + t) = Nat.count (fun k => key k = t) Q) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w prefixCode x y (10 * numBands L m N ^ 2 + 6) ∧ + Parameters L m N Q y ∧ + (∀ t ≤ numBands L m N ^ 2, + y.mem (offsetAddress L m N Q + t) = Nat.count (fun k => key k < t) Q) ∧ + (∀ t < numBands L m N ^ 2, + y.mem (cursorAddressBase L m N Q + t) = Nat.count (fun k => key k < t) Q) ∧ + y.mem (offsetAddress L m N Q + numBands L m N ^ 2) = Q ∧ + (∀ a, a < offsetAddress L m N Q ∨ codeAddress L m N Q ≤ a → y.mem a = x.mem a) := by + let T := numBands L m N ^ 2 + let I (z : Data 96) : Prop := Parameters L m N Q z ∧ z.regs index ≤ T ∧ + z.regs total = Nat.count (fun k => key k < (z.regs index)) Q ∧ + (∀ t < z.regs index, + z.mem (offsetAddress L m N Q + t) = Nat.count (fun k => key k < t) Q ∧ + z.mem (cursorAddressBase L m N Q + t) = Nat.count (fun k => key k < t) Q) ∧ + (∀ t < T, z.mem (countAddress L Q + t) = Nat.count (fun k => key k = t) Q) ∧ + (∀ a, a < offsetAddress L m N Q ∨ codeAddress L m N Q ≤ a → z.mem a = x.mem a) + let start := (x.set index 0).set total 0 + have hs : ExecWithin w (ram[index ← #0; total ← #0] : Code .standard 96) x start 2 := by + ram_eval [start] + have hp : Parameters L m N Q start := hx.reframe (by + intro i hi + apply hs.regs_eq + have h : ∀ i ∈ parameterRegisters, + (ram[index ← #0; total ← #0] : Code .standard 96).Preserves i := by decide +kernel + exact h i hi) + have hi : I start := by + refine ⟨hp, by simp [start, index, total], ?_, by simp [start, index, total], + hcounts, by simp [start]⟩ + simp [start, index, total] + have hb (z : Data 96) (hz : I z) (hc : (Cond.lt index tileCount).Holds z) : + ∃ u, ExecWithin w prefixStep z u 8 ∧ I u ∧ + T - u.regs index < T - z.regs index := by + have hn : z.regs index < T := by + simpa only [Cond.holds_lt, hz.1.tileCount] using hc + obtain ⟨u, he, hu, hidx, hsum, hoff, hcur, hf⟩ := exec_prefixStep key hn z hz.1 rfl + hz.2.2.1 (hz.2.2.2.2.1 _ hn) hfit + refine ⟨u, he, ⟨hu, by rw [hidx]; omega, ?_, ?_, ?_, ?_⟩, by rw [hidx]; omega⟩ + · simpa only [hidx] using hsum + · intro t ht + rw [hidx] at ht + by_cases ht' : t = z.regs index + · subst t + exact ⟨hoff, hcur⟩ + · have hlt : t < z.regs index := by omega + have hsep : offsetAddress L m N Q + t < cursorAddressBase L m N Q := by + unfold cursorAddressBase + omega + have hsep' : offsetAddress L m N Q + z.regs index < cursorAddressBase L m N Q := by + unfold cursorAddressBase + dsimp [T] at hn + omega + rw [hf _ (by omega) (by omega), hf _ (by omega) (by omega)] + exact hz.2.2.2.1 t hlt + · intro t ht + have hcount : countAddress L Q + t < offsetAddress L m N Q := by + unfold offsetAddress + exact Nat.add_lt_add_left ht _ + have hcursor : offsetAddress L m N Q ≤ cursorAddressBase L m N Q := by + unfold cursorAddressBase + omega + rw [hf _ (by omega) (by omega)] + exact hz.2.2.2.2.1 t ht + · intro a ha + have hsep : cursorAddressBase L m N Q + z.regs index < codeAddress L m N Q := by + unfold codeAddress + exact Nat.add_lt_add_left hn _ + have hsep' : offsetAddress L m N Q + z.regs index < codeAddress L m N Q := by + unfold codeAddress cursorAddressBase + dsimp [T] at hn + omega + rw [hf _ (by rcases ha with ha | ha <;> omega) + (by rcases ha with ha | ha <;> unfold cursorAddressBase at * <;> omega)] + exact hz.2.2.2.2.2 a ha + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I (fun z => T - z.regs index) hb hi + have hn : y.regs index = T := by + have := hy.2.1 + simp only [Cond.holds_lt, hy.1.tileCount] at hc + dsimp [T] at * + omega + have hsum : y.regs total = Nat.count (fun k => key k < T) Q := by + simpa only [hn] using hy.2.2.1 + have hl := local_fits hfit + have hend := memoryLimit_eq L m N Q + have h10 := Nat.zero_le (10 ^ L) + have haddr : Fits w (offsetAddress L m N Q + T) := hl.of_le (by + unfold offsetAddress countAddress tableAddress inputLimit + dsimp [T] + omega) + let z := (y.set pointer (offsetAddress L m N Q + T)).write + (offsetAddress L m N Q + T) (Nat.count (fun k => key k < T) Q) + have hz : ExecWithin w (ram[pointer ← offsets + tileCount; *pointer ← total] : + Code .standard 96) y z 2 := by + ram_eval [z, hy.1.offsets, hy.1.tileCount, hsum, haddr, T, + pointer, offsets, tileCount, total, Fin.mk.injEq] + have zp : Parameters L m N Q z := hy.1.reframe (by + intro i hi + apply hz.regs_eq + have h : ∀ i ∈ parameterRegisters, + (ram[pointer ← offsets + tileCount; *pointer ← total] : Code .standard 96).Preserves i := by + decide +kernel + exact h i hi) + have hlast : Nat.count (fun k => key k < T) Q = Q := Nat.count_of_forall hkey + refine ⟨z, ?_, zp, ?_, ?_, ?_, ?_⟩ + · apply (ExecWithin.seq_assoc.mp (hs.seq (he.seq hz))).mono + have h0 : start.regs index = 0 := by simp [start, index, total] + rw [h0, Nat.sub_zero] + dsimp [T] + omega + · intro t ht + by_cases h : t = T + · subst t + exact Function.update_self _ _ _ + · have ht' : t < y.regs index := by rw [hn]; omega + change Function.update y.mem (offsetAddress L m N Q + T) + (Nat.count (fun k => key k < T) Q) (offsetAddress L m N Q + t) = _ + rw [Function.update_of_ne (by omega)] + exact (hy.2.2.2.1 t ht').1 + · intro t ht + change Function.update y.mem (offsetAddress L m N Q + T) + (Nat.count (fun k => key k < T) Q) (cursorAddressBase L m N Q + t) = _ + rw [Function.update_of_ne (by unfold cursorAddressBase; omega)] + exact (hy.2.2.2.1 t (by rw [hn]; exact ht)).2 + · change Function.update y.mem (offsetAddress L m N Q + T) + (Nat.count (fun k => key k < T) Q) (offsetAddress L m N Q + T) = Q + rw [Function.update_self, hlast] + · intro a ha + change Function.update y.mem (offsetAddress L m N Q + T) + (Nat.count (fun k => key k < T) Q) a = x.mem a + rw [Function.update_of_ne (by + rcases ha with ha | ha + · omega + · unfold codeAddress cursorAddressBase at ha + omega)] + exact hy.2.2.2.2.2 a ha + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/PrefixStep.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/PrefixStep.lean new file mode 100644 index 000000000..2e3a9d002 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/PrefixStep.lean @@ -0,0 +1,93 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Setup + +/-! +# One native tile-prefix update + +## Main results + +* `Wanted.exec_prefixStep`: load the actual bucket count, store its immutable start and + mutable cursor, and advance the running sum with every instruction charged. + +This stage lemma is an internal implementation API for the complete grouping execution. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- One actual prefix update; the two stored cells are the only possible memory changes. -/ +theorem exec_prefixStep {w L m N Q t : ℕ} (key : ℕ → ℕ) + (ht : t < numBands L m N ^ 2) (x : Data 96) (hx : Parameters L m N Q x) + (hi : x.regs index = t) (hs : x.regs total = Nat.count (fun k => key k < t) Q) + (hc : x.mem (countAddress L Q + t) = Nat.count (fun k => key k = t) Q) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w prefixStep x y 8 ∧ Parameters L m N Q y ∧ + y.regs index = t + 1 ∧ y.regs total = Nat.count (fun k => key k < (t + 1)) Q ∧ + y.mem (offsetAddress L m N Q + t) = Nat.count (fun k => key k < t) Q ∧ + y.mem (cursorAddressBase L m N Q + t) = Nat.count (fun k => key k < t) Q ∧ + (∀ a, a ≠ offsetAddress L m N Q + t → a ≠ cursorAddressBase L m N Q + t → + y.mem a = x.mem a) := by + have hl := local_fits hfit + have hend := memoryLimit_eq L m N Q + have h10 := Nat.zero_le (10 ^ L) + have hf (v : ℕ) (hv : v ≤ memoryLimit L m N Q) : Fits w v := hl.of_le (by omega) + have hcount : Fits w (countAddress L Q + t) := hf _ (by + unfold countAddress tableAddress inputLimit + omega) + have hoff : Fits w (offsetAddress L m N Q + t) := hf _ (by + unfold offsetAddress countAddress tableAddress inputLimit + omega) + have hcursor : Fits w (cursorAddressBase L m N Q + t) := hf _ (by + unfold cursorAddressBase offsetAddress countAddress tableAddress inputLimit + omega) + have hadd : Fits w (Nat.count (fun k => key k < t) Q + Nat.count (fun k => key k = t) Q) := + hf _ (by + rw [← CountingSort.count_lt_succ] + have := Nat.count_le (fun i => key i < t + 1) (n := Q) + omega) + have hnext : Fits w (t + 1) := hf _ (by omega) + let s := Nat.count (fun k => key k < t) Q + let c := Nat.count (fun k => key k = t) Q + let y := ((((((((x.set pointer (countAddress L Q + t)).set count c).set + pointer (offsetAddress L m N Q + t)).write (offsetAddress L m N Q + t) s).set + pointer (cursorAddressBase L m N Q + t)).write (cursorAddressBase L m N Q + t) s).set + total (s + c)).set index (t + 1)) + have he : ExecWithin w prefixStep x y 8 := by + ram_eval [prefixStep, y, hx.counts, hx.offsets, hx.cursors, hx.tables.one, + hi, hs, hc, hcount, hoff, hcursor, hadd, hnext, s, c, + pointer, count, counts, offsets, cursors, total, index, BandPreparation.one, + Fin.mk.injEq] + have hp : Parameters L m N Q y := hx.reframe (by + intro i hi + apply he.regs_eq + have h : ∀ i ∈ parameterRegisters, prefixStep.Preserves i := by decide +kernel + exact h i hi) + have hsep : offsetAddress L m N Q + t ≠ cursorAddressBase L m N Q + t := by + unfold cursorAddressBase + omega + refine ⟨y, he, hp, by simp [y, index], ?_, ?_, ?_, ?_⟩ + · simpa only [y, Data.set_regs, Data.write_regs, total, index, + Function.update_of_ne (by decide : (30 : Fin 96) ≠ 23), Function.update_self, + s, c] using (CountingSort.count_lt_succ key Q t).symm + · change Function.update (Function.update x.mem (offsetAddress L m N Q + t) s) + (cursorAddressBase L m N Q + t) s (offsetAddress L m N Q + t) = s + rw [Function.update_of_ne hsep, Function.update_self] + · exact Function.update_self _ _ _ + · intro a ho hc + change Function.update (Function.update x.mem (offsetAddress L m N Q + t) s) + (cursorAddressBase L m N Q + t) s a = x.mem a + rw [Function.update_of_ne hc, Function.update_of_ne ho] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Program.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Program.lean index cf90403ba..dd5b245ca 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Program.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Program.lean @@ -25,7 +25,17 @@ No sorted request list, layout certificate or dispatch decision is supplied as i * `Wanted.program`: computed tables followed by counting, prefix and stable scatter passes. These programs use only the existing standard unsigned instructions. Correctness, width, -frame and resource theorems are separate from the definitions. +frame and resource theorems are separate from the definitions. Supporting `exec_*` lemmas +are internal implementation APIs exported across proof modules; consumers use `runs`, +`runs_grouped` or the generic-memory `exec_program` when composing a caller. + +## References + +The routing goal comes from [AlmanWilliams2026, Section 2.4.4] and Anthropic's pinned +[Wanted/Codes.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Programs/Sec2/Theorem5/Wanted/Codes.lean) at revision +`16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. This is original native code with its own charged +proofs, using the existing finite output-string definitions. It does not import the source +Light machine, its sorting procedure or its instruction counts. -/ @[expose] public section @@ -182,8 +192,8 @@ def scatter : Code .standard 96 := /-- Complete initialized native routing; no mathematical layout is an input word. -/ def program : Code .standard 96 := - .seq BandPreparation.header (.seq BandPreparation.countCode (.seq setup - (.seq BandPreparation.tablesCode (.seq clearCounts - (.seq countQueries (.seq prefixCode scatter)))))) + .seq (.seq BandPreparation.header (.seq BandPreparation.countCode + (.seq setup BandPreparation.tablesCode))) + (.seq clearCounts (.seq countQueries (.seq prefixCode scatter))) end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Scatter.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Scatter.lean new file mode 100644 index 000000000..15a26876e --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Scatter.lean @@ -0,0 +1,171 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.ScatterStep + +/-! +# Complete native wanted-query scatter + +## Main results + +* `Wanted.exec_scatter`: compute every original position's output code and stable destination, + retaining repetitions and writing the original-position permutation with a charged bound. + +This internal implementation API composes the actual router and stores. The finite scatter +permutation is proved from occurrence counts; it is neither supplied nor checked by an oracle. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The actual original-order scan fills all grouped codes and the original-position +permutation. Its only writes are scatter cursors and the two grouped output arrays. -/ +theorem exec_scatter {w L m N Q : ℕ} (hm : m ≤ L) (pairs : ℕ → ℕ × ℕ) + (hpairs : ∀ i < Q, (pairs i).1 < N ∧ (pairs i).2 < N) (x : Data 96) + (hx : Parameters L m N Q x) + (hinput : ∀ i < Q, x.mem (5 + 2 * i) = (pairs i).1 ∧ + x.mem (5 + 2 * i + 1) = (pairs i).2) + (htable : ∀ gh : Fin (K0 L m) × Fin (K0 L m), + x.mem (tableAddress L Q + (MaskTable.pairRank gh).val) = (MaskTable.selectedMask gh).val) + (hcursors : ∀ t < numBands L m N ^ 2, + x.mem (cursorAddressBase L m N Q + t) = Nat.count (fun k => (tileKey L m N ∘ pairs) k < t) Q) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w scatter x y ((15 * L + 41) * Q + 3) ∧ Parameters L m N Q y ∧ + (∀ i < Q, + y.mem (codeAddress L m N Q + CountingSort.position (tileKey L m N ∘ pairs) Q i) = + (PrunedQuery.queryIndex + (outStrOfPos (MaskTable.layout hm) (pairs i).1 (pairs i).2)).val ∧ + y.mem (originalAddress L m N Q + CountingSort.position (tileKey L m N ∘ pairs) Q i) = i) ∧ + (∀ t < numBands L m N ^ 2, y.mem (cursorAddressBase L m N Q + t) = + Nat.count (fun k => (tileKey L m N ∘ pairs) k < (t + 1)) Q) ∧ + (∀ a, a < cursorAddressBase L m N Q ∨ memoryLimit L m N Q ≤ a → + y.mem a = x.mem a) := by + let key := tileKey L m N ∘ pairs + let I (z : Data 96) : Prop := Parameters L m N Q z ∧ z.regs index ≤ Q ∧ + (∀ i < Q, z.mem (5 + 2 * i) = (pairs i).1 ∧ + z.mem (5 + 2 * i + 1) = (pairs i).2) ∧ + (∀ gh : Fin (K0 L m) × Fin (K0 L m), + z.mem (tableAddress L Q + (MaskTable.pairRank gh).val) = (MaskTable.selectedMask gh).val) ∧ + (∀ t < numBands L m N ^ 2, z.mem (cursorAddressBase L m N Q + t) = + Nat.count (fun k => key k < t) Q + Nat.count (fun k => key k = t) (z.regs index)) ∧ + (∀ i < z.regs index, + z.mem (codeAddress L m N Q + CountingSort.position key Q i) = + (PrunedQuery.queryIndex + (outStrOfPos (MaskTable.layout hm) (pairs i).1 (pairs i).2)).val ∧ + z.mem (originalAddress L m N Q + CountingSort.position key Q i) = i) ∧ + (∀ a, a < cursorAddressBase L m N Q ∨ memoryLimit L m N Q ≤ a → z.mem a = x.mem a) + let start := x.set index 0 + have hs : ExecWithin w (ram[index ← #0] : Code .standard 96) x start 1 := by + ram_eval [start] + have hp : Parameters L m N Q start := hx.reframe (by + intro i hi + apply hs.regs_eq + have h : ∀ i ∈ parameterRegisters, (ram[index ← #0] : Code .standard 96).Preserves i := by + decide +kernel + exact h i hi) + have hi : I start := by + refine ⟨hp, by simp [start], hinput, htable, ?_, by simp [start], by simp [start]⟩ + intro t ht + simpa only [start, Data.set_regs, Function.update_self, Data.set_mem, + Nat.count_zero, Nat.add_zero] using hcursors t ht + have hb (z : Data 96) (hz : I z) (hc : (Cond.lt index queryCount).Holds z) : + ∃ u, ExecWithin w scatterStep z u (15 * L + 39) ∧ I u ∧ + Q - u.regs index < Q - z.regs index := by + have hn : z.regs index < Q := by + simpa only [Cond.holds_lt, hz.1.queryCount] using hc + obtain ⟨u, he, hu, hidx, hcur, hcode, horig, hf⟩ := exec_scatterStep hm pairs hn + (hpairs _ hn) z hz.1 rfl (hz.2.2.1 _ hn) hz.2.2.2.1 hz.2.2.2.2.1 hfit + change ∀ a, a ≠ cursorAddressBase L m N Q + key (z.regs index) → + a ≠ codeAddress L m N Q + CountingSort.position key Q (z.regs index) → + a ≠ originalAddress L m N Q + CountingSort.position key Q (z.regs index) → + u.mem a = z.mem a at hf + have hk : key (z.regs index) < numBands L m N ^ 2 := tileKey_lt hm _ (hpairs _ hn).1 + (hpairs _ hn).2 + have hpos : CountingSort.position key Q (z.regs index) < Q := + CountingSort.position_lt key hn + have hcursor : cursorAddressBase L m N Q + key (z.regs index) < codeAddress L m N Q := by + unfold codeAddress + omega + have hcodes : codeAddress L m N Q + CountingSort.position key Q (z.regs index) < + originalAddress L m N Q := by + unfold originalAddress + omega + have horiginals : originalAddress L m N Q + CountingSort.position key Q (z.regs index) < + memoryLimit L m N Q := by + unfold memoryLimit + omega + have horder : cursorAddressBase L m N Q ≤ codeAddress L m N Q ∧ + codeAddress L m N Q ≤ originalAddress L m N Q ∧ + originalAddress L m N Q ≤ memoryLimit L m N Q := by + simp only [codeAddress, originalAddress, memoryLimit] + omega + have hlow (a : ℕ) (ha : a < cursorAddressBase L m N Q) : u.mem a = z.mem a := + hf a (by omega) (by omega) (by omega) + refine ⟨u, he, ⟨hu, by rw [hidx]; omega, ?_, ?_, ?_, ?_, ?_⟩, by rw [hidx]; omega⟩ + · intro i hi + have h2 := Nat.zero_le (2 ^ L) + have ha : 5 + 2 * i + 1 < cursorAddressBase L m N Q := by + unfold cursorAddressBase offsetAddress countAddress tableAddress inputLimit + omega + rw [hlow _ (by omega), hlow _ ha] + exact hz.2.2.1 i hi + · intro gh + have hr : (MaskTable.pairRank gh).val < 2 ^ L := + (MaskTable.pairRank gh).isLt.trans_le (Nat.choose_le_two_pow L m) + have ha : tableAddress L Q + (MaskTable.pairRank gh).val < + cursorAddressBase L m N Q := by + unfold cursorAddressBase offsetAddress countAddress + omega + rw [hlow _ ha] + exact hz.2.2.2.1 gh + · intro t ht + rw [hidx] + exact hcur t ht + · intro i hi + rw [hidx] at hi + by_cases h : i = z.regs index + · subst i + exact ⟨hcode, horig⟩ + · have hip : i < z.regs index := by omega + have hiQ : i < Q := hip.trans hn + have hp : CountingSort.position key Q i < Q := CountingSort.position_lt key hiQ + have hne : CountingSort.position key Q i ≠ CountingSort.position key Q (z.regs index) := by + intro heq + have hfin : (⟨i, hiQ⟩ : Fin Q) = ⟨z.regs index, hn⟩ := + CountingSort.position_injective key Q heq + exact h (congrArg (fun j : Fin Q => j.val) hfin) + have hop : originalAddress L m N Q = codeAddress L m N Q + Q := rfl + rw [hf _ (by omega) (by omega) (by omega), hf _ (by omega) (by omega) (by omega)] + exact hz.2.2.2.2.2.1 i hip + · intro a ha + rw [hf _ (by rcases ha with ha | ha <;> omega) + (by rcases ha with ha | ha <;> omega) (by rcases ha with ha | ha <;> omega)] + exact hz.2.2.2.2.2.2 a ha + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I (fun z => Q - z.regs index) hb hi + have hn : y.regs index = Q := by + have hle := hy.2.1 + simp only [Cond.holds_lt, hy.1.queryCount] at hc + omega + refine ⟨y, ?_, hy.1, ?_, ?_, hy.2.2.2.2.2.2⟩ + · apply (hs.seq he).mono + have h0 : start.regs index = 0 := by simp [start] + rw [h0, Nat.sub_zero] + nlinarith + · intro i hi + exact hy.2.2.2.2.2.1 i (hn ▸ hi) + · intro t ht + have hv := hy.2.2.2.2.1 t ht + rw [hn] at hv + exact hv.trans (CountingSort.count_lt_succ key Q t).symm + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/ScatterStep.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/ScatterStep.lean new file mode 100644 index 000000000..26e9806f1 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/ScatterStep.lean @@ -0,0 +1,124 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.ScatterWrite +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.CountStep + +/-! +# One computed wanted-query scatter + +## Main results + +* `Wanted.exec_scatterStep`: the actual coordinate loads, table lookup, output-digit loop + and three scatter stores preserve the exact stable-counting cursor invariant. + +This stage contract is an internal implementation API; the query code is calculated from the +original pair, rather than supplied as a source memory word or an output certificate. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Load, compute and scatter one original query, preserving the original input and exact +per-tile cursors. Every loop in the digit computation and every address instruction is charged. -/ +theorem exec_scatterStep {w L m N Q n : ℕ} (hm : m ≤ L) (pairs : ℕ → ℕ × ℕ) + (hn : n < Q) (hpair : (pairs n).1 < N ∧ (pairs n).2 < N) (x : Data 96) + (hx : Parameters L m N Q x) (hi : x.regs index = n) + (hinput : x.mem (5 + 2 * n) = (pairs n).1 ∧ + x.mem (5 + 2 * n + 1) = (pairs n).2) + (htable : ∀ gh : Fin (K0 L m) × Fin (K0 L m), + x.mem (tableAddress L Q + (MaskTable.pairRank gh).val) = (MaskTable.selectedMask gh).val) + (hcursors : ∀ t < numBands L m N ^ 2, + x.mem (cursorAddressBase L m N Q + t) = Nat.count (fun k => (tileKey L m N ∘ pairs) k < t) Q + + Nat.count (fun k => (tileKey L m N ∘ pairs) k = t) n) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w scatterStep x y (15 * L + 39) ∧ Parameters L m N Q y ∧ + y.regs index = n + 1 ∧ + (∀ t < numBands L m N ^ 2, + y.mem (cursorAddressBase L m N Q + t) = + Nat.count (fun k => (tileKey L m N ∘ pairs) k < t) Q + + Nat.count (fun k => (tileKey L m N ∘ pairs) k = t) (n + 1)) ∧ + y.mem (codeAddress L m N Q + CountingSort.position (tileKey L m N ∘ pairs) Q n) = + (PrunedQuery.queryIndex (outStrOfPos (MaskTable.layout hm) (pairs n).1 (pairs n).2)).val ∧ + y.mem (originalAddress L m N Q + CountingSort.position (tileKey L m N ∘ pairs) Q n) = n ∧ + (∀ a, a ≠ cursorAddressBase L m N Q + (tileKey L m N ∘ pairs) n → + a ≠ codeAddress L m N Q + CountingSort.position (tileKey L m N ∘ pairs) Q n → + a ≠ originalAddress L m N Q + CountingSort.position (tileKey L m N ∘ pairs) Q n → + y.mem a = x.mem a) := by + let key := tileKey L m N ∘ pairs + let p := CountingSort.position key Q n + let I : Fin N := ⟨(pairs n).1, hpair.1⟩ + let J : Fin N := ⟨(pairs n).2, hpair.2⟩ + have hk : key n < numBands L m N ^ 2 := tileKey_lt hm _ hpair.1 hpair.2 + have hl := local_fits hfit + have hend := memoryLimit_eq L m N Q + have h10 := Nat.zero_le (10 ^ L) + have h2 := Nat.zero_le (2 ^ L) + have hn2 : Fits w (n * 2) := hl.of_le (by omega) + have ha : Fits w (n * 2 + 5) := hl.of_le (by omega) + have ha1 : Fits w (n * 2 + 5 + 1) := hl.of_le (by omega) + have hI : x.mem (n * 2 + 5) = I.val := + (congrArg x.mem (by omega : n * 2 + 5 = 5 + 2 * n)).trans hinput.1 + have hJ : x.mem (n * 2 + 5 + 1) = J.val := + (congrArg x.mem (by omega : n * 2 + 5 + 1 = 5 + 2 * n + 1)).trans hinput.2 + let loaded := ((((x.set pointer (n * 2)).set pointer (n * 2 + 5)).set queryRow I.val).set + pointer (n * 2 + 5 + 1)).set queryCol J.val + have hload : ExecWithin w loadQuery x loaded 5 := by + ram_eval [loadQuery, loaded, hx.queries, hx.tables.two, hx.tables.one, hi, hI, hJ, + hn2, ha, ha1, pointer, index, queries, queryRow, queryCol, + BandPreparation.one, BandPreparation.two, Fin.mk.injEq] + have lp : Parameters L m N Q loaded := hx.reframe (by + intro i hi + apply hload.regs_eq + have hp : ∀ i ∈ parameterRegisters, loadQuery.Preserves i := by decide +kernel + exact hp i hi) + have hrfit : Fits w (routeBudget L m N (tableAddress L Q)) := hfit.of_le (by + unfold wordBudget + omega) + obtain ⟨routed, hr, _, _, htile, hvalue, hrmem, hrregs⟩ := exec_route hm I J loaded lp.route + (by simp [loaded, queryRow, queryCol, pointer]) (by simp [loaded]) htable hrfit + change routed.mem = x.mem at hrmem + have rp : Parameters L m N Q routed := lp.reframe (by + intro i hi + have hf : ∀ i ∈ parameterRegisters, ¬RouteLocal i := by decide +kernel + exact hrregs i (hf i hi)) + have hidx : routed.regs index = n := + (hrregs index (by decide)).trans (by simp [loaded, index, pointer, queryRow, queryCol, hi]) + have hcursor : routed.mem (cursorAddressBase L m N Q + key n) = p := by + rw [hrmem] + exact hcursors _ hk + obtain ⟨y, hy, yp, hiy, hcode, horig, hcur, hf⟩ := exec_scatterWrite key hn hk routed rp hidx + htile hvalue hcursor hfit + refine ⟨y, ?_, yp, hiy, ?_, hcode, horig, ?_⟩ + · exact (hload.seq (hr.seq hy)).mono (by omega) + · intro t ht + change y.mem (cursorAddressBase L m N Q + t) = + Nat.count (fun k => key k < t) Q + Nat.count (fun k => key k = t) (n + 1) + rw [Nat.count_succ] + by_cases h : key n = t + · subst t + rw [hcur] + simp only [↓reduceIte] + rfl + · have hlow : cursorAddressBase L m N Q + t < codeAddress L m N Q := by + unfold codeAddress + omega + have horiglow : codeAddress L m N Q ≤ originalAddress L m N Q := by + unfold originalAddress + omega + rw [hf _ (by omega) (by omega) (by omega), hrmem, hcursors t ht, ite_eq_right h] + rfl + · intro a hc hv ho + exact (hf a hc hv ho).trans (congrFun hrmem a) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/ScatterWrite.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/ScatterWrite.lean new file mode 100644 index 000000000..5c86a4f55 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/ScatterWrite.lean @@ -0,0 +1,105 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Setup + +/-! +# Native stable-scatter stores + +## Main results + +* `Wanted.exec_scatterWrite`: store one computed code and original position at the actual + cursor, then increment that cursor and the original-input counter. + +This is an internal implementation API. The scatter position is the existing counting-sort +formula, whose range follows from the original input length, rather than an execution oracle. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Three actual memory stores suffice for one stable occurrence. Address fits and +separation follow from the original query length and the proved counting-sort position. -/ +theorem exec_scatterWrite {w L m N Q n v : ℕ} (key : ℕ → ℕ) (hn : n < Q) + (hk : key n < numBands L m N ^ 2) (x : Data 96) (hx : Parameters L m N Q x) + (hi : x.regs index = n) (ht : x.regs tile = key n) + (hv : x.regs (packRegisters code) = v) + (hc : x.mem (cursorAddressBase L m N Q + key n) = CountingSort.position key Q n) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w scatterWrite x y 9 ∧ Parameters L m N Q y ∧ y.regs index = n + 1 ∧ + y.mem (codeAddress L m N Q + CountingSort.position key Q n) = v ∧ + y.mem (originalAddress L m N Q + CountingSort.position key Q n) = n ∧ + y.mem (cursorAddressBase L m N Q + key n) = CountingSort.position key Q n + 1 ∧ + (∀ a, a ≠ cursorAddressBase L m N Q + key n → + a ≠ codeAddress L m N Q + CountingSort.position key Q n → + a ≠ originalAddress L m N Q + CountingSort.position key Q n → y.mem a = x.mem a) := by + let p := CountingSort.position key Q n + have hp : p < Q := CountingSort.position_lt key hn + have hl := local_fits hfit + have hend := memoryLimit_eq L m N Q + have h10 := Nat.zero_le (10 ^ L) + have h2 := Nat.zero_le (2 ^ L) + have hf (v : ℕ) (hv : v ≤ memoryLimit L m N Q) : Fits w v := hl.of_le (by omega) + have hcursor : Fits w (cursorAddressBase L m N Q + key n) := hf _ (by + unfold cursorAddressBase offsetAddress countAddress tableAddress inputLimit + omega) + have hcode : Fits w (codeAddress L m N Q + p) := hf _ (by + unfold memoryLimit originalAddress + omega) + have horig : Fits w (originalAddress L m N Q + p) := hf _ (by + unfold memoryLimit + omega) + have hnext : Fits w (p + 1) := hf _ (by omega) + have hnnext : Fits w (n + 1) := hf _ (by omega) + have hcurcode : cursorAddressBase L m N Q + key n < codeAddress L m N Q := by + unfold codeAddress + omega + have hcodeorig : codeAddress L m N Q + p < originalAddress L m N Q := by + unfold originalAddress + omega + let y := (((((((((x.set cursorAddress (cursorAddressBase L m N Q + key n)).set position p).set + pointer (codeAddress L m N Q + p)).write (codeAddress L m N Q + p) v).set + pointer (originalAddress L m N Q + p)).write (originalAddress L m N Q + p) n).set + position (p + 1)).write (cursorAddressBase L m N Q + key n) (p + 1)).set index (n + 1)) + have he : ExecWithin w scatterWrite x y 9 := by + dsimp only [p] at hcode horig hnext + simp only [packRegisters, code] at hv + ram_eval [scatterWrite, y, hx.cursors, hx.codes, hx.originals, hx.tables.one, + hi, ht, hv, hc, hcursor, hcode, horig, hnext, hnnext, p, + cursorAddress, position, pointer, cursors, codes, originals, tile, index, + packRegisters, code, BandPreparation.one, Fin.reduceFinMk, Fin.mk.injEq] + have hy : Parameters L m N Q y := hx.reframe (by + intro i hi + apply he.regs_eq + have h : ∀ i ∈ parameterRegisters, scatterWrite.Preserves i := by decide +kernel + exact h i hi) + refine ⟨y, he, hy, by simp [y, index], ?_, ?_, ?_, ?_⟩ + · change Function.update (Function.update + (Function.update x.mem (codeAddress L m N Q + p) v) + (originalAddress L m N Q + p) n) + (cursorAddressBase L m N Q + key n) (p + 1) (codeAddress L m N Q + p) = v + rw [Function.update_of_ne (by omega), Function.update_of_ne (by omega), Function.update_self] + · change Function.update (Function.update + (Function.update x.mem (codeAddress L m N Q + p) v) + (originalAddress L m N Q + p) n) + (cursorAddressBase L m N Q + key n) (p + 1) (originalAddress L m N Q + p) = n + rw [Function.update_of_ne (by omega), Function.update_self] + · exact Function.update_self _ _ _ + · intro a hc hk ho + change Function.update (Function.update + (Function.update x.mem (codeAddress L m N Q + p) v) + (originalAddress L m N Q + p) n) + (cursorAddressBase L m N Q + key n) (p + 1) a = x.mem a + rw [Function.update_of_ne hc, Function.update_of_ne ho, Function.update_of_ne hk] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Width.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Width.lean new file mode 100644 index 000000000..6f3227800 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Width.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Flat +public import Mathlib.Data.Nat.Log + +/-! +# Sufficient physical width for native wanted-query grouping + +## Main definitions + +* `Wanted.admissionWidth`: a positive ceiling-logarithmic threshold from original parameters. + +## Main results + +* `Wanted.runs_wide`: input fit and actual initialized runs at every admitted physical width. +* `Wanted.program_length_le`: a fixed finite code-size bound independent of the input. + +Coordinates and query codes are unsigned. No signed-kernel width adjustment is needed for +this routing component. Relating its parameters to a whole 3SUM input remains composition work. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Positive sufficient physical width, logarithmic in the explicit original-parameter budget. +The program does not read the width or this threshold. -/ +def admissionWidth (L m N Q : ℕ) : ℕ := Nat.clog 2 (wordBudget L m N Q + 1) + 1 + +/-- The width condition admits positive widths even for zero dimension and empty queries. -/ +theorem admissionWidth_pos (L m N Q : ℕ) : 0 < admissionWidth L m N Q := by + unfold admissionWidth + omega + +/-- A sufficiently large physical width guarantees the actual numerical fit premise. -/ +theorem admission_fit {W L m N Q : ℕ} (hW : admissionWidth L m N Q ≤ W) : + Fits W (wordBudget L m N Q) := by + have hc := Nat.le_pow_clog (by decide : 1 < (2 : ℕ)) (wordBudget L m N Q + 1) + have hpow := Nat.pow_le_pow_right (by decide : 1 ≤ (2 : ℕ)) + (by unfold admissionWidth at hW; omega : Nat.clog 2 (wordBudget L m N Q + 1) ≤ W) + change wordBudget L m N Q < 2 ^ W + omega + +/-- Actual initialized runs at every sufficiently wide physical width. The same returned +memory holds the original-index permutation, query codes, exact spans and readonly frame. -/ +theorem runs_wide {W L m N : ℕ} (hm : m ≤ L) (qs : List (Fin N × Fin N)) + (hW : admissionWidth L m N qs.length ≤ W) : + InputFits W (input L m qs) ∧ + ∃ y n, n ≤ timeBound L m N qs.length ∧ + Runs W (program.program codes) (State.init (input L m qs)) ⟨program.size, y⟩ n ∧ + Executes W (program.program codes) (State.init (input L m qs)) + (codeAddress L m N qs.length) (n + 1) ∧ y.Bounded W ∧ + (∀ i : Fin qs.length, + y.mem (codeAddress L m N qs.length + + CountingSort.position (tileKey L m N ∘ inputPairs qs) qs.length i.val) = + (PrunedQuery.queryIndex + (outStrOfPos (MaskTable.layout hm) (qs.get i).1 (qs.get i).2)).val ∧ + y.mem (originalAddress L m N qs.length + + CountingSort.position (tileKey L m N ∘ inputPairs qs) qs.length i.val) = i.val) ∧ + (∀ t ≤ numBands L m N ^ 2, y.mem (offsetAddress L m N qs.length + t) = + Nat.count (fun k => (tileKey L m N ∘ inputPairs qs) k < t) qs.length) ∧ + y.mem (offsetAddress L m N qs.length + numBands L m N ^ 2) = qs.length ∧ + (∀ a, a < inputLimit qs.length ∨ memoryLimit L m N qs.length ≤ a → + y.mem a = (State.init (input L m qs) : State 96).data.mem a) := + runs hm qs (admission_fit hW) + +/-- The code is one finite 96-register program; all data-dependent repetition is in loops. -/ +theorem program_length_le : (program.program codes).length ≤ 4096 := by decide +kernel + +/-- The native program is uniform across admitted widths. -/ +theorem not_readsWidth : ¬program.ReadsWidth := by decide +kernel + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted diff --git a/Complexity/Computability/WordRAM/Sequence.lean b/Complexity/Computability/WordRAM/Sequence.lean new file mode 100644 index 000000000..e4099331a --- /dev/null +++ b/Complexity/Computability/WordRAM/Sequence.lean @@ -0,0 +1,43 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Structured + +/-! +# Reassociating structured native execution proofs + +## Main results + +* `WordRAM.ExecWithin.seq_assoc`: compose an already proved instruction block with the + surrounding right-associated program, without repeating its execution proof. + +Reassociation preserves the same data states and charged bound. This narrow proof-rule +module avoids extending the interface of the widely imported structured-code core. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.ExecWithin + +/-- Sequential blocks may be regrouped while preserving every instruction charge. -/ +theorem seq_assoc {B : Basis} {r w T : ℕ} {a b c : Code B r} {x y : Data r} : + ExecWithin w (.seq (.seq a b) c) x y T ↔ ExecWithin w (.seq a (.seq b c)) x y T := by + constructor + · rintro ⟨n, hn, he⟩ + cases he with + | seq hab hc => + cases hab with + | seq ha hb => exact ⟨_, by omega, .seq ha (.seq hb hc)⟩ + · rintro ⟨n, hn, he⟩ + cases he with + | seq ha hbc => + cases hbc with + | seq hb hc => exact ⟨_, by omega, .seq (.seq ha hb) hc⟩ + +end Cslib.WordRAM.ExecWithin diff --git a/ComplexityTests/SchonhageWanted.lean b/ComplexityTests/SchonhageWanted.lean index 297ddf5f1..c3a40a6a0 100644 --- a/ComplexityTests/SchonhageWanted.lean +++ b/ComplexityTests/SchonhageWanted.lean @@ -4,20 +4,19 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Erik Demaine -/ -module -- upstream: local-only (tests) import Complexity.Init -import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Route +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.GroupedRuns import Complexity.Algorithms.MatrixMultiplication.Schonhage.MaskTable set_option linter.hashCommand false -set_option linter.privateModule false open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage open Cslib.WordRAM open Cslib.Algorithms.MatrixMultiplication.Schonhage open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted +open Cslib.Algorithms private def expectedCode {L m : ℕ} (hm : m ≤ L) (I J : ℕ) : ℕ := (PrunedQuery.queryIndex (outStrOfPos (MaskTable.layout hm) I J)).val @@ -61,3 +60,81 @@ private def observe (L m N : ℕ) (qs : List (ℕ × ℕ)) : -- This exercises only routing; it never materializes a 10^4-leaf encoded array. #guard observe 4 1 110 [(2, 28), (28, 2), (55, 109), (2, 28), (109, 55)] == some ([97, 905, 97, 49, 49], [0, 1, 3, 2, 4], [0, 3, 3, 3, 3, 3, 4, 4, 5, 5], true) + +private def repeatedQueries : List (Fin 2 × Fin 2) := + [(1, 0), (0, 1), (1, 0)] + +-- The kernel-checked initialized run and its halt use the same memory witness. Both +-- equal coordinate pairs survive as separate occurrences in the original-index array. +example : InputFits 32 (input 0 0 repeatedQueries) ∧ + ∃ y n, n ≤ timeBound 0 0 2 3 ∧ + Runs 32 (program.program codes) (State.init (input 0 0 repeatedQueries)) + ⟨program.size, y⟩ n ∧ + Executes 32 (program.program codes) (State.init (input 0 0 repeatedQueries)) + (codeAddress 0 0 2 3) (n + 1) ∧ + y.mem (codeAddress 0 0 2 3 + 1) = 0 ∧ + y.mem (originalAddress 0 0 2 3 + 1) = 0 ∧ + y.mem (originalAddress 0 0 2 3 + 2) = 2 ∧ + y.mem 1 = 0 := by + obtain ⟨hi, y, n, hn, hr, he, _, hv, _, _, hf⟩ := + runs (w := 32) (by decide : 0 ≤ 0) repeatedQueries (by decide +kernel) + have h0 := hv ⟨0, by decide⟩ + have h2 := hv ⟨2, by decide⟩ + have hp0 : CountingSort.position (tileKey 0 0 2 ∘ inputPairs repeatedQueries) 3 0 = 1 := by + decide +kernel + have hp2 : CountingSort.position (tileKey 0 0 2 ∘ inputPairs repeatedQueries) 3 2 = 2 := by + decide +kernel + have hz : (PrunedQuery.queryIndex + (outStrOfPos (MaskTable.layout (by decide : 0 ≤ 0)) + (repeatedQueries.get ⟨0, by decide⟩).1 + (repeatedQueries.get ⟨0, by decide⟩).2)).val = 0 := by + have hlt := (PrunedQuery.queryIndex + (outStrOfPos (MaskTable.layout (by decide : 0 ≤ 0)) + (repeatedQueries.get ⟨0, by decide⟩).1 (repeatedQueries.get ⟨0, by decide⟩).2)).isLt + change _ < 1 at hlt + omega + simp only [show repeatedQueries.length = 3 from rfl, hp0] at h0 + simp only [show repeatedQueries.length = 3 from rfl, hp2] at h2 + refine ⟨hi, y, n, hn, hr, he, h0.1.trans hz, h0.2, h2.2, ?_⟩ + exact hf 1 (Or.inl (by decide)) + +-- The entirely empty input is admitted too; the terminal offset is written by real code. +example : ∃ y n, Runs 32 (program.program codes) (State.init (input 0 0 + ([] : List (Fin 0 × Fin 0)))) ⟨program.size, y⟩ n ∧ + Executes 32 (program.program codes) (State.init (input 0 0 + ([] : List (Fin 0 × Fin 0)))) (codeAddress 0 0 0 0) (n + 1) ∧ + y.mem (offsetAddress 0 0 0 0) = 0 := by + obtain ⟨_, y, n, _, hr, he, _, _, _, ht, _⟩ := + runs (w := 32) (by decide : 0 ≤ 0) ([] : List (Fin 0 × Fin 0)) (by decide +kernel) + exact ⟨y, n, hr, he, ht⟩ + +-- A physical width can fail the numerical admission premise, even for zero depth. +example : ¬Fits 1 (wordBudget 0 0 0 0) := by decide +kernel +example : Fits 32 (wordBudget 0 0 0 0) := by decide +kernel +example : (program.program codes).length ≤ 4096 := program_length_le +example : ¬program.ReadsWidth := not_readsWidth + +private def asymmetricQueries : List (Fin 110 × Fin 110) := + [(2, 28), (28, 2), (55, 109), (2, 28), (109, 55)] + +-- All admission premises are discharged on the asymmetric positive-inner-depth family. +-- The code-set assertion uses the same y as the actual initialized Runs and charged halt. +example : InputFits 64 (input 4 1 asymmetricQueries) ∧ + ∃ y n, Runs 64 (program.program codes) (State.init (input 4 1 asymmetricQueries)) + ⟨program.size, y⟩ n ∧ + Executes 64 (program.program codes) (State.init (input 4 1 asymmetricQueries)) + (codeAddress 4 1 110 5) (n + 1) ∧ + n + 1 ≤ 128 * 5 * (5 + 3 ^ 2 + 2 ^ 4 + 1) ∧ + (Finset.Ico (y.mem (offsetAddress 4 1 110 5)) + (y.mem (offsetAddress 4 1 110 5 + 1))).image + (fun j => y.mem (codeAddress 4 1 110 5 + j)) = + (wantedStrings (MaskTable.layout (by decide : 1 ≤ 4)) + asymmetricQueries.toFinset 0 0).image (fun w => (PrunedQuery.queryIndex w).val) ∧ + Function.Injective (fun j : Fin 5 => y.mem (originalAddress 4 1 110 5 + j.val)) := by + obtain ⟨hi, y, n, hn, hr, he, _, _, hg, _, _, _, hj, _, _, _⟩ := + runs_grouped (W := 64) (by decide : 1 ≤ 4) asymmetricQueries (by decide +kernel) + have hg0 := hg (0, 0) (by decide +kernel) + have hbands : numBands 4 1 110 = 3 := by decide +kernel + simp only [show asymmetricQueries.length = 5 from rfl, hbands] at hn + simp only [Nat.zero_mul, Nat.zero_add, Nat.add_zero] at hg0 + exact ⟨hi, y, n, hr, he, hn, hg0, hj⟩ diff --git a/ROADMAP.md b/ROADMAP.md index 19c2dc209..d71504325 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -442,9 +442,14 @@ each statement against the numbered result. Organize modules by subject, not cha runs preserve query order and duplicates, the input and outside frame. Setup, sparse clearing, all control/index work and halt fit `8192*(L+Q+1+(L+1)*|leaves(requested queries)|)` instructions; widths follow from explicit original magnitude/address bounds. A callable pointer/epoch kernel - permits directory reuse across tiles. This does not construct the product's tile query groups - or supply a sparse-product/Exact Triangle solver. Native Chan–He preprocessing and the reduction - host are separate stack components. + permits directory reuse across tiles. Native wanted-query routing ([#1561]) computes the + selected-subset table, tile keys, query codes, counts, prefix offsets and stable scatter from + original raw coordinates. Its initialized run preserves every original occurrence and gives + each tile exactly its finite `wantedStrings` code set, within + `128*(L+1)*(Q+numBands²+2^L+1)` instructions including halt. It uses fixed 96-register code and + an explicit positive sufficient-width threshold. Joining these groups to shared band encoders, + invoking the pruned kernel and reconstructing queried products remain separate native work. + Native Chan–He preprocessing and the reduction host are separate stack components. Native sparse-product and Exact Triangle solving, full input/time/width/asymptotic composition, and the improved Section 4 route to exponent 1.9992 remain under #1394; size-based width @@ -580,3 +585,4 @@ not planned, not as proved. [#1493]: https://github.com/cslib-community/complexitylib/issues/1493 [#1523]: https://github.com/cslib-community/complexitylib/issues/1523 [#1526]: https://github.com/cslib-community/complexitylib/issues/1526 +[#1561]: https://github.com/cslib-community/complexitylib/issues/1561 diff --git a/UPSTREAM.md b/UPSTREAM.md index e1da7bd2f..77848f767 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -245,7 +245,22 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he signed kernels and reporting. Its complete initialized runs and callable pointer/epoch kernel have explicit source-magnitude, address and outside-frame contracts. The actual bound including halt is `8192*(L+Q+1+(L+1)*|leaves(requested queries)|)`. The source Light implementation's - semantics and instruction constants are not reused. Query grouping, sparse-product/Exact + semantics and instruction constants are not reused. + Native wanted-query routing ([#1561](https://github.com/cslib-community/complexitylib/issues/1561)) + uses actual count, prefix and stable-scatter passes after computing the subset table and each + query's tile/code. It reuses `MaskTable.layout`, `outStrOfPos`, `wantedStrings` and the existing + `PrunedQuery.queryIndex`/encoder leaf order; no parallel layout or output-string universe is + added. Primitive counts use Mathlib's `Nat.count` and its prefix/monotonicity APIs directly. + The generic `CountingSort` topic module adds stable positions, their permutation and + bucket-span meaning, without redundant count/offset definitions. The narrow `WordRAM.Sequence` + proof module reassociates existing + structured execution derivations without changing code or charges. Original scalar headers + and raw coordinate pairs are the entire initialized input. Fixed 96-register standard code + computes all tables and grouped arrays, preserves the original input/outside frame, and + charges at most `128*(L+1)*(Q+numBands²+2^L+1)` instructions including halt. Its positive + ceiling-logarithmic admission threshold depends only on the original numerical budget. + Within-tile order is stable input order; no sorted or duplicate-free query premise is needed + by the pruned kernel. Shared-band/kernel calls, answer reconstruction, sparse-product/Exact Triangle solving and full input/time/width composition, including the improved Section 4 route to exponent 1.9992, remain in #1394. The band catalogue retains the same leaf ordering and canonical signed words. Raw 0/1 matrix producers must From bd9bd2b09eb1d325f5b3b541ec52f71c7001a804 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Wed, 28 Oct 2026 23:24:27 -0400 Subject: [PATCH 40/73] fix(imports): regenerate aggregate after shared-encoder integration --- Complexity.lean | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Complexity.lean b/Complexity.lean index 9f2c0f857..a97bde25b 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -103,6 +103,11 @@ public import Complexity.Algorithms.WordRAM.FusionTree.Query.Traversal public import Complexity.Algorithms.WordRAM.FusionTree.Rank public import Complexity.Algorithms.WordRAM.FusionTree.ReadHeader public import Complexity.Algorithms.WordRAM.FusionTree.Static +public import Complexity.Algorithms.WordRAM.Karger.Contract +public import Complexity.Algorithms.WordRAM.Karger.FastCut +public import Complexity.Algorithms.WordRAM.Karger.Graph +public import Complexity.Algorithms.WordRAM.KargerMinCut +public import Complexity.Algorithms.WordRAM.KargerStein public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Cell public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Controller public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Copy @@ -114,11 +119,6 @@ public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Heade public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Input public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Meaning public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Stage -public import Complexity.Algorithms.WordRAM.Karger.Contract -public import Complexity.Algorithms.WordRAM.Karger.FastCut -public import Complexity.Algorithms.WordRAM.Karger.Graph -public import Complexity.Algorithms.WordRAM.KargerMinCut -public import Complexity.Algorithms.WordRAM.KargerStein public import Complexity.Algorithms.WordRAM.PrefixSums.Dynamic public import Complexity.Algorithms.WordRAM.PrefixSums.Query public import Complexity.Algorithms.WordRAM.PrefixSums.Tree From 9c8b9fcc3ccc5a6282bbd3b3e3cbcfc4c0070c62 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Wed, 28 Oct 2026 23:31:21 -0400 Subject: [PATCH 41/73] perf(wordram): reduce native parameter proof heartbeat budget --- Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Parameters.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Parameters.lean b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Parameters.lean index f26ccbe18..26812d641 100644 --- a/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Parameters.lean +++ b/Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Parameters.lean @@ -193,8 +193,8 @@ private theorem time_zero (n U : ℕ) : time 0 n U - 20 = 44 + 5 * Nat.sqrt n + dsimp [time] omega -set_option maxHeartbeats 800000 in --- Elaborating twelve native segments and their explicit register frames exceeds the default budget. +set_option maxHeartbeats 300000 in +-- Measured 256376 heartbeats for the twelve native segments and explicit register frames. private theorem exec_calculate {w n U : ℕ} (x : Data 24) (hlen : x.regs len = n) (hU : x.regs magnitude = U) (h1 : x.regs one = 1) (h2 : x.regs two = 2) (h3 : x.regs three = 3) From 452924eae8db34ac5c3ae00dcad77e8b5e40dea7 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Wed, 28 Oct 2026 23:33:03 -0400 Subject: [PATCH 42/73] chore(wordram): scope measured parameter proof budget to reduction task --- scripts/heartbeat_allowlist.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/heartbeat_allowlist.txt b/scripts/heartbeat_allowlist.txt index fc443bbff..02ca8b8a2 100644 --- a/scripts/heartbeat_allowlist.txt +++ b/scripts/heartbeat_allowlist.txt @@ -6,3 +6,6 @@ # #1528: closed incidence-table certificates from #1281. Complexity/Computability/Complexity/Problems/SAT/ExactOne/GeometryProfilesPrefix.lean 8000000 + +# #1583: split native parameter segment/frame proofs to reach the default budget. +Complexity/Algorithms/WordRAM/ThreeSum/ChanHe/Parameters.lean 300000 From d91296fb62bec52aefb720992de592f0739c1d78 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Wed, 28 Oct 2026 23:35:06 -0400 Subject: [PATCH 43/73] perf(schonhage): focus cell arithmetic and use the default heartbeat budget --- .../MatrixMultiplication/Schonhage/Cell.lean | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Cell.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Cell.lean index d743deb96..160e70cd7 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Cell.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Cell.lean @@ -42,9 +42,6 @@ private theorem cell_preserves (side : Bool) (i : Fin 32) : 9 ≤ i.val → ¬CellLocal i → (cellBody side).Preserves i := by decide +kernel +revert -set_option maxHeartbeats 2000000 in --- Symbolic execution retains nine instructions' functional register updates; normalizing them --- and proving the explicit nonlinear address inequalities exceeds the default heartbeat budget. /-- One output entry, with all index arithmetic and the counter increment charged. A single ordinary explicit natural-number bound covers the addresses and index products; no fit condition is imposed on an unspecified intermediate machine state. -/ @@ -65,23 +62,26 @@ theorem exec_cell {w : ℕ} (hw : 0 < w) (side : Bool) (x : Data 32) let p := i / (10 * n) let t : Fin 10 := ⟨i / n % 10, Nat.mod_lt _ (by decide)⟩ let r := i % n + -- Keep nonlinear arithmetic focused on index bounds, rather than the execution context. have hp : p < P := by apply (Nat.div_lt_iff_lt_mul (by positivity : 0 < 10 * n)).mpr - nlinarith [hi] + nlinarith only [hi] have hr : r < n := Nat.mod_lt _ hnpos have hn1 : 1 ≤ n := hnpos - have hPn : n ≤ P * n := by nlinarith + have hPn : n ≤ P * n := Nat.le_mul_of_pos_left n hP have hmul : p * n < P * n := Nat.mul_lt_mul_of_pos_right hp hnpos - have h10n : Fits (w + 2) (10 * n) := haddr.of_le (by nlinarith) - have hpfit : Fits (w + 2) p := haddr.of_le (by nlinarith) + have h10n : Fits (w + 2) (10 * n) := haddr.of_le (by nlinarith only [hPn]) + have hpfit : Fits (w + 2) p := haddr.of_le (by nlinarith only [hp, hn1]) have hdiv : i / n ≤ i := Nat.div_le_self _ _ - have hdivfit : Fits (w + 2) (i / n) := haddr.of_le (by nlinarith) + have hdivfit : Fits (w + 2) (i / n) := haddr.of_le (by nlinarith only [hdiv, hi]) have htfit : Fits (w + 2) t.val := haddr.of_le (by have := t.isLt; omega) - have hrfit : Fits (w + 2) r := haddr.of_le (by nlinarith) - have hp7 : Fits (w + 2) (p * 7) := haddr.of_le (by nlinarith) - have hp7n : Fits (w + 2) (p * 7 * n) := haddr.of_le (by nlinarith) - have hp7nr : Fits (w + 2) (p * 7 * n + r) := haddr.of_le (by nlinarith) - have hgroup : Fits (w + 2) (s + (p * 7 * n + r)) := haddr.of_le (by nlinarith) + have hrfit : Fits (w + 2) r := haddr.of_le (by nlinarith only [hr, hPn]) + have hp7 : Fits (w + 2) (p * 7) := haddr.of_le (by nlinarith only [hp, hn1]) + have hp7n : Fits (w + 2) (p * 7 * n) := haddr.of_le (by nlinarith only [hmul]) + have hp7nr : Fits (w + 2) (p * 7 * n + r) := + haddr.of_le (by nlinarith only [hmul, hr, hPn]) + have hgroup : Fits (w + 2) (s + (p * 7 * n + r)) := + haddr.of_le (by nlinarith only [hmul, hr, hPn]) let z := ((((((((x.set temporary (10 * n)).set block p).set term (i / n)).set term t.val).set suffix r).set group (p * 7)).set group (p * 7 * n)).set group (p * 7 * n + r)).set group (s + (p * 7 * n + r)) @@ -99,7 +99,7 @@ theorem exec_cell {w : ℕ} (hw : 0 < w) (side : Bool) (x : Data 32) have hidx : s + (p * 7 * n + r) + a.val * n = s + (p * 7 + a.val) * n + r := by ring simpa only [z, Data.set_mem, hidx] using hmem a have hspan : Fits (w + 2) (s + (p * 7 * n + r) + 7 * n) := - haddr.of_le (by nlinarith) + haddr.of_le (by nlinarith only [hmul, hr, hPn]) obtain ⟨u, hu, huacc, humem, hureg⟩ := exec_dispatch hw side (List.finRange 10) t (List.mem_finRange _) z B (s + (p * 7 * n + r)) n v hzterm hzgroup hzn hzmem hB hv hval hspan (haddr.of_le (by omega)) @@ -112,8 +112,8 @@ theorem exec_cell {w : ℕ} (hw : 0 < w) (side : Bool) (x : Data 32) have zuone : u.regs one = 1 := by rw [hureg one (by decide) (by decide) (by decide) (by decide)] simpa [z, temporary, block, term, suffix, group, one, Fin.mk.injEq] using hone - have hdfit : Fits (w + 2) (d + i) := haddr.of_le (by nlinarith) - have hinc : Fits (w + 2) (i + 1) := haddr.of_le (by nlinarith) + have hdfit : Fits (w + 2) (d + i) := haddr.of_le (by nlinarith only [hi]) + have hinc : Fits (w + 2) (i + 1) := haddr.of_le (by nlinarith only [hi]) let y := ((u.set address (d + i)).write (d + i) (u.regs accumulator)).set cell (i + 1) have hy : ExecWithin (w + 2) cellFinish u y 3 := by ram_eval [cellFinish, y, zucell, zudst, zuone, hdfit, hinc, From d8f41623613bcf6207688216e23ee62f905ff2e3 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Wed, 28 Oct 2026 23:37:55 -0400 Subject: [PATCH 44/73] fix(doc): restore unrelated upstream coordination records Restore the exact 231-line tail common to both integration parents after review caught an overbroad conflict-marker match. The intended 3SUM integration prose is retained; no Lean change. AI-assisted correction by OpenAI Codex. --- UPSTREAM.md | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) diff --git a/UPSTREAM.md b/UPSTREAM.md index 900334b68..153abe289 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -208,3 +208,234 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he [#1394](https://github.com/cslib-community/complexitylib/issues/1394), including the improved Section 4 route needed for the published exponent 1.9992. Anthropic's copyright and attribution are retained; the native proofs and representation choices are local. +* **Typed RAM upstream core.** [CSLib #1158](https://github.com/leanprover/cslib/pull/1158) at + `beea60a15eddc91d93e92fa5af9fb965c351d0d5` proposes + `Cslib/Computability/Machines/RAM/Basic.lean`, in namespace `Cslib.RAM.Typed`. + This redesigns the local WordRAM/GenericRAM core with a register and word-addressed memory bank + per sort, `Fin (2 ^ w)` words, and absorbing halt/fault states. A halt retains the data without + choosing an output register; `RelSeries` run paths permit terminal padding. Before migrating + local clients, bridge their operation bases, input/output conventions and exact halting costs. + Algorithms, interpretation transfer, native simulations and time-class APIs remain separate + follow-ups. The existing local modules remain candidates, not copies of this submission. +* **Encoded sparse memory.** `Computability/GenericRAM/SparseMemory/*` reuses Mathlib's `AList` + with constant value type and the existing native `ComputableTidilyInO` equality, find and filter + routines. Its certificates expose actual native TM witnesses, parameterized time/space bounds, + and polynomial-time corollaries, and feed the fixed-program step and whole-run simulators in + `GenericRAM/Simulation/{Turing,Run}` (#298, #292). +* **Stateful algorithms prototype.** `Foundations/Semantics/Stateful/*` reuses Quang Dao's merged + `PFunctor.FreeM` for adaptive clients and substitution, adding persistent representation + simulation, interface composition, semantic Hoare rules and separate native-resource + certificates. Its queue and reset-and-replay constructions deliberately count backend calls; + arbitrary pure continuations do not have machine-time certificates. `CostHoare` adds client + operation-cost contracts and separate method-budget composition. The `stateful` Aesop rules + support these contracts. `WordRAM/Controller` adds fixed-code client refinements with charged + preparation, control flow and final halt; the adaptive stack example constructs its empty + representation from the standard input. `WordRAM/Function` gives separate nonuniform and + uniform RAM function-time predicates, with named-certificate reuse by `polytime`. Arbitrary continuation + synthesis, iterative clients and a Turing-machine simulation remain open. + `Foundations/Data/OrderedSet` and `Foundations/Semantics/Stateful/OrderedSet` provide neutral + optional-minimum contracts, a typed finite-set interface and adaptive clients, with no tree or + cost dependency. Bounded-key implementations may specialize insertion admissibility. + `Algorithms/Lean/SearchTree/*` reuses Mathlib's `BinaryTree`, height and node count for an + unbalanced ordered-set realization. Finite-set behavior, strict invariants and height-bounded + node visits are proved, including for the pure deletion operation `erase`; allocation, + comparison implementation and client computation are excluded from that cost model. It is not + a balanced tree or machine-time certificate. + `WordRAM/Dynamic` separately connects + fixed bounded-word operation programs and faithful memory transcripts to those contracts. + The direct shared-heap queue uses the existing FIFO operation signature and native operation + traces, with a hidden two-stack representation, a concrete-register potential, framed storage, + and separately charged initialization. Its `2 * capacity` reservation is explicit; external + client computation and argument delivery are not charged by the operation-history bound. + Coordinate placement and overlap with Srinivas's #685 and the merged #401 (`Algorithms/Lean/Query`) + before upstream submission; #685 is not copied here. This is a prototype of composition and + refinement, not a replacement for their query models or a general cell-probe lower-bound semantics. +* **Earlier review requests.** The withdrawn #1099–#1101 requested abstract word encodings and + textbook sources for FP/poly, P/poly and their machine simulation. The withdrawn #869 and + #871–#875 requested composition above configuration level; use `TransformsTapes`-style specs. + +These are coordination references, not a second record of live PR status. Check the relevant +upstream discussion before starting or submitting overlapping work. + +## Placement decisions not already recorded in source + +Paths below omit `Complexity/` and `.lean`. The default CSLib destination mirrors the source +path. A destination recorded in a source marker takes precedence; the alternatives below need +agreement during upstream review. + +| Local module or declarations | Integration note | +| --- | --- | +| `Computability/Machines/Turing/MultiTape/Encodings/StreamFold` | The candidate `streamFold_sum` generalizes the vendored proof; `streamFold` is its compatibility corollary. When resynchronizing or removing the copy, preserve the summed contract and shared proof until upstream includes them. | +| `Computability/Machines/Turing/MultiTape/Encodings/Length` | Add to the tidy branch's `Encodings/List`. | +| `Computability/Machines/Turing/MultiTape/TapeLemmas` | `exists_visitedByTapeHead_eq_Icc` and `workTapes_runFrom_eq_of_notMem_visitedByTapeHead` were relocated from `Plumbing/Shadow` (#316); upstream with `TapeLemmas`. | +| `Computability/Machines/Turing/MultiTape/Configuration` | `Turing.Action.instFinite` was relocated from `Machine/ChoiceTable` (#354); place beside `Action` in upstream `Configuration`. | +| `Computability/Machines/Turing/MultiTape/Plumbing/ExtendTapesView` | Derive the old `tapeEmb`/`noTapes` lemmas from `viewCfg`; `oneTapeCfg i = viewCfg (tapeEmb i)` by `rfl`. | +| `Computability/Machines/Turing/MultiTape/Probabilistic/IterateWhile` | Its private tape helpers parallel the vendored deterministic `Combinators/IterateWhile`; share them once the tidy branch exposes them. | +| `Computability/Machines/Turing/MultiTape/Plumbing/TransformsTapes` | `runFrom_nop_withState` was relocated from `Combinators/TidyNormalForm` (#354); place beside `runFrom_nop_one` in upstream `Plumbing/TransformsTapes`. | +| `Computability/Machines/Turing/MultiTape/Encodings/Decoder` | Total decoders with tidy certificates; add to the tidy branch's `Encodings` beside `Option`/`Pair`/`List`. | +| `Computability/Machines/Turing/MultiTape/Encodings/NatPower` | Addition to the tidy branch's `Encodings/Nat`: `encNat (2 ^ n)` and its linear clock certificate. | +| `Computability/Circuit/{Unfolding,MapOp}`, `Computability/Circuit/Encoding/MapOp` | Consider integrating into `Circuit/Formula`, `Circuit/Program`, and `Circuit/Encoding`, respectively. | +| `Computability/Circuit/Boolean/FirstOrder` | Consider a descriptive-complexity directory. | +| `Foundations/Data/BitString/{Agree,Block}` | Add to CSLib's `Foundations/Data/BitString`. | +| `Foundations/Data/List/{Fold,Lookup,Counting,Append}` | Fold invariants, lookup and register-file bounds, predicate counts, and append identities including unary headers; `Basic` reexports these topics. Consider Mathlib's `Data/List/GetD` and `Data/List/Basic`. | +| `Foundations/Relation/RelatesWithinSteps` | Bounded reachability helpers; consider Mathlib's `Logic/Relation`. | +| `Foundations/BooleanAnalysis/Fourier` | Consider Mathlib beside `Analysis/Fourier/FiniteAbelian`. | +| `Foundations/BooleanAnalysis/{Bernoulli,CoordinateSampling,Fibers,HarmonicTransform,LightPatterns,MirrorSets}`, `Foundations/Analysis/HarmonicMean` | Consider Mathlib probability APIs; generic finite-type `harmonicMean` is split into `Foundations/Analysis/HarmonicMean` (#317). | +| `Foundations/BooleanAnalysis/HadamardTester` | Consider the PCP hierarchy. | +| `Foundations/Combinatorics/CentralBinomial` | Consider Mathlib's `Data/Nat/Choose/Central` (`Nat.centralBinom_sq_mul_le`) and `Combinatorics/Choose`. | +| `Foundations/Combinatorics/{FiniteCover,Hoeffding,Hypergeometric,PowersetTail,Sunflower}` | Consider Mathlib combinatorics; probabilistic-method lemmas belong near `Combinatorics/Probabilistic`. | +| `Foundations/Data/Finset/Expect` | Product-type expectation helpers extracted from `Foundations/Combinatorics/Expander/Products` (#317); Mathlib candidates. | +| `Foundations/Data/Finset/Markov` | Mathlib candidate: `Algebra/Order/BigOperators/Group/Finset` (`Finset.card_filter_mul_le_sum`, `Finset.mul_card_filter_le_sum`). | +| `Foundations/Data/Matrix/Permanent` | Mathlib candidate: Laplace expansion of the permanent along row and column 0, beside `LinearAlgebra/Matrix/Permanent` (mirroring `Matrix.det_succ_row_zero`). | +| `Foundations/LinearAlgebra/{Rank,Matrix/CutRank,Matrix/Lindsey}` | Mathlib candidates: `LinearAlgebra/Dimension/LinearMap` and `LinearAlgebra/Matrix/Rank`; placement of Lindsey's lemma needs agreement. | +| `Probability/PairwiseIndependentHash/Shift` | Consider integration into `PairwiseIndependentHash`. | +| `Probability/PairwiseIndependentHash/Toeplitz` | Move `Matrix.toeplitz` to Mathlib beside `LinearAlgebra/Matrix/Circulant`. | +| `Probability/ApproximateCounting/Relative` | Extract the natural-number root lemmas to Mathlib. | +| `Foundations/Data/Nat/Bits` | `Nat.foldr_bit_bits` is a candidate for Mathlib's `Data/Nat/Bits`; its local relocation is complete, but upstream integration is still pending. | + +## Deprecation policy and breaking changes + +From `2110aa5a` (2026-10-19), renaming, moving or removing a public declaration already on `main` +left a deprecated alias or theorem at the old full name; the sweep integrated at `7738dcea` added +aliases for the names that changed before the rule existed and restored old binder orders where +they had changed. The library supports no downstream users yet, so #587 deleted every deprecated +declaration, and such changes now update all clients in this repository instead (`AGENTS.md`, +Proof hygiene). This list records earlier changes that had no mechanical successor. + +* **Signature changes under the old name.** `GeneralizedGeography.decide_mem_eq_any` became the + more general `List.decide_mem_eq_any`; `RotationMap.le_edgeExpansion_of_spectralExpansion_le` + takes `V` implicitly; `PolyTimeComplete` takes `{β}` before `C`; `PMF.iid_map` has the opposite + orientation; `eval_toUnbounded_atomsOf` changed its signature. +* **Removed or made private, without a successor of the same shape.** + - Raz–Wigderson helpers: `bobAnswer_aliceMatching`, `aliceGraph_swap_swap`, `bobGraph_swap_swap`, + `exists_run_oneRun`, `run_oneRun_of_disjointness`, `run_oneRun_of_mem`. + - Graph non-isomorphism: `PublicCoin.entry`, `entry_fin`, `value`, `value_fin` (use `matrixBit`) + and `mem_orbit_self`. + - Communication: `PublicCoin.majority` and `le_card_filter_ne_of_majority_ne` (use + `BitString.strictMajorityOn` and `card_filter_eq_le_half_of_strictMajorityOn_ne`); + `ecomplexity_le_succ`, `IsFoolingSet.le_ecomplexity`, + `PublicCoin.Solves.sq_mul_two_pow_le_four_pow`. + - Hamiltonian cycle: `SAT.rowLength`, `rowLength_sub_one`, `diamondArcs`, `literalArcs`, + `diamondGraphArcs`, `diamondGraphVertices`, `mem_diamondGraphArcs`, `mem_diamondGraphVertices` + (use `Diamond.graph`); `Diamond.Follows` and its lemmas are private. + - `SAT.SparseIndependentSet.mem_clauseEdges` and `mem_variableEdges`; the eight + `CookLevin.determined_of_mem_*` lemmas (private); `SharpSAT.getD_idxOf`, + `getD_mem_of_isFirst`, `isFirst_idxOf`; `Crypto.le_length_encPreimagePrefix` and + `UniformLearning.toOuterMeasure_uniformOfFintype_ne` (private); + `UniformLearning.signVal_mul_chi_mem_Icc`. + - Landings of 2026-10-19 before the rule: about 25 `AffineDisperser` names; the natural-proofs + predicates `IsNatural`, `IsLarge`, `IsUseful`, `IsCircuitTest`, `truthTableGenerator` and their + lemmas, moved into the `NaturalProof` namespace; 12 Ladner helpers (among them `boolDecoder`, + `natDecoder`, `ofBits`, `dropFalsesT`); `BakerGillSolovay.unaryLanguage` and three lemmas. +* **Meaning strengthened under the old name.** On 2026-10-20 `Turing.MultiTapeNTM.DecidesInSpace` + started to require that every branch halt (Sipser, Definition 8.1), so `NL` and `NPSPACE` + follow the textbook convention. The former meaning is `Turing.MultiTapeNTM.RecognizesInSpace` + (`DecidesInSpace.recognizesInSpace`); membership proofs now also need a per-instance halting + bound, the second component of `DecidesInSpace` (read back with + `DecidesInSpace.exists_runsInTime`). +* **Word-RAM operation bases.** On 2026-10-21 the word RAM became parametric in a basis of + operations. `Cslib.WordRAM.Op`, `Instr`, `Program` and `Code` take a basis `B` before the register + count, and `DecidableInTime`, `DecidableInTimeAt`, `LinearTime` and `PolynomialTime` take it as + their first explicit argument; the previous instruction set is `Cslib.WordRAM.Basis.standard`. + The constructors `Op.bin` and `Op.width` became definitions of the same names over the standard + basis, so construction is unchanged but pattern matching, `Op.bin.injEq` and `Op.width.injEq` + are gone (match on `Op.alu`). `Op`, `Instr` and `Code` no longer derive `Repr`. The binder `B` of + `Exec.loop`, `ExecWithin.loop` and `ExecWithin.loop_invariant` is now `T`, and the `B : Set β` + binder of the `recode` lemmas in `WordRAM/Time` is now `A'`; named-argument callers must follow. + On the same date `Cslib.Circuits.Signature.Hom` and `Signature.Hom.Preserves` moved, with their + names unchanged, from `Circuit/MapOp` to `Circuit/SignatureHom`, which `Circuit/MapOp` imports. +* **Word-RAM bases with undefined values.** On 2026-10-23 `Cslib.WordRAM.Basis.interp` became + `Option`-valued, and an operation whose value is `none` faults. The field + `Basis.interp_mono_of_not_readsWidth` replaces `Basis.interp_eq_of_not_readsWidth`; it only keeps + values that fit a word. + `Basis.Hom.preserves` is now a field of its own, an equation per word size, operation and + arguments, rather than `Signature.Hom.Preserves`. `StandardOp.eval`, `Basis.standard_interp_bin` + and `Basis.standard_interp_width` wrap their old values in `some`. `Op.eval_alu` now reads + `(B.interp w f (x.regs ∘ args)).bind (x.put w d)`: an undefined value propagates, and the word + check follows for a defined one. `Exec.alu` and `ExecWithin.alu` take the value `v` and the + hypothesis `B.interp w f (x.regs ∘ args) = some v`. `Basis.standard` keeps its semantics; the new + `Basis.exact` faults on `x - y` for `y > x` and on `x / 0` and `x % 0`. +* **Renamed modules.** On 2026-10-20 `Circuit/Boolean/MassProduction/{Sharp,SharpParameters, + SharpStatement}` became `…/{Fine,FineParameters,FineStatement}`, with their `Sharp` declarations + renamed to `Fine`. Imports of the old module paths must be updated: a module stub would have no + upstream counterpart, so none is kept. +* **Withdrawn results of 2026-10-22 (#372).** On 2026-10-22 the maintainer withdrew results merged + that morning without adequate correctness review, deleting them without aliases. An alias would + keep exactly the names being withdrawn, and none of them had been submitted to cslib or used + downstream. + - Regev's reduction (`Crypto/Lattice/LWE.lean`, whole module) and OT completeness + (`Crypto/Multiparty/ObliviousTransfer.lean`, whole module). + - `ACC0.lean`: `SymPlusCompilation`, `symPlusCompilation_zero`, + `exists_fastSymPlusEval_of_symPlusCompilation`, `exists_mem_NEXP_notMem_ACC0`, + `not_NEXP_subset_ACC0`. The duplicate `Cslib.Circuits.Boolean.SigmaP_subset_EXP` and + `SigmaP_subset_NEXP` were also deleted; use `Cslib.Complexity.SigmaP_subset_EXP` and + `SigmaP_subset_NEXP`, now in `Classes/Inclusions`. + - `IdentityTesting.lean`: `kabanets_impagliazzo`, `kabanets_impagliazzo_of_EXP_subset_P`, + `PermPITCertified`, `exists_certified_perm_family_of_PITInt_mem_P_of_mem_VP`, + `NEXP_eq_EXP_of_NEXP_subset_SigmaP`. + - `Hastad.lean`: `hastadSATProb` and its three lemmas, `hastadGraphSATProb`, and + `hastadGraphSATProb_eq_add_hastadGraphLinearCorr`. The SAT conjuncts were dropped from + `hastadGraphProb_longCode_of_satisfies` (now `graphLinearProb_longCode_of_satisfies`) and + `hastad_gapLabelCover_dichotomy` (now `gapLabelCover_dichotomy`). + - The conditional sumset-extractor frontier layer (#391), whose extractor-family hypothesis was + never discharged: `Circuit/Frontier/Extractor.lean` and `Complexity/FrontierExtractor.lean` + (whole modules), and the decimal restatements + `eventually_456249_div_100000_sub_mul_lt_innerSize`, + `circuitLowerBound_456249_div_100000`, `averageCaseBound_456249_div_100000`, + `eventually_456249_div_100000_sub_mul_lt_innerSize_of_nondeterministicDecides` and + `eventually_456249_div_100000_sub_mul_lt_card_of_aggregates`. Combine the exact-`L` theorems + with `Gaussian.circuitCoefficient_ge_456249` instead. + - #392 withdrew Gentry's bootstrapping (`Crypto/Computational/Bootstrapping.lean` and + `Bootstrapping/{HomomorphicEval,KeyChain,KeyEncryption}.lean`, whole modules), whose + `Bootstrappable` interface assumed the content of the theorem. From the Håstad modules it + deleted `GapExactThreeDistinctSAT`, `no_GapExactThreeDistinctSAT_eq_empty`, `xorThreeCNF` and + its five lemmas, `hastadLinearProb_le_of_hastadLinearCorr_le` and the same-day alias + `Cslib.PCP.Hastad.fourierCoeff_sq_signVal_le_one`. The kept long-code, folding and projection + lemmas moved to `Cslib.BitString` in `Foundations/BooleanAnalysis/LongCode` (`IsFolded` + became `IsOdd`), and `Hastad/ExactThreeDistinct` merged into + `PCP/Approximation/MaxExactThreeSAT`, keeping its declaration names; imports of the deleted + module path must be updated. `hastad_gapLabelCover_dichotomy` (now `gapLabelCover_dichotomy`) + now chooses `η` before the label sets. + - Follow-up #390 (same date and policy): the rest of the `ACC0.lean` Williams scaffolding, + `SymPlusCircuit` and its API, `evalMask` and its lemmas, `zetaTransform`, `fastZeta`, + `fastZeta_eq_zetaTransform`, `parity_mem_EXP`, `parity_mem_NEXP`, + `exists_mem_NEXP_notMem_{AC0,AC0Mod,SIZEMod}`, `exists_mem_SIZEMod_of_mem_AC0Mod`, + `NEXP_subset_PPoly_of_NEXP_subset_ACC0`, `PH_eq_SigmaP_two_of_NEXP_subset_ACC0` and + `Kannan.language_mem_SigmaP_two_of_NEXP_subset_ACC0`; the remaining `ACC0` development is + split into `ACC0/Basic` and `ACC0/Simulation`, and `NC_subset_PPoly`, `AC_subset_PPoly` and + `size_majorityDeMorganCircuit_le_sq` moved to `DepthClasses`, `FanInTwo` and `ThresholdNC1`, + all with names unchanged. The module + `Circuit/Arithmetic/IdentityTesting` is deleted. Its Schwartz–Zippel restatements + (`MvPolynomial.card_mul_card_roots_le`, `card_roots_le`, + `exists_eval_ne_zero_of_totalDegree_lt_card`; use Mathlib's + `MvPolynomial.schwartz_zippel_totalDegree`), `encArith*`, `PITInt` and its lemmas, the + arithmetic circuit combinators, the permanent chain (`minorVar` through + `isLaplacePermChain_laplacePermCircuit`), `intPermFamily` and `exists_mem_NEXP_notMem_SIZE` + are deleted. `Matrix.permanent_succ_{column,row}_zero` moved to + `Foundations/Data/Matrix/Permanent`, `Cslib.Circuits.Circuit.eval_polynomial` to + `Arithmetic/VP`, and `permFamily`, `isPFamily_permFamily` and + `totalDegree_permanent_genericMatrix_le` to `Arithmetic/Determinant`, with names unchanged. +* **Two multigraph namespaces.** Since 2026-10-20 the frontier multigraph + (`Cslib.Circuits.Frontier.Multigraph`) defines its simple graph as that of the multigraph given by + its ends (`Cslib.Multigraph`), and its module publicly imports the latter. Their shared base names + `toSimpleGraph`, `toSimpleGraph_adj`, `edgeCut` and `mem_edgeCut` are ambiguous when both + namespaces are visible, for example inside `namespace Cslib.Circuits.Frontier` or with both + opened; use dot notation (`G.toSimpleGraph_adj`) or full names. Upstreaming the frontier + multigraph therefore follows `Foundations/Combinatorics/Multigraph.lean`. + +The [historical ledger](https://github.com/cslib-community/complexitylib/blob/d0e6373970029a91018528463ca7a5ccd0fc9fd1/UPSTREAM.md) +preserves earlier integration notes and per-module follow-up ideas. + +* **Word-RAM word-size contracts (2026-10-22).** Time and encoding arguments now depend on + word size, and an ordinary predicate `allowed : α → ℕ → Prop` admits input/width pairs. + `WordSizeDependentProgram` selects code only by width; the primary decision and function classes + require a fixed register count and uniformly bounded instruction-list length. + `Uniform` variants retain one fixed program, + which may read the width. Existing fixed-code clients prove the uniform variants and can + forget uniformity. The `LogWord` wrappers express the transdichotomous assumption, require a + positive constant, and remain conditional on input fitting. Asymptotic time keeps correctness + at every admitted fitting input + and makes only the runtime estimate eventual, with constants shared by all widths. Width + schedules connect to Mathlib's real-valued big-O after casting. No effective selection of code, + width or encoding, or Turing-machine complexity identification, follows. These breaking local + API changes intentionally retain no deprecated aliases, as authorized by Erik for this redesign. From 780531b426051080bb274a0511f49142af03557c Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Wed, 28 Oct 2026 23:49:08 -0400 Subject: [PATCH 45/73] fix(imports): regenerate band-preparation aggregate from merged inventory --- Complexity.lean | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Complexity.lean b/Complexity.lean index 7c5648cd0..e148bc3bc 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -106,6 +106,11 @@ public import Complexity.Algorithms.WordRAM.FusionTree.Query.Traversal public import Complexity.Algorithms.WordRAM.FusionTree.Rank public import Complexity.Algorithms.WordRAM.FusionTree.ReadHeader public import Complexity.Algorithms.WordRAM.FusionTree.Static +public import Complexity.Algorithms.WordRAM.Karger.Contract +public import Complexity.Algorithms.WordRAM.Karger.FastCut +public import Complexity.Algorithms.WordRAM.Karger.Graph +public import Complexity.Algorithms.WordRAM.KargerMinCut +public import Complexity.Algorithms.WordRAM.KargerStein public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandAll public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandCatalogue public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandCollect @@ -128,11 +133,6 @@ public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandR public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandSplit public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandTables public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandThin -public import Complexity.Algorithms.WordRAM.Karger.Contract -public import Complexity.Algorithms.WordRAM.Karger.FastCut -public import Complexity.Algorithms.WordRAM.Karger.Graph -public import Complexity.Algorithms.WordRAM.KargerMinCut -public import Complexity.Algorithms.WordRAM.KargerStein public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Cell public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Controller public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Copy From 5bf1f0ed245eb79fd504e989dbf74542c6c05fbe Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Wed, 28 Oct 2026 23:50:02 -0400 Subject: [PATCH 46/73] doc(order-glue): preserve reviewed source credit through supplier merge --- Complexity/Foundations/Data/Finset/OrderGlue.lean | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Complexity/Foundations/Data/Finset/OrderGlue.lean b/Complexity/Foundations/Data/Finset/OrderGlue.lean index 252b1e6b3..7d2253b09 100644 --- a/Complexity/Foundations/Data/Finset/OrderGlue.lean +++ b/Complexity/Foundations/Data/Finset/OrderGlue.lean @@ -26,9 +26,10 @@ public import Mathlib.Data.Finset.Sort ## References -Adapted selectively from +Adapted selectively from [AlmanWilliams2026, Section 2] and [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). -Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical +counts and identities; they give no native program or instruction-cost certificate. -/ @[expose] public section From c3e1bcd3c8d9bb1dc4819d1b0a994bf52119731b Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Thu, 29 Oct 2026 00:25:16 -0400 Subject: [PATCH 47/73] doc(order-glue): restore paper citation and finite scope --- Complexity/Foundations/Data/Finset/OrderGlue.lean | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Complexity/Foundations/Data/Finset/OrderGlue.lean b/Complexity/Foundations/Data/Finset/OrderGlue.lean index 3cff17ec3..9a61f690c 100644 --- a/Complexity/Foundations/Data/Finset/OrderGlue.lean +++ b/Complexity/Foundations/Data/Finset/OrderGlue.lean @@ -23,9 +23,10 @@ public import Mathlib.Data.Finset.Sort ## References -Adapted selectively from +Adapted selectively from [AlmanWilliams2026, Section 2] and [Anthropic's pinned formal source](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Sec2). -Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. +Source pin: `16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. These are finite mathematical +counts and identities; they give no native program or instruction-cost certificate. -/ @[expose] public section From 59c6f80ca1b0ed01e32fd311b25ee80cf7f9090c Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 29 Oct 2026 00:37:09 -0400 Subject: [PATCH 48/73] feat(wordram): checkpoint native queried-product construction (#1581) Unfinished implementation above exact a6e4005b2 on the existing isolated child branch. The fixed native program computes both producers, protected layouts/copies, tile ABI, one shared timestamp directory, actual pruned calls and original-order reconstruction. Targeted Lean MCP checks pass for the new native program and supporting original-input, parameter/layout, copy/frame, grouping-span, raw-conversion, producer and directory proofs. Five actual initialized interpreter regressions pass: empty, signed/duplicate, raw01, multiple tiles/padding/epochs, and nonempty inner tensor coordinates. This is a recovery checkpoint, not a complete native product result or PR candidate. Full tile/epoch loop and reconstruction correctness, initialized composition, complete charged cost and logarithmic-width/saving results remain open under #1581/#1394. All six/global/static validation remains held for root; no aggregate or static audit ran. Existing curated abstract statements and scope are unaffected; no headline promotion. Implemented personally by codex-three-sum-continuation-t3-3dda8289 with AI assistance. --- .../Schonhage/Product/Admission.lean | 60 +++ .../Schonhage/Product/Arguments.lean | 127 ++++++ .../Schonhage/Product/Bands.lean | 85 ++++ .../Schonhage/Product/Copy.lean | 84 ++++ .../Schonhage/Product/Directory.lean | 101 +++++ .../Schonhage/Product/Groups.lean | 237 ++++++++++ .../Schonhage/Product/Header.lean | 406 ++++++++++++++++++ .../Schonhage/Product/Input.lean | 205 +++++++++ .../Schonhage/Product/Layout.lean | 113 +++++ .../Schonhage/Product/MatrixTransfer.lean | 165 +++++++ .../Schonhage/Product/Memory.lean | 165 +++++++ .../Schonhage/Product/Parameters.lean | 67 +++ .../Schonhage/Product/Program.lean | 238 ++++++++++ .../Schonhage/Product/Route.lean | 73 ++++ .../Schonhage/Product/SavedGroups.lean | 169 ++++++++ .../Schonhage/Product/Transfer.lean | 141 ++++++ ComplexityTests/SchonhageProduct.lean | 50 +++ 17 files changed, 2486 insertions(+) create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Admission.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Arguments.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Bands.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Copy.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Directory.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Groups.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Header.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Input.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Layout.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/MatrixTransfer.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Memory.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Parameters.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Program.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Route.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/SavedGroups.lean create mode 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Transfer.lean create mode 100644 ComplexityTests/SchonhageProduct.lean diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Admission.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Admission.lean new file mode 100644 index 000000000..cb0288641 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Admission.lean @@ -0,0 +1,60 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Input + +/-! +# Deriving producer and tile-kernel admission from original inputs + +## Main results + +* `QueriedProduct.kernel_fits`: actual separated tile pointers and bounded fresh epochs fit + from the single original numerical allocation envelope. +* `QueriedProduct.magnitude_admissions`: the original entry envelope covers shared encodings, + every pruned partial sum and the actual raw-word doubling instruction. + +No intermediate-state fit contract is part of the final caller's input. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM + +/-- The shared original allocation bound admits every computed tile's actual pointer values. +Query counts retain occurrences; epoch bounds are established by the caller's loop invariant. -/ +public theorem kernel_fits {w L m N Q a b q o n E : ℕ} + (hfit : Fits w (wordBudget L m N Q)) + (ha : a ≤ BandPreparation.memoryEnd L m N) (hb : b ≤ BandPreparation.memoryEnd L m N) + (hq : q ≤ codeBase L m N Q + Q) (ho : o ≤ outputBase L m N Q) (hn : n ≤ Q) + (hE : E ≤ epochBound L m N) : + Fits w (PrunedQuery.kernelBudget L (scratchBase L m N Q) a b q o n E) := by + apply hfit.of_le + unfold wordBudget PrunedQuery.kernelBudget PrunedQuery.evaluationBudget + PrunedQuery.nodeBudget PrunedQuery.addressBound + omega + +/-- All signed and conversion admission follows from the original integer-entry magnitude. +The extra two physical bits are the existing native signed-kernel convention. -/ +public theorem magnitude_admissions {w L A : ℕ} (hw : 0 < w) + (hmag : magnitudeBound L A < 2 ^ (w - 1)) : + (7 ^ L : ℕ) * (A : ℤ) < (2 : ℤ) ^ (w - 1) ∧ + PrunedQuery.magnitudeBound L (7 ^ L * A) (7 ^ L * A) < 2 ^ (w - 1) ∧ + Fits (w + 2) (2 * A) := by + have hk : PrunedQuery.magnitudeBound L (7 ^ L * A) (7 ^ L * A) < 2 ^ (w - 1) := by + unfold magnitudeBound at hmag + omega + have hp : 7 ^ L * A < 2 ^ (w - 1) := by + unfold PrunedQuery.magnitudeBound at hk + omega + refine ⟨by exact_mod_cast hp, hk, ?_⟩ + simpa only [entryWord, Bool.false_eq_true, ↓reduceIte, Signed.encode, + Int.natAbs_natCast, ite_eq_right (show ¬(A : ℤ) < 0 by omega), Nat.add_zero] using + entryWord_fits hw false (A : ℤ) (by simp) hmag + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Arguments.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Arguments.lean new file mode 100644 index 000000000..0d593cc4b --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Arguments.lean @@ -0,0 +1,127 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Transfer + +/-! +# Computing each tile's actual kernel arguments + +## Main results + +* `QueriedProduct.exec_tileArguments`: load real group endpoints, divide the tile index, + compute shared catalogue/query/output pointers and populate the existing native kernel ABI. + +The span values in this internal interface describe the actual saved offset words. The full +caller establishes their bounds from its executed counting/prefix/scatter passes. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +set_option maxRecDepth 4096 in +-- The fixed 160-register footprint check simplifies a 32-member persistent register set. +/-- Actual tile-index arithmetic and kernel-argument setup cost twenty-one instructions. +Only the two real offset words are read. Every unsigned arithmetic fit follows from the +original allocation bound and the computed span bounds. -/ +public theorem exec_tileArguments {w L m N Q t a b : ℕ} (x : Data 160) + (hp : Parameters L m N Q x) (hx : x.regs index = t) (ht : t < numBands L m N ^ 2) + (ha : x.mem (offsetBase L m N Q + t) = a) + (hb : x.mem (offsetBase L m N Q + t + 1) = b) (hab : a ≤ b) (hbQ : b ≤ Q) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w tileArguments x y 21 ∧ Parameters L m N Q y ∧ y.mem = x.mem ∧ + y.regs index = t ∧ y.regs epoch = x.regs epoch ∧ + (y.comap bank).regs PrunedQuery.inputDepth = L ∧ + (y.comap bank).regs PrunedQuery.arrayA = + BandPreparation.catalogueBase L m N + t / numBands L m N * 10 ^ L ∧ + (y.comap bank).regs PrunedQuery.arrayB = + BandPreparation.catalogueBase L m N + + (numBands L m N + t % numBands L m N) * 10 ^ L ∧ + (y.comap bank).regs PrunedQuery.queries = codeBase L m N Q + a ∧ + (y.comap bank).regs PrunedQuery.queryCount = b - a ∧ + (y.comap bank).regs PrunedQuery.output = groupedBase L m N Q + a ∧ + (y.comap bank).regs PrunedQuery.scratch = scratchBase L m N Q ∧ + (y.comap bank).regs PrunedQuery.epoch = x.regs epoch := by + let B := numBands L m N + let H := 10 ^ L + let C := BandPreparation.catalogueBase L m N + have hB : 0 < B := by dsimp [B] at *; nlinarith only [ht, Nat.zero_le t] + have hH : 1 ≤ H := Nat.one_le_pow L _ (by decide) + have hr : t / B < B := (Nat.div_lt_iff_lt_mul hB).mpr (by simpa [B, pow_two] using ht) + have hc : t % B < B := Nat.mod_lt t hB + have hrH : t / B * H ≤ B * H := Nat.mul_le_mul_right H hr.le + have hcH : (t % B + B) * H ≤ 2 * B * H := Nat.mul_le_mul_right H (by omega) + have horder : C + 2 * B * H ≤ memoryEnd L m N Q := by + have h := (producers_le_snapshot L m N Q).1 + have h' := (transfer_bounds L m N Q).2.1 + dsimp only [C, B, H, BandPreparation.memoryEnd] at * + omega + have hcodes : codeBase L m N Q + Q ≤ memoryEnd L m N Q := by + have h10 := Nat.zero_le H + unfold memoryEnd stampBase scratchBase outputBase groupedBase originalBase + omega + have hgroups : groupedBase L m N Q + Q ≤ memoryEnd L m N Q := by + unfold memoryEnd stampBase scratchBase outputBase + have h10 := Nat.zero_le (10 ^ L) + omega + have htiles : offsetBase L m N Q + B ^ 2 + 1 ≤ memoryEnd L m N Q := by + dsimp only [B] + unfold codeBase at hcodes + omega + have foffset : Fits w (offsetBase L m N Q + t) := allocated_fits hfit (by + dsimp only [B] at * + omega) + have fnext : Fits w (offsetBase L m N Q + t + 1) := allocated_fits hfit (by + dsimp only [B] at * + omega) + have frow : Fits w (t / B) := allocated_fits hfit (by nlinarith only [hr, hH, horder]) + have fcol : Fits w (t % B) := allocated_fits hfit (by nlinarith only [hc, hH, horder]) + have frowH : Fits w (t / B * H) := allocated_fits hfit (by nlinarith only [hrH, horder]) + have fleft : Fits w (t / B * H + C) := allocated_fits hfit (by nlinarith only [hrH, horder]) + have fcb : Fits w (t % B + B) := allocated_fits hfit (by + nlinarith only [hcH, hH, horder, Nat.zero_le (t % B + B)]) + have fcbH : Fits w ((t % B + B) * H) := allocated_fits hfit (by omega) + have fright : Fits w ((t % B + B) * H + C) := allocated_fits hfit (by omega) + have fq : Fits w (codeBase L m N Q + a) := allocated_fits hfit (by omega) + have fo : Fits w (groupedBase L m N Q + a) := allocated_fits hfit (by omega) + have fn : Fits w (b - a) := allocated_fits hfit (by omega) + let y := (((((((((((((((((((((x.set address (offsetBase L m N Q + t)).set first a).set + address (offsetBase L m N Q + t + 1)).set stop b).set rowBand (t / B)).set colBand + (t % B)).set leftPointer (t / B * H)).set leftPointer (t / B * H + C)).set + rightPointer (t % B + B)).set rightPointer ((t % B + B) * H)).set rightPointer + ((t % B + B) * H + C)).set queryPointer (codeBase L m N Q + a)).set answerPointer + (groupedBase L m N Q + a)).set (bank PrunedQuery.inputDepth) L).set + (bank PrunedQuery.arrayA) (t / B * H + C)).set (bank PrunedQuery.arrayB) + ((t % B + B) * H + C)).set (bank PrunedQuery.queries) (codeBase L m N Q + a)).set + (bank PrunedQuery.queryCount) (b - a)).set (bank PrunedQuery.output) + (groupedBase L m N Q + a)).set (bank PrunedQuery.scratch) (scratchBase L m N Q)).set + (bank PrunedQuery.epoch) (x.regs epoch)) + have he : ExecWithin w tileArguments x y 21 := by + ram_eval [tileArguments, y, hp.offsets, hp.one, hp.bands, hp.leaves, hp.catalogue, + hp.codes, hp.grouped, hp.depth, hp.scratch, hx, ha, hb, foffset, fnext, frow, fcol, + frowH, fleft, fcb, fcbH, fright, fq, fo, fn, B, H, C, bank, Fin.val_ofNat, + PrunedQuery.inputDepth, PrunedQuery.arrayA, PrunedQuery.arrayB, PrunedQuery.queries, + PrunedQuery.queryCount, PrunedQuery.output, PrunedQuery.scratch, PrunedQuery.epoch, + address, offsets, index, first, stop, one, rowBand, colBand, bands, leftPointer, + rightPointer, leaves, catalogue, queryPointer, answerPointer, codes, grouped, depth, + scratch, epoch, Fin.mk.injEq] + have yp : Parameters L m N Q y := hp.of_frame (by + intro i hi + apply he.regs_eq + have h : ∀ i ∈ parameterRegisters, tileArguments.Preserves i := by decide + exact h i hi) + refine ⟨y, he, yp, rfl, ?_⟩ + simp +decide [y, Data.comap_regs, hx, B, H, C, bank, + PrunedQuery.inputDepth, PrunedQuery.arrayA, PrunedQuery.arrayB, PrunedQuery.queries, + PrunedQuery.queryCount, PrunedQuery.output, PrunedQuery.scratch, PrunedQuery.epoch, + address, first, stop, rowBand, colBand, leftPointer, rightPointer, queryPointer, + answerPointer, index, epoch, Nat.add_comm] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Bands.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Bands.lean new file mode 100644 index 000000000..b6d4fa556 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Bands.lean @@ -0,0 +1,85 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Admission +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Memory +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandFlat + +/-! +# Running the actual shared catalogue producer in the product caller + +## Main results + +* `QueriedProduct.exec_bands`: invoke the complete actual band/encoding program in the low bank, + yielding the unchanged mathematical `encodingL/R` arrays and preserving the caller's scalars. + +The input memory equations are discharged by the preceding charged matrix adaptation. +No encoded catalogue, subset layout or producer execution is supplied to the program. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The actual native producer computes every shared band encoding, once per band. +Its complete outside frame protects the caller's original input and saved routing arrays. -/ +public theorem exec_bands {w L m N Q A : ℕ} (hw : 0 < w) (hm : m ≤ L) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (x : Data 160) (hp : Parameters L m N Q x) + (hL : x.mem 1 = L) (hmemory : x.mem 2 = m) (hN : x.mem 3 = N) + (hmat : ∀ side r, r < N → ∀ k, k < D m → + x.mem (BandPreparation.matrixOffset m N side r k + 4) = + Signed.encode (BandPreparation.matrixValues X Y side r k)) + (hX : ∀ i k, |X i k| ≤ (A : ℤ)) (hY : ∀ k j, |Y k j| ≤ (A : ℤ)) + (hmag : magnitudeBound L A < 2 ^ (w - 1)) + (hfit : Fits (w + 2) (wordBudget L m N Q)) : + ∃ y, ExecWithin (w + 2) (BandPreparation.program.rename bank) x y + (BandPreparation.timeBound L m N) ∧ Parameters L m N Q y ∧ + (∀ β < numBands L m N, ∀ τ, + y.mem (BandPreparation.catalogueAddress L m N false β (Encoding.leafIndex τ).val) = + Signed.encode (encodingL (bandArrayL (MaskTable.layout hm) X β) τ)) ∧ + (∀ β < numBands L m N, ∀ τ, + y.mem (BandPreparation.catalogueAddress L m N true β (Encoding.leafIndex τ).val) = + Signed.encode (encodingR (bandArrayR (MaskTable.layout hm) Y β) τ)) ∧ + (∀ a, a < BandPreparation.inputEnd m N ∨ BandPreparation.memoryEnd L m N ≤ a → + y.mem a = x.mem a) := by + have hbound (side : Bool) (r : ℕ) (hr : r < N) (k : ℕ) (hk : k < D m) : + |BandPreparation.matrixValues X Y side r k| ≤ (A : ℤ) := by + cases side <;> + simp only [BandPreparation.matrixValues, dite_eq_left hr, dite_eq_left hk, + Bool.false_eq_true, ↓reduceIte] + · exact hX _ _ + · exact hY _ _ + obtain ⟨z, he, _, hv, _, _, hf⟩ := BandPreparation.exec hw hm (x.comap bank) hL hmemory hN + (BandPreparation.matrixValues X Y) hmat (A : ℤ) (by positivity) hbound + (magnitude_admissions hw hmag).1 (producer_fits hfit).1 + let y := z.extend bank x + have hframe (i : Fin 160) (hi : 96 ≤ i.val) : y.regs i = x.regs i := by + have hnot : ¬∃ j, bank j = i := by + rintro ⟨j, hj⟩ + have hv := congrArg (fun r : Fin 160 => r.val) hj + change j.val = i.val at hv + omega + exact Function.extend_apply' _ _ _ hnot + refine ⟨y, he.rename bank_injective, hp.reframe hframe, ?_, ?_, ?_⟩ + · intro β hβ τ + change z.mem _ = _ + simpa only [BandPreparation.catalogueValue_left hm X (BandPreparation.matrixValues X Y false) + (by intro i k; simp [BandPreparation.matrixValues])] using + hv false β hβ (Encoding.leafIndex τ).val (Encoding.leafIndex τ).isLt + · intro β hβ τ + change z.mem _ = _ + simpa only [BandPreparation.catalogueValue_right hm Y (BandPreparation.matrixValues X Y true) + (by intro k i; simp [BandPreparation.matrixValues])] using + hv true β hβ (Encoding.leafIndex τ).val (Encoding.leafIndex τ).isLt + · intro a ha + exact hf a (by rcases ha with ha | ha <;> omega) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Copy.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Copy.lean new file mode 100644 index 000000000..3518f5431 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Copy.lean @@ -0,0 +1,84 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Program + +/-! +# Charged copies across the producer/consumer boundary + +## Main results + +* `QueriedProduct.exec_copyCall`: real argument setup and the unchanged dynamic native copy, + with both address orders, equality, complete memory meaning and caller-register frame. + +These are internal execution interfaces. Complete product clients derive the numerical fits +and separation from the original input and the explicit computed layout. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM + +/-- Each existing 96-register callee has exactly its original register semantics. -/ +public theorem bank_injective : Function.Injective bank := by + intro i j h + exact Fin.ext (congrArg (fun i : Fin 160 => i.val) h) + +private theorem copyBank_injective : Function.Injective copyBank := by + intro i j h + exact Fin.ext (congrArg (fun i : Fin 160 => i.val) h) + +/-- Dynamic native argument setup and every copy instruction are charged. The caller's +source/destination/length registers, and every register beyond the seven-register callee, +are preserved. Source and destination separation is a numerical layout obligation. -/ +public theorem exec_copyCall {w : ℕ} (x : Data 160) (h1 : Fits w 1) + (hs : Fits w (x.regs source + x.regs length)) + (hd : Fits w (x.regs destination + x.regs length)) + (hsep : x.regs source + x.regs length ≤ x.regs destination ∨ + x.regs destination + x.regs length ≤ x.regs source ∨ + x.regs source = x.regs destination) : + ∃ y, ExecWithin w copyCall x y (7 * x.regs length + 7) ∧ + y.mem = ArrayCopy.copyPrefix x.mem (x.regs source) (x.regs destination) (x.regs length) ∧ + (∀ i, 7 ≤ i.val → y.regs i = x.regs i) := by + let x1 := x.set (0 : Fin 160) (x.regs source) + let x2 := x1.set (1 : Fin 160) (x.regs destination) + let x3 := x2.set (2 : Fin 160) (x.regs length) + have h0 : ExecWithin (B := .standard) w (.op (.copy (0 : Fin 160) source)) x x1 1 := + ExecWithin.copy + have h01 : ExecWithin (B := .standard) w (.op (.copy (1 : Fin 160) destination)) x1 x2 1 := by + ram_eval [x1, x2, source, destination] + have h02 : ExecWithin (B := .standard) w (.op (.copy (2 : Fin 160) length)) x2 x3 1 := by + ram_eval [x1, x2, x3, source, destination, length] + obtain ⟨z, hz, hm, _, _, _⟩ := ArrayCopy.exec_of_separated_or_eq (x3.comap copyBank) h1 + (by simpa [x1, x2, x3, copyBank, ArrayCopy.src, ArrayCopy.len] using hs) + (by simpa [x1, x2, x3, copyBank, ArrayCopy.dst, ArrayCopy.len] using hd) + (by simpa [x1, x2, x3, copyBank, ArrayCopy.src, ArrayCopy.dst, ArrayCopy.len] using hsep) + let y := z.extend copyBank x3 + have he := hz.rename copyBank_injective + refine ⟨y, ?_, ?_, ?_⟩ + · have hlen : (x3.comap copyBank).regs ArrayCopy.len = x.regs length := by + simp [x1, x2, x3, copyBank, ArrayCopy.len] + rw [hlen] at he + exact (h0.seq (h01.seq (h02.seq he))).mono (by omega) + · change z.mem = _ + simpa [x1, x2, x3, copyBank, ArrayCopy.src, ArrayCopy.dst, ArrayCopy.len] using hm + · intro i hi + have hnot : ¬∃ j, copyBank j = i := by + rintro ⟨j, hj⟩ + have hv := congrArg (fun r : Fin 160 => r.val) hj + change j.val = i.val at hv + omega + change Function.extend copyBank z.regs x3.regs i = x.regs i + rw [Function.extend_apply' _ _ _ hnot] + simp only [x3, x2, x1, Data.set_regs, + Function.update_of_ne (show i ≠ (2 : Fin 160) by omega), + Function.update_of_ne (show i ≠ (1 : Fin 160) by omega), + Function.update_of_ne (show i ≠ (0 : Fin 160) by omega)] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Directory.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Directory.lean new file mode 100644 index 000000000..35c4a8fc0 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Directory.lean @@ -0,0 +1,101 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Transfer + +/-! +# One charged timestamp initialization for all product tiles + +## Main results + +* `QueriedProduct.exec_initializeDirectory`: clear the actual shared directory once and set + the initial epoch and tile index, with a complete outside frame. + +Subsequent kernel calls propagate their returned epoch. This initialization is not repeated +per tile or request. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM + +/-- Actual directory stores and loop control, followed by the epoch/index setup. -/ +public theorem exec_initializeDirectory {w L m N Q : ℕ} (x : Data 160) + (hp : Parameters L m N Q x) (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w initializeDirectory x y (5 * 10 ^ L + 5) ∧ + Parameters L m N Q y ∧ y.regs epoch = 0 ∧ y.regs index = 0 ∧ + (∀ j < 10 ^ L, y.mem (stampBase L m N Q + j) = 0) ∧ + (∀ a, a < stampBase L m N Q ∨ memoryEnd L m N Q ≤ a → y.mem a = x.mem a) := by + let H := 10 ^ L + let body : Code .standard 160 := ram[ + address ← stamps + index; *address ← zero; index ← index + one ] + let I (z : Data 160) : Prop := Parameters L m N Q z ∧ z.regs index ≤ H ∧ + (∀ j < z.regs index, z.mem (stampBase L m N Q + j) = 0) ∧ + (∀ a, a < stampBase L m N Q ∨ memoryEnd L m N Q ≤ a → z.mem a = x.mem a) + let start := x.set index 0 + have hs : ExecWithin (B := .standard) w (ram[index ← #0]) x start 1 := by + ram_eval [start] + have sp : Parameters L m N Q start := hp.set_work index (by decide) _ + have hstart : I start := ⟨sp, by simp [start], by simp [start], by simp [start]⟩ + have hbody (z : Data 160) (hz : I z) (hc : (Cond.lt index leaves).Holds z) : + ∃ u, ExecWithin w body z u 3 ∧ I u ∧ H - u.regs index < H - z.regs index := by + have hi : z.regs index < H := by simpa only [Cond.holds_lt, hz.1.leaves, H] using hc + have ha : Fits w (stampBase L m N Q + z.regs index) := allocated_fits hfit (by + unfold memoryEnd + dsimp only [H] at hi + omega) + have hn : Fits w (z.regs index + 1) := allocated_fits hfit (by + unfold memoryEnd + dsimp only [H] at hi + omega) + let u := ((z.set address (stampBase L m N Q + z.regs index)).write + (stampBase L m N Q + z.regs index) 0).set index (z.regs index + 1) + have he : ExecWithin w body z u 3 := by + ram_eval [body, u, hz.1.stamps, hz.1.zero, hz.1.one, ha, hn, + address, stamps, index, zero, one] + have up : Parameters L m N Q u := + ((hz.1.set_work address (by decide) _).write _ _).set_work index (by decide) _ + have hnext : u.regs index = z.regs index + 1 := by simp [u] + refine ⟨u, he, ⟨up, by rw [hnext]; omega, ?_, ?_⟩, by rw [hnext]; omega⟩ + · intro j hj + rw [hnext] at hj + change Function.update z.mem (stampBase L m N Q + z.regs index) 0 + (stampBase L m N Q + j) = 0 + by_cases h : j = z.regs index + · subst j + exact Function.update_self _ _ _ + · rw [Function.update_of_ne (by omega)] + exact hz.2.2.1 j (by omega) + · intro a ha + change Function.update z.mem (stampBase L m N Q + z.regs index) 0 a = _ + rw [Function.update_of_ne (by + unfold memoryEnd at ha + dsimp only [H] at hi + rcases ha with ha | ha <;> omega)] + exact hz.2.2.2 a ha + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I (fun z => H - z.regs index) hbody hstart + have hn : y.regs index = H := by + have hi := hy.2.1 + simp only [Cond.holds_lt, hy.1.leaves] at hc + dsimp only [H] at hi ⊢ + omega + let z := (y.set epoch 0).set index 0 + have hz : ExecWithin (B := .standard) w (ram[epoch ← #0; index ← #0]) y z 2 := by + ram_eval [z] + refine ⟨z, ?_, (hy.1.set_work epoch (by decide) _).set_work index (by decide) _, + by simp [z, epoch, index], by simp [z], ?_, ?_⟩ + · apply (hs.seq (he.seq hz)).mono + have hzero : start.regs index = 0 := by simp [start] + rw [hzero, Nat.sub_zero] + dsimp only [H] + omega + · simpa only [z, Data.set_mem, hn, H] using hy.2.2.1 + · simpa only [z, Data.set_mem] using hy.2.2.2 + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Groups.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Groups.lean new file mode 100644 index 000000000..b97f5bd22 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Groups.lean @@ -0,0 +1,237 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Transfer +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Memory + +/-! +# Saving actual routing arrays across producer calls + +## Main results + +* `QueriedProduct.exec_saveOffsets`: save all computed offsets, including the terminal word. +* `QueriedProduct.exec_saveCodes`: save the actual grouped query codes. +* `QueriedProduct.exec_saveOriginals`: save the actual original-position permutation. + +Each internal interface proves its charged copy execution. The protected outputs are copied +from actual router memory, without constructing or supplying a routing certificate. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Every low routing source precedes the protected destinations. -/ +public theorem group_copy_bounds (L m N Q : ℕ) : + Wanted.offsetAddress L m N Q + numBands L m N ^ 2 + 1 ≤ Wanted.memoryLimit L m N Q ∧ + Wanted.codeAddress L m N Q + Q ≤ Wanted.memoryLimit L m N Q ∧ + Wanted.originalAddress L m N Q + Q ≤ Wanted.memoryLimit L m N Q ∧ + Wanted.memoryLimit L m N Q ≤ offsetBase L m N Q ∧ + offsetBase L m N Q + numBands L m N ^ 2 + 1 ≤ memoryEnd L m N Q ∧ + codeBase L m N Q + Q ≤ memoryEnd L m N Q ∧ + originalBase L m N Q + Q ≤ memoryEnd L m N Q := by + have h10 := Nat.zero_le (10 ^ L) + simp only [Wanted.memoryLimit, Wanted.originalAddress, Wanted.codeAddress, + Wanted.cursorAddressBase, + offsetBase, snapshotBase, memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase] + omega + +/-- Actual argument setup and native copying preserve the caller protocol and every other +register beyond the copy routine. This is an internal producer-consumer execution interface. -/ +public theorem exec_saveOffsets {w L m N Q : ℕ} (x : Data 160) + (hp : Parameters L m N Q x) + (hsrc : x.regs (bank Wanted.offsets) = Wanted.offsetAddress L m N Q) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w saveOffsets x y (7 * numBands L m N ^ 2 + 17) ∧ Parameters L m N Q y ∧ + y.mem = ArrayCopy.copyPrefix x.mem (Wanted.offsetAddress L m N Q) + (offsetBase L m N Q) (numBands L m N ^ 2 + 1) ∧ + (∀ i, 7 ≤ i.val → i ≠ source → i ≠ destination → i ≠ length → y.regs i = x.regs i) := by + have hb := group_copy_bounds L m N Q + have hdest : offsetBase L m N Q + (numBands L m N ^ 2 + 1) ≤ memoryEnd L m N Q := by + exact hb.2.2.2.2.1 + have hsource : Wanted.offsetAddress L m N Q + (numBands L m N ^ 2 + 1) ≤ Wanted.memoryLimit L m + N Q := + hb.1 + have hsep : Wanted.offsetAddress L m N Q + (numBands L m N ^ 2 + 1) ≤ offsetBase L m N Q := by + have horder : offsetBase L m N Q ≤ offsetBase L m N Q := by + exact le_rfl + omega + have hn : Fits w (numBands L m N ^ 2 + 1) := allocated_fits hfit (by omega) + have hs : Fits w (Wanted.offsetAddress L m N Q + (numBands L m N ^ 2 + 1)) := allocated_fits + hfit (by omega) + have hd : Fits w (offsetBase L m N Q + (numBands L m N ^ 2 + 1)) := allocated_fits hfit (by omega) + let z := ((x.set source (Wanted.offsetAddress L m N Q)).set destination + (offsetBase L m N Q)).set length (numBands L m N ^ 2 + 1) + have hz : ExecWithin (B := .standard) w (.seq (.op (.copy source (bank Wanted.offsets))) (ram[ + destination ← offsets; length ← tiles + one ])) x z 3 := by + ram_eval [z, hsrc, hp.offsets, hp.tiles, hp.one, hn, tiles, one, source, destination, length, + offsets] + have zp : Parameters L m N Q z := + ((hp.set_work source (by decide) _).set_work destination (by decide) _).set_work + length (by decide) _ + obtain ⟨y, he, hm, hf⟩ := exec_copyCall z (allocated_fits hfit (by omega)) + (by simpa [z, source, destination, length] using hs) + (by simpa [z, source, destination, length] using hd) + (Or.inl (by simpa [z, source, destination, length] using hsep)) + refine ⟨y, ?_, zp.reframe (fun i hi => hf i (by omega)), ?_, ?_⟩ + · have hlen : z.regs length = (numBands L m N ^ 2 + 1) := by simp [z] + rw [hlen] at he + exact (hz.seq he).mono (by omega) + · simpa [z, source, destination, length] using hm + · intro i hi hs hd hl + rw [hf i hi] + simp only [z, Data.set_regs, Function.update_of_ne hl, Function.update_of_ne hd, + Function.update_of_ne hs] + +/-- Actual argument setup and native copying preserve the caller protocol and every other +register beyond the copy routine. This is an internal producer-consumer execution interface. -/ +public theorem exec_saveCodes {w L m N Q : ℕ} (x : Data 160) + (hp : Parameters L m N Q x) + (hsrc : x.regs (bank Wanted.codes) = Wanted.codeAddress L m N Q) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w saveCodes x y (7 * Q + 10) ∧ Parameters L m N Q y ∧ + y.mem = ArrayCopy.copyPrefix x.mem (Wanted.codeAddress L m N Q) + (codeBase L m N Q) (Q) ∧ + (∀ i, 7 ≤ i.val → i ≠ source → i ≠ destination → i ≠ length → y.regs i = x.regs i) := by + have hb := group_copy_bounds L m N Q + have hdest : codeBase L m N Q + (Q) ≤ memoryEnd L m N Q := by + exact hb.2.2.2.2.2.1 + have hsource : Wanted.codeAddress L m N Q + (Q) ≤ Wanted.memoryLimit L m N Q := + hb.2.1 + have hsep : Wanted.codeAddress L m N Q + (Q) ≤ codeBase L m N Q := by + have horder : offsetBase L m N Q ≤ codeBase L m N Q := by + unfold codeBase + omega + omega + have hn : Fits w (Q) := allocated_fits hfit (by omega) + have hs : Fits w (Wanted.codeAddress L m N Q + (Q)) := allocated_fits hfit (by omega) + have hd : Fits w (codeBase L m N Q + (Q)) := allocated_fits hfit (by omega) + let z := ((x.set source (Wanted.codeAddress L m N Q)).set destination + (codeBase L m N Q)).set length (Q) + have hz : ExecWithin (B := .standard) w (.seq (.op (.copy source (bank Wanted.codes))) (ram[ + destination ← codes; length ← queryCount ])) x z 3 := by + ram_eval [z, hsrc, hp.codes, hp.queryCount, queryCount, source, destination, length, codes] + have zp : Parameters L m N Q z := + ((hp.set_work source (by decide) _).set_work destination (by decide) _).set_work + length (by decide) _ + obtain ⟨y, he, hm, hf⟩ := exec_copyCall z (allocated_fits hfit (by omega)) + (by simpa [z, source, destination, length] using hs) + (by simpa [z, source, destination, length] using hd) + (Or.inl (by simpa [z, source, destination, length] using hsep)) + refine ⟨y, ?_, zp.reframe (fun i hi => hf i (by omega)), ?_, ?_⟩ + · have hlen : z.regs length = (Q) := by simp [z] + rw [hlen] at he + exact (hz.seq he).mono (by omega) + · simpa [z, source, destination, length] using hm + · intro i hi hs hd hl + rw [hf i hi] + simp only [z, Data.set_regs, Function.update_of_ne hl, Function.update_of_ne hd, + Function.update_of_ne hs] + +/-- Actual argument setup and native copying preserve the caller protocol and every other +register beyond the copy routine. This is an internal producer-consumer execution interface. -/ +public theorem exec_saveOriginals {w L m N Q : ℕ} (x : Data 160) + (hp : Parameters L m N Q x) + (hsrc : x.regs (bank Wanted.originals) = Wanted.originalAddress L m N Q) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w saveOriginals x y (7 * Q + 10) ∧ Parameters L m N Q y ∧ + y.mem = ArrayCopy.copyPrefix x.mem (Wanted.originalAddress L m N Q) + (originalBase L m N Q) (Q) ∧ + (∀ i, 7 ≤ i.val → i ≠ source → i ≠ destination → i ≠ length → y.regs i = x.regs i) := by + have hb := group_copy_bounds L m N Q + have hdest : originalBase L m N Q + (Q) ≤ memoryEnd L m N Q := by + exact hb.2.2.2.2.2.2 + have hsource : Wanted.originalAddress L m N Q + (Q) ≤ Wanted.memoryLimit L m N Q := + hb.2.2.1 + have hsep : Wanted.originalAddress L m N Q + (Q) ≤ originalBase L m N Q := by + have horder : offsetBase L m N Q ≤ originalBase L m N Q := by + unfold originalBase codeBase + omega + omega + have hn : Fits w (Q) := allocated_fits hfit (by omega) + have hs : Fits w (Wanted.originalAddress L m N Q + (Q)) := allocated_fits hfit (by omega) + have hd : Fits w (originalBase L m N Q + (Q)) := allocated_fits hfit (by omega) + let z := ((x.set source (Wanted.originalAddress L m N Q)).set destination + (originalBase L m N Q)).set length (Q) + have hz : ExecWithin (B := .standard) w (.seq (.op (.copy source (bank Wanted.originals))) (ram[ + destination ← originals; length ← queryCount ])) x z 3 := by + ram_eval [z, hsrc, hp.originals, hp.queryCount, queryCount, source, destination, length, + originals] + have zp : Parameters L m N Q z := + ((hp.set_work source (by decide) _).set_work destination (by decide) _).set_work + length (by decide) _ + obtain ⟨y, he, hm, hf⟩ := exec_copyCall z (allocated_fits hfit (by omega)) + (by simpa [z, source, destination, length] using hs) + (by simpa [z, source, destination, length] using hd) + (Or.inl (by simpa [z, source, destination, length] using hsep)) + refine ⟨y, ?_, zp.reframe (fun i hi => hf i (by omega)), ?_, ?_⟩ + · have hlen : z.regs length = (Q) := by simp [z] + rw [hlen] at he + exact (hz.seq he).mono (by omega) + · simpa [z, source, destination, length] using hm + · intro i hi hs hd hl + rw [hf i hi] + simp only [z, Data.set_regs, Function.update_of_ne hl, Function.update_of_ne hd, + Function.update_of_ne hs] + +/-- Save all three actual routing arrays before the second producer reuses low memory. +Every protected value is read from the router's executed memory, and the complete outside +frame includes the protected original input and all untouched low cells. -/ +public theorem exec_saveGroups {w L m N Q : ℕ} (x : Data 160) + (hp : Parameters L m N Q x) (wp : Wanted.Parameters L m N Q (x.comap bank)) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w saveGroups x y (7 * numBands L m N ^ 2 + 14 * Q + 37) ∧ + Parameters L m N Q y ∧ + (∀ t ≤ numBands L m N ^ 2, + y.mem (offsetBase L m N Q + t) = x.mem (Wanted.offsetAddress L m N Q + t)) ∧ + (∀ j < Q, y.mem (codeBase L m N Q + j) = x.mem (Wanted.codeAddress L m N Q + j)) ∧ + (∀ j < Q, y.mem (originalBase L m N Q + j) = + x.mem (Wanted.originalAddress L m N Q + j)) ∧ + (∀ a, a < offsetBase L m N Q ∨ originalBase L m N Q + Q ≤ a → y.mem a = x.mem a) := by + obtain ⟨a, ha, ap, am, af⟩ := exec_saveOffsets x hp wp.offsets hfit + have ac : a.regs (bank Wanted.codes) = Wanted.codeAddress L m N Q := + (af _ (by decide) (by decide) (by decide) (by decide)).trans wp.codes + obtain ⟨b, hb, bp, bm, bf⟩ := exec_saveCodes a ap ac hfit + have bo : b.regs (bank Wanted.originals) = Wanted.originalAddress L m N Q := + (bf _ (by decide) (by decide) (by decide) (by decide)).trans + ((af _ (by decide) (by decide) (by decide) (by decide)).trans wp.originals) + obtain ⟨y, hy, yp, ym, _⟩ := exec_saveOriginals b bp bo hfit + have hbounds := group_copy_bounds L m N Q + have horder : offsetBase L m N Q ≤ codeBase L m N Q ∧ + codeBase L m N Q ≤ originalBase L m N Q := by + simp only [codeBase, originalBase] + omega + refine ⟨y, (ha.seq (hb.seq hy)).mono (by omega), yp, ?_, ?_, ?_, ?_⟩ + · intro t ht + have hbefore : offsetBase L m N Q + t < codeBase L m N Q := by unfold codeBase; omega + rw [ym, ArrayCopy.copyPrefix_outside (by omega), bm, + ArrayCopy.copyPrefix_outside (by omega), am] + exact ArrayCopy.copyPrefix_read (by omega) + · intro j hj + have hbefore : codeBase L m N Q + j < originalBase L m N Q := by + unfold originalBase + omega + have hlow : Wanted.codeAddress L m N Q + j < offsetBase L m N Q := by omega + rw [ym, ArrayCopy.copyPrefix_outside (by omega), bm, ArrayCopy.copyPrefix_read hj, + am, ArrayCopy.copyPrefix_outside (by omega)] + · intro j hj + have hlow : Wanted.originalAddress L m N Q + j < offsetBase L m N Q := by omega + rw [ym, ArrayCopy.copyPrefix_read hj, bm, ArrayCopy.copyPrefix_outside (by omega), + am, ArrayCopy.copyPrefix_outside (by omega)] + · intro p hp + have hoff : offsetBase L m N Q + (numBands L m N ^ 2 + 1) = codeBase L m N Q := by + unfold codeBase + omega + have hcode : codeBase L m N Q + Q = originalBase L m N Q := rfl + rw [ym, ArrayCopy.copyPrefix_outside (by omega), bm, + ArrayCopy.copyPrefix_outside (by omega), am, ArrayCopy.copyPrefix_outside (by omega)] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Header.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Header.lean new file mode 100644 index 000000000..642e50b90 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Header.lean @@ -0,0 +1,406 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Memory +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Setup + +/-! +# Charged protected-buffer calculation + +## Main results + +* `QueriedProduct.exec_layout`: calculate every persistent scalar from the actual producer + header and original request count, with all intermediate fits derived from original inputs. + +This is an internal execution API. It receives ordinary header equalities and a numerical +input bound, not a supplied layout or intermediate-state certificate. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +set_option maxHeartbeats 2000000 in +-- The straight-line proof symbolically checks fifty register updates and their fits. +/-- Actual native layout calculation, preserving all original memory. -/ +public theorem exec_layout {w L m N Q : ℕ} (x : Data 160) + (hp : BandPreparation.BandParameters L m N (x.comap bank)) (hQ : x.mem 4 = Q) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w layout x y 50 ∧ Parameters L m N Q y ∧ y.mem = x.mem := by + have hl := caller_fits hfit + have h2 : 2 ^ L ≤ 10 ^ L := Nat.pow_le_pow_left (by decide) _ + have h7 : 7 ^ L ≤ 10 ^ L := Nat.pow_le_pow_left (by decide) _ + have hsmall (v : ℕ) (hv : v ≤ 16 * (L + N + Q + memoryEnd L m N Q + 16)) : + Fits w v := hl.of_le hv + have hb0 : x.regs (bank BandPreparation.levels) = L := hp.fixed.levels + have hb1 : x.regs (bank BandPreparation.inner) = m := hp.fixed.inner + have hb2 : x.regs (bank BandPreparation.rows) = N := hp.fixed.rows + have hb3 : x.regs (bank BandPreparation.columns) = D m := hp.columns + have hb4 : x.regs (bank BandPreparation.masks) = 2 ^ L := hp.masks + have hb5 : x.regs (bank BandPreparation.cells) = 7 ^ L := hp.cells + have hb6 : x.regs (bank BandPreparation.leaves) = 10 ^ L := hp.leaves + have hb7 : x.regs (bank BandPreparation.bands) = numBands L m N := hp.bands + have hb8 : x.regs (bank BandPreparation.catalogue) = BandPreparation.catalogueBase L m N := + hp.catalogue + have hf0 : Fits w (4) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf1 : Fits w (0) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf2 : Fits w (1) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf3 : Fits w (2) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf4 : Fits w (3) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf5 : Fits w (5) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf6 : Fits w (6) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf7 : Fits w ((numBands L m N) * (numBands L m N)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf8 : Fits w ((N) * (D m)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf9 : Fits w (((N) * (D m)) * (2)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf10 : Fits w ((((N) * (D m)) * (2)) + (5)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf11 : Fits w ((Q) * (2)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf12 : Fits w (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5))) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf13 : Fits w ((numBands L m N) * (10 ^ L)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf14 : Fits w (((numBands L m N) * (10 ^ L)) * (2)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf15 : Fits w ((((numBands L m N) * (10 ^ L)) * (2)) + (BandPreparation.catalogueBase L m + N)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf16 : Fits w ((Q) * (4)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf17 : Fits w (((Q) * (4)) + (6)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf18 : Fits w ((2 ^ L) * (2)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf19 : Fits w ((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf20 : Fits w (((numBands L m N) * (numBands L m N)) * (3)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf21 : Fits w (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m + N)) * (3))) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf22 : Fits w (((((numBands L m N) * (10 ^ L)) * (2)) + (BandPreparation.catalogueBase L m + N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) * + (3)))) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf23 : Fits w ((((((numBands L m N) * (10 ^ L)) * (2)) + (BandPreparation.catalogueBase L m + N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) * + (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf24 : Fits w (((((((numBands L m N) * (10 ^ L)) * (2)) + (BandPreparation.catalogueBase L + m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) * + (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * + (2)) + (5)))) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf25 : Fits w ((((((((numBands L m N) * (10 ^ L)) * (2)) + (BandPreparation.catalogueBase L + m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) * + (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * + (2)) + (5)))) + ((numBands L m N) * (numBands L m N))) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf26 : Fits w (((((((((numBands L m N) * (10 ^ L)) * (2)) + (BandPreparation.catalogueBase + L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) + * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * + (2)) + (5)))) + ((numBands L m N) * (numBands L m N))) + (1)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf27 : Fits w ((((((((((numBands L m N) * (10 ^ L)) * (2)) + (BandPreparation.catalogueBase + L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) + * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * + (2)) + (5)))) + ((numBands L m N) * (numBands L m N))) + (1)) + (Q)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf28 : Fits w (((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf29 : Fits w ((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf30 : Fits w (((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf31 : Fits w ((L) + (1)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf32 : Fits w (((L) + (1)) * (4)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf33 : Fits w ((((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q)) + (((L) + (1)) * (4))) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf34 : Fits w (((L) + (1)) * (10 ^ L)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf35 : Fits w ((((L) + (1)) * (10 ^ L)) * (2)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf36 : Fits w (((((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q)) + (((L) + (1)) * (4))) + ((((L) + (1)) * (10 ^ L)) * + (2))) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + have hf37 : Fits w ((((((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q)) + (((L) + (1)) * (4))) + ((((L) + (1)) * (10 ^ L)) * + (2))) + (10 ^ L)) := hsmall _ (by + simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, + originalBase, codeBase, offsetBase, snapshotBase, inputEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq] + ring_nf + omega) + let y := ((((((((((((((((((((((((((((((((((((((((((((((((((x.set depth (L)).set inner (m)).set + rows (N)).set address (4)).set queryCount (Q)).set columns (D m)).set masks (2 ^ L)).set + cells (7 ^ L)).set leaves (10 ^ L)).set bands (numBands L m N)).set catalogue + (BandPreparation.catalogueBase L m N)).set zero (0)).set one (1)).set two (2)).set three + (3)).set four (4)).set five (5)).set six (6)).set tiles ((numBands L m N) * (numBands L m + N))).set matrixWords ((N) * (D m))).set matrixWords (((N) * (D m)) * (2))).set queryOffset + ((((N) * (D m)) * (2)) + (5))).set inputSize ((Q) * (2))).set inputSize (((Q) * (2)) + + ((((N) * (D m)) * (2)) + (5)))).set bandEnd ((numBands L m N) * (10 ^ L))).set bandEnd + (((numBands L m N) * (10 ^ L)) * (2))).set bandEnd ((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N))).set wantedEnd ((Q) * (4))).set wantedEnd (((Q) * + (4)) + (6))).set temporary ((2 ^ L) * (2))).set wantedEnd ((((Q) * (4)) + (6)) + ((2 ^ L) * + (2)))).set temporary (((numBands L m N) * (numBands L m N)) * (3))).set wantedEnd (((((Q) * + (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) * (3)))).set + snapshot (((((numBands L m N) * (10 ^ L)) * (2)) + (BandPreparation.catalogueBase L m N)) + + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) * + (3))))).set snapshot ((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5))))).set offsets (((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5))))).set codes ((((((((numBands L m N) * + (10 ^ L)) * (2)) + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) + * (2))) + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * + (2)) + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * + (numBands L m N)))).set codes (((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1))).set originals ((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q))).set grouped (((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q))).set output ((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q))).set scratch (((((((((((((numBands L m N) * (10 ^ L)) * + (2)) + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q))).set temporary ((L) + (1))).set temporary (((L) + + (1)) * (4))).set stamps ((((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q)) + (((L) + (1)) * (4)))).set temporary ((L) + + (1))).set temporary (((L) + (1)) * (10 ^ L))).set temporary ((((L) + (1)) * (10 ^ L)) * + (2))).set stamps (((((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q)) + (((L) + (1)) * (4))) + ((((L) + (1)) * (10 ^ L)) * + (2)))).set memoryLimit ((((((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q)) + (((L) + (1)) * (4))) + ((((L) + (1)) * (10 ^ L)) * + (2))) + (10 ^ L))) + have he : ExecWithin w layout x y 50 := by + ram_eval [layout, y, hQ, hf0, hf1, hf2, hf3, hf4, hf5, hf6, hf7, hf8, hf9, hf10, hf11, hf12, + hf13, hf14, hf15, hf16, hf17, hf18, hf19, hf20, hf21, hf22, hf23, hf24, hf25, hf26, hf27, + hf28, hf29, hf30, hf31, hf32, hf33, hf34, hf35, hf36, hf37, depth, inner, rows, address, + queryCount, columns, masks, cells, leaves, bands, catalogue, zero, one, two, three, four, + five, six, tiles, matrixWords, queryOffset, inputSize, bandEnd, wantedEnd, temporary, + snapshot, offsets, codes, originals, grouped, output, scratch, stamps, memoryLimit, hb0, + hb1, hb2, hb3, hb4, hb5, hb6, hb7, hb8] + refine ⟨y, he, ?_, rfl⟩ + constructor <;> + simp +decide only [y, Data.set_regs, Function.update_apply, depth, inner, rows, address, + queryCount, columns, masks, cells, leaves, bands, catalogue, zero, one, two, three, four, + five, six, tiles, matrixWords, queryOffset, inputSize, bandEnd, wantedEnd, temporary, + snapshot, offsets, codes, originals, grouped, output, scratch, stamps, memoryLimit, + ↓reduceIte] <;> + (try simp only [inputEnd, queryInputBase, snapshotBase, offsetBase, codeBase, + originalBase, groupedBase, outputBase, scratchBase, stampBase, memoryEnd, + BandPreparation.memoryEnd, Wanted.memoryLimit_eq, pow_two]) <;> ring + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Input.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Input.lean new file mode 100644 index 000000000..65c80cdb3 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Input.lean @@ -0,0 +1,205 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Program +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Input +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandInput +public import Complexity.Foundations.Data.List.RowMajor +import Mathlib.Data.List.GetD +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Transfer + +/-! +# Original matrix and coordinate inputs for queried products + +## Main definitions + +* `QueriedProduct.entryWord`: canonical signed words or original raw nonnegative words. +* `QueriedProduct.input`: four scalar headers, both row-major matrices and raw coordinates. + +The raw-input program performs actual doubling before calling the signed producer. +In particular, raw 0/1 entries become canonical signed 0/2 words. This input definition +neither supplies encodings nor performs that target conversion on the caller's behalf. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Ordinary source words. In raw mode only nonnegative source values are represented; +the native matrix-copy loop performs the subsequent signed-encoding conversion. -/ +@[expose] public def entryWord (bits : Bool) (z : ℤ) : ℕ := + if bits then z.toNat else Signed.encode z + +/-- Actual doubling converts every nonnegative raw entry into its canonical signed word. +In particular, the raw unsigned zero/one producer convention becomes signed zero/two. -/ +public theorem convert_entryWord (bits : Bool) (z : ℤ) (hz : bits = true → 0 ≤ z) : + (if bits then 2 * entryWord bits z else entryWord bits z) = Signed.encode z := by + cases bits + · rfl + · have hnonneg := hz rfl + cases z with + | ofNat n => simp [entryWord, Signed.encode] + | negSucc n => omega + +/-- Both original matrices in their ordinary row-major input order. -/ +@[expose] public def matrixInput {m N : ℕ} (bits : Bool) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) : List ℕ := + (List.finRange N).flatMap (fun i => List.ofFn fun k => entryWord bits (X i k)) ++ + (List.finRange (D m)).flatMap (fun k => List.ofFn fun j => entryWord bits (Y k j)) + +/-- Actual initialized input, using exactly the existing router's raw coordinate convention. -/ +@[expose] public def input {m N : ℕ} (bits : Bool) (L : ℕ) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) : List ℕ := + [L, m, N, qs.length] ++ matrixInput bits X Y ++ (Wanted.input L m qs).drop 4 + +/-- The matrix block contains exactly both original input arrays. -/ +public theorem matrixInput_length {m N : ℕ} (bits : Bool) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) : + (matrixInput bits X Y).length = 2 * (N * D m) := by + simp only [matrixInput, List.flatMap_ofFn_prod, List.length_append, List.length_ofFn] + ring + +/-- Every original scalar, matrix entry and coordinate lies within the snapshot prefix. -/ +public theorem input_length {m N : ℕ} (bits : Bool) (L : ℕ) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) : + (input bits L X Y qs).length + 1 = inputEnd m N qs.length := by + have hq := Wanted.input_length L m qs + simp only [input, List.length_append, matrixInput_length, List.length_cons, + List.length_nil, List.length_drop, Wanted.inputLimit, inputEnd] at * + omega + +/-- The complete initialized input retains the router's raw coordinate values; the matrices +only shift their original address. No query code or tile is supplied in these words. -/ +public theorem input_query_word {m N : ℕ} (bits : Bool) (L : ℕ) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (i : Fin qs.length) (s : Fin 2) : + (State.init (input bits L X Y qs) : State 160).data.mem + (queryInputBase m N + 2 * i.val + s.val) = + if s = 0 then (qs.get i).1.val else (qs.get i).2.val := by + have hw := Wanted.input_word L m qs i s + change (Data.init (Wanted.input L m qs) : Data 96).mem _ = _ at hw + rw [show 5 + 2 * i.val + s.val = (4 + 2 * i.val + s.val) + 1 by omega, + Data.init_mem_succ] at hw + change (Data.init (input bits L X Y qs) : Data 160).mem _ = _ + rw [show queryInputBase m N + 2 * i.val + s.val = + (4 + 2 * (N * D m) + 2 * i.val + s.val) + 1 by unfold queryInputBase; omega, + Data.init_mem_succ] + unfold input + rw [List.getD_append_right _ _ _ _ (by + simp only [List.length_append, matrixInput_length, List.length_cons, List.length_nil] + omega)] + simp only [List.length_append, matrixInput_length, List.length_cons, List.length_nil] + rw [show 4 + 2 * (N * D m) + 2 * i.val + s.val - + (1 + (1 + (1 + (1 + 0))) + 2 * (N * D m)) = 2 * i.val + s.val by omega] + simp only [List.getD_eq_getElem?_getD, List.getElem?_drop] at hw ⊢ + simpa only [Nat.add_assoc] using hw + +/-- Both original row-major matrix blocks contain precisely their supplied entry words, +before any copying, sign conversion or encoding takes place. -/ +public theorem input_matrix_word {L m N : ℕ} (bits : Bool) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (side : Bool) (r k : ℕ) (hr : r < N) (hk : k < D m) : + (State.init (input bits L X Y qs) : State 160).data.mem + (BandPreparation.matrixOffset m N side r k + 5) = + entryWord bits (BandPreparation.matrixValues X Y side r k) := by + let xs := (List.finRange N).flatMap (fun i => List.ofFn fun k => entryWord bits (X i k)) + let ys := (List.finRange (D m)).flatMap (fun k => List.ofFn fun j => entryWord bits (Y k j)) + have hxs : xs.length = N * D m := by + simp only [xs, List.flatMap_ofFn_prod, List.length_ofFn] + have hys : ys.length = D m * N := by + simp only [ys, List.flatMap_ofFn_prod, List.length_ofFn] + have hoff : BandPreparation.matrixOffset m N side r k < (xs ++ ys).length := by + simp only [List.length_append, hxs, hys] + cases side + · have h := (finProdFinEquiv (⟨r, hr⟩, ⟨k, hk⟩)).isLt + simp only [finProdFinEquiv, Equiv.coe_fn_mk] at h + simp only [BandPreparation.matrixOffset, Bool.false_eq_true, ↓reduceIte] + nlinarith only [h, Nat.zero_le (N * D m)] + · have h := (finProdFinEquiv (⟨k, hk⟩, ⟨r, hr⟩)).isLt + simp only [finProdFinEquiv, Equiv.coe_fn_mk] at h + simp only [BandPreparation.matrixOffset, ↓reduceIte] + nlinarith only [h] + change (Data.init (input bits L X Y qs) : Data 160).mem _ = _ + rw [show BandPreparation.matrixOffset m N side r k + 5 = + (BandPreparation.matrixOffset m N side r k + 4) + 1 by omega, Data.init_mem_succ] + change (([L, m, N, qs.length] ++ (xs ++ ys) ++ (Wanted.input L m qs).drop 4).getD + (BandPreparation.matrixOffset m N side r k + 4) 0) = _ + rw [List.getD_append _ _ _ _ (by + simp only [List.length_append, List.length_cons, List.length_nil, hxs, hys] at hoff ⊢ + omega)] + rw [List.getD_append_right _ _ _ _ (by simp)] + simp only [List.length_cons, List.length_nil, Nat.reduceAdd, Nat.add_sub_cancel_right] + cases side + · have hi : r * D m + k < N * D m := by + have h := (finProdFinEquiv (⟨r, hr⟩, ⟨k, hk⟩)).isLt + simpa only [finProdFinEquiv, Equiv.coe_fn_mk, Nat.add_comm, Nat.mul_comm] using h + simp only [BandPreparation.matrixOffset, Bool.false_eq_true, ↓reduceIte] + rw [List.getD_append _ _ _ _ (by simpa only [hxs] using hi)] + simpa only [xs, BandPreparation.matrixValues, dite_eq_left hr, dite_eq_left hk, + Bool.false_eq_true, ↓reduceIte] using + List.getD_flatMap_ofFn (fun i k => entryWord bits (X i k)) 0 ⟨r, hr⟩ ⟨k, hk⟩ + · simp only [BandPreparation.matrixOffset, ↓reduceIte] + rw [List.getD_append_right _ _ _ _ (by rw [hxs]; omega), hxs, + Nat.add_sub_cancel_right] + simpa only [ys, BandPreparation.matrixValues, dite_eq_left hr, dite_eq_left hk, + ↓reduceIte] using + List.getD_flatMap_ofFn (fun k j => entryWord bits (Y k j)) 0 ⟨k, hk⟩ ⟨r, hr⟩ + +/-- The original magnitude envelope covers both canonical signed and raw unsigned words. +Raw-word fit does not itself assert nonnegativity of the represented source entries; that +extra semantic premise is used by the actual conversion proof. -/ +public theorem entryWord_fits {w L A : ℕ} (hw : 0 < w) (bits : Bool) (z : ℤ) + (hz : |z| ≤ (A : ℤ)) (hmag : magnitudeBound L A < 2 ^ (w - 1)) : + Fits (w + 2) (entryWord bits z) := by + have hA : A < 2 ^ (w - 1) := by + unfold magnitudeBound at hmag + omega + have hAi : (A : ℤ) < (2 : ℤ) ^ (w - 1) := by exact_mod_cast hA + have hzf : GenericRAM.Signed.Fits w z := + ⟨hw, (abs_lt.mp (hz.trans_lt hAi)).1.le, (abs_lt.mp (hz.trans_lt hAi)).2⟩ + cases bits + · exact (Signed.encode_fits hzf).mono (by omega) + · have hn : z.toNat ≤ A := Int.toNat_le.mpr ((le_abs_self z).trans hz) + exact ((show Fits (w - 1) A from hA).mono (show w - 1 ≤ w + 2 by omega)).of_le hn + +/-- Every actual initialized word fits from original dimensions and entry magnitudes. +No intermediate machine-state fit certificate is supplied. -/ +public theorem input_fits {w L m N A : ℕ} (hw : 0 < w) (hm : m ≤ L) (bits : Bool) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (hX : ∀ i k, |X i k| ≤ (A : ℤ)) + (hY : ∀ k j, |Y k j| ≤ (A : ℤ)) (hmag : magnitudeBound L A < 2 ^ (w - 1)) + (hfit : Fits (w + 2) (wordBudget L m N qs.length)) : + InputFits (w + 2) (input bits L X Y qs) := by + have hs := caller_fits hfit + have hb := transfer_bounds L m N qs.length + have hq := Wanted.input_fits hm qs (producer_fits hfit).2 + refine ⟨?_, ?_⟩ + · have hi := input_length bits L X Y qs + exact allocated_fits hfit (by omega) + · intro v hv + simp only [input, List.mem_append, List.mem_cons, List.not_mem_nil, or_false] at hv + rcases hv with ((rfl | rfl | rfl | rfl) | hv) | hv + · exact hs.of_le (by omega) + · exact hs.of_le (by omega) + · exact hs.of_le (by omega) + · exact hs.of_le (by omega) + · simp only [matrixInput, List.mem_append] at hv + rcases hv with hv | hv + · obtain ⟨i, _, hv⟩ := List.mem_flatMap.mp hv + obtain ⟨k, rfl⟩ := List.mem_ofFn.mp hv + exact entryWord_fits hw bits _ (hX i k) hmag + · obtain ⟨k, _, hv⟩ := List.mem_flatMap.mp hv + obtain ⟨j, rfl⟩ := List.mem_ofFn.mp hv + exact entryWord_fits hw bits _ (hY k j) hmag + · exact hq.2 v (List.mem_of_mem_drop hv) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Layout.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Layout.lean new file mode 100644 index 000000000..ddc061723 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Layout.lean @@ -0,0 +1,113 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandProgram +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Memory +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Bounds + +/-! +# Protected buffers for native queried products + +## Main definitions + +* `QueriedProduct.inputEnd`: four original headers, both matrices and all coordinate pairs. +* `QueriedProduct.snapshotBase`: protected original input above both producers' work areas. +* `QueriedProduct.offsetBase`, `QueriedProduct.codeBase`, `QueriedProduct.originalBase`: + protected copies of the actual computed routing arrays. +* `QueriedProduct.outputBase`: answers in original request order. +* `QueriedProduct.scratchBase`: reusable pruned-kernel workspace after every protected array. + +These numeric specifications describe the addresses computed by the native header. +Neither a layout nor an execution/cost certificate is supplied to the program. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- One past the original initialized input, including the native length word. -/ +@[expose] public def inputEnd (m N Q : ℕ) : ℕ := 5 + 2 * (N * D m) + 2 * Q + +/-- Original coordinate pairs follow both row-major input matrices. -/ +@[expose] public def queryInputBase (m N : ℕ) : ℕ := 5 + 2 * (N * D m) + +/-- The sum reserves disjoint space above the input and both low producer allocations. +Using a sum permits direct arithmetic without a hidden maximum/layout operation. -/ +@[expose] public def snapshotBase (L m N Q : ℕ) : ℕ := + BandPreparation.memoryEnd L m N + Wanted.memoryLimit L m N Q + inputEnd m N Q + +/-- Actual prefix offsets are saved here before the band producer reuses low memory. -/ +@[expose] public def offsetBase (L m N Q : ℕ) : ℕ := + snapshotBase L m N Q + inputEnd m N Q + +/-- The grouped numerical codes follow all offsets, including their terminal word. -/ +@[expose] public def codeBase (L m N Q : ℕ) : ℕ := + offsetBase L m N Q + numBands L m N ^ 2 + 1 + +/-- The actual original-index permutation follows the grouped codes. -/ +@[expose] public def originalBase (L m N Q : ℕ) : ℕ := codeBase L m N Q + Q + +/-- Kernel reporting writes answers in grouped order into this separate array. -/ +@[expose] public def groupedBase (L m N Q : ℕ) : ℕ := originalBase L m N Q + Q + +/-- Reconstructed answers in original list order; repeated requests have separate cells. -/ +@[expose] public def outputBase (L m N Q : ℕ) : ℕ := groupedBase L m N Q + Q + +/-- Every source, saved routing array and answer block precedes reusable kernel scratch. -/ +@[expose] public def scratchBase (L m N Q : ℕ) : ℕ := outputBase L m N Q + Q + +/-- The shared timestamp directory follows the kernel's actual frames and depth rows. -/ +@[expose] public def stampBase (L m N Q : ℕ) : ℕ := + scratchBase L m N Q + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L + +/-- One past the explicit whole-program allocation. -/ +@[expose] public def memoryEnd (L m N Q : ℕ) : ℕ := stampBase L m N Q + 10 ^ L + +/-- Every tile call increases its actual returned epoch by at most this total envelope. -/ +@[expose] public def epochBound (L m N : ℕ) : ℕ := + numBands L m N ^ 2 * (1 + PrunedQuery.epochAllowance L) + +/-- Ordinary scalar bound covering all producer, caller and kernel address arithmetic. +Signed arithmetic has its separate original-entry magnitude envelope. -/ +@[expose] public def wordBudget (L m N Q : ℕ) : ℕ := + 1024 * (BandPreparation.wordBudget L m N + Wanted.wordBudget L m N Q + + PrunedQuery.kernelBudget L (scratchBase L m N Q) (BandPreparation.memoryEnd L m N) + (BandPreparation.memoryEnd L m N) (codeBase L m N Q) (outputBase L m N Q) Q + (epochBound L m N) + memoryEnd L m N Q + inputEnd m N Q + L + N + Q + 16) + +/-- The source's magnitude bounds on shared encodings derive the kernel's operand bounds. -/ +@[expose] public def magnitudeBound (L A : ℕ) : ℕ := + PrunedQuery.magnitudeBound L (7 ^ L * A) (7 ^ L * A) + A + +/-- Both low producer work areas and the complete original input precede its snapshot. -/ +public theorem producers_le_snapshot (L m N Q : ℕ) : + BandPreparation.memoryEnd L m N ≤ snapshotBase L m N Q ∧ + Wanted.memoryLimit L m N Q ≤ snapshotBase L m N Q ∧ + inputEnd m N Q ≤ snapshotBase L m N Q := by + unfold snapshotBase + omega + +/-- The high buffers are adjacent and ordered, including zero-length answer arrays. -/ +public theorem buffer_order (L m N Q : ℕ) : + snapshotBase L m N Q + inputEnd m N Q = offsetBase L m N Q ∧ + offsetBase L m N Q + numBands L m N ^ 2 + 1 = codeBase L m N Q ∧ + codeBase L m N Q + Q = originalBase L m N Q ∧ + originalBase L m N Q + Q = groupedBase L m N Q ∧ + groupedBase L m N Q + Q = outputBase L m N Q ∧ + outputBase L m N Q + Q = scratchBase L m N Q := by + exact ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩ + +/-- Real original query addresses lie strictly inside the original input prefix. -/ +public theorem query_address_lt {m N Q i s : ℕ} (hi : i < Q) (hs : s < 2) : + queryInputBase m N + 2 * i + s < inputEnd m N Q := by + unfold queryInputBase inputEnd + omega + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/MatrixTransfer.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/MatrixTransfer.lean new file mode 100644 index 000000000..57038dd7c --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/MatrixTransfer.lean @@ -0,0 +1,165 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Transfer + +/-! +# Actual matrix-word adaptation between producer inputs + +## Main results + +* `QueriedProduct.exec_prepareBits`: copy each original raw unsigned entry while actually + doubling it for the canonical signed producer convention. + +The conversion uses a native load/multiply/store loop. Its original-entry bound derives +every arithmetic fit; its invariant protects unread source words and all caller scalars. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Source and destination blocks in the actual matrix adaptation are disjoint and fit. -/ +public theorem matrix_transfer_bounds (L m N Q : ℕ) : + 4 + 2 * (N * D m) ≤ snapshotBase L m N Q + 5 ∧ + snapshotBase L m N Q + 5 + 2 * (N * D m) ≤ memoryEnd L m N Q := by + have h := transfer_bounds L m N Q + unfold inputEnd at h + omega + +/-- Every raw entry is converted by an actual charged multiplication, with the complete +outside frame. The numerical bound concerns original source words, not intermediate states. -/ +public theorem exec_prepareBits {w L m N Q A : ℕ} (x : Data 160) + (hp : Parameters L m N Q x) (hfit : Fits w (wordBudget L m N Q)) + (hA : Fits w (2 * A)) + (hentries : ∀ i < 2 * (N * D m), x.mem (snapshotBase L m N Q + 5 + i) ≤ A) : + ∃ y, ExecWithin w (prepareMatrices true) x y (8 * (2 * (N * D m)) + 4) ∧ + Parameters L m N Q y ∧ + (∀ i < 2 * (N * D m), + y.mem (4 + i) = 2 * x.mem (snapshotBase L m N Q + 5 + i)) ∧ + (∀ a, a < 4 ∨ 4 + 2 * (N * D m) ≤ a → y.mem a = x.mem a) := by + let M := 2 * (N * D m) + let S := snapshotBase L m N Q + 5 + have hb := matrix_transfer_bounds L m N Q + have hsource : Fits w S := allocated_fits hfit (by dsimp [S]; omega) + let start := (x.set source S).set index 0 + have hs : ExecWithin (B := .standard) w (ram[ source ← snapshot + five; index ← #0 ]) + x start 2 := by + ram_eval [start, hp.snapshot, hp.five, hsource, S, source, snapshot, five, index] + have sp : Parameters L m N Q start := + (hp.set_work source (by decide) _).set_work index (by decide) _ + let I (z : Data 160) : Prop := Parameters L m N Q z ∧ z.regs source = S ∧ + z.regs index ≤ M ∧ + (∀ j < z.regs index, z.mem (4 + j) = 2 * x.mem (S + j)) ∧ + (∀ a, a < 4 ∨ 4 + z.regs index ≤ a → z.mem a = x.mem a) + have hstart : I start := ⟨sp, by simp [start, source, index], by simp [start], + by simp [start], by simp [start]⟩ + have hbody (z : Data 160) (hz : I z) (hc : (Cond.lt index matrixWords).Holds z) : + ∃ u, ExecWithin w bitCopyStep z u 6 ∧ I u ∧ M - u.regs index < M - z.regs index := by + have hi : z.regs index < M := by + simpa only [Cond.holds_lt, hz.1.matrixWords, M] using hc + have hload : z.mem (S + z.regs index) = x.mem (S + z.regs index) := + hz.2.2.2.2 _ (Or.inr (by dsimp [S, M] at *; omega)) + have hsrc : Fits w (S + z.regs index) := allocated_fits hfit (by + dsimp [S, M] at * + omega) + have hdst : Fits w (4 + z.regs index) := allocated_fits hfit (by + dsimp [S, M] at * + omega) + have hnext : Fits w (z.regs index + 1) := allocated_fits hfit (by + dsimp [S, M] at * + omega) + have hvalue : Fits w (x.mem (S + z.regs index) * 2) := hA.of_le (by + have hv := hentries (z.regs index) hi + dsimp only [S] + omega) + let u := (((((z.set address (S + z.regs index)).set value + (x.mem (S + z.regs index))).set value (x.mem (S + z.regs index) * 2)).set address + (4 + z.regs index)).write (4 + z.regs index) (x.mem (S + z.regs index) * 2)).set + index (z.regs index + 1) + have he : ExecWithin w bitCopyStep z u 6 := by + ram_eval [bitCopyStep, u, hz.2.1, hz.1.two, hz.1.four, hz.1.one, hload, + hsrc, hdst, hnext, hvalue, source, address, index, value, two, four, one] + have up : Parameters L m N Q u := + (((((hz.1.set_work address (by decide) _).set_work value (by decide) _).set_work + value (by decide) _).set_work address (by decide) _).write _ _).set_work + index (by decide) _ + have hn : u.regs index = z.regs index + 1 := by simp [u] + refine ⟨u, he, ⟨up, ?_, by rw [hn]; omega, ?_, ?_⟩, by rw [hn]; omega⟩ + · simpa [u, source, address, index, value] using hz.2.1 + · intro j hj + rw [hn] at hj + change Function.update z.mem (4 + z.regs index) (x.mem (S + z.regs index) * 2) + (4 + j) = 2 * x.mem (S + j) + by_cases h : j = z.regs index + · subst j + simp only [Function.update_self, Nat.mul_comm] + · rw [Function.update_of_ne (by omega)] + exact hz.2.2.2.1 j (by omega) + · intro a ha + change Function.update z.mem (4 + z.regs index) (x.mem (S + z.regs index) * 2) a = _ + rw [Function.update_of_ne (by rw [hn] at ha; rcases ha with ha | ha <;> omega)] + apply hz.2.2.2.2 a + rw [hn] at ha + rcases ha with ha | ha <;> omega + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I (fun z => M - z.regs index) hbody hstart + have hn : y.regs index = M := by + have hu := hy.2.2.1 + simp only [Cond.holds_lt, hy.1.matrixWords] at hc + dsimp only [M] at hu ⊢ + omega + refine ⟨y, (hs.seq he).mono ?_, hy.1, ?_, ?_⟩ + · have hzero : start.regs index = 0 := by simp [start] + rw [hzero, Nat.sub_zero] + dsimp [M] + omega + · simpa only [hn, M, S] using hy.2.2.2.1 + · simpa only [hn, M] using hy.2.2.2.2 + +/-- Canonical signed inputs use actual dynamic copying with no conversion arithmetic. -/ +public theorem exec_prepareSigned {w L m N Q : ℕ} (x : Data 160) + (hp : Parameters L m N Q x) (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w (prepareMatrices false) x y (7 * (2 * (N * D m)) + 10) ∧ + Parameters L m N Q y ∧ + (∀ i < 2 * (N * D m), + y.mem (4 + i) = x.mem (snapshotBase L m N Q + 5 + i)) ∧ + (∀ a, a < 4 ∨ 4 + 2 * (N * D m) ≤ a → y.mem a = x.mem a) := by + have hb := matrix_transfer_bounds L m N Q + have hs : Fits w (snapshotBase L m N Q + 5) := allocated_fits hfit (by omega) + let z := ((x.set source (snapshotBase L m N Q + 5)).set destination 4).set + length (2 * (N * D m)) + have hz : ExecWithin (B := .standard) w (ram[ + source ← snapshot + five; destination ← four; length ← matrixWords ]) x z 3 := by + ram_eval [z, hp.snapshot, hp.five, hp.four, hp.matrixWords, hs, + source, snapshot, five, destination, four, length, matrixWords] + have zp : Parameters L m N Q z := + ((hp.set_work source (by decide) _).set_work destination (by decide) _).set_work + length (by decide) _ + obtain ⟨y, he, hm, hf⟩ := exec_copyCall z (allocated_fits hfit (by omega)) + (by simpa [z, source, destination, length] using + allocated_fits hfit (show snapshotBase L m N Q + 5 + 2 * (N * D m) ≤ + memoryEnd L m N Q + 16 by omega)) + (by simpa [z, source, destination, length] using + allocated_fits hfit (show 4 + 2 * (N * D m) ≤ memoryEnd L m N Q + 16 by omega)) + (Or.inr (Or.inl (by simpa [z, source, destination, length] using hb.1))) + have hy : y.mem = ArrayCopy.copyPrefix x.mem (snapshotBase L m N Q + 5) 4 + (2 * (N * D m)) := by simpa [z, source, destination, length] using hm + refine ⟨y, ?_, zp.reframe (fun i hi => hf i (by omega)), ?_, ?_⟩ + · have hlen : z.regs length = 2 * (N * D m) := by simp [z] + rw [hlen] at he + exact (hz.seq he).mono (by omega) + · intro i hi + rw [hy] + exact ArrayCopy.copyPrefix_read hi + · intro a ha + rw [hy] + exact ArrayCopy.copyPrefix_outside (by rcases ha with ha | ha <;> omega) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Memory.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Memory.lean new file mode 100644 index 000000000..fd87249f5 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Memory.lean @@ -0,0 +1,165 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Parameters + +/-! +# Persistent queried-product caller scalars + +## Main definitions + +* `QueriedProduct.Parameters`: ordinary equalities describing computed high-register values. + +## Main results + +* `QueriedProduct.Parameters.reframe`: preserve the scalar protocol through actual low-bank calls. + +The record contains no execution, cost, layout-validity or intermediate-state-fit assertion. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Persistent high-register contents after the native layout calculation. -/ +public structure Parameters (L m N Q : ℕ) (x : Data 160) : Prop where + /-- Original depth. -/ + depth : x.regs depth = L + /-- Original inner depth. -/ + inner : x.regs inner = m + /-- Original outer matrix dimension. -/ + rows : x.regs rows = N + /-- Original request count, including repetitions. -/ + queryCount : x.regs queryCount = Q + /-- Original thin dimension. -/ + columns : x.regs columns = D m + /-- Complete subset-scan size. -/ + masks : x.regs masks = 2 ^ L + /-- Original band-array size. -/ + cells : x.regs cells = 7 ^ L + /-- Shared encoding-array size. -/ + leaves : x.regs leaves = 10 ^ L + /-- Computed number of shared bands. -/ + bands : x.regs bands = numBands L m N + /-- Computed number of tile calls. -/ + tiles : x.regs tiles = numBands L m N ^ 2 + /-- Complete original prefix size. -/ + inputSize : x.regs inputSize = inputEnd m N Q + /-- Protected source snapshot. -/ + snapshot : x.regs snapshot = snapshotBase L m N Q + /-- Saved actual offsets. -/ + offsets : x.regs offsets = offsetBase L m N Q + /-- Saved actual query codes. -/ + codes : x.regs codes = codeBase L m N Q + /-- Saved original-position permutation. -/ + originals : x.regs originals = originalBase L m N Q + /-- Grouped-answer block. -/ + grouped : x.regs grouped = groupedBase L m N Q + /-- Original-order answer block. -/ + output : x.regs output = outputBase L m N Q + /-- Reusable native kernel scratch. -/ + scratch : x.regs scratch = scratchBase L m N Q + /-- Shared timestamp directory. -/ + stamps : x.regs stamps = stampBase L m N Q + /-- One past the complete allocation. -/ + memoryLimit : x.regs memoryLimit = memoryEnd L m N Q + /-- Constant zero. -/ + zero : x.regs zero = 0 + /-- Constant one. -/ + one : x.regs one = 1 + /-- Unsigned multiplier for signed encoding. -/ + two : x.regs two = 2 + /-- Constant three. -/ + three : x.regs three = 3 + /-- Constant four. -/ + four : x.regs four = 4 + /-- Original matrix payload offset. -/ + five : x.regs five = 5 + /-- Constant six. -/ + six : x.regs six = 6 + /-- Both original matrix payloads. -/ + matrixWords : x.regs matrixWords = 2 * (N * D m) + /-- Original coordinate-pair offset. -/ + queryOffset : x.regs queryOffset = queryInputBase m N + /-- Low routing allocation end. -/ + wantedEnd : x.regs wantedEnd = Wanted.memoryLimit L m N Q + /-- Low producer allocation end. -/ + bandEnd : x.regs bandEnd = BandPreparation.memoryEnd L m N + /-- Computed low shared catalogue base. -/ + catalogue : x.regs catalogue = BandPreparation.catalogueBase L m N + +/-- Registers holding the persistent caller protocol. -/ +@[expose] public def parameterRegisters : Finset (Fin 160) := + {depth, inner, rows, queryCount, columns, masks, cells, leaves, bands, tiles, inputSize, + snapshot, offsets, codes, originals, grouped, output, scratch, stamps, memoryLimit, zero, one, + two, three, four, five, six, matrixWords, queryOffset, wantedEnd, bandEnd, catalogue} + +/-- Preserve the protocol through operations on any of the other caller work registers. -/ +public theorem Parameters.of_frame {L m N Q : ℕ} {x y : Data 160} + (hx : Parameters L m N Q x) (h : ∀ i ∈ parameterRegisters, y.regs i = x.regs i) : + Parameters L m N Q y := by + constructor + · exact (h _ (by decide)).trans hx.depth + · exact (h _ (by decide)).trans hx.inner + · exact (h _ (by decide)).trans hx.rows + · exact (h _ (by decide)).trans hx.queryCount + · exact (h _ (by decide)).trans hx.columns + · exact (h _ (by decide)).trans hx.masks + · exact (h _ (by decide)).trans hx.cells + · exact (h _ (by decide)).trans hx.leaves + · exact (h _ (by decide)).trans hx.bands + · exact (h _ (by decide)).trans hx.tiles + · exact (h _ (by decide)).trans hx.inputSize + · exact (h _ (by decide)).trans hx.snapshot + · exact (h _ (by decide)).trans hx.offsets + · exact (h _ (by decide)).trans hx.codes + · exact (h _ (by decide)).trans hx.originals + · exact (h _ (by decide)).trans hx.grouped + · exact (h _ (by decide)).trans hx.output + · exact (h _ (by decide)).trans hx.scratch + · exact (h _ (by decide)).trans hx.stamps + · exact (h _ (by decide)).trans hx.memoryLimit + · exact (h _ (by decide)).trans hx.zero + · exact (h _ (by decide)).trans hx.one + · exact (h _ (by decide)).trans hx.two + · exact (h _ (by decide)).trans hx.three + · exact (h _ (by decide)).trans hx.four + · exact (h _ (by decide)).trans hx.five + · exact (h _ (by decide)).trans hx.six + · exact (h _ (by decide)).trans hx.matrixWords + · exact (h _ (by decide)).trans hx.queryOffset + · exact (h _ (by decide)).trans hx.wantedEnd + · exact (h _ (by decide)).trans hx.bandEnd + · exact (h _ (by decide)).trans hx.catalogue + +set_option maxRecDepth 2048 in +-- Simplifying the fixed 32-member register set unfolds its finite membership expressions. +/-- Existing native callees operate below register 96, leaving this caller protocol intact. -/ +public theorem Parameters.reframe {L m N Q : ℕ} {x y : Data 160} + (hx : Parameters L m N Q x) (h : ∀ i, 96 ≤ i.val → y.regs i = x.regs i) : + Parameters L m N Q y := hx.of_frame (by + intro i hi + have hs : ∀ i ∈ parameterRegisters, 96 ≤ i.val := by decide + exact h i (hs i hi)) + +/-- Assigning a working register does not change any persistent parameter. -/ +public theorem Parameters.set_work {L m N Q : ℕ} {x : Data 160} + (hx : Parameters L m N Q x) (i : Fin 160) (hi : i ∉ parameterRegisters) (v : ℕ) : + Parameters L m N Q (x.set i v) := hx.of_frame (by + intro j hj + have hji : j ≠ i := fun heq => hi (heq ▸ hj) + simp only [Data.set_regs, Function.update_of_ne hji]) + +/-- Memory writes preserve the scalar protocol. -/ +public theorem Parameters.write {L m N Q : ℕ} {x : Data 160} + (hx : Parameters L m N Q x) (a v : ℕ) : Parameters L m N Q (x.write a v) := + hx.of_frame (by intro i _; rfl) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Parameters.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Parameters.lean new file mode 100644 index 000000000..4aabeed0a --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Parameters.lean @@ -0,0 +1,67 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Copy +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandParameters + +/-! +# Caller scalars from actual native parameters + +## Main results + +* `QueriedProduct.producer_fits`: original numerical admission covers both producer kernels. +* `QueriedProduct.exec_parameters`: actual powers, mask counting, root and band count, + with a charge and complete high-register/memory frame. + +The parameter phase reads original headers only. No computed roots, subset tables or +intermediate-state fit assertions are supplied by the caller. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM + +/-- Numerical admission is on original scalar parameters, not machine states. -/ +public theorem producer_fits {w L m N Q : ℕ} (hfit : Fits w (wordBudget L m N Q)) : + Fits w (BandPreparation.wordBudget L m N) ∧ Fits w (Wanted.wordBudget L m N Q) := by + constructor <;> apply hfit.of_le <;> unfold wordBudget <;> omega + +/-- A common numerical margin covers caller counters, all allocation endpoints and constants. -/ +public theorem caller_fits {w L m N Q : ℕ} (hfit : Fits w (wordBudget L m N Q)) : + Fits w (16 * (L + N + Q + memoryEnd L m N Q + 16)) := by + apply hfit.of_le + unfold wordBudget + omega + +/-- All actual parameter computation is charged, and the original matrix/query memory is +untouched. Reusing the low bank preserves every high caller register. -/ +public theorem exec_parameters {w L m N Q : ℕ} (hm : m ≤ L) (x : Data 160) + (hL : x.mem 1 = L) (hmemory : x.mem 2 = m) (hN : x.mem 3 = N) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w parameters x y + ((16 * L + 49) + ((6 * L + 16) * 2 ^ L + 14)) ∧ + BandPreparation.BandParameters L m N (y.comap bank) ∧ y.mem = x.mem ∧ + (∀ i, 96 ≤ i.val → y.regs i = x.regs i) := by + obtain ⟨h, hh, hp, hm⟩ := BandPreparation.exec_header hm (x.comap bank) hL hmemory hN + (producer_fits hfit).1 + obtain ⟨c, hc, cp, cm⟩ := BandPreparation.exec_countCode h hp (producer_fits hfit).1 + have he := (hh.seq hc).rename bank_injective + change ExecWithin w parameters x (c.extend bank x) + ((16 * L + 49) + ((6 * L + 16) * 2 ^ L + 14)) at he + refine ⟨c.extend bank x, he, ?_, cm.trans hm, ?_⟩ + · simpa only [Data.comap_extend bank_injective] using cp + · intro i hi + have hnot : ¬∃ j, bank j = i := by + rintro ⟨j, hj⟩ + have hv := congrArg (fun r : Fin 160 => r.val) hj + change j.val = i.val at hv + omega + exact Function.extend_apply' _ _ _ hnot + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Program.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Program.lean new file mode 100644 index 000000000..d08acd588 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Program.lean @@ -0,0 +1,238 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Layout + +/-! +# Actual producer, tile-kernel and reconstruction code + +## Main definitions + +* `QueriedProduct.program`: fixed native composition, with ordinary signed or raw-bit matrices. +* `QueriedProduct.tileStep`: actual offset loads, catalogue pointers, epoch call and update. +* `QueriedProduct.reconstruct`: restore the original request order from actual stored indices. + +All three existing 96-register callees use the low bank. The caller's 64 additional +registers retain its scalar layout and counters. Copying, initial directory clearing, +header adaptation, bit doubling, dispatch, reporting and restoration are real instructions. +Supporting code definitions are exposed for execution proofs; they are implementation APIs. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM + +/-- Reuse each existing native program in the same low 96-register bank. -/ +@[expose] public def bank (i : Fin 96) : Fin 160 := ⟨i.val, by omega⟩ +/-- The unchanged copy routine uses the first seven low registers. -/ +@[expose] public def copyBank (i : Fin 7) : Fin 160 := ⟨i.val, by omega⟩ + +/-- Persistent caller depth. -/ +public abbrev depth : Fin 160 := 96 +public abbrev inner : Fin 160 := 97 +public abbrev rows : Fin 160 := 98 +public abbrev queryCount : Fin 160 := 99 +public abbrev columns : Fin 160 := 100 +public abbrev masks : Fin 160 := 101 +public abbrev cells : Fin 160 := 102 +public abbrev leaves : Fin 160 := 103 +public abbrev bands : Fin 160 := 104 +public abbrev tiles : Fin 160 := 105 +public abbrev inputSize : Fin 160 := 106 +public abbrev snapshot : Fin 160 := 107 +public abbrev offsets : Fin 160 := 108 +public abbrev codes : Fin 160 := 109 +public abbrev originals : Fin 160 := 110 +public abbrev grouped : Fin 160 := 111 +public abbrev output : Fin 160 := 112 +public abbrev scratch : Fin 160 := 113 +public abbrev stamps : Fin 160 := 114 +public abbrev memoryLimit : Fin 160 := 115 +public abbrev epoch : Fin 160 := 116 +public abbrev index : Fin 160 := 117 +public abbrev first : Fin 160 := 118 +public abbrev stop : Fin 160 := 119 +public abbrev rowBand : Fin 160 := 120 +public abbrev colBand : Fin 160 := 121 +public abbrev leftPointer : Fin 160 := 122 +public abbrev rightPointer : Fin 160 := 123 +public abbrev queryPointer : Fin 160 := 124 +public abbrev answerPointer : Fin 160 := 125 +public abbrev source : Fin 160 := 126 +public abbrev destination : Fin 160 := 127 +public abbrev length : Fin 160 := 128 +public abbrev address : Fin 160 := 129 +public abbrev value : Fin 160 := 130 +public abbrev zero : Fin 160 := 131 +public abbrev one : Fin 160 := 132 +public abbrev two : Fin 160 := 133 +public abbrev four : Fin 160 := 134 +public abbrev five : Fin 160 := 135 +public abbrev six : Fin 160 := 136 +public abbrev matrixWords : Fin 160 := 137 +public abbrev queryOffset : Fin 160 := 138 +public abbrev wantedEnd : Fin 160 := 139 +public abbrev bandEnd : Fin 160 := 140 +public abbrev temporary : Fin 160 := 141 +public abbrev catalogue : Fin 160 := 145 +public abbrev three : Fin 160 := 146 + +/-- Compute genuine band scalars before selecting a protected caller area. No memory writes. -/ +@[expose] public def parameters : Code .standard 160 := + .seq (BandPreparation.header.rename bank) (BandPreparation.countCode.rename bank) + +/-- Preserve the computed original parameters and calculate every caller buffer address. -/ +@[expose] public def layout : Code .standard 160 := + let bLevels := bank BandPreparation.levels + let bInner := bank BandPreparation.inner + let bRows := bank BandPreparation.rows + let bColumns := bank BandPreparation.columns + let bMasks := bank BandPreparation.masks + let bCells := bank BandPreparation.cells + let bLeaves := bank BandPreparation.leaves + let bBands := bank BandPreparation.bands + let bCatalogue := bank BandPreparation.catalogue + ram[ + depth ← bLevels; inner ← bInner; rows ← bRows; + address ← #4; queryCount ← *address; + columns ← bColumns; masks ← bMasks; cells ← bCells; leaves ← bLeaves; + bands ← bBands; catalogue ← bCatalogue; + zero ← #0; one ← #1; two ← #2; three ← #3; four ← #4; five ← #5; six ← #6; + tiles ← bands * bands; + matrixWords ← rows * columns; matrixWords ← matrixWords * two; + queryOffset ← matrixWords + five; + inputSize ← queryCount * two; inputSize ← inputSize + queryOffset; + bandEnd ← bands * leaves; bandEnd ← bandEnd * two; bandEnd ← bandEnd + catalogue; + wantedEnd ← queryCount * four; wantedEnd ← wantedEnd + six; + temporary ← masks * two; wantedEnd ← wantedEnd + temporary; + temporary ← tiles * three; wantedEnd ← wantedEnd + temporary; + snapshot ← bandEnd + wantedEnd; snapshot ← snapshot + inputSize; + offsets ← snapshot + inputSize; + codes ← offsets + tiles; codes ← codes + one; + originals ← codes + queryCount; grouped ← originals + queryCount; + output ← grouped + queryCount; scratch ← output + queryCount; + temporary ← depth + one; temporary ← temporary * four; + stamps ← scratch + temporary; + temporary ← depth + one; temporary ← temporary * leaves; + temporary ← temporary * two; stamps ← stamps + temporary; + memoryLimit ← stamps + leaves ] + +/-- Real argument setup followed by the unchanged dynamic-length native copy. -/ +@[expose] public def copyCall : Code .standard 160 := + let copySrc : Fin 160 := 0 + let copyDst : Fin 160 := 1 + let copyLen : Fin 160 := 2 + ram[ + copySrc ← source; copyDst ← destination; copyLen ← length; + ⟪ArrayCopy.program.rename copyBank⟫ ] + +/-- Save the entire ordinary input, including its length word and coordinate list. -/ +@[expose] public def saveInput : Code .standard 160 := + .seq (ram[ source ← zero; destination ← snapshot; length ← inputSize ]) copyCall + +/-- Put the original raw coordinate pairs in the actual Wanted input convention. -/ +@[expose] public def prepareQueries : Code .standard 160 := + .seq (ram[ source ← snapshot + queryOffset; destination ← five; + length ← queryCount * two ]) copyCall + +/-- Save every computed tile offset, including the terminal offset. -/ +@[expose] public def saveOffsets : Code .standard 160 := + let wOffsets := bank Wanted.offsets + .seq (ram[ source ← wOffsets; destination ← offsets; length ← tiles + one ]) copyCall + +/-- Save the actual grouped query codes. -/ +@[expose] public def saveCodes : Code .standard 160 := + let wCodes := bank Wanted.codes + .seq (ram[ source ← wCodes; destination ← codes; length ← queryCount ]) copyCall + +/-- Save the actual original-position permutation. -/ +@[expose] public def saveOriginals : Code .standard 160 := + let wOriginals := bank Wanted.originals + .seq (ram[ source ← wOriginals; destination ← originals; length ← queryCount ]) copyCall + +/-- Preserve the computed arrays before the band producer reuses the low work area. -/ +@[expose] public def saveGroups : Code .standard 160 := + .seq saveOffsets (.seq saveCodes saveOriginals) + +/-- One actual load, unsigned doubling and store converts a raw 0/1 matrix entry. -/ +@[expose] public def bitCopyStep : Code .standard 160 := ram[ + address ← source + index; value ← *address; value ← value * two; + address ← four + index; *address ← value; index ← index + one ] + +/-- Copy real matrix words into the band producer's adjacent front input blocks. +The bit-input variant executes the conversion instruction at every entry. -/ +@[expose] public def prepareMatrices (bits : Bool) : Code .standard 160 := + if bits then + .seq (ram[ source ← snapshot + five; index ← #0 ]) + (.loop (.lt index matrixWords) bitCopyStep) + else + .seq (ram[ source ← snapshot + five; destination ← four; length ← matrixWords ]) copyCall + +/-- Initialize the one timestamp directory by actual stores, once for the entire product. -/ +@[expose] public def initializeDirectory : Code .standard 160 := ram[ + index ← #0; + while index < leaves do [ + address ← stamps + index; *address ← zero; index ← index + one ]; + epoch ← #0; index ← #0 ] + +/-- Load actual group endpoints and compute both shared catalogue pointers and kernel ABI. -/ +@[expose] public def tileArguments : Code .standard 160 := + let kDepth := bank PrunedQuery.inputDepth + let kLeft := bank PrunedQuery.arrayA + let kRight := bank PrunedQuery.arrayB + let kQueries := bank PrunedQuery.queries + let kCount := bank PrunedQuery.queryCount + let kOutput := bank PrunedQuery.output + let kScratch := bank PrunedQuery.scratch + let kEpoch := bank PrunedQuery.epoch + ram[ + address ← offsets + index; first ← *address; + address ← address + one; stop ← *address; + rowBand ← index / bands; colBand ← index % bands; + leftPointer ← rowBand * leaves; leftPointer ← leftPointer + catalogue; + rightPointer ← colBand + bands; rightPointer ← rightPointer * leaves; + rightPointer ← rightPointer + catalogue; + queryPointer ← codes + first; answerPointer ← grouped + first; + kDepth ← depth; kLeft ← leftPointer; kRight ← rightPointer; + kQueries ← queryPointer; kCount ← stop - first; kOutput ← answerPointer; + kScratch ← scratch; kEpoch ← epoch ] + +/-- One real pruned-kernel call, with the returned epoch propagated to the next tile. -/ +@[expose] public def tileStep : Code .standard 160 := + let kEpoch := bank PrunedQuery.epoch + .seq tileArguments (.seq (PrunedQuery.kernel.rename bank) + (ram[ epoch ← kEpoch; index ← index + one ])) + +/-- Compute all tiles from the actual group arrays; the zero-tile case never divides by zero. -/ +@[expose] public def evaluateTiles : Code .standard 160 := + .loop (.lt index tiles) tileStep + +/-- Read one actual original index and scatter its computed answer into request order. -/ +@[expose] public def reconstructStep : Code .standard 160 := ram[ + address ← originals + index; temporary ← *address; + address ← grouped + index; value ← *address; + address ← output + temporary; *address ← value; index ← index + one ] + +/-- Every original occurrence is reconstructed by charged native reads and stores. -/ +@[expose] public def reconstruct : Code .standard 160 := ram[ + index ← #0; while index < queryCount do [ ⟪reconstructStep⟫ ] ] + +/-- Restore the complete actual input after all private low-memory reuse has finished. -/ +@[expose] public def restoreInput : Code .standard 160 := + .seq (ram[ source ← snapshot; destination ← zero; length ← inputSize ]) copyCall + +/-- The complete fixed producer/consumer program. `bits=false` takes canonical signed matrix +words; `bits=true` takes raw unsigned 0/1 words and performs actual per-entry doubling. -/ +@[expose] public def program (bits : Bool) : Code .standard 160 := + .seq parameters (.seq layout (.seq saveInput (.seq prepareQueries + (.seq (Wanted.program.rename bank) (.seq saveGroups (.seq (prepareMatrices bits) + (.seq (BandPreparation.program.rename bank) (.seq initializeDirectory + (.seq evaluateTiles (.seq reconstruct restoreInput)))))))))) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Route.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Route.lean new file mode 100644 index 000000000..7ff6d8b88 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Route.lean @@ -0,0 +1,73 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Memory +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Exec +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Input + +/-! +# Actual routing in the product caller's register bank + +## Main results + +* `QueriedProduct.exec_wanted`: invoke the existing router on real coordinates while retaining + the caller's protected addresses, including its untouched original-input snapshot. + +No routing or permutation certificate is an input. The computed memory equations in this +internal interface are conclusions of the actual finite native program. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The existing actual router runs in the low bank and preserves every caller scalar. +Its stable permutation retains separate occurrences of duplicate requests. -/ +public theorem exec_wanted {w L m N : ℕ} (hm : m ≤ L) (qs : List (Fin N × Fin N)) + (x : Data 160) (hp : Parameters L m N qs.length x) + (hL : x.mem 1 = L) (hmemory : x.mem 2 = m) (hN : x.mem 3 = N) + (hQ : x.mem 4 = qs.length) + (hinput : ∀ i < qs.length, + x.mem (5 + 2 * i) = (Wanted.inputPairs qs i).1 ∧ + x.mem (5 + 2 * i + 1) = (Wanted.inputPairs qs i).2) + (hfit : Fits w (wordBudget L m N qs.length)) : + ∃ y, ExecWithin w (Wanted.program.rename bank) x y (Wanted.timeBound L m N qs.length) ∧ + Parameters L m N qs.length y ∧ Wanted.Parameters L m N qs.length (y.comap bank) ∧ + (∀ i : Fin qs.length, + y.mem (Wanted.codeAddress L m N qs.length + + CountingSort.position (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) qs.length i) = + (PrunedQuery.queryIndex + (outStrOfPos (MaskTable.layout hm) (qs.get i).1 (qs.get i).2)).val ∧ + y.mem (Wanted.originalAddress L m N qs.length + + CountingSort.position (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) qs.length i) = + i.val) ∧ + (∀ t ≤ numBands L m N ^ 2, + y.mem (Wanted.offsetAddress L m N qs.length + t) = + Nat.count (fun k => (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) k < t) qs.length) ∧ + y.mem (Wanted.offsetAddress L m N qs.length + numBands L m N ^ 2) = qs.length ∧ + (∀ a, a < Wanted.inputLimit qs.length ∨ Wanted.memoryLimit L m N qs.length ≤ a → + y.mem a = x.mem a) := by + obtain ⟨z, he, zp, zv, zo, zlast, zframe⟩ := Wanted.exec_program hm (Wanted.inputPairs qs) + (Wanted.inputPairs_lt qs) (x.comap bank) hL hmemory hN hQ hinput (producer_fits hfit).2 + let y := z.extend bank x + have hf (i : Fin 160) (hi : 96 ≤ i.val) : y.regs i = x.regs i := by + have hnot : ¬∃ j, bank j = i := by + rintro ⟨j, hj⟩ + have hv := congrArg (fun r : Fin 160 => r.val) hj + change j.val = i.val at hv + omega + exact Function.extend_apply' _ _ _ hnot + refine ⟨y, he.rename bank_injective, hp.reframe hf, ?_, ?_, zo, zlast, zframe⟩ + · simpa only [y, Data.comap_extend bank_injective] using zp + · intro i + change z.mem _ = _ ∧ z.mem _ = _ + simpa only [Wanted.inputPairs_get] using zv i.val i.isLt + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/SavedGroups.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/SavedGroups.lean new file mode 100644 index 000000000..fc6c0ff9a --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/SavedGroups.lean @@ -0,0 +1,169 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Groups +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wanted.Groups + +/-! +# Memory invariants for the saved routing arrays + +## Main definitions + +* `QueriedProduct.SavedGroups`: protected words agree with an earlier actual router state. + +## Main results + +* `QueriedProduct.SavedGroups.reframe`: kernel and producer frames retain the saved arrays. +* `QueriedProduct.router_code_lt`: executed stable scatter gives a valid query code at every + grouped position, including duplicate requests. +* `QueriedProduct.SavedGroups.span_bounds`: actual saved offsets form admitted kernel spans. + +The earlier memory is a proof reference to an executed state, not program input. The native +copy theorem establishes these equalities before any low-memory reuse occurs. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Values copied from the actual router state into protected caller-owned arrays. -/ +public structure SavedGroups (L m N Q : ℕ) (old μ : ℕ → ℕ) : Prop where + /-- All actual offsets, including the terminal offset. -/ + offsets : ∀ t ≤ numBands L m N ^ 2, + μ (offsetBase L m N Q + t) = old (Wanted.offsetAddress L m N Q + t) + /-- All actual grouped codes. -/ + codes : ∀ j < Q, μ (codeBase L m N Q + j) = old (Wanted.codeAddress L m N Q + j) + /-- All actual original-position words. -/ + originals : ∀ j < Q, + μ (originalBase L m N Q + j) = old (Wanted.originalAddress L m N Q + j) + +/-- Preserving the protected readonly interval preserves the saved routing arrays. -/ +public theorem SavedGroups.reframe {L m N Q : ℕ} {old μ ν : ℕ → ℕ} + (h : SavedGroups L m N Q old μ) + (hf : ∀ a, offsetBase L m N Q ≤ a → a < groupedBase L m N Q → ν a = μ a) : + SavedGroups L m N Q old ν := by + have hc : offsetBase L m N Q + numBands L m N ^ 2 + 1 = codeBase L m N Q := by + unfold codeBase + omega + have ho : codeBase L m N Q + Q = originalBase L m N Q := rfl + have hg : originalBase L m N Q + Q = groupedBase L m N Q := rfl + constructor + · intro t ht + exact (hf _ (by omega) (by omega)).trans (h.offsets t ht) + · intro j hj + exact (hf _ (by omega) (by omega)).trans (h.codes j hj) + · intro j hj + exact (hf _ (by omega) (by omega)).trans (h.originals j hj) + +/-- Stable scatter covers every grouped cell, so every stored code is a valid query index. +The proof uses the existing permutation theorem rather than a supplied inverse table. -/ +public theorem router_code_lt {L m N : ℕ} (hm : m ≤ L) (qs : List (Fin N × Fin N)) + (old : ℕ → ℕ) + (hvalues : ∀ i : Fin qs.length, + old (Wanted.codeAddress L m N qs.length + + CountingSort.position (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) qs.length i) = + (PrunedQuery.queryIndex + (outStrOfPos (MaskTable.layout hm) (qs.get i).1 (qs.get i).2)).val) + (j : ℕ) (hj : j < qs.length) : + old (Wanted.codeAddress L m N qs.length + j) < 10 ^ L := by + obtain ⟨i, hi⟩ := (CountingSort.position_bijective + (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) qs.length).surjective ⟨j, hj⟩ + have hp := congrArg (fun k : Fin qs.length => k.val) hi + change CountingSort.position (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) qs.length i = j at hp + have hv := hvalues i + rw [hp] at hv + rw [hv] + exact (PrunedQuery.queryIndex _).isLt + +/-- The actual protected offset words satisfy the kernel's monotone span and count bounds. +These inequalities follow from the executed prefix-count meanings, not input certificates. -/ +public theorem SavedGroups.span_bounds {L m N Q : ℕ} {old μ : ℕ → ℕ} + (h : SavedGroups L m N Q old μ) (key : ℕ → ℕ) + (hoffsets : ∀ t ≤ numBands L m N ^ 2, + old (Wanted.offsetAddress L m N Q + t) = Nat.count (fun k => key k < t) Q) + {t : ℕ} (ht : t < numBands L m N ^ 2) : + μ (offsetBase L m N Q + t) ≤ μ (offsetBase L m N Q + t + 1) ∧ + μ (offsetBase L m N Q + t + 1) ≤ Q := by + rw [show offsetBase L m N Q + t + 1 = offsetBase L m N Q + (t + 1) by omega, + h.offsets t ht.le, h.offsets (t + 1) (by omega), hoffsets t ht.le, + hoffsets (t + 1) (by omega)] + exact ⟨Nat.count_mono_left (fun k _ hk => by omega), Nat.count_le _⟩ + +/-- A typed view of the actual saved code span requests exactly the original finite tile +set. This uses the existing router's span theorem; the native caller constructs the view +by decoding its real words, rather than receiving a query-set or routing certificate. -/ +public theorem SavedGroups.span_queries {L m N : ℕ} (hm : m ≤ L) + (qs : List (Fin N × Fin N)) {old μ : ℕ → ℕ} (h : SavedGroups L m N qs.length old μ) + (hvalues : ∀ i : Fin qs.length, + old (Wanted.codeAddress L m N qs.length + + CountingSort.position (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) qs.length i) = + (PrunedQuery.queryIndex + (outStrOfPos (MaskTable.layout hm) (qs.get i).1 (qs.get i).2)).val) + (hoffsets : ∀ t ≤ numBands L m N ^ 2, + old (Wanted.offsetAddress L m N qs.length + t) = + Nat.count (fun k => (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) k < t) qs.length) + (T : ℕ × ℕ) (hT : T ∈ Cslib.Algorithms.MatrixMultiplication.Schonhage.tiles L m N) (a b : ℕ) + (ha : μ (offsetBase L m N qs.length + (T.1 * numBands L m N + T.2)) = a) + (hb : μ (offsetBase L m N qs.length + (T.1 * numBands L m N + T.2 + 1)) = b) + (queries : Fin (b - a) → OutStr L) + (hinput : ∀ i, μ (codeBase L m N qs.length + (a + i.val)) = + (PrunedQuery.queryIndex (queries i)).val) : + Finset.univ.image queries = wantedStrings (MaskTable.layout hm) qs.toFinset T.1 T.2 := by + have hband : T.1 < numBands L m N ∧ T.2 < numBands L m N := by + simpa only [Cslib.Algorithms.MatrixMultiplication.Schonhage.tiles, + Finset.mem_product, Finset.mem_range] using hT + have ht : T.1 * numBands L m N + T.2 < numBands L m N ^ 2 := by + nlinarith only [Nat.mul_le_mul_right (numBands L m N) (Nat.succ_le_of_lt hband.1), hband.2] + have has : old (Wanted.offsetAddress L m N qs.length + + (T.1 * numBands L m N + T.2)) = a := (h.offsets _ ht.le).symm.trans ha + have hbs : old (Wanted.offsetAddress L m N qs.length + + (T.1 * numBands L m N + T.2 + 1)) = b := + (h.offsets _ (by omega)).symm.trans hb + have habQ : a ≤ b ∧ b ≤ qs.length := by + have hs := h.span_bounds (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) hoffsets ht + simpa only [ha, show offsetBase L m N qs.length + + (T.1 * numBands L m N + T.2) + 1 = offsetBase L m N qs.length + + (T.1 * numBands L m N + T.2 + 1) by omega, hb] using hs + have hword (i : Fin (b - a)) : old (Wanted.codeAddress L m N qs.length + (a + i.val)) = + (PrunedQuery.queryIndex (queries i)).val := by + rw [← h.codes (a + i.val) (by have hi := i.isLt; omega)] + exact hinput i + have hspan := Wanted.code_span_eq_wantedStrings hm qs old T hT hvalues hoffsets + rw [has, hbs] at hspan + have hinj : Function.Injective (fun v : OutStr L => (PrunedQuery.queryIndex v).val) := by + intro u v he + simpa only [PrunedQuery.queryAt_queryIndex] using + congrArg PrunedQuery.queryAt (Fin.ext he) + ext w + constructor + · intro hw + obtain ⟨i, _, rfl⟩ := Finset.mem_image.mp hw + have hcode : (PrunedQuery.queryIndex (queries i)).val ∈ + (wantedStrings (MaskTable.layout hm) qs.toFinset T.1 T.2).image + (fun w => (PrunedQuery.queryIndex w).val) := by + rw [← hspan] + exact Finset.mem_image.mpr ⟨a + i.val, Finset.mem_Ico.mpr + ⟨by omega, by have hi := i.isLt; omega⟩, hword i⟩ + obtain ⟨v, hv, he⟩ := Finset.mem_image.mp hcode + exact hinj he ▸ hv + · intro hw + have hcode : (PrunedQuery.queryIndex w).val ∈ + (Finset.Ico a b).image (fun j => old (Wanted.codeAddress L m N qs.length + j)) := by + rw [hspan] + exact Finset.mem_image.mpr ⟨w, hw, rfl⟩ + obtain ⟨j, hj, he⟩ := Finset.mem_image.mp hcode + have hj' := Finset.mem_Ico.mp hj + let i : Fin (b - a) := ⟨j - a, by omega⟩ + have hai : a + i.val = j := by dsimp [i]; omega + have hread := hword i + rw [hai] at hread + exact Finset.mem_image.mpr ⟨i, Finset.mem_univ _, hinj (hread.symm.trans he)⟩ + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Transfer.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Transfer.lean new file mode 100644 index 000000000..87b43502a --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Transfer.lean @@ -0,0 +1,141 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Memory + +/-! +# Protected original-input and coordinate transfers + +## Main results + +* `QueriedProduct.exec_saveInput`: snapshot the complete original input by actual native copying. +* `QueriedProduct.exec_prepareQueries`: adapt real coordinates to the router's input convention. + +These internal execution interfaces derive copy separation and arithmetic fits from the +original numerical parameters. Their memory conclusions describe the actual executed state. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM + +/-- Original input, its protected copy and the coordinate adaptation all fit the allocation. -/ +public theorem transfer_bounds (L m N Q : ℕ) : + inputEnd m N Q ≤ snapshotBase L m N Q ∧ + snapshotBase L m N Q + inputEnd m N Q ≤ memoryEnd L m N Q ∧ + 5 + 2 * Q ≤ snapshotBase L m N Q + queryInputBase m N ∧ + snapshotBase L m N Q + queryInputBase m N + 2 * Q ≤ memoryEnd L m N Q := by + have h10 := Nat.zero_le (10 ^ L) + unfold memoryEnd stampBase scratchBase outputBase groupedBase originalBase codeBase + offsetBase snapshotBase queryInputBase inputEnd + omega + +/-- Any allocated endpoint fits without assumptions about intermediate machine states. -/ +public theorem allocated_fits {w L m N Q a : ℕ} (hfit : Fits w (wordBudget L m N Q)) + (ha : a ≤ memoryEnd L m N Q + 16) : Fits w a := by + apply (caller_fits hfit).of_le + omega + +/-- The actual input snapshot uses three argument instructions and the native copy loop. -/ +public theorem exec_saveInput {w L m N Q : ℕ} (x : Data 160) + (hp : Parameters L m N Q x) (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w saveInput x y (7 * inputEnd m N Q + 10) ∧ + Parameters L m N Q y ∧ + y.mem = ArrayCopy.copyPrefix x.mem 0 (snapshotBase L m N Q) (inputEnd m N Q) := by + let z := ((x.set source 0).set destination (snapshotBase L m N Q)).set length (inputEnd m N Q) + have hz : ExecWithin (B := .standard) w (ram[ + source ← zero; destination ← snapshot; length ← inputSize ]) x z 3 := by + ram_eval [z, hp.zero, hp.snapshot, hp.inputSize, source, destination, length, + zero, snapshot, inputSize] + have zp : Parameters L m N Q z := + ((hp.set_work source (by decide) 0).set_work destination (by decide) _).set_work + length (by decide) _ + have hb := transfer_bounds L m N Q + have hi : Fits w (inputEnd m N Q) := allocated_fits hfit (by omega) + have hs : Fits w (snapshotBase L m N Q + inputEnd m N Q) := + allocated_fits hfit (by omega) + obtain ⟨y, he, hm, hf⟩ := exec_copyCall z (allocated_fits hfit (by omega)) + (by simpa [z, source, destination, length] using hi) + (by simpa [z, source, destination, length] using hs) + (Or.inl (by simpa [z, source, destination, length] using hb.1)) + refine ⟨y, ?_, zp.reframe (fun i hi => hf i (by omega)), ?_⟩ + · have hlen : z.regs length = inputEnd m N Q := by simp [z] + rw [hlen] at he + exact (hz.seq he).mono (by omega) + · simpa [z, source, destination, length] using hm + +/-- Real coordinate words are copied from the protected original input to address five. +The caller's original prefix remains protected in its snapshot for final restoration. -/ +public theorem exec_prepareQueries {w L m N Q : ℕ} (x : Data 160) + (hp : Parameters L m N Q x) (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w prepareQueries x y (14 * Q + 10) ∧ Parameters L m N Q y ∧ + y.mem = ArrayCopy.copyPrefix x.mem + (snapshotBase L m N Q + queryInputBase m N) 5 (2 * Q) := by + let z := ((x.set source (snapshotBase L m N Q + queryInputBase m N)).set destination 5).set + length (Q * 2) + have hb := transfer_bounds L m N Q + have hs : Fits w (snapshotBase L m N Q + queryInputBase m N) := + allocated_fits hfit (by omega) + have hn : Fits w (Q * 2) := allocated_fits hfit (by + have h10 := Nat.zero_le (10 ^ L) + unfold memoryEnd stampBase scratchBase outputBase groupedBase originalBase codeBase + offsetBase snapshotBase inputEnd + omega) + have hz : ExecWithin (B := .standard) w (ram[ + source ← snapshot + queryOffset; destination ← five; length ← queryCount * two ]) + x z 3 := by + ram_eval [z, hp.snapshot, hp.queryOffset, hp.five, hp.queryCount, hp.two, hs, hn, + source, destination, length, snapshot, queryOffset, five, queryCount, two] + have zp : Parameters L m N Q z := + ((hp.set_work source (by decide) _).set_work destination (by decide) _).set_work + length (by decide) _ + obtain ⟨y, he, hm, hf⟩ := exec_copyCall z (allocated_fits hfit (by omega)) + (by simpa [z, source, destination, length, Nat.mul_comm] using + allocated_fits hfit (show snapshotBase L m N Q + queryInputBase m N + 2 * Q ≤ + memoryEnd L m N Q + 16 by omega)) + (by simpa [z, source, destination, length, Nat.mul_comm] using + allocated_fits hfit (show 5 + 2 * Q ≤ memoryEnd L m N Q + 16 by omega)) + (Or.inr (Or.inl (by + simpa [z, source, destination, length, Nat.mul_comm] using hb.2.2.1))) + refine ⟨y, ?_, zp.reframe (fun i hi => hf i (by omega)), ?_⟩ + · have hlen : z.regs length = Q * 2 := by simp [z] + rw [hlen] at he + exact (hz.seq he).mono (by omega) + · simpa [z, source, destination, length, Nat.mul_comm] using hm + +/-- Restore the entire original prefix through the same actual copy routine. Answers and +all memory outside the prefix remain unchanged. -/ +public theorem exec_restoreInput {w L m N Q : ℕ} (x : Data 160) + (hp : Parameters L m N Q x) (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w restoreInput x y (7 * inputEnd m N Q + 10) ∧ + Parameters L m N Q y ∧ + y.mem = ArrayCopy.copyPrefix x.mem (snapshotBase L m N Q) 0 (inputEnd m N Q) := by + let z := ((x.set source (snapshotBase L m N Q)).set destination 0).set length (inputEnd m N Q) + have hz : ExecWithin (B := .standard) w (ram[ + source ← snapshot; destination ← zero; length ← inputSize ]) x z 3 := by + ram_eval [z, hp.zero, hp.snapshot, hp.inputSize, source, destination, length, + zero, snapshot, inputSize] + have zp : Parameters L m N Q z := + ((hp.set_work source (by decide) _).set_work destination (by decide) 0).set_work + length (by decide) _ + have hb := transfer_bounds L m N Q + have hi : Fits w (inputEnd m N Q) := allocated_fits hfit (by omega) + have hs : Fits w (snapshotBase L m N Q + inputEnd m N Q) := + allocated_fits hfit (by omega) + obtain ⟨y, he, hm, hf⟩ := exec_copyCall z (allocated_fits hfit (by omega)) + (by simpa [z, source, destination, length] using hs) + (by simpa [z, source, destination, length] using hi) + (Or.inr (Or.inl (by simpa [z, source, destination, length] using hb.1))) + refine ⟨y, ?_, zp.reframe (fun i hi => hf i (by omega)), ?_⟩ + · have hlen : z.regs length = inputEnd m N Q := by simp [z] + rw [hlen] at he + exact (hz.seq he).mono (by omega) + · simpa [z, source, destination, length] using hm + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/ComplexityTests/SchonhageProduct.lean b/ComplexityTests/SchonhageProduct.lean new file mode 100644 index 000000000..a91940a62 --- /dev/null +++ b/ComplexityTests/SchonhageProduct.lean @@ -0,0 +1,50 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Input + +-- Actual native interpreter regressions supplement the general execution proofs. +set_option linter.hashCommand false + +namespace ComplexityTests.SchonhageProduct + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +private def observe {m N : ℕ} (bits : Bool) (L : ℕ) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) : Option (List ℤ × Bool) := + let data := (State.init (input bits L X Y qs) : State 160).data + let sentinel := memoryEnd L m N qs.length + 17 + let start := data.write sentinel 91 + ((program bits).eval 32 4096 start).map fun (y, _) => + ((List.range qs.length).map (fun i => decode (y.mem (y.regs output + i))), + decide ((∀ a < inputEnd m N qs.length, y.mem a = data.mem a) ∧ y.mem sentinel = 91)) + +-- The actual complete pipeline handles an empty original input without a tile division. +#guard observe false 0 (m := 0) (N := 0) (fun _ _ => 0) (fun _ _ => 0) [] == + some ([], true) + +-- Signed operands and repeated requests survive both producer calls and reconstruction. +#guard observe false 0 (m := 0) (N := 1) (fun _ _ => -3) (fun _ _ => 2) + [(0, 0), (0, 0), (0, 0)] == some ([-6, -6, -6], true) + +-- Raw unsigned entries are doubled by real code before signed encoding, not in the input. +#guard observe true 0 (m := 0) (N := 1) (fun _ _ => 1) (fun _ _ => 1) + [(0, 0), (0, 0)] == some ([1, 1], true) + +-- Distinct tile pointers and returned epochs are reused across all four padded band pairs. +#guard observe false 1 (m := 0) (N := 4) + (fun i _ => (i.val : ℤ) - 2) (fun _ j => (j.val : ℤ) - 1) + [(3, 0), (0, 3), (3, 3), (1, 0), (3, 0)] == some ([-1, -4, 2, 1, -1], true) + +-- A nonempty inner coordinate makes the pruned kernel visit all ten signed tensor terms. +#guard observe false 1 (m := 1) (N := 2) + (fun i k => (i.val : ℤ) + k.val - 1) (fun k j => (k.val : ℤ) + j.val - 2) + [(1, 0), (0, 1), (1, 1), (1, 0)] == some ([2, 6, 8, 2], true) + +end ComplexityTests.SchonhageProduct From 6a86e258db83b7724f5bd93c43c0cbc4221a992f Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Thu, 29 Oct 2026 01:34:50 -0400 Subject: [PATCH 49/73] fix(imports): regenerate thin-product aggregate after main integration --- Complexity.lean | 3 --- 1 file changed, 3 deletions(-) diff --git a/Complexity.lean b/Complexity.lean index 26b3e885d..4cf21c14b 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -43,9 +43,6 @@ public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Pruned public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Sparse public import Complexity.Algorithms.MatrixMultiplication.Schonhage.ThinProduct public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Tiling -public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Full -public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Identity -public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Pruned public import Complexity.Algorithms.Reachability.BreadthFirst public import Complexity.Algorithms.Reachability.InductiveCounting public import Complexity.Algorithms.Reachability.InductiveCounting.Bounds From 61e7fe769e7f9de5b977185885850d08c93193ab Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Thu, 29 Oct 2026 02:05:56 -0400 Subject: [PATCH 50/73] doc(wordram): separate shared-encoder integration records Keep the reviewed finite-matrix supplier records intact and record native-encoder scope and source credit separately, so squash-queue integration does not overlap the ancestor documentation hunks. Preserve code, tests, concrete bounds and the complete unrelated upstream-record suffix. Queue revisions are used only for read-only projection, never as parents. --- NOTICE | 7 +++++-- ROADMAP.md | 10 +++++----- UPSTREAM.md | 42 ++++++++++++++++++++++-------------------- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/NOTICE b/NOTICE index 3314cc0f0..f62270f42 100644 --- a/NOTICE +++ b/NOTICE @@ -1,9 +1,8 @@ -The 3SUM/Exact Triangle reductions, finite counting/tensor lemmas and native programs in +The 3SUM/Exact Triangle reductions, finite counting/tensor lemmas and native matrix builder in Complexity/Algorithms/ExactTriangle/{Defs,Weights,Lopsided,Hashing,Splitting,Witnesses}.lean, Complexity/Algorithms/MatrixMultiplication/Schonhage/, Complexity/Algorithms/ThreeSum/, Complexity/Algorithms/WordRAM/ThreeSum/, -Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/, Complexity/Foundations/Algebra/{FiniteMagnitude,GeometricTail}.lean, Complexity/Foundations/Combinatorics/FunctionPlaces.lean, Complexity/Foundations/Data/Finset/{Average,OrderGlue,Quotient}.lean, @@ -28,3 +27,7 @@ This product is licensed under the Apache License, Version 2.0 (see LICENSE). The prime-window proof also adapts Mathlib/NumberTheory/Bertrand.lean, Copyright (c) 2020 Patrick Stevens; authors Patrick Stevens, Bolton Bailey, under the Apache License, Version 2.0. + +The shared tensor encoder in +Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/ +is also part of the Anthropic adaptation described above. diff --git a/ROADMAP.md b/ROADMAP.md index db0d5f2ed..64f55a89b 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -425,6 +425,9 @@ each statement against the numbered result. Organize modules by subject, not cha theorems; simulating operations by programs over smaller bases; structured programs and time classes over arbitrary value domains with weighted or bit costs; simulations between instances; the typed core proposed in [CSLib #1158](https://github.com/leanprover/cslib/pull/1158). +- **Shared native matrix encoders** ([#1493]): compute all `10^L` leaf values from `7^L` + signed inputs within `240*(L+1)*10^L` standard WordRAM instructions, plus the final halt, + with fixed code/registers and concrete initialized inputs, frames and explicit width bounds. - **Word RAM:** an AC⁰ basis; wider-fragment and Turing-machine simulations; transfer of Anthropic's modular machine and 3SUM/APSP algorithms; direct 3SUM adaptation ([#1394]) has the Convolution-3SUM/Exact Triangle correctness reduction, a charged native single-instance @@ -436,11 +439,8 @@ each statement against the numbered result. Organize modules by subject, not cha shared encodings and pruned-evaluation core ([#1459]) include exact visited-leaf and passed-set bounds, with the empty zero-depth root handled explicitly. The finite thin-product component ([#1463]) proves valid layouts, arbitrary-size padding/tiling, exact queried signed products and - sparse encoding/leaf/passed-set totals. Those counts are not native instruction costs. - The shared native left/right encoders ([#1493]) compute all `10^L` leaf values from `7^L` - signed inputs within `240*(L+1)*10^L` standard WordRAM instructions, plus the final halt, - with fixed code/registers and concrete initialized inputs, frames and explicit width bounds. - Band/tile preparation, native pruned/sparse-query programs, native Chan–He preprocessing, the reduction + sparse encoding/leaf/passed-set totals. These counts are not native instruction costs. It still + needs native tensor/sparse-query programs, native Chan–He preprocessing, the reduction host, the fast Exact Triangle solver and full input/time/width/asymptotic composition, then the improved Section 4 route to exponent 1.9992; size-based width diff --git a/UPSTREAM.md b/UPSTREAM.md index 09304c8ec..59c18cc94 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -147,6 +147,22 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he theorems preserve the complete source memory and register bank. This is forward correctness, not reflection of source faults; transfer from Anthropic's modular signed machine and its 3SUM/APSP algorithms remains separate. +* **Native shared tensor encoder.** `Algorithms/WordRAM/MatrixMultiplication/Schonhage/` implements the complete shared left/right + encoding milestone ([#1493](https://github.com/cslib-community/complexitylib/issues/1493)). + Fixed standard-WordRAM code with 32 registers transforms one coordinate at a time between + two disjoint `10^L` buffers. It starts from `State.init` with a depth header and `7^L` + sign-and-magnitude words, computes all public `encodingL`/`encodingR` values, and preserves + the input and every cell outside the two buffers. Mathlib's `finFunctionFinEquiv` supplies + least-significant-first string numbering. All stages, powers, indices, address arithmetic, + signed add/subtract blocks and the final output copy cost at most `240*(L+1)*10^L` + instructions; halt adds one. Signed operands have magnitude bounded from the original + input by `7^L*A < 2^(w-1)`; the explicit natural bound `L+64+64*10^L` must fit target width + `w+2`. The two extra bits and actual arithmetic-block costs come from the existing native + signed kernels, without a copied source machine or a whole-program simulation hypothesis. + The existing array-copy interface is retained, with a narrow generalization for disjoint + blocks in either address order or identical blocks. + This is the encoder component only; band/tile preparation and full native composition remain + in [#1394](https://github.com/cslib-community/complexitylib/issues/1394). * **Direct 3SUM adaptation.** `Algorithms/ThreeSum/Convolution.lean` adapts the explicit Convolution-3SUM to Exact Triangle correctness reduction from Anthropic's [`formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp), @@ -207,26 +223,12 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he and finite-sum bounds live in small Foundations modules. Mathlib `Finset.orderEmbOfFin` indexes both a level set and its complement, and `Finset.piecewise` restricts answers. No source machine, problem-statement universe or solver/time hypothesis is imported. - `Algorithms/WordRAM/MatrixMultiplication/Schonhage/` implements the complete shared left/right - encoding milestone ([#1493](https://github.com/cslib-community/complexitylib/issues/1493)). - Fixed standard-WordRAM code with 32 registers transforms one coordinate at a time between - two disjoint `10^L` buffers. It starts from `State.init` with a depth header and `7^L` - sign-and-magnitude words, computes all public `encodingL`/`encodingR` values, and preserves - the input and every cell outside the two buffers. Mathlib's `finFunctionFinEquiv` supplies - least-significant-first string numbering. All stages, powers, indices, address arithmetic, - signed add/subtract blocks and the final output copy cost at most `240*(L+1)*10^L` - instructions; halt adds one. Signed operands have magnitude bounded from the original - input by `7^L*A < 2^(w-1)`; the explicit natural bound `L+64+64*10^L` must fit target width - `w+2`. The two extra bits and actual arithmetic-block costs come from the existing native - signed kernels, without a copied source machine or a whole-program simulation hypothesis. - The existing array-copy interface is retained, with a narrow generalization for disjoint - blocks in either address order or identical blocks. - The existing signed and unsigned APIs were reused; no source machine or separate run semantics - were copied. Band/tile preparation, native pruned and sparse-query programs, the full reduction - host, native Chan–He preprocessing, fast Exact Triangle and full input/time/width/asymptotic - composition remain in [#1394](https://github.com/cslib-community/complexitylib/issues/1394), - including the improved Section 4 route needed for the published exponent 1.9992. Anthropic's - copyright and attribution are retained; the native proofs and representation choices are local. + The existing signed and unsigned APIs were reused; no source machine or separate run semantics were copied. + Tensor/sparse query programs, the full reduction host, native Chan–He preprocessing, fast Exact + Triangle and full input/time/width/asymptotic composition remain in + [#1394](https://github.com/cslib-community/complexitylib/issues/1394), including the improved + Section 4 route needed for the published exponent 1.9992. Anthropic's copyright and attribution + are retained; the native proofs and representation choices are local. * **Typed RAM upstream core.** [CSLib #1158](https://github.com/leanprover/cslib/pull/1158) at `beea60a15eddc91d93e92fa5af9fb965c351d0d5` proposes `Cslib/Computability/Machines/RAM/Basic.lean`, in namespace `Cslib.RAM.Typed`. From debd649ef7b6ac793e29ebe36f635a778d2b91d9 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Thu, 29 Oct 2026 02:38:45 -0400 Subject: [PATCH 51/73] doc(matrix): keep progress links out of shared reference append block --- ROADMAP.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 2496e4fc2..d7087577a 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -429,10 +429,10 @@ each statement against the numbered result. Organize modules by subject, not cha theorems; simulating operations by programs over smaller bases; structured programs and time classes over arbitrary value domains with weighted or bit costs; simulations between instances; the typed core proposed in [CSLib #1158](https://github.com/leanprover/cslib/pull/1158). -- **Shared native matrix encoders** ([#1493]): compute all `10^L` leaf values from `7^L` +- **Shared native matrix encoders** ([#1493](https://github.com/cslib-community/complexitylib/issues/1493)): compute all `10^L` leaf values from `7^L` signed inputs within `240*(L+1)*10^L` standard WordRAM instructions, plus the final halt, with fixed code/registers and concrete initialized inputs, frames and explicit width bounds. -- **Native shared-band preparation** ([#1523]): computes deterministic subset tables and a valid +- **Native shared-band preparation** ([#1523](https://github.com/cslib-community/complexitylib/issues/1523)): computes deterministic subset tables and a valid layout, prepares padded signed matrix bands, invokes both shared encoders for every band, and restores the original initialized input. Fixed 96-register code has a charged bound, including halt, of `4096*(L+1)*(1+N*4^m+2^L+2*numBands*10^L)+1`; at `L=19m`, `N≥(4^m)^18`, it is at most @@ -445,9 +445,9 @@ each statement against the numbered result. Organize modules by subject, not cha interface ([#1438]) adds padding, detection and signed digit reconstruction. Modular counting and the selected-prime false-positive bound ([#1442]), finite residue/chunk/piece splitting and witness recovery ([#1447]) are proved mathematically. The tensor identity, full recursion, - shared encodings and pruned-evaluation core ([#1459]) include exact visited-leaf and passed-set + shared encodings and pruned-evaluation core ([#1459](https://github.com/cslib-community/complexitylib/issues/1459)) include exact visited-leaf and passed-set bounds, with the empty zero-depth root handled explicitly. The finite thin-product component - ([#1463]) proves valid layouts, arbitrary-size padding/tiling, exact queried signed products and + ([#1463](https://github.com/cslib-community/complexitylib/issues/1463)) proves valid layouts, arbitrary-size padding/tiling, exact queried signed products and sparse encoding/leaf/passed-set totals. These counts are not native instruction costs. It still needs native tensor/sparse-query programs, native Chan–He preprocessing, the reduction host, the fast Exact Triangle solver and full @@ -598,8 +598,4 @@ not planned, not as proved. [#1442]: https://github.com/cslib-community/complexitylib/issues/1442 [#1447]: https://github.com/cslib-community/complexitylib/issues/1447 [#1449]: https://github.com/cslib-community/complexitylib/issues/1449 -[#1459]: https://github.com/cslib-community/complexitylib/issues/1459 -[#1463]: https://github.com/cslib-community/complexitylib/issues/1463 -[#1493]: https://github.com/cslib-community/complexitylib/issues/1493 -[#1523]: https://github.com/cslib-community/complexitylib/issues/1523 [#1572]: https://github.com/cslib-community/complexitylib/issues/1572 From af208636bce9da5bb6177761d5f7117c9c0bea90 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 29 Oct 2026 02:39:32 -0400 Subject: [PATCH 52/73] feat(wordram): checkpoint complete queried-product source before supplier merge (#1581) Local recovery checkpoint only. Complete native queried-product execution, initialized clients, quantitative thin saving and logarithmic width have clean targeted Lean MCP diagnostics. Import generation adds the 29 new modules. No all-six/static validation or independent candidate review is claimed by this checkpoint; it is not a PR candidate. The actual #1576 supplier advanced from a6e4005b to integration91f485ec. Preserve this source before a normal child merge, reconcile only shared docs/imports, regenerate the aggregate, then validate the final source together against the actual supplier. Original native implementation by Erik Demaine with Codex assistance, reusing credited finite Anthropic Section 2 mathematics and actual native producer/router/pruned kernels. Ordinary-3SUM/Exact Triangle and the full #1394 1.9992 endpoint remain open. --- Complexity.lean | 29 +++ .../Schonhage/Product.lean | 50 ++++ .../Schonhage/Product/Catalogue.lean | 61 +++++ .../Schonhage/Product/Cost.lean | 126 +++++++++ .../Schonhage/Product/Exec.lean | 153 +++++++++++ .../Schonhage/Product/Flat.lean | 83 ++++++ .../Schonhage/Product/Input.lean | 28 ++ .../Schonhage/Product/MatrixInput.lean | 83 ++++++ .../Schonhage/Product/Program.lean | 16 +- .../Schonhage/Product/Reconstruct.lean | 134 ++++++++++ .../Schonhage/Product/Routing.lean | 129 ++++++++++ .../Schonhage/Product/Thin.lean | 111 ++++++++ .../Schonhage/Product/ThinWidth.lean | 216 ++++++++++++++++ .../Schonhage/Product/Tile.lean | 243 ++++++++++++++++++ .../Schonhage/Product/Tiles.lean | 165 ++++++++++++ .../Schonhage/Product/Width.lean | 80 ++++++ .../Schonhage/Wanted/Groups.lean | 3 +- ComplexityTests/SchonhageProduct.lean | 115 ++++++++- ROADMAP.md | 11 +- UPSTREAM.md | 25 +- 20 files changed, 1846 insertions(+), 15 deletions(-) create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Catalogue.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Cost.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Exec.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Flat.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/MatrixInput.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Reconstruct.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Routing.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Thin.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/ThinWidth.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Tile.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Tiles.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Width.lean diff --git a/Complexity.lean b/Complexity.lean index 709d85185..799f0d01f 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -142,6 +142,35 @@ public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Forms public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Header public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Input public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Meaning +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Admission +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Arguments +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Bands +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Catalogue +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Copy +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Cost +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Directory +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Exec +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Flat +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Groups +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Header +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Input +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Layout +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.MatrixInput +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.MatrixTransfer +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Memory +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Parameters +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Program +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Reconstruct +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Route +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Routing +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.SavedGroups +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Thin +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.ThinWidth +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Tile +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Tiles +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Transfer +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Width public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Accumulate public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.AccumulateStep public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Add diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product.lean new file mode 100755 index 000000000..e2531eb56 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product.lean @@ -0,0 +1,50 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.ThinWidth + +/-! +# Native queried thin-matrix products + +## Main definitions + +* `QueriedProduct.program`: fixed standard unsigned WordRAM code with 160 registers. +* `QueriedProduct.input`: original scalar headers, both row-major matrices and raw query pairs. + +## Main results + +* `QueriedProduct.exec`: complete execution from original words, with arbitrary header zero + and outside memory, exact `Matrix.mul` answers and input restoration. +* `QueriedProduct.runs_wide`: initialized flat runs, guaranteed input fit and a charged halt. +* `QueriedProduct.runs_thin`: the same native execution at `L=19m`, `D=4^m`, `D^18≤N`, + with instruction bound `4325376*(19m+1)*N²/D^(1/18)` when `Q*2^m≤N²`, and sufficient + physical width `128*clog₂(N+Q+A+2)` for entry bound `A`. +* `QueriedProduct.thin_timeBound`: distinct-set sparsity gives a saving with an additive + charge for every query occurrence, without imposing the stronger occurrence-count premise. + +The program computes its own valid-subset tables, shared band encodings, tile keys, query +codes, counts, prefix offsets, stable scatter, tile pointers, pruned traversals, epochs and +answer reconstruction. Original query order and duplicates are retained. Signed mode uses +canonical `Signed.encode` input words. Raw mode requires nonnegative entries and actually +doubles each word before calling the signed producer; this includes raw unsigned 0/1 inputs. +One timestamp directory is initialized and reused across all tiles. + +The component reuses the finite Section 2 tensor/pruning and thin-product mathematics from +[AlmanWilliams2026], with original programs and execution proofs against native WordRAM APIs. +It does not import the source Light machine or assume a solver, execution or cost contract. +Stage `exec_*` lemmas are internal implementation APIs shared between proof modules. Callers +use the results above; the initialized input contains no computed layout or query certificate. +This is a queried-product component, not a complete Exact Triangle or ordinary-3SUM algorithm. +The full endpoint and improved Section 4 route to exponent 1.9992 remain under #1394. + +## References + +* [AlmanWilliams2026], Section 2: finite tensor encodings and sparse queried products. +-/ diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Catalogue.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Catalogue.lean new file mode 100755 index 000000000..815906202 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Catalogue.lean @@ -0,0 +1,61 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Bands + +/-! +# Preserving the actual shared catalogue between tile calls + +## Main definitions + +* `QueriedProduct.EncodedBands`: the unchanged producer's mathematical encodings in real memory. + +## Main results + +* `QueriedProduct.EncodedBands.reframe`: retain both catalogues through a readonly low-memory frame. + +This predicate contains only ordinary word equalities. `exec_bands` establishes them by +running the native producer; no execution, fit or cost assertion is bundled in the predicate. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Actual shared arrays, using the original band arrays and authoritative leaf numbering. -/ +public structure EncodedBands {L m N : ℕ} (hm : m ≤ L) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (μ : ℕ → ℕ) : Prop where + /-- The left encoding is computed once for each row band. -/ + left : ∀ β < numBands L m N, ∀ τ, + μ (BandPreparation.catalogueAddress L m N false β (Encoding.leafIndex τ).val) = + Signed.encode (encodingL (bandArrayL (MaskTable.layout hm) X β) τ) + /-- The right encoding is computed once for each column band. -/ + right : ∀ β < numBands L m N, ∀ τ, + μ (BandPreparation.catalogueAddress L m N true β (Encoding.leafIndex τ).val) = + Signed.encode (encodingR (bandArrayR (MaskTable.layout hm) Y β) τ) + +/-- The native tile kernel's low readonly frame preserves all shared encodings. -/ +public theorem EncodedBands.reframe {L m N : ℕ} {hm : m ≤ L} + {X : Matrix (Fin N) (Fin (D m)) ℤ} {Y : Matrix (Fin (D m)) (Fin N) ℤ} + {μ ν : ℕ → ℕ} (h : EncodedBands hm X Y μ) + (hf : ∀ a < BandPreparation.memoryEnd L m N, ν a = μ a) : EncodedBands hm X Y ν := by + have hread (side : Bool) (β : ℕ) (hβ : β < numBands L m N) (τ : Leaf L) : + ν (BandPreparation.catalogueAddress L m N side β (Encoding.leafIndex τ).val) = + μ (BandPreparation.catalogueAddress L m N side β (Encoding.leafIndex τ).val) := by + have hb := BandPreparation.catalogue_block_le side hβ + have ht := (Encoding.leafIndex τ).isLt + apply hf + unfold BandPreparation.catalogueAddress at hb ⊢ + omega + exact ⟨fun β hβ τ => (hread false β hβ τ).trans (h.left β hβ τ), + fun β hβ τ => (hread true β hβ τ).trans (h.right β hβ τ)⟩ + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Cost.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Cost.lean new file mode 100755 index 000000000..ae8168538 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Cost.lean @@ -0,0 +1,126 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Routing +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Tiles + +/-! +# Complete native queried-product instruction ledger + +## Main definitions + +* `QueriedProduct.timeBound`: actual routing, copies, conversion, shared encoding, one directory + initialization, all tile calls, reconstruction and original-input restoration; halt is separate. + +Every term is derived from ordinary original inputs and the finite pruned trace definition. +No solver or running-time assertion is supplied as an input field. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open scoped BigOperators + +/-- A uniform instruction bound for either original signed inputs or raw nonnegative inputs. +The raw-conversion envelope charges a multiplication for every original matrix entry. -/ +@[expose] public def timeBound {L m N : ℕ} (hm : m ≤ L) (qs : List (Fin N × Fin N)) : ℕ := + routingTimeBound L m N qs.length + (8 * (2 * (N * D m)) + 10) + + BandPreparation.timeBound L m N + (5 * 10 ^ L + 5) + + ((∑ t ∈ Finset.range (numBands L m N ^ 2), tileCharge hm qs t) + 2) + + (9 * qs.length + 3) + (7 * inputEnd m N qs.length + 10) + +private theorem sum_bucket_counts (key : ℕ → ℕ) (Q T : ℕ) : + (∑ t ∈ Finset.range T, Nat.count (fun i => key i = t) Q) = + Nat.count (fun i => key i < T) Q := by + induction T with + | zero => simp + | succ T ih => rw [Finset.sum_range_succ, ih, CountingSort.count_lt_succ] + +private theorem sum_tile_indices (B : ℕ) (f : ℕ × ℕ → ℕ) : + (∑ t ∈ Finset.range (B ^ 2), f (t / B, t % B)) = + ∑ p ∈ Finset.range B ×ˢ Finset.range B, f p := by + have he := (finProdFinEquiv : Fin B × Fin B ≃ Fin (B * B)).symm.sum_comp + (fun p => f (p.1.val, p.2.val)) + change (∑ t : Fin (B * B), f (t.val / B, t.val % B)) = + ∑ p : Fin B × Fin B, f (p.1.val, p.2.val) at he + rw [pow_two, Finset.sum_product, + ← Fin.sum_univ_eq_sum_range (fun t => f (t / B, t % B)) (B * B), he, + Fintype.sum_prod_type, + Fin.sum_univ_eq_sum_range (fun i => ∑ j : Fin B, f (i, j.val)) B] + exact Finset.sum_congr rfl (fun i _ => Fin.sum_univ_eq_sum_range (fun j => f (i, j)) B) + +/-- The exact sum of occurrence-dependent call charges uses every original request once. +It uses no distinct-set bound on list multiplicity. -/ +public theorem sum_tileCharge {L m N : ℕ} (hm : m ≤ L) (qs : List (Fin N × Fin N)) : + (∑ t ∈ Finset.range (numBands L m N ^ 2), tileCharge hm qs t) = + (4 * L + 66) * numBands L m N ^ 2 + 29 * qs.length + + ∑ T ∈ Cslib.Algorithms.MatrixMultiplication.Schonhage.tiles L m N, + PrunedQuery.traceWeight (wantedStrings (MaskTable.layout hm) qs.toFinset T.1 T.2) := by + have hkey (i : ℕ) (hi : i < qs.length) : + (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) i < numBands L m N ^ 2 := + Wanted.tileKey_lt hm _ (Wanted.inputPairs_lt qs i hi).1 (Wanted.inputPairs_lt qs i hi).2 + have hcount := sum_bucket_counts (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) qs.length + (numBands L m N ^ 2) + rw [Nat.count_of_forall hkey] at hcount + simp only [tileCharge, Finset.sum_add_distrib, Finset.sum_const, + Finset.card_range, smul_eq_mul, ← Finset.mul_sum] + have htiles := sum_tile_indices (numBands L m N) (fun T => + PrunedQuery.traceWeight (wantedStrings (MaskTable.layout hm) qs.toFinset T.1 T.2)) + rw [hcount, htiles] + unfold Cslib.Algorithms.MatrixMultiplication.Schonhage.tiles + ring + +/-- Sum the actual native kernel trace charges using the existing contributing-leaf bound. -/ +public theorem sum_traceWeight_le {L m N : ℕ} (hm : m ≤ L) (qs : List (Fin N × Fin N)) : + (∑ T ∈ Cslib.Algorithms.MatrixMultiplication.Schonhage.tiles L m N, + PrunedQuery.traceWeight (wantedStrings (MaskTable.layout hm) qs.toFinset T.1 T.2)) ≤ + 8192 * (L + 1) * + (∑ T ∈ Cslib.Algorithms.MatrixMultiplication.Schonhage.tiles L m N, + (Cslib.Algorithms.MatrixMultiplication.Schonhage.leaves + (wantedStrings (MaskTable.layout hm) qs.toFinset T.1 T.2)).card) + + 4096 * numBands L m N ^ 2 := by + have h := Finset.sum_le_sum (s := Cslib.Algorithms.MatrixMultiplication.Schonhage.tiles L m N) + (fun T _ => PrunedQuery.traceWeight_le + (wantedStrings (MaskTable.layout hm) qs.toFinset T.1 T.2)) + have hc : (Cslib.Algorithms.MatrixMultiplication.Schonhage.tiles L m N).card = + numBands L m N ^ 2 := by + simp only [Cslib.Algorithms.MatrixMultiplication.Schonhage.tiles, + Finset.card_product, Finset.card_range, pow_two] + have heq (v : ℕ) : 4096 * (2 * (L + 1) * v + 1) = 8192 * (L + 1) * v + 4096 := by ring + simp_rw [heq] at h + simpa only [Finset.sum_add_distrib, ← Finset.mul_sum, Finset.sum_const, smul_eq_mul, + hc, Nat.mul_comm (numBands L m N ^ 2) 4096] using h + +/-- Every actual phase, including halt, is bounded by original input size, shared encodings, +tile count and contributing leaves. In particular, repeated requests retain a charged term. -/ +public theorem timeBound_le {L m N : ℕ} (hm : m ≤ L) (qs : List (Fin N × Fin N)) : + timeBound hm qs + 1 ≤ 65536 * (L + 1) * + (1 + N * D m + qs.length + 2 ^ L + (numBands L m N + 1) * 10 ^ L + + numBands L m N ^ 2 + + ∑ T ∈ Cslib.Algorithms.MatrixMultiplication.Schonhage.tiles L m N, + (Cslib.Algorithms.MatrixMultiplication.Schonhage.leaves + (wantedStrings (MaskTable.layout hm) qs.toFinset T.1 T.2)).card) := by + have htrace := sum_traceWeight_le hm qs + have hleaves := Nat.zero_le (∑ T ∈ Cslib.Algorithms.MatrixMultiplication.Schonhage.tiles L m N, + (Cslib.Algorithms.MatrixMultiplication.Schonhage.leaves + (wantedStrings (MaskTable.layout hm) qs.toFinset T.1 T.2)).card) + have hLleaves := Nat.zero_le (L * + (∑ T ∈ Cslib.Algorithms.MatrixMultiplication.Schonhage.tiles L m N, + (Cslib.Algorithms.MatrixMultiplication.Schonhage.leaves + (wantedStrings (MaskTable.layout hm) qs.toFinset T.1 T.2)).card)) + rw [timeBound, sum_tileCharge] + unfold routingTimeBound Wanted.timeBound BandPreparation.timeBound inputEnd + nlinarith only [htrace, hleaves, hLleaves, Nat.zero_le L, Nat.zero_le (2 ^ L), + Nat.zero_le (10 ^ L), Nat.zero_le (L * 10 ^ L), + Nat.zero_le (N * D m), Nat.zero_le (L * (N * D m)), Nat.zero_le qs.length, + Nat.zero_le (L * qs.length), Nat.zero_le (L * 2 ^ L), + Nat.zero_le (numBands L m N * 10 ^ L), Nat.zero_le (L * (numBands L m N * 10 ^ L)), + Nat.zero_le (numBands L m N ^ 2), Nat.zero_le (L * numBands L m N ^ 2)] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Exec.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Exec.lean new file mode 100755 index 000000000..c746fc788 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Exec.lean @@ -0,0 +1,153 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Cost +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.MatrixInput +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Directory +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Reconstruct + +/-! +# Complete actual native queried matrix products + +## Main results + +* `QueriedProduct.exec`: original matrix/coordinate words suffice for actual finite native + execution, exact original-order product values, original-input restoration and outside frame. + +The program computes all tables, groups, catalogues, tile arguments, epochs and reporting +decisions. The original-word predicate permits arbitrary header zero and arbitrary outside +memory; initialized clients discharge it directly from the literal `input` list. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Actual composed execution from only ordinary original words, with every phase charged. +No table/layout/query/producer/run/time certificate or fast-solver assumption is an input. -/ +public theorem exec {w L m N A : ℕ} (hw : 0 < w) (hm : m ≤ L) (bits : Bool) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (x : Data 160) + (hinput : ∀ a, 0 < a → a < inputEnd m N qs.length → x.mem a = + (State.init (input bits L X Y qs) : State 160).data.mem a) + (hX : ∀ i k, |X i k| ≤ (A : ℤ)) (hY : ∀ k j, |Y k j| ≤ (A : ℤ)) + (hbits : bits = true → (∀ i k, 0 ≤ X i k) ∧ (∀ k j, 0 ≤ Y k j)) + (hmag : magnitudeBound L A < 2 ^ (w - 1)) + (hfit : Fits (w + 2) (wordBudget L m N qs.length)) : + ∃ y, ExecWithin (w + 2) (program bits) x y (timeBound hm qs) ∧ + Parameters L m N qs.length y ∧ + (∀ i : Fin qs.length, y.mem (outputBase L m N qs.length + i.val) = + Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2)) ∧ + (∀ a, ¬(inputEnd m N qs.length ≤ a ∧ a < memoryEnd L m N qs.length) → + y.mem a = x.mem a) := by + have h10 := Nat.zero_le (10 ^ L) + have hI : 5 + 2 * (N * D m) + 2 * qs.length = inputEnd m N qs.length := rfl + have hS := producers_le_snapshot L m N qs.length + have hOff : snapshotBase L m N qs.length + inputEnd m N qs.length = + offsetBase L m N qs.length := rfl + have hCode : offsetBase L m N qs.length + numBands L m N ^ 2 + 1 = + codeBase L m N qs.length := rfl + have hOrig : codeBase L m N qs.length + qs.length = originalBase L m N qs.length := rfl + have hGroup : originalBase L m N qs.length + qs.length = groupedBase L m N qs.length := rfl + have hOut : groupedBase L m N qs.length + qs.length = outputBase L m N qs.length := rfl + have hFree : outputBase L m N qs.length + qs.length = scratchBase L m N qs.length := rfl + have hStamp : scratchBase L m N qs.length + 4 * (L + 1) + 2 * (L + 1) * 10 ^ L = + stampBase L m N qs.length := rfl + have hEnd : stampBase L m N qs.length + 10 ^ L = memoryEnd L m N qs.length := rfl + have hL : x.mem 1 = L := by + rw [hinput 1 (by decide) (by omega)] + simp [State.init, Data.init, input] + have hm' : x.mem 2 = m := by + rw [hinput 2 (by decide) (by omega)] + simp [State.init, Data.init, input] + have hN : x.mem 3 = N := by + rw [hinput 3 (by decide) (by omega)] + simp [State.init, Data.init, input] + have hQ : x.mem 4 = qs.length := by + rw [hinput 4 (by decide) (by omega)] + simp [State.init, Data.init, input] + have hqueries (i : ℕ) (hi : i < qs.length) : + x.mem (queryInputBase m N + 2 * i) = (Wanted.inputPairs qs i).1 ∧ + x.mem (queryInputBase m N + 2 * i + 1) = (Wanted.inputPairs qs i).2 := by + have hr := input_query_word bits L X Y qs ⟨i, hi⟩ 0 + have hc := input_query_word bits L X Y qs ⟨i, hi⟩ 1 + rw [hinput _ (by unfold queryInputBase; omega) (by + simpa only [Nat.add_zero] using query_address_lt hi (by decide : 0 < 2)), + hinput _ (by unfold queryInputBase; omega) (query_address_lt hi (by decide : 1 < 2))] + rw [Wanted.inputPairs_get qs ⟨i, hi⟩] + simpa only [Fin.val_zero, Fin.val_one, Nat.add_zero, ↓reduceIte, Fin.reduceEq] using + And.intro hr hc + obtain ⟨r, old, hr, rp, rg, rv, ro, rs, rf⟩ := + exec_prepareRouting hm qs x hL hm' hN hQ hqueries hfit + have rsource (j : ℕ) (hj : j < 2 * (N * D m)) : + r.mem (snapshotBase L m N qs.length + 5 + j) = + (State.init (input bits L X Y qs) : State 160).data.mem (5 + j) := by + rw [show snapshotBase L m N qs.length + 5 + j = + snapshotBase L m N qs.length + (5 + j) by omega, rs _ (by omega)] + exact hinput _ (by omega) (by omega) + obtain ⟨c, hc, cp, cm, cf⟩ := + exec_prepareInputMatrices hw bits X Y qs r rp rsource hX hY hbits hmag hfit + have cs (j : ℕ) (hj : j < inputEnd m N qs.length) : + c.mem (snapshotBase L m N qs.length + j) = x.mem j := by + rw [cf _ (Or.inr (by omega))] + exact rs j hj + have cg : SavedGroups L m N qs.length old c.mem := + rg.reframe (fun a ha _ => cf a (Or.inr (by omega))) + obtain ⟨b, hb, bp, bl, br, bf⟩ := exec_bands hw hm X Y c cp + (by rw [cf 1 (Or.inl (by decide)), rf 1 (Or.inl (by decide))]; exact hL) + (by rw [cf 2 (Or.inl (by decide)), rf 2 (Or.inl (by decide))]; exact hm') + (by rw [cf 3 (Or.inl (by decide)), rf 3 (Or.inl (by decide))]; exact hN) + cm hX hY hmag hfit + have bg : SavedGroups L m N qs.length old b.mem := + cg.reframe (fun a ha _ => bf a (Or.inr (by omega))) + have be : EncodedBands hm X Y b.mem := ⟨bl, br⟩ + have bs (j : ℕ) (hj : j < inputEnd m N qs.length) : + b.mem (snapshotBase L m N qs.length + j) = x.mem j := by + rw [bf _ (Or.inr (by omega))] + exact cs j hj + obtain ⟨d, hd, dp, de, di, dm, df⟩ := exec_initializeDirectory b bp hfit + have dg : SavedGroups L m N qs.length old d.mem := + bg.reframe (fun a _ ha => df a (Or.inl (by omega))) + have de' : EncodedBands hm X Y d.mem := be.reframe (fun a ha => df a (Or.inl (by omega))) + have ds (j : ℕ) (hj : j < inputEnd m N qs.length) : + d.mem (snapshotBase L m N qs.length + j) = x.mem j := by + rw [df _ (Or.inl (by omega))] + exact bs j hj + obtain ⟨t, ht, tp, tg, _, tv, tf⟩ := exec_evaluateTiles hw hm X Y qs old d dp di de dg de' + (fun i => (rv i).1) ro dm hX hY hmag hfit + have ts (j : ℕ) (hj : j < inputEnd m N qs.length) : + t.mem (snapshotBase L m N qs.length + j) = x.mem j := by + rw [tf _ (by omega) (by omega)] + exact ds j hj + obtain ⟨o, ho, op, ov, of⟩ := exec_reconstruct X Y qs old t tp tg (fun i => (rv i).2) tv hfit + have os (j : ℕ) (hj : j < inputEnd m N qs.length) : + o.mem (snapshotBase L m N qs.length + j) = x.mem j := by + rw [of _ (Or.inl (by omega))] + exact ts j hj + obtain ⟨y, hy, yp, ym⟩ := exec_restoreInput o op hfit + refine ⟨y, ?_, yp, ?_, ?_⟩ + · apply (hr.seq (hc.seq (hb.seq (hd.seq (ht.seq (ho.seq hy)))))).mono + unfold timeBound + omega + · intro i + rw [ym, ArrayCopy.copyPrefix_outside (by omega)] + exact ov i + · intro a ha + by_cases hi : a < inputEnd m N qs.length + · rw [ym] + have hread := ArrayCopy.copyPrefix_read (mem := o.mem) + (src := snapshotBase L m N qs.length) (dst := 0) hi + simpa only [Nat.zero_add] using hread.trans (os a hi) + · have habove : memoryEnd L m N qs.length ≤ a := by omega + rw [ym, ArrayCopy.copyPrefix_outside (by omega), of _ (Or.inr (by omega)), + tf _ (by omega) (by omega), df _ (Or.inr habove), bf _ (Or.inr (by omega)), + cf _ (Or.inr (by omega)), rf _ (Or.inr (by omega))] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Flat.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Flat.lean new file mode 100755 index 000000000..15c182148 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Flat.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Exec + +/-! +# Initialized flat native queried products + +## Main results + +* `QueriedProduct.runs`: actual `State.init` executions, exact original-order product values, + guaranteed input fit, complete frame and a charged halt tied to the same final memory. +* `QueriedProduct.bounded_reachable`: every initialized reachable prefix has fitting data words. +* `QueriedProduct.program_length_le`: one fixed finite 160-register program for each input mode. + +The code is independent of all dimensions, entry magnitudes, query counts and word widths. +These results are component results; they do not instantiate a complete Exact Triangle solver +or the ordinary-3SUM endpoint under #1394. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +set_option maxRecDepth 8192 in +-- This closed structural check traverses the two fixed native program trees and signed kernels. +/-- The full flattened instruction list, including halt, is fixed independently of the input. -/ +public theorem program_length_le (bits : Bool) : + (program bits |>.program output).length ≤ 32768 := by + cases bits <;> decide +kernel + +set_option maxRecDepth 8192 in +-- This is a finite syntactic check of the actual code, including each renamed producer/kernel. +/-- Neither mode reads the physical width; the same fixed code runs at every admitted width. -/ +public theorem not_readsWidth (bits : Bool) : ¬(program bits).ReadsWidth := by + cases bits <;> decide +kernel + +/-- Original matrices and coordinates initialize the real native run. The charged prefix +reaches the same memory that contains all product values, and its next instruction halts. -/ +public theorem runs {w L m N A : ℕ} (hw : 0 < w) (hm : m ≤ L) (bits : Bool) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (hX : ∀ i k, |X i k| ≤ (A : ℤ)) + (hY : ∀ k j, |Y k j| ≤ (A : ℤ)) + (hbits : bits = true → (∀ i k, 0 ≤ X i k) ∧ (∀ k j, 0 ≤ Y k j)) + (hmag : magnitudeBound L A < 2 ^ (w - 1)) + (hfit : Fits (w + 2) (wordBudget L m N qs.length)) : + InputFits (w + 2) (input bits L X Y qs) ∧ + ∃ y n, n ≤ timeBound hm qs ∧ + Runs (w + 2) ((program bits).program output) (State.init (input bits L X Y qs)) + ⟨(program bits).size, y⟩ n ∧ + Executes (w + 2) ((program bits).program output) (State.init (input bits L X Y qs)) + (outputBase L m N qs.length) (n + 1) ∧ y.Bounded (w + 2) ∧ + (∀ i : Fin qs.length, y.mem (outputBase L m N qs.length + i.val) = + Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2)) ∧ + (∀ a, ¬(inputEnd m N qs.length ≤ a ∧ a < memoryEnd L m N qs.length) → + y.mem a = (State.init (input bits L X Y qs) : State 160).data.mem a) := by + have hi := input_fits hw hm bits X Y qs hX hY hmag hfit + obtain ⟨y, he, hp, hv, hf⟩ := exec hw hm bits X Y qs + (State.init (input bits L X Y qs)).data (by intro _ _ _; rfl) hX hY hbits hmag hfit + have hy := he.bounded (Data.Bounded.init hi) + obtain ⟨n, hn, he⟩ := he + refine ⟨hi, y, n, hn, ?_, ?_, hy, hv, hf⟩ + · simpa only [Nat.zero_add, State.init] using he.runs (pc := 0) + (p := (program bits).program output) (Program.Contains.self _).append_left + · simpa only [hp.output, State.init] using he.executes output + +/-- Every reachable initialized prefix is bounded at the same physical width. -/ +public theorem bounded_reachable {w L m N A n : ℕ} (hw : 0 < w) (hm : m ≤ L) (bits : Bool) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (hX : ∀ i k, |X i k| ≤ (A : ℤ)) + (hY : ∀ k j, |Y k j| ≤ (A : ℤ)) (hmag : magnitudeBound L A < 2 ^ (w - 1)) + (hfit : Fits (w + 2) (wordBudget L m N qs.length)) {y : State 160} + (hy : Runs (w + 2) ((program bits).program output) (State.init (input bits L X Y qs)) y n) : + y.data.Bounded (w + 2) := hy.bounded_init (input_fits hw hm bits X Y qs hX hY hmag hfit) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Input.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Input.lean index 65c80cdb3..cb840da6e 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Input.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Input.lean @@ -202,4 +202,32 @@ public theorem input_fits {w L m N A : ℕ} (hw : 0 < w) (hm : m ≤ L) (bits : exact entryWord_fits hw bits _ (hY k j) hmag · exact hq.2 v (List.mem_of_mem_drop hv) +/-- Original raw matrix words are bounded by the original absolute-entry bound, at every +flat position. This supplies the actual conversion loop without an intermediate fit premise. -/ +public theorem input_raw_matrix_le {L m N A : ℕ} + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (hX : ∀ i k, |X i k| ≤ (A : ℤ)) + (hY : ∀ k j, |Y k j| ≤ (A : ℤ)) (j : ℕ) (hj : j < 2 * (N * D m)) : + (State.init (input true L X Y qs) : State 160).data.mem (5 + j) ≤ A := by + have hj' : j < (matrixInput true X Y).length := by rw [matrixInput_length]; exact hj + have hbound (v : ℕ) (hv : v ∈ matrixInput true X Y) : v ≤ A := by + simp only [matrixInput, List.mem_append] at hv + rcases hv with hv | hv + · obtain ⟨i, _, hv⟩ := List.mem_flatMap.mp hv + obtain ⟨k, rfl⟩ := List.mem_ofFn.mp hv + exact Int.toNat_le.mpr ((le_abs_self _).trans (hX i k)) + · obtain ⟨k, _, hv⟩ := List.mem_flatMap.mp hv + obtain ⟨i, rfl⟩ := List.mem_ofFn.mp hv + exact Int.toNat_le.mpr ((le_abs_self _).trans (hY k i)) + change (Data.init (input true L X Y qs) : Data 160).mem _ ≤ A + rw [show 5 + j = (4 + j) + 1 by omega, Data.init_mem_succ] + unfold input + rw [List.getD_append _ _ _ _ (by + simp only [List.length_append, List.length_cons, List.length_nil] + omega)] + rw [List.getD_append_right _ _ _ _ (by simp)] + simp only [List.length_cons, List.length_nil, Nat.reduceAdd, Nat.add_sub_cancel_left] + rw [List.getD_eq_get _ _ ⟨j, hj'⟩] + exact hbound _ (List.get_mem _ _) + end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/MatrixInput.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/MatrixInput.lean new file mode 100755 index 000000000..ea776dea0 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/MatrixInput.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.MatrixTransfer +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Admission + +/-! +# Original matrices through the actual native input adapter + +## Main results + +* `QueriedProduct.exec_prepareInputMatrices`: real signed copies or raw-entry doubling + establish the exact input words required by the existing band producer. + +The source-word equalities come from the preceding native original-input snapshot. The +raw path represents nonnegative entries, including the ET frontend's raw zero/one matrices. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Every original matrix entry reaches the producer in its canonical signed convention. +The instruction envelope includes the actual conversion at every raw input entry. -/ +public theorem exec_prepareInputMatrices {w L m N A : ℕ} (hw : 0 < w) (bits : Bool) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (x : Data 160) (hp : Parameters L m N qs.length x) + (hsource : ∀ j < 2 * (N * D m), x.mem (snapshotBase L m N qs.length + 5 + j) = + (State.init (input bits L X Y qs) : State 160).data.mem (5 + j)) + (hX : ∀ i k, |X i k| ≤ (A : ℤ)) (hY : ∀ k j, |Y k j| ≤ (A : ℤ)) + (hbits : bits = true → (∀ i k, 0 ≤ X i k) ∧ (∀ k j, 0 ≤ Y k j)) + (hmag : magnitudeBound L A < 2 ^ (w - 1)) + (hfit : Fits (w + 2) (wordBudget L m N qs.length)) : + ∃ y, ExecWithin (w + 2) (prepareMatrices bits) x y (8 * (2 * (N * D m)) + 10) ∧ + Parameters L m N qs.length y ∧ + (∀ side r, r < N → ∀ k, k < D m → + y.mem (BandPreparation.matrixOffset m N side r k + 4) = + Signed.encode (BandPreparation.matrixValues X Y side r k)) ∧ + (∀ a, a < 4 ∨ 4 + 2 * (N * D m) ≤ a → y.mem a = x.mem a) := by + obtain ⟨y, he, yp, hy, hf⟩ : + ∃ y, ExecWithin (w + 2) (prepareMatrices bits) x y (8 * (2 * (N * D m)) + 10) ∧ + Parameters L m N qs.length y ∧ + (∀ j < 2 * (N * D m), y.mem (4 + j) = + if bits then 2 * x.mem (snapshotBase L m N qs.length + 5 + j) + else x.mem (snapshotBase L m N qs.length + 5 + j)) ∧ + (∀ a, a < 4 ∨ 4 + 2 * (N * D m) ≤ a → y.mem a = x.mem a) := by + cases bits + · obtain ⟨y, he, yp, hy, hf⟩ := exec_prepareSigned x hp hfit + exact ⟨y, he.mono (by omega), yp, hy, hf⟩ + · obtain ⟨y, he, yp, hy, hf⟩ := exec_prepareBits x hp hfit + (magnitude_admissions hw hmag).2.2 (by + intro j hj + rw [hsource j hj] + exact input_raw_matrix_le X Y qs hX hY j hj) + exact ⟨y, he.mono (by omega), yp, hy, hf⟩ + refine ⟨y, he, yp, ?_, hf⟩ + intro side r hr k hk + have hindex := BandPreparation.matrixAddress_lt_inverse (L := L) side hr hk + unfold BandPreparation.matrixAddress BandPreparation.inverseBase + BandPreparation.inputEnd at hindex + have hi : BandPreparation.matrixOffset m N side r k < 2 * (N * D m) := by omega + have hv := input_matrix_word (L := L) bits X Y qs side r k hr hk + rw [show BandPreparation.matrixOffset m N side r k + 4 = + 4 + BandPreparation.matrixOffset m N side r k by omega, hy _ hi, hsource _ hi, + show 5 + BandPreparation.matrixOffset m N side r k = + BandPreparation.matrixOffset m N side r k + 5 by omega, hv] + exact convert_entryWord bits _ (by + intro hb + have hnonneg := hbits hb + cases side <;> + simp only [BandPreparation.matrixValues, dite_eq_left hr, dite_eq_left hk, + Bool.false_eq_true, ↓reduceIte] + · exact hnonneg.1 _ _ + · exact hnonneg.2 _ _) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Program.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Program.lean index d08acd588..ca66fd622 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Program.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Program.lean @@ -14,13 +14,13 @@ public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Produ ## Main definitions -* `QueriedProduct.program`: fixed native composition, with ordinary signed or raw-bit matrices. +* `QueriedProduct.program`: fixed native composition, with signed or raw nonnegative matrices. * `QueriedProduct.tileStep`: actual offset loads, catalogue pointers, epoch call and update. * `QueriedProduct.reconstruct`: restore the original request order from actual stored indices. All three existing 96-register callees use the low bank. The caller's 64 additional registers retain its scalar layout and counters. Copying, initial directory clearing, -header adaptation, bit doubling, dispatch, reporting and restoration are real instructions. +header adaptation, raw-entry doubling, dispatch, reporting and restoration are real instructions. Supporting code definitions are exposed for execution proofs; they are implementation APIs. -/ @@ -227,12 +227,16 @@ The bit-input variant executes the conversion instruction at every entry. -/ @[expose] public def restoreInput : Code .standard 160 := .seq (ram[ source ← snapshot; destination ← zero; length ← inputSize ]) copyCall +/-- Complete actual routing preparation and protected preservation of the original input. -/ +@[expose] public def prepareRouting : Code .standard 160 := + .seq parameters (.seq layout (.seq saveInput (.seq prepareQueries + (.seq (Wanted.program.rename bank) saveGroups)))) + /-- The complete fixed producer/consumer program. `bits=false` takes canonical signed matrix words; `bits=true` takes raw unsigned 0/1 words and performs actual per-entry doubling. -/ @[expose] public def program (bits : Bool) : Code .standard 160 := - .seq parameters (.seq layout (.seq saveInput (.seq prepareQueries - (.seq (Wanted.program.rename bank) (.seq saveGroups (.seq (prepareMatrices bits) - (.seq (BandPreparation.program.rename bank) (.seq initializeDirectory - (.seq evaluateTiles (.seq reconstruct restoreInput)))))))))) + .seq prepareRouting (.seq (prepareMatrices bits) + (.seq (BandPreparation.program.rename bank) (.seq initializeDirectory + (.seq evaluateTiles (.seq reconstruct restoreInput))))) end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Reconstruct.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Reconstruct.lean new file mode 100755 index 000000000..4c0b33e99 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Reconstruct.lean @@ -0,0 +1,134 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.SavedGroups + +/-! +# Actual reconstruction in original request order + +## Main results + +* `QueriedProduct.exec_reconstruct`: read the stored original indices and grouped answers, + then scatter every answer to its original request position, charging every instruction. + +The existing stable-position bijection proves that no earlier answer is overwritten. +Neither an inverse permutation nor a scatter execution is supplied to the program. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Actual reconstruction retains repeated input occurrences, input words and outside memory. +Only the designated original-order answer block is written. -/ +public theorem exec_reconstruct {w L m N : ℕ} + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (old : ℕ → ℕ) (x : Data 160) + (hp : Parameters L m N qs.length x) (hg : SavedGroups L m N qs.length old x.mem) + (horiginals : ∀ i : Fin qs.length, + old (Wanted.originalAddress L m N qs.length + + CountingSort.position (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) qs.length i) = i.val) + (hanswers : ∀ i : Fin qs.length, + x.mem (groupedBase L m N qs.length + + CountingSort.position (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) qs.length i) = + Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2)) + (hfit : Fits w (wordBudget L m N qs.length)) : + ∃ y, ExecWithin w reconstruct x y (9 * qs.length + 3) ∧ + Parameters L m N qs.length y ∧ + (∀ i : Fin qs.length, y.mem (outputBase L m N qs.length + i.val) = + Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2)) ∧ + (∀ a, a < outputBase L m N qs.length ∨ scratchBase L m N qs.length ≤ a → + y.mem a = x.mem a) := by + let key := Wanted.tileKey L m N ∘ Wanted.inputPairs qs + let pos := CountingSort.position key qs.length + let I (z : Data 160) : Prop := Parameters L m N qs.length z ∧ z.regs index ≤ qs.length ∧ + (∀ i : Fin qs.length, pos i.val < z.regs index → + z.mem (outputBase L m N qs.length + i.val) = + Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2)) ∧ + (∀ a, a < outputBase L m N qs.length ∨ scratchBase L m N qs.length ≤ a → z.mem a = x.mem a) + have hout : outputBase L m N qs.length + qs.length = scratchBase L m N qs.length := rfl + have hgroup : groupedBase L m N qs.length + qs.length = outputBase L m N qs.length := rfl + have horig : originalBase L m N qs.length + qs.length = groupedBase L m N qs.length := rfl + have hfree : scratchBase L m N qs.length ≤ memoryEnd L m N qs.length := by + unfold memoryEnd stampBase + have h10 := Nat.zero_le (10 ^ L) + omega + let start := x.set index 0 + have hs : ExecWithin (B := .standard) w (ram[index ← #0]) x start 1 := by ram_eval [start] + have sp : Parameters L m N qs.length start := hp.set_work index (by decide) _ + have hstart : I start := ⟨sp, by simp [start], by simp [start, pos], by simp [start]⟩ + have hbody (z : Data 160) (hz : I z) (hc : (Cond.lt index queryCount).Holds z) : + ∃ u, ExecWithin w reconstructStep z u 7 ∧ I u ∧ + qs.length - u.regs index < qs.length - z.regs index := by + have hi : z.regs index < qs.length := by simpa only [Cond.holds_lt, hz.1.queryCount] using hc + obtain ⟨i, hip⟩ := (CountingSort.position_bijective key qs.length).surjective ⟨z.regs index, hi⟩ + have hpos := congrArg (fun j : Fin qs.length => j.val) hip + change pos i.val = z.regs index at hpos + have hreadIndex : z.mem (originalBase L m N qs.length + z.regs index) = i.val := by + rw [hz.2.2.2 _ (Or.inl (by omega)), hg.originals _ hi, ← hpos] + exact horiginals i + have hreadValue : z.mem (groupedBase L m N qs.length + z.regs index) = + Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2) := by + rw [hz.2.2.2 _ (Or.inl (by omega)), ← hpos] + exact hanswers i + have fo : Fits w (originalBase L m N qs.length + z.regs index) := allocated_fits hfit (by omega) + have fg : Fits w (groupedBase L m N qs.length + z.regs index) := allocated_fits hfit (by omega) + have fy : Fits w (outputBase L m N qs.length + i.val) := allocated_fits hfit (by + have h := i.isLt + omega) + have fn : Fits w (z.regs index + 1) := allocated_fits hfit (by omega) + let answer := Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2) + let u := ((((((z.set address (originalBase L m N qs.length + z.regs index)).set + temporary i.val).set address (groupedBase L m N qs.length + z.regs index)).set value + answer).set address (outputBase L m N qs.length + i.val)).write + (outputBase L m N qs.length + i.val) answer).set index (z.regs index + 1) + have he : ExecWithin w reconstructStep z u 7 := by + ram_eval [reconstructStep, u, answer, hz.1.originals, hz.1.grouped, hz.1.output, + hz.1.one, hreadIndex, hreadValue, fo, fg, fy, fn, address, originals, index, + temporary, grouped, value, output, one] + have up : Parameters L m N qs.length u := + ((((((hz.1.set_work address (by decide) _).set_work temporary (by decide) _).set_work + address (by decide) _).set_work value (by decide) _).set_work address (by decide) _).write + _ _).set_work index (by decide) _ + have un : u.regs index = z.regs index + 1 := by simp [u] + refine ⟨u, he, ⟨up, by rw [un]; omega, ?_, ?_⟩, by rw [un]; omega⟩ + · intro j hj + rw [un] at hj + change Function.update z.mem (outputBase L m N qs.length + i.val) answer + (outputBase L m N qs.length + j.val) = _ + by_cases hij : j = i + · subst j + exact Function.update_self _ _ _ + · have hne : j.val ≠ i.val := fun heq => hij (Fin.ext heq) + rw [Function.update_of_ne (by omega)] + have hpne : pos j.val ≠ pos i.val := fun heq => hij + (CountingSort.position_injective key qs.length heq) + exact hz.2.2.1 j (by omega) + · intro a ha + change Function.update z.mem (outputBase L m N qs.length + i.val) answer a = _ + rw [Function.update_of_ne (by have h := i.isLt; rcases ha with ha | ha <;> omega)] + exact hz.2.2.2 a ha + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I (fun z => qs.length - z.regs index) + hbody hstart + have hn : y.regs index = qs.length := by + have h := hy.2.1 + simp only [Cond.holds_lt, hy.1.queryCount] at hc + omega + refine ⟨y, ?_, hy.1, ?_, hy.2.2.2⟩ + · apply (hs.seq he).mono + have hzero : start.regs index = 0 := by simp [start] + rw [hzero, Nat.sub_zero] + omega + · intro i + apply hy.2.2.1 i + rw [hn] + exact CountingSort.position_lt key i.isLt + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Routing.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Routing.lean new file mode 100755 index 000000000..6d5f61239 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Routing.lean @@ -0,0 +1,129 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Header +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Route +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.SavedGroups + +/-! +# Native routing preparation from original coordinates + +## Main results + +* `QueriedProduct.exec_prepareRouting`: run parameters, layout, original-input snapshot, + coordinate adaptation, actual routing and all protected group copies in sequence. + +The earlier router memory in the conclusion is an existential witness produced by its +actual execution. It is not a catalogue, layout or run certificate supplied by the caller. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The sum of the actual preparation phases, before matrix adaptation and encoding. -/ +@[expose] public def routingTimeBound (L m N Q : ℕ) : ℕ := + ((16 * L + 49) + ((6 * L + 16) * 2 ^ L + 14)) + 50 + + (7 * inputEnd m N Q + 10) + (14 * Q + 10) + Wanted.timeBound L m N Q + + (7 * numBands L m N ^ 2 + 14 * Q + 37) + +/-- Real original coordinates suffice to establish all routing invariants and a protected +copy of the entire original input. Header zero and outside memory may be arbitrary. -/ +public theorem exec_prepareRouting {w L m N : ℕ} (hm : m ≤ L) (qs : List (Fin N × Fin N)) + (x : Data 160) (hL : x.mem 1 = L) (hmemory : x.mem 2 = m) (hN : x.mem 3 = N) + (hQ : x.mem 4 = qs.length) + (hqueries : ∀ i < qs.length, + x.mem (queryInputBase m N + 2 * i) = (Wanted.inputPairs qs i).1 ∧ + x.mem (queryInputBase m N + 2 * i + 1) = (Wanted.inputPairs qs i).2) + (hfit : Fits w (wordBudget L m N qs.length)) : + ∃ (y : Data 160) (old : ℕ → ℕ), + ExecWithin w prepareRouting x y (routingTimeBound L m N qs.length) ∧ + Parameters L m N qs.length y ∧ SavedGroups L m N qs.length old y.mem ∧ + (∀ i : Fin qs.length, + old (Wanted.codeAddress L m N qs.length + + CountingSort.position (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) qs.length i) = + (PrunedQuery.queryIndex + (outStrOfPos (MaskTable.layout hm) (qs.get i).1 (qs.get i).2)).val ∧ + old (Wanted.originalAddress L m N qs.length + + CountingSort.position (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) qs.length i) = + i.val) ∧ + (∀ t ≤ numBands L m N ^ 2, + old (Wanted.offsetAddress L m N qs.length + t) = + Nat.count (fun k => (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) k < t) qs.length) ∧ + (∀ j < inputEnd m N qs.length, + y.mem (snapshotBase L m N qs.length + j) = x.mem j) ∧ + (∀ a, a < 5 ∨ groupedBase L m N qs.length ≤ a → y.mem a = x.mem a) := by + have hgeom : 5 ≤ inputEnd m N qs.length ∧ + 5 + 2 * qs.length ≤ inputEnd m N qs.length ∧ + inputEnd m N qs.length ≤ snapshotBase L m N qs.length ∧ + snapshotBase L m N qs.length + inputEnd m N qs.length ≤ groupedBase L m N qs.length := by + simp only [inputEnd, snapshotBase, groupedBase, originalBase, codeBase, offsetBase] + omega + obtain ⟨p, hp, pp, pm, _⟩ := exec_parameters hm x hL hmemory hN hfit + obtain ⟨l, hl, lp, lm⟩ := exec_layout p pp (by rw [pm]; exact hQ) hfit + have lmem : l.mem = x.mem := lm.trans pm + obtain ⟨s, hs, sp, sm⟩ := exec_saveInput l lp hfit + have ssnapshot (j : ℕ) (hj : j < inputEnd m N qs.length) : + s.mem (snapshotBase L m N qs.length + j) = x.mem j := by + rw [sm, ArrayCopy.copyPrefix_read hj, Nat.zero_add, lmem] + have sframe (a : ℕ) (ha : a < snapshotBase L m N qs.length ∨ + snapshotBase L m N qs.length + inputEnd m N qs.length ≤ a) : s.mem a = x.mem a := by + rw [sm, ArrayCopy.copyPrefix_outside (by rcases ha with ha | ha <;> omega), lmem] + obtain ⟨q, hq, qp, qm⟩ := exec_prepareQueries s sp hfit + have qsnapshot (j : ℕ) (hj : j < inputEnd m N qs.length) : + q.mem (snapshotBase L m N qs.length + j) = x.mem j := by + rw [qm, ArrayCopy.copyPrefix_outside (by omega)] + exact ssnapshot j hj + have qframe (a : ℕ) (ha : a < 5 ∨ groupedBase L m N qs.length ≤ a) : q.mem a = x.mem a := by + rw [qm, ArrayCopy.copyPrefix_outside (by rcases ha with ha | ha <;> omega)] + exact sframe a (by rcases ha with ha | ha <;> omega) + have qinput (i : ℕ) (hi : i < qs.length) : + q.mem (5 + 2 * i) = (Wanted.inputPairs qs i).1 ∧ + q.mem (5 + 2 * i + 1) = (Wanted.inputPairs qs i).2 := by + have hr := ArrayCopy.copyPrefix_read (mem := s.mem) + (src := snapshotBase L m N qs.length + queryInputBase m N) (dst := 5) + (k := 2 * qs.length) (j := 2 * i) (by omega) + have hc := ArrayCopy.copyPrefix_read (mem := s.mem) + (src := snapshotBase L m N qs.length + queryInputBase m N) (dst := 5) + (k := 2 * qs.length) (j := 2 * i + 1) (by omega) + have hrow := ssnapshot (queryInputBase m N + 2 * i) (query_address_lt hi (by decide : 0 < 2)) + have hcol := ssnapshot (queryInputBase m N + 2 * i + 1) + (query_address_lt hi (by decide : 1 < 2)) + constructor + · rw [qm, hr] + simpa only [Nat.add_assoc] using hrow.trans (hqueries i hi).1 + · rw [qm, show 5 + 2 * i + 1 = 5 + (2 * i + 1) by omega, hc] + simpa only [Nat.add_assoc] using hcol.trans (hqueries i hi).2 + obtain ⟨w, hw, wp, wparams, wv, wo, _, wf⟩ := exec_wanted hm qs q qp + ((qframe 1 (Or.inl (by decide))).trans hL) + ((qframe 2 (Or.inl (by decide))).trans hmemory) + ((qframe 3 (Or.inl (by decide))).trans hN) + ((qframe 4 (Or.inl (by decide))).trans hQ) qinput hfit + obtain ⟨y, hy, yp, yo, yc, yi, yf⟩ := exec_saveGroups w wp wparams hfit + have hlow : Wanted.memoryLimit L m N qs.length ≤ snapshotBase L m N qs.length := + (producers_le_snapshot L m N qs.length).2.1 + have hoff : snapshotBase L m N qs.length + inputEnd m N qs.length = + offsetBase L m N qs.length := rfl + have hg : originalBase L m N qs.length + qs.length = groupedBase L m N qs.length := rfl + refine ⟨y, w.mem, ?_, yp, ⟨yo, yc, yi⟩, wv, wo, ?_, ?_⟩ + · apply (hp.seq (hl.seq (hs.seq (hq.seq (hw.seq hy))))).mono + unfold routingTimeBound + omega + · intro j hj + rw [yf _ (Or.inl (by omega)), wf _ (Or.inr (by omega))] + exact qsnapshot j hj + · intro a ha + rw [yf _ (by rcases ha with ha | ha <;> omega), wf _ (by + rcases ha with ha | ha + · exact Or.inl (by unfold Wanted.inputLimit; omega) + · exact Or.inr (by omega))] + exact qframe a ha + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Thin.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Thin.lean new file mode 100755 index 000000000..634a55cd6 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Thin.lean @@ -0,0 +1,111 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Width + +/-! +# Quantitative saving for actual native queried products + +## Main results + +* `QueriedProduct.thin_timeBound`: the native charge, including halt, has the proved thin + saving plus an explicit term for every original query occurrence. +* `QueriedProduct.thin_timeBound_sparse`: an occurrence-count sparsity condition absorbs + that term and gives the complete charged `N² / D^(1/18)` bound, with a depth factor. + +These results compose the actual native charge with the existing finite encoding/tile/leaf +bounds from [AlmanWilliams2026, Section 2]. The occurrence hypothesis in the second result is +stronger than the source's distinct-position cardinality condition when requests repeat. +No sparse solver or running-time assertion is assumed. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The source's distinct-position sparsity condition retains an explicit charge for list +multiplicity. Every actual copy, conversion, producer, tile, epoch and report is included. -/ +public theorem thin_timeBound (m N : ℕ) (qs : List (Fin N × Fin N)) + (hN : D m ^ 18 ≤ N) (hW : qs.toFinset.card * 2 ^ m ≤ N ^ 2) : + ((timeBound (show m ≤ 19 * m by omega) qs + 1 : ℕ) : ℝ) ≤ + 65536 * ((19 * m + 1 : ℕ) : ℝ) * + (65 * ((D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2) + qs.length) := by + have hm : m ≤ 19 * m := by omega + have hNpos : 0 < N := (Nat.pow_pos (show 0 < D m from Nat.pow_pos (by decide))).trans_le hN + have hB := numBands_pos hm hNpos + have hH : 10 ^ (19 * m) ≤ numBands (19 * m) m N * 10 ^ (19 * m) := + Nat.le_mul_of_pos_left _ hB + have hprep := preparation_shape_le hm hNpos + have henc := encodings_total_le m N hN + have htiles := minutiae_total_le m N qs.toFinset hN hW + have hleaves := total_leaves_le (MaskTable.layout hm) qs.toFinset hN hW + have hc : (Cslib.Algorithms.MatrixMultiplication.Schonhage.tiles (19 * m) m N).card = + numBands (19 * m) m N ^ 2 := by + simp only [Cslib.Algorithms.MatrixMultiplication.Schonhage.tiles, + Finset.card_product, Finset.card_range, pow_two] + rw [hc] at htiles + have hprepR : ((1 + N * D m + 2 ^ (19 * m) + + 2 * numBands (19 * m) m N * 10 ^ (19 * m) : ℕ) : ℝ) ≤ + 5 * ((numBands (19 * m) m N * 10 ^ (19 * m) : ℕ) : ℝ) := by exact_mod_cast hprep + have hHR : ((10 ^ (19 * m) : ℕ) : ℝ) ≤ + ((numBands (19 * m) m N * 10 ^ (19 * m) : ℕ) : ℝ) := by exact_mod_cast hH + have hshape : ((1 + N * D m + qs.length + 2 ^ (19 * m) + + (numBands (19 * m) m N + 1) * 10 ^ (19 * m) + numBands (19 * m) m N ^ 2 + + ∑ T ∈ Cslib.Algorithms.MatrixMultiplication.Schonhage.tiles (19 * m) m N, + (Cslib.Algorithms.MatrixMultiplication.Schonhage.leaves + (wantedStrings (MaskTable.layout hm) qs.toFinset T.1 T.2)).card : ℕ) : ℝ) ≤ + 65 * ((D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2) + qs.length := by + have hnonneg : 0 ≤ (D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2 := by positivity + push_cast at hprepR hHR henc htiles hleaves ⊢ + nlinarith only [hprepR, hHR, henc, htiles, hleaves, hnonneg, + Nat.cast_nonneg (α := ℝ) (K (19 * m) m), Nat.cast_nonneg (α := ℝ) qs.toFinset.card, + Nat.cast_nonneg (α := ℝ) (N * D m), Nat.cast_nonneg (α := ℝ) (2 ^ (19 * m))] + have hcost := timeBound_le hm qs + have hcostR : ((timeBound hm qs + 1 : ℕ) : ℝ) ≤ + 65536 * ((19 * m + 1 : ℕ) : ℝ) * + ((1 + N * D m + qs.length + 2 ^ (19 * m) + + (numBands (19 * m) m N + 1) * 10 ^ (19 * m) + numBands (19 * m) m N ^ 2 + + ∑ T ∈ Cslib.Algorithms.MatrixMultiplication.Schonhage.tiles (19 * m) m N, + (Cslib.Algorithms.MatrixMultiplication.Schonhage.leaves + (wantedStrings (MaskTable.layout hm) qs.toFinset T.1 T.2)).card : ℕ) : ℝ) := by + exact_mod_cast hcost + exact hcostR.trans (mul_le_mul_of_nonneg_left hshape (by positivity)) + +/-- A bound on all original occurrences, rather than only distinct coordinates, absorbs +the actual list-input/reporting charge. The final halt is included. -/ +public theorem thin_timeBound_sparse (m N : ℕ) (qs : List (Fin N × Fin N)) + (hN : D m ^ 18 ≤ N) (hQ : qs.length * 2 ^ m ≤ N ^ 2) : + ((timeBound (show m ≤ 19 * m by omega) qs + 1 : ℕ) : ℝ) ≤ + 4325376 * ((19 * m + 1 : ℕ) : ℝ) * (N : ℝ) ^ 2 / (D m : ℝ) ^ (1 / 18 : ℝ) := by + have hW : qs.toFinset.card * 2 ^ m ≤ N ^ 2 := + (Nat.mul_le_mul_right _ (List.toFinset_card_le qs)).trans hQ + have hQ' : (qs.length : ℝ) * (2 : ℝ) ^ m ≤ (N : ℝ) ^ 2 := by exact_mod_cast hQ + have hp : (2 : ℝ) ^ ((m : ℝ) / 9) ≤ (2 : ℝ) ^ m := by + rw [← Real.rpow_natCast] + exact Real.rpow_le_rpow_of_exponent_le (by norm_num) + (by have := Nat.cast_nonneg (α := ℝ) m; linarith) + have hlen : (qs.length : ℝ) ≤ (D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2 := by + rw [← saving_eq_D_rpow] + calc (qs.length : ℝ) = saving m * (qs.length * (2 : ℝ) ^ ((m : ℝ) / 9)) := by + rw [mul_left_comm, saving_mul_two_rpow, mul_one] + _ ≤ saving m * (qs.length * (2 : ℝ) ^ m) := + mul_le_mul_of_nonneg_left (mul_le_mul_of_nonneg_left hp (Nat.cast_nonneg _)) + (saving_pos m).le + _ ≤ saving m * (N : ℝ) ^ 2 := mul_le_mul_of_nonneg_left hQ' (saving_pos m).le + have h := thin_timeBound m N qs hN hW + have hfinal : ((timeBound (show m ≤ 19 * m by omega) qs + 1 : ℕ) : ℝ) ≤ + 4325376 * ((19 * m + 1 : ℕ) : ℝ) * + ((D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2) := by + have hL : 0 ≤ ((19 * m + 1 : ℕ) : ℝ) := Nat.cast_nonneg _ + nlinarith only [h, hlen, mul_nonneg hL (sub_nonneg.mpr hlen)] + rw [Real.rpow_neg (by positivity)] at hfinal + simpa only [div_eq_mul_inv, mul_assoc, mul_left_comm, mul_comm] using hfinal + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/ThinWidth.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/ThinWidth.lean new file mode 100755 index 000000000..09a79d9c5 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/ThinWidth.lean @@ -0,0 +1,216 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Thin + +/-! +# Source-regime logarithmic width for actual queried products + +## Main results + +* `QueriedProduct.thin_envelopes`: original dimensions give polynomial magnitude/address + envelopes and an explicit polynomial allocation bound. +* `QueriedProduct.thin_admissionWidth_le`: a positive constant times the logarithm of original + dimension, request count and entry magnitude suffices in the source's thin regime. + +These bounds concern the real native program and its actual allocation formulas. They assume +neither intermediate-state fits nor a polynomial-space certificate. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The source regime controls every original power, computed band count and address term. +The deliberately loose integer constants leave a uniform logarithmic-width margin. -/ +public theorem thin_envelopes (m N Q A : ℕ) (hN : D m ^ 18 ≤ N) : + magnitudeBound (19 * m) A + wordBudget (19 * m) m N Q + 1 ≤ + 2 ^ 40 * (N + Q + A + 2) ^ 10 ∧ + memoryEnd (19 * m) m N Q ≤ 128 * (N + Q + A + 2) ^ 4 := by + let S := N + Q + A + 2 + let Z := S ^ 2 + have hs : 2 ≤ S := by dsimp only [S]; omega + have hz : 1 ≤ Z := Nat.one_le_pow _ _ (by omega) + have hsz : S ≤ Z := Nat.le_self_pow (by decide) _ + have hNN : N ≤ S := by dsimp only [S]; omega + have hN0 : N ≤ Z := hNN.trans hsz + have hQ0 : Q ≤ Z := (show Q ≤ S by dsimp only [S]; omega).trans hsz + have hA0 : A ≤ Z := (show A ≤ S by dsimp only [S]; omega).trans hsz + have hp : 10 ^ (19 * m) ≤ N ^ 2 := by + calc 10 ^ (19 * m) = (10 ^ 19) ^ m := by rw [pow_mul] + _ ≤ (4 ^ 36) ^ m := Nat.pow_le_pow_left (by decide) m + _ = (D m ^ 18) ^ 2 := by + simp only [D, ← pow_mul] + congr 1 + omega + _ ≤ N ^ 2 := Nat.pow_le_pow_left hN 2 + have h10 : 10 ^ (19 * m) ≤ Z := hp.trans (Nat.pow_le_pow_left hNN 2) + have h2 : 2 ^ (19 * m) ≤ Z := (Nat.pow_le_pow_left (by decide : 2 ≤ 10) _).trans h10 + have h7 : 7 ^ (19 * m) ≤ Z := (Nat.pow_le_pow_left (by decide : 7 ≤ 10) _).trans h10 + have hL : 19 * m ≤ Z := (Nat.lt_pow_self (by decide : 1 < 10)).le.trans h10 + have hD : D m ≤ Z := ((Nat.le_self_pow (by decide : 18 ≠ 0) _).trans hN).trans hN0 + have hm : m ≤ 19 * m := by omega + have hbandpos : 0 < bandSize (19 * m) m := Nat.mul_pos (K0_pos hm) (N0_pos _ _) + have hbands : numBands (19 * m) m N ≤ N := by + apply Nat.le_of_lt_succ + change (N + bandSize (19 * m) m - 1) / bandSize (19 * m) m < N + 1 + apply (Nat.div_lt_iff_lt_mul hbandpos).mpr + have hmul := Nat.le_mul_of_pos_right N hbandpos + rw [Nat.add_mul, Nat.one_mul] + omega + have hB : numBands (19 * m) m N ≤ Z := hbands.trans hN0 + have hall : PrunedQuery.epochAllowance (19 * m) ≤ 2 * Z := by + unfold PrunedQuery.epochAllowance + omega + let bi := 4 + 2 * (Z * Z) + let bc := (bi + (2 + Z + Z) + Z) + bi + Z + Z + let be := bc + 2 * Z * Z + let we := 6 + 4 * Z + 2 * Z + 3 * Z ^ 2 + let ii := 5 + 2 * (Z * Z) + 2 * Z + let ss := be + we + ii + let oo := ss + ii + let cc := oo + Z ^ 2 + 1 + let ou := cc + Z + Z + Z + let ff := ou + Z + let ee := ff + 4 * (Z + 1) + 2 * (Z + 1) * Z + Z + let ep := Z ^ 2 * (1 + 2 * Z) + have hbc : BandPreparation.catalogueBase (19 * m) m N ≤ bc := by + dsimp only [bc, bi] + simp only [BandPreparation.catalogueBase, BandPreparation.tableBase, + BandPreparation.inverseBase, BandPreparation.snapshotBase, BandPreparation.inputEnd, + Encoding.scratchBase, Encoding.outputBase] + gcongr + have hbe : BandPreparation.memoryEnd (19 * m) m N ≤ be := by + dsimp only [be] + unfold BandPreparation.memoryEnd + gcongr + have hwe : Wanted.memoryLimit (19 * m) m N Q ≤ we := by + dsimp only [we] + rw [Wanted.memoryLimit_eq] + gcongr + have hii : inputEnd m N Q ≤ ii := by dsimp only [ii, inputEnd]; gcongr + have hss : snapshotBase (19 * m) m N Q ≤ ss := by + dsimp only [ss, snapshotBase] + gcongr + have hoo : offsetBase (19 * m) m N Q ≤ oo := by + dsimp only [oo, offsetBase] + gcongr + have hcc : codeBase (19 * m) m N Q ≤ cc := by + dsimp only [cc, codeBase] + gcongr + have hou : outputBase (19 * m) m N Q ≤ ou := by + dsimp only [ou, outputBase, groupedBase, originalBase] + gcongr + have hff : scratchBase (19 * m) m N Q ≤ ff := by + dsimp only [ff, scratchBase] + gcongr + have hee : memoryEnd (19 * m) m N Q ≤ ee := by + dsimp only [ee, memoryEnd, stampBase] + gcongr + have hep : epochBound (19 * m) m N ≤ ep := by + dsimp only [ep, epochBound] + gcongr + let bb := 256 * (Z + Z + Z * Z + Z + Z + 2 * Z * Z + 1) + let tab := Z * 2 + 5 + Z + let rb := 64 * (Z + Z + tab + Z ^ 2 + Z + Z + 1) + let wb := 256 * (bb + rb + we + Z + Z + Z + Z ^ 2 + Z + Z + 16) + let kb := (256 * (Z + 1) * Z + 32 * Z + 512) + + ((8 * ff + 64 * (Z + 1) * Z + be + be + 2 * Z + (ep + 1) + 2 * Z + 128) + + 4 * (cc + ou + Z + 1)) + let ab := 1024 * (bb + wb + kb + ee + ii + Z + Z + Z + 16) + let mg := Z * Z + Z * Z + Z * ((Z * Z) * (Z * Z)) + Z + have hbb : BandPreparation.wordBudget (19 * m) m N ≤ bb := by + dsimp only [bb, BandPreparation.wordBudget] + gcongr + have htab : Wanted.tableAddress (19 * m) Q ≤ tab := by + dsimp only [tab, Wanted.tableAddress, Wanted.inputLimit] + gcongr + have hrb : Wanted.routeBudget (19 * m) m N (Wanted.tableAddress (19 * m) Q) ≤ rb := by + dsimp only [rb, Wanted.routeBudget] + gcongr + have hwb : Wanted.wordBudget (19 * m) m N Q ≤ wb := by + dsimp only [wb, Wanted.wordBudget] + gcongr + have hkb : PrunedQuery.kernelBudget (19 * m) (scratchBase (19 * m) m N Q) + (BandPreparation.memoryEnd (19 * m) m N) (BandPreparation.memoryEnd (19 * m) m N) + (codeBase (19 * m) m N Q) (outputBase (19 * m) m N Q) Q (epochBound (19 * m) m N) ≤ kb := by + dsimp only [kb, PrunedQuery.kernelBudget, PrunedQuery.addressBound, + PrunedQuery.evaluationBudget, PrunedQuery.nodeBudget] + gcongr + have hab : wordBudget (19 * m) m N Q ≤ ab := by + dsimp only [ab, wordBudget] + gcongr + have hmg : magnitudeBound (19 * m) A ≤ mg := by + dsimp only [mg, magnitudeBound, PrunedQuery.magnitudeBound] + gcongr + have h0 : 1 ≤ Z ^ 5 := Nat.one_le_pow _ _ hz + have h1 : Z ≤ Z ^ 5 := Nat.le_self_pow (by decide) _ + have h2' : Z ^ 2 ≤ Z ^ 5 := Nat.pow_le_pow_right hz (by decide) + have h3 : Z ^ 3 ≤ Z ^ 5 := Nat.pow_le_pow_right hz (by decide) + have htotal : mg + ab + 1 ≤ 2 ^ 40 * Z ^ 5 := by + dsimp only [mg, ab, wb, bb, rb, tab, kb, ee, ff, ou, cc, oo, ss, be, bc, bi, we, ii, ep] + nlinarith only [h0, h1, h2', h3] + have hmem : ee ≤ 128 * Z ^ 2 := by + have hz2 := Nat.le_self_pow (by decide : 2 ≠ 0) Z + have h02 : 1 ≤ Z ^ 2 := Nat.one_le_pow _ _ hz + dsimp only [ee, ff, ou, cc, oo, ss, be, bc, bi, we, ii] + nlinarith only [hz2, h02] + constructor + · have h := (Nat.add_le_add_right (Nat.add_le_add hmg hab) 1).trans htotal + simpa only [Z, S, ← pow_mul, Nat.reduceMul] using h + · have h := hee.trans hmem + simpa only [Z, S, ← pow_mul, Nat.reduceMul] using h + +/-- A fixed positive logarithmic-width coefficient suffices throughout the thin regime. -/ +public theorem thin_admissionWidth_le (m N Q A : ℕ) (hN : D m ^ 18 ≤ N) : + admissionWidth (19 * m) m N Q A ≤ 128 * Nat.clog 2 (N + Q + A + 2) := by + let S := N + Q + A + 2 + have hs : 1 < S := by dsimp only [S]; omega + have hl := Nat.clog_pos (by decide : 1 < (2 : ℕ)) hs + have hp := Nat.le_pow_clog (by decide : 1 < (2 : ℕ)) S + have he := (thin_envelopes m N Q A hN).1 + have hpow : magnitudeBound (19 * m) A + wordBudget (19 * m) m N Q + 1 ≤ + 2 ^ (40 + 10 * Nat.clog 2 S) := by + calc _ ≤ 2 ^ 40 * S ^ 10 := he + _ ≤ 2 ^ 40 * (2 ^ Nat.clog 2 S) ^ 10 := Nat.mul_le_mul_left _ (Nat.pow_le_pow_left hp 10) + _ = _ := by rw [pow_add, ← pow_mul, Nat.mul_comm (Nat.clog 2 S) 10] + have hc := (Nat.clog_le_iff_le_pow (by decide : 1 < (2 : ℕ))).mpr hpow + unfold admissionWidth + dsimp only [S] at hc hl + omega + +/-- Complete initialized native thin-product execution at a fixed positive logarithmic-width +threshold. Every original occurrence is answered, including duplicates, and halt is charged. +The occurrence-count condition is stated explicitly rather than replaced by set cardinality. -/ +public theorem runs_thin {W m N A : ℕ} (bits : Bool) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (hN : D m ^ 18 ≤ N) (hQ : qs.length * 2 ^ m ≤ N ^ 2) + (hX : ∀ i k, |X i k| ≤ (A : ℤ)) (hY : ∀ k j, |Y k j| ≤ (A : ℤ)) + (hbits : bits = true → (∀ i k, 0 ≤ X i k) ∧ (∀ k j, 0 ≤ Y k j)) + (hW : 128 * Nat.clog 2 (N + qs.length + A + 2) ≤ W) : + InputFits W (input bits (19 * m) X Y qs) ∧ + ∃ y n, + Runs W ((program bits).program output) (State.init (input bits (19 * m) X Y qs)) + ⟨(program bits).size, y⟩ n ∧ + Executes W ((program bits).program output) (State.init (input bits (19 * m) X Y qs)) + (outputBase (19 * m) m N qs.length) (n + 1) ∧ + ((n + 1 : ℕ) : ℝ) ≤ 4325376 * ((19 * m + 1 : ℕ) : ℝ) * (N : ℝ) ^ 2 / + (D m : ℝ) ^ (1 / 18 : ℝ) ∧ y.Bounded W ∧ + (∀ i : Fin qs.length, y.mem (outputBase (19 * m) m N qs.length + i.val) = + Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2)) ∧ + (∀ a, ¬(inputEnd m N qs.length ≤ a ∧ a < memoryEnd (19 * m) m N qs.length) → + y.mem a = (State.init (input bits (19 * m) X Y qs) : State 160).data.mem a) := by + obtain ⟨hi, y, n, hn, hr, he, hy, hv, hf⟩ := runs_wide (show m ≤ 19 * m by omega) + bits X Y qs hX hY hbits ((thin_admissionWidth_le m N qs.length A hN).trans hW) + have hnR : ((n + 1 : ℕ) : ℝ) ≤ (timeBound (show m ≤ 19 * m by omega) qs + 1 : ℕ) := by + exact_mod_cast Nat.add_le_add_right hn 1 + exact ⟨hi, y, n, hr, he, hnR.trans (thin_timeBound_sparse m N qs hN hQ), hy, hv, hf⟩ + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Tile.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Tile.lean new file mode 100755 index 000000000..019118421 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Tile.lean @@ -0,0 +1,243 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Arguments +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Catalogue +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.SavedGroups +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Pruned.Kernel +public import Complexity.Algorithms.MatrixMultiplication.Schonhage.Magnitude + +/-! +# One actual tile call on produced arrays and computed routing words + +## Main results + +* `QueriedProduct.exec_tileStep`: load the actual span, construct the kernel ABI, execute the + native pruned kernel and propagate its returned epoch while advancing the real tile index. + +The proof decodes typed queries from actual saved words; they are not program input. +The finite request set, encoding arrays, frames, arithmetic +fits and call charge are obtained from the actual producer contracts. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- One charged native tile, with the actual query set and a reusable timestamp directory. +The final memory witness contains every reported pruned value and is the same state whose +epoch is passed to the next tile. Its answers are the original signed matrix product. -/ +public theorem exec_tileStep {w L m N A t E : ℕ} (hw : 0 < w) (hm : m ≤ L) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (old : ℕ → ℕ) (x : Data 160) + (hp : Parameters L m N qs.length x) (hx : x.regs index = t) + (ht : t < numBands L m N ^ 2) (hE : x.regs epoch = E) (hEbound : E ≤ epochBound L m N) + (hg : SavedGroups L m N qs.length old x.mem) (he : EncodedBands hm X Y x.mem) + (hvalues : ∀ i : Fin qs.length, + old (Wanted.codeAddress L m N qs.length + + CountingSort.position (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) qs.length i) = + (PrunedQuery.queryIndex + (outStrOfPos (MaskTable.layout hm) (qs.get i).1 (qs.get i).2)).val) + (hoffsets : ∀ t ≤ numBands L m N ^ 2, + old (Wanted.offsetAddress L m N qs.length + t) = + Nat.count (fun k => (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) k < t) qs.length) + (hmarks : ∀ j < 10 ^ L, x.mem (stampBase L m N qs.length + j) ≤ E) + (hX : ∀ i k, |X i k| ≤ (A : ℤ)) (hY : ∀ k j, |Y k j| ≤ (A : ℤ)) + (hmag : magnitudeBound L A < 2 ^ (w - 1)) + (hfit : Fits (w + 2) (wordBudget L m N qs.length)) : + let a := x.mem (offsetBase L m N qs.length + t) + let b := x.mem (offsetBase L m N qs.length + t + 1) + let β := t / numBands L m N + let γ := t % numBands L m N + ∃ y, + ExecWithin (w + 2) tileStep x y + (4 * L + 29 * (b - a) + + PrunedQuery.traceWeight (wantedStrings (MaskTable.layout hm) qs.toFinset β γ) + 64) ∧ + Parameters L m N qs.length y ∧ y.regs index = t + 1 ∧ + E + 1 ≤ y.regs epoch ∧ y.regs epoch ≤ E + 1 + PrunedQuery.epochAllowance L ∧ + SavedGroups L m N qs.length old y.mem ∧ EncodedBands hm X Y y.mem ∧ + (∀ j < 10 ^ L, y.mem (stampBase L m N qs.length + j) ≤ y.regs epoch) ∧ + (∀ i : Fin qs.length, + (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) i.val = t → + y.mem (groupedBase L m N qs.length + + CountingSort.position (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) qs.length i) = + Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2)) ∧ + (∀ p, ¬(groupedBase L m N qs.length + a ≤ p ∧ + p < groupedBase L m N qs.length + b) → + ¬(scratchBase L m N qs.length ≤ p ∧ p < memoryEnd L m N qs.length) → + y.mem p = x.mem p) := by + dsimp only + let B := numBands L m N + let a := x.mem (offsetBase L m N qs.length + t) + let b := x.mem (offsetBase L m N qs.length + t + 1) + let β := t / B + let γ := t % B + let pa := BandPreparation.catalogueBase L m N + β * 10 ^ L + let pb := BandPreparation.catalogueBase L m N + (B + γ) * 10 ^ L + have habQ : a ≤ b ∧ b ≤ qs.length := + hg.span_bounds (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) hoffsets ht + have hB : 0 < B := by dsimp only [B] at *; nlinarith only [ht, Nat.zero_le t] + have hβ : β < B := (Nat.div_lt_iff_lt_mul hB).mpr (by simpa [B, pow_two] using ht) + have hγ : γ < B := Nat.mod_lt t hB + have hT : (β, γ) ∈ Cslib.Algorithms.MatrixMultiplication.Schonhage.tiles L m N := + Finset.mem_product.mpr ⟨Finset.mem_range.mpr hβ, Finset.mem_range.mpr hγ⟩ + have htnum : β * numBands L m N + γ = t := by + simpa only [β, γ, B, Nat.mul_comm] using Nat.div_add_mod t B + have hcode (j : ℕ) (hj : j < qs.length) : + x.mem (codeBase L m N qs.length + j) < 10 ^ L := by + rw [hg.codes j hj] + exact router_code_lt hm qs old hvalues j hj + let queries : Fin (b - a) → OutStr L := fun i => PrunedQuery.queryAt + ⟨x.mem (codeBase L m N qs.length + (a + i.val)), hcode _ (by have hi := i.isLt; omega)⟩ + have hinput (i : Fin (b - a)) : x.mem (codeBase L m N qs.length + (a + i.val)) = + (PrunedQuery.queryIndex (queries i)).val := by + simp only [queries, PrunedQuery.queryIndex_queryAt] + have hset : Finset.univ.image queries = + wantedStrings (MaskTable.layout hm) qs.toFinset β γ := + hg.span_queries hm qs hvalues hoffsets (β, γ) hT a b + (by rw [htnum]) + (by rw [htnum]; rfl) queries hinput + obtain ⟨arg, har, arp, arm, ari, _, arL, arA, arB, arq, arQ, aro, arf, arE⟩ := + exec_tileArguments x hp hx ht (rfl : x.mem (offsetBase L m N qs.length + t) = a) + (rfl : x.mem (offsetBase L m N qs.length + t + 1) = b) habQ.1 habQ.2 hfit + change (arg.comap bank).regs PrunedQuery.arrayA = pa at arA + change (arg.comap bank).regs PrunedQuery.arrayB = pb at arB + change (arg.comap bank).regs PrunedQuery.queries = codeBase L m N qs.length + a at arq + change (arg.comap bank).regs PrunedQuery.queryCount = b - a at arQ + change (arg.comap bank).regs PrunedQuery.output = groupedBase L m N qs.length + a at aro + have hpab : pa + 10 ^ L ≤ BandPreparation.memoryEnd L m N ∧ + pb + 10 ^ L ≤ BandPreparation.memoryEnd L m N := by + have hl := BandPreparation.catalogue_block_le false hβ + have hr := BandPreparation.catalogue_block_le true hγ + simpa only [BandPreparation.catalogueAddress, Bool.false_eq_true, ↓reduceIte, + Nat.zero_add, Nat.add_zero, pa, pb, B] using And.intro hl hr + have hlow : BandPreparation.memoryEnd L m N ≤ snapshotBase L m N qs.length ∧ + snapshotBase L m N qs.length ≤ offsetBase L m N qs.length ∧ + offsetBase L m N qs.length ≤ groupedBase L m N qs.length := by + refine ⟨(producers_le_snapshot L m N qs.length).1, ?_, ?_⟩ + · unfold offsetBase + omega + · unfold groupedBase originalBase codeBase + omega + have hout : groupedBase L m N qs.length + qs.length = outputBase L m N qs.length := rfl + have hqueries : codeBase L m N qs.length + qs.length ≤ groupedBase L m N qs.length := by + unfold groupedBase originalBase + omega + have hfree : outputBase L m N qs.length ≤ scratchBase L m N qs.length := by + unfold scratchBase + omega + have hbefore : BandPreparation.memoryEnd L m N ≤ scratchBase L m N qs.length := by omega + have hleft (τ : Leaf L) : arg.mem (pa + (Encoding.leafIndex τ).val) = + Signed.encode (encodingL (bandArrayL (MaskTable.layout hm) X β) τ) := by + rw [arm] + simpa only [pa, BandPreparation.catalogueAddress, Bool.false_eq_true, ↓reduceIte, + Nat.zero_add, Nat.add_assoc] using he.left β hβ τ + have hright (τ : Leaf L) : arg.mem (pb + (Encoding.leafIndex τ).val) = + Signed.encode (encodingR (bandArrayR (MaskTable.layout hm) Y γ) τ) := by + rw [arm] + simpa only [pb, B, BandPreparation.catalogueAddress, ↓reduceIte, Nat.add_assoc] using + he.right γ hγ τ + have habsL (τ : Leaf L) : |encodingL (bandArrayL (MaskTable.layout hm) X β) τ| ≤ + ((7 ^ L * A : ℕ) : ℤ) := by + exact_mod_cast abs_encodingL_le + (abs_bandArrayL_le (MaskTable.layout hm) (by positivity) hX β) τ + have habsR (τ : Leaf L) : |encodingR (bandArrayR (MaskTable.layout hm) Y γ) τ| ≤ + ((7 ^ L * A : ℕ) : ℤ) := by + exact_mod_cast abs_encodingR_le + (abs_bandArrayR_le (MaskTable.layout hm) (by positivity) hY γ) τ + have hpa := (Nat.le_add_right pa (10 ^ L)).trans hpab.1 + have hpb := (Nat.le_add_right pb (10 ^ L)).trans hpab.2 + have hkernel := kernel_fits hfit hpa hpb + (show codeBase L m N qs.length + a ≤ codeBase L m N qs.length + qs.length by omega) + (show groupedBase L m N qs.length + a ≤ outputBase L m N qs.length by omega) + (show b - a ≤ qs.length by omega) hEbound + obtain ⟨k, hk, kv, kf, _, kel, keu, km⟩ := PrunedQuery.exec_kernel + (L := L) (f := scratchBase L m N qs.length) (a0 := pa) (b0 := pb) + (q0 := codeBase L m N qs.length + a) (o0 := groupedBase L m N qs.length + a) + (Q := b - a) (E := E) + (encodingL (bandArrayL (MaskTable.layout hm) X β)) + (encodingR (bandArrayR (MaskTable.layout hm) Y γ)) queries (arg.comap bank) + arL arA arB arQ arq aro arf (arE.trans hE) + (by intro i; simpa only [Data.comap_mem, arm, Nat.add_assoc] using hinput i) + hleft hright (by omega) (by omega) (by omega) (by omega) + (by simpa only [Data.comap_mem, arm, stampBase] using hmarks) + habsL habsR hw (magnitude_admissions hw hmag).2.1 hkernel + rw [hset] at hk kv + let u := k.extend bank arg + have hhigh (i : Fin 160) (hi : 96 ≤ i.val) : u.regs i = arg.regs i := by + have hnot : ¬∃ j, bank j = i := by + rintro ⟨j, hj⟩ + have hv := congrArg (fun r : Fin 160 => r.val) hj + change j.val = i.val at hv + omega + exact Function.extend_apply' _ _ _ hnot + have up : Parameters L m N qs.length u := arp.reframe hhigh + have ui : u.regs index = t := (hhigh _ (by decide)).trans ari + have ue : u.regs (bank PrunedQuery.epoch) = k.regs PrunedQuery.epoch := + bank_injective.extend_apply _ _ _ + have hnext : Fits (w + 2) (t + 1) := allocated_fits hfit (by + unfold memoryEnd stampBase scratchBase outputBase groupedBase originalBase codeBase offsetBase + have h10 := Nat.zero_le (10 ^ L) + omega) + let y := (u.set epoch (k.regs PrunedQuery.epoch)).set index (t + 1) + have hpost : ExecWithin (B := .standard) (w + 2) + (.seq (.op (.copy epoch (bank PrunedQuery.epoch))) (ram[index ← index + one])) u y 2 := by + ram_eval [y, ue, ui, up.one, hnext, epoch, index, one] + have yp : Parameters L m N qs.length y := + (up.set_work epoch (by decide) _).set_work index (by decide) _ + have yepoch : y.regs epoch = k.regs PrunedQuery.epoch := by simp [y, epoch, index] + have yframe (p : ℕ) + (ho : ¬(groupedBase L m N qs.length + a ≤ p ∧ + p < groupedBase L m N qs.length + b)) + (hf : ¬(scratchBase L m N qs.length ≤ p ∧ p < memoryEnd L m N qs.length)) : + y.mem p = x.mem p := by + change k.mem p = x.mem p + rw [kf p (by omega) (by simpa only [memoryEnd, stampBase] using hf), + Data.comap_mem, arm] + refine ⟨y, ?_, yp, by simp [y], by simpa only [yepoch] using kel, + by simpa only [yepoch] using keu, ?_, ?_, ?_, ?_, yframe⟩ + · apply (har.seq ((hk.rename bank_injective).seq hpost)).mono + dsimp only [β, γ, B, a, b] + omega + · exact hg.reframe (fun p hp hbefore => yframe p (by omega) (by omega)) + · exact he.reframe (fun p hp => yframe p (by omega) (by omega)) + · intro j hj + change k.mem (stampBase L m N qs.length + j) ≤ y.regs epoch + simpa only [stampBase, yepoch] using km j hj + · intro i hkey + let p := CountingSort.position (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) qs.length i + have hp : p < qs.length := CountingSort.position_lt _ i.isLt + have hspan : a ≤ p ∧ p < b := by + have hs := (CountingSort.position_mem_iff + (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) i.isLt t).mpr hkey + have ha := (hg.offsets t ht.le).trans (hoffsets t ht.le) + have hb := (hg.offsets (t + 1) (by omega)).trans (hoffsets (t + 1) (by omega)) + change a = _ at ha + change b = _ at hb + simpa only [← ha, ← hb] using hs + let j : Fin (b - a) := ⟨p - a, by omega⟩ + have hap : a + j.val = p := by dsimp only [j]; omega + have hquery : queries j = outStrOfPos (MaskTable.layout hm) (qs.get i).1 (qs.get i).2 := by + have hr := hinput j + rw [hap, hg.codes p hp, hvalues i] at hr + simpa only [PrunedQuery.queryAt_queryIndex] using + congrArg PrunedQuery.queryAt (Fin.ext hr.symm) + have htile : Wanted.tileKey L m N ((qs.get i).1.val, (qs.get i).2.val) = + β * numBands L m N + γ := by + simpa only [Function.comp_apply, Wanted.inputPairs_get, htnum] using hkey + have hbands := (Wanted.tileKey_eq_iff hm (qs.get i).1 (qs.get i).2 (β, γ) hT).mp htile + change bandOf L m (qs.get i).1.val = β ∧ bandOf L m (qs.get i).2.val = γ at hbands + have hr := kv j + rw [Nat.add_assoc, hap, hquery, ← hbands.1, ← hbands.2, + thin_product_spec (MaskTable.layout hm) X Y qs.toFinset (qs.get i).1 (qs.get i).2 + (List.mem_toFinset.mpr (List.get_mem _ _))] at hr + exact hr + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Tiles.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Tiles.lean new file mode 100755 index 000000000..1e2cb0060 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Tiles.lean @@ -0,0 +1,165 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Tile +import Mathlib.Algebra.BigOperators.Intervals + +/-! +# Actual all-tile execution with shared encodings and epochs + +## Main definitions + +* `QueriedProduct.tileCharge`: the charge of one actual tile call and its outer loop control. + +## Main results + +* `QueriedProduct.exec_evaluateTiles`: every original request receives its exact matrix-product + value in grouped order, while actual returned epochs retain one shared timestamp directory. + +The finite charge uses original coordinate occurrences and the existing pruned trace count. +The proof derives it from executed spans and calls. No sparse bound is assumed. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open scoped BigOperators + +/-- Per-tile analysis of the actual program, including call setup, returned epoch/index +updates and two outer-loop instructions. Duplicate coordinate occurrences remain charged. -/ +@[expose] public def tileCharge {L m N : ℕ} (hm : m ≤ L) + (qs : List (Fin N × Fin N)) (t : ℕ) : ℕ := + 4 * L + 29 * Nat.count (fun i => (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) i = t) + qs.length + PrunedQuery.traceWeight + (wantedStrings (MaskTable.layout hm) qs.toFinset (t / numBands L m N) + (t % numBands L m N)) + 66 + +private structure Progress {L m N : ℕ} (hm : m ≤ L) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (old initial : ℕ → ℕ) (z : Data 160) : Prop where + fixed : Parameters L m N qs.length z + index_le : z.regs index ≤ numBands L m N ^ 2 + epoch_le : z.regs epoch ≤ z.regs index * (1 + PrunedQuery.epochAllowance L) + groups : SavedGroups L m N qs.length old z.mem + catalogue : EncodedBands hm X Y z.mem + marks : ∀ j < 10 ^ L, z.mem (stampBase L m N qs.length + j) ≤ z.regs epoch + answers : ∀ i : Fin qs.length, + (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) i.val < z.regs index → + z.mem (groupedBase L m N qs.length + + CountingSort.position (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) qs.length i) = + Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2) + frame : ∀ p, ¬(groupedBase L m N qs.length ≤ p ∧ p < outputBase L m N qs.length) → + ¬(scratchBase L m N qs.length ≤ p ∧ p < memoryEnd L m N qs.length) → z.mem p = initial p + +/-- Every real tile is evaluated by the same fixed program. The zero-band case exits without +division, and repeated requested coordinates retain their distinct grouped positions. -/ +public theorem exec_evaluateTiles {w L m N A : ℕ} (hw : 0 < w) (hm : m ≤ L) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (old : ℕ → ℕ) (x : Data 160) + (hp : Parameters L m N qs.length x) (hi : x.regs index = 0) (hE : x.regs epoch = 0) + (hg : SavedGroups L m N qs.length old x.mem) (he : EncodedBands hm X Y x.mem) + (hvalues : ∀ i : Fin qs.length, + old (Wanted.codeAddress L m N qs.length + + CountingSort.position (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) qs.length i) = + (PrunedQuery.queryIndex + (outStrOfPos (MaskTable.layout hm) (qs.get i).1 (qs.get i).2)).val) + (hoffsets : ∀ t ≤ numBands L m N ^ 2, + old (Wanted.offsetAddress L m N qs.length + t) = + Nat.count (fun k => (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) k < t) qs.length) + (hmarks : ∀ j < 10 ^ L, x.mem (stampBase L m N qs.length + j) = 0) + (hX : ∀ i k, |X i k| ≤ (A : ℤ)) (hY : ∀ k j, |Y k j| ≤ (A : ℤ)) + (hmag : magnitudeBound L A < 2 ^ (w - 1)) + (hfit : Fits (w + 2) (wordBudget L m N qs.length)) : + ∃ y, ExecWithin (w + 2) evaluateTiles x y + ((∑ t ∈ Finset.range (numBands L m N ^ 2), tileCharge hm qs t) + 2) ∧ + Parameters L m N qs.length y ∧ SavedGroups L m N qs.length old y.mem ∧ + EncodedBands hm X Y y.mem ∧ + (∀ i : Fin qs.length, + y.mem (groupedBase L m N qs.length + + CountingSort.position (Wanted.tileKey L m N ∘ Wanted.inputPairs qs) qs.length i) = + Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2)) ∧ + (∀ p, ¬(groupedBase L m N qs.length ≤ p ∧ p < outputBase L m N qs.length) → + ¬(scratchBase L m N qs.length ≤ p ∧ p < memoryEnd L m N qs.length) → + y.mem p = x.mem p) := by + let key := Wanted.tileKey L m N ∘ Wanted.inputPairs qs + let T := numBands L m N ^ 2 + let I := Progress hm X Y qs old x.mem + let Φ (z : Data 160) := ∑ t ∈ Finset.Ico (z.regs index) T, tileCharge hm qs t + have hout : groupedBase L m N qs.length + qs.length = outputBase L m N qs.length := rfl + have hfree : outputBase L m N qs.length ≤ scratchBase L m N qs.length := by + unfold scratchBase + omega + have hstart : I x := ⟨hp, by rw [hi]; omega, by rw [hi, hE]; simp, + hg, he, by intro j hj; rw [hmarks j hj, hE], + by intro i hk; rw [hi] at hk; omega, by intro p _ _; rfl⟩ + have hbody (z : Data 160) (hz : I z) (hc : (Cond.lt index tiles).Holds z) : + ∃ y C, ExecWithin (w + 2) tileStep z y C ∧ I y ∧ C + 2 + Φ y ≤ Φ z := by + have ht : z.regs index < T := by simpa only [Cond.holds_lt, hz.fixed.tiles, T] using hc + have hepoch : z.regs epoch ≤ epochBound L m N := by + have h := hz.epoch_le.trans + (Nat.mul_le_mul_right (1 + PrunedQuery.epochAllowance L) hz.index_le) + simpa only [epochBound] using h + obtain ⟨y, hr, yp, yi, _, yeu, yg, ye, ym, yvalues, yf⟩ := exec_tileStep hw hm X Y qs old z + hz.fixed rfl ht rfl hepoch hz.groups hz.catalogue hvalues hoffsets hz.marks hX hY hmag hfit + have ha := (hz.groups.offsets _ ht.le).trans (hoffsets _ ht.le) + have hb := (hz.groups.offsets _ (Nat.succ_le_of_lt ht)).trans + (hoffsets _ (Nat.succ_le_of_lt ht)) + have hdiff : z.mem (offsetBase L m N qs.length + z.regs index + 1) - + z.mem (offsetBase L m N qs.length + z.regs index) = + Nat.count (fun k => key k = z.regs index) qs.length := by + rw [show offsetBase L m N qs.length + z.regs index + 1 = + offsetBase L m N qs.length + (z.regs index + 1) by omega, hb, ha, + CountingSort.count_lt_succ] + dsimp only [key] + omega + rw [hdiff] at hr + refine ⟨y, _, hr, ?_, ?_⟩ + · refine ⟨yp, by rw [yi]; omega, ?_, yg, ye, ym, ?_, ?_⟩ + · rw [yi, Nat.add_mul, Nat.one_mul] + have h := hz.epoch_le + omega + · intro i hik + change key i.val < y.regs index at hik + by_cases hki : key i.val = z.regs index + · exact yvalues i hki + · have hold : key i.val < z.regs index := by rw [yi] at hik; omega + have hs := (CountingSort.position_mem key i.isLt).2 + have hmcount : Nat.count (fun k => key k < key i.val + 1) qs.length ≤ + Nat.count (fun k => key k < z.regs index) qs.length := + Nat.count_mono_left (fun k _ hk => by omega) + have hpos := CountingSort.position_lt key i.isLt + dsimp only [key] at hs hmcount hpos + rw [yf _ (by omega) (by omega)] + exact hz.answers i hold + · intro p ho hf + have hbQ := (hz.groups.span_bounds key hoffsets ht).2 + rw [yf p (by omega) hf] + exact hz.frame p ho hf + · have hsplit := Finset.sum_eq_sum_Ico_succ_bot ht (tileCharge hm qs) + dsimp only [Φ] + rw [yi, hsplit] + dsimp only [tileCharge, key] + omega + obtain ⟨y, hr, hy, hc⟩ := ExecWithin.loop_potential I Φ hbody hstart + have hlast : y.regs index = T := by + have h := hy.index_le + simp only [Cond.holds_lt, hy.fixed.tiles] at hc + dsimp only [T] at * + omega + refine ⟨y, ?_, hy.fixed, hy.groups, hy.catalogue, ?_, hy.frame⟩ + · simpa only [Φ, hi, Nat.Ico_zero_eq_range, T, evaluateTiles] using hr + · intro i + apply hy.answers i + rw [hlast] + have hk := Wanted.tileKey_lt hm (Wanted.inputPairs qs i.val) + (Wanted.inputPairs_lt qs i i.isLt).1 (Wanted.inputPairs_lt qs i i.isLt).2 + exact hk + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Width.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Width.lean new file mode 100755 index 000000000..b21437d12 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Width.lean @@ -0,0 +1,80 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Flat +public import Mathlib.Data.Nat.Log + +/-! +# Positive original-input width admission for native queried products + +## Main results + +* `QueriedProduct.admissionWidth`: sufficient physical width, derived only from original scalar + dimensions, entry magnitude and request count. +* `QueriedProduct.runs_wide`: actual initialized product execution at every sufficiently wide + admitted width, with guaranteed input fit and no physical-width reads by the code. + +The logarithm applies to the explicit original-input magnitude/address envelope. Relating it +to a polynomially bounded whole-3SUM input remains separate composition work under #1394. +-/ + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- An original-input-derived sufficient physical width, including the existing two-bit +native signed-kernel margin. It is not read by the native program. -/ +@[expose] public def admissionWidth (L m N Q A : ℕ) : ℕ := + Nat.clog 2 (magnitudeBound L A + wordBudget L m N Q + 1) + 3 + +/-- The physical threshold is strictly positive even for empty and zero-magnitude inputs. -/ +public theorem admissionWidth_pos (L m N Q A : ℕ) : 0 < admissionWidth L m N Q A := by + unfold admissionWidth + omega + +/-- Mathlib's ceiling logarithm supplies both numerical admission hypotheses. -/ +public theorem admission_bounds {W L m N Q A : ℕ} (hW : admissionWidth L m N Q A ≤ W) : + 0 < W - 2 ∧ magnitudeBound L A < 2 ^ ((W - 2) - 1) ∧ Fits W (wordBudget L m N Q) := by + have hl := Nat.le_pow_clog (by decide : 1 < (2 : ℕ)) + (magnitudeBound L A + wordBudget L m N Q + 1) + have he : Nat.clog 2 (magnitudeBound L A + wordBudget L m N Q + 1) ≤ (W - 2) - 1 := by + unfold admissionWidth at hW + omega + have hp := Nat.pow_le_pow_right (by decide : 1 ≤ (2 : ℕ)) he + refine ⟨by unfold admissionWidth at hW; omega, by omega, ?_⟩ + change wordBudget L m N Q < 2 ^ W + have hp' := Nat.pow_le_pow_right (by decide : 1 ≤ (2 : ℕ)) (by omega : + Nat.clog 2 (magnitudeBound L A + wordBudget L m N Q + 1) ≤ W) + omega + +/-- Both real input modes run at every sufficiently wide physical width. All product values, +the charged halt and complete restored-input/outside frame refer to the same actual run. -/ +public theorem runs_wide {W L m N A : ℕ} (hm : m ≤ L) (bits : Bool) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (hX : ∀ i k, |X i k| ≤ (A : ℤ)) + (hY : ∀ k j, |Y k j| ≤ (A : ℤ)) + (hbits : bits = true → (∀ i k, 0 ≤ X i k) ∧ (∀ k j, 0 ≤ Y k j)) + (hW : admissionWidth L m N qs.length A ≤ W) : + InputFits W (input bits L X Y qs) ∧ + ∃ y n, n ≤ timeBound hm qs ∧ + Runs W ((program bits).program output) (State.init (input bits L X Y qs)) + ⟨(program bits).size, y⟩ n ∧ + Executes W ((program bits).program output) (State.init (input bits L X Y qs)) + (outputBase L m N qs.length) (n + 1) ∧ y.Bounded W ∧ + (∀ i : Fin qs.length, y.mem (outputBase L m N qs.length + i.val) = + Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2)) ∧ + (∀ a, ¬(inputEnd m N qs.length ≤ a ∧ a < memoryEnd L m N qs.length) → + y.mem a = (State.init (input bits L X Y qs) : State 160).data.mem a) := by + obtain ⟨hw, hmagnitude, haddress⟩ := admission_bounds hW + have htwo : W - 2 + 2 = W := by unfold admissionWidth at hW; omega + have h := runs hw hm bits X Y qs hX hY hbits hmagnitude (by simpa only [htwo] using haddress) + simpa only [htwo] using h + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Groups.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Groups.lean index fcbfc7d29..9e289791b 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Groups.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Wanted/Groups.lean @@ -31,7 +31,8 @@ open Cslib.WordRAM open Cslib.Algorithms.MatrixMultiplication.Schonhage open Finset -private theorem tileKey_eq_iff {L m N : ℕ} (hm : m ≤ L) (i j : Fin N) (T : ℕ × ℕ) +/-- An in-range tile key identifies exactly the original row and column bands. -/ +theorem tileKey_eq_iff {L m N : ℕ} (hm : m ≤ L) (i j : Fin N) (T : ℕ × ℕ) (hT : T ∈ tiles L m N) : tileKey L m N (i.val, j.val) = T.1 * numBands L m N + T.2 ↔ bandOf L m i.val = T.1 ∧ bandOf L m j.val = T.2 := by diff --git a/ComplexityTests/SchonhageProduct.lean b/ComplexityTests/SchonhageProduct.lean index a91940a62..f191ea7c0 100644 --- a/ComplexityTests/SchonhageProduct.lean +++ b/ComplexityTests/SchonhageProduct.lean @@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Erik Demaine -/ -import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.Input +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product -- Actual native interpreter regressions supplement the general execution proofs. set_option linter.hashCommand false @@ -47,4 +47,117 @@ private def observe {m N : ℕ} (bits : Bool) (L : ℕ) (fun i k => (i.val : ℤ) + k.val - 1) (fun k j => (k.val : ℤ) + j.val - 2) [(1, 0), (0, 1), (1, 1), (1, 0)] == some ([2, 6, 8, 2], true) +-- Empty matrices also admit positive tensor depth without any tile division. +#guard observe false 1 (m := 1) (N := 0) (fun _ _ => 0) (fun _ _ => 0) [] == + some ([], true) + +private def signedX : Matrix (Fin 1) (Fin (D 0)) ℤ := fun _ _ => -3 +private def signedY : Matrix (Fin (D 0)) (Fin 1) ℤ := fun _ _ => 2 +private def repeated : List (Fin 1 × Fin 1) := [(0, 0), (0, 0), (0, 0)] + +-- Kernel-checked execution clients use the same final memory for prefix Runs, halt and values. +example : + InputFits (admissionWidth 0 0 1 3 3) (input false 0 signedX signedY repeated) ∧ + ∃ y n, n ≤ timeBound (by decide : 0 ≤ 0) repeated ∧ + Runs (admissionWidth 0 0 1 3 3) ((program false).program output) + (State.init (input false 0 signedX signedY repeated)) ⟨(program false).size, y⟩ n ∧ + Executes (admissionWidth 0 0 1 3 3) ((program false).program output) + (State.init (input false 0 signedX signedY repeated)) (outputBase 0 0 1 3) (n + 1) ∧ + (∀ i : Fin 3, y.mem (outputBase 0 0 1 3 + i.val) = encode (-6)) := by + obtain ⟨hi, y, n, hn, hr, he, _, hv, _⟩ := runs_wide (A := 3) (by decide : 0 ≤ 0) + false signedX signedY repeated (by intro i k; norm_num [signedX]) + (by intro k j; norm_num [signedY]) (by simp) le_rfl + refine ⟨hi, y, n, hn, hr, he, ?_⟩ + intro i + have h := hv i + fin_cases i <;> norm_num [signedX, signedY, repeated, Matrix.mul_apply, D, + Fin.sum_univ_succ] at h ⊢ <;> exact h + +private def thinN (m : ℕ) : ℕ := D m ^ 18 +private def thinIndex (m : ℕ) : Fin (thinN m) := + ⟨0, Nat.pow_pos (Nat.pow_pos (by decide))⟩ +private def thinQuery (m : ℕ) : List (Fin (thinN m) × Fin (thinN m)) := + [(thinIndex m, thinIndex m)] + +-- A nonempty, fully admitted thin family exists at every inner depth. The input contains +-- literal matrix words and a raw coordinate; the symbolic client does not execute a huge VM run. +example (m : ℕ) : + InputFits (128 * Nat.clog 2 (thinN m + 3)) + (input (m := m) true (19 * m) (fun _ _ => (0 : ℤ)) (fun _ _ => (0 : ℤ)) (thinQuery m)) ∧ + ∃ y n, + Runs (128 * Nat.clog 2 (thinN m + 3)) ((program true).program output) + (State.init (input (m := m) true (19 * m) (fun _ _ => (0 : ℤ)) (fun _ _ => (0 : ℤ)) + (thinQuery m))) ⟨(program true).size, y⟩ n ∧ + Executes (128 * Nat.clog 2 (thinN m + 3)) ((program true).program output) + (State.init (input (m := m) true (19 * m) (fun _ _ => (0 : ℤ)) (fun _ _ => (0 : ℤ)) + (thinQuery m))) (outputBase (19 * m) m (thinN m) 1) (n + 1) ∧ + ((n + 1 : ℕ) : ℝ) ≤ 4325376 * ((19 * m + 1 : ℕ) : ℝ) * (thinN m : ℝ) ^ 2 / + (D m : ℝ) ^ (1 / 18 : ℝ) ∧ y.mem (outputBase (19 * m) m (thinN m) 1) = 0 := by + have hQ : (thinQuery m).length * 2 ^ m ≤ thinN m ^ 2 := by + calc (thinQuery m).length * 2 ^ m = 2 ^ m := by simp [thinQuery] + _ ≤ D m := Nat.pow_le_pow_left (by decide : 2 ≤ 4) m + _ ≤ (D m) ^ 36 := Nat.le_self_pow (by decide : 36 ≠ 0) _ + _ = thinN m ^ 2 := by norm_num [thinN, ← pow_mul] + obtain ⟨hi, y, n, hr, he, hn, _, hv, _⟩ := runs_thin + (W := 128 * Nat.clog 2 (thinN m + 3)) (m := m) (A := 0) true + (fun _ _ => (0 : ℤ)) (fun _ _ => (0 : ℤ)) (thinQuery m) le_rfl hQ + (by simp) (by simp) (by simp) (by simp [thinQuery, Nat.add_assoc]) + refine ⟨hi, y, n, hr, he, hn, ?_⟩ + have h := hv ⟨0, by simp [thinQuery]⟩ + change y.mem (outputBase (19 * m) m (thinN m) 1) = encode + (((0 : Matrix (Fin (thinN m)) (Fin (D m)) ℤ) * + (0 : Matrix (Fin (D m)) (Fin (thinN m)) ℤ)) (thinIndex m) (thinIndex m)) at h + simpa only [Matrix.zero_mul, Matrix.zero_apply, encode, Int.natAbs_zero, + Int.lt_irrefl, ↓reduceIte, Nat.mul_zero, Nat.zero_add] using h + +-- The sufficient numerical budget has a strict boundary; this is not a necessary runtime width. +example : ¬Fits 29 (wordBudget 0 0 1 3) ∧ Fits 30 (wordBudget 0 0 1 3) := by + decide +kernel + +-- Repeated requests satisfy the distinct-set condition without satisfying occurrence sparsity. +example : repeated.toFinset.card * 2 ^ 0 ≤ 1 ^ 2 ∧ ¬repeated.length * 2 ^ 0 ≤ 1 ^ 2 := by + decide +kernel + +-- Negative integers require signed mode: raw input deliberately represents nonnegative values. +#guard observe false 0 (m := 0) (N := 1) (fun _ _ => -1) (fun _ _ => 2) + [(0, 0)] == some ([-2], true) +#guard observe true 0 (m := 0) (N := 1) (fun _ _ => -1) (fun _ _ => 2) + [(0, 0)] == some ([0], true) + +private def framedSigned : Data 160 := + ((State.init (input false 0 signedX signedY repeated) : State 160).data.write 0 77).write + (memoryEnd 0 0 1 3) 91 + +-- A real call accepts an unrelated length/header word and preserves a sentinel exactly at +-- the allocation boundary, while computing the signed products and restoring the whole input. +example : ∃ y : Data 160, ∃ n, n ≤ timeBound (by decide : 0 ≤ 0) repeated ∧ + Runs 34 ((program false).program output) ⟨0, framedSigned⟩ + ⟨(program false).size, y⟩ n ∧ + Executes 34 ((program false).program output) ⟨0, framedSigned⟩ + (outputBase 0 0 1 3) (n + 1) ∧ + y.mem 0 = 77 ∧ y.mem (memoryEnd 0 0 1 3) = 91 ∧ + y.mem (outputBase 0 0 1 3) = encode (-6) := by + obtain ⟨y, ⟨n, hn, he⟩, hp, hv, hf⟩ := exec (w := 32) (A := 3) (by decide) + (by decide : 0 ≤ 0) false signedX signedY repeated framedSigned + (by + intro a ha hb + have : a < 13 := by norm_num [inputEnd, repeated, D] at hb ⊢; exact hb + interval_cases a <;> decide +kernel) + (by intro i k; norm_num [signedX]) (by intro k j; norm_num [signedY]) + (by simp) (by decide +kernel) (by decide +kernel) + change Parameters 0 0 1 3 y at hp + change ∀ a, ¬(inputEnd 0 1 3 ≤ a ∧ a < memoryEnd 0 0 1 3) → + y.mem a = framedSigned.mem a at hf + refine ⟨y, n, hn, ?_, ?_, ?_, ?_, ?_⟩ + · simpa only [Nat.zero_add] using he.runs (pc := 0) + (p := (program false).program output) (Program.Contains.self _).append_left + · simpa only [hp.output] using he.executes output + · rw [hf 0 (by decide +kernel)] + decide +kernel + · rw [hf (memoryEnd 0 0 1 3) (by omega)] + rfl + · have h := hv ⟨0, by decide⟩ + norm_num [signedX, signedY, repeated, Matrix.mul_apply, D, Fin.sum_univ_succ] at h ⊢ + exact h + end ComplexityTests.SchonhageProduct diff --git a/ROADMAP.md b/ROADMAP.md index d71504325..d68c1045a 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -447,8 +447,14 @@ each statement against the numbered result. Organize modules by subject, not cha original raw coordinates. Its initialized run preserves every original occurrence and gives each tile exactly its finite `wantedStrings` code set, within `128*(L+1)*(Q+numBands²+2^L+1)` instructions including halt. It uses fixed 96-register code and - an explicit positive sufficient-width threshold. Joining these groups to shared band encoders, - invoking the pruned kernel and reconstructing queried products remain separate native work. + an explicit positive sufficient-width threshold. The native queried-product composition + ([#1581]) joins the actual router and band producers to the pointer/epoch kernel, restores the + original input, and reconstructs every requested signed product in original order. It charges + raw-entry conversion, all copies, dispatch, one shared directory initialization, epochs and + reporting. At `L=19m`, `D=4^m`, `D^18≤N` and occurrence sparsity `Q*2^m≤N²`, its initialized + fixed 160-register program uses at most `4325376*(19m+1)*N²/D^(1/18)` instructions including + halt, with sufficient width `128*clog₂(N+Q+A+2)` for entry bound `A`. The distinct-set sparsity + theorem instead retains an explicit additive charge for all `Q` occurrences. Native Chan–He preprocessing and the reduction host are separate stack components. Native sparse-product and Exact Triangle solving, full input/time/width/asymptotic composition, and the improved Section 4 route to exponent 1.9992 remain under #1394; @@ -586,3 +592,4 @@ not planned, not as proved. [#1523]: https://github.com/cslib-community/complexitylib/issues/1523 [#1526]: https://github.com/cslib-community/complexitylib/issues/1526 [#1561]: https://github.com/cslib-community/complexitylib/issues/1561 +[#1581]: https://github.com/cslib-community/complexitylib/issues/1581 diff --git a/UPSTREAM.md b/UPSTREAM.md index 77848f767..ca5ca7076 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -260,11 +260,26 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he charges at most `128*(L+1)*(Q+numBands²+2^L+1)` instructions including halt. Its positive ceiling-logarithmic admission threshold depends only on the original numerical budget. Within-tile order is stable input order; no sorted or duplicate-free query premise is needed - by the pruned kernel. Shared-band/kernel calls, answer reconstruction, sparse-product/Exact - Triangle solving and full input/time/width composition, including the improved Section 4 route - to exponent 1.9992, remain in #1394. The band catalogue retains the same leaf ordering and - canonical signed words. Raw 0/1 matrix producers must - actually double entries before copying into signed input arrays, with that work charged. + by the pruned kernel. + Native queried-product composition ([#1581](https://github.com/cslib-community/complexitylib/issues/1581)) + now connects those actual programs. It computes all protected buffers and tile arguments, + calls the shared encoders and pruned pointer/epoch kernel, and reconstructs every requested + `Matrix.mul` value in original order, retaining duplicates. Original signed matrices and raw + coordinate pairs are the entire initialized input. Raw nonnegative mode actually doubles + every matrix word before signed encoding, covering the raw 0/1 producer ABI. Copies, + header adaptation, one directory initialization, dispatch, epochs, reporting and restoration + are all charged. Existing `MaskTable.layout`, `wantedStrings`, `encodingL/R`, leaf numbering, + `Pruned.result` and stable-position meanings are reused; the existing `Wanted.tileKey_eq_iff` + lemma is exposed for the concrete tile-correctness proof without changing its statement. + The fixed 160-register program has at most 32768 flat instructions including halt and reads + no physical-width instruction. At `L=19m`, `D=4^m`, `D^18≤N`, `Q*2^m≤N²`, the complete charge + including halt is `4325376*(19m+1)*N²/D^(1/18)`. Distinct-set sparsity retains an additive + occurrence charge instead. The sufficient physical width is `128*clog₂(N+Q+A+2)`; its + magnitude/address budget and allocation have explicit polynomial envelopes in the original + dimensions, request count and entry bound. These are original native execution/resource + proofs, not a transfer of the source Light semantics or instruction constants. Exact Triangle + parameter/instance preparation and outer solving, full ordinary-3SUM composition, and the + improved Section 4 route to exponent 1.9992 remain separate work under #1394. Stage `exec_*` lemmas are internal implementation APIs exported across proof modules; callers use the flat initialized runs, width theorem or pointer/epoch kernel. The narrow `WordRAM.ArrayAppend` and `WordRAM.InputArray` topic modules keep append/input dependencies From ff31104be7acf96798c13b38cb079c58605a1ec3 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 29 Oct 2026 05:30:46 -0400 Subject: [PATCH 53/73] feat(wordram): execute queried thin-matrix products (#1581) Complete #1581 implementation candidate under #1394, awaiting required fresh exact-head non-authoring correctness/API/source/resource review. No PR is opened or integration approval claimed. Normal child history preserves source checkpoint af208636b and actual reviewed #1576 supplier91f485ec; no ancestor branch or remote was edited. One actual fixed standard unsigned WordRAM program per signed/raw input mode consumes original headers [L,m,N,Q], both row-major integer matrices X:N x 4^m and Y:4^m x N, and raw interleaved requested coordinates. It computes tables/layout, shared encodings, Wanted counts/prefix/stable scatter, tile pointers, decoded queries, directory/epochs, pruned evaluation and original-order reconstruction. Raw nonnegative entries are doubled by actual charged load/multiply/store instructions (including raw0/1 -> signed0/2). There is no source Light machine, assumed layout, sorting, solver, run or time oracle. QueriedProduct.exec/runs/runs_wide prove actual native execution from original words / State.init, same-y prefix Runs and charged halt, every original occurrence's exact Signed.encode((X*Y)[qs[i]]) including duplicates, original-input restoration and the complete outside frame. Generic execution assumes m<=L, original absolute entry bound A, raw nonnegativity in raw mode, and the explicit original numerical magnitude/address admission; it has NO query-sparsity hypothesis. The real pointer/epoch kernel uses a directory initialized once, with scratch/frame separation and propagated fresh bounded epochs. Code has 160 registers, <=32768 flat instructions including halt, no width reads, and is independent of dimensions, magnitudes, query counts and physical widths. Thin hypotheses: L=19m, D=4^m, N>=D^18. With distinct-set sparsity |qs.toFinset|*2^m<=N^2, complete charge including halt is <= 65536*(19m+1)*(65*N^2/D^(1/18)+Q). With the stronger occurrence hypothesis Q*2^m<=N^2 it is <=4325376*(19m+1)*N^2/D^(1/18). Multiplicity/output work is never replaced by set cardinality. runs_thin holds at EVERY physical W>= 128*clog_2(N+Q+A+2), deriving InputFits and all intermediate admissions. Its explicit magnitude/address envelope is <=2^40*(N+Q+A+2)^10, allocation <=128*(N+Q+A+2)^4. Physical all-prefix boundedness is proved; a separate source-polynomial prefix bound at arbitrarily larger widths is not extracted here. ET record/input adaptation and all its copies/calls, Exact Triangle solving, ordinary3SUM composition and improved Section4 / 1.9992 remain open. This component proves neither full3SUM exponent. Source mapping checked against actual Git blobs at anthropics/formal-math 16a5ce41304fcdaea4e0be7c132e1c361dc26da5, under3sum-apsp/ThreeSumApsp: - Sec2/Theorem5.lean:61-67 concludes the pruned band value equals (X*Y) I J. Existing finite thin_product_spec/MaskTable.layout/bandArrayL/R/encodingL/R and wantedStrings are reused; W=qs.toFinset, with a separate occurrence permutation. - Sec2/Theorem5.lean:146-149, paraphrase of its conclusion: ((sum T in tiles (19*m) m N, |Leaves(wantedStrings lay W T.1 T.2)|):Real) <=33*(D m)^(-(1/18):Real)*N^2. Source hypotheses N>=D^18 and W.card*2^m<=N^2 are retained. Existing encoding, tile/leaf/passed-set estimates bound ACTUAL native execution, not an assumed cost. - Programs/Sec2/Theorem5/Pruned/AllTiles.lean:86-92 and Wanted/Report.lean:30-37 are the per-tile/report specification references; Solver.lean:150-161 is conditional on Light callees. Native code here is original, accepts stable unsorted duplicates, charges empty tile calls and proves all native callees directly. Instruction constants, widths, protected layout and frames are independently proved for the native model. The UTF-8 PR mapping draft contains two exact Lean quotation slices, both checked against Sec2/Theorem5 at that pin; all four cited source files equal their pinned Git blobs. Anthropic, PBC's existing mathematical source credit is preserved. New native programs and execution/resource proofs are original work by Erik Demaine with Codex assistance. FINAL VALIDATION (whole source tree, not only this commit's proof/doc delta): Tree 555b6b52141f6e13f9f4aa913d6e7f7e5f8db474; four compiler threads; cache downloads disabled. 1. lake --no-cache build --wfail --iofail : PASS (13.56s). 2. lake --no-cache exe mk_all --check : PASS (11.33s). 3. lake --no-cache test --wfail --iofail : PASS (27.14s). 4. lake --no-cache lint : PASS (43.22s). 5. lake --no-cache exe lint-style : PASS (17.09s). 6. lake --no-cache exe checkInitImports : PASS (16.8s). Strict tests cover new initialized/product clients, Smoke and global Axioms. Full citation-aware hygiene --check-simp, fresh actual-base30-library/one-test lints, changed-file checks, 25-name strict final-body audit and staged/unstaged diff checks PASS. Actual comparison base: 91f485ec38ca6b878f338e2cdbdedbf5d4f20257. 3533 inherited source/abstract files checked; only the existing tile-key lemma is exposed with the same statement/proof. All35 inherited sibling blobs equal actual parent91. All native code bodies/register definitions are byte-identical to af208636b; comment-erased Program tokens match. Current full Program source SHA256: 2b86fe04f6afa49a7eadc75292559dca62f4d65c5f7bdaf3b1f4386994aa8ebc Public Header.exec_layout statement is identical; proof split passes at DEFAULT heartbeats, without a new allowlist entry. Final register docs clear the initial lint failure (47 undocumented aliases); prior incomplete/failed checkpoint logs are retained. Checked PR/source mapping draft SHA256: be43f587d5c0ac6dd871093d5d19c283ef7dd9732b5c5d3cfa77b24e42899a24 Local reproducible records: .lake/codex-three-sum-continuation/queried-product/ summary.json, static-summary.json, source-native-identity.json, source-mapping.json, run-identity.py, final-body-names.log and per-gate logs. This commit description is the durable evidence until a PR exists; issue1581 links it. Examples discharge real width/magnitude premises and connect the same final y to prefix Runs, halt and memory: signed/raw, duplicate, padded/multiple-tile, positive/zero depth, empty inputs, nonzero original header, adjacent sentinel, strict budget boundary and nonempty symbolic admitted thin family at every inner depth. Abstract assessment: no selected statement/definition/scope/catalogue/rendering impact. ROADMAP/UPSTREAM preserve remaining1394 scope; no headline promotion. Fresh review of all NEW native semantics/cost/width/frame/init/API/source mapping remains pending root arrangement. Unchanged prerequisite outside coverage can be reused after the recorded identities (1576 outside FINAL6487515809). No child watch/approval/merge. --- .../Schonhage/Product/Header.lean | 260 ++++++++++++------ .../Schonhage/Product/Program.lean | 47 ++++ 2 files changed, 224 insertions(+), 83 deletions(-) diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Header.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Header.lean index 642e50b90..304ea325e 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Header.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Header.lean @@ -27,18 +27,158 @@ namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.QueriedProduct open Cslib.WordRAM open Cslib.Algorithms.MatrixMultiplication.Schonhage -set_option maxHeartbeats 2000000 in --- The straight-line proof symbolically checks fifty register updates and their fits. -/-- Actual native layout calculation, preserving all original memory. -/ -public theorem exec_layout {w L m N Q : ℕ} (x : Data 160) +-- Name the fifty-update state once; separating execution from admission keeps each proof +-- within the default heartbeat limit without changing the actual header code. +private def layoutState (x : Data 160) (L m N Q : ℕ) : Data 160 := + ((((((((((((((((((((((((((((((((((((((((((((((((((x.set depth (L)).set inner (m)).set + rows (N)).set address (4)).set queryCount (Q)).set columns (D m)).set masks (2 ^ L)).set + cells (7 ^ L)).set leaves (10 ^ L)).set bands (numBands L m N)).set catalogue + (BandPreparation.catalogueBase L m N)).set zero (0)).set one (1)).set two (2)).set three + (3)).set four (4)).set five (5)).set six (6)).set tiles ((numBands L m N) * (numBands L m + N))).set matrixWords ((N) * (D m))).set matrixWords (((N) * (D m)) * (2))).set queryOffset + ((((N) * (D m)) * (2)) + (5))).set inputSize ((Q) * (2))).set inputSize (((Q) * (2)) + + ((((N) * (D m)) * (2)) + (5)))).set bandEnd ((numBands L m N) * (10 ^ L))).set bandEnd + (((numBands L m N) * (10 ^ L)) * (2))).set bandEnd ((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N))).set wantedEnd ((Q) * (4))).set wantedEnd (((Q) * + (4)) + (6))).set temporary ((2 ^ L) * (2))).set wantedEnd ((((Q) * (4)) + (6)) + ((2 ^ L) * + (2)))).set temporary (((numBands L m N) * (numBands L m N)) * (3))).set wantedEnd (((((Q) * + (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) * (3)))).set + snapshot (((((numBands L m N) * (10 ^ L)) * (2)) + (BandPreparation.catalogueBase L m N)) + + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) * + (3))))).set snapshot ((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5))))).set offsets (((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5))))).set codes ((((((((numBands L m N) * + (10 ^ L)) * (2)) + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) + * (2))) + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * + (2)) + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * + (numBands L m N)))).set codes (((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1))).set originals ((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q))).set grouped (((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q))).set output ((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q))).set scratch (((((((((((((numBands L m N) * (10 ^ L)) * + (2)) + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q))).set temporary ((L) + (1))).set temporary (((L) + + (1)) * (4))).set stamps ((((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q)) + (((L) + (1)) * (4)))).set temporary ((L) + + (1))).set temporary (((L) + (1)) * (10 ^ L))).set temporary ((((L) + (1)) * (10 ^ L)) * + (2))).set stamps (((((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q)) + (((L) + (1)) * (4))) + ((((L) + (1)) * (10 ^ L)) * + (2)))).set memoryLimit ((((((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q)) + (((L) + (1)) * (4))) + ((((L) + (1)) * (10 ^ L)) * + (2))) + (10 ^ L))) + +private theorem exec_layout_raw {w L m N Q : ℕ} (x : Data 160) (hp : BandPreparation.BandParameters L m N (x.comap bank)) (hQ : x.mem 4 = Q) - (hfit : Fits w (wordBudget L m N Q)) : - ∃ y, ExecWithin w layout x y 50 ∧ Parameters L m N Q y ∧ y.mem = x.mem := by - have hl := caller_fits hfit - have h2 : 2 ^ L ≤ 10 ^ L := Nat.pow_le_pow_left (by decide) _ - have h7 : 7 ^ L ≤ 10 ^ L := Nat.pow_le_pow_left (by decide) _ - have hsmall (v : ℕ) (hv : v ≤ 16 * (L + N + Q + memoryEnd L m N Q + 16)) : - Fits w v := hl.of_le hv + (hf0 : Fits w (4)) + (hf1 : Fits w (0)) + (hf2 : Fits w (1)) + (hf3 : Fits w (2)) + (hf4 : Fits w (3)) + (hf5 : Fits w (5)) + (hf6 : Fits w (6)) + (hf7 : Fits w ((numBands L m N) * (numBands L m N))) + (hf8 : Fits w ((N) * (D m))) + (hf9 : Fits w (((N) * (D m)) * (2))) + (hf10 : Fits w ((((N) * (D m)) * (2)) + (5))) + (hf11 : Fits w ((Q) * (2))) + (hf12 : Fits w (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + (hf13 : Fits w ((numBands L m N) * (10 ^ L))) + (hf14 : Fits w (((numBands L m N) * (10 ^ L)) * (2))) + (hf15 : Fits w ((((numBands L m N) * (10 ^ L)) * (2)) + (BandPreparation.catalogueBase L m + N))) + (hf16 : Fits w ((Q) * (4))) + (hf17 : Fits w (((Q) * (4)) + (6))) + (hf18 : Fits w ((2 ^ L) * (2))) + (hf19 : Fits w ((((Q) * (4)) + (6)) + ((2 ^ L) * (2)))) + (hf20 : Fits w (((numBands L m N) * (numBands L m N)) * (3))) + (hf21 : Fits w (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m + N)) * (3)))) + (hf22 : Fits w (((((numBands L m N) * (10 ^ L)) * (2)) + (BandPreparation.catalogueBase L m + N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) * + (3))))) + (hf23 : Fits w ((((((numBands L m N) * (10 ^ L)) * (2)) + (BandPreparation.catalogueBase L m + N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) * + (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5))))) + (hf24 : Fits w (((((((numBands L m N) * (10 ^ L)) * (2)) + (BandPreparation.catalogueBase L + m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) * + (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * + (2)) + (5))))) + (hf25 : Fits w ((((((((numBands L m N) * (10 ^ L)) * (2)) + (BandPreparation.catalogueBase L + m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) * + (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * + (2)) + (5)))) + ((numBands L m N) * (numBands L m N)))) + (hf26 : Fits w (((((((((numBands L m N) * (10 ^ L)) * (2)) + (BandPreparation.catalogueBase + L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) + * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * + (2)) + (5)))) + ((numBands L m N) * (numBands L m N))) + (1))) + (hf27 : Fits w ((((((((((numBands L m N) * (10 ^ L)) * (2)) + (BandPreparation.catalogueBase + L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) + * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * + (2)) + (5)))) + ((numBands L m N) * (numBands L m N))) + (1)) + (Q))) + (hf28 : Fits w (((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q))) + (hf29 : Fits w ((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q))) + (hf30 : Fits w (((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q))) + (hf31 : Fits w ((L) + (1))) + (hf32 : Fits w (((L) + (1)) * (4))) + (hf33 : Fits w ((((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q)) + (((L) + (1)) * (4)))) + (hf34 : Fits w (((L) + (1)) * (10 ^ L))) + (hf35 : Fits w ((((L) + (1)) * (10 ^ L)) * (2))) + (hf36 : Fits w (((((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q)) + (((L) + (1)) * (4))) + ((((L) + (1)) * (10 ^ L)) * + (2)))) + (hf37 : Fits w ((((((((((((((((numBands L m N) * (10 ^ L)) * (2)) + + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m + N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q)) + (((L) + (1)) * (4))) + ((((L) + (1)) * (10 ^ L)) * + (2))) + (10 ^ L))) : + ExecWithin w layout x (layoutState x L m N Q) 50 := by have hb0 : x.regs (bank BandPreparation.levels) = L := hp.fixed.levels have hb1 : x.regs (bank BandPreparation.inner) = m := hp.fixed.inner have hb2 : x.regs (bank BandPreparation.rows) = N := hp.fixed.rows @@ -49,6 +189,25 @@ public theorem exec_layout {w L m N Q : ℕ} (x : Data 160) have hb7 : x.regs (bank BandPreparation.bands) = numBands L m N := hp.bands have hb8 : x.regs (bank BandPreparation.catalogue) = BandPreparation.catalogueBase L m N := hp.catalogue + ram_eval [layout, layoutState, hQ, hf0, hf1, hf2, hf3, hf4, hf5, hf6, hf7, hf8, hf9, hf10, hf11, + hf12, + hf13, hf14, hf15, hf16, hf17, hf18, hf19, hf20, hf21, hf22, hf23, hf24, hf25, hf26, hf27, + hf28, hf29, hf30, hf31, hf32, hf33, hf34, hf35, hf36, hf37, depth, inner, rows, address, + queryCount, columns, masks, cells, leaves, bands, catalogue, zero, one, two, three, four, + five, six, tiles, matrixWords, queryOffset, inputSize, bandEnd, wantedEnd, temporary, + snapshot, offsets, codes, originals, grouped, output, scratch, stamps, memoryLimit, hb0, + hb1, hb2, hb3, hb4, hb5, hb6, hb7, hb8] + +/-- Actual native layout calculation, preserving all original memory. -/ +public theorem exec_layout {w L m N Q : ℕ} (x : Data 160) + (hp : BandPreparation.BandParameters L m N (x.comap bank)) (hQ : x.mem 4 = Q) + (hfit : Fits w (wordBudget L m N Q)) : + ∃ y, ExecWithin w layout x y 50 ∧ Parameters L m N Q y ∧ y.mem = x.mem := by + have hl := caller_fits hfit + have h2 : 2 ^ L ≤ 10 ^ L := Nat.pow_le_pow_left (by decide) _ + have h7 : 7 ^ L ≤ 10 ^ L := Nat.pow_le_pow_left (by decide) _ + have hsmall (v : ℕ) (hv : v ≤ 16 * (L + N + Q + memoryEnd L m N Q + 16)) : + Fits w v := hl.of_le hv have hf0 : Fits w (4) := hsmall _ (by simp only [memoryEnd, stampBase, scratchBase, outputBase, groupedBase, originalBase, codeBase, offsetBase, snapshotBase, inputEnd, @@ -321,80 +480,15 @@ public theorem exec_layout {w L m N Q : ℕ} (x : Data 160) BandPreparation.memoryEnd, Wanted.memoryLimit_eq] ring_nf omega) - let y := ((((((((((((((((((((((((((((((((((((((((((((((((((x.set depth (L)).set inner (m)).set - rows (N)).set address (4)).set queryCount (Q)).set columns (D m)).set masks (2 ^ L)).set - cells (7 ^ L)).set leaves (10 ^ L)).set bands (numBands L m N)).set catalogue - (BandPreparation.catalogueBase L m N)).set zero (0)).set one (1)).set two (2)).set three - (3)).set four (4)).set five (5)).set six (6)).set tiles ((numBands L m N) * (numBands L m - N))).set matrixWords ((N) * (D m))).set matrixWords (((N) * (D m)) * (2))).set queryOffset - ((((N) * (D m)) * (2)) + (5))).set inputSize ((Q) * (2))).set inputSize (((Q) * (2)) + - ((((N) * (D m)) * (2)) + (5)))).set bandEnd ((numBands L m N) * (10 ^ L))).set bandEnd - (((numBands L m N) * (10 ^ L)) * (2))).set bandEnd ((((numBands L m N) * (10 ^ L)) * (2)) + - (BandPreparation.catalogueBase L m N))).set wantedEnd ((Q) * (4))).set wantedEnd (((Q) * - (4)) + (6))).set temporary ((2 ^ L) * (2))).set wantedEnd ((((Q) * (4)) + (6)) + ((2 ^ L) * - (2)))).set temporary (((numBands L m N) * (numBands L m N)) * (3))).set wantedEnd (((((Q) * - (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) * (3)))).set - snapshot (((((numBands L m N) * (10 ^ L)) * (2)) + (BandPreparation.catalogueBase L m N)) + - (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + (((numBands L m N) * (numBands L m N)) * - (3))))).set snapshot ((((((numBands L m N) * (10 ^ L)) * (2)) + - (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + - (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + - (5))))).set offsets (((((((numBands L m N) * (10 ^ L)) * (2)) + - (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + - (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + - (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5))))).set codes ((((((((numBands L m N) * - (10 ^ L)) * (2)) + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) - * (2))) + (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * - (2)) + (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * - (numBands L m N)))).set codes (((((((((numBands L m N) * (10 ^ L)) * (2)) + - (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + - (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + - (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m - N))) + (1))).set originals ((((((((((numBands L m N) * (10 ^ L)) * (2)) + - (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + - (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + - (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m - N))) + (1)) + (Q))).set grouped (((((((((((numBands L m N) * (10 ^ L)) * (2)) + - (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + - (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + - (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m - N))) + (1)) + (Q)) + (Q))).set output ((((((((((((numBands L m N) * (10 ^ L)) * (2)) + - (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + - (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + - (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m - N))) + (1)) + (Q)) + (Q)) + (Q))).set scratch (((((((((((((numBands L m N) * (10 ^ L)) * - (2)) + (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + - (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + - (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m - N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q))).set temporary ((L) + (1))).set temporary (((L) + - (1)) * (4))).set stamps ((((((((((((((numBands L m N) * (10 ^ L)) * (2)) + - (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + - (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + - (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m - N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q)) + (((L) + (1)) * (4)))).set temporary ((L) + - (1))).set temporary (((L) + (1)) * (10 ^ L))).set temporary ((((L) + (1)) * (10 ^ L)) * - (2))).set stamps (((((((((((((((numBands L m N) * (10 ^ L)) * (2)) + - (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + - (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + - (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m - N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q)) + (((L) + (1)) * (4))) + ((((L) + (1)) * (10 ^ L)) * - (2)))).set memoryLimit ((((((((((((((((numBands L m N) * (10 ^ L)) * (2)) + - (BandPreparation.catalogueBase L m N)) + (((((Q) * (4)) + (6)) + ((2 ^ L) * (2))) + - (((numBands L m N) * (numBands L m N)) * (3)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + - (5)))) + (((Q) * (2)) + ((((N) * (D m)) * (2)) + (5)))) + ((numBands L m N) * (numBands L m - N))) + (1)) + (Q)) + (Q)) + (Q)) + (Q)) + (((L) + (1)) * (4))) + ((((L) + (1)) * (10 ^ L)) * - (2))) + (10 ^ L))) - have he : ExecWithin w layout x y 50 := by - ram_eval [layout, y, hQ, hf0, hf1, hf2, hf3, hf4, hf5, hf6, hf7, hf8, hf9, hf10, hf11, hf12, - hf13, hf14, hf15, hf16, hf17, hf18, hf19, hf20, hf21, hf22, hf23, hf24, hf25, hf26, hf27, - hf28, hf29, hf30, hf31, hf32, hf33, hf34, hf35, hf36, hf37, depth, inner, rows, address, - queryCount, columns, masks, cells, leaves, bands, catalogue, zero, one, two, three, four, - five, six, tiles, matrixWords, queryOffset, inputSize, bandEnd, wantedEnd, temporary, - snapshot, offsets, codes, originals, grouped, output, scratch, stamps, memoryLimit, hb0, - hb1, hb2, hb3, hb4, hb5, hb6, hb7, hb8] + let y := layoutState x L m N Q + have he : ExecWithin w layout x y 50 := + exec_layout_raw x hp hQ hf0 hf1 hf2 hf3 hf4 hf5 hf6 hf7 hf8 hf9 hf10 hf11 hf12 hf13 hf14 hf15 + hf16 hf17 hf18 hf19 hf20 hf21 hf22 hf23 hf24 hf25 hf26 hf27 hf28 hf29 hf30 hf31 hf32 hf33 + hf34 hf35 hf36 hf37 refine ⟨y, he, ?_, rfl⟩ constructor <;> - simp +decide only [y, Data.set_regs, Function.update_apply, depth, inner, rows, address, + simp +decide only [y, layoutState, Data.set_regs, Function.update_apply, depth, inner, rows, + address, queryCount, columns, masks, cells, leaves, bands, catalogue, zero, one, two, three, four, five, six, tiles, matrixWords, queryOffset, inputSize, bandEnd, wantedEnd, temporary, snapshot, offsets, codes, originals, grouped, output, scratch, stamps, memoryLimit, diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Program.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Program.lean index ca66fd622..ed765df7a 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Program.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Program.lean @@ -35,52 +35,99 @@ open Cslib.WordRAM /-- Persistent caller depth. -/ public abbrev depth : Fin 160 := 96 +/-- Original inner depth, determining the shared dimension `4^m`. -/ public abbrev inner : Fin 160 := 97 +/-- Original outer matrix dimension. -/ public abbrev rows : Fin 160 := 98 +/-- Number of original request occurrences, including duplicates. -/ public abbrev queryCount : Fin 160 := 99 +/-- Computed shared dimension `4^m`. -/ public abbrev columns : Fin 160 := 100 +/-- Number of masks visited by the actual subset-table scan. -/ public abbrev masks : Fin 160 := 101 +/-- Number of cells in each padded band array. -/ public abbrev cells : Fin 160 := 102 +/-- Number of leaf entries in each shared encoding. -/ public abbrev leaves : Fin 160 := 103 +/-- Computed number of row bands, also used for column bands. -/ public abbrev bands : Fin 160 := 104 +/-- Number of tile calls, the square of the band count. -/ public abbrev tiles : Fin 160 := 105 +/-- Length of the complete original memory prefix, including word zero. -/ public abbrev inputSize : Fin 160 := 106 +/-- Protected snapshot pointer used to restore the original input. -/ public abbrev snapshot : Fin 160 := 107 +/-- Protected copy of the router's immutable prefix offsets. -/ public abbrev offsets : Fin 160 := 108 +/-- Protected copy of the actual grouped query codes. -/ public abbrev codes : Fin 160 := 109 +/-- Protected permutation from grouped positions to original occurrence indices. -/ public abbrev originals : Fin 160 := 110 +/-- Base of the grouped answers written by tile calls. -/ public abbrev grouped : Fin 160 := 111 +/-- Base of the reconstructed original-order answers, returned by halt. -/ public abbrev output : Fin 160 := 112 +/-- Base of reusable pruned-kernel stack and cache space. -/ public abbrev scratch : Fin 160 := 113 +/-- Base of the timestamp directory shared by all tiles. -/ public abbrev stamps : Fin 160 := 114 +/-- One past the complete caller and kernel allocation. -/ public abbrev memoryLimit : Fin 160 := 115 +/-- Returned kernel epoch propagated to the next tile. -/ public abbrev epoch : Fin 160 := 116 +/-- Current tile, directory-entry or reporting-loop index. -/ public abbrev index : Fin 160 := 117 +/-- First grouped position of the current tile's actual span. -/ public abbrev first : Fin 160 := 118 +/-- One past the last grouped position of the current tile. -/ public abbrev stop : Fin 160 := 119 +/-- Row band computed by division of the current tile key. -/ public abbrev rowBand : Fin 160 := 120 +/-- Column band computed by remainder of the current tile key. -/ public abbrev colBand : Fin 160 := 121 +/-- Actual shared left-encoding pointer for the current tile. -/ public abbrev leftPointer : Fin 160 := 122 +/-- Actual shared right-encoding pointer for the current tile. -/ public abbrev rightPointer : Fin 160 := 123 +/-- Actual grouped-query pointer passed to the kernel. -/ public abbrev queryPointer : Fin 160 := 124 +/-- Current grouped-answer pointer passed to the kernel. -/ public abbrev answerPointer : Fin 160 := 125 +/-- Source pointer supplied to a real native copy. -/ public abbrev source : Fin 160 := 126 +/-- Destination pointer supplied to a real native copy. -/ public abbrev destination : Fin 160 := 127 +/-- Number of words supplied to a real native copy. -/ public abbrev length : Fin 160 := 128 +/-- Temporary address for scalar loads, stores and reporting. -/ public abbrev address : Fin 160 := 129 +/-- Temporary loaded matrix value, answer or original-position index. -/ public abbrev value : Fin 160 := 130 +/-- Persistent unsigned zero. -/ public abbrev zero : Fin 160 := 131 +/-- Persistent loop-increment constant one. -/ public abbrev one : Fin 160 := 132 +/-- Persistent multiplier two for raw-to-signed conversion. -/ public abbrev two : Fin 160 := 133 +/-- Persistent constant four for header offsets and kernel stack sizing. -/ public abbrev four : Fin 160 := 134 +/-- First original matrix-payload address, immediately after the four headers. -/ public abbrev five : Fin 160 := 135 +/-- Persistent constant six used in the computed router allocation. -/ public abbrev six : Fin 160 := 136 +/-- Total word count of both original matrix payloads. -/ public abbrev matrixWords : Fin 160 := 137 +/-- Original coordinate-pair block address. -/ public abbrev queryOffset : Fin 160 := 138 +/-- One past the actual router allocation. -/ public abbrev wantedEnd : Fin 160 := 139 +/-- One past the shared-band producer allocation. -/ public abbrev bandEnd : Fin 160 := 140 +/-- Temporary product used while computing separated buffer addresses. -/ public abbrev temporary : Fin 160 := 141 +/-- Base of the producer's shared two-sided encoding catalogue. -/ public abbrev catalogue : Fin 160 := 145 +/-- Persistent constant three used in the router allocation. -/ public abbrev three : Fin 160 := 146 /-- Compute genuine band scalars before selecting a protected caller area. No memory writes. -/ From 9a57e13469a8138941619711adc0bea0eabdd4a9 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 29 Oct 2026 05:46:12 -0400 Subject: [PATCH 54/73] style(Schonhage): remove trailing space in queried products Independent nonauthoring /root/queried_product_review examined the complete native #1581 layer at ff31104be7acf96798c13b38cb079c58605a1ec3 against 91f485ec38ca6b878f338e2cdbdedbf5d4f20257, including all 29 new library modules, actual execution/frame/width/cost, multiplicities, source mapping and initialized clients. No correctness/API/source/resource blockers; this is agent review within the root fleet, not outside or human approval. The single P3 finding was a trailing space in Product/Groups.lean:111. This coordinator correction removes exactly that ASCII space. An exact byte check and git diff -w verify no other change. Full base-to-head diff --check now passes, covering committed changes as well. All six gates and static/source checks pass on reviewed tree555b6b52, as recorded in ff31104. The whitespace delta is validated separately; final hosted CI and outside coordinator review remain required. Curated abstracts unaffected. AI assistance: Codex. --- .../WordRAM/MatrixMultiplication/Schonhage/Product/Groups.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Groups.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Groups.lean index b97f5bd22..a06d8cd4e 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Groups.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Product/Groups.lean @@ -108,7 +108,7 @@ public theorem exec_saveCodes {w L m N Q : ℕ} (x : Data 160) hb.2.1 have hsep : Wanted.codeAddress L m N Q + (Q) ≤ codeBase L m N Q := by have horder : offsetBase L m N Q ≤ codeBase L m N Q := by - unfold codeBase + unfold codeBase omega omega have hn : Fits w (Q) := allocated_fits hfit (by omega) From 37306efe0204a4f277aca3d4e15d7025928d1cd9 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 29 Oct 2026 05:59:43 -0400 Subject: [PATCH 55/73] doc(Schonhage): retain band catalogue encoding contract Restore the one supplier sentence in UPSTREAM.md about unchanged leaf ordering and canonical signed words, addressing optional nit1 in outside FINAL6493515098 on PR1615. All other paragraphs are preserved. Exact byte comparison against9a57e134 verifies this one sentence is the entire tracked delta; all Lean/native/math/API/abstract source and every other tracked byte are unchanged. Documentation-only validation: consistency with the existing catalogue interfaces, full base-to-head diff --check, citation/simp hygiene and strict25-name final PR-body audit pass. No Lean, import, aggregate or full rebuild was run for prose. Original all-six evidence remains for tree555b6b52141f6e13f9f4aa913d6e7f7e5f8db474; this does not claim a new-head all-six run. No changed abstract meaning or rendering. Full nonauthoring native/source review and outside FINAL coverage are reused across this bounded doc delta. A focused outside DOC delta check is requested, not another full review. Register naming remains a separately coordinated follow-up; existing nonzero interpreter coverage was accepted, so no test change is included. AI assistance: Codex implemented the requested one-sentence documentation repair. --- UPSTREAM.md | 1 + 1 file changed, 1 insertion(+) diff --git a/UPSTREAM.md b/UPSTREAM.md index 2d8be22e4..38c8369dc 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -262,6 +262,7 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he ceiling-logarithmic admission threshold depends only on the original numerical budget. Within-tile order is stable input order; no sorted or duplicate-free query premise is needed by the pruned kernel. + The band catalogue retains the same leaf ordering and canonical signed words. Native queried-product composition ([#1581](https://github.com/cslib-community/complexitylib/issues/1581)) now connects those actual programs. It computes all protected buffers and tile arguments, calls the shared encoders and pruned pointer/epoch kernel, and reconstructs every requested From 70898daa1554fa56d8c3980b6fece0bf9b36d622 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Thu, 29 Oct 2026 06:44:10 -0400 Subject: [PATCH 56/73] fix(imports): regenerate wanted-routing aggregate from actual inventory --- Complexity.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Complexity.lean b/Complexity.lean index 90e92addc..4af7e444d 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1853,8 +1853,8 @@ public import Complexity.Computability.WordRAM.Randomized.Defs public import Complexity.Computability.WordRAM.Randomized.Expected public import Complexity.Computability.WordRAM.Randomized.LoopSegment public import Complexity.Computability.WordRAM.Randomized.Structured -public import Complexity.Computability.WordRAM.Sequence public import Complexity.Computability.WordRAM.RegisterFrame +public import Complexity.Computability.WordRAM.Sequence public import Complexity.Computability.WordRAM.Signed.Arithmetic public import Complexity.Computability.WordRAM.Signed.Comparison public import Complexity.Computability.WordRAM.Signed.Compile From 7cb7ade0aa47ac5ab8270ff92b8707e6a67a46cd Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 29 Oct 2026 07:03:25 -0400 Subject: [PATCH 57/73] wip(wordram): checkpoint callable thin-product source Personal #1618 source checkpoint before integrating the refreshed #1615 parent. Complete generic native memory-window compiler, computed-modulus callable raw01 product, initialized Runs/halt, width/window/cost and dirty-memory/frame tests have targeted Lean MCP diagnostics clear. Canonical imports and final all-six/static validation remain pending; this is not a publication candidate. Reuses the exact landed NatClog module from a1d1fa882, with original credit. No full Exact Triangle or ordinary3SUM endpoint claim. --- .../Schonhage/Callable.lean | 48 +++ .../Schonhage/Callable/Bounds.lean | 134 ++++++ .../Schonhage/Callable/Core.lean | 111 +++++ .../Schonhage/Callable/Exec.lean | 109 +++++ .../Schonhage/Callable/Flat.lean | 97 +++++ .../Schonhage/Callable/Input.lean | 93 ++++ .../Schonhage/Callable/Program.lean | 107 +++++ .../Schonhage/Callable/Return.lean | 102 +++++ .../Schonhage/Callable/Setup.lean | 145 +++++++ .../Schonhage/Callable/Width.lean | 111 +++++ Complexity/Algorithms/WordRAM/NatClog.lean | 116 +++++ .../Computability/WordRAM/MemoryWindow.lean | 400 ++++++++++++++++++ .../Computability/WordRAM/Normalize.lean | 137 ++++++ ComplexityTests/SchonhageCallable.lean | 78 ++++ ComplexityTests/WordRAMMemoryWindow.lean | 51 +++ ROADMAP.md | 10 + UPSTREAM.md | 18 + 17 files changed, 1867 insertions(+) create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Bounds.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Core.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Exec.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Flat.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Input.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Program.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Return.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Setup.lean create mode 100755 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Width.lean create mode 100644 Complexity/Algorithms/WordRAM/NatClog.lean create mode 100755 Complexity/Computability/WordRAM/MemoryWindow.lean create mode 100755 Complexity/Computability/WordRAM/Normalize.lean create mode 100755 ComplexityTests/SchonhageCallable.lean create mode 100755 ComplexityTests/WordRAMMemoryWindow.lean diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable.lean new file mode 100755 index 000000000..0bbcfbb37 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable.lean @@ -0,0 +1,48 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Width + +/-! +# Caller-safe native queried thin products + +## Main definitions + +* `CallableProduct.program`: fixed standard unsigned code on 163 registers, with a runtime + source-memory base in register 160 and an absolute answer pointer returned in register zero. +* `CallableProduct.entryProgram`, `CallableProduct.input`: actual initialized client, with a + protected caller prefix and original raw matrices/interleaved request coordinates. +* `CallableProduct.windowSize`: explicit polynomial virtual address-space reservation; + this interval is not initialized or copied in bulk. + +## Main results + +* `CallableProduct.exec`: actual caller-safe product execution from original zero/one matrices, + arbitrary query occurrences and dirty scratch, with exact local-input restoration and full + outside-window frame. A bounded caller state supplies physical word validity throughout. +* `CallableProduct.runs_wide`: actual `State.init` input fit, same-data prefix `Runs` and charged + halt at every physical width above `256*clog₂(base+N+Q+P+5)`, for caller-word bound `P`. +* `CallableProduct.timeBound_le`: at most three times the original charged product plus + `2048*(N*D+Q+1)` standalone adaptation work, including halt. +* `CallableProduct.runs_thin`: at `L=19m`, `D=4^m`, `D^18≤N` and `Q*2^m≤N²`, the actual charge + is at most `17301504*(19m+1)*N²/D^(1/18)`. Distinct-set sparsity instead retains an explicit + additive charge for duplicate occurrences through `CallableProduct.thin_timeBound`. + +The original product program is unchanged. `MemoryWindow.compile` translates each load into +actual base addition, load and remainder, each store into addition and store, and preserves +native branching/loop decisions. Computed logarithm/power setup and two linear normalization +loops are charged. Raw zero/one input is doubled by the original actual matrix-input loop. +No source-Light semantics, new ALU operation, assumed execution or fast solver is used. + +The finite tensor, band and query mathematics and their source mapping remain those of +`QueriedProduct`; the compiler and callable controller are original native work. Stage APIs +are internal implementation interfaces. The concrete ET-record adapter, backend/witness +composition, whole ordinary-3SUM solver and improved 1.9992 endpoint remain open under #1394. +-/ diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Bounds.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Bounds.lean new file mode 100755 index 000000000..6db6aca92 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Bounds.lean @@ -0,0 +1,134 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Flat + +/-! +# Original-parameter width, space and charge for callable products + +The virtual window reservation is polynomial in the original row and occurrence counts. Its +large fixed exponent is explicit; the program does not initialize that whole interval. Actual +setup and return work is linear in original matrix/input size, plus the translated product. +Distinct-set sparsity retains the additive occurrence charge; occurrence sparsity absorbs it. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The reserved window has a fixed polynomial size in original dimensions and query count. -/ +theorem windowSize_le (N Q : ℕ) : windowSize N Q ≤ (2 * (N + Q + 3)) ^ 128 := by + have hc := Nat.clog_pos (by decide : 1 < (2 : ℕ)) (by omega : 1 < N + Q + 3) + have hp := Nat.pow_pred_clog_lt_self (by decide : 1 < (2 : ℕ)) (by omega : 1 < N + Q + 3) + have he : Nat.clog 2 (N + Q + 3) = (Nat.clog 2 (N + Q + 3)).pred + 1 := + (Nat.succ_pred_eq_of_pos hc).symm + have hceil : 2 ^ Nat.clog 2 (N + Q + 3) ≤ 2 * (N + Q + 3) := by + rw [he, pow_succ] + omega + have h := Nat.pow_le_pow_left hceil 128 + simpa only [windowSize, sourceWidth, ← pow_mul, Nat.mul_comm 128] using h + +/-- Every physical width above a positive logarithmic threshold admits the complete runtime +window and all setup/return addresses. No intermediate-fit certificate is needed. -/ +theorem physical_fits {W b N Q : ℕ} + (hW : 256 * Nat.clog 2 (b + N + Q + 5) ≤ W) : + Fits W (b + 4 * windowSize N Q + 1024) := by + let c := Nat.clog 2 (b + N + Q + 5) + have hc : 0 < c := Nat.clog_pos (by decide) (by omega) + have hceil := Nat.le_pow_clog (by decide : 1 < (2 : ℕ)) (b + N + Q + 5) + change b + N + Q + 5 ≤ 2 ^ c at hceil + have hm : windowSize N Q ≤ 2 ^ (128 * c) := by + apply Nat.pow_le_pow_right (by decide) + apply Nat.mul_le_mul_left 128 + exact Nat.clog_mono_right 2 (by omega) + have hb : b ≤ 2 ^ (128 * c) := by + have he : c ≤ 128 * c := by omega + exact (show b ≤ 2 ^ c by omega).trans (Nat.pow_le_pow_right (by decide) he) + have hpos : 1 ≤ 2 ^ (128 * c) := Nat.one_le_pow _ _ (by decide) + have hsum : b + 4 * windowSize N Q + 1024 < 2 ^ (128 * c + 11) := by + rw [pow_add] + norm_num only [Nat.reducePow] + omega + exact hsum.trans_le (Nat.pow_le_pow_right (by decide) (by change 256 * c ≤ W at hW; omega)) + +/-- Full standalone charge (including its base load and halt): constant-factor actual product +cost plus linear adaptation. No bulk-window or caller-catalogue copy is hidden in this bound. -/ +theorem timeBound_le {m N : ℕ} (qs : List (Fin N × Fin N)) : + timeBound (m := m) qs + 3 ≤ + 3 * (QueriedProduct.timeBound (show m ≤ 19 * m by omega) qs + 1) + + 2048 * (N * D m + qs.length + 1) := by + have hlog : Nat.clog 2 (N + qs.length + 3) ≤ N + qs.length + 3 := + Nat.clog_le_of_le_pow (Nat.lt_two_pow_self.le) + have hD : 0 < D m := Nat.pow_pos (by decide) + have hND : N ≤ N * D m := Nat.le_mul_of_pos_right N hD + unfold timeBound coreTime QueriedProduct.inputEnd + omega + +/-- Under distinct-position sparsity, every duplicate occurrence retains its additive charge. +This quantitative saving bounds the real callable program, including initialization and halt. -/ +theorem thin_timeBound {m N : ℕ} (qs : List (Fin N × Fin N)) (hN : D m ^ 18 ≤ N) + (hQ : qs.toFinset.card * 2 ^ m ≤ N ^ 2) : + ((timeBound (m := m) qs + 3 : ℕ) : ℝ) ≤ + 262144 * ((19 * m + 1 : ℕ) : ℝ) * + (65 * ((D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2) + qs.length) := by + have hNpos : 0 < N := (Nat.pow_pos (Nat.pow_pos (by decide : 0 < (4 : ℕ)))).trans_le hN + have hp := preparation_shape_le (show m ≤ 19 * m by omega) hNpos + have hpR : ((1 + N * D m : ℕ) : ℝ) ≤ + ((5 * (numBands (19 * m) m N * 10 ^ (19 * m)) : ℕ) : ℝ) := by + exact_mod_cast ((Nat.le_add_right (1 + N * D m) (2 ^ (19 * m))).trans + (Nat.le_add_right _ (2 * numBands (19 * m) m N * 10 ^ (19 * m)))).trans hp + have he := encodings_total_le m N hN + have hlinear : ((N * D m + qs.length + 1 : ℕ) : ℝ) ≤ + 10 * ((D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2) + qs.length := by + push_cast at hpR he ⊢ + nlinarith only [hpR, he] + have hbase := QueriedProduct.thin_timeBound m N qs hN hQ + have htime : ((timeBound (m := m) qs + 3 : ℕ) : ℝ) ≤ + 3 * ((QueriedProduct.timeBound (show m ≤ 19 * m by omega) qs + 1 : ℕ) : ℝ) + + 2048 * ((N * D m + qs.length + 1 : ℕ) : ℝ) := by exact_mod_cast timeBound_le qs + have hL : (1 : ℝ) ≤ ((19 * m + 1 : ℕ) : ℝ) := by norm_cast; omega + have hG : 0 ≤ (D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2 := by positivity + have hQ0 : 0 ≤ (qs.length : ℝ) := Nat.cast_nonneg _ + nlinarith only [htime, hbase, hlinear, mul_nonneg (sub_nonneg.mpr hL) hG, + mul_nonneg (sub_nonneg.mpr hL) hQ0, hG, hQ0] + +/-- Occurrence sparsity absorbs the duplicate charge and yields the complete charged saving. -/ +theorem thin_timeBound_sparse {m N : ℕ} (qs : List (Fin N × Fin N)) (hN : D m ^ 18 ≤ N) + (hQ : qs.length * 2 ^ m ≤ N ^ 2) : + ((timeBound (m := m) qs + 3 : ℕ) : ℝ) ≤ + 17301504 * ((19 * m + 1 : ℕ) : ℝ) * (N : ℝ) ^ 2 / (D m : ℝ) ^ (1 / 18 : ℝ) := by + have hset : qs.toFinset.card * 2 ^ m ≤ N ^ 2 := + (Nat.mul_le_mul_right _ (List.toFinset_card_le qs)).trans hQ + have h := thin_timeBound qs hN hset + have hQr : (qs.length : ℝ) * (2 : ℝ) ^ m ≤ (N : ℝ) ^ 2 := by exact_mod_cast hQ + have hlen := mul_le_mul_of_nonneg_left hQr (saving_pos m).le + have hpow : 1 ≤ saving m * (2 : ℝ) ^ m := by + rw [← saving_mul_two_rpow m] + apply mul_le_mul_of_nonneg_left _ (saving_pos m).le + rw [← Real.rpow_natCast] + apply Real.rpow_le_rpow_of_exponent_le (by norm_num) + have := Nat.cast_nonneg (α := ℝ) m + linarith + have hlen' : (qs.length : ℝ) ≤ + (D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2 := by + rw [← saving_eq_D_rpow] + nlinarith only [hlen, mul_le_mul_of_nonneg_left hpow (Nat.cast_nonneg qs.length : + (0 : ℝ) ≤ qs.length)] + have hL : 0 ≤ ((19 * m + 1 : ℕ) : ℝ) := Nat.cast_nonneg _ + have hfinal : ((timeBound (m := m) qs + 3 : ℕ) : ℝ) ≤ + 17301504 * ((19 * m + 1 : ℕ) : ℝ) * + ((D m : ℝ) ^ (-(1 / 18 : ℝ)) * (N : ℝ) ^ 2) := by + nlinarith only [h, mul_nonneg hL (sub_nonneg.mpr hlen')] + rw [Real.rpow_neg (by positivity)] at hfinal + simpa only [div_eq_mul_inv, mul_assoc, mul_left_comm, mul_comm] using hfinal + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Core.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Core.lean new file mode 100755 index 000000000..c05bc0cac --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Core.lean @@ -0,0 +1,111 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Setup + +/-! +# Actual translated queried-product execution + +The execution is derived from original zero/one matrices and request occurrences. The source +register bank and dirty source scratch are normalized by actual setup and translated loads. +No execution, routing, layout or cost certificate is a premise. These stage lemmas are internal +implementation APIs; exact physical answer/input words are normalized by the final spans. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Internal stage charge, before the two final span normalizations. -/ +@[expose] def coreTime {m N : ℕ} (qs : List (Fin N × Fin N)) : ℕ := + 517 * Nat.clog 2 (N + qs.length + 3) + 184 + + 3 * QueriedProduct.timeBound (show m ≤ 19 * m by omega) qs + +/-- Source admission follows from the original thin geometry and zero/one entry bound. -/ +theorem source_admission {m N Q : ℕ} (hN : D m ^ 18 ≤ N) : + 0 < sourceWidth N Q - 2 ∧ + QueriedProduct.magnitudeBound (19 * m) 1 < 2 ^ ((sourceWidth N Q - 2) - 1) ∧ + Fits (sourceWidth N Q) (QueriedProduct.wordBudget (19 * m) m N Q) := by + exact QueriedProduct.admission_bounds (QueriedProduct.thin_admissionWidth_le m N Q 1 hN) + +/-- Original words initialize an actual callable native product prefix, even with arbitrary +dirty target memory in the reserved window. Answers here are clipped physical words; the +subsequent real normalization makes them exact. Original duplicate requests retain their order. -/ +theorem exec_core {W b m N : ℕ} + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (x : Data 163) (hb : x.regs Reg.base = b) + (hinput : ∀ a, a < QueriedProduct.inputEnd m N qs.length → + x.mem (b + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem a) + (hX : ∀ i k, 0 ≤ X i k ∧ X i k ≤ 1) (hY : ∀ k j, 0 ≤ Y k j ∧ Y k j ≤ 1) + (hN : D m ^ 18 ≤ N) (hfit : Fits W (b + 4 * windowSize N qs.length + 1024)) : + ∃ y, ExecWithin W core x y (coreTime (m := m) qs) ∧ + y.regs Reg.base = b ∧ y.regs Reg.modulus = windowSize N qs.length ∧ + y.regs Reg.inputSize = QueriedProduct.inputEnd m N qs.length ∧ + y.regs Reg.queryCount = qs.length ∧ + y.regs Reg.output = QueriedProduct.outputBase (19 * m) m N qs.length ∧ + (∀ i : Fin qs.length, + y.mem (b + (QueriedProduct.outputBase (19 * m) m N qs.length + i.val)) % + windowSize N qs.length = Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2)) ∧ + (∀ a, a < QueriedProduct.inputEnd m N qs.length → + y.mem (b + a) % windowSize N qs.length = x.mem (b + a)) ∧ + (∀ a, a < b ∨ b + windowSize N qs.length ≤ a → y.mem a = x.mem a) := by + have hXabs (i k) : |X i k| ≤ (1 : ℤ) := by rw [abs_of_nonneg (hX i k).1]; exact (hX i k).2 + have hYabs (k j) : |Y k j| ≤ (1 : ℤ) := by rw [abs_of_nonneg (hY k j).1]; exact (hY k j).2 + have hbits : true = true → (∀ i k, 0 ≤ X i k) ∧ (∀ k j, 0 ≤ Y k j) := + fun _ => ⟨fun i k => (hX i k).1, fun k j => (hY k j).1⟩ + obtain ⟨hw, hmag, hbudget⟩ := source_admission (Q := qs.length) hN + have htwo : sourceWidth N qs.length - 2 + 2 = sourceWidth N qs.length := by omega + have hbudget' : Fits (sourceWidth N qs.length - 2 + 2) + (QueriedProduct.wordBudget (19 * m) m N qs.length) := by simpa only [htwo] using hbudget + have hi := QueriedProduct.input_fits hw (show m ≤ 19 * m by omega) + true X Y qs hXabs hYabs hmag hbudget' + have hi' : (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.Bounded + (sourceWidth N qs.length) := by + simpa only [htwo, State.init] using (Data.Bounded.init (r := 160) hi) + have hrow : x.mem (b + 3) = N := by + rw [hinput 3 (by unfold QueriedProduct.inputEnd; omega)] + simp [State.init, Data.init, QueriedProduct.input] + have hcount : x.mem (b + 4) = qs.length := by + rw [hinput 4 (by unfold QueriedProduct.inputEnd; omega)] + simp [State.init, Data.init, QueriedProduct.input] + obtain ⟨s, hs, smem, smatch, sbound⟩ := exec_setup x hb hrow hcount hfit + let v := MemoryWindow.view (sourceWidth N qs.length) b s + have vinput (a : ℕ) (ha : a < QueriedProduct.inputEnd m N qs.length) : + v.mem a = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem a := by + change s.mem (b + a) % 2 ^ sourceWidth N qs.length = _ + rw [smem, hinput a ha, Nat.mod_eq_of_lt (hi'.2 a)] + obtain ⟨p, hp, pp, pv, pf⟩ := QueriedProduct.exec hw (show m ≤ 19 * m by omega) + true X Y qs v (fun a _ ha => vinput a ha) hXabs hYabs hbits hmag hbudget' + have hp' : ExecWithin (sourceWidth N qs.length) (QueriedProduct.program true) v p + (QueriedProduct.timeBound (show m ≤ 19 * m by omega) qs) := by + simpa only [htwo] using hp + have hwindow : Fits W (b + 2 ^ sourceWidth N qs.length) := hfit.of_le (by + change b + windowSize N qs.length ≤ _ + omega) + obtain ⟨y, hy, ymatch, yframe⟩ := MemoryWindow.execWithin hp' + (QueriedProduct.not_readsWidth true) sbound smatch (setup_fits hfit).1 hwindow + refine ⟨y, hs.seq hy, ymatch.base_eq, ymatch.modulus_eq, + (ymatch.regs _).trans pp.inputSize, (ymatch.regs _).trans pp.queryCount, + (ymatch.regs _).trans pp.output, ?_, ?_, ?_⟩ + · intro i + exact (ymatch.mem _).trans (pv i) + · intro a ha + change y.mem (b + a) % 2 ^ sourceWidth N qs.length = _ + rw [ymatch.mem, pf a (by omega)] + change s.mem (b + a) % 2 ^ sourceWidth N qs.length = _ + rw [smem, hinput a ha, Nat.mod_eq_of_lt (hi'.2 a)] + · intro a ha + exact (yframe a ha).trans (congrFun smem a) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Exec.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Exec.lean new file mode 100755 index 000000000..926311e42 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Exec.lean @@ -0,0 +1,109 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Core +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Return + +/-! +# Complete native callable queried-product execution + +The caller supplies original zero/one matrices and coordinate occurrences in a local original +input block. The product computes its own tables, layouts, groups, encoded bands, epochs and +answers. Every source memory access is translated to actual base addition and load/store code; +dirty words are normalized without bulk clearing. The final answer pointer is absolute. + +This is the callable product prerequisite for an ET-record adapter, not that adapter, an +Exact Triangle solver or the complete ordinary-3SUM bound under #1394. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The complete charge includes two linear span normalizations and the computed setup. -/ +@[expose] def timeBound {m N : ℕ} (qs : List (Fin N × Fin N)) : ℕ := + coreTime (m := m) qs + 7 * QueriedProduct.inputEnd m N qs.length + 7 * qs.length + 14 + +private theorem spans_le_budget (L m N Q : ℕ) : + QueriedProduct.inputEnd m N Q ≤ QueriedProduct.outputBase L m N Q ∧ + QueriedProduct.inputEnd m N Q + QueriedProduct.outputBase L m N Q + Q + 2 ≤ + QueriedProduct.wordBudget L m N Q := by + have hsep : QueriedProduct.inputEnd m N Q ≤ QueriedProduct.outputBase L m N Q := by + unfold QueriedProduct.outputBase QueriedProduct.groupedBase QueriedProduct.originalBase + QueriedProduct.codeBase QueriedProduct.offsetBase QueriedProduct.snapshotBase + omega + have hend : QueriedProduct.outputBase L m N Q + Q ≤ QueriedProduct.memoryEnd L m N Q := by + unfold QueriedProduct.memoryEnd QueriedProduct.stampBase QueriedProduct.scratchBase + have := Nat.zero_le (2 * (L + 1) * 10 ^ L) + have := Nat.zero_le (10 ^ L) + omega + refine ⟨hsep, ?_⟩ + unfold QueriedProduct.wordBudget + omega + +/-- Caller-safe actual native product execution. The only local memory premise is the original +input block; all remaining target memory may be dirty. Every original occurrence is answered +exactly, original input words are restored exactly, and the caller memory outside the window +is preserved. The numerical width hypothesis is derived from original dimensions and base. -/ +theorem exec {W b m N : ℕ} + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (x : Data 163) (hb : x.regs Reg.base = b) + (hinput : ∀ a, a < QueriedProduct.inputEnd m N qs.length → + x.mem (b + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem a) + (hX : ∀ i k, 0 ≤ X i k ∧ X i k ≤ 1) (hY : ∀ k j, 0 ≤ Y k j ∧ Y k j ≤ 1) + (hN : D m ^ 18 ≤ N) (hfit : Fits W (b + 4 * windowSize N qs.length + 1024)) : + ∃ y, ExecWithin W program x y (timeBound (m := m) qs) ∧ + y.regs Reg.result = b + QueriedProduct.outputBase (19 * m) m N qs.length ∧ + y.regs Reg.base = b ∧ y.regs Reg.modulus = windowSize N qs.length ∧ + (∀ i : Fin qs.length, + y.mem (b + (QueriedProduct.outputBase (19 * m) m N qs.length + i.val)) = + Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2)) ∧ + (∀ a, a < QueriedProduct.inputEnd m N qs.length → y.mem (b + a) = x.mem (b + a)) ∧ + (∀ a, a < b ∨ b + windowSize N qs.length ≤ a → y.mem a = x.mem a) := by + obtain ⟨hsep, hspan⟩ := spans_le_budget (19 * m) m N qs.length + have hbudget := (source_admission (Q := qs.length) hN).2.2 + have hbound : QueriedProduct.inputEnd m N qs.length + + QueriedProduct.outputBase (19 * m) m N qs.length + qs.length + 2 < + windowSize N qs.length := hspan.trans_lt hbudget + have hM : 0 < windowSize N qs.length := Nat.two_pow_pos _ + obtain ⟨c, hc, cb, cM, cI, cQ, cO, cv, ci, cf⟩ := exec_core X Y qs x hb hinput hX hY hN hfit + have hIfit : Fits W (b + QueriedProduct.inputEnd m N qs.length + + windowSize N qs.length + 2) := hfit.of_le (by omega) + obtain ⟨s, hs, sm, sr⟩ := exec_restoreInput c cb cI cM hM hIfit + have sb : s.regs Reg.base = b := (sr _ (by decide)).trans cb + have sM : s.regs Reg.modulus = windowSize N qs.length := (sr _ (by decide)).trans cM + have sQ : s.regs Reg.queryCount = qs.length := (sr _ (by decide)).trans cQ + have sO : s.regs Reg.output = QueriedProduct.outputBase (19 * m) m N qs.length := + (sr _ (by decide)).trans cO + have hOfit : Fits W (b + QueriedProduct.outputBase (19 * m) m N qs.length + qs.length + + windowSize N qs.length + 2) := hfit.of_le (by omega) + obtain ⟨y, hy, ym, yr, yf⟩ := exec_returnOutput s sb sO sQ sM hM hOfit + refine ⟨y, (hc.seq (hs.seq hy)).mono (by unfold timeBound; omega), yr, + (yf _ (by decide)).trans sb, (yf _ (by decide)).trans sM, ?_, ?_, ?_⟩ + · intro i + rw [ym] + rw [show b + (QueriedProduct.outputBase (19 * m) m N qs.length + i.val) = + (b + QueriedProduct.outputBase (19 * m) m N qs.length) + i.val by omega] + rw [ArrayNormalize.normalizePrefix_read _ _ _ _ _ i.isLt, sm, + ArrayNormalize.normalizePrefix_outside _ _ _ _ _ (by omega)] + simpa only [Nat.add_assoc] using cv i + · intro a ha + rw [ym, ArrayNormalize.normalizePrefix_outside _ _ _ _ _ (by omega), sm, + ArrayNormalize.normalizePrefix_read _ _ _ _ _ ha] + exact ci a ha + · intro a ha + rw [ym, ArrayNormalize.normalizePrefix_outside _ _ _ _ _ (by omega), sm, + ArrayNormalize.normalizePrefix_outside _ _ _ _ _ (by omega)] + exact cf a ha + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Flat.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Flat.lean new file mode 100755 index 000000000..9eac7b35c --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Flat.lean @@ -0,0 +1,97 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Input + +/-! +# Initialized flat callable products + +The same final native data witnesses the charged prefix run, exact physical product words, +restored local original input, protected caller memory and the final halt. The program and its +163 registers are fixed independently of dimensions, runtime base and physical word width. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Actual initialized execution with no computed-layout, bounded-scratch or execution premise. +Every original duplicate request has its own exact physical answer cell. The `n+1` charge +includes the halt reached by the same final data `y` in the prefix `Runs`. -/ +theorem runs {W m N : ℕ} (caller : List ℕ) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (hN : D m ^ 18 ≤ N) + (hX : ∀ i k, 0 ≤ X i k ∧ X i k ≤ 1) (hY : ∀ k j, 0 ≤ Y k j ∧ Y k j ≤ 1) + (hcaller : ∀ v ∈ caller, Fits W v) + (hfit : Fits W (inputBase caller + 4 * windowSize N qs.length + 1024)) : + InputFits W (input caller X Y qs) ∧ + ∃ y n, n ≤ timeBound (m := m) qs + 2 ∧ + Runs W (entryProgram.program Reg.result) (State.init (input caller X Y qs)) + ⟨entryProgram.size, y⟩ n ∧ + Executes W (entryProgram.program Reg.result) (State.init (input caller X Y qs)) + (inputBase caller + QueriedProduct.outputBase (19 * m) m N qs.length) (n + 1) ∧ + y.Bounded W ∧ + (∀ i : Fin qs.length, + y.mem (inputBase caller + (QueriedProduct.outputBase (19 * m) m N qs.length + i.val)) = + Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2)) ∧ + (∀ a, a < QueriedProduct.inputEnd m N qs.length → + y.mem (inputBase caller + a) = + (State.init (input caller X Y qs) : State 163).data.mem (inputBase caller + a)) ∧ + (∀ a, a < inputBase caller ∨ inputBase caller + windowSize N qs.length ≤ a → + y.mem a = (State.init (input caller X Y qs) : State 163).data.mem a) := by + have hi := input_fits caller X Y qs hN hX hY hcaller hfit + let x := (State.init (input caller X Y qs) : State 163).data + let s := (x.set Reg.address 1).set Reg.base (inputBase caller) + have h1 : Fits W 1 := hfit.of_le (by omega) + have hread : x.mem 1 = inputBase caller := by simp [x, input, State.init, Data.init] + have hs : ExecWithin (B := .standard) W + (ram[ Reg.address ← #1; Reg.base ← *Reg.address ]) x s 2 := by + ram_eval [s, h1, hread, Reg.address, Reg.base, Fin.mk.injEq] + obtain ⟨y, hy, yr, yb, yM, yv, yi, yf⟩ := exec X Y qs s + (by simp [s]) (fun a _ => input_word caller X Y qs a) hX hY hN hfit + have he : ExecWithin W entryProgram x y (2 + timeBound (m := m) qs) := hs.seq hy + have ybound : y.Bounded W := he.bounded (by simpa only [x, State.init] using Data.Bounded.init hi) + obtain ⟨n, hn, he⟩ := he + refine ⟨hi, y, n, by omega, ?_, ?_, ybound, yv, yi, yf⟩ + · simpa only [Nat.zero_add, x, State.init] using he.runs (pc := 0) + (p := entryProgram.program Reg.result) (Program.Contains.self _).append_left + · simpa only [yr, x, State.init] using he.executes Reg.result + +set_option maxRecDepth 4096 in +-- This finite syntactic check visits the fixed 160-register normalization sequence. +private theorem setup_size : setup.size ≤ 1024 := by decide +kernel + +set_option maxRecDepth 4096 in +-- This finite syntactic check visits the fixed normalization and reused arithmetic routines. +private theorem setup_not_readsWidth : ¬setup.ReadsWidth := by decide +kernel + +/-- The complete standalone caller has a fixed finite flat program, including halt. -/ +theorem program_length_le : (entryProgram.program Reg.result).length ≤ 131072 := by + have hp := QueriedProduct.program_length_le true + simp only [Code.program, List.length_append, Code.length_assemble, + List.length_singleton] at hp ⊢ + have hc := MemoryWindow.size_compile (QueriedProduct.program true) + have hs := setup_size + have hi : restoreInput.size = 13 := by decide +kernel + have ho : returnOutput.size = 13 := by decide +kernel + simp only [entryProgram, program, Code.size] at * + omega + +/-- The width-independent code computes its own virtual modulus without reading physical width. -/ +theorem not_readsWidth : ¬entryProgram.ReadsWidth := by + have hr : ¬restoreInput.ReadsWidth := by decide +kernel + have ho : ¬returnOutput.ReadsWidth := by decide +kernel + simp only [entryProgram, program, Code.ReadsWidth, Op.ReadsWidth, + MemoryWindow.readsWidth_compile, QueriedProduct.not_readsWidth, setup_not_readsWidth, + hr, ho, not_false_eq_true, or_self] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Input.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Input.lean new file mode 100755 index 000000000..336f6a0b8 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Input.lean @@ -0,0 +1,93 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Exec +import Mathlib.Data.List.GetD + +/-! +# Ordinary initialized input for a callable product + +The first input word is a runtime base pointer. An arbitrary caller prefix follows, then a +local ordinary input-length word and the unchanged original product input. No computed tables, +grouping, encodings, bounded scratch contents or execution certificates occur in this input. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Runtime location of the local length word after an arbitrary caller prefix. -/ +@[expose] def inputBase (caller : List ℕ) : ℕ := caller.length + 2 + +/-- Original scalar headers, raw matrices and request pairs above a protected caller prefix. -/ +@[expose] def input {m N : ℕ} (caller : List ℕ) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) : List ℕ := + let payload := QueriedProduct.input true (19 * m) X Y qs + [inputBase caller] ++ caller ++ payload.length :: payload + +/-- The initialized client loads the supplied runtime base before invoking the real kernel. -/ +@[expose] def entryProgram : Code .standard 163 := + .seq (ram[ Reg.address ← #1; Reg.base ← *Reg.address ]) program + +private theorem block_word (caller payload : List ℕ) (a : ℕ) : + (Data.init ([inputBase caller] ++ caller ++ payload.length :: payload) : Data 163).mem + (inputBase caller + a) = (Data.init payload : Data 160).mem a := by + change (Data.init _ : Data 163).mem (caller.length + 2 + a) = _ + rw [show caller.length + 2 + a = (caller.length + 1 + a) + 1 by omega, Data.init_mem_succ] + rw [List.getD_append_right _ _ _ _ (by simp)] + simp only [List.length_append, List.length_singleton] + have hoff : caller.length + 1 + a - (1 + caller.length) = a := by omega + rw [hoff] + cases a with + | zero => simp [Data.init] + | succ a => simp [Data.init_mem_succ, List.getD_eq_getElem?_getD] + +/-- Every local original input word has its actual initialized meaning at the runtime base. -/ +theorem input_word {m N : ℕ} (caller : List ℕ) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (a : ℕ) : + (State.init (input caller X Y qs) : State 163).data.mem (inputBase caller + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem a := + block_word caller _ a + +/-- Original caller words and admitted original product words suffice for the global input fit. -/ +theorem input_fits {W m N : ℕ} (caller : List ℕ) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (hN : D m ^ 18 ≤ N) + (hX : ∀ i k, 0 ≤ X i k ∧ X i k ≤ 1) (hY : ∀ k j, 0 ≤ Y k j ∧ Y k j ≤ 1) + (hcaller : ∀ v ∈ caller, Fits W v) + (hfit : Fits W (inputBase caller + 4 * windowSize N qs.length + 1024)) : + InputFits W (input caller X Y qs) := by + have hXabs (i k) : |X i k| ≤ (1 : ℤ) := by rw [abs_of_nonneg (hX i k).1]; exact (hX i k).2 + have hYabs (k j) : |Y k j| ≤ (1 : ℤ) := by rw [abs_of_nonneg (hY k j).1]; exact (hY k j).2 + obtain ⟨hw, hmag, hbudget⟩ := source_admission (Q := qs.length) hN + have htwo : sourceWidth N qs.length - 2 + 2 = sourceWidth N qs.length := by omega + have hi := QueriedProduct.input_fits hw (show m ≤ 19 * m by omega) + true X Y qs hXabs hYabs hmag (by simpa only [htwo] using hbudget) + have hi' : InputFits (sourceWidth N qs.length) (QueriedProduct.input true (19 * m) X Y qs) := by + simpa only [htwo] using hi + have hlen : (QueriedProduct.input true (19 * m) X Y qs).length < windowSize N qs.length := hi'.1 + refine ⟨?_, ?_⟩ + · apply hfit.of_le + simp only [input, List.length_append, List.length_cons, List.length_nil, inputBase] + unfold inputBase at hfit + omega + · intro v hv + simp only [input, List.mem_append, List.mem_cons, List.not_mem_nil, or_false] at hv + rcases hv with ((rfl | hv) | rfl | hv) + · exact hfit.of_le (by omega) + · exact hcaller _ hv + · exact (hi'.1.mono (setup_fits hfit).1) + · exact (hi'.2 v hv).mono (setup_fits hfit).1 + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Program.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Program.lean new file mode 100755 index 000000000..7c8ce187b --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Program.lean @@ -0,0 +1,107 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.ThinWidth +public import Complexity.Algorithms.WordRAM.NatClog +public import Complexity.Computability.WordRAM.MemoryWindow +public import Complexity.Computability.WordRAM.Normalize +public import Complexity.Computability.WordRAM.Power + +/-! +# Callable queried-product code + +The source product is unchanged. Its translated memory accesses use a runtime base in register +160 and a source-word modulus computed from the original row and query counts. The 160 source +registers occupy the low bank; registers 161 and 162 are the modulus and address temporary. + +Raw zero/one entries are still doubled by the original charged matrix-input loop. Setup and +final normalization touch only the original input and answer spans, never the whole reserved +window or caller catalogue. The code definitions here are internal implementation APIs. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct + +open Cslib.WordRAM + +namespace Reg + +/-- Absolute result pointer returned to the caller. Also a setup argument register. -/ +abbrev result : Fin 163 := 0 +/-- Setup argument register. -/ +abbrev second : Fin 163 := 1 +/-- Setup constant or normalization modulus. -/ +abbrev third : Fin 163 := 2 +/-- Power exponent during setup. -/ +abbrev fourth : Fin 163 := 3 +/-- Runtime source-memory base supplied by the caller. -/ +abbrev base : Fin 163 := 160 +/-- Computed source-word modulus, preserved across the translated product. -/ +abbrev modulus : Fin 163 := 161 +/-- Temporary absolute address. -/ +abbrev address : Fin 163 := 162 +/-- Source register retaining the original input span length. -/ +abbrev inputSize : Fin 163 := QueriedProduct.inputSize.castAdd 3 +/-- Source register retaining the relative answer pointer. -/ +abbrev output : Fin 163 := QueriedProduct.output.castAdd 3 +/-- Source register retaining the query occurrence count. -/ +abbrev queryCount : Fin 163 := QueriedProduct.queryCount.castAdd 3 + +end Reg + +/-- The logarithm routine uses four low scratch registers. -/ +@[expose] def clogBank (i : Fin 4) : Fin 163 := i.castAdd 159 +/-- The power routine uses five low scratch registers. -/ +@[expose] def powerBank (i : Fin 5) : Fin 163 := i.castAdd 158 +/-- Array normalization uses seven low scratch registers. -/ +@[expose] def normalizeBank (i : Fin 7) : Fin 163 := i.castAdd 156 + +/-- Read the actual row and occurrence-count headers, and form `N+Q+3`. -/ +@[expose] def readSize : Code .standard 163 := ram[ + Reg.result ← #3; Reg.address ← Reg.base + Reg.result; Reg.result ← *Reg.address; + Reg.second ← #4; Reg.address ← Reg.base + Reg.second; Reg.second ← *Reg.address; + Reg.result ← Reg.result + Reg.second; Reg.second ← #3; + Reg.result ← Reg.result + Reg.second ] + +/-- Compute `2^(128*clog₂(N+Q+3))` with the existing logarithm and power programs. -/ +@[expose] def setup : Code .standard 163 := + .seq readSize <| .seq (NatClog.program.rename clogBank) <| + .seq (ram[ Reg.third ← #128; Reg.fourth ← Reg.second * Reg.third; Reg.third ← #2 ]) <| + .seq (Power.program.rename powerBank) <| + .seq (.op (.copy Reg.modulus Reg.result)) (MemoryWindow.normalizeRegisters 160) + +/-- Normalize the restored local input, so its actual physical words equal their source words. -/ +@[expose] def restoreInput : Code .standard 163 := + .seq (ram[ Reg.result ← Reg.base; Reg.second ← Reg.inputSize; + Reg.third ← Reg.modulus ]) (ArrayNormalize.program.rename normalizeBank) + +/-- Normalize the exact output span and return its absolute address. -/ +@[expose] def returnOutput : Code .standard 163 := + .seq (ram[ Reg.result ← Reg.base + Reg.output; + Reg.second ← Reg.queryCount; Reg.third ← Reg.modulus ]) + (ArrayNormalize.program.rename normalizeBank) + +/-- One fixed native raw-input product callable above an arbitrary caller-supplied base. +Every memory translation, source instruction and normalization is part of this code. -/ +@[expose] def program : Code .standard 163 := + .seq (.seq setup (MemoryWindow.compile (QueriedProduct.program true))) + (.seq restoreInput returnOutput) + +/-- Internal execution prefix through setup and the actual translated product. -/ +@[expose] def core : Code .standard 163 := + .seq setup (MemoryWindow.compile (QueriedProduct.program true)) + +/-- The derived source width is independent of the physical machine width. -/ +@[expose] def sourceWidth (N Q : ℕ) : ℕ := 128 * Nat.clog 2 (N + Q + 3) + +/-- Polynomially bounded virtual address-space reservation; no bulk initialization is implied. -/ +@[expose] def windowSize (N Q : ℕ) : ℕ := 2 ^ sourceWidth N Q + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Return.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Return.lean new file mode 100755 index 000000000..bbd2db757 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Return.lean @@ -0,0 +1,102 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Program + +/-! +# Exact physical input and answer words + +The callable compiler relates clipped memory to source memory. Two actual in-place span loops +make the restored input and returned answers exact physical words. These are internal execution +interfaces; both loops charge their setup, address calculations, remainders and stores. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct + +open Cslib.WordRAM + +private theorem normalizeBank_injective : Function.Injective normalizeBank := by + intro i j h + exact Fin.ext (congrArg (fun k : Fin 163 => k.val) h) + +private theorem exec_normalizeBank {W b n M : ℕ} (x : Data 163) + (hb : x.regs Reg.result = b) (hn : x.regs Reg.second = n) + (hm : x.regs Reg.third = M) (hM : 0 < M) (hfit : Fits W (b + n + M + 2)) : + ∃ y, ExecWithin W (ArrayNormalize.program.rename normalizeBank) x y (7 * n + 4) ∧ + y.mem = ArrayNormalize.normalizePrefix x.mem b M n ∧ y.regs Reg.result = b ∧ + ∀ i : Fin 163, 7 ≤ i.val → y.regs i = x.regs i := by + obtain ⟨a, ha, am, ab, an, aM⟩ := ArrayNormalize.exec (x.comap normalizeBank) hb hn hm hM hfit + refine ⟨a.extend normalizeBank x, ha.rename normalizeBank_injective, am, + (normalizeBank_injective.extend_apply _ _ ArrayNormalize.base).trans ab, ?_⟩ + intro i hi + have hnot : ¬∃ j, normalizeBank j = i := by + rintro ⟨j, hj⟩ + have h := congrArg (fun k : Fin 163 => k.val) hj + change j.val = i.val at h + omega + exact Function.extend_apply' _ _ _ hnot + +/-- Normalize precisely the local original input; high product/caller registers survive. -/ +theorem exec_restoreInput {W b n M : ℕ} (x : Data 163) (hb : x.regs Reg.base = b) + (hn : x.regs Reg.inputSize = n) (hm : x.regs Reg.modulus = M) (hM : 0 < M) + (hfit : Fits W (b + n + M + 2)) : + ∃ y, ExecWithin W restoreInput x y (7 * n + 7) ∧ + y.mem = ArrayNormalize.normalizePrefix x.mem b M n ∧ + ∀ i : Fin 163, 7 ≤ i.val → y.regs i = x.regs i := by + let z := ((x.set Reg.result b).set Reg.second n).set Reg.third M + have hz : ExecWithin (B := .standard) W + (ram[ Reg.result ← Reg.base; Reg.second ← Reg.inputSize; + Reg.third ← Reg.modulus ]) x z 3 := by + ram_eval [z, hb, hn, hm, Reg.result, Reg.second, Reg.third, Reg.base, Reg.inputSize, + Reg.modulus, QueriedProduct.inputSize, Fin.mk.injEq] + obtain ⟨y, hy, ym, yr, yh⟩ := exec_normalizeBank (W := W) (b := b) (n := n) (M := M) z + (by simp [z, Reg.result, Reg.second, Reg.third]) + (by simp [z, Reg.second, Reg.third]) (by simp [z]) hM hfit + refine ⟨y, (hz.seq hy).mono (by omega), ym, ?_⟩ + intro i hi + rw [yh i hi] + have h0 : i ≠ Reg.result := by intro h; subst i; simp [Reg.result] at hi + have h1 : i ≠ Reg.second := by intro h; subst i; simp [Reg.second] at hi + have h2 : i ≠ Reg.third := by intro h; subst i; simp [Reg.third] at hi + simp only [z, Data.set_regs, Function.update_of_ne h0, Function.update_of_ne h1, + Function.update_of_ne h2] + +/-- Normalize the answer span, preserving all high registers, and return its absolute pointer. -/ +theorem exec_returnOutput {W b o n M : ℕ} (x : Data 163) (hb : x.regs Reg.base = b) + (ho : x.regs Reg.output = o) (hn : x.regs Reg.queryCount = n) + (hm : x.regs Reg.modulus = M) (hM : 0 < M) (hfit : Fits W (b + o + n + M + 2)) : + ∃ y, ExecWithin W returnOutput x y (7 * n + 7) ∧ + y.mem = ArrayNormalize.normalizePrefix x.mem (b + o) M n ∧ + y.regs Reg.result = b + o ∧ + ∀ i : Fin 163, 7 ≤ i.val → y.regs i = x.regs i := by + have haddr : Fits W (b + o) := hfit.of_le (by omega) + let z := ((x.set Reg.result (b + o)).set Reg.second n).set Reg.third M + have hz : ExecWithin W + (ram[ Reg.result ← Reg.base + Reg.output; + Reg.second ← Reg.queryCount; Reg.third ← Reg.modulus ]) x z 3 := by + ram_eval [z, hb, ho, hn, hm, haddr, Reg.result, Reg.second, Reg.third, Reg.base, + Reg.output, Reg.queryCount, Reg.modulus, QueriedProduct.output, + QueriedProduct.queryCount, Fin.mk.injEq] + obtain ⟨y, hy, ym, yr, yh⟩ := + exec_normalizeBank (W := W) (b := b + o) (n := n) (M := M) z + (by simp [z, Reg.result, Reg.second, Reg.third]) + (by simp [z, Reg.second, Reg.third]) (by simp [z]) hM (by + simpa only [Nat.add_assoc] using hfit) + refine ⟨y, (hz.seq hy).mono (by omega), ym, yr, ?_⟩ + intro i hi + rw [yh i hi] + have h0 : i ≠ Reg.result := by intro h; subst i; simp [Reg.result] at hi + have h1 : i ≠ Reg.second := by intro h; subst i; simp [Reg.second] at hi + have h2 : i ≠ Reg.third := by intro h; subst i; simp [Reg.third] at hi + simp only [z, Data.set_regs, Function.update_of_ne h0, Function.update_of_ne h1, + Function.update_of_ne h2] + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Setup.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Setup.lean new file mode 100755 index 000000000..1baba6d4d --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Setup.lean @@ -0,0 +1,145 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Program + +/-! +# Computed virtual width and register normalization + +Setup reads the actual original headers and executes the existing ceiling-logarithm and +natural-power programs. No logarithm, modulus, bounded initial scratch or source register +certificate is supplied in the input. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct + +open Cslib.WordRAM + +private theorem clogBank_injective : Function.Injective clogBank := by + intro i j h + exact Fin.ext (congrArg (fun k : Fin 163 => k.val) h) + +private theorem powerBank_injective : Function.Injective powerBank := by + intro i j h + exact Fin.ext (congrArg (fun k : Fin 163 => k.val) h) + +/-- The explicit physical numerical margin covers setup and the smaller native source width. -/ +theorem setup_fits {W b N Q : ℕ} (hfit : Fits W (b + 4 * windowSize N Q + 1024)) : + sourceWidth N Q ≤ W ∧ N + Q + 3 ≤ windowSize N Q ∧ + sourceWidth N Q < windowSize N Q := by + have hlog : Nat.clog 2 (N + Q + 3) ≤ sourceWidth N Q := by unfold sourceWidth; omega + have hp : N + Q + 3 ≤ windowSize N Q := + (Nat.le_pow_clog (by decide) _).trans (Nat.pow_le_pow_right (by decide) hlog) + have hwidth : sourceWidth N Q < windowSize N Q := Nat.lt_two_pow_self + refine ⟨?_, hp, hwidth⟩ + by_contra h + have hW : W ≤ sourceWidth N Q := by omega + have he := Nat.pow_le_pow_right (by decide : 1 ≤ (2 : ℕ)) hW + unfold Fits windowSize at hfit + omega + +/-- Actual setup computes the modulus, normalizes the fixed source register bank and preserves +every caller memory word. Its charge is logarithmic in original dimensions and query count. -/ +theorem exec_setup {W b N Q : ℕ} (x : Data 163) (hb : x.regs Reg.base = b) + (hN : x.mem (b + 3) = N) (hQ : x.mem (b + 4) = Q) + (hfit : Fits W (b + 4 * windowSize N Q + 1024)) : + ∃ y, ExecWithin W setup x y (517 * Nat.clog 2 (N + Q + 3) + 184) ∧ + y.mem = x.mem ∧ + MemoryWindow.Matches (sourceWidth N Q) b + (MemoryWindow.view (sourceWidth N Q) b y) y ∧ + (MemoryWindow.view (sourceWidth N Q) b y).Bounded (sourceWidth N Q) := by + change x.regs (160 : Fin 163) = b at hb + obtain ⟨hw, hS, hwidth⟩ := setup_fits hfit + have hc : Nat.clog 2 (N + Q + 3) ≤ sourceWidth N Q := by unfold sourceWidth; omega + have h0 : Fits W 0 := fits_zero _ + have h2 : Fits W 2 := hfit.of_le (by omega) + have h3 : Fits W 3 := hfit.of_le (by omega) + have h4 : Fits W 4 := hfit.of_le (by omega) + have h128 : Fits W 128 := hfit.of_le (by omega) + have hb3 : Fits W (b + 3) := hfit.of_le (by omega) + have hb4 : Fits W (b + 4) := hfit.of_le (by omega) + have hsum : Fits W (N + Q) := hfit.of_le (by omega) + have hsize : Fits W (N + Q + 3) := hfit.of_le (by omega) + have hs1 : Fits W (N + Q + 3 + 1) := hfit.of_le (by omega) + have hc1 : Fits W (Nat.clog 2 (N + Q + 3) + 1) := hfit.of_le (by omega) + have hexp : Fits W (128 * Nat.clog 2 (N + Q + 3)) := by + apply hfit.of_le + change sourceWidth N Q ≤ _ + omega + have hpfit : Fits W (2 ^ (128 * Nat.clog 2 (N + Q + 3)) + + 128 * Nat.clog 2 (N + Q + 3) + 2) := by + apply hfit.of_le + change windowSize N Q + sourceWidth N Q + 2 ≤ _ + omega + obtain ⟨s, hs, ss, sm, sb⟩ : + ∃ s, ExecWithin W readSize x s 9 ∧ s.regs Reg.result = N + Q + 3 ∧ + s.mem = x.mem ∧ s.regs Reg.base = b := by + refine ⟨?s, ?run, ?result, ?memory, ?base⟩ + case run => ram_eval [readSize, hb, hN, hQ, h3, h4, hb3, hb4, hsum, hsize, + Reg.result, Reg.second, Reg.base, Reg.address, Fin.mk.injEq] + case result => simp +decide + case memory => rfl + case base => simpa +decide [Reg.base, Function.update_apply] using hb + obtain ⟨l, hl, lr, lm, lo, lt⟩ := NatClog.exec_rename clogBank clogBank_injective s h2 + (by change Fits W (s.regs Reg.result + 1); rw [ss]; exact hs1) + (by change Fits W (Nat.clog 2 (s.regs Reg.result) + 1); rw [ss]; exact hc1) + have lc : l.regs Reg.second = Nat.clog 2 (N + Q + 3) := by + change l.regs Reg.second = Nat.clog 2 (s.regs Reg.result) at lr + simpa only [ss] using lr + have lb : l.regs Reg.base = b := (hl.regs_eq (by + simp [NatClog.program, NatClog.body, Code.rename, Code.Preserves, Op.rename, Op.dest, Op.bin, + clogBank, Reg.base, NatClog.result, NatClog.arg, + NatClog.one, NatClog.two])).trans sb + let p := ((l.set Reg.third 128).set Reg.fourth + (Nat.clog 2 (N + Q + 3) * 128)).set Reg.third 2 + have hexp' : Fits W (Nat.clog 2 (N + Q + 3) * 128) := by + simpa only [Nat.mul_comm] using hexp + have hprep : ExecWithin W + (ram[ Reg.third ← #128; Reg.fourth ← Reg.second * Reg.third; Reg.third ← #2 ]) l p 3 := by + ram_eval [p, lc, h128, h2, hexp', Reg.third, Reg.fourth, Reg.second, + Fin.mk.injEq] + obtain ⟨a, ha, av, ac, ao, am, ab, ae⟩ := Power.exec (w := W) 2 + (128 * Nat.clog 2 (N + Q + 3)) (by decide) (p.comap powerBank) + (by simp [p, powerBank, Power.base, Reg.third, Reg.fourth]) + (by simp [p, powerBank, Power.exponent, Reg.third, Reg.fourth, Nat.mul_comm]) hpfit + let z := a.extend powerBank p + have hz : ExecWithin W (Power.program.rename powerBank) p z + (4 * (128 * Nat.clog 2 (N + Q + 3)) + 5) := ha.rename powerBank_injective + have zv : z.regs Reg.result = windowSize N Q := + (powerBank_injective.extend_apply _ _ Power.value).trans av + have zb : z.regs Reg.base = b := by + have hnot : ¬∃ i, powerBank i = Reg.base := by + rintro ⟨i, hi⟩ + have := congrArg (fun k : Fin 163 => k.val) hi + simp only [powerBank, Reg.base, Fin.val_castAdd] at this + omega + change Function.extend powerBank a.regs p.regs Reg.base = b + rw [Function.extend_apply' _ _ _ hnot] + simpa [p, Reg.base, Reg.third, Reg.fourth, + Function.update_apply, Fin.mk.injEq] using lb + let z' := z.set Reg.modulus (windowSize N Q) + have hcopy : ExecWithin (B := .standard) W (.op (.copy Reg.modulus Reg.result)) z z' 1 := by + simpa only [z', zv] using + (ExecWithin.copy (B := .standard) (w := W) (x := z) (d := Reg.modulus) (s := Reg.result)) + have z'b : z'.regs Reg.base = b := by + simpa [z', Reg.base, Reg.modulus, + Function.update_apply, Fin.mk.injEq] using zb + have z'm : z'.regs Reg.modulus = 2 ^ sourceWidth N Q := by simp [z', windowSize] + obtain ⟨y, hy, ym, ymatch, ybound, yr⟩ := MemoryWindow.exec_normalizeRegisters + z' z'b z'm hw + refine ⟨y, ?_, ?_, ymatch, ybound⟩ + · apply (hs.seq (hl.seq (hprep.seq (hz.seq (hcopy.seq hy))))).mono + change 9 + (5 * Nat.clog 2 (s.regs Reg.result) + 5 + _) ≤ _ + rw [ss] + omega + · exact ym.trans (am.trans (lm.trans sm)) + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Width.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Width.lean new file mode 100755 index 000000000..d60c9778a --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Width.lean @@ -0,0 +1,111 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Bounds + +/-! +# Every-wide-width initialized callable product + +The width threshold depends only on original row/occurrence counts and the length/word bound of +the protected caller prefix. The actual program never reads physical width. Generic execution +does not assume query sparsity; only the final quantitative saving does. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Positive logarithmic physical width derived entirely from original input and caller data. -/ +@[expose] def admissionWidth (caller : List ℕ) (N Q P : ℕ) : ℕ := + 256 * Nat.clog 2 (inputBase caller + N + Q + P + 5) + +/-- The concrete threshold remains positive at all boundary parameters. -/ +theorem admissionWidth_pos (caller : List ℕ) (N Q P : ℕ) : 0 < admissionWidth caller N Q P := + Nat.mul_pos (by decide) (Nat.clog_pos (by decide) (by omega)) + +/-- Physical admission covers both the computed virtual window and all caller-prefix words. -/ +theorem admission_bounds {W N Q P : ℕ} (caller : List ℕ) + (hP : ∀ v ∈ caller, v ≤ P) (hW : admissionWidth caller N Q P ≤ W) : + Fits W (inputBase caller + 4 * windowSize N Q + 1024) ∧ + ∀ v ∈ caller, Fits W v := by + have hc : Nat.clog 2 (inputBase caller + N + Q + P + 5) ≤ W := by + unfold admissionWidth at hW + omega + refine ⟨physical_fits ?_, ?_⟩ + · have h := Nat.clog_mono_right 2 (by omega : + inputBase caller + N + Q + 5 ≤ inputBase caller + N + Q + P + 5) + unfold admissionWidth at hW + omega + · intro v hv + have h := hP v hv + have hceil := Nat.le_pow_clog (by decide : 1 < (2 : ℕ)) + (inputBase caller + N + Q + P + 5) + have hpow := Nat.pow_le_pow_right (by decide : 1 ≤ (2 : ℕ)) hc + change v < 2 ^ W + omega + +/-- At every admitted physical width, the initialized callable program returns all requested +exact product words and preserves caller/local-input memory. No sparsity premise is used. -/ +theorem runs_wide {W m N P : ℕ} (caller : List ℕ) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (hN : D m ^ 18 ≤ N) + (hX : ∀ i k, 0 ≤ X i k ∧ X i k ≤ 1) (hY : ∀ k j, 0 ≤ Y k j ∧ Y k j ≤ 1) + (hP : ∀ v ∈ caller, v ≤ P) (hW : admissionWidth caller N qs.length P ≤ W) : + InputFits W (input caller X Y qs) ∧ + ∃ y n, n + 1 ≤ timeBound (m := m) qs + 3 ∧ + Runs W (entryProgram.program Reg.result) (State.init (input caller X Y qs)) + ⟨entryProgram.size, y⟩ n ∧ + Executes W (entryProgram.program Reg.result) (State.init (input caller X Y qs)) + (inputBase caller + QueriedProduct.outputBase (19 * m) m N qs.length) (n + 1) ∧ + y.Bounded W ∧ + (∀ i : Fin qs.length, + y.mem (inputBase caller + (QueriedProduct.outputBase (19 * m) m N qs.length + i.val)) = + Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2)) ∧ + (∀ a, a < QueriedProduct.inputEnd m N qs.length → + y.mem (inputBase caller + a) = + (State.init (input caller X Y qs) : State 163).data.mem (inputBase caller + a)) ∧ + (∀ a, a < inputBase caller ∨ inputBase caller + windowSize N qs.length ≤ a → + y.mem a = (State.init (input caller X Y qs) : State 163).data.mem a) := by + obtain ⟨hfit, hcaller⟩ := admission_bounds caller hP hW + obtain ⟨hi, y, n, hn, hr, he, hy, hv, hin, hf⟩ := runs caller X Y qs hN hX hY hcaller hfit + exact ⟨hi, y, n, by omega, hr, he, hy, hv, hin, hf⟩ + +/-- Complete actual initialized thin saving, including base loading, all translated memory +accesses, span normalization and halt. The occurrence bound is explicit and stronger than a +distinct-set bound for lists containing duplicate requests. -/ +theorem runs_thin {W m N P : ℕ} (caller : List ℕ) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (hN : D m ^ 18 ≤ N) (hQ : qs.length * 2 ^ m ≤ N ^ 2) + (hX : ∀ i k, 0 ≤ X i k ∧ X i k ≤ 1) (hY : ∀ k j, 0 ≤ Y k j ∧ Y k j ≤ 1) + (hP : ∀ v ∈ caller, v ≤ P) (hW : admissionWidth caller N qs.length P ≤ W) : + InputFits W (input caller X Y qs) ∧ + ∃ y n, + Runs W (entryProgram.program Reg.result) (State.init (input caller X Y qs)) + ⟨entryProgram.size, y⟩ n ∧ + Executes W (entryProgram.program Reg.result) (State.init (input caller X Y qs)) + (inputBase caller + QueriedProduct.outputBase (19 * m) m N qs.length) (n + 1) ∧ + ((n + 1 : ℕ) : ℝ) ≤ + 17301504 * ((19 * m + 1 : ℕ) : ℝ) * (N : ℝ) ^ 2 / (D m : ℝ) ^ (1 / 18 : ℝ) ∧ + y.Bounded W ∧ + (∀ i : Fin qs.length, + y.mem (inputBase caller + (QueriedProduct.outputBase (19 * m) m N qs.length + i.val)) = + Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2)) ∧ + (∀ a, a < QueriedProduct.inputEnd m N qs.length → + y.mem (inputBase caller + a) = + (State.init (input caller X Y qs) : State 163).data.mem (inputBase caller + a)) ∧ + (∀ a, a < inputBase caller ∨ inputBase caller + windowSize N qs.length ≤ a → + y.mem a = (State.init (input caller X Y qs) : State 163).data.mem a) := by + obtain ⟨hi, y, n, hn, hr, he, hy, hv, hin, hf⟩ := runs_wide caller X Y qs hN hX hY hP hW + have hnR : ((n + 1 : ℕ) : ℝ) ≤ (timeBound (m := m) qs + 3 : ℕ) := by exact_mod_cast hn + exact ⟨hi, y, n, hr, he, hnR.trans (thin_timeBound_sparse qs hN hQ), hy, hv, hin, hf⟩ + +end Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage.CallableProduct diff --git a/Complexity/Algorithms/WordRAM/NatClog.lean b/Complexity/Algorithms/WordRAM/NatClog.lean new file mode 100644 index 000000000..215117b05 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/NatClog.lean @@ -0,0 +1,116 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Notation +public import Mathlib.Data.Nat.Log + +/-! +# A charged native ceiling binary logarithm + +## Main definitions + +* `NatClog.program`: successive ceiling division by two. + +## Main results + +* `NatClog.exec`: the ceiling logarithm with a logarithmic charged instruction bound. +-/ + +@[expose] public section + +namespace Cslib.Algorithms.WordRAM.NatClog + +open Cslib.WordRAM + +/-- Quotient, initially the input. -/ +abbrev arg : Fin 4 := 0 +/-- Ceiling logarithm output. -/ +abbrev result : Fin 4 := 1 +/-- Constant one. -/ +abbrev one : Fin 4 := 2 +/-- Constant two. -/ +abbrev two : Fin 4 := 3 + +/-- Divide the quotient by two and increment the answer. -/ +def body : Code .standard 4 := ram[ arg ← arg + one; arg ← arg / two; result ← result + one ] + +/-- Fixed code for `Nat.clog 2`, including its value zero at input zero. -/ +def program : Code .standard 4 := + .seq (ram[ result ← #0; one ← #1; two ← #2 ]) (.loop (.lt one arg) body) + +/-- Compute the ceiling binary logarithm in at most `5 ⌈log₂ n⌉ + 5` instructions. +Memory is untouched and the input quotient is consumed. -/ +theorem exec {w : ℕ} (x : Data 4) (h2 : Fits w 2) (hn : Fits w (x.regs arg + 1)) + (hout : Fits w (Nat.clog 2 (x.regs arg) + 1)) : + ∃ y, ExecWithin w program x y (5 * Nat.clog 2 (x.regs arg) + 5) ∧ + y.regs result = Nat.clog 2 (x.regs arg) ∧ y.mem = x.mem ∧ + y.regs one = 1 ∧ y.regs two = 2 := by + let I (y : Data 4) : Prop := y.regs one = 1 ∧ y.regs two = 2 ∧ + y.regs arg ≤ x.regs arg ∧ + y.regs result + Nat.clog 2 (y.regs arg) = Nat.clog 2 (x.regs arg) ∧ y.mem = x.mem + have hbody (y : Data 4) (hy : I y) (hc : (Cond.lt one arg).Holds y) : + ∃ z, ExecWithin w body y z 3 ∧ I z ∧ + Nat.clog 2 (z.regs arg) < Nat.clog 2 (y.regs arg) := by + obtain ⟨ho, ht, harg, hsum, hm⟩ := hy + have hgt : 1 < y.regs arg := by simpa only [Cond.holds_lt, ho] using hc + have hpos : 0 < Nat.clog 2 (y.regs arg) := Nat.clog_pos (by decide) (by omega) + have hadd : Fits w (y.regs arg + 1) := hn.of_le (by omega) + have hdiv : Fits w ((y.regs arg + 1) / 2) := + hn.of_le ((Nat.div_le_self _ _).trans (by omega)) + have hinc : Fits w (y.regs result + 1) := hout.of_le (by omega) + refine ⟨?z, ?run, ?inv, ?decrease⟩ + case run => ram_eval [body, ho, ht, hadd, hdiv, hinc, arg, result, one, two, Fin.mk.injEq] + case inv => + refine ⟨?_, ?_, ?_, ?_, ?_⟩ + · simpa +decide [Function.update_apply] using ho + · simpa +decide [Function.update_apply] using ht + · simpa +decide [Function.update_apply] using + (show (y.regs arg + 1) / 2 ≤ x.regs arg from by omega) + · change y.regs result + 1 + Nat.clog 2 ((y.regs arg + 1) / 2) = Nat.clog 2 (x.regs arg) + have h := Nat.clog_of_two_le (b := 2) (by decide) (by omega : 2 ≤ y.regs arg) + simp only [show y.regs arg + 2 - 1 = y.regs arg + 1 by omega] at h + omega + · exact hm + case decrease => + change Nat.clog 2 ((y.regs arg + 1) / 2) < Nat.clog 2 (y.regs arg) + have h := Nat.clog_of_two_le (b := 2) (by decide) (by omega : 2 ≤ y.regs arg) + simp only [show y.regs arg + 2 - 1 = y.regs arg + 1 by omega] at h + omega + let x₀ := ((x.set result 0).set one 1).set two 2 + have hx₀ : I x₀ := by simp +decide [I, x₀, arg, result, one, two] + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I + (fun y => Nat.clog 2 (y.regs arg)) hbody hx₀ + obtain ⟨ho, ht, harg, hsum, hm⟩ := hy + have hzero : Nat.clog 2 (y.regs arg) = 0 := by + have hle : y.regs arg ≤ 1 := by simpa only [Cond.holds_lt, ho, not_lt] using hc + exact Nat.clog_of_right_le_one hle 2 + refine ⟨y, ?_, by omega, hm, ho, ht⟩ + have h1 : Fits w 1 := h2.of_le (by decide) + have hinit : ExecWithin (B := .standard) w (ram[ result ← #0; one ← #1; two ← #2 ]) x x₀ 3 := by + ram_eval [x₀, h1, h2] + convert hinit.seq he using 1 + · rfl + · simp +decide only [x₀, Data.set_regs, Function.update_apply, arg, result, one, two, + ↓reduceIte] + omega + +/-- Run the routine on any injectively chosen registers. -/ +theorem exec_rename {r w : ℕ} (f : Fin 4 → Fin r) (hf : f.Injective) (x : Data r) + (h2 : Fits w 2) (hn : Fits w (x.regs (f arg) + 1)) + (hout : Fits w (Nat.clog 2 (x.regs (f arg)) + 1)) : + ∃ y, ExecWithin w (program.rename f) x y (5 * Nat.clog 2 (x.regs (f arg)) + 5) ∧ + y.regs (f result) = Nat.clog 2 (x.regs (f arg)) ∧ y.mem = x.mem ∧ + y.regs (f one) = 1 ∧ y.regs (f two) = 2 := by + obtain ⟨a, ha, hr, hm, ho, ht⟩ := exec (x.comap f) h2 hn hout + refine ⟨a.extend f x, ha.rename hf, ?_, hm, ?_, ?_⟩ + · exact (hf.extend_apply _ _ result).trans hr + · exact (hf.extend_apply _ _ one).trans ho + · exact (hf.extend_apply _ _ two).trans ht + +end Cslib.Algorithms.WordRAM.NatClog diff --git a/Complexity/Computability/WordRAM/MemoryWindow.lean b/Complexity/Computability/WordRAM/MemoryWindow.lean new file mode 100755 index 000000000..ee191ca49 --- /dev/null +++ b/Complexity/Computability/WordRAM/MemoryWindow.lean @@ -0,0 +1,400 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Structured + +/-! +# Native programs in a caller-owned memory window + +`MemoryWindow.compile` translates standard native code to use a runtime memory base. Three +additional registers hold the base, the source word modulus and an address temporary. A load +executes an addition, a load and a remainder; a store executes an addition and a store. Other +instructions and all branch decisions retain the source register values. + +`MemoryWindow.view` clips memory words to the source width. Consequently arbitrary dirty memory +in the caller's window is allowed: no bulk initialization or copying of that window is implicit +in the translation. The view is only a relation between ordinary native data, not a new machine +or execution semantics. Source executions must still be established using `WordRAM.Exec`. + +This compiler is original native program work. It does not supply an algorithm, an execution +certificate or a bound for any source program. +-/ + +public section + +namespace Cslib.WordRAM.MemoryWindow + +variable {r : ℕ} + +namespace Reg + +/-- Runtime address of source memory cell zero. -/ +@[expose] def base : Fin (r + 3) := ⟨r, by omega⟩ + +/-- Runtime value `2^w` for the source word width `w`. -/ +@[expose] def modulus : Fin (r + 3) := ⟨r + 1, by omega⟩ + +/-- Temporary absolute address used by translated loads and stores. -/ +@[expose] def address : Fin (r + 3) := ⟨r + 2, by omega⟩ + +end Reg + +/-- One source instruction with actual address additions and load normalization. -/ +@[expose] def compileOp : Op .standard r → Code .standard (r + 3) + | .load d a => + .seq (.op (.bin .add Reg.address Reg.base (a.castAdd 3))) + (.seq (.op (.load (d.castAdd 3) Reg.address)) + (.op (.bin .mod (d.castAdd 3) (d.castAdd 3) Reg.modulus))) + | .store a s => + .seq (.op (.bin .add Reg.address Reg.base (a.castAdd 3))) + (.op (.store Reg.address (s.castAdd 3))) + | o => .op (o.rename (Fin.castAdd 3)) + +/-- Rebase all memory accesses, retaining ordinary native branching and loop control. -/ +@[expose] def compile : Code .standard r → Code .standard (r + 3) + | .op o => compileOp o + | .seq a b => .seq (compile a) (compile b) + | .branch c a b => .branch (c.rename (Fin.castAdd 3)) (compile a) (compile b) + | .loop c a => .loop (c.rename (Fin.castAdd 3)) (compile a) + | .rand d s => .rand (d.castAdd 3) (s.castAdd 3) + +/-- Source data represented by a target register bank and clipped, shifted memory. -/ +@[expose] def view (w b : ℕ) (x : Data (r + 3)) : Data r := + ⟨fun i => x.regs (i.castAdd 3), fun a => x.mem (b + a) % 2 ^ w⟩ + +/-- Ordinary value equalities relating source and target native data. -/ +structure Matches (w b : ℕ) (x : Data r) (y : Data (r + 3)) : Prop where + /-- The source register bank and clipped memory agree. -/ + view_eq : view w b y = x + /-- The base register has the chosen runtime address. -/ + base_eq : y.regs Reg.base = b + /-- The modulus register has the source word modulus. -/ + modulus_eq : y.regs Reg.modulus = 2 ^ w + +/-- The compiler expands a flat instruction into at most three instructions. -/ +theorem size_compileOp (o : Op .standard r) : (compileOp o).size ≤ 3 := by + cases o <;> simp [compileOp] + +/-- Branches and loop overhead also satisfy the factor-three code-size bound. -/ +theorem size_compile (c : Code .standard r) : (compile c).size ≤ 3 * c.size := by + induction c with + | op o => exact size_compileOp o + | seq a b ha hb | branch c a b ha hb => simp only [compile, Code.size]; omega + | loop c a ha => simp only [compile, Code.size]; omega + | rand => simp [compile] + +/-- Memory rebasing introduces no reads of the physical word width. -/ +theorem readsWidth_compileOp (o : Op .standard r) : (compileOp o).ReadsWidth ↔ o.ReadsWidth := by + cases o <;> simp [compileOp, Code.ReadsWidth, Op.rename, Op.ReadsWidth, Op.bin] + +/-- The compiled code reads the word width exactly when the source does. -/ +theorem readsWidth_compile (c : Code .standard r) : (compile c).ReadsWidth ↔ c.ReadsWidth := by + induction c <;> simp_all [compile, Code.ReadsWidth, readsWidth_compileOp] + +private theorem bank_ne_base (i : Fin r) : i.castAdd 3 ≠ (Reg.base : Fin (r + 3)) := by + intro h + have := congrArg Fin.val h + simp only [Fin.val_castAdd, Reg.base] at this + omega + +private theorem bank_ne_modulus (i : Fin r) : i.castAdd 3 ≠ (Reg.modulus : Fin (r + 3)) := by + intro h + have := congrArg Fin.val h + simp only [Fin.val_castAdd, Reg.modulus] at this + omega + +private theorem bank_ne_address (i : Fin r) : i.castAdd 3 ≠ (Reg.address : Fin (r + 3)) := by + intro h + have := congrArg Fin.val h + simp only [Fin.val_castAdd, Reg.address] at this + omega + +private theorem view_set (w b : ℕ) (x : Data (r + 3)) (i : Fin r) (v : ℕ) : + view w b (x.set (i.castAdd 3) v) = (view w b x).set i v := by + ext j + · change Function.update x.regs (i.castAdd 3) v (j.castAdd 3) = + Function.update (fun k : Fin r => x.regs (k.castAdd 3)) i v j + by_cases h : j = i + · subst j; simp + · have h' : j.castAdd 3 ≠ i.castAdd 3 := by + intro he + apply h + exact Fin.ext (congrArg (fun k : Fin (r + 3) => k.val) he) + simp only [Function.update_of_ne h, Function.update_of_ne h'] + · rfl + +private theorem view_address (w b : ℕ) (x : Data (r + 3)) (v : ℕ) : + view w b (x.set Reg.address v) = view w b x := by + ext j + · exact Function.update_of_ne (bank_ne_address j) _ _ + · rfl + +private theorem view_write (w b : ℕ) (x : Data (r + 3)) (a v : ℕ) (hv : Fits w v) : + view w b (x.write (b + a) v) = (view w b x).write a v := by + ext j + · rfl + · change Function.update x.mem (b + a) v (b + j) % 2 ^ w = + Function.update (fun k => x.mem (b + k) % 2 ^ w) a v j + by_cases h : j = a + · subst j; simp [Nat.mod_eq_of_lt hv] + · have h' : b + j ≠ b + a := by omega + simp [Function.update_of_ne h, Function.update_of_ne h'] + +/-- Matching source registers have their exact target-bank values. -/ +theorem Matches.regs {w b : ℕ} {x : Data r} {y : Data (r + 3)} + (h : Matches w b x y) (i : Fin r) : y.regs (i.castAdd 3) = x.regs i := + congrArg (fun z : Data r => z.regs i) h.view_eq + +/-- Matching source memory has the clipped target word at its shifted address. -/ +theorem Matches.mem {w b : ℕ} {x : Data r} {y : Data (r + 3)} + (h : Matches w b x y) (a : ℕ) : y.mem (b + a) % 2 ^ w = x.mem a := + congrArg (fun z : Data r => z.mem a) h.view_eq + +private theorem Matches.set {w b : ℕ} {x : Data r} {y : Data (r + 3)} + (h : Matches w b x y) (i : Fin r) (v : ℕ) : + Matches w b (x.set i v) (y.set (i.castAdd 3) v) := by + refine ⟨by rw [view_set, h.view_eq], ?_, ?_⟩ + · simpa only [Data.set_regs, Function.update_of_ne (Ne.symm (bank_ne_base i))] using h.base_eq + · simpa only [Data.set_regs, Function.update_of_ne (Ne.symm (bank_ne_modulus i))] using + h.modulus_eq + +private theorem Matches.address {w b : ℕ} {x : Data r} {y : Data (r + 3)} + (h : Matches w b x y) (v : ℕ) : Matches w b x (y.set Reg.address v) := by + refine ⟨by rw [view_address, h.view_eq], ?_, ?_⟩ + · simpa [Reg.base, Reg.address, Function.update_apply, Fin.mk.injEq] using h.base_eq + · simpa [Reg.modulus, Reg.address, Function.update_apply, Fin.mk.injEq] using h.modulus_eq + +private theorem Matches.write {w b : ℕ} {x : Data r} {y : Data (r + 3)} + (h : Matches w b x y) (a v : ℕ) (hv : Fits w v) : + Matches w b (x.write a v) (y.write (b + a) v) := + ⟨by rw [view_write _ _ _ _ _ hv, h.view_eq], h.base_eq, h.modulus_eq⟩ + +private theorem set_overwrite (x : Data r) (i : Fin r) (u v : ℕ) : + (x.set i u).set i v = x.set i v := by + ext j + · simp only [Data.set_regs, Function.update_idem] + · rfl + +/-- A translated source instruction has actual native execution within three instructions. +All source addresses fit the bounded source word, so the only writes are inside the runtime +window. Dirty words are normalized by the translated load itself. -/ +theorem exec_compileOp {w W b : ℕ} {o : Op .standard r} {x x' : Data r} + {y : Data (r + 3)} (ho : ¬o.ReadsWidth) (he : o.eval w x = some x') + (hx : x.Bounded w) (hxy : Matches w b x y) (hw : w ≤ W) + (hfit : Fits W (b + 2 ^ w)) : + ∃ y', ExecWithin W (compileOp o) y y' 3 ∧ Matches w b x' y' ∧ + ∀ a, a < b ∨ b + 2 ^ w ≤ a → y'.mem a = y.mem a := by + cases o with + | const d v => + obtain ⟨hv, rfl⟩ := Data.put_eq_some.1 he + refine ⟨y.set (d.castAdd 3) v, (ExecWithin.const (hv.mono hw)).mono (by omega), + hxy.set d v, fun _ _ => rfl⟩ + | copy d s => + simp only [Op.eval, Option.some.injEq] at he + subst x' + refine ⟨y.set (d.castAdd 3) (x.regs s), ?_, hxy.set d _, fun _ _ => rfl⟩ + simpa only [compileOp, Op.rename, hxy.regs s] using + (ExecWithin.copy (w := W) (B := .standard) (d := d.castAdd 3) + (s := s.castAdd 3) (x := y)).mono (by omega : 1 ≤ 3) + | alu f d args => + obtain ⟨v, hf, hv, rfl⟩ := Op.eval_alu_eq_some.1 he + have hargs : y.regs ∘ (Fin.castAdd 3 ∘ args) = x.regs ∘ args := by + funext i + exact hxy.regs (args i) + have hf' := Basis.standard.interp_mono_of_not_readsWidth f ho hw hf hv + refine ⟨y.set (d.castAdd 3) v, ?_, hxy.set d v, fun _ _ => rfl⟩ + exact (ExecWithin.alu (by rw [hargs]; exact hf') (hv.mono hw)).mono (by omega) + | load d a => + simp only [Op.eval, Option.some.injEq] at he + subst x' + have ha : Fits W (b + x.regs a) := hfit.of_le (by have := hx.1 a; unfold Fits at this; omega) + let z := y.set Reg.address (b + x.regs a) + have hz : Matches w b x z := hxy.address _ + have had : z.regs Reg.address = b + x.regs a := by simp [z] + have h1 : ExecWithin W + (.op (.bin .add Reg.address Reg.base (a.castAdd 3))) y z 1 := by + simpa only [BinOp.eval, hxy.base_eq, hxy.regs a] using + (ExecWithin.bin (f := .add) (d := Reg.address) (i := Reg.base) + (j := a.castAdd 3) (x := y) (w := W) (by simpa [hxy.base_eq, hxy.regs a] using ha)) + let z' := z.set (d.castAdd 3) (y.mem (b + x.regs a)) + have h2 : ExecWithin (B := .standard) W (.op (.load (d.castAdd 3) Reg.address)) z z' 1 := by + simpa only [had, z, Data.set_mem] using + (ExecWithin.load (w := W) (B := .standard) (x := z) + (d := d.castAdd 3) (a := Reg.address)) + have hmod : z'.regs Reg.modulus = 2 ^ w := by + simpa only [z', Data.set_regs, + Function.update_of_ne (Ne.symm (bank_ne_modulus d))] using hz.modulus_eq + have hd : z'.regs (d.castAdd 3) = y.mem (b + x.regs a) := by simp [z'] + have hm : y.mem (b + x.regs a) % 2 ^ w = x.mem (x.regs a) := hxy.mem _ + have h3 : ExecWithin W + (.op (.bin .mod (d.castAdd 3) (d.castAdd 3) Reg.modulus)) z' + (z'.set (d.castAdd 3) (x.mem (x.regs a))) 1 := by + simpa only [BinOp.eval, hd, hmod, hm] using + (ExecWithin.bin (f := .mod) (d := d.castAdd 3) (i := d.castAdd 3) + (j := Reg.modulus) (x := z') (w := W) + (by simpa only [BinOp.eval, hd, hmod, hm] using (hx.2 (x.regs a)).mono hw)) + refine ⟨z'.set (d.castAdd 3) (x.mem (x.regs a)), ?_, ?_, fun _ _ => rfl⟩ + · exact h1.seq (h2.seq h3) + · change Matches w b (x.set d _) ((z.set (d.castAdd 3) _).set (d.castAdd 3) _) + rw [set_overwrite] + exact hz.set d _ + | store a s => + simp only [Op.eval, Option.some.injEq] at he + subst x' + have ha : Fits W (b + x.regs a) := hfit.of_le (by have := hx.1 a; unfold Fits at this; omega) + let z := y.set Reg.address (b + x.regs a) + have hz : Matches w b x z := hxy.address _ + have had : z.regs Reg.address = b + x.regs a := by simp [z] + have h1 : ExecWithin W + (.op (.bin .add Reg.address Reg.base (a.castAdd 3))) y z 1 := by + simpa only [BinOp.eval, hxy.base_eq, hxy.regs a] using + (ExecWithin.bin (f := .add) (d := Reg.address) (i := Reg.base) + (j := a.castAdd 3) (x := y) (w := W) (by simpa [hxy.base_eq, hxy.regs a] using ha)) + have h2 : ExecWithin (B := .standard) W (.op (.store Reg.address (s.castAdd 3))) z + (z.write (b + x.regs a) (x.regs s)) 1 := by + simpa only [had, hz.regs s] using + (ExecWithin.store (w := W) (B := .standard) (x := z) + (a := Reg.address) (s := s.castAdd 3)) + refine ⟨z.write (b + x.regs a) (x.regs s), (h1.seq h2).mono (by omega), + hz.write _ _ (hx.1 s), ?_⟩ + intro j hj + have hja : j ≠ b + x.regs a := by + have := hx.1 a + unfold Fits at this + omega + exact Function.update_of_ne hja _ _ + +private theorem Matches.holds {w b : ℕ} {x : Data r} {y : Data (r + 3)} + (h : Matches w b x y) (c : Cond r) : (c.rename (Fin.castAdd 3)).Holds y ↔ c.Holds x := by + cases c <;> simp only [Cond.rename, Cond.Holds, h.regs] + +/-- Clipped memory is source-word bounded whenever the represented register bank is bounded. +No condition is imposed on the unnormalized physical memory words. -/ +theorem view_bounded (w b : ℕ) (y : Data (r + 3)) + (hregs : ∀ i : Fin r, Fits w (y.regs (i.castAdd 3))) : (view w b y).Bounded w := + ⟨hregs, fun _ => Nat.mod_lt _ (Nat.two_pow_pos _)⟩ + +/-- Every actual bounded deterministic source execution gives an actual target execution with +at most three times its charged instructions. The base and modulus survive, source registers +and clipped memory agree, and the entire caller memory outside the reserved window survives. +This is a compiler correctness rule over the existing native `Exec`, not an assumed execution +contract for an algorithm. -/ +theorem exec {w W b : ℕ} {c : Code .standard r} {x x' : Data r} {n : ℕ} + (he : Exec w c x x' n) (hc : ¬c.ReadsWidth) (hx : x.Bounded w) + {y : Data (r + 3)} (hxy : Matches w b x y) (hw : w ≤ W) + (hfit : Fits W (b + 2 ^ w)) : + ∃ y', ExecWithin W (compile c) y y' (3 * n) ∧ Matches w b x' y' ∧ + ∀ a, a < b ∨ b + 2 ^ w ≤ a → y'.mem a = y.mem a := by + induction he generalizing y with + | op ho => exact exec_compileOp hc ho hx hxy hw hfit + | seq h₁ h₂ ih₁ ih₂ => + obtain ⟨z, hz, hzm, hzf⟩ := ih₁ (fun h => hc (Or.inl h)) hx hxy + obtain ⟨z', hz', hz'm, hz'f⟩ := ih₂ (fun h => hc (Or.inr h)) (h₁.bounded hx) hzm + refine ⟨z', (hz.seq hz').mono (by omega), hz'm, ?_⟩ + intro a ha + exact (hz'f a ha).trans (hzf a ha) + | branch_pos hp h ih => + obtain ⟨z, hz, hzm, hzf⟩ := ih (fun h => hc (Or.inl h)) hx hxy + refine ⟨z, ?_, hzm, hzf⟩ + exact (ExecWithin.branch_pos ((hxy.holds _).2 hp) hz).mono (by omega) + | branch_neg hp h ih => + obtain ⟨z, hz, hzm, hzf⟩ := ih (fun h => hc (Or.inr h)) hx hxy + refine ⟨z, ?_, hzm, hzf⟩ + exact (ExecWithin.branch_neg (fun h => hp ((hxy.holds _).1 h)) hz).mono (by omega) + | loop_exit hp => + refine ⟨y, ?_, hxy, fun _ _ => rfl⟩ + exact (Exec.loop_exit (fun h => hp ((hxy.holds _).1 h))).within.mono (by omega) + | loop_iter hp h₁ h₂ ih₁ ih₂ => + obtain ⟨z, hz, hzm, hzf⟩ := ih₁ hc hx hxy + obtain ⟨z', hz', hz'm, hz'f⟩ := ih₂ hc (h₁.bounded hx) hzm + refine ⟨z', ?_, hz'm, ?_⟩ + · exact (ExecWithin.loop_iter ((hxy.holds _).2 hp) hz hz').mono (by omega) + · intro a ha + exact (hz'f a ha).trans (hzf a ha) + +/-- The bounded-time form of memory-window compiler correctness. -/ +theorem execWithin {w W b T : ℕ} {c : Code .standard r} {x x' : Data r} + (he : ExecWithin w c x x' T) (hc : ¬c.ReadsWidth) (hx : x.Bounded w) + {y : Data (r + 3)} (hxy : Matches w b x y) (hw : w ≤ W) + (hfit : Fits W (b + 2 ^ w)) : + ∃ y', ExecWithin W (compile c) y y' (3 * T) ∧ Matches w b x' y' ∧ + ∀ a, a < b ∨ b + 2 ^ w ≤ a → y'.mem a = y.mem a := by + obtain ⟨n, hn, he⟩ := he + obtain ⟨z, hz, hzm, hzf⟩ := exec he hc hx hxy hw hfit + exact ⟨z, hz.mono (by omega), hzm, hzf⟩ + +/-- Normalize a static list of source registers, retaining the runtime base and modulus. -/ +@[expose] def normalizeBankList : List (Fin r) → Code .standard (r + 3) + | [] => .op (.copy Reg.address Reg.address) + | i :: is => .seq (.op (.bin .mod (i.castAdd 3) (i.castAdd 3) Reg.modulus)) + (normalizeBankList is) + +/-- A fixed instruction sequence normalizing each source register once. The final self-copy +also makes the zero-register case an ordinary nonempty native program. -/ +@[expose] def normalizeRegisters (r : ℕ) : Code .standard (r + 3) := + normalizeBankList (List.finRange r) + +private theorem exec_normalizeBankList {w W : ℕ} (is : List (Fin r)) (x : Data (r + 3)) + (hm : x.regs Reg.modulus = 2 ^ w) (hw : w ≤ W) : + ∃ y, ExecWithin W (normalizeBankList is) x y (is.length + 1) ∧ y.mem = x.mem ∧ + y.regs Reg.base = x.regs Reg.base ∧ y.regs Reg.modulus = 2 ^ w ∧ + (∀ j : Fin r, y.regs (j.castAdd 3) = + if j ∈ is then x.regs (j.castAdd 3) % 2 ^ w else x.regs (j.castAdd 3)) := by + induction is generalizing x with + | nil => + refine ⟨x.set Reg.address (x.regs Reg.address), ExecWithin.copy, rfl, ?_, ?_, ?_⟩ + · simp [Reg.base, Reg.address, Fin.mk.injEq] + · simpa [Reg.modulus, Reg.address, Function.update_apply, Fin.mk.injEq] using hm + · intro j + simp only [List.not_mem_nil, ↓reduceIte, Data.set_regs, + Function.update_of_ne (bank_ne_address j)] + | cons i is ih => + let z := x.set (i.castAdd 3) (x.regs (i.castAdd 3) % 2 ^ w) + have hz : z.regs Reg.modulus = 2 ^ w := by + simpa only [z, Data.set_regs, + Function.update_of_ne (Ne.symm (bank_ne_modulus i))] using hm + have h1 : ExecWithin W + (.op (.bin .mod (i.castAdd 3) (i.castAdd 3) Reg.modulus)) x z 1 := by + simpa only [BinOp.eval, hm] using + (ExecWithin.bin (f := .mod) (d := i.castAdd 3) (i := i.castAdd 3) + (j := Reg.modulus) (x := x) (w := W) + (by simpa only [BinOp.eval, hm] using + (show Fits w (x.regs (i.castAdd 3) % 2 ^ w) from + Nat.mod_lt _ (Nat.two_pow_pos _)).mono hw)) + obtain ⟨y, he, hmem, hbase, hmod, hregs⟩ := ih z hz + refine ⟨y, ?_, hmem, ?_, hmod, ?_⟩ + · simpa only [normalizeBankList, List.length_cons, Nat.add_left_comm] using h1.seq he + · simpa only [z, Data.set_regs, + Function.update_of_ne (Ne.symm (bank_ne_base i))] using hbase + · intro j + rw [hregs] + by_cases hji : j = i + · subst j + simp [z] + · have hji' : j.castAdd 3 ≠ i.castAdd 3 := by + intro heq + exact hji (Fin.ext (congrArg (fun k : Fin (r + 3) => k.val) heq)) + simp only [z, Data.set_regs, Function.update_of_ne hji', List.mem_cons, + hji, false_or] + +/-- Actual register normalization supplies a bounded source view without clearing any memory. +The base, modulus and every caller memory cell are preserved. -/ +theorem exec_normalizeRegisters {w W b : ℕ} (x : Data (r + 3)) + (hb : x.regs Reg.base = b) (hm : x.regs Reg.modulus = 2 ^ w) (hw : w ≤ W) : + ∃ y, ExecWithin W (normalizeRegisters r) x y (r + 1) ∧ y.mem = x.mem ∧ + Matches w b (view w b y) y ∧ (view w b y).Bounded w ∧ + ∀ j : Fin r, y.regs (j.castAdd 3) = x.regs (j.castAdd 3) % 2 ^ w := by + obtain ⟨y, he, hmem, hbase, hmod, hregs⟩ := exec_normalizeBankList (List.finRange r) x hm hw + have hr (j : Fin r) : y.regs (j.castAdd 3) = x.regs (j.castAdd 3) % 2 ^ w := by + simpa only [List.mem_finRange, ↓reduceIte] using hregs j + refine ⟨y, ?_, hmem, ⟨rfl, hbase.trans hb, hmod⟩, ?_, hr⟩ + · simpa only [normalizeRegisters, List.length_finRange] using he + · exact view_bounded w b y (fun j => by rw [hr]; exact Nat.mod_lt _ (Nat.two_pow_pos _)) + +end Cslib.WordRAM.MemoryWindow diff --git a/Complexity/Computability/WordRAM/Normalize.lean b/Complexity/Computability/WordRAM/Normalize.lean new file mode 100755 index 000000000..b7a7f9137 --- /dev/null +++ b/Complexity/Computability/WordRAM/Normalize.lean @@ -0,0 +1,137 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Notation + +/-! +# Charged in-place normalization of an array + +`ArrayNormalize.program` replaces each word of a supplied interval by its remainder modulo a +supplied positive modulus. It executes actual loads, remainders and stores; the untouched +memory need not already be normalized. This small topic module avoids adding an application +specific transformation to the allocation API. +-/ + +@[expose] public section + +namespace Cslib.WordRAM.ArrayNormalize + +/-- First address to normalize. -/ +abbrev base : Fin 7 := 0 +/-- Number of words. -/ +abbrev length : Fin 7 := 1 +/-- Positive normalization modulus. -/ +abbrev modulus : Fin 7 := 2 +/-- Current index. -/ +abbrev index : Fin 7 := 3 +/-- Temporary absolute address. -/ +abbrev address : Fin 7 := 4 +/-- Loaded and normalized word. -/ +abbrev value : Fin 7 := 5 +/-- Constant one. -/ +abbrev one : Fin 7 := 6 + +/-- Normalize one cell with five actual native instructions. -/ +def body : Code .standard 7 := ram[ + address ← base + index; value ← *address; value ← value % modulus; + *address ← value; index ← index + one ] + +/-- Fixed code for in-place normalization of an interval supplied in registers. -/ +def program : Code .standard 7 := + .seq (ram[ index ← #0; one ← #1 ]) (.loop (.lt index length) body) + +/-- Mathematical memory after normalizing a prefix; this is not a machine operation. -/ +def normalizePrefix (mem : ℕ → ℕ) (b M k : ℕ) : ℕ → ℕ := + fun a => if b ≤ a ∧ a < b + k then mem a % M else mem a + +/-- Every cell in the normalized interval contains its original remainder. -/ +theorem normalizePrefix_read (mem : ℕ → ℕ) (b M n j : ℕ) (hj : j < n) : + normalizePrefix mem b M n (b + j) = mem (b + j) % M := by + simp [normalizePrefix, hj] + +/-- Normalizing an interval leaves all outside memory unchanged. -/ +theorem normalizePrefix_outside (mem : ℕ → ℕ) (b M n a : ℕ) + (ha : ¬(b ≤ a ∧ a < b + n)) : normalizePrefix mem b M n a = mem a := by + simp [normalizePrefix, ha] + +private theorem normalizePrefix_succ (mem : ℕ → ℕ) (b M k : ℕ) : + normalizePrefix mem b M (k + 1) = + Function.update (normalizePrefix mem b M k) (b + k) (mem (b + k) % M) := by + funext a + by_cases ha : a = b + k + · subst a; simp [normalizePrefix] + · simp only [normalizePrefix, Function.update_of_ne ha] + have h : (b ≤ a ∧ a < b + (k + 1)) ↔ (b ≤ a ∧ a < b + k) := by omega + simp only [h] + +private theorem normalizePrefix_zero (mem : ℕ → ℕ) (b M : ℕ) : + normalizePrefix mem b M 0 = mem := by + funext a + simp [normalizePrefix] + +/-- In-place normalization costs at most `7n+4` native instructions, including every indexed +load/store and loop decision. The input arguments and all outside memory are preserved. -/ +theorem exec {W b M n : ℕ} (x : Data 7) (hb : x.regs base = b) + (hn : x.regs length = n) (hm : x.regs modulus = M) (hM : 0 < M) + (hfit : Fits W (b + n + M + 2)) : + ∃ y, ExecWithin W program x y (7 * n + 4) ∧ y.mem = normalizePrefix x.mem b M n ∧ + y.regs base = b ∧ y.regs length = n ∧ y.regs modulus = M := by + have h0 : Fits W 0 := fits_zero _ + have h1 : Fits W 1 := hfit.of_le (by omega) + let start := (x.set index 0).set one 1 + have hs : ExecWithin (B := .standard) W (ram[ index ← #0; one ← #1 ]) x start 2 := by + ram_eval [start, h0, h1] + let I (y : Data 7) : Prop := y.regs base = b ∧ y.regs length = n ∧ + y.regs modulus = M ∧ y.regs one = 1 ∧ y.regs index ≤ n ∧ + y.mem = normalizePrefix x.mem b M (y.regs index) + have hi : I start := by + simp [I, start, normalizePrefix_zero, hb, hn, hm, base, length, modulus, index, one] + have step (y : Data 7) (hy : I y) (hc : (Cond.lt index length).Holds y) : + ∃ z, ExecWithin W body y z 5 ∧ I z ∧ n - z.regs index < n - y.regs index := by + obtain ⟨yb, yn, ym, yo, yi, ymem⟩ := hy + have hlt : y.regs index < n := by simpa only [Cond.holds_lt, yn] using hc + have ha : Fits W (b + y.regs index) := hfit.of_le (by omega) + have hv : Fits W (x.mem (b + y.regs index) % M) := + hfit.of_le ((Nat.mod_lt _ hM).le.trans (by omega)) + have hj : Fits W (y.regs index + 1) := hfit.of_le (by omega) + have hread : y.mem (b + y.regs index) = x.mem (b + y.regs index) := by + rw [ymem] + simp [normalizePrefix] + let z := ((((y.set address (b + y.regs index)).set value + (x.mem (b + y.regs index))).set value + (x.mem (b + y.regs index) % M)).write (b + y.regs index) + (x.mem (b + y.regs index) % M)).set index (y.regs index + 1) + have he : ExecWithin W body y z 5 := by + ram_eval [body, z, yb, ym, yo, hread, ha, hv, hj, + base, length, modulus, index, address, value, one, Fin.mk.injEq] + refine ⟨z, he, ?_, ?_⟩ + · refine ⟨?_, ?_, ?_, ?_, ?_, ?_⟩ + · simpa +decide [z, Function.update_apply] using yb + · simpa +decide [z, Function.update_apply] using yn + · simpa +decide [z, Function.update_apply] using ym + · simpa +decide [z, Function.update_apply] using yo + · simpa +decide [z, Function.update_apply] using (show y.regs index + 1 ≤ n by omega) + · simpa +decide only [z, Data.set_regs, Data.set_mem, Data.write_regs, Data.write_mem, + Function.update_apply, ymem, index, address, value, one, Fin.reduceEq, ↓reduceIte] using + (normalizePrefix_succ x.mem b M (y.regs index)).symm + · simp only [z, Data.set_regs, Function.update_self] + omega + obtain ⟨y, he, hy, hc⟩ := ExecWithin.loop_invariant I (fun y => n - y.regs index) step hi + have hj : y.regs index = n := by + have := hy.2.2.2.2.1 + simp only [Cond.holds_lt, hy.2.1] at hc + omega + refine ⟨y, ?_, ?_, hy.1, hy.2.1, hy.2.2.1⟩ + · apply (hs.seq he).mono + have hstart : start.regs index = 0 := by simp [start, index, one] + rw [hstart] + omega + · simpa only [hj] using hy.2.2.2.2.2 + +end Cslib.WordRAM.ArrayNormalize diff --git a/ComplexityTests/SchonhageCallable.lean b/ComplexityTests/SchonhageCallable.lean new file mode 100755 index 000000000..700f8fb8c --- /dev/null +++ b/ComplexityTests/SchonhageCallable.lean @@ -0,0 +1,78 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +-- upstream: local-only (tests) + +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Width + +set_option linter.hashCommand false + +namespace ComplexityTests.SchonhageCallable + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +private def observe {m N : ℕ} (caller : List ℕ) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) : Option (List ℤ × Bool) := + let original := (State.init (CallableProduct.input caller X Y qs) : State 163).data + let b := CallableProduct.inputBase caller + let sentinel := b + CallableProduct.windowSize N qs.length + 7 + let initial := original.write sentinel 91 + (CallableProduct.entryProgram.eval 512 1024 initial).map fun (y, _) => + ((List.range qs.length).map (fun i => Signed.decode (y.mem (y.regs 0 + i))), + decide ((∀ a < b, y.mem a = original.mem a) ∧ + (∀ a < QueriedProduct.inputEnd m N qs.length, y.mem (b + a) = original.mem (b + a)) ∧ + y.mem sentinel = 91)) + +-- Actual raw-one multiplication and duplicate requests at a dynamic nonzero base. +#guard observe [17, 29] (m := 0) (N := 1) (fun _ _ => 1) (fun _ _ => 1) + [(0, 0), (0, 0), (0, 0)] == some ([1, 1, 1], true) + +-- Actual empty query and zero-product cases still restore the caller and local input. +#guard observe [23] (m := 0) (N := 1) (fun _ _ => 1) (fun _ _ => 0) [] == some ([], true) +#guard observe [] (m := 0) (N := 1) (fun _ _ => 1) (fun _ _ => 0) [(0, 0)] == some ([0], true) + +private def X : Matrix (Fin 1) (Fin (D 0)) ℤ := fun _ _ => 1 +private def Y : Matrix (Fin (D 0)) (Fin 1) ℤ := fun _ _ => 1 +private def requests : List (Fin 1 × Fin 1) := [(0, 0)] + +-- Kernel-checked complete initialized execution. The same y is reached by charged Runs, +-- supplies the exact physical signed answer 2, preserves the caller, and reaches halt. +example : ∃ y n, + Runs 512 (CallableProduct.entryProgram.program CallableProduct.Reg.result) + (State.init (CallableProduct.input [17, 29] X Y requests)) + ⟨CallableProduct.entryProgram.size, y⟩ n ∧ + Executes 512 (CallableProduct.entryProgram.program CallableProduct.Reg.result) + (State.init (CallableProduct.input [17, 29] X Y requests)) + (4 + QueriedProduct.outputBase 0 0 1 1) (n + 1) ∧ + y.mem (4 + QueriedProduct.outputBase 0 0 1 1) = 2 ∧ y.mem 2 = 17 ∧ y.mem 3 = 29 := by + obtain ⟨hi, y, n, hn, hr, he, hy, hv, hin, hf⟩ := CallableProduct.runs + (W := 512) [17, 29] X Y requests (by decide +kernel) + (by intro i k; constructor <;> norm_num [X]) + (by intro k j; constructor <;> norm_num [Y]) + (by + intro v hv + simp only [List.mem_cons, List.not_mem_nil, or_false] at hv + rcases hv with rfl | rfl <;> decide +kernel) + (by decide +kernel) + refine ⟨y, n, hr, he, ?_, ?_, ?_⟩ + · have hone : (X * Y) 0 0 = 1 := by decide +kernel + have h := hv (0 : Fin 1) + change y.mem (4 + QueriedProduct.outputBase 0 0 1 1) = Signed.encode ((X * Y) 0 0) at h + rw [hone] at h + exact h + · simpa [CallableProduct.input, CallableProduct.inputBase, State.init, Data.init] using + hf 2 (Or.inl (by decide)) + · simpa [CallableProduct.input, CallableProduct.inputBase, State.init, Data.init] using + hf 3 (Or.inl (by decide)) + +-- The actual computed reservation has a sharp physical-width boundary for this input. +example : Fits 387 (4 + 4 * CallableProduct.windowSize 1 1 + 1024) ∧ + ¬Fits 386 (4 + 4 * CallableProduct.windowSize 1 1 + 1024) := by decide +kernel + +end ComplexityTests.SchonhageCallable diff --git a/ComplexityTests/WordRAMMemoryWindow.lean b/ComplexityTests/WordRAMMemoryWindow.lean new file mode 100755 index 000000000..4f25dfb5a --- /dev/null +++ b/ComplexityTests/WordRAMMemoryWindow.lean @@ -0,0 +1,51 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +-- upstream: local-only (tests) + +import Complexity.Computability.WordRAM.MemoryWindow +import Complexity.Computability.WordRAM.Normalize +import Mathlib.Tactic.FinCases + +/-! # Dirty-memory, dynamic-base and frame clients for actual memory-window code -/ + +open Cslib.WordRAM + +private def dirty : Data 6 := + ⟨![2, 0, 1, 17, 8, 0], fun a => if a = 19 then 27 else if a = 16 then 99 + else if a = 25 then 77 else 0⟩ + +private def sourceCode : Code .standard 3 := + .seq (.op (.load 1 0)) <| .seq (.op (.bin .add 1 1 2)) (.op (.store 0 1)) + +-- The real masked load reads 27 % 8 = 3, then actual arithmetic/store writes 4. +-- Omitting the remainder would write 28. Both adjacent caller sentinels survive. +example : ∃ y, ExecWithin 8 (MemoryWindow.compile sourceCode) dirty y 6 ∧ + y.mem 19 = 4 ∧ y.mem 16 = 99 ∧ y.mem 25 = 77 ∧ y.regs 3 = 17 ∧ y.regs 4 = 8 := by + refine ⟨?y, ?run, ?value, ?left, ?right, ?base, ?modulus⟩ + case run => + apply ExecWithin.of_eval (fuel := 0) (n := 6) + · rfl + · decide + all_goals decide +kernel + +example : Fits 5 (17 + 2 ^ 3) ∧ ¬Fits 4 (17 + 2 ^ 3) := by decide +kernel + +-- The source view is bounded despite dirty physical memory and caller words above 2^3. +example : (MemoryWindow.view 3 17 dirty : Data 3).Bounded 3 := by + apply MemoryWindow.view_bounded + intro i + fin_cases i <;> decide +kernel + +-- Only the requested span is normalized, including the final cell. +example : ∃ y, ExecWithin 8 ArrayNormalize.program + ⟨![10, 2, 8, 99, 99, 99, 99], fun a => if a = 10 then 27 else 19⟩ y 18 ∧ + y.mem 9 = 19 ∧ y.mem 10 = 3 ∧ y.mem 11 = 3 ∧ y.mem 12 = 19 := by + refine ⟨?y, ?run, ?values⟩ + case run => ram_eval 2 [ArrayNormalize.program, ArrayNormalize.body, ArrayNormalize.base, + ArrayNormalize.length, ArrayNormalize.modulus, ArrayNormalize.index, ArrayNormalize.address, + ArrayNormalize.value, ArrayNormalize.one, Fits, Fin.mk.injEq] + case values => decide +kernel diff --git a/ROADMAP.md b/ROADMAP.md index e388017c1..3f2c15409 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -455,6 +455,15 @@ each statement against the numbered result. Organize modules by subject, not cha fixed 160-register program uses at most `4325376*(19m+1)*N²/D^(1/18)` instructions including halt, with sufficient width `128*clog₂(N+Q+A+2)` for entry bound `A`. The distinct-set sparsity theorem instead retains an explicit additive charge for all `Q` occurrences. + A caller-safe raw-zero/one product ([#1618]) rebases actual memory operations above a runtime + caller base, using a computed source modulus and charged load normalization. Its initialized + 163-register program restores local input, returns an absolute answer pointer, and preserves + the caller outside the reserved polynomial window. Setup/return costs are bounded by three + times the original product charge plus `2048*(N*D+Q+1)`; the same thin geometry and occurrence + sparsity give `17301504*(19m+1)*N²/D^(1/18)`, including halt. Its every-wide-width threshold is + `256*clog₂(base+N+Q+P+5)` for caller-word bound `P`. The explicit reservation + `(2*(N+Q+3))^128` is not bulk-initialized. Actual ET-record reads, matrix/query placement and + producer-readback composition remain the next adapter milestone. Native Chan–He preprocessing and the reduction host are separate stack components. Native sparse-product and Exact Triangle solving, full input/time/width/asymptotic composition, and the improved Section 4 route to exponent 1.9992 remain under #1394; @@ -610,3 +619,4 @@ not planned, not as proved. [#1526]: https://github.com/cslib-community/complexitylib/issues/1526 [#1561]: https://github.com/cslib-community/complexitylib/issues/1561 [#1581]: https://github.com/cslib-community/complexitylib/issues/1581 +[#1618]: https://github.com/cslib-community/complexitylib/issues/1618 diff --git a/UPSTREAM.md b/UPSTREAM.md index 38c8369dc..48d359e03 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -282,6 +282,24 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he proofs, not a transfer of the source Light semantics or instruction constants. Exact Triangle parameter/instance preparation and outer solving, full ordinary-3SUM composition, and the improved Section 4 route to exponent 1.9992 remain separate work under #1394. + Caller-safe queried products ([#1618](https://github.com/cslib-community/complexitylib/issues/1618)) + rebase actual memory accesses above a runtime caller base. The generic `WordRAM.MemoryWindow` + compiler charges an addition/load/remainder for each load and an addition/store for each + store, reusing ordinary native `Exec` and `Code`. Its clipped-memory view permits dirty + workspace without bulk initialization; it is a value relation, not independent run semantics. + The original product program is unchanged. Existing `NatClog` (the exact module already + landed in #1469) and `WordRAM.Power` compute the source modulus from original counts; fixed + register normalization and two `WordRAM.ArrayNormalize` span loops are actual charged code. + The callable raw-zero/one thin-regime client uses 163 registers and at most 131072 flat + instructions. It restores local input, returns exact product words at an absolute pointer, + preserves the caller outside its window, and retains the original charged raw-to-signed + doubling. At `D=4^m`, `L=19m`, `D^18≤N`, its standalone charge including halt is at most + three times the original product charge plus `2048*(N*D+Q+1)`. Distinct-set sparsity retains + the additive occurrence charge; `Q*2^m≤N²` gives `17301504*(19m+1)*N²/D^(1/18)`. + The window is at most `(2*(N+Q+3))^128`; this deliberately large polynomial address-space + reservation is not a claim about a tightly packed physical allocation. Every width above + `256*clog₂(base+N+Q+P+5)` suffices for initialized callers with prefix words bounded by `P`. + The real ET-record/matrix/query adapter and whole solver remain subsequent work under #1394. Stage `exec_*` lemmas are internal implementation APIs exported across proof modules; callers use the flat initialized runs, width theorem or pointer/epoch kernel. The narrow `WordRAM.ArrayAppend` and `WordRAM.InputArray` topic modules keep append/input dependencies From f5e6949201e5dbc18e63a6eb3b993e1bd70eb6e6 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 29 Oct 2026 08:46:03 -0400 Subject: [PATCH 58/73] feat(WordRAM): run queried thin products in caller-owned memory Implements the bounded callable prerequisite #1618 under #1394. Owner/session: codex-three-sum-continuation-t3-3dda8289, personally on work/three-sum-native-callable-product-20261029. Ordinary parent merge904c62068 preserves the reviewed #1615 a8cb7c984cd77e7eb150522e1353a2da22c84839 source. Generic MemoryWindow.compile adds actual add/load/remainder and add/store instructions, retaining ordinary Code/Exec/ExecWithin/Runs semantics. Proved bounded native-source execution transfers within3x charged instructions/code size, with3 additional registers and a full outside-window frame. The clipped memory view is a value relation, not another run semantics or an algorithm oracle. Concrete raw01 CallableProduct derives the actual source execution from original matrices/request occurrences. Native NatClog/Power compute the source modulus; fixed register normalization, translated producer/query execution and two exact input/output span-normalization loops are all charged. Fixed163 registers, <=131072 flat slots including halt, no physical-width reads. Actual initialized prefix Runs and halt refer to the same final data and exact requested products. Local input is restored and caller memory outside[b,b+M) is preserved. Thin admission is explicitly D=4^m,L=19m,D^18<=N, with raw zero/one matrices. M=2^(128*clog2(N+Q+3)) <= (2*(N+Q+3))^128. No whole-window or catalogue clear/copy. Every W>=256*clog2(b+N+Q+P+5) suffices. For arbitrary dirty callers P bounds EVERY initial register/memory word, including physical loads before remainder. For the initialized client scratch is zero and P only bounds the protected caller prefix. All-prefix assertions are physical word fits, not an independent-of-W polynomial intermediate-word theorem. The explicit window reservation is polynomial. Including base loading/halt: charge <=3*(originalT+1)+2048*(N*D+Q+1). Distinct-set sparsity retains+Q; occurrence sparsity Q*2^m<=N^2 gives 17301504*(19m+1)*N^2/D^(1/18). Neither sparsity premise is needed for execution. The ET-record adapter, fast ET/backend/witness composition and full ordinary3SUM 1.9992 remain open. Root retains parameters/prime selection/outer ET host. FINAL VALIDATION: tree f8ff0209469bb668837b9602fb71ecf3aaf3bc99. All SIX together PASS, four compiler threads, cache downloads disabled: lake --no-cache build --wfail --iofail (22.27s) lake --no-cache exe mk_all --check (13.75s) lake --no-cache test --wfail --iofail (183.59s), including Smoke/global Axioms lake --no-cache lint (45.02s) lake --no-cache exe lint-style (22.14s) lake --no-cache exe checkInitImports (16.56s). Final full hygiene --check-simp, actual-base12-library/two-test lints, changed-file checks, strict14-name PR draft audit, COMPLETE a8-to-final diff --check, staged and unstaged diff checks all PASS. Canonical imports generated; all new tracked file modes100644. No sorry/custom axiom/native_decide or new heartbeat override. Only justified finite syntax-check recursion-depth options. Source audit: all3686 inherited source/abstract files and modes unchanged from actual reviewed parenta8. Original Product, Wanted/Pruned/Band/encoding, NatClog and Power suppliers remain unchanged. New MemoryWindow and Callable/Program files are byte-identical to source checkpoint7cb7ade0a. ArrayNormalize native bodies and theorem statements are identical; the only API metadata fix replaces blanket exposure with explicit exposure of its two code definitions. All affected clients check with the public pure-specification lemmas. The deliberately interrupted e8 checkpoint is NOT used as an all-six claim. One overlapping MCP dependency setup had a transient Windows artifact lock; serial retries and final six are clean. Kernel clients tie actual initialized Runs/halt to exact physical output and caller sentinels. Native VM cases cover nonzero/zero products, duplicate/empty queries and dirty workspace above the source modulus. A direct compiler client loads27%8=3, increments/stores4 and preserves both adjacent sentinels. Concrete numerical width budget passes387/fails386; zero N fails the explicit thin premise. Pinned source: anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5, 3sum-apsp/ThreeSumApsp/Sec2/Theorem5.lean61-67 (exact products),146-149 (sparse leaves). Local file equals the pinned Git blob; conclusion fragments checked verbatim. Finite mathematics/source credit is inherited. Compiler/controller/proofs here are original native work, not a source-Light semantics/count transfer. Abstract assessment: no own selected statements, models, catalogue or rendering change. No whole3SUM headline promotion. ROADMAP/UPSTREAM retain the #1394 gap. PUBLICATION LIMIT: #1615 landed DURING this audit via ee1ab6f08ac77b1404d19c0371b0b0ac7daaa915 (reviewed carrier7da96e65cf2c352d08142bb5a7ea65098008d8cc); main now includes later native/frontend additions. The five direct machine/arithmetic prerequisites and entire Schonhage supplier remain byte-identical. This exact validated candidate is preserved on its reviewed a8 parent for coordinator-directed current-main compatibility work before publication. No parent remote is changed. Fresh exact-whole-layer NONAUTHORING compiler/callee/API/resource review is still required and is owned by root. Existing supplier reviews do not approve the new compiler. No new PR, outside approval, merge or endpoint completion is claimed. Local reproducible records: .lake/codex-three-sum-continuation/callable-product/ summary.json,static-summary.json,source-identity.json,source-mapping.json, pr-body.md,audit-source.py and logs. Durable evidence is this commit description; the issue handoff links it. AI assistance: Codex implementation/self-review with Lean MCP; fresh nonauthoring and outside reviews remain pending. --- Complexity.lean | 12 ++++ .../Schonhage/Callable.lean | 4 ++ .../Schonhage/Callable/Bounds.lean | 0 .../Schonhage/Callable/Core.lean | 0 .../Schonhage/Callable/Exec.lean | 0 .../Schonhage/Callable/Flat.lean | 0 .../Schonhage/Callable/Input.lean | 0 .../Schonhage/Callable/Program.lean | 0 .../Schonhage/Callable/Return.lean | 0 .../Schonhage/Callable/Setup.lean | 0 .../Schonhage/Callable/Width.lean | 72 +++++++++++++++++++ .../Computability/WordRAM/MemoryWindow.lean | 0 .../Computability/WordRAM/Normalize.lean | 6 +- ComplexityTests/SchonhageCallable.lean | 19 ++++- ComplexityTests/WordRAMMemoryWindow.lean | 0 UPSTREAM.md | 5 ++ 16 files changed, 112 insertions(+), 6 deletions(-) mode change 100755 => 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Bounds.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Core.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Exec.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Flat.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Input.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Program.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Return.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Setup.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Width.lean mode change 100755 => 100644 Complexity/Computability/WordRAM/MemoryWindow.lean mode change 100755 => 100644 Complexity/Computability/WordRAM/Normalize.lean mode change 100755 => 100644 ComplexityTests/SchonhageCallable.lean mode change 100755 => 100644 ComplexityTests/WordRAMMemoryWindow.lean diff --git a/Complexity.lean b/Complexity.lean index 391cea6be..3452aad5a 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -140,6 +140,16 @@ public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandR public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandSplit public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandTables public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.BandThin +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Bounds +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Core +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Exec +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Flat +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Input +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Program +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Return +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Setup +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Width public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Cell public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Controller public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Copy @@ -1856,6 +1866,7 @@ public import Complexity.Computability.WordRAM.Heap public import Complexity.Computability.WordRAM.InputArray public import Complexity.Computability.WordRAM.ListArray public import Complexity.Computability.WordRAM.MapOp +public import Complexity.Computability.WordRAM.MemoryWindow public import Complexity.Computability.WordRAM.Multiword public import Complexity.Computability.WordRAM.Multiword.Arithmetic public import Complexity.Computability.WordRAM.Multiword.Bank @@ -1873,6 +1884,7 @@ public import Complexity.Computability.WordRAM.Multiword.Sub public import Complexity.Computability.WordRAM.Multiword.Transfer public import Complexity.Computability.WordRAM.Multiword.Workspace public import Complexity.Computability.WordRAM.Native +public import Complexity.Computability.WordRAM.Normalize public import Complexity.Computability.WordRAM.Notation public import Complexity.Computability.WordRAM.PackedCount public import Complexity.Computability.WordRAM.Power diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable.lean old mode 100755 new mode 100644 index 0bbcfbb37..a9debf925 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable.lean @@ -29,6 +29,10 @@ public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Calla outside-window frame. A bounded caller state supplies physical word validity throughout. * `CallableProduct.runs_wide`: actual `State.init` input fit, same-data prefix `Runs` and charged halt at every physical width above `256*clog₂(base+N+Q+P+5)`, for caller-word bound `P`. +* `CallableProduct.runs_caller`, `CallableProduct.caller_prefix_bounded`: the corresponding + callable flat run and every-prefix physical fit with `P` bounding all initial caller words, + including dirty memory words loaded before the real remainder. In initialized clients, + scratch is zero and `P` only needs to bound the protected caller prefix. * `CallableProduct.timeBound_le`: at most three times the original charged product plus `2048*(N*D+Q+1)` standalone adaptation work, including halt. * `CallableProduct.runs_thin`: at `L=19m`, `D=4^m`, `D^18≤N` and `Q*2^m≤N²`, the actual charge diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Bounds.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Bounds.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Core.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Core.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Exec.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Exec.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Flat.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Flat.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Input.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Input.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Program.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Program.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Return.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Return.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Setup.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Setup.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Width.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Width.lean old mode 100755 new mode 100644 index d60c9778a..c10ab2619 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Width.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Width.lean @@ -53,6 +53,64 @@ theorem admission_bounds {W N Q P : ℕ} (caller : List ℕ) change v < 2 ^ W omega +private theorem context_fits {W b N Q P : ℕ} + (hW : 256 * Nat.clog 2 (b + N + Q + P + 5) ≤ W) : Fits W P := by + have hc : Nat.clog 2 (b + N + Q + P + 5) ≤ W := by omega + have hceil := Nat.le_pow_clog (by decide : 1 < (2 : ℕ)) (b + N + Q + P + 5) + have hpow := Nat.pow_le_pow_right (by decide : 1 ≤ (2 : ℕ)) hc + change P < 2 ^ W + omega + +/-- Every native caller prefix is physically word-bounded, including a loaded dirty word +before the following remainder. Here `P` bounds every initial register and memory word, +not merely the source input or normalized values. It is an initial-data bound, not a bound +on intermediates supplied by the caller. -/ +theorem caller_prefix_bounded {W b N Q P k : ℕ} {x : Data 163} {z : State 163} + (hregs : ∀ i, x.regs i ≤ P) (hmem : ∀ a, x.mem a ≤ P) + (hW : 256 * Nat.clog 2 (b + N + Q + P + 5) ≤ W) + (hr : Runs W (program.program Reg.result) ⟨0, x⟩ z k) : z.data.Bounded W := + hr.bounded ⟨fun i => (context_fits hW).of_le (hregs i), + fun a => (context_fits hW).of_le (hmem a)⟩ + +/-- Actual callable flat runs at every admitted physical width, with arbitrary dirty initial +workspace bounded by `P`. This is the interface for a real producer's reachable caller state: +`P` bounds all its initial words, including dirty pre-modulo loads. Original local input words +derive the concrete source execution; no solver, run, time, routing or layout certificate is +assumed. The same final `y` contains exact answers, restored input and the caller frame. -/ +theorem runs_caller {W b m N P : ℕ} + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (x : Data 163) (hb : x.regs Reg.base = b) + (hinput : ∀ a, a < QueriedProduct.inputEnd m N qs.length → + x.mem (b + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem a) + (hX : ∀ i k, 0 ≤ X i k ∧ X i k ≤ 1) (hY : ∀ k j, 0 ≤ Y k j ∧ Y k j ≤ 1) + (hN : D m ^ 18 ≤ N) (hregs : ∀ i, x.regs i ≤ P) (hmem : ∀ a, x.mem a ≤ P) + (hW : 256 * Nat.clog 2 (b + N + qs.length + P + 5) ≤ W) : + ∃ y n, n ≤ timeBound (m := m) qs ∧ + Runs W (program.program Reg.result) ⟨0, x⟩ ⟨program.size, y⟩ n ∧ + Executes W (program.program Reg.result) ⟨0, x⟩ + (b + QueriedProduct.outputBase (19 * m) m N qs.length) (n + 1) ∧ + y.Bounded W ∧ + (∀ i : Fin qs.length, + y.mem (b + (QueriedProduct.outputBase (19 * m) m N qs.length + i.val)) = + Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2)) ∧ + (∀ a, a < QueriedProduct.inputEnd m N qs.length → y.mem (b + a) = x.mem (b + a)) ∧ + (∀ a, a < b ∨ b + windowSize N qs.length ≤ a → y.mem a = x.mem a) := by + have hfit : Fits W (b + 4 * windowSize N qs.length + 1024) := by + apply physical_fits + have h := Nat.clog_mono_right 2 (by omega : + b + N + qs.length + 5 ≤ b + N + qs.length + P + 5) + omega + obtain ⟨y, hy, yr, yb, yM, hv, hi, hf⟩ := exec X Y qs x hb hinput hX hY hN hfit + have hx : x.Bounded W := ⟨fun i => (context_fits hW).of_le (hregs i), + fun a => (context_fits hW).of_le (hmem a)⟩ + have ybound := hy.bounded hx + obtain ⟨n, hn, hy⟩ := hy + refine ⟨y, n, hn, ?_, ?_, ybound, hv, hi, hf⟩ + · simpa only [Nat.zero_add] using hy.runs (pc := 0) + (p := program.program Reg.result) (Program.Contains.self _).append_left + · simpa only [yr] using hy.executes Reg.result + /-- At every admitted physical width, the initialized callable program returns all requested exact product words and preserves caller/local-input memory. No sparsity premise is used. -/ theorem runs_wide {W m N P : ℕ} (caller : List ℕ) @@ -79,6 +137,20 @@ theorem runs_wide {W m N P : ℕ} (caller : List ℕ) obtain ⟨hi, y, n, hn, hr, he, hy, hv, hin, hf⟩ := runs caller X Y qs hN hX hY hcaller hfit exact ⟨hi, y, n, by omega, hr, he, hy, hv, hin, hf⟩ +/-- Every initialized prefix, including raw dirty loads before normalization, fits the admitted +physical width. Outside the explicit original input the initialized memory is zero. Thus `P` +in this initialized interface need only bound the protected caller prefix. -/ +theorem bounded_reachable {W m N P k : ℕ} (caller : List ℕ) + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (hN : D m ^ 18 ≤ N) + (hX : ∀ i k, 0 ≤ X i k ∧ X i k ≤ 1) (hY : ∀ k j, 0 ≤ Y k j ∧ Y k j ≤ 1) + (hP : ∀ v ∈ caller, v ≤ P) (hW : admissionWidth caller N qs.length P ≤ W) + {z : State 163} + (hz : Runs W (entryProgram.program Reg.result) (State.init (input caller X Y qs)) z k) : + z.data.Bounded W := by + obtain ⟨hfit, hcaller⟩ := admission_bounds caller hP hW + exact hz.bounded_init (input_fits caller X Y qs hN hX hY hcaller hfit) + /-- Complete actual initialized thin saving, including base loading, all translated memory accesses, span normalization and halt. The occurrence bound is explicit and stronger than a distinct-set bound for lists containing duplicate requests. -/ diff --git a/Complexity/Computability/WordRAM/MemoryWindow.lean b/Complexity/Computability/WordRAM/MemoryWindow.lean old mode 100755 new mode 100644 diff --git a/Complexity/Computability/WordRAM/Normalize.lean b/Complexity/Computability/WordRAM/Normalize.lean old mode 100755 new mode 100644 index b7a7f9137..e0d6123bb --- a/Complexity/Computability/WordRAM/Normalize.lean +++ b/Complexity/Computability/WordRAM/Normalize.lean @@ -18,7 +18,7 @@ memory need not already be normalized. This small topic module avoids adding an specific transformation to the allocation API. -/ -@[expose] public section +public section namespace Cslib.WordRAM.ArrayNormalize @@ -38,12 +38,12 @@ abbrev value : Fin 7 := 5 abbrev one : Fin 7 := 6 /-- Normalize one cell with five actual native instructions. -/ -def body : Code .standard 7 := ram[ +@[expose] def body : Code .standard 7 := ram[ address ← base + index; value ← *address; value ← value % modulus; *address ← value; index ← index + one ] /-- Fixed code for in-place normalization of an interval supplied in registers. -/ -def program : Code .standard 7 := +@[expose] def program : Code .standard 7 := .seq (ram[ index ← #0; one ← #1 ]) (.loop (.lt index length) body) /-- Mathematical memory after normalizing a prefix; this is not a machine operation. -/ diff --git a/ComplexityTests/SchonhageCallable.lean b/ComplexityTests/SchonhageCallable.lean old mode 100755 new mode 100644 index 700f8fb8c..dbb887423 --- a/ComplexityTests/SchonhageCallable.lean +++ b/ComplexityTests/SchonhageCallable.lean @@ -8,6 +8,7 @@ Authors: Erik Demaine import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Width +-- VM checks exercise actual code alongside the separate kernel-checked general execution. set_option linter.hashCommand false namespace ComplexityTests.SchonhageCallable @@ -18,11 +19,15 @@ open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage private def observe {m N : ℕ} (caller : List ℕ) (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) - (qs : List (Fin N × Fin N)) : Option (List ℤ × Bool) := + (qs : List (Fin N × Fin N)) (dirtyWorkspace : Bool := false) : Option (List ℤ × Bool) := let original := (State.init (CallableProduct.input caller X Y qs) : State 163).data let b := CallableProduct.inputBase caller let sentinel := b + CallableProduct.windowSize N qs.length + 7 - let initial := original.write sentinel 91 + let dirty : Data 163 := ⟨original.regs, fun a => + if dirtyWorkspace && decide (b + QueriedProduct.inputEnd m N qs.length ≤ a ∧ + a < b + CallableProduct.windowSize N qs.length) + then 2 * CallableProduct.windowSize N qs.length + 27 else original.mem a⟩ + let initial := dirty.write sentinel 91 (CallableProduct.entryProgram.eval 512 1024 initial).map fun (y, _) => ((List.range qs.length).map (fun i => Signed.decode (y.mem (y.regs 0 + i))), decide ((∀ a < b, y.mem a = original.mem a) ∧ @@ -37,6 +42,11 @@ private def observe {m N : ℕ} (caller : List ℕ) #guard observe [23] (m := 0) (N := 1) (fun _ _ => 1) (fun _ _ => 0) [] == some ([], true) #guard observe [] (m := 0) (N := 1) (fun _ _ => 1) (fun _ _ => 0) [(0, 0)] == some ([0], true) +-- A preexisting caller workspace can contain physical words larger than the source modulus. +-- This is a supplied dirty caller state; the callee performs no bulk fill or clearing. +#guard observe [17, 29] (m := 0) (N := 1) (fun _ _ => 1) (fun _ _ => 1) + [(0, 0), (0, 0)] true == some ([1, 1], true) + private def X : Matrix (Fin 1) (Fin (D 0)) ℤ := fun _ _ => 1 private def Y : Matrix (Fin (D 0)) (Fin 1) ℤ := fun _ _ => 1 private def requests : List (Fin 1 × Fin 1) := [(0, 0)] @@ -71,8 +81,11 @@ example : ∃ y n, · simpa [CallableProduct.input, CallableProduct.inputBase, State.init, Data.init] using hf 3 (Or.inl (by decide)) --- The actual computed reservation has a sharp physical-width boundary for this input. +-- The numerical reservation/admission budget has a strict width boundary for this input. example : Fits 387 (4 + 4 * CallableProduct.windowSize 1 1 + 1024) ∧ ¬Fits 386 (4 + 4 * CallableProduct.windowSize 1 1 + 1024) := by decide +kernel +-- Empty query lists are admitted at positive geometry; zero N fails the explicit thin premise. +example : ¬D 0 ^ 18 ≤ 0 := by decide +kernel + end ComplexityTests.SchonhageCallable diff --git a/ComplexityTests/WordRAMMemoryWindow.lean b/ComplexityTests/WordRAMMemoryWindow.lean old mode 100755 new mode 100644 diff --git a/UPSTREAM.md b/UPSTREAM.md index a41cb0cfb..d53bdb5bb 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -323,6 +323,11 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he The window is at most `(2*(N+Q+3))^128`; this deliberately large polynomial address-space reservation is not a claim about a tightly packed physical allocation. Every width above `256*clog₂(base+N+Q+P+5)` suffices for initialized callers with prefix words bounded by `P`. + The arbitrary dirty-caller interface uses `P` to bound every initial register and memory + word, including words loaded before the actual remainder; native prefix bounds then follow + from that derived physical admission. Initialized scratch is zero, so its `P` only needs + to bound the protected caller prefix. The generic compiler theorem remains separate from + the concrete thin-geometry admission and saving. The real ET-record/matrix/query adapter and whole solver remain subsequent work under #1394. Stage `exec_*` lemmas are internal implementation APIs exported across proof modules; callers use the flat initialized runs, width theorem or pointer/epoch kernel. The narrow From ce10624831101975600d48d4e51d4bcb2afc6d24 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 29 Oct 2026 09:25:03 -0400 Subject: [PATCH 59/73] refactor(WordRAM): align ArrayNormalize module name and admission prose Address optional review nits on PR1625 at fd1f2fce. Rename Normalize.lean to ArrayNormalize.lean with identical file bytes, migrate every consumer and the sorted canonical aggregate without a compatibility shim, say widths at least the threshold, and record that full VM cases use m0/N1 while general execution/resource proofs cover all valid geometry. Existing review6497681504 is already linked; the missing-record observation was a timing snapshot. Validation: serial targeted Lean MCP ArrayNormalize, WordRAMMemoryWindow and SchonhageCallable all clean. Exact moved-blob identity and comment-erased Lean token identity modulo module-import migration pass; public declaration statements/bodies/attributes and native program meaning are unchanged. Canonical source import order and removal of every old path verified. Full citation/simp hygiene, targeted repository doc-name/new-test checks (12 modules/2 tests), strict14 PR-body names and complete d010-to-final/staged/unstaged diff checks pass. No aggregate/import-generation/all-six run for this bounded delta. Original f5/f8 all-six and final fd1 hosted all-checks green run40879707895 remain scoped to those revisions; repaired head requires refreshed hosted CI before integration. Full compiler/source reviews reused; focused module/import/doc/test-comment delta review requested after push. No abstract/model/resource change or endpoint claim. Evidence .lake/codex-three-sum-continuation/callable-product/review-rename. AI assistance: Codex implemented the mechanical API/documentation repair; no self-approval, human approval or new full review is claimed. --- Complexity.lean | 2 +- .../WordRAM/MatrixMultiplication/Schonhage/Callable.lean | 2 +- .../WordRAM/MatrixMultiplication/Schonhage/Callable/Bounds.lean | 2 +- .../MatrixMultiplication/Schonhage/Callable/Program.lean | 2 +- .../WordRAM/{Normalize.lean => ArrayNormalize.lean} | 0 ComplexityTests/SchonhageCallable.lean | 2 ++ ComplexityTests/WordRAMMemoryWindow.lean | 2 +- ROADMAP.md | 2 +- UPSTREAM.md | 2 +- 9 files changed, 9 insertions(+), 7 deletions(-) rename Complexity/Computability/WordRAM/{Normalize.lean => ArrayNormalize.lean} (100%) diff --git a/Complexity.lean b/Complexity.lean index 095d48f86..227ff8d31 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1967,6 +1967,7 @@ public import Complexity.Computability.USTCON.Reader.Invariant public import Complexity.Computability.USTCON.Space public import Complexity.Computability.WordRAM.Allocation public import Complexity.Computability.WordRAM.ArrayAppend +public import Complexity.Computability.WordRAM.ArrayNormalize public import Complexity.Computability.WordRAM.Basis public import Complexity.Computability.WordRAM.Compile public import Complexity.Computability.WordRAM.Controller @@ -1999,7 +2000,6 @@ public import Complexity.Computability.WordRAM.Multiword.Sub public import Complexity.Computability.WordRAM.Multiword.Transfer public import Complexity.Computability.WordRAM.Multiword.Workspace public import Complexity.Computability.WordRAM.Native -public import Complexity.Computability.WordRAM.Normalize public import Complexity.Computability.WordRAM.Notation public import Complexity.Computability.WordRAM.PackedCount public import Complexity.Computability.WordRAM.Power diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable.lean index a9debf925..c70237b14 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable.lean @@ -28,7 +28,7 @@ public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Calla arbitrary query occurrences and dirty scratch, with exact local-input restoration and full outside-window frame. A bounded caller state supplies physical word validity throughout. * `CallableProduct.runs_wide`: actual `State.init` input fit, same-data prefix `Runs` and charged - halt at every physical width above `256*clog₂(base+N+Q+P+5)`, for caller-word bound `P`. + halt at every physical width at least `256*clog₂(base+N+Q+P+5)`, for caller-word bound `P`. * `CallableProduct.runs_caller`, `CallableProduct.caller_prefix_bounded`: the corresponding callable flat run and every-prefix physical fit with `P` bounding all initial caller words, including dirty memory words loaded before the real remainder. In initialized clients, diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Bounds.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Bounds.lean index 6db6aca92..a0744f350 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Bounds.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Bounds.lean @@ -37,7 +37,7 @@ theorem windowSize_le (N Q : ℕ) : windowSize N Q ≤ (2 * (N + Q + 3)) ^ 128 : have h := Nat.pow_le_pow_left hceil 128 simpa only [windowSize, sourceWidth, ← pow_mul, Nat.mul_comm 128] using h -/-- Every physical width above a positive logarithmic threshold admits the complete runtime +/-- Every physical width at least a positive logarithmic threshold admits the complete runtime window and all setup/return addresses. No intermediate-fit certificate is needed. -/ theorem physical_fits {W b N Q : ℕ} (hW : 256 * Nat.clog 2 (b + N + Q + 5) ≤ W) : diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Program.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Program.lean index 7c8ce187b..3cbb582cd 100644 --- a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Program.lean +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Schonhage/Callable/Program.lean @@ -10,7 +10,7 @@ module public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Product.ThinWidth public import Complexity.Algorithms.WordRAM.NatClog public import Complexity.Computability.WordRAM.MemoryWindow -public import Complexity.Computability.WordRAM.Normalize +public import Complexity.Computability.WordRAM.ArrayNormalize public import Complexity.Computability.WordRAM.Power /-! diff --git a/Complexity/Computability/WordRAM/Normalize.lean b/Complexity/Computability/WordRAM/ArrayNormalize.lean similarity index 100% rename from Complexity/Computability/WordRAM/Normalize.lean rename to Complexity/Computability/WordRAM/ArrayNormalize.lean diff --git a/ComplexityTests/SchonhageCallable.lean b/ComplexityTests/SchonhageCallable.lean index dbb887423..42ee7fc41 100644 --- a/ComplexityTests/SchonhageCallable.lean +++ b/ComplexityTests/SchonhageCallable.lean @@ -9,6 +9,8 @@ Authors: Erik Demaine import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Width -- VM checks exercise actual code alongside the separate kernel-checked general execution. +-- Complete interpreter clients use m=0,N=1: (4^m)^18 ≤ N makes larger depths impractical. +-- Positive-depth thin bounds are established by the general execution/resource theorems. set_option linter.hashCommand false namespace ComplexityTests.SchonhageCallable diff --git a/ComplexityTests/WordRAMMemoryWindow.lean b/ComplexityTests/WordRAMMemoryWindow.lean index 4f25dfb5a..ae21ca830 100644 --- a/ComplexityTests/WordRAMMemoryWindow.lean +++ b/ComplexityTests/WordRAMMemoryWindow.lean @@ -7,7 +7,7 @@ Authors: Erik Demaine -- upstream: local-only (tests) import Complexity.Computability.WordRAM.MemoryWindow -import Complexity.Computability.WordRAM.Normalize +import Complexity.Computability.WordRAM.ArrayNormalize import Mathlib.Tactic.FinCases /-! # Dirty-memory, dynamic-base and frame clients for actual memory-window code -/ diff --git a/ROADMAP.md b/ROADMAP.md index 106272095..d80448735 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -525,7 +525,7 @@ each statement against the numbered result. Organize modules by subject, not cha Setup/return adds at most `2048*(N*D+Q+1)` to three times the original product charge. At `D=4^m`, `L=19m`, `D^18≤N` and `Q*2^m≤N²`, the full charge including halt is at most `17301504*(19m+1)*N²/D^(1/18)`. Distinct-set sparsity retains an additive occurrence charge. - Every width above `256*clog₂(base+N+Q+P+5)` suffices for initialized caller-word bound `P`. + Every width at least `256*clog₂(base+N+Q+P+5)` suffices for initialized caller-word bound `P`. The reserved window is at most `(2*(N+Q+3))^128` and is not bulk-initialized. Actual ET-record reads, matrix/query placement, producer-readback composition and the full [#1394] endpoint remain open. diff --git a/UPSTREAM.md b/UPSTREAM.md index 569c852b8..e55fed516 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -377,7 +377,7 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he three times the original product charge plus `2048*(N*D+Q+1)`. Distinct-set sparsity retains the additive occurrence charge; `Q*2^m≤N²` gives `17301504*(19m+1)*N²/D^(1/18)`. The window is at most `(2*(N+Q+3))^128`; this deliberately large polynomial address-space - reservation is not a claim about a tightly packed physical allocation. Every width above + reservation is not a claim about a tightly packed physical allocation. Every width at least `256*clog₂(base+N+Q+P+5)` suffices for initialized callers with prefix words bounded by `P`. The arbitrary dirty-caller interface uses `P` to bound every initial register and memory word, including words loaded before the actual remainder; native prefix bounds then follow From 8f46341d456f6e3651beb02aac898a22a34a9883 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 29 Oct 2026 10:24:35 -0400 Subject: [PATCH 60/73] feat(ExactTriangle): checkpoint actual record product composition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Personal worker codex-three-sum-continuation-t3-3dda8289, issue1626, ordinary child from1625 repairce1062483; no ancestor remote edits. Implements fixed320-register actual record selector/log/header/copies/complete163-register callable bank, all readback separations from actual producer Output, raw01 identity, occurrence/query-cap sparse bound, native initialized producer→first-record consumer, same-y prefix Runs/chargedhalt/answers/frame, original positive logarithmic width and physical-width-independent polynomial prefix words/support. Generic caller consumes every occupied ordinal. Initialized fixed client selects ordinalzero and proves it exists; this is not an outer instance loop or aggregate ETsolver. PreparationInput.exec_input is the coordinator-authorized narrow API seam, reusing the existing derivation/private fits_at_width; public runs_input reuses it with unchanged statement/hypotheses/budget. No native producer/callee program or input definition changes. All own modules, modified producer module, and new native prefix/copy/admitted closed positive-depth initialized test have clean targeted Lean MCP diagnostics. Tests include actual second-record decode at m2,n16^18 and raw01 copy; the huge complete input family is kernel-checked symbolically, not VM-enumerated. SOURCE CHECKPOINT ONLY: canonical import generation, final all-six, full static/hygiene/source identity, PRmapping and required fresh nonauthoring review remain pending. Root owns sole aggregate/static slot; no such process was launched. Original #1394 ordinary3SUM1.9992, aggregate ET/outerloop/prime/parameter/backend/witness work remain open. No PR/review/approval/merge/watch. Credit retained; original native adaptation/composition by Erik Demaine with Codex AI assistance. --- .../ExactTriangle/PreparationInput.lean | 32 ++- .../WordRAM/ExactTriangle/RecordProduct.lean | 65 +++++ .../ExactTriangle/RecordProduct/Bounds.lean | 154 ++++++++++++ .../ExactTriangle/RecordProduct/Copy.lean | 129 ++++++++++ .../ExactTriangle/RecordProduct/Decode.lean | 128 ++++++++++ .../ExactTriangle/RecordProduct/Entry.lean | 145 +++++++++++ .../ExactTriangle/RecordProduct/Exec.lean | 88 +++++++ .../ExactTriangle/RecordProduct/First.lean | 67 +++++ .../ExactTriangle/RecordProduct/Flat.lean | 99 ++++++++ .../ExactTriangle/RecordProduct/Header.lean | 83 ++++++ .../RecordProduct/InputSpec.lean | 87 +++++++ .../ExactTriangle/RecordProduct/Kernel.lean | 119 +++++++++ .../ExactTriangle/RecordProduct/Prepare.lean | 111 ++++++++ .../ExactTriangle/RecordProduct/Producer.lean | 238 ++++++++++++++++++ .../RecordProduct/ProducerQueries.lean | 91 +++++++ .../ExactTriangle/RecordProduct/Program.lean | 174 +++++++++++++ .../ExactTriangle/RecordProduct/RawInput.lean | 114 +++++++++ .../RecordProduct/SourceBounds.lean | 109 ++++++++ .../WordRAMExactTriangleRecordProduct.lean | 135 ++++++++++ ROADMAP.md | 16 +- UPSTREAM.md | 21 +- 21 files changed, 2194 insertions(+), 11 deletions(-) create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Bounds.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Copy.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Decode.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Entry.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Exec.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/First.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Flat.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Header.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/InputSpec.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Kernel.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Prepare.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Producer.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/ProducerQueries.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Program.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/RawInput.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/SourceBounds.lean create mode 100644 ComplexityTests/WordRAMExactTriangleRecordProduct.lean diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/PreparationInput.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/PreparationInput.lean index 876bc8d8d..f126a6eb9 100644 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/PreparationInput.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/PreparationInput.lean @@ -29,6 +29,7 @@ pre-halt state and the final halt charged. ## Main results +* `PreparationInput.exec_input`: structured execution for composition, without the final halt. * `PreparationInput.runs_input`: initialized execution, exact readback, fits and charged halt. * `PreparationInput.bounded_reachable`: input-derived word and address bounds at every prefix. * `PreparationInput.width_le_log`, `PreparationInput.wordBound_le_pow`: logarithmic physical @@ -213,18 +214,17 @@ private theorem input_fits {n D g p A : ℕ} (values : List ℤ) have hs := hmag.of_natAbs_le ((hv z hz).trans (by omega : A ≤ A + p + 1)) exact (Signed.encode_fits hs).mono (by dsimp only [width]; omega) -/-- Complete initialized execution at every admitted physical width. The prefix witness and -the output predicate use the same actual pre-halt memory, and the bound includes the halt. -/ -theorem runs_input {n D g p A W : ℕ} (values : List ℤ) +/-- Structured initialized execution for real native caller composition. The budget excludes +only the final halt; all existing original-input, width and exact readback hypotheses remain. +This does not accept an execution, layout or intermediate-fit certificate. -/ +theorem exec_input {n D g p A W : ℕ} (values : List ℤ) (hlen : values.length = 3 * (n * n)) (hD : 1 ≤ D) (hDn : D ≤ n) (hg : 1 ≤ g) (hp : 0 < p) (hv : ∀ z ∈ values, z.natAbs ≤ A) (hw : width n D g p A ≤ W) : let C := fun rho => Nat.count (fun q => (values.getD q 0 % (p : ℤ)).toNat = rho) (n * n) let K := ChunkCatalogue.before C (queryCap n D) p - ∃ y t, t ≤ Instances.timeBound n D p K (numPieces n D g) ∧ - Runs W flatProgram (State.init (input n D g p values)) ⟨Instances.program.size, y⟩ - (t - 1) ∧ - Executes W flatProgram (State.init (input n D g p values)) (y.regs Instances.output) t ∧ + ∃ y, ExecWithin W Instances.program (.init (input n D g p values)) y + (Instances.timeBound n D p K (numPieces n D g) - 1) ∧ Instances.Output n D g p (fun q => values.getD q 0) (.init (input n D g p values)) y ∧ InputFits W (input n D g p values) ∧ y.Bounded W := by @@ -244,6 +244,24 @@ theorem runs_input {n D g p A W : ℕ} (values : List ℤ) have hew := ExecWithin.mono_width Instances.program_fixed.2 hy hw have hi := (input_fits values hlen hv).mono hw have hybounded := hew.bounded (Data.Bounded.init hi) + exact ⟨y, hew, hout, hi, hybounded⟩ + +/-- Complete initialized execution at every admitted physical width. The prefix witness and +the output predicate use the same actual pre-halt memory, and the bound includes the halt. -/ +theorem runs_input {n D g p A W : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) + (hD : 1 ≤ D) (hDn : D ≤ n) (hg : 1 ≤ g) (hp : 0 < p) + (hv : ∀ z ∈ values, z.natAbs ≤ A) (hw : width n D g p A ≤ W) : + let C := fun rho => Nat.count (fun q => (values.getD q 0 % (p : ℤ)).toNat = rho) (n * n) + let K := ChunkCatalogue.before C (queryCap n D) p + ∃ y t, t ≤ Instances.timeBound n D p K (numPieces n D g) ∧ + Runs W flatProgram (State.init (input n D g p values)) ⟨Instances.program.size, y⟩ + (t - 1) ∧ + Executes W flatProgram (State.init (input n D g p values)) (y.regs Instances.output) t ∧ + Instances.Output n D g p (fun q => values.getD q 0) + (.init (input n D g p values)) y ∧ + InputFits W (input n D g p values) ∧ y.Bounded W := by + obtain ⟨y, hew, hout, hi, hybounded⟩ := exec_input values hlen hD hDn hg hp hv hw obtain ⟨t, ht, he⟩ := hew have hr := he.runs (pc := 0) (p := flatProgram) (Program.Contains.self _).append_left refine ⟨y, t + 1, ?_, ?_, ?_, hout, hi, hybounded⟩ diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct.lean new file mode 100644 index 000000000..6d0ca1299 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct.lean @@ -0,0 +1,65 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.SourceBounds + +/-! +# Native ExactTriangle record-to-product adapter + +## Main definitions + +* `RecordProduct.program`: fixed 320-register consumer of any occupied producer record ordinal. +* `RecordProduct.entryProgram`: actual original-input producer followed by ordinal-zero selection + and that consumer; original geometry proves the first record exists. +* `RecordProduct.leftMatrix`, `RecordProduct.rightMatrix`: the unchanged native writer's actual + raw zero/one matrices, read in their respective row-major orders. +* `RecordProduct.admissionWidth`, `RecordProduct.wordBound`: original-header/weight bounds for + every sufficiently wide physical execution and its complete prefix words/address support. + +## Main results + +* `RecordProduct.exec_of_output`: actual producer readback derives all six record words, every + matrix/query pointer and separation, the coordinate occurrences and their sparsity; the actual + callee returns all requested product values and preserves the producer catalogue and registers. +* `RecordProduct.runs_input`: complete native `State.init`, same-data prefix `Runs`, returned + pointer, charged halt, exact product words, caller frame and occurrence sparsity. This fixed + initialized client selects the first record, rather than solving all prepared instances. +* `RecordProduct.timeBound_le`: actual callee work plus `O(nD+Q)` descriptor/log/header/copy/ + invocation overhead. There is no whole-window/catalogue initialization or snapshot. +* `RecordProduct.thin_timeBound_sparse`: under `D=4^m`, `L=19m`, `D^18≤n` and `Q·2^m≤n²`, the + per-record bound including halt is `34603008·(19m+1)·n²/D^(1/18)`. The actual chunk derives + its occurrence bound; a mere distinct-set bound instead retains the additive occurrence term. +* `RecordProduct.admissionWidth_le_log`: original polynomial header/weight envelopes give the + positive logarithmic threshold `(257κ+5906)·clog₂(n+2)` as a sufficient upper envelope. +* `RecordProduct.bounded_reachable_original`, `RecordProduct.wordBound_le_pow`: all wider native + prefixes have polynomial words/support independent of the chosen physical width. + +The native code computes the record stride/address, reads every descriptor word, recovers `m` +with the existing binary-logarithm program and division by two, writes original local headers, +and copies raw X/Y and all interleaved coordinate occurrences above the actual producer heap. +The entire 163-register callable bank is renamed together. Memory rebasing is performed by its +actual compiler; register renaming alone is not a memory relocation. Raw matrix entries remain +zero/one during adapter copying: the actual callee already charges their canonical-signed doubling. + +`PreparationInput.exec_input` exposes the existing structured initialized producer execution, +and `PreparationInput.runs_input` reuses it without changing its statement or cost. Stage lemmas +in the submodules are internal implementation interfaces. No intermediate fits, layouts, routing +tables, run/time/solver or sparse-bound certificate is an original input hypothesis. + +This is per-record composition, not an aggregate subcubic ExactTriangle solver. Global parameter +and prime selection, the outer instance loop, backend/witness reconstruction, the full ordinary +distinct-position 3SUM composition and the improved 1.9992 endpoint remain open under #1394. + +## References + +The existing matrix writer and queried-product mathematics retain their pinned +[AlmanWilliams2026] / Anthropic source mapping. This adapter, register framing, initialized +composition and charged native proofs are original work; they do not transfer source Light +semantics or instruction constants. The value-only matrix wrappers reuse `WriteMatrices.entry`. +-/ diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Bounds.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Bounds.lean new file mode 100644 index 000000000..97df3f2d4 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Bounds.lean @@ -0,0 +1,154 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Producer +public import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationInput + +/-! +# Input-derived admission and charged adapter overhead + +The actual producer supplies a polynomial bound for every pre-adapter word and address. +Its existing `wordBound` determines a sufficient positive logarithmic physical width for the +selected product's computed window. Adaptation is linear in `nD+Q`; this per-record statement +does not sum an ExactTriangle solver or establish its aggregate subcubic bound. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.WordRAM Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +/-- Original-input physical width covering the actual producer, every dirty producer word +before normalization, and all relocated product addresses. -/ +@[expose] def admissionWidth (n D g p A : ℕ) : ℕ := + PreparationInput.width n D g p A + + 256 * Nat.clog 2 (2 * PreparationInput.wordBound n D g p A + n + n ^ 2 + 5) + +/-- Physical admission is positive, including parameter boundary cases. -/ +theorem admissionWidth_pos (n D g p A : ℕ) : 0 < admissionWidth n D g p A := by + unfold admissionWidth PreparationInput.width + omega + +/-- Actual producer word/address bounds and the computed chunk size discharge the physical +fit numerically; neither a window nor an intermediate arithmetic certificate is an input. -/ +theorem physical_fits {W n D g p A b Q : ℕ} + (hb : b ≤ PreparationInput.wordBound n D g p A) (hQ : Q ≤ n ^ 2) + (hW : admissionWidth n D g p A ≤ W) : + Fits W (2 * b + 4 * CallableProduct.windowSize n Q + 1024) := by + have hw : 256 * Nat.clog 2 (2 * PreparationInput.wordBound n D g p A + n + n ^ 2 + 5) ≤ W := + by unfold admissionWidth at hW; omega + have hfit := CallableProduct.physical_fits hw + have hM : CallableProduct.windowSize n Q ≤ CallableProduct.windowSize n (n ^ 2) := by + apply Nat.pow_le_pow_right (by decide) + exact Nat.mul_le_mul_left 128 (Nat.clog_mono_right 2 (by omega)) + exact hfit.of_le (by omega) + +/-- The final threshold is logarithmic in the original outer dimension under the producer's +standard polynomial original-header/weight hypotheses. The constant is positive for every κ. -/ +theorem admissionWidth_le_log {n D g p A κ : ℕ} (hn : 1 ≤ n) + (hD : D ≤ n) (hg : g ≤ n) (hp : p ≤ n) (hA : A ≤ (n + 2) ^ κ) : + admissionWidth n D g p A ≤ (257 * κ + 5906) * Nat.clog 2 (n + 2) := by + let b := n + 2 + have hb : 2 ≤ b := by dsimp only [b]; omega + have hP := PreparationInput.wordBound_le_pow hn hD hg hp hA + change PreparationInput.wordBound n D g p A ≤ b ^ (κ + 19) at hP + have hunit : 1 ≤ b ^ (κ + 19) := Nat.one_le_pow _ _ (by omega) + have hnpow : n ≤ b ^ (κ + 19) := (show n ≤ b by dsimp only [b]; omega).trans + (Nat.le_self_pow (by omega) b) + have hn2 : n ^ 2 ≤ b ^ (κ + 19) := + (Nat.pow_le_pow_left (by dsimp only [b]; omega : n ≤ b) 2).trans + (Nat.pow_le_pow_right (by omega : 1 ≤ b) (by omega : 2 ≤ κ + 19)) + have harg : 2 * PreparationInput.wordBound n D g p A + n + n ^ 2 + 5 ≤ b ^ (κ + 23) := by + have hsum : 2 * PreparationInput.wordBound n D g p A + n + n ^ 2 + 5 ≤ + 9 * b ^ (κ + 19) := by omega + have hc : 9 ≤ b ^ 4 := (show 9 ≤ 2 ^ 4 by decide).trans (Nat.pow_le_pow_left hb 4) + calc + _ ≤ 9 * b ^ (κ + 19) := hsum + _ ≤ b ^ 4 * b ^ (κ + 19) := Nat.mul_le_mul_right _ hc + _ = b ^ (κ + 23) := by rw [← Nat.pow_add]; congr 1; omega + have hbase := Nat.le_pow_clog (by decide : 1 < (2 : ℕ)) b + have hlogBound := harg.trans (Nat.pow_le_pow_left hbase (κ + 23)) + rw [← Nat.pow_mul] at hlogBound + have hlog := Nat.clog_le_of_le_pow hlogBound + have hprep := PreparationInput.width_le_log hn hD hg hp hA + change Nat.clog 2 (2 * PreparationInput.wordBound n D g p A + n + n ^ 2 + 5) ≤ + Nat.clog 2 b * (κ + 23) at hlog + unfold admissionWidth + dsimp only [b] at hlog + nlinarith only [hlog, hprep] + +/-- All real descriptor/log/header/copy/invocation work is linear in original matrices and +occurrences. This includes standalone halt, but not another execution of the producer. -/ +theorem timeBound_le {m n : ℕ} (qs : List (Fin n × Fin n)) (hN : D m ^ 18 ≤ n) : + timeBound (m := m) qs + 1 ≤ + 3 * (QueriedProduct.timeBound (show m ≤ 19 * m by omega) qs + 1) + + 4096 * (n * D m + qs.length + 1) := by + have hn : 1 ≤ n := Nat.pow_pos (Nat.pow_pos (by decide : 0 < (4 : ℕ))) |>.trans_le hN + have hm : m ≤ D m := by + have hlog := Nat.log_le_self 2 (4 ^ m) + rw [log_two_four_pow] at hlog + change m ≤ 4 ^ m + omega + have hND : D m ≤ n * D m := Nat.le_mul_of_pos_left _ hn + have ht := CallableProduct.timeBound_le (m := m) qs + unfold timeBound + omega + +/-- The actual callee envelope already dominates a second copy of this linear adaptation. +This comparison reuses its established sparse bounds without a separate polylog oracle. -/ +theorem timeBound_le_twice_callable {m n : ℕ} (qs : List (Fin n × Fin n)) + (hN : D m ^ 18 ≤ n) : + timeBound (m := m) qs + 1 ≤ 2 * (CallableProduct.timeBound (m := m) qs + 3) := by + have hn : 1 ≤ n := Nat.pow_pos (Nat.pow_pos (by decide : 0 < (4 : ℕ))) |>.trans_le hN + have hm : m ≤ n * D m := by + have hlog := Nat.log_le_self 2 (4 ^ m) + rw [log_two_four_pow] at hlog + have hmul : D m ≤ n * D m := Nat.le_mul_of_pos_left _ hn + change 4 ^ m ≤ n * D m at hmul + omega + unfold timeBound CallableProduct.timeBound CallableProduct.coreTime QueriedProduct.timeBound + QueriedProduct.inputEnd + omega + +/-- Distinct-set sparsity retains the actual list-occurrence charge. -/ +theorem thin_timeBound {m n : ℕ} (qs : List (Fin n × Fin n)) (hN : D m ^ 18 ≤ n) + (hQ : qs.toFinset.card * 2 ^ m ≤ n ^ 2) : + ((timeBound (m := m) qs + 1 : ℕ) : ℝ) ≤ + 524288 * ((19 * m + 1 : ℕ) : ℝ) * + (65 * ((D m : ℝ) ^ (-(1 / 18 : ℝ)) * (n : ℝ) ^ 2) + qs.length) := by + have ht : ((timeBound (m := m) qs + 1 : ℕ) : ℝ) ≤ + 2 * ((CallableProduct.timeBound (m := m) qs + 3 : ℕ) : ℝ) := by + exact_mod_cast timeBound_le_twice_callable qs hN + have h := CallableProduct.thin_timeBound qs hN hQ + calc + _ ≤ 2 * ((CallableProduct.timeBound (m := m) qs + 3 : ℕ) : ℝ) := ht + _ ≤ 2 * (262144 * ((19 * m + 1 : ℕ) : ℝ) * + (65 * ((D m : ℝ) ^ (-(1 / 18 : ℝ)) * (n : ℝ) ^ 2) + qs.length)) := by linarith + _ = _ := by ring + +/-- Occurrence sparsity, which actual producer chunks supply, absorbs every occurrence charge. +The bound includes the adapter's standalone halt; producer execution is charged separately. -/ +theorem thin_timeBound_sparse {m n : ℕ} (qs : List (Fin n × Fin n)) + (hN : D m ^ 18 ≤ n) (hQ : qs.length * 2 ^ m ≤ n ^ 2) : + ((timeBound (m := m) qs + 1 : ℕ) : ℝ) ≤ + 34603008 * ((19 * m + 1 : ℕ) : ℝ) * (n : ℝ) ^ 2 / (D m : ℝ) ^ (1 / 18 : ℝ) := by + have ht : ((timeBound (m := m) qs + 1 : ℕ) : ℝ) ≤ + 2 * ((CallableProduct.timeBound (m := m) qs + 3 : ℕ) : ℝ) := by + exact_mod_cast timeBound_le_twice_callable qs hN + have h := CallableProduct.thin_timeBound_sparse qs hN hQ + calc + _ ≤ 2 * ((CallableProduct.timeBound (m := m) qs + 3 : ℕ) : ℝ) := ht + _ ≤ 2 * (17301504 * ((19 * m + 1 : ℕ) : ℝ) * (n : ℝ) ^ 2 / + (D m : ℝ) ^ (1 / 18 : ℝ)) := by linarith + _ = _ := by ring + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Copy.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Copy.lean new file mode 100644 index 000000000..aa2ca1f2c --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Copy.lean @@ -0,0 +1,129 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Header + +/-! +# Three charged original-input copies + +The left and right raw matrices and every interleaved query occurrence are copied separately. +Sources lie below the actual producer heap end. Only the destination spans are written; no +caller catalogue, virtual window or unrelated allocation is cleared or copied. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.WordRAM + +/-- All three original arrays are copied with native loops, at fourteen instructions per +matrix cell/query pair plus constant argument and loop overhead. -/ +theorem exec_copyInputs {W b A Q s t q : ℕ} (x : Data 320) + (hb : x.regs Reg.base = b) (hA : x.regs Reg.area = A) + (hQ : x.regs Reg.count = Q) (hs : x.regs Reg.left = s) + (ht : x.regs Reg.right = t) (hq : x.regs Reg.queries = q) + (hsb : s + A ≤ b) (htb : t + A ≤ b) (hqb : q + 2 * Q ≤ b) + (hfit : Fits W (2 * b + 2 * A + 2 * Q + 64)) : + ∃ y, ExecWithin W copyInputs x y (14 * A + 14 * Q + 21) ∧ + (∀ a < A, y.mem (b + 5 + a) = x.mem (s + a)) ∧ + (∀ a < A, y.mem (b + 5 + A + a) = x.mem (t + a)) ∧ + (∀ a < 2 * Q, y.mem (b + 5 + 2 * A + a) = x.mem (q + a)) ∧ + (∀ a, a < b + 5 ∨ b + 5 + 2 * A + 2 * Q ≤ a → y.mem a = x.mem a) ∧ + ∀ i : Fin 320, i.val < 96 → i ≠ Reg.address → y.regs i = x.regs i := by + have h5 : Fits W 5 := hfit.of_le (by omega) + have hb5 : Fits W (b + 5) := hfit.of_le (by omega) + have hbA : Fits W (b + 5 + A) := hfit.of_le (by omega) + have hbAA : Fits W (b + 5 + A + A) := hfit.of_le (by omega) + have hQQ : Fits W (Q + Q) := hfit.of_le (by omega) + let l := x.set Reg.source s |>.set Reg.address 5 |>.set Reg.destination (b + 5) + |>.set Reg.length A + have hl : ExecWithin W deliverLeft x l 4 := by + ram_eval [deliverLeft, l, hs, hb, hA, h5, hb5, Reg.source, Reg.left, Reg.address, + Reg.destination, Reg.base, Reg.length, Reg.area] + have lmeta (i : Fin 320) (hi : i.val < 96) (hne : i ≠ Reg.address) : + l.regs i = x.regs i := by + have hsource : i ≠ Reg.source := by intro h; subst i; simp [Reg.source] at hi + have hdest : i ≠ Reg.destination := by intro h; subst i; simp [Reg.destination] at hi + have hlen : i ≠ Reg.length := by intro h; subst i; simp [Reg.length] at hi + simp only [l, Data.set_regs, Function.update_of_ne hsource, Function.update_of_ne hdest, + Function.update_of_ne hlen, Function.update_of_ne hne] + obtain ⟨a, ha, am, asrc, adst, alen, ar⟩ := exec_copy l + (by simp +decide [l, Reg.source, Reg.address, Reg.destination, Reg.length]) + (by simp +decide [l, Reg.destination, Reg.length]) (by simp [l]) + (by omega : s + A ≤ b + 5) (hfit.of_le (by omega)) + have aA : a.regs Reg.area = A := + (ar _ (by decide)).trans ((lmeta _ (by decide) (by decide)).trans hA) + have aRight : a.regs Reg.right = t := + (ar _ (by decide)).trans ((lmeta _ (by decide) (by decide)).trans ht) + let r := a.set Reg.source t |>.set Reg.destination (b + 5 + A) + have hr : ExecWithin W deliverRight a r 2 := by + ram_eval [deliverRight, r, aRight, adst, aA, hbA, Reg.source, Reg.right, + Reg.destination, Reg.area] + obtain ⟨c, hc, cm, csrc, cdst, clen, cr⟩ := exec_copy r + (by simp +decide [r, Reg.source, Reg.destination]) (by simp [r]) + (by simpa +decide [r, Reg.length, Reg.source, Reg.destination] using alen) + (by omega : t + A ≤ b + 5 + A) + (hfit.of_le (by omega)) + have rmeta (i : Fin 320) (hi : i.val < 96) : r.regs i = a.regs i := by + have hsource : i ≠ Reg.source := by intro h; subst i; simp [Reg.source] at hi + have hdest : i ≠ Reg.destination := by intro h; subst i; simp [Reg.destination] at hi + simp only [r, Data.set_regs, Function.update_of_ne hsource, Function.update_of_ne hdest] + have cA : c.regs Reg.area = A := + (cr _ (by decide)).trans ((rmeta _ (by decide)).trans aA) + have cq : c.regs Reg.queries = q := (cr _ (by decide)).trans + ((rmeta _ (by decide)).trans + ((ar _ (by decide)).trans ((lmeta _ (by decide) (by decide)).trans hq))) + have cQ : c.regs Reg.count = Q := (cr _ (by decide)).trans + ((rmeta _ (by decide)).trans + ((ar _ (by decide)).trans ((lmeta _ (by decide) (by decide)).trans hQ))) + let z := c.set Reg.source q |>.set Reg.destination (b + 5 + A + A) + |>.set Reg.length (Q + Q) + have hz : ExecWithin W deliverQueries c z 3 := by + ram_eval [deliverQueries, z, cq, cdst, cA, cQ, hbAA, hQQ, Reg.source, + Reg.queries, Reg.destination, Reg.area, Reg.length, Reg.count] + obtain ⟨y, hy, ym, ysrc, ydst, ylen, yr⟩ := exec_copy z + (by simp +decide [z, Reg.source, Reg.destination, Reg.length]) + (by simp +decide [z, Reg.destination, Reg.length]) (by simp [z]) + (by omega : q + (Q + Q) ≤ b + 5 + A + A) (hfit.of_le (by omega)) + have arMem : a.mem = ArrayCopy.copyPrefix x.mem s (b + 5) A := am + have crMem : c.mem = ArrayCopy.copyPrefix a.mem t (b + 5 + A) A := cm + have yrMem : y.mem = ArrayCopy.copyPrefix c.mem q (b + 5 + A + A) (Q + Q) := ym + refine ⟨y, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · exact (hl.seq (ha.seq (hr.seq (hc.seq (hz.seq hy))))).mono (by omega) + · intro j hj + rw [yrMem, ArrayCopy.copyPrefix_outside (by omega), crMem, + ArrayCopy.copyPrefix_outside (by omega), arMem, ArrayCopy.copyPrefix_read hj] + · intro j hj + rw [yrMem, ArrayCopy.copyPrefix_outside (by omega), crMem, ArrayCopy.copyPrefix_read hj, + arMem, ArrayCopy.copyPrefix_outside (by omega)] + · intro j hj + have hj' : j < Q + Q := by omega + rw [show b + 5 + 2 * A + j = b + 5 + A + A + j by omega, + yrMem, ArrayCopy.copyPrefix_read hj', crMem, + ArrayCopy.copyPrefix_outside (by omega), arMem, + ArrayCopy.copyPrefix_outside (by omega)] + · intro j hj + rw [yrMem, ArrayCopy.copyPrefix_outside (by omega), crMem, + ArrayCopy.copyPrefix_outside (by omega), arMem, + ArrayCopy.copyPrefix_outside (by omega)] + · intro i hi hne + have hsource : i ≠ Reg.source := by intro h; subst i; simp [Reg.source] at hi + have hdest : i ≠ Reg.destination := by intro h; subst i; simp [Reg.destination] at hi + have hlen : i ≠ Reg.length := by intro h; subst i; simp [Reg.length] at hi + rw [yr i (Or.inl hi)] + simp only [z, Data.set_regs, Function.update_of_ne hsource, + Function.update_of_ne hdest, Function.update_of_ne hlen] + rw [cr i (Or.inl hi)] + simp only [r, Data.set_regs, Function.update_of_ne hsource, Function.update_of_ne hdest] + rw [ar i (Or.inl hi)] + simp only [l, Data.set_regs, Function.update_of_ne hsource, Function.update_of_ne hdest, + Function.update_of_ne hlen, Function.update_of_ne hne] + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Decode.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Decode.lean new file mode 100644 index 000000000..a539d34bb --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Decode.lean @@ -0,0 +1,128 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Kernel + +/-! +# Actual record selection and depth recovery + +These internal lemmas account for the actual original-header and six descriptor loads, every +stride/address calculation, and the existing floor-logarithm loop. The final producer client +derives each loaded word from its actual readback theorem. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.WordRAM + +/-- The actual binary logarithm recovers twice the power-of-four exponent. -/ +theorem log_two_four_pow (m : ℕ) : Nat.log 2 (4 ^ m) = 2 * m := by + rw [show 4 ^ m = 2 ^ (2 * m) by rw [Nat.pow_mul], Nat.log_pow (by decide)] + +private theorem logBank_injective : Function.Injective logBank := by + intro i j h + apply Fin.ext + simpa only [logBank, Nat.add_left_cancel_iff] using congrArg Fin.val h + +-- This closed preservation test enumerates the finite relocated register bank. +set_option maxRecDepth 4096 in +private theorem log_preserves : ∀ i : Fin 320, + i.val < 96 ∨ 100 ≤ i.val → logarithm.Preserves i := by decide + +/-- The computed logarithm is read from the actual existing loop, with no logarithm input +certificate. Its charge is `8m+5` at a power of four. -/ +theorem exec_logarithm {W m : ℕ} (x : Data 320) + (hD : x.regs Reg.source = 4 ^ m) (hfit : Fits W (4 ^ m + 2 * m + 3)) : + ∃ y, ExecWithin W logarithm x y (8 * m + 5) ∧ + y.regs Reg.destination = 2 * m ∧ y.mem = x.mem ∧ + ∀ i : Fin 320, i.val < 96 ∨ 100 ≤ i.val → y.regs i = x.regs i := by + have harg : x.regs (logBank NatLog.arg) = 4 ^ m := hD + obtain ⟨y, hy, hr, hm, -, -⟩ := NatLog.exec_rename logBank logBank_injective x + (hfit.of_le (by omega)) (by rw [harg]; exact hfit.of_le (by omega)) + (by rw [harg, log_two_four_pow]; exact hfit.of_le (by omega)) + refine ⟨y, ?_, ?_, hm, fun i hi => hy.regs_eq (log_preserves i hi)⟩ + · have he : 4 * Nat.log 2 (x.regs (logBank NatLog.arg)) + 5 = 8 * m + 5 := by + rw [harg, log_two_four_pow]; omega + simpa only [logarithm, he] using hy + · rw [harg, log_two_four_pow] at hr + exact hr + +/-- Read an actual six-word producer record at its computed ordinal. The input memory is +unchanged; arbitrary source words are loaded, rather than supplied as predecoded registers. -/ +theorem exec_readRecord {W n D i out b : ℕ} (x : Data 320) (v : Fin 6 → ℕ) + (hD : x.mem 1 = D) (hn : x.mem 3 = n) + (hi : x.regs Reg.ordinal = i) (hout : x.regs 39 = out) (hb : x.regs 42 = b) + (hrec : out + i * (6 + 2 * (n * D)) + 6 ≤ b) + (hv : ∀ k : Fin 6, x.mem (out + i * (6 + 2 * (n * D)) + k.val) = v k) + (hfit : Fits W (b + 2 * (n * D) + D + n + 128)) : + ∃ y, ExecWithin W readRecord x y 28 ∧ y.mem = x.mem ∧ + y.regs Reg.dimension = D ∧ y.regs Reg.size = n ∧ + y.regs Reg.area = n * D ∧ y.regs Reg.twiceArea = 2 * (n * D) ∧ + y.regs Reg.base = b ∧ y.regs Reg.record = out + i * (6 + 2 * (n * D)) ∧ + y.regs Reg.rho = v 0 ∧ y.regs Reg.piece = v 1 ∧ y.regs Reg.queries = v 2 ∧ + y.regs Reg.count = v 3 ∧ y.regs Reg.left = v 4 ∧ y.regs Reg.right = v 5 ∧ + y.regs Reg.source = D ∧ y.regs Reg.one = 1 ∧ y.regs Reg.two = 2 ∧ + y.regs Reg.nineteen = 19 ∧ y.regs Reg.four = 4 := by + let r := out + i * (n * D + n * D + 6) + have hmul : n * D + n * D + 6 = 6 + 2 * (n * D) := by omega + have hrec' : r + 6 ≤ b := by simpa only [r, hmul] using hrec + have h1 : Fits W 1 := hfit.of_le (by omega) + have h2 : Fits W 2 := hfit.of_le (by omega) + have h3 : Fits W 3 := hfit.of_le (by omega) + have h4 : Fits W 4 := hfit.of_le (by omega) + have h6 : Fits W 6 := hfit.of_le (by omega) + have h19 : Fits W 19 := hfit.of_le (by omega) + have hA : Fits W (n * D) := hfit.of_le (by omega) + have hAA : Fits W (n * D + n * D) := hfit.of_le (by omega) + have hstride : Fits W (n * D + n * D + 6) := hfit.of_le (by omega) + have his : Fits W (i * (n * D + n * D + 6)) := by + rw [hmul]; exact hfit.of_le (by omega) + have hr : Fits W r := hfit.of_le (by dsimp only [r]; omega) + have hr1 : Fits W (r + 1) := hfit.of_le (by dsimp only [r]; omega) + have hr2 : Fits W (r + 1 + 1) := hfit.of_le (by dsimp only [r]; omega) + have hr3 : Fits W (r + 1 + 1 + 1) := hfit.of_le (by dsimp only [r]; omega) + have hr4 : Fits W (r + 1 + 1 + 1 + 1) := hfit.of_le (by dsimp only [r]; omega) + have hr5 : Fits W (r + 1 + 1 + 1 + 1 + 1) := hfit.of_le (by dsimp only [r]; omega) + have hv0 : x.mem r = v 0 := by simpa only [r, hmul, Fin.val_zero, Nat.add_zero] using hv 0 + have hv1 : x.mem (r + 1) = v 1 := by simpa +decide [r, hmul] using hv ⟨1, by decide⟩ + have hv2 : x.mem (r + 1 + 1) = v 2 := by + change x.mem (r + 2) = v 2 + simpa +decide [r, hmul] using hv ⟨2, by decide⟩ + have hv3 : x.mem (r + 1 + 1 + 1) = v 3 := by + change x.mem (r + 3) = v 3 + simpa +decide [r, hmul] using hv ⟨3, by decide⟩ + have hv4 : x.mem (r + 1 + 1 + 1 + 1) = v 4 := by + change x.mem (r + 4) = v 4 + simpa +decide [r, hmul] using hv ⟨4, by decide⟩ + have hv5 : x.mem (r + 1 + 1 + 1 + 1 + 1) = v 5 := by + change x.mem (r + 5) = v 5 + simpa +decide [r, hmul] using hv ⟨5, by decide⟩ + let y := x.set Reg.one 1 |>.set Reg.two 2 |>.set Reg.six 6 |>.set Reg.nineteen 19 + |>.set Reg.four 4 |>.set Reg.address 1 |>.set Reg.dimension D |>.set Reg.address 3 + |>.set Reg.size n |>.set Reg.area (n * D) |>.set Reg.twiceArea (n * D + n * D) + |>.set Reg.address (n * D + n * D + 6) + |>.set Reg.address (i * (n * D + n * D + 6)) |>.set Reg.record r |>.set Reg.base b + |>.set Reg.address r |>.set Reg.rho (v 0) |>.set Reg.address (r + 1) + |>.set Reg.piece (v 1) |>.set Reg.address (r + 1 + 1) |>.set Reg.queries (v 2) + |>.set Reg.address (r + 1 + 1 + 1) |>.set Reg.count (v 3) + |>.set Reg.address (r + 1 + 1 + 1 + 1) |>.set Reg.left (v 4) + |>.set Reg.address (r + 1 + 1 + 1 + 1 + 1) |>.set Reg.right (v 5) |>.set Reg.source D + refine ⟨y, ?run, ?post⟩ + case run => + ram_eval [readRecord, y, hi, hout, hb, hD, hn, h1, h2, h3, h4, h6, h19, hA, hAA, + hstride, his, hr, hr1, hr2, hr3, hr4, hr5, r, hv0, hv1, hv2, hv3, hv4, hv5, + Reg.one, Reg.two, Reg.six, Reg.nineteen, Reg.four, Reg.address, Reg.dimension, + Reg.size, Reg.area, Reg.twiceArea, Reg.ordinal, Reg.record, Reg.base, Reg.rho, + Reg.piece, Reg.queries, Reg.count, Reg.left, Reg.right, Reg.source] + case post => + simp +decide [y, r, two_mul, Nat.add_comm, Nat.add_left_comm, Nat.add_assoc] + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Entry.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Entry.lean new file mode 100644 index 000000000..c39594b23 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Entry.lean @@ -0,0 +1,145 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.First + +/-! +# Initialized producer-to-adapter composition + +One fixed native program runs the complete producer from its original four headers and three +signed weight arrays, selects actual ordinal zero and invokes the actual record consumer. +The general caller interface handles any occupied ordinal; the initialized client here chooses +the first one with real code. Its existence follows from the original positive geometry. + +The producer's pre-halt native witness and polynomial word bound derive every source pointer, +coordinate, caller base and dirty-word bound used by the record consumer. No producer layout, +run, solver or time certificate is an input hypothesis. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.WordRAM Cslib.Algorithms.ExactTriangle Cslib.Matrix +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +/-- The complete real producer, using its original low bank. -/ +@[expose] def produce : Code .standard 320 := Instances.program.rename producerBank + +/-- One fixed initialized composition; selecting the first record costs one actual instruction. -/ +@[expose] def entryProgram : Code .standard 320 := + .seq produce (.seq (.op (.const Reg.ordinal 0)) program) + +/-- The unchanged producer's original-header budget, including its standalone halt. -/ +@[expose] def preparationTime (n D g p : ℕ) (values : List ℤ) : ℕ := + let C := fun rho => Nat.count (fun q => (values.getD q 0 % (p : ℤ)).toNat = rho) (n * n) + Instances.timeBound n D p (ChunkCatalogue.before C (queryCap n D) p) (numPieces n D g) + +private theorem producerBank_injective : Function.Injective producerBank := by + intro i j h + apply Fin.ext + exact congrArg (fun k : Fin 320 => k.val) h + +/-- Complete initialized structured execution. The actual producer execution is a conclusion, +and its same native memory supplies the coordinate list consumed by the composed adapter. -/ +theorem exec_input {W m n g p A : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hD : 16 ≤ D m) + (hg : 1 ≤ g) (hp : 0 < p) (hN : D m ^ 18 ≤ n) + (hv : ∀ z ∈ values, z.natAbs ≤ A) (hW : admissionWidth n (D m) g p A ≤ W) : + InputFits W (PreparationInput.input n (D m) g p values) ∧ + let hn : 0 < n := lt_of_lt_of_le (by omega : 0 < D m) + ((Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN) + ∃ a : Data 64, ∃ idx : InstanceIndex p, + ExecWithin W Instances.program (.init (PreparationInput.input n (D m) g p values)) a + (preparationTime n (D m) g p values - 1) ∧ + idx ∈ instanceIndices (fun a b : Fin n => values.getD (pairIndex (a, b)) 0) (D m) g p ∧ + recordOrdinal n (D m) g p (fun q => values.getD q 0) idx = 0 ∧ + let qs := decodedQueries hn a.mem (recordQueries n (D m) p idx) + (recordQueryCount n (D m) p (fun q => values.getD q 0) idx) + ∃ y, ExecWithin W entryProgram (.init (PreparationInput.input n (D m) g p values)) y + (preparationTime n (D m) g p values + timeBound (m := m) qs) ∧ + y.regs Reg.result = a.regs Instances.heap + + QueriedProduct.outputBase (19 * m) m n qs.length ∧ + (∀ j : Fin qs.length, + y.mem (a.regs Instances.heap + + (QueriedProduct.outputBase (19 * m) m n qs.length + j.val)) = + Signed.encode ((leftMatrix m n g p (fun q => values.getD q 0) idx * + rightMatrix m n g p (fun q => values.getD q 0) idx) (qs.get j).1 (qs.get j).2)) ∧ + (∀ u, u < a.regs Instances.heap ∨ + a.regs Instances.heap + CallableProduct.windowSize n qs.length ≤ u → + y.mem u = a.mem u) ∧ + (∀ j : Fin 64, y.regs (producerBank j) = a.regs j) ∧ + qs.length * 2 ^ m ≤ n ^ 2 := by + have hDn : D m ≤ n := (Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN + have hw : PreparationInput.width n (D m) g p A ≤ W := by + unfold admissionWidth at hW + omega + obtain ⟨a, ha, houtput, hinput, abound⟩ := PreparationInput.exec_input values hlen + (by omega) hDn hg hp hv hw + have ha' : ExecWithin W Instances.program (.init (PreparationInput.input n (D m) g p values)) a + (preparationTime n (D m) g p values - 1) := ha + let x₀ : Data 320 := .init (PreparationInput.input n (D m) g p values) + let x := a.extend producerBank x₀ + have hx : ExecWithin W produce x₀ x (preparationTime n (D m) g p values - 1) := + ha'.rename producerBank_injective + obtain ⟨t, ht, he⟩ := ha' + have hr : Runs W PreparationInput.flatProgram + (State.init (PreparationInput.input n (D m) g p values)) + ⟨Instances.program.size, a⟩ t := by + simpa only [Nat.zero_add, State.init] using he.runs (pc := 0) + (p := PreparationInput.flatProgram) (Program.Contains.self _).append_left + have hwords := PreparationInput.bounded_reachable values hlen (by omega) hDn hg hp hv hw + ⟨Instances.program.size, a⟩ t hr + obtain ⟨idx, hidx, hord⟩ := first_index (n := n) (D := D m) (g := g) (p := p) + (fun q => values.getD q 0) hD hDn hg (by omega) + let z := x.set Reg.ordinal 0 + have hz : ExecWithin (B := .standard) W (.op (.const Reg.ordinal 0)) x z 1 := + ExecWithin.const (fits_zero W) + have zmap : z.comap producerBank = a := by + ext j + · change Function.update x.regs Reg.ordinal 0 (producerBank j) = a.regs j + have hne : producerBank j ≠ Reg.ordinal := by + intro heq + have hh := congrArg Fin.val heq + change j.val = 64 at hh + omega + rw [Function.update_of_ne hne] + exact producerBank_injective.extend_apply _ _ j + · rfl + have hheap : z.regs 42 = a.regs Instances.heap := by + exact congrArg (fun d : Data 64 => d.regs Instances.heap) zmap + have hQ : recordQueryCount n (D m) p (fun q => values.getD q 0) idx ≤ n ^ 2 := by + unfold recordQueryCount + rw [pow_two] + exact (min_le_right _ _).trans ((Nat.sub_le _ _).trans (Nat.count_le _)) + have hfit := physical_fits (b := z.regs 42) (n := n) (D := D m) (g := g) (p := p) (A := A) + (Q := recordQueryCount n (D m) p (fun q => values.getD q 0) idx) + (by rw [hheap]; exact (hwords.1 Instances.heap).le) hQ hW + have zout : Instances.Output n (D m) g p (fun q => values.getD q 0) + (.init (PreparationInput.input n (D m) g p values)) (z.comap producerBank) := by + rw [zmap] + exact houtput + obtain ⟨y, hy, yr, yv, yf, yg, yQ⟩ := exec_of_output (fun q => values.getD q 0) + (.init (PreparationInput.input n (D m) g p values)) z zout hD hg (by omega) hN + rfl rfl idx hidx (by + simpa only [z, Data.set_regs, Function.update_self] using hord.symm) hfit + have hmem : z.mem = a.mem := rfl + simp only [hheap, hmem] at hy yr yv yf yQ + refine ⟨hinput, a, idx, ⟨t, ht, he⟩, hidx, hord, y, ?_, yr, yv, yf, ?_, yQ⟩ + · have hpos : 1 ≤ preparationTime n (D m) g p values := by + dsimp only [preparationTime, Instances.timeBound] + omega + exact (hx.seq (hz.seq hy)).mono (by omega) + · intro j + have hj : j.val < 64 := j.isLt + rw [yg (producerBank j) (by change j.val < 65; omega)] + exact congrArg (fun d : Data 64 => d.regs j) zmap + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Exec.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Exec.lean new file mode 100644 index 000000000..bc0f824b1 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Exec.lean @@ -0,0 +1,88 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Prepare + +/-! +# Complete native per-record product execution + +The native preparation is followed by the complete existing callable product. Every returned +word answers its original occurrence; the whole producer catalogue remains below the owned +window and is preserved. These are internal readback composition rules; the producer client +discharges their descriptor and source-span assertions from its actual initialized execution. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +/-- The complete per-record charge includes actual decoding, copying and callee invocation. -/ +@[expose] def timeBound {m n : ℕ} (qs : List (Fin n × Fin n)) : ℕ := + CallableProduct.timeBound (m := m) qs + 8 * m + 14 * (n * D m) + 14 * qs.length + 71 + +/-- All preparation arithmetic and the original input lie below the already computed virtual +window. This follows from the existing actual product admission, rather than an input fit table. -/ +theorem preparation_margin {m n Q : ℕ} (hN : D m ^ 18 ≤ n) : + 2 * (n * D m) + 2 * Q + 20 * m + D m + n + 256 < CallableProduct.windowSize n Q := by + have hD : D m ≤ n := (Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN + have hbudget := (CallableProduct.source_admission (Q := Q) hN).2.2 + change QueriedProduct.wordBudget (19 * m) m n Q < CallableProduct.windowSize n Q at hbudget + unfold QueriedProduct.wordBudget QueriedProduct.inputEnd at hbudget + omega + +/-- Actual record-to-product execution, with only original input/readback premises and no +solver/run/time oracle. The final initialized client derives these premises from the producer. -/ +theorem exec {W b m n i out rho piece s t q : ℕ} + (X : Matrix (Fin n) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin n) ℤ) + (qs : List (Fin n × Fin n)) (x : Data 320) + (hD : x.mem 1 = D m) (hn : x.mem 3 = n) + (hi : x.regs Reg.ordinal = i) (hout : x.regs 39 = out) (hb : x.regs 42 = b) + (hrec : out + i * (6 + 2 * (n * D m)) + 6 ≤ b) + (hrecord : (Block.mk (out + i * (6 + 2 * (n * D m))) 6).Realizes x.mem + ![rho, piece, q, qs.length, s, t]) + (hsb : s + n * D m ≤ b) (htb : t + n * D m ≤ b) + (hqb : q + 2 * qs.length ≤ b) + (hleft : ∀ a < n * D m, x.mem (s + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem (5 + a)) + (hright : ∀ a < n * D m, x.mem (t + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem + (5 + n * D m + a)) + (hqueries : ∀ a < 2 * qs.length, x.mem (q + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem + (5 + 2 * (n * D m) + a)) + (hX : ∀ a k, 0 ≤ X a k ∧ X a k ≤ 1) (hY : ∀ k a, 0 ≤ Y k a ∧ Y k a ≤ 1) + (hN : D m ^ 18 ≤ n) + (hfit : Fits W (2 * b + 4 * CallableProduct.windowSize n qs.length + 1024)) : + ∃ y, ExecWithin W program x y (timeBound (m := m) qs) ∧ + y.regs Reg.result = b + QueriedProduct.outputBase (19 * m) m n qs.length ∧ + (∀ j : Fin qs.length, + y.mem (b + (QueriedProduct.outputBase (19 * m) m n qs.length + j.val)) = + Signed.encode ((X * Y) (qs.get j).1 (qs.get j).2)) ∧ + (∀ a, a < b ∨ b + CallableProduct.windowSize n qs.length ≤ a → y.mem a = x.mem a) ∧ + ∀ j : Fin 320, j.val < 65 → y.regs j = x.regs j := by + have hmargin := preparation_margin (Q := qs.length) hN + obtain ⟨a, ha, ab, ain, af, ar⟩ := exec_prepare X Y qs x hD hn hi hout hb hrec hrecord + hsb htb hqb hleft hright hqueries (hfit.of_le (by omega)) + obtain ⟨y, hy, yr, yv, yi, yf, yg⟩ := exec_invoke X Y qs a ab ain hX hY hN + (hfit.of_le (by omega)) + refine ⟨y, (ha.seq hy).mono (by unfold timeBound; omega), yr, yv, ?_, ?_⟩ + · intro j hj + rw [yf j hj] + apply af j + unfold QueriedProduct.inputEnd + omega + · intro j hj + have hne : j ≠ Reg.result := by intro h; subst j; simp [Reg.result] at hj + exact (yg j (by omega) hne).trans (ar j hj) + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/First.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/First.lean new file mode 100644 index 000000000..b3828cd25 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/First.lean @@ -0,0 +1,67 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Bounds + +/-! +# The actual first prepared record exists + +The fixed initialized composition selects ordinal zero. An original vertex pair belongs to +one existing residue class, so that class has a chunk and the vertex set has a piece. The native +ordinal characterization then supplies the unique existing finite index of the first record. +No nonempty catalogue or index certificate is an extra runtime input. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.WordRAM Cslib.Algorithms.ExactTriangle Cslib.Matrix + +/-- Under the real producer geometry, ordinal zero always names an existing finite instance. -/ +theorem first_index {n D g p : ℕ} (values : ℕ → ℤ) + (hD : 16 ≤ D) (hDn : D ≤ n) (hg : 1 ≤ g) (hp : p ≠ 0) : + ∃ idx : InstanceIndex p, + idx ∈ instanceIndices (fun a b : Fin n => values (pairIndex (a, b))) D g p ∧ + recordOrdinal n D g p values idx = 0 := by + let wAB := fun a b : Fin n => values (pairIndex (a, b)) + let R := fun q => (values q % (p : ℤ)).toNat + let C := fun rho => Nat.count (fun q => R q = rho) (n * n) + have hn : 0 < n := by omega + let a : Fin n := ⟨0, hn⟩ + let rho := resFin hp (wAB a a) + have hmem : (a, a) ∈ residueClass wAB p rho := + (mem_residueClass wAB hp rho (a, a)).mpr rfl + have hcard : 0 < (residueClass wAB p rho).card := Finset.card_pos.mpr ⟨_, hmem⟩ + have hcap : 0 < queryCap n D := one_le_queryCap (by omega) hDn + have hchunks : 0 < numChunks (residueClass wAB p rho) (queryCap n D) := by + have hc : (residueClass wAB p rho).card ≤ + numChunks (residueClass wAB p rho) (queryCap n D) * queryCap n D := + Nat.le_ceilDiv_mul hcap + by_contra h + have he : numChunks (residueClass wAB p rho) (queryCap n D) = 0 := by omega + rw [he, Nat.zero_mul] at hc + omega + have hpieces : 0 < numPieces n D g := by + have hc : n ≤ numPieces n D g * pieceSize D g := + Nat.le_ceilDiv_mul (pieceSize_pos hD hg) + by_contra h + have he : numPieces n D g = 0 := by omega + rw [he, Nat.zero_mul] at hc + omega + have hnonempty : (instanceIndices wAB D g p).Nonempty := by + refine ⟨(rho, 0, 0), (mem_instanceIndices wAB _).mpr ⟨hchunks, hpieces⟩⟩ + have hcount : 0 < ChunkCatalogue.before C (queryCap n D) p * numPieces n D g := by + rw [QuerySpec.before_mul_numPieces wAB hp R (by intro q; rfl)] + exact Finset.card_pos.mpr hnonempty + obtain ⟨idx, ⟨hidx, hzero⟩, -⟩ := PreparationSpec.index_unique values wAB hp + (by intro a b; rfl) hD hDn hg 0 hcount + exact ⟨idx, hidx, hzero.symm⟩ + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Flat.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Flat.lean new file mode 100644 index 000000000..ebf559ed3 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Flat.lean @@ -0,0 +1,99 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Entry + +/-! +# Initialized flat native producer and product runs + +The original four scalar headers and three signed weight arrays initialize one fixed native +program. Prefix `Runs`, the charged final halt, exact answer words and the caller frame all +refer to the same actual final data. The initialized client selects the first actual record; +the caller interface `exec_of_output` supports every occupied ordinal. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.WordRAM Cslib.Algorithms.ExactTriangle Cslib.Matrix +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +/-- Fixed register count, instruction bound including halt, and physical-width independence. -/ +theorem entryProgram_fixed : (entryProgram.program Reg.result).length ≤ 132352 ∧ + ¬(entryProgram.program Reg.result).ReadsWidth := by + constructor + · simp only [Code.program, List.length_append, Code.length_assemble, + List.length_singleton, entryProgram, Code.size, produce, Code.size_rename] + have hc := program_fixed.1 + have hp := Instances.program_fixed.1 + omega + · apply Code.not_readsWidth_program + simp only [entryProgram, Code.ReadsWidth, produce, Code.readsWidth_rename, Op.ReadsWidth, + false_or] + exact not_or.mpr ⟨Instances.program_fixed.2, program_fixed.2⟩ + +/-- Every sufficiently wide physical execution starts from the actual original input and +returns the first prepared record's exact requested product values. There is no supplied +layout, run, time, sparse-count or solver certificate. Source prime selection and the outer +instance loop are separate; this theorem alone is not an aggregate ExactTriangle bound. -/ +theorem runs_input {W m n g p A : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hD : 16 ≤ D m) + (hg : 1 ≤ g) (hp : 0 < p) (hN : D m ^ 18 ≤ n) + (hv : ∀ z ∈ values, z.natAbs ≤ A) (hW : admissionWidth n (D m) g p A ≤ W) : + InputFits W (PreparationInput.input n (D m) g p values) ∧ + let hn : 0 < n := lt_of_lt_of_le (by omega : 0 < D m) + ((Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN) + ∃ a : Data 64, ∃ idx : InstanceIndex p, + ExecWithin W Instances.program (.init (PreparationInput.input n (D m) g p values)) a + (preparationTime n (D m) g p values - 1) ∧ + idx ∈ instanceIndices (fun a b : Fin n => values.getD (pairIndex (a, b)) 0) (D m) g p ∧ + recordOrdinal n (D m) g p (fun q => values.getD q 0) idx = 0 ∧ + let qs := decodedQueries hn a.mem (recordQueries n (D m) p idx) + (recordQueryCount n (D m) p (fun q => values.getD q 0) idx) + ∃ y t, t + 1 ≤ preparationTime n (D m) g p values + timeBound (m := m) qs + 1 ∧ + Runs W (entryProgram.program Reg.result) + (State.init (PreparationInput.input n (D m) g p values)) ⟨entryProgram.size, y⟩ t ∧ + Executes W (entryProgram.program Reg.result) + (State.init (PreparationInput.input n (D m) g p values)) + (a.regs Instances.heap + QueriedProduct.outputBase (19 * m) m n qs.length) (t + 1) ∧ + y.Bounded W ∧ + y.regs Reg.result = a.regs Instances.heap + + QueriedProduct.outputBase (19 * m) m n qs.length ∧ + (∀ j : Fin qs.length, + y.mem (a.regs Instances.heap + + (QueriedProduct.outputBase (19 * m) m n qs.length + j.val)) = + Signed.encode ((leftMatrix m n g p (fun q => values.getD q 0) idx * + rightMatrix m n g p (fun q => values.getD q 0) idx) (qs.get j).1 (qs.get j).2)) ∧ + (∀ u, u < a.regs Instances.heap ∨ + a.regs Instances.heap + CallableProduct.windowSize n qs.length ≤ u → + y.mem u = a.mem u) ∧ + (∀ j : Fin 64, y.regs (producerBank j) = a.regs j) ∧ qs.length * 2 ^ m ≤ n ^ 2 := by + obtain ⟨hi, a, idx, ha, hidx, hord, y, hy, yr, yv, yf, yg, hQ⟩ := + exec_input values hlen hD hg hp hN hv hW + have ybound := hy.bounded (Data.Bounded.init hi) + obtain ⟨t, ht, he⟩ := hy + refine ⟨hi, a, idx, ha, hidx, hord, y, t, by omega, ?_, ?_, ybound, yr, yv, yf, yg, hQ⟩ + · simpa only [Nat.zero_add, State.init] using he.runs (pc := 0) + (p := entryProgram.program Reg.result) (Program.Contains.self _).append_left + · simpa only [yr, State.init] using he.executes Reg.result + +/-- Every native prefix is physically word-bounded from the actual admitted original input. +In particular this includes dirty loads before the callable compiler's remainder instruction. -/ +theorem bounded_reachable {W m n g p A k : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hD : 16 ≤ D m) + (hg : 1 ≤ g) (hp : 0 < p) (hN : D m ^ 18 ≤ n) + (hv : ∀ z ∈ values, z.natAbs ≤ A) (hW : admissionWidth n (D m) g p A ≤ W) + {s : State 320} (hr : Runs W (entryProgram.program Reg.result) + (State.init (PreparationInput.input n (D m) g p values)) s k) : s.data.Bounded W := by + have hi := (exec_input values hlen hD hg hp hN hv hW).1 + exact hr.bounded_init hi + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Header.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Header.lean new file mode 100644 index 000000000..64bf49a99 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Header.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Decode + +/-! +# Original callee header stores + +The native stores place the payload length, computed tensor level and recovered inner depth, +outer dimension and actual occurrence count above the producer's heap. No encoded matrix or +query table is supplied by this header. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.WordRAM + +/-- Fifteen actual arithmetic/copy/store instructions write the five original header words. +All metadata below register 78, including the complete producer bank, is preserved. -/ +theorem exec_writeHeader {W b m n A Q : ℕ} (x : Data 320) + (hb : x.regs Reg.base = b) (hm : x.regs Reg.destination = 2 * m) + (hA : x.regs Reg.twiceArea = 2 * A) (hQ : x.regs Reg.count = Q) + (hn : x.regs Reg.size = n) (h1 : x.regs Reg.one = 1) (h2 : x.regs Reg.two = 2) + (h19 : x.regs Reg.nineteen = 19) (h4 : x.regs Reg.four = 4) + (hfit : Fits W (b + 19 * m + 2 * A + 2 * Q + 64)) : + ∃ y, ExecWithin W writeHeader x y 15 ∧ + (Block.mk b 5).Realizes y.mem ![4 + 2 * A + 2 * Q, 19 * m, m, n, Q] ∧ + (∀ a, a < b ∨ b + 5 ≤ a → y.mem a = x.mem a) ∧ + ∀ i : Fin 320, i.val < 78 → y.regs i = x.regs i := by + have hdepth : Fits W m := hfit.of_le (by omega) + have hlevel : Fits W (m * 19) := hfit.of_le (by omega) + have hAQ : Fits W (2 * A + Q) := hfit.of_le (by omega) + have hAQQ : Fits W (2 * A + Q + Q) := hfit.of_le (by omega) + have hlen : Fits W (2 * A + Q + Q + 4) := hfit.of_le (by omega) + have hb1 : Fits W (b + 1) := hfit.of_le (by omega) + have hb2 : Fits W (b + 1 + 1) := hfit.of_le (by omega) + have hb3 : Fits W (b + 1 + 1 + 1) := hfit.of_le (by omega) + have hb4 : Fits W (b + 1 + 1 + 1 + 1) := hfit.of_le (by omega) + have hquot : 2 * m / 2 = m := by omega + let y := x.set Reg.depth m |>.set Reg.level (m * 19) + |>.set Reg.payloadLength (2 * A + Q) |>.set Reg.payloadLength (2 * A + Q + Q) + |>.set Reg.payloadLength (2 * A + Q + Q + 4) |>.set Reg.address b + |>.write b (2 * A + Q + Q + 4) |>.set Reg.address (b + 1) |>.write (b + 1) (m * 19) + |>.set Reg.address (b + 1 + 1) |>.write (b + 1 + 1) m + |>.set Reg.address (b + 1 + 1 + 1) |>.write (b + 1 + 1 + 1) n + |>.set Reg.address (b + 1 + 1 + 1 + 1) |>.write (b + 1 + 1 + 1 + 1) Q + have he : ExecWithin W writeHeader x y 15 := by + ram_eval [writeHeader, y, hb, hm, hA, hQ, hn, h1, h2, h19, h4, hquot, hdepth, hlevel, + hAQ, hAQQ, hlen, hb1, hb2, hb3, hb4, Reg.depth, Reg.destination, Reg.two, + Reg.level, Reg.nineteen, Reg.payloadLength, Reg.twiceArea, Reg.count, Reg.four, + Reg.address, Reg.base, Reg.one, Reg.size] + refine ⟨y, he, ?_, ?_, ?_⟩ + · intro j + change y.mem (b + j.val) = _ + fin_cases j <;> simp +decide [y, Nat.add_assoc, Nat.mul_comm] + omega + · intro a ha + have h0 : a ≠ b := by omega + have h1 : a ≠ b + 1 := by omega + have h2 : a ≠ b + 1 + 1 := by omega + have h3 : a ≠ b + 1 + 1 + 1 := by omega + have h4 : a ≠ b + 1 + 1 + 1 + 1 := by omega + simp only [y, Data.write_mem, Data.set_mem, Function.update_of_ne h0, + Function.update_of_ne h1, Function.update_of_ne h2, Function.update_of_ne h3, + Function.update_of_ne h4] + · intro i hi + have hdepth : i ≠ Reg.depth := by intro h; subst i; simp [Reg.depth] at hi + have hlevel : i ≠ Reg.level := by intro h; subst i; simp [Reg.level] at hi + have hlen : i ≠ Reg.payloadLength := by + intro h; subst i; simp [Reg.payloadLength] at hi + have haddr : i ≠ Reg.address := by intro h; subst i; simp [Reg.address] at hi + simp only [y, Data.write_regs, Data.set_regs, Function.update_of_ne hdepth, + Function.update_of_ne hlevel, Function.update_of_ne hlen, Function.update_of_ne haddr] + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/InputSpec.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/InputSpec.lean new file mode 100644 index 000000000..3a7c84215 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/InputSpec.lean @@ -0,0 +1,87 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Copy + +/-! +# Composing the actual copied spans with the original product input + +The local input meaning uses the unchanged queried-product `State.init` convention. Matrix +and coordinate readback is separated by the three actual source spans; no encoded bands, +routing data or precomputed execution contract is present in these assertions. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +/-- The original product's first five memory words, including its actual payload length. -/ +theorem original_header {m n : ℕ} + (X : Matrix (Fin n) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin n) ℤ) + (qs : List (Fin n × Fin n)) : + (Block.mk 0 5).Realizes + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem + ![4 + 2 * (n * D m) + 2 * qs.length, 19 * m, m, n, qs.length] := by + intro j + change (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem + (0 + j.val) = _ + fin_cases j + · change (QueriedProduct.input true (19 * m) X Y qs).length = _ + have h := QueriedProduct.input_length true (19 * m) X Y qs + unfold QueriedProduct.inputEnd at h + simpa only [Matrix.cons_val_zero'] using + (show (QueriedProduct.input true (19 * m) X Y qs).length = + 4 + 2 * (n * D m) + 2 * qs.length by omega) + · rfl + · rfl + · rfl + · rfl + +/-- Exact readback of the five header words and three copied spans identifies the entire +unchanged original input. This is a pure consequence of those native stores and copies. -/ +theorem local_input {m n b : ℕ} + (X : Matrix (Fin n) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin n) ℤ) + (qs : List (Fin n × Fin n)) (mem : ℕ → ℕ) + (hhead : (Block.mk b 5).Realizes mem + ![4 + 2 * (n * D m) + 2 * qs.length, 19 * m, m, n, qs.length]) + (hleft : ∀ a < n * D m, mem (b + 5 + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem (5 + a)) + (hright : ∀ a < n * D m, mem (b + 5 + n * D m + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem + (5 + n * D m + a)) + (hqueries : ∀ a < 2 * qs.length, mem (b + 5 + 2 * (n * D m) + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem + (5 + 2 * (n * D m) + a)) : + ∀ a, a < QueriedProduct.inputEnd m n qs.length → mem (b + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem a := by + intro a ha + unfold QueriedProduct.inputEnd at ha + by_cases h5 : a < 5 + · have hh := hhead ⟨a, h5⟩ + have hs := original_header X Y qs ⟨a, h5⟩ + change mem (b + a) = _ at hh + change (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem + (0 + a) = _ at hs + exact hh.trans (by simpa only [Nat.zero_add] using hs.symm) + by_cases hl : a < 5 + n * D m + · have h := hleft (a - 5) (by omega) + simpa only [Nat.add_assoc, Nat.add_sub_of_le (by omega : 5 ≤ a)] using h + by_cases hr : a < 5 + 2 * (n * D m) + · have h := hright (a - (5 + n * D m)) (by omega) + have he : 5 + n * D m + (a - (5 + n * D m)) = a := by omega + simpa only [Nat.add_assoc, ← Nat.add_assoc 5 (n * D m), he] using h + · have h := hqueries (a - (5 + 2 * (n * D m))) (by omega) + have he : 5 + 2 * (n * D m) + (a - (5 + 2 * (n * D m))) = a := by omega + simpa only [Nat.add_assoc, ← Nat.add_assoc 5 (2 * (n * D m)), he] using h + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Kernel.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Kernel.lean new file mode 100644 index 000000000..2c916b38d --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Kernel.lean @@ -0,0 +1,119 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Program + +/-! +# Relocated native copy and queried-product calls + +All registers of each existing native program are renamed together. Memory relocation is +performed by the callable product's actual address compiler, not by register renaming. +These internal execution rules preserve the low producer bank and full outside-memory frame. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.WordRAM +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +theorem copyBank_injective : Function.Injective copyBank := by + intro i j h + apply Fin.ext + simpa only [copyBank, Nat.add_left_cancel_iff] using congrArg Fin.val h + +theorem calleeBank_injective : Function.Injective calleeBank := by + intro i j h + apply Fin.ext + simpa only [calleeBank, Nat.add_left_cancel_iff] using congrArg Fin.val h + +/-- The real copy loop preserves all adapter metadata and the complete producer register bank. -/ +theorem exec_copy {W s d n : ℕ} (x : Data 320) + (hs : x.regs Reg.source = s) (hd : x.regs Reg.destination = d) + (hn : x.regs Reg.length = n) (hsep : s + n ≤ d) + (hfit : Fits W (s + d + n + 2)) : + ∃ y, ExecWithin W copy x y (7 * n + 4) ∧ + y.mem = ArrayCopy.copyPrefix x.mem s d n ∧ + y.regs Reg.source = s ∧ y.regs Reg.destination = d ∧ y.regs Reg.length = n ∧ + ∀ i : Fin 320, i.val < 96 ∨ 103 ≤ i.val → y.regs i = x.regs i := by + have hs' : (x.comap copyBank).regs ArrayCopy.src = s := hs + have hd' : (x.comap copyBank).regs ArrayCopy.dst = d := hd + have hn' : (x.comap copyBank).regs ArrayCopy.len = n := hn + obtain ⟨a, ha, am, asrc, adst, alen⟩ := ArrayCopy.exec (x.comap copyBank) + (hfit.of_le (by omega)) + (by rw [hs', hn']; exact hfit.of_le (by omega)) + (by rw [hd', hn']; exact hfit.of_le (by omega)) (by simpa only [hs', hd', hn'] using hsep) + refine ⟨a.extend copyBank x, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · simpa only [copy, hn'] using ha.rename copyBank_injective + · simpa only [Data.extend, Data.comap_mem, hs', hd', hn'] using am + · exact (copyBank_injective.extend_apply _ _ ArrayCopy.src).trans (asrc.trans hs') + · exact (copyBank_injective.extend_apply _ _ ArrayCopy.dst).trans (adst.trans hd') + · exact (copyBank_injective.extend_apply _ _ ArrayCopy.len).trans (alen.trans hn') + · intro i hi + apply Function.extend_apply' + rintro ⟨j, hj⟩ + have h := congrArg Fin.val hj + change 96 + j.val = i.val at h + omega + +/-- The concrete original-input callee derives its execution from the matrices and coordinate +occurrences. No execution or time certificate is accepted by this invocation rule. -/ +theorem exec_invoke {W b m N : ℕ} + (X : Matrix (Fin N) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin N) ℤ) + (qs : List (Fin N × Fin N)) (x : Data 320) (hb : x.regs Reg.base = b) + (hinput : ∀ a, a < QueriedProduct.inputEnd m N qs.length → + x.mem (b + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem a) + (hX : ∀ i k, 0 ≤ X i k ∧ X i k ≤ 1) (hY : ∀ k j, 0 ≤ Y k j ∧ Y k j ≤ 1) + (hN : D m ^ 18 ≤ N) (hfit : Fits W (b + 4 * CallableProduct.windowSize N qs.length + 1024)) : + ∃ y, ExecWithin W invoke x y (CallableProduct.timeBound (m := m) qs + 2) ∧ + y.regs Reg.result = b + QueriedProduct.outputBase (19 * m) m N qs.length ∧ + (∀ i : Fin qs.length, + y.mem (b + (QueriedProduct.outputBase (19 * m) m N qs.length + i.val)) = + Signed.encode ((X * Y) (qs.get i).1 (qs.get i).2)) ∧ + (∀ a, a < QueriedProduct.inputEnd m N qs.length → y.mem (b + a) = x.mem (b + a)) ∧ + (∀ a, a < b ∨ b + CallableProduct.windowSize N qs.length ≤ a → y.mem a = x.mem a) ∧ + ∀ i : Fin 320, i.val < 128 → i ≠ Reg.result → y.regs i = x.regs i := by + let z := x.set (calleeBank CallableProduct.Reg.base) b + have hz : ExecWithin (B := .standard) W + (.op (.copy (calleeBank CallableProduct.Reg.base) Reg.base)) x z 1 := by + simpa only [z, hb] using + (ExecWithin.copy (w := W) (x := x) (d := calleeBank CallableProduct.Reg.base) + (s := Reg.base)) + have zb : (z.comap calleeBank).regs CallableProduct.Reg.base = b := by simp [z] + obtain ⟨a, ha, ar, ab, aM, av, ai, af⟩ := + CallableProduct.exec X Y qs (z.comap calleeBank) zb hinput hX hY hN hfit + let c := a.extend calleeBank z + have hc : ExecWithin W (CallableProduct.program.rename calleeBank) z c + (CallableProduct.timeBound (m := m) qs) := ha.rename calleeBank_injective + have cr : c.regs (calleeBank CallableProduct.Reg.result) = + b + QueriedProduct.outputBase (19 * m) m N qs.length := + (calleeBank_injective.extend_apply _ _ _).trans ar + let y := c.set Reg.result (b + QueriedProduct.outputBase (19 * m) m N qs.length) + have hy : ExecWithin (B := .standard) W + (.op (.copy Reg.result (calleeBank CallableProduct.Reg.result))) c y 1 := by + simpa only [y, cr] using (ExecWithin.copy (w := W) (x := c) (d := Reg.result) + (s := calleeBank CallableProduct.Reg.result)) + refine ⟨y, ?_, by simp [y], av, ai, af, ?_⟩ + · exact (hz.seq (hc.seq hy)).mono (by omega) + · intro i hi hne + have hbank : ¬∃ j, calleeBank j = i := by + rintro ⟨j, hj⟩ + have h := congrArg Fin.val hj + change 128 + j.val = i.val at h + omega + have hiz : i ≠ calleeBank CallableProduct.Reg.base := by + intro h + exact hbank ⟨_, h.symm⟩ + simp only [y, Data.set_regs, Function.update_of_ne hne, c, Data.extend, + Function.extend_apply' _ _ _ hbank, z, Function.update_of_ne hiz] + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Prepare.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Prepare.lean new file mode 100644 index 000000000..769a2ae08 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Prepare.lean @@ -0,0 +1,111 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.InputSpec + +/-! +# Complete record-to-original-input preparation + +All runtime metadata is read by the real descriptor code. The logarithm, five original-header +stores and three native array copies compose to linear preparation work. The readback premises +here are internal; the producer client derives them from the actual prepared instance. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +-- The finite preservation test traverses the relocated 320-register code. +set_option maxRecDepth 4096 in +private theorem read_preserves : ∀ i : Fin 320, i.val < 65 → readRecord.Preserves i := by decide + +/-- Complete native preparation from a selected record and its actual three source spans. +No source execution, encoded matrix or routing table is supplied. -/ +theorem exec_prepare {W b m n i out rho piece s t q : ℕ} + (X : Matrix (Fin n) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin n) ℤ) + (qs : List (Fin n × Fin n)) (x : Data 320) + (hD : x.mem 1 = D m) (hn : x.mem 3 = n) + (hi : x.regs Reg.ordinal = i) (hout : x.regs 39 = out) (hb : x.regs 42 = b) + (hrec : out + i * (6 + 2 * (n * D m)) + 6 ≤ b) + (hrecord : (Block.mk (out + i * (6 + 2 * (n * D m))) 6).Realizes x.mem + ![rho, piece, q, qs.length, s, t]) + (hsb : s + n * D m ≤ b) (htb : t + n * D m ≤ b) + (hqb : q + 2 * qs.length ≤ b) + (hleft : ∀ a < n * D m, x.mem (s + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem (5 + a)) + (hright : ∀ a < n * D m, x.mem (t + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem + (5 + n * D m + a)) + (hqueries : ∀ a < 2 * qs.length, x.mem (q + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem + (5 + 2 * (n * D m) + a)) + (hfit : Fits W (2 * b + 2 * (n * D m) + 2 * qs.length + 20 * m + D m + n + 256)) : + ∃ y, ExecWithin W prepare x y (8 * m + 14 * (n * D m) + 14 * qs.length + 69) ∧ + y.regs Reg.base = b ∧ + (∀ a, a < QueriedProduct.inputEnd m n qs.length → y.mem (b + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem a) ∧ + (∀ a, a < b ∨ b + QueriedProduct.inputEnd m n qs.length ≤ a → y.mem a = x.mem a) ∧ + ∀ j : Fin 320, j.val < 65 → y.regs j = x.regs j := by + obtain ⟨r, hr, rm, rD, rn, rA, rAA, rb, rrec, rrho, rpiece, rq, rQ, rs, rt, + rarg, r1, r2, r19, r4⟩ := exec_readRecord x ![rho, piece, q, qs.length, s, t] + hD hn hi hout hb hrec hrecord (hfit.of_le (by omega)) + have rq' : r.regs Reg.queries = q := by simpa +decide using rq + have rQ' : r.regs Reg.count = qs.length := by simpa +decide using rQ + have rs' : r.regs Reg.left = s := by simpa +decide using rs + have rt' : r.regs Reg.right = t := by simpa +decide using rt + obtain ⟨l, hl, lmDepth, lm, lr⟩ := exec_logarithm r rarg (hfit.of_le (by + change D m + 2 * m + 3 ≤ _ + omega)) + have lb : l.regs Reg.base = b := (lr _ (by decide)).trans rb + obtain ⟨h, hh, hhead, hframe, hregs⟩ := exec_writeHeader l lb lmDepth + ((lr _ (by decide)).trans rAA) ((lr _ (by decide)).trans rQ') + ((lr _ (by decide)).trans rn) ((lr _ (by decide)).trans r1) + ((lr _ (by decide)).trans r2) ((lr _ (by decide)).trans r19) + ((lr _ (by decide)).trans r4) (hfit.of_le (by omega)) + have hb' : h.regs Reg.base = b := (hregs _ (by decide)).trans lb + obtain ⟨y, hy, yleft, yright, yqueries, yframe, yregs⟩ := exec_copyInputs h hb' + ((hregs _ (by decide)).trans ((lr _ (by decide)).trans rA)) + ((hregs _ (by decide)).trans ((lr _ (by decide)).trans rQ')) + ((hregs _ (by decide)).trans ((lr _ (by decide)).trans rs')) + ((hregs _ (by decide)).trans ((lr _ (by decide)).trans rt')) + ((hregs _ (by decide)).trans ((lr _ (by decide)).trans rq')) + hsb htb hqb (hfit.of_le (by omega)) + have hread (a : ℕ) (ha : a < b) : h.mem a = x.mem a := + (hframe a (Or.inl ha)).trans (congrFun lm a |>.trans (congrFun rm a)) + refine ⟨y, (hr.seq (hl.seq (hh.seq hy))).mono (by omega), + (yregs _ (by decide) (by decide)).trans hb', ?_, ?_, ?_⟩ + · apply local_input X Y qs y.mem + · intro j + have hj : j.val < 5 := j.isLt + change y.mem (b + j.val) = _ + rw [yframe _ (Or.inl (by omega))] + exact hhead j + · intro a ha + rw [yleft a ha, hread _ (by omega)] + exact hleft a ha + · intro a ha + rw [yright a ha, hread _ (by omega)] + exact hright a ha + · intro a ha + rw [yqueries a ha, hread _ (by omega)] + exact hqueries a ha + · intro a ha + have hI : QueriedProduct.inputEnd m n qs.length = + 5 + 2 * (n * D m) + 2 * qs.length := rfl + rw [yframe _ (by rw [hI] at ha; omega), hframe _ (by rw [hI] at ha; omega), lm, rm] + · intro j hj + have jne : j ≠ Reg.address := by intro h; subst j; simp [Reg.address] at hj + rw [yregs _ (by omega) jne, hregs _ (by omega), lr _ (Or.inl (by omega))] + exact hr.regs_eq (read_preserves j hj) + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Producer.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Producer.lean new file mode 100644 index 000000000..0876048cd --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Producer.lean @@ -0,0 +1,238 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.ProducerQueries + +/-! +# Applying the adapter to the actual native producer + +The record, source pointers, original zero/one matrices and ordered coordinate occurrences +are derived from `Instances.Output` and the existing native readback theorems. The initialized +client obtains that output from the actual producer execution, not from an algorithm oracle. +Prime selection, global parameters and the outer ExactTriangle host are separate components. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.WordRAM Cslib.Algorithms.ExactTriangle Cslib.Matrix +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +/-- The unchanged complete producer occupies the original low register bank. -/ +@[expose] def producerBank (i : Fin 64) : Fin 320 := i.castLE (by decide) + +/-- Existing native writer arguments, with pointer fields omitted from the value-only view. -/ +@[expose] def matrixArgs (n D g p rho piece : ℕ) : WriteMatrices.Args := + ⟨0, 0, n, D, p, piece * pieceSize D g, + min (pieceSize D g) (n - piece * pieceSize D g), rho⟩ + +/-- Exact raw left adjacency matrix written by the actual producer. -/ +@[expose] def leftMatrix (m n g p : ℕ) (values : ℕ → ℤ) (idx : InstanceIndex p) : + Matrix (Fin n) (Fin (D m)) ℤ := fun a k => + (WriteMatrices.entry false (matrixArgs n (D m) g p idx.1.val idx.2.2) + (fun q => (values (2 * (n * n) + q) % (p : ℤ)).toNat) (a.val * D m + k.val) : ℤ) + +/-- Exact raw right adjacency matrix, in its actual `D×n` storage order. -/ +@[expose] def rightMatrix (m n g p : ℕ) (values : ℕ → ℤ) (idx : InstanceIndex p) : + Matrix (Fin (D m)) (Fin n) ℤ := fun k a => + (WriteMatrices.entry true (matrixArgs n (D m) g p idx.1.val idx.2.2) + (fun q => (values (n * n + q) % (p : ℤ)).toNat) (k.val * n + a.val) : ℤ) + +/-- Both native writer value functions are raw zero/one words at every index. -/ +theorem entry_le_one (side : Bool) (A : WriteMatrices.Args) (R : ℕ → ℕ) (a : ℕ) : + WriteMatrices.entry side A R a ≤ 1 := by + cases side <;> dsimp only [WriteMatrices.entry] <;> split_ifs <;> omega + +/-- The readback matrices satisfy the raw callee's original-entry hypotheses. -/ +theorem matrices_zero_one (m n g p : ℕ) (values : ℕ → ℤ) (idx : InstanceIndex p) : + (∀ a k, 0 ≤ leftMatrix m n g p values idx a k ∧ + leftMatrix m n g p values idx a k ≤ 1) ∧ + (∀ k a, 0 ≤ rightMatrix m n g p values idx k a ∧ + rightMatrix m n g p values idx k a ≤ 1) := by + constructor <;> intro a k + · exact ⟨Int.natCast_nonneg _, by + dsimp only [leftMatrix] + exact_mod_cast entry_le_one false _ _ _⟩ + · exact ⟨Int.natCast_nonneg _, by + dsimp only [rightMatrix] + exact_mod_cast entry_le_one true _ _ _⟩ + +/-- The actual computed ordinal of an existing finite class/chunk/piece. This is a value +specification for the caller's selection register, not a table supplied to the adapter. -/ +@[expose] def recordOrdinal (n D g p : ℕ) (values : ℕ → ℤ) (idx : InstanceIndex p) : ℕ := + let C := fun rho => Nat.count (fun q => (values q % (p : ℤ)).toNat = rho) (n * n) + (ChunkCatalogue.before C (queryCap n D) idx.1.val + idx.2.1) * numPieces n D g + idx.2.2 + +/-- Actual descriptor occurrence count, rather than an assumed sparse set size. -/ +@[expose] def recordQueryCount (n D p : ℕ) (values : ℕ → ℤ) (idx : InstanceIndex p) : ℕ := + min (queryCap n D) + (Nat.count (fun q => (values q % (p : ℤ)).toNat = idx.1.val) (n * n) - + idx.2.1 * queryCap n D) + +/-- Every occupied finite instance has a nonempty actual descriptor query slice. -/ +theorem recordQueryCount_pos {n D g p : ℕ} (values : ℕ → ℤ) (hp : p ≠ 0) + (hD : 1 ≤ D) (hDn : D ≤ n) (idx : InstanceIndex p) + (hidx : idx ∈ instanceIndices (fun a b : Fin n => values (pairIndex (a, b))) D g p) : + 0 < recordQueryCount n D p values idx := by + let wAB := fun a b : Fin n => values (pairIndex (a, b)) + let R := fun q => (values q % (p : ℤ)).toNat + have hk := ((mem_instanceIndices wAB idx).mp hidx).1 + have hcard := QuerySpec.card_residueClass wAB hp R (by intro q; rfl) idx.1 + have hcap : 0 < queryCap n D := one_le_queryCap hD hDn + have hkcard : idx.2.1 * queryCap n D < (residueClass wAB p idx.1).card := by + by_contra h + have hc := (Nat.ceilDiv_le_iff hcap).mpr + (by omega : (residueClass wAB p idx.1).card ≤ idx.2.1 * queryCap n D) + change idx.2.1 < (residueClass wAB p idx.1).card ⌈/⌉ queryCap n D at hk + omega + rw [hcard] at hkcard + unfold recordQueryCount + change 0 < min (queryCap n D) (Nat.count (fun q => R q = idx.1.val) (n * n) - + idx.2.1 * queryCap n D) + omega + +/-- Actual query pointer into the protected producer catalogue. -/ +@[expose] def recordQueries (n D p : ℕ) (idx : InstanceIndex p) : ℕ := + 5 + 6 * (n * n) + p + 2 * idx.1.val * (n * n) + 2 * (idx.2.1 * queryCap n D) + +-- The composed readback contains nested finite writer-argument structures and vector fields. +set_option maxRecDepth 4096 in +/-- Full adapter execution over a genuine producer output. Every descriptor and source-span +premise of `exec` is derived here from the actual output; none remains an independent caller +certificate. A following initialized client obtains this output by executing the producer. -/ +theorem exec_of_output {W m n g p : ℕ} (values : ℕ → ℤ) + (initial : Data 64) (x : Data 320) + (h : Instances.Output n (D m) g p values initial (x.comap producerBank)) + (hD : 16 ≤ D m) (hg : 1 ≤ g) (hp : p ≠ 0) (hN : D m ^ 18 ≤ n) + (hheaderD : initial.mem 1 = D m) (hheaderN : initial.mem 3 = n) + (idx : InstanceIndex p) + (hidx : idx ∈ instanceIndices (fun a b : Fin n => values (pairIndex (a, b))) (D m) g p) + (hi : x.regs Reg.ordinal = recordOrdinal n (D m) g p values idx) + (hfit : Fits W (2 * x.regs 42 + + 4 * CallableProduct.windowSize n (recordQueryCount n (D m) p values idx) + 1024)) : + let hn : 0 < n := lt_of_lt_of_le (by omega : 0 < D m) + ((Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN) + let qs := decodedQueries hn x.mem (recordQueries n (D m) p idx) + (recordQueryCount n (D m) p values idx) + ∃ y, ExecWithin W program x y (timeBound (m := m) qs) ∧ + y.regs Reg.result = x.regs 42 + QueriedProduct.outputBase (19 * m) m n qs.length ∧ + (∀ j : Fin qs.length, + y.mem (x.regs 42 + (QueriedProduct.outputBase (19 * m) m n qs.length + j.val)) = + Signed.encode ((leftMatrix m n g p values idx * rightMatrix m n g p values idx) + (qs.get j).1 (qs.get j).2)) ∧ + (∀ a, a < x.regs 42 ∨ x.regs 42 + CallableProduct.windowSize n qs.length ≤ a → + y.mem a = x.mem a) ∧ + (∀ j : Fin 320, j.val < 65 → y.regs j = x.regs j) ∧ + qs.length * 2 ^ m ≤ n ^ 2 := by + let wAB := fun a b : Fin n => values (pairIndex (a, b)) + let N := n * n + let R := fun q => (values q % (p : ℤ)).toNat + let C := fun rho => Nat.count (fun q => R q = rho) N + let H := numPieces n (D m) g + let K := ChunkCatalogue.before C (queryCap n (D m)) p + let out := 5 + 6 * N + p + 2 * N * p + 3 * K + let ord := recordOrdinal n (D m) g p values idx + let recordBase := out + ord * (6 + 2 * (n * D m)) + let q := recordQueries n (D m) p idx + let Q := recordQueryCount n (D m) p values idx + have hDn : D m ≤ n := (Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN + have hn : 0 < n := lt_of_lt_of_le (by omega) hDn + let qs := decodedQueries hn x.mem q Q + have hlen : qs.length = Q := decodedQueries_length hn x.mem q Q + obtain ⟨hk, hj⟩ := (mem_instanceIndices wAB idx).mp hidx + have hcard : (residueClass wAB p idx.1).card = C idx.1.val := + QuerySpec.card_residueClass wAB hp R (by intro pair; rfl) idx.1 + have hchunk : idx.2.1 < C idx.1.val ⌈/⌉ queryCap n (D m) := by + simpa only [numChunks, hcard] using hk + have hord := ChunkCatalogue.before_add_lt C (queryCap n (D m)) idx.1.isLt hchunk + have hiCount : ord < K * H := by + have hh := Nat.mul_le_mul_right H (Nat.succ_le_of_lt hord) + change (ChunkCatalogue.before C (queryCap n (D m)) idx.1.val + idx.2.1) * H + + idx.2.2 < K * H + change (ChunkCatalogue.before C (queryCap n (D m)) idx.1.val + idx.2.1 + 1) * H ≤ + K * H at hh + change idx.2.2 < H at hj + rw [Nat.add_mul, Nat.one_mul] at hh + omega + have hfull := h + obtain ⟨yn, yD, yp, yo, yc, yb, yres, ycnt, yscan, ydesc, yblocks, yframe⟩ := h + change x.regs 39 = out at yo + change x.regs 42 = out + (K * H) * (6 + 2 * (n * D m)) at yb + have hrecEnd : recordBase + 6 + 2 * (n * D m) ≤ x.regs 42 := by + have ht := Nat.mul_le_mul_right (6 + 2 * (n * D m)) (Nat.succ_le_of_lt hiCount) + dsimp only [recordBase] + rw [yb] + nlinarith only [ht] + have hcap : 0 < queryCap n (D m) := one_le_queryCap (by omega) hDn + have hkcard : idx.2.1 * queryCap n (D m) < C idx.1.val := by + have hpositive := recordQueryCount_pos values hp (by omega) hDn idx hidx + change 0 < min (queryCap n (D m)) (C idx.1.val - idx.2.1 * queryCap n (D m)) at hpositive + omega + have hQ : idx.2.1 * queryCap n (D m) + Q ≤ N := by + have hc : C idx.1.val ≤ N := Nat.count_le _ + dsimp only [Q, recordQueryCount] + change idx.2.1 * queryCap n (D m) + + min (queryCap n (D m)) (C idx.1.val - idx.2.1 * queryCap n (D m)) ≤ N + omega + have hqEnd : q + 2 * Q ≤ x.regs 42 := by + have hrho := Nat.mul_le_mul_right (2 * N) (Nat.succ_le_of_lt idx.1.isLt) + dsimp only [q, recordQueries] + change 5 + 6 * N + p + 2 * idx.1.val * N + 2 * (idx.2.1 * queryCap n (D m)) + + 2 * Q ≤ _ + rw [yb] + dsimp only [out] + nlinarith only [hQ, hrho, Nat.zero_le (3 * K), + Nat.zero_le (K * H * (6 + 2 * (n * D m)))] + obtain ⟨hdr, hleft, hright⟩ := PreparationSpec.instance_readback values wAB hp + (by intro a b; rfl) hD hDn hg hfull idx hidx + have hcoord := (output_query_coordinates values wAB hp (by intro a b; rfl) + (by omega) hDn hfull idx.1 idx.2.1 hk).1 + rw [hcard] at hcoord + have hc : ∀ j < Q, x.mem (q + 2 * j) < n ∧ x.mem (q + 2 * j + 1) < n := hcoord + have hvalues := matrices_zero_one m n g p values idx + obtain ⟨y, hy, yr, yv, yf, yg⟩ := exec (b := x.regs 42) (i := ord) (out := out) + (rho := idx.1.val) (piece := idx.2.2) (s := recordBase + 6) + (t := recordBase + 6 + n * D m) (q := q) (leftMatrix m n g p values idx) + (rightMatrix m n g p values idx) qs x + ((yframe 1 (Or.inl (by omega))).trans hheaderD) + ((yframe 3 (Or.inl (by omega))).trans hheaderN) hi yo rfl + (by change recordBase + 6 ≤ _; omega) + (by simpa only [hlen, hcard, recordBase, out, ord, recordOrdinal, C, R, N, K, Q, + recordQueryCount, q, recordQueries, Data.comap_mem] using hdr) + (by omega) (by omega) + (by simpa only [hlen] using hqEnd) + (raw_left_words x.mem _ _ qs (by + intro a k + have hflat : a.val * D m + k.val < n * D m := by + have ht := Nat.mul_le_mul_right (D m) a.isLt + rw [Nat.succ_mul] at ht + have := k.isLt + omega + simpa +decide only [leftMatrix, matrixArgs, WriteMatrices.entry, Int.toNat_natCast, + Block.read, Data.comap_mem, recordBase, out, ord, recordOrdinal, K, C, R, N] using + hleft ⟨a.val * D m + k.val, hflat⟩)) + (raw_right_words hn x.mem _ _ qs (by + intro k a + have hflat : k.val * n + a.val < n * D m := by + have ht := Nat.mul_le_mul_right n k.isLt + rw [Nat.succ_mul, Nat.mul_comm (D m) n] at ht + have := a.isLt + omega + simpa only [rightMatrix, matrixArgs, WriteMatrices.entry, Int.toNat_natCast, + Block.read, Data.comap_mem, recordBase, out, ord, recordOrdinal, K, C, R, N] using + hright ⟨k.val * n + a.val, hflat⟩)) + (by simpa only [hlen] using decodedQueries_words hn x.mem _ _ hc) + hvalues.1 hvalues.2 hN (by simpa only [hlen] using hfit) + refine ⟨y, hy, yr, yv, yf, yg, ?_⟩ + rw [hlen] + exact (Nat.mul_le_mul_right (2 ^ m) (min_le_left _ _)).trans (queryCap_mul_two_pow_le n m) + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/ProducerQueries.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/ProducerQueries.lean new file mode 100644 index 000000000..5115fc21f --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/ProducerQueries.lean @@ -0,0 +1,91 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.RawInput + +/-! +# Actual producer chunks admit the native query copies + +The class scanner's real coordinate stores and the existing finite chunk theorem derive +in-range query words. Their original occurrence count is the descriptor's computed minimum. +The power-of-four query-capacity bound supplies occurrence sparsity without assuming a sparse +execution or replacing a list length by a set cardinality. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.WordRAM Cslib.Algorithms.ExactTriangle Cslib.Matrix +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The existing computed chunk capacity supplies the stronger occurrence bound at `4^m`. -/ +theorem queryCap_mul_two_pow_le (n m : ℕ) : queryCap n (D m) * 2 ^ m ≤ n ^ 2 := by + have hroot := Nat.sqrt_le ((n ^ 2) ^ 2 / D m) + have hmul := (Nat.mul_le_mul_right (D m) hroot).trans + (Nat.div_mul_le_self ((n ^ 2) ^ 2) (D m)) + have hD : D m = (2 ^ m) ^ 2 := by + change 4 ^ m = (2 ^ m) ^ 2 + rw [← Nat.pow_mul, Nat.mul_comm m 2, Nat.pow_mul] + have hs : (queryCap n (D m) * 2 ^ m) ^ 2 ≤ (n ^ 2) ^ 2 := by + change (Nat.sqrt ((n ^ 2) ^ 2 / D m) * 2 ^ m) ^ 2 ≤ _ + rw [mul_pow, ← hD] + simpa only [pow_two] using hmul + nlinarith only [hs, Nat.zero_le (queryCap n (D m) * 2 ^ m), Nat.zero_le (n ^ 2)] + +/-- The producer's actual scanner stores identify every local chunk occurrence with its +unique existing finite pair. Thus neither coordinate range nor the query count is supplied +as an independent adapter certificate. -/ +theorem output_query_coordinates {n D g p : ℕ} (values : ℕ → ℤ) + (wAB : Fin n → Fin n → ℤ) (hp : p ≠ 0) + (hAB : ∀ a b, values (pairIndex (a, b)) = wAB a b) + (hD : 1 ≤ D) (hDn : D ≤ n) + {initial y : Data 64} (h : Instances.Output n D g p values initial y) + (rho : Fin p) (k : ℕ) + (hk : k < numChunks (residueClass wAB p rho) (queryCap n D)) : + let q := 5 + 6 * (n * n) + p + 2 * rho.val * (n * n) + 2 * (k * queryCap n D) + let Q := min (queryCap n D) ((residueClass wAB p rho).card - k * queryCap n D) + (∀ i < Q, y.mem (q + 2 * i) < n ∧ y.mem (q + 2 * i + 1) < n) ∧ + (∀ i < Q, ∃! pair : Fin n × Fin n, + pair ∈ chunkOf wAB D p rho k ∧ + rankIn (residueClass wAB p rho) pair = k * queryCap n D + i ∧ + y.mem (q + 2 * i) = pair.1.val ∧ y.mem (q + 2 * i + 1) = pair.2.val) := by + let R := fun q => (values q % (p : ℤ)).toNat + have hR (pair : Fin n × Fin n) : R (pairIndex pair) = (resFin hp (wAB pair.1 pair.2)).val := by + dsimp only [R] + rw [hAB] + rfl + have hscan := h.2.2.2.2.2.2.2.2.1 + have hread (q : ℕ) (hq : q < n * n) (hrho : R q = rho.val) : + y.mem (5 + 6 * (n * n) + p + 2 * rho.val * (n * n) + + 2 * Nat.count (fun j => R j = rho.val) q) = q / n ∧ + y.mem (5 + 6 * (n * n) + p + 2 * rho.val * (n * n) + + 2 * Nat.count (fun j => R j = rho.val) q + 1) = q % n := by + simpa only [R, Nat.mul_right_comm 2 (n * n) rho.val] using + hscan rho.val rho.isLt q hq hrho + have hcap : 0 < queryCap n D := one_le_queryCap hD hDn + have hpair (i : ℕ) (hi : i < min (queryCap n D) + ((residueClass wAB p rho).card - k * queryCap n D)) := + QuerySpec.chunk_readback wAB hp R hR D rho y.mem + (5 + 6 * (n * n) + p + 2 * rho.val * (n * n)) hread hcap k hk i hi + have haddr (i : ℕ) : + 5 + 6 * (n * n) + p + 2 * rho.val * (n * n) + 2 * (k * queryCap n D + i) = + 5 + 6 * (n * n) + p + 2 * rho.val * (n * n) + 2 * (k * queryCap n D) + 2 * i := by + omega + dsimp only + constructor + · intro i hi + obtain ⟨pair, hpair, -⟩ := hpair i hi + rw [haddr i] at hpair + rw [hpair.2.2.1, hpair.2.2.2] + exact ⟨pair.1.isLt, pair.2.isLt⟩ + · intro i hi + simpa only [haddr i] using hpair i hi + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Program.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Program.lean new file mode 100644 index 000000000..6368cd58a --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Program.lean @@ -0,0 +1,174 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationAdjacency +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Callable.Width +public import Complexity.Algorithms.WordRAM.NatLog +public import Complexity.Computability.WordRAM.Copy + +/-! +# Calling a queried product from an actual prepared record + +The low 64 producer registers are retained. Register 64 selects an actual record ordinal; +the native code computes its stride, reads its six words and places the original raw matrices +and interleaved query coordinates at the actual producer heap end. The complete callable +product occupies a separate 163-register bank. Its existing matrix-input code, not this adapter, +performs raw-zero/one to canonical-signed doubling. + +The logarithm is computed by `NatLog.program`. Under `D=4^m`, dividing its binary result by two +recovers `m` exactly. No layout, logarithm, execution or solver certificate is a runtime input. +These definitions are internal stages of the complete record consumer. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.WordRAM +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +namespace Reg + +/-- Actual record ordinal supplied by the outer caller. -/ +abbrev ordinal : Fin 320 := 64 +/-- Absolute result pointer. -/ +abbrev result : Fin 320 := 65 +/-- Actual producer heap end, and the callee's local-memory base. -/ +abbrev base : Fin 320 := 66 +/-- Original padded middle dimension. -/ +abbrev dimension : Fin 320 := 67 +/-- Original outer dimension. -/ +abbrev size : Fin 320 := 68 +/-- Computed selected record pointer. -/ +abbrev record : Fin 320 := 69 +/-- Loaded residue and piece metadata, retained without reinterpreting them. -/ +abbrev rho : Fin 320 := 70 +/-- Loaded piece index. -/ +abbrev piece : Fin 320 := 71 +/-- Loaded interleaved query pointer. -/ +abbrev queries : Fin 320 := 72 +/-- Loaded number of query occurrences. -/ +abbrev count : Fin 320 := 73 +/-- Loaded left-matrix pointer. -/ +abbrev left : Fin 320 := 74 +/-- Loaded right-matrix pointer. -/ +abbrev right : Fin 320 := 75 +/-- Computed size of either matrix block. -/ +abbrev area : Fin 320 := 76 +/-- Computed combined matrix-block length. -/ +abbrev twiceArea : Fin 320 := 77 +/-- Recovered inner depth. -/ +abbrev depth : Fin 320 := 78 +/-- Computed tensor level, nineteen times the inner depth. -/ +abbrev level : Fin 320 := 79 +/-- Computed original callee payload length. -/ +abbrev payloadLength : Fin 320 := 80 +/-- Constant one. -/ +abbrev one : Fin 320 := 81 +/-- Constant two. -/ +abbrev two : Fin 320 := 82 +/-- Record header length. -/ +abbrev six : Fin 320 := 83 +/-- Tensor-level multiplier. -/ +abbrev nineteen : Fin 320 := 84 +/-- Original callee scalar header length. -/ +abbrev four : Fin 320 := 85 +/-- Adapter address scratch. -/ +abbrev address : Fin 320 := 86 +/-- Temporary source pointer, also the logarithm input. -/ +abbrev source : Fin 320 := 96 +/-- Temporary destination pointer, also the logarithm result. -/ +abbrev destination : Fin 320 := 97 +/-- Temporary copy length. -/ +abbrev length : Fin 320 := 98 + +end Reg + +/-- The logarithm and copy routines share an adapter-local temporary register bank. -/ +@[expose] def logBank (i : Fin 4) : Fin 320 := ⟨96 + i.val, by omega⟩ +/-- All seven registers of the real array-copy routine are relocated together. -/ +@[expose] def copyBank (i : Fin 7) : Fin 320 := ⟨96 + i.val, by omega⟩ +/-- The complete callable product bank is disjoint from producer and adapter registers. -/ +@[expose] def calleeBank (i : Fin 163) : Fin 320 := ⟨128 + i.val, by omega⟩ + +/-- Compute the record pointer and read every actual descriptor field. -/ +@[expose] def readRecord : Code .standard 320 := + let output : Fin 320 := 39 + let heap : Fin 320 := 42 + ram[ Reg.one ← #1; Reg.two ← #2; Reg.six ← #6; Reg.nineteen ← #19; Reg.four ← #4; + Reg.address ← #1; Reg.dimension ← *Reg.address; + Reg.address ← #3; Reg.size ← *Reg.address; + Reg.area ← Reg.size * Reg.dimension; Reg.twiceArea ← Reg.area + Reg.area; + Reg.address ← Reg.twiceArea + Reg.six; Reg.address ← Reg.ordinal * Reg.address; + Reg.record ← output + Reg.address; Reg.base ← heap; + Reg.address ← Reg.record; Reg.rho ← *Reg.address; + Reg.address ← Reg.address + Reg.one; Reg.piece ← *Reg.address; + Reg.address ← Reg.address + Reg.one; Reg.queries ← *Reg.address; + Reg.address ← Reg.address + Reg.one; Reg.count ← *Reg.address; + Reg.address ← Reg.address + Reg.one; Reg.left ← *Reg.address; + Reg.address ← Reg.address + Reg.one; Reg.right ← *Reg.address; + Reg.source ← Reg.dimension ] + +/-- Recover the exact power-of-four exponent with the existing charged binary logarithm. -/ +@[expose] def logarithm : Code .standard 320 := NatLog.program.rename logBank + +/-- Write the complete original local input header, including its length word. -/ +@[expose] def writeHeader : Code .standard 320 := ram[ + Reg.depth ← Reg.destination / Reg.two; + Reg.level ← Reg.depth * Reg.nineteen; + Reg.payloadLength ← Reg.twiceArea + Reg.count; + Reg.payloadLength ← Reg.payloadLength + Reg.count; + Reg.payloadLength ← Reg.payloadLength + Reg.four; + Reg.address ← Reg.base; *Reg.address ← Reg.payloadLength; + Reg.address ← Reg.address + Reg.one; *Reg.address ← Reg.level; + Reg.address ← Reg.address + Reg.one; *Reg.address ← Reg.depth; + Reg.address ← Reg.address + Reg.one; *Reg.address ← Reg.size; + Reg.address ← Reg.address + Reg.one; *Reg.address ← Reg.count ] + +/-- Deliver real source and destination pointers for the left-matrix copy. -/ +@[expose] def deliverLeft : Code .standard 320 := ram[ + Reg.source ← Reg.left; Reg.address ← #5; + Reg.destination ← Reg.base + Reg.address; Reg.length ← Reg.area ] + +/-- The right block follows the left destination block. -/ +@[expose] def deliverRight : Code .standard 320 := ram[ + Reg.source ← Reg.right; Reg.destination ← Reg.destination + Reg.area ] + +/-- Preserve each query occurrence's two original raw coordinates, in its original order. -/ +@[expose] def deliverQueries : Code .standard 320 := ram[ + Reg.source ← Reg.queries; Reg.destination ← Reg.destination + Reg.area; + Reg.length ← Reg.count + Reg.count ] + +/-- Real copying, with every address calculation, load/store and loop instruction charged. -/ +@[expose] def copy : Code .standard 320 := ArrayCopy.program.rename copyBank + +/-- Three actual copies; no catalogue or virtual-window snapshot is made. -/ +@[expose] def copyInputs : Code .standard 320 := + .seq deliverLeft <| .seq copy <| .seq deliverRight <| .seq copy <| + .seq deliverQueries copy + +/-- Runtime memory rebasing is provided by the callee; renaming moves only its registers. -/ +@[expose] def invoke : Code .standard 320 := + .seq (.op (.copy (calleeBank CallableProduct.Reg.base) Reg.base)) <| + .seq (CallableProduct.program.rename calleeBank) + (.op (.copy Reg.result (calleeBank CallableProduct.Reg.result))) + +/-- Runtime preparation prefix, useful for admitted decoder/copy tests without the full solver. -/ +@[expose] def prepare : Code .standard 320 := + .seq readRecord <| .seq logarithm <| .seq writeHeader copyInputs + +/-- One selected actual producer record, including all copying and the complete native callee. -/ +@[expose] def program : Code .standard 320 := .seq prepare invoke + +-- The closed syntactic traversal includes the complete existing large callee. +set_option maxRecDepth 4096 in +/-- One fixed finite native program, independent of dimensions, record ordinal and word width. -/ +theorem program_fixed : program.size ≤ 131584 ∧ ¬program.ReadsWidth := by decide + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/RawInput.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/RawInput.lean new file mode 100644 index 000000000..bd86b23f7 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/RawInput.lean @@ -0,0 +1,114 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Exec + +/-! +# Original raw matrix and coordinate spans + +These readback lemmas use the existing original product input and row-major numbering. +The query list is the finite mathematical reading of actual native coordinate words; the +adapter copies those words unchanged, and performs no uncharged query decoding operation. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +/-- Mathematical reading of an existing interleaved native query block. The modulo is only +a total specification; actual producer coordinates are proved in range before using it. -/ +@[expose] def decodedQueries {n : ℕ} (hn : 0 < n) (mem : ℕ → ℕ) (q Q : ℕ) : + List (Fin n × Fin n) := + List.ofFn fun i : Fin Q => + (⟨mem (q + 2 * i.val) % n, Nat.mod_lt _ hn⟩, + ⟨mem (q + 2 * i.val + 1) % n, Nat.mod_lt _ hn⟩) + +/-- Decoding retains exactly the original occurrence count. -/ +theorem decodedQueries_length {n : ℕ} (hn : 0 < n) (mem : ℕ → ℕ) (q Q : ℕ) : + (decodedQueries hn mem q Q).length = Q := List.length_ofFn + +/-- Every actual in-range coordinate word reappears unchanged in the ordinary callee input. -/ +theorem decodedQueries_words {m n q Q : ℕ} (hn : 0 < n) (mem : ℕ → ℕ) + (X : Matrix (Fin n) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin n) ℤ) + (hcoord : ∀ i < Q, mem (q + 2 * i) < n ∧ mem (q + 2 * i + 1) < n) : + ∀ a < 2 * Q, mem (q + a) = + (State.init (QueriedProduct.input true (19 * m) X Y + (decodedQueries hn mem q Q)) : State 160).data.mem (5 + 2 * (n * D m) + a) := by + intro a ha + have hi : a / 2 < Q := (Nat.div_lt_iff_lt_mul (by decide : 0 < 2)).mpr (by omega) + let i : Fin (decodedQueries hn mem q Q).length := ⟨a / 2, by + rw [decodedQueries_length]; exact hi⟩ + let s : Fin 2 := ⟨a % 2, Nat.mod_lt _ (by decide)⟩ + have hw := QueriedProduct.input_query_word true (19 * m) X Y + (decodedQueries hn mem q Q) i s + have hsplit : 2 * (a / 2) + a % 2 = a := by omega + change (State.init (QueriedProduct.input true (19 * m) X Y + (decodedQueries hn mem q Q)) : State 160).data.mem + (QueriedProduct.queryInputBase m n + 2 * (a / 2) + a % 2) = _ at hw + rw [show QueriedProduct.queryInputBase m n + 2 * (a / 2) + a % 2 = + 5 + 2 * (n * D m) + a by unfold QueriedProduct.queryInputBase; omega] at hw + rw [hw] + have hc := hcoord (a / 2) hi + simp only [decodedQueries, List.get_ofFn, i, Fin.val_cast] + by_cases hzero : a % 2 = 0 + · have he : s = 0 := Fin.ext hzero + simp only [he, ↓reduceIte] + rw [Nat.mod_eq_of_lt hc.1] + congr 1 + omega + · have hone : a % 2 = 1 := by have := Nat.mod_lt a (by decide : 0 < 2); omega + have he : s ≠ 0 := by intro h; exact hzero (congrArg Fin.val h) + simp only [he, ↓reduceIte] + rw [Nat.mod_eq_of_lt hc.2] + congr 1 + omega + +/-- A native left-matrix span reads exactly as the ordinary product's original raw input. -/ +theorem raw_left_words {m n s : ℕ} (mem : ℕ → ℕ) + (X : Matrix (Fin n) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin n) ℤ) + (qs : List (Fin n × Fin n)) + (hX : ∀ (i : Fin n) (k : Fin (D m)), mem (s + (i.val * D m + k.val)) = (X i k).toNat) : + ∀ a < n * D m, mem (s + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem (5 + a) := by + intro a ha + have hd : 0 < D m := by change 0 < 4 ^ m; positivity + have hi : a / D m < n := (Nat.div_lt_iff_lt_mul hd).mpr ha + have hk : a % D m < D m := Nat.mod_lt _ hd + have hw := QueriedProduct.input_matrix_word (L := 19 * m) true X Y qs false + (a / D m) (a % D m) hi hk + have he : a / D m * D m + a % D m = a := Nat.div_add_mod' a (D m) + simp only [BandPreparation.matrixOffset, Bool.false_eq_true, ↓reduceIte, he, + BandPreparation.matrixValues, QueriedProduct.entryWord, hi, hk, ↓reduceDIte] at hw + rw [show 5 + a = a + 5 by omega, hw] + simpa only [he] using hX ⟨a / D m, hi⟩ ⟨a % D m, hk⟩ + +/-- The right original block retains its `D×n` row-major order. -/ +theorem raw_right_words {m n t : ℕ} (hn : 0 < n) (mem : ℕ → ℕ) + (X : Matrix (Fin n) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin n) ℤ) + (qs : List (Fin n × Fin n)) + (hY : ∀ (k : Fin (D m)) (i : Fin n), mem (t + (k.val * n + i.val)) = (Y k i).toNat) : + ∀ a < n * D m, mem (t + a) = + (State.init (QueriedProduct.input true (19 * m) X Y qs) : State 160).data.mem + (5 + n * D m + a) := by + intro a ha + have hi : a % n < n := Nat.mod_lt _ hn + have hk : a / n < D m := (Nat.div_lt_iff_lt_mul hn).mpr (by simpa only [Nat.mul_comm] using ha) + have hw := QueriedProduct.input_matrix_word (L := 19 * m) true X Y qs true + (a % n) (a / n) hi hk + have he : a / n * n + a % n = a := Nat.div_add_mod' a n + simp only [BandPreparation.matrixOffset, ↓reduceIte, he, + BandPreparation.matrixValues, QueriedProduct.entryWord, hi, hk, ↓reduceDIte] at hw + rw [show 5 + n * D m + a = a + n * D m + 5 by omega, hw] + simpa only [he] using hY ⟨a / n, hk⟩ ⟨a % n, hi⟩ + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/SourceBounds.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/SourceBounds.lean new file mode 100644 index 000000000..bc1d61eea --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/SourceBounds.lean @@ -0,0 +1,109 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Flat +public import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationBounds + +/-! +# Original-size prefix bounds and the per-record thin saving + +The initialized code has physical-width-independent polynomial words and memory support at +every admitted wider width. Its charge is the actual producer preparation plus the selected +record's actual thin product. Summing all products, solving ExactTriangle and reconstructing +witnesses remain separate; no aggregate subcubic claim is made here. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.WordRAM Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Explicit input-derived bound for all prefix words and the address support, independent +of any larger admitted physical width. This is a reservation, not a bulk initialization. -/ +@[expose] def wordBound (n D g p A : ℕ) : ℕ := 2 ^ admissionWidth n D g p A + +/-- The prefix bound is polynomial in original size for the usual original scalar/weight +envelopes, with an explicit fixed exponent for each magnitude exponent κ. -/ +theorem wordBound_le_pow {n D g p A κ : ℕ} (hn : 1 ≤ n) + (hD : D ≤ n) (hg : g ≤ n) (hp : p ≤ n) (hA : A ≤ (n + 2) ^ κ) : + wordBound n D g p A ≤ (n + 2) ^ (514 * κ + 11812) := by + let b := n + 2 + have hb : 1 < b := by dsimp only [b]; omega + have hc := Nat.clog_pos (by decide : 1 < (2 : ℕ)) hb + have hp2 := Nat.pow_pred_clog_lt_self (by decide : 1 < (2 : ℕ)) hb + have he : Nat.clog 2 b = (Nat.clog 2 b).pred + 1 := (Nat.succ_pred_eq_of_pos hc).symm + have hbase : 2 ^ Nat.clog 2 b ≤ b ^ 2 := by + rw [he, pow_succ] + nlinarith only [hp2, show 2 ≤ b by omega] + have hw := admissionWidth_le_log hn hD hg hp hA + have hpow := Nat.pow_le_pow_right (by decide : 1 ≤ (2 : ℕ)) hw + change 2 ^ admissionWidth n D g p A ≤ _ + calc + _ ≤ 2 ^ ((257 * κ + 5906) * Nat.clog 2 b) := hpow + _ = (2 ^ Nat.clog 2 b) ^ (257 * κ + 5906) := by rw [← Nat.pow_mul, Nat.mul_comm] + _ ≤ (b ^ 2) ^ (257 * κ + 5906) := Nat.pow_le_pow_left hbase _ + _ = b ^ (514 * κ + 11812) := by + rw [← Nat.pow_mul] + exact congrArg (fun k : ℕ => b ^ k) (by omega : 2 * (257 * κ + 5906) = 514 * κ + 11812) + +/-- Every actual native prefix at every admitted width has input-derived bounded words and +zero memory beyond the same bound. The real halting run at the minimum admitted width and +the existing width-independent prefix theorem derive the result. -/ +theorem bounded_reachable_original {W m n g p A k : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hD : 16 ≤ D m) + (hg : 1 ≤ g) (hp : 0 < p) (hN : D m ^ 18 ≤ n) + (hv : ∀ z ∈ values, z.natAbs ≤ A) (hW : admissionWidth n (D m) g p A ≤ W) + {s : State 320} (hr : Runs W (entryProgram.program Reg.result) + (State.init (PreparationInput.input n (D m) g p values)) s k) : + (∀ i, s.data.regs i < wordBound n (D m) g p A) ∧ + (∀ a, s.data.mem a < wordBound n (D m) g p A) ∧ + ∀ a, wordBound n (D m) g p A ≤ a → s.data.mem a = 0 := by + obtain ⟨hi, a, idx, ha, hidx, hord, y, t, ht, hpref, he, hy, yr, hv', hf, hreg, hQ⟩ := + runs_input values hlen hD hg hp hN hv le_rfl + have hlow := Runs.prefix_at_smaller_width entryProgram_fixed.2 hW hr he + have hs := hlow.bounded (Data.Bounded.init hi) + refine ⟨hs.1, hs.2, ?_⟩ + intro a ha + rw [hlow.mem_above (Data.Bounded.init hi) ha] + cases a with + | zero => + change 2 ^ admissionWidth n (D m) g p A ≤ 0 at ha + have := Nat.two_pow_pos (admissionWidth n (D m) g p A) + omega + | succ a => + change (PreparationInput.input n (D m) g p values).getD a 0 = 0 + apply List.getD_eq_default + have hlen := hi.1 + dsimp only [Fits] at hlen + change 2 ^ admissionWidth n (D m) g p A ≤ a + 1 at ha + omega + +/-- Complete producer preparation plus one actual record product, including the final halt. +The per-record sparsity is supplied by its actual native chunk, not an aggregate solver oracle. -/ +theorem initialized_time_le {m n g p : ℕ} (values : List ℤ) (qs : List (Fin n × Fin n)) + (hD : 16 ≤ D m) (hN : D m ^ 18 ≤ n) (hg : 1 ≤ g) + (hp : p ∈ Nat.sqrtPrimeWindow (D m)) (hQ : qs.length * 2 ^ m ≤ n ^ 2) : + ((preparationTime n (D m) g p values + timeBound (m := m) qs + 1 : ℕ) : ℝ) ≤ + 690 * (n : ℝ) ^ 2 * D m * g + + 34603008 * ((19 * m + 1 : ℕ) : ℝ) * (n : ℝ) ^ 2 / (D m : ℝ) ^ (1 / 18 : ℝ) := by + have hDn : D m ≤ n := (Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN + have hpTime := PreparationBounds.computed_timeBound_le values hD hDn hg hp + have hpReal : (preparationTime n (D m) g p values : ℝ) ≤ + 690 * (n : ℝ) ^ 2 * D m * g := by + have hpNat : preparationTime n (D m) g p values ≤ 690 * n ^ 2 * D m * g := by + simpa only [preparationTime, pow_two] using hpTime + exact_mod_cast hpNat + have ht := thin_timeBound_sparse qs hN hQ + push_cast + push_cast at ht + linarith only [hpReal, ht] + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/ComplexityTests/WordRAMExactTriangleRecordProduct.lean b/ComplexityTests/WordRAMExactTriangleRecordProduct.lean new file mode 100644 index 000000000..4c1fed3b6 --- /dev/null +++ b/ComplexityTests/WordRAMExactTriangleRecordProduct.lean @@ -0,0 +1,135 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +-- upstream: local-only (tests) + +import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.SourceBounds + +/-! +# Real record-decoder prefixes at admitted positive-depth geometry + +Full positive-depth multiplication is too large for a concrete VM regression: its smallest +producer middle dimension is sixteen and the thin geometry requires `n≥16^18`. These tests +execute actual descriptor/log/header code at that valid geometry, without allocating matrices +or claiming a complete producer run for the fixture. General initialized composition is +checked separately by the library execution theorem. +-/ + +set_option linter.hashCommand false + +namespace ComplexityTests.WordRAMExactTriangleRecordProduct + +open Cslib.WordRAM Cslib.Algorithms.WordRAM.ExactTriangle +open Cslib.Algorithms.ExactTriangle Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +private def side : ℕ := 16 ^ 18 +private def area : ℕ := side * 16 +private def stride : ℕ := 6 + 2 * area +private def record : ℕ := 100 + stride +private def heap : ℕ := 100 + 2 * stride + +private def fixture : Data 320 := + { regs := fun i => if i.val = 39 then 100 else if i.val = 42 then heap + else if i.val = 64 then 1 else 73 + mem := fun a => if a = 1 then 16 else if a = 3 then side + else if record ≤ a && a < record + 6 then + [3, 1, 50, 2, record + 6, record + 6 + area].getD (a - record) 0 + else if a = 50 then side - 1 else if a = 51 then 7 + else if a = 52 then 8 else if a = 53 then side - 2 else 0 } + +private def decodePrefix : Code .standard 320 := + .seq RecordProduct.readRecord (.seq RecordProduct.logarithm RecordProduct.writeHeader) + +private def observe : Option (List ℕ × List ℕ × Bool × ℕ) := + (decodePrefix.eval 32768 512 fixture).map fun (y, steps) => + ((List.range 5).map (fun i => y.mem (heap + i)), + (List.range 4).map (fun i => y.mem (50 + i)), + decide (∀ i : Fin 64, y.regs (RecordProduct.producerBank i) = + fixture.regs (RecordProduct.producerBank i)), steps) + +-- Actual nonzero ordinal selects the second six-word record, recovers m=2 and writes L=38. +#guard observe == some ([4 + 2 * area + 4, 38, 2, side, 2], + [side - 1, 7, 8, side - 2], true, 64) + +-- Geometry is fully admitted; the smaller tempting VM dimension would violate it. +#guard side ≥ 16 ^ 18 +#guard ¬(16 ≥ 16 ^ 18) + +-- Actual source words are not doubled by the adapter's copy routine. +private def copyFixture : Data 320 := + { regs := fun i => if i.val = 96 then 10 else if i.val = 97 then 30 + else if i.val = 98 then 2 else 91 + mem := fun a => if a = 10 then 1 else if a = 11 then 0 else 77 } + +#guard (RecordProduct.copy.eval 16 8 copyFixture).map (fun (y, steps) => + (y.mem 30, y.mem 31, y.mem 29, y.mem 32, y.regs 39, steps)) == + some (1, 0, 77, 77, 91, 18) + +private def zeroWeights (n : ℕ) : List ℤ := List.replicate (3 * (n * n)) 0 +private def physicalWidth (n : ℕ) : ℕ := RecordProduct.admissionWidth n (D 2) 1 3 0 + +/-- A fully admitted, nonempty producer→adapter run at positive inner depth. The list is kept +symbolic: this kernel proof uses the general actual execution theorem, not VM enumeration of +the enormous matrix family. Its observed answer and charged halt use the same final memory. -/ +private theorem initialized_zero (N : ℕ) (hgeo : D 2 ^ 18 ≤ N) : + ∃ a : Data 64, ∃ idx : InstanceIndex 3, ∃ y : Data 320, ∃ steps : ℕ, + Runs (physicalWidth N) (RecordProduct.entryProgram.program RecordProduct.Reg.result) + (State.init (PreparationInput.input N (D 2) 1 3 (zeroWeights N))) + ⟨RecordProduct.entryProgram.size, y⟩ steps ∧ + Executes (physicalWidth N) (RecordProduct.entryProgram.program RecordProduct.Reg.result) + (State.init (PreparationInput.input N (D 2) 1 3 (zeroWeights N))) + (y.regs RecordProduct.Reg.result) (steps + 1) ∧ + let qs := RecordProduct.decodedQueries + ((Nat.pow_pos (by decide : 0 < D 2) (n := 18)).trans_le hgeo) a.mem + (RecordProduct.recordQueries N (D 2) 3 idx) + (RecordProduct.recordQueryCount N (D 2) 3 (fun q => (zeroWeights N).getD q 0) idx) + ∃ first : Fin qs.length, + y.mem (y.regs RecordProduct.Reg.result) = Signed.encode + ((RecordProduct.leftMatrix 2 N 1 3 (fun q => (zeroWeights N).getD q 0) idx * + RecordProduct.rightMatrix 2 N 1 3 (fun q => (zeroWeights N).getD q 0) idx) + (qs.get first).1 (qs.get first).2) := by + have hv : ∀ z ∈ (zeroWeights N), z.natAbs ≤ 0 := by + intro z hz + have he : z = 0 := List.eq_of_mem_replicate hz + subst z + decide + obtain ⟨hi, a, idx, ha, hidx, hord, y, steps, ht, hr, he, hb, yr, hvals, hf, hg, hQ⟩ := + RecordProduct.runs_input (m := 2) (n := N) (g := 1) (p := 3) (A := 0) + (W := (physicalWidth N)) (zeroWeights N) (by simp [zeroWeights]) + (by decide) (by decide) (by decide) hgeo hv le_rfl + have hpos := RecordProduct.recordQueryCount_pos (n := N) (D := D 2) (g := 1) (p := 3) + (fun q => (zeroWeights N).getD q 0) + (by decide : (3 : ℕ) ≠ 0) (by decide : 1 ≤ D 2) + ((Nat.le_self_pow (by decide : 18 ≠ 0) (D 2)).trans hgeo) idx hidx + let qs := RecordProduct.decodedQueries + ((Nat.pow_pos (by decide : 0 < D 2) (n := 18)).trans_le hgeo) a.mem + (RecordProduct.recordQueries N (D 2) 3 idx) + (RecordProduct.recordQueryCount N (D 2) 3 (fun q => (zeroWeights N).getD q 0) idx) + have hlen : 0 < qs.length := by rw [RecordProduct.decodedQueries_length]; exact hpos + let first : Fin qs.length := ⟨0, hlen⟩ + have hv0 := hvals first + refine ⟨a, idx, y, steps, hr, ?_, first, ?_⟩ + · simpa only [yr] using he + · simpa only [first, Fin.val_mk, Nat.add_zero, ← yr] using hv0 + +example : ∃ a : Data 64, ∃ idx : InstanceIndex 3, ∃ y : Data 320, ∃ steps : ℕ, + Runs (physicalWidth side) (RecordProduct.entryProgram.program RecordProduct.Reg.result) + (State.init (PreparationInput.input side (D 2) 1 3 (zeroWeights side))) + ⟨RecordProduct.entryProgram.size, y⟩ steps ∧ + Executes (physicalWidth side) (RecordProduct.entryProgram.program RecordProduct.Reg.result) + (State.init (PreparationInput.input side (D 2) 1 3 (zeroWeights side))) + (y.regs RecordProduct.Reg.result) (steps + 1) ∧ + let qs := RecordProduct.decodedQueries (by dsimp [side]; positivity : 0 < side) a.mem + (RecordProduct.recordQueries side (D 2) 3 idx) + (RecordProduct.recordQueryCount side (D 2) 3 (fun q => (zeroWeights side).getD q 0) idx) + ∃ first : Fin qs.length, + y.mem (y.regs RecordProduct.Reg.result) = Signed.encode + ((RecordProduct.leftMatrix 2 side 1 3 (fun q => (zeroWeights side).getD q 0) idx * + RecordProduct.rightMatrix 2 side 1 3 (fun q => (zeroWeights side).getD q 0) idx) + (qs.get first).1 (qs.get first).2) := initialized_zero side le_rfl + +end ComplexityTests.WordRAMExactTriangleRecordProduct diff --git a/ROADMAP.md b/ROADMAP.md index d80448735..a6be60a66 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -526,9 +526,19 @@ each statement against the numbered result. Organize modules by subject, not cha `D=4^m`, `L=19m`, `D^18≤N` and `Q*2^m≤N²`, the full charge including halt is at most `17301504*(19m+1)*N²/D^(1/18)`. Distinct-set sparsity retains an additive occurrence charge. Every width at least `256*clog₂(base+N+Q+P+5)` suffices for initialized caller-word bound `P`. - The reserved window is at most `(2*(N+Q+3))^128` and is not bulk-initialized. Actual ET-record - reads, matrix/query placement, producer-readback composition and the full [#1394] endpoint - remain open. + The reserved window is at most `(2*(N+Q+3))^128` and is not bulk-initialized. +- **Prepared ET-record product calls** ([#1626](https://github.com/cslib-community/complexitylib/issues/1626)): + the native adapter reads the computed six-word record, recovers the depth with actual binary + logarithm code, and copies raw matrices and query occurrences above the producer's heap. + It invokes the complete callable product in a separate register bank and preserves the + catalogue, sources and producer registers. The fixed 320-register initialized client runs + the producer and consumes its first record; the caller interface supports every occupied + ordinal. Original polynomial headers/weights give positive logarithmic admission and + polynomial words/address support at every wider width. Adaptation costs `O(nD+Q)` beyond + the actual callee; actual chunks derive `Q*2^m≤n²`, giving the per-record halt-inclusive bound + `34603008*(19m+1)*n²/D^(1/18)` at `D=4^m`, `L=19m`, `D^18≤n`. + The outer instance loop, aggregate subcubic ET bound, backend/witness reconstruction and + the full [#1394] ordinary-3SUM 1.9992 endpoint remain open. - **Conditional Convolution-3SUM host** ([#1524]): the charged native host initializes from the ordinary input and runs every generated instance, conditional on a concrete solver's execution, correctness and frame theorem. This completes the conditional host milestone. Integrating the diff --git a/UPSTREAM.md b/UPSTREAM.md index e55fed516..7f5575c79 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -384,7 +384,26 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he from that derived physical admission. Initialized scratch is zero, so its `P` only needs to bound the protected caller prefix. The generic compiler theorem remains separate from the concrete thin-geometry admission and saving. - The real ET-record/matrix/query adapter and whole solver remain subsequent work under #1394. + The real ET-record/matrix/query adapter is tracked in + [#1626](https://github.com/cslib-community/complexitylib/issues/1626). Its fixed 320-register + native code computes the selected record address, reads all six fields, recovers the exact + `4^m` exponent with existing `NatLog`, stores local original headers and copies the two raw + matrices and interleaved occurrence coordinates above the actual producer heap. Raw zero/one + words are copied unchanged; the callee's existing loop performs charged signed doubling. + The complete 163-register callable bank is renamed together, preserving the producer's low + 64 registers. Memory relocation uses the real callable compiler, never register renaming alone. + `PreparationInput.exec_input` exposes the existing structured initialized derivation so real + caller code can compose it. Its private fit proof stays private; existing `runs_input` reuses + the new rule with unchanged statement, assumptions, exact readback and halt-inclusive budget. + The first-record initialized client supplies no layout/run/time/fit/solver certificate; + actual producer readback derives all source pointer ranges and coordinate meanings, and + actual chunk counts derive occurrence sparsity. The general caller supports every occupied + record ordinal. The per-record charge including halt is at most three times the original + product plus `4096*(nD+Q+1)`, or `34603008*(19m+1)*n²/D^(1/18)` under the admitted geometry. + The original producer word bound supplies caller `P` and heap placement; the complete wider + initialized prefixes have a positive logarithmic width threshold and polynomial words/support + independent of the physical width. Outer parameter/prime selection and instance iteration, + aggregate solving/witness reconstruction and the whole #1394 endpoint remain separate. Stage `exec_*` lemmas are internal implementation APIs exported across proof modules; callers use the flat initialized runs, width theorem or pointer/epoch kernel. The narrow `WordRAM.ArrayAppend` and `WordRAM.InputArray` topic modules keep append/input dependencies From 96842bbf6470cb9082ed2a7beb57e74a575b84db Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 29 Oct 2026 11:48:22 -0400 Subject: [PATCH 61/73] feat(ExactTriangle): validate initialized record product adapter Refs #1626. The full #1394 ordinary distinct-position 3SUM 1.9992 endpoint remains open. No PR is opened by this worker; fresh whole-layer nonauthoring review is pending coordinator arrangement. Validated source tree: 8f59a54c6de13459c0a85d86546b12fc12c5003c Pinned main base: 8569de69ef05751c7e39e2ddd99d24d49f6aa1ec Preserved source checkpoint: 8f46341d456f6e3651beb02aac898a22a34a9883 Normal main merge: 8612aff051db4590f15efcb02fa37deb2992f6f5. Only ROADMAP/UPSTREAM conflicted; complete landed prose is preserved. After the checkpoint the only own Lean deltas are canonical imports and terminal Header proof simplification. Native programs, public statements and supplier credit are preserved. Original native implementation: fixed 320 registers and <=132352 flat slots including halt, no width reads. Reads the real six-word record/heap, computes m using NatLog, copies raw X/Y and all interleaved query occurrences, and invokes the complete 163-register runtime-relocated callable bank. Copying keeps raw 0/1; the callee charges signed doubling once. No catalogue/window snapshot or clearing. All producer registers and catalogue memory are preserved. PreparationInput.exec_input is a new public structured theorem using the unchanged private fit derivation and original budget minus halt. Existing runs_input consumes it with identical statement, assumptions, same-y Output/flat Runs facts and halt-inclusive bound. This API/proof delta requires fresh review, not mechanical carry-forward. RecordProduct.exec_of_output supports every occupied ordinal. RecordProduct.runs_input starts actual State.init from raw D,g,n,p and 3n^2 canonical signed weights, selects first ordinal zero with an actual instruction and proves it exists. It concludes same-y prefix Runs, charged halt, exact product words, frames and chunk occurrence sparsity. Assumptions: D=4^m >=16, g>=1, p>0, n>=D^18, weights natAbs<=A and W>=admissionWidth. No record, layout, intermediate-fit, run/time or solver certificate is an original algorithm input. P0 is the actual producer wordBound. After setup/copy all prefix words, including dirty loads before remainder, are bounded by the derived P*=2^admissionWidth. P0 is not assumed unchanged by adapter arithmetic, and generic runs_caller is not silently instantiated. For n>=1, D,g,p<=n and A<=(n+2)^kappa, admissionWidth <=(257*kappa+5906)*clog_2(n+2), with positive threshold, and all wider-prefix words/address support are bounded by (n+2)^(514*kappa+11812), independently of physical W. Actual adapter time excluding halt is callableTime+8m+14nD+14Q+71. Generic execution preserves duplicate occurrences; distinct-set sparsity retains additive Q. Actual chunks derive Q*2^m<=n^2. Per-record thin charge including halt is 34603008*(19m+1)*n^2/D^(1/18). Producer charge 690*n^2*D*g is separate and requires p in Nat.sqrtPrimeWindow D. No aggregate ET iteration/subcubic solver, witness backend or whole 3SUM endpoint is claimed. All six gates passed together, LEAN_NUM_THREADS=4, COMPLEXITYLIB_CACHE_AUTO=0, lake --no-cache, PYTHONUTF8=1: - lake --no-cache build --wfail --iofail: PASS (1321.86s) - lake --no-cache exe mk_all --check: PASS (20.45s) - lake --no-cache test --wfail --iofail: PASS (1393.41s) - lake --no-cache lint: PASS (49.44s) - lake --no-cache exe lint-style: PASS (18.25s) - lake --no-cache exe checkInitImports: PASS (17.45s) Supplemental serial native Lean -Ewarning checks, LEAN_NUM_THREADS=1: - lake --no-cache env lean -Ewarning ComplexityTests/Smoke.lean: PASS (18.86s) - lake --no-cache env lean -Ewarning ComplexityTests/Axioms.lean: PASS (23.69s) Final static/source checks: - hygiene.log: PASS - changed-library-lint.log: PASS - changed-test-lint.log: PASS - changed-files.log: PASS - pr-names.log: PASS - full-base-diff.log: PASS - staged-diff.log: PASS - unstaged-diff.log: PASS - 18 changed library modules / 1 new test; 3840 inherited source/abstract files unchanged. All changed modes 100644. Existing producer public statements and private fit proofs are identical; complete native producer/callee suppliers and own Program/invoke bodies unchanged. - Actual-base-to-final whitespace check covers the whole layer, not only staged/unstaged edits. Tests: actual decoder/log/header VM at admitted m=2,n=16^18 and nonzero ordinal; raw 0/1 copy/sentinels; rejected small geometry; closed fully admitted nonempty initialized producer-to-product kernel client, with observed answer tied to same-y Runs and halt. No huge-input VM enumeration is claimed. Source: anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5, ThreeSumApsp/Sec2/Theorem5.lean:61-67 (checked conclusion fragment = (X * Y) I J), 146-149 (distinct-set thin leaves), and Programs/Sec3/Theorem17/Instances/WriteMatrices.lean:13-23 (raw matrix shapes/local numbering). Both inspected files equal pinned Git blobs. Existing finite/source credit retained; adapter/control/native execution and resource proofs are original. No Light semantics or instruction constants transferred. Abstract assessment: selected statements, catalogue and rendering unchanged; no headline promotion. AI assistance: Codex implementation/self-review with Lean MCP. Independent whole-layer review, including new structured producer theorem and all native/API/resource reasoning, remains pending. No self/platform/human approval claim. --- Complexity.lean | 17 +++++++++++++++++ .../ExactTriangle/RecordProduct/Header.lean | 4 ++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Complexity.lean b/Complexity.lean index 227ff8d31..6f3915c1f 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -102,6 +102,23 @@ public import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationInput public import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationSpec public import Complexity.Algorithms.WordRAM.ExactTriangle.QueryScan public import Complexity.Algorithms.WordRAM.ExactTriangle.QuerySpec +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Bounds +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Copy +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Decode +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Entry +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Exec +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.First +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Flat +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Header +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.InputSpec +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Kernel +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Prepare +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Producer +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.ProducerQueries +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Program +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.RawInput +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.SourceBounds public import Complexity.Algorithms.WordRAM.ExactTriangle.ResidueClasses public import Complexity.Algorithms.WordRAM.ExactTriangle.WriteMatrices public import Complexity.Algorithms.WordRAM.FusionTree.Binary diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Header.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Header.lean index 64bf49a99..3cb3f2ec3 100644 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Header.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Header.lean @@ -60,8 +60,8 @@ theorem exec_writeHeader {W b m n A Q : ℕ} (x : Data 320) refine ⟨y, he, ?_, ?_, ?_⟩ · intro j change y.mem (b + j.val) = _ - fin_cases j <;> simp +decide [y, Nat.add_assoc, Nat.mul_comm] - omega + fin_cases j <;> simp +decide [y, two_mul, Nat.add_assoc, Nat.add_left_comm, Nat.add_comm, + Nat.mul_comm] · intro a ha have h0 : a ≠ b := by omega have h1 : a ≠ b + 1 := by omega From 0e107006b59bae33fc4554b74cccf8321b9fb7e8 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 29 Oct 2026 13:45:35 -0400 Subject: [PATCH 62/73] feat(ExactTriangle): checkpoint native candidate-verification source Work in progress for #1639, owned by codex-three-sum-continuation-t3-3dda8289. This local checkpoint is not a review/publication candidate. Implemented actual 32-register signed interval verification and 400-register record decoder/query consumer, with native execution, memory/register frame, query-loop potential, original witness soundness and actual scan accounting. The consumer charges 5sqrt(D)+8Q+(42*length+28)*S+52 before halt, with S at most failed accepted occurrences plus one. Product detection is derived from the actual raw value matrices and existing finite lopsided graph; distinct failed chunk occurrences inject into existing false-positive accounting. Also packages decoded producer chunks in rank order with a concrete initialized client and moves the zero/one writer fact to its actual writer module. Shared piece coordinates are extracted with the existing adjacency client migrated. Targeted Lean MCP diagnostics are clean for all source modules and three new test files at this checkpoint. Actual native regressions exercise first success, two modular false positives, zero answers, signed/empty/restricted intervals, query word order, sentinels and the full low product-bank frame. Positive-depth consumer fixtures use n=16^18,D=16 and do not claim a full producer run. No import aggregates, full six-gate or static audit has run: root retains the sole global/static slot. Initialized producer/product/consumer composition, original-input width and every-prefix/support bounds, the full finite failure bridge and final source/cost mapping remain unfinished. #1394/1.9992 stays open. No source or ref of the published supplier PR #1637 was changed. AI-assisted implementation using Codex and targeted Lean MCP diagnostics. --- .../Algorithms/ExactTriangle/PieceBounds.lean | 65 ++++ .../ExactTriangle/PreparationAdjacency.lean | 324 +++++++++--------- .../RecordProduct/Detection.lean | 197 +++++++++++ .../ExactTriangle/RecordProduct/Producer.lean | 9 +- .../RecordProduct/QueryValues.lean | 98 ++++++ .../ExactTriangle/RecordWitness/Consume.lean | 100 ++++++ .../ExactTriangle/RecordWitness/Header.lean | 250 ++++++++++++++ .../ExactTriangle/RecordWitness/Kernel.lean | 188 ++++++++++ .../ExactTriangle/RecordWitness/Pair.lean | 83 +++++ .../ExactTriangle/RecordWitness/Program.lean | 188 ++++++++++ .../ExactTriangle/RecordWitness/Queries.lean | 237 +++++++++++++ .../ExactTriangle/RecordWitness/Readback.lean | 113 ++++++ .../ExactTriangle/RecordWitness/Spec.lean | 79 +++++ .../ExactTriangle/RecordWitness/Step.lean | 145 ++++++++ .../ExactTriangle/RecordWitness/Trace.lean | 62 ++++ .../ExactTriangle/WitnessScan/Cell.lean | 74 ++++ .../ExactTriangle/WitnessScan/Exec.lean | 231 +++++++++++++ .../ExactTriangle/WitnessScan/Program.lean | 139 ++++++++ .../ExactTriangle/WitnessScan/Setup.lean | 87 +++++ .../WordRAM/ExactTriangle/WriteMatrices.lean | 5 + .../WordRAMExactTriangleQueryValues.lean | 86 +++++ .../WordRAMExactTriangleRecordWitness.lean | 69 ++++ .../WordRAMExactTriangleWitnessScan.lean | 88 +++++ 23 files changed, 2739 insertions(+), 178 deletions(-) create mode 100755 Complexity/Algorithms/ExactTriangle/PieceBounds.lean create mode 100755 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Detection.lean create mode 100755 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/QueryValues.lean create mode 100755 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Consume.lean create mode 100755 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Header.lean create mode 100755 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Kernel.lean create mode 100755 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Pair.lean create mode 100755 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Program.lean create mode 100755 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Queries.lean create mode 100755 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Readback.lean create mode 100755 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Spec.lean create mode 100755 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Step.lean create mode 100755 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Trace.lean create mode 100755 Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Cell.lean create mode 100755 Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Exec.lean create mode 100755 Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Program.lean create mode 100755 Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Setup.lean create mode 100755 ComplexityTests/WordRAMExactTriangleQueryValues.lean create mode 100755 ComplexityTests/WordRAMExactTriangleRecordWitness.lean create mode 100755 ComplexityTests/WordRAMExactTriangleWitnessScan.lean diff --git a/Complexity/Algorithms/ExactTriangle/PieceBounds.lean b/Complexity/Algorithms/ExactTriangle/PieceBounds.lean new file mode 100755 index 000000000..2cf000b45 --- /dev/null +++ b/Complexity/Algorithms/ExactTriangle/PieceBounds.lean @@ -0,0 +1,65 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.ExactTriangle.Splitting + +/-! +# Numeric coordinates of ExactTriangle pieces + +These bounds connect the existing finite pieces with the truncated original-vertex intervals +used by both native matrix preparation and witness recovery. They expose the same coordinate +facts previously private to the prepared-adjacency consumer, without changing the finite pieces. +-/ + +public section + +namespace Cslib.Algorithms.ExactTriangle + +/-- The active piece/label rectangle fits in the source middle dimension. -/ +theorem pieceLength_mul_le_of_mem_sqrtPrimeWindow {D g p n k : ℕ} (hg : 1 ≤ g) + (hp : p ∈ Nat.sqrtPrimeWindow D) : + min (pieceSize D g) (n - k * pieceSize D g) * p ≤ D := by + have hpS : p ≤ sOf D := Nat.le_sqrt'.mpr (Nat.mem_sqrtPrimeWindow_iff_sq.mp hp).2.2.le + have hlen : min (pieceSize D g) (n - k * pieceSize D g) ≤ sOf D := + (min_le_left _ _).trans (pieceSize_le_sOf hg) + exact (Nat.mul_le_mul hlen hpS).trans (Nat.sqrt_le D) + +/-- An original piece vertex has its computed local coordinate in the actual truncated interval. -/ +theorem piece_coordinate {n D g k : ℕ} (hD : 16 ≤ D) (hg : 1 ≤ g) + (c : Fin n) (hc : c ∈ piece n D g k) : + k * pieceSize D g + c.val % pieceSize D g = c.val ∧ + c.val % pieceSize D g < min (pieceSize D g) (n - k * pieceSize D g) := by + have hB := pieceSize_pos hD hg + have hdiv := (mem_piece c).mp hc + have hsplit := Nat.div_add_mod c.val (pieceSize D g) + rw [hdiv, Nat.mul_comm] at hsplit + have hmod := Nat.mod_lt c.val hB + exact ⟨hsplit, lt_min hmod (by have := c.isLt; omega)⟩ + +/-- The finite piece is precisely its original-coordinate truncated interval. -/ +theorem mem_piece_iff_interval {n D g k : ℕ} (hD : 16 ≤ D) (hg : 1 ≤ g) (c : Fin n) : + c ∈ piece n D g k ↔ k * pieceSize D g ≤ c.val ∧ + c.val < k * pieceSize D g + min (pieceSize D g) (n - k * pieceSize D g) := by + constructor + · intro hc + obtain ⟨he, hv⟩ := piece_coordinate hD hg c hc + omega + · rintro ⟨hlo, hhi⟩ + apply (mem_piece c).mpr + have hB := pieceSize_pos hD hg + have hsmall : c.val - k * pieceSize D g < pieceSize D g := by + have := min_le_left (pieceSize D g) (n - k * pieceSize D g) + omega + have he : c.val = k * pieceSize D g + (c.val - k * pieceSize D g) := by omega + have hsmall' : c.val - pieceSize D g * k < pieceSize D g := by + simpa only [Nat.mul_comm k (pieceSize D g)] using hsmall + rw [he, Nat.mul_comm k (pieceSize D g), Nat.mul_add_div hB, + Nat.div_eq_of_lt hsmall', Nat.add_zero] + +end Cslib.Algorithms.ExactTriangle diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/PreparationAdjacency.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/PreparationAdjacency.lean index e28b7fb0e..34f5f03df 100644 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/PreparationAdjacency.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/PreparationAdjacency.lean @@ -1,171 +1,153 @@ -/- -Copyright (c) 2026 Erik Demaine. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Erik Demaine --/ - -module --- upstream: candidate - -public import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationSpec - -/-! -# Adjacency in prepared native ExactTriangle matrices - -These consumer lemmas connect actual prepared memory to the existing finite lopsided graph. -The source prime window supplies the padding bound, and piece membership supplies each local -coordinate. Neither is an additional layout certificate for the program. - -## Main results - -* `PreparationSpec.instance_adjacency`: actual stored cells give the finite adjacency relations - and zero padding, with piece coordinates and middle-width admissibility derived. - -## References - -The adjacency relations are those of the pinned -[Theorem 17 matrix construction](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Programs/Sec3/Theorem17/Instances/WriteMatrices.lean). -The composition with native memory readback is original. --/ - -public section - -namespace Cslib.Algorithms.WordRAM.ExactTriangle.PreparationSpec - -open Cslib.WordRAM Cslib.Algorithms.ExactTriangle Cslib.Matrix - -private theorem prepared_middle_fits {D g p n k : ℕ} (hg : 1 ≤ g) - (hp : p ∈ Nat.sqrtPrimeWindow D) : - min (pieceSize D g) (n - k * pieceSize D g) * p ≤ D := by - have hpS : p ≤ sOf D := Nat.le_sqrt'.mpr (Nat.mem_sqrtPrimeWindow_iff_sq.mp hp).2.2.le - have hlen : min (pieceSize D g) (n - k * pieceSize D g) ≤ sOf D := - (min_le_left _ _).trans (pieceSize_le_sOf hg) - exact (Nat.mul_le_mul hlen hpS).trans (Nat.sqrt_le D) - -private theorem piece_coordinate {n D g k : ℕ} (hD : 16 ≤ D) (hg : 1 ≤ g) - (c : Fin n) (hc : c ∈ piece n D g k) : - k * pieceSize D g + c.val % pieceSize D g = c.val ∧ - c.val % pieceSize D g < min (pieceSize D g) (n - k * pieceSize D g) := by - have hB := pieceSize_pos hD hg - have hdiv := (mem_piece c).mp hc - have hsplit := Nat.div_add_mod c.val (pieceSize D g) - rw [hdiv, Nat.mul_comm] at hsplit - have hmod := Nat.mod_lt c.val hB - exact ⟨hsplit, lt_min hmod (by have := c.isLt; omega)⟩ - -/-- The actual prepared matrix cells express the existing finite graph's adjacency, and the -unused middle positions are zero. The prime-window and piece hypotheses discharge every -padding and local-coordinate premise of the matrix-entry API. -/ -theorem instance_adjacency {n D g p : ℕ} (values : ℕ → ℤ) - (wAB wBC wAC : Fin n → Fin n → ℤ) - (hD : 16 ≤ D) (hDn : D ≤ n) (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow D) - (hAB : ∀ a b, values (pairIndex (a, b)) = wAB a b) - (hBC : ∀ b c, values (n * n + pairIndex (b, c)) = wBC b c) - (hAC : ∀ a c, values (2 * (n * n) + pairIndex (a, c)) = wAC a c) - {initial y : Data 64} (h : Instances.Output n D g p values initial y) - (idx : InstanceIndex p) (hidx : idx ∈ instanceIndices wAB D g p) : - let N := n * n - let C := fun rho => Nat.count (fun q => (values q % (p : ℤ)).toNat = rho) N - let H := numPieces n D g - let B := pieceSize D g - let ord := ChunkCatalogue.before C (queryCap n D) idx.1.val + idx.2.1 - let base := y.regs Instances.output + - (ord * H + idx.2.2) * (6 + 2 * (n * D)) - let G := lopInstance wAB wBC wAC D g p idx - (∀ (a b c : Fin n) (hc : c ∈ piece n D g idx.2.2) (label : Fin p), - let middle := c.val % B * p + label.val - (y.mem (base + 6 + (a.val * D + middle)) = 1 ↔ - G.adjA a (⟨c, hc⟩, label)) ∧ - (y.mem (base + 6 + n * D + (middle * n + b.val)) = 1 ↔ - G.adjB (⟨c, hc⟩, label) b)) ∧ - (∀ (a b : Fin n) (middle : Fin D), - min B (n - idx.2.2 * B) ≤ middle.val / p → - y.mem (base + 6 + (a.val * D + middle.val)) = 0 ∧ - y.mem (base + 6 + n * D + (middle.val * n + b.val)) = 0) := by - let N := n * n - let R := fun q => (values q % (p : ℤ)).toNat - let C := fun rho => Nat.count (fun q => R q = rho) N - let H := numPieces n D g - let B := pieceSize D g - let K := ChunkCatalogue.before C (queryCap n D) p - let ord := ChunkCatalogue.before C (queryCap n D) idx.1.val + idx.2.1 - let base := 5 + 6 * N + p + 2 * N * p + 3 * K + - (ord * H + idx.2.2) * (6 + 2 * (n * D)) - let A : WriteMatrices.Args := - ⟨5 + 5 * N, base + 6, n, D, p, idx.2.2 * B, min B (n - idx.2.2 * B), idx.1.val⟩ - let A' := { A with src := 5 + 4 * N, dst := base + 6 + n * D } - have hp0 : p ≠ 0 := (Nat.mem_sqrtPrimeWindow_iff_sq.mp hp).1.ne_zero - obtain ⟨-, hL, hR⟩ := instance_readback values wAB hp0 hAB hD hDn hg h idx hidx - change (Block.mk A.dst (n * D)).Realizes y.mem - (fun j => WriteMatrices.entry false A (fun q => R (2 * N + q)) j) at hL - change (Block.mk A'.dst (n * D)).Realizes y.mem - (fun j => WriteMatrices.entry true A' (fun q => R (N + q)) j) at hR - have hpad : A.length * A.p ≤ A.D := prepared_middle_fits hg hp - change min B (n - idx.2.2 * B) * p ≤ D at hpad - have hloadL (a c : Fin n) : - R (2 * N + (a.val * n + c.val)) = (wAC a c).natMod p := by - change (values (2 * (n * n) + pairIndex (a, c)) % (p : ℤ)).toNat = _ - rw [hAC] - rfl - have hloadR (b c : Fin n) : - R (N + (b.val * n + c.val)) = (wBC b c).natMod p := by - change (values (n * n + pairIndex (b, c)) % (p : ℤ)).toNat = _ - rw [hBC] - rfl - have hout : y.regs Instances.output = 5 + 6 * N + p + 2 * N * p + 3 * K := h.2.2.2.1 - dsimp only - rw [hout] - constructor - · intro a b c hc label - obtain ⟨hvertex, hv⟩ := piece_coordinate hD hg c hc - let v : Fin A.length := ⟨c.val % B, hv⟩ - have hm : v.val * p + label.val < D := by - have hstep := Nat.mul_le_mul_right p v.isLt - have hl := label.isLt - change (v.val + 1) * p ≤ min B (n - idx.2.2 * B) * p at hstep - rw [Nat.add_mul, Nat.one_mul] at hstep - omega - have hli : a.val * D + (v.val * p + label.val) < n * D := by - have hi := Nat.mul_le_mul_right D a.isLt - rw [Nat.succ_mul] at hi - omega - have hri : (v.val * p + label.val) * n + b.val < n * D := by - have hi := Nat.mul_le_mul_right n (Nat.succ_le_of_lt hm) - rw [Nat.succ_mul] at hi - have hb := b.isLt - rw [Nat.mul_comm n D] - omega - have hleft := hL ⟨a.val * D + (v.val * p + label.val), hli⟩ - have hright := hR ⟨(v.val * p + label.val) * n + b.val, hri⟩ - change y.mem (A.dst + (a.val * D + (v.val * p + label.val))) = _ at hleft - change y.mem (A'.dst + ((v.val * p + label.val) * n + b.val)) = _ at hright - constructor - · rw [hleft] - exact MatrixSpec.left_lopInstance A (fun q => R (2 * N + q)) wAB wBC wAC - g idx.2.1 idx.2.2 idx.1 rfl a c v label hc hvertex hpad hloadL - · rw [hright] - exact MatrixSpec.right_lopInstance A' (fun q => R (N + q)) wAB wBC wAC - g idx.2.1 idx.2.2 idx.1 b c v label hc hvertex hloadR - · intro a b middle hmiddle - have hli : a.val * D + middle.val < n * D := by - have hi := Nat.mul_le_mul_right D a.isLt - rw [Nat.succ_mul] at hi - have hm := middle.isLt - omega - have hri : middle.val * n + b.val < n * D := by - have hi := Nat.mul_le_mul_right n (Nat.succ_le_of_lt middle.isLt) - rw [Nat.succ_mul] at hi - have hb := b.isLt - rw [Nat.mul_comm n D] - omega - have hleft := hL ⟨a.val * D + middle.val, hli⟩ - have hright := hR ⟨middle.val * n + b.val, hri⟩ - change y.mem (A.dst + (a.val * D + middle.val)) = _ at hleft - change y.mem (A'.dst + (middle.val * n + b.val)) = _ at hright - constructor - · rw [hleft] - exact MatrixSpec.left_padding A (fun q => R (2 * N + q)) a middle hmiddle - · rw [hright] - exact MatrixSpec.right_padding A' (fun q => R (N + q)) b middle hmiddle - -end Cslib.Algorithms.WordRAM.ExactTriangle.PreparationSpec +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationSpec +public import Complexity.Algorithms.ExactTriangle.PieceBounds + +/-! +# Adjacency in prepared native ExactTriangle matrices + +These consumer lemmas connect actual prepared memory to the existing finite lopsided graph. +The source prime window supplies the padding bound, and piece membership supplies each local +coordinate. Neither is an additional layout certificate for the program. + +## Main results + +* `PreparationSpec.instance_adjacency`: actual stored cells give the finite adjacency relations + and zero padding, with piece coordinates and middle-width admissibility derived. + +## References + +The adjacency relations are those of the pinned +[Theorem 17 matrix construction](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Programs/Sec3/Theorem17/Instances/WriteMatrices.lean). +The composition with native memory readback is original. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.PreparationSpec + +open Cslib.WordRAM Cslib.Algorithms.ExactTriangle Cslib.Matrix + +/-- The actual prepared matrix cells express the existing finite graph's adjacency, and the +unused middle positions are zero. The prime-window and piece hypotheses discharge every +padding and local-coordinate premise of the matrix-entry API. -/ +theorem instance_adjacency {n D g p : ℕ} (values : ℕ → ℤ) + (wAB wBC wAC : Fin n → Fin n → ℤ) + (hD : 16 ≤ D) (hDn : D ≤ n) (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow D) + (hAB : ∀ a b, values (pairIndex (a, b)) = wAB a b) + (hBC : ∀ b c, values (n * n + pairIndex (b, c)) = wBC b c) + (hAC : ∀ a c, values (2 * (n * n) + pairIndex (a, c)) = wAC a c) + {initial y : Data 64} (h : Instances.Output n D g p values initial y) + (idx : InstanceIndex p) (hidx : idx ∈ instanceIndices wAB D g p) : + let N := n * n + let C := fun rho => Nat.count (fun q => (values q % (p : ℤ)).toNat = rho) N + let H := numPieces n D g + let B := pieceSize D g + let ord := ChunkCatalogue.before C (queryCap n D) idx.1.val + idx.2.1 + let base := y.regs Instances.output + + (ord * H + idx.2.2) * (6 + 2 * (n * D)) + let G := lopInstance wAB wBC wAC D g p idx + (∀ (a b c : Fin n) (hc : c ∈ piece n D g idx.2.2) (label : Fin p), + let middle := c.val % B * p + label.val + (y.mem (base + 6 + (a.val * D + middle)) = 1 ↔ + G.adjA a (⟨c, hc⟩, label)) ∧ + (y.mem (base + 6 + n * D + (middle * n + b.val)) = 1 ↔ + G.adjB (⟨c, hc⟩, label) b)) ∧ + (∀ (a b : Fin n) (middle : Fin D), + min B (n - idx.2.2 * B) ≤ middle.val / p → + y.mem (base + 6 + (a.val * D + middle.val)) = 0 ∧ + y.mem (base + 6 + n * D + (middle.val * n + b.val)) = 0) := by + let N := n * n + let R := fun q => (values q % (p : ℤ)).toNat + let C := fun rho => Nat.count (fun q => R q = rho) N + let H := numPieces n D g + let B := pieceSize D g + let K := ChunkCatalogue.before C (queryCap n D) p + let ord := ChunkCatalogue.before C (queryCap n D) idx.1.val + idx.2.1 + let base := 5 + 6 * N + p + 2 * N * p + 3 * K + + (ord * H + idx.2.2) * (6 + 2 * (n * D)) + let A : WriteMatrices.Args := + ⟨5 + 5 * N, base + 6, n, D, p, idx.2.2 * B, min B (n - idx.2.2 * B), idx.1.val⟩ + let A' := { A with src := 5 + 4 * N, dst := base + 6 + n * D } + have hp0 : p ≠ 0 := (Nat.mem_sqrtPrimeWindow_iff_sq.mp hp).1.ne_zero + obtain ⟨-, hL, hR⟩ := instance_readback values wAB hp0 hAB hD hDn hg h idx hidx + change (Block.mk A.dst (n * D)).Realizes y.mem + (fun j => WriteMatrices.entry false A (fun q => R (2 * N + q)) j) at hL + change (Block.mk A'.dst (n * D)).Realizes y.mem + (fun j => WriteMatrices.entry true A' (fun q => R (N + q)) j) at hR + have hpad : A.length * A.p ≤ A.D := pieceLength_mul_le_of_mem_sqrtPrimeWindow hg hp + change min B (n - idx.2.2 * B) * p ≤ D at hpad + have hloadL (a c : Fin n) : + R (2 * N + (a.val * n + c.val)) = (wAC a c).natMod p := by + change (values (2 * (n * n) + pairIndex (a, c)) % (p : ℤ)).toNat = _ + rw [hAC] + rfl + have hloadR (b c : Fin n) : + R (N + (b.val * n + c.val)) = (wBC b c).natMod p := by + change (values (n * n + pairIndex (b, c)) % (p : ℤ)).toNat = _ + rw [hBC] + rfl + have hout : y.regs Instances.output = 5 + 6 * N + p + 2 * N * p + 3 * K := h.2.2.2.1 + dsimp only + rw [hout] + constructor + · intro a b c hc label + obtain ⟨hvertex, hv⟩ := piece_coordinate hD hg c hc + let v : Fin A.length := ⟨c.val % B, hv⟩ + have hm : v.val * p + label.val < D := by + have hstep := Nat.mul_le_mul_right p v.isLt + have hl := label.isLt + change (v.val + 1) * p ≤ min B (n - idx.2.2 * B) * p at hstep + rw [Nat.add_mul, Nat.one_mul] at hstep + omega + have hli : a.val * D + (v.val * p + label.val) < n * D := by + have hi := Nat.mul_le_mul_right D a.isLt + rw [Nat.succ_mul] at hi + omega + have hri : (v.val * p + label.val) * n + b.val < n * D := by + have hi := Nat.mul_le_mul_right n (Nat.succ_le_of_lt hm) + rw [Nat.succ_mul] at hi + have hb := b.isLt + rw [Nat.mul_comm n D] + omega + have hleft := hL ⟨a.val * D + (v.val * p + label.val), hli⟩ + have hright := hR ⟨(v.val * p + label.val) * n + b.val, hri⟩ + change y.mem (A.dst + (a.val * D + (v.val * p + label.val))) = _ at hleft + change y.mem (A'.dst + ((v.val * p + label.val) * n + b.val)) = _ at hright + constructor + · rw [hleft] + exact MatrixSpec.left_lopInstance A (fun q => R (2 * N + q)) wAB wBC wAC + g idx.2.1 idx.2.2 idx.1 rfl a c v label hc hvertex hpad hloadL + · rw [hright] + exact MatrixSpec.right_lopInstance A' (fun q => R (N + q)) wAB wBC wAC + g idx.2.1 idx.2.2 idx.1 b c v label hc hvertex hloadR + · intro a b middle hmiddle + have hli : a.val * D + middle.val < n * D := by + have hi := Nat.mul_le_mul_right D a.isLt + rw [Nat.succ_mul] at hi + have hm := middle.isLt + omega + have hri : middle.val * n + b.val < n * D := by + have hi := Nat.mul_le_mul_right n (Nat.succ_le_of_lt middle.isLt) + rw [Nat.succ_mul] at hi + have hb := b.isLt + rw [Nat.mul_comm n D] + omega + have hleft := hL ⟨a.val * D + middle.val, hli⟩ + have hright := hR ⟨middle.val * n + b.val, hri⟩ + change y.mem (A.dst + (a.val * D + middle.val)) = _ at hleft + change y.mem (A'.dst + (middle.val * n + b.val)) = _ at hright + constructor + · rw [hleft] + exact MatrixSpec.left_padding A (fun q => R (2 * N + q)) a middle hmiddle + · rw [hright] + exact MatrixSpec.right_padding A' (fun q => R (N + q)) b middle hmiddle + +end Cslib.Algorithms.WordRAM.ExactTriangle.PreparationSpec diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Detection.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Detection.lean new file mode 100755 index 000000000..425cce365 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Detection.lean @@ -0,0 +1,197 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Producer +public import Complexity.Algorithms.ExactTriangle.PieceBounds +public import Complexity.Algorithms.ExactTriangle.Witnesses +public import Mathlib.Data.Nat.Count +import Complexity.Algorithms.WordRAM.ExactTriangle.MatrixSpec + +/-! +# Actual record products detect the existing modular graph + +The zero/one value matrices used by the real product adapter have a nonzero product entry +exactly at a common neighbor of the existing finite `lopInstance`. Active middle coordinates +and zero padding follow from the original source prime window and piece geometry. Thus a +consumer can derive detection semantics from the actual product, without an answer oracle. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The value matrices written by the producer and consumed by the actual native product +have precisely the common-neighbor semantics of the existing finite modular instance. -/ +theorem product_ne_zero_iff {m n g p : ℕ} (values : ℕ → ℤ) + (wAB wBC wAC : Fin n → Fin n → ℤ) + (hD : 16 ≤ D m) (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) + (hBC : ∀ b c, values (n * n + pairIndex (b, c)) = wBC b c) + (hAC : ∀ a c, values (2 * (n * n) + pairIndex (a, c)) = wAC a c) + (idx : InstanceIndex p) (a b : Fin n) : + (leftMatrix m n g p values idx * rightMatrix m n g p values idx) a b ≠ 0 ↔ + (lopInstance wAB wBC wAC (D m) g p idx).HasCommonNeighbor a b := by + classical + let X := leftMatrix m n g p values idx + let Y := rightMatrix m n g p values idx + let G := lopInstance wAB wBC wAC (D m) g p idx + let A := matrixArgs n (D m) g p idx.1.val idx.2.2 + let Rleft (q : ℕ) := (values (2 * (n * n) + q) % (p : ℤ)).toNat + let Rright (q : ℕ) := (values (n * n + q) % (p : ℤ)).toNat + have hpad : A.length * p ≤ D m := pieceLength_mul_le_of_mem_sqrtPrimeWindow hg hp + have hp0 : 0 < p := (Nat.mem_sqrtPrimeWindow_iff_sq.mp hp).1.pos + have loadLeft (u c : Fin n) : Rleft (u.val * n + c.val) = (wAC u c).natMod p := by + change (values (2 * (n * n) + pairIndex (u, c)) % (p : ℤ)).toNat = _ + rw [hAC] + rfl + have loadRight (u c : Fin n) : Rright (u.val * n + c.val) = (wBC u c).natMod p := by + change (values (n * n + pairIndex (u, c)) % (p : ℤ)).toNat = _ + rw [hBC] + rfl + have edges (c : Fin n) (hc : c ∈ piece n (D m) g idx.2.2) + (v : Fin A.length) (label : Fin p) (j : Fin (D m)) + (hv : A.first + v.val = c.val) (hj : j.val = v.val * p + label.val) : + (X a j = 1 ↔ G.adjA a (⟨c, hc⟩, label)) ∧ + (Y j b = 1 ↔ G.adjB (⟨c, hc⟩, label) b) := by + have hl := MatrixSpec.left_lopInstance A Rleft wAB wBC wAC g idx.2.1 idx.2.2 + idx.1 rfl a c v label hc hv hpad loadLeft + have hr := MatrixSpec.right_lopInstance A Rright wAB wBC wAC g idx.2.1 idx.2.2 + idx.1 b c v label hc hv loadRight + constructor + · change (WriteMatrices.entry false A Rleft (a.val * D m + j.val) : ℤ) = 1 ↔ _ + rw [hj] + exact_mod_cast hl + · change (WriteMatrices.entry true A Rright (j.val * n + b.val) : ℤ) = 1 ↔ _ + rw [hj] + exact_mod_cast hr + obtain ⟨hX, hY⟩ := matrices_zero_one m n g p values idx + have hx : ∀ a j, X a j = 0 ∨ X a j = 1 := by + intro a j + have := hX a j + change 0 ≤ X a j ∧ X a j ≤ 1 at this + omega + have hy : ∀ j b, Y j b = 0 ∨ Y j b = 1 := by + intro j b + have := hY j b + change 0 ≤ Y j b ∧ Y j b ≤ 1 at this + omega + rw [LopsidedGraph.mul_apply_eq_commonNeighborCount_ofMatrices X Y ∅ hx hy] + have hnz := (LopsidedGraph.ofMatrices X Y ∅).commonNeighborCount_ne_zero_iff a b + rw [show ((LopsidedGraph.ofMatrices X Y ∅).commonNeighborCount a b : ℤ) ≠ 0 ↔ + (LopsidedGraph.ofMatrices X Y ∅).commonNeighborCount a b ≠ 0 by exact_mod_cast Iff.rfl] + rw [hnz] + change (∃ j : Fin (D m), X a j = 1 ∧ Y j b = 1) ↔ G.HasCommonNeighbor a b + constructor + · rintro ⟨j, hleft, hright⟩ + have hj : j.val / p < A.length := by + by_contra h + have hz := MatrixSpec.left_padding A Rleft a j (by change A.length ≤ j.val / p; omega) + change WriteMatrices.entry false A Rleft (a.val * D m + j.val) = 0 at hz + have hl : (WriteMatrices.entry false A Rleft (a.val * D m + j.val) : ℤ) = 1 := hleft + rw [hz] at hl + norm_num at hl + let v : Fin A.length := ⟨j.val / p, hj⟩ + let label : Fin p := ⟨j.val % p, Nat.mod_lt _ hp0⟩ + have hn : A.first + v.val < n := by + have hl := min_le_right (pieceSize (D m) g) (n - A.first) + have hv := v.isLt + change v.val < min (pieceSize (D m) g) (n - A.first) at hv + omega + let c : Fin n := ⟨A.first + v.val, hn⟩ + have hc : c ∈ piece n (D m) g idx.2.2 := by + apply (mem_piece_iff_interval hD hg c).mpr + change idx.2.2 * pieceSize (D m) g ≤ A.first + v.val ∧ + A.first + v.val < idx.2.2 * pieceSize (D m) g + A.length + change A.first ≤ A.first + v.val ∧ A.first + v.val < A.first + A.length + exact ⟨by omega, Nat.add_lt_add_left v.isLt _⟩ + have he : j.val = v.val * p + label.val := by + simpa only [v, label, Nat.mul_comm] using (Nat.div_add_mod j.val p).symm + obtain ⟨heL, heR⟩ := edges c hc v label j rfl he + exact ⟨(⟨c, hc⟩, label), heL.mp hleft, heR.mp hright⟩ + · rintro ⟨⟨⟨c, hc⟩, label⟩, hleft, hright⟩ + obtain ⟨hv, hlen⟩ := piece_coordinate hD hg c hc + let v : Fin A.length := ⟨c.val % pieceSize (D m) g, hlen⟩ + have hj : v.val * p + label.val < D m := by + have hm := Nat.mul_le_mul_right p v.isLt + rw [Nat.succ_mul] at hm + have hl := label.isLt + omega + let j : Fin (D m) := ⟨v.val * p + label.val, hj⟩ + obtain ⟨heL, heR⟩ := edges c hc v label j hv rfl + exact ⟨j, heL.mpr hleft, heR.mpr hright⟩ + +/-- The mathematical answers computed by the actual product discharge the finite recovery +contract. Correct answers are a conclusion here, not a solver assumption. -/ +theorem product_answers_correct {m n g p : ℕ} (values : ℕ → ℤ) + (wAB wBC wAC : Fin n → Fin n → ℤ) + (hD : 16 ≤ D m) (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) + (hBC : ∀ b c, values (n * n + pairIndex (b, c)) = wBC b c) + (hAC : ∀ a c, values (2 * (n * n) + pairIndex (a, c)) = wAC a c) : + AnswersCorrect wAB wBC wAC (D m) g p (fun idx q => decide + ((leftMatrix m n g p values idx * rightMatrix m n g p values idx) q.1 q.2 ≠ 0)) := by + intro idx _ q _ + simp only [decide_eq_true_eq] + exact propext (product_ne_zero_iff values wAB wBC wAC hD hg hp hBC hAC idx q.1 q.2) + +/-- Distinct occurrences of one actual query chunk that are accepted by the product but +have no original witness inject into the existing finite failed scans. Their number is +therefore bounded by the existing original-weight false-positive count. -/ +theorem failed_occurrences_le {m n g p : ℕ} (values : ℕ → ℤ) + (wAB wBC wAC : Fin n → Fin n → ℤ) + (hD : 16 ≤ D m) (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) + (hBC : ∀ b c, values (n * n + pairIndex (b, c)) = wBC b c) + (hAC : ∀ a c, values (2 * (n * n) + pairIndex (a, c)) = wAC a c) + (idx : InstanceIndex p) (hidx : idx ∈ instanceIndices wAB (D m) g p) + (qs : List (Fin n × Fin n)) (default : Fin n × Fin n) (hqs : qs.Nodup) + (hquery : ∀ q ∈ qs, q ∈ chunkOf wAB (D m) p idx.1 idx.2.1) : + Nat.count (fun i => + (leftMatrix m n g p values idx * rightMatrix m n g p values idx) + (qs.getD i default).1 (qs.getD i default).2 ≠ 0 ∧ + scanPiece wAB wBC wAC (D m) g idx.2.2 + (qs.getD i default).1 (qs.getD i default).2 = none) qs.length ≤ + falsePositiveCount wAB wBC wAC p := by + classical + let ans : InstanceIndex p → Fin n × Fin n → Bool := fun idx q => decide + ((leftMatrix m n g p values idx * rightMatrix m n g p values idx) q.1 q.2 ≠ 0) + let P (i : ℕ) : Prop := + (leftMatrix m n g p values idx * rightMatrix m n g p values idx) + (qs.getD i default).1 (qs.getD i default).2 ≠ 0 ∧ + scanPiece wAB wBC wAC (D m) g idx.2.2 + (qs.getD i default).1 (qs.getD i default).2 = none + let := Nat.CountSet.fintype P qs.length + let failures := failedScans wAB wBC wAC (D m) g p ans + have hget (i : ℕ) (hi : i < qs.length) : qs.getD i default = qs.get ⟨i, hi⟩ := by + rw [List.getD_eq_getElem _ _ hi, List.get_eq_getElem] + let f : {i : ℕ // i < qs.length ∧ P i} → {s // s ∈ failures} := fun i => + ⟨(idx, qs.get ⟨i.val, i.prop.1⟩), by + apply Finset.mem_filter.mpr + refine ⟨mem_acceptedPairs.mpr ⟨hidx, ?_, ?_⟩, ?_⟩ + · exact hquery _ (List.get_mem _ _) + · have he := i.prop.2.1 + simpa only [ans, decide_eq_true_eq, hget i.val i.prop.1] using he + · have he := i.prop.2.2 + simpa only [scanOf, hget i.val i.prop.1] using he⟩ + have hf : Function.Injective f := by + intro i j hij + have hpair : qs.get ⟨i.val, i.prop.1⟩ = qs.get ⟨j.val, j.prop.1⟩ := + congrArg (fun s => s.val.2) hij + have hi := List.nodup_iff_injective_get.mp hqs hpair + exact Subtype.ext (congrArg Fin.val hi) + change Nat.count P qs.length ≤ _ + rw [Nat.count_eq_card_fintype] + calc + _ ≤ Fintype.card {s // s ∈ failures} := Fintype.card_le_of_injective f hf + _ = failures.card := Fintype.card_coe _ + _ ≤ falsePositiveCount wAB wBC wAC p := card_failedScans_le + (Nat.mem_sqrtPrimeWindow_iff_sq.mp hp).1.ne_zero + (product_answers_correct values wAB wBC wAC hD hg hp hBC hAC) + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Producer.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Producer.lean index 0876048cd..253538c18 100644 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Producer.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Producer.lean @@ -46,11 +46,6 @@ open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage (WriteMatrices.entry true (matrixArgs n (D m) g p idx.1.val idx.2.2) (fun q => (values (n * n + q) % (p : ℤ)).toNat) (k.val * n + a.val) : ℤ) -/-- Both native writer value functions are raw zero/one words at every index. -/ -theorem entry_le_one (side : Bool) (A : WriteMatrices.Args) (R : ℕ → ℕ) (a : ℕ) : - WriteMatrices.entry side A R a ≤ 1 := by - cases side <;> dsimp only [WriteMatrices.entry] <;> split_ifs <;> omega - /-- The readback matrices satisfy the raw callee's original-entry hypotheses. -/ theorem matrices_zero_one (m n g p : ℕ) (values : ℕ → ℤ) (idx : InstanceIndex p) : (∀ a k, 0 ≤ leftMatrix m n g p values idx a k ∧ @@ -60,10 +55,10 @@ theorem matrices_zero_one (m n g p : ℕ) (values : ℕ → ℤ) (idx : Instance constructor <;> intro a k · exact ⟨Int.natCast_nonneg _, by dsimp only [leftMatrix] - exact_mod_cast entry_le_one false _ _ _⟩ + exact_mod_cast WriteMatrices.entry_le_one false _ _ _⟩ · exact ⟨Int.natCast_nonneg _, by dsimp only [rightMatrix] - exact_mod_cast entry_le_one true _ _ _⟩ + exact_mod_cast WriteMatrices.entry_le_one true _ _ _⟩ /-- The actual computed ordinal of an existing finite class/chunk/piece. This is a value specification for the caller's selection register, not a table supplied to the adapter. -/ diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/QueryValues.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/QueryValues.lean new file mode 100755 index 000000000..9d668636f --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/QueryValues.lean @@ -0,0 +1,98 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Producer +import Mathlib.Data.List.Nodup + +/-! +# The value of an actual decoded producer query block + +The actual occurrence list is exactly the existing finite `chunkOf`, without repetitions, +and its `i`-th pair has class rank `k*queryCap+i`. Consumers can use original ET pairs directly +through this packaged readback; no alternative query notion or supplied routing table is added. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle + +/-- The actual decoded list consists of all pairs of the existing chunk, once each, in +increasing class rank. The indexed equation specifies the order as well as set membership. -/ +theorem decodedQueries_chunkOf {n D g p : ℕ} (values : ℕ → ℤ) + (wAB : Fin n → Fin n → ℤ) (hn : 0 < n) (hp : p ≠ 0) + (hAB : ∀ a b, values (pairIndex (a, b)) = wAB a b) + (hD : 1 ≤ D) (hDn : D ≤ n) + {initial y : Data 64} (h : Instances.Output n D g p values initial y) + (rho : Fin p) (k : ℕ) + (hk : k < numChunks (residueClass wAB p rho) (queryCap n D)) : + let q := 5 + 6 * (n * n) + p + 2 * rho.val * (n * n) + 2 * (k * queryCap n D) + let Q := min (queryCap n D) ((residueClass wAB p rho).card - k * queryCap n D) + let qs := decodedQueries hn y.mem q Q + (∀ i : Fin qs.length, qs.get i ∈ chunkOf wAB D p rho k ∧ + rankIn (residueClass wAB p rho) (qs.get i) = k * queryCap n D + i.val) ∧ + qs.Nodup ∧ qs.toFinset = chunkOf wAB D p rho k := by + let S := residueClass wAB p rho + let cap := queryCap n D + let q := 5 + 6 * (n * n) + p + 2 * rho.val * (n * n) + 2 * (k * cap) + let Q := min cap (S.card - k * cap) + let qs := decodedQueries hn y.mem q Q + obtain ⟨-, hpair⟩ := output_query_coordinates values wAB hp hAB hD hDn h rho k hk + have hget (i : Fin qs.length) : qs.get i ∈ chunkOf wAB D p rho k ∧ + rankIn S (qs.get i) = k * cap + i.val := by + have hi : i.val < Q := by simpa only [qs, decodedQueries_length] using i.isLt + obtain ⟨pair, hvalue, -⟩ := hpair i.val hi + have he : qs.get i = pair := by + apply Prod.ext <;> apply Fin.ext + · rw [List.get_eq_getElem] + simp only [qs, decodedQueries, List.getElem_ofFn] + have hv : y.mem (q + 2 * i.val) = pair.1.val := hvalue.2.2.1 + rw [hv, Nat.mod_eq_of_lt pair.1.isLt] + · rw [List.get_eq_getElem] + simp only [qs, decodedQueries, List.getElem_ofFn] + have hv : y.mem (q + 2 * i.val + 1) = pair.2.val := hvalue.2.2.2 + rw [hv, Nat.mod_eq_of_lt pair.2.isLt] + rw [he] + exact ⟨hvalue.1, hvalue.2.1⟩ + refine ⟨hget, ?_, ?_⟩ + · apply List.nodup_iff_injective_get.mpr + intro i j hij + have hi := (hget i).2 + have hj := (hget j).2 + exact Fin.ext (Nat.add_left_cancel + (hi.symm.trans ((congrArg (rankIn S) hij).trans hj))) + · apply Finset.ext + intro pair + rw [List.mem_toFinset] + constructor + · intro hm + obtain ⟨i, rfl⟩ := List.mem_iff_get.mp hm + exact (hget i).1 + · intro hm + have hchunk : pair ∈ S ∧ rankIn S pair / cap = k := by + simpa only [chunkOf, chunk, Finset.mem_filter, S, cap] using hm + have hrank := rankIn_lt_card hchunk.1 + have hcap : 0 < cap := one_le_queryCap hD hDn + have hrem := Nat.mod_lt (rankIn S pair) hcap + have hsplit := Nat.div_add_mod (rankIn S pair) cap + rw [hchunk.2] at hsplit + rw [Nat.mul_comm cap k] at hsplit + have hi : rankIn S pair % cap < Q := by dsimp only [Q]; omega + let i : Fin qs.length := ⟨rankIn S pair % cap, by + simpa only [qs, decodedQueries_length] using hi⟩ + have hv := hget i + have hmem : qs.get i ∈ S := by + exact (Finset.mem_filter.mp hv.1).1 + have he : qs.get i = pair := rankIn_injOn S hmem hchunk.1 (by + rw [hv.2] + simpa only [i] using hsplit) + exact List.mem_iff_get.mpr ⟨i, he⟩ + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordProduct diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Consume.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Consume.lean new file mode 100755 index 000000000..f215525bf --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Consume.lean @@ -0,0 +1,100 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Header +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Queries + +/-! +# Complete per-record candidate consumption + +The real header reads the record and computes its piece interval. The real query loop then +loads product answers and original pairs, verifies the original signed weights and returns a +genuine original-coordinate witness when one exists among the selected pairs. All memory and +the complete earlier producer/product register bank are preserved. + +The numerical component premises are discharged from original caller data by the initialized +composition. In particular the record and answer words here are read from actual memory, not +supplied witness or execution certificates. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM Cslib.Algorithms.ExactTriangle + +-- This checks the finite write sets of the fixed consumer, including the renamed scan bank. +set_option maxRecDepth 4096 in +private theorem consume_preserves : ∀ j : Fin 400, j.val < 320 → consume.Preserves j := by + decide + +/-- Execute the actual descriptor decoder and short-circuit verifier loop. The additional +scan term uses the native counter, bounded by failed accepted pairs plus one success. -/ +theorem exec_consume {w U n D g i records heap out : ℕ} + (AB BC AC : ℕ → ℕ → ℤ) (a b : ℕ → ℕ) (x : Data 400) (v : Fin 6 → ℕ) + (hD : x.mem 1 = D) (hg : x.mem 2 = g) (hn : x.mem 3 = n) + (hi : x.regs (productBank RecordProduct.Reg.ordinal) = i) + (hrecords : x.regs (productBank (RecordProduct.producerBank Instances.output)) = records) + (hout : x.regs (productBank RecordProduct.Reg.result) = out) + (hrec : records + i * (6 + 2 * (n * D)) + 6 ≤ heap) + (hv : ∀ j : Fin 6, x.mem (records + i * (6 + 2 * (n * D)) + j.val) = v j) + (hfirst : v 1 * pieceSize D g ≤ n) + (hquery : ∀ j < v 3, x.mem (v 2 + 2 * j) = a j ∧ + x.mem (v 2 + 2 * j + 1) = b j ∧ a j < n ∧ b j < n) + (hmem : ∀ u < n, ∀ z < n, + x.mem (5 + (u * n + z)) = Signed.encode (AB u z) ∧ + x.mem (5 + n * n + (u * n + z)) = Signed.encode (BC u z) ∧ + x.mem (5 + n * n + n * n + (u * n + z)) = Signed.encode (AC u z)) + (hmag : ∀ u < n, ∀ z < n, + (AB u z).natAbs ≤ U ∧ (BC u z).natAbs ≤ U ∧ (AC u z).natAbs ≤ U) + (hU : GenericRAM.Signed.Fits w (2 * U : ℕ)) + (hfit : Fits (w + 2) (heap + out + v 2 + 4 * v 3 + 4 * D + + 2 * (n * D) + 9 * (n * n) + 4 * n + g + 2048)) : + let first := v 1 * pieceSize D g + let length := min (pieceSize D g) (n - first) + ∃ y S, ExecWithin (w + 2) consume x y + (5 * Nat.sqrt D + 8 * v 3 + (42 * length + 28) * S + 52) ∧ + y.mem = x.mem ∧ y.regs Reg.scans = S ∧ + S ≤ Nat.count (fun j => x.mem (out + j) ≠ 0 ∧ + ∀ c : Fin length, AB (a j) (b j) + BC (b j) (first + c.val) + + AC (a j) (first + c.val) ≠ 0) (v 3) + 1 ∧ + (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ + (y.regs Reg.found = 1 ↔ ∃ j < v 3, x.mem (out + j) ≠ 0 ∧ + ∃ c, first ≤ c ∧ c < first + length ∧ AB (a j) (b j) + BC (b j) c + AC (a j) c = 0) ∧ + (y.regs Reg.found = 1 → ∃ j < v 3, + x.mem (out + j) ≠ 0 ∧ y.regs Reg.witnessA = a j ∧ y.regs Reg.witnessB = b j ∧ + first ≤ y.regs Reg.witnessC ∧ y.regs Reg.witnessC < first + length ∧ + AB (a j) (b j) + BC (b j) (y.regs Reg.witnessC) + + AC (a j) (y.regs Reg.witnessC) = 0) ∧ + ∀ j : Fin 400, j.val < 320 → y.regs j = x.regs j := by + let first := v 1 * pieceSize D g + let length := min (pieceSize D g) (n - first) + have hlength : first + length ≤ n := by + dsimp only [length, first] + have := min_le_right (pieceSize D g) (n - v 1 * pieceSize D g) + omega + obtain ⟨s, hs, sFixed, sm, sf, -, -, -, ss, si, -⟩ := exec_header x v hD hg hn hi + hrecords hout hrec hv hfirst (hfit.of_le (by omega)) + obtain ⟨y, S, he, -, ym, -, ys, hS, yf, iffFound, yw⟩ := exec_queries + AB BC AC a b s sFixed si ss sf (by simpa only [sm] using hquery) + hlength (by simpa only [sm] using hmem) hmag hU + (hfit.of_le (by omega)) + change ExecWithin (w + 2) (.loop (.lt Reg.index Reg.count) step) s y + (8 * v 3 + (42 * length + 28) * S + 2) at he + have hexe : ExecWithin (w + 2) consume x y + (5 * Nat.sqrt D + 8 * v 3 + (42 * length + 28) * S + 52) := + (hs.seq he).mono (by omega) + refine ⟨y, S, hexe, ym.trans sm, ys, ?_, yf, ?_, ?_, ?_⟩ + · simpa only [sm] using hS + · simpa only [sm] using iffFound + · simpa only [sm] using yw + · intro j hj + exact hexe.regs_eq (consume_preserves j hj) + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Header.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Header.lean new file mode 100755 index 000000000..3201d2b1b --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Header.lean @@ -0,0 +1,250 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Spec +import Complexity.Computability.WordRAM.RegisterFrame + +/-! +# Actual record and interval-header execution + +These internal rules read the producer's real scalar/record words and run the existing native +square-root loop. Record and piece metadata are never supplied as predecoded algorithm inputs. +The original producer/product bank and all memory are preserved. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM +open Cslib.Algorithms.ExactTriangle + +-- Closed preservation checks enumerate the four-hundred-register caller bank. +set_option maxRecDepth 4096 in +private theorem readRecord_preserves : ∀ i : Fin 400, i.val < 320 → readRecord.Preserves i := by + decide + +private theorem sqrtBank_injective : Function.Injective sqrtBank := by + intro i j h + apply Fin.ext + simpa only [sqrtBank, Nat.add_left_cancel_iff] using congrArg Fin.val h + +private theorem squareRoot_preserves (i : Fin 400) (hi : i.val < 336 ∨ 342 ≤ i.val) : + squareRoot.Preserves i := by + apply Code.preserves_rename_of_forall_ne + intro j hj + have hv := congrArg Fin.val hj + change 336 + j.val = i.val at hv + have hj := j.isLt + omega + +/-- Read the original dimension/divisor and this actual six-word record, then reset the +per-record search state. The record address is computed from the native selected ordinal. -/ +theorem exec_readRecord {W n D g i records heap out : ℕ} (x : Data 400) (v : Fin 6 → ℕ) + (hD : x.mem 1 = D) (hg : x.mem 2 = g) (hn : x.mem 3 = n) + (hi : x.regs (productBank RecordProduct.Reg.ordinal) = i) + (hrecords : x.regs (productBank (RecordProduct.producerBank Instances.output)) = records) + (hout : x.regs (productBank RecordProduct.Reg.result) = out) + (hrec : records + i * (6 + 2 * (n * D)) + 6 ≤ heap) + (hv : ∀ j : Fin 6, x.mem (records + i * (6 + 2 * (n * D)) + j.val) = v j) + (hfit : Fits W (heap + 2 * (n * D) + D + n + g + 128)) : + ∃ y, ExecWithin W readRecord x y 28 ∧ y.mem = x.mem ∧ + y.regs (sqrtBank NatSqrt.arg) = D ∧ y.regs Reg.size = n ∧ + y.regs Reg.divisor = g ∧ y.regs Reg.output = out ∧ + y.regs Reg.piece = v 1 ∧ y.regs Reg.queries = v 2 ∧ y.regs Reg.count = v 3 ∧ + y.regs Reg.found = 0 ∧ y.regs Reg.witnessA = 0 ∧ y.regs Reg.witnessB = 0 ∧ + y.regs Reg.witnessC = 0 ∧ y.regs Reg.scans = 0 ∧ y.regs Reg.index = 0 ∧ + y.regs Reg.one = 1 ∧ y.regs Reg.zero = 0 ∧ + ∀ j : Fin 400, j.val < 320 → y.regs j = x.regs j := by + let stride := n * D + n * D + 6 + let r := records + i * stride + have heq : stride = 6 + 2 * (n * D) := by dsimp only [stride]; omega + have hrange : r + 6 ≤ heap := by simpa only [r, heq] using hrec + have h1 : Fits W 1 := hfit.of_le (by omega) + have h2 : Fits W 2 := hfit.of_le (by omega) + have h3 : Fits W 3 := hfit.of_le (by omega) + have h6 : Fits W 6 := hfit.of_le (by omega) + have harea : Fits W (n * D) := hfit.of_le (by omega) + have htwice : Fits W (n * D + n * D) := hfit.of_le (by omega) + have hstride : Fits W stride := hfit.of_le (by dsimp only [stride]; omega) + have hmul : Fits W (i * stride) := hfit.of_le (by dsimp only [r] at hrange; omega) + have hr : Fits W r := hfit.of_le (by omega) + have hr1 : Fits W (r + 1) := hfit.of_le (by omega) + have hr2 : Fits W (r + 1 + 1) := hfit.of_le (by omega) + have hr3 : Fits W (r + 1 + 1 + 1) := hfit.of_le (by omega) + have hv1 : x.mem (r + 1) = v 1 := by simpa +decide [r, heq] using hv ⟨1, by decide⟩ + have hv2 : x.mem (r + 1 + 1) = v 2 := by + change x.mem (r + 2) = v 2 + simpa +decide [r, heq] using hv ⟨2, by decide⟩ + have hv3 : x.mem (r + 1 + 1 + 1) = v 3 := by + change x.mem (r + 3) = v 3 + simpa +decide [r, heq] using hv ⟨3, by decide⟩ + dsimp only [productBank, RecordProduct.producerBank, RecordProduct.Reg.ordinal, + RecordProduct.Reg.result, Instances.output, Fin.castLE] at hi hrecords hout + dsimp only [r, stride] at hstride hmul hr hr1 hr2 hr3 hv1 hv2 hv3 + let y := x.set Reg.one 1 |>.set Reg.zero 0 |>.set Reg.address 3 |>.set Reg.size n + |>.set Reg.address 1 |>.set Reg.temp D |>.set (sqrtBank NatSqrt.arg) D + |>.set Reg.address 2 |>.set Reg.divisor g |>.set Reg.output out + |>.set Reg.temp (n * D) |>.set Reg.temp (n * D + n * D) |>.set Reg.piece 6 + |>.set Reg.temp stride |>.set Reg.address (i * stride) |>.set Reg.address r + |>.set Reg.address (r + 1) |>.set Reg.piece (v 1) + |>.set Reg.address (r + 1 + 1) |>.set Reg.queries (v 2) + |>.set Reg.address (r + 1 + 1 + 1) |>.set Reg.count (v 3) + |>.set Reg.found 0 |>.set Reg.witnessA 0 |>.set Reg.witnessB 0 |>.set Reg.witnessC 0 + |>.set Reg.scans 0 |>.set Reg.index 0 + have he : ExecWithin W readRecord x y 28 := by + ram_eval [readRecord, y, r, stride, hD, hg, hn, hi, hrecords, hout, hv1, hv2, hv3, + h1, h2, h3, h6, harea, htwice, hstride, hmul, hr, hr1, hr2, hr3, + Reg.one, Reg.zero, Reg.address, Reg.size, Reg.temp, Reg.divisor, Reg.output, + Reg.piece, Reg.queries, Reg.count, Reg.found, Reg.witnessA, Reg.witnessB, + Reg.witnessC, Reg.scans, Reg.index, productBank, sqrtBank, RecordProduct.producerBank, + RecordProduct.Reg.ordinal, RecordProduct.Reg.result, Instances.output, NatSqrt.arg, + Fin.castLE] + refine ⟨y, he, rfl, ?_⟩ + refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + all_goals first + | exact fun j hj => he.regs_eq (readRecord_preserves j hj) + | simp +decide [y, Reg.one, Reg.zero, Reg.address, Reg.size, Reg.temp, Reg.divisor, + Reg.output, Reg.piece, Reg.queries, Reg.count, Reg.found, Reg.witnessA, + Reg.witnessB, Reg.witnessC, Reg.scans, Reg.index, sqrtBank, NatSqrt.arg] + +/-- The local interval decoder executes the actual square-root loop, preserving the complete +old bank and controller registers. Its result is derived, not a square-root certificate. -/ +theorem exec_squareRoot {W D : ℕ} (x : Data 400) + (hD : x.regs (sqrtBank NatSqrt.arg) = D) (hfit : Fits W (4 * D + 4)) : + ∃ y, ExecWithin W squareRoot x y (5 * Nat.sqrt D + 7) ∧ + y.regs (sqrtBank NatSqrt.root) = Nat.sqrt D ∧ y.mem = x.mem ∧ + ∀ i : Fin 400, i.val < 336 ∨ 342 ≤ i.val → y.regs i = x.regs i := by + obtain ⟨y, hy, hr, -, hm, -⟩ := NatSqrt.exec_rename sqrtBank sqrtBank_injective x (by + rw [hD]; exact hfit) + refine ⟨y, ?_, ?_, hm, fun i hi => hy.regs_eq (squareRoot_preserves i hi)⟩ + · simpa only [squareRoot, hD] using hy + · simpa only [hD] using hr + +-- Closed preservation over the finite controller and old product bank. +set_option maxRecDepth 4096 in +private theorem finishHeader_preserves : ∀ i : Fin 400, + i.val < 324 ∨ i = Reg.index ∨ i = Reg.one ∨ i = Reg.piece ∨ + i = Reg.divisor ∨ 392 ≤ i.val → finishHeader.Preserves i := by decide + +/-- Compute the actual interval and original signed-array pointers after the real root loop. +All query/output metadata and success/counter initialization are preserved. -/ +theorem exec_finishHeader {W n S g k : ℕ} (x : Data 400) + (hroot : x.regs (sqrtBank NatSqrt.root) = S) (hn : x.regs Reg.size = n) + (hg : x.regs Reg.divisor = g) (hk : x.regs Reg.piece = k) + (h1 : x.regs Reg.one = 1) (hc : k * (S ⌈/⌉ g) ≤ n) + (hfit : Fits W (S + g + 3 * (n * n) + n + 64)) : + let B := S ⌈/⌉ g + let first := k * B + let length := min B (n - first) + ∃ y, ExecWithin W finishHeader x y 15 ∧ y.mem = x.mem ∧ + y.regs Reg.blockLength = B ∧ y.regs Reg.first = first ∧ y.regs Reg.length = length ∧ + y.regs (scanBank WitnessScan.Reg.ab) = 5 ∧ + y.regs (scanBank WitnessScan.Reg.bc) = 5 + n * n ∧ + y.regs (scanBank WitnessScan.Reg.ac) = 5 + n * n + n * n ∧ + y.regs (scanBank WitnessScan.Reg.size) = n ∧ + y.regs (scanBank WitnessScan.Reg.first) = first ∧ + y.regs (scanBank WitnessScan.Reg.length) = length ∧ + ∀ i : Fin 400, i.val < 324 ∨ i = Reg.index ∨ i = Reg.one ∨ i = Reg.piece ∨ + i = Reg.divisor ∨ 392 ≤ i.val → y.regs i = x.regs i := by + let B := S ⌈/⌉ g + let first := k * B + let length := min B (n - first) + have hquot : (S + g - 1) / g = B := by simp only [B, Nat.ceilDiv_eq_add_pred_div] + have hsum : Fits W (S + g) := hfit.of_le (by omega) + have hsub : Fits W (S + g - 1) := hsum.of_le (Nat.sub_le ..) + have hB : Fits W B := by rw [← hquot]; exact hsub.of_le (Nat.div_le_self ..) + have hfirst : Fits W first := hfit.of_le (by change k * B ≤ n at hc; dsimp only [first]; omega) + have hlen : Fits W (n - first) := hfit.of_le (by omega) + have harea : Fits W (n * n) := hfit.of_le (by omega) + have h5 : Fits W 5 := hfit.of_le (by omega) + have hbc : Fits W (5 + n * n) := hfit.of_le (by omega) + have hac : Fits W (5 + n * n + n * n) := hfit.of_le (by omega) + dsimp only [sqrtBank, NatSqrt.root] at hroot + let y := x.set Reg.blockLength (S + g) |>.set Reg.blockLength (S + g - 1) + |>.set Reg.blockLength B |>.set Reg.first first |>.set Reg.length (n - first) + |>.set Reg.length length |>.set (scanBank WitnessScan.Reg.ab) 5 + |>.set Reg.temp (n * n) |>.set (scanBank WitnessScan.Reg.bc) (5 + n * n) + |>.set (scanBank WitnessScan.Reg.ac) (5 + n * n + n * n) + |>.set (scanBank WitnessScan.Reg.size) n |>.set (scanBank WitnessScan.Reg.first) first + |>.set (scanBank WitnessScan.Reg.length) length + have he : ExecWithin W finishHeader x y 15 := by + by_cases hlt : B < n - first + · have hl : length = B := min_eq_left hlt.le + ram_eval [finishHeader, y, hroot, hn, hg, hk, h1, hquot, hlt, hl, + hsum, hsub, hB, hfirst, hlen, harea, h5, hbc, hac, B, first, length, + Reg.blockLength, Reg.divisor, Reg.one, Reg.piece, Reg.first, Reg.length, Reg.size, + Reg.temp, scanBank, sqrtBank, WitnessScan.Reg.ab, WitnessScan.Reg.bc, WitnessScan.Reg.ac, + WitnessScan.Reg.size, WitnessScan.Reg.first, WitnessScan.Reg.length, NatSqrt.root] + · have hl : length = n - first := min_eq_right (by omega) + ram_eval [finishHeader, y, hroot, hn, hg, hk, h1, hquot, hlt, hl, + hsum, hsub, hB, hfirst, hlen, harea, h5, hbc, hac, B, first, length, + Reg.blockLength, Reg.divisor, Reg.one, Reg.piece, Reg.first, Reg.length, Reg.size, + Reg.temp, scanBank, sqrtBank, WitnessScan.Reg.ab, WitnessScan.Reg.bc, WitnessScan.Reg.ac, + WitnessScan.Reg.size, WitnessScan.Reg.first, WitnessScan.Reg.length, NatSqrt.root] + refine ⟨y, he, rfl, ?_⟩ + refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + all_goals first + | exact fun i hi => he.regs_eq (finishHeader_preserves i hi) + | rfl + +/-- Complete charged local setup from real scalar and descriptor reads. The computed original +piece interval and all verifier arguments are conclusions of the actual code. -/ +theorem exec_header {W n D g i records heap out : ℕ} (x : Data 400) (v : Fin 6 → ℕ) + (hD : x.mem 1 = D) (hg : x.mem 2 = g) (hn : x.mem 3 = n) + (hi : x.regs (productBank RecordProduct.Reg.ordinal) = i) + (hrecords : x.regs (productBank (RecordProduct.producerBank Instances.output)) = records) + (hout : x.regs (productBank RecordProduct.Reg.result) = out) + (hrec : records + i * (6 + 2 * (n * D)) + 6 ≤ heap) + (hv : ∀ j : Fin 6, x.mem (records + i * (6 + 2 * (n * D)) + j.val) = v j) + (hfirst : v 1 * pieceSize D g ≤ n) + (hfit : Fits W (heap + 4 * D + 2 * (n * D) + 3 * (n * n) + n + g + 512)) : + let first := v 1 * pieceSize D g + let length := min (pieceSize D g) (n - first) + ∃ y, ExecWithin W header x y (5 * Nat.sqrt D + 50) ∧ + Fixed n first length out (v 2) (v 3) y ∧ y.mem = x.mem ∧ + y.regs Reg.found = 0 ∧ y.regs Reg.witnessA = 0 ∧ y.regs Reg.witnessB = 0 ∧ + y.regs Reg.witnessC = 0 ∧ y.regs Reg.scans = 0 ∧ y.regs Reg.index = 0 ∧ + ∀ j : Fin 400, j.val < 320 → y.regs j = x.regs j := by + obtain ⟨a, ha, am, aD, an, ag, ao, ak, aq, aQ, af, aa, ab, ac, as, ai, a1, a0, ar⟩ := + exec_readRecord x v hD hg hn hi hrecords hout hrec hv (hfit.of_le (by omega)) + obtain ⟨b, hb, bS, bm, br⟩ := exec_squareRoot a aD (hfit.of_le (by omega)) + have bn : b.regs Reg.size = n := (br _ (by decide)).trans an + have bg : b.regs Reg.divisor = g := (br _ (by decide)).trans ag + have bk : b.regs Reg.piece = v 1 := (br _ (by decide)).trans ak + have b1 : b.regs Reg.one = 1 := (br _ (by decide)).trans a1 + have hsqrt := Nat.sqrt_le_self D + obtain ⟨y, hy, ym, yB, yf, yl, yab, ybc, yac, yn, yfirst, ylength, yr⟩ := + exec_finishHeader b bS bn bg bk b1 hfirst (hfit.of_le (by omega)) + refine ⟨y, (ha.seq (hb.seq hy)).mono (by omega), ?_, ym.trans (bm.trans am), + (yr _ (by decide)).trans ((br _ (by decide)).trans af), + (yr _ (by decide)).trans ((br _ (by decide)).trans aa), + (yr _ (by decide)).trans ((br _ (by decide)).trans ab), + (yr _ (by decide)).trans ((br _ (by decide)).trans ac), + (yr _ (by decide)).trans ((br _ (by decide)).trans as), + (yr _ (by decide)).trans ((br _ (by decide)).trans ai), ?_⟩ + · exact + { output := (yr _ (by decide)).trans ((br _ (by decide)).trans ao) + queries := (yr _ (by decide)).trans ((br _ (by decide)).trans aq) + count := (yr _ (by decide)).trans ((br _ (by decide)).trans aQ) + size := (yr _ (by decide)).trans bn + first := yf + length := yl + one := (yr _ (by decide)).trans b1 + zero := (yr _ (by decide)).trans ((br _ (by decide)).trans a0) + ab := yab + bc := ybc + ac := yac + scanSize := yn + scanFirst := yfirst + scanLength := ylength } + · intro j hj + exact (yr j (Or.inl (by omega))).trans ((br j (Or.inl (by omega))).trans (ar j hj)) + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Kernel.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Kernel.lean new file mode 100755 index 000000000..3a6e50476 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Kernel.lean @@ -0,0 +1,188 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Spec +import Complexity.Computability.WordRAM.RegisterFrame + +/-! +# Real coordinate delivery, verifier calls and witness returns + +The internal call rule invokes the complete native signed-weight verifier, preserves the +controller and old product bank, and returns its original zero-sum vertex. Query delivery +loads the actual interleaved words and increments the actual scan counter. No run or witness +routine is supplied by a caller. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM + +private abbrev Stable (i : Fin 400) : Prop := + i.val < 326 ∨ i = Reg.one ∨ i = Reg.zero ∨ + (345 ≤ i.val ∧ i.val ≤ 348) ∨ (351 ≤ i.val ∧ i.val ≤ 352) + +-- These closed checks enumerate only the finite caller bank and loop-free delivery/return code. +set_option maxRecDepth 4096 in +private theorem deliverPair_preserves : ∀ i : Fin 400, Stable i → deliverPair.Preserves i := by + decide + +-- The same finite controller-frame check, without unfolding the verifier program. +set_option maxRecDepth 4096 in +private theorem receiveWitness_preserves : ∀ i : Fin 400, Stable i → + receiveWitness.Preserves i := by decide + +private theorem scanBank_injective : Function.Injective scanBank := by + intro i j h + apply Fin.ext + simpa only [scanBank, Nat.add_left_cancel_iff] using congrArg Fin.val h + +/-- Execute the real original-weight verifier on the disjoint bank. Every scanned weight +comes from the original arrays; a nonzero product answer supplies no original-weight witness. -/ +theorem exec_scan {w U n first length out queries count a b : ℕ} + (AB BC AC : ℕ → ℕ → ℤ) (x : Data 400) + (hx : Fixed n first length out queries count x) + (hargA : x.regs (scanBank WitnessScan.Reg.a) = a) + (hargB : x.regs (scanBank WitnessScan.Reg.b) = b) + (ha : a < n) (hb : b < n) (hc : first + length ≤ n) + (hmem : ∀ i < n, ∀ j < n, + x.mem (5 + (i * n + j)) = Signed.encode (AB i j) ∧ + x.mem (5 + n * n + (i * n + j)) = Signed.encode (BC i j) ∧ + x.mem (5 + n * n + n * n + (i * n + j)) = Signed.encode (AC i j)) + (hmag : ∀ i < n, ∀ j < n, + (AB i j).natAbs ≤ U ∧ (BC i j).natAbs ≤ U ∧ (AC i j).natAbs ≤ U) + (hU : GenericRAM.Signed.Fits w (2 * U : ℕ)) + (hfit : Fits (w + 2) (6 * (n * n) + n + first + length + 24)) : + ∃ y, ExecWithin (w + 2) scan x y (42 * length + 17) ∧ + Fixed n first length out queries count y ∧ y.mem = x.mem ∧ + y.regs (scanBank WitnessScan.Reg.a) = a ∧ + y.regs (scanBank WitnessScan.Reg.b) = b ∧ + (y.regs (scanBank WitnessScan.Reg.found) = 0 ∨ + y.regs (scanBank WitnessScan.Reg.found) = 1) ∧ + (y.regs (scanBank WitnessScan.Reg.found) = 1 ↔ + ∃ c, first ≤ c ∧ c < first + length ∧ AB a b + BC b c + AC a c = 0) ∧ + (y.regs (scanBank WitnessScan.Reg.found) = 1 → + first ≤ y.regs (scanBank WitnessScan.Reg.witness) ∧ + y.regs (scanBank WitnessScan.Reg.witness) < first + length ∧ + AB a b + BC b (y.regs (scanBank WitnessScan.Reg.witness)) + + AC a (y.regs (scanBank WitnessScan.Reg.witness)) = 0) ∧ + ∀ i : Fin 400, i.val < 336 ∨ 368 ≤ i.val → y.regs i = x.regs i := by + let A : WitnessScan.Args := ⟨5, 5 + n * n, 5 + n * n + n * n, n, a, b, first, length⟩ + have hargs : A.InRegisters (x.comap scanBank) := + ⟨hx.ab, hx.bc, hx.ac, hx.scanSize, hargA, hargB, hx.scanFirst, hx.scanLength⟩ + obtain ⟨s, hs, sa, sm, sf, si, sw⟩ := WitnessScan.exec A AB BC AC (x.comap scanBank) + hargs ha hb hc hmem hmag hU (hfit.of_le (by + dsimp only [A, WitnessScan.Args.addressBound]; omega)) + let y := s.extend scanBank x + have he : ExecWithin (w + 2) scan x y (42 * length + 17) := hs.rename scanBank_injective + have yr (i : Fin 32) : y.regs (scanBank i) = s.regs i := + scanBank_injective.extend_apply _ _ i + have frame (i : Fin 400) (hi : i.val < 336 ∨ 368 ≤ i.val) : y.regs i = x.regs i := by + apply he.regs_eq + apply Code.preserves_rename_of_forall_ne + intro j hj + have hv := congrArg Fin.val hj + change 336 + j.val = i.val at hv + have hj := j.isLt + omega + obtain ⟨sab, sbc, sac, sn, sa', sb', sfirst, slength⟩ := sa + refine ⟨y, he, ?_, sm, (yr _).trans sa', (yr _).trans sb', ?_, ?_, ?_, frame⟩ + · exact + { output := (frame _ (by decide)).trans hx.output + queries := (frame _ (by decide)).trans hx.queries + count := (frame _ (by decide)).trans hx.count + size := (frame _ (by decide)).trans hx.size + first := (frame _ (by decide)).trans hx.first + length := (frame _ (by decide)).trans hx.length + one := (frame _ (by decide)).trans hx.one + zero := (frame _ (by decide)).trans hx.zero + ab := (yr _).trans sab + bc := (yr _).trans sbc + ac := (yr _).trans sac + scanSize := (yr _).trans sn + scanFirst := (yr _).trans sfirst + scanLength := (yr _).trans slength } + · simpa only [yr] using sf + · simpa only [yr] using si + · simpa only [yr] using sw + +/-- Load the actual query occurrence into the verifier arguments and increment the native +scan count. No query enumeration or pair supplied as an algorithm argument replaces the loads. -/ +theorem exec_deliverPair {W n first length out queries count i scans a b : ℕ} (x : Data 400) + (hx : Fixed n first length out queries count x) + (hi : x.regs Reg.index = i) (hs : x.regs Reg.scans = scans) + (ha : x.mem (queries + 2 * i) = a) (hb : x.mem (queries + 2 * i + 1) = b) + (hfit : Fits W (queries + 2 * i + scans + 8)) : + ∃ y, ExecWithin W deliverPair x y 6 ∧ Fixed n first length out queries count y ∧ + y.mem = x.mem ∧ y.regs (scanBank WitnessScan.Reg.a) = a ∧ + y.regs (scanBank WitnessScan.Reg.b) = b ∧ y.regs Reg.scans = scans + 1 ∧ + y.regs Reg.index = i ∧ y.regs Reg.found = x.regs Reg.found ∧ + ∀ j : Fin 400, j.val < 326 ∨ j = Reg.one ∨ j = Reg.zero ∨ + (345 ≤ j.val ∧ j.val ≤ 348) ∨ (351 ≤ j.val ∧ j.val ≤ 352) → + y.regs j = x.regs j := by + have hd : Fits W (i + i) := hfit.of_le (by omega) + have hp : Fits W (i + i + queries) := hfit.of_le (by omega) + have hp1 : Fits W (i + i + queries + 1) := hfit.of_le (by omega) + have hs1 : Fits W (scans + 1) := hfit.of_le (by omega) + have hreadA : x.mem (i + i + queries) = a := by simpa only [two_mul, Nat.add_comm] using ha + have hreadB : x.mem (i + i + queries + 1) = b := by + simpa only [two_mul, Nat.add_comm] using hb + let y := x.set Reg.address (i + i) |>.set Reg.address (i + i + queries) + |>.set (scanBank WitnessScan.Reg.a) a |>.set Reg.address (i + i + queries + 1) + |>.set (scanBank WitnessScan.Reg.b) b |>.set Reg.scans (scans + 1) + have he : ExecWithin W deliverPair x y 6 := by + ram_eval [deliverPair, y, hi, hs, hx.queries, hx.one, hreadA, hreadB, + hd, hp, hp1, hs1, Reg.address, Reg.index, Reg.queries, Reg.one, Reg.scans, + scanBank, WitnessScan.Reg.a, WitnessScan.Reg.b] + have frame (j : Fin 400) (hj : Stable j) : y.regs j = x.regs j := + he.regs_eq (deliverPair_preserves j hj) + refine ⟨y, he, hx.of_frame frame, rfl, ?_, ?_, ?_, ?_, ?_, frame⟩ + · rfl + · rfl + · rfl + · exact (he.regs_eq (by decide)).trans hi + · exact he.regs_eq (by decide) + +/-- Copy back only a genuinely reported verifier success and its actual original coordinates. +This is a charged native branch, preserving all fixed arguments, memory and the scan count. -/ +theorem exec_receiveWitness {W n first length out queries count flag a b c : ℕ} (x : Data 400) + (hx : Fixed n first length out queries count x) + (hf : x.regs (scanBank WitnessScan.Reg.found) = flag) + (ha : x.regs (scanBank WitnessScan.Reg.a) = a) + (hb : x.regs (scanBank WitnessScan.Reg.b) = b) + (hc : x.regs (scanBank WitnessScan.Reg.witness) = c) : + ∃ y, ExecWithin W receiveWitness x y 5 ∧ Fixed n first length out queries count y ∧ + y.mem = x.mem ∧ y.regs Reg.found = (if flag = 1 then 1 else x.regs Reg.found) ∧ + y.regs Reg.witnessA = (if flag = 1 then a else x.regs Reg.witnessA) ∧ + y.regs Reg.witnessB = (if flag = 1 then b else x.regs Reg.witnessB) ∧ + y.regs Reg.witnessC = (if flag = 1 then c else x.regs Reg.witnessC) ∧ + y.regs Reg.scans = x.regs Reg.scans ∧ y.regs Reg.index = x.regs Reg.index ∧ + ∀ j : Fin 400, j.val < 326 ∨ j = Reg.one ∨ j = Reg.zero ∨ + (345 ≤ j.val ∧ j.val ≤ 348) ∨ (351 ≤ j.val ∧ j.val ≤ 352) → + y.regs j = x.regs j := by + let y := if flag = 1 then x.set Reg.found 1 |>.set Reg.witnessA a + |>.set Reg.witnessB b |>.set Reg.witnessC c + else x.set Reg.found (x.regs Reg.found) + dsimp only [scanBank, WitnessScan.Reg.found, WitnessScan.Reg.a, + WitnessScan.Reg.b, WitnessScan.Reg.witness] at hf ha hb hc + have he : ExecWithin W receiveWitness x y 5 := by + by_cases h : flag = 1 <;> + ram_eval [receiveWitness, y, h, hf, ha, hb, hc, hx.one, Reg.one, Reg.found, + Reg.witnessA, Reg.witnessB, Reg.witnessC, scanBank, WitnessScan.Reg.found, + WitnessScan.Reg.a, WitnessScan.Reg.b, WitnessScan.Reg.witness] + have frame (j : Fin 400) (hj : Stable j) : y.regs j = x.regs j := + he.regs_eq (receiveWitness_preserves j hj) + refine ⟨y, he, hx.of_frame frame, ?_, ?_, ?_, ?_, ?_, + he.regs_eq (by decide), he.regs_eq (by decide), frame⟩ + all_goals + dsimp only [y] + split_ifs <;> rfl + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Pair.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Pair.lean new file mode 100755 index 000000000..6048bc776 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Pair.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Kernel + +/-! +# One actual product-selected original-weight verification + +The coordinate words are loaded from the original interleaved query block. The full renamed +native verifier reads the original three signed arrays, and its result is returned to the +controller. This internal call rule charges delivery, the complete scan and witness return. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM + +/-- Execute one selected candidate from its actual query words. A reported witness satisfies +the original integer equation, independently of whether the modular candidate was genuine. -/ +theorem exec_scanPair {w U n first length out queries count i scans a b : ℕ} + (AB BC AC : ℕ → ℕ → ℤ) (x : Data 400) + (hx : Fixed n first length out queries count x) + (hi : x.regs Reg.index = i) (hs : x.regs Reg.scans = scans) + (hf : x.regs Reg.found = 0) + (haWord : x.mem (queries + 2 * i) = a) + (hbWord : x.mem (queries + 2 * i + 1) = b) + (ha : a < n) (hb : b < n) (hc : first + length ≤ n) + (hmem : ∀ u < n, ∀ v < n, + x.mem (5 + (u * n + v)) = Signed.encode (AB u v) ∧ + x.mem (5 + n * n + (u * n + v)) = Signed.encode (BC u v) ∧ + x.mem (5 + n * n + n * n + (u * n + v)) = Signed.encode (AC u v)) + (hmag : ∀ u < n, ∀ v < n, + (AB u v).natAbs ≤ U ∧ (BC u v).natAbs ≤ U ∧ (AC u v).natAbs ≤ U) + (hU : GenericRAM.Signed.Fits w (2 * U : ℕ)) + (hfit : Fits (w + 2) + (queries + 2 * i + scans + 6 * (n * n) + n + first + length + 32)) : + ∃ y, ExecWithin (w + 2) scanPair x y (42 * length + 28) ∧ + Fixed n first length out queries count y ∧ y.mem = x.mem ∧ + y.regs Reg.index = i ∧ y.regs Reg.scans = scans + 1 ∧ + (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ + (y.regs Reg.found = 1 ↔ + ∃ c, first ≤ c ∧ c < first + length ∧ AB a b + BC b c + AC a c = 0) ∧ + (y.regs Reg.found = 1 → + y.regs Reg.witnessA = a ∧ y.regs Reg.witnessB = b ∧ + first ≤ y.regs Reg.witnessC ∧ y.regs Reg.witnessC < first + length ∧ + AB a b + BC b (y.regs Reg.witnessC) + AC a (y.regs Reg.witnessC) = 0) := by + obtain ⟨d, hd, dFixed, dm, da, db, ds, di, df, -⟩ := + exec_deliverPair x hx hi hs haWord hbWord (hfit.of_le (by omega)) + obtain ⟨s, he, sFixed, sm, sa, sb, sf, iffFound, sw, frame⟩ := + exec_scan AB BC AC d dFixed da db ha hb hc (by simpa only [dm] using hmem) + hmag hU (hfit.of_le (by omega)) + obtain ⟨y, hy, yFixed, ym, yf, ya, yb, yc, ys, yi, -⟩ := + exec_receiveWitness s sFixed rfl sa sb rfl + have sIndex : s.regs Reg.index = i := (frame _ (by decide)).trans di + have sScans : s.regs Reg.scans = scans + 1 := (frame _ (by decide)).trans ds + have sFound : s.regs Reg.found = 0 := (frame _ (by decide)).trans (df.trans hf) + have hexe : ExecWithin (w + 2) scanPair x y (42 * length + 28) := + (hd.seq (he.seq hy)).mono (by omega) + have yflag : y.regs Reg.found = s.regs (scanBank WitnessScan.Reg.found) := by + rcases sf with sf | sf <;> simp only [yf, sf, sFound, Nat.zero_ne_one, ↓reduceIte] + refine ⟨y, hexe, yFixed, ym.trans (sm.trans dm), yi.trans sIndex, ys.trans sScans, + ?_, ?_, ?_⟩ + · simpa only [yflag] using sf + · simpa only [yflag] using iffFound + · intro hfound + have hsfound : s.regs (scanBank WitnessScan.Reg.found) = 1 := yflag.symm.trans hfound + obtain ⟨lo, hi, hz⟩ := sw hsfound + refine ⟨?_, ?_, ?_, ?_, ?_⟩ + · simpa only [hsfound, ↓reduceIte] using ya + · simpa only [hsfound, ↓reduceIte] using yb + · simpa only [yc, hsfound, ↓reduceIte] using lo + · simpa only [yc, hsfound, ↓reduceIte] using hi + · simpa only [yc, hsfound, ↓reduceIte] using hz + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Program.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Program.lean new file mode 100755 index 000000000..2f6468795 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Program.lean @@ -0,0 +1,188 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.QueryValues +public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Exec +public import Complexity.Algorithms.WordRAM.NatSqrt + +/-! +# Native consumption of one prepared record's product answers + +The existing 320-register producer/product bank stays in place. A disjoint controller reads +the actual record, derives the original piece interval with charged square-root/ceiling code, +and reads nonzero encoded product answers and their raw interleaved coordinates. Each accepted +pair invokes the real original-weight verifier. Once a witness is found, later scans are skipped. +All memory is read-only in this phase; no result, catalogue or window snapshot is needed. + +The consumer adapts the control of the pinned `Witnesses/ScanPairs.lean` procedure in the +proof of Theorem 17 at `anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. +The native ABI, code and charged constants are original. These stage definitions are internal +implementation interfaces. The outer instance loop and prime/global parameter choice are separate. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM + +namespace Reg + +/-- Absolute product-answer pointer. -/ +abbrev output : Fin 400 := 320 +/-- Protected interleaved query-pair pointer. -/ +abbrev queries : Fin 400 := 321 +/-- Actual number of query occurrences. -/ +abbrev count : Fin 400 := 322 +/-- Original outer dimension. -/ +abbrev size : Fin 400 := 323 +/-- First original vertex of this piece. -/ +abbrev first : Fin 400 := 324 +/-- Actual number of original vertices in this piece. -/ +abbrev length : Fin 400 := 325 +/-- Number of query occurrences already treated. -/ +abbrev index : Fin 400 := 326 +/-- Constant one. -/ +abbrev one : Fin 400 := 327 +/-- Actual memory-address temporary. -/ +abbrev address : Fin 400 := 328 +/-- Loaded canonical-signed product answer. -/ +abbrev answer : Fin 400 := 329 +/-- Actual piece ordinal read from the prepared record. -/ +abbrev piece : Fin 400 := 332 +/-- Computed maximum piece length. -/ +abbrev blockLength : Fin 400 := 333 +/-- Original positive piece divisor. -/ +abbrev divisor : Fin 400 := 334 +/-- Index/product temporary. -/ +abbrev temp : Fin 400 := 335 +/-- Raw unsigned success flag. -/ +abbrev found : Fin 400 := 392 +/-- Original first coordinate of a verified triangle. -/ +abbrev witnessA : Fin 400 := 393 +/-- Original second coordinate of a verified triangle. -/ +abbrev witnessB : Fin 400 := 394 +/-- Original third coordinate of a verified triangle. -/ +abbrev witnessC : Fin 400 := 395 +/-- Actual number of original-weight interval scans performed. -/ +abbrev scans : Fin 400 := 396 +/-- Constant zero, disjoint from both procedure banks. -/ +abbrev zero : Fin 400 := 399 + +end Reg + +/-- Preserve the complete existing producer/product register bank. -/ +@[expose] def productBank : Fin 320 → Fin 400 := Fin.castLE (by decide) + +/-- The entire thirty-two-register original-weight verifier moves together. -/ +@[expose] def scanBank (i : Fin 32) : Fin 400 := ⟨336 + i.val, by omega⟩ + +/-- The root computation temporarily reuses the first six verifier registers. -/ +@[expose] def sqrtBank (i : Fin 6) : Fin 400 := ⟨336 + i.val, by omega⟩ + +/-- Actual charged square root for the local piece-size decoder. -/ +@[expose] def squareRoot : Code .standard 400 := NatSqrt.program.rename sqrtBank + +open Reg in +/-- Read original scalar words and the selected record's piece/query fields. -/ +@[expose] def readRecord : Code .standard 400 := + let sqrtArg := sqrtBank NatSqrt.arg + let productResult := productBank RecordProduct.Reg.result + let ordinal := productBank RecordProduct.Reg.ordinal + let records := productBank (RecordProduct.producerBank Instances.output) + ram[ + one ← #1; zero ← #0; address ← #3; size ← *address; + address ← #1; temp ← *address; sqrtArg ← temp; + address ← #2; divisor ← *address; output ← productResult; + temp ← size * temp; temp ← temp + temp; piece ← #6; temp ← temp + piece; + address ← ordinal * temp; + address ← records + address; address ← address + one; piece ← *address; + address ← address + one; queries ← *address; address ← address + one; count ← *address; + found ← #0; witnessA ← #0; witnessB ← #0; witnessC ← #0; scans ← #0; index ← #0 ] + +open Reg in +/-- Compute the original interval and deliver the unchanged signed-array pointers once. -/ +@[expose] def finishHeader : Code .standard 400 := + let root := sqrtBank NatSqrt.root + let scanAB := scanBank WitnessScan.Reg.ab + let scanBC := scanBank WitnessScan.Reg.bc + let scanAC := scanBank WitnessScan.Reg.ac + let scanN := scanBank WitnessScan.Reg.size + let scanFirst := scanBank WitnessScan.Reg.first + let scanLength := scanBank WitnessScan.Reg.length + ram[ + blockLength ← root + divisor; blockLength ← blockLength - one; + blockLength ← blockLength / divisor; first ← piece * blockLength; length ← size - first; + if blockLength < length then [ length ← blockLength ] else [ length ← length ]; + scanAB ← #5; temp ← size * size; + scanBC ← scanAB + temp; + scanAC ← scanBC + temp; + scanN ← size; scanFirst ← first; + scanLength ← length ] + +/-- Complete actual local descriptor and interval decoding. -/ +@[expose] def header : Code .standard 400 := + .seq readRecord (.seq squareRoot finishHeader) + +/-- Actual original signed-weight scan, with the complete bank renamed together. -/ +@[expose] def scan : Code .standard 400 := WitnessScan.program.rename scanBank + +open Reg in +/-- Load the real original coordinate pair and charge this invocation in the native counter. -/ +@[expose] def deliverPair : Code .standard 400 := + let scanA := scanBank WitnessScan.Reg.a + let scanB := scanBank WitnessScan.Reg.b + ram[ + address ← index + index; address ← address + queries; + scanA ← *address; address ← address + one; + scanB ← *address; scans ← scans + one ] + +open Reg in +/-- A verifier success returns its original vertex together with the actual pair that was read. -/ +@[expose] def receiveWitness : Code .standard 400 := + let scanFound := scanBank WitnessScan.Reg.found + let scanA := scanBank WitnessScan.Reg.a + let scanB := scanBank WitnessScan.Reg.b + let scanC := scanBank WitnessScan.Reg.witness + ram[ + if scanFound = one then [ + found ← one; witnessA ← scanA; witnessB ← scanB; + witnessC ← scanC ] else [ found ← found ] ] + +/-- The accepted-candidate branch contains the real verifier, not a supplied witness routine. -/ +@[expose] def scanPair : Code .standard 400 := + .seq deliverPair (.seq scan receiveWitness) + +open Reg in +/-- Read a product answer only while searching, and skip all later scans after a success. -/ +@[expose] def step : Code .standard 400 := + .seq (.branch (.eq found zero) + (.seq (ram[ address ← output + index; answer ← *address ]) + (.branch (.eq answer zero) (.op (.copy answer answer)) scanPair)) + (.op (.copy answer answer))) (.op (.bin .add index index one)) + +/-- Consume one actual product-answer slice without modifying any memory. -/ +@[expose] def consume : Code .standard 400 := + .seq header (.loop (.lt Reg.index Reg.count) step) + +/-- Actual product calculation followed immediately by original-weight verification. -/ +@[expose] def program : Code .standard 400 := + .seq (RecordProduct.program.rename productBank) consume + +/-- Original initialized producer/product code followed by the real candidate consumer. +The fixed first-record client reloads its selected ordinal with one charged instruction. -/ +@[expose] def entryProgram : Code .standard 400 := + .seq (RecordProduct.entryProgram.rename productBank) + (.seq (.op (.const (productBank RecordProduct.Reg.ordinal) 0)) consume) + +/-- The consumer is one fixed finite instruction graph and never reads the physical width. -/ +theorem consume_fixed : consume.size ≤ 512 ∧ ¬consume.ReadsWidth := by decide + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Queries.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Queries.lean new file mode 100755 index 000000000..82fc4a87c --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Queries.lean @@ -0,0 +1,237 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Step +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Trace + +/-! +# The actual short-circuit query loop + +The proof derives its scan trace from the original answer words and the verifier's integer +equation. Runtime execution receives neither trace nor witness function. Variable-cost loop +accounting charges a complete scan exactly when the native controller actually invokes it. +The finite scan bound follows the counting argument of pinned `Witnesses/ScanPairs.lean`; +the native code, ABI and instruction constants are original. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM + +private theorem potential_step (active : ℕ → Prop) [DecidablePred active] + {i Q T : ℕ} (hi : i < Q) : + (if active i then T + 6 else 6) + 2 + + (8 * (Q - (i + 1)) + T * (Nat.count active Q - Nat.count active (i + 1))) ≤ + 8 * (Q - i) + T * (Nat.count active Q - Nat.count active i) := by + have hcount : Nat.count active (i + 1) = Nat.count active i + + (if active i then 1 else 0) := Nat.count_succ active i + have hle := Nat.count_monotone active (Nat.succ_le_of_lt hi) + change Nat.count active (i + 1) ≤ Nat.count active Q at hle + have hindex : Q - i = Q - (i + 1) + 1 := by omega + by_cases h : active i + · rw [ite_eq_left h] at hcount ⊢ + rw [hcount] at hle + have hd : Nat.count active Q - Nat.count active i = + (Nat.count active Q - Nat.count active (i + 1)) + 1 := by omega + rw [hindex, hd, Nat.mul_add, Nat.mul_add] + omega + · rw [ite_eq_right h] at hcount ⊢ + simp only [Nat.add_zero] at hcount + rw [hindex, hcount, Nat.mul_add, Nat.mul_one] + omega + +/-- Run the actual answer/query loop. Every query coordinate is read from the protected +interleaved block, and every success is verified against the original three integer arrays. +Only accepted pairs without an original witness contribute to the failure count. -/ +theorem exec_queries {w U n first length out queries Q : ℕ} + (AB BC AC : ℕ → ℕ → ℤ) (a b : ℕ → ℕ) (x : Data 400) + (hx : Fixed n first length out queries Q x) + (hi : x.regs Reg.index = 0) (hs : x.regs Reg.scans = 0) + (hf : x.regs Reg.found = 0) + (hquery : ∀ i < Q, x.mem (queries + 2 * i) = a i ∧ + x.mem (queries + 2 * i + 1) = b i ∧ a i < n ∧ b i < n) + (hc : first + length ≤ n) + (hmem : ∀ u < n, ∀ v < n, + x.mem (5 + (u * n + v)) = Signed.encode (AB u v) ∧ + x.mem (5 + n * n + (u * n + v)) = Signed.encode (BC u v) ∧ + x.mem (5 + n * n + n * n + (u * n + v)) = Signed.encode (AC u v)) + (hmag : ∀ u < n, ∀ v < n, + (AB u v).natAbs ≤ U ∧ (BC u v).natAbs ≤ U ∧ (AC u v).natAbs ≤ U) + (hU : GenericRAM.Signed.Fits w (2 * U : ℕ)) + (hfit : Fits (w + 2) + (out + queries + 4 * Q + 6 * (n * n) + n + first + length + 64)) : + ∃ y S, ExecWithin (w + 2) (.loop (.lt Reg.index Reg.count) step) x y + (8 * Q + (42 * length + 28) * S + 2) ∧ + Fixed n first length out queries Q y ∧ y.mem = x.mem ∧ + y.regs Reg.index = Q ∧ y.regs Reg.scans = S ∧ + S ≤ Nat.count (fun i => x.mem (out + i) ≠ 0 ∧ + ∀ c : Fin length, AB (a i) (b i) + BC (b i) (first + c.val) + + AC (a i) (first + c.val) ≠ 0) Q + 1 ∧ + (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ + (y.regs Reg.found = 1 ↔ ∃ i < Q, x.mem (out + i) ≠ 0 ∧ + ∃ c, first ≤ c ∧ c < first + length ∧ AB (a i) (b i) + BC (b i) c + AC (a i) c = 0) ∧ + (y.regs Reg.found = 1 → ∃ i < Q, + x.mem (out + i) ≠ 0 ∧ y.regs Reg.witnessA = a i ∧ y.regs Reg.witnessB = b i ∧ + first ≤ y.regs Reg.witnessC ∧ y.regs Reg.witnessC < first + length ∧ + AB (a i) (b i) + BC (b i) (y.regs Reg.witnessC) + + AC (a i) (y.regs Reg.witnessC) = 0) := by + let accept (i : ℕ) : Bool := decide (x.mem (out + i) ≠ 0) + let hit (i : ℕ) : Bool := decide (∃ c : Fin length, + AB (a i) (b i) + BC (b i) (first + c.val) + AC (a i) (first + c.val) = 0) + let active (i : ℕ) : Prop := (accept i && !foundBefore accept hit i) = true + have hit_iff (i : ℕ) : hit i = true ↔ ∃ c, first ≤ c ∧ c < first + length ∧ + AB (a i) (b i) + BC (b i) c + AC (a i) c = 0 := by + simp only [hit, decide_eq_true_eq] + constructor + · rintro ⟨c, hz⟩ + exact ⟨first + c.val, by omega, by have := c.isLt; omega, hz⟩ + · rintro ⟨c, hlo, hhi, hz⟩ + refine ⟨⟨c - first, by omega⟩, ?_⟩ + simpa only [Nat.add_sub_of_le hlo] using hz + let I (y : Data 400) : Prop := + Fixed n first length out queries Q y ∧ y.mem = x.mem ∧ y.regs Reg.index ≤ Q ∧ + y.regs Reg.found = (foundBefore accept hit (y.regs Reg.index)).toNat ∧ + y.regs Reg.scans = Nat.count active (y.regs Reg.index) ∧ + (y.regs Reg.found = 1 → ∃ i < y.regs Reg.index, + x.mem (out + i) ≠ 0 ∧ y.regs Reg.witnessA = a i ∧ y.regs Reg.witnessB = b i ∧ + first ≤ y.regs Reg.witnessC ∧ y.regs Reg.witnessC < first + length ∧ + AB (a i) (b i) + BC (b i) (y.regs Reg.witnessC) + + AC (a i) (y.regs Reg.witnessC) = 0) + let Φ (y : Data 400) := 8 * (Q - y.regs Reg.index) + + (42 * length + 28) * (Nat.count active Q - Nat.count active (y.regs Reg.index)) + have hstart : I x := by + refine ⟨hx, rfl, by omega, ?_, ?_, ?_⟩ + · simp only [hi, hf, foundBefore_zero, Bool.toNat_false] + · simp only [hi, hs, Nat.count_zero] + · intro h; omega + have body (y : Data 400) (hy : I y) (hcond : (Cond.lt Reg.index Reg.count).Holds y) : + ∃ z T, ExecWithin (w + 2) step y z T ∧ I z ∧ T + 2 + Φ z ≤ Φ y := by + obtain ⟨yFixed, ym, yi, yf, ys, yw⟩ := hy + let i := y.regs Reg.index + change y.regs Reg.found = (foundBefore accept hit i).toNat at yf + have hilt : i < Q := by simpa only [Cond.holds_lt, yFixed.count] using hcond + have hscans : y.regs Reg.scans ≤ Q := by + rw [ys] + exact (Nat.count_le active).trans yi + have hstepfit : Fits (w + 2) + (out + queries + 3 * i + y.regs Reg.scans + 6 * (n * n) + n + first + length + 40) := + hfit.of_le (by dsimp only [i]; omega) + by_cases hact : active i + · have hinfo : accept i = true ∧ foundBefore accept hit i = false := by + simpa only [active, Bool.and_eq_true, Bool.not_eq_true_eq_eq_false] using hact + have yzero : y.regs Reg.found = 0 := by rw [yf, hinfo.2]; rfl + have hanswer : y.mem (out + i) ≠ 0 := by + rw [ym] + exact of_decide_eq_true hinfo.1 + obtain ⟨haWord, hbWord, ha, hb⟩ := hquery i hilt + obtain ⟨z, he, zFixed, zm, zi, zs, zf, zh, zw⟩ := exec_step_scan (i := i) AB BC AC y yFixed + rfl rfl yzero hanswer (by simpa only [ym] using haWord) + (by simpa only [ym] using hbWord) ha hb hc (by simpa only [ym] using hmem) + hmag hU hstepfit + have zflag : z.regs Reg.found = (hit i).toNat := by + cases hh : hit i + · have hn : z.regs Reg.found ≠ 1 := by + intro h + have := (hit_iff i).mpr (zh.mp h) + simp only [hh, Bool.false_eq_true] at this + rcases zf with hz | hz + · simpa only [hh, Bool.toNat_false] using hz + · exact (hn hz).elim + · simpa only [hh, Bool.toNat_true] using zh.mpr ((hit_iff i).mp hh) + have zI : I z := by + refine ⟨zFixed, zm.trans ym, by omega, ?_, ?_, ?_⟩ + · rw [zi, foundBefore_succ, hinfo.1, hinfo.2] + simpa only [Bool.true_and, Bool.false_or] using zflag + · rw [zi, Nat.count_succ, ite_eq_left hact, ← ys] + exact zs + · intro hz + obtain ⟨za, zb, zlo, zhi, zeq⟩ := zw hz + exact ⟨i, by omega, by simpa only [ym] using hanswer, za, zb, zlo, zhi, zeq⟩ + refine ⟨z, 42 * length + 34, he, zI, ?_⟩ + have hp := potential_step active (T := 42 * length + 28) hilt + simpa only [ite_eq_left hact, Φ, zi, i] using hp + · have hskip : y.regs Reg.found ≠ 0 ∨ y.mem (out + i) = 0 := by + by_cases hz : y.regs Reg.found = 0 + · right + by_contra hn + apply hact + have ha : accept i = true := decide_eq_true (by simpa only [ym] using hn) + have hb : foundBefore accept hit i = false := by + have hz' : (foundBefore accept hit i).toNat = 0 := yf.symm.trans hz + cases h : foundBefore accept hit i + · rfl + · simp only [h, Bool.toNat_true, Nat.one_ne_zero] at hz' + simp only [active, ha, hb, Bool.not_false, Bool.true_and] + · exact Or.inl hz + obtain ⟨z, he, zFixed, zm, zi, frame⟩ := exec_step_skip (i := i) y yFixed rfl hskip + (hfit.of_le (by dsimp only [i]; omega)) + have zfound : z.regs Reg.found = y.regs Reg.found := + frame _ (by decide) (by decide) (by decide) + have zscans : z.regs Reg.scans = y.regs Reg.scans := + frame _ (by decide) (by decide) (by decide) + have hnext : foundBefore accept hit (i + 1) = foundBefore accept hit i := by + rw [foundBefore_succ] + cases ha : accept i + · simp only [Bool.false_and, Bool.or_false] + · cases hb : foundBefore accept hit i + · exact (hact (by simp [active, ha, hb])).elim + · simp only [Bool.true_or] + have zI : I z := by + refine ⟨zFixed, zm.trans ym, by omega, ?_, ?_, ?_⟩ + · rw [zi, hnext, zfound] + exact yf + · rw [zi, Nat.count_succ, ite_eq_right hact, Nat.add_zero, zscans] + exact ys + · intro hz + obtain ⟨j, hj, hm, hwa, hwb, hlo, hhi, hzsum⟩ := yw (zfound.symm.trans hz) + have za := frame Reg.witnessA (by decide) (by decide) (by decide) + have zb := frame Reg.witnessB (by decide) (by decide) (by decide) + have zc := frame Reg.witnessC (by decide) (by decide) (by decide) + exact ⟨j, by omega, hm, za.trans hwa, zb.trans hwb, + by simpa only [zc] using hlo, by simpa only [zc] using hhi, + by simpa only [zc] using hzsum⟩ + refine ⟨z, 6, he, zI, ?_⟩ + have hp := potential_step active (T := 42 * length + 28) hilt + simpa only [ite_eq_right hact, Φ, zi, i] using hp + obtain ⟨y, he, hIy, hend⟩ := ExecWithin.loop_potential I Φ body hstart + obtain ⟨yFixed, ym, yi, yf, ys, yw⟩ := hIy + have yindex : y.regs Reg.index = Q := by + have hh : ¬ y.regs Reg.index < Q := by + simpa only [Cond.holds_lt, yFixed.count] using hend + omega + have hcount := scan_count_le accept hit Q + have hfail : Nat.count (fun i => (accept i && !hit i) = true) Q = + Nat.count (fun i => x.mem (out + i) ≠ 0 ∧ + ∀ c : Fin length, AB (a i) (b i) + BC (b i) (first + c.val) + + AC (a i) (first + c.val) ≠ 0) Q := by + congr 1 + funext i + simp only [accept, hit, Bool.and_eq_true, decide_eq_true_eq, + Bool.not_eq_true_eq_eq_false, decide_eq_false_iff_not, not_exists] + have flag_le : (foundBefore accept hit Q).toNat ≤ 1 := by + cases foundBefore accept hit Q <;> decide + refine ⟨y, Nat.count active Q, ?_, yFixed, ym, yindex, ?_, ?_, ?_, ?_, ?_⟩ + · simpa only [Φ, hi, Nat.sub_zero, Nat.count_zero, Nat.mul_zero, Nat.add_zero] using he + · simpa only [yindex] using ys + · rw [← hfail] + exact hcount.trans (Nat.add_le_add_left flag_le _) + · rw [yf, yindex] + cases foundBefore accept hit Q <;> simp + · rw [yf, yindex] + simp only [Bool.toNat_eq_one, foundBefore_eq_true, accept, decide_eq_true_eq] + constructor + · rintro ⟨i, hi, ha, hh⟩ + exact ⟨i, hi, ha, (hit_iff i).mp hh⟩ + · rintro ⟨i, hi, ha, hh⟩ + exact ⟨i, hi, ha, (hit_iff i).mpr hh⟩ + · simpa only [yindex] using yw + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Readback.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Readback.lean new file mode 100755 index 000000000..dd61118b8 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Readback.lean @@ -0,0 +1,113 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.QueryValues +public import Complexity.Algorithms.ExactTriangle.PieceBounds + +/-! +# Protected original data for a real record consumer + +The original arrays, selected record and original interleaved queries all precede the actual +producer heap. These bounds are derived from `Instances.Output` and membership in the existing +finite instance family. A product execution can therefore preserve the actual words needed +by candidate recovery without copying the catalogue or snapshotting its workspace. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle + +/-- Read the real selected record and prove every subsequent read is in the protected prefix. +The native program obtains these fields by loads; this theorem only derives their meaning. -/ +theorem record_view {n D g p : ℕ} (values : ℕ → ℤ) (initial y : Data 64) + (h : Instances.Output n D g p values initial y) + (hD : 16 ≤ D) (hDn : D ≤ n) (hg : 1 ≤ g) (hp : p ≠ 0) + (idx : InstanceIndex p) + (hidx : idx ∈ instanceIndices (fun a b : Fin n => values (pairIndex (a, b))) D g p) : + let ord := RecordProduct.recordOrdinal n D g p values idx + let base := y.regs Instances.output + ord * (6 + 2 * (n * D)) + let q := RecordProduct.recordQueries n D p idx + let Q := RecordProduct.recordQueryCount n D p values idx + 5 + 3 * (n * n) ≤ y.regs Instances.heap ∧ + base + 6 ≤ y.regs Instances.heap ∧ q + 2 * Q ≤ y.regs Instances.heap ∧ + idx.2.2 * pieceSize D g ≤ n ∧ + (∀ j : Fin 6, y.mem (base + j.val) = + ![idx.1.val, idx.2.2, q, Q, base + 6, base + 6 + n * D] j) ∧ + (∀ j < Q, y.mem (q + 2 * j) < n ∧ y.mem (q + 2 * j + 1) < n) ∧ + (∀ j < 5 + 3 * (n * n), y.mem j = initial.mem j) := by + let wAB := fun a b : Fin n => values (pairIndex (a, b)) + let N := n * n + let C := fun rho => Nat.count (fun q => (values q % (p : ℤ)).toNat = rho) N + let H := numPieces n D g + let K := ChunkCatalogue.before C (queryCap n D) p + let ord := RecordProduct.recordOrdinal n D g p values idx + let base := y.regs Instances.output + ord * (6 + 2 * (n * D)) + let q := RecordProduct.recordQueries n D p idx + let Q := RecordProduct.recordQueryCount n D p values idx + obtain ⟨hk, hj⟩ := (mem_instanceIndices wAB idx).mp hidx + have hcard : (residueClass wAB p idx.1).card = C idx.1.val := + QuerySpec.card_residueClass wAB hp _ (by intro pair; rfl) idx.1 + have hchunk : idx.2.1 < C idx.1.val ⌈/⌉ queryCap n D := by + simpa only [numChunks, hcard] using hk + have hord := ChunkCatalogue.before_add_lt C (queryCap n D) idx.1.isLt hchunk + have hiCount : ord < K * H := by + have hh := Nat.mul_le_mul_right H (Nat.succ_le_of_lt hord) + change (ChunkCatalogue.before C (queryCap n D) idx.1.val + idx.2.1) * H + idx.2.2 < K * H + change (ChunkCatalogue.before C (queryCap n D) idx.1.val + idx.2.1 + 1) * H ≤ K * H at hh + change idx.2.2 < H at hj + rw [Nat.add_mul, Nat.one_mul] at hh + omega + have hout : y.regs Instances.output = 5 + 6 * N + p + 2 * N * p + 3 * K := h.2.2.2.1 + have hheap : y.regs Instances.heap = y.regs Instances.output + K * H * (6 + 2 * (n * D)) := + h.2.2.2.2.2.1.trans (congrArg (fun v => v + K * H * (6 + 2 * (n * D))) hout.symm) + have hbase : base + 6 ≤ y.regs Instances.heap := by + have hh := Nat.mul_le_mul_right (6 + 2 * (n * D)) (Nat.succ_le_of_lt hiCount) + rw [hheap] + dsimp only [base] + nlinarith only [hh] + have hQ : idx.2.1 * queryCap n D + Q ≤ N := by + have hc : C idx.1.val ≤ N := Nat.count_le _ + have hpositive := RecordProduct.recordQueryCount_pos values hp (by omega) hDn idx hidx + change 0 < min (queryCap n D) (C idx.1.val - idx.2.1 * queryCap n D) at hpositive + dsimp only [Q, RecordProduct.recordQueryCount] + change idx.2.1 * queryCap n D + + min (queryCap n D) (C idx.1.val - idx.2.1 * queryCap n D) ≤ N + omega + have hqEnd : q + 2 * Q ≤ y.regs Instances.heap := by + have hrho := Nat.mul_le_mul_right (2 * N) (Nat.succ_le_of_lt idx.1.isLt) + dsimp only [q, RecordProduct.recordQueries] + change 5 + 6 * N + p + 2 * idx.1.val * N + + 2 * (idx.2.1 * queryCap n D) + 2 * Q ≤ _ + rw [hheap, hout] + nlinarith only [hQ, hrho, Nat.zero_le (3 * K), + Nat.zero_le (K * H * (6 + 2 * (n * D)))] + have hfirst : idx.2.2 * pieceSize D g ≤ n := by + have hB := pieceSize_pos hD hg + by_contra hh + have hc : numPieces n D g ≤ idx.2.2 := (Nat.ceilDiv_le_iff hB).mpr (by omega) + change idx.2.2 < numPieces n D g at hj + omega + obtain ⟨hdr, -, -⟩ := PreparationSpec.instance_readback values wAB hp + (by intro a b; rfl) hD hDn hg h idx hidx + have hcoord := (RecordProduct.output_query_coordinates values wAB hp (by intro a b; rfl) + (by omega) hDn h idx.1 idx.2.1 hk).1 + rw [hcard] at hcoord + dsimp only + refine ⟨?_, hbase, hqEnd, hfirst, ?_, hcoord, ?_⟩ + · rw [hheap, hout] + dsimp only [N] + omega + · simpa only [base, ord, RecordProduct.recordOrdinal, Q, RecordProduct.recordQueryCount, + q, RecordProduct.recordQueries, hout, N, K, C, hcard, Block.Realizes, Block.read] using hdr + · intro j hj + exact h.2.2.2.2.2.2.2.2.2.2.2 j (Or.inl hj) + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Spec.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Spec.lean new file mode 100755 index 000000000..ab9f73f4b --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Spec.lean @@ -0,0 +1,79 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Program + +/-! +# Fixed registers of the per-record witness controller + +This internal invariant contains only ordinary runtime register equalities. It packages +neither execution nor witness, fit, sparse-count or time certificates. The actual header +establishes it from the original scalar words and record; the query loop preserves it. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM + +/-- The controller's fixed arguments and the verifier's shared array/interval arguments. -/ +structure Fixed (n firstVertex spanLen out queryBase queryCount : ℕ) (x : Data 400) : Prop where + /-- Absolute answer pointer. -/ + output : x.regs Reg.output = out + /-- Original interleaved query pointer. -/ + queries : x.regs Reg.queries = queryBase + /-- Actual occurrence count. -/ + count : x.regs Reg.count = queryCount + /-- Original outer dimension. -/ + size : x.regs Reg.size = n + /-- Original first piece vertex. -/ + first : x.regs Reg.first = firstVertex + /-- Number of original piece vertices. -/ + length : x.regs Reg.length = spanLen + /-- Constant one. -/ + one : x.regs Reg.one = 1 + /-- Constant zero. -/ + zero : x.regs Reg.zero = 0 + /-- Original signed A–B pointer delivered to the verifier. -/ + ab : x.regs (scanBank WitnessScan.Reg.ab) = 5 + /-- Original signed B–C pointer delivered to the verifier. -/ + bc : x.regs (scanBank WitnessScan.Reg.bc) = 5 + n * n + /-- Original signed A–C pointer delivered to the verifier. -/ + ac : x.regs (scanBank WitnessScan.Reg.ac) = 5 + n * n + n * n + /-- Verifier outer dimension. -/ + scanSize : x.regs (scanBank WitnessScan.Reg.size) = n + /-- Verifier original first vertex. -/ + scanFirst : x.regs (scanBank WitnessScan.Reg.first) = firstVertex + /-- Verifier interval length. -/ + scanLength : x.regs (scanBank WitnessScan.Reg.length) = spanLen + +/-- Reuse the fixed ABI under a frame that excludes the query cursor, loaded coordinates, +verifier scratch and result/counter registers. -/ +theorem Fixed.of_frame {n first length out queries count : ℕ} {x y : Data 400} + (hx : Fixed n first length out queries count x) + (he : ∀ i : Fin 400, i.val < 326 ∨ i = Reg.one ∨ i = Reg.zero ∨ + (345 ≤ i.val ∧ i.val ≤ 348) ∨ (351 ≤ i.val ∧ i.val ≤ 352) → + y.regs i = x.regs i) : Fixed n first length out queries count y where + output := (he _ (by decide)).trans hx.output + queries := (he _ (by decide)).trans hx.queries + count := (he _ (by decide)).trans hx.count + size := (he _ (by decide)).trans hx.size + first := (he _ (by decide)).trans hx.first + length := (he _ (by decide)).trans hx.length + one := (he _ (by decide)).trans hx.one + zero := (he _ (by decide)).trans hx.zero + ab := (he _ (by decide)).trans hx.ab + bc := (he _ (by decide)).trans hx.bc + ac := (he _ (by decide)).trans hx.ac + scanSize := (he _ (by decide)).trans hx.scanSize + scanFirst := (he _ (by decide)).trans hx.scanFirst + scanLength := (he _ (by decide)).trans hx.scanLength + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Step.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Step.lean new file mode 100755 index 000000000..1d1a37f98 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Step.lean @@ -0,0 +1,145 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Pair + +/-! +# Charged query-controller steps + +An accepted step reads the actual answer word and invokes the actual original-weight verifier. +The separate skipped-step rule accounts for both zero answers and the already-successful +short circuit. These are internal execution rules, not additional algorithm inputs. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM + +/-- A nonzero actual product word, while still searching, causes exactly one native scan. +The six additional instructions include both branches, answer access and cursor advancement. -/ +theorem exec_step_scan {w U n first length out queries count i scans a b : ℕ} + (AB BC AC : ℕ → ℕ → ℤ) (x : Data 400) + (hx : Fixed n first length out queries count x) + (hi : x.regs Reg.index = i) (hs : x.regs Reg.scans = scans) + (hf : x.regs Reg.found = 0) (hanswer : x.mem (out + i) ≠ 0) + (haWord : x.mem (queries + 2 * i) = a) + (hbWord : x.mem (queries + 2 * i + 1) = b) + (ha : a < n) (hb : b < n) (hc : first + length ≤ n) + (hmem : ∀ u < n, ∀ v < n, + x.mem (5 + (u * n + v)) = Signed.encode (AB u v) ∧ + x.mem (5 + n * n + (u * n + v)) = Signed.encode (BC u v) ∧ + x.mem (5 + n * n + n * n + (u * n + v)) = Signed.encode (AC u v)) + (hmag : ∀ u < n, ∀ v < n, + (AB u v).natAbs ≤ U ∧ (BC u v).natAbs ≤ U ∧ (AC u v).natAbs ≤ U) + (hU : GenericRAM.Signed.Fits w (2 * U : ℕ)) + (hfit : Fits (w + 2) + (out + queries + 3 * i + scans + 6 * (n * n) + n + first + length + 40)) : + ∃ y, ExecWithin (w + 2) step x y (42 * length + 34) ∧ + Fixed n first length out queries count y ∧ y.mem = x.mem ∧ + y.regs Reg.index = i + 1 ∧ y.regs Reg.scans = scans + 1 ∧ + (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ + (y.regs Reg.found = 1 ↔ + ∃ c, first ≤ c ∧ c < first + length ∧ AB a b + BC b c + AC a c = 0) ∧ + (y.regs Reg.found = 1 → + y.regs Reg.witnessA = a ∧ y.regs Reg.witnessB = b ∧ + first ≤ y.regs Reg.witnessC ∧ y.regs Reg.witnessC < first + length ∧ + AB a b + BC b (y.regs Reg.witnessC) + AC a (y.regs Reg.witnessC) = 0) := by + let d := x.set Reg.address (out + i) |>.set Reg.answer (x.mem (out + i)) + have hd : ExecWithin (w + 2) + (ram[ Reg.address ← Reg.output + Reg.index; Reg.answer ← *Reg.address ]) x d 2 := by + ram_eval [d, hx.output, hi, Reg.address, Reg.output, Reg.index, Reg.answer, + hfit.of_le (by omega : out + i ≤ + out + queries + 3 * i + scans + 6 * (n * n) + n + first + length + 40)] + have dFixed : Fixed n first length out queries count d := hx.of_frame (by + intro j hj + have haddr : j ≠ Reg.address := by + intro he + subst j + norm_num [Reg.address, Reg.one, Reg.zero] at hj + have hans : j ≠ Reg.answer := by + intro he + subst j + norm_num [Reg.answer, Reg.one, Reg.zero] at hj + simp [d, haddr, hans]) + obtain ⟨s, he, sFixed, sm, si, ss, sf, sh, sw⟩ := exec_scanPair AB BC AC d dFixed + (by simpa +decide [d, Reg.address, Reg.answer, Reg.index] using hi) + (by simpa +decide [d, Reg.address, Reg.answer, Reg.scans] using hs) + (by simpa +decide [d, Reg.address, Reg.answer, Reg.found] using hf) + haWord hbWord ha hb hc hmem hmag hU + (hfit.of_le (by omega)) + have hb : ExecWithin (w + 2) + (.branch (.eq Reg.found Reg.zero) + (.seq (ram[ Reg.address ← Reg.output + Reg.index; Reg.answer ← *Reg.address ]) + (.branch (.eq Reg.answer Reg.zero) (.op (.copy Reg.answer Reg.answer)) scanPair)) + (.op (.copy Reg.answer Reg.answer))) x s (42 * length + 33) := by + apply ExecWithin.branch_pos (by simp only [Cond.holds_eq, hf, hx.zero]) + exact (hd.seq (ExecWithin.branch_neg (cond := .eq Reg.answer Reg.zero) (by + simpa +decide [Cond.holds_eq, d, Reg.answer, Reg.address, Reg.zero, + show x.regs 399 = 0 from hx.zero] using hanswer) he)).mono (by omega) + let y := s.set Reg.index (i + 1) + have hy : ExecWithin (w + 2) (.op (.bin .add Reg.index Reg.index Reg.one)) s y 1 := by + ram_eval [y, si, sFixed.one, Reg.index, Reg.one, + hfit.of_le (by omega : i + 1 ≤ + out + queries + 3 * i + scans + 6 * (n * n) + n + first + length + 40)] + have yFixed : Fixed n first length out queries count y := sFixed.of_frame (by + intro j hj + have hne : j ≠ Reg.index := by + intro he + subst j + norm_num [Reg.index, Reg.one, Reg.zero] at hj + simp [y, hne]) + refine ⟨y, hb.seq hy, yFixed, sm, rfl, ?_, ?_, ?_, ?_⟩ + · simpa +decide [y, Reg.index, Reg.scans] using ss + · simpa +decide [y, Reg.index, Reg.found] using sf + · simpa +decide [y, Reg.index, Reg.found] using sh + · simpa +decide [y, Reg.index, Reg.found, Reg.witnessA, Reg.witnessB, + Reg.witnessC] using sw + +/-- Zero answers and an already successful flag incur no verifier call. In particular the +actual scan counter and all returned witness coordinates are preserved. -/ +theorem exec_step_skip {W n first length out queries count i : ℕ} (x : Data 400) + (hx : Fixed n first length out queries count x) (hi : x.regs Reg.index = i) + (hskip : x.regs Reg.found ≠ 0 ∨ x.mem (out + i) = 0) + (hfit : Fits W (out + i + 2)) : + ∃ y, ExecWithin W step x y 6 ∧ Fixed n first length out queries count y ∧ + y.mem = x.mem ∧ y.regs Reg.index = i + 1 ∧ + ∀ j : Fin 400, j ≠ Reg.index → j ≠ Reg.address → j ≠ Reg.answer → + y.regs j = x.regs j := by + let y := if x.regs Reg.found = 0 then + (x.set Reg.address (out + i) |>.set Reg.answer 0 |>.set Reg.answer 0) + |>.set Reg.index (i + 1) + else (x.set Reg.answer (x.regs Reg.answer)).set Reg.index (i + 1) + have ha : Fits W (out + i) := hfit.of_le (by omega) + have ht : Fits W (i + 1) := hfit.of_le (by omega) + have he : ExecWithin W step x y 6 := by + by_cases hf : x.regs Reg.found = 0 + · have hz : x.mem (out + i) = 0 := hskip.resolve_left (not_not_intro hf) + ram_eval [step, y, hf, hz, hi, hx.output, hx.zero, hx.one, ha, ht, + Reg.found, Reg.zero, Reg.address, Reg.output, Reg.index, Reg.answer, Reg.one] + · ram_eval [step, y, hf, hi, hx.zero, hx.one, ht, + Reg.found, Reg.zero, Reg.answer, Reg.index, Reg.one] + have frame (j : Fin 400) (hj : j ≠ Reg.index) (ha : j ≠ Reg.address) + (hb : j ≠ Reg.answer) : y.regs j = x.regs j := by + by_cases hf : x.regs Reg.found = 0 <;> simp [y, hf, hj, ha, hb] + have yFixed : Fixed n first length out queries count y := hx.of_frame (by + intro j hj + have hindex : j ≠ Reg.index := by + intro he; subst j; norm_num [Reg.index, Reg.one, Reg.zero] at hj + have haddr : j ≠ Reg.address := by + intro he; subst j; norm_num [Reg.address, Reg.one, Reg.zero] at hj + have hans : j ≠ Reg.answer := by + intro he; subst j; norm_num [Reg.answer, Reg.one, Reg.zero] at hj + exact frame j hindex haddr hans) + refine ⟨y, he, yFixed, ?_, ?_, frame⟩ + · by_cases hf : x.regs Reg.found = 0 <;> simp [y, hf] + · by_cases hf : x.regs Reg.found = 0 <;> simp [y, hf] + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Trace.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Trace.lean new file mode 100755 index 000000000..61db4c42f --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Trace.lean @@ -0,0 +1,62 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Data.Nat.Count +public import Mathlib.Data.List.Range + +/-! +# The actual witness controller's short-circuit scan trace + +`foundBefore` specifies the cached native flag after an input prefix. It is not a witness +oracle or an extra runtime computation: the execution invariant derives it from the actual +loads and verifier calls. Scan and failure counts use Mathlib's `Nat.count` directly. + +The argument follows the scan-accounting portion of pinned `Witnesses/ScanPairs.lean`, +`anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. This controller starts +with a false flag; the source's optional already-successful incoming flag is not needed. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +/-- Pure value of the cached flag: an earlier accepted pair has a genuine original witness. -/ +@[expose] def foundBefore (accept hit : ℕ → Bool) (i : ℕ) : Bool := + (List.range i).any fun j => accept j && hit j + +/-- Before reading any answer, the native success flag is false. -/ +theorem foundBefore_zero (accept hit : ℕ → Bool) : foundBefore accept hit 0 = false := rfl + +/-- One actual query extends the previous success flag by its own accepted witness result. -/ +theorem foundBefore_succ (accept hit : ℕ → Bool) (i : ℕ) : + foundBefore accept hit (i + 1) = (foundBefore accept hit i || (accept i && hit i)) := by + simp [foundBefore, List.range_succ] + +/-- The cached success flag records an actually accepted earlier query. -/ +theorem foundBefore_eq_true (accept hit : ℕ → Bool) (i : ℕ) : + foundBefore accept hit i = true ↔ ∃ j < i, accept j = true ∧ hit j = true := by + simp [foundBefore, List.any_eq_true, List.mem_range] + +/-- Every actual scan except at most the successful one is a failed accepted candidate. +The functions describe original input values; no such function is an algorithm argument. -/ +theorem scan_count_le (accept hit : ℕ → Bool) (Q : ℕ) : + Nat.count (fun i => (accept i && !foundBefore accept hit i) = true) Q ≤ + Nat.count (fun i => (accept i && !hit i) = true) Q + + (foundBefore accept hit Q).toNat := by + induction Q with + | zero => simp [foundBefore] + | succ Q ih => + rw [Nat.count_succ, Nat.count_succ, foundBefore_succ] + rcases ha : accept Q <;> rcases hh : hit Q <;> rcases hf : foundBefore accept hit Q <;> + simp_all only [Bool.not_false, Bool.not_true, Bool.and_false, Bool.and_true, + Bool.or_false, Bool.or_true, Bool.toNat_false, Bool.toNat_true, + Bool.false_eq_true, ↓reduceIte] <;> omega + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Cell.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Cell.lean new file mode 100755 index 000000000..8936a7c1f --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Cell.lean @@ -0,0 +1,74 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Program + +/-! +# Charged original-weight zero-sum test + +The internal cell rule composes the actual two loads, their address calculations and both +existing signed additions. Its result is the exact sum of the three original weights; no +modular product or candidate certificate can make a nonzero original sum pass this test. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.WitnessScan + +open Cslib.WordRAM + +private theorem loads_preserve : ∀ i : Fin 32, 9 ≤ i.val → i ≠ Reg.address → + loadBC.Preserves i ∧ loadAC.Preserves i := by decide + +/-- Actual signed verification of one original candidate vertex, with every address and +arithmetic instruction charged. The first partial sum must fit the next kernel's signed +source range; the complete initialized caller derives this from the original weight bound. -/ +theorem exec_testVertex {w rb ra c : ℕ} {ab bc ac : ℤ} (x : Data 32) + (hrowB : x.regs Reg.rowB = rb) (hrowA : x.regs Reg.rowA = ra) + (hindex : x.regs Reg.index = c) (hwab : x.regs Reg.wab = Signed.encode ab) + (hBC : x.mem (rb + c) = Signed.encode bc) + (hAC : x.mem (ra + c) = Signed.encode ac) + (haddr : Fits (w + 2) (rb + ra + c + 1)) + (hab : GenericRAM.Signed.Fits w ab) (hbc : GenericRAM.Signed.Fits w bc) + (hac : GenericRAM.Signed.Fits w ac) (hpartial : GenericRAM.Signed.Fits w (ab + bc)) : + ∃ y, ExecWithin (w + 2) testVertex x y 36 ∧ + y.regs Signed.result = Signed.encode (ab + bc + ac) ∧ y.mem = x.mem ∧ + ∀ i : Fin 32, 9 ≤ i.val → i ≠ Reg.address → y.regs i = x.regs i := by + let a := x.set Signed.left (Signed.encode ab) |>.set Reg.address (rb + c) + |>.set Signed.right (Signed.encode bc) + have hloadB : ExecWithin (w + 2) loadBC x a 3 := by + have hf : Fits (w + 2) (rb + c) := haddr.of_le (by omega) + ram_eval [loadBC, a, hrowB, hindex, hwab, hBC, hf, + Reg.address, Reg.rowB, Reg.index, Reg.wab, Signed.left, Signed.right, Fin.mk.injEq] + obtain ⟨b, hsumB, bsum, bframe⟩ := Signed.exec_add hab hbc a + (by simp +decide [a, Signed.left, Signed.right, Reg.address]) + (by simp +decide [a, Signed.left, Signed.right, Reg.address]) + change b.regs Signed.result = Signed.encode (ab + bc) at bsum + have bmeta (i : Fin 32) (hi : 9 ≤ i.val) (hne : i ≠ Reg.address) : + b.regs i = x.regs i := + (bframe.2 i (Or.inr hi)).trans (hloadB.regs_eq (loads_preserve i hi hne).1) + have bra : b.regs Reg.rowA = ra := (bmeta _ (by decide) (by decide)).trans hrowA + have bindex : b.regs Reg.index = c := (bmeta _ (by decide) (by decide)).trans hindex + have bmem : b.mem = x.mem := bframe.1 + let d := b.set Signed.left (Signed.encode (ab + bc)) |>.set Reg.address (ra + c) + |>.set Signed.right (Signed.encode ac) + have hloadA : ExecWithin (w + 2) loadAC b d 3 := by + have hf : Fits (w + 2) (ra + c) := haddr.of_le (by omega) + ram_eval [loadAC, d, bra, bindex, bsum, bmem, hAC, hf, + Reg.address, Reg.rowA, Reg.index, Signed.left, Signed.right, Signed.result, Fin.mk.injEq] + obtain ⟨y, hsumA, ysum, yframe⟩ := Signed.exec_add hpartial hac d + (by simp +decide [d, Signed.left, Signed.right, Reg.address]) + (by simp +decide [d, Signed.left, Signed.right, Reg.address]) + refine ⟨y, hloadB.seq (hsumB.seq (hloadA.seq hsumA)), ysum, ?_, ?_⟩ + · exact yframe.1.trans bmem + · intro i hi hne + exact (yframe.2 i (Or.inr hi)).trans + ((hloadA.regs_eq (loads_preserve i hi hne).2).trans (bmeta i hi hne)) + +end Cslib.Algorithms.WordRAM.ExactTriangle.WitnessScan diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Exec.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Exec.lean new file mode 100755 index 000000000..dbc618745 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Exec.lean @@ -0,0 +1,231 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Cell +public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Setup +public import Complexity.Computability.GenericRAM.Signed.Fits + +/-! +# Complete native original-weight interval verification + +Every interval vertex is tested by actual charged code. The returned flag characterizes +existence of an original zero sum, and a successful run returns an original-coordinate witness. +Arguments and all memory are preserved. The magnitude and address bounds here are numerical +component premises; the producer/product consumer derives them from its original input. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.WitnessScan + +open Cslib.WordRAM + +private theorem args_of_eq (A : Args) {x y : Data 32} (hx : A.InRegisters x) + (h : ∀ i : Fin 32, 9 ≤ i.val → i.val < 17 → y.regs i = x.regs i) : + A.InRegisters y := by + simpa only [Args.InRegisters, h Reg.ab (by decide) (by decide), + h Reg.bc (by decide) (by decide), h Reg.ac (by decide) (by decide), + h Reg.size (by decide) (by decide), h Reg.a (by decide) (by decide), + h Reg.b (by decide) (by decide), h Reg.first (by decide) (by decide), + h Reg.length (by decide) (by decide)] using hx + +private theorem finish_preserves : ∀ i : Fin 32, + (9 ≤ i.val ∧ i.val < 20) ∨ i = Reg.one ∨ i = Reg.zero → + finishVertex.Preserves i := by decide + +/-- Read and verify the original signed arrays, preserving the argument bank and all memory. +The bound excludes only a final halt. The success flag is raw unsigned 0/1; the witness is an +original vertex, not a middle-label or modular candidate. -/ +theorem exec {w U : ℕ} (A : Args) (AB BC AC : ℕ → ℕ → ℤ) (x : Data 32) + (hx : A.InRegisters x) (ha : A.a < A.n) (hb : A.b < A.n) + (hc : A.first + A.length ≤ A.n) + (hmem : ∀ i < A.n, ∀ j < A.n, + x.mem (A.ab + (i * A.n + j)) = Signed.encode (AB i j) ∧ + x.mem (A.bc + (i * A.n + j)) = Signed.encode (BC i j) ∧ + x.mem (A.ac + (i * A.n + j)) = Signed.encode (AC i j)) + (hmag : ∀ i < A.n, ∀ j < A.n, + (AB i j).natAbs ≤ U ∧ (BC i j).natAbs ≤ U ∧ (AC i j).natAbs ≤ U) + (hU : GenericRAM.Signed.Fits w (2 * U : ℕ)) + (hfit : Fits (w + 2) A.addressBound) : + ∃ y, ExecWithin (w + 2) program x y (42 * A.length + 17) ∧ + A.InRegisters y ∧ y.mem = x.mem ∧ (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ + (y.regs Reg.found = 1 ↔ ∃ c, A.first ≤ c ∧ c < A.first + A.length ∧ + AB A.a A.b + BC A.b c + AC A.a c = 0) ∧ + (y.regs Reg.found = 1 → A.first ≤ y.regs Reg.witness ∧ + y.regs Reg.witness < A.first + A.length ∧ + AB A.a A.b + BC A.b (y.regs Reg.witness) + AC A.a (y.regs Reg.witness) = 0) := by + let total (c : ℕ) := AB A.a A.b + BC A.b c + AC A.a c + obtain ⟨s, hs, sargs, smem, sone, szero, sfound, sindex, srowB, srowA, swab⟩ := + exec_setup A x (AB A.a A.b) hx ha hb hc (hmem A.a ha A.b hb).1 hfit + let I (y : Data 32) : Prop := A.InRegisters y ∧ y.mem = x.mem ∧ + y.regs Reg.one = 1 ∧ y.regs Reg.zero = 0 ∧ + y.regs Reg.rowB = A.bc + A.b * A.n + A.first ∧ + y.regs Reg.rowA = A.ac + A.a * A.n + A.first ∧ + y.regs Reg.wab = Signed.encode (AB A.a A.b) ∧ y.regs Reg.index ≤ A.length ∧ + (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ + (y.regs Reg.found = 1 ↔ ∃ j < y.regs Reg.index, total (A.first + j) = 0) ∧ + (y.regs Reg.found = 1 → A.first ≤ y.regs Reg.witness ∧ + y.regs Reg.witness < A.first + y.regs Reg.index ∧ total (y.regs Reg.witness) = 0) + have hstart : I s := by + refine ⟨sargs, smem, sone, szero, srowB, srowA, swab, by omega, + Or.inl sfound, ?_, ?_⟩ + · constructor + · intro h; omega + · rintro ⟨j, hj, -⟩; omega + · intro h; omega + have hrowB : A.b * A.n + A.first ≤ A.n * A.n := by + have hstep := Nat.mul_le_mul_right A.n (Nat.succ_le_of_lt hb) + rw [Nat.succ_mul] at hstep + omega + have hrowA : A.a * A.n + A.first ≤ A.n * A.n := by + have hstep := Nat.mul_le_mul_right A.n (Nat.succ_le_of_lt ha) + rw [Nat.succ_mul] at hstep + omega + have hbody (y : Data 32) (hy : I y) (hc' : (Cond.lt Reg.index Reg.length).Holds y) : + ∃ z, ExecWithin (w + 2) step y z 40 ∧ I z ∧ + A.length - z.regs Reg.index < A.length - y.regs Reg.index := by + obtain ⟨yargs, ymem, yone, yzero, yrowB, yrowA, ywab, yindex, yflag, yiff, ywit⟩ := hy + have yl : y.regs Reg.length = A.length := yargs.2.2.2.2.2.2.2 + have hi : y.regs Reg.index < A.length := by + simpa only [Cond.holds_lt, yl] using hc' + let i := y.regs Reg.index + have hcurrent : A.first + i < A.n := by dsimp only [i]; omega + have hreadB : y.mem (A.bc + A.b * A.n + A.first + i) = + Signed.encode (BC A.b (A.first + i)) := by + rw [ymem] + simpa only [Nat.add_assoc] using (hmem A.b hb (A.first + i) hcurrent).2.1 + have hreadA : y.mem (A.ac + A.a * A.n + A.first + i) = + Signed.encode (AC A.a (A.first + i)) := by + rw [ymem] + simpa only [Nat.add_assoc] using (hmem A.a ha (A.first + i) hcurrent).2.2 + have habmag := (hmag A.a ha A.b hb).1 + have hbcmag := (hmag A.b hb (A.first + i) hcurrent).2.1 + have hacmag := (hmag A.a ha (A.first + i) hcurrent).2.2 + have habfit := hU.of_natAbs_le (habmag.trans (by omega : U ≤ 2 * U)) + have hbcfit := hU.of_natAbs_le (hbcmag.trans (by omega : U ≤ 2 * U)) + have hacfit := hU.of_natAbs_le (hacmag.trans (by omega : U ≤ 2 * U)) + have hsumMag : (AB A.a A.b + BC A.b (A.first + i)).natAbs ≤ 2 * U := by + have := Int.natAbs_add_le (AB A.a A.b) (BC A.b (A.first + i)) + omega + have hsumfit := hU.of_natAbs_le hsumMag + obtain ⟨b, hbtest, bsum, bmem, bmeta⟩ := exec_testVertex y yrowB yrowA rfl ywab + hreadB hreadA (hfit.of_le (by dsimp only [Args.addressBound]; omega)) + habfit hbcfit hacfit hsumfit + change b.regs Signed.result = Signed.encode (total (A.first + i)) at bsum + have bone : b.regs Reg.one = 1 := (bmeta _ (by decide) (by decide)).trans yone + have bzero : b.regs Reg.zero = 0 := (bmeta _ (by decide) (by decide)).trans yzero + have bfirst : b.regs Reg.first = A.first := + (bmeta _ (by decide) (by decide)).trans yargs.2.2.2.2.2.2.1 + have bindex : b.regs Reg.index = i := bmeta _ (by decide) (by decide) + have bfound : b.regs Reg.found = y.regs Reg.found := bmeta _ (by decide) (by decide) + have bwit : b.regs Reg.witness = y.regs Reg.witness := bmeta _ (by decide) (by decide) + let z := (if total (A.first + i) = 0 then + b.set Reg.found 1 |>.set Reg.witness (A.first + i) + else b.set Reg.witness (b.regs Reg.witness)).set Reg.index (i + 1) + have hfinish : ExecWithin (w + 2) finishVertex b z 4 := by + have hinc : Fits (w + 2) (i + 1) := hfit.of_le (by + dsimp only [Args.addressBound]; dsimp only [i]; omega) + have hpos : Fits (w + 2) (A.first + i) := hfit.of_le (by + dsimp only [Args.addressBound]; omega) + by_cases hz : total (A.first + i) = 0 + · have hresult : b.regs Signed.result = 0 := by rw [bsum]; rw [hz]; rfl + simp only [z, ite_eq_left hz] + ram_eval [finishVertex, hresult, bone, bzero, bfirst, bindex, hinc, hpos, + Reg.found, Reg.one, Reg.zero, Reg.witness, Reg.index, Reg.first, + Signed.result, Fin.mk.injEq] + · have hresult : b.regs Signed.result ≠ 0 := by + intro he + apply hz + apply Signed.encode_injective + exact bsum.symm.trans he + simp only [z, ite_eq_right hz] + ram_eval [finishVertex, hresult, bone, bzero, bfirst, bindex, hinc, + Reg.found, Reg.one, Reg.zero, Reg.witness, Reg.index, Reg.first, + Signed.result, Fin.mk.injEq] + have zindex : z.regs Reg.index = i + 1 := by simp [z] + have zflag : z.regs Reg.found = if total (A.first + i) = 0 then 1 else y.regs Reg.found := by + by_cases hz : total (A.first + i) = 0 <;> + simp +decide [z, hz, Reg.index, Reg.witness, Reg.found, bfound] + have zwit : z.regs Reg.witness = + if total (A.first + i) = 0 then A.first + i else y.regs Reg.witness := by + by_cases hz : total (A.first + i) = 0 <;> + simp +decide [z, hz, Reg.index, Reg.witness, bwit] + have keep (j : Fin 32) + (hj : (9 ≤ j.val ∧ j.val < 20) ∨ j = Reg.one ∨ j = Reg.zero) : + z.regs j = y.regs j := by + have hj9 : 9 ≤ j.val := by + rcases hj with h | rfl | rfl + · exact h.1 + · decide + · decide + have hjaddr : j ≠ Reg.address := by + rcases hj with h | rfl | rfl + · intro he + have hv := congrArg Fin.val he + change j.val = 24 at hv + omega + · decide + · decide + exact (hfinish.regs_eq (finish_preserves j hj)).trans (bmeta j hj9 hjaddr) + have zmem : z.mem = b.mem := by + by_cases hz : total (A.first + i) = 0 <;> simp [z, hz] + refine ⟨z, hbtest.seq hfinish, ?_, ?_⟩ + · refine ⟨args_of_eq A yargs (fun j hj hj' => keep j (Or.inl ⟨hj, by omega⟩)), + zmem.trans (bmem.trans ymem), (keep _ (Or.inr (Or.inl rfl))).trans yone, + (keep _ (Or.inr (Or.inr rfl))).trans yzero, + (keep _ (Or.inl ⟨by decide, by decide⟩)).trans yrowB, + (keep _ (Or.inl ⟨by decide, by decide⟩)).trans yrowA, + (keep _ (Or.inl ⟨by decide, by decide⟩)).trans ywab, + by rw [zindex]; dsimp only [i]; omega, ?_, ?_, ?_⟩ + · rw [zflag] + split_ifs <;> omega + · rw [zflag, zindex] + by_cases hz : total (A.first + i) = 0 + · rw [ite_eq_left hz] + exact ⟨fun _ => ⟨i, by omega, hz⟩, fun _ => rfl⟩ + · rw [ite_eq_right hz, yiff] + constructor + · rintro ⟨j, hj, hzero⟩ + exact ⟨j, by dsimp only [i]; omega, hzero⟩ + · rintro ⟨j, hj, hzero⟩ + refine ⟨j, ?_, hzero⟩ + have hne : j ≠ i := by intro he; subst j; exact hz hzero + dsimp only [i] at * + omega + · intro hfound + by_cases hz : total (A.first + i) = 0 + · rw [zwit, ite_eq_left hz, zindex] + exact ⟨by omega, by omega, hz⟩ + · rw [zflag, ite_eq_right hz] at hfound + obtain ⟨hlo, hhi, hzero⟩ := ywit hfound + rw [zwit, ite_eq_right hz, zindex] + exact ⟨hlo, by dsimp only [i]; omega, hzero⟩ + · rw [zindex] + dsimp only [i] + omega + obtain ⟨y, hloop, hy, hdone⟩ := ExecWithin.loop_invariant I + (fun y => A.length - y.regs Reg.index) hbody hstart + obtain ⟨yargs, ymem, yone, yzero, yrowB, yrowA, ywab, yindex, yflag, yiff, ywit⟩ := hy + have ylen : y.regs Reg.length = A.length := yargs.2.2.2.2.2.2.2 + have yend : y.regs Reg.index = A.length := by + simp only [Cond.holds_lt, ylen] at hdone + omega + refine ⟨y, (hs.seq hloop).mono (by rw [sindex]; omega), yargs, ymem, yflag, ?_, ?_⟩ + · constructor + · intro hfound + obtain ⟨j, hj, hz⟩ := yiff.mp hfound + exact ⟨A.first + j, by omega, by rw [yend] at hj; omega, hz⟩ + · rintro ⟨c, hlo, hhi, hz⟩ + apply yiff.mpr + refine ⟨c - A.first, by rw [yend]; omega, ?_⟩ + simpa only [Nat.add_sub_of_le hlo] using hz + · intro hfound + simpa only [yend] using ywit hfound + +end Cslib.Algorithms.WordRAM.ExactTriangle.WitnessScan diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Program.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Program.lean new file mode 100755 index 000000000..070424f25 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Program.lean @@ -0,0 +1,139 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.WordRAM.Signed.Arithmetic + +/-! +# Native original-weight verification in one vertex interval + +The program reads the original canonical-signed A–B, B–C and A–C arrays, tests each vertex +in the supplied interval using two actual signed-addition kernels, and returns a flag and +an original vertex witnessing a zero sum. It writes no memory and preserves all arguments. +This is the inner verifier used after a nonzero product answer, not a product or ET oracle. + +The interval scan follows the pinned `Witnesses/Scan.lean` procedure in the proof of Theorem 17 +at `anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. The native code, +register layout and charged constants are rebuilt against the existing WordRAM APIs. +The stage definitions are internal implementation interfaces. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.WitnessScan + +open Cslib.WordRAM + +namespace Reg + +/-- Original canonical-signed A–B array pointer. -/ +abbrev ab : Fin 32 := 9 +/-- Original canonical-signed B–C array pointer. -/ +abbrev bc : Fin 32 := 10 +/-- Original canonical-signed A–C array pointer. -/ +abbrev ac : Fin 32 := 11 +/-- Outer matrix dimension. -/ +abbrev size : Fin 32 := 12 +/-- Original first query coordinate. -/ +abbrev a : Fin 32 := 13 +/-- Original second query coordinate. -/ +abbrev b : Fin 32 := 14 +/-- First original vertex of the scanned interval. -/ +abbrev first : Fin 32 := 15 +/-- Number of vertices in the interval. -/ +abbrev length : Fin 32 := 16 +/-- B–C row pointer at the first interval vertex. -/ +abbrev rowB : Fin 32 := 17 +/-- A–C row pointer at the first interval vertex. -/ +abbrev rowA : Fin 32 := 18 +/-- Cached canonical-signed A–B weight. -/ +abbrev wab : Fin 32 := 19 +/-- Number of interval vertices already tested. -/ +abbrev index : Fin 32 := 20 +/-- Unsigned success flag. -/ +abbrev found : Fin 32 := 21 +/-- Original vertex of the most recently verified zero sum. -/ +abbrev witness : Fin 32 := 22 +/-- Constant one. -/ +abbrev one : Fin 32 := 23 +/-- Actual load-address temporary. -/ +abbrev address : Fin 32 := 24 +/-- Constant zero, preserved by the signed arithmetic bank. -/ +abbrev zero : Fin 32 := 25 + +end Reg + +/-- Ordinary runtime arguments of the original-weight interval verifier. This stores no +execution, fit, witness or time certificate. -/ +structure Args where + /-- Original canonical-signed A–B array base. -/ + ab : ℕ + /-- Original canonical-signed B–C array base. -/ + bc : ℕ + /-- Original canonical-signed A–C array base. -/ + ac : ℕ + /-- Matrix dimension. -/ + n : ℕ + /-- First query coordinate. -/ + a : ℕ + /-- Second query coordinate. -/ + b : ℕ + /-- First interval vertex. -/ + first : ℕ + /-- Number of interval vertices. -/ + length : ℕ + +/-- The eight scalar arguments occupy their actual native registers. -/ +@[expose] def Args.InRegisters (A : Args) (x : Data 32) : Prop := + x.regs Reg.ab = A.ab ∧ x.regs Reg.bc = A.bc ∧ x.regs Reg.ac = A.ac ∧ + x.regs Reg.size = A.n ∧ x.regs Reg.a = A.a ∧ x.regs Reg.b = A.b ∧ + x.regs Reg.first = A.first ∧ x.regs Reg.length = A.length + +/-- A concrete numerical envelope for all address/index arithmetic in this verifier. -/ +@[expose] def Args.addressBound (A : Args) : ℕ := + A.ab + A.bc + A.ac + 3 * (A.n * A.n) + A.n + A.first + A.length + 8 + +open Reg in +/-- Load the fixed A–B weight and compute the two row pointers with actual instructions. -/ +@[expose] def setup : Code .standard 32 := ram[ + one ← #1; zero ← #0; found ← #0; index ← #0; witness ← first; + address ← a * size; address ← address + b; address ← address + ab; wab ← *address; + rowB ← b * size; rowB ← rowB + first; rowB ← rowB + bc; + rowA ← a * size; rowA ← rowA + first; rowA ← rowA + ac ] + +open Reg in +/-- First two original signed weights, read from the original memory. -/ +@[expose] def loadBC : Code .standard 32 := ram[ + Signed.left ← wab; address ← rowB + index; Signed.right ← *address ] + +open Reg in +/-- The true partial sum and the original A–C weight. -/ +@[expose] def loadAC : Code .standard 32 := ram[ + Signed.left ← Signed.result; address ← rowA + index; Signed.right ← *address ] + +/-- Two charged standard-native signed additions, with no extra machine operation. -/ +@[expose] def testVertex : Code .standard 32 := + .seq loadBC (.seq Signed.add (.seq loadAC Signed.add)) + +open Reg in +/-- Remember a genuine zero-sum vertex, then advance the interval counter. -/ +@[expose] def finishVertex : Code .standard 32 := ram[ + if Signed.result = zero then [ found ← one; witness ← first + index ] + else [ witness ← witness ]; index ← index + one ] + +/-- One complete verification step. -/ +@[expose] def step : Code .standard 32 := .seq testVertex finishVertex + +/-- Fixed native interval verifier; only the actual loop count depends on input. -/ +@[expose] def program : Code .standard 32 := + .seq setup (.loop (.lt Reg.index Reg.length) step) + +/-- Fixed instruction graph, independent of dimensions and physical width. -/ +theorem program_fixed : program.size ≤ 160 ∧ ¬program.ReadsWidth := by decide + +end Cslib.Algorithms.WordRAM.ExactTriangle.WitnessScan diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Setup.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Setup.lean new file mode 100755 index 000000000..fde58d47c --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Setup.lean @@ -0,0 +1,87 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Program + +/-! +# Native interval-verifier initialization + +The actual header caches one original weight and computes two original row pointers. Its +numeric address bound covers every intermediate product/sum, including empty intervals at +the one-past-end boundary. These are internal rules for composing the verifier loop. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.WitnessScan + +open Cslib.WordRAM + +/-- Initialize the actual verifier in fifteen instructions, preserving the original arguments +and memory. In particular the weight is loaded from the original A–B array, not supplied as +an accepted modular candidate. -/ +theorem exec_setup {W : ℕ} (A : Args) (x : Data 32) (v : ℤ) + (hx : A.InRegisters x) (ha : A.a < A.n) (hb : A.b < A.n) + (hc : A.first + A.length ≤ A.n) + (hread : x.mem (A.ab + (A.a * A.n + A.b)) = Signed.encode v) + (hfit : Fits W A.addressBound) : + ∃ y, ExecWithin W setup x y 15 ∧ A.InRegisters y ∧ y.mem = x.mem ∧ + y.regs Reg.one = 1 ∧ y.regs Reg.zero = 0 ∧ y.regs Reg.found = 0 ∧ + y.regs Reg.index = 0 ∧ y.regs Reg.rowB = A.bc + A.b * A.n + A.first ∧ + y.regs Reg.rowA = A.ac + A.a * A.n + A.first ∧ + y.regs Reg.wab = Signed.encode v := by + obtain ⟨hab, hbc, hac, hn, ha', hb', hf, hl⟩ := hx + have hstepA : A.a * A.n + A.n ≤ A.n * A.n := by + simpa only [Nat.succ_mul] using Nat.mul_le_mul_right A.n (Nat.succ_le_of_lt ha) + have hstepB : A.b * A.n + A.n ≤ A.n * A.n := by + simpa only [Nat.succ_mul] using Nat.mul_le_mul_right A.n (Nat.succ_le_of_lt hb) + have hiab : A.a * A.n + A.b < A.n * A.n := by omega + have hrowB : A.b * A.n + A.first ≤ A.n * A.n := by omega + have hrowA : A.a * A.n + A.first ≤ A.n * A.n := by omega + have hmulA : A.a * A.n ≤ A.n * A.n := Nat.mul_le_mul_right _ ha.le + have hmulB : A.b * A.n ≤ A.n * A.n := Nat.mul_le_mul_right _ hb.le + have h1 : Fits W 1 := hfit.of_le (by unfold Args.addressBound; omega) + have hfirst : Fits W A.first := hfit.of_le (by unfold Args.addressBound; omega) + have hmA : Fits W (A.a * A.n) := hfit.of_le (by unfold Args.addressBound; omega) + have hmB : Fits W (A.b * A.n) := hfit.of_le (by unfold Args.addressBound; omega) + have hpair : Fits W (A.a * A.n + A.b) := hfit.of_le (by unfold Args.addressBound; omega) + have hpaddr : Fits W (A.a * A.n + A.b + A.ab) := + hfit.of_le (by unfold Args.addressBound; omega) + have hrB : Fits W (A.b * A.n + A.first) := + hfit.of_le (by unfold Args.addressBound; omega) + have hrA : Fits W (A.a * A.n + A.first) := + hfit.of_le (by unfold Args.addressBound; omega) + have hpB : Fits W (A.b * A.n + A.first + A.bc) := + hfit.of_le (by unfold Args.addressBound; omega) + have hpA : Fits W (A.a * A.n + A.first + A.ac) := + hfit.of_le (by unfold Args.addressBound; omega) + have hread' : x.mem (A.a * A.n + A.b + A.ab) = Signed.encode v := by + simpa only [Nat.add_comm] using hread + let y := x.set Reg.one 1 |>.set Reg.zero 0 |>.set Reg.found 0 |>.set Reg.index 0 + |>.set Reg.witness A.first |>.set Reg.address (A.a * A.n) + |>.set Reg.address (A.a * A.n + A.b) |>.set Reg.address (A.a * A.n + A.b + A.ab) + |>.set Reg.wab (Signed.encode v) |>.set Reg.rowB (A.b * A.n) + |>.set Reg.rowB (A.b * A.n + A.first) |>.set Reg.rowB (A.b * A.n + A.first + A.bc) + |>.set Reg.rowA (A.a * A.n) |>.set Reg.rowA (A.a * A.n + A.first) + |>.set Reg.rowA (A.a * A.n + A.first + A.ac) + have he : ExecWithin W setup x y 15 := by + ram_eval [setup, y, hab, hbc, hac, hn, ha', hb', hf, hl, hread', + h1, hfirst, hmA, hmB, hpair, hpaddr, hrA, hrB, hpA, hpB, + Reg.one, Reg.zero, Reg.found, Reg.index, Reg.witness, Reg.address, Reg.ab, Reg.bc, + Reg.ac, Reg.a, Reg.b, Reg.size, Reg.first, Reg.rowA, Reg.rowB, Reg.wab, Reg.length, + Fin.mk.injEq] + refine ⟨y, he, ?_, rfl, ?_⟩ + · simpa +decide [Args.InRegisters, y, Reg.one, Reg.zero, Reg.found, Reg.index, Reg.witness, + Reg.address, Reg.ab, Reg.bc, Reg.ac, Reg.a, Reg.b, Reg.size, Reg.first, Reg.rowA, + Reg.rowB, Reg.wab, Reg.length] using + (show Args.InRegisters A x from ⟨hab, hbc, hac, hn, ha', hb', hf, hl⟩) + · simp +decide [y, Reg.one, Reg.zero, Reg.found, Reg.index, Reg.witness, Reg.address, + Reg.rowA, Reg.rowB, Reg.wab, Nat.add_comm, Nat.add_left_comm] + +end Cslib.Algorithms.WordRAM.ExactTriangle.WitnessScan diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/WriteMatrices.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/WriteMatrices.lean index 6ee0235e5..6425557ce 100644 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/WriteMatrices.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/WriteMatrices.lean @@ -77,6 +77,11 @@ def entry (rightSide : Bool) (A : Args) (R : ℕ → ℕ) (q : ℕ) : ℕ := if label = wanted then 1 else 0 else 0 +/-- Every raw adjacency entry is a zero/one word, including padding. -/ +theorem entry_le_one (side : Bool) (A : Args) (R : ℕ → ℕ) (q : ℕ) : + entry side A R q ≤ 1 := by + cases side <;> dsimp only [entry] <;> split_ifs <;> omega + /-- Residue input pointer. -/ abbrev src : Fin 17 := 0 /-- Output matrix pointer. -/ diff --git a/ComplexityTests/WordRAMExactTriangleQueryValues.lean b/ComplexityTests/WordRAMExactTriangleQueryValues.lean new file mode 100755 index 000000000..3f436d077 --- /dev/null +++ b/ComplexityTests/WordRAMExactTriangleQueryValues.lean @@ -0,0 +1,86 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +-- upstream: local-only (tests) + +import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.QueryValues +import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationInput + +/-! +# Original pairs from an actual producer query block + +The small concrete producer instance tests the readback API independently of thin-product +geometry. The kernel client starts with the original scalar headers and twelve zero weights; +neither a prepared query list nor a layout certificate is supplied as input. +-/ + +set_option linter.hashCommand false + +namespace ComplexityTests.WordRAMExactTriangleQueryValues + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.WordRAM.ExactTriangle + +private def values : List ℤ := List.replicate 12 0 +private def zeroAB : Fin 2 → Fin 2 → ℤ := fun _ _ => 0 + +-- The actual producer writes all four original coordinate pairs in row-major rank order. +#guard (Instances.program.eval 256 64 (Data.init (PreparationInput.input 2 1 1 2 values))).map + (fun (y, _) => (List.range 8).map (fun j => y.mem (31 + j))) == + some [0, 0, 0, 1, 1, 0, 1, 1] + +/-- Fully admitted initialized producer execution, connected to the new query-value API. +The original pairs occur once each and their order is the existing finite class rank. -/ +example : ∃ T : ℕ, ∃ y : Data 64, + ExecWithin 256 Instances.program (Data.init (PreparationInput.input 2 1 1 2 values)) y T ∧ + let qs := RecordProduct.decodedQueries (by decide : 0 < 2) y.mem 31 4 + qs.Nodup ∧ qs.toFinset = Finset.univ ∧ + ∀ i : Fin qs.length, rankIn (Finset.univ : Finset (Fin 2 × Fin 2)) (qs.get i) = i.val := by + obtain ⟨y, he, hout, -, -⟩ := PreparationInput.exec_input + (n := 2) (D := 1) (g := 1) (p := 2) (A := 0) (W := 256) values + (by decide) (by decide) (by decide) (by decide) (by decide) + (by intro z hz; exact le_of_eq (congrArg Int.natAbs (List.eq_of_mem_replicate hz))) + (by decide) + have hAB : ∀ a b, values.getD (pairIndex (a, b)) 0 = zeroAB a b := by + intro a b + change (List.replicate 12 (0 : ℤ))[pairIndex (a, b)]?.getD 0 = 0 + exact List.getElem?_getD_replicate_default_eq _ _ _ + have hcap : queryCap 2 1 = 4 := by + have hl : 4 ≤ Nat.sqrt 16 := Nat.le_sqrt.mpr (by decide) + have hu : Nat.sqrt 16 < 5 := Nat.sqrt_lt.mpr (by decide) + change Nat.sqrt 16 = 4 + omega + have hclass : residueClass zeroAB 2 0 = Finset.univ := by decide + have hk : 0 < numChunks (residueClass zeroAB 2 0) (queryCap 2 1) := by + rw [hcap, hclass] + decide + obtain ⟨hget, hnodup, hset⟩ := RecordProduct.decodedQueries_chunkOf + (fun q => values.getD q 0) zeroAB (by decide) (by decide) hAB + (by decide) (by decide) hout 0 0 hk + have hchunk : chunkOf zeroAB 1 2 0 0 = Finset.univ := by + rw [chunkOf, chunk, hclass, hcap] + ext pair + simp only [Finset.mem_filter, Finset.mem_univ, true_and, rankIn_univ] + apply iff_true_intro + apply Nat.div_eq_of_lt + have ha := pair.1.isLt + have hb := pair.2.isLt + dsimp only [pairIndex] + omega + simp_rw [hcap, hclass] at hget + simp only [hcap, hclass] at hnodup hset + simp only [Nat.reduceAdd, Nat.reduceMul, Fin.val_zero, zero_mul, Nat.add_zero, + Nat.sub_zero, Finset.card_univ, Fintype.card_prod, Fintype.card_fin, + min_self] at hget hnodup hset + refine ⟨_, y, he, ?_⟩ + refine ⟨?_, ?_, ?_⟩ + · exact hnodup + · exact hset.trans hchunk + · intro i + simpa [hcap, hclass] using (hget ⟨i.val, by + simpa [RecordProduct.decodedQueries_length, hcap, hclass] using i.isLt⟩).2 + +end ComplexityTests.WordRAMExactTriangleQueryValues diff --git a/ComplexityTests/WordRAMExactTriangleRecordWitness.lean b/ComplexityTests/WordRAMExactTriangleRecordWitness.lean new file mode 100755 index 000000000..e7024522a --- /dev/null +++ b/ComplexityTests/WordRAMExactTriangleRecordWitness.lean @@ -0,0 +1,69 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +-- upstream: local-only (tests) + +import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Consume + +/-! +# Actual candidate-consumer executions at valid thin geometry + +These sparse-memory fixtures execute the full descriptor/root/query/verifier consumer at +`D=16`, `n=16^18`. They supply a post-product memory fixture, not a full producer/product run. +The initialized composition client is separate. No enormous encoder run is evaluated here. +-/ + +set_option linter.hashCommand false + +namespace ComplexityTests.WordRAMExactTriangleRecordWitness + +open Cslib.WordRAM Cslib.Algorithms.WordRAM.ExactTriangle + +private def side : ℕ := 16 ^ 18 +private def queryBase : ℕ := 8 + 6 * (side * side) +private def records : ℕ := 23 + 12 * (side * side) +private def heap : ℕ := records + 6 + 2 * (side * 16) +private def answers : ℕ := heap + 100 + +private def fixture (success zeroAnswers : Bool) : Data 400 := + { regs := fun i => if i.val = 39 then records else if i.val = 42 then heap + else if i.val = 64 then 0 else if i.val = 65 then answers else 77 + mem := fun a => + if a = 1 then 16 else if a = 2 then 1 else if a = 3 then side else if a = 4 then 3 + else if 5 ≤ a && a < 5 + side * side then + if a = 5 + (side - 1) * side + (side - 2) || + a = 5 + (side - 1) * side + (side - 1) then 0 else Signed.encode 1 + else if 5 + side * side ≤ a && a < 5 + 2 * (side * side) then + if success then 0 else Signed.encode 3 + else if records ≤ a && a < records + 6 then + [0, 0, queryBase, 2, records + 6, records + 6 + side * 16].getD (a - records) 0 + else if a = queryBase then side - 1 else if a = queryBase + 1 then side - 2 + else if a = queryBase + 2 then side - 1 else if a = queryBase + 3 then side - 1 + else if a = answers || a = answers + 1 then if zeroAnswers then 0 else Signed.encode 4 + else if a = answers + 2 then 99 else 0 } + +private def observe (success zeroAnswers : Bool) : Option (List ℕ × Bool × Bool) := + (RecordWitness.consume.eval 512 64 (fixture success zeroAnswers)).map fun (y, t) => + ([y.regs RecordWitness.Reg.found, y.regs RecordWitness.Reg.witnessA, + y.regs RecordWitness.Reg.witnessB, y.regs RecordWitness.Reg.witnessC, + y.regs RecordWitness.Reg.scans, y.regs RecordWitness.Reg.index, y.mem (answers + 2)], + decide (t ≤ 20 + 16 + 196 * y.regs RecordWitness.Reg.scans + 52), + decide (∀ j : Fin 320, y.regs (RecordWitness.productBank j) = + (fixture success zeroAnswers).regs (RecordWitness.productBank j))) + +-- The first pair succeeds; the second candidate incurs no further original-weight scan. +#guard observe true false == some ([1, side - 1, side - 2, 3, 1, 2, 99], true, true) + +-- Both accepted modular candidates have original sum three, and both scans fail. +#guard observe false false == some ([0, 0, 0, 0, 2, 2, 99], true, true) + +-- Zero answer words produce no verifier call, even though original weights have zero sums. +#guard observe true true == some ([0, 0, 0, 0, 0, 2, 99], true, true) + +#guard side ≥ 16 ^ 18 +#guard ¬(16 ≥ 16 ^ 18) + +end ComplexityTests.WordRAMExactTriangleRecordWitness diff --git a/ComplexityTests/WordRAMExactTriangleWitnessScan.lean b/ComplexityTests/WordRAMExactTriangleWitnessScan.lean new file mode 100755 index 000000000..9e9472e65 --- /dev/null +++ b/ComplexityTests/WordRAMExactTriangleWitnessScan.lean @@ -0,0 +1,88 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +-- upstream: local-only (tests) + +import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Exec +import Mathlib.Tactic.IntervalCases +import Mathlib.Tactic.NormNum + +/-! +# Signed original-weight verifier executions + +These test the interval verifier on small original arrays; they do not assert the much larger +thin-product geometry. The complete producer/product/recovery client is a separate test. +-/ + +set_option linter.hashCommand false + +namespace ComplexityTests.WordRAMExactTriangleWitnessScan + +open Cslib.WordRAM Cslib.Algorithms.WordRAM.ExactTriangle + +private def weights (last : ℤ) : List ℤ := [2, 5, 7, 8, -1, 3, 0, 0, 4, last, 0, 0] + +private def args (first length : ℕ) : WitnessScan.Args := ⟨1, 5, 9, 2, 0, 0, first, length⟩ + +private def fixture (last : ℤ) (first length : ℕ) : Data 32 := + ((Data.init ((weights last).map Signed.encode)).write 20 99) + |>.set WitnessScan.Reg.ab 1 |>.set WitnessScan.Reg.bc 5 |>.set WitnessScan.Reg.ac 9 + |>.set WitnessScan.Reg.size 2 |>.set WitnessScan.Reg.a 0 |>.set WitnessScan.Reg.b 0 + |>.set WitnessScan.Reg.first first |>.set WitnessScan.Reg.length length + +private def observe (last : ℤ) (first length : ℕ) : Option (ℕ × ℕ × ℕ × Bool) := + (WitnessScan.program.eval 10 8 (fixture last first length)).map fun (y, t) => + (y.regs WitnessScan.Reg.found, y.regs WitnessScan.Reg.witness, y.mem 20, + decide (t ≤ 42 * length + 17)) + +-- c=0 fails (2-1+4=5), then the original signed weights at c=1 give 2+3-5=0. +#guard observe (-5) 0 2 == some (1, 1, 99, true) + +-- A nonzero product candidate cannot turn 2+3+1 into an original zero sum. +#guard observe 1 0 2 == some (0, 0, 99, true) + +-- An empty interval performs no candidate-vertex read and cannot report a witness. +#guard observe (-5) 2 0 == some (0, 2, 99, true) + +-- Restricting the scan to c=0 excludes the later true witness. +#guard observe (-5) 0 1 == some (0, 0, 99, true) + +private def AB (i j : ℕ) : ℤ := (weights (-5)).getD (i * 2 + j) 0 +private def BC (i j : ℕ) : ℤ := (weights (-5)).getD (4 + (i * 2 + j)) 0 +private def AC (i j : ℕ) : ℤ := (weights (-5)).getD (8 + (i * 2 + j)) 0 + +/-- Closed kernel client: every runtime, address, signed-fit and input-array premise is +discharged. The actual successful execution observes the unique original witness c=1. -/ +example : ∃ y, ExecWithin 10 WitnessScan.program (fixture (-5) 0 2) y 101 ∧ + y.regs WitnessScan.Reg.found = 1 ∧ y.regs WitnessScan.Reg.witness = 1 ∧ + y.mem = (fixture (-5) 0 2).mem := by + have hm : ∀ i < 2, ∀ j < 2, + (fixture (-5) 0 2).mem (1 + (i * 2 + j)) = Signed.encode (AB i j) ∧ + (fixture (-5) 0 2).mem (5 + (i * 2 + j)) = Signed.encode (BC i j) ∧ + (fixture (-5) 0 2).mem (9 + (i * 2 + j)) = Signed.encode (AC i j) := by + intro i hi j hj + interval_cases i <;> interval_cases j <;> decide + have hb : ∀ i < 2, ∀ j < 2, + (AB i j).natAbs ≤ 8 ∧ (BC i j).natAbs ≤ 8 ∧ (AC i j).natAbs ≤ 8 := by + intro i hi j hj + interval_cases i <;> interval_cases j <;> decide + obtain ⟨y, he, hy, hmem, hflag, hiff, hwit⟩ := WitnessScan.exec + (w := 8) (U := 8) (args 0 2) AB BC AC (fixture (-5) 0 2) + (by unfold WitnessScan.Args.InRegisters; decide) + (by decide) (by decide) (by decide) hm hb (by decide) (by decide) + have hf : y.regs WitnessScan.Reg.found = 1 := hiff.mpr ⟨1, by decide, by decide, by decide⟩ + obtain ⟨hlo, hhi, hz⟩ := hwit hf + have hc : y.regs WitnessScan.Reg.witness = 1 := by + change 0 ≤ y.regs WitnessScan.Reg.witness at hlo + change y.regs WitnessScan.Reg.witness < 2 at hhi + have hzero : y.regs WitnessScan.Reg.witness ≠ 0 := by + intro h + rw [h] at hz + norm_num [AB, BC, AC, weights, args] at hz + omega + exact ⟨y, he, hf, hc, hmem⟩ + +end ComplexityTests.WordRAMExactTriangleWitnessScan From b461744614c96b7110873e55d8846f598d0dcd97 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 29 Oct 2026 14:40:28 -0400 Subject: [PATCH 63/73] feat(ExactTriangle): compose initialized per-record native witness recovery Complete source checkpoint for #1639, owned by codex-three-sum-continuation-t3-3dda8289 on its ordinary child branch. Not a validated review/publication candidate: full six/static and canonical import generation remain held for the coordinator's explicit audit handoff. One fixed standard-native 400-register program executes the actual original-input producer, charged first-record selection, real queried product and original-weight verification. Flat.runs_input ties actual State.init, prefix Runs, charged halt, returned original coordinates, finite selected chunk/piece completeness, and caller frame to the same final y. The generic caller executes any occupied ordinal. All matrix/query data come from actual producer readback. No run, answer, witness, layout, sparse-count, fit or time certificate is supplied as an algorithm input. The actual scan count satisfies S <= falsePositiveCount + returnedFlag (raw0/1), not an additive one per unsuccessful record. The proof derives native answer semantics, distinct chunk occurrence injection and original-weight failure from existing finite Witnesses/falsePositiveCount. It retains the actual occurrence count and charges all descriptor/root/query/control and original-weight scanning. Prefix cost is preparationTime+recordProductTime+5sqrt(D)+8Q+(42length+28)S+52; Flat.runs_input adds the final halt. The product's raw0/1 doubling is reused once. Original-input physical admission is positive, logarithmic with explicit (257kappa+5909)clog2(n+2) bound under original polynomial scalar/weight bounds. Every native prefix at every admitted wider W has W-independent polynomial words and address support, at most (n+2)^(514kappa+11818). Width transport uses the actual minimum-width halting run. Fixed400 regs, <=133120 flat slots, no readsWidth. Targeted Lean MCP diagnostics PASS (no warnings/errors) for the full library umbrella and all three new tests. This includes the complete initialized/flat, width/prefix/support and source-charge proofs. Actual native cases cover signed success/failure, empty/restricted intervals, real producer pair order, and at D16/n16^18 full consumer first-success skips, two false positives (original sum3), zero answers, sentinel and full low320 frame. A separate fully admitted symbolic original-input producer/product/verifier run at m2/n16^18 proves same-state prefix, halt, actual original-coordinate success and S<=1. Post-product VM fixtures are not claimed to execute the entire enormous producer/product. Scope remains one record. Outer ET iteration/aggregate saving, global parameter and prime choice and the full ordinary-3SUM 1.9992 endpoint remain open under1394. No curated abstract/catalogue/rendering changes. No ancestor branch/ref changes. Source credit retained; pinned native source locators and model/quantifier/cost mapping are prepared in the PR draft, not claimed to inherit Light costs. No global/import/static processes were started. No PR or review commissioned. No all-six, global Axioms, hygiene/name or full changed-file audit claim is made. These remain required on the coordinated final candidate before publication. AI-assisted implementation: Codex with Lean MCP targeted proof diagnostics. --- .../Algorithms/ExactTriangle/PieceBounds.lean | 0 .../RecordProduct/Detection.lean | 0 .../RecordProduct/QueryValues.lean | 14 +- .../ExactTriangle/RecordProduct/RawInput.lean | 15 + .../WordRAM/ExactTriangle/RecordWitness.lean | 59 +++ .../ExactTriangle/RecordWitness/Consume.lean | 200 ++++----- .../ExactTriangle/RecordWitness/Entry.lean | 200 +++++++++ .../ExactTriangle/RecordWitness/Exec.lean | 206 ++++++++++ .../ExactTriangle/RecordWitness/Failures.lean | 143 +++++++ .../ExactTriangle/RecordWitness/Flat.lean | 103 +++++ .../ExactTriangle/RecordWitness/Header.lean | 0 .../ExactTriangle/RecordWitness/Kernel.lean | 0 .../ExactTriangle/RecordWitness/Original.lean | 46 +++ .../ExactTriangle/RecordWitness/Pair.lean | 0 .../ExactTriangle/RecordWitness/Prepare.lean | 113 ++++++ .../ExactTriangle/RecordWitness/Program.lean | 378 +++++++++--------- .../ExactTriangle/RecordWitness/Queries.lean | 8 +- .../ExactTriangle/RecordWitness/Readback.lean | 0 .../RecordWitness/SourceBounds.lean | 151 +++++++ .../ExactTriangle/RecordWitness/Spec.lean | 0 .../ExactTriangle/RecordWitness/Step.lean | 0 .../RecordWitness/TimeBound.lean | 84 ++++ .../ExactTriangle/RecordWitness/Trace.lean | 0 .../ExactTriangle/RecordWitness/Width.lean | 100 +++++ .../ExactTriangle/WitnessScan/Cell.lean | 0 .../ExactTriangle/WitnessScan/Exec.lean | 0 .../ExactTriangle/WitnessScan/Finite.lean | 67 ++++ .../ExactTriangle/WitnessScan/Program.lean | 0 .../ExactTriangle/WitnessScan/Setup.lean | 0 .../WordRAMExactTriangleQueryValues.lean | 0 .../WordRAMExactTriangleRecordWitness.lean | 82 +++- .../WordRAMExactTriangleWitnessScan.lean | 0 ROADMAP.md | 8 + UPSTREAM.md | 17 + 34 files changed, 1690 insertions(+), 304 deletions(-) mode change 100755 => 100644 Complexity/Algorithms/ExactTriangle/PieceBounds.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Detection.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/QueryValues.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Consume.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Entry.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Exec.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Failures.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Flat.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Header.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Kernel.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Original.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Pair.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Prepare.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Program.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Queries.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Readback.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/SourceBounds.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Spec.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Step.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/TimeBound.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Trace.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Width.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Cell.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Exec.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Finite.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Program.lean mode change 100755 => 100644 Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Setup.lean mode change 100755 => 100644 ComplexityTests/WordRAMExactTriangleQueryValues.lean mode change 100755 => 100644 ComplexityTests/WordRAMExactTriangleRecordWitness.lean mode change 100755 => 100644 ComplexityTests/WordRAMExactTriangleWitnessScan.lean diff --git a/Complexity/Algorithms/ExactTriangle/PieceBounds.lean b/Complexity/Algorithms/ExactTriangle/PieceBounds.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Detection.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/Detection.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/QueryValues.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/QueryValues.lean old mode 100755 new mode 100644 index 9d668636f..f90c45d9f --- a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/QueryValues.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/QueryValues.lean @@ -49,16 +49,12 @@ theorem decodedQueries_chunkOf {n D g p : ℕ} (values : ℕ → ℤ) rankIn S (qs.get i) = k * cap + i.val := by have hi : i.val < Q := by simpa only [qs, decodedQueries_length] using i.isLt obtain ⟨pair, hvalue, -⟩ := hpair i.val hi + have hvA : y.mem (q + 2 * i.val) = pair.1.val := hvalue.2.2.1 + have hvB : y.mem (q + 2 * i.val + 1) = pair.2.val := hvalue.2.2.2 + have hv := decodedQueries_get_val hn y.mem q Q i + (by rw [hvA]; exact pair.1.isLt) (by rw [hvB]; exact pair.2.isLt) have he : qs.get i = pair := by - apply Prod.ext <;> apply Fin.ext - · rw [List.get_eq_getElem] - simp only [qs, decodedQueries, List.getElem_ofFn] - have hv : y.mem (q + 2 * i.val) = pair.1.val := hvalue.2.2.1 - rw [hv, Nat.mod_eq_of_lt pair.1.isLt] - · rw [List.get_eq_getElem] - simp only [qs, decodedQueries, List.getElem_ofFn] - have hv : y.mem (q + 2 * i.val + 1) = pair.2.val := hvalue.2.2.2 - rw [hv, Nat.mod_eq_of_lt pair.2.isLt] + exact Prod.ext (Fin.ext (hv.1.trans hvA)) (Fin.ext (hv.2.trans hvB)) rw [he] exact ⟨hvalue.1, hvalue.2.1⟩ refine ⟨hget, ?_, ?_⟩ diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/RawInput.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/RawInput.lean index bd86b23f7..113123c8c 100644 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/RawInput.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordProduct/RawInput.lean @@ -37,6 +37,21 @@ a total specification; actual producer coordinates are proved in range before us theorem decodedQueries_length {n : ℕ} (hn : 0 < n) (mem : ℕ → ℕ) (q Q : ℕ) : (decodedQueries hn mem q Q).length = Q := List.length_ofFn +/-- In-range actual query words are exactly the original coordinate values in the decoded +occurrence. The total specification's modulo does not change valid producer words. -/ +theorem decodedQueries_get_val {n : ℕ} (hn : 0 < n) (mem : ℕ → ℕ) (q Q : ℕ) + (i : Fin (decodedQueries hn mem q Q).length) + (ha : mem (q + 2 * i.val) < n) (hb : mem (q + 2 * i.val + 1) < n) : + ((decodedQueries hn mem q Q).get i).1.val = mem (q + 2 * i.val) ∧ + ((decodedQueries hn mem q Q).get i).2.val = mem (q + 2 * i.val + 1) := by + constructor + · rw [List.get_eq_getElem] + simp only [decodedQueries, List.getElem_ofFn] + exact Nat.mod_eq_of_lt ha + · rw [List.get_eq_getElem] + simp only [decodedQueries, List.getElem_ofFn] + exact Nat.mod_eq_of_lt hb + /-- Every actual in-range coordinate word reappears unchanged in the ordinary callee input. -/ theorem decodedQueries_words {m n q Q : ℕ} (hn : 0 < n) (mem : ℕ → ℕ) (X : Matrix (Fin n) (Fin (D m)) ℤ) (Y : Matrix (Fin (D m)) (Fin n) ℤ) diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness.lean new file mode 100644 index 000000000..0bc8f554d --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness.lean @@ -0,0 +1,59 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.TimeBound + +/-! +# Native per-record ExactTriangle witness recovery + +One fixed standard unsigned WordRAM program starts from the existing original `(D,g,n,p)` +headers and three canonical-signed weight arrays. It runs the real producer, selects the first +occupied record, calls the actual native queried product, reads its actual answers/interleaved +pairs and verifies the original signed sums. No catalogue, layout, execution, answer, witness, +fit or time certificate is an algorithm input. The generic caller supports any occupied record. + +## Main definitions + +* `RecordWitness.program`, `RecordWitness.entryProgram`: caller and initialized code, using + 400 fixed registers with the complete 320-register product bank and 32-register verifier. +* `RecordWitness.admissionWidth`, `RecordWitness.wordBound`: original-input width and prefix + word/support envelopes, independent of every larger admitted physical width. + +## Main results + +* `RecordWitness.exec_of_output`: actual product and verification for a genuine producer output. +* `RecordWitness.runs_input`: original `State.init`, same-state prefix `Runs`, charged halt, + finite chunk/piece correctness, original-coordinate witness and caller frame. +* `RecordWitness.admissionWidth_le_log`, `RecordWitness.wordBound_le_pow`, + `RecordWitness.bounded_reachable_original`: positive logarithmic width and polynomial words + and memory support at every prefix, including physical dirty loads before normalization. +* `RecordWitness.initialized_time_le`: preparation and actual product charges, plus all actual + root/query/control work and scans; unsuccessful records have no extra successful-scan term. + +The physical program is fixed and has at most 133120 flat slots including halt. Geometry is +`D=4^m≥16`, `L=19m`, `n≥D^18`, `g≥1`, and `p` in the existing source prime window. The raw +0/1 matrix words are converted exactly once by the reused real product. Native success is +equivalent to an original zero in the selected chunk/piece. The actual scan counter satisfies +`S≤falsePositiveCount+returnedFlag`, where the returned flag is raw unsigned 0/1. All memory +outside the original product window and the low producer bank are preserved. + +This is a per-record component. Prime/global parameter selection, the outer ET instance loop, +aggregate subcubic ET analysis and the full ordinary 3SUM 1.9992 endpoint remain open under #1394. +No curated headline abstract is affected. + +## References + +The original-weight scan and short-circuit accounting adapt +[Scan.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Programs/Sec3/Theorem17/Witnesses/Scan.lean#L73-L89) +and [ScanPairs.lean](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Programs/Sec3/Theorem17/Witnesses/ScanPairs.lean#L69-L70). +The native code, initialized composition, fits, frames and instruction constants are original; +no Light-machine execution or resource bound is transferred by assumption. Finite `scanPiece` +returns its first zero, while the native verifier returns its last zero in the same interval; +their failure conditions agree, and every returned original-coordinate witness is verified. +-/ diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Consume.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Consume.lean old mode 100755 new mode 100644 index f215525bf..2a9aee858 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Consume.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Consume.lean @@ -1,100 +1,100 @@ -/- -Copyright (c) 2026 Erik Demaine. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Erik Demaine --/ - -module --- upstream: candidate - -public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Header -public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Queries - -/-! -# Complete per-record candidate consumption - -The real header reads the record and computes its piece interval. The real query loop then -loads product answers and original pairs, verifies the original signed weights and returns a -genuine original-coordinate witness when one exists among the selected pairs. All memory and -the complete earlier producer/product register bank are preserved. - -The numerical component premises are discharged from original caller data by the initialized -composition. In particular the record and answer words here are read from actual memory, not -supplied witness or execution certificates. --/ - -public section - -namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness - -open Cslib.WordRAM Cslib.Algorithms.ExactTriangle - --- This checks the finite write sets of the fixed consumer, including the renamed scan bank. -set_option maxRecDepth 4096 in -private theorem consume_preserves : ∀ j : Fin 400, j.val < 320 → consume.Preserves j := by - decide - -/-- Execute the actual descriptor decoder and short-circuit verifier loop. The additional -scan term uses the native counter, bounded by failed accepted pairs plus one success. -/ -theorem exec_consume {w U n D g i records heap out : ℕ} - (AB BC AC : ℕ → ℕ → ℤ) (a b : ℕ → ℕ) (x : Data 400) (v : Fin 6 → ℕ) - (hD : x.mem 1 = D) (hg : x.mem 2 = g) (hn : x.mem 3 = n) - (hi : x.regs (productBank RecordProduct.Reg.ordinal) = i) - (hrecords : x.regs (productBank (RecordProduct.producerBank Instances.output)) = records) - (hout : x.regs (productBank RecordProduct.Reg.result) = out) - (hrec : records + i * (6 + 2 * (n * D)) + 6 ≤ heap) - (hv : ∀ j : Fin 6, x.mem (records + i * (6 + 2 * (n * D)) + j.val) = v j) - (hfirst : v 1 * pieceSize D g ≤ n) - (hquery : ∀ j < v 3, x.mem (v 2 + 2 * j) = a j ∧ - x.mem (v 2 + 2 * j + 1) = b j ∧ a j < n ∧ b j < n) - (hmem : ∀ u < n, ∀ z < n, - x.mem (5 + (u * n + z)) = Signed.encode (AB u z) ∧ - x.mem (5 + n * n + (u * n + z)) = Signed.encode (BC u z) ∧ - x.mem (5 + n * n + n * n + (u * n + z)) = Signed.encode (AC u z)) - (hmag : ∀ u < n, ∀ z < n, - (AB u z).natAbs ≤ U ∧ (BC u z).natAbs ≤ U ∧ (AC u z).natAbs ≤ U) - (hU : GenericRAM.Signed.Fits w (2 * U : ℕ)) - (hfit : Fits (w + 2) (heap + out + v 2 + 4 * v 3 + 4 * D + - 2 * (n * D) + 9 * (n * n) + 4 * n + g + 2048)) : - let first := v 1 * pieceSize D g - let length := min (pieceSize D g) (n - first) - ∃ y S, ExecWithin (w + 2) consume x y - (5 * Nat.sqrt D + 8 * v 3 + (42 * length + 28) * S + 52) ∧ - y.mem = x.mem ∧ y.regs Reg.scans = S ∧ - S ≤ Nat.count (fun j => x.mem (out + j) ≠ 0 ∧ - ∀ c : Fin length, AB (a j) (b j) + BC (b j) (first + c.val) + - AC (a j) (first + c.val) ≠ 0) (v 3) + 1 ∧ - (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ - (y.regs Reg.found = 1 ↔ ∃ j < v 3, x.mem (out + j) ≠ 0 ∧ - ∃ c, first ≤ c ∧ c < first + length ∧ AB (a j) (b j) + BC (b j) c + AC (a j) c = 0) ∧ - (y.regs Reg.found = 1 → ∃ j < v 3, - x.mem (out + j) ≠ 0 ∧ y.regs Reg.witnessA = a j ∧ y.regs Reg.witnessB = b j ∧ - first ≤ y.regs Reg.witnessC ∧ y.regs Reg.witnessC < first + length ∧ - AB (a j) (b j) + BC (b j) (y.regs Reg.witnessC) + - AC (a j) (y.regs Reg.witnessC) = 0) ∧ - ∀ j : Fin 400, j.val < 320 → y.regs j = x.regs j := by - let first := v 1 * pieceSize D g - let length := min (pieceSize D g) (n - first) - have hlength : first + length ≤ n := by - dsimp only [length, first] - have := min_le_right (pieceSize D g) (n - v 1 * pieceSize D g) - omega - obtain ⟨s, hs, sFixed, sm, sf, -, -, -, ss, si, -⟩ := exec_header x v hD hg hn hi - hrecords hout hrec hv hfirst (hfit.of_le (by omega)) - obtain ⟨y, S, he, -, ym, -, ys, hS, yf, iffFound, yw⟩ := exec_queries - AB BC AC a b s sFixed si ss sf (by simpa only [sm] using hquery) - hlength (by simpa only [sm] using hmem) hmag hU - (hfit.of_le (by omega)) - change ExecWithin (w + 2) (.loop (.lt Reg.index Reg.count) step) s y - (8 * v 3 + (42 * length + 28) * S + 2) at he - have hexe : ExecWithin (w + 2) consume x y - (5 * Nat.sqrt D + 8 * v 3 + (42 * length + 28) * S + 52) := - (hs.seq he).mono (by omega) - refine ⟨y, S, hexe, ym.trans sm, ys, ?_, yf, ?_, ?_, ?_⟩ - · simpa only [sm] using hS - · simpa only [sm] using iffFound - · simpa only [sm] using yw - · intro j hj - exact hexe.regs_eq (consume_preserves j hj) - -end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Header +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Queries + +/-! +# Complete per-record candidate consumption + +The real header reads the record and computes its piece interval. The real query loop then +loads product answers and original pairs, verifies the original signed weights and returns a +genuine original-coordinate witness when one exists among the selected pairs. All memory and +the complete earlier producer/product register bank are preserved. + +The numerical component premises are discharged from original caller data by the initialized +composition. In particular the record and answer words here are read from actual memory, not +supplied witness or execution certificates. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM Cslib.Algorithms.ExactTriangle + +-- This checks the finite write sets of the fixed consumer, including the renamed scan bank. +set_option maxRecDepth 4096 in +private theorem consume_preserves : ∀ j : Fin 400, j.val < 320 → consume.Preserves j := by + decide + +/-- Execute the actual descriptor decoder and short-circuit verifier loop. The additional +scan term uses the native counter, bounded by failed accepted pairs plus one success. -/ +theorem exec_consume {w U n D g i records heap out : ℕ} + (AB BC AC : ℕ → ℕ → ℤ) (a b : ℕ → ℕ) (x : Data 400) (v : Fin 6 → ℕ) + (hD : x.mem 1 = D) (hg : x.mem 2 = g) (hn : x.mem 3 = n) + (hi : x.regs (productBank RecordProduct.Reg.ordinal) = i) + (hrecords : x.regs (productBank (RecordProduct.producerBank Instances.output)) = records) + (hout : x.regs (productBank RecordProduct.Reg.result) = out) + (hrec : records + i * (6 + 2 * (n * D)) + 6 ≤ heap) + (hv : ∀ j : Fin 6, x.mem (records + i * (6 + 2 * (n * D)) + j.val) = v j) + (hfirst : v 1 * pieceSize D g ≤ n) + (hquery : ∀ j < v 3, x.mem (v 2 + 2 * j) = a j ∧ + x.mem (v 2 + 2 * j + 1) = b j ∧ a j < n ∧ b j < n) + (hmem : ∀ u < n, ∀ z < n, + x.mem (5 + (u * n + z)) = Signed.encode (AB u z) ∧ + x.mem (5 + n * n + (u * n + z)) = Signed.encode (BC u z) ∧ + x.mem (5 + n * n + n * n + (u * n + z)) = Signed.encode (AC u z)) + (hmag : ∀ u < n, ∀ z < n, + (AB u z).natAbs ≤ U ∧ (BC u z).natAbs ≤ U ∧ (AC u z).natAbs ≤ U) + (hU : GenericRAM.Signed.Fits w (2 * U : ℕ)) + (hfit : Fits (w + 2) (heap + out + v 2 + 4 * v 3 + 4 * D + + 2 * (n * D) + 9 * (n * n) + 4 * n + g + 2048)) : + let first := v 1 * pieceSize D g + let length := min (pieceSize D g) (n - first) + ∃ y S, ExecWithin (w + 2) consume x y + (5 * Nat.sqrt D + 8 * v 3 + (42 * length + 28) * S + 52) ∧ + y.mem = x.mem ∧ y.regs Reg.scans = S ∧ + S ≤ Nat.count (fun j => x.mem (out + j) ≠ 0 ∧ + ∀ c : Fin length, AB (a j) (b j) + BC (b j) (first + c.val) + + AC (a j) (first + c.val) ≠ 0) (v 3) + y.regs Reg.found ∧ + (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ + (y.regs Reg.found = 1 ↔ ∃ j < v 3, x.mem (out + j) ≠ 0 ∧ + ∃ c, first ≤ c ∧ c < first + length ∧ AB (a j) (b j) + BC (b j) c + AC (a j) c = 0) ∧ + (y.regs Reg.found = 1 → ∃ j < v 3, + x.mem (out + j) ≠ 0 ∧ y.regs Reg.witnessA = a j ∧ y.regs Reg.witnessB = b j ∧ + first ≤ y.regs Reg.witnessC ∧ y.regs Reg.witnessC < first + length ∧ + AB (a j) (b j) + BC (b j) (y.regs Reg.witnessC) + + AC (a j) (y.regs Reg.witnessC) = 0) ∧ + ∀ j : Fin 400, j.val < 320 → y.regs j = x.regs j := by + let first := v 1 * pieceSize D g + let length := min (pieceSize D g) (n - first) + have hlength : first + length ≤ n := by + dsimp only [length, first] + have := min_le_right (pieceSize D g) (n - v 1 * pieceSize D g) + omega + obtain ⟨s, hs, sFixed, sm, sf, -, -, -, ss, si, -⟩ := exec_header x v hD hg hn hi + hrecords hout hrec hv hfirst (hfit.of_le (by omega)) + obtain ⟨y, S, he, -, ym, -, ys, hS, yf, iffFound, yw⟩ := exec_queries + AB BC AC a b s sFixed si ss sf (by simpa only [sm] using hquery) + hlength (by simpa only [sm] using hmem) hmag hU + (hfit.of_le (by omega)) + change ExecWithin (w + 2) (.loop (.lt Reg.index Reg.count) step) s y + (8 * v 3 + (42 * length + 28) * S + 2) at he + have hexe : ExecWithin (w + 2) consume x y + (5 * Nat.sqrt D + 8 * v 3 + (42 * length + 28) * S + 52) := + (hs.seq he).mono (by omega) + refine ⟨y, S, hexe, ym.trans sm, ys, ?_, yf, ?_, ?_, ?_⟩ + · simpa only [sm] using hS + · simpa only [sm] using iffFound + · simpa only [sm] using yw + · intro j hj + exact hexe.regs_eq (consume_preserves j hj) + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Entry.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Entry.lean new file mode 100644 index 000000000..c6ab79af3 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Entry.lean @@ -0,0 +1,200 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Prepare +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Original +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Failures + +/-! +# Initialized producer, actual product and original-weight witness recovery + +The input contains only the original scalar headers and three signed arrays. The fixed native +program constructs its real catalogue, selects its first actual record, computes its product, +loads its real queries and verifies original weights. False-positive accounting is derived +from the producer's actual repetition-free chunk. This is one record, not an outer ET solver. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +/-- Actual initialized structured execution, with original-coordinate soundness and complete +coverage of the selected chunk/piece. Failed records incur no extra successful-scan charge. -/ +theorem exec_input {w m n g p A : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hD : 16 ≤ D m) + (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) (hN : D m ^ 18 ≤ n) + (hv : ∀ z ∈ values, z.natAbs ≤ A) + (hW : admissionWidth n (D m) g p A ≤ w + 2) : + let V := fun q => values.getD q 0 + let wAB := fun a b : Fin n => V (pairIndex (a, b)) + let wBC := fun b c : Fin n => V (n * n + pairIndex (b, c)) + let wAC := fun a c : Fin n => V (2 * (n * n) + pairIndex (a, c)) + InputFits (w + 2) (PreparationInput.input n (D m) g p values) ∧ + ∃ a : Data 64, ∃ idx : InstanceIndex p, + ExecWithin (w + 2) Instances.program + (.init (PreparationInput.input n (D m) g p values)) a + (RecordProduct.preparationTime n (D m) g p values - 1) ∧ + Instances.Output n (D m) g p V + (.init (PreparationInput.input n (D m) g p values)) a ∧ + idx ∈ instanceIndices wAB (D m) g p ∧ + RecordProduct.recordOrdinal n (D m) g p V idx = 0 ∧ + let hn : 0 < n := lt_of_lt_of_le (by omega : 0 < D m) + ((Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN) + let Q := RecordProduct.recordQueryCount n (D m) p V idx + let qs := RecordProduct.decodedQueries hn a.mem (RecordProduct.recordQueries n (D m) p idx) Q + let length := min (pieceSize (D m) g) (n - idx.2.2 * pieceSize (D m) g) + ∃ y S, ExecWithin (w + 2) entryProgram + (.init (PreparationInput.input n (D m) g p values)) y + (RecordProduct.preparationTime n (D m) g p values + + RecordProduct.timeBound (m := m) qs + 5 * Nat.sqrt (D m) + 8 * Q + + (42 * length + 28) * S + 52) ∧ + y.Bounded (w + 2) ∧ y.regs Reg.scans = S ∧ + (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ + S ≤ falsePositiveCount wAB wBC wAC p + y.regs Reg.found ∧ + (y.regs Reg.found = 1 ↔ + ∃ q ∈ chunkOf wAB (D m) p idx.1 idx.2.1, + ∃ c ∈ piece n (D m) g idx.2.2, weightSum wAB wBC wAC q.1 q.2 c = 0) ∧ + (y.regs Reg.found = 1 → ∃ u v c : Fin n, + y.regs Reg.witnessA = u.val ∧ y.regs Reg.witnessB = v.val ∧ + y.regs Reg.witnessC = c.val ∧ (u, v) ∈ chunkOf wAB (D m) p idx.1 idx.2.1 ∧ + c ∈ piece n (D m) g idx.2.2 ∧ weightSum wAB wBC wAC u v c = 0) ∧ + (∀ u, u < a.regs Instances.heap ∨ + a.regs Instances.heap + CallableProduct.windowSize n Q ≤ u → y.mem u = a.mem u) ∧ + ∀ j : Fin 64, y.regs (productBank (RecordProduct.producerBank j)) = a.regs j := by + let V := fun q => values.getD q 0 + let wAB := fun a b : Fin n => V (pairIndex (a, b)) + let wBC := fun b c : Fin n => V (n * n + pairIndex (b, c)) + let wAC := fun a c : Fin n => V (2 * (n * n) + pairIndex (a, c)) + let AB := fun a b : ℕ => V (a * n + b) + let BC := fun b c : ℕ => V (n * n + (b * n + c)) + let AC := fun a c : ℕ => V (2 * (n * n) + (a * n + c)) + have hDn : D m ≤ n := (Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN + have hn : 0 < n := lt_of_lt_of_le (by omega) hDn + have hp0 := (Nat.mem_sqrtPrimeWindow_iff_sq.mp hp).1.pos + obtain ⟨hinput, a, idx, z, ha, hout, hidx, hord, hz, zmap, zi, zheap⟩ := + prepare_input values hlen hD hg hp0 hN hv hW + let q := RecordProduct.recordQueries n (D m) p idx + let Q := RecordProduct.recordQueryCount n (D m) p V idx + let qs := RecordProduct.decodedQueries hn a.mem q Q + have hlenQ : qs.length = Q := RecordProduct.decodedQueries_length hn a.mem q Q + have zmem : z.mem = a.mem := congrArg (fun d : Data 64 => d.mem) zmap + have hzout : Instances.Output n (D m) g p V + (.init (PreparationInput.input n (D m) g p values)) + ((z.comap productBank).comap RecordProduct.producerBank) := by + rw [zmap] + exact hout + have hQ : Q ≤ n ^ 2 := by + unfold Q RecordProduct.recordQueryCount + rw [pow_two] + exact (min_le_right _ _).trans ((Nat.sub_le _ _).trans (Nat.count_le _)) + have hmem : ∀ u < n, ∀ v < n, + z.mem (5 + (u * n + v)) = Signed.encode (AB u v) ∧ + z.mem (5 + n * n + (u * n + v)) = Signed.encode (BC u v) ∧ + z.mem (5 + n * n + n * n + (u * n + v)) = Signed.encode (AC u v) := by + intro u hu v hv' + have hn := Nat.mul_le_mul_right n (Nat.succ_le_of_lt hu) + rw [Nat.succ_mul] at hn + refine ⟨original_word values a z hout zmap (by omega), ?_, ?_⟩ + · simpa only [Nat.add_assoc] using + original_word (q := n * n + (u * n + v)) values a z hout zmap (by omega) + · have he : 5 + n * n + n * n + (u * n + v) = + 5 + (2 * (n * n) + (u * n + v)) := by omega + rw [he] + exact original_word values a z hout zmap (by omega) + have hmag : ∀ u < n, ∀ v < n, + (AB u v).natAbs ≤ A ∧ (BC u v).natAbs ≤ A ∧ (AC u v).natAbs ≤ A := by + intro u hu v hv' + have hn := Nat.mul_le_mul_right n (Nat.succ_le_of_lt hu) + rw [Nat.succ_mul] at hn + exact ⟨original_magnitude values hlen hv (by omega), + original_magnitude values hlen hv (by omega), original_magnitude values hlen hv (by omega)⟩ + obtain ⟨y, S, he, ys, yf, hS, yiff, answers, yw, frame, regs⟩ := exec_of_output + V AB BC AC (.init (PreparationInput.input n (D m) g p values)) z hzout hD hg + (by omega : p ≠ 0) hN rfl rfl rfl idx hidx (by rw [zi, hord]) hmem hmag + (signed_fits hW) (physical_fits zheap hQ hN hW) + simp only [zmem] at he hS yiff answers yw frame + rw [zmem] at answers + change ∀ j : Fin qs.length, + y.mem (y.regs (productBank RecordProduct.Reg.result) + j.val) = Signed.encode + ((RecordProduct.leftMatrix m n g p V idx * RecordProduct.rightMatrix m n g p V idx) + (qs.get j).1 (qs.get j).2) at answers + change ExecWithin (w + 2) program z y (RecordProduct.timeBound (m := m) qs + + 5 * Nat.sqrt (D m) + 8 * Q + + (42 * min (pieceSize (D m) g) (n - idx.2.2 * pieceSize (D m) g) + 28) * S + 52) at he + have hheap : z.regs (productBank 42) = a.regs Instances.heap := + congrArg (fun d : Data 64 => d.regs Instances.heap) zmap + have hex : ExecWithin (w + 2) entryProgram + (.init (PreparationInput.input n (D m) g p values)) y + (RecordProduct.preparationTime n (D m) g p values + + RecordProduct.timeBound (m := m) qs + 5 * Nat.sqrt (D m) + 8 * Q + + (42 * min (pieceSize (D m) g) (n - idx.2.2 * pieceSize (D m) g) + 28) * S + 52) := by + obtain ⟨t, ht, hpref⟩ := hz + cases hpref with + | seq hp hs => exact ((hp.within.seq (hs.within.seq he))).mono (by omega) + obtain ⟨-, -, -, hfirst, -, hcoords, -⟩ := record_view V + (.init (PreparationInput.input n (D m) g p values)) a hout hD hDn hg (by omega) idx hidx + change ∀ i < Q, a.mem (q + 2 * i) < n ∧ a.mem (q + 2 * i + 1) < n at hcoords + have card := QuerySpec.card_residueClass wAB (by omega : p ≠ 0) + (fun q => (V q % (p : ℤ)).toNat) (by intro pair; rfl) idx.1 + obtain ⟨-, hnodup, hset⟩ := RecordProduct.decodedQueries_chunkOf V wAB hn (by omega) + (by intro u v; rfl) (by omega) hDn hout idx.1 idx.2.1 ((mem_instanceIndices wAB idx).mp hidx).1 + rw [card] at hnodup hset + change qs.Nodup at hnodup + change qs.toFinset = chunkOf wAB (D m) p idx.1 idx.2.1 at hset + let argA := fun j => a.mem (q + 2 * j) + let argB := fun j => a.mem (q + 2 * j + 1) + have hcoordinate (i : Fin qs.length) : + argA i.val = (qs.get i).1.val ∧ argB i.val = (qs.get i).2.val := by + have hi : i.val < Q := by simpa only [hlenQ] using i.isLt + have hh := RecordProduct.decodedQueries_get_val hn a.mem q Q i + (hcoords i.val hi).1 (hcoords i.val hi).2 + exact ⟨hh.1.symm, hh.2.symm⟩ + have hquery : ∀ pair ∈ qs, pair ∈ chunkOf wAB (D m) p idx.1 idx.2.1 := by + intro pair hm + rw [← hset, List.mem_toFinset] + exact hm + let default : Fin n × Fin n := (⟨0, hn⟩, ⟨0, hn⟩) + have hfail := native_failed_occurrences_le V AB BC AC wAB wBC wAC hD hg hp + (by intro u v; rfl) (by intro u v; rfl) (by intro u v; rfl) + (by intro u v; rfl) (by intro u v; rfl) idx hidx qs default hnodup hquery + argA argB hcoordinate y.mem (y.regs (productBank RecordProduct.Reg.result)) answers + have hsuccess := actual_success_iff V AB BC AC wAB wBC wAC hD hg hp + (by intro u v; rfl) (by intro u v; rfl) (by intro u v; rfl) + (by intro u v; rfl) (by intro u v; rfl) idx qs hset + argA argB hcoordinate y.mem (y.regs (productBank RecordProduct.Reg.result)) answers + rw [hlenQ] at hfail hsuccess + refine ⟨hinput, a, idx, ha, hout, hidx, hord, y, S, hex, + hex.bounded (Data.Bounded.init hinput), ys, yf, ?_, yiff.trans hsuccess, ?_, ?_, ?_⟩ + · exact hS.trans (Nat.add_le_add_right hfail _) + · intro hy + obtain ⟨ha, hb, hlo, hhi, hz, j, hj, hwa, hwb⟩ := yw hy + have hc : y.regs Reg.witnessC < n := by + have := min_le_right (pieceSize (D m) g) (n - idx.2.2 * pieceSize (D m) g) + omega + let u : Fin n := ⟨y.regs Reg.witnessA, ha⟩ + let v : Fin n := ⟨y.regs Reg.witnessB, hb⟩ + let c : Fin n := ⟨y.regs Reg.witnessC, hc⟩ + let i : Fin qs.length := ⟨j, by simpa only [hlenQ] using hj⟩ + have hpq : (u, v) = qs.get i := Prod.ext (Fin.ext (hwa.trans (hcoordinate i).1)) + (Fin.ext (hwb.trans (hcoordinate i).2)) + refine ⟨u, v, c, rfl, rfl, rfl, ?_, ?_, hz⟩ + · rw [hpq] + exact hquery _ (List.get_mem _ _) + · exact (mem_piece_iff_interval hD hg c).mpr ⟨hlo, hhi⟩ + · intro u hu + exact frame u (by simpa only [hheap] using hu) + · intro j + rw [regs _ (by change j.val < 65; omega)] + exact congrArg (fun d : Data 64 => d.regs j) zmap + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Exec.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Exec.lean new file mode 100644 index 000000000..b62cbe95a --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Exec.lean @@ -0,0 +1,206 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Consume +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Readback + +/-! +# The real product followed by candidate verification + +The general caller selects any occupied record. The actual product is executed on its whole +renamed 320-register bank; the consumer then reads its real answers and protected query words. +The original signed arrays and caller catalogue remain untouched. The initialized entry derives +the numerical premises from original input, rather than supplying an intermediate run contract. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +private theorem productBank_injective : Function.Injective productBank := by + intro i j h + exact Fin.ext (congrArg (fun k : Fin 400 => k.val) h) + +/-- Execute the actual product and verifier from one genuine producer output. No product +execution, accepted-pair list, witness or time certificate is a hypothesis. -/ +theorem exec_of_output {w U m n g p : ℕ} (values : ℕ → ℤ) + (AB BC AC : ℕ → ℕ → ℤ) (initial : Data 64) (x : Data 400) + (h : Instances.Output n (D m) g p values initial + ((x.comap productBank).comap RecordProduct.producerBank)) + (hD : 16 ≤ D m) (hg : 1 ≤ g) (hp : p ≠ 0) (hN : D m ^ 18 ≤ n) + (hheaderD : initial.mem 1 = D m) (hheaderG : initial.mem 2 = g) + (hheaderN : initial.mem 3 = n) + (idx : InstanceIndex p) + (hidx : idx ∈ instanceIndices (fun a b : Fin n => values (pairIndex (a, b))) (D m) g p) + (hi : x.regs (productBank RecordProduct.Reg.ordinal) = + RecordProduct.recordOrdinal n (D m) g p values idx) + (hmem : ∀ a < n, ∀ b < n, + x.mem (5 + (a * n + b)) = Signed.encode (AB a b) ∧ + x.mem (5 + n * n + (a * n + b)) = Signed.encode (BC a b) ∧ + x.mem (5 + n * n + n * n + (a * n + b)) = Signed.encode (AC a b)) + (hmag : ∀ a < n, ∀ b < n, + (AB a b).natAbs ≤ U ∧ (BC a b).natAbs ≤ U ∧ (AC a b).natAbs ≤ U) + (hU : GenericRAM.Signed.Fits w (2 * U : ℕ)) + (hfit : Fits (w + 2) + (4 * x.regs (productBank 42) + + 4 * CallableProduct.windowSize n (RecordProduct.recordQueryCount n (D m) p values idx) + + QueriedProduct.outputBase (19 * m) m n + (RecordProduct.recordQueryCount n (D m) p values idx) + + 4 * RecordProduct.recordQueryCount n (D m) p values idx + 4 * D m + + 2 * (n * D m) + 9 * (n * n) + 4 * n + g + 4096)) : + let hn : 0 < n := lt_of_lt_of_le (by omega : 0 < D m) + ((Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN) + let q := RecordProduct.recordQueries n (D m) p idx + let Q := RecordProduct.recordQueryCount n (D m) p values idx + let qs := RecordProduct.decodedQueries hn x.mem q Q + let first := idx.2.2 * pieceSize (D m) g + let length := min (pieceSize (D m) g) (n - first) + ∃ y S, ExecWithin (w + 2) program x y + (RecordProduct.timeBound (m := m) qs + 5 * Nat.sqrt (D m) + 8 * Q + + (42 * length + 28) * S + 52) ∧ + y.regs Reg.scans = S ∧ (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ + S ≤ Nat.count (fun j => y.mem (y.regs (productBank RecordProduct.Reg.result) + j) ≠ 0 ∧ + ∀ c : Fin length, + AB (x.mem (q + 2 * j)) (x.mem (q + 2 * j + 1)) + + BC (x.mem (q + 2 * j + 1)) (first + c.val) + + AC (x.mem (q + 2 * j)) (first + c.val) ≠ 0) Q + y.regs Reg.found ∧ + (y.regs Reg.found = 1 ↔ ∃ j < Q, + y.mem (y.regs (productBank RecordProduct.Reg.result) + j) ≠ 0 ∧ + ∃ c, first ≤ c ∧ c < first + length ∧ + AB (x.mem (q + 2 * j)) (x.mem (q + 2 * j + 1)) + + BC (x.mem (q + 2 * j + 1)) c + AC (x.mem (q + 2 * j)) c = 0) ∧ + (∀ j : Fin qs.length, + y.mem (y.regs (productBank RecordProduct.Reg.result) + j.val) = Signed.encode + ((RecordProduct.leftMatrix m n g p values idx * + RecordProduct.rightMatrix m n g p values idx) (qs.get j).1 (qs.get j).2)) ∧ + (y.regs Reg.found = 1 → + y.regs Reg.witnessA < n ∧ y.regs Reg.witnessB < n ∧ + first ≤ y.regs Reg.witnessC ∧ y.regs Reg.witnessC < first + length ∧ + AB (y.regs Reg.witnessA) (y.regs Reg.witnessB) + + BC (y.regs Reg.witnessB) (y.regs Reg.witnessC) + + AC (y.regs Reg.witnessA) (y.regs Reg.witnessC) = 0 ∧ + ∃ j < Q, y.regs Reg.witnessA = x.mem (q + 2 * j) ∧ + y.regs Reg.witnessB = x.mem (q + 2 * j + 1)) ∧ + (∀ u, u < x.regs (productBank 42) ∨ + x.regs (productBank 42) + CallableProduct.windowSize n Q ≤ u → y.mem u = x.mem u) ∧ + (∀ j : Fin 320, j.val < 65 → y.regs (productBank j) = x.regs (productBank j)) := by + let x' := x.comap productBank + let produced := x'.comap RecordProduct.producerBank + let heap := x.regs (productBank 42) + let q := RecordProduct.recordQueries n (D m) p idx + let Q := RecordProduct.recordQueryCount n (D m) p values idx + let ord := RecordProduct.recordOrdinal n (D m) g p values idx + let base := x.regs (productBank (RecordProduct.producerBank Instances.output)) + + ord * (6 + 2 * (n * D m)) + let out := heap + QueriedProduct.outputBase (19 * m) m n Q + change Fits (w + 2) (4 * heap + 4 * CallableProduct.windowSize n Q + + QueriedProduct.outputBase (19 * m) m n Q + 4 * Q + 4 * D m + + 2 * (n * D m) + 9 * (n * n) + 4 * n + g + 4096) at hfit + have hDn : D m ≤ n := (Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN + have hn : 0 < n := lt_of_lt_of_le (by omega) hDn + let qs := RecordProduct.decodedQueries hn x.mem q Q + have hlen : qs.length = Q := RecordProduct.decodedQueries_length hn x.mem q Q + obtain ⟨hinputEnd, hrec, hq, hfirst, hv, hcoords, hprefix⟩ := + record_view values initial produced h hD hDn hg hp idx hidx + change 5 + 3 * (n * n) ≤ heap at hinputEnd + change base + 6 ≤ heap at hrec + change q + 2 * Q ≤ heap at hq + change ∀ j : Fin 6, x.mem (base + j.val) = + ![idx.1.val, idx.2.2, q, Q, base + 6, base + 6 + n * D m] j at hv + change ∀ j < Q, x.mem (q + 2 * j) < n ∧ x.mem (q + 2 * j + 1) < n at hcoords + change ∀ j < 5 + 3 * (n * n), x.mem j = initial.mem j at hprefix + obtain ⟨r, hr, rr, rvalues, rmem, rregs, -⟩ := RecordProduct.exec_of_output values + initial x' h hD hg hp hN hheaderD hheaderN idx hidx hi (hfit.of_le (by + change 2 * heap + 4 * CallableProduct.windowSize n Q + 1024 ≤ _ + omega)) + change r.regs RecordProduct.Reg.result = + heap + QueriedProduct.outputBase (19 * m) m n qs.length at rr + change ∀ j : Fin qs.length, + r.mem (heap + (QueriedProduct.outputBase (19 * m) m n qs.length + j.val)) = + Signed.encode ((RecordProduct.leftMatrix m n g p values idx * + RecordProduct.rightMatrix m n g p values idx) (qs.get j).1 (qs.get j).2) at rvalues + simp only [RecordProduct.decodedQueries_length] at rmem + change ∀ u, u < heap ∨ heap + CallableProduct.windowSize n Q ≤ u → + r.mem u = x.mem u at rmem + let s := r.extend productBank x + have hs : ExecWithin (w + 2) (RecordProduct.program.rename productBank) x s + (RecordProduct.timeBound (m := m) qs) := hr.rename productBank_injective + have sr (j : Fin 320) : s.regs (productBank j) = r.regs j := + productBank_injective.extend_apply _ _ j + have sm (u : ℕ) (hu : u < heap) : s.mem u = x.mem u := rmem u (Or.inl hu) + have snative (j : Fin 320) (hj : j.val < 65) : + s.regs (productBank j) = x.regs (productBank j) := (sr j).trans (rregs j hj) + have sout : s.regs (productBank RecordProduct.Reg.result) = out := by + simpa only [out, hlen] using (sr RecordProduct.Reg.result).trans rr + let v : Fin 6 → ℕ := ![idx.1.val, idx.2.2, q, Q, base + 6, base + 6 + n * D m] + have hread (j : Fin 6) : s.mem (base + j.val) = v j := + (sm _ (by have := j.isLt; omega)).trans (hv j) + have hquery : ∀ j < Q, s.mem (q + 2 * j) = x.mem (q + 2 * j) ∧ + s.mem (q + 2 * j + 1) = x.mem (q + 2 * j + 1) ∧ + x.mem (q + 2 * j) < n ∧ x.mem (q + 2 * j + 1) < n := by + intro j hj + exact ⟨sm _ (by omega), sm _ (by omega), hcoords j hj⟩ + have smem : ∀ a < n, ∀ b < n, + s.mem (5 + (a * n + b)) = Signed.encode (AB a b) ∧ + s.mem (5 + n * n + (a * n + b)) = Signed.encode (BC a b) ∧ + s.mem (5 + n * n + n * n + (a * n + b)) = Signed.encode (AC a b) := by + intro a ha b hb + have hn := Nat.mul_le_mul_right n (Nat.succ_le_of_lt ha) + rw [Nat.succ_mul] at hn + simpa only [sm _ (by omega : 5 + (a * n + b) < heap), + sm _ (by omega : 5 + n * n + (a * n + b) < heap), + sm _ (by omega : 5 + n * n + n * n + (a * n + b) < heap)] using hmem a ha b hb + obtain ⟨y, S, he, ym, ys, hS, yf, yiff, yw, frame⟩ := exec_consume AB BC AC + (fun j => x.mem (q + 2 * j)) (fun j => x.mem (q + 2 * j + 1)) s v + ((sm 1 (by omega)).trans ((hprefix 1 (by omega)).trans hheaderD)) + ((sm 2 (by omega)).trans ((hprefix 2 (by omega)).trans hheaderG)) + ((sm 3 (by omega)).trans ((hprefix 3 (by omega)).trans hheaderN)) + ((snative _ (by decide)).trans hi) (snative _ (by decide)) sout hrec hread hfirst + hquery smem hmag hU (hfit.of_le (by + change heap + out + q + 4 * Q + 4 * D m + 2 * (n * D m) + + 9 * (n * n) + 4 * n + g + 2048 ≤ _ + dsimp only [out] + omega)) + change ExecWithin (w + 2) consume s y + (5 * Nat.sqrt (D m) + 8 * Q + + (42 * min (pieceSize (D m) g) (n - idx.2.2 * pieceSize (D m) g) + 28) * S + 52) at he + change S ≤ Nat.count (fun j => s.mem (out + j) ≠ 0 ∧ + ∀ c : Fin (min (pieceSize (D m) g) (n - idx.2.2 * pieceSize (D m) g)), + AB (x.mem (q + 2 * j)) (x.mem (q + 2 * j + 1)) + + BC (x.mem (q + 2 * j + 1)) (idx.2.2 * pieceSize (D m) g + c.val) + + AC (x.mem (q + 2 * j)) (idx.2.2 * pieceSize (D m) g + c.val) ≠ 0) Q + y.regs Reg.found at hS + simp_rw [show v 1 = idx.2.2 from rfl, show v 3 = Q from rfl] at yiff + have hexe : ExecWithin (w + 2) program x y + (RecordProduct.timeBound (m := m) qs + 5 * Nat.sqrt (D m) + 8 * Q + + (42 * min (pieceSize (D m) g) (n - idx.2.2 * pieceSize (D m) g) + 28) * S + 52) := + (hs.seq he).mono (by omega) + refine ⟨y, S, hexe, ys, yf, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · simpa only [ym, frame (productBank RecordProduct.Reg.result) (by decide), sout] using hS + · simpa only [ym, frame (productBank RecordProduct.Reg.result) (by decide), sout] using yiff + · intro j + have hv := rvalues j + rw [frame _ (by decide), sout, ym] + change r.mem (out + j.val) = _ + simpa only [out, hlen, Nat.add_assoc] using hv + · intro hy + obtain ⟨j, hj, -, hwa, hwb, hlo, hhi, hz⟩ := yw hy + obtain ⟨ha, hb⟩ := hcoords j hj + exact ⟨by simpa only [hwa] using ha, by simpa only [hwb] using hb, + hlo, hhi, by simpa only [hwa, hwb] using hz, j, hj, hwa, hwb⟩ + · intro u hu + rw [ym] + exact rmem u hu + · intro j hj + exact (frame _ (by change j.val < 320; omega)).trans (snative j hj) + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Failures.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Failures.lean new file mode 100644 index 000000000..5618c23a4 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Failures.lean @@ -0,0 +1,143 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Detection +public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Finite + +/-! +# Original false positives bound the actual native failed occurrences + +Actual answer words and original query coordinates determine the native failure predicate. +For the producer's repetition-free chunk, these occurrences inject into the existing finite +failed scans, whose distinct false-positive bound is already proved. This is a value/readback +lemma: actual execution supplies its premises, with no assumed answer or witness oracle. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Interpret the native failure predicate on real product words. Each failed original query +occurs only once, so its count is bounded by original-weight modular false positives. -/ +theorem native_failed_occurrences_le {m n g p : ℕ} (values : ℕ → ℤ) + (AB BC AC : ℕ → ℕ → ℤ) (wAB wBC wAC : Fin n → Fin n → ℤ) + (hD : 16 ≤ D m) (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) + (hAB : ∀ u v : Fin n, AB u.val v.val = wAB u v) + (hBC : ∀ u v : Fin n, BC u.val v.val = wBC u v) + (hAC : ∀ u v : Fin n, AC u.val v.val = wAC u v) + (hvaluesBC : ∀ b c, values (n * n + pairIndex (b, c)) = wBC b c) + (hvaluesAC : ∀ a c, values (2 * (n * n) + pairIndex (a, c)) = wAC a c) + (idx : InstanceIndex p) (hidx : idx ∈ instanceIndices wAB (D m) g p) + (qs : List (Fin n × Fin n)) (default : Fin n × Fin n) (hqs : qs.Nodup) + (hquery : ∀ q ∈ qs, q ∈ chunkOf wAB (D m) p idx.1 idx.2.1) + (a b : ℕ → ℕ) (hcoords : ∀ i : Fin qs.length, + a i.val = (qs.get i).1.val ∧ b i.val = (qs.get i).2.val) + (mem : ℕ → ℕ) (out : ℕ) + (hanswer : ∀ i : Fin qs.length, mem (out + i.val) = Signed.encode + ((RecordProduct.leftMatrix m n g p values idx * + RecordProduct.rightMatrix m n g p values idx) (qs.get i).1 (qs.get i).2)) : + let first := idx.2.2 * pieceSize (D m) g + let length := min (pieceSize (D m) g) (n - first) + Nat.count (fun i => mem (out + i) ≠ 0 ∧ + ∀ c : Fin length, AB (a i) (b i) + BC (b i) (first + c.val) + + AC (a i) (first + c.val) ≠ 0) qs.length ≤ falsePositiveCount wAB wBC wAC p := by + apply (Nat.count_mono_left (q := fun i => + (RecordProduct.leftMatrix m n g p values idx * RecordProduct.rightMatrix m n g p values idx) + (qs.getD i default).1 (qs.getD i default).2 ≠ 0 ∧ + scanPiece wAB wBC wAC (D m) g idx.2.2 + (qs.getD i default).1 (qs.getD i default).2 = none) ?_).trans + (RecordProduct.failed_occurrences_le values wAB wBC wAC hD hg hp + hvaluesBC hvaluesAC idx hidx qs default hqs hquery) + intro i hi hn + let j : Fin qs.length := ⟨i, hi⟩ + have hget : qs.getD i default = qs.get j := by + rw [List.getD_eq_getElem _ _ hi, List.get_eq_getElem] + rw [hget] + constructor + · intro hz + apply hn.1 + rw [hanswer j, hz] + rfl + · apply (WitnessScan.fails_iff_scanPiece_eq_none AB BC AC wAB wBC wAC + hD hg hAB hBC hAC (qs.get j).1 (qs.get j).2).mp + obtain ⟨ha, hb⟩ := hcoords j + simpa only [j, ha, hb] using hn.2 + +/-- The actual native success condition is exactly an original zero triangle in the selected +existing chunk and piece. The nonzero-product condition is derived for every genuine zero. -/ +theorem actual_success_iff {m n g p : ℕ} (values : ℕ → ℤ) + (AB BC AC : ℕ → ℕ → ℤ) (wAB wBC wAC : Fin n → Fin n → ℤ) + (hD : 16 ≤ D m) (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) + (hAB : ∀ u v : Fin n, AB u.val v.val = wAB u v) + (hBC : ∀ u v : Fin n, BC u.val v.val = wBC u v) + (hAC : ∀ u v : Fin n, AC u.val v.val = wAC u v) + (hvaluesBC : ∀ b c, values (n * n + pairIndex (b, c)) = wBC b c) + (hvaluesAC : ∀ a c, values (2 * (n * n) + pairIndex (a, c)) = wAC a c) + (idx : InstanceIndex p) (qs : List (Fin n × Fin n)) + (hset : qs.toFinset = chunkOf wAB (D m) p idx.1 idx.2.1) + (a b : ℕ → ℕ) (hcoords : ∀ i : Fin qs.length, + a i.val = (qs.get i).1.val ∧ b i.val = (qs.get i).2.val) + (mem : ℕ → ℕ) (out : ℕ) + (hanswer : ∀ i : Fin qs.length, mem (out + i.val) = Signed.encode + ((RecordProduct.leftMatrix m n g p values idx * + RecordProduct.rightMatrix m n g p values idx) (qs.get i).1 (qs.get i).2)) : + let first := idx.2.2 * pieceSize (D m) g + let length := min (pieceSize (D m) g) (n - first) + (∃ i < qs.length, mem (out + i) ≠ 0 ∧ + ∃ c, first ≤ c ∧ c < first + length ∧ AB (a i) (b i) + BC (b i) c + AC (a i) c = 0) ↔ + ∃ q ∈ chunkOf wAB (D m) p idx.1 idx.2.1, + ∃ c ∈ piece n (D m) g idx.2.2, weightSum wAB wBC wAC q.1 q.2 c = 0 := by + let first := idx.2.2 * pieceSize (D m) g + let length := min (pieceSize (D m) g) (n - first) + have pair_mem (i : Fin qs.length) : qs.get i ∈ chunkOf wAB (D m) p idx.1 idx.2.1 := by + rw [← hset, List.mem_toFinset] + exact List.get_mem _ _ + constructor + · rintro ⟨i, hi, -, c, hlo, hhi, hz⟩ + let j : Fin qs.length := ⟨i, hi⟩ + have hl := min_le_right (pieceSize (D m) g) (n - first) + have hn : c < n := by change first ≤ c at hlo; change c < first + length at hhi; omega + let vertex : Fin n := ⟨c, hn⟩ + have hc : vertex ∈ piece n (D m) g idx.2.2 := + (mem_piece_iff_interval hD hg vertex).mpr ⟨hlo, hhi⟩ + obtain ⟨ha, hb⟩ := hcoords j + have he : AB (a i) (b i) + BC (b i) c + AC (a i) c = + weightSum wAB wBC wAC (qs.get j).1 (qs.get j).2 vertex := by + rw [ha, hb] + change AB (qs.get j).1.val (qs.get j).2.val + + BC (qs.get j).2.val vertex.val + AC (qs.get j).1.val vertex.val = _ + rw [hAB, hBC, hAC] + rfl + exact ⟨qs.get j, pair_mem j, vertex, hc, he.symm.trans hz⟩ + · rintro ⟨q, hq, c, hc, hz⟩ + have hmem : q ∈ qs := by rw [← List.mem_toFinset, hset]; exact hq + obtain ⟨j, hj⟩ := List.mem_iff_get.mp hmem + obtain ⟨ha, hb⟩ := hcoords j + have hpair : (lopInstance wAB wBC wAC (D m) g p idx).HasCommonNeighbor q.1 q.2 := by + apply (hasCommonNeighbor_lopInstance_iff wAB wBC wAC + (Nat.mem_sqrtPrimeWindow_iff_sq.mp hp).1.ne_zero hq).mpr + exact ⟨c, hc, by rw [hz]⟩ + have hprod := (RecordProduct.product_ne_zero_iff values wAB wBC wAC hD hg hp + hvaluesBC hvaluesAC idx q.1 q.2).mpr hpair + refine ⟨j.val, j.isLt, ?_, c.val, ?_, ?_, ?_⟩ + · intro hzero + apply hprod + have he := hanswer j + rw [hj] at he + apply Signed.encode_injective + exact he.symm.trans hzero + · exact ((mem_piece_iff_interval hD hg c).mp hc).1 + · exact ((mem_piece_iff_interval hD hg c).mp hc).2 + · rw [ha, hb, hj, hAB, hBC, hAC] + exact hz + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Flat.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Flat.lean new file mode 100644 index 000000000..247834a07 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Flat.lean @@ -0,0 +1,103 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Entry + +/-! +# Actual initialized flat witness-recovery runs + +One fixed standard unsigned program starts from the original input, computes the actual first +record's product and verifies its accepted pairs. The same actual state witnesses the prefix +run, final charged halt, original-coordinate result, false-positive accounting and caller frame. +This is a per-record endpoint; choosing parameters and the outer ET record loop remain separate. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +/-- The complete initialized standard-native program uses 400 fixed registers, fewer than +133120 flat instruction slots including halt, and never inspects physical word width. -/ +theorem entryProgram_fixed : (entryProgram.program Reg.found).length ≤ 133120 ∧ + ¬(entryProgram.program Reg.found).ReadsWidth := by + constructor + · simp only [Code.program, List.length_append, Code.length_assemble, + List.length_singleton, entryProgram, Code.size, produce, RecordProduct.produce, + program, Code.size_rename] + have hc := RecordProduct.program_fixed.1 + have hp := Instances.program_fixed.1 + have hs := consume_fixed.1 + omega + · apply Code.not_readsWidth_program + simp only [entryProgram, Code.ReadsWidth, produce, RecordProduct.produce, + Code.readsWidth_rename, Op.ReadsWidth, false_or, program] + exact not_or.mpr ⟨Instances.program_fixed.2, + not_or.mpr ⟨RecordProduct.program_fixed.2, consume_fixed.2⟩⟩ + +/-- Every sufficiently wide physical word size executes the actual original-input program. +Returned success is exactly an original zero in this actual selected chunk/piece, and every +returned coordinate belongs to that original witness. The instruction bound includes halt. -/ +theorem runs_input {W m n g p A : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hD : 16 ≤ D m) + (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) (hN : D m ^ 18 ≤ n) + (hv : ∀ z ∈ values, z.natAbs ≤ A) (hW : admissionWidth n (D m) g p A ≤ W) : + let V := fun q => values.getD q 0 + let wAB := fun a b : Fin n => V (pairIndex (a, b)) + let wBC := fun b c : Fin n => V (n * n + pairIndex (b, c)) + let wAC := fun a c : Fin n => V (2 * (n * n) + pairIndex (a, c)) + InputFits W (PreparationInput.input n (D m) g p values) ∧ + ∃ a : Data 64, ∃ idx : InstanceIndex p, + ExecWithin W Instances.program (.init (PreparationInput.input n (D m) g p values)) a + (RecordProduct.preparationTime n (D m) g p values - 1) ∧ + Instances.Output n (D m) g p V (.init (PreparationInput.input n (D m) g p values)) a ∧ + idx ∈ instanceIndices wAB (D m) g p ∧ + RecordProduct.recordOrdinal n (D m) g p V idx = 0 ∧ + let hn : 0 < n := lt_of_lt_of_le (by omega : 0 < D m) + ((Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN) + let Q := RecordProduct.recordQueryCount n (D m) p V idx + let qs := RecordProduct.decodedQueries hn a.mem (RecordProduct.recordQueries n (D m) p idx) Q + let length := min (pieceSize (D m) g) (n - idx.2.2 * pieceSize (D m) g) + ∃ y t S, t + 1 ≤ RecordProduct.preparationTime n (D m) g p values + + RecordProduct.timeBound (m := m) qs + 5 * Nat.sqrt (D m) + 8 * Q + + (42 * length + 28) * S + 53 ∧ + Runs W (entryProgram.program Reg.found) + (State.init (PreparationInput.input n (D m) g p values)) ⟨entryProgram.size, y⟩ t ∧ + Executes W (entryProgram.program Reg.found) + (State.init (PreparationInput.input n (D m) g p values)) (y.regs Reg.found) (t + 1) ∧ + y.Bounded W ∧ y.regs Reg.scans = S ∧ + (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ + S ≤ falsePositiveCount wAB wBC wAC p + y.regs Reg.found ∧ + (y.regs Reg.found = 1 ↔ + ∃ q ∈ chunkOf wAB (D m) p idx.1 idx.2.1, + ∃ c ∈ piece n (D m) g idx.2.2, weightSum wAB wBC wAC q.1 q.2 c = 0) ∧ + (y.regs Reg.found = 1 → ∃ u v c : Fin n, + y.regs Reg.witnessA = u.val ∧ y.regs Reg.witnessB = v.val ∧ + y.regs Reg.witnessC = c.val ∧ (u, v) ∈ chunkOf wAB (D m) p idx.1 idx.2.1 ∧ + c ∈ piece n (D m) g idx.2.2 ∧ weightSum wAB wBC wAC u v c = 0) ∧ + (∀ u, u < a.regs Instances.heap ∨ + a.regs Instances.heap + CallableProduct.windowSize n Q ≤ u → y.mem u = a.mem u) ∧ + ∀ j : Fin 64, y.regs (productBank (RecordProduct.producerBank j)) = a.regs j := by + have htwo : 2 ≤ W := by unfold admissionWidth at hW; omega + have heq : W - 2 + 2 = W := Nat.sub_add_cancel htwo + have h := exec_input (w := W - 2) values hlen hD hg hp hN hv + (by simpa only [heq] using hW) + rw [heq] at h + obtain ⟨hi, a, idx, ha, hout, hidx, hord, y, S, he, yb, ys, yf, hS, yiff, yw, frame, regs⟩ := h + obtain ⟨t, ht, hex⟩ := he + refine ⟨hi, a, idx, ha, hout, hidx, hord, y, t, S, by omega, + ?_, ?_, yb, ys, yf, hS, yiff, yw, frame, regs⟩ + · simpa only [Nat.zero_add, State.init] using hex.runs (pc := 0) + (p := entryProgram.program Reg.found) (Program.Contains.self _).append_left + · simpa only [State.init] using hex.executes Reg.found + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Header.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Header.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Kernel.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Kernel.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Original.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Original.lean new file mode 100644 index 000000000..7f7ea3d07 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Original.lean @@ -0,0 +1,46 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Program +public import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationInput + +/-! +# Original signed arrays after real preparation + +The producer's existing prefix frame retains every original signed input word. These internal +readback rules relate the recovery bank to those same words and to their original magnitude +bound; they do not assume intermediate signed fits or add a second input encoding. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM + +/-- Each original payload word survives preparation in the unchanged caller prefix. -/ +theorem original_word {n D g p q : ℕ} (values : List ℤ) (a : Data 64) (z : Data 400) + (hout : Instances.Output n D g p (fun q => values.getD q 0) + (.init (PreparationInput.input n D g p values)) a) + (hmap : (z.comap productBank).comap RecordProduct.producerBank = a) + (hq : q < 3 * (n * n)) : z.mem (5 + q) = Signed.encode (values.getD q 0) := by + have hm : z.mem = a.mem := congrArg (fun d : Data 64 => d.mem) hmap + rw [hm, hout.2.2.2.2.2.2.2.2.2.2.2 (5 + q) (Or.inl (by omega))] + exact PreparationInput.input_word n D g p values q + +/-- Original magnitude bounds supply every original array entry, not merely a fitting +intermediate machine state. The implicit index equation is the producer's existing layout. -/ +theorem original_magnitude {n A q : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hv : ∀ z ∈ values, z.natAbs ≤ A) + (hq : q < 3 * (n * n)) : (values.getD q 0).natAbs ≤ A := by + have hi : q < values.length := by omega + rw [List.getD_eq_getElem _ _ hi] + exact hv _ (List.getElem_mem hi) + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Pair.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Pair.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Prepare.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Prepare.lean new file mode 100644 index 000000000..d69d7b429 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Prepare.lean @@ -0,0 +1,113 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Width + +/-! +# Real initialized preparation for candidate recovery + +This internal composition rule executes the existing producer on its complete renamed bank, +then selects ordinal zero with one real instruction. Nonemptiness and the producer's original +word bound are derived from the original input and actual execution. No prepared catalogue, +index, run or intermediate fit certificate is an input to this rule. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Actual preparation and charged first-record selection, from the original initialized +input. All later structured/flat clients reuse this same producer state and selected record. -/ +theorem prepare_input {w m n g p A : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hD : 16 ≤ D m) + (hg : 1 ≤ g) (hp : 0 < p) (hN : D m ^ 18 ≤ n) + (hv : ∀ z ∈ values, z.natAbs ≤ A) + (hW : admissionWidth n (D m) g p A ≤ w + 2) : + InputFits (w + 2) (PreparationInput.input n (D m) g p values) ∧ + ∃ a : Data 64, ∃ idx : InstanceIndex p, ∃ z : Data 400, + ExecWithin (w + 2) Instances.program + (.init (PreparationInput.input n (D m) g p values)) a + (RecordProduct.preparationTime n (D m) g p values - 1) ∧ + Instances.Output n (D m) g p (fun q => values.getD q 0) + (.init (PreparationInput.input n (D m) g p values)) a ∧ + idx ∈ instanceIndices (fun a b : Fin n => values.getD (pairIndex (a, b)) 0) (D m) g p ∧ + RecordProduct.recordOrdinal n (D m) g p (fun q => values.getD q 0) idx = 0 ∧ + ExecWithin (w + 2) + (.seq produce (.op (.const (productBank RecordProduct.Reg.ordinal) 0))) + (.init (PreparationInput.input n (D m) g p values)) z + (RecordProduct.preparationTime n (D m) g p values) ∧ + (z.comap productBank).comap RecordProduct.producerBank = a ∧ + z.regs (productBank RecordProduct.Reg.ordinal) = 0 ∧ + z.regs (productBank 42) ≤ PreparationInput.wordBound n (D m) g p A := by + have hDn : D m ≤ n := (Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN + have hw : PreparationInput.width n (D m) g p A ≤ w + 2 := by + unfold admissionWidth RecordProduct.admissionWidth at hW + omega + obtain ⟨a, ha, hout, hi, -⟩ := PreparationInput.exec_input values hlen + (by omega) hDn hg hp hv hw + have ha' : ExecWithin (w + 2) Instances.program + (.init (PreparationInput.input n (D m) g p values)) a + (RecordProduct.preparationTime n (D m) g p values - 1) := ha + have hf : Function.Injective RecordProduct.producerBank := by + intro i j h + exact Fin.ext (congrArg (fun k : Fin 320 => k.val) h) + have hf' : Function.Injective productBank := by + intro i j h + exact Fin.ext (congrArg (fun k : Fin 400 => k.val) h) + let x₀ : Data 320 := .init (PreparationInput.input n (D m) g p values) + let x := a.extend RecordProduct.producerBank x₀ + have hx : ExecWithin (w + 2) RecordProduct.produce x₀ x + (RecordProduct.preparationTime n (D m) g p values - 1) := ha'.rename hf + let y₀ : Data 400 := .init (PreparationInput.input n (D m) g p values) + let y := x.extend productBank y₀ + have hy : ExecWithin (w + 2) produce y₀ y + (RecordProduct.preparationTime n (D m) g p values - 1) := hx.rename hf' + let z := y.set (productBank RecordProduct.Reg.ordinal) 0 + have hz : ExecWithin (B := .standard) (w + 2) + (.op (.const (productBank RecordProduct.Reg.ordinal) 0)) y z 1 := + ExecWithin.const (fits_zero _) + have low (j : Fin 64) : y.regs (productBank (RecordProduct.producerBank j)) = a.regs j := + (hf'.extend_apply _ _ _).trans (hf.extend_apply _ _ _) + have zmap : (z.comap productBank).comap RecordProduct.producerBank = a := by + ext j + · change Function.update y.regs (productBank RecordProduct.Reg.ordinal) 0 + (productBank (RecordProduct.producerBank j)) = a.regs j + have hne : productBank (RecordProduct.producerBank j) ≠ + productBank RecordProduct.Reg.ordinal := by + intro he + have hh := congrArg (fun k : Fin 400 => k.val) he + change j.val = 64 at hh + omega + rw [Function.update_of_ne hne] + exact low j + · rfl + obtain ⟨idx, hidx, hord⟩ := RecordProduct.first_index (fun q => values.getD q 0) + hD hDn hg (by omega : p ≠ 0) + obtain ⟨t, ht, he⟩ := ha' + have hr : Runs (w + 2) PreparationInput.flatProgram + (State.init (PreparationInput.input n (D m) g p values)) + ⟨Instances.program.size, a⟩ t := by + simpa only [Nat.zero_add, State.init] using he.runs (pc := 0) + (p := PreparationInput.flatProgram) (Program.Contains.self _).append_left + have hb := PreparationInput.bounded_reachable values hlen (by omega) hDn hg hp hv hw + ⟨Instances.program.size, a⟩ t hr + refine ⟨hi, a, idx, z, ⟨t, ht, he⟩, hout, hidx, hord, ?_, zmap, rfl, ?_⟩ + · have hpos : 1 ≤ RecordProduct.preparationTime n (D m) g p values := by + dsimp only [RecordProduct.preparationTime, Instances.timeBound] + omega + exact (hy.seq hz).mono (by omega) + · have hheap : z.regs (productBank 42) = a.regs Instances.heap := + congrArg (fun d : Data 64 => d.regs Instances.heap) zmap + rw [hheap] + exact (hb.1 Instances.heap).le + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Program.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Program.lean old mode 100755 new mode 100644 index 2f6468795..5e760b606 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Program.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Program.lean @@ -1,188 +1,190 @@ -/- -Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Anthropic, PBC, Erik Demaine --/ - -module --- upstream: candidate - -public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct -public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.QueryValues -public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Exec -public import Complexity.Algorithms.WordRAM.NatSqrt - -/-! -# Native consumption of one prepared record's product answers - -The existing 320-register producer/product bank stays in place. A disjoint controller reads -the actual record, derives the original piece interval with charged square-root/ceiling code, -and reads nonzero encoded product answers and their raw interleaved coordinates. Each accepted -pair invokes the real original-weight verifier. Once a witness is found, later scans are skipped. -All memory is read-only in this phase; no result, catalogue or window snapshot is needed. - -The consumer adapts the control of the pinned `Witnesses/ScanPairs.lean` procedure in the -proof of Theorem 17 at `anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. -The native ABI, code and charged constants are original. These stage definitions are internal -implementation interfaces. The outer instance loop and prime/global parameter choice are separate. --/ - -public section - -namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness - -open Cslib.WordRAM - -namespace Reg - -/-- Absolute product-answer pointer. -/ -abbrev output : Fin 400 := 320 -/-- Protected interleaved query-pair pointer. -/ -abbrev queries : Fin 400 := 321 -/-- Actual number of query occurrences. -/ -abbrev count : Fin 400 := 322 -/-- Original outer dimension. -/ -abbrev size : Fin 400 := 323 -/-- First original vertex of this piece. -/ -abbrev first : Fin 400 := 324 -/-- Actual number of original vertices in this piece. -/ -abbrev length : Fin 400 := 325 -/-- Number of query occurrences already treated. -/ -abbrev index : Fin 400 := 326 -/-- Constant one. -/ -abbrev one : Fin 400 := 327 -/-- Actual memory-address temporary. -/ -abbrev address : Fin 400 := 328 -/-- Loaded canonical-signed product answer. -/ -abbrev answer : Fin 400 := 329 -/-- Actual piece ordinal read from the prepared record. -/ -abbrev piece : Fin 400 := 332 -/-- Computed maximum piece length. -/ -abbrev blockLength : Fin 400 := 333 -/-- Original positive piece divisor. -/ -abbrev divisor : Fin 400 := 334 -/-- Index/product temporary. -/ -abbrev temp : Fin 400 := 335 -/-- Raw unsigned success flag. -/ -abbrev found : Fin 400 := 392 -/-- Original first coordinate of a verified triangle. -/ -abbrev witnessA : Fin 400 := 393 -/-- Original second coordinate of a verified triangle. -/ -abbrev witnessB : Fin 400 := 394 -/-- Original third coordinate of a verified triangle. -/ -abbrev witnessC : Fin 400 := 395 -/-- Actual number of original-weight interval scans performed. -/ -abbrev scans : Fin 400 := 396 -/-- Constant zero, disjoint from both procedure banks. -/ -abbrev zero : Fin 400 := 399 - -end Reg - -/-- Preserve the complete existing producer/product register bank. -/ -@[expose] def productBank : Fin 320 → Fin 400 := Fin.castLE (by decide) - -/-- The entire thirty-two-register original-weight verifier moves together. -/ -@[expose] def scanBank (i : Fin 32) : Fin 400 := ⟨336 + i.val, by omega⟩ - -/-- The root computation temporarily reuses the first six verifier registers. -/ -@[expose] def sqrtBank (i : Fin 6) : Fin 400 := ⟨336 + i.val, by omega⟩ - -/-- Actual charged square root for the local piece-size decoder. -/ -@[expose] def squareRoot : Code .standard 400 := NatSqrt.program.rename sqrtBank - -open Reg in -/-- Read original scalar words and the selected record's piece/query fields. -/ -@[expose] def readRecord : Code .standard 400 := - let sqrtArg := sqrtBank NatSqrt.arg - let productResult := productBank RecordProduct.Reg.result - let ordinal := productBank RecordProduct.Reg.ordinal - let records := productBank (RecordProduct.producerBank Instances.output) - ram[ - one ← #1; zero ← #0; address ← #3; size ← *address; - address ← #1; temp ← *address; sqrtArg ← temp; - address ← #2; divisor ← *address; output ← productResult; - temp ← size * temp; temp ← temp + temp; piece ← #6; temp ← temp + piece; - address ← ordinal * temp; - address ← records + address; address ← address + one; piece ← *address; - address ← address + one; queries ← *address; address ← address + one; count ← *address; - found ← #0; witnessA ← #0; witnessB ← #0; witnessC ← #0; scans ← #0; index ← #0 ] - -open Reg in -/-- Compute the original interval and deliver the unchanged signed-array pointers once. -/ -@[expose] def finishHeader : Code .standard 400 := - let root := sqrtBank NatSqrt.root - let scanAB := scanBank WitnessScan.Reg.ab - let scanBC := scanBank WitnessScan.Reg.bc - let scanAC := scanBank WitnessScan.Reg.ac - let scanN := scanBank WitnessScan.Reg.size - let scanFirst := scanBank WitnessScan.Reg.first - let scanLength := scanBank WitnessScan.Reg.length - ram[ - blockLength ← root + divisor; blockLength ← blockLength - one; - blockLength ← blockLength / divisor; first ← piece * blockLength; length ← size - first; - if blockLength < length then [ length ← blockLength ] else [ length ← length ]; - scanAB ← #5; temp ← size * size; - scanBC ← scanAB + temp; - scanAC ← scanBC + temp; - scanN ← size; scanFirst ← first; - scanLength ← length ] - -/-- Complete actual local descriptor and interval decoding. -/ -@[expose] def header : Code .standard 400 := - .seq readRecord (.seq squareRoot finishHeader) - -/-- Actual original signed-weight scan, with the complete bank renamed together. -/ -@[expose] def scan : Code .standard 400 := WitnessScan.program.rename scanBank - -open Reg in -/-- Load the real original coordinate pair and charge this invocation in the native counter. -/ -@[expose] def deliverPair : Code .standard 400 := - let scanA := scanBank WitnessScan.Reg.a - let scanB := scanBank WitnessScan.Reg.b - ram[ - address ← index + index; address ← address + queries; - scanA ← *address; address ← address + one; - scanB ← *address; scans ← scans + one ] - -open Reg in -/-- A verifier success returns its original vertex together with the actual pair that was read. -/ -@[expose] def receiveWitness : Code .standard 400 := - let scanFound := scanBank WitnessScan.Reg.found - let scanA := scanBank WitnessScan.Reg.a - let scanB := scanBank WitnessScan.Reg.b - let scanC := scanBank WitnessScan.Reg.witness - ram[ - if scanFound = one then [ - found ← one; witnessA ← scanA; witnessB ← scanB; - witnessC ← scanC ] else [ found ← found ] ] - -/-- The accepted-candidate branch contains the real verifier, not a supplied witness routine. -/ -@[expose] def scanPair : Code .standard 400 := - .seq deliverPair (.seq scan receiveWitness) - -open Reg in -/-- Read a product answer only while searching, and skip all later scans after a success. -/ -@[expose] def step : Code .standard 400 := - .seq (.branch (.eq found zero) - (.seq (ram[ address ← output + index; answer ← *address ]) - (.branch (.eq answer zero) (.op (.copy answer answer)) scanPair)) - (.op (.copy answer answer))) (.op (.bin .add index index one)) - -/-- Consume one actual product-answer slice without modifying any memory. -/ -@[expose] def consume : Code .standard 400 := - .seq header (.loop (.lt Reg.index Reg.count) step) - -/-- Actual product calculation followed immediately by original-weight verification. -/ -@[expose] def program : Code .standard 400 := - .seq (RecordProduct.program.rename productBank) consume - -/-- Original initialized producer/product code followed by the real candidate consumer. -The fixed first-record client reloads its selected ordinal with one charged instruction. -/ -@[expose] def entryProgram : Code .standard 400 := - .seq (RecordProduct.entryProgram.rename productBank) - (.seq (.op (.const (productBank RecordProduct.Reg.ordinal) 0)) consume) - -/-- The consumer is one fixed finite instruction graph and never reads the physical width. -/ -theorem consume_fixed : consume.size ≤ 512 ∧ ¬consume.ReadsWidth := by decide - -end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.QueryValues +public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Exec +public import Complexity.Algorithms.WordRAM.NatSqrt + +/-! +# Native consumption of one prepared record's product answers + +The existing 320-register producer/product bank stays in place. A disjoint controller reads +the actual record, derives the original piece interval with charged square-root/ceiling code, +and reads nonzero encoded product answers and their raw interleaved coordinates. Each accepted +pair invokes the real original-weight verifier. Once a witness is found, later scans are skipped. +All memory is read-only in this phase; no result, catalogue or window snapshot is needed. + +The consumer adapts the control of the pinned `Witnesses/ScanPairs.lean` procedure in the +proof of Theorem 17 at `anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. +The native ABI, code and charged constants are original. These stage definitions are internal +implementation interfaces. The outer instance loop and prime/global parameter choice are separate. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM + +namespace Reg + +/-- Absolute product-answer pointer. -/ +abbrev output : Fin 400 := 320 +/-- Protected interleaved query-pair pointer. -/ +abbrev queries : Fin 400 := 321 +/-- Actual number of query occurrences. -/ +abbrev count : Fin 400 := 322 +/-- Original outer dimension. -/ +abbrev size : Fin 400 := 323 +/-- First original vertex of this piece. -/ +abbrev first : Fin 400 := 324 +/-- Actual number of original vertices in this piece. -/ +abbrev length : Fin 400 := 325 +/-- Number of query occurrences already treated. -/ +abbrev index : Fin 400 := 326 +/-- Constant one. -/ +abbrev one : Fin 400 := 327 +/-- Actual memory-address temporary. -/ +abbrev address : Fin 400 := 328 +/-- Loaded canonical-signed product answer. -/ +abbrev answer : Fin 400 := 329 +/-- Actual piece ordinal read from the prepared record. -/ +abbrev piece : Fin 400 := 332 +/-- Computed maximum piece length. -/ +abbrev blockLength : Fin 400 := 333 +/-- Original positive piece divisor. -/ +abbrev divisor : Fin 400 := 334 +/-- Index/product temporary. -/ +abbrev temp : Fin 400 := 335 +/-- Raw unsigned success flag. -/ +abbrev found : Fin 400 := 392 +/-- Original first coordinate of a verified triangle. -/ +abbrev witnessA : Fin 400 := 393 +/-- Original second coordinate of a verified triangle. -/ +abbrev witnessB : Fin 400 := 394 +/-- Original third coordinate of a verified triangle. -/ +abbrev witnessC : Fin 400 := 395 +/-- Actual number of original-weight interval scans performed. -/ +abbrev scans : Fin 400 := 396 +/-- Constant zero, disjoint from both procedure banks. -/ +abbrev zero : Fin 400 := 399 + +end Reg + +/-- Preserve the complete existing producer/product register bank. -/ +@[expose] def productBank : Fin 320 → Fin 400 := Fin.castLE (by decide) + +/-- The entire thirty-two-register original-weight verifier moves together. -/ +@[expose] def scanBank (i : Fin 32) : Fin 400 := ⟨336 + i.val, by omega⟩ + +/-- The root computation temporarily reuses the first six verifier registers. -/ +@[expose] def sqrtBank (i : Fin 6) : Fin 400 := ⟨336 + i.val, by omega⟩ + +/-- Actual charged square root for the local piece-size decoder. -/ +@[expose] def squareRoot : Code .standard 400 := NatSqrt.program.rename sqrtBank + +open Reg in +/-- Read original scalar words and the selected record's piece/query fields. -/ +@[expose] def readRecord : Code .standard 400 := + let sqrtArg := sqrtBank NatSqrt.arg + let productResult := productBank RecordProduct.Reg.result + let ordinal := productBank RecordProduct.Reg.ordinal + let records := productBank (RecordProduct.producerBank Instances.output) + ram[ + one ← #1; zero ← #0; address ← #3; size ← *address; + address ← #1; temp ← *address; sqrtArg ← temp; + address ← #2; divisor ← *address; output ← productResult; + temp ← size * temp; temp ← temp + temp; piece ← #6; temp ← temp + piece; + address ← ordinal * temp; + address ← records + address; address ← address + one; piece ← *address; + address ← address + one; queries ← *address; address ← address + one; count ← *address; + found ← #0; witnessA ← #0; witnessB ← #0; witnessC ← #0; scans ← #0; index ← #0 ] + +open Reg in +/-- Compute the original interval and deliver the unchanged signed-array pointers once. -/ +@[expose] def finishHeader : Code .standard 400 := + let root := sqrtBank NatSqrt.root + let scanAB := scanBank WitnessScan.Reg.ab + let scanBC := scanBank WitnessScan.Reg.bc + let scanAC := scanBank WitnessScan.Reg.ac + let scanN := scanBank WitnessScan.Reg.size + let scanFirst := scanBank WitnessScan.Reg.first + let scanLength := scanBank WitnessScan.Reg.length + ram[ + blockLength ← root + divisor; blockLength ← blockLength - one; + blockLength ← blockLength / divisor; first ← piece * blockLength; length ← size - first; + if blockLength < length then [ length ← blockLength ] else [ length ← length ]; + scanAB ← #5; temp ← size * size; + scanBC ← scanAB + temp; + scanAC ← scanBC + temp; + scanN ← size; scanFirst ← first; + scanLength ← length ] + +/-- Complete actual local descriptor and interval decoding. -/ +@[expose] def header : Code .standard 400 := + .seq readRecord (.seq squareRoot finishHeader) + +/-- Actual original signed-weight scan, with the complete bank renamed together. -/ +@[expose] def scan : Code .standard 400 := WitnessScan.program.rename scanBank + +open Reg in +/-- Load the real original coordinate pair and charge this invocation in the native counter. -/ +@[expose] def deliverPair : Code .standard 400 := + let scanA := scanBank WitnessScan.Reg.a + let scanB := scanBank WitnessScan.Reg.b + ram[ + address ← index + index; address ← address + queries; + scanA ← *address; address ← address + one; + scanB ← *address; scans ← scans + one ] + +open Reg in +/-- A verifier success returns its original vertex together with the actual pair that was read. -/ +@[expose] def receiveWitness : Code .standard 400 := + let scanFound := scanBank WitnessScan.Reg.found + let scanA := scanBank WitnessScan.Reg.a + let scanB := scanBank WitnessScan.Reg.b + let scanC := scanBank WitnessScan.Reg.witness + ram[ + if scanFound = one then [ + found ← one; witnessA ← scanA; witnessB ← scanB; + witnessC ← scanC ] else [ found ← found ] ] + +/-- The accepted-candidate branch contains the real verifier, not a supplied witness routine. -/ +@[expose] def scanPair : Code .standard 400 := + .seq deliverPair (.seq scan receiveWitness) + +open Reg in +/-- Read a product answer only while searching, and skip all later scans after a success. -/ +@[expose] def step : Code .standard 400 := + .seq (.branch (.eq found zero) + (.seq (ram[ address ← output + index; answer ← *address ]) + (.branch (.eq answer zero) (.op (.copy answer answer)) scanPair)) + (.op (.copy answer answer))) (.op (.bin .add index index one)) + +/-- Consume one actual product-answer slice without modifying any memory. -/ +@[expose] def consume : Code .standard 400 := + .seq header (.loop (.lt Reg.index Reg.count) step) + +/-- Actual product calculation followed immediately by original-weight verification. -/ +@[expose] def program : Code .standard 400 := + .seq (RecordProduct.program.rename productBank) consume + +/-- The real producer occupies the existing low bank of the larger caller. -/ +@[expose] def produce : Code .standard 400 := RecordProduct.produce.rename productBank + +/-- Original initialized producer, actual first-record selection and actual product/verifier. +The selected ordinal is preserved by the real product, so no later reset is needed. -/ +@[expose] def entryProgram : Code .standard 400 := + .seq produce (.seq (.op (.const (productBank RecordProduct.Reg.ordinal) 0)) program) + +/-- The consumer is one fixed finite instruction graph and never reads the physical width. -/ +theorem consume_fixed : consume.size ≤ 512 ∧ ¬consume.ReadsWidth := by decide + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Queries.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Queries.lean old mode 100755 new mode 100644 index 82fc4a87c..5af87ea52 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Queries.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Queries.lean @@ -74,7 +74,7 @@ theorem exec_queries {w U n first length out queries Q : ℕ} y.regs Reg.index = Q ∧ y.regs Reg.scans = S ∧ S ≤ Nat.count (fun i => x.mem (out + i) ≠ 0 ∧ ∀ c : Fin length, AB (a i) (b i) + BC (b i) (first + c.val) + - AC (a i) (first + c.val) ≠ 0) Q + 1 ∧ + AC (a i) (first + c.val) ≠ 0) Q + y.regs Reg.found ∧ (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ (y.regs Reg.found = 1 ↔ ∃ i < Q, x.mem (out + i) ≠ 0 ∧ ∃ c, first ≤ c ∧ c < first + length ∧ AB (a i) (b i) + BC (b i) c + AC (a i) c = 0) ∧ @@ -216,13 +216,11 @@ theorem exec_queries {w U n first length out queries Q : ℕ} funext i simp only [accept, hit, Bool.and_eq_true, decide_eq_true_eq, Bool.not_eq_true_eq_eq_false, decide_eq_false_iff_not, not_exists] - have flag_le : (foundBefore accept hit Q).toNat ≤ 1 := by - cases foundBefore accept hit Q <;> decide refine ⟨y, Nat.count active Q, ?_, yFixed, ym, yindex, ?_, ?_, ?_, ?_, ?_⟩ · simpa only [Φ, hi, Nat.sub_zero, Nat.count_zero, Nat.mul_zero, Nat.add_zero] using he · simpa only [yindex] using ys - · rw [← hfail] - exact hcount.trans (Nat.add_le_add_left flag_le _) + · rw [← hfail, yf, yindex] + exact hcount · rw [yf, yindex] cases foundBefore accept hit Q <;> simp · rw [yf, yindex] diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Readback.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Readback.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/SourceBounds.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/SourceBounds.lean new file mode 100644 index 000000000..ff3be4fa4 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/SourceBounds.lean @@ -0,0 +1,151 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Flat + +/-! +# Original-size width and prefix bounds for candidate recovery + +The complete initialized producer/product/verifier has one positive logarithmic physical-width +threshold for polynomial original scalars and weights. Its words and memory support at every +prefix have a polynomial bound independent of any larger physical width. These are consequences +of the actual minimum-width halting run and existing native width transport. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +/-- Every admitted physical width can be chosen logarithmic in original size. The positive +constant is explicit for each original magnitude exponent; geometry is not weakened. -/ +theorem admissionWidth_le_log {n D g p A κ : ℕ} (hn : 1 ≤ n) + (hD : D ≤ n) (hg : g ≤ n) (hp : p ≤ n) (hA : A ≤ (n + 2) ^ κ) : + admissionWidth n D g p A ≤ (257 * κ + 5909) * Nat.clog 2 (n + 2) := by + let b := n + 2 + let E := 2 * κ + 512 + have hb : 2 ≤ b := by dsimp only [b]; omega + have hb1 : 1 ≤ b := le_trans (by decide : 1 ≤ (2 : ℕ)) hb + have hP : PreparationInput.wordBound n D g p A ≤ b ^ E := by + exact (PreparationInput.wordBound_le_pow hn hD hg hp hA).trans + (Nat.pow_le_pow_right hb1 (by dsimp only [E]; omega)) + have hfour : 2 * (n + n ^ 2 + 3) ≤ b ^ 4 := by + dsimp only [b] + nlinarith only [Nat.zero_le (n ^ 4), Nat.zero_le (n ^ 3), Nat.zero_le (n ^ 2), + Nat.zero_le n] + have hfour' : 2 * (n + n ^ 2 + 3) ≤ (b ^ 2) ^ 2 := hfour.trans_eq (Nat.pow_mul b 2 2) + have hM : CallableProduct.windowSize n (n ^ 2) ≤ b ^ E := by + calc + _ ≤ (2 * (n + n ^ 2 + 3)) ^ 128 := CallableProduct.windowSize_le n (n ^ 2) + _ ≤ ((b ^ 2) ^ 2) ^ 128 := Nat.pow_le_pow_left hfour' _ + _ = (b ^ 2) ^ 256 := (Nat.pow_mul (b ^ 2) 2 128).symm + _ ≤ (b ^ 2) ^ (κ + 256) := Nat.pow_le_pow_right (Nat.pow_pos hb1) + (Nat.le_add_left 256 κ) + _ = b ^ E := by + rw [← Nat.pow_mul] + exact congrArg (fun j : ℕ => b ^ j) + (by dsimp only [E]; omega : 2 * (κ + 256) = E) + have hn2 : n ^ 2 ≤ b ^ E := (Nat.pow_le_pow_left (by dsimp only [b]; omega : n ≤ b) 2).trans + (Nat.pow_le_pow_right hb1 (by dsimp only [E]; omega)) + have hnE : n ≤ b ^ E := (show n ≤ n ^ 2 by nlinarith only [hn]).trans hn2 + have hprod : n * D ≤ b ^ E := (Nat.mul_le_mul_left n hD).trans + (by simpa only [pow_two] using hn2) + have hAE : A ≤ b ^ E := hA.trans + (Nat.pow_le_pow_right hb1 (by dsimp only [E]; omega)) + have hconst : 4096 ≤ b ^ E := (show 4096 ≤ (2 : ℕ) ^ 12 by decide).trans + ((Nat.pow_le_pow_left hb 12).trans + (Nat.pow_le_pow_right hb1 (by dsimp only [E]; omega))) + have hsum : envelope n D g p A ≤ 576 * b ^ E := by + unfold envelope + have hgE := hg.trans hnE + have hDE := hD.trans hnE + omega + have henvelope : envelope n D g p A ≤ b ^ (2 * κ + 522) := by + calc + _ ≤ 576 * b ^ E := hsum + _ ≤ b ^ 10 * b ^ E := Nat.mul_le_mul_right _ + ((show 576 ≤ (2 : ℕ) ^ 10 by decide).trans (Nat.pow_le_pow_left hb 10)) + _ = b ^ (2 * κ + 522) := by + rw [← Nat.pow_add] + exact congrArg (fun j : ℕ => b ^ j) (by dsimp only [E]; omega : 10 + E = 2 * κ + 522) + have hbase := Nat.le_pow_clog (by decide : 1 < (2 : ℕ)) b + have hlogBound := henvelope.trans (Nat.pow_le_pow_left hbase (2 * κ + 522)) + rw [← Nat.pow_mul] at hlogBound + have hlog := Nat.clog_le_of_le_pow hlogBound + change Nat.clog 2 (envelope n D g p A) ≤ Nat.clog 2 b * (2 * κ + 522) at hlog + have hrecord := RecordProduct.admissionWidth_le_log hn hD hg hp hA + have hlog' : Nat.clog 2 (envelope n D g p A) ≤ (257 * κ + 5906) * Nat.clog 2 b := by + nlinarith only [hlog, Nat.zero_le (κ * Nat.clog 2 b)] + have hmax := max_le hrecord hlog' + have hpositive : 1 ≤ Nat.clog 2 b := Nat.clog_pos (by decide) (by omega : 1 < b) + unfold admissionWidth + dsimp only [b] at hmax hpositive + nlinarith only [hmax, hpositive] + +/-- Original-input prefix word/address envelope, independent of the caller's larger width. -/ +@[expose] def wordBound (n D g p A : ℕ) : ℕ := 2 ^ admissionWidth n D g p A + +/-- All prefix words and memory support have a fixed polynomial envelope for each κ. -/ +theorem wordBound_le_pow {n D g p A κ : ℕ} (hn : 1 ≤ n) + (hD : D ≤ n) (hg : g ≤ n) (hp : p ≤ n) (hA : A ≤ (n + 2) ^ κ) : + wordBound n D g p A ≤ (n + 2) ^ (514 * κ + 11818) := by + let b := n + 2 + have hb : 1 < b := by dsimp only [b]; omega + have hc := Nat.clog_pos (by decide : 1 < (2 : ℕ)) hb + have hp2 := Nat.pow_pred_clog_lt_self (by decide : 1 < (2 : ℕ)) hb + have he : Nat.clog 2 b = (Nat.clog 2 b).pred + 1 := (Nat.succ_pred_eq_of_pos hc).symm + have hbase : 2 ^ Nat.clog 2 b ≤ b ^ 2 := by + rw [he, pow_succ] + nlinarith only [hp2, show 2 ≤ b by omega] + have hw := admissionWidth_le_log hn hD hg hp hA + change 2 ^ admissionWidth n D g p A ≤ _ + calc + _ ≤ 2 ^ ((257 * κ + 5909) * Nat.clog 2 b) := Nat.pow_le_pow_right (by decide) hw + _ = (2 ^ Nat.clog 2 b) ^ (257 * κ + 5909) := by rw [← Nat.pow_mul, Nat.mul_comm] + _ ≤ (b ^ 2) ^ (257 * κ + 5909) := Nat.pow_le_pow_left hbase _ + _ = b ^ (514 * κ + 11818) := by + rw [← Nat.pow_mul] + exact congrArg (fun j : ℕ => b ^ j) (by omega : 2 * (257 * κ + 5909) = 514 * κ + 11818) + +/-- At every admitted wider width, every actual prefix has original-input polynomial words +and zero memory above the same original-input envelope. Dirty pre-normalization loads are +included because the entire actual native execution is compared with its minimum-width run. -/ +theorem bounded_reachable_original {W m n g p A k : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hD : 16 ≤ D m) + (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) (hN : D m ^ 18 ≤ n) + (hv : ∀ z ∈ values, z.natAbs ≤ A) (hW : admissionWidth n (D m) g p A ≤ W) + {s : State 400} (hr : Runs W (entryProgram.program Reg.found) + (State.init (PreparationInput.input n (D m) g p values)) s k) : + (∀ i, s.data.regs i < wordBound n (D m) g p A) ∧ + (∀ a, s.data.mem a < wordBound n (D m) g p A) ∧ + ∀ a, wordBound n (D m) g p A ≤ a → s.data.mem a = 0 := by + obtain ⟨hi, a, idx, ha, hout, hidx, hord, y, t, S, ht, hpref, he, -⟩ := + runs_input values hlen hD hg hp hN hv le_rfl + have hlow := Runs.prefix_at_smaller_width entryProgram_fixed.2 hW hr he + have hs := hlow.bounded (Data.Bounded.init hi) + refine ⟨hs.1, hs.2, ?_⟩ + intro a ha + rw [hlow.mem_above (Data.Bounded.init hi) ha] + cases a with + | zero => + change 2 ^ admissionWidth n (D m) g p A ≤ 0 at ha + have := Nat.two_pow_pos (admissionWidth n (D m) g p A) + omega + | succ a => + change (PreparationInput.input n (D m) g p values).getD a 0 = 0 + apply List.getD_eq_default + have hlen := hi.1 + dsimp only [Fits] at hlen + change 2 ^ admissionWidth n (D m) g p A ≤ a + 1 at ha + omega + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Spec.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Spec.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Step.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Step.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/TimeBound.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/TimeBound.lean new file mode 100644 index 000000000..262c0d0a1 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/TimeBound.lean @@ -0,0 +1,84 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.SourceBounds + +/-! +# Charged per-record witness-recovery bound + +The actual producer chunk derives the required occurrence sparsity. Preparation and the thin +product retain their already proved charges, and recovery adds actual root/query/control work +plus full interval scans. The scan surplus is the returned native flag, so unsuccessful records +have no additive successful-scan term. This does not sum the outer instance loop or choose a +prime, and it establishes neither aggregate subcubic ET nor the ordinary 3SUM endpoint. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM Cslib.Algorithms.ExactTriangle Cslib.Matrix +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The quantitative bound applied to the actual initialized execution. Occurrence sparsity +is derived from its computed query count. The scan/count and interval premises are conclusions +of `runs_input`; they are not algorithm inputs or supplied solver/time certificates. -/ +theorem initialized_time_le {m n g p length S flag : ℕ} (values : List ℤ) + (idx : InstanceIndex p) (qs : List (Fin n × Fin n)) + (hcount : qs.length = RecordProduct.recordQueryCount n (D m) p + (fun q => values.getD q 0) idx) + (hD : 16 ≤ D m) (hN : D m ^ 18 ≤ n) (hg : 1 ≤ g) + (hp : p ∈ Nat.sqrtPrimeWindow (D m)) + (hlength : length ≤ pieceSize (D m) g) + (hS : S ≤ falsePositiveCount + (fun a b : Fin n => values.getD (pairIndex (a, b)) 0) + (fun b c : Fin n => values.getD (n * n + pairIndex (b, c)) 0) + (fun a c : Fin n => values.getD (2 * (n * n) + pairIndex (a, c)) 0) p + flag) : + let F := falsePositiveCount + (fun a b : Fin n => values.getD (pairIndex (a, b)) 0) + (fun b c : Fin n => values.getD (n * n + pairIndex (b, c)) 0) + (fun a c : Fin n => values.getD (2 * (n * n) + pairIndex (a, c)) 0) p + ((RecordProduct.preparationTime n (D m) g p values + + RecordProduct.timeBound (m := m) qs + 5 * Nat.sqrt (D m) + 8 * qs.length + + (42 * length + 28) * S + 53 : ℕ) : ℝ) ≤ + 690 * (n : ℝ) ^ 2 * D m * g + + 34603008 * ((19 * m + 1 : ℕ) : ℝ) * (n : ℝ) ^ 2 / + (D m : ℝ) ^ (1 / 18 : ℝ) + + ((5 * Nat.sqrt (D m) + 8 * qs.length + + (42 * pieceSize (D m) g + 28) * (F + flag) + 52 : ℕ) : ℝ) := by + let F := falsePositiveCount + (fun a b : Fin n => values.getD (pairIndex (a, b)) 0) + (fun b c : Fin n => values.getD (n * n + pairIndex (b, c)) 0) + (fun a c : Fin n => values.getD (2 * (n * n) + pairIndex (a, c)) 0) p + change S ≤ F + flag at hS + have hQ : qs.length * 2 ^ m ≤ n ^ 2 := by + rw [hcount] + exact (Nat.mul_le_mul_right (2 ^ m) (min_le_left _ _)).trans + (RecordProduct.queryCap_mul_two_pow_le n m) + have hbase := RecordProduct.initialized_time_le values qs hD hN hg hp hQ + have hscan : (42 * length + 28) * S ≤ + (42 * pieceSize (D m) g + 28) * (F + flag) := + Nat.mul_le_mul (by omega) hS + let extra := 5 * Nat.sqrt (D m) + 8 * qs.length + + (42 * pieceSize (D m) g + 28) * (F + flag) + 52 + have htotal : RecordProduct.preparationTime n (D m) g p values + + RecordProduct.timeBound (m := m) qs + 5 * Nat.sqrt (D m) + 8 * qs.length + + (42 * length + 28) * S + 53 ≤ + RecordProduct.preparationTime n (D m) g p values + + RecordProduct.timeBound (m := m) qs + 1 + extra := by + dsimp only [extra] + omega + calc + _ ≤ ((RecordProduct.preparationTime n (D m) g p values + + RecordProduct.timeBound (m := m) qs + 1 + extra : ℕ) : ℝ) := Nat.cast_le.mpr htotal + _ = ((RecordProduct.preparationTime n (D m) g p values + + RecordProduct.timeBound (m := m) qs + 1 : ℕ) : ℝ) + (extra : ℝ) := Nat.cast_add _ _ + _ ≤ _ := add_le_add hbase (le_rfl : (extra : ℝ) ≤ extra) + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Trace.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Trace.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Width.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Width.lean new file mode 100644 index 000000000..24c43619f --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Width.lean @@ -0,0 +1,100 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Exec + +/-! +# Original-input admission for product-selected witness recovery + +One positive width threshold covers the original producer/product admission, all real +descriptor/query/address arithmetic and both signed additions of original weights. It depends +on the original scalar headers and magnitude envelope, not on the physical width chosen by +the caller. The reserved product window is an address bound, not a charged clearing operation. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +/-- Numerical envelope in original dimensions and weight magnitude; it covers all recovery +setup and calls after the genuine producer has established its own word/address bound. -/ +@[expose] def envelope (n D g p A : ℕ) : ℕ := + 64 * (PreparationInput.wordBound n D g p A + CallableProduct.windowSize n (n ^ 2) + + n * D + n ^ 2 + n + g + D + A + 4096) + +/-- Physical width for the same fixed native program at every larger admitted word size. -/ +@[expose] def admissionWidth (n D g p A : ℕ) : ℕ := + max (RecordProduct.admissionWidth n D g p A) (Nat.clog 2 (envelope n D g p A)) + 3 + +/-- The initialized native threshold is positive for every original scalar tuple. -/ +theorem admissionWidth_pos (n D g p A : ℕ) : 0 < admissionWidth n D g p A := by + unfold admissionWidth + omega + +/-- The existing product's output slice precedes its admitted virtual-window boundary. -/ +theorem outputBase_lt_window {m n Q : ℕ} (hN : D m ^ 18 ≤ n) : + QueriedProduct.outputBase (19 * m) m n Q < CallableProduct.windowSize n Q := by + have hout : QueriedProduct.outputBase (19 * m) m n Q ≤ + QueriedProduct.memoryEnd (19 * m) m n Q := by + unfold QueriedProduct.memoryEnd QueriedProduct.stampBase QueriedProduct.scratchBase + have hten := Nat.zero_le (10 ^ (19 * m)) + have hframes := Nat.zero_le (2 * (19 * m + 1) * 10 ^ (19 * m)) + omega + have hword : QueriedProduct.memoryEnd (19 * m) m n Q ≤ + QueriedProduct.wordBudget (19 * m) m n Q := by + unfold QueriedProduct.wordBudget + omega + exact (hout.trans hword).trans_lt (CallableProduct.source_admission hN).2.2 + +/-- Every pointer and temporary of the actual per-record caller fits from the original +producer word bound and the actual chunk count. No intermediate fit certificate is supplied. -/ +theorem physical_fits {w m n g p A heap Q : ℕ} + (hheap : heap ≤ PreparationInput.wordBound n (D m) g p A) + (hQ : Q ≤ n ^ 2) (hN : D m ^ 18 ≤ n) + (hW : admissionWidth n (D m) g p A ≤ w + 2) : + Fits (w + 2) (4 * heap + 4 * CallableProduct.windowSize n Q + + QueriedProduct.outputBase (19 * m) m n Q + 4 * Q + 4 * D m + + 2 * (n * D m) + 9 * (n * n) + 4 * n + g + 4096) := by + have hwindow : CallableProduct.windowSize n Q ≤ CallableProduct.windowSize n (n ^ 2) := by + unfold CallableProduct.windowSize CallableProduct.sourceWidth + apply Nat.pow_le_pow_right (by decide) + exact Nat.mul_le_mul_left 128 (Nat.clog_mono_right 2 (by omega)) + have hout := (outputBase_lt_window (Q := Q) hN).le.trans hwindow + have he : Fits (w + 2) (envelope n (D m) g p A) := by + have hn := Nat.le_pow_clog (by decide : 1 < (2 : ℕ)) (envelope n (D m) g p A) + have hw : Nat.clog 2 (envelope n (D m) g p A) < w + 2 := by + unfold admissionWidth at hW + omega + exact hn.trans_lt (Nat.pow_lt_pow_right (by decide : 1 < (2 : ℕ)) hw) + apply he.of_le + unfold envelope + rw [pow_two] at hQ hwindow hout ⊢ + omega + +/-- Two signed additions use the original envelope `2A`; the physical compiler extension +is included in the same original-input threshold. -/ +theorem signed_fits {w n D g p A : ℕ} (hW : admissionWidth n D g p A ≤ w + 2) : + GenericRAM.Signed.Fits w (2 * A : ℕ) := by + have hw : Nat.clog 2 (envelope n D g p A) + 1 ≤ w := by + unfold admissionWidth at hW + omega + have he : 2 * A < envelope n D g p A := by unfold envelope; omega + have hp := Nat.le_pow_clog (by decide : 1 < (2 : ℕ)) (envelope n D g p A) + have ht : 2 * A < 2 ^ (w - 1) := he.trans_le + (hp.trans (Nat.pow_le_pow_right (by decide) (by omega))) + refine ⟨by omega, ?_, by exact_mod_cast ht⟩ + have hz : (0 : ℤ) ≤ (2 * A : ℕ) := by exact_mod_cast Nat.zero_le (2 * A) + have hp : (0 : ℤ) ≤ 2 ^ (w - 1) := by positivity + omega + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Cell.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Cell.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Exec.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Exec.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Finite.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Finite.lean new file mode 100644 index 000000000..8dd68e063 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Finite.lean @@ -0,0 +1,67 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Exec +public import Complexity.Algorithms.ExactTriangle.PieceBounds +public import Complexity.Algorithms.ExactTriangle.Witnesses + +/-! +# Native interval failure and existing finite witness scans + +The actual verifier reports the last zero it reads, whereas `scanPiece` returns the first. +Their failure conditions agree: both exhaust precisely the original vertices in the existing +finite piece. This value lemma connects native failure accounting to the existing finite +false-positive theory without identifying the two witness-order conventions. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.WitnessScan + +open Cslib.Algorithms.ExactTriangle + +/-- Native interval exhaustion is exactly failure of the existing finite piece scan. The +array equations describe the same original integer weights; no witness routine is assumed. -/ +theorem fails_iff_scanPiece_eq_none {n D g k : ℕ} + (AB BC AC : ℕ → ℕ → ℤ) (wAB wBC wAC : Fin n → Fin n → ℤ) + (hD : 16 ≤ D) (hg : 1 ≤ g) + (hAB : ∀ u v : Fin n, AB u.val v.val = wAB u v) + (hBC : ∀ u v : Fin n, BC u.val v.val = wBC u v) + (hAC : ∀ u v : Fin n, AC u.val v.val = wAC u v) + (a b : Fin n) : + let first := k * pieceSize D g + let length := min (pieceSize D g) (n - first) + (∀ c : Fin length, AB a.val b.val + BC b.val (first + c.val) + + AC a.val (first + c.val) ≠ 0) ↔ scanPiece wAB wBC wAC D g k a b = none := by + let first := k * pieceSize D g + let length := min (pieceSize D g) (n - first) + have heq (c : Fin n) : AB a.val b.val + BC b.val c.val + AC a.val c.val = + weightSum wAB wBC wAC a b c := by rw [hAB, hBC, hAC]; rfl + rw [scanPiece_eq_none_iff] + constructor + · intro hfail c hc + have hinterval := (mem_piece_iff_interval hD hg c).mp hc + change first ≤ c.val ∧ c.val < first + length at hinterval + let j : Fin length := ⟨c.val - first, by omega⟩ + have he : first + j.val = c.val := by dsimp only [j]; omega + have hz := hfail j + rw [he, heq] at hz + exact hz + · intro hfail j + have hj := j.isLt + have hl := min_le_right (pieceSize D g) (n - first) + have hn : first + j.val < n := by change j.val < length at hj; omega + let c : Fin n := ⟨first + j.val, hn⟩ + have hc : c ∈ piece n D g k := by + apply (mem_piece_iff_interval hD hg c).mpr + exact ⟨Nat.le_add_right _ _, Nat.add_lt_add_left j.isLt _⟩ + have hz := hfail c hc + simpa only [← heq c] using hz + +end Cslib.Algorithms.WordRAM.ExactTriangle.WitnessScan diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Program.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Program.lean old mode 100755 new mode 100644 diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Setup.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Setup.lean old mode 100755 new mode 100644 diff --git a/ComplexityTests/WordRAMExactTriangleQueryValues.lean b/ComplexityTests/WordRAMExactTriangleQueryValues.lean old mode 100755 new mode 100644 diff --git a/ComplexityTests/WordRAMExactTriangleRecordWitness.lean b/ComplexityTests/WordRAMExactTriangleRecordWitness.lean old mode 100755 new mode 100644 index e7024522a..97908121e --- a/ComplexityTests/WordRAMExactTriangleRecordWitness.lean +++ b/ComplexityTests/WordRAMExactTriangleRecordWitness.lean @@ -6,7 +6,7 @@ Authors: Erik Demaine -- upstream: local-only (tests) -import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Consume +import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.SourceBounds /-! # Actual candidate-consumer executions at valid thin geometry @@ -20,7 +20,9 @@ set_option linter.hashCommand false namespace ComplexityTests.WordRAMExactTriangleRecordWitness -open Cslib.WordRAM Cslib.Algorithms.WordRAM.ExactTriangle +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.WordRAM.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage private def side : ℕ := 16 ^ 18 private def queryBase : ℕ := 8 + 6 * (side * side) @@ -66,4 +68,80 @@ private def observe (success zeroAnswers : Bool) : Option (List ℕ × Bool × B #guard side ≥ 16 ^ 18 #guard ¬(16 ≥ 16 ^ 18) +private def zeroWeights (N : ℕ) : List ℤ := List.replicate (3 * (N * N)) 0 +private def width (N : ℕ) : ℕ := RecordWitness.admissionWidth N (D 2) 1 3 0 + +/-- All original-input premises are discharged at valid positive depth. The real initialized +producer/product/verifier returns an actual original witness in the same state as prefix and +halt. This symbolic kernel proof does not evaluate the enormous positive-depth encoder. -/ +private theorem initialized_zero (N : ℕ) (hgeo : D 2 ^ 18 ≤ N) : + ∃ y : Data 400, ∃ t S : ℕ, + Runs (width N) (RecordWitness.entryProgram.program RecordWitness.Reg.found) + (State.init (PreparationInput.input N (D 2) 1 3 (zeroWeights N))) + ⟨RecordWitness.entryProgram.size, y⟩ t ∧ + Executes (width N) (RecordWitness.entryProgram.program RecordWitness.Reg.found) + (State.init (PreparationInput.input N (D 2) 1 3 (zeroWeights N))) 1 (t + 1) ∧ + y.regs RecordWitness.Reg.found = 1 ∧ y.regs RecordWitness.Reg.scans = S ∧ S ≤ 1 ∧ + ∃ u v c : Fin N, y.regs RecordWitness.Reg.witnessA = u.val ∧ + y.regs RecordWitness.Reg.witnessB = v.val ∧ y.regs RecordWitness.Reg.witnessC = c.val := by + have hprime : 3 ∈ Nat.sqrtPrimeWindow (D 2) := + Nat.mem_sqrtPrimeWindow_iff_sq.mpr ⟨by decide, by decide, by decide⟩ + have hmag : ∀ z ∈ zeroWeights N, z.natAbs ≤ 0 := by + intro z hz + have he : z = 0 := List.eq_of_mem_replicate hz + subst z + decide + obtain ⟨hi, a, idx, ha, hout, hidx, hord, y, t, S, ht, hr, he, yb, ys, yf, + hS, yiff, yw, frame, regs⟩ := RecordWitness.runs_input (m := 2) (n := N) + (g := 1) (p := 3) (A := 0) (W := width N) (zeroWeights N) + (by simp only [zeroWeights, List.length_replicate]) (by decide) (by decide) + hprime hgeo hmag le_rfl + let V := fun q => (zeroWeights N).getD q 0 + let wAB := fun u v : Fin N => V (pairIndex (u, v)) + let wBC := fun v c : Fin N => V (N * N + pairIndex (v, c)) + let wAC := fun u c : Fin N => V (2 * (N * N) + pairIndex (u, c)) + have hvalues (q : ℕ) : V q = 0 := by + change (List.replicate (3 * (N * N)) (0 : ℤ))[q]?.getD 0 = 0 + exact List.getElem?_getD_replicate_default_eq _ _ _ + have hN : D 2 ≤ N := (Nat.le_self_pow (by decide : 18 ≠ 0) (D 2)).trans hgeo + have hn : 0 < N := lt_of_lt_of_le (by decide : 0 < D 2) hN + have hk := ((mem_instanceIndices wAB idx).mp hidx).1 + have hQ := RecordProduct.recordQueryCount_pos V (by decide : 3 ≠ 0) + (by decide : 1 ≤ D 2) hN idx hidx + have hcard := QuerySpec.card_residueClass wAB (by decide : 3 ≠ 0) + (fun q => (V q % (3 : ℤ)).toNat) (by intro pair; rfl) idx.1 + have hpairs := (RecordProduct.output_query_coordinates V wAB (by decide) + (by intro u v; rfl) (by decide) hN hout idx.1 idx.2.1 hk).2 + rw [hcard] at hpairs + obtain ⟨pair, hpair, -⟩ := hpairs 0 hQ + have hkzero : idx.2.2 = 0 := by + exact (Nat.add_eq_zero_iff.mp hord).2 + let c : Fin N := ⟨0, hn⟩ + have hc : c ∈ piece N (D 2) 1 idx.2.2 := by + rw [mem_piece, hkzero] + exact Nat.zero_div _ + have hz : weightSum wAB wBC wAC pair.1 pair.2 c = 0 := by + simp only [weightSum, wAB, wBC, wAC, hvalues, zero_add] + have hfound : y.regs RecordWitness.Reg.found = 1 := yiff.mpr + ⟨pair, hpair.1, c, hc, hz⟩ + have hfalse : falsePositiveCount wAB wBC wAC 3 = 0 := by + simp only [falsePositiveCount, IsFalsePositive, weightSum, wAB, wBC, wAC, hvalues, + zero_add, ne_eq, not_true_eq_false, false_and, Finset.filter_false, Finset.card_empty] + obtain ⟨u, v, c, hu, hv, hc, -⟩ := yw hfound + change S ≤ falsePositiveCount wAB wBC wAC 3 + y.regs RecordWitness.Reg.found at hS + refine ⟨y, t, S, hr, ?_, hfound, ys, ?_, u, v, c, hu, hv, hc⟩ + · simpa only [hfound] using he + · simpa only [hfalse, hfound, zero_add] using hS + +example : ∃ y : Data 400, ∃ t S : ℕ, + Runs (width side) (RecordWitness.entryProgram.program RecordWitness.Reg.found) + (State.init (PreparationInput.input side (D 2) 1 3 (zeroWeights side))) + ⟨RecordWitness.entryProgram.size, y⟩ t ∧ + Executes (width side) (RecordWitness.entryProgram.program RecordWitness.Reg.found) + (State.init (PreparationInput.input side (D 2) 1 3 (zeroWeights side))) 1 (t + 1) ∧ + y.regs RecordWitness.Reg.found = 1 ∧ y.regs RecordWitness.Reg.scans = S ∧ S ≤ 1 ∧ + ∃ u v c : Fin side, y.regs RecordWitness.Reg.witnessA = u.val ∧ + y.regs RecordWitness.Reg.witnessB = v.val ∧ y.regs RecordWitness.Reg.witnessC = c.val := + initialized_zero side le_rfl + end ComplexityTests.WordRAMExactTriangleRecordWitness diff --git a/ComplexityTests/WordRAMExactTriangleWitnessScan.lean b/ComplexityTests/WordRAMExactTriangleWitnessScan.lean old mode 100755 new mode 100644 diff --git a/ROADMAP.md b/ROADMAP.md index a6be60a66..2b9e95d1f 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -539,6 +539,14 @@ each statement against the numbered result. Organize modules by subject, not cha `34603008*(19m+1)*n²/D^(1/18)` at `D=4^m`, `L=19m`, `D^18≤n`. The outer instance loop, aggregate subcubic ET bound, backend/witness reconstruction and the full [#1394] ordinary-3SUM 1.9992 endpoint remain open. +- **Per-record native witness recovery** ([#1639](https://github.com/cslib-community/complexitylib/issues/1639)): + actual initialized producer/product calls now feed real answer/query loads and original signed + weight verification. The fixed 400-register program returns original-coordinate witnesses and + decides zeros in the selected existing chunk/piece. Its actual scan counter satisfies + `S≤falsePositiveCount+returnedFlag`, so unsuccessful records have no successful-scan surplus. + Root decoding, all control and original-weight scans are charged; positive logarithmic width + and polynomial words/support hold at every wider admitted width. The outer instance loop, + aggregate subcubic ET analysis and ordinary-3SUM 1.9992 composition remain open under [#1394]. - **Conditional Convolution-3SUM host** ([#1524]): the charged native host initializes from the ordinary input and runs every generated instance, conditional on a concrete solver's execution, correctness and frame theorem. This completes the conditional host milestone. Integrating the diff --git a/UPSTREAM.md b/UPSTREAM.md index 7f5575c79..f84aaa139 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -404,6 +404,23 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he initialized prefixes have a positive logarithmic width threshold and polynomial words/support independent of the physical width. Outer parameter/prime selection and instance iteration, aggregate solving/witness reconstruction and the whole #1394 endpoint remain separate. + The original-weight consumer in + [#1639](https://github.com/cslib-community/complexitylib/issues/1639) composes actual producer, + product and verification code in 400 registers. It reads original interleaved pairs, computes + the piece interval with the native square-root kernel, and invokes the entire renamed + 32-register signed verifier only while searching and on nonzero actual answers. The native + scan counter is at most existing finite false positives plus the returned 0/1 flag. Thus a + failed record has no additive successful-scan charge. Caller memory outside the original + product window and the complete producer bank are framed; widths and all-prefix words/support + are derived from original headers/weights at every sufficiently wide physical word size. + Source Light semantics and costs are not imported or simulated by assumption. The native + verifier returns its last zero, while finite `scanPiece` returns its first; failure and + original-weight soundness agree. The existing decoded queries now expose chunk membership, + nonrepetition and rank order, with an actual initialized client. The general raw writer bound + is placed at `WriteMatrices.entry_le_one`, and shared finite piece coordinates are in the + narrow `ExactTriangle.PieceBounds` module, reused by preparation and recovery. Parameter/prime + choice, the outer ET loop, aggregate savings and the full #1394 endpoint remain open; no curated + abstract changes. Stage `exec_*` lemmas are internal implementation APIs exported across proof modules; callers use the flat initialized runs, width theorem or pointer/epoch kernel. The narrow `WordRAM.ArrayAppend` and `WordRAM.InputArray` topic modules keep append/input dependencies From 888918d31bf556a04215a38559542b30a557bc44 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Thu, 29 Oct 2026 15:10:19 -0400 Subject: [PATCH 64/73] chore(deps): track CSLib main and Lean rc2 Advance CSLib to 8a8ad748 and synchronize Mathlib, the documentation generator, Verso, and all three lockfiles with Lean v4.36.0-rc2. Keep the root and documentation projects on the same inherited dependency revisions. --- docbuild/lake-manifest.json | 34 +++++++++++++++++----------------- docbuild/lakefile.toml | 2 +- guide/lake-manifest.json | 30 +++++++++++++++--------------- guide/lakefile.toml | 2 +- lake-manifest.json | 26 +++++++++++++------------- lakefile.toml | 2 +- lean-toolchain | 2 +- 7 files changed, 49 insertions(+), 49 deletions(-) diff --git a/docbuild/lake-manifest.json b/docbuild/lake-manifest.json index 3609d26b5..a1f5e4692 100644 --- a/docbuild/lake-manifest.json +++ b/docbuild/lake-manifest.json @@ -5,10 +5,10 @@ "type": "git", "subDir": null, "scope": "leanprover", - "rev": "0656b7ebd305f089c8d6bd8684a0797f19414cc2", + "rev": "20afd4ddabd2ef38ffb1a8b930ee0f375e3f75e8", "name": "«doc-gen4»", "manifestFile": "lake-manifest.json", - "inputRev": "v4.36.0-rc1", + "inputRev": "v4.36.0-rc2", "inherited": false, "configFile": "lakefile.lean"}, {"type": "path", @@ -23,7 +23,7 @@ "type": "git", "subDir": null, "scope": "", - "rev": "e1faac1842cb10b3a68c644643b6e3568766343c", + "rev": "f582e4bc64d6f43e6e711f89b09567d2088fab8d", "name": "leansqlite", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -33,7 +33,7 @@ "type": "git", "subDir": null, "scope": "", - "rev": "34d2efffcb6dc8d5f3537cf6a7f88bfaa999ec49", + "rev": "c09cb1e4f1e6e1c231a1175a12b681b075054a1b", "name": "Cli", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -43,7 +43,7 @@ "type": "git", "subDir": null, "scope": "", - "rev": "a24bcde05a7971144ba0c0b5b1d60c3a0f0bc4af", + "rev": "91ece4f8d8f93aac566e247874f6b27b57249f1b", "name": "UnicodeBasic", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -53,7 +53,7 @@ "type": "git", "subDir": null, "scope": "", - "rev": "78f28e27259a4b2f5a8199800334cebd92730f3d", + "rev": "cdf8963f8b4c9a5593c5aaa9fa4f2759f8dec1c1", "name": "BibtexQuery", "manifestFile": "lake-manifest.json", "inputRev": "master", @@ -73,27 +73,27 @@ "type": "git", "subDir": null, "scope": "", - "rev": "f4a472560f78c59bbedd795fe041ec507f7d0582", + "rev": "8a8ad7489cdccb007ad7279bfb9d03bbda2229ad", "name": "cslib", "manifestFile": "lake-manifest.json", - "inputRev": "f4a472560f78c59bbedd795fe041ec507f7d0582", + "inputRev": "8a8ad7489cdccb007ad7279bfb9d03bbda2229ad", "inherited": true, "configFile": "lakefile.toml"}, {"url": "https://github.com/leanprover-community/mathlib4", "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "625d8914199bc567cca9b91f90e8e5afafa5ae51", + "rev": "9d581dfff2e41de989072ea63abf3daf69311520", "name": "mathlib", "manifestFile": "lake-manifest.json", - "inputRev": "v4.36.0-rc1", + "inputRev": "v4.36.0-rc2", "inherited": true, "configFile": "lakefile.lean"}, {"url": "https://github.com/leanprover-community/plausible", "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "c2d7840c65673533e15b499f25beecdc6227f7d8", + "rev": "129d1ba9a16bc0660f46c79e9fb041ef5d5d80de", "name": "plausible", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -103,7 +103,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "9d39f9bb0c30aa2f6200808805ff16e6ce950890", + "rev": "c61286b7b63cbfd563b28e11e81d0a18233a044a", "name": "LeanSearchClient", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -113,7 +113,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "b3cc36c295aa5e3e3c0197b09a6556298e710447", + "rev": "b8cc5dd0a36233b88a6ed5a7dbf1207d09338c60", "name": "importGraph", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -123,7 +123,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "17137fad38a2cc782ff00b323a13c3a991d085ab", + "rev": "16259c0d07d61ee37d999d05b5cf96338b8daa11", "name": "proofwidgets", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -133,7 +133,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "cbdde3aa2665915fb286c48bcd4cdf885c629511", + "rev": "7d11bfa88558f8d3d90afdf6bd877ed70001498d", "name": "aesop", "manifestFile": "lake-manifest.json", "inputRev": "master", @@ -143,7 +143,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "6d26f8bb4b2bb78f929ad160feac508866821316", + "rev": "b0256621cb88178e9ad932a53633077b3029e621", "name": "Qq", "manifestFile": "lake-manifest.json", "inputRev": "master", @@ -153,7 +153,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "4453b52d2468f4c346a8711d728c0742e7483c9b", + "rev": "d4c39b228d4a06f3b00748c2c445a7b9f056a6e3", "name": "batteries", "manifestFile": "lake-manifest.json", "inputRev": "main", diff --git a/docbuild/lakefile.toml b/docbuild/lakefile.toml index ed4689727..b9c4092df 100644 --- a/docbuild/lakefile.toml +++ b/docbuild/lakefile.toml @@ -12,4 +12,4 @@ path = ".." [[require]] scope = "leanprover" name = "doc-gen4" -rev = "v4.36.0-rc1" +rev = "v4.36.0-rc2" diff --git a/guide/lake-manifest.json b/guide/lake-manifest.json index b8da10f21..3f19786a0 100644 --- a/guide/lake-manifest.json +++ b/guide/lake-manifest.json @@ -5,10 +5,10 @@ "type": "git", "subDir": null, "scope": "", - "rev": "f7a4dc6867f8df27778b2b320763ec9e3b7be070", + "rev": "71a2035b44e7f54a797ce799fd1b03cdd3a44684", "name": "verso", "manifestFile": "lake-manifest.json", - "inputRev": "v4.36.0-rc1", + "inputRev": "v4.36.0-rc2", "inherited": false, "configFile": "lakefile.lean"}, {"type": "path", @@ -23,7 +23,7 @@ "type": "git", "subDir": null, "scope": "", - "rev": "34d2efffcb6dc8d5f3537cf6a7f88bfaa999ec49", + "rev": "c09cb1e4f1e6e1c231a1175a12b681b075054a1b", "name": "Cli", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -33,7 +33,7 @@ "type": "git", "subDir": null, "scope": "", - "rev": "d22e76ef3d2a925132315af902ea432233b4a44a", + "rev": "758bff3509b8e90ca82e37aa5de81de3c9e70fc0", "name": "illuminate", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -43,7 +43,7 @@ "type": "git", "subDir": null, "scope": "", - "rev": "c2d7840c65673533e15b499f25beecdc6227f7d8", + "rev": "129d1ba9a16bc0660f46c79e9fb041ef5d5d80de", "name": "plausible", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -73,27 +73,27 @@ "type": "git", "subDir": null, "scope": "", - "rev": "f4a472560f78c59bbedd795fe041ec507f7d0582", + "rev": "8a8ad7489cdccb007ad7279bfb9d03bbda2229ad", "name": "cslib", "manifestFile": "lake-manifest.json", - "inputRev": "f4a472560f78c59bbedd795fe041ec507f7d0582", + "inputRev": "8a8ad7489cdccb007ad7279bfb9d03bbda2229ad", "inherited": true, "configFile": "lakefile.toml"}, {"url": "https://github.com/leanprover-community/mathlib4", "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "625d8914199bc567cca9b91f90e8e5afafa5ae51", + "rev": "9d581dfff2e41de989072ea63abf3daf69311520", "name": "mathlib", "manifestFile": "lake-manifest.json", - "inputRev": "v4.36.0-rc1", + "inputRev": "v4.36.0-rc2", "inherited": true, "configFile": "lakefile.lean"}, {"url": "https://github.com/leanprover-community/LeanSearchClient", "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "9d39f9bb0c30aa2f6200808805ff16e6ce950890", + "rev": "c61286b7b63cbfd563b28e11e81d0a18233a044a", "name": "LeanSearchClient", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -103,7 +103,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "b3cc36c295aa5e3e3c0197b09a6556298e710447", + "rev": "b8cc5dd0a36233b88a6ed5a7dbf1207d09338c60", "name": "importGraph", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -113,7 +113,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "17137fad38a2cc782ff00b323a13c3a991d085ab", + "rev": "16259c0d07d61ee37d999d05b5cf96338b8daa11", "name": "proofwidgets", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -123,7 +123,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "cbdde3aa2665915fb286c48bcd4cdf885c629511", + "rev": "7d11bfa88558f8d3d90afdf6bd877ed70001498d", "name": "aesop", "manifestFile": "lake-manifest.json", "inputRev": "master", @@ -133,7 +133,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "6d26f8bb4b2bb78f929ad160feac508866821316", + "rev": "b0256621cb88178e9ad932a53633077b3029e621", "name": "Qq", "manifestFile": "lake-manifest.json", "inputRev": "master", @@ -143,7 +143,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "4453b52d2468f4c346a8711d728c0742e7483c9b", + "rev": "d4c39b228d4a06f3b00748c2c445a7b9f056a6e3", "name": "batteries", "manifestFile": "lake-manifest.json", "inputRev": "main", diff --git a/guide/lakefile.toml b/guide/lakefile.toml index fb7601692..f62d2a91a 100644 --- a/guide/lakefile.toml +++ b/guide/lakefile.toml @@ -21,7 +21,7 @@ path = ".." [[require]] name = "verso" git = "https://github.com/leanprover/verso" -rev = "v4.36.0-rc1" +rev = "v4.36.0-rc2" [[lean_lib]] name = "ComplexityGuide" diff --git a/lake-manifest.json b/lake-manifest.json index a4892b4fc..a2c1eaffd 100644 --- a/lake-manifest.json +++ b/lake-manifest.json @@ -5,27 +5,27 @@ "type": "git", "subDir": null, "scope": "", - "rev": "f4a472560f78c59bbedd795fe041ec507f7d0582", + "rev": "8a8ad7489cdccb007ad7279bfb9d03bbda2229ad", "name": "cslib", "manifestFile": "lake-manifest.json", - "inputRev": "f4a472560f78c59bbedd795fe041ec507f7d0582", + "inputRev": "8a8ad7489cdccb007ad7279bfb9d03bbda2229ad", "inherited": false, "configFile": "lakefile.toml"}, {"url": "https://github.com/leanprover-community/mathlib4", "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "625d8914199bc567cca9b91f90e8e5afafa5ae51", + "rev": "9d581dfff2e41de989072ea63abf3daf69311520", "name": "mathlib", "manifestFile": "lake-manifest.json", - "inputRev": "v4.36.0-rc1", + "inputRev": "v4.36.0-rc2", "inherited": true, "configFile": "lakefile.lean"}, {"url": "https://github.com/leanprover-community/plausible", "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "c2d7840c65673533e15b499f25beecdc6227f7d8", + "rev": "129d1ba9a16bc0660f46c79e9fb041ef5d5d80de", "name": "plausible", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -35,7 +35,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "9d39f9bb0c30aa2f6200808805ff16e6ce950890", + "rev": "c61286b7b63cbfd563b28e11e81d0a18233a044a", "name": "LeanSearchClient", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -45,7 +45,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "b3cc36c295aa5e3e3c0197b09a6556298e710447", + "rev": "b8cc5dd0a36233b88a6ed5a7dbf1207d09338c60", "name": "importGraph", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -55,7 +55,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "17137fad38a2cc782ff00b323a13c3a991d085ab", + "rev": "16259c0d07d61ee37d999d05b5cf96338b8daa11", "name": "proofwidgets", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -65,7 +65,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "cbdde3aa2665915fb286c48bcd4cdf885c629511", + "rev": "7d11bfa88558f8d3d90afdf6bd877ed70001498d", "name": "aesop", "manifestFile": "lake-manifest.json", "inputRev": "master", @@ -75,7 +75,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "6d26f8bb4b2bb78f929ad160feac508866821316", + "rev": "b0256621cb88178e9ad932a53633077b3029e621", "name": "Qq", "manifestFile": "lake-manifest.json", "inputRev": "master", @@ -85,7 +85,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "4453b52d2468f4c346a8711d728c0742e7483c9b", + "rev": "d4c39b228d4a06f3b00748c2c445a7b9f056a6e3", "name": "batteries", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -95,10 +95,10 @@ "type": "git", "subDir": null, "scope": "leanprover", - "rev": "34d2efffcb6dc8d5f3537cf6a7f88bfaa999ec49", + "rev": "c09cb1e4f1e6e1c231a1175a12b681b075054a1b", "name": "Cli", "manifestFile": "lake-manifest.json", - "inputRev": "v4.36.0-rc1", + "inputRev": "v4.36.0-rc2", "inherited": true, "configFile": "lakefile.toml"}], "name": "complexitylib", diff --git a/lakefile.toml b/lakefile.toml index ce7b8cd77..00f09a874 100644 --- a/lakefile.toml +++ b/lakefile.toml @@ -33,7 +33,7 @@ autoImplicit = false [[require]] name = "cslib" git = "https://github.com/leanprover/cslib" -rev = "f4a472560f78c59bbedd795fe041ec507f7d0582" +rev = "8a8ad7489cdccb007ad7279bfb9d03bbda2229ad" [[lean_lib]] name = "Complexity" diff --git a/lean-toolchain b/lean-toolchain index 2afd38716..0fc433692 100644 --- a/lean-toolchain +++ b/lean-toolchain @@ -1 +1 @@ -leanprover/lean4:v4.36.0-rc1 +leanprover/lean4:v4.36.0-rc2 From 9a1b75416b7723a073411f0ee9a7d2ccfdd49ec9 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Thu, 29 Oct 2026 15:10:24 -0400 Subject: [PATCH 65/73] refactor(probability): use generic infinite-sum lemmas Replace 187 deprecated ENNReal wrapper uses across 55 proof files with their generic counterparts. The exact mapping is: - ENNReal.tsum_le_tsum -> tsum_le_tsum (94) - ENNReal.tsum_add -> tsum_add (54) - ENNReal.tsum_eq_zero -> tsum_eq_zero_iff (14) - ENNReal.summable -> CompleteLattice.summable (6) - ENNReal.tsum_comm -> tsum_comm (6) - ENNReal.tsum_prod' -> tsum_prod' (5) - ENNReal.le_tsum -> le_tsum (5) - ENNReal.tsum_eq_add_tsum_ite -> tsum_eq_add_tsum_ite (2) - ENNReal.tsum_sigma -> tsum_sigma (1) The old wrappers invoke these same theorems specialized to ENNReal. All five le_tsum calls already pass the function by name. No statements or definitions change. --- .../Algorithms/WordRAM/Karger/FastCut.lean | 2 +- .../Algorithms/WordRAM/Karger/Graph.lean | 6 +-- .../Algorithms/WordRAM/KargerMinCut.lean | 2 +- .../Algorithms/WordRAM/KargerStein.lean | 6 +-- .../Computability/Complexity/Classes/IP.lean | 2 +- .../Classes/IP/GraphNonIsomorphism.lean | 2 +- .../Complexity/Classes/IP/Guessing.lean | 6 +-- .../Complexity/Classes/IP/OptimalProver.lean | 8 ++-- .../Complexity/Classes/IP/Repetition.lean | 6 +-- .../LogSpace/Randomized/Amplification.lean | 2 +- .../Complexity/Classes/MIP/OptimalProver.lean | 4 +- .../Complexity/FineGrained/PPSZ/Basic.lean | 2 +- .../Complexity/FineGrained/PPZ.lean | 2 +- .../Computability/Interactive/Plain.lean | 4 +- .../Computability/Kolmogorov/AverageCase.lean | 4 +- .../Computability/PCP/TwoProverGame.lean | 2 +- .../WordRAM/Randomized/Expected.lean | 12 ++--- .../CramerShoup/ProjectiveHash.lean | 4 +- .../Computational/CramerShoup/Scheme.lean | 2 +- .../Crypto/Computational/DiffieHellman.lean | 16 +++---- .../Computational/DiffieHellman/Order.lean | 4 +- .../Encryption/Authenticated.lean | 14 +++--- .../Encryption/EncryptThenMAC.lean | 8 ++-- .../Crypto/Computational/GMW/Soundness.lean | 6 +-- Complexity/Crypto/Computational/MAC.lean | 8 ++-- .../Computational/NaorReingold/Security.lean | 10 ++-- .../Computational/OneWay/LiuPassConverse.lean | 2 +- .../Computational/OneWayToSignature.lean | 6 +-- .../Crypto/Computational/PRGToOneWay.lean | 2 +- .../Computational/PedersenCommitment.lean | 8 ++-- .../Pseudoentropy/MaskedExtraction.lean | 2 +- Complexity/Crypto/Game.lean | 20 ++++---- Complexity/Crypto/Game/UntilBad.lean | 16 +++---- Complexity/Crypto/Primitives/PRG/NextBit.lean | 4 +- .../Crypto/Protocols/Cipher/EvenMansour.lean | 6 +-- .../Crypto/Protocols/Cipher/LubyRackoff.lean | 48 +++++++++---------- .../Crypto/Protocols/MAC/CarterWegman.lean | 16 +++---- .../Protocols/SigmaProtocol/FiatShamir.lean | 6 +-- .../Probabilistic/BoundedUniform.lean | 6 +-- .../Languages/Probabilistic/Expect.lean | 8 ++-- .../Probabilistic/Oracle/Interaction.lean | 2 +- .../Probabilistic/RandomPermutation.lean | 8 ++-- .../Languages/Probabilistic/UntilBad.lean | 10 ++-- .../Languages/Probabilistic/Vector.lean | 2 +- Complexity/Probability/Conditioning.lean | 6 +-- .../Probability/CorrelatedSampling.lean | 2 +- Complexity/Probability/Forking.lean | 2 +- Complexity/Probability/IID.lean | 2 +- Complexity/Probability/MinEntropy.lean | 2 +- Complexity/Probability/Minimax.lean | 4 +- Complexity/Probability/PMF/Bind.lean | 14 +++--- Complexity/Probability/PMF/Event.lean | 16 +++---- Complexity/Probability/PMF/Map.lean | 2 +- .../ProductConditioning/SideInformation.lean | 2 +- Complexity/Probability/RejectionSampling.lean | 4 +- 55 files changed, 186 insertions(+), 186 deletions(-) diff --git a/Complexity/Algorithms/WordRAM/Karger/FastCut.lean b/Complexity/Algorithms/WordRAM/Karger/FastCut.lean index a216c2ae8..30a2afa92 100644 --- a/Complexity/Algorithms/WordRAM/Karger/FastCut.lean +++ b/Complexity/Algorithms/WordRAM/Karger/FastCut.lean @@ -658,7 +658,7 @@ theorem two_branch_hit_prob_ge {α β γ : Type} (m₁ : ProbComp α) (m₂ : α have ha : a ∈ m₁.support := (ProbComp.support_eval m₁) ▸ ha_eval have ha_hit : a ∈ hit₁ := not_not.mp ha_not_miss rw [PMF.toOuterMeasure_map_apply, PMF.toOuterMeasure_apply] - refine ENNReal.tsum_eq_zero.mpr fun b => ?_ + refine tsum_eq_zero_iff.mpr fun b => ?_ by_cases hb_eval : b ∈ (m₂ a).eval.support · have hb : b ∈ (m₂ a).support := (ProbComp.support_eval (m₂ a)) ▸ hb_eval have hmem : f a b ∈ hit := hprop₁ a ha ha_hit b hb diff --git a/Complexity/Algorithms/WordRAM/Karger/Graph.lean b/Complexity/Algorithms/WordRAM/Karger/Graph.lean index 9afa1d805..c03da4013 100644 --- a/Complexity/Algorithms/WordRAM/Karger/Graph.lean +++ b/Complexity/Algorithms/WordRAM/Karger/Graph.lean @@ -1107,7 +1107,7 @@ theorem toOuterMeasure_bind_ge_mul_ennreal {α β : Type*} (p : PMF α) (kernel s₁ * s₂ ≤ p.toOuterMeasure pre * s₂ := mul_le_mul_of_nonneg_right hp zero_le _ ≤ (p.bind kernel).toOuterMeasure event := by rw [PMF.toOuterMeasure_bind_apply, PMF.toOuterMeasure_apply, ← ENNReal.tsum_mul_right] - refine ENNReal.tsum_le_tsum fun a => ?_ + refine tsum_le_tsum fun a => ?_ by_cases ha : a ∈ pre · simp only [Set.indicator_of_mem ha] by_cases hs : a ∈ p.support @@ -1131,7 +1131,7 @@ theorem toOuterMeasure_bind_le_mul_ennreal {α β : Type*} (p : PMF α) (kernel (p.bind kernel).toOuterMeasure failure ≤ p.toOuterMeasure stillFailing * q₂ := by rw [PMF.toOuterMeasure_bind_apply, PMF.toOuterMeasure_apply, ← ENNReal.tsum_mul_right] - refine ENNReal.tsum_le_tsum fun a => ?_ + refine tsum_le_tsum fun a => ?_ by_cases hs : a ∈ p.support · by_cases ha : a ∈ stillFailing · simp only [Set.indicator_of_mem ha] @@ -1146,7 +1146,7 @@ valid on arbitrary types without measurability hypotheses. -/ theorem toOuterMeasure_add_compl_ennreal {α : Type*} (p : PMF α) (s : Set α) : p.toOuterMeasure s + p.toOuterMeasure sᶜ = 1 := by classical - rw [PMF.toOuterMeasure_apply, PMF.toOuterMeasure_apply, ← ENNReal.tsum_add] + rw [PMF.toOuterMeasure_apply, PMF.toOuterMeasure_apply, ← tsum_add] have hpt : (fun x => s.indicator p x + sᶜ.indicator p x) = p := by ext x by_cases hx : x ∈ s <;> simp [hx] diff --git a/Complexity/Algorithms/WordRAM/KargerMinCut.lean b/Complexity/Algorithms/WordRAM/KargerMinCut.lean index b3ef003a5..c07c84a3c 100644 --- a/Complexity/Algorithms/WordRAM/KargerMinCut.lean +++ b/Complexity/Algorithms/WordRAM/KargerMinCut.lean @@ -1181,7 +1181,7 @@ theorem ampLoop_spec {w sb n R : ℕ} {W : Fin n → Fin n → ℕ} {mem₀ : obtain ⟨m_r, hm_r, _, hsup_r, _⟩ := hnext z₁ hz₁ rw [hml_def, hm_r.codeComp_eq, PMF.toOuterMeasure_map_apply, PMF.toOuterMeasure_apply] - convert ENNReal.tsum_eq_zero.mpr _ + convert tsum_eq_zero_iff.mpr _ intro z₂ by_cases hz₂_eval : z₂ ∈ m_r.eval.support · have hz₂ : z₂ ∈ m_r.support := (ProbComp.support_eval m_r) ▸ hz₂_eval diff --git a/Complexity/Algorithms/WordRAM/KargerStein.lean b/Complexity/Algorithms/WordRAM/KargerStein.lean index 92368fc36..5a2a42a1a 100644 --- a/Complexity/Algorithms/WordRAM/KargerStein.lean +++ b/Complexity/Algorithms/WordRAM/KargerStein.lean @@ -168,7 +168,7 @@ private theorem toOuterMeasure_mono_support {α : Type} (m : ProbComp α) {S T : m.eval.toOuterMeasure S ≤ m.eval.toOuterMeasure T := by classical rw [PMF.toOuterMeasure_apply, PMF.toOuterMeasure_apply] - refine ENNReal.tsum_le_tsum fun a => ?_ + refine tsum_le_tsum fun a => ?_ by_cases ha : a ∈ S · simp only [Set.indicator_of_mem ha] by_cases hs : a ∈ m.support @@ -2684,7 +2684,7 @@ theorem fastCutNode_spec {n w : ℕ} {W₀ : Fin n → Fin n → ℕ} have hcompl0 : (ms2_fn z₂.1).eval.toOuterMeasure {zs | zs.1.regs retVal = minCut W₀}ᶜ = 0 := by rw [PMF.toOuterMeasure_apply] - refine ENNReal.tsum_eq_zero.mpr fun zs => ?_ + refine tsum_eq_zero_iff.mpr fun zs => ?_ by_cases hzs_ev : zs ∈ (ms2_fn z₂.1).eval.support · have hzs : zs ∈ (ms2_fn z₂.1).support := (ProbComp.support_eval (ms2_fn z₂.1)) ▸ hzs_ev @@ -3679,7 +3679,7 @@ theorem fastCutAmpLoop_spec {w sb n R : ℕ} {W : Fin n → Fin n → ℕ} {mem have hbv₁ : z₁.1.regs bestVal = minCut W := not_not.mp hhit obtain ⟨m_r, hm_r, _, hsup_r, _⟩ := hnext z₁ hz₁ rw [hml_def, hm_r.codeComp_eq, PMF.toOuterMeasure_map_apply, PMF.toOuterMeasure_apply] - refine ENNReal.tsum_eq_zero.mpr fun z₂ => ?_ + refine tsum_eq_zero_iff.mpr fun z₂ => ?_ by_cases hz₂_eval : z₂ ∈ m_r.eval.support · have hz₂ : z₂ ∈ m_r.support := (ProbComp.support_eval m_r) ▸ hz₂_eval have hbv₂ : z₂.1.regs bestVal = minCut W := diff --git a/Complexity/Computability/Complexity/Classes/IP.lean b/Complexity/Computability/Complexity/Classes/IP.lean index d2c73a5bc..d583e0f90 100644 --- a/Complexity/Computability/Complexity/Classes/IP.lean +++ b/Complexity/Computability/Complexity/Classes/IP.lean @@ -148,7 +148,7 @@ theorem acceptProb_eq_acceptProbWith (verifier : WordOracleComp Operation Bool) /-- The acceptance probability sums the masses of the accepting outcomes over final states. -/ theorem acceptProb_eq_tsum (verifier : WordOracleComp Operation Bool) (P : Prover Operation) : acceptProb verifier P = ∑' s, P.run verifier P.init (true, s) := by - rw [acceptProb, PMF.map_apply, ENNReal.tsum_prod', tsum_bool] + rw [acceptProb, PMF.map_apply, tsum_prod', tsum_bool] simp /-- Private coins drawn before any message average the acceptance probabilities of the diff --git a/Complexity/Computability/Complexity/Classes/IP/GraphNonIsomorphism.lean b/Complexity/Computability/Complexity/Classes/IP/GraphNonIsomorphism.lean index dfce7de33..659f372bf 100644 --- a/Complexity/Computability/Complexity/Classes/IP/GraphNonIsomorphism.lean +++ b/Complexity/Computability/Complexity/Classes/IP/GraphNonIsomorphism.lean @@ -413,7 +413,7 @@ private theorem card_filter_not_injective_div_le (n : ℕ) : private theorem tsum_map_mul {β γ : Type*} (μ : PMF β) (m : β → γ) (f : γ → ENNReal) : ∑' c, μ.map m c * f c = ∑' b, μ b * f (m b) := by simp only [PMF.map_apply, ENNReal.tsum_mul_right.symm] - rw [ENNReal.tsum_comm] + rw [tsum_comm] refine tsum_congr fun b => ?_ rw [tsum_eq_single (m b)] · simp diff --git a/Complexity/Computability/Complexity/Classes/IP/Guessing.lean b/Complexity/Computability/Complexity/Classes/IP/Guessing.lean index 6eaedc8da..02e8929d3 100644 --- a/Complexity/Computability/Complexity/Classes/IP/Guessing.lean +++ b/Complexity/Computability/Complexity/Classes/IP/Guessing.lean @@ -77,8 +77,8 @@ noncomputable def Prover.guessProb (P : Prover Unit) (s : P.State) (m : List Boo theorem Prover.guessProb_add_le_one (P : Prover Unit) (s : P.State) (m : List Bool) : P.guessProb s m false + P.guessProb s m true ≤ 1 := by simp only [Prover.guessProb, PMF.map_apply] - rw [← ENNReal.tsum_add, ← PMF.tsum_coe ((P.respond ((), m)).run s)] - refine ENNReal.tsum_le_tsum fun y => ?_ + rw [← tsum_add, ← PMF.tsum_coe ((P.respond ((), m)).run s)] + refine tsum_le_tsum fun y => ?_ by_cases h : List.take 1 y.1 = [false] · simp [h] · by_cases h' : List.take 1 y.1 = [true] <;> simp [h, h'] @@ -120,7 +120,7 @@ theorem acceptProb_guessingRound_le {message : Bool → ProbComp (List Bool)} { exact hclose _ fun m => PMF.coe_le_one _ _ _ = ∑' m, ProbComp.eval (message false) m * (P.guessProb P.init m false + P.guessProb P.init m true) + δ := by - rw [← add_assoc, ← ENNReal.tsum_add] + rw [← add_assoc, ← tsum_add] simp only [mul_add] _ ≤ 1 + δ := by gcongr diff --git a/Complexity/Computability/Complexity/Classes/IP/OptimalProver.lean b/Complexity/Computability/Complexity/Classes/IP/OptimalProver.lean index 3ad366c14..7fd4eec66 100644 --- a/Complexity/Computability/Complexity/Classes/IP/OptimalProver.lean +++ b/Complexity/Computability/Complexity/Classes/IP/OptimalProver.lean @@ -304,7 +304,7 @@ private theorem sum_acceptFrom_after_le (hF : IsBounded F D L) (P : Prover Unit) lia rw [Finset.sum_eq_zero fun m _ => ?_, add_zero] · split_ifs <;> simp - · refine ENNReal.tsum_eq_zero.mpr fun ys => ?_ + · refine tsum_eq_zero_iff.mpr fun ys => ?_ rw [after_append] unfold outcome at hnil rcases hah : after (F r) h with _ | ⟨b⟩ | ⟨q, next⟩ @@ -320,12 +320,12 @@ private theorem sum_acceptFrom_after_le (hF : IsBounded F D L) (P : Prover Unit) exact (Finset.sum_congr rfl fun r _ => by split_ifs <;> simp).le rw [Finset.sum_comm] refine Finset.sum_le_sum fun m _ => ?_ - rw [← Summable.tsum_finsetSum fun r _ => ENNReal.summable] + rw [← Summable.tsum_finsetSum fun r _ => CompleteLattice.summable] calc ∑' ys : List Bool × P.State, ∑ r, (P.respond ((), m)).run s ys * acceptFrom P ys.2 (after (F r) (h ++ [(m, ys.1)])) ≤ ∑' ys : List Bool × P.State, (P.respond ((), m)).run s ys * ((BitString.shortWords L).sup fun y => value F L n (h ++ [(m, y)]) : ℕ) := by - refine ENNReal.tsum_le_tsum fun ys => ?_ + refine tsum_le_tsum fun ys => ?_ rw [← Finset.mul_sum] refine mul_le_mul_right ?_ _ refine (ih (h ++ [(m, ys.1)]) (by @@ -395,7 +395,7 @@ private theorem sum_acceptFrom_optimalProver_after (hF : IsBounded F D L) (n : lia rw [Finset.sum_eq_zero fun m _ => ?_, add_zero] · split_ifs <;> simp - · refine ENNReal.tsum_eq_zero.mpr fun ys => ?_ + · refine tsum_eq_zero_iff.mpr fun ys => ?_ rw [after_append] unfold outcome at hnil rcases hah : after (F r) h with _ | ⟨b⟩ | ⟨q, next⟩ diff --git a/Complexity/Computability/Complexity/Classes/IP/Repetition.lean b/Complexity/Computability/Complexity/Classes/IP/Repetition.lean index 66023d528..77f8fb503 100644 --- a/Complexity/Computability/Complexity/Classes/IP/Repetition.lean +++ b/Complexity/Computability/Complexity/Classes/IP/Repetition.lean @@ -64,7 +64,7 @@ private theorem acceptProb_bind_ite (P : Prover Operation) acceptProb (do let ok ← round; if ok then rest else pure false) P = ∑' s, P.run round P.init (true, s) * acceptProb rest { P with init := s } := by simp only [acceptProb, Prover.run_bind, PMF.map_bind] - rw [PMF.bind_apply, ENNReal.tsum_prod', tsum_bool] + rw [PMF.bind_apply, tsum_prod', tsum_bool] simp only [Bool.false_eq_true, Bool.true_eq_false, ↓reduceIte, Prover.run_pure, PMF.pure_map, PMF.pure_apply, mul_zero, tsum_zero, zero_add] rfl @@ -81,7 +81,7 @@ theorem acceptProb_sequentialRepeat_le {round : WordOracleComp Operation Bool} { calc ∑' s, P.run round P.init (true, s) * acceptProb (sequentialRepeat k round) { P with init := s } ≤ ∑' s, P.run round P.init (true, s) * ε ^ k := - ENNReal.tsum_le_tsum fun s => mul_le_mul' le_rfl (ih _) + tsum_le_tsum fun s => mul_le_mul' le_rfl (ih _) _ = (∑' s, P.run round P.init (true, s)) * ε ^ k := ENNReal.tsum_mul_right _ ≤ ε * ε ^ k := by gcongr @@ -103,7 +103,7 @@ theorem le_acceptProb_sequentialRepeat {round : WordOracleComp Operation Bool} { gcongr exact (h s).trans_eq (acceptProb_eq_tsum round _) _ = ∑' s', P.run round s (true, s') * c ^ k := ENNReal.tsum_mul_right.symm - _ ≤ _ := ENNReal.tsum_le_tsum fun s' => mul_le_mul' le_rfl (ih s') + _ ≤ _ := tsum_le_tsum fun s' => mul_le_mul' le_rfl (ih s') /-- A constant number of sequential runs of a strict oracle PPT verifier is strict oracle PPT. -/ theorem isOraclePPTOn_sequentialRepeat [DecidableEq Operation] [Finite Operation] diff --git a/Complexity/Computability/Complexity/Classes/LogSpace/Randomized/Amplification.lean b/Complexity/Computability/Complexity/Classes/LogSpace/Randomized/Amplification.lean index 1c3606355..56fd01273 100644 --- a/Complexity/Computability/Complexity/Classes/LogSpace/Randomized/Amplification.lean +++ b/Complexity/Computability/Complexity/Classes/LogSpace/Randomized/Amplification.lean @@ -126,7 +126,7 @@ theorem eval_runClosed_orElse_apply (h₁ : first.toNTM.RunsInTime input t₁) exact congrArg _ (PMF.pure_apply_self _) · rw [add_zero] _ = q + ProbComp.eval (first.runClosed t₁ input) [true] * (1 - q) := by - rw [ENNReal.tsum_add, ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul, tsum_ite_eq] + rw [tsum_add, ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul, tsum_ite_eq] /-- **Rejections multiply under `orElse`.** Counted on coin strings, the probability that `first.orElse second` does not accept is the product of the probabilities that `first` and diff --git a/Complexity/Computability/Complexity/Classes/MIP/OptimalProver.lean b/Complexity/Computability/Complexity/Classes/MIP/OptimalProver.lean index ebf6834a9..0f1a57869 100644 --- a/Complexity/Computability/Complexity/Classes/MIP/OptimalProver.lean +++ b/Complexity/Computability/Complexity/Classes/MIP/OptimalProver.lean @@ -581,7 +581,7 @@ private theorem tsum_mul_le_bestOfList (μ : PMF α) (g : α → ℝ≥0∞) (de ∑' x, μ x * g x ≤ g (bestOfList g default l) := by calc ∑' x, μ x * g x ≤ ∑' x, μ x * g (bestOfList g default l) := by - refine ENNReal.tsum_le_tsum fun x => ?_ + refine tsum_le_tsum fun x => ?_ by_cases hx : x ∈ μ.support · exact mul_le_mul_right (le_bestOfList g default l (hsupp x hx)) (μ x) · rw [PMF.mem_support_iff, not_not] at hx @@ -618,7 +618,7 @@ theorem exists_deterministic_prover_family [Finite ι] [DecidableEq ι] {γ : Ty rw [run_eq_bind_piPresample (hF c) P (fun _ => d) (fun _ => le_rfl) (Prover.independent P).init, PMF.bind_apply] simp only [hc, ← ENNReal.tsum_mul_left] - rw [← Summable.tsum_finsetSum fun _ _ => ENNReal.summable] + rw [← Summable.tsum_finsetSum fun _ _ => CompleteLattice.summable] refine tsum_congr fun f => ?_ rw [Finset.mul_sum] exact Finset.sum_congr rfl fun c _ => by rw [mul_left_comm] diff --git a/Complexity/Computability/Complexity/FineGrained/PPSZ/Basic.lean b/Complexity/Computability/Complexity/FineGrained/PPSZ/Basic.lean index e47a1f654..a73035873 100644 --- a/Complexity/Computability/Complexity/FineGrained/PPSZ/Basic.lean +++ b/Complexity/Computability/Complexity/FineGrained/PPSZ/Basic.lean @@ -276,7 +276,7 @@ theorem ppz_round_apply_le {φ : CNF (Fin n)} {w : ℕ} (hw : 1 ≤ w) {σ : Fin (hσ : φ.eval σ = true) : PPZ.round φ (fun x => some (σ x)) ≤ round φ w (fun x => some (σ x)) := by rw [PPZ.round, round, PPZ.roundWith, PPZ.roundWith, PMF.bind_apply, PMF.bind_apply] - refine ENNReal.tsum_le_tsum fun π => mul_le_mul_right ?_ _ + refine tsum_le_tsum fun π => mul_le_mul_right ?_ _ rw [← PPZ.restrict_prefixSet_self σ π] rw [PPZ.runWith_apply_restrict (O := PPZ.forced φ) (fun _ hτ _ _ h => PPZ.forced_eq hσ hτ h) π le_rfl, PPZ.runWith_apply_restrict (O := implied φ w) (fun _ hτ _ _ h => implied_eq hσ hτ h) π diff --git a/Complexity/Computability/Complexity/FineGrained/PPZ.lean b/Complexity/Computability/Complexity/FineGrained/PPZ.lean index 29084952a..a7c4fa2c3 100644 --- a/Complexity/Computability/Complexity/FineGrained/PPZ.lean +++ b/Complexity/Computability/Complexity/FineGrained/PPZ.lean @@ -458,7 +458,7 @@ theorem runWith_apply_restrict {O : (Fin n → Option Bool) → Fin n → Option by_cases hsupp : τ ∈ (runWith O π i).support · rw [PMF.map_apply] convert mul_zero _ - refine ENNReal.tsum_eq_zero.mpr fun b => ite_eq_right fun e => hτ ?_ + refine tsum_eq_zero_iff.mpr fun b => ite_eq_right fun e => hτ ?_ funext y by_cases hy : y = x · subst hy diff --git a/Complexity/Computability/Interactive/Plain.lean b/Complexity/Computability/Interactive/Plain.lean index 23a157b84..822083780 100644 --- a/Complexity/Computability/Interactive/Plain.lean +++ b/Complexity/Computability/Interactive/Plain.lean @@ -201,9 +201,9 @@ private theorem rbrFrom_le : ∀ (s : Spec) (smp : PerChallenge ProbComp s) st' calc ∑' c, ProbComp.eval smp.1 c * _ ≤ ∑' c, ProbComp.eval smp.1 c * (A.indicator 1 c + PerChallenge.sum s ε.2) := - ENNReal.tsum_le_tsum fun c => by gcongr; exact hinner c + tsum_le_tsum fun c => by gcongr; exact hinner c _ = (ProbComp.eval smp.1).toOuterMeasure A + PerChallenge.sum s ε.2 := by - simp only [mul_add, ENNReal.tsum_add, ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul, + simp only [mul_add, tsum_add, ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul, PMF.toOuterMeasure_apply] congr 1 refine tsum_congr fun c => ?_ diff --git a/Complexity/Computability/Kolmogorov/AverageCase.lean b/Complexity/Computability/Kolmogorov/AverageCase.lean index 0ead74cf0..86083ba86 100644 --- a/Complexity/Computability/Kolmogorov/AverageCase.lean +++ b/Complexity/Computability/Kolmogorov/AverageCase.lean @@ -277,7 +277,7 @@ private theorem pmf_map_true_le_one_sub_of_imp_false {α : Type*} (q : PMF α) ( ((q.map P) true).toReal ≤ 1 - ((q.map Q) true).toReal := by have hle : (q.map P) true ≤ (q.map Q) false := by rw [PMF.map_apply, PMF.map_apply] - refine ENNReal.tsum_le_tsum fun v => ?_ + refine tsum_le_tsum fun v => ?_ by_cases hp : P v = true · have hq : Q v = false := hdisj v hp simp [hp, hq] @@ -292,7 +292,7 @@ private theorem pmf_map_true_le_of_imp_true {α : Type*} (q : PMF α) (P Q : α ((q.map P) true).toReal ≤ ((q.map Q) true).toReal := by apply ENNReal.toReal_mono (PMF.apply_ne_top _ _) rw [PMF.map_apply, PMF.map_apply] - refine ENNReal.tsum_le_tsum fun v => ?_ + refine tsum_le_tsum fun v => ?_ by_cases hp : P v = true · have hq : Q v = true := himp v hp simp [hp, hq] diff --git a/Complexity/Computability/PCP/TwoProverGame.lean b/Complexity/Computability/PCP/TwoProverGame.lean index d918d1eba..e742d8fc8 100644 --- a/Complexity/Computability/PCP/TwoProverGame.lean +++ b/Complexity/Computability/PCP/TwoProverGame.lean @@ -117,7 +117,7 @@ theorem winProb_shared_le_value [Nonempty A] [Nonempty B] {R : Type*} (ρ : PMF exact ENNReal.ofReal_le_ofReal (G.winProb_le_value (fun x => ha x r) (fun y => hb y r)) calc ∑' r, ρ r * (G.questions.map fun q => (q, ha q.1 r, hb q.2 r)).toOuterMeasure {z | G.accepts z.1.1 z.1.2 z.2.1 z.2.2} - ≤ ∑' r, ρ r * ENNReal.ofReal G.value := ENNReal.tsum_le_tsum fun r => by gcongr; exact hr r + ≤ ∑' r, ρ r * ENNReal.ofReal G.value := tsum_le_tsum fun r => by gcongr; exact hr r _ = ENNReal.ofReal G.value := by rw [ENNReal.tsum_mul_right, ρ.tsum_coe, one_mul] end Finite diff --git a/Complexity/Computability/WordRAM/Randomized/Expected.lean b/Complexity/Computability/WordRAM/Randomized/Expected.lean index 7d9e1844a..4f1881163 100644 --- a/Complexity/Computability/WordRAM/Randomized/Expected.lean +++ b/Complexity/Computability/WordRAM/Randomized/Expected.lean @@ -117,7 +117,7 @@ theorem evalRand_haltedSet_mono (w : ℕ) (p : Program B r) (s : State r) : rw [evalRand, runRand_add w p T 1, ProbComp.eval_bind] simp only [evalRand, runRand_succ, runRand_zero, bind_pure] rw [hstep, PMF.toOuterMeasure_bind_apply, PMF.toOuterMeasure_apply] - refine ENNReal.tsum_le_tsum fun cfg => ?_ + refine tsum_le_tsum fun cfg => ?_ by_cases hcfg : cfg ∈ RandCfg.haltedSet r · have hmem := hcfg obtain ⟨v, d, rfl⟩ := hcfg @@ -139,7 +139,7 @@ theorem RandHaltsWithin.almostSurelyHalts {w T : ℕ} {p : Program B r} {s : Sta exact hnot (h cfg hmem) have hsum : (evalRand w p T s).toOuterMeasure (RandCfg.haltedSet r) + (evalRand w p T s).toOuterMeasure (RandCfg.haltedSet r)ᶜ = 1 := by - rw [PMF.toOuterMeasure_apply, PMF.toOuterMeasure_apply, ← ENNReal.tsum_add] + rw [PMF.toOuterMeasure_apply, PMF.toOuterMeasure_apply, ← tsum_add] convert (evalRand w p T s).tsum_coe using 1 congr 1 funext cfg @@ -172,7 +172,7 @@ theorem expectedStepsFrom_faulted (w : ℕ) (p : Program B r) : private theorem tsum_nat_eq_zero_add (g : ℕ → ℝ≥0∞) : ∑' t : ℕ, g t = g 0 + ∑' t : ℕ, g (t + 1) := by - rw [ENNReal.tsum_eq_add_tsum_ite (f := g) 0] + rw [tsum_eq_add_tsum_ite (f := g) 0] congr 1 have hsh := Function.Injective.tsum_eq Nat.succ_injective (f := fun t => if t = 0 then 0 else g t) fun t ht => by @@ -199,7 +199,7 @@ theorem expectedStepsFrom_running (w : ℕ) (p : Program B r) (s : State r) : intro t rw [runRand_succ, ProbComp.eval_bind, PMF.toOuterMeasure_bind_apply] simp only [hsucc] - rw [ENNReal.tsum_comm] + rw [tsum_comm] simp only [ProbComp.expect, expectedStepsFrom, ENNReal.tsum_mul_left] /-- Under `RandFaultFree`, every horizon partitions probability `1` between `.running` and @@ -208,7 +208,7 @@ theorem RandFaultFree.running_add_halted_eq_one {w : ℕ} {p : Program B r} {s : (hf : RandFaultFree w p s) (t : ℕ) : (evalRand w p t s).toOuterMeasure {cfg | ∃ u, cfg = .running u} + (evalRand w p t s).toOuterMeasure (RandCfg.haltedSet r) = 1 := by - rw [PMF.toOuterMeasure_apply, PMF.toOuterMeasure_apply, ← ENNReal.tsum_add] + rw [PMF.toOuterMeasure_apply, PMF.toOuterMeasure_apply, ← tsum_add] convert (evalRand w p t s).tsum_coe using 1 congr 1 funext cfg @@ -242,7 +242,7 @@ theorem AlmostSurelyHalts.of_expectedTime_ne_top {w : ℕ} {p : Program B r} {s have hinf : (⊤ : ℝ≥0∞) ≤ expectedTime w p s := by calc (⊤ : ℝ≥0∞) = ∑' _ : ℕ, (1 - supH) := (ENNReal.tsum_const_eq_top_of_ne_zero hpos.ne').symm - _ ≤ expectedTime w p s := ENNReal.tsum_le_tsum hle_run + _ ≤ expectedTime w p s := tsum_le_tsum hle_run exact htop (top_unique hinf) /-- Under `RandHaltsWithin w p s T`, the active-configuration tail sum vanishes from `T` onward and diff --git a/Complexity/Crypto/Computational/CramerShoup/ProjectiveHash.lean b/Complexity/Crypto/Computational/CramerShoup/ProjectiveHash.lean index 09e298a66..c52fe95e6 100644 --- a/Complexity/Crypto/Computational/CramerShoup/ProjectiveHash.lean +++ b/Complexity/Crypto/Computational/CramerShoup/ProjectiveHash.lean @@ -555,8 +555,8 @@ theorem uniform_projHash2_fiber_any_le (d : F.Desc) [DecidableEq (F.Carrier d)] have hor : (μ.map (fun τ₂ => (q :: qs).any (fun q' => hit q' τ₂))) true ≤ (μ.map (hit q)) true + (μ.map (fun τ₂ => qs.any (fun q' => hit q' τ₂))) true := by - simp only [List.any_cons, PMF.map_apply, ← ENNReal.tsum_add] - refine ENNReal.tsum_le_tsum fun τ₂ => ?_ + simp only [List.any_cons, PMF.map_apply, ← tsum_add] + refine tsum_le_tsum fun τ₂ => ?_ cases hit q τ₂ <;> cases qs.any (fun q' => hit q' τ₂) <;> simp have hsingle : (μ.map (hit q)) true = (F.order d : ℝ≥0∞)⁻¹ := by have heq : diff --git a/Complexity/Crypto/Computational/CramerShoup/Scheme.lean b/Complexity/Crypto/Computational/CramerShoup/Scheme.lean index 58a4a0937..0e6744c8c 100644 --- a/Complexity/Crypto/Computational/CramerShoup/Scheme.lean +++ b/Complexity/Crypto/Computational/CramerShoup/Scheme.lean @@ -1008,7 +1008,7 @@ private theorem tsum_and_isCramerShoupBadDH_le rw [hpre, Bool.true_and, ← hvElem, ← hwElem, encodeProjHash2_encode, he_equiv τ] rw [hbad_eq] cases cond <;> cases F.encode d (e_equiv τ • F.generator d) == parts.2.2.2 <;> simp - exact (ENNReal.tsum_le_tsum fun τ => + exact (tsum_le_tsum fun τ => mul_le_mul_of_nonneg_left (hle τ) zero_le).trans (F.tsum_uniform_smul_generator_eq e_equiv parts.2.2.2) · simp only [Bool.not_eq_true] at hpre diff --git a/Complexity/Crypto/Computational/DiffieHellman.lean b/Complexity/Crypto/Computational/DiffieHellman.lean index 00a002fc8..2f8a35c45 100644 --- a/Complexity/Crypto/Computational/DiffieHellman.lean +++ b/Complexity/Crypto/Computational/DiffieHellman.lean @@ -173,14 +173,14 @@ theorem succProbability_discreteLogGame_le_cdhGame (setup : ℕ → PMF F.Desc) simp only [discreteLogGame, cdhGame, dlToCDHAdversary, ProbComp.eval_bind, ProbComp.eval_pure, PMF.monad_bind_eq_bind, PMF.monad_pure_eq_pure, PMF.bind_bind, PMF.pure_bind] rw [PMF.bind_apply, PMF.bind_apply] - refine ENNReal.tsum_le_tsum fun d => mul_le_mul_of_nonneg_left ?_ zero_le + refine tsum_le_tsum fun d => mul_le_mul_of_nonneg_left ?_ zero_le rw [PMF.bind_apply, PMF.bind_apply] - refine ENNReal.tsum_le_tsum fun α => mul_le_mul_of_nonneg_left ?_ zero_le + refine tsum_le_tsum fun α => mul_le_mul_of_nonneg_left ?_ zero_le rw [← PMF.bind_const (PMF.uniformOfFintype (ZMod (F.order d))) ((ProbComp.eval (adversary n (d, F.encode d (α • F.generator d)))).bind _)] rw [PMF.bind_apply, PMF.bind_apply] - refine ENNReal.tsum_le_tsum fun β => mul_le_mul_of_nonneg_left ?_ zero_le - refine ENNReal.tsum_le_tsum fun guess => mul_le_mul_of_nonneg_left ?_ zero_le + refine tsum_le_tsum fun β => mul_le_mul_of_nonneg_left ?_ zero_le + refine tsum_le_tsum fun guess => mul_le_mul_of_nonneg_left ?_ zero_le by_cases hα : (ofBits guess : ZMod (F.order d)) = α · simp only [hα, F.smul_encode, smul_smul, decide_true, le_rfl] · simp only [hα, decide_false, PMF.pure_apply, Bool.true_eq_false, ↓reduceIte, zero_le] @@ -301,7 +301,7 @@ theorem ddhIdealGame_cdhToDDHDistinguisher_true_le (setup : ℕ → PMF F.Desc) fun guess => PMF.pure (decide (guess = F.encode d (γ • F.generator d)))) true ≤ ∑' d, setup n d * (m : ℝ≥0∞)⁻¹ := by - refine ENNReal.tsum_le_tsum fun d => ?_ + refine tsum_le_tsum fun d => ?_ by_cases hd : d ∈ (setup n).support · refine mul_le_mul_of_nonneg_left ?_ zero_le have hord : (F.order d : ℝ≥0∞)⁻¹ ≤ (m : ℝ≥0∞)⁻¹ := @@ -315,8 +315,8 @@ theorem ddhIdealGame_cdhToDDHDistinguisher_true_le (setup : ℕ → PMF F.Desc) PMF.pure (decide (guess = F.encode d (γ • F.generator d)))) true ≤ ∑' α, PMF.uniformOfFintype (ZMod (F.order d)) α * ∑' β, PMF.uniformOfFintype (ZMod (F.order d)) β * (m : ℝ≥0∞)⁻¹ := by - refine ENNReal.tsum_le_tsum fun α => mul_le_mul_of_nonneg_left ?_ zero_le - refine ENNReal.tsum_le_tsum fun β => mul_le_mul_of_nonneg_left ?_ zero_le + refine tsum_le_tsum fun α => mul_le_mul_of_nonneg_left ?_ zero_le + refine tsum_le_tsum fun β => mul_le_mul_of_nonneg_left ?_ zero_le rw [PMF.bind_comm, PMF.bind_apply] calc ∑' guess, ProbComp.eval (adversary n (d, F.encode d (α • F.generator d), F.encode d (β • F.generator d))) guess * @@ -325,7 +325,7 @@ theorem ddhIdealGame_cdhToDDHDistinguisher_true_le (setup : ℕ → PMF F.Desc) ≤ ∑' guess, ProbComp.eval (adversary n (d, F.encode d (α • F.generator d), F.encode d (β • F.generator d))) guess * (m : ℝ≥0∞)⁻¹ := by - refine ENNReal.tsum_le_tsum fun guess => + refine tsum_le_tsum fun guess => mul_le_mul_of_nonneg_left ((F.uniform_decide_eq_encode_le d guess).trans hord) zero_le _ = (m : ℝ≥0∞)⁻¹ := by rw [ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul] diff --git a/Complexity/Crypto/Computational/DiffieHellman/Order.lean b/Complexity/Crypto/Computational/DiffieHellman/Order.lean index b0a3dbafa..478cd484b 100644 --- a/Complexity/Crypto/Computational/DiffieHellman/Order.lean +++ b/Complexity/Crypto/Computational/DiffieHellman/Order.lean @@ -138,7 +138,7 @@ private theorem expected_inv_order_le_two_mul_advantage_ddh (F : PrimeOrderGroup (((F.order d - 1 : ℕ) : ℝ≥0∞) * ((F.order d : ℝ≥0∞)⁻¹ * (F.order d : ℝ≥0∞)⁻¹))) := by dsimp only [ddhRealGame, ddhZeroDistinguisher] simp only [ProbComp.eval_pure, PMF.monad_bind_eq_bind, PMF.bind_apply] - rw [← ENNReal.tsum_add] + rw [← tsum_add] refine tsum_congr fun d => ?_ have hstep : (∑' α, PMF.uniformOfFintype (ZMod (F.order d)) α * @@ -164,7 +164,7 @@ private theorem expected_inv_order_le_two_mul_advantage_ddh (F : PrimeOrderGroup 2 * ∑' d, setup n d * (((F.order d - 1 : ℕ) : ℝ≥0∞) * ((F.order d : ℝ≥0∞)⁻¹ * (F.order d : ℝ≥0∞)⁻¹)) := by rw [← ENNReal.tsum_mul_left] - refine ENNReal.tsum_le_tsum fun d => ?_ + refine tsum_le_tsum fun d => ?_ rw [← mul_assoc, mul_comm (2 : ℝ≥0∞) (setup n d), mul_assoc] refine mul_le_mul_of_nonneg_left ?_ zero_le have hq2 : 2 ≤ F.order d := (F.prime_order d).two_le diff --git a/Complexity/Crypto/Computational/Encryption/Authenticated.lean b/Complexity/Crypto/Computational/Encryption/Authenticated.lean index a177011ae..951e29dce 100644 --- a/Complexity/Crypto/Computational/Encryption/Authenticated.lean +++ b/Complexity/Crypto/Computational/Encryption/Authenticated.lean @@ -1662,7 +1662,7 @@ private theorem ennreal_tsum_finsetSum {α β : Type*} induction s using Finset.induction_on with | empty => simp | insert b s hb ih => - simp only [Finset.sum_insert hb, ENNReal.tsum_add, ih] + simp only [Finset.sum_insert hb, tsum_add, ih] private theorem eval_ccaRejectWithBadOracle_false (E : SymmetricCipher) (n : ℕ) (k : List Bool) (bit : Bool) (request : Bool × List Bool) (queried : List (List Bool)) : @@ -1761,7 +1761,7 @@ private theorem bad_ccaRejectWithBad_le_sum_ciCombined {α : Type} (E : Symmetri (fun x => PMF.pure (!x.2.2.contains x.2.1.2.2 && (E.decrypt n k x.2.1.2.2).isSome)) true := - ENNReal.tsum_le_tsum fun a => mul_le_mul_of_nonneg_left (ih a queried) zero_le + tsum_le_tsum fun a => mul_le_mul_of_nonneg_left (ih a queried) zero_le _ = _ := by simp only [Finset.mul_sum] rw [ennreal_tsum_finsetSum] @@ -1818,7 +1818,7 @@ private theorem bad_ccaRejectWithBad_le_sum_ciCombined {α : Type} (E : Symmetri PMF.pure (!x.2.2.contains x.2.1.2.2 && (E.decrypt n k x.2.1.2.2).isSome)) true := - ENNReal.tsum_le_tsum fun ct => + tsum_le_tsum fun ct => mul_le_mul_of_nonneg_left (ih ct (ct :: queried)) zero_le _ = _ := by simp only [Finset.mul_sum] @@ -1993,8 +1993,8 @@ private theorem advantage_ccaGame_ccaRejectGame_le_sum (E : SymmetricCipher) (ct ProbOracleComp.eval_run_interpret, hrejEq] exact PMF.bind_apply _ _ _ have hle1 : pCCA ≤ pRej + pSum := by - rw [hCCA_eq, hRej_eq, hsum_eq, ← ENNReal.tsum_add] - refine ENNReal.tsum_le_tsum fun k => ?_ + rw [hCCA_eq, hRej_eq, hsum_eq, ← tsum_add] + refine tsum_le_tsum fun k => ?_ rw [← mul_add] by_cases hk : k ∈ (ProbComp.eval (E.keyGen n)).support · refine mul_le_mul_of_nonneg_left ?_ zero_le @@ -2007,8 +2007,8 @@ private theorem advantage_ccaGame_ccaRejectGame_le_sum (E : SymmetricCipher) (ct rw [eval_ciGame_ccaToCIAtIndexAdversary_key E ctBound j bit adversary n k hk hctBound] · rw [(PMF.apply_eq_zero_iff _ _).mpr hk, zero_mul, zero_mul] have hle2 : pRej ≤ pCCA + pSum := by - rw [hCCA_eq, hRej_eq, hsum_eq, ← ENNReal.tsum_add] - refine ENNReal.tsum_le_tsum fun k => ?_ + rw [hCCA_eq, hRej_eq, hsum_eq, ← tsum_add] + refine tsum_le_tsum fun k => ?_ rw [← mul_add] by_cases hk : k ∈ (ProbComp.eval (E.keyGen n)).support · refine mul_le_mul_of_nonneg_left ?_ zero_le diff --git a/Complexity/Crypto/Computational/Encryption/EncryptThenMAC.lean b/Complexity/Crypto/Computational/Encryption/EncryptThenMAC.lean index d8d240fb8..e33925650 100644 --- a/Complexity/Crypto/Computational/Encryption/EncryptThenMAC.lean +++ b/Complexity/Crypto/Computational/Encryption/EncryptThenMAC.lean @@ -1629,7 +1629,7 @@ private theorem step_bad_prfCipherForgetfulWithBadOracle_le (n : ℕ) (bit : Boo {x | x.2.2 = true} ≤ ∑' r, (BitString.uniformList n) r * {r : List Bool | (cache.lookup r).isSome}.indicator 1 r := by - refine ENNReal.tsum_le_tsum fun r => mul_le_mul_of_nonneg_left ?_ zero_le + refine tsum_le_tsum fun r => mul_le_mul_of_nonneg_left ?_ zero_le by_cases hr : (cache.lookup r).isSome = true · rw [Set.indicator_of_mem (show r ∈ {r : List Bool | (cache.lookup r).isSome} from hr), Pi.one_apply] @@ -1697,7 +1697,7 @@ private theorem bad_prfCipherForgetfulWithBadOracle_le {α : Type} (n : ℕ) (bi (cont y.1)).run y.2).toOuterMeasure {x | x.2.2 = true} ≤ ∑' y, ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifOracle m)) (prfCipherForgetfulWithBadOracle n bit) (Sum.inl m) (cache, false) y * B := by - refine ENNReal.tsum_le_tsum fun y => ?_ + refine tsum_le_tsum fun y => ?_ by_cases hy : y ∈ (ProbOracleComp.handler (fun m => StateT.lift (ProbComp.unifOracle m)) (prfCipherForgetfulWithBadOracle n bit) (Sum.inl m) (cache, false)).support @@ -1749,10 +1749,10 @@ private theorem bad_prfCipherForgetfulWithBadOracle_le {α : Type} (n : ℕ) (bi ≤ ∑' y, prfCipherForgetfulWithBadOracle n bit request (cache, false) y * ({x : List Bool × (List (List Bool × List Bool) × Bool) | x.2.2 = true}.indicator 1 y + K) := - ENNReal.tsum_le_tsum hterm + tsum_le_tsum hterm _ = (prfCipherForgetfulWithBadOracle n bit request (cache, false)).toOuterMeasure {x | x.2.2 = true} + K := by - simp only [mul_add, ENNReal.tsum_add, ENNReal.tsum_mul_right, PMF.tsum_coe, + simp only [mul_add, tsum_add, ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul, PMF.toOuterMeasure_apply] congr 1 refine tsum_congr fun y => ?_ diff --git a/Complexity/Crypto/Computational/GMW/Soundness.lean b/Complexity/Crypto/Computational/GMW/Soundness.lean index 7841dd8ec..99dcd2c2f 100644 --- a/Complexity/Crypto/Computational/GMW/Soundness.lean +++ b/Complexity/Crypto/Computational/GMW/Soundness.lean @@ -562,7 +562,7 @@ theorem acceptProb_gmwVerifier_le {α : Type} {generator : List Bool → List Bo P.init x * ProbOracleComp.acceptProbWith P.respond (step2 r x.1) x.2 ≤ ∑' x, ProbOracleComp.runWith P.respond (List.take (K * m) <$> ProbOracleComp.query ((), r)) P.init x * (1 / 8) := - ENNReal.tsum_le_tsum fun x => mul_le_mul_right (hstep2_le r x.1 x.2 hbind) _ + tsum_le_tsum fun x => mul_le_mul_right (hstep2_le r x.1 x.2 hbind) _ _ = (∑' x, ProbOracleComp.runWith P.respond (List.take (K * m) <$> ProbOracleComp.query ((), r)) P.init x) * (1 / 8) := ENNReal.tsum_mul_right @@ -591,9 +591,9 @@ theorem acceptProb_gmwVerifier_le {α : Type} {generator : List Bool → List Bo calc ∑' r, BitString.uniformList m r * acceptProb (step1 r) P ≤ ∑' r, (({r | ¬ NaorBinding generator s r}.indicator (BitString.uniformList m) r) + - BitString.uniformList m r * (1 / 8)) := ENNReal.tsum_le_tsum hterm_le + BitString.uniformList m r * (1 / 8)) := tsum_le_tsum hterm_le _ = (∑' r, {r | ¬ NaorBinding generator s r}.indicator (BitString.uniformList m) r) + - ∑' r, BitString.uniformList m r * (1 / 8) := ENNReal.tsum_add + ∑' r, BitString.uniformList m r * (1 / 8) := tsum_add _ = naorBindingProb generator length s + 1 / 8 := by rw [← PMF.toOuterMeasure_apply, ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul] rfl diff --git a/Complexity/Crypto/Computational/MAC.lean b/Complexity/Crypto/Computational/MAC.lean index 5b35fad27..5d8f4e36f 100644 --- a/Complexity/Crypto/Computational/MAC.lean +++ b/Complexity/Crypto/Computational/MAC.lean @@ -797,7 +797,7 @@ private theorem pmf_bind_pure_bool_le {α : Type} (p : PMF α) (f g : α → Boo (p.bind (fun a => PMF.pure (f a))) true ≤ (p.bind (fun a => PMF.pure (g a))) true := by rw [PMF.bind_apply, PMF.bind_apply] - refine ENNReal.tsum_le_tsum fun a => ?_ + refine tsum_le_tsum fun a => ?_ by_cases ha : a ∈ p.support · refine mul_le_mul_of_nonneg_left ?_ zero_le cases hf : f a @@ -816,7 +816,7 @@ theorem succProbability_eufCmaGame_le_sufCmaGame (M : MACScheme) simp only [Game.succProbability, sufCmaGame, ProbComp.eval_bind, ProbComp.eval_pure] refine ENNReal.toNNReal_mono (PMF.apply_ne_top _ true) ?_ rw [PMF.bind_apply, PMF.bind_apply] - refine ENNReal.tsum_le_tsum fun sk => ?_ + refine tsum_le_tsum fun sk => ?_ refine mul_le_mul_of_nonneg_left (pmf_bind_pure_bool_le _ _ _ fun out _ hout => ?_) zero_le simp only [Bool.and_eq_true, Bool.not_eq_true'] at hout ⊢ refine ⟨⟨hout.1.1, ?_⟩, hout.2⟩ @@ -1450,7 +1450,7 @@ theorem succProbability_prfIdealGame_macToPrfDistinguisher_le (adversary : Forge zero_le] have htotal : (phase1.bind phase2) true ≤ (1 : ℝ≥0∞) / 2 ^ n := by rw [PMF.bind_apply] - refine (ENNReal.tsum_le_tsum fun a => ?_).trans + refine (tsum_le_tsum fun a => ?_).trans (PMF.tsum_mul_le_of_forall_le phase1 fun _ => le_rfl) by_cases ha : a ∈ phase1.support · exact mul_le_mul_right (hstep_bound a ha) _ @@ -2012,7 +2012,7 @@ theorem succProbability_eufCmaGame_le_sufCmaGame (M : NonceMACScheme) simp only [Game.succProbability, eufCmaGame, sufCmaGame, ProbComp.eval_bind, ProbComp.eval_pure] refine ENNReal.toNNReal_mono (PMF.apply_ne_top _ true) ?_ rw [PMF.bind_apply, PMF.bind_apply] - refine ENNReal.tsum_le_tsum fun sk => ?_ + refine tsum_le_tsum fun sk => ?_ refine mul_le_mul_of_nonneg_left (MACScheme.pmf_bind_pure_bool_le _ _ _ fun out _ hout => ?_) zero_le simp only [Bool.and_eq_true, Bool.not_eq_true'] at hout ⊢ diff --git a/Complexity/Crypto/Computational/NaorReingold/Security.lean b/Complexity/Crypto/Computational/NaorReingold/Security.lean index ad05595ed..bcc1e4022 100644 --- a/Complexity/Crypto/Computational/NaorReingold/Security.lean +++ b/Complexity/Crypto/Computational/NaorReingold/Security.lean @@ -2995,7 +2995,7 @@ theorem DDH.negligible_naorReingoldHashedGame_const {F : PrimeOrderGroupFamily} refine ne_of_lt (lt_of_le_of_lt ?_ ENNReal.one_lt_top) calc (∑' d, setup n d * (F.order d : ℝ≥0∞)⁻¹) ≤ ∑' d, setup n d * 1 := by - refine ENNReal.tsum_le_tsum fun d => mul_le_mul_of_nonneg_left ?_ (by positivity) + refine tsum_le_tsum fun d => mul_le_mul_of_nonneg_left ?_ (by positivity) exact ENNReal.inv_le_one.mpr (by exact_mod_cast (F.prime_order d).pos) _ = 1 := by simp [(setup n).tsum_coe] have hε_pos : 0 < ε n := by @@ -3007,7 +3007,7 @@ theorem DDH.negligible_naorReingoldHashedGame_const {F : PrimeOrderGroupFamily} ENNReal.mul_pos hd₀_ne hord_ne exact ENNReal.toNNReal_pos (ne_of_gt (lt_of_lt_of_le hterm_pos - (ENNReal.le_tsum (f := fun d => setup n d * (F.order d : ℝ≥0∞)⁻¹) d₀))) + (le_tsum (f := fun d => setup n d * (F.order d : ℝ≥0∞)⁻¹) d₀))) hexp_top let τ : ℝ≥0 := NNReal.sqrt (ε n) have hτ_pos : 0 < τ := NNReal.sqrt_pos.mpr hε_pos @@ -3079,7 +3079,7 @@ theorem DDH.negligible_naorReingoldHashedGame_const {F : PrimeOrderGroupFamily} have hsum_err : (∑' d, setup n d * errTerm d) = ((lhlBound n : ℝ≥0) : ℝ≥0∞) := by dsimp only [errTerm, lhlBound] - simp_rw [mul_add, ENNReal.tsum_add, ← mul_assoc] + simp_rw [mul_add, tsum_add, ← mul_assoc] rw [ENNReal.tsum_mul_right, ENNReal.tsum_mul_right, (setup n).tsum_coe, one_mul, ENNReal.coe_add] congr 1 @@ -3095,8 +3095,8 @@ theorem DDH.negligible_naorReingoldHashedGame_const {F : PrimeOrderGroupFamily} (hfg : ∀ d ∈ (setup n).support, f d true ≤ g d true + ((Game.advantage (g1 d) (g2 d) : ℝ≥0) : ℝ≥0∞)) : ((setup n).bind f) true ≤ ((setup n).bind g) true + ((lhlBound n : ℝ≥0) : ℝ≥0∞) := by - rw [← hsum_err, PMF.bind_apply, PMF.bind_apply, ← ENNReal.tsum_add] - refine ENNReal.tsum_le_tsum fun d => ?_ + rw [← hsum_err, PMF.bind_apply, PMF.bind_apply, ← tsum_add] + refine tsum_le_tsum fun d => ?_ by_cases hd : d ∈ (setup n).support · rw [← mul_add] exact mul_le_mul_of_nonneg_left ((hfg d hd).trans (add_le_add le_rfl (hpoint d hd))) diff --git a/Complexity/Crypto/Computational/OneWay/LiuPassConverse.lean b/Complexity/Crypto/Computational/OneWay/LiuPassConverse.lean index 3c997b6a5..2350d6382 100644 --- a/Complexity/Crypto/Computational/OneWay/LiuPassConverse.lean +++ b/Complexity/Crypto/Computational/OneWay/LiuPassConverse.lean @@ -610,7 +610,7 @@ private theorem heuristic_fail_mul_le_family_fail {t : ℕ → ℕ} (ht : ∀ n, rw [hfailA_eq] dsimp only [failH] rw [PMF.map_apply, PMF.map_apply] - refine ENNReal.tsum_le_tsum fun coins => ?_ + refine tsum_le_tsum fun coins => ?_ split_ifs with h1 h2 · rfl · exfalso diff --git a/Complexity/Crypto/Computational/OneWayToSignature.lean b/Complexity/Crypto/Computational/OneWayToSignature.lean index ce784ba7d..a3cc11acb 100644 --- a/Complexity/Crypto/Computational/OneWayToSignature.lean +++ b/Complexity/Crypto/Computational/OneWayToSignature.lean @@ -663,7 +663,7 @@ private theorem map_bool_apply_true {α : Type*} (p : PMF α) (P : α → Bool) private theorem ennreal_tsum_finsetSum {α ι : Type*} (s : Finset ι) (g : ι → α → ℝ≥0∞) : (∑' a, ∑ i ∈ s, g i a) = ∑ i ∈ s, ∑' a, g i a := - Summable.tsum_finsetSum fun _ _ => ENNReal.summable + Summable.tsum_finsetSum fun _ _ => CompleteLattice.summable /-- **Lamport's one-time signature theorem**: for any one-way function `f`, `lamportScheme f` is existentially unforgeable under one-time adaptive chosen-message attack (`OneTimeEUFCMA`). -/ @@ -817,7 +817,7 @@ theorem OneWay.oneTimeEUFCMA_lamport {f : List Bool → List Bool} (hf : OneWay ≤ ∑' out, ∑ j : Fin (dyadicSize (2 * n)), if j.val < 2 * n then runWith (pkOf w) (dummySkOf w j.val) out * (if invWin w j.val out then 1 else 0) - else 0 := ENNReal.tsum_le_tsum (hpointwise w hw) + else 0 := tsum_le_tsum (hpointwise w hw) _ = ∑ j : Fin (dyadicSize (2 * n)), ∑' out, if j.val < 2 * n then runWith (pkOf w) (dummySkOf w j.val) out * (if invWin w j.val out then 1 else 0) @@ -845,7 +845,7 @@ theorem OneWay.oneTimeEUFCMA_lamport {f : List Bool → List Bool} (hf : OneWay if j.val < 2 * n then ((runWith (pkOf w) (dummySkOf w j.val)).map (invWin w j.val)) true else 0 := by - refine ENNReal.tsum_le_tsum fun w => ?_ + refine tsum_le_tsum fun w => ?_ by_cases hw : w ∈ μ.support · exact mul_le_mul_right (hw_sum w hw) (μ w) · simp [(PMF.apply_eq_zero_iff μ w).mpr hw] diff --git a/Complexity/Crypto/Computational/PRGToOneWay.lean b/Complexity/Crypto/Computational/PRGToOneWay.lean index 1fb8c7c47..70e7f7aea 100644 --- a/Complexity/Crypto/Computational/PRGToOneWay.lean +++ b/Complexity/Crypto/Computational/PRGToOneWay.lean @@ -107,7 +107,7 @@ theorem succProbability_uniformList_test_le {generator : List Bool → List Bool split_ifs with hr · exact PMF.coe_le_one _ _ simp only [test, ProbComp.eval_bind, ProbComp.eval_pure, PMF.bind_apply, PMF.pure_apply] - refine le_of_eq (ENNReal.tsum_eq_zero.2 fun x => ?_) + refine le_of_eq (tsum_eq_zero_iff.2 fun x => ?_) have hne : generator x ≠ List.ofFn r := fun hx => hr <| mem_image.2 ⟨x, hP x (by rw [← hlen x, hx, List.length_ofFn]), hx⟩ simp [hne] diff --git a/Complexity/Crypto/Computational/PedersenCommitment.lean b/Complexity/Crypto/Computational/PedersenCommitment.lean index e77853f5a..09842b79b 100644 --- a/Complexity/Crypto/Computational/PedersenCommitment.lean +++ b/Complexity/Crypto/Computational/PedersenCommitment.lean @@ -405,11 +405,11 @@ theorem pedersenHidingGame_apply_true_le (setup : ℕ → PMF F.Desc) ≤ ∑' α, (PMF.uniformOfFintype (ZMod (F.order d)) α * F.pedersenScheme.hidingForKey ⟨d, α • F.generator d⟩ adversary b₂ n true + if α = 0 then (F.order d : ℝ≥0∞)⁻¹ else 0) := - ENNReal.tsum_le_tsum hα + tsum_le_tsum hα _ = (∑' α, PMF.uniformOfFintype (ZMod (F.order d)) α * F.pedersenScheme.hidingForKey ⟨d, α • F.generator d⟩ adversary b₂ n true) + (F.order d : ℝ≥0∞)⁻¹ := by - rw [ENNReal.tsum_add, tsum_ite_eq] + rw [tsum_add, tsum_ite_eq] _ ≤ (∑' α, PMF.uniformOfFintype (ZMod (F.order d)) α * F.pedersenScheme.hidingForKey ⟨d, α • F.generator d⟩ adversary b₂ n true) + (m : ℝ≥0∞)⁻¹ := add_le_add le_rfl hord @@ -423,7 +423,7 @@ theorem pedersenHidingGame_apply_true_le (setup : ℕ → PMF F.Desc) (((PMF.uniformOfFintype (ZMod (F.order d))).bind fun α => F.pedersenScheme.hidingForKey ⟨d, α • F.generator d⟩ adversary b₂ n) true + (m : ℝ≥0∞)⁻¹) := by - refine ENNReal.tsum_le_tsum fun d => ?_ + refine tsum_le_tsum fun d => ?_ by_cases hd : d ∈ (setup n).support · exact mul_le_mul_of_nonneg_left (hd_bound d hd) zero_le · have hd0 : setup n d = 0 := by rwa [PMF.mem_support_iff, not_not] at hd @@ -432,7 +432,7 @@ theorem pedersenHidingGame_apply_true_le (setup : ℕ → PMF F.Desc) ((PMF.uniformOfFintype (ZMod (F.order d))).bind fun α => F.pedersenScheme.hidingForKey ⟨d, α • F.generator d⟩ adversary b₂ n) true) + (m : ℝ≥0∞)⁻¹ := by - simp only [mul_add, ENNReal.tsum_add, ENNReal.tsum_mul_right, (setup n).tsum_coe, one_mul] + simp only [mul_add, tsum_add, ENNReal.tsum_mul_right, (setup n).tsum_coe, one_mul] /-- **Unconditional hiding bound for Pedersen commitments**: when every supported group at parameter `n` has order at least `m > 0`, any (even computationally unbounded) diff --git a/Complexity/Crypto/Computational/Pseudoentropy/MaskedExtraction.lean b/Complexity/Crypto/Computational/Pseudoentropy/MaskedExtraction.lean index 9a46827f4..ffd5f9b0b 100644 --- a/Complexity/Crypto/Computational/Pseudoentropy/MaskedExtraction.lean +++ b/Complexity/Crypto/Computational/Pseudoentropy/MaskedExtraction.lean @@ -109,7 +109,7 @@ theorem maskedSample_mass_ge {α β : Type} (source : ProbComp α) rw [← ENNReal.toReal_mul] apply ENNReal.toReal_mono (PMF.apply_ne_top _ _) rw [PMF.bind_apply] - exact ENNReal.le_tsum (f := fun x => ProbComp.eval source x * + exact le_tsum (f := fun x => ProbComp.eval source x * ((ProbComp.eval (maskBit (mask x) (truth x))).map (observe x, ·)) (observe value, bit)) value exact (mul_le_mul (hsource value hvalue) (hbitMass.trans hmap) diff --git a/Complexity/Crypto/Game.lean b/Complexity/Crypto/Game.lean index e289aabc6..4f37d2714 100644 --- a/Complexity/Crypto/Game.lean +++ b/Complexity/Crypto/Game.lean @@ -295,16 +295,16 @@ theorem advantage_bind_le {α : Type*} (d : PMF α) (game1 game2 : α → Game) rw [PMF.bind_apply, PMF.bind_apply] calc ∑' a, d a * game1 a true ≤ ∑' a, d a * (game2 a true + ENNReal.ofReal ε) := - ENNReal.tsum_le_tsum fun a => mul_le_mul_of_nonneg_left (h1 a) zero_le + tsum_le_tsum fun a => mul_le_mul_of_nonneg_left (h1 a) zero_le _ = (∑' a, d a * game2 a true) + ENNReal.ofReal ε := by - simp only [mul_add, ENNReal.tsum_add, ENNReal.tsum_mul_right, d.tsum_coe, one_mul] + simp only [mul_add, tsum_add, ENNReal.tsum_mul_right, d.tsum_coe, one_mul] have hsum2 : (d.bind game2) true ≤ (d.bind game1) true + ENNReal.ofReal ε := by rw [PMF.bind_apply, PMF.bind_apply] calc ∑' a, d a * game2 a true ≤ ∑' a, d a * (game1 a true + ENNReal.ofReal ε) := - ENNReal.tsum_le_tsum fun a => mul_le_mul_of_nonneg_left (h2 a) zero_le + tsum_le_tsum fun a => mul_le_mul_of_nonneg_left (h2 a) zero_le _ = (∑' a, d a * game1 a true) + ENNReal.ofReal ε := by - simp only [mul_add, ENNReal.tsum_add, ENNReal.tsum_mul_right, d.tsum_coe, one_mul] + simp only [mul_add, tsum_add, ENNReal.tsum_mul_right, d.tsum_coe, one_mul] have hadv := advantage_le_of_apply_le ENNReal.ofReal_ne_top hsum1 hsum2 rwa [ENNReal.toReal_ofReal hε0] at hadv @@ -324,8 +324,8 @@ theorem advantage_bind_uniformOfFintype_of_ne_le {α : Type*} [Fintype α] [None have hle₁ : ((PMF.uniformOfFintype α).bind g) true ≤ p true + (Fintype.card α : ℝ≥0∞)⁻¹ := by rw [← PMF.bind_const (PMF.uniformOfFintype α) p, ← hsingle, PMF.bind_apply, PMF.bind_apply, - PMF.bind_apply, ← ENNReal.tsum_add] - refine ENNReal.tsum_le_tsum fun a => ?_ + PMF.bind_apply, ← tsum_add] + refine tsum_le_tsum fun a => ?_ rw [← mul_add] refine mul_le_mul_of_nonneg_left ?_ zero_le by_cases ha : a = a₀ @@ -335,8 +335,8 @@ theorem advantage_bind_uniformOfFintype_of_ne_le {α : Type*} [Fintype α] [None have hle₂ : p true ≤ ((PMF.uniformOfFintype α).bind g) true + (Fintype.card α : ℝ≥0∞)⁻¹ := by nth_rw 1 [← PMF.bind_const (PMF.uniformOfFintype α) p] - rw [← hsingle, PMF.bind_apply, PMF.bind_apply, PMF.bind_apply, ← ENNReal.tsum_add] - refine ENNReal.tsum_le_tsum fun a => ?_ + rw [← hsingle, PMF.bind_apply, PMF.bind_apply, PMF.bind_apply, ← tsum_add] + refine tsum_le_tsum fun a => ?_ rw [← mul_add] refine mul_le_mul_of_nonneg_left ?_ zero_le by_cases ha : a = a₀ @@ -363,8 +363,8 @@ theorem bind_apply_le_add_bind_apply {β : Type*} (μ : PMF β) (μ.bind game2) b ≤ (μ.bind game1) b + (μ.bind err) b := by have h1 (f g : β → Game) (hle : ∀ x, f x b ≤ g x b + err x b) : (μ.bind f) b ≤ (μ.bind g) b + (μ.bind err) b := by - simp only [PMF.bind_apply, ← ENNReal.tsum_add, ← mul_add] - exact ENNReal.tsum_le_tsum fun x => mul_le_mul_of_nonneg_left (hle x) zero_le + simp only [PMF.bind_apply, ← tsum_add, ← mul_add] + exact tsum_le_tsum fun x => mul_le_mul_of_nonneg_left (hle x) zero_le exact ⟨h1 game1 game2 fun x => (h x).1, h1 game2 game1 fun x => (h x).2⟩ /-- Averaging pointwise acceptance-probability bounds `game1 x true ≤ game2 x true + err x true` diff --git a/Complexity/Crypto/Game/UntilBad.lean b/Complexity/Crypto/Game/UntilBad.lean index 52f3c2892..f80d1e46d 100644 --- a/Complexity/Crypto/Game/UntilBad.lean +++ b/Complexity/Crypto/Game/UntilBad.lean @@ -164,9 +164,9 @@ theorem runStatelessOracle_bind_le {T α : Type} (μ : PMF T) (∑' x, PMF.uniformOfFintype (Fin (m + 1)) x * g x) + (Q : ℝ≥0∞) * δ := by calc ∑' x, PMF.uniformOfFintype (Fin (m + 1)) x * f x ≤ ∑' x, PMF.uniformOfFintype (Fin (m + 1)) x * (g x + (Q : ℝ≥0∞) * δ) := - ENNReal.tsum_le_tsum fun x => mul_le_mul_of_nonneg_left (hle x) zero_le + tsum_le_tsum fun x => mul_le_mul_of_nonneg_left (hle x) zero_le _ = (∑' x, PMF.uniformOfFintype (Fin (m + 1)) x * g x) + (Q : ℝ≥0∞) * δ := by - simp only [mul_add, ENNReal.tsum_add, ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul] + simp only [mul_add, tsum_add, ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul] exact ⟨hsum _ _ (fun x => (ih x).1), hsum _ _ (fun x => (ih x).2)⟩ · simp only [Sum.elim_inr] have hinr₁ : @@ -184,13 +184,13 @@ theorem runStatelessOracle_bind_le {T α : Type} (μ : PMF T) simp only [PMF.bind_apply] calc ∑' τ, μ τ * f τ b ≤ ∑' τ, (μ τ * g τ b + μ τ * if isBad τ req.2 then 1 else 0) := by - refine ENNReal.tsum_le_tsum fun τ => ?_ + refine tsum_le_tsum fun τ => ?_ by_cases hb : isBad τ req.2 = true · simp only [hb, ↓reduceIte, mul_one] exact le_add_of_le_right (mul_le_of_le_one_right zero_le (PMF.coe_le_one _ _)) · simp only [Bool.not_eq_true] at hb rw [hfg τ hb]; simp [hb] - _ = (∑' τ, μ τ * g τ b) + ∑' τ, μ τ * if isBad τ req.2 then 1 else 0 := ENNReal.tsum_add + _ = (∑' τ, μ τ * g τ b) + ∑' τ, μ τ * if isBad τ req.2 then 1 else 0 := tsum_add _ ≤ (∑' τ, μ τ * g τ b) + δ := add_le_add le_rfl (hbad req.2) have hstep1 := hstep (fun τ => runStatelessOracle (o₁ τ) (k (o₁ τ req.2)) (next τ)) @@ -372,20 +372,20 @@ theorem runStatelessOracle_le_runFinderOracle {α : Type} ≤ ∑' x, PMF.uniformOfFintype (Fin (m + 1)) x * (runStatelessOracle o₂ (k x) next b + runFinderOracle o₂ isBad extract (k x) none isGood true) := - ENNReal.tsum_le_tsum fun x => mul_le_mul_of_nonneg_left (ih x).1 zero_le + tsum_le_tsum fun x => mul_le_mul_of_nonneg_left (ih x).1 zero_le _ = (∑' x, PMF.uniformOfFintype (Fin (m + 1)) x * runStatelessOracle o₂ (k x) next b) + ∑' x, PMF.uniformOfFintype (Fin (m + 1)) x * runFinderOracle o₂ isBad extract (k x) none isGood true := by - simp only [mul_add, ENNReal.tsum_add] + simp only [mul_add, tsum_add] · calc ∑' x, PMF.uniformOfFintype (Fin (m + 1)) x * runStatelessOracle o₂ (k x) next b ≤ ∑' x, PMF.uniformOfFintype (Fin (m + 1)) x * (runStatelessOracle o₁ (k x) next b + runFinderOracle o₂ isBad extract (k x) none isGood true) := - ENNReal.tsum_le_tsum fun x => mul_le_mul_of_nonneg_left (ih x).2 zero_le + tsum_le_tsum fun x => mul_le_mul_of_nonneg_left (ih x).2 zero_le _ = (∑' x, PMF.uniformOfFintype (Fin (m + 1)) x * runStatelessOracle o₁ (k x) next b) + ∑' x, PMF.uniformOfFintype (Fin (m + 1)) x * runFinderOracle o₂ isBad extract (k x) none isGood true := by - simp only [mul_add, ENNReal.tsum_add] + simp only [mul_add, tsum_add] · rw [runStatelessOracle_query_inr, runStatelessOracle_query_inr, runFinderOracle_query_inr] by_cases hb : isBad req.2 = true · simp only [hb, ↓reduceIte] diff --git a/Complexity/Crypto/Primitives/PRG/NextBit.lean b/Complexity/Crypto/Primitives/PRG/NextBit.lean index 886b68555..76fe10070 100644 --- a/Complexity/Crypto/Primitives/PRG/NextBit.lean +++ b/Complexity/Crypto/Primitives/PRG/NextBit.lean @@ -121,9 +121,9 @@ private theorem bind_apply_add_of_forall {α β : Type*} (μ : PMF α) {f g h : {c : ℝ≥0∞} (hfg : ∀ x, f x b + g x b = h x b + c) : μ.bind f b + μ.bind g b = μ.bind h b + c := by simp only [PMF.bind_apply] - rw [← ENNReal.tsum_add] + rw [← tsum_add] simp_rw [← mul_add, hfg, mul_add] - rw [ENNReal.tsum_add, ENNReal.tsum_mul_right, μ.tsum_coe, one_mul] + rw [tsum_add, ENNReal.tsum_mul_right, μ.tsum_coe, one_mul] /-- **The distinguisher/predictor lemma** [Boneh and Shoup, Lemma 3.5][BonehShoup2023]. Draw `x` from `μ`. The guess `guessOfDistinguisher (d x)` equals `target x` with probability `1 / 2`, plus diff --git a/Complexity/Crypto/Protocols/Cipher/EvenMansour.lean b/Complexity/Crypto/Protocols/Cipher/EvenMansour.lean index b88c043a5..ca41828a6 100644 --- a/Complexity/Crypto/Protocols/Cipher/EvenMansour.lean +++ b/Complexity/Crypto/Protocols/Cipher/EvenMansour.lean @@ -1955,7 +1955,7 @@ private theorem idealGame_apply_le_realGame_add_of_keyDist (keyDist : PMF (G × calc (∑' res : Bool × (Cache G × Cache G), M_ideal res * keyDist.toOuterMeasure {k | isBadEvenMansourKey res.2.1 res.2.2 k = true}) ≤ ∑' res : Bool × (Cache G × Cache G), M_ideal res * ε := by - refine ENNReal.tsum_le_tsum fun res => ?_ + refine tsum_le_tsum fun res => ?_ by_cases hmem : res ∈ M_ideal.support · refine mul_le_mul_of_nonneg_left (hbad_le res.2.1 res.2.2 ?_ ?_) zero_le · simpa using length_fst_idealLazyHandler_le hcall.1 hmem @@ -1968,14 +1968,14 @@ private theorem idealGame_apply_le_realGame_add_of_keyDist (keyDist : PMF (G × ≤ ∑' k : G × G, keyDist k * ((((OracleComp.interpret (realLazyHandler k) program).run []).map Prod.fst) b + M_ideal.toOuterMeasure {res | isBadEvenMansourKey res.2.1 res.2.2 k = true}) := - ENNReal.tsum_le_tsum fun k => mul_le_mul_of_nonneg_left (hpoint k) zero_le + tsum_le_tsum fun k => mul_le_mul_of_nonneg_left (hpoint k) zero_le _ = (∑' k : G × G, keyDist k * (((OracleComp.interpret (realLazyHandler k) program).run []).map Prod.fst) b) + (∑' k : G × G, keyDist k * M_ideal.toOuterMeasure {res | isBadEvenMansourKey res.2.1 res.2.2 k = true}) := by - simp_rw [mul_add, ENNReal.tsum_add] + simp_rw [mul_add, tsum_add] _ ≤ _ := add_le_add le_rfl hsum_bad private theorem advantage_le_of_forall_bool_le {g₁ g₂ : Game} {ε : ℝ≥0∞} (hε : ε ≠ ⊤) diff --git a/Complexity/Crypto/Protocols/Cipher/LubyRackoff.lean b/Complexity/Crypto/Protocols/Cipher/LubyRackoff.lean index ac6f10219..a46a9a06e 100644 --- a/Complexity/Crypto/Protocols/Cipher/LubyRackoff.lean +++ b/Complexity/Crypto/Protocols/Cipher/LubyRackoff.lean @@ -1013,7 +1013,7 @@ private theorem tsum_seed_toOuterMeasure_hasCollision_le {seed₁ : PMF K₁} {h ∑' x, idealRun x * ∑' k₁, ({k₁ : K₁ | hasUCollision (h₁ k₁) x.2 = true}.indicator seed₁ k₁) := by simp only [← ENNReal.tsum_mul_left] - rw [ENNReal.tsum_comm] + rw [tsum_comm] refine tsum_congr fun x => tsum_congr fun k₁ => ?_ by_cases hx : hasUCollision (h₁ k₁) x.2 = true · rw [Set.indicator_of_mem hx, Set.indicator_of_mem hx, mul_comm] @@ -1022,7 +1022,7 @@ private theorem tsum_seed_toOuterMeasure_hasCollision_le {seed₁ : PMF K₁} {h calc ∑' x, idealRun x * ∑' k₁, ({k₁ : K₁ | hasUCollision (h₁ k₁) x.2 = true}.indicator seed₁ k₁) ≤ ∑' x, idealRun x * (Q.choose 2 * ε) := by - refine ENNReal.tsum_le_tsum fun x => ?_ + refine tsum_le_tsum fun x => ?_ by_cases hmem : x ∈ idealRun.support · refine mul_le_mul_of_nonneg_left ?_ zero_le rw [← PMF.toOuterMeasure_apply] @@ -1045,10 +1045,10 @@ private theorem tsum_seed_toOuterMeasure_hasCollision_le {seed₁ : PMF K₁} {h ≤ ∑' k₁, seed₁ k₁ * (idealRun.toOuterMeasure {x | hasUCollision (h₁ k₁) x.2 = true} + idealRun.toOuterMeasure {x | hasVCollision x.2 = true}) := - ENNReal.tsum_le_tsum fun k₁ => mul_le_mul_of_nonneg_left (hsplit k₁) zero_le + tsum_le_tsum fun k₁ => mul_le_mul_of_nonneg_left (hsplit k₁) zero_le _ = (∑' k₁, seed₁ k₁ * idealRun.toOuterMeasure {x | hasUCollision (h₁ k₁) x.2 = true}) + (∑' k₁, seed₁ k₁ * idealRun.toOuterMeasure {x | hasVCollision x.2 = true}) := by - simp only [mul_add, ENNReal.tsum_add] + simp only [mul_add, tsum_add] _ ≤ Q.choose 2 * ε + Q.choose 2 * (Fintype.card G : ℝ≥0∞)⁻¹ := add_le_add hu hv /-- For every fixed `f₁`, the acceptance probability of `feistel3FixedGame f₁ program` and @@ -1092,13 +1092,13 @@ theorem feistel3HashIdealGame_apply_le {seed₁ : PMF K₁} {h₁ : K₁ → G ≤ ∑' k₁, seed₁ k₁ * (randomFunctionGame program b + ((OracleComp.interpret (memoize (fun _ : G × G => PMF.uniformOfFintype (G × G))) program).run []).toOuterMeasure {x | hasCollision (h₁ k₁) x.2 = true}) := - ENNReal.tsum_le_tsum fun k₁ => + tsum_le_tsum fun k₁ => mul_le_mul_of_nonneg_left (feistel3FixedGame_apply_le (h₁ k₁) program b) zero_le _ = randomFunctionGame program b + ∑' k₁, seed₁ k₁ * ((OracleComp.interpret (memoize (fun _ : G × G => PMF.uniformOfFintype (G × G))) program).run []).toOuterMeasure {x | hasCollision (h₁ k₁) x.2 = true} := by - simp only [mul_add, ENNReal.tsum_add, ENNReal.tsum_mul_right, seed₁.tsum_coe, one_mul] + simp only [mul_add, tsum_add, ENNReal.tsum_mul_right, seed₁.tsum_coe, one_mul] _ ≤ randomFunctionGame program b + (Q.choose 2 * ε + Q.choose 2 * (Fintype.card G : ℝ≥0∞)⁻¹) := add_le_add le_rfl (tsum_seed_toOuterMeasure_hasCollision_le hh₁ hQ) @@ -1120,14 +1120,14 @@ theorem randomFunctionGame_apply_le_feistel3HashIdealGame {seed₁ : PMF K₁} { ≤ ∑' k₁, seed₁ k₁ * (feistel3FixedGame (h₁ k₁) program b + ((OracleComp.interpret (memoize (fun _ : G × G => PMF.uniformOfFintype (G × G))) program).run []).toOuterMeasure {x | hasCollision (h₁ k₁) x.2 = true}) := - ENNReal.tsum_le_tsum fun k₁ => + tsum_le_tsum fun k₁ => mul_le_mul_of_nonneg_left (randomFunctionGame_apply_le_feistel3FixedGame (h₁ k₁) program b) zero_le _ = (∑' k₁, seed₁ k₁ * feistel3FixedGame (h₁ k₁) program b) + ∑' k₁, seed₁ k₁ * ((OracleComp.interpret (memoize (fun _ : G × G => PMF.uniformOfFintype (G × G))) program).run []).toOuterMeasure {x | hasCollision (h₁ k₁) x.2 = true} := by - simp only [mul_add, ENNReal.tsum_add] + simp only [mul_add, tsum_add] _ ≤ (∑' k₁, seed₁ k₁ * feistel3FixedGame (h₁ k₁) program b) + (Q.choose 2 * ε + Q.choose 2 * (Fintype.card G : ℝ≥0∞)⁻¹) := add_le_add le_rfl (tsum_seed_toOuterMeasure_hasCollision_le hh₁ hQ) @@ -1619,7 +1619,7 @@ theorem le_idealStrongGame_feistel3CCAAttack_false {l₀ l₁ l₂ r₀ : G} _ ≤ ∑' π : Equiv.Perm (G × G), PMF.uniformOfFintype (Equiv.Perm (G × G)) π * OracleComp.interpret (feistelStrongOracle π) (feistel3CCAAttack l₀ l₁ r₀) false := - ENNReal.le_tsum (f := fun π : Equiv.Perm (G × G) => + le_tsum (f := fun π : Equiv.Perm (G × G) => PMF.uniformOfFintype (Equiv.Perm (G × G)) π * OracleComp.interpret (feistelStrongOracle π) (feistel3CCAAttack l₀ l₁ r₀) false) π₀ @@ -1875,9 +1875,9 @@ theorem idealStrongGame_feistel3CCAAttack_true_le {l₀ l₁ : G} (r₀ : G) (h0 (fun x => decide (-vw₀.1 + x.1.2 = -vw₁.1 + r₀))) true ≤ ∑' vw₁, ((PMF.uniformOfFintype (G × G)) vw₁ * (Fintype.card G : ℝ≥0∞)⁻¹ + ({q₃} : Set (G × G)).indicator (PMF.uniformOfFintype (G × G)) vw₁) := - ENNReal.tsum_le_tsum hvw₁ + tsum_le_tsum hvw₁ _ = (Fintype.card G : ℝ≥0∞)⁻¹ + (Fintype.card (G × G) : ℝ≥0∞)⁻¹ := by - rw [ENNReal.tsum_add, ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul, + rw [tsum_add, ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul, ← PMF.toOuterMeasure_apply, PMF.toOuterMeasure_apply_singleton, PMF.uniformOfFintype_apply] calc ∑' vw₀, (PMF.uniformOfFintype (G × G)) vw₀ * @@ -1888,7 +1888,7 @@ theorem idealStrongGame_feistel3CCAAttack_true_le {l₀ l₁ : G} (r₀ : G) (h0 (fun x => decide (-vw₀.1 + x.1.2 = -vw₁.1 + r₀))) true ≤ ∑' vw₀, (PMF.uniformOfFintype (G × G)) vw₀ * ((Fintype.card G : ℝ≥0∞)⁻¹ + (Fintype.card (G × G) : ℝ≥0∞)⁻¹) := - ENNReal.tsum_le_tsum fun vw₀ => mul_le_mul_of_nonneg_left (hvw₀ vw₀) zero_le + tsum_le_tsum fun vw₀ => mul_le_mul_of_nonneg_left (hvw₀ vw₀) zero_le _ = (Fintype.card G : ℝ≥0∞)⁻¹ + (Fintype.card (G × G) : ℝ≥0∞)⁻¹ := by rw [ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul] have hchoose : ((Nat.choose 3 2 : ℕ) : ℝ≥0∞) = 3 := rfl @@ -3043,7 +3043,7 @@ private theorem tsum_toOuterMeasure_of_pointwise_le {α β : Type} (μ : PMF α) (∑' a, μ a * ∑' y, {y | P a y}.indicator d y) = ∑' y, d y * ∑' a, {a | P a y}.indicator μ a := by simp only [← ENNReal.tsum_mul_left] - rw [ENNReal.tsum_comm] + rw [tsum_comm] refine tsum_congr fun y => tsum_congr fun a => ?_ by_cases h : P a y · rw [Set.indicator_of_mem h, Set.indicator_of_mem h, mul_comm] @@ -3051,7 +3051,7 @@ private theorem tsum_toOuterMeasure_of_pointwise_le {α β : Type} (μ : PMF α) rw [hcomm] calc ∑' y, d y * ∑' a, {a | P a y}.indicator μ a ≤ ∑' y, d y * M := - ENNReal.tsum_le_tsum fun y => mul_le_mul_of_nonneg_left (by + tsum_le_tsum fun y => mul_le_mul_of_nonneg_left (by rw [← PMF.toOuterMeasure_apply] exact hM y) zero_le _ = M := by rw [ENNReal.tsum_mul_right, d.tsum_coe, one_mul] @@ -3155,14 +3155,14 @@ private theorem tsum_uniform_toOuterMeasure_hasCollision4_le d.toOuterMeasure {x | IsGoodCache4 Q x.2 ∧ hasVCollision4 f₄ x.2 = true} + (Q.choose 2 : ℝ≥0∞) * (Fintype.card (G × G) : ℝ≥0∞)⁻¹) := - ENNReal.tsum_le_tsum fun f₄ => + tsum_le_tsum fun f₄ => mul_le_mul_of_nonneg_left (toOuterMeasure_hasCollision4_le f₁ f₄ hQ) zero_le _ = d.toOuterMeasure {x | IsGoodCache4 Q x.2 ∧ hasUCollision f₁ x.2 = true} + (∑' f₄, unif f₄ * d.toOuterMeasure {x | IsGoodCache4 Q x.2 ∧ hasVCollision4 f₄ x.2 = true}) + (Q.choose 2 : ℝ≥0∞) * (Fintype.card (G × G) : ℝ≥0∞)⁻¹ := by - simp only [mul_add, ENNReal.tsum_add, ENNReal.tsum_mul_right, unif.tsum_coe, one_mul] + simp only [mul_add, tsum_add, ENNReal.tsum_mul_right, unif.tsum_coe, one_mul] _ ≤ _ := add_le_add (add_le_add le_rfl (tsum_uniform_toOuterMeasure_hasVCollision4_le d Q)) le_rfl calc ∑' f₁, unif f₁ * ∑' f₄, unif f₄ * d.toOuterMeasure {x | hasCollision4 f₁ f₄ x.2 = true} @@ -3171,12 +3171,12 @@ private theorem tsum_uniform_toOuterMeasure_hasCollision4_le {x | IsGoodCache4 Q x.2 ∧ hasUCollision f₁ x.2 = true} + Q.choose 2 * (Fintype.card G : ℝ≥0∞)⁻¹ + (Q.choose 2 : ℝ≥0∞) * (Fintype.card (G × G) : ℝ≥0∞)⁻¹) := - ENNReal.tsum_le_tsum fun f₁ => mul_le_mul_of_nonneg_left (hinner f₁) zero_le + tsum_le_tsum fun f₁ => mul_le_mul_of_nonneg_left (hinner f₁) zero_le _ = (∑' f₁, unif f₁ * d.toOuterMeasure {x | IsGoodCache4 Q x.2 ∧ hasUCollision f₁ x.2 = true}) + Q.choose 2 * (Fintype.card G : ℝ≥0∞)⁻¹ + (Q.choose 2 : ℝ≥0∞) * (Fintype.card (G × G) : ℝ≥0∞)⁻¹ := by - simp only [mul_add, ENNReal.tsum_add, ENNReal.tsum_mul_right, unif.tsum_coe, one_mul] + simp only [mul_add, tsum_add, ENNReal.tsum_mul_right, unif.tsum_coe, one_mul] _ ≤ Q.choose 2 * (Fintype.card G : ℝ≥0∞)⁻¹ + Q.choose 2 * (Fintype.card G : ℝ≥0∞)⁻¹ + (Q.choose 2 : ℝ≥0∞) * (Fintype.card (G × G) : ℝ≥0∞)⁻¹ := @@ -3253,13 +3253,13 @@ theorem feistel4StrongRandomRoundsGame_apply_le (forgetVal + ((OracleComp.interpret RandomPermutation.forgetfulAnswer prog).run []).toOuterMeasure {x | hasCollision4 f₁ f₄ x.2 = true}) := - ENNReal.tsum_le_tsum fun f₁ => mul_le_mul_of_nonneg_left - (ENNReal.tsum_le_tsum fun f₄ => mul_le_mul_of_nonneg_left + tsum_le_tsum fun f₁ => mul_le_mul_of_nonneg_left + (tsum_le_tsum fun f₄ => mul_le_mul_of_nonneg_left (feistel4StrongFixedGame_apply_le f₁ f₄ program b) zero_le) zero_le _ = forgetVal + ∑' f₁, unif f₁ * ∑' f₄, unif f₄ * ((OracleComp.interpret RandomPermutation.forgetfulAnswer prog).run []).toOuterMeasure {x | hasCollision4 f₁ f₄ x.2 = true} := by - simp only [mul_add, ENNReal.tsum_add, ENNReal.tsum_mul_right, unif.tsum_coe, one_mul] + simp only [mul_add, tsum_add, ENNReal.tsum_mul_right, unif.tsum_coe, one_mul] _ ≤ _ := add_le_add le_rfl (tsum_uniform_toOuterMeasure_hasCollision4_le hQ') have hperm := RandomPermutation.forgetfulAnswer_bind_apply_le_uniformPerm hQ' PMF.pure b simp only [Probability.PMF.monad_bind_eq_bind, PMF.bind_pure] at hperm @@ -3303,14 +3303,14 @@ theorem idealStrongGame_apply_le_feistel4StrongRandomRoundsGame (feistel4StrongFixedGame f₁ f₄ program b + ((OracleComp.interpret RandomPermutation.forgetfulAnswer prog).run []).toOuterMeasure {x | hasCollision4 f₁ f₄ x.2 = true}) := - ENNReal.tsum_le_tsum fun f₁ => mul_le_mul_of_nonneg_left - (ENNReal.tsum_le_tsum fun f₄ => mul_le_mul_of_nonneg_left + tsum_le_tsum fun f₁ => mul_le_mul_of_nonneg_left + (tsum_le_tsum fun f₄ => mul_le_mul_of_nonneg_left (forgetfulAnswer_apply_le_feistel4StrongFixedGame f₁ f₄ program b) zero_le) zero_le _ = (∑' f₁, unif f₁ * ∑' f₄, unif f₄ * feistel4StrongFixedGame f₁ f₄ program b) + ∑' f₁, unif f₁ * ∑' f₄, unif f₄ * ((OracleComp.interpret RandomPermutation.forgetfulAnswer prog).run []).toOuterMeasure {x | hasCollision4 f₁ f₄ x.2 = true} := by - simp only [mul_add, ENNReal.tsum_add] + simp only [mul_add, tsum_add] _ ≤ _ := add_le_add le_rfl (tsum_uniform_toOuterMeasure_hasCollision4_le hQ') have hperm := RandomPermutation.uniformPerm_bind_apply_le_forgetful hQ' PMF.pure b simp only [Probability.PMF.monad_bind_eq_bind, PMF.bind_pure] at hperm diff --git a/Complexity/Crypto/Protocols/MAC/CarterWegman.lean b/Complexity/Crypto/Protocols/MAC/CarterWegman.lean index dd32115b4..4b4f2bb30 100644 --- a/Complexity/Crypto/Protocols/MAC/CarterWegman.lean +++ b/Complexity/Crypto/Protocols/MAC/CarterWegman.lean @@ -3802,10 +3802,10 @@ theorem randomNonceSimHandler_not_nodup_le {α : Type} [Fintype N] [Nonempty N] PMF.pure (!decide (res.2.map (fun e => e.1.2)).Nodup)) true ≤ ∑' x, PMF.uniformOfFintype N x * ((PMF.pure (decide (x ∈ tr.map (fun e => e.1.2)))) true + B) := - ENNReal.tsum_le_tsum fun x => mul_le_mul_of_nonneg_left (hx_bound x) zero_le + tsum_le_tsum fun x => mul_le_mul_of_nonneg_left (hx_bound x) zero_le _ = ((PMF.uniformOfFintype N).bind fun x => PMF.pure (decide (x ∈ tr.map (fun e => e.1.2)))) true + B := by - simp only [mul_add, ENNReal.tsum_add, ENNReal.tsum_mul_right, + simp only [mul_add, tsum_add, ENNReal.tsum_mul_right, (PMF.uniformOfFintype N).tsum_coe, one_mul, PMF.bind_apply] _ ≤ (tr.length : ℝ≥0∞) / Fintype.card N + B := add_le_add hmem_le le_rfl _ = ((∑ i ∈ Finset.range (Q + 1), (tr.length + i) : ℕ) : ℝ≥0∞) / Fintype.card N := by @@ -4029,11 +4029,11 @@ theorem randomNonceStrongGame_le_sum_add_max {seed : PMF K} {hash : K → M → ≤ ∑' res, simPMF res * ((PMF.pure (!decide (res.2.map (fun e => e.1.2)).Nodup)) true + max ε (Fintype.card Y : ℝ≥0∞)⁻¹) := - ENNReal.tsum_le_tsum fun res => mul_le_mul_of_nonneg_left (hpoint res) zero_le + tsum_le_tsum fun res => mul_le_mul_of_nonneg_left (hpoint res) zero_le _ = (simPMF.bind fun res => PMF.pure (!decide (res.2.map (fun e => e.1.2)).Nodup)) true + max ε (Fintype.card Y : ℝ≥0∞)⁻¹ := by - simp only [mul_add, ENNReal.tsum_add, ENNReal.tsum_mul_right, simPMF.tsum_coe, one_mul, + simp only [mul_add, tsum_add, ENNReal.tsum_mul_right, simPMF.tsum_coe, one_mul, PMF.bind_apply] _ ≤ ((∑ i ∈ Finset.range Q, i : ℕ) : ℝ≥0∞) / Fintype.card N + max ε (Fintype.card Y : ℝ≥0∞)⁻¹ := by @@ -4485,10 +4485,10 @@ theorem probOracleComp_randomNonceSimHandler_not_nodup_le {α : Type} PMF.pure (!decide (res.2.map (fun e => e.1.2)).Nodup)) true ≤ ∑' x, PMF.uniformOfFintype N x * ((PMF.pure (decide (x ∈ tr.map (fun e => e.1.2)))) true + B) := - ENNReal.tsum_le_tsum fun x => mul_le_mul_of_nonneg_left (hx_bound x) zero_le + tsum_le_tsum fun x => mul_le_mul_of_nonneg_left (hx_bound x) zero_le _ = ((PMF.uniformOfFintype N).bind fun x => PMF.pure (decide (x ∈ tr.map (fun e => e.1.2)))) true + B := by - simp only [mul_add, ENNReal.tsum_add, ENNReal.tsum_mul_right, + simp only [mul_add, tsum_add, ENNReal.tsum_mul_right, (PMF.uniformOfFintype N).tsum_coe, one_mul, PMF.bind_apply] _ ≤ (tr.length : ℝ≥0∞) / Fintype.card N + B := add_le_add hmem_le le_rfl _ = ((∑ i ∈ Finset.range (Q + 1), (tr.length + i) : ℕ) : ℝ≥0∞) / Fintype.card N := by @@ -4610,11 +4610,11 @@ theorem seed_uniform_probOracleComp_checkRandomNonceSUF_le {α : Type} ≤ ∑' res, simPMF res * ((PMF.pure (!decide (res.2.map (fun e => e.1.2)).Nodup)) true + max ε (Fintype.card Y : ℝ≥0∞)⁻¹) := - ENNReal.tsum_le_tsum fun res => mul_le_mul_of_nonneg_left (hpoint res) zero_le + tsum_le_tsum fun res => mul_le_mul_of_nonneg_left (hpoint res) zero_le _ = (simPMF.bind fun res => PMF.pure (!decide (res.2.map (fun e => e.1.2)).Nodup)) true + max ε (Fintype.card Y : ℝ≥0∞)⁻¹ := by - simp only [mul_add, ENNReal.tsum_add, ENNReal.tsum_mul_right, simPMF.tsum_coe, one_mul, + simp only [mul_add, tsum_add, ENNReal.tsum_mul_right, simPMF.tsum_coe, one_mul, PMF.bind_apply] _ ≤ ((∑ i ∈ Finset.range Q, i : ℕ) : ℝ≥0∞) / Fintype.card N + max ε (Fintype.card Y : ℝ≥0∞)⁻¹ := by diff --git a/Complexity/Crypto/Protocols/SigmaProtocol/FiatShamir.lean b/Complexity/Crypto/Protocols/SigmaProtocol/FiatShamir.lean index 2075b932a..39a83a0cb 100644 --- a/Complexity/Crypto/Protocols/SigmaProtocol/FiatShamir.lean +++ b/Complexity/Crypto/Protocols/SigmaProtocol/FiatShamir.lean @@ -391,7 +391,7 @@ private theorem toOuterMeasure_flag_le (P : SigmaProtocol X Y T C Z) (key : X × {x | x.2.2 = true} ≤ ∑' ts, (P.commit key.1 key.2) ts * {ts : T × P.State | (st.1.1.lookup (m, ts.1)).isSome}.indicator 1 ts := by - refine ENNReal.tsum_le_tsum fun ts => mul_le_mul_of_nonneg_left ?_ zero_le + refine tsum_le_tsum fun ts => mul_le_mul_of_nonneg_left ?_ zero_le by_cases hts : (st.1.1.lookup (m, ts.1)).isSome · rw [Set.indicator_of_mem (show ts ∈ {ts : T × P.State | (st.1.1.lookup (m, ts.1)).isSome} from hts), Pi.one_apply] @@ -828,12 +828,12 @@ private theorem bind_apply_le_add {α β : Type} {μ : PMF α} {f g : α → PMF (μ.bind f) b ≤ (μ.bind g) b + K := by simp only [PMF.bind_apply] calc ∑' a, μ a * f a b ≤ ∑' a, μ a * (g a b + K) := by - refine ENNReal.tsum_le_tsum fun a => ?_ + refine tsum_le_tsum fun a => ?_ by_cases ha : a ∈ μ.support · exact mul_le_mul_of_nonneg_left (h a ha) zero_le · rw [(PMF.apply_eq_zero_iff _ _).mpr ha, zero_mul, zero_mul] _ = ∑' a, μ a * g a b + K := by - simp only [mul_add, ENNReal.tsum_add, ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul] + simp only [mul_add, tsum_add, ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul] omit [DecidableEq C] in /-- **The game hops** for one key and one choice of the forger's coins: the experiment wins at diff --git a/Complexity/Languages/Probabilistic/BoundedUniform.lean b/Complexity/Languages/Probabilistic/BoundedUniform.lean index 6fa41f00f..a4166310d 100644 --- a/Complexity/Languages/Probabilistic/BoundedUniform.lean +++ b/Complexity/Languages/Probabilistic/BoundedUniform.lean @@ -187,7 +187,7 @@ private theorem retry_some (bound value : ℕ) (next : PMF (Option ℕ)) : simp [hib, hiv] change (∑' i, μ i * (if i < bound then PMF.pure (some i) else next) (some value)) = _ simp_rw [hterm] - rw [ENNReal.tsum_add] + rw [tsum_add] simp only [tsum_ite_eq] rw [show μ bound = (intervalFailure bound : ℝ≥0∞) from eval_sampleBoundedIndex_sentinel bound] by_cases hv : value < bound @@ -342,7 +342,7 @@ private theorem uniformFin_nat_apply (bound value : ℕ) [NeZero bound] : · intro i hi have : value ≠ i.val := fun h => hi (Fin.ext h.symm) simp [this] - · rw [ENNReal.tsum_eq_zero.mpr] + · rw [tsum_eq_zero_iff.mpr] · simp [hv] · intro i have : value ≠ i.val := by omega @@ -364,7 +364,7 @@ theorem eval_sampleUniformInterval_some_eq (bound attempts value : ℕ) [NeZero private theorem tsum_option_eq {α : Type} (f : Option α → ℝ≥0∞) : ∑' x, f x = f none + ∑' x, f (some x) := by rw [← (Equiv.optionEquivSumPUnit.{0, 0} α).symm.tsum_eq f] - rw [ENNReal.summable.tsum_sum ENNReal.summable] + rw [CompleteLattice.summable.tsum_sum CompleteLattice.summable] simp [add_comm] private theorem optional_map_density {α β : Type} (μ : PMF (Option α)) (ν : PMF α) diff --git a/Complexity/Languages/Probabilistic/Expect.lean b/Complexity/Languages/Probabilistic/Expect.lean index 3785b103e..6b22d82f8 100644 --- a/Complexity/Languages/Probabilistic/Expect.lean +++ b/Complexity/Languages/Probabilistic/Expect.lean @@ -70,7 +70,7 @@ theorem expect_zero (m : ProbComp α) : expect m (fun _ => 0) = 0 := theorem expect_add (m : ProbComp α) (f g : α → ℝ≥0∞) : expect m (fun a => f a + g a) = expect m f + expect m g := by - simp only [expect, mul_add, ENNReal.tsum_add] + simp only [expect, mul_add, tsum_add] theorem expect_mul_left (m : ProbComp α) (c : ℝ≥0∞) (f : α → ℝ≥0∞) : expect m (fun a => c * f a) = c * expect m f := by @@ -86,7 +86,7 @@ theorem expect_mul_right (m : ProbComp α) (f : α → ℝ≥0∞) (c : ℝ≥0 theorem expect_bind (m : ProbComp α) (k : α → ProbComp β) (f : β → ℝ≥0∞) : expect (m >>= k) f = expect m (fun a => expect (k a) f) := by simp only [expect, eval_bind, PMF.bind_apply, ← ENNReal.tsum_mul_right] - rw [ENNReal.tsum_comm] + rw [tsum_comm] congr 1 funext a simp only [mul_assoc, ENNReal.tsum_mul_left] @@ -107,7 +107,7 @@ theorem expect_congr_on_support {m : ProbComp α} {f g : α → ℝ≥0∞} theorem expect_mono_on_support {m : ProbComp α} {f g : α → ℝ≥0∞} (h : ∀ a ∈ m.support, f a ≤ g a) : expect m f ≤ expect m g := by - refine ENNReal.tsum_le_tsum fun a => ?_ + refine tsum_le_tsum fun a => ?_ by_cases ha : a ∈ m.support · exact mul_le_mul_right (h a ha) _ · have hzero : (eval m) a = 0 := (PMF.apply_eq_zero_iff (eval m) a).mpr (by @@ -129,7 +129,7 @@ theorem expect_eq_top_of_mem_support {m : ProbComp α} {f : α → ℝ≥0∞} { refine top_unique ?_ calc (⊤ : ℝ≥0∞) = (eval m) a * f a := by rw [hf, ENNReal.mul_top hpos] - _ ≤ expect m f := ENNReal.le_tsum (f := fun b => (eval m) b * f b) a + _ ≤ expect m f := le_tsum (f := fun b => (eval m) b * f b) a theorem expect_unifFin (n : ℕ) (f : Fin (n + 1) → ℝ≥0∞) : expect (unifFin n) f = (∑ k : Fin (n + 1), f k) / (n + 1 : ℝ≥0∞) := by diff --git a/Complexity/Languages/Probabilistic/Oracle/Interaction.lean b/Complexity/Languages/Probabilistic/Oracle/Interaction.lean index 6aa1215f3..d822a739c 100644 --- a/Complexity/Languages/Probabilistic/Oracle/Interaction.lean +++ b/Complexity/Languages/Probabilistic/Oracle/Interaction.lean @@ -168,7 +168,7 @@ theorem acceptProbWith_bind_ite_le (program : ProbOracleComp spec Bool) acceptProbWith oracle program s := by conv_rhs => rw [← bind_pure program, acceptProbWith_bind] rw [acceptProbWith_bind] - refine ENNReal.tsum_le_tsum fun x => mul_le_mul_right ?_ _ + refine tsum_le_tsum fun x => mul_le_mul_right ?_ _ cases x.1 · simp only [Bool.false_eq_true, ↓reduceIte, le_refl] · simp only [↓reduceIte, acceptProbWith_pure] diff --git a/Complexity/Languages/Probabilistic/RandomPermutation.lean b/Complexity/Languages/Probabilistic/RandomPermutation.lean index f8b175e6d..153ac0a84 100644 --- a/Complexity/Languages/Probabilistic/RandomPermutation.lean +++ b/Complexity/Languages/Probabilistic/RandomPermutation.lean @@ -1175,9 +1175,9 @@ theorem toOuterMeasure_bad_choose_two_le {ι σ β : Type} {spec : ι → Type} calc ∑' y, (handler q s) y * ((OracleComp.interpret handler (cont y.1)).run y.2).toOuterMeasure {x | bad x.2} ≤ ∑' y, (handler q s) y * ({x : spec q × σ | bad x.2}.indicator 1 y + K) := - ENNReal.tsum_le_tsum hterm + tsum_le_tsum hterm _ = (handler q s).toOuterMeasure {x | bad x.2} + K := by - simp only [mul_add, ENNReal.tsum_add, ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul, + simp only [mul_add, tsum_add, ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul, PMF.toOuterMeasure_apply] congr 1 refine tsum_congr fun y => ?_ @@ -1413,8 +1413,8 @@ theorem toOuterMeasure_forgetfulAnswer_le_pairwise_and_length_le_add {β : Type} {x | (x.2.Pairwise (fun a b => a.1 ≠ b.1) ∧ x.2.Pairwise (fun a b => a.2 ≠ b.2) ∧ x.2.length ≤ Q) ∧ P x.2}) + q.toOuterMeasure {x | x.2.2 = true} - simp only [PMF.toOuterMeasure_apply, ← ENNReal.tsum_add] - refine ENNReal.tsum_le_tsum fun ⟨a, c, b⟩ => ?_ + simp only [PMF.toOuterMeasure_apply, ← tsum_add] + refine tsum_le_tsum fun ⟨a, c, b⟩ => ?_ by_cases hmem : (a, (c, b)) ∈ q.support · by_cases hP : P c · cases b with diff --git a/Complexity/Languages/Probabilistic/UntilBad.lean b/Complexity/Languages/Probabilistic/UntilBad.lean index 7bcd7e202..ac26422b6 100644 --- a/Complexity/Languages/Probabilistic/UntilBad.lean +++ b/Complexity/Languages/Probabilistic/UntilBad.lean @@ -129,8 +129,8 @@ theorem bind_apply_le (h : IdenticalUntilBad first second bad) (program : Oracle ((interpret first program).run s).toOuterMeasure {x | bad x.2} := by classical simp only [PMF.bind_apply, PMF.toOuterMeasure_apply] - rw [← ENNReal.tsum_add] - refine ENNReal.tsum_le_tsum fun x => ?_ + rw [← tsum_add] + refine tsum_le_tsum fun x => ?_ by_cases hx : bad x.2 · have hbad : x ∈ {x : α × σ | bad x.2} := hx rw [Set.indicator_of_mem hbad] @@ -153,7 +153,7 @@ private theorem toOuterMeasure_bad_add_good (p : PMF (α × σ)) : p.toOuterMeasure {x | bad x.2} + p.toOuterMeasure {x | ¬bad x.2} = 1 := by classical simp only [PMF.toOuterMeasure_apply] - rw [← ENNReal.tsum_add, ← p.tsum_coe] + rw [← tsum_add, ← p.tsum_coe] refine tsum_congr fun x => ?_ by_cases hx : bad x.2 · rw [Set.indicator_of_mem (show x ∈ {x : α × σ | bad x.2} from hx), @@ -244,9 +244,9 @@ theorem toOuterMeasure_bad_le {handler : (q : ι) → StateT σ PMF (spec q)} {b calc ∑' y, (handler q s) y * ((interpret handler (cont y.1)).run y.2).toOuterMeasure {x | bad x.2} ≤ ∑' y, (handler q s) y * ({x : spec q × σ | bad x.2}.indicator 1 y + K) := - ENNReal.tsum_le_tsum hterm + tsum_le_tsum hterm _ = (handler q s).toOuterMeasure {x | bad x.2} + K := by - simp only [mul_add, ENNReal.tsum_add, ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul, + simp only [mul_add, tsum_add, ENNReal.tsum_mul_right, PMF.tsum_coe, one_mul, PMF.toOuterMeasure_apply] congr 1 refine tsum_congr fun y => ?_ diff --git a/Complexity/Languages/Probabilistic/Vector.lean b/Complexity/Languages/Probabilistic/Vector.lean index 5e43aaaa5..4f3e7ead7 100644 --- a/Complexity/Languages/Probabilistic/Vector.lean +++ b/Complexity/Languages/Probabilistic/Vector.lean @@ -274,7 +274,7 @@ theorem eval_pi {m : ℕ} {α : Fin m → Type} [∀ i, Finite (α i)] funext i simpa using (congrFun hEq (Fin.succ i)).symm · intro a ha - simp only [mul_eq_zero, ENNReal.tsum_eq_zero] + simp only [mul_eq_zero, tsum_eq_zero_iff] right intro b rw [ite_eq_right] diff --git a/Complexity/Probability/Conditioning.lean b/Complexity/Probability/Conditioning.lean index faca6d699..340e70ef4 100644 --- a/Complexity/Probability/Conditioning.lean +++ b/Complexity/Probability/Conditioning.lean @@ -48,7 +48,7 @@ variable {α β : Type*} theorem map_fst_apply (joint : PMF (α × β)) (a : α) : (joint.map Prod.fst) a = ∑' b, joint (a, b) := by classical - rw [PMF.map_apply, ENNReal.tsum_prod'] + rw [PMF.map_apply, tsum_prod'] rw [tsum_eq_single a] · simp · intro x hx @@ -72,9 +72,9 @@ theorem conditionalSnd_apply (joint : PMF (α × β)) (a : α) conditionalSnd joint a b = joint (a, b) / (joint.map Prod.fst) a := by classical have hpost : a ∈ (joint.bind (fun pair => PMF.pure pair.1)).support := ha - simp only [conditionalSnd, hpost, dite_true, PMF.map_apply, ENNReal.tsum_prod', + simp only [conditionalSnd, hpost, dite_true, PMF.map_apply, tsum_prod', posteriorDist_apply, bind_pair_apply, PMF.pure_apply] - simp [ENNReal.tsum_prod', div_eq_mul_inv] + simp [tsum_prod', div_eq_mul_inv] /-- Multiplying by the marginal recovers the joint mass, also at zero-probability observations. -/ theorem marginal_mul_conditionalSnd (joint : PMF (α × β)) (a : α) (b : β) : diff --git a/Complexity/Probability/CorrelatedSampling.lean b/Complexity/Probability/CorrelatedSampling.lean index 5e9f61775..ae9ebd647 100644 --- a/Complexity/Probability/CorrelatedSampling.lean +++ b/Complexity/Probability/CorrelatedSampling.lean @@ -171,7 +171,7 @@ theorem map_firstIn_weightedOrderAux (w : Ω → ℝ≥0) : rcases o with _ | c · rw [PMF.bind_apply, PMF.map_apply] simp only [reduceCtorEq, ite_false, tsum_zero] - refine ENNReal.tsum_eq_zero.mpr fun a => ?_ + refine tsum_eq_zero_iff.mpr fun a => ?_ split_ifs <;> simp [PMF.map_apply] rw [PMF.bind_apply, tsum_fintype, hrhs] set du := weightedDraw w U hU0 diff --git a/Complexity/Probability/Forking.lean b/Complexity/Probability/Forking.lean index 960ea5e2e..b13827992 100644 --- a/Complexity/Probability/Forking.lean +++ b/Complexity/Probability/Forking.lean @@ -215,7 +215,7 @@ private theorem bind_match_apply {X : Type} (μ : PMF X) (index : X → Option ( | some j => decide (event j x))) true = ∑ j, (μ.bind fun x => PMF.pure (decide (index x = some j ∧ event j x))) true := by simp only [PMF.bind_apply, PMF.pure_apply] - rw [← Summable.tsum_finsetSum fun _ _ => ENNReal.summable] + rw [← Summable.tsum_finsetSum fun _ _ => CompleteLattice.summable] refine tsum_congr fun x => ?_ rw [← Finset.mul_sum] congr 1 diff --git a/Complexity/Probability/IID.lean b/Complexity/Probability/IID.lean index 74388f5b5..0a600cf4a 100644 --- a/Complexity/Probability/IID.lean +++ b/Complexity/Probability/IID.lean @@ -73,7 +73,7 @@ noncomputable def iid (μ : PMF α) : (k : ℕ) → PMF (Fin k → α) intro h exact absurd (by rw [h, Fin.tail_cons]) hw · intro a ha - rw [PMF.map_apply, ENNReal.tsum_eq_zero.mpr, mul_zero] + rw [PMF.map_apply, tsum_eq_zero_iff.mpr, mul_zero] intro w simp only [ite_eq_right_iff] intro h diff --git a/Complexity/Probability/MinEntropy.lean b/Complexity/Probability/MinEntropy.lean index 15120ffd6..29fd28b12 100644 --- a/Complexity/Probability/MinEntropy.lean +++ b/Complexity/Probability/MinEntropy.lean @@ -183,7 +183,7 @@ theorem le_minEntropy_bind (μ : PMF β) (f : β → PMF α) {k : ℝ} apply ENNReal.toReal_le_of_le_ofReal hk calc μ.bind f a = ∑' b, μ b * f b a := PMF.bind_apply μ f a - _ ≤ ∑' b, μ b * ENNReal.ofReal (2 ^ (-k)) := ENNReal.tsum_le_tsum hcomponent + _ ≤ ∑' b, μ b * ENNReal.ofReal (2 ^ (-k)) := tsum_le_tsum hcomponent _ = ENNReal.ofReal (2 ^ (-k)) := by rw [ENNReal.tsum_mul_right, μ.tsum_coe, one_mul] /-! ### Weak random sources -/ diff --git a/Complexity/Probability/Minimax.lean b/Complexity/Probability/Minimax.lean index e34129b86..ba395a192 100644 --- a/Complexity/Probability/Minimax.lean +++ b/Complexity/Probability/Minimax.lean @@ -213,10 +213,10 @@ theorem exists_mem_support_tsum_mul_le (μ : PMF α) (ν : PMF β) (loss : α calc ∑' a, μ a * ∑' b, ν b * loss a b = ∑' b, ν b * ∑' a, μ a * loss a b := by simp only [← ENNReal.tsum_mul_left] - rw [ENNReal.tsum_comm] + rw [tsum_comm] exact tsum_congr fun b => tsum_congr fun a => by ring _ ≤ ∑' b, ν b * ε := by - refine ENNReal.tsum_le_tsum fun b => ?_ + refine tsum_le_tsum fun b => ?_ by_cases hb : b ∈ ν.support · gcongr exact h b hb diff --git a/Complexity/Probability/PMF/Bind.lean b/Complexity/Probability/PMF/Bind.lean index fcdd9502b..c3b9858bc 100644 --- a/Complexity/Probability/PMF/Bind.lean +++ b/Complexity/Probability/PMF/Bind.lean @@ -53,7 +53,7 @@ theorem bind_sigma_apply {β : α → Type*} (p : PMF α) (f : (a : α) → PMF (p.bind (fun a => (f a).map (Sigma.mk a))) ⟨a, b⟩ = p a * f a b := by classical simp only [PMF.bind_apply, PMF.map_apply, ← ENNReal.tsum_mul_left, mul_ite, mul_zero] - rw [← ENNReal.tsum_sigma] + rw [← tsum_sigma] simp /-- A continuation only needs to agree on outcomes the preceding distribution can produce. -/ @@ -76,7 +76,7 @@ theorem le_bind_apply_on_support (μ : PMF α) {f : α → PMF β} {b : β} {c : (h : ∀ a ∈ μ.support, c ≤ f a b) : c ≤ μ.bind f b := by rw [PMF.bind_apply] calc c = ∑' a, μ a * c := by rw [ENNReal.tsum_mul_right, μ.tsum_coe, one_mul] - _ ≤ ∑' a, μ a * f a b := ENNReal.tsum_le_tsum fun a => by + _ ≤ ∑' a, μ a * f a b := tsum_le_tsum fun a => by by_cases ha : a ∈ μ.support · exact mul_le_mul_right (h a ha) _ · simp [(PMF.apply_eq_zero_iff μ a).2 ha] @@ -87,7 +87,7 @@ theorem bind_apply_le_on_support (μ : PMF α) {f : α → PMF β} {b : β} {c : (h : ∀ a ∈ μ.support, f a b ≤ c) : μ.bind f b ≤ c := by rw [PMF.bind_apply] calc ∑' a, μ a * f a b - _ ≤ ∑' a, μ a * c := ENNReal.tsum_le_tsum fun a => by + _ ≤ ∑' a, μ a * c := tsum_le_tsum fun a => by by_cases ha : a ∈ μ.support · exact mul_le_mul_right (h a ha) _ · simp [(PMF.apply_eq_zero_iff μ a).2 ha] @@ -98,7 +98,7 @@ probability in the whole experiment. The sample spaces may be infinite. -/ theorem bind_apply_mono_on_support (μ : PMF α) {f g : α → PMF β} (b : β) (h : ∀ a ∈ μ.support, f a b ≤ g a b) : μ.bind f b ≤ μ.bind g b := by simp only [PMF.bind_apply] - apply ENNReal.tsum_le_tsum + apply tsum_le_tsum intro a by_cases ha : a ∈ μ.support · exact mul_le_mul_right (h a ha) _ @@ -117,7 +117,7 @@ theorem exists_bind_apply_le [Finite α] (μ : PMF α) (f : α → PMF β) (b : /-- An average of values at most `B` is at most `B`. -/ theorem tsum_mul_le_of_forall_le (μ : PMF α) {f : α → ℝ≥0∞} {B : ℝ≥0∞} (h : ∀ a, f a ≤ B) : ∑' a, μ a * f a ≤ B := - calc ∑' a, μ a * f a ≤ ∑' a, μ a * B := ENNReal.tsum_le_tsum fun a => mul_le_mul_right (h a) _ + calc ∑' a, μ a * f a ≤ ∑' a, μ a * B := tsum_le_tsum fun a => mul_le_mul_right (h a) _ _ = B := by rw [ENNReal.tsum_mul_right, μ.tsum_coe, one_mul] /-- Values at most `1` on a finite set `T` and at most `B` elsewhere average to at most @@ -126,9 +126,9 @@ theorem tsum_mul_le_card_mul_add [DecidableEq α] (μ : PMF α) {ε : ℝ≥0∞ (T : Finset α) {f : α → ℝ≥0∞} {B : ℝ≥0∞} (h : ∀ a, f a ≤ (if a ∈ T then 1 else 0) + B) : ∑' a, μ a * f a ≤ T.card * ε + B := by calc ∑' a, μ a * f a ≤ ∑' a, (μ a * (if a ∈ T then 1 else 0) + μ a * B) := - ENNReal.tsum_le_tsum fun a => (mul_le_mul_right (h a) _).trans_eq (mul_add _ _ _) + tsum_le_tsum fun a => (mul_le_mul_right (h a) _).trans_eq (mul_add _ _ _) _ = ∑ a ∈ T, μ a + B := by - rw [ENNReal.tsum_add, ENNReal.tsum_mul_right, μ.tsum_coe, one_mul, + rw [tsum_add, ENNReal.tsum_mul_right, μ.tsum_coe, one_mul, tsum_eq_sum (s := T) fun a ha => by simp only [ha, ↓reduceIte, mul_zero]] congr 1 exact Finset.sum_congr rfl fun a ha => by simp only [ha, ↓reduceIte, mul_one] diff --git a/Complexity/Probability/PMF/Event.lean b/Complexity/Probability/PMF/Event.lean index ce673d7ce..16a45d6e8 100644 --- a/Complexity/Probability/PMF/Event.lean +++ b/Complexity/Probability/PMF/Event.lean @@ -73,7 +73,7 @@ theorem toOuterMeasure_ne_top (p : PMF α) (event : Set α) : p.toOuterMeasure e /-- An event has probability at most one. -/ theorem toOuterMeasure_le_one (p : PMF α) (event : Set α) : p.toOuterMeasure event ≤ 1 := by rw [PMF.toOuterMeasure_apply, ← p.tsum_coe] - exact ENNReal.tsum_le_tsum (fun a => Set.indicator_apply_le (fun _ => le_rfl)) + exact tsum_le_tsum (fun a => Set.indicator_apply_le (fun _ => le_rfl)) /-- The event that `f a` deviates from `m` by at least `ε` is covered by the upper tail `m + ε ≤ f a` and the lower tail `f a ≤ m - ε`. -/ @@ -111,7 +111,7 @@ theorem toOuterMeasure_apply_toReal [Fintype α] (p : PMF α) (event : Set α) : theorem toOuterMeasure_toReal_add_compl (p : PMF α) (event : Set α) : (p.toOuterMeasure event).toReal + (p.toOuterMeasure eventᶜ).toReal = 1 := by have h : p.toOuterMeasure event + p.toOuterMeasure eventᶜ = 1 := by - rw [PMF.toOuterMeasure_apply, PMF.toOuterMeasure_apply, ← ENNReal.tsum_add] + rw [PMF.toOuterMeasure_apply, PMF.toOuterMeasure_apply, ← tsum_add] convert p.tsum_coe using 1 congr 1 funext a @@ -154,7 +154,7 @@ theorem toOuterMeasure_bind_failure_le (p : PMF α) (kernel : α → PMF β) rw [PMF.toOuterMeasure_bind_apply, PMF.toOuterMeasure_apply] calc _ ≤ ∑' a, ((preᶜ).indicator p a + p a * error) := by - apply ENNReal.tsum_le_tsum + apply tsum_le_tsum intro a by_cases ha : a ∈ pre · simp only [Set.indicator_of_notMem (show a ∉ preᶜ by simpa), zero_add] @@ -165,7 +165,7 @@ theorem toOuterMeasure_bind_failure_le (p : PMF α) (kernel : α → PMF β) · simp only [Set.indicator_of_mem (show a ∈ preᶜ from ha)] exact (mul_le_mul_of_nonneg_left (toOuterMeasure_le_one (kernel a) failure) zero_le).trans (by simp) - _ = _ := by rw [ENNReal.tsum_add, ENNReal.tsum_mul_right, p.tsum_coe, one_mul] + _ = _ := by rw [tsum_add, ENNReal.tsum_mul_right, p.tsum_coe, one_mul] /-- The real-valued error rule for a randomized continuation. -/ theorem toOuterMeasure_bind_failure_toReal_le (p : PMF α) (kernel : α → PMF β) @@ -193,7 +193,7 @@ theorem toOuterMeasure_bind_ge_mul (p : PMF α) (kernel : α → PMF β) classical have hle : p.toOuterMeasure pre * ENNReal.ofReal s₂ ≤ (p.bind kernel).toOuterMeasure event := by rw [PMF.toOuterMeasure_bind_apply, PMF.toOuterMeasure_apply, ← ENNReal.tsum_mul_right] - refine ENNReal.tsum_le_tsum fun a => ?_ + refine tsum_le_tsum fun a => ?_ by_cases ha : a ∈ pre · simp only [Set.indicator_of_mem ha] by_cases hs : a ∈ p.support @@ -242,7 +242,7 @@ theorem map_true_mono (p : PMF α) {E F : α → Bool} (h : ∀ a ∈ p.support, E a = true → F a = true) : (p.map E) true ≤ (p.map F) true := by rw [PMF.map_apply, PMF.map_apply] - refine ENNReal.tsum_le_tsum fun a => ?_ + refine tsum_le_tsum fun a => ?_ by_cases ha : a ∈ p.support · by_cases hE : true = E a · simp [← hE, h a ha hE.symm] @@ -253,8 +253,8 @@ theorem map_true_mono (p : PMF α) {E F : α → Bool} theorem map_true_le_add (p : PMF α) {E A B : α → Bool} (h : ∀ a ∈ p.support, E a = true → A a = true ∨ B a = true) : (p.map E) true ≤ (p.map A) true + (p.map B) true := by - rw [PMF.map_apply, PMF.map_apply, PMF.map_apply, ← ENNReal.tsum_add] - refine ENNReal.tsum_le_tsum fun a => ?_ + rw [PMF.map_apply, PMF.map_apply, PMF.map_apply, ← tsum_add] + refine tsum_le_tsum fun a => ?_ by_cases ha : a ∈ p.support · by_cases hE : true = E a · rcases h a ha hE.symm with hA | hB diff --git a/Complexity/Probability/PMF/Map.lean b/Complexity/Probability/PMF/Map.lean index eed796d9b..4aad0a45d 100644 --- a/Complexity/Probability/PMF/Map.lean +++ b/Complexity/Probability/PMF/Map.lean @@ -40,7 +40,7 @@ variable {α β : Type*} theorem le_map_apply (p : PMF α) (f : α → β) (a : α) : p a ≤ (p.map f) (f a) := by classical rw [PMF.map_apply] - simpa using ENNReal.le_tsum (f := fun x => if f a = f x then p x else 0) a + simpa using le_tsum (f := fun x => if f a = f x then p x else 0) a /-- Relabeling a distribution by an equivalence preserves each corresponding point mass. -/ theorem map_equiv_apply (p : PMF α) (e : α ≃ β) (b : β) : diff --git a/Complexity/Probability/ProductConditioning/SideInformation.lean b/Complexity/Probability/ProductConditioning/SideInformation.lean index 316b7c63c..11c12f8fe 100644 --- a/Complexity/Probability/ProductConditioning/SideInformation.lean +++ b/Complexity/Probability/ProductConditioning/SideInformation.lean @@ -186,7 +186,7 @@ theorem sideJoint_apply (μ : PMF T) (J : T → PMF ((∀ i, β i) × V)) (t : T exact hx (Prod.ext (congrArg Prod.snd h).symm (congrArg (·.2) (congrArg Prod.fst h)).symm) · intro t' ht' rw [PMF.map_apply] - refine mul_eq_zero_of_right _ (ENNReal.tsum_eq_zero.mpr fun x => ite_eq_right fun h => ?_) + refine mul_eq_zero_of_right _ (tsum_eq_zero_iff.mpr fun x => ite_eq_right fun h => ?_) exact absurd (congrArg (·.1) (congrArg Prod.fst h)) (Ne.symm ht') /-- The pairs `(u, v)` that complete `(t, v)` to an outcome in `W`. -/ diff --git a/Complexity/Probability/RejectionSampling.lean b/Complexity/Probability/RejectionSampling.lean index d317c0d2f..83b85bf82 100644 --- a/Complexity/Probability/RejectionSampling.lean +++ b/Complexity/Probability/RejectionSampling.lean @@ -144,7 +144,7 @@ theorem shiftReject_apply_some (hv : ∀ z ∈ S, z - v ∈ B) (z : α) : · intro y hy have hyz : y + v ≠ z := fun h => hy (eq_sub_of_add_eq h) split_ifs <;> simp_all [eq_comm] - · refine ENNReal.tsum_eq_zero.mpr fun y => ?_ + · refine tsum_eq_zero_iff.mpr fun y => ?_ split_ifs with h₁ h₂ <;> simp_all /-- **Rejection sampling, aborts.** Under the same hypothesis, the sampler aborts with @@ -156,7 +156,7 @@ theorem shiftReject_apply_none (hv : ∀ z ∈ S, z - v ∈ B) : rw [tsum_eq_sum (s := S) fun z hz => by simp [hz], Finset.sum_ite_mem, Finset.inter_self, Finset.sum_const, nsmul_eq_mul, div_eq_mul_inv] have htotal := (shiftReject B S hB v).tsum_coe - rw [ENNReal.tsum_eq_add_tsum_ite none, ← (Option.some_injective α).tsum_eq] at htotal + rw [tsum_eq_add_tsum_ite none, ← (Option.some_injective α).tsum_eq] at htotal · simp only [reduceCtorEq, ite_false, hsome] at htotal exact ENNReal.eq_sub_of_add_eq (ENNReal.div_ne_top (ENNReal.natCast_ne_top _) (by simpa using hB.ne_empty)) htotal From d7dcd7985e43c0678127230df389fa8ea3e3ea61 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Thu, 29 Oct 2026 15:21:52 -0400 Subject: [PATCH 66/73] fix(build): adapt proofs to the rc2 Mathlib API Qualify newly protected matrix and finite-sum lemmas, publicly import the relocated Matrix.Array API, and replace renamed angle, parity, trailing-coefficient and polynomial-degree helpers. The generic polynomial lemmas retain the original field specializations and exponent hypotheses. No theorem statement or definition body changes. Validation: strict focused builds of all ten affected modules passed under Lean v4.36.0-rc2. Source comparison against d6ccb24f shows all Lean edits are inside theorem proofs except the required Bird import. --- Complexity/Computability/Circuit/Bird.lean | 1 + Complexity/Computability/Circuit/Quantum/Basic.lean | 4 ++-- Complexity/Computability/Circuit/Rational/Wronskian.lean | 2 +- .../Foundations/BooleanAnalysis/LowDegree/Addition.lean | 2 +- .../Foundations/BooleanAnalysis/SparseEquations.lean | 2 +- .../FieldTheory/Finite/LinearizedPolynomial.lean | 6 +++--- Complexity/Foundations/LinearAlgebra/Matrix/ListProd.lean | 4 ++-- Complexity/Foundations/LinearAlgebra/Matrix/Rank.lean | 2 +- .../Foundations/LinearAlgebra/Matrix/SubmatrixRobust.lean | 8 ++++---- Complexity/Foundations/Probability/Gaussian/Sheppard.lean | 2 +- 10 files changed, 17 insertions(+), 16 deletions(-) diff --git a/Complexity/Computability/Circuit/Bird.lean b/Complexity/Computability/Circuit/Bird.lean index 62b11531b..a39c61ae9 100644 --- a/Complexity/Computability/Circuit/Bird.lean +++ b/Complexity/Computability/Circuit/Bird.lean @@ -8,6 +8,7 @@ module -- upstream: candidate public import Complexity.Init +public import Mathlib.LinearAlgebra.Matrix.Array public import Mathlib.LinearAlgebra.Matrix.Determinant.Bird.Defs public import Mathlib.LinearAlgebra.Matrix.Determinant.Basic import Mathlib.Algebra.Order.BigOperators.Group.LocallyFinite diff --git a/Complexity/Computability/Circuit/Quantum/Basic.lean b/Complexity/Computability/Circuit/Quantum/Basic.lean index 7797874a4..c9265f0f4 100644 --- a/Complexity/Computability/Circuit/Quantum/Basic.lean +++ b/Complexity/Computability/Circuit/Quantum/Basic.lean @@ -172,7 +172,7 @@ theorem place_apply (A : Fin k ↪ Fin n) (U : unitaryGroup (Fin k → Bool) ℂ (place A U : Matrix _ _ ℂ) y x = if ∀ j, (¬∃ i, A i = j) → y j = x j then (U : Matrix _ _ ℂ) (y ∘ A) (x ∘ A) else 0 := by simp only [place, MonoidHom.coe_mk, OneHom.coe_mk, submatrix_apply, kroneckerMap_apply, - splitEquiv_apply_fst, splitEquiv_apply_snd, one_apply, funext_iff, Subtype.forall] + splitEquiv_apply_fst, splitEquiv_apply_snd, Matrix.one_apply, funext_iff, Subtype.forall] split_ifs <;> simp namespace PlacedGate @@ -281,7 +281,7 @@ theorem hadamardMatrix_mem_unitaryGroup : hadamardMatrix ∈ unitaryGroup Bool rw [mem_unitaryGroup_iff] ext b c cases b <;> cases c <;> - simp [hadamardMatrix, mul_apply, star_eq_conjTranspose, Complex.conj_ofReal, h2, h1] + simp [hadamardMatrix, Matrix.mul_apply, star_eq_conjTranspose, Complex.conj_ofReal, h2, h1] theorem star_hadamardMatrix : star hadamardMatrix = hadamardMatrix := by ext b c diff --git a/Complexity/Computability/Circuit/Rational/Wronskian.lean b/Complexity/Computability/Circuit/Rational/Wronskian.lean index f2ce76817..138a37718 100644 --- a/Complexity/Computability/Circuit/Rational/Wronskian.lean +++ b/Complexity/Computability/Circuit/Rational/Wronskian.lean @@ -148,7 +148,7 @@ lemma wronskian_order_of_distinct_orders {K : Type*} [Field K] [CharZero K] Matrix.det_mul_row (fun j => (f j).trailingCoeff) (Matrix.of fun i j : Fin h => ((f j).natTrailingDegree.descFactorial i.val : K))] exact mul_ne_zero (Finset.prod_ne_zero_iff.mpr fun j _ => - trailingCoeff_nonzero_iff_nonzero.mpr (hf j)) (det_descFactorial_ne_zero _ hd) + trailingCoeff_ne_zero.mpr (hf j)) (det_descFactorial_ne_zero _ hd) have hB0 : B.det ≠ 0 := fun hz => by simp [hz] at hBc have hA0 : A.det ≠ 0 := hdet ▸ mul_ne_zero (pow_ne_zero _ X_ne_zero) hB0 have hscale : A.det = X ^ (∑ i : Fin h, i.val) * wronskianFamily f := by diff --git a/Complexity/Foundations/BooleanAnalysis/LowDegree/Addition.lean b/Complexity/Foundations/BooleanAnalysis/LowDegree/Addition.lean index 905b6c11a..271991b3b 100644 --- a/Complexity/Foundations/BooleanAnalysis/LowDegree/Addition.lean +++ b/Complexity/Foundations/BooleanAnalysis/LowDegree/Addition.lean @@ -82,7 +82,7 @@ theorem hasSliceDecomposition_add (p q : ℕ) : have hexpand : (fun x y : ι → Bool => monomial K S.1 (x + y)) = ∑ T ∈ S.1.powerset, (fun x y => monomial K T x * monomial K (S.1 \ T) y) := by funext x y - simp only [sum_apply, monomial_add] + simp only [Finset.sum_apply, monomial_add] rw [hexpand] apply (sliceSpace (lowDegree K ι p) (lowDegree K ι q)).sum_mem intro T hT diff --git a/Complexity/Foundations/BooleanAnalysis/SparseEquations.lean b/Complexity/Foundations/BooleanAnalysis/SparseEquations.lean index d9daca94e..d1bc719e3 100644 --- a/Complexity/Foundations/BooleanAnalysis/SparseEquations.lean +++ b/Complexity/Foundations/BooleanAnalysis/SparseEquations.lean @@ -143,7 +143,7 @@ is even. -/ theorem monomialSum_zmod_two_eq_zero_iff_even (𝓜 : Finset (Finset ι)) (x : ι → Bool) : monomialSum (ZMod 2) 𝓜 x = 0 ↔ Even (monomialSum ℤ 𝓜 x) := by rw [monomialSum_eq_card_filter (ZMod 2), monomialSum_eq_card_filter ℤ, - ZMod.natCast_eq_zero_iff_even, Int.even_coe_nat] + ZMod.natCast_eq_zero_iff_even, Int.even_natCast] /-- An assignment `x : ι → Bool` is a common zero of the family `{P i}_{i ∈ I}` over `𝔽₂` if every equation `P i` (`i ∈ I`) evaluates to `0` in `ZMod 2` at `x`. -/ diff --git a/Complexity/Foundations/FieldTheory/Finite/LinearizedPolynomial.lean b/Complexity/Foundations/FieldTheory/Finite/LinearizedPolynomial.lean index 09309146e..a27032be5 100644 --- a/Complexity/Foundations/FieldTheory/Finite/LinearizedPolynomial.lean +++ b/Complexity/Foundations/FieldTheory/Finite/LinearizedPolynomial.lean @@ -252,7 +252,7 @@ private theorem comp_eq_X_pow_card_sub_X [Fintype F] {P Q : F[X]} ⟨by rw [natDegree_comp, hdeg], hQm.comp hPm hPdeg⟩ have hX : IsMonicOfDegree (X ^ Fintype.card F - X : F[X]) (Fintype.card F) := by rw [isMonicOfDegree_iff] - refine ⟨(FiniteField.X_pow_card_sub_X_natDegree_eq F hq1).le, ?_⟩ + refine ⟨(natDegree_X_pow_sub_X (R := F) hq1).le, ?_⟩ have h1 : (1 : ℕ) ≠ Fintype.card F := hq1.ne simp [coeff_X, h1] rw [← sub_eq_zero] @@ -410,10 +410,10 @@ theorem exists_subfield_card_le [Finite F] {n k : ℕ} (hn : finrank (ZMod 2) F have h1 : 1 < 2 ^ k := Nat.one_lt_two_pow hk0.ne' have hcard : Fintype.card F = 2 ^ n := by rw [Module.card_eq_pow_finrank (K := ZMod 2) (V := F), ZMod.card, hn] - refine FiniteField.X_pow_card_sub_X_ne_zero F h1 + refine X_pow_sub_X_ne_zero (R := F) h1 (eq_zero_of_natDegree_lt_card_of_eval_eq_zero _ (f := id) Function.injective_id (fun x => by simp [hall x]) ?_) - rw [FiniteField.X_pow_card_sub_X_natDegree_eq F h1, hcard] + rw [natDegree_X_pow_sub_X (R := F) h1, hcard] exact Nat.pow_lt_pow_right (by norm_num) hkn · by_cases hW : ∃ y₀ ∈ W, y₀ ≠ 0 · obtain ⟨y₀, hy₀W, hy₀⟩ := hW diff --git a/Complexity/Foundations/LinearAlgebra/Matrix/ListProd.lean b/Complexity/Foundations/LinearAlgebra/Matrix/ListProd.lean index 11579b762..50ea8c7ef 100644 --- a/Complexity/Foundations/LinearAlgebra/Matrix/ListProd.lean +++ b/Complexity/Foundations/LinearAlgebra/Matrix/ListProd.lean @@ -38,7 +38,7 @@ theorem list_prod_ofFn_apply : ∀ {d : ℕ} (M : Fin d → Matrix ι ι R) (a b if p 0 = a ∧ p (Fin.last d) = b then ∏ i : Fin d, M i (p i.castSucc) (p i.succ) else 0 | 0, M, a, b => by rw [← (Equiv.funUnique (Fin 1) ι).symm.sum_comp] - simp only [List.ofFn_zero, List.prod_nil, one_apply, Finset.univ_eq_empty, + simp only [List.ofFn_zero, List.prod_nil, Matrix.one_apply, Finset.univ_eq_empty, Finset.prod_empty, Equiv.funUnique_symm_apply, Fin.last_zero, uniqueElim_const] by_cases hab : a = b · subst hab @@ -51,7 +51,7 @@ theorem list_prod_ofFn_apply : ∀ {d : ℕ} (M : Fin d → Matrix ι ι R) (a b have hL : (List.ofFn M).prod a b = ∑ q : Fin (d + 1) → ι, if q (Fin.last d) = b then M 0 a (q 0) * ∏ i : Fin d, M i.succ (q i.castSucc) (q i.succ) else 0 := by - rw [List.ofFn_succ, List.prod_cons, mul_apply] + rw [List.ofFn_succ, List.prod_cons, Matrix.mul_apply] simp only [list_prod_ofFn_apply (fun i => M i.succ), Finset.mul_sum, mul_ite, mul_zero] rw [Finset.sum_comm] refine Finset.sum_congr rfl fun q _ => ?_ diff --git a/Complexity/Foundations/LinearAlgebra/Matrix/Rank.lean b/Complexity/Foundations/LinearAlgebra/Matrix/Rank.lean index 04e6d3604..de84069b6 100644 --- a/Complexity/Foundations/LinearAlgebra/Matrix/Rank.lean +++ b/Complexity/Foundations/LinearAlgebra/Matrix/Rank.lean @@ -87,7 +87,7 @@ theorem exists_eq_mul_rank (A : Matrix m n K) : (b.sum_repr (col j)) simp only [Submodule.coe_sum, Submodule.coe_smul, Finset.sum_apply, Pi.smul_apply, smul_eq_mul] at h - simpa [mul_apply, col, mul_comm] using h.symm + simpa [Matrix.mul_apply, col, mul_comm] using h.symm /-- The natural rank of a finite matrix agrees with the cardinal rank of its linear map. -/ theorem natCast_rank_eq_rank_toLin' [DecidableEq n] (M : Matrix m n K) : diff --git a/Complexity/Foundations/LinearAlgebra/Matrix/SubmatrixRobust.lean b/Complexity/Foundations/LinearAlgebra/Matrix/SubmatrixRobust.lean index 16c25ab37..7d6f58267 100644 --- a/Complexity/Foundations/LinearAlgebra/Matrix/SubmatrixRobust.lean +++ b/Complexity/Foundations/LinearAlgebra/Matrix/SubmatrixRobust.lean @@ -340,17 +340,17 @@ theorem add_blockRank_le_cutRank_add_transpose [Finite ι] (hfg.symm.mono inter_subset_right inter_subset_right) (fun i hi j hj hj' => by have hjY : j ∉ Y := fun h => hj' ⟨hj, h⟩ - simp only [add_apply, transpose_apply, hMY i j hjY, hMY j i (hnotY i hi.2), add_zero]) + simp only [Matrix.add_apply, transpose_apply, hMY i j hjY, hMY j i (hnotY i hi.2), add_zero]) (fun i hi j hj hj' => by have hjX : j ∉ X := fun h => hj' ⟨hj, h⟩ - simp only [add_apply, transpose_apply, hMX i j (hnotX i hi.2), hMX j i hjX, add_zero]) + simp only [Matrix.add_apply, transpose_apply, hMX i j (hnotX i hi.2), hMX j i hjX, add_zero]) have hb₁ : R.blockRank (f ⁻¹' U) (g ⁻¹' Uᶜ) ≤ (M + Mᵀ).blockRank (U ∩ X) Uᶜ := by calc R.blockRank (f ⁻¹' U) (g ⁻¹' Uᶜ) = M.blockRank (U ∩ X) (Uᶜ ∩ Y) := by rw [← hMR, M.blockRank_submatrix hf hg, image_preimage_eq_inter_range, image_preimage_eq_inter_range] _ = (M + Mᵀ).blockRank (U ∩ X) (Uᶜ ∩ Y) := blockRank_congr fun i hi j _ => by - simp only [add_apply, transpose_apply, hMY j i (hnotY i hi.2), add_zero] + simp only [Matrix.add_apply, transpose_apply, hMY j i (hnotY i hi.2), add_zero] _ ≤ (M + Mᵀ).blockRank (U ∩ X) Uᶜ := blockRank_mono_right _ _ inter_subset_left have hb₂ : R.blockRank (f ⁻¹' Uᶜ) (g ⁻¹' U) ≤ (M + Mᵀ).blockRank (U ∩ Y) Uᶜ := by calc R.blockRank (f ⁻¹' Uᶜ) (g ⁻¹' U) @@ -359,7 +359,7 @@ theorem add_blockRank_le_cutRank_add_transpose [Finite ι] image_preimage_eq_inter_range] _ = Mᵀ.blockRank (U ∩ Y) (Uᶜ ∩ X) := (M.blockRank_transpose _ _).symm _ = (M + Mᵀ).blockRank (U ∩ Y) (Uᶜ ∩ X) := blockRank_congr fun i hi j _ => by - simp only [add_apply, transpose_apply, hMX i j (hnotX i hi.2), zero_add] + simp only [Matrix.add_apply, transpose_apply, hMX i j (hnotX i hi.2), zero_add] _ ≤ (M + Mᵀ).blockRank (U ∩ Y) Uᶜ := blockRank_mono_right _ _ inter_subset_left unfold cutRank omega diff --git a/Complexity/Foundations/Probability/Gaussian/Sheppard.lean b/Complexity/Foundations/Probability/Gaussian/Sheppard.lean index e3c419ba0..e18ca8f6d 100644 --- a/Complexity/Foundations/Probability/Gaussian/Sheppard.lean +++ b/Complexity/Foundations/Probability/Gaussian/Sheppard.lean @@ -308,7 +308,7 @@ theorem measureReal_stdGaussian_mem_uIoc_le_angle {u v : E} (huv : ‖u‖ = ‖ simp only [hu, hv, inner_zero_left, uIoc_of_le le_rfl, Ioc_self, mem_empty_iff_false, ofPred_false, measureReal_empty] exact div_nonneg (InnerProductGeometry.angle_nonneg _ _) pi_pos.le - · rw [eq_neg_of_add_eq_zero_right hne, InnerProductGeometry.angle_self_neg_of_nonzero hu, + · rw [eq_neg_of_add_eq_zero_right hne, InnerProductGeometry.angle_self_neg_of_ne_zero hu, div_self pi_ne_zero] exact measureReal_le_one · rw [InnerProductGeometry.angle_eq_two_mul_arctan huv hne] From f5a70cb9826c88744c1dfbc560340d3efebbfb04 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Thu, 29 Oct 2026 15:26:19 -0400 Subject: [PATCH 67/73] test(quantum): qualify the protected matrix lemma Preserve the existing non-unitary matrix counterexample under rc2 Mathlib by explicitly selecting Matrix.mul_apply, as in the library proof repairs. --- ComplexityTests/QuantumCircuit.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ComplexityTests/QuantumCircuit.lean b/ComplexityTests/QuantumCircuit.lean index a440e1a4b..cca9dbe2c 100644 --- a/ComplexityTests/QuantumCircuit.lean +++ b/ComplexityTests/QuantumCircuit.lean @@ -158,7 +158,7 @@ example : ¬Circuit.IsOver {⟨3, Gate.toffoli⟩} [h₀] := fun h => example : !![(1 : ℂ), 1; 0, 1] ∉ unitaryGroup (Fin 2) ℂ := by intro h have := congrFun (congrFun (mem_unitaryGroup_iff.mp h) 0) 0 - simp [mul_apply, Fin.sum_univ_two, star_eq_conjTranspose] at this + simp [Matrix.mul_apply, Fin.sum_univ_two, star_eq_conjTranspose] at this -- A two-qubit gate cannot be placed on a one-qubit register: the wires must be distinct. example : IsEmpty (Fin 2 ↪ Fin 1) := From bb3e910dfc197e46f4b11e0c666b6546a075b7c8 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Thu, 29 Oct 2026 15:46:20 -0400 Subject: [PATCH 68/73] fix(build): normalize goals before proof automation Use expected-type-directed refinement for the generic zero-sum lemma and expose the determinant variable-count function before fun_prop. Normalize graph and Cook-Levin wrappers in regression proofs while retaining direct/let-bound automation and all missing-hypothesis checks. Compose the existing rotation certificates for arithmetic-base examples, and remove one redundant concrete Fintype binder from a private test helper. Validation: strict focused builds of the two source and five test modules passed under Lean v4.36.0-rc2. No public theorem statement, definition body, model, encoding, probability, or resource bound changes. No heartbeat or fun_prop compatibility option was introduced. --- Complexity/Algorithms/WordRAM/KargerMinCut.lean | 2 +- .../Computability/Circuit/Arithmetic/Determinant.lean | 4 +++- ComplexityTests/ExpanderContraction.lean | 3 ++- ComplexityTests/ExpanderFamily.lean | 1 + ComplexityTests/ExplicitExpanders.lean | 8 ++++++-- ComplexityTests/Iteration/SparseDisplacement.lean | 2 +- ComplexityTests/MultiTapePolyTime.lean | 1 + 7 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Complexity/Algorithms/WordRAM/KargerMinCut.lean b/Complexity/Algorithms/WordRAM/KargerMinCut.lean index c07c84a3c..5cf511394 100644 --- a/Complexity/Algorithms/WordRAM/KargerMinCut.lean +++ b/Complexity/Algorithms/WordRAM/KargerMinCut.lean @@ -1181,7 +1181,7 @@ theorem ampLoop_spec {w sb n R : ℕ} {W : Fin n → Fin n → ℕ} {mem₀ : obtain ⟨m_r, hm_r, _, hsup_r, _⟩ := hnext z₁ hz₁ rw [hml_def, hm_r.codeComp_eq, PMF.toOuterMeasure_map_apply, PMF.toOuterMeasure_apply] - convert tsum_eq_zero_iff.mpr _ + refine tsum_eq_zero_iff.mpr ?_ intro z₂ by_cases hz₂_eval : z₂ ∈ m_r.eval.support · have hz₂ : z₂ ∈ m_r.support := (ProbComp.support_eval m_r) ▸ hz₂_eval diff --git a/Complexity/Computability/Circuit/Arithmetic/Determinant.lean b/Complexity/Computability/Circuit/Arithmetic/Determinant.lean index 1f231b4cf..01cf49231 100644 --- a/Complexity/Computability/Circuit/Arithmetic/Determinant.lean +++ b/Complexity/Computability/Circuit/Arithmetic/Determinant.lean @@ -272,7 +272,9 @@ generic determinants has `n * n` variables, degree at most `n` and arithmetic ci `3 * n ^ 4 + 4` gates. -/ theorem detFamily_mem_VP : detFamily K ∈ VP K := by choose c hc hsize using exists_circuit_det (K := K) - refine ⟨⟨by fun_prop, PolynomiallyBounded.id.mono_of_le fun n => + refine ⟨⟨?_, PolynomiallyBounded.id.mono_of_le fun n => totalDegree_det_genericMatrix_le n⟩, c, PolynomiallyBounded.mono_of_le (by fun_prop) hsize, hc⟩ + change PolynomiallyBounded (fun n : ℕ => n * n) + fun_prop end Cslib.Circuits.Arithmetic diff --git a/ComplexityTests/ExpanderContraction.lean b/ComplexityTests/ExpanderContraction.lean index ac59474d9..1bd9d7150 100644 --- a/ComplexityTests/ExpanderContraction.lean +++ b/ComplexityTests/ExpanderContraction.lean @@ -68,7 +68,7 @@ example : ComputableInPolyTime threeVertices.rot have hg : ComputableInPolyTime completeFive.rot (encPair (encFinEnum (Fin 5)) (encFinEnum (Fin 5))) (encPair (encFinEnum (Fin 5)) (encFinEnum (Fin 5))) := .of_finite _ - fun_prop [threeVertices, RotationMap.contractFin] + exact ComputableInPolyTime.rotationMap_contract he hd hg example {V W I D : Type} (G : RotationMap V D) (encode : V → W × I) (decode : W × I → Option V) (hinv : Function.IsPartialInv encode decode) @@ -80,6 +80,7 @@ example {V W I D : Type} (G : RotationMap V D) (encode : V → W × I) (fun p : (W × (I × D)) × Bool => ((RotationMap.contract encode decode hinv G).rot p.1, !p.2)) (encPair (encPair ew (encPair ei ed)) encBool) (encPair (encPair ew (encPair ei ed)) encBool) := by + dsimp only [RotationMap.contract, RotationMap.contractRot, RotationMap.contractDart] fail_if_success (clear he; fun_prop) fail_if_success (clear hd; fun_prop) fail_if_success (clear hg; fun_prop) diff --git a/ComplexityTests/ExpanderFamily.lean b/ComplexityTests/ExpanderFamily.lean index 0dd5ac6e9..51163712e 100644 --- a/ComplexityTests/ExpanderFamily.lean +++ b/ComplexityTests/ExpanderFamily.lean @@ -96,6 +96,7 @@ example {V W D : Type} (G : RotationMap V D) (e : V ≃ W) let next := (G.reindex e).rot p.1 (next, !p.2)) (encPair (encPair ew ed) encBool) (encPair (encPair ew ed) encBool) := by + dsimp only [RotationMap.reindex, RotationMap.reindexRot] fail_if_success (clear hf; fun_prop) fail_if_success (clear hb; fun_prop) fail_if_success (clear hg; fun_prop) diff --git a/ComplexityTests/ExplicitExpanders.lean b/ComplexityTests/ExplicitExpanders.lean index a912aabc8..c8d706921 100644 --- a/ComplexityTests/ExplicitExpanders.lean +++ b/ComplexityTests/ExplicitExpanders.lean @@ -64,7 +64,8 @@ example {K : Type} [Field K] (encode : K ↪ List Bool) ComputableInPolyTime (RotationMap.affinePlaneTowerBase K).rot (encPair (baseEncoding encode) (planeEncoding encode)) (encPair (baseEncoding encode) (planeEncoding encode)) := by - fun_prop [RotationMap.affinePlaneTowerBase] + have hplane := ComputableInPolyTime.rotationMap_affinePlane hmul hsub + exact ((hplane.rotationMap_tensor hplane).rotationMap_zigzag hplane).rotationMap_zigzag hplane -- Only the fixed field operations use the finite-domain theorem; graph products compose them. example (encode : ZMod 257 ↪ List Bool) : @@ -75,7 +76,8 @@ example (encode : ZMod 257 ↪ List Bool) : (planeEncoding encode) encode := .of_finite _ have hsub : ComputableInPolyTime (fun p : ZMod 257 × ZMod 257 => p.1 - p.2) (planeEncoding encode) encode := .of_finite _ - fun_prop [RotationMap.affinePlaneTowerBase] + have hplane := ComputableInPolyTime.rotationMap_affinePlane hmul hsub + exact ((hplane.rotationMap_tensor hplane).rotationMap_zigzag hplane).rotationMap_zigzag hplane -- Equivalent direct and let-bound spellings, with a supplied rotation on an arbitrary type. example {V D E : Type} (G : RotationMap V D) (H : RotationMap D E) @@ -86,6 +88,7 @@ example {V D E : Type} (G : RotationMap V D) (H : RotationMap D E) (fun p : ((V × D) × (E × E)) × Bool => ((G.zigzag H).rot p.1, !p.2)) (encPair (encPair (encPair vertex label) (encPair small small)) encBool) (encPair (encPair (encPair vertex label) (encPair small small)) encBool) := by + dsimp only [RotationMap.zigzag, RotationMap.zigzagRot] fail_if_success (clear hG; fun_prop) fail_if_success (clear hH; fun_prop) fun_prop @@ -100,6 +103,7 @@ example {V D E : Type} (G : RotationMap V D) (H : RotationMap D E) (next, !p.2)) (encPair (encPair (encPair vertex label) (encPair small small)) encBool) (encPair (encPair (encPair vertex label) (encPair small small)) encBool) := by + dsimp only [RotationMap.zigzag, RotationMap.zigzagRot] fun_prop end ComplexityTests.ExplicitExpanders diff --git a/ComplexityTests/Iteration/SparseDisplacement.lean b/ComplexityTests/Iteration/SparseDisplacement.lean index 634e74f09..fbe746f4f 100644 --- a/ComplexityTests/Iteration/SparseDisplacement.lean +++ b/ComplexityTests/Iteration/SparseDisplacement.lean @@ -34,7 +34,7 @@ example : private def editedCounter : Equiv.Perm (BitVec 2) := (Equiv.swap 0 1).trans (Equiv.addRight 1) -private theorem editedCounter_disagreement [Fintype (BitVec 2)] : +private theorem editedCounter_disagreement : disagreement editedCounter (fun x => x + 1) = {0, 1} := by ext x simp only [mem_disagreement, Finset.mem_insert, Finset.mem_singleton] diff --git a/ComplexityTests/MultiTapePolyTime.lean b/ComplexityTests/MultiTapePolyTime.lean index 2042aace5..8fde4c4a0 100644 --- a/ComplexityTests/MultiTapePolyTime.lean +++ b/ComplexityTests/MultiTapePolyTime.lean @@ -139,6 +139,7 @@ example {α : Type} (enc : α ↪ List Bool) (p : Polynomial ℕ) (c d : ℕ) : ComputableTidilyInPolyTime (fun a => List.range (Cslib.Complexity.CookLevin.parameters p c d enc a).2.2) enc (encList encUnary) := by + dsimp only [Cslib.Complexity.CookLevin.parameters] fun_prop private def finish (move : SignType) (symbol : Bool) : Turing.MultiTapeTM 0 Bool Unit := From d4b0493621c25ff403e8e63101cb23993756c5f9 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 29 Oct 2026 16:36:13 -0400 Subject: [PATCH 69/73] chore(WordRAM): finalize native record witness validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complete #1639 candidate on pinned main d6ccb24fd2ba2f34d015eaa9c6a15c2d41195e30. Preserve source checkpoint b461744614c96b7110873e55d8846f598d0dcd97 and its normal merge c02ed2748c05ccbc2911dd1e1b0c2e71234083b0. Final validated source tree: 78662a0eebdc15dc6e8738adcf4a1d29b183161b. No later main snapshot was merged. One fixed 400-register standard-native program executes original State.init input, real preparation, first-record selection, actual queried product and original signed weight verification. Generic execution covers any occupied record. Same-y prefix Runs, charged halt, exact selected chunk/piece correctness, original coordinates, full frame and S <= falsePositiveCount + actual returned flag are proved. Width admission is positive logarithmic for polynomial original scalars/weights; prefix words/support are polynomial independently of every wider admitted physical width. Code has <=133120 flat slots including halt and does not read word width. Outer ET/prime/global choices, aggregate saving and ordinary 3SUM 1.9992 remain open under #1394. All six gates PASS on the exact final tree, LEAN_NUM_THREADS=4, cache downloads disabled: lake --no-cache build --wfail --iofail : exit 0 (608.22 s) lake --no-cache exe mk_all --check : exit 0 (8.61 s) lake --no-cache test --wfail --iofail : exit 0 (845.36 s) lake --no-cache lint : exit 0 (52.05 s) lake --no-cache exe lint-style : exit 0 (15.28 s) lake --no-cache exe checkInitImports : exit 0 (17.28 s) Gates 1-2 retained across provider/agent cancellation; unfinished gates 3-6 resumed only after native PID/command-line checks proved the prior runner had ended. Same source and flags throughout; interrupted partial tests are archived and not counted as passed. Strict tests explicitly built WordRAMExactTriangleWitnessScan, QueryValues, RecordWitness, Smoke and global Axioms. Cases cover signed success/failure, empty intervals, actual producer pair order, valid-geometry post-product consumer short circuit/false positives, frame sentinels and a fully admitted symbolic initialized positive-depth pipeline. VM consumer fixtures are not represented as full producer/product VM executions. Final static checks PASS: full hygiene --check-simp; fresh exact-base 32-library and 3-test module lints; changed-file/doc/test checks; strict body names; complete base, staged and unstaged diff --check; actual source-pin/Git-blob/quote mapping. Eight final Lean MCP targeted umbrella/client diagnostics were clean before the final sequence. Compatibility preserves own b461 mathematical/native bodies, statements, attributes and imports. Canonical root imports and four source-reference doc tokens are fixed. Only CRLF -> LF changes in five Lean files and two docs resolve the caught whitespace failure; all non-EOL bytes are identical. All 3876 other protected pinned-main source, abstract and test files are unchanged. Original credit and all landed main prose remain. No curated abstract/catalogue/rendering impact. No source/API/model change was hidden as compatibility. New whole-layer APIs/proofs still require fresh nonauthoring review. Pinned source: anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5, Programs/Sec3/Theorem17/Witnesses/Scan.lean:73-89, ScanPairs.lean:69-70 and finite Sec3/Theorem17/Witnesses.lean. Actual read-only source bytes equal pinned Git blobs. Checked quote: execsUpto acc hit f w + f.toNat ≤ failsUpto acc hit w + (foundAt acc hit f w).toNat Light semantics/costs are not imported or assumed. Native code/ABI/constants are proved directly. Source incoming flag may be arbitrary; this header begins false. Finite scan returns first zero, native verifier last zero: failure equivalence is proved, not an exact witness-order identity. Source credit retained. Evidence: .lake/codex-three-sum-continuation/record-witness/final-validation/ summary.json, static-summary.json, test-coverage.json, source-identity.json, source-mapping.json, eol-normalization.json and per-command logs. Earlier interrupted CRLF/bootstrap/syntax/cancellation records are retained separately. This commit is the durable public validation record; #1639 links it. Fresh whole-layer review pending. AI assistance: Codex implementation/proofs, Lean MCP and scripted validation. --- Complexity.lean | 28 + .../Algorithms/ExactTriangle/PieceBounds.lean | 130 +- .../ExactTriangle/PreparationAdjacency.lean | 306 ++-- .../ExactTriangle/RecordWitness/Consume.lean | 200 +-- .../ExactTriangle/RecordWitness/Exec.lean | 412 ++--- .../ExactTriangle/RecordWitness/Program.lean | 380 ++--- .../ExactTriangle/RecordWitness/Queries.lean | 470 +++--- .../RecordWitness/SourceBounds.lean | 302 ++-- .../ExactTriangle/RecordWitness/Trace.lean | 2 +- .../ExactTriangle/WitnessScan/Program.lean | 2 +- ROADMAP.md | 1416 ++++++++--------- UPSTREAM.md | 1328 ++++++++-------- 12 files changed, 2502 insertions(+), 2474 deletions(-) diff --git a/Complexity.lean b/Complexity.lean index 312878daa..bb02e5d9d 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -3,6 +3,7 @@ module -- shake: keep-all --deprecated_module: ignore public import Complexity.Algorithms.ExactTriangle.Defs public import Complexity.Algorithms.ExactTriangle.Hashing public import Complexity.Algorithms.ExactTriangle.Lopsided +public import Complexity.Algorithms.ExactTriangle.PieceBounds public import Complexity.Algorithms.ExactTriangle.Splitting public import Complexity.Algorithms.ExactTriangle.Weights public import Complexity.Algorithms.ExactTriangle.Witnesses @@ -107,6 +108,7 @@ public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Bounds public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Copy public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Decode +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Detection public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Entry public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Exec public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.First @@ -118,9 +120,35 @@ public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Prepare public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Producer public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.ProducerQueries public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Program +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.QueryValues public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.RawInput public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.SourceBounds +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Consume +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Entry +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Exec +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Failures +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Flat +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Header +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Kernel +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Original +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Pair +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Prepare +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Program +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Queries +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Readback +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.SourceBounds +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Spec +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Step +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.TimeBound +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Trace +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Width public import Complexity.Algorithms.WordRAM.ExactTriangle.ResidueClasses +public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Cell +public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Exec +public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Finite +public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Program +public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Setup public import Complexity.Algorithms.WordRAM.ExactTriangle.WriteMatrices public import Complexity.Algorithms.WordRAM.FusionTree.Binary public import Complexity.Algorithms.WordRAM.FusionTree.Boundary diff --git a/Complexity/Algorithms/ExactTriangle/PieceBounds.lean b/Complexity/Algorithms/ExactTriangle/PieceBounds.lean index 2cf000b45..ca9da0e09 100644 --- a/Complexity/Algorithms/ExactTriangle/PieceBounds.lean +++ b/Complexity/Algorithms/ExactTriangle/PieceBounds.lean @@ -1,65 +1,65 @@ -/- -Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Anthropic, PBC, Erik Demaine --/ - -module --- upstream: candidate - -public import Complexity.Algorithms.ExactTriangle.Splitting - -/-! -# Numeric coordinates of ExactTriangle pieces - -These bounds connect the existing finite pieces with the truncated original-vertex intervals -used by both native matrix preparation and witness recovery. They expose the same coordinate -facts previously private to the prepared-adjacency consumer, without changing the finite pieces. --/ - -public section - -namespace Cslib.Algorithms.ExactTriangle - -/-- The active piece/label rectangle fits in the source middle dimension. -/ -theorem pieceLength_mul_le_of_mem_sqrtPrimeWindow {D g p n k : ℕ} (hg : 1 ≤ g) - (hp : p ∈ Nat.sqrtPrimeWindow D) : - min (pieceSize D g) (n - k * pieceSize D g) * p ≤ D := by - have hpS : p ≤ sOf D := Nat.le_sqrt'.mpr (Nat.mem_sqrtPrimeWindow_iff_sq.mp hp).2.2.le - have hlen : min (pieceSize D g) (n - k * pieceSize D g) ≤ sOf D := - (min_le_left _ _).trans (pieceSize_le_sOf hg) - exact (Nat.mul_le_mul hlen hpS).trans (Nat.sqrt_le D) - -/-- An original piece vertex has its computed local coordinate in the actual truncated interval. -/ -theorem piece_coordinate {n D g k : ℕ} (hD : 16 ≤ D) (hg : 1 ≤ g) - (c : Fin n) (hc : c ∈ piece n D g k) : - k * pieceSize D g + c.val % pieceSize D g = c.val ∧ - c.val % pieceSize D g < min (pieceSize D g) (n - k * pieceSize D g) := by - have hB := pieceSize_pos hD hg - have hdiv := (mem_piece c).mp hc - have hsplit := Nat.div_add_mod c.val (pieceSize D g) - rw [hdiv, Nat.mul_comm] at hsplit - have hmod := Nat.mod_lt c.val hB - exact ⟨hsplit, lt_min hmod (by have := c.isLt; omega)⟩ - -/-- The finite piece is precisely its original-coordinate truncated interval. -/ -theorem mem_piece_iff_interval {n D g k : ℕ} (hD : 16 ≤ D) (hg : 1 ≤ g) (c : Fin n) : - c ∈ piece n D g k ↔ k * pieceSize D g ≤ c.val ∧ - c.val < k * pieceSize D g + min (pieceSize D g) (n - k * pieceSize D g) := by - constructor - · intro hc - obtain ⟨he, hv⟩ := piece_coordinate hD hg c hc - omega - · rintro ⟨hlo, hhi⟩ - apply (mem_piece c).mpr - have hB := pieceSize_pos hD hg - have hsmall : c.val - k * pieceSize D g < pieceSize D g := by - have := min_le_left (pieceSize D g) (n - k * pieceSize D g) - omega - have he : c.val = k * pieceSize D g + (c.val - k * pieceSize D g) := by omega - have hsmall' : c.val - pieceSize D g * k < pieceSize D g := by - simpa only [Nat.mul_comm k (pieceSize D g)] using hsmall - rw [he, Nat.mul_comm k (pieceSize D g), Nat.mul_add_div hB, - Nat.div_eq_of_lt hsmall', Nat.add_zero] - -end Cslib.Algorithms.ExactTriangle +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.ExactTriangle.Splitting + +/-! +# Numeric coordinates of ExactTriangle pieces + +These bounds connect the existing finite pieces with the truncated original-vertex intervals +used by both native matrix preparation and witness recovery. They expose the same coordinate +facts previously private to the prepared-adjacency consumer, without changing the finite pieces. +-/ + +public section + +namespace Cslib.Algorithms.ExactTriangle + +/-- The active piece/label rectangle fits in the source middle dimension. -/ +theorem pieceLength_mul_le_of_mem_sqrtPrimeWindow {D g p n k : ℕ} (hg : 1 ≤ g) + (hp : p ∈ Nat.sqrtPrimeWindow D) : + min (pieceSize D g) (n - k * pieceSize D g) * p ≤ D := by + have hpS : p ≤ sOf D := Nat.le_sqrt'.mpr (Nat.mem_sqrtPrimeWindow_iff_sq.mp hp).2.2.le + have hlen : min (pieceSize D g) (n - k * pieceSize D g) ≤ sOf D := + (min_le_left _ _).trans (pieceSize_le_sOf hg) + exact (Nat.mul_le_mul hlen hpS).trans (Nat.sqrt_le D) + +/-- An original piece vertex has its computed local coordinate in the actual truncated interval. -/ +theorem piece_coordinate {n D g k : ℕ} (hD : 16 ≤ D) (hg : 1 ≤ g) + (c : Fin n) (hc : c ∈ piece n D g k) : + k * pieceSize D g + c.val % pieceSize D g = c.val ∧ + c.val % pieceSize D g < min (pieceSize D g) (n - k * pieceSize D g) := by + have hB := pieceSize_pos hD hg + have hdiv := (mem_piece c).mp hc + have hsplit := Nat.div_add_mod c.val (pieceSize D g) + rw [hdiv, Nat.mul_comm] at hsplit + have hmod := Nat.mod_lt c.val hB + exact ⟨hsplit, lt_min hmod (by have := c.isLt; omega)⟩ + +/-- The finite piece is precisely its original-coordinate truncated interval. -/ +theorem mem_piece_iff_interval {n D g k : ℕ} (hD : 16 ≤ D) (hg : 1 ≤ g) (c : Fin n) : + c ∈ piece n D g k ↔ k * pieceSize D g ≤ c.val ∧ + c.val < k * pieceSize D g + min (pieceSize D g) (n - k * pieceSize D g) := by + constructor + · intro hc + obtain ⟨he, hv⟩ := piece_coordinate hD hg c hc + omega + · rintro ⟨hlo, hhi⟩ + apply (mem_piece c).mpr + have hB := pieceSize_pos hD hg + have hsmall : c.val - k * pieceSize D g < pieceSize D g := by + have := min_le_left (pieceSize D g) (n - k * pieceSize D g) + omega + have he : c.val = k * pieceSize D g + (c.val - k * pieceSize D g) := by omega + have hsmall' : c.val - pieceSize D g * k < pieceSize D g := by + simpa only [Nat.mul_comm k (pieceSize D g)] using hsmall + rw [he, Nat.mul_comm k (pieceSize D g), Nat.mul_add_div hB, + Nat.div_eq_of_lt hsmall', Nat.add_zero] + +end Cslib.Algorithms.ExactTriangle diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/PreparationAdjacency.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/PreparationAdjacency.lean index 34f5f03df..57437d950 100644 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/PreparationAdjacency.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/PreparationAdjacency.lean @@ -1,153 +1,153 @@ -/- -Copyright (c) 2026 Erik Demaine. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Erik Demaine --/ - -module --- upstream: candidate - -public import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationSpec -public import Complexity.Algorithms.ExactTriangle.PieceBounds - -/-! -# Adjacency in prepared native ExactTriangle matrices - -These consumer lemmas connect actual prepared memory to the existing finite lopsided graph. -The source prime window supplies the padding bound, and piece membership supplies each local -coordinate. Neither is an additional layout certificate for the program. - -## Main results - -* `PreparationSpec.instance_adjacency`: actual stored cells give the finite adjacency relations - and zero padding, with piece coordinates and middle-width admissibility derived. - -## References - -The adjacency relations are those of the pinned -[Theorem 17 matrix construction](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Programs/Sec3/Theorem17/Instances/WriteMatrices.lean). -The composition with native memory readback is original. --/ - -public section - -namespace Cslib.Algorithms.WordRAM.ExactTriangle.PreparationSpec - -open Cslib.WordRAM Cslib.Algorithms.ExactTriangle Cslib.Matrix - -/-- The actual prepared matrix cells express the existing finite graph's adjacency, and the -unused middle positions are zero. The prime-window and piece hypotheses discharge every -padding and local-coordinate premise of the matrix-entry API. -/ -theorem instance_adjacency {n D g p : ℕ} (values : ℕ → ℤ) - (wAB wBC wAC : Fin n → Fin n → ℤ) - (hD : 16 ≤ D) (hDn : D ≤ n) (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow D) - (hAB : ∀ a b, values (pairIndex (a, b)) = wAB a b) - (hBC : ∀ b c, values (n * n + pairIndex (b, c)) = wBC b c) - (hAC : ∀ a c, values (2 * (n * n) + pairIndex (a, c)) = wAC a c) - {initial y : Data 64} (h : Instances.Output n D g p values initial y) - (idx : InstanceIndex p) (hidx : idx ∈ instanceIndices wAB D g p) : - let N := n * n - let C := fun rho => Nat.count (fun q => (values q % (p : ℤ)).toNat = rho) N - let H := numPieces n D g - let B := pieceSize D g - let ord := ChunkCatalogue.before C (queryCap n D) idx.1.val + idx.2.1 - let base := y.regs Instances.output + - (ord * H + idx.2.2) * (6 + 2 * (n * D)) - let G := lopInstance wAB wBC wAC D g p idx - (∀ (a b c : Fin n) (hc : c ∈ piece n D g idx.2.2) (label : Fin p), - let middle := c.val % B * p + label.val - (y.mem (base + 6 + (a.val * D + middle)) = 1 ↔ - G.adjA a (⟨c, hc⟩, label)) ∧ - (y.mem (base + 6 + n * D + (middle * n + b.val)) = 1 ↔ - G.adjB (⟨c, hc⟩, label) b)) ∧ - (∀ (a b : Fin n) (middle : Fin D), - min B (n - idx.2.2 * B) ≤ middle.val / p → - y.mem (base + 6 + (a.val * D + middle.val)) = 0 ∧ - y.mem (base + 6 + n * D + (middle.val * n + b.val)) = 0) := by - let N := n * n - let R := fun q => (values q % (p : ℤ)).toNat - let C := fun rho => Nat.count (fun q => R q = rho) N - let H := numPieces n D g - let B := pieceSize D g - let K := ChunkCatalogue.before C (queryCap n D) p - let ord := ChunkCatalogue.before C (queryCap n D) idx.1.val + idx.2.1 - let base := 5 + 6 * N + p + 2 * N * p + 3 * K + - (ord * H + idx.2.2) * (6 + 2 * (n * D)) - let A : WriteMatrices.Args := - ⟨5 + 5 * N, base + 6, n, D, p, idx.2.2 * B, min B (n - idx.2.2 * B), idx.1.val⟩ - let A' := { A with src := 5 + 4 * N, dst := base + 6 + n * D } - have hp0 : p ≠ 0 := (Nat.mem_sqrtPrimeWindow_iff_sq.mp hp).1.ne_zero - obtain ⟨-, hL, hR⟩ := instance_readback values wAB hp0 hAB hD hDn hg h idx hidx - change (Block.mk A.dst (n * D)).Realizes y.mem - (fun j => WriteMatrices.entry false A (fun q => R (2 * N + q)) j) at hL - change (Block.mk A'.dst (n * D)).Realizes y.mem - (fun j => WriteMatrices.entry true A' (fun q => R (N + q)) j) at hR - have hpad : A.length * A.p ≤ A.D := pieceLength_mul_le_of_mem_sqrtPrimeWindow hg hp - change min B (n - idx.2.2 * B) * p ≤ D at hpad - have hloadL (a c : Fin n) : - R (2 * N + (a.val * n + c.val)) = (wAC a c).natMod p := by - change (values (2 * (n * n) + pairIndex (a, c)) % (p : ℤ)).toNat = _ - rw [hAC] - rfl - have hloadR (b c : Fin n) : - R (N + (b.val * n + c.val)) = (wBC b c).natMod p := by - change (values (n * n + pairIndex (b, c)) % (p : ℤ)).toNat = _ - rw [hBC] - rfl - have hout : y.regs Instances.output = 5 + 6 * N + p + 2 * N * p + 3 * K := h.2.2.2.1 - dsimp only - rw [hout] - constructor - · intro a b c hc label - obtain ⟨hvertex, hv⟩ := piece_coordinate hD hg c hc - let v : Fin A.length := ⟨c.val % B, hv⟩ - have hm : v.val * p + label.val < D := by - have hstep := Nat.mul_le_mul_right p v.isLt - have hl := label.isLt - change (v.val + 1) * p ≤ min B (n - idx.2.2 * B) * p at hstep - rw [Nat.add_mul, Nat.one_mul] at hstep - omega - have hli : a.val * D + (v.val * p + label.val) < n * D := by - have hi := Nat.mul_le_mul_right D a.isLt - rw [Nat.succ_mul] at hi - omega - have hri : (v.val * p + label.val) * n + b.val < n * D := by - have hi := Nat.mul_le_mul_right n (Nat.succ_le_of_lt hm) - rw [Nat.succ_mul] at hi - have hb := b.isLt - rw [Nat.mul_comm n D] - omega - have hleft := hL ⟨a.val * D + (v.val * p + label.val), hli⟩ - have hright := hR ⟨(v.val * p + label.val) * n + b.val, hri⟩ - change y.mem (A.dst + (a.val * D + (v.val * p + label.val))) = _ at hleft - change y.mem (A'.dst + ((v.val * p + label.val) * n + b.val)) = _ at hright - constructor - · rw [hleft] - exact MatrixSpec.left_lopInstance A (fun q => R (2 * N + q)) wAB wBC wAC - g idx.2.1 idx.2.2 idx.1 rfl a c v label hc hvertex hpad hloadL - · rw [hright] - exact MatrixSpec.right_lopInstance A' (fun q => R (N + q)) wAB wBC wAC - g idx.2.1 idx.2.2 idx.1 b c v label hc hvertex hloadR - · intro a b middle hmiddle - have hli : a.val * D + middle.val < n * D := by - have hi := Nat.mul_le_mul_right D a.isLt - rw [Nat.succ_mul] at hi - have hm := middle.isLt - omega - have hri : middle.val * n + b.val < n * D := by - have hi := Nat.mul_le_mul_right n (Nat.succ_le_of_lt middle.isLt) - rw [Nat.succ_mul] at hi - have hb := b.isLt - rw [Nat.mul_comm n D] - omega - have hleft := hL ⟨a.val * D + middle.val, hli⟩ - have hright := hR ⟨middle.val * n + b.val, hri⟩ - change y.mem (A.dst + (a.val * D + middle.val)) = _ at hleft - change y.mem (A'.dst + (middle.val * n + b.val)) = _ at hright - constructor - · rw [hleft] - exact MatrixSpec.left_padding A (fun q => R (2 * N + q)) a middle hmiddle - · rw [hright] - exact MatrixSpec.right_padding A' (fun q => R (N + q)) b middle hmiddle - -end Cslib.Algorithms.WordRAM.ExactTriangle.PreparationSpec +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationSpec +public import Complexity.Algorithms.ExactTriangle.PieceBounds + +/-! +# Adjacency in prepared native ExactTriangle matrices + +These consumer lemmas connect actual prepared memory to the existing finite lopsided graph. +The source prime window supplies the padding bound, and piece membership supplies each local +coordinate. Neither is an additional layout certificate for the program. + +## Main results + +* `PreparationSpec.instance_adjacency`: actual stored cells give the finite adjacency relations + and zero padding, with piece coordinates and middle-width admissibility derived. + +## References + +The adjacency relations are those of the pinned +[Theorem 17 matrix construction](https://github.com/anthropics/formal-math/blob/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp/Programs/Sec3/Theorem17/Instances/WriteMatrices.lean). +The composition with native memory readback is original. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.PreparationSpec + +open Cslib.WordRAM Cslib.Algorithms.ExactTriangle Cslib.Matrix + +/-- The actual prepared matrix cells express the existing finite graph's adjacency, and the +unused middle positions are zero. The prime-window and piece hypotheses discharge every +padding and local-coordinate premise of the matrix-entry API. -/ +theorem instance_adjacency {n D g p : ℕ} (values : ℕ → ℤ) + (wAB wBC wAC : Fin n → Fin n → ℤ) + (hD : 16 ≤ D) (hDn : D ≤ n) (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow D) + (hAB : ∀ a b, values (pairIndex (a, b)) = wAB a b) + (hBC : ∀ b c, values (n * n + pairIndex (b, c)) = wBC b c) + (hAC : ∀ a c, values (2 * (n * n) + pairIndex (a, c)) = wAC a c) + {initial y : Data 64} (h : Instances.Output n D g p values initial y) + (idx : InstanceIndex p) (hidx : idx ∈ instanceIndices wAB D g p) : + let N := n * n + let C := fun rho => Nat.count (fun q => (values q % (p : ℤ)).toNat = rho) N + let H := numPieces n D g + let B := pieceSize D g + let ord := ChunkCatalogue.before C (queryCap n D) idx.1.val + idx.2.1 + let base := y.regs Instances.output + + (ord * H + idx.2.2) * (6 + 2 * (n * D)) + let G := lopInstance wAB wBC wAC D g p idx + (∀ (a b c : Fin n) (hc : c ∈ piece n D g idx.2.2) (label : Fin p), + let middle := c.val % B * p + label.val + (y.mem (base + 6 + (a.val * D + middle)) = 1 ↔ + G.adjA a (⟨c, hc⟩, label)) ∧ + (y.mem (base + 6 + n * D + (middle * n + b.val)) = 1 ↔ + G.adjB (⟨c, hc⟩, label) b)) ∧ + (∀ (a b : Fin n) (middle : Fin D), + min B (n - idx.2.2 * B) ≤ middle.val / p → + y.mem (base + 6 + (a.val * D + middle.val)) = 0 ∧ + y.mem (base + 6 + n * D + (middle.val * n + b.val)) = 0) := by + let N := n * n + let R := fun q => (values q % (p : ℤ)).toNat + let C := fun rho => Nat.count (fun q => R q = rho) N + let H := numPieces n D g + let B := pieceSize D g + let K := ChunkCatalogue.before C (queryCap n D) p + let ord := ChunkCatalogue.before C (queryCap n D) idx.1.val + idx.2.1 + let base := 5 + 6 * N + p + 2 * N * p + 3 * K + + (ord * H + idx.2.2) * (6 + 2 * (n * D)) + let A : WriteMatrices.Args := + ⟨5 + 5 * N, base + 6, n, D, p, idx.2.2 * B, min B (n - idx.2.2 * B), idx.1.val⟩ + let A' := { A with src := 5 + 4 * N, dst := base + 6 + n * D } + have hp0 : p ≠ 0 := (Nat.mem_sqrtPrimeWindow_iff_sq.mp hp).1.ne_zero + obtain ⟨-, hL, hR⟩ := instance_readback values wAB hp0 hAB hD hDn hg h idx hidx + change (Block.mk A.dst (n * D)).Realizes y.mem + (fun j => WriteMatrices.entry false A (fun q => R (2 * N + q)) j) at hL + change (Block.mk A'.dst (n * D)).Realizes y.mem + (fun j => WriteMatrices.entry true A' (fun q => R (N + q)) j) at hR + have hpad : A.length * A.p ≤ A.D := pieceLength_mul_le_of_mem_sqrtPrimeWindow hg hp + change min B (n - idx.2.2 * B) * p ≤ D at hpad + have hloadL (a c : Fin n) : + R (2 * N + (a.val * n + c.val)) = (wAC a c).natMod p := by + change (values (2 * (n * n) + pairIndex (a, c)) % (p : ℤ)).toNat = _ + rw [hAC] + rfl + have hloadR (b c : Fin n) : + R (N + (b.val * n + c.val)) = (wBC b c).natMod p := by + change (values (n * n + pairIndex (b, c)) % (p : ℤ)).toNat = _ + rw [hBC] + rfl + have hout : y.regs Instances.output = 5 + 6 * N + p + 2 * N * p + 3 * K := h.2.2.2.1 + dsimp only + rw [hout] + constructor + · intro a b c hc label + obtain ⟨hvertex, hv⟩ := piece_coordinate hD hg c hc + let v : Fin A.length := ⟨c.val % B, hv⟩ + have hm : v.val * p + label.val < D := by + have hstep := Nat.mul_le_mul_right p v.isLt + have hl := label.isLt + change (v.val + 1) * p ≤ min B (n - idx.2.2 * B) * p at hstep + rw [Nat.add_mul, Nat.one_mul] at hstep + omega + have hli : a.val * D + (v.val * p + label.val) < n * D := by + have hi := Nat.mul_le_mul_right D a.isLt + rw [Nat.succ_mul] at hi + omega + have hri : (v.val * p + label.val) * n + b.val < n * D := by + have hi := Nat.mul_le_mul_right n (Nat.succ_le_of_lt hm) + rw [Nat.succ_mul] at hi + have hb := b.isLt + rw [Nat.mul_comm n D] + omega + have hleft := hL ⟨a.val * D + (v.val * p + label.val), hli⟩ + have hright := hR ⟨(v.val * p + label.val) * n + b.val, hri⟩ + change y.mem (A.dst + (a.val * D + (v.val * p + label.val))) = _ at hleft + change y.mem (A'.dst + ((v.val * p + label.val) * n + b.val)) = _ at hright + constructor + · rw [hleft] + exact MatrixSpec.left_lopInstance A (fun q => R (2 * N + q)) wAB wBC wAC + g idx.2.1 idx.2.2 idx.1 rfl a c v label hc hvertex hpad hloadL + · rw [hright] + exact MatrixSpec.right_lopInstance A' (fun q => R (N + q)) wAB wBC wAC + g idx.2.1 idx.2.2 idx.1 b c v label hc hvertex hloadR + · intro a b middle hmiddle + have hli : a.val * D + middle.val < n * D := by + have hi := Nat.mul_le_mul_right D a.isLt + rw [Nat.succ_mul] at hi + have hm := middle.isLt + omega + have hri : middle.val * n + b.val < n * D := by + have hi := Nat.mul_le_mul_right n (Nat.succ_le_of_lt middle.isLt) + rw [Nat.succ_mul] at hi + have hb := b.isLt + rw [Nat.mul_comm n D] + omega + have hleft := hL ⟨a.val * D + middle.val, hli⟩ + have hright := hR ⟨middle.val * n + b.val, hri⟩ + change y.mem (A.dst + (a.val * D + middle.val)) = _ at hleft + change y.mem (A'.dst + (middle.val * n + b.val)) = _ at hright + constructor + · rw [hleft] + exact MatrixSpec.left_padding A (fun q => R (2 * N + q)) a middle hmiddle + · rw [hright] + exact MatrixSpec.right_padding A' (fun q => R (N + q)) b middle hmiddle + +end Cslib.Algorithms.WordRAM.ExactTriangle.PreparationSpec diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Consume.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Consume.lean index 2a9aee858..3c1606b04 100644 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Consume.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Consume.lean @@ -1,100 +1,100 @@ -/- -Copyright (c) 2026 Erik Demaine. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Erik Demaine --/ - -module --- upstream: candidate - -public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Header -public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Queries - -/-! -# Complete per-record candidate consumption - -The real header reads the record and computes its piece interval. The real query loop then -loads product answers and original pairs, verifies the original signed weights and returns a -genuine original-coordinate witness when one exists among the selected pairs. All memory and -the complete earlier producer/product register bank are preserved. - -The numerical component premises are discharged from original caller data by the initialized -composition. In particular the record and answer words here are read from actual memory, not -supplied witness or execution certificates. --/ - -public section - -namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness - -open Cslib.WordRAM Cslib.Algorithms.ExactTriangle - --- This checks the finite write sets of the fixed consumer, including the renamed scan bank. -set_option maxRecDepth 4096 in -private theorem consume_preserves : ∀ j : Fin 400, j.val < 320 → consume.Preserves j := by - decide - -/-- Execute the actual descriptor decoder and short-circuit verifier loop. The additional -scan term uses the native counter, bounded by failed accepted pairs plus one success. -/ -theorem exec_consume {w U n D g i records heap out : ℕ} - (AB BC AC : ℕ → ℕ → ℤ) (a b : ℕ → ℕ) (x : Data 400) (v : Fin 6 → ℕ) - (hD : x.mem 1 = D) (hg : x.mem 2 = g) (hn : x.mem 3 = n) - (hi : x.regs (productBank RecordProduct.Reg.ordinal) = i) - (hrecords : x.regs (productBank (RecordProduct.producerBank Instances.output)) = records) - (hout : x.regs (productBank RecordProduct.Reg.result) = out) - (hrec : records + i * (6 + 2 * (n * D)) + 6 ≤ heap) - (hv : ∀ j : Fin 6, x.mem (records + i * (6 + 2 * (n * D)) + j.val) = v j) - (hfirst : v 1 * pieceSize D g ≤ n) - (hquery : ∀ j < v 3, x.mem (v 2 + 2 * j) = a j ∧ - x.mem (v 2 + 2 * j + 1) = b j ∧ a j < n ∧ b j < n) - (hmem : ∀ u < n, ∀ z < n, - x.mem (5 + (u * n + z)) = Signed.encode (AB u z) ∧ - x.mem (5 + n * n + (u * n + z)) = Signed.encode (BC u z) ∧ - x.mem (5 + n * n + n * n + (u * n + z)) = Signed.encode (AC u z)) - (hmag : ∀ u < n, ∀ z < n, - (AB u z).natAbs ≤ U ∧ (BC u z).natAbs ≤ U ∧ (AC u z).natAbs ≤ U) - (hU : GenericRAM.Signed.Fits w (2 * U : ℕ)) - (hfit : Fits (w + 2) (heap + out + v 2 + 4 * v 3 + 4 * D + - 2 * (n * D) + 9 * (n * n) + 4 * n + g + 2048)) : - let first := v 1 * pieceSize D g - let length := min (pieceSize D g) (n - first) - ∃ y S, ExecWithin (w + 2) consume x y - (5 * Nat.sqrt D + 8 * v 3 + (42 * length + 28) * S + 52) ∧ - y.mem = x.mem ∧ y.regs Reg.scans = S ∧ - S ≤ Nat.count (fun j => x.mem (out + j) ≠ 0 ∧ - ∀ c : Fin length, AB (a j) (b j) + BC (b j) (first + c.val) + - AC (a j) (first + c.val) ≠ 0) (v 3) + y.regs Reg.found ∧ - (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ - (y.regs Reg.found = 1 ↔ ∃ j < v 3, x.mem (out + j) ≠ 0 ∧ - ∃ c, first ≤ c ∧ c < first + length ∧ AB (a j) (b j) + BC (b j) c + AC (a j) c = 0) ∧ - (y.regs Reg.found = 1 → ∃ j < v 3, - x.mem (out + j) ≠ 0 ∧ y.regs Reg.witnessA = a j ∧ y.regs Reg.witnessB = b j ∧ - first ≤ y.regs Reg.witnessC ∧ y.regs Reg.witnessC < first + length ∧ - AB (a j) (b j) + BC (b j) (y.regs Reg.witnessC) + - AC (a j) (y.regs Reg.witnessC) = 0) ∧ - ∀ j : Fin 400, j.val < 320 → y.regs j = x.regs j := by - let first := v 1 * pieceSize D g - let length := min (pieceSize D g) (n - first) - have hlength : first + length ≤ n := by - dsimp only [length, first] - have := min_le_right (pieceSize D g) (n - v 1 * pieceSize D g) - omega - obtain ⟨s, hs, sFixed, sm, sf, -, -, -, ss, si, -⟩ := exec_header x v hD hg hn hi - hrecords hout hrec hv hfirst (hfit.of_le (by omega)) - obtain ⟨y, S, he, -, ym, -, ys, hS, yf, iffFound, yw⟩ := exec_queries - AB BC AC a b s sFixed si ss sf (by simpa only [sm] using hquery) - hlength (by simpa only [sm] using hmem) hmag hU - (hfit.of_le (by omega)) - change ExecWithin (w + 2) (.loop (.lt Reg.index Reg.count) step) s y - (8 * v 3 + (42 * length + 28) * S + 2) at he - have hexe : ExecWithin (w + 2) consume x y - (5 * Nat.sqrt D + 8 * v 3 + (42 * length + 28) * S + 52) := - (hs.seq he).mono (by omega) - refine ⟨y, S, hexe, ym.trans sm, ys, ?_, yf, ?_, ?_, ?_⟩ - · simpa only [sm] using hS - · simpa only [sm] using iffFound - · simpa only [sm] using yw - · intro j hj - exact hexe.regs_eq (consume_preserves j hj) - -end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Header +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Queries + +/-! +# Complete per-record candidate consumption + +The real header reads the record and computes its piece interval. The real query loop then +loads product answers and original pairs, verifies the original signed weights and returns a +genuine original-coordinate witness when one exists among the selected pairs. All memory and +the complete earlier producer/product register bank are preserved. + +The numerical component premises are discharged from original caller data by the initialized +composition. In particular the record and answer words here are read from actual memory, not +supplied witness or execution certificates. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM Cslib.Algorithms.ExactTriangle + +-- This checks the finite write sets of the fixed consumer, including the renamed scan bank. +set_option maxRecDepth 4096 in +private theorem consume_preserves : ∀ j : Fin 400, j.val < 320 → consume.Preserves j := by + decide + +/-- Execute the actual descriptor decoder and short-circuit verifier loop. The additional +scan term uses the native counter, bounded by failed accepted pairs plus one success. -/ +theorem exec_consume {w U n D g i records heap out : ℕ} + (AB BC AC : ℕ → ℕ → ℤ) (a b : ℕ → ℕ) (x : Data 400) (v : Fin 6 → ℕ) + (hD : x.mem 1 = D) (hg : x.mem 2 = g) (hn : x.mem 3 = n) + (hi : x.regs (productBank RecordProduct.Reg.ordinal) = i) + (hrecords : x.regs (productBank (RecordProduct.producerBank Instances.output)) = records) + (hout : x.regs (productBank RecordProduct.Reg.result) = out) + (hrec : records + i * (6 + 2 * (n * D)) + 6 ≤ heap) + (hv : ∀ j : Fin 6, x.mem (records + i * (6 + 2 * (n * D)) + j.val) = v j) + (hfirst : v 1 * pieceSize D g ≤ n) + (hquery : ∀ j < v 3, x.mem (v 2 + 2 * j) = a j ∧ + x.mem (v 2 + 2 * j + 1) = b j ∧ a j < n ∧ b j < n) + (hmem : ∀ u < n, ∀ z < n, + x.mem (5 + (u * n + z)) = Signed.encode (AB u z) ∧ + x.mem (5 + n * n + (u * n + z)) = Signed.encode (BC u z) ∧ + x.mem (5 + n * n + n * n + (u * n + z)) = Signed.encode (AC u z)) + (hmag : ∀ u < n, ∀ z < n, + (AB u z).natAbs ≤ U ∧ (BC u z).natAbs ≤ U ∧ (AC u z).natAbs ≤ U) + (hU : GenericRAM.Signed.Fits w (2 * U : ℕ)) + (hfit : Fits (w + 2) (heap + out + v 2 + 4 * v 3 + 4 * D + + 2 * (n * D) + 9 * (n * n) + 4 * n + g + 2048)) : + let first := v 1 * pieceSize D g + let length := min (pieceSize D g) (n - first) + ∃ y S, ExecWithin (w + 2) consume x y + (5 * Nat.sqrt D + 8 * v 3 + (42 * length + 28) * S + 52) ∧ + y.mem = x.mem ∧ y.regs Reg.scans = S ∧ + S ≤ Nat.count (fun j => x.mem (out + j) ≠ 0 ∧ + ∀ c : Fin length, AB (a j) (b j) + BC (b j) (first + c.val) + + AC (a j) (first + c.val) ≠ 0) (v 3) + y.regs Reg.found ∧ + (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ + (y.regs Reg.found = 1 ↔ ∃ j < v 3, x.mem (out + j) ≠ 0 ∧ + ∃ c, first ≤ c ∧ c < first + length ∧ AB (a j) (b j) + BC (b j) c + AC (a j) c = 0) ∧ + (y.regs Reg.found = 1 → ∃ j < v 3, + x.mem (out + j) ≠ 0 ∧ y.regs Reg.witnessA = a j ∧ y.regs Reg.witnessB = b j ∧ + first ≤ y.regs Reg.witnessC ∧ y.regs Reg.witnessC < first + length ∧ + AB (a j) (b j) + BC (b j) (y.regs Reg.witnessC) + + AC (a j) (y.regs Reg.witnessC) = 0) ∧ + ∀ j : Fin 400, j.val < 320 → y.regs j = x.regs j := by + let first := v 1 * pieceSize D g + let length := min (pieceSize D g) (n - first) + have hlength : first + length ≤ n := by + dsimp only [length, first] + have := min_le_right (pieceSize D g) (n - v 1 * pieceSize D g) + omega + obtain ⟨s, hs, sFixed, sm, sf, -, -, -, ss, si, -⟩ := exec_header x v hD hg hn hi + hrecords hout hrec hv hfirst (hfit.of_le (by omega)) + obtain ⟨y, S, he, -, ym, -, ys, hS, yf, iffFound, yw⟩ := exec_queries + AB BC AC a b s sFixed si ss sf (by simpa only [sm] using hquery) + hlength (by simpa only [sm] using hmem) hmag hU + (hfit.of_le (by omega)) + change ExecWithin (w + 2) (.loop (.lt Reg.index Reg.count) step) s y + (8 * v 3 + (42 * length + 28) * S + 2) at he + have hexe : ExecWithin (w + 2) consume x y + (5 * Nat.sqrt D + 8 * v 3 + (42 * length + 28) * S + 52) := + (hs.seq he).mono (by omega) + refine ⟨y, S, hexe, ym.trans sm, ys, ?_, yf, ?_, ?_, ?_⟩ + · simpa only [sm] using hS + · simpa only [sm] using iffFound + · simpa only [sm] using yw + · intro j hj + exact hexe.regs_eq (consume_preserves j hj) + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Exec.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Exec.lean index b62cbe95a..6d0780e09 100644 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Exec.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Exec.lean @@ -1,206 +1,206 @@ -/- -Copyright (c) 2026 Erik Demaine. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Erik Demaine --/ - -module --- upstream: candidate - -public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Consume -public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Readback - -/-! -# The real product followed by candidate verification - -The general caller selects any occupied record. The actual product is executed on its whole -renamed 320-register bank; the consumer then reads its real answers and protected query words. -The original signed arrays and caller catalogue remain untouched. The initialized entry derives -the numerical premises from original input, rather than supplying an intermediate run contract. --/ - -public section - -namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness - -open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle -open Cslib.Algorithms.MatrixMultiplication.Schonhage -open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage - -private theorem productBank_injective : Function.Injective productBank := by - intro i j h - exact Fin.ext (congrArg (fun k : Fin 400 => k.val) h) - -/-- Execute the actual product and verifier from one genuine producer output. No product -execution, accepted-pair list, witness or time certificate is a hypothesis. -/ -theorem exec_of_output {w U m n g p : ℕ} (values : ℕ → ℤ) - (AB BC AC : ℕ → ℕ → ℤ) (initial : Data 64) (x : Data 400) - (h : Instances.Output n (D m) g p values initial - ((x.comap productBank).comap RecordProduct.producerBank)) - (hD : 16 ≤ D m) (hg : 1 ≤ g) (hp : p ≠ 0) (hN : D m ^ 18 ≤ n) - (hheaderD : initial.mem 1 = D m) (hheaderG : initial.mem 2 = g) - (hheaderN : initial.mem 3 = n) - (idx : InstanceIndex p) - (hidx : idx ∈ instanceIndices (fun a b : Fin n => values (pairIndex (a, b))) (D m) g p) - (hi : x.regs (productBank RecordProduct.Reg.ordinal) = - RecordProduct.recordOrdinal n (D m) g p values idx) - (hmem : ∀ a < n, ∀ b < n, - x.mem (5 + (a * n + b)) = Signed.encode (AB a b) ∧ - x.mem (5 + n * n + (a * n + b)) = Signed.encode (BC a b) ∧ - x.mem (5 + n * n + n * n + (a * n + b)) = Signed.encode (AC a b)) - (hmag : ∀ a < n, ∀ b < n, - (AB a b).natAbs ≤ U ∧ (BC a b).natAbs ≤ U ∧ (AC a b).natAbs ≤ U) - (hU : GenericRAM.Signed.Fits w (2 * U : ℕ)) - (hfit : Fits (w + 2) - (4 * x.regs (productBank 42) + - 4 * CallableProduct.windowSize n (RecordProduct.recordQueryCount n (D m) p values idx) + - QueriedProduct.outputBase (19 * m) m n - (RecordProduct.recordQueryCount n (D m) p values idx) + - 4 * RecordProduct.recordQueryCount n (D m) p values idx + 4 * D m + - 2 * (n * D m) + 9 * (n * n) + 4 * n + g + 4096)) : - let hn : 0 < n := lt_of_lt_of_le (by omega : 0 < D m) - ((Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN) - let q := RecordProduct.recordQueries n (D m) p idx - let Q := RecordProduct.recordQueryCount n (D m) p values idx - let qs := RecordProduct.decodedQueries hn x.mem q Q - let first := idx.2.2 * pieceSize (D m) g - let length := min (pieceSize (D m) g) (n - first) - ∃ y S, ExecWithin (w + 2) program x y - (RecordProduct.timeBound (m := m) qs + 5 * Nat.sqrt (D m) + 8 * Q + - (42 * length + 28) * S + 52) ∧ - y.regs Reg.scans = S ∧ (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ - S ≤ Nat.count (fun j => y.mem (y.regs (productBank RecordProduct.Reg.result) + j) ≠ 0 ∧ - ∀ c : Fin length, - AB (x.mem (q + 2 * j)) (x.mem (q + 2 * j + 1)) + - BC (x.mem (q + 2 * j + 1)) (first + c.val) + - AC (x.mem (q + 2 * j)) (first + c.val) ≠ 0) Q + y.regs Reg.found ∧ - (y.regs Reg.found = 1 ↔ ∃ j < Q, - y.mem (y.regs (productBank RecordProduct.Reg.result) + j) ≠ 0 ∧ - ∃ c, first ≤ c ∧ c < first + length ∧ - AB (x.mem (q + 2 * j)) (x.mem (q + 2 * j + 1)) + - BC (x.mem (q + 2 * j + 1)) c + AC (x.mem (q + 2 * j)) c = 0) ∧ - (∀ j : Fin qs.length, - y.mem (y.regs (productBank RecordProduct.Reg.result) + j.val) = Signed.encode - ((RecordProduct.leftMatrix m n g p values idx * - RecordProduct.rightMatrix m n g p values idx) (qs.get j).1 (qs.get j).2)) ∧ - (y.regs Reg.found = 1 → - y.regs Reg.witnessA < n ∧ y.regs Reg.witnessB < n ∧ - first ≤ y.regs Reg.witnessC ∧ y.regs Reg.witnessC < first + length ∧ - AB (y.regs Reg.witnessA) (y.regs Reg.witnessB) + - BC (y.regs Reg.witnessB) (y.regs Reg.witnessC) + - AC (y.regs Reg.witnessA) (y.regs Reg.witnessC) = 0 ∧ - ∃ j < Q, y.regs Reg.witnessA = x.mem (q + 2 * j) ∧ - y.regs Reg.witnessB = x.mem (q + 2 * j + 1)) ∧ - (∀ u, u < x.regs (productBank 42) ∨ - x.regs (productBank 42) + CallableProduct.windowSize n Q ≤ u → y.mem u = x.mem u) ∧ - (∀ j : Fin 320, j.val < 65 → y.regs (productBank j) = x.regs (productBank j)) := by - let x' := x.comap productBank - let produced := x'.comap RecordProduct.producerBank - let heap := x.regs (productBank 42) - let q := RecordProduct.recordQueries n (D m) p idx - let Q := RecordProduct.recordQueryCount n (D m) p values idx - let ord := RecordProduct.recordOrdinal n (D m) g p values idx - let base := x.regs (productBank (RecordProduct.producerBank Instances.output)) + - ord * (6 + 2 * (n * D m)) - let out := heap + QueriedProduct.outputBase (19 * m) m n Q - change Fits (w + 2) (4 * heap + 4 * CallableProduct.windowSize n Q + - QueriedProduct.outputBase (19 * m) m n Q + 4 * Q + 4 * D m + - 2 * (n * D m) + 9 * (n * n) + 4 * n + g + 4096) at hfit - have hDn : D m ≤ n := (Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN - have hn : 0 < n := lt_of_lt_of_le (by omega) hDn - let qs := RecordProduct.decodedQueries hn x.mem q Q - have hlen : qs.length = Q := RecordProduct.decodedQueries_length hn x.mem q Q - obtain ⟨hinputEnd, hrec, hq, hfirst, hv, hcoords, hprefix⟩ := - record_view values initial produced h hD hDn hg hp idx hidx - change 5 + 3 * (n * n) ≤ heap at hinputEnd - change base + 6 ≤ heap at hrec - change q + 2 * Q ≤ heap at hq - change ∀ j : Fin 6, x.mem (base + j.val) = - ![idx.1.val, idx.2.2, q, Q, base + 6, base + 6 + n * D m] j at hv - change ∀ j < Q, x.mem (q + 2 * j) < n ∧ x.mem (q + 2 * j + 1) < n at hcoords - change ∀ j < 5 + 3 * (n * n), x.mem j = initial.mem j at hprefix - obtain ⟨r, hr, rr, rvalues, rmem, rregs, -⟩ := RecordProduct.exec_of_output values - initial x' h hD hg hp hN hheaderD hheaderN idx hidx hi (hfit.of_le (by - change 2 * heap + 4 * CallableProduct.windowSize n Q + 1024 ≤ _ - omega)) - change r.regs RecordProduct.Reg.result = - heap + QueriedProduct.outputBase (19 * m) m n qs.length at rr - change ∀ j : Fin qs.length, - r.mem (heap + (QueriedProduct.outputBase (19 * m) m n qs.length + j.val)) = - Signed.encode ((RecordProduct.leftMatrix m n g p values idx * - RecordProduct.rightMatrix m n g p values idx) (qs.get j).1 (qs.get j).2) at rvalues - simp only [RecordProduct.decodedQueries_length] at rmem - change ∀ u, u < heap ∨ heap + CallableProduct.windowSize n Q ≤ u → - r.mem u = x.mem u at rmem - let s := r.extend productBank x - have hs : ExecWithin (w + 2) (RecordProduct.program.rename productBank) x s - (RecordProduct.timeBound (m := m) qs) := hr.rename productBank_injective - have sr (j : Fin 320) : s.regs (productBank j) = r.regs j := - productBank_injective.extend_apply _ _ j - have sm (u : ℕ) (hu : u < heap) : s.mem u = x.mem u := rmem u (Or.inl hu) - have snative (j : Fin 320) (hj : j.val < 65) : - s.regs (productBank j) = x.regs (productBank j) := (sr j).trans (rregs j hj) - have sout : s.regs (productBank RecordProduct.Reg.result) = out := by - simpa only [out, hlen] using (sr RecordProduct.Reg.result).trans rr - let v : Fin 6 → ℕ := ![idx.1.val, idx.2.2, q, Q, base + 6, base + 6 + n * D m] - have hread (j : Fin 6) : s.mem (base + j.val) = v j := - (sm _ (by have := j.isLt; omega)).trans (hv j) - have hquery : ∀ j < Q, s.mem (q + 2 * j) = x.mem (q + 2 * j) ∧ - s.mem (q + 2 * j + 1) = x.mem (q + 2 * j + 1) ∧ - x.mem (q + 2 * j) < n ∧ x.mem (q + 2 * j + 1) < n := by - intro j hj - exact ⟨sm _ (by omega), sm _ (by omega), hcoords j hj⟩ - have smem : ∀ a < n, ∀ b < n, - s.mem (5 + (a * n + b)) = Signed.encode (AB a b) ∧ - s.mem (5 + n * n + (a * n + b)) = Signed.encode (BC a b) ∧ - s.mem (5 + n * n + n * n + (a * n + b)) = Signed.encode (AC a b) := by - intro a ha b hb - have hn := Nat.mul_le_mul_right n (Nat.succ_le_of_lt ha) - rw [Nat.succ_mul] at hn - simpa only [sm _ (by omega : 5 + (a * n + b) < heap), - sm _ (by omega : 5 + n * n + (a * n + b) < heap), - sm _ (by omega : 5 + n * n + n * n + (a * n + b) < heap)] using hmem a ha b hb - obtain ⟨y, S, he, ym, ys, hS, yf, yiff, yw, frame⟩ := exec_consume AB BC AC - (fun j => x.mem (q + 2 * j)) (fun j => x.mem (q + 2 * j + 1)) s v - ((sm 1 (by omega)).trans ((hprefix 1 (by omega)).trans hheaderD)) - ((sm 2 (by omega)).trans ((hprefix 2 (by omega)).trans hheaderG)) - ((sm 3 (by omega)).trans ((hprefix 3 (by omega)).trans hheaderN)) - ((snative _ (by decide)).trans hi) (snative _ (by decide)) sout hrec hread hfirst - hquery smem hmag hU (hfit.of_le (by - change heap + out + q + 4 * Q + 4 * D m + 2 * (n * D m) + - 9 * (n * n) + 4 * n + g + 2048 ≤ _ - dsimp only [out] - omega)) - change ExecWithin (w + 2) consume s y - (5 * Nat.sqrt (D m) + 8 * Q + - (42 * min (pieceSize (D m) g) (n - idx.2.2 * pieceSize (D m) g) + 28) * S + 52) at he - change S ≤ Nat.count (fun j => s.mem (out + j) ≠ 0 ∧ - ∀ c : Fin (min (pieceSize (D m) g) (n - idx.2.2 * pieceSize (D m) g)), - AB (x.mem (q + 2 * j)) (x.mem (q + 2 * j + 1)) + - BC (x.mem (q + 2 * j + 1)) (idx.2.2 * pieceSize (D m) g + c.val) + - AC (x.mem (q + 2 * j)) (idx.2.2 * pieceSize (D m) g + c.val) ≠ 0) Q + y.regs Reg.found at hS - simp_rw [show v 1 = idx.2.2 from rfl, show v 3 = Q from rfl] at yiff - have hexe : ExecWithin (w + 2) program x y - (RecordProduct.timeBound (m := m) qs + 5 * Nat.sqrt (D m) + 8 * Q + - (42 * min (pieceSize (D m) g) (n - idx.2.2 * pieceSize (D m) g) + 28) * S + 52) := - (hs.seq he).mono (by omega) - refine ⟨y, S, hexe, ys, yf, ?_, ?_, ?_, ?_, ?_, ?_⟩ - · simpa only [ym, frame (productBank RecordProduct.Reg.result) (by decide), sout] using hS - · simpa only [ym, frame (productBank RecordProduct.Reg.result) (by decide), sout] using yiff - · intro j - have hv := rvalues j - rw [frame _ (by decide), sout, ym] - change r.mem (out + j.val) = _ - simpa only [out, hlen, Nat.add_assoc] using hv - · intro hy - obtain ⟨j, hj, -, hwa, hwb, hlo, hhi, hz⟩ := yw hy - obtain ⟨ha, hb⟩ := hcoords j hj - exact ⟨by simpa only [hwa] using ha, by simpa only [hwb] using hb, - hlo, hhi, by simpa only [hwa, hwb] using hz, j, hj, hwa, hwb⟩ - · intro u hu - rw [ym] - exact rmem u hu - · intro j hj - exact (frame _ (by change j.val < 320; omega)).trans (snative j hj) - -end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Consume +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Readback + +/-! +# The real product followed by candidate verification + +The general caller selects any occupied record. The actual product is executed on its whole +renamed 320-register bank; the consumer then reads its real answers and protected query words. +The original signed arrays and caller catalogue remain untouched. The initialized entry derives +the numerical premises from original input, rather than supplying an intermediate run contract. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +private theorem productBank_injective : Function.Injective productBank := by + intro i j h + exact Fin.ext (congrArg (fun k : Fin 400 => k.val) h) + +/-- Execute the actual product and verifier from one genuine producer output. No product +execution, accepted-pair list, witness or time certificate is a hypothesis. -/ +theorem exec_of_output {w U m n g p : ℕ} (values : ℕ → ℤ) + (AB BC AC : ℕ → ℕ → ℤ) (initial : Data 64) (x : Data 400) + (h : Instances.Output n (D m) g p values initial + ((x.comap productBank).comap RecordProduct.producerBank)) + (hD : 16 ≤ D m) (hg : 1 ≤ g) (hp : p ≠ 0) (hN : D m ^ 18 ≤ n) + (hheaderD : initial.mem 1 = D m) (hheaderG : initial.mem 2 = g) + (hheaderN : initial.mem 3 = n) + (idx : InstanceIndex p) + (hidx : idx ∈ instanceIndices (fun a b : Fin n => values (pairIndex (a, b))) (D m) g p) + (hi : x.regs (productBank RecordProduct.Reg.ordinal) = + RecordProduct.recordOrdinal n (D m) g p values idx) + (hmem : ∀ a < n, ∀ b < n, + x.mem (5 + (a * n + b)) = Signed.encode (AB a b) ∧ + x.mem (5 + n * n + (a * n + b)) = Signed.encode (BC a b) ∧ + x.mem (5 + n * n + n * n + (a * n + b)) = Signed.encode (AC a b)) + (hmag : ∀ a < n, ∀ b < n, + (AB a b).natAbs ≤ U ∧ (BC a b).natAbs ≤ U ∧ (AC a b).natAbs ≤ U) + (hU : GenericRAM.Signed.Fits w (2 * U : ℕ)) + (hfit : Fits (w + 2) + (4 * x.regs (productBank 42) + + 4 * CallableProduct.windowSize n (RecordProduct.recordQueryCount n (D m) p values idx) + + QueriedProduct.outputBase (19 * m) m n + (RecordProduct.recordQueryCount n (D m) p values idx) + + 4 * RecordProduct.recordQueryCount n (D m) p values idx + 4 * D m + + 2 * (n * D m) + 9 * (n * n) + 4 * n + g + 4096)) : + let hn : 0 < n := lt_of_lt_of_le (by omega : 0 < D m) + ((Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN) + let q := RecordProduct.recordQueries n (D m) p idx + let Q := RecordProduct.recordQueryCount n (D m) p values idx + let qs := RecordProduct.decodedQueries hn x.mem q Q + let first := idx.2.2 * pieceSize (D m) g + let length := min (pieceSize (D m) g) (n - first) + ∃ y S, ExecWithin (w + 2) program x y + (RecordProduct.timeBound (m := m) qs + 5 * Nat.sqrt (D m) + 8 * Q + + (42 * length + 28) * S + 52) ∧ + y.regs Reg.scans = S ∧ (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ + S ≤ Nat.count (fun j => y.mem (y.regs (productBank RecordProduct.Reg.result) + j) ≠ 0 ∧ + ∀ c : Fin length, + AB (x.mem (q + 2 * j)) (x.mem (q + 2 * j + 1)) + + BC (x.mem (q + 2 * j + 1)) (first + c.val) + + AC (x.mem (q + 2 * j)) (first + c.val) ≠ 0) Q + y.regs Reg.found ∧ + (y.regs Reg.found = 1 ↔ ∃ j < Q, + y.mem (y.regs (productBank RecordProduct.Reg.result) + j) ≠ 0 ∧ + ∃ c, first ≤ c ∧ c < first + length ∧ + AB (x.mem (q + 2 * j)) (x.mem (q + 2 * j + 1)) + + BC (x.mem (q + 2 * j + 1)) c + AC (x.mem (q + 2 * j)) c = 0) ∧ + (∀ j : Fin qs.length, + y.mem (y.regs (productBank RecordProduct.Reg.result) + j.val) = Signed.encode + ((RecordProduct.leftMatrix m n g p values idx * + RecordProduct.rightMatrix m n g p values idx) (qs.get j).1 (qs.get j).2)) ∧ + (y.regs Reg.found = 1 → + y.regs Reg.witnessA < n ∧ y.regs Reg.witnessB < n ∧ + first ≤ y.regs Reg.witnessC ∧ y.regs Reg.witnessC < first + length ∧ + AB (y.regs Reg.witnessA) (y.regs Reg.witnessB) + + BC (y.regs Reg.witnessB) (y.regs Reg.witnessC) + + AC (y.regs Reg.witnessA) (y.regs Reg.witnessC) = 0 ∧ + ∃ j < Q, y.regs Reg.witnessA = x.mem (q + 2 * j) ∧ + y.regs Reg.witnessB = x.mem (q + 2 * j + 1)) ∧ + (∀ u, u < x.regs (productBank 42) ∨ + x.regs (productBank 42) + CallableProduct.windowSize n Q ≤ u → y.mem u = x.mem u) ∧ + (∀ j : Fin 320, j.val < 65 → y.regs (productBank j) = x.regs (productBank j)) := by + let x' := x.comap productBank + let produced := x'.comap RecordProduct.producerBank + let heap := x.regs (productBank 42) + let q := RecordProduct.recordQueries n (D m) p idx + let Q := RecordProduct.recordQueryCount n (D m) p values idx + let ord := RecordProduct.recordOrdinal n (D m) g p values idx + let base := x.regs (productBank (RecordProduct.producerBank Instances.output)) + + ord * (6 + 2 * (n * D m)) + let out := heap + QueriedProduct.outputBase (19 * m) m n Q + change Fits (w + 2) (4 * heap + 4 * CallableProduct.windowSize n Q + + QueriedProduct.outputBase (19 * m) m n Q + 4 * Q + 4 * D m + + 2 * (n * D m) + 9 * (n * n) + 4 * n + g + 4096) at hfit + have hDn : D m ≤ n := (Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN + have hn : 0 < n := lt_of_lt_of_le (by omega) hDn + let qs := RecordProduct.decodedQueries hn x.mem q Q + have hlen : qs.length = Q := RecordProduct.decodedQueries_length hn x.mem q Q + obtain ⟨hinputEnd, hrec, hq, hfirst, hv, hcoords, hprefix⟩ := + record_view values initial produced h hD hDn hg hp idx hidx + change 5 + 3 * (n * n) ≤ heap at hinputEnd + change base + 6 ≤ heap at hrec + change q + 2 * Q ≤ heap at hq + change ∀ j : Fin 6, x.mem (base + j.val) = + ![idx.1.val, idx.2.2, q, Q, base + 6, base + 6 + n * D m] j at hv + change ∀ j < Q, x.mem (q + 2 * j) < n ∧ x.mem (q + 2 * j + 1) < n at hcoords + change ∀ j < 5 + 3 * (n * n), x.mem j = initial.mem j at hprefix + obtain ⟨r, hr, rr, rvalues, rmem, rregs, -⟩ := RecordProduct.exec_of_output values + initial x' h hD hg hp hN hheaderD hheaderN idx hidx hi (hfit.of_le (by + change 2 * heap + 4 * CallableProduct.windowSize n Q + 1024 ≤ _ + omega)) + change r.regs RecordProduct.Reg.result = + heap + QueriedProduct.outputBase (19 * m) m n qs.length at rr + change ∀ j : Fin qs.length, + r.mem (heap + (QueriedProduct.outputBase (19 * m) m n qs.length + j.val)) = + Signed.encode ((RecordProduct.leftMatrix m n g p values idx * + RecordProduct.rightMatrix m n g p values idx) (qs.get j).1 (qs.get j).2) at rvalues + simp only [RecordProduct.decodedQueries_length] at rmem + change ∀ u, u < heap ∨ heap + CallableProduct.windowSize n Q ≤ u → + r.mem u = x.mem u at rmem + let s := r.extend productBank x + have hs : ExecWithin (w + 2) (RecordProduct.program.rename productBank) x s + (RecordProduct.timeBound (m := m) qs) := hr.rename productBank_injective + have sr (j : Fin 320) : s.regs (productBank j) = r.regs j := + productBank_injective.extend_apply _ _ j + have sm (u : ℕ) (hu : u < heap) : s.mem u = x.mem u := rmem u (Or.inl hu) + have snative (j : Fin 320) (hj : j.val < 65) : + s.regs (productBank j) = x.regs (productBank j) := (sr j).trans (rregs j hj) + have sout : s.regs (productBank RecordProduct.Reg.result) = out := by + simpa only [out, hlen] using (sr RecordProduct.Reg.result).trans rr + let v : Fin 6 → ℕ := ![idx.1.val, idx.2.2, q, Q, base + 6, base + 6 + n * D m] + have hread (j : Fin 6) : s.mem (base + j.val) = v j := + (sm _ (by have := j.isLt; omega)).trans (hv j) + have hquery : ∀ j < Q, s.mem (q + 2 * j) = x.mem (q + 2 * j) ∧ + s.mem (q + 2 * j + 1) = x.mem (q + 2 * j + 1) ∧ + x.mem (q + 2 * j) < n ∧ x.mem (q + 2 * j + 1) < n := by + intro j hj + exact ⟨sm _ (by omega), sm _ (by omega), hcoords j hj⟩ + have smem : ∀ a < n, ∀ b < n, + s.mem (5 + (a * n + b)) = Signed.encode (AB a b) ∧ + s.mem (5 + n * n + (a * n + b)) = Signed.encode (BC a b) ∧ + s.mem (5 + n * n + n * n + (a * n + b)) = Signed.encode (AC a b) := by + intro a ha b hb + have hn := Nat.mul_le_mul_right n (Nat.succ_le_of_lt ha) + rw [Nat.succ_mul] at hn + simpa only [sm _ (by omega : 5 + (a * n + b) < heap), + sm _ (by omega : 5 + n * n + (a * n + b) < heap), + sm _ (by omega : 5 + n * n + n * n + (a * n + b) < heap)] using hmem a ha b hb + obtain ⟨y, S, he, ym, ys, hS, yf, yiff, yw, frame⟩ := exec_consume AB BC AC + (fun j => x.mem (q + 2 * j)) (fun j => x.mem (q + 2 * j + 1)) s v + ((sm 1 (by omega)).trans ((hprefix 1 (by omega)).trans hheaderD)) + ((sm 2 (by omega)).trans ((hprefix 2 (by omega)).trans hheaderG)) + ((sm 3 (by omega)).trans ((hprefix 3 (by omega)).trans hheaderN)) + ((snative _ (by decide)).trans hi) (snative _ (by decide)) sout hrec hread hfirst + hquery smem hmag hU (hfit.of_le (by + change heap + out + q + 4 * Q + 4 * D m + 2 * (n * D m) + + 9 * (n * n) + 4 * n + g + 2048 ≤ _ + dsimp only [out] + omega)) + change ExecWithin (w + 2) consume s y + (5 * Nat.sqrt (D m) + 8 * Q + + (42 * min (pieceSize (D m) g) (n - idx.2.2 * pieceSize (D m) g) + 28) * S + 52) at he + change S ≤ Nat.count (fun j => s.mem (out + j) ≠ 0 ∧ + ∀ c : Fin (min (pieceSize (D m) g) (n - idx.2.2 * pieceSize (D m) g)), + AB (x.mem (q + 2 * j)) (x.mem (q + 2 * j + 1)) + + BC (x.mem (q + 2 * j + 1)) (idx.2.2 * pieceSize (D m) g + c.val) + + AC (x.mem (q + 2 * j)) (idx.2.2 * pieceSize (D m) g + c.val) ≠ 0) Q + y.regs Reg.found at hS + simp_rw [show v 1 = idx.2.2 from rfl, show v 3 = Q from rfl] at yiff + have hexe : ExecWithin (w + 2) program x y + (RecordProduct.timeBound (m := m) qs + 5 * Nat.sqrt (D m) + 8 * Q + + (42 * min (pieceSize (D m) g) (n - idx.2.2 * pieceSize (D m) g) + 28) * S + 52) := + (hs.seq he).mono (by omega) + refine ⟨y, S, hexe, ys, yf, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · simpa only [ym, frame (productBank RecordProduct.Reg.result) (by decide), sout] using hS + · simpa only [ym, frame (productBank RecordProduct.Reg.result) (by decide), sout] using yiff + · intro j + have hv := rvalues j + rw [frame _ (by decide), sout, ym] + change r.mem (out + j.val) = _ + simpa only [out, hlen, Nat.add_assoc] using hv + · intro hy + obtain ⟨j, hj, -, hwa, hwb, hlo, hhi, hz⟩ := yw hy + obtain ⟨ha, hb⟩ := hcoords j hj + exact ⟨by simpa only [hwa] using ha, by simpa only [hwb] using hb, + hlo, hhi, by simpa only [hwa, hwb] using hz, j, hj, hwa, hwb⟩ + · intro u hu + rw [ym] + exact rmem u hu + · intro j hj + exact (frame _ (by change j.val < 320; omega)).trans (snative j hj) + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Program.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Program.lean index 5e760b606..83d4be514 100644 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Program.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Program.lean @@ -1,190 +1,190 @@ -/- -Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Anthropic, PBC, Erik Demaine --/ - -module --- upstream: candidate - -public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct -public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.QueryValues -public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Exec -public import Complexity.Algorithms.WordRAM.NatSqrt - -/-! -# Native consumption of one prepared record's product answers - -The existing 320-register producer/product bank stays in place. A disjoint controller reads -the actual record, derives the original piece interval with charged square-root/ceiling code, -and reads nonzero encoded product answers and their raw interleaved coordinates. Each accepted -pair invokes the real original-weight verifier. Once a witness is found, later scans are skipped. -All memory is read-only in this phase; no result, catalogue or window snapshot is needed. - -The consumer adapts the control of the pinned `Witnesses/ScanPairs.lean` procedure in the -proof of Theorem 17 at `anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. -The native ABI, code and charged constants are original. These stage definitions are internal -implementation interfaces. The outer instance loop and prime/global parameter choice are separate. --/ - -public section - -namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness - -open Cslib.WordRAM - -namespace Reg - -/-- Absolute product-answer pointer. -/ -abbrev output : Fin 400 := 320 -/-- Protected interleaved query-pair pointer. -/ -abbrev queries : Fin 400 := 321 -/-- Actual number of query occurrences. -/ -abbrev count : Fin 400 := 322 -/-- Original outer dimension. -/ -abbrev size : Fin 400 := 323 -/-- First original vertex of this piece. -/ -abbrev first : Fin 400 := 324 -/-- Actual number of original vertices in this piece. -/ -abbrev length : Fin 400 := 325 -/-- Number of query occurrences already treated. -/ -abbrev index : Fin 400 := 326 -/-- Constant one. -/ -abbrev one : Fin 400 := 327 -/-- Actual memory-address temporary. -/ -abbrev address : Fin 400 := 328 -/-- Loaded canonical-signed product answer. -/ -abbrev answer : Fin 400 := 329 -/-- Actual piece ordinal read from the prepared record. -/ -abbrev piece : Fin 400 := 332 -/-- Computed maximum piece length. -/ -abbrev blockLength : Fin 400 := 333 -/-- Original positive piece divisor. -/ -abbrev divisor : Fin 400 := 334 -/-- Index/product temporary. -/ -abbrev temp : Fin 400 := 335 -/-- Raw unsigned success flag. -/ -abbrev found : Fin 400 := 392 -/-- Original first coordinate of a verified triangle. -/ -abbrev witnessA : Fin 400 := 393 -/-- Original second coordinate of a verified triangle. -/ -abbrev witnessB : Fin 400 := 394 -/-- Original third coordinate of a verified triangle. -/ -abbrev witnessC : Fin 400 := 395 -/-- Actual number of original-weight interval scans performed. -/ -abbrev scans : Fin 400 := 396 -/-- Constant zero, disjoint from both procedure banks. -/ -abbrev zero : Fin 400 := 399 - -end Reg - -/-- Preserve the complete existing producer/product register bank. -/ -@[expose] def productBank : Fin 320 → Fin 400 := Fin.castLE (by decide) - -/-- The entire thirty-two-register original-weight verifier moves together. -/ -@[expose] def scanBank (i : Fin 32) : Fin 400 := ⟨336 + i.val, by omega⟩ - -/-- The root computation temporarily reuses the first six verifier registers. -/ -@[expose] def sqrtBank (i : Fin 6) : Fin 400 := ⟨336 + i.val, by omega⟩ - -/-- Actual charged square root for the local piece-size decoder. -/ -@[expose] def squareRoot : Code .standard 400 := NatSqrt.program.rename sqrtBank - -open Reg in -/-- Read original scalar words and the selected record's piece/query fields. -/ -@[expose] def readRecord : Code .standard 400 := - let sqrtArg := sqrtBank NatSqrt.arg - let productResult := productBank RecordProduct.Reg.result - let ordinal := productBank RecordProduct.Reg.ordinal - let records := productBank (RecordProduct.producerBank Instances.output) - ram[ - one ← #1; zero ← #0; address ← #3; size ← *address; - address ← #1; temp ← *address; sqrtArg ← temp; - address ← #2; divisor ← *address; output ← productResult; - temp ← size * temp; temp ← temp + temp; piece ← #6; temp ← temp + piece; - address ← ordinal * temp; - address ← records + address; address ← address + one; piece ← *address; - address ← address + one; queries ← *address; address ← address + one; count ← *address; - found ← #0; witnessA ← #0; witnessB ← #0; witnessC ← #0; scans ← #0; index ← #0 ] - -open Reg in -/-- Compute the original interval and deliver the unchanged signed-array pointers once. -/ -@[expose] def finishHeader : Code .standard 400 := - let root := sqrtBank NatSqrt.root - let scanAB := scanBank WitnessScan.Reg.ab - let scanBC := scanBank WitnessScan.Reg.bc - let scanAC := scanBank WitnessScan.Reg.ac - let scanN := scanBank WitnessScan.Reg.size - let scanFirst := scanBank WitnessScan.Reg.first - let scanLength := scanBank WitnessScan.Reg.length - ram[ - blockLength ← root + divisor; blockLength ← blockLength - one; - blockLength ← blockLength / divisor; first ← piece * blockLength; length ← size - first; - if blockLength < length then [ length ← blockLength ] else [ length ← length ]; - scanAB ← #5; temp ← size * size; - scanBC ← scanAB + temp; - scanAC ← scanBC + temp; - scanN ← size; scanFirst ← first; - scanLength ← length ] - -/-- Complete actual local descriptor and interval decoding. -/ -@[expose] def header : Code .standard 400 := - .seq readRecord (.seq squareRoot finishHeader) - -/-- Actual original signed-weight scan, with the complete bank renamed together. -/ -@[expose] def scan : Code .standard 400 := WitnessScan.program.rename scanBank - -open Reg in -/-- Load the real original coordinate pair and charge this invocation in the native counter. -/ -@[expose] def deliverPair : Code .standard 400 := - let scanA := scanBank WitnessScan.Reg.a - let scanB := scanBank WitnessScan.Reg.b - ram[ - address ← index + index; address ← address + queries; - scanA ← *address; address ← address + one; - scanB ← *address; scans ← scans + one ] - -open Reg in -/-- A verifier success returns its original vertex together with the actual pair that was read. -/ -@[expose] def receiveWitness : Code .standard 400 := - let scanFound := scanBank WitnessScan.Reg.found - let scanA := scanBank WitnessScan.Reg.a - let scanB := scanBank WitnessScan.Reg.b - let scanC := scanBank WitnessScan.Reg.witness - ram[ - if scanFound = one then [ - found ← one; witnessA ← scanA; witnessB ← scanB; - witnessC ← scanC ] else [ found ← found ] ] - -/-- The accepted-candidate branch contains the real verifier, not a supplied witness routine. -/ -@[expose] def scanPair : Code .standard 400 := - .seq deliverPair (.seq scan receiveWitness) - -open Reg in -/-- Read a product answer only while searching, and skip all later scans after a success. -/ -@[expose] def step : Code .standard 400 := - .seq (.branch (.eq found zero) - (.seq (ram[ address ← output + index; answer ← *address ]) - (.branch (.eq answer zero) (.op (.copy answer answer)) scanPair)) - (.op (.copy answer answer))) (.op (.bin .add index index one)) - -/-- Consume one actual product-answer slice without modifying any memory. -/ -@[expose] def consume : Code .standard 400 := - .seq header (.loop (.lt Reg.index Reg.count) step) - -/-- Actual product calculation followed immediately by original-weight verification. -/ -@[expose] def program : Code .standard 400 := - .seq (RecordProduct.program.rename productBank) consume - -/-- The real producer occupies the existing low bank of the larger caller. -/ -@[expose] def produce : Code .standard 400 := RecordProduct.produce.rename productBank - -/-- Original initialized producer, actual first-record selection and actual product/verifier. -The selected ordinal is preserved by the real product, so no later reset is needed. -/ -@[expose] def entryProgram : Code .standard 400 := - .seq produce (.seq (.op (.const (productBank RecordProduct.Reg.ordinal) 0)) program) - -/-- The consumer is one fixed finite instruction graph and never reads the physical width. -/ -theorem consume_fixed : consume.size ≤ 512 ∧ ¬consume.ReadsWidth := by decide - -end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.QueryValues +public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Exec +public import Complexity.Algorithms.WordRAM.NatSqrt + +/-! +# Native consumption of one prepared record's product answers + +The existing 320-register producer/product bank stays in place. A disjoint controller reads +the actual record, derives the original piece interval with charged square-root/ceiling code, +and reads nonzero encoded product answers and their raw interleaved coordinates. Each accepted +pair invokes the real original-weight verifier. Once a witness is found, later scans are skipped. +All memory is read-only in this phase; no result, catalogue or window snapshot is needed. + +The consumer adapts the control of the pinned `ScanPairs.lean` procedure in the +proof of Theorem 17 at `anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. +The native ABI, code and charged constants are original. These stage definitions are internal +implementation interfaces. The outer instance loop and prime/global parameter choice are separate. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM + +namespace Reg + +/-- Absolute product-answer pointer. -/ +abbrev output : Fin 400 := 320 +/-- Protected interleaved query-pair pointer. -/ +abbrev queries : Fin 400 := 321 +/-- Actual number of query occurrences. -/ +abbrev count : Fin 400 := 322 +/-- Original outer dimension. -/ +abbrev size : Fin 400 := 323 +/-- First original vertex of this piece. -/ +abbrev first : Fin 400 := 324 +/-- Actual number of original vertices in this piece. -/ +abbrev length : Fin 400 := 325 +/-- Number of query occurrences already treated. -/ +abbrev index : Fin 400 := 326 +/-- Constant one. -/ +abbrev one : Fin 400 := 327 +/-- Actual memory-address temporary. -/ +abbrev address : Fin 400 := 328 +/-- Loaded canonical-signed product answer. -/ +abbrev answer : Fin 400 := 329 +/-- Actual piece ordinal read from the prepared record. -/ +abbrev piece : Fin 400 := 332 +/-- Computed maximum piece length. -/ +abbrev blockLength : Fin 400 := 333 +/-- Original positive piece divisor. -/ +abbrev divisor : Fin 400 := 334 +/-- Index/product temporary. -/ +abbrev temp : Fin 400 := 335 +/-- Raw unsigned success flag. -/ +abbrev found : Fin 400 := 392 +/-- Original first coordinate of a verified triangle. -/ +abbrev witnessA : Fin 400 := 393 +/-- Original second coordinate of a verified triangle. -/ +abbrev witnessB : Fin 400 := 394 +/-- Original third coordinate of a verified triangle. -/ +abbrev witnessC : Fin 400 := 395 +/-- Actual number of original-weight interval scans performed. -/ +abbrev scans : Fin 400 := 396 +/-- Constant zero, disjoint from both procedure banks. -/ +abbrev zero : Fin 400 := 399 + +end Reg + +/-- Preserve the complete existing producer/product register bank. -/ +@[expose] def productBank : Fin 320 → Fin 400 := Fin.castLE (by decide) + +/-- The entire thirty-two-register original-weight verifier moves together. -/ +@[expose] def scanBank (i : Fin 32) : Fin 400 := ⟨336 + i.val, by omega⟩ + +/-- The root computation temporarily reuses the first six verifier registers. -/ +@[expose] def sqrtBank (i : Fin 6) : Fin 400 := ⟨336 + i.val, by omega⟩ + +/-- Actual charged square root for the local piece-size decoder. -/ +@[expose] def squareRoot : Code .standard 400 := NatSqrt.program.rename sqrtBank + +open Reg in +/-- Read original scalar words and the selected record's piece/query fields. -/ +@[expose] def readRecord : Code .standard 400 := + let sqrtArg := sqrtBank NatSqrt.arg + let productResult := productBank RecordProduct.Reg.result + let ordinal := productBank RecordProduct.Reg.ordinal + let records := productBank (RecordProduct.producerBank Instances.output) + ram[ + one ← #1; zero ← #0; address ← #3; size ← *address; + address ← #1; temp ← *address; sqrtArg ← temp; + address ← #2; divisor ← *address; output ← productResult; + temp ← size * temp; temp ← temp + temp; piece ← #6; temp ← temp + piece; + address ← ordinal * temp; + address ← records + address; address ← address + one; piece ← *address; + address ← address + one; queries ← *address; address ← address + one; count ← *address; + found ← #0; witnessA ← #0; witnessB ← #0; witnessC ← #0; scans ← #0; index ← #0 ] + +open Reg in +/-- Compute the original interval and deliver the unchanged signed-array pointers once. -/ +@[expose] def finishHeader : Code .standard 400 := + let root := sqrtBank NatSqrt.root + let scanAB := scanBank WitnessScan.Reg.ab + let scanBC := scanBank WitnessScan.Reg.bc + let scanAC := scanBank WitnessScan.Reg.ac + let scanN := scanBank WitnessScan.Reg.size + let scanFirst := scanBank WitnessScan.Reg.first + let scanLength := scanBank WitnessScan.Reg.length + ram[ + blockLength ← root + divisor; blockLength ← blockLength - one; + blockLength ← blockLength / divisor; first ← piece * blockLength; length ← size - first; + if blockLength < length then [ length ← blockLength ] else [ length ← length ]; + scanAB ← #5; temp ← size * size; + scanBC ← scanAB + temp; + scanAC ← scanBC + temp; + scanN ← size; scanFirst ← first; + scanLength ← length ] + +/-- Complete actual local descriptor and interval decoding. -/ +@[expose] def header : Code .standard 400 := + .seq readRecord (.seq squareRoot finishHeader) + +/-- Actual original signed-weight scan, with the complete bank renamed together. -/ +@[expose] def scan : Code .standard 400 := WitnessScan.program.rename scanBank + +open Reg in +/-- Load the real original coordinate pair and charge this invocation in the native counter. -/ +@[expose] def deliverPair : Code .standard 400 := + let scanA := scanBank WitnessScan.Reg.a + let scanB := scanBank WitnessScan.Reg.b + ram[ + address ← index + index; address ← address + queries; + scanA ← *address; address ← address + one; + scanB ← *address; scans ← scans + one ] + +open Reg in +/-- A verifier success returns its original vertex together with the actual pair that was read. -/ +@[expose] def receiveWitness : Code .standard 400 := + let scanFound := scanBank WitnessScan.Reg.found + let scanA := scanBank WitnessScan.Reg.a + let scanB := scanBank WitnessScan.Reg.b + let scanC := scanBank WitnessScan.Reg.witness + ram[ + if scanFound = one then [ + found ← one; witnessA ← scanA; witnessB ← scanB; + witnessC ← scanC ] else [ found ← found ] ] + +/-- The accepted-candidate branch contains the real verifier, not a supplied witness routine. -/ +@[expose] def scanPair : Code .standard 400 := + .seq deliverPair (.seq scan receiveWitness) + +open Reg in +/-- Read a product answer only while searching, and skip all later scans after a success. -/ +@[expose] def step : Code .standard 400 := + .seq (.branch (.eq found zero) + (.seq (ram[ address ← output + index; answer ← *address ]) + (.branch (.eq answer zero) (.op (.copy answer answer)) scanPair)) + (.op (.copy answer answer))) (.op (.bin .add index index one)) + +/-- Consume one actual product-answer slice without modifying any memory. -/ +@[expose] def consume : Code .standard 400 := + .seq header (.loop (.lt Reg.index Reg.count) step) + +/-- Actual product calculation followed immediately by original-weight verification. -/ +@[expose] def program : Code .standard 400 := + .seq (RecordProduct.program.rename productBank) consume + +/-- The real producer occupies the existing low bank of the larger caller. -/ +@[expose] def produce : Code .standard 400 := RecordProduct.produce.rename productBank + +/-- Original initialized producer, actual first-record selection and actual product/verifier. +The selected ordinal is preserved by the real product, so no later reset is needed. -/ +@[expose] def entryProgram : Code .standard 400 := + .seq produce (.seq (.op (.const (productBank RecordProduct.Reg.ordinal) 0)) program) + +/-- The consumer is one fixed finite instruction graph and never reads the physical width. -/ +theorem consume_fixed : consume.size ≤ 512 ∧ ¬consume.ReadsWidth := by decide + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Queries.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Queries.lean index 5af87ea52..929916181 100644 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Queries.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Queries.lean @@ -1,235 +1,235 @@ -/- -Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Anthropic, PBC, Erik Demaine --/ - -module --- upstream: candidate - -public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Step -public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Trace - -/-! -# The actual short-circuit query loop - -The proof derives its scan trace from the original answer words and the verifier's integer -equation. Runtime execution receives neither trace nor witness function. Variable-cost loop -accounting charges a complete scan exactly when the native controller actually invokes it. -The finite scan bound follows the counting argument of pinned `Witnesses/ScanPairs.lean`; -the native code, ABI and instruction constants are original. --/ - -public section - -namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness - -open Cslib.WordRAM - -private theorem potential_step (active : ℕ → Prop) [DecidablePred active] - {i Q T : ℕ} (hi : i < Q) : - (if active i then T + 6 else 6) + 2 + - (8 * (Q - (i + 1)) + T * (Nat.count active Q - Nat.count active (i + 1))) ≤ - 8 * (Q - i) + T * (Nat.count active Q - Nat.count active i) := by - have hcount : Nat.count active (i + 1) = Nat.count active i + - (if active i then 1 else 0) := Nat.count_succ active i - have hle := Nat.count_monotone active (Nat.succ_le_of_lt hi) - change Nat.count active (i + 1) ≤ Nat.count active Q at hle - have hindex : Q - i = Q - (i + 1) + 1 := by omega - by_cases h : active i - · rw [ite_eq_left h] at hcount ⊢ - rw [hcount] at hle - have hd : Nat.count active Q - Nat.count active i = - (Nat.count active Q - Nat.count active (i + 1)) + 1 := by omega - rw [hindex, hd, Nat.mul_add, Nat.mul_add] - omega - · rw [ite_eq_right h] at hcount ⊢ - simp only [Nat.add_zero] at hcount - rw [hindex, hcount, Nat.mul_add, Nat.mul_one] - omega - -/-- Run the actual answer/query loop. Every query coordinate is read from the protected -interleaved block, and every success is verified against the original three integer arrays. -Only accepted pairs without an original witness contribute to the failure count. -/ -theorem exec_queries {w U n first length out queries Q : ℕ} - (AB BC AC : ℕ → ℕ → ℤ) (a b : ℕ → ℕ) (x : Data 400) - (hx : Fixed n first length out queries Q x) - (hi : x.regs Reg.index = 0) (hs : x.regs Reg.scans = 0) - (hf : x.regs Reg.found = 0) - (hquery : ∀ i < Q, x.mem (queries + 2 * i) = a i ∧ - x.mem (queries + 2 * i + 1) = b i ∧ a i < n ∧ b i < n) - (hc : first + length ≤ n) - (hmem : ∀ u < n, ∀ v < n, - x.mem (5 + (u * n + v)) = Signed.encode (AB u v) ∧ - x.mem (5 + n * n + (u * n + v)) = Signed.encode (BC u v) ∧ - x.mem (5 + n * n + n * n + (u * n + v)) = Signed.encode (AC u v)) - (hmag : ∀ u < n, ∀ v < n, - (AB u v).natAbs ≤ U ∧ (BC u v).natAbs ≤ U ∧ (AC u v).natAbs ≤ U) - (hU : GenericRAM.Signed.Fits w (2 * U : ℕ)) - (hfit : Fits (w + 2) - (out + queries + 4 * Q + 6 * (n * n) + n + first + length + 64)) : - ∃ y S, ExecWithin (w + 2) (.loop (.lt Reg.index Reg.count) step) x y - (8 * Q + (42 * length + 28) * S + 2) ∧ - Fixed n first length out queries Q y ∧ y.mem = x.mem ∧ - y.regs Reg.index = Q ∧ y.regs Reg.scans = S ∧ - S ≤ Nat.count (fun i => x.mem (out + i) ≠ 0 ∧ - ∀ c : Fin length, AB (a i) (b i) + BC (b i) (first + c.val) + - AC (a i) (first + c.val) ≠ 0) Q + y.regs Reg.found ∧ - (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ - (y.regs Reg.found = 1 ↔ ∃ i < Q, x.mem (out + i) ≠ 0 ∧ - ∃ c, first ≤ c ∧ c < first + length ∧ AB (a i) (b i) + BC (b i) c + AC (a i) c = 0) ∧ - (y.regs Reg.found = 1 → ∃ i < Q, - x.mem (out + i) ≠ 0 ∧ y.regs Reg.witnessA = a i ∧ y.regs Reg.witnessB = b i ∧ - first ≤ y.regs Reg.witnessC ∧ y.regs Reg.witnessC < first + length ∧ - AB (a i) (b i) + BC (b i) (y.regs Reg.witnessC) + - AC (a i) (y.regs Reg.witnessC) = 0) := by - let accept (i : ℕ) : Bool := decide (x.mem (out + i) ≠ 0) - let hit (i : ℕ) : Bool := decide (∃ c : Fin length, - AB (a i) (b i) + BC (b i) (first + c.val) + AC (a i) (first + c.val) = 0) - let active (i : ℕ) : Prop := (accept i && !foundBefore accept hit i) = true - have hit_iff (i : ℕ) : hit i = true ↔ ∃ c, first ≤ c ∧ c < first + length ∧ - AB (a i) (b i) + BC (b i) c + AC (a i) c = 0 := by - simp only [hit, decide_eq_true_eq] - constructor - · rintro ⟨c, hz⟩ - exact ⟨first + c.val, by omega, by have := c.isLt; omega, hz⟩ - · rintro ⟨c, hlo, hhi, hz⟩ - refine ⟨⟨c - first, by omega⟩, ?_⟩ - simpa only [Nat.add_sub_of_le hlo] using hz - let I (y : Data 400) : Prop := - Fixed n first length out queries Q y ∧ y.mem = x.mem ∧ y.regs Reg.index ≤ Q ∧ - y.regs Reg.found = (foundBefore accept hit (y.regs Reg.index)).toNat ∧ - y.regs Reg.scans = Nat.count active (y.regs Reg.index) ∧ - (y.regs Reg.found = 1 → ∃ i < y.regs Reg.index, - x.mem (out + i) ≠ 0 ∧ y.regs Reg.witnessA = a i ∧ y.regs Reg.witnessB = b i ∧ - first ≤ y.regs Reg.witnessC ∧ y.regs Reg.witnessC < first + length ∧ - AB (a i) (b i) + BC (b i) (y.regs Reg.witnessC) + - AC (a i) (y.regs Reg.witnessC) = 0) - let Φ (y : Data 400) := 8 * (Q - y.regs Reg.index) + - (42 * length + 28) * (Nat.count active Q - Nat.count active (y.regs Reg.index)) - have hstart : I x := by - refine ⟨hx, rfl, by omega, ?_, ?_, ?_⟩ - · simp only [hi, hf, foundBefore_zero, Bool.toNat_false] - · simp only [hi, hs, Nat.count_zero] - · intro h; omega - have body (y : Data 400) (hy : I y) (hcond : (Cond.lt Reg.index Reg.count).Holds y) : - ∃ z T, ExecWithin (w + 2) step y z T ∧ I z ∧ T + 2 + Φ z ≤ Φ y := by - obtain ⟨yFixed, ym, yi, yf, ys, yw⟩ := hy - let i := y.regs Reg.index - change y.regs Reg.found = (foundBefore accept hit i).toNat at yf - have hilt : i < Q := by simpa only [Cond.holds_lt, yFixed.count] using hcond - have hscans : y.regs Reg.scans ≤ Q := by - rw [ys] - exact (Nat.count_le active).trans yi - have hstepfit : Fits (w + 2) - (out + queries + 3 * i + y.regs Reg.scans + 6 * (n * n) + n + first + length + 40) := - hfit.of_le (by dsimp only [i]; omega) - by_cases hact : active i - · have hinfo : accept i = true ∧ foundBefore accept hit i = false := by - simpa only [active, Bool.and_eq_true, Bool.not_eq_true_eq_eq_false] using hact - have yzero : y.regs Reg.found = 0 := by rw [yf, hinfo.2]; rfl - have hanswer : y.mem (out + i) ≠ 0 := by - rw [ym] - exact of_decide_eq_true hinfo.1 - obtain ⟨haWord, hbWord, ha, hb⟩ := hquery i hilt - obtain ⟨z, he, zFixed, zm, zi, zs, zf, zh, zw⟩ := exec_step_scan (i := i) AB BC AC y yFixed - rfl rfl yzero hanswer (by simpa only [ym] using haWord) - (by simpa only [ym] using hbWord) ha hb hc (by simpa only [ym] using hmem) - hmag hU hstepfit - have zflag : z.regs Reg.found = (hit i).toNat := by - cases hh : hit i - · have hn : z.regs Reg.found ≠ 1 := by - intro h - have := (hit_iff i).mpr (zh.mp h) - simp only [hh, Bool.false_eq_true] at this - rcases zf with hz | hz - · simpa only [hh, Bool.toNat_false] using hz - · exact (hn hz).elim - · simpa only [hh, Bool.toNat_true] using zh.mpr ((hit_iff i).mp hh) - have zI : I z := by - refine ⟨zFixed, zm.trans ym, by omega, ?_, ?_, ?_⟩ - · rw [zi, foundBefore_succ, hinfo.1, hinfo.2] - simpa only [Bool.true_and, Bool.false_or] using zflag - · rw [zi, Nat.count_succ, ite_eq_left hact, ← ys] - exact zs - · intro hz - obtain ⟨za, zb, zlo, zhi, zeq⟩ := zw hz - exact ⟨i, by omega, by simpa only [ym] using hanswer, za, zb, zlo, zhi, zeq⟩ - refine ⟨z, 42 * length + 34, he, zI, ?_⟩ - have hp := potential_step active (T := 42 * length + 28) hilt - simpa only [ite_eq_left hact, Φ, zi, i] using hp - · have hskip : y.regs Reg.found ≠ 0 ∨ y.mem (out + i) = 0 := by - by_cases hz : y.regs Reg.found = 0 - · right - by_contra hn - apply hact - have ha : accept i = true := decide_eq_true (by simpa only [ym] using hn) - have hb : foundBefore accept hit i = false := by - have hz' : (foundBefore accept hit i).toNat = 0 := yf.symm.trans hz - cases h : foundBefore accept hit i - · rfl - · simp only [h, Bool.toNat_true, Nat.one_ne_zero] at hz' - simp only [active, ha, hb, Bool.not_false, Bool.true_and] - · exact Or.inl hz - obtain ⟨z, he, zFixed, zm, zi, frame⟩ := exec_step_skip (i := i) y yFixed rfl hskip - (hfit.of_le (by dsimp only [i]; omega)) - have zfound : z.regs Reg.found = y.regs Reg.found := - frame _ (by decide) (by decide) (by decide) - have zscans : z.regs Reg.scans = y.regs Reg.scans := - frame _ (by decide) (by decide) (by decide) - have hnext : foundBefore accept hit (i + 1) = foundBefore accept hit i := by - rw [foundBefore_succ] - cases ha : accept i - · simp only [Bool.false_and, Bool.or_false] - · cases hb : foundBefore accept hit i - · exact (hact (by simp [active, ha, hb])).elim - · simp only [Bool.true_or] - have zI : I z := by - refine ⟨zFixed, zm.trans ym, by omega, ?_, ?_, ?_⟩ - · rw [zi, hnext, zfound] - exact yf - · rw [zi, Nat.count_succ, ite_eq_right hact, Nat.add_zero, zscans] - exact ys - · intro hz - obtain ⟨j, hj, hm, hwa, hwb, hlo, hhi, hzsum⟩ := yw (zfound.symm.trans hz) - have za := frame Reg.witnessA (by decide) (by decide) (by decide) - have zb := frame Reg.witnessB (by decide) (by decide) (by decide) - have zc := frame Reg.witnessC (by decide) (by decide) (by decide) - exact ⟨j, by omega, hm, za.trans hwa, zb.trans hwb, - by simpa only [zc] using hlo, by simpa only [zc] using hhi, - by simpa only [zc] using hzsum⟩ - refine ⟨z, 6, he, zI, ?_⟩ - have hp := potential_step active (T := 42 * length + 28) hilt - simpa only [ite_eq_right hact, Φ, zi, i] using hp - obtain ⟨y, he, hIy, hend⟩ := ExecWithin.loop_potential I Φ body hstart - obtain ⟨yFixed, ym, yi, yf, ys, yw⟩ := hIy - have yindex : y.regs Reg.index = Q := by - have hh : ¬ y.regs Reg.index < Q := by - simpa only [Cond.holds_lt, yFixed.count] using hend - omega - have hcount := scan_count_le accept hit Q - have hfail : Nat.count (fun i => (accept i && !hit i) = true) Q = - Nat.count (fun i => x.mem (out + i) ≠ 0 ∧ - ∀ c : Fin length, AB (a i) (b i) + BC (b i) (first + c.val) + - AC (a i) (first + c.val) ≠ 0) Q := by - congr 1 - funext i - simp only [accept, hit, Bool.and_eq_true, decide_eq_true_eq, - Bool.not_eq_true_eq_eq_false, decide_eq_false_iff_not, not_exists] - refine ⟨y, Nat.count active Q, ?_, yFixed, ym, yindex, ?_, ?_, ?_, ?_, ?_⟩ - · simpa only [Φ, hi, Nat.sub_zero, Nat.count_zero, Nat.mul_zero, Nat.add_zero] using he - · simpa only [yindex] using ys - · rw [← hfail, yf, yindex] - exact hcount - · rw [yf, yindex] - cases foundBefore accept hit Q <;> simp - · rw [yf, yindex] - simp only [Bool.toNat_eq_one, foundBefore_eq_true, accept, decide_eq_true_eq] - constructor - · rintro ⟨i, hi, ha, hh⟩ - exact ⟨i, hi, ha, (hit_iff i).mp hh⟩ - · rintro ⟨i, hi, ha, hh⟩ - exact ⟨i, hi, ha, (hit_iff i).mpr hh⟩ - · simpa only [yindex] using yw - -end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Step +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Trace + +/-! +# The actual short-circuit query loop + +The proof derives its scan trace from the original answer words and the verifier's integer +equation. Runtime execution receives neither trace nor witness function. Variable-cost loop +accounting charges a complete scan exactly when the native controller actually invokes it. +The finite scan bound follows the counting argument of pinned `ScanPairs.lean`; +the native code, ABI and instruction constants are original. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM + +private theorem potential_step (active : ℕ → Prop) [DecidablePred active] + {i Q T : ℕ} (hi : i < Q) : + (if active i then T + 6 else 6) + 2 + + (8 * (Q - (i + 1)) + T * (Nat.count active Q - Nat.count active (i + 1))) ≤ + 8 * (Q - i) + T * (Nat.count active Q - Nat.count active i) := by + have hcount : Nat.count active (i + 1) = Nat.count active i + + (if active i then 1 else 0) := Nat.count_succ active i + have hle := Nat.count_monotone active (Nat.succ_le_of_lt hi) + change Nat.count active (i + 1) ≤ Nat.count active Q at hle + have hindex : Q - i = Q - (i + 1) + 1 := by omega + by_cases h : active i + · rw [ite_eq_left h] at hcount ⊢ + rw [hcount] at hle + have hd : Nat.count active Q - Nat.count active i = + (Nat.count active Q - Nat.count active (i + 1)) + 1 := by omega + rw [hindex, hd, Nat.mul_add, Nat.mul_add] + omega + · rw [ite_eq_right h] at hcount ⊢ + simp only [Nat.add_zero] at hcount + rw [hindex, hcount, Nat.mul_add, Nat.mul_one] + omega + +/-- Run the actual answer/query loop. Every query coordinate is read from the protected +interleaved block, and every success is verified against the original three integer arrays. +Only accepted pairs without an original witness contribute to the failure count. -/ +theorem exec_queries {w U n first length out queries Q : ℕ} + (AB BC AC : ℕ → ℕ → ℤ) (a b : ℕ → ℕ) (x : Data 400) + (hx : Fixed n first length out queries Q x) + (hi : x.regs Reg.index = 0) (hs : x.regs Reg.scans = 0) + (hf : x.regs Reg.found = 0) + (hquery : ∀ i < Q, x.mem (queries + 2 * i) = a i ∧ + x.mem (queries + 2 * i + 1) = b i ∧ a i < n ∧ b i < n) + (hc : first + length ≤ n) + (hmem : ∀ u < n, ∀ v < n, + x.mem (5 + (u * n + v)) = Signed.encode (AB u v) ∧ + x.mem (5 + n * n + (u * n + v)) = Signed.encode (BC u v) ∧ + x.mem (5 + n * n + n * n + (u * n + v)) = Signed.encode (AC u v)) + (hmag : ∀ u < n, ∀ v < n, + (AB u v).natAbs ≤ U ∧ (BC u v).natAbs ≤ U ∧ (AC u v).natAbs ≤ U) + (hU : GenericRAM.Signed.Fits w (2 * U : ℕ)) + (hfit : Fits (w + 2) + (out + queries + 4 * Q + 6 * (n * n) + n + first + length + 64)) : + ∃ y S, ExecWithin (w + 2) (.loop (.lt Reg.index Reg.count) step) x y + (8 * Q + (42 * length + 28) * S + 2) ∧ + Fixed n first length out queries Q y ∧ y.mem = x.mem ∧ + y.regs Reg.index = Q ∧ y.regs Reg.scans = S ∧ + S ≤ Nat.count (fun i => x.mem (out + i) ≠ 0 ∧ + ∀ c : Fin length, AB (a i) (b i) + BC (b i) (first + c.val) + + AC (a i) (first + c.val) ≠ 0) Q + y.regs Reg.found ∧ + (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ + (y.regs Reg.found = 1 ↔ ∃ i < Q, x.mem (out + i) ≠ 0 ∧ + ∃ c, first ≤ c ∧ c < first + length ∧ AB (a i) (b i) + BC (b i) c + AC (a i) c = 0) ∧ + (y.regs Reg.found = 1 → ∃ i < Q, + x.mem (out + i) ≠ 0 ∧ y.regs Reg.witnessA = a i ∧ y.regs Reg.witnessB = b i ∧ + first ≤ y.regs Reg.witnessC ∧ y.regs Reg.witnessC < first + length ∧ + AB (a i) (b i) + BC (b i) (y.regs Reg.witnessC) + + AC (a i) (y.regs Reg.witnessC) = 0) := by + let accept (i : ℕ) : Bool := decide (x.mem (out + i) ≠ 0) + let hit (i : ℕ) : Bool := decide (∃ c : Fin length, + AB (a i) (b i) + BC (b i) (first + c.val) + AC (a i) (first + c.val) = 0) + let active (i : ℕ) : Prop := (accept i && !foundBefore accept hit i) = true + have hit_iff (i : ℕ) : hit i = true ↔ ∃ c, first ≤ c ∧ c < first + length ∧ + AB (a i) (b i) + BC (b i) c + AC (a i) c = 0 := by + simp only [hit, decide_eq_true_eq] + constructor + · rintro ⟨c, hz⟩ + exact ⟨first + c.val, by omega, by have := c.isLt; omega, hz⟩ + · rintro ⟨c, hlo, hhi, hz⟩ + refine ⟨⟨c - first, by omega⟩, ?_⟩ + simpa only [Nat.add_sub_of_le hlo] using hz + let I (y : Data 400) : Prop := + Fixed n first length out queries Q y ∧ y.mem = x.mem ∧ y.regs Reg.index ≤ Q ∧ + y.regs Reg.found = (foundBefore accept hit (y.regs Reg.index)).toNat ∧ + y.regs Reg.scans = Nat.count active (y.regs Reg.index) ∧ + (y.regs Reg.found = 1 → ∃ i < y.regs Reg.index, + x.mem (out + i) ≠ 0 ∧ y.regs Reg.witnessA = a i ∧ y.regs Reg.witnessB = b i ∧ + first ≤ y.regs Reg.witnessC ∧ y.regs Reg.witnessC < first + length ∧ + AB (a i) (b i) + BC (b i) (y.regs Reg.witnessC) + + AC (a i) (y.regs Reg.witnessC) = 0) + let Φ (y : Data 400) := 8 * (Q - y.regs Reg.index) + + (42 * length + 28) * (Nat.count active Q - Nat.count active (y.regs Reg.index)) + have hstart : I x := by + refine ⟨hx, rfl, by omega, ?_, ?_, ?_⟩ + · simp only [hi, hf, foundBefore_zero, Bool.toNat_false] + · simp only [hi, hs, Nat.count_zero] + · intro h; omega + have body (y : Data 400) (hy : I y) (hcond : (Cond.lt Reg.index Reg.count).Holds y) : + ∃ z T, ExecWithin (w + 2) step y z T ∧ I z ∧ T + 2 + Φ z ≤ Φ y := by + obtain ⟨yFixed, ym, yi, yf, ys, yw⟩ := hy + let i := y.regs Reg.index + change y.regs Reg.found = (foundBefore accept hit i).toNat at yf + have hilt : i < Q := by simpa only [Cond.holds_lt, yFixed.count] using hcond + have hscans : y.regs Reg.scans ≤ Q := by + rw [ys] + exact (Nat.count_le active).trans yi + have hstepfit : Fits (w + 2) + (out + queries + 3 * i + y.regs Reg.scans + 6 * (n * n) + n + first + length + 40) := + hfit.of_le (by dsimp only [i]; omega) + by_cases hact : active i + · have hinfo : accept i = true ∧ foundBefore accept hit i = false := by + simpa only [active, Bool.and_eq_true, Bool.not_eq_true_eq_eq_false] using hact + have yzero : y.regs Reg.found = 0 := by rw [yf, hinfo.2]; rfl + have hanswer : y.mem (out + i) ≠ 0 := by + rw [ym] + exact of_decide_eq_true hinfo.1 + obtain ⟨haWord, hbWord, ha, hb⟩ := hquery i hilt + obtain ⟨z, he, zFixed, zm, zi, zs, zf, zh, zw⟩ := exec_step_scan (i := i) AB BC AC y yFixed + rfl rfl yzero hanswer (by simpa only [ym] using haWord) + (by simpa only [ym] using hbWord) ha hb hc (by simpa only [ym] using hmem) + hmag hU hstepfit + have zflag : z.regs Reg.found = (hit i).toNat := by + cases hh : hit i + · have hn : z.regs Reg.found ≠ 1 := by + intro h + have := (hit_iff i).mpr (zh.mp h) + simp only [hh, Bool.false_eq_true] at this + rcases zf with hz | hz + · simpa only [hh, Bool.toNat_false] using hz + · exact (hn hz).elim + · simpa only [hh, Bool.toNat_true] using zh.mpr ((hit_iff i).mp hh) + have zI : I z := by + refine ⟨zFixed, zm.trans ym, by omega, ?_, ?_, ?_⟩ + · rw [zi, foundBefore_succ, hinfo.1, hinfo.2] + simpa only [Bool.true_and, Bool.false_or] using zflag + · rw [zi, Nat.count_succ, ite_eq_left hact, ← ys] + exact zs + · intro hz + obtain ⟨za, zb, zlo, zhi, zeq⟩ := zw hz + exact ⟨i, by omega, by simpa only [ym] using hanswer, za, zb, zlo, zhi, zeq⟩ + refine ⟨z, 42 * length + 34, he, zI, ?_⟩ + have hp := potential_step active (T := 42 * length + 28) hilt + simpa only [ite_eq_left hact, Φ, zi, i] using hp + · have hskip : y.regs Reg.found ≠ 0 ∨ y.mem (out + i) = 0 := by + by_cases hz : y.regs Reg.found = 0 + · right + by_contra hn + apply hact + have ha : accept i = true := decide_eq_true (by simpa only [ym] using hn) + have hb : foundBefore accept hit i = false := by + have hz' : (foundBefore accept hit i).toNat = 0 := yf.symm.trans hz + cases h : foundBefore accept hit i + · rfl + · simp only [h, Bool.toNat_true, Nat.one_ne_zero] at hz' + simp only [active, ha, hb, Bool.not_false, Bool.true_and] + · exact Or.inl hz + obtain ⟨z, he, zFixed, zm, zi, frame⟩ := exec_step_skip (i := i) y yFixed rfl hskip + (hfit.of_le (by dsimp only [i]; omega)) + have zfound : z.regs Reg.found = y.regs Reg.found := + frame _ (by decide) (by decide) (by decide) + have zscans : z.regs Reg.scans = y.regs Reg.scans := + frame _ (by decide) (by decide) (by decide) + have hnext : foundBefore accept hit (i + 1) = foundBefore accept hit i := by + rw [foundBefore_succ] + cases ha : accept i + · simp only [Bool.false_and, Bool.or_false] + · cases hb : foundBefore accept hit i + · exact (hact (by simp [active, ha, hb])).elim + · simp only [Bool.true_or] + have zI : I z := by + refine ⟨zFixed, zm.trans ym, by omega, ?_, ?_, ?_⟩ + · rw [zi, hnext, zfound] + exact yf + · rw [zi, Nat.count_succ, ite_eq_right hact, Nat.add_zero, zscans] + exact ys + · intro hz + obtain ⟨j, hj, hm, hwa, hwb, hlo, hhi, hzsum⟩ := yw (zfound.symm.trans hz) + have za := frame Reg.witnessA (by decide) (by decide) (by decide) + have zb := frame Reg.witnessB (by decide) (by decide) (by decide) + have zc := frame Reg.witnessC (by decide) (by decide) (by decide) + exact ⟨j, by omega, hm, za.trans hwa, zb.trans hwb, + by simpa only [zc] using hlo, by simpa only [zc] using hhi, + by simpa only [zc] using hzsum⟩ + refine ⟨z, 6, he, zI, ?_⟩ + have hp := potential_step active (T := 42 * length + 28) hilt + simpa only [ite_eq_right hact, Φ, zi, i] using hp + obtain ⟨y, he, hIy, hend⟩ := ExecWithin.loop_potential I Φ body hstart + obtain ⟨yFixed, ym, yi, yf, ys, yw⟩ := hIy + have yindex : y.regs Reg.index = Q := by + have hh : ¬ y.regs Reg.index < Q := by + simpa only [Cond.holds_lt, yFixed.count] using hend + omega + have hcount := scan_count_le accept hit Q + have hfail : Nat.count (fun i => (accept i && !hit i) = true) Q = + Nat.count (fun i => x.mem (out + i) ≠ 0 ∧ + ∀ c : Fin length, AB (a i) (b i) + BC (b i) (first + c.val) + + AC (a i) (first + c.val) ≠ 0) Q := by + congr 1 + funext i + simp only [accept, hit, Bool.and_eq_true, decide_eq_true_eq, + Bool.not_eq_true_eq_eq_false, decide_eq_false_iff_not, not_exists] + refine ⟨y, Nat.count active Q, ?_, yFixed, ym, yindex, ?_, ?_, ?_, ?_, ?_⟩ + · simpa only [Φ, hi, Nat.sub_zero, Nat.count_zero, Nat.mul_zero, Nat.add_zero] using he + · simpa only [yindex] using ys + · rw [← hfail, yf, yindex] + exact hcount + · rw [yf, yindex] + cases foundBefore accept hit Q <;> simp + · rw [yf, yindex] + simp only [Bool.toNat_eq_one, foundBefore_eq_true, accept, decide_eq_true_eq] + constructor + · rintro ⟨i, hi, ha, hh⟩ + exact ⟨i, hi, ha, (hit_iff i).mp hh⟩ + · rintro ⟨i, hi, ha, hh⟩ + exact ⟨i, hi, ha, (hit_iff i).mpr hh⟩ + · simpa only [yindex] using yw + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/SourceBounds.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/SourceBounds.lean index ff3be4fa4..2ce7025ac 100644 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/SourceBounds.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/SourceBounds.lean @@ -1,151 +1,151 @@ -/- -Copyright (c) 2026 Erik Demaine. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Erik Demaine --/ - -module --- upstream: candidate - -public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Flat - -/-! -# Original-size width and prefix bounds for candidate recovery - -The complete initialized producer/product/verifier has one positive logarithmic physical-width -threshold for polynomial original scalars and weights. Its words and memory support at every -prefix have a polynomial bound independent of any larger physical width. These are consequences -of the actual minimum-width halting run and existing native width transport. --/ - -public section - -namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness - -open Cslib.WordRAM Cslib.Algorithms.ExactTriangle -open Cslib.Algorithms.MatrixMultiplication.Schonhage -open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage - -/-- Every admitted physical width can be chosen logarithmic in original size. The positive -constant is explicit for each original magnitude exponent; geometry is not weakened. -/ -theorem admissionWidth_le_log {n D g p A κ : ℕ} (hn : 1 ≤ n) - (hD : D ≤ n) (hg : g ≤ n) (hp : p ≤ n) (hA : A ≤ (n + 2) ^ κ) : - admissionWidth n D g p A ≤ (257 * κ + 5909) * Nat.clog 2 (n + 2) := by - let b := n + 2 - let E := 2 * κ + 512 - have hb : 2 ≤ b := by dsimp only [b]; omega - have hb1 : 1 ≤ b := le_trans (by decide : 1 ≤ (2 : ℕ)) hb - have hP : PreparationInput.wordBound n D g p A ≤ b ^ E := by - exact (PreparationInput.wordBound_le_pow hn hD hg hp hA).trans - (Nat.pow_le_pow_right hb1 (by dsimp only [E]; omega)) - have hfour : 2 * (n + n ^ 2 + 3) ≤ b ^ 4 := by - dsimp only [b] - nlinarith only [Nat.zero_le (n ^ 4), Nat.zero_le (n ^ 3), Nat.zero_le (n ^ 2), - Nat.zero_le n] - have hfour' : 2 * (n + n ^ 2 + 3) ≤ (b ^ 2) ^ 2 := hfour.trans_eq (Nat.pow_mul b 2 2) - have hM : CallableProduct.windowSize n (n ^ 2) ≤ b ^ E := by - calc - _ ≤ (2 * (n + n ^ 2 + 3)) ^ 128 := CallableProduct.windowSize_le n (n ^ 2) - _ ≤ ((b ^ 2) ^ 2) ^ 128 := Nat.pow_le_pow_left hfour' _ - _ = (b ^ 2) ^ 256 := (Nat.pow_mul (b ^ 2) 2 128).symm - _ ≤ (b ^ 2) ^ (κ + 256) := Nat.pow_le_pow_right (Nat.pow_pos hb1) - (Nat.le_add_left 256 κ) - _ = b ^ E := by - rw [← Nat.pow_mul] - exact congrArg (fun j : ℕ => b ^ j) - (by dsimp only [E]; omega : 2 * (κ + 256) = E) - have hn2 : n ^ 2 ≤ b ^ E := (Nat.pow_le_pow_left (by dsimp only [b]; omega : n ≤ b) 2).trans - (Nat.pow_le_pow_right hb1 (by dsimp only [E]; omega)) - have hnE : n ≤ b ^ E := (show n ≤ n ^ 2 by nlinarith only [hn]).trans hn2 - have hprod : n * D ≤ b ^ E := (Nat.mul_le_mul_left n hD).trans - (by simpa only [pow_two] using hn2) - have hAE : A ≤ b ^ E := hA.trans - (Nat.pow_le_pow_right hb1 (by dsimp only [E]; omega)) - have hconst : 4096 ≤ b ^ E := (show 4096 ≤ (2 : ℕ) ^ 12 by decide).trans - ((Nat.pow_le_pow_left hb 12).trans - (Nat.pow_le_pow_right hb1 (by dsimp only [E]; omega))) - have hsum : envelope n D g p A ≤ 576 * b ^ E := by - unfold envelope - have hgE := hg.trans hnE - have hDE := hD.trans hnE - omega - have henvelope : envelope n D g p A ≤ b ^ (2 * κ + 522) := by - calc - _ ≤ 576 * b ^ E := hsum - _ ≤ b ^ 10 * b ^ E := Nat.mul_le_mul_right _ - ((show 576 ≤ (2 : ℕ) ^ 10 by decide).trans (Nat.pow_le_pow_left hb 10)) - _ = b ^ (2 * κ + 522) := by - rw [← Nat.pow_add] - exact congrArg (fun j : ℕ => b ^ j) (by dsimp only [E]; omega : 10 + E = 2 * κ + 522) - have hbase := Nat.le_pow_clog (by decide : 1 < (2 : ℕ)) b - have hlogBound := henvelope.trans (Nat.pow_le_pow_left hbase (2 * κ + 522)) - rw [← Nat.pow_mul] at hlogBound - have hlog := Nat.clog_le_of_le_pow hlogBound - change Nat.clog 2 (envelope n D g p A) ≤ Nat.clog 2 b * (2 * κ + 522) at hlog - have hrecord := RecordProduct.admissionWidth_le_log hn hD hg hp hA - have hlog' : Nat.clog 2 (envelope n D g p A) ≤ (257 * κ + 5906) * Nat.clog 2 b := by - nlinarith only [hlog, Nat.zero_le (κ * Nat.clog 2 b)] - have hmax := max_le hrecord hlog' - have hpositive : 1 ≤ Nat.clog 2 b := Nat.clog_pos (by decide) (by omega : 1 < b) - unfold admissionWidth - dsimp only [b] at hmax hpositive - nlinarith only [hmax, hpositive] - -/-- Original-input prefix word/address envelope, independent of the caller's larger width. -/ -@[expose] def wordBound (n D g p A : ℕ) : ℕ := 2 ^ admissionWidth n D g p A - -/-- All prefix words and memory support have a fixed polynomial envelope for each κ. -/ -theorem wordBound_le_pow {n D g p A κ : ℕ} (hn : 1 ≤ n) - (hD : D ≤ n) (hg : g ≤ n) (hp : p ≤ n) (hA : A ≤ (n + 2) ^ κ) : - wordBound n D g p A ≤ (n + 2) ^ (514 * κ + 11818) := by - let b := n + 2 - have hb : 1 < b := by dsimp only [b]; omega - have hc := Nat.clog_pos (by decide : 1 < (2 : ℕ)) hb - have hp2 := Nat.pow_pred_clog_lt_self (by decide : 1 < (2 : ℕ)) hb - have he : Nat.clog 2 b = (Nat.clog 2 b).pred + 1 := (Nat.succ_pred_eq_of_pos hc).symm - have hbase : 2 ^ Nat.clog 2 b ≤ b ^ 2 := by - rw [he, pow_succ] - nlinarith only [hp2, show 2 ≤ b by omega] - have hw := admissionWidth_le_log hn hD hg hp hA - change 2 ^ admissionWidth n D g p A ≤ _ - calc - _ ≤ 2 ^ ((257 * κ + 5909) * Nat.clog 2 b) := Nat.pow_le_pow_right (by decide) hw - _ = (2 ^ Nat.clog 2 b) ^ (257 * κ + 5909) := by rw [← Nat.pow_mul, Nat.mul_comm] - _ ≤ (b ^ 2) ^ (257 * κ + 5909) := Nat.pow_le_pow_left hbase _ - _ = b ^ (514 * κ + 11818) := by - rw [← Nat.pow_mul] - exact congrArg (fun j : ℕ => b ^ j) (by omega : 2 * (257 * κ + 5909) = 514 * κ + 11818) - -/-- At every admitted wider width, every actual prefix has original-input polynomial words -and zero memory above the same original-input envelope. Dirty pre-normalization loads are -included because the entire actual native execution is compared with its minimum-width run. -/ -theorem bounded_reachable_original {W m n g p A k : ℕ} (values : List ℤ) - (hlen : values.length = 3 * (n * n)) (hD : 16 ≤ D m) - (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) (hN : D m ^ 18 ≤ n) - (hv : ∀ z ∈ values, z.natAbs ≤ A) (hW : admissionWidth n (D m) g p A ≤ W) - {s : State 400} (hr : Runs W (entryProgram.program Reg.found) - (State.init (PreparationInput.input n (D m) g p values)) s k) : - (∀ i, s.data.regs i < wordBound n (D m) g p A) ∧ - (∀ a, s.data.mem a < wordBound n (D m) g p A) ∧ - ∀ a, wordBound n (D m) g p A ≤ a → s.data.mem a = 0 := by - obtain ⟨hi, a, idx, ha, hout, hidx, hord, y, t, S, ht, hpref, he, -⟩ := - runs_input values hlen hD hg hp hN hv le_rfl - have hlow := Runs.prefix_at_smaller_width entryProgram_fixed.2 hW hr he - have hs := hlow.bounded (Data.Bounded.init hi) - refine ⟨hs.1, hs.2, ?_⟩ - intro a ha - rw [hlow.mem_above (Data.Bounded.init hi) ha] - cases a with - | zero => - change 2 ^ admissionWidth n (D m) g p A ≤ 0 at ha - have := Nat.two_pow_pos (admissionWidth n (D m) g p A) - omega - | succ a => - change (PreparationInput.input n (D m) g p values).getD a 0 = 0 - apply List.getD_eq_default - have hlen := hi.1 - dsimp only [Fits] at hlen - change 2 ^ admissionWidth n (D m) g p A ≤ a + 1 at ha - omega - -end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Flat + +/-! +# Original-size width and prefix bounds for candidate recovery + +The complete initialized producer/product/verifier has one positive logarithmic physical-width +threshold for polynomial original scalars and weights. Its words and memory support at every +prefix have a polynomial bound independent of any larger physical width. These are consequences +of the actual minimum-width halting run and existing native width transport. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +/-- Every admitted physical width can be chosen logarithmic in original size. The positive +constant is explicit for each original magnitude exponent; geometry is not weakened. -/ +theorem admissionWidth_le_log {n D g p A κ : ℕ} (hn : 1 ≤ n) + (hD : D ≤ n) (hg : g ≤ n) (hp : p ≤ n) (hA : A ≤ (n + 2) ^ κ) : + admissionWidth n D g p A ≤ (257 * κ + 5909) * Nat.clog 2 (n + 2) := by + let b := n + 2 + let E := 2 * κ + 512 + have hb : 2 ≤ b := by dsimp only [b]; omega + have hb1 : 1 ≤ b := le_trans (by decide : 1 ≤ (2 : ℕ)) hb + have hP : PreparationInput.wordBound n D g p A ≤ b ^ E := by + exact (PreparationInput.wordBound_le_pow hn hD hg hp hA).trans + (Nat.pow_le_pow_right hb1 (by dsimp only [E]; omega)) + have hfour : 2 * (n + n ^ 2 + 3) ≤ b ^ 4 := by + dsimp only [b] + nlinarith only [Nat.zero_le (n ^ 4), Nat.zero_le (n ^ 3), Nat.zero_le (n ^ 2), + Nat.zero_le n] + have hfour' : 2 * (n + n ^ 2 + 3) ≤ (b ^ 2) ^ 2 := hfour.trans_eq (Nat.pow_mul b 2 2) + have hM : CallableProduct.windowSize n (n ^ 2) ≤ b ^ E := by + calc + _ ≤ (2 * (n + n ^ 2 + 3)) ^ 128 := CallableProduct.windowSize_le n (n ^ 2) + _ ≤ ((b ^ 2) ^ 2) ^ 128 := Nat.pow_le_pow_left hfour' _ + _ = (b ^ 2) ^ 256 := (Nat.pow_mul (b ^ 2) 2 128).symm + _ ≤ (b ^ 2) ^ (κ + 256) := Nat.pow_le_pow_right (Nat.pow_pos hb1) + (Nat.le_add_left 256 κ) + _ = b ^ E := by + rw [← Nat.pow_mul] + exact congrArg (fun j : ℕ => b ^ j) + (by dsimp only [E]; omega : 2 * (κ + 256) = E) + have hn2 : n ^ 2 ≤ b ^ E := (Nat.pow_le_pow_left (by dsimp only [b]; omega : n ≤ b) 2).trans + (Nat.pow_le_pow_right hb1 (by dsimp only [E]; omega)) + have hnE : n ≤ b ^ E := (show n ≤ n ^ 2 by nlinarith only [hn]).trans hn2 + have hprod : n * D ≤ b ^ E := (Nat.mul_le_mul_left n hD).trans + (by simpa only [pow_two] using hn2) + have hAE : A ≤ b ^ E := hA.trans + (Nat.pow_le_pow_right hb1 (by dsimp only [E]; omega)) + have hconst : 4096 ≤ b ^ E := (show 4096 ≤ (2 : ℕ) ^ 12 by decide).trans + ((Nat.pow_le_pow_left hb 12).trans + (Nat.pow_le_pow_right hb1 (by dsimp only [E]; omega))) + have hsum : envelope n D g p A ≤ 576 * b ^ E := by + unfold envelope + have hgE := hg.trans hnE + have hDE := hD.trans hnE + omega + have henvelope : envelope n D g p A ≤ b ^ (2 * κ + 522) := by + calc + _ ≤ 576 * b ^ E := hsum + _ ≤ b ^ 10 * b ^ E := Nat.mul_le_mul_right _ + ((show 576 ≤ (2 : ℕ) ^ 10 by decide).trans (Nat.pow_le_pow_left hb 10)) + _ = b ^ (2 * κ + 522) := by + rw [← Nat.pow_add] + exact congrArg (fun j : ℕ => b ^ j) (by dsimp only [E]; omega : 10 + E = 2 * κ + 522) + have hbase := Nat.le_pow_clog (by decide : 1 < (2 : ℕ)) b + have hlogBound := henvelope.trans (Nat.pow_le_pow_left hbase (2 * κ + 522)) + rw [← Nat.pow_mul] at hlogBound + have hlog := Nat.clog_le_of_le_pow hlogBound + change Nat.clog 2 (envelope n D g p A) ≤ Nat.clog 2 b * (2 * κ + 522) at hlog + have hrecord := RecordProduct.admissionWidth_le_log hn hD hg hp hA + have hlog' : Nat.clog 2 (envelope n D g p A) ≤ (257 * κ + 5906) * Nat.clog 2 b := by + nlinarith only [hlog, Nat.zero_le (κ * Nat.clog 2 b)] + have hmax := max_le hrecord hlog' + have hpositive : 1 ≤ Nat.clog 2 b := Nat.clog_pos (by decide) (by omega : 1 < b) + unfold admissionWidth + dsimp only [b] at hmax hpositive + nlinarith only [hmax, hpositive] + +/-- Original-input prefix word/address envelope, independent of the caller's larger width. -/ +@[expose] def wordBound (n D g p A : ℕ) : ℕ := 2 ^ admissionWidth n D g p A + +/-- All prefix words and memory support have a fixed polynomial envelope for each κ. -/ +theorem wordBound_le_pow {n D g p A κ : ℕ} (hn : 1 ≤ n) + (hD : D ≤ n) (hg : g ≤ n) (hp : p ≤ n) (hA : A ≤ (n + 2) ^ κ) : + wordBound n D g p A ≤ (n + 2) ^ (514 * κ + 11818) := by + let b := n + 2 + have hb : 1 < b := by dsimp only [b]; omega + have hc := Nat.clog_pos (by decide : 1 < (2 : ℕ)) hb + have hp2 := Nat.pow_pred_clog_lt_self (by decide : 1 < (2 : ℕ)) hb + have he : Nat.clog 2 b = (Nat.clog 2 b).pred + 1 := (Nat.succ_pred_eq_of_pos hc).symm + have hbase : 2 ^ Nat.clog 2 b ≤ b ^ 2 := by + rw [he, pow_succ] + nlinarith only [hp2, show 2 ≤ b by omega] + have hw := admissionWidth_le_log hn hD hg hp hA + change 2 ^ admissionWidth n D g p A ≤ _ + calc + _ ≤ 2 ^ ((257 * κ + 5909) * Nat.clog 2 b) := Nat.pow_le_pow_right (by decide) hw + _ = (2 ^ Nat.clog 2 b) ^ (257 * κ + 5909) := by rw [← Nat.pow_mul, Nat.mul_comm] + _ ≤ (b ^ 2) ^ (257 * κ + 5909) := Nat.pow_le_pow_left hbase _ + _ = b ^ (514 * κ + 11818) := by + rw [← Nat.pow_mul] + exact congrArg (fun j : ℕ => b ^ j) (by omega : 2 * (257 * κ + 5909) = 514 * κ + 11818) + +/-- At every admitted wider width, every actual prefix has original-input polynomial words +and zero memory above the same original-input envelope. Dirty pre-normalization loads are +included because the entire actual native execution is compared with its minimum-width run. -/ +theorem bounded_reachable_original {W m n g p A k : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hD : 16 ≤ D m) + (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) (hN : D m ^ 18 ≤ n) + (hv : ∀ z ∈ values, z.natAbs ≤ A) (hW : admissionWidth n (D m) g p A ≤ W) + {s : State 400} (hr : Runs W (entryProgram.program Reg.found) + (State.init (PreparationInput.input n (D m) g p values)) s k) : + (∀ i, s.data.regs i < wordBound n (D m) g p A) ∧ + (∀ a, s.data.mem a < wordBound n (D m) g p A) ∧ + ∀ a, wordBound n (D m) g p A ≤ a → s.data.mem a = 0 := by + obtain ⟨hi, a, idx, ha, hout, hidx, hord, y, t, S, ht, hpref, he, -⟩ := + runs_input values hlen hD hg hp hN hv le_rfl + have hlow := Runs.prefix_at_smaller_width entryProgram_fixed.2 hW hr he + have hs := hlow.bounded (Data.Bounded.init hi) + refine ⟨hs.1, hs.2, ?_⟩ + intro a ha + rw [hlow.mem_above (Data.Bounded.init hi) ha] + cases a with + | zero => + change 2 ^ admissionWidth n (D m) g p A ≤ 0 at ha + have := Nat.two_pow_pos (admissionWidth n (D m) g p A) + omega + | succ a => + change (PreparationInput.input n (D m) g p values).getD a 0 = 0 + apply List.getD_eq_default + have hlen := hi.1 + dsimp only [Fits] at hlen + change 2 ^ admissionWidth n (D m) g p A ≤ a + 1 at ha + omega + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Trace.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Trace.lean index 61db4c42f..9af3ab238 100644 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Trace.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Trace.lean @@ -18,7 +18,7 @@ public import Mathlib.Data.List.Range oracle or an extra runtime computation: the execution invariant derives it from the actual loads and verifier calls. Scan and failure counts use Mathlib's `Nat.count` directly. -The argument follows the scan-accounting portion of pinned `Witnesses/ScanPairs.lean`, +The argument follows the scan-accounting portion of pinned `ScanPairs.lean`, `anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. This controller starts with a false flag; the source's optional already-successful incoming flag is not needed. -/ diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Program.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Program.lean index 070424f25..855fd10b9 100644 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Program.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/WitnessScan/Program.lean @@ -17,7 +17,7 @@ in the supplied interval using two actual signed-addition kernels, and returns a an original vertex witnessing a zero sum. It writes no memory and preserves all arguments. This is the inner verifier used after a nonzero product answer, not a product or ET oracle. -The interval scan follows the pinned `Witnesses/Scan.lean` procedure in the proof of Theorem 17 +The interval scan follows the pinned `Scan.lean` procedure in the proof of Theorem 17 at `anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. The native code, register layout and charged constants are rebuilt against the existing WordRAM APIs. The stage definitions are internal implementation interfaces. diff --git a/ROADMAP.md b/ROADMAP.md index 337638638..1eb96901d 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,708 +1,708 @@ -# Roadmap - -The directions Complexitylib aims to cover, grouped by area. This file lists directions only: -status, progress and completion notes live on the linked issues and pull requests, module -docstrings describe what is proved, and the [README](README.md) and the curated formal abstracts -present completed results. A PR edits this file when it opens or closes a direction. - -A direction is complete only when its machine, resource and quantitative conclusions are proved -for the actual algorithms, with explicit models, encodings and assumptions. An abstract algorithm, -a class definition or an assumed reconstruction theorem does not complete it. Search Mathlib, -CSLib and this library before starting, and check upstream work. - -The [pre-rewrite roadmap][archive] is the archive of past detail and design records, including -the FHE industrial context, the first-order design decisions and the repository's history. - -## Programs - -| Program | Issues | -| --- | --- | -| Frontier program: ambitious theorems toward the research frontier | [#897]; its tracks are linked below | -| Metacomplexity: time-bounded Kolmogorov complexity, partial MCSP, hardness magnification | [#798], audit [#884] | -| Plain computability: delete the tidy calculus, keeping a machine-level kernel | [#862], from [#456] | -| Consolidation: duplicates, accurate statements and docs, less churn | [#949], [#939] | -| Fully homomorphic encryption: follow-ups to the instantiated bootstrapping theorem, then modern schemes | [#1104] | -| Boneh–Shoup cryptography tracks; zero-knowledge architecture | [#640], [#643] | -| Crypto frontier tracks: Kilian's succinct arguments, chosen-ciphertext security, signatures from one-way functions | [#999], [#1000], [#1001] | -| OpenAI math TCS results: review Fortnow's rewrites and extract in-scope results | [#1002] | -| A Verso guide with a chapter per area | [#585] | -| Planar SAT and geometric hardness: orthogonal planar 3SAT-3 with explicit grid bounds, parsimonious compilers | [#1079] | -| Randomized Word RAM: broader expected-time rules and simulation bridges | [#1124] | -| Proof automation: predictable polynomial-time certificate search | [#1155] | - -### Landmark theorems, in priority order - -Priorities live in this list and in the tiers of [#897]. - -1. [Goldreich–Micali–Wigderson zero knowledge in full](#cryptographic-landmarks) -2. [Rompel's signature theorem](#cryptographic-landmarks) ([#1001]) -3. [Completeness of oblivious transfer](#cryptographic-landmarks) -4. [Håstad's optimal MAX-E3SAT hardness](#approximation-and-pcp) ([#886]) -5. [Williams's NEXP ⊄ ACC⁰](#circuits-and-algebraic-complexity) ([#889]) -6. [Valiant's VNP-completeness of the permanent](#circuits-and-algebraic-complexity) -7. [The Kabanets–Impagliazzo theorem](#circuits-and-algebraic-complexity) ([#891]) -8. [MIP = NEXP](#interactive-proofs) ([#892]) -9. [Regev's worst-case to average-case reduction](#cryptographic-landmarks) -10. [The Immerman–Vardi theorem](#descriptive-complexity-and-first-order-reductions) - -## Cryptography - -### Cryptographic landmarks - -- **Goldreich–Micali–Wigderson zero knowledge in full.** If one-way functions exist, every - language in NP has a computational zero-knowledge proof in Goldreich's sense (Def. 4.3.10): - for every `x ∈ L` and arbitrary auxiliary input `z`, against probabilistic polynomial-time - verifiers. This needs a bridge between the security premise and a clock independent of an - unbounded auxiliary word, witness-level Levin reductions, and sequential soundness - amplification to negligible error. State it under nonuniformly one-way functions, as - Goldreich does (*Foundations of Cryptography*, Vol. 1, Section 4.4), and derive the - nonuniform (advice-secure) PRG premise of the auxiliary-input theorem from that; uniform - one-wayness is not known to give such a generator. -- **Rompel's signature theorem** ([#1001]). One-way functions imply digital signatures that are - existentially unforgeable under adaptive chosen-message attacks, for arbitrary - polynomial-length messages. Authentication trees from one-time signatures and - collision-resistant hashing are an intermediate development step; for the theorem itself, - universal one-way hash functions replace the collision-resistance assumption. -- **Completeness of oblivious transfer.** In the ideal OT-hybrid model, every polynomial-time - multiparty functionality admits a protocol secure against static malicious corruptions, with - real/ideal simulation and abort, under the standard communication assumptions. -- **Regev's worst-case to average-case reduction.** For suitable `q` and Gaussian noise rate - `α` with `αq > 2√n`, an efficient search-LWE algorithm yields a quantum polynomial-time - algorithm for worst-case GapSVP and SIVP within `Õ(n/α)`. The noise distribution, modulus - restrictions and quantum reduction are part of the theorem. -- **Kilian's succinct arguments** ([#999]). If collision-resistant hash families exist, every - language in NP has an interactive argument with polylogarithmic communication, by committing - to a PCP with Merkle trees; Micali's CS proofs and the BCS transform give the random-oracle - versions. - -### Modern fully homomorphic encryption - -Gentry's bootstrapping is instantiated with GSW ([#486]; formal abstract `gsw-fhe`), including -uniform compactness for the leveled wrapper decryption circuits and level-indexed multi-hop -evaluation. Its remaining follow-up ([#1104]) is Gentry's exact concrete-security loss together -with the `poly(ℓ, d)` running-time clause of his Theorem 4. Then develop these in reviewable -milestones, reusing the finite sampling, gadget algebra, native certificates and game reductions -where their hypotheses match. The [pre-rewrite FHE section][archive-fhe] maps the schemes to -current libraries and workloads. - -1. **Ring arithmetic and RLWE.** Polynomial-ring operations over `(Z/qZ)[X]/(X^d + 1)`, RNS/CRT - conversions, rounding and modulus-switching error bounds, and negacyclic NTTs implementing - ring multiplication, with native certificates. Define the RLWE secret and error laws and - experiment separately; an ordinary-LWE reduction does not give them. -2. **FHEW/TFHE programmable bootstrapping** - ([Ducas–Micciancio](https://eprint.iacr.org/2014/816); - [Chillotti–Gama–Georgieva–Izabachène](https://eprint.iacr.org/2016/870)). Ring-GSW external - products, blind rotation, sample extraction and key switching; prove the bootstrap decrypts - to `f(m)` with its noise, failure, width and runtime bounds, including negacyclic lookup - restrictions and torus representations; floating-point FFT implementations need their own - rounding-error proof. Then the [smaller-key methods of Lee et al.](https://eprint.iacr.org/2022/198) - and batched variants, preserving their exact secret distributions and joint error bounds. -3. **Exact packed BFV/BGV** ([Brakerski–Gentry–Vaikuntanathan](https://eprint.iacr.org/2011/277); - [Fan–Vercauteren](https://eprint.iacr.org/2012/144)). Slot encoding, - multiplication, relinearization, rotations and level management, with leveled evaluation - before a separately proved refresh. Equality modulo the plaintext modulus differs from - ordinary integer equality, which needs a range bound. -4. **Approximate packed CKKS and its bootstrap** - ([Cheon–Kim–Kim–Song](https://eprint.iacr.org/2016/421)), with an explicit - approximate-correctness interface and a norm bound on decoded error covering rescaling, - rounding, key switching and rotations. -5. **Scheme switching and threshold variants**, with explicit range/precision contracts, - evaluation-key assumptions, corruption thresholds, interaction, robustness and - decryption-query access, distinguishing honest-but-curious from malicious security. - -For each scheme, prove chosen-plaintext security for the actual algorithms and public evaluation -keys; keep LWE/RLWE, circular/KDM assumptions and attack-cost estimates distinct; introduce a -decryption-failure game when correctness is not perfect; and require nonvacuous parameter -families and full-input native runtime. - -### Public-key, lattice and symmetric constructions ([#640]) - -- **Sigma protocols and Fiat–Shamir** ([#581]): computational Schnorr signatures under discrete - log against strict PPT forgers; eavesdropping security in the strict-PPT model, with the - statistical-distance term from sampling `ZMod q`. -- **Diffie–Hellman and ElGamal:** arbitrary-key-length standalone symmetric-cipher bridge for - general-cipher Hashed ElGamal (Boneh–Shoup Theorem 11.5; the general-cipher construction, - Game 0/1/2 hybrids, Equation `(11.14)` reductions, and normalized `n`-bit-key `DEMOneTimeINDCPAOn` - / `linearHashKDF` endpoints have landed) ([#1047]). -- **Chosen-ciphertext security** ([#1000]): authenticated encryption by encrypt-then-MAC and - Cramer–Shoup from DDH, with KEM–DEM hybrid encryption ([#881]) as the chosen-plaintext - starting point (Boneh–Shoup Exercise 11.9). -- **Lattices** ([#645]): Fiat–Shamir with aborts and signatures from SIS (Lyubashevsky 2009, - 2012); strict-PPT samplers for integer boxes; identification security under SIS; efficiency - certificates for the simulator and extractor; full interactive-view simulation; SIS for - general moduli; module SIS and module LWE toward Dilithium-style schemes, as theory. -- **General:** reusable adaptive reductions, public-key encryption, universal-instance zero - knowledge, hard-core predicates and hardness amplification, and connections to learning and - average-case hardness. - -### Zero-knowledge proof systems toward libzk ([#643]) - -In order: -- the `PublicCoin → IP` bridge, with a uniform word verifier and adversarial decoding; -- parallel and AND/OR composition, and round-by-round knowledge soundness; -- oracle statements, so a later phase can query an earlier phase's oracle message (virtual - output oracles, as in ArkLib), with knowledge soundness and zero knowledge in oracle worlds; -- efficiency-certified simulators and extractors, and auxiliary-input zero knowledge on the core; -- polynomial commitment and evaluation-query reductions, and the Ligero IOP; -- the BCS transform to non-interactive arguments in the random-oracle model; -- VOLE as an ideal functionality, and VOLE-in-the-head; -- libzk's compositional structure as a composition of these components, without ciphersuites. - -## Complexity classes and reductions - -### Coverage catalogue - -Build an auditable coverage index spanning the [Complexity Zoo](https://complexityzoo.net/Complexity_Zoo) -and the canonical problems and theorems that ECCC papers need, verified against primary sources. -Each entry records its source and aliases, precise model and conventions, Lean declarations, -missing prerequisites and level (definition, partial development, verified theorem). Track -containments, separations, closure properties, complete problems and reductions separately, and -cover decision, function, search, counting, promise, distributional and parameterized problems, -with uniformity, advice, oracle access, error gaps, resources and reduction types explicit. The -index links to canonical definitions rather than keeping a second set. - -### Structure, time and space - -- **PRIMES ∈ P** ([#893]): the polynomial-time AKS algorithm on binary inputs, on top of the - proved criterion and small-`r` bound. -- Time, space and exponential hierarchies; oracle, advice, unambiguous and promise classes; - completeness, padding, self-reducibility, relativization and oracle separations, preserving - each reduction's resource bound. -- **Time hierarchies** ([#1572]): native polynomial padding separates every pair of integer - degrees `1 ≤ a < b`. Remaining targets include the general deterministic `f log f` hierarchy - with its simulation bound ([#800]), noninteger polynomial degrees and finer logarithmic - gaps, and sharp nondeterministic and alternating time hierarchies with certified simulators. -- The space hierarchy for `log n ≤ g n`, which needs a universal simulation that does not hold - its input. -- **Space:** Savitch bounds for general space-constructible functions. -- **Randomized logspace** ([#459], [#474]): RL and BPL, `USTCON ∈ RL`, Nisan's generator, - `BPL ⊆ L²` and Saks–Zhou. -- **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. -- Alternating time and space classes and bounded-alternation characterizations of PH; sharp - bounds for sublinear space, distinct from polynomial-space simulation. - -### Reducibilities and the structure of complete sets - -- **Reducibilities** ([#1351]): logspace Turing reductions, in an oracle model whose query tape is - not charged to the space bound, and their transitivity; the rest of the inclusion hierarchy of - Ladner, Lynch and Selman, and its separations. -- **Sparse and tally sets:** SPARSE and TALLY; `P/poly = P^SPARSE` (Meyer), extending - `PPoly.of_sparse` in `Circuit/Boolean/Sparse.lean`; a sparse set that is NP-hard under many-one - reductions gives `P = NP` (Mahaney), as does a sparse coNP-hard set (Fortune) and a sparse NP-hard - set under bounded truth-table reductions (Ogihara and Watanabe). -- **Berman–Hartmanis:** p-isomorphism and paddability; paddable NP-complete sets are p-isomorphic, - so the standard NP-complete problems are all p-isomorphic to SAT; the isomorphism conjecture as a - hypothesis; every many-one complete set for EXP is complete under one-one, length-increasing - reductions (Berman); and the isomorphism theorem for sets complete under AC⁰ reductions (Agrawal, - Allender and Rudich). -- **Autoreducibility and mitoticity:** for nontrivial sets, `m`-autoreducible equals `m`-mitotic, so - every NP-complete set splits into two NP-complete sets (Glaßer, Pavan, Selman and Zhang); - separations of complexity classes by autoreducibility (Buhrman, Fortnow, van Melkebeek and - Torenvliet). -- **Uniform diagonalization** (Schöning), which generalizes Ladner's theorem. -- **Selectivity and downward self-reducibility:** downward self-reducible sets lie in PSPACE. - P-selective sets (Selman): a P-selective NP-complete set gives `P = NP`, P-selective sets lie in - `P/poly` (Ko), and self-reducible P-selective sets lie in P. -- **Low and high hierarchies in NP** (Schöning): sparse NP sets lie in `Low₂` (Ko and Schöning), and - so does graph isomorphism, via graph non-isomorphism in AM. -- **Immunity:** immune and bi-immune sets, P-bi-immune sets in EXP (Balcázar and Schöning), and - almost-everywhere hardness. - -### Counting and modular classes - -- The Another Solution Problem (ASP) framework: certify polynomial-time forward and inverse - witness maps. The parsimonious reductions to positive exact-one SAT and its planar - three-occurrence variants already preserve counts over occurring variables; the new local - replacements have constant-factor growth and preserve the earlier linear grid sides and - quadratic planar costs. See the [reduction catalogue](guide/ComplexityGuide/NPCompleteness.lean) - for the completed NP-completeness and NP-hardness endpoints. Exact count preservation alone - does not supply the ASP runtime certificates. -- Counting reductions and further oracle results preserving exact multiplicities; permanent - #P-completeness; modular and counting hierarchies; the oracle-algorithm form of Stockmeyer's - approximate counting. -- For composite moduli: the prime-power collapse `Mod_{p^e}P = Mod_pP` and union closure. - Complement closure for moduli that are not prime powers is open, and relativizing proofs - cannot give it (Barrington–Beigel–Rudich), so it is not a library direction. -- Target-relativized `LWPP^A`, `WPP^A` and `SBP^A` with `FP^A` targets; adaptive WPP closure and - UP-oracle containment. -- Native oracle counting machines: efficient encoded oracle checking, response-access costs and - oracle-machine characterizations, with resource bounds uniform over admissible oracles. - -### Interactive proofs - -- **MIP = NEXP** ([#892]): `NEXP ⊆ MIP₂` through the BFL multilinearity test and arithmetization - of exponential-time tableaux; an efficient GKR verifier under log-space uniformity. -- Private-to-public-coin transformations, `AM[k] = AM`, interactive-machine characterizations - of Arthur–Merlin classes, and statistical and perfect zero knowledge. - -### Descriptive complexity and first-order reductions - -Ordered first-order complexity follows Immerman: structures on `{0, …, n-1}` with `n ≥ 2`, FO -means FO(≤, BIT), and `≤_fo`, `≤_fop` and `≤_qfp` keep their standard meanings; the order-free -`FO.Reduces` and `FO.QFReduces` stay as a general layer. Numeric predicates are computed from the -universe size, not supplied as unverified input tables. In dependency order: - -- **Ordered completeness.** SAT, 3SAT, 3COL, independent set, clique and vertex cover - NP-complete under first-order or quantifier-free projections; REACH NL-complete under - first-order reductions. -- **Projections:** projection reducibility on finite structure classes and the standard ordered - machine-encoding bridge. The classical qfp convention excludes BIT (Immerman–Landau, - Definition 3.6), and arbitrary quantifier-free combinations of input relations are not - projections. -- **Circuit Value P-complete under first-order reductions**, via `REACHa → MCVP → CVP` (Immerman, - Theorem 3.26 and Exercise 3.28), with the alternating-logspace simulation of polynomial-time - machines and the translation to `encCircuitValue`. -- Numeric certificates for further restricted-domain families; canonical BIT on a restricted - domain need not be first-order definable. -- The ordered ESO-to-NP direction of Fagin's theorem. -- Order-free: SAT-to-3SAT, graph-property hardness, and bridges to the separately encoded - problems. -- **The Immerman–Vardi theorem:** on ordered finite structures, first-order logic with least - fixed points defines exactly the polynomial-time properties, with the machine side proved for - actual native algorithms in both directions. -- Further logical characterizations on ordered structures: FO(TC) = NL and FO(DTC) = L, - SO-Horn = P (Grädel), and DLOGTIME-uniform AC⁰ = FO(≤, BIT) (Barrington–Immerman–Straubing); - then order-invariant and fixed-point queries and uniform translations between syntax, - structures and algorithms. -- A witness-carrying reduction structure once a client needs one, for example size bounds for - Cook–Levin. - -### Uniform circuit families - -Define logspace and DLOGTIME uniformity with explicit gate-address, direct-connection and input -encodings; certify circuit generation, evaluation and the machine-to-circuit simulations, -preserving size, depth and construction resources; and prove FO with order and BIT equals -DLOGTIME-uniform AC⁰ under explicit conventions. - -### Search, parameterized and fine-grained complexity - -- **TFNP:** CLS, further local search, fixed-point and equilibrium problems. -- **Parameterized:** strongly uniform variants (computable `f`), further explicit polynomial - kernels, the W and A hierarchies, and parameterized lower bounds. -- **Exponential-time SAT** ([#894]): amplify Schöning's random walk, with a time bound, then - PPSZ. Further fine-grained reductions and consequences of ETH, SETH and other named hypotheses. - -### Approximation and PCP - -- **Håstad's optimal MAX-E3SAT hardness** ([#886]): for `0 < ε < 1/8`, NP-hardness of - distinguishing satisfiable E3-CNF formulas from those where every assignment satisfies at most - `7/8 + ε` of the clauses. This needs the reduction to MAX-E3LIN and, through the 4-clause XOR - gadget, to MAX-E3SAT; Raz–Holenstein parallel repetition for small-soundness Label Cover; and - the perfect-completeness reduction. -- The FGLSS clique-hardness reduction and its amplification, retaining the exact gap and - reduction runtime. -- Gap and approximation-preserving reductions, APX/PTAS/FPTAS, inapproximability consequences of - PCP, CSP classifications and relaxation gaps, with assumptions such as the Unique Games - Conjecture stated explicitly. The 2-to-2 games theorem waits for [#886]. -- **A practical PCP construction:** a reasonably runnable construction and its cost on complete - reductions; rotation certificates polynomial in the bit length of a vertex name; machine-level - error reduction and extractors from expander walks, with certified seed and evaluation costs. - -### Proof complexity - -Proof-space lower bounds, Frege, cutting planes, polynomial calculus and sum-of-squares, and -certified DPLL solver runtimes. - -## Circuits and algebraic complexity - -- **Williams's NEXP ⊄ ACC⁰ and Murray–Williams's NQP ⊄ ACC⁰** ([#889]): no polynomial-size, - constant-depth circuits over AND, OR, NOT and fixed-modulus counting gates for some language in - NEXP. -- Uniform and nonuniform NC, AC and TC, circuit-to-machine bridges, algorithm-to-lower-bound - arguments, and barriers such as natural proofs and algebrization. NC simulation of unrestricted - `TCⁱ` needs Muroga's threshold weight-bounding theorem. -- Formula lower bounds beyond Andreev's, following Jukna; Raz–Wigderson's monotone depth bound for - matching on general graphs. -- **An explicit frontier extractor:** port v1's sumset-extractor construction with an explicit - family in `Complexity.P encBits` that discharges the frontier lower bounds' hypotheses. It would - exceed the library's unconditional `3.5 N - o(N)` bound in P, so it needs an independent - correctness review of the frontier core. -- **Weighted threshold representations:** v1's explicit source-reduction family, universal scalar - circuit synthesis, entropy and asymptotic activity bounds, and geometric cutting. -- **Construction runtimes**, certified separately from gate counts: the mass-production - constructors, with constructive choices of information sets and line menus; the - iterated-function preprocessing; and the bit complexity of formula evaluation on the rational - hitting-list matrices. -- **Algebraic complexity** ([#891]): the rest of Limaye–Srinivasan–Tavenas. Proved: simple - circuits (product gates reading distinct wires) of constant product-depth computing iterated - matrix multiplication need superpolynomial size over any field, at `d = ⌊log₂ ⌊log₂ n⌋⌋`, from an - explicit bound with exponent `d ^ (1 / (4 ^ Δ - 1))`. Remaining: their semantic set-multilinear - model; growing `Δ` with exponent `d ^ exp(-O(Δ))`; the `√d` bound at `Δ = 1` (LST Lemma 13 and - Corollary 4); the full range of `d`; and the `0/1`-substitution remark. Also the - Kabanets–Impagliazzo theorem (a deterministic PIT algorithm implies NEXP ⊄ P/poly or the permanent - has no polynomial-size arithmetic circuits); Valiant's VNP-completeness of the permanent over - fields of characteristic other than two; formula and branching-program models, identity testing, - hitting sets, and tensor and rank methods, including partial-derivative measures (Nisan–Wigderson - depth-3 bounds and shifted partials) and the depth-reduction chasms at depth four (Agrawal–Vinay) - and depth three (Gupta–Kamath–Kayal–Saptharishi), with uniform construction costs certified - separately from gate counts. -- **Real-algebraic decision procedures:** port the Sturm–Tarski query evaluator, recursive - evaluator, implicit arithmetic, native space certificates and the ETR upper bound from - [lean-canny](https://github.com/SamuelSchlesinger/lean-canny). - -### Jukna coverage - -Formalize the established results, constructions and worthwhile exercises of -[Jukna's *Boolean Function Complexity* (2012)](https://web.vu.lt/mif/s.jukna/boolean/index.html), -with the author's [errata](https://web.vu.lt/mif/s.jukna/boolean/misprints-BFC.html), auditing -each statement against the numbered result. Organize modules by subject, not chapter. - -| Chapters | Next coverage work | -| --- | --- | -| 1–2: foundations and Boolean analysis | Counting, graph complexity, degree and approximation results; the partial-function circuit upper bounds that use Lemma 1.17. | -| 3–5: communication | Matrix, randomized and multiparty results. | -| 6–10: formulas and circuits | Remaining formula and monotone bounds; span programs and negation complexity. | -| 11–13: depth restrictions and general gates | Depth-three, threshold, entropy, wire and linear-operator bounds. | -| 14–17: trees and branching programs | Bounded-read and bounded-time lower bounds and the remaining tree results. | -| 18–19: proof complexity | Proof-space and cutting-plane lower bounds. | -| 20: barriers and further methods | The book's generator, natural-proof, fusion and indirect lower-bound results. | - -## Pseudorandomness, coding and Boolean analysis - -- **Derandomization from hardness:** the infinitely-often variant, and the EXP ⊄ P/poly endpoint - of Babai, Fortnow, Nisan and Wigderson. -- **Extractors** ([#895]) beyond Chor–Goldreich: two-source extractors for lower min-entropy, - where the `t`-wise independent parts of Chattopadhyay–Zuckerman's Theorem 4.1 are formalized with - exponent `O(d²)` in place of their `3 d + 6` (the almost `t`-wise independent parts are not), with - Tribes as a small instance (coalitions of fewer than `w` bits only), and their resilient functions - (Theorems 3 and 4) and extractor (Theorem 1) are not yet formalized; - condensers, limited independence, explicit expanders and space-bounded generators, with finite - guarantees separate from uniform construction and decoding costs. -- **Codes:** Guruswami–Sudan list decoding and local decoding, keeping interpolation, - list-size/agreement guarantees and decoding runtime as separate obligations; concrete - list-decodable codes for Nisan–Wigderson. -- **Boolean analysis:** hypercontractivity beyond `p ≤ 2 ≤ q` and its applications. - -## Communication, queries, learning and other models - -- **Lifting** ([#890]): applications of deterministic lifting, such as partition-number - separations, and randomized lifting. -- Multiparty protocols, discrepancy and information complexity, direct-sum and direct-product - results, and reductions to streaming, sketching and cell-probe lower bounds. -- **Streaming and sketching:** native time and space bounds for Count-Min, including hash-seed - storage and evaluation; AMS frequency moments, CountSketch, turnstile updates, and heavy-hitter - recovery; communication-to-streaming transfers with explicit state encodings and pass counts. - The first Count-Min milestone is scoped in [#1201](https://github.com/cslib-community/complexitylib/issues/1201). -- **Learning:** machine-certified running times for the LMN and Kushilevitz–Mansour learners; - distributional problems and reductions, samplability, average-to-worst-case connections, - PAC/agnostic/SQ learning, learning lower bounds, compression and refutation. -- **Property and distribution testing:** access models, one/two-sided and tolerant testing, - query and sample bounds, canonical testers and lower bounds. -- **Kolmogorov complexity and metacomplexity** ([#798], [#884]): the near-linear threshold regime - of Liu–Pass Theorem 3.1 (c), hardness for every polynomial `t(n) ≥ (1 + ε)n` rather than above a - polynomial threshold, which needs a rate-1 efficient conditional EP-PRG running in `n + O(n^c)` - multi-tape steps for a constant `c < 1`; prefix-free complexity, - resource-bounded MCSP variants and connections to learning, cryptography and lower bounds; the - circuitized anti-checker with an efficient generator; the subexponential-formula consequence - Theorem 17(ii) and asymptotic parameter instantiations of Theorem 17(iii) and Theorem 5 in - `MCSP/FormulaMagnification.lean`; and hardness results for `MINKT` and `MINcKT` (whose - definitions, conditional `K^t` measure, and `NP` memberships are in `Kolmogorov/MINKT.lean`, - `Kolmogorov/Conditional.lean`, and `Kolmogorov/MINcKT.lean`), as independently scoped - developments; the reverse - multi-tape-to-single-tape simulation and machine-backed Nisan–Wigderson decoding. -- **Quantum computation** ([#896]): BQP ⊆ PP; reusable - circuit, channel and measurement semantics with uniformity and approximation costs, reusing - suitable quantum foundations rather than incompatible models; BQP, QMA and related query, - communication and interactive classes, with amplification, containments and complete - problems. - -[#1459]: https://github.com/cslib-community/complexitylib/issues/1459 -[#1463]: https://github.com/cslib-community/complexitylib/issues/1463 -[#1481]: https://github.com/cslib-community/complexitylib/issues/1481 -[#1482]: https://github.com/cslib-community/complexitylib/issues/1482 - -## Machines, RAM and data structures - -- **Native distinct-position frontend** ([#1482]): initialized signed input produces exact - compact root descriptors, with derived logarithmic width and uniform - `Oκ((n+1) log²(n+2))` charged cost. This frontend is composed into the complete native - preprocessing and the conditional ordinary host described below. -- **Efficient universal simulation** ([#800]): realize the buffered Hennie–Stearns backend - with time `O_M(t log(t + 2))`, together with a source-space-sensitive storage bound. - The current interpreter has a stronger polynomial certificate and a streaming input lookup; - the buffered representation, its native cleanup routines and their amortized bounds remain. -- **Plain computability** ([#862]): complete the plain calculus ([#608]), delete the tidy - calculus, keeping only a machine-level kernel inside the Machines layer, and switch the - abstract bridges to the shared predicate. -- **Logspace automation** ([#1449]): `fun_prop` rules for `ComputableInLogSpace` (composition, - `ite`, word, arithmetic and list primitives), with the one-way transition from logspace to - polynomial time. -- **Certificate APIs and automation** ([#1155]): predictable search, with fixed heartbeat budgets - on regression goals, failures that name the subterm lacking a certificate, and cheap - syntax-directed composition for glue code. Infer encodings for ordinary functional expressions - through constructor, projection, lookup and composition rules; generalize list, bounded-table, - cache and nested-loop invariants where clients need them; keep separate time, workspace and - output-size bounds through composition; apply cumulative streaming contracts to captured - iteration and nested loops; certify PRG stretching with seed length, rounds and generator cost - kept separate; and develop general open-program composition. Use Cook–Levin and GGM as - regression clients. Improve configuration views, tape partitions and handoff identities only - where several clients benefit, keeping machine transitions out of global simp. -- **Native Chan–He components** ([#1446]): native parameter, collision-counting, prime-table, - collision-tested minimum-search and complete two-prime modulus programs have explicit local - fit/frame/cost contracts. The complete native distinct-position frontend ([#1482]) starts from - initialized signed input and produces exact compact root descriptors with derived logarithmic - width and uniform `Oκ((n+1) log²(n+2))` charged cost. The complete native one-node component - ([#1481]) computes all residues/histograms, three compact heavy arrays and exact packed output - in linear time. Native bounded recursive traversal ([#1512](https://github.com/cslib-community/complexitylib/issues/1512)) executes those components with an - explicit stack, exact preorder output, polynomial local fit/frame contracts and charged - ternary-tree bounds. Complete native preprocessing ([#1446]) composes every root from actual - initialized input, returns the exact finite packed instances and derives positive logarithmic - width, polynomial space/intermediates and an `Oκ(n^1.5 log¹³(n+2))` charged bound. -- **Conditional native ordinary host** ([#1559](https://github.com/cslib-community/complexitylib/issues/1559)): - connects actual preprocessing output to caller-safe Convolution calls, proves the existing - distinct-position verdict from `State.init`, derives logarithmic width and every-prefix - polynomial words/space, and charges concrete callee work at the actual packed size. Its finite - callee contract has a proved slow exhaustive native instance; no fast backend or final - subquadratic bound is supplied. The native fast backend, Exact Triangle solver and final - input/time/width/asymptotic composition, including exponent 1.9992, remain under #1394. -- **Mathlib TM2:** sublinear work-space comparisons and a linear-space forward simulation. -- **Generic RAM:** uniform arbitrary-program dispatch and general width-selection and input-fit - theorems; simulating operations by programs over smaller bases; structured programs and time - classes over arbitrary value domains with weighted or bit costs; simulations between instances; - the typed core proposed in [CSLib #1158](https://github.com/leanprover/cslib/pull/1158). -- **Native pruned-query evaluator** ([#1526](https://github.com/cslib-community/complexitylib/issues/1526)): - fixed 96-register code computes distinct root/child queries, stack traversal and signed - accumulators. Initialized flat runs preserve query order and duplicates, input and outside - frame. Setup, sparse clearing, control/index work and halt fit - `8192*(L+Q+1+(L+1)*|leaves(requested queries)|)` instructions, with widths derived from - original magnitude/address bounds. A callable pointer/epoch kernel permits directory reuse. - Query grouping, band/tile composition, sparse-product and Exact Triangle solving, and full - input/time/width/asymptotic composition remain under #1394. - -- **Shared native matrix encoders** ([#1493](https://github.com/cslib-community/complexitylib/issues/1493)): compute all `10^L` leaf values from `7^L` - signed inputs within `240*(L+1)*10^L` standard WordRAM instructions, plus the final halt, - with fixed code/registers and concrete initialized inputs, frames and explicit width bounds. -- **Native shared-band preparation** ([#1523](https://github.com/cslib-community/complexitylib/issues/1523)): computes deterministic subset tables and a valid - layout, prepares padded signed matrix bands, invokes both shared encoders for every band, and - restores the original initialized input. Fixed 96-register code has a charged bound, including - halt, of `4096*(L+1)*(1+N*4^m+2^L+2*numBands*10^L)+1`; at `L=19m`, `N≥(4^m)^18`, it is at most - `81920*(L+1)*N²/(4^m)^(1/18)`. -- **Native wanted-query routing** - ([#1561](https://github.com/cslib-community/complexitylib/issues/1561)): computes the - selected-subset table, tile keys, query codes, counts, prefix offsets and stable scatter from - original raw coordinates. Its initialized run preserves every original occurrence and gives - each tile exactly its finite `wantedStrings` code set, within - `128*(L+1)*(Q+numBands²+2^L+1)` instructions including halt. It uses fixed 96-register code and - an explicit positive sufficient-width threshold. Joining these groups to shared band encoders, - invoking the pruned kernel and reconstructing queried products remain separate native work. -- **Native queried-product composition** - ([#1581](https://github.com/cslib-community/complexitylib/issues/1581)): joins the actual router - and band producers to the pointer/epoch kernel, restores the original input, and reconstructs - every requested signed product in original order. It charges raw-entry conversion, all copies, - dispatch, one shared directory initialization, epochs and reporting. At `L=19m`, `D=4^m`, - `D^18≤N` and occurrence sparsity `Q*2^m≤N²`, its initialized fixed 160-register program uses at - most `4325376*(19m+1)*N²/D^(1/18)` instructions including halt, with sufficient width - `128*clog₂(N+Q+A+2)` for entry bound `A`. The distinct-set sparsity theorem instead retains an - explicit additive charge for all `Q` occurrences. Native sparse-product and Exact Triangle - solving, full input/time/width/asymptotic composition, and the improved Section 4 route to - exponent 1.9992 remain under [#1394]. -- **Word RAM:** an AC⁰ basis; wider-fragment and Turing-machine simulations; - transfer of Anthropic's modular machine and 3SUM/APSP algorithms; direct 3SUM adaptation ([#1394]) - has the Convolution-3SUM/Exact Triangle correctness reduction, a charged native single-instance - matrix builder and the complete finite mathematical Chan–He reduction ([#1435]), including - ordinary distinct-position inputs and explicit size bounds. The finite lopsided matrix/counting - interface ([#1438]) adds padding, detection and signed digit reconstruction. Modular counting - and the selected-prime false-positive bound ([#1442]), finite residue/chunk/piece splitting and - witness recovery ([#1447]) are proved mathematically. The tensor identity, full recursion, - shared encodings and pruned-evaluation core ([#1459](https://github.com/cslib-community/complexitylib/issues/1459)) include exact visited-leaf and passed-set - bounds, with the empty zero-depth root handled explicitly. The finite thin-product component - ([#1463](https://github.com/cslib-community/complexitylib/issues/1463)) proves valid layouts, arbitrary-size padding/tiling, exact queried signed products and - sparse encoding/leaf/passed-set totals. These counts are not native instruction costs. Complete - native preprocessing and the conditional ordinary host with an exhaustive reference callee - are available. Native pruned/sparse-query backend composition, a concrete fast Exact Triangle solver - and the unconditional fast input/time/width/asymptotic bound remain, including the improved - Section 4 route to exponent 1.9992; - size-based width - selection with an input-fit guarantee and explicit overhead; expected-time rules for unbounded - almost-surely terminating structured loops; random-tape equivalence and BPP/RP/ZPP simulation - bridges for the randomized word RAM ([#1124]); transfer of cell-probe lower bounds; and the - Horn-SAT, 2SAT, matching and domino-tiling clients. -- **Caller-safe queried thin products** ([#1618](https://github.com/cslib-community/complexitylib/issues/1618)): - actual memory operations are rebased above a runtime caller base, using a computed source - modulus and charged load normalization. The initialized 163-register program restores local - input, returns an absolute answer pointer and preserves the caller outside a polynomial window. - Setup/return adds at most `2048*(N*D+Q+1)` to three times the original product charge. At - `D=4^m`, `L=19m`, `D^18≤N` and `Q*2^m≤N²`, the full charge including halt is at most - `17301504*(19m+1)*N²/D^(1/18)`. Distinct-set sparsity retains an additive occurrence charge. - Every width at least `256*clog₂(base+N+Q+P+5)` suffices for initialized caller-word bound `P`. - The reserved window is at most `(2*(N+Q+3))^128` and is not bulk-initialized. -- **Prepared ET-record product calls** ([#1626](https://github.com/cslib-community/complexitylib/issues/1626)): - the native adapter reads the computed six-word record, recovers the depth with actual binary - logarithm code, and copies raw matrices and query occurrences above the producer's heap. - It invokes the complete callable product in a separate register bank and preserves the - catalogue, sources and producer registers. The fixed 320-register initialized client runs - the producer and consumes its first record; the caller interface supports every occupied - ordinal. Original polynomial headers/weights give positive logarithmic admission and - polynomial words/address support at every wider width. Adaptation costs `O(nD+Q)` beyond - the actual callee; actual chunks derive `Q*2^m≤n²`, giving the per-record halt-inclusive bound - `34603008*(19m+1)*n²/D^(1/18)` at `D=4^m`, `L=19m`, `D^18≤n`. - The outer instance loop, aggregate subcubic ET bound, backend/witness reconstruction and - the full [#1394] ordinary-3SUM 1.9992 endpoint remain open. -- **Per-record native witness recovery** ([#1639](https://github.com/cslib-community/complexitylib/issues/1639)): - actual initialized producer/product calls now feed real answer/query loads and original signed - weight verification. The fixed 400-register program returns original-coordinate witnesses and - decides zeros in the selected existing chunk/piece. Its actual scan counter satisfies - `S≤falsePositiveCount+returnedFlag`, so unsuccessful records have no successful-scan surplus. - Root decoding, all control and original-weight scans are charged; positive logarithmic width - and polynomial words/support hold at every wider admitted width. The outer instance loop, - aggregate subcubic ET analysis and ordinary-3SUM 1.9992 composition remain open under [#1394]. -- **Conditional Convolution-3SUM host** ([#1524]): the charged native host initializes from the - ordinary input and runs every generated instance, conditional on a concrete solver's execution, - correctness and frame theorem. This completes the conditional host milestone. Integrating the - concrete fast Exact Triangle solver and the full input/time/width/asymptotic reduction remains - open under [#1394]. -- **Stateful algorithms and data structures:** Word-RAM controllers for larger adaptive and - iterative clients, including the two-stack queue and timeline-editing controllers; reusable - allocation and deallocation, shrinking, and live and peak space accounting; ownership and - recursive procedures; model realizations for cell probes, caches and randomness; present and - historical queries for the retroactive interface; deletion in the shared ordered-set interface, - balancing, and heap or RAM representations of search trees; uniform preprocessing and dynamic - updates for fusion trees; deletion and predecessor for van Emde Boas sets; and deallocation, - shrinking and compiled client controllers for the doubling array. -- **A typed-call evaluator** for fixed-program Word-RAM clients, returning the typed observation, - final state and halt-inclusive instruction cost, so the prefix-sum and stack clients no longer - assemble preparation, evaluation and halt accounting by hand. -- **Priority-queue and shortest-path consolidation** ([#1363]): lift operation-count helpers - (`numQueries`, `numInserts`, `numDecreaseKeys`, `numExtractMins`) to `IndexedMinPQ`, parameterize - flat-program operation-sequence execution (`RunsOps`) over `PQCode` in `PriorityQueue/Basic.lean` - so `BinaryHeap` and `FibonacciHeap` share a generic `run_ops_exact` theorem, and factor a common - `ShortestPathSpec p bound` predicate in `Dijkstra/HeapStatement.lean` across `LogLinearSpec`, - `FibonacciSpec`, and `FibonacciLinearSpec`. - -- **Native ExactTriangle preparation** ([#1532](https://github.com/cslib-community/complexitylib/issues/1532)): - initialized execution computes the complete catalogue and raw zero/one instance matrices, - with exact finite readback, logarithmic width, polynomial prefix words and address space, - and a `690n²Dg` charged bound in the source's admissible parameter regime. Prime selection, - solving the prepared instances, product/backend composition and the full #1394 endpoint remain. - -## Coverage and reuse - -The long-term bar is to support formalizing every paper in the -[Electronic Colloquium on Computational Complexity](https://eccc.weizmann.ac.il/colloquium/call_for_papers/), -with the additional work per paper increasingly its new mathematics: standard models, -representation changes, reductions, resource certificates and proof techniques come from the -library. Use a growing, source-pinned sample of papers, including some chosen after an API was -designed, as demanding clients, and promote recurring obligations into shared APIs only after -reuse is demonstrated. - -- **One sensitivity API.** Define the sensitivity measures over any `Fintype` in CSLib and drop - the separate measures of the sensitivity-separation development. -- **Weighted multigraph cuts and `PMF` event bounds** ([#1336]): extract `IsMultigraph` (renamed - e.g. `IsSymmLoopless` or phrased via `Matrix.IsSymm` with zero diagonal), `IsNonTrivialCut`, - `cutWeight`, `minCut`, `rowDeg`, `totalDoubledWeight`, `flattenMatrix`, and `IsContractionOf` from - `Complexity/Algorithms/WordRAM/Karger/Graph.lean` (shared by `KargerMinCut` and `KargerStein`) into - `Complexity/Foundations/Combinatorics/WeightedCut.lean` with a bridge lemma relating `cutWeight W C` - to `(Cslib.Multigraph.edgeCut ends C).card`, and move `toOuterMeasure_bind_ge_mul_ennreal`, - `toOuterMeasure_bind_le_mul_ennreal`, `toOuterMeasure_add_compl_ennreal`, - `toOuterMeasure_compl_le_one_sub`, and `two_branch_hit_prob_ge` into - `Complexity/Probability/PMF/Event.lean` (reusable by `toOuterMeasure_toReal_add_compl`). -- **CNF evaluation certificates:** `fun_prop` certificates for evaluating literals, clauses and - formulas under a polynomial-time assignment, so clients stop passing `CNF.eval`, - `Clause.eval` and `Literal.eval` to `fun_prop` as unfolding hints. -- Factor indicator-expectation identities from PCP's second-moment calculation, and recurring - cardinality and logarithm conversions, once a second client benefits. - -### OpenAI math results ([#1002]) - -Review Lance Fortnow's [expository rewrites](https://github.com/fortnow/AI-Papers) of the OpenAI -math papers and his [TCS catalogue](https://lance.fortnow.com/openai-math-tcs.htm) of the -openai/math release, and extract the results in scope: the headline theorems and also the minor -lemmas useful in their own right. Prove each against Complexitylib's own definitions, from the -cited manuscript's exact statement with its version pinned; a source formalization is a -reference, not evidence. Label unrefereed results as such. The openai/math formalizations are -Apache 2.0, as is Complexitylib, so adaptation with attribution is permitted. The practice is -still to read them for the mathematics and write proofs against Complexitylib's definitions; any -adapted code keeps its Apache 2.0 attribution. The rewrites state no license and are only read -and credited. - -- **Native declared-variable counting** ([#1226](https://github.com/cslib-community/complexitylib/issues/1226)): - `CountingNativeEndpoint.exists_declaredCounter` supplies one native counter for each fixed width, - exact on every valid declared-size/CNF input and zero otherwise. It has - `2^{O_k(interpolationScale N)} * (L + 1)^{O_k(1)}` time and independent polynomial workspace in the - original external encoded length `L`, with no supplier premises. The module derives - subexponential-time `k`-SAT, zero SAT exponents, `¬ETH`, and `¬SETH`. This is the Sections 3–6 and - Appendices A–B interpolation-scale bound, not the sharper Appendix D runtime. Integration, - validation, and the curated formal abstract are tracked in #1226. - -## Documentation and repository - -- **The guide** ([#585]): a chapter per area, including Foundations (bit strings, Fourier - analysis, PMF and `ProbComp`, codes), proof complexity, descriptive complexity, and Word RAM - and algorithms, reusing checked examples. -- **Curated formal abstracts:** improve readability, and compare each changed abstract with its - final source statements. -- **Archive `complexitylib-v1`** once its remaining work is dispositioned. - -## Deferred scopes - -These remain coverage targets but are deferred from the active queue; their issues were closed as -not planned, not as proved. - -- The full VNP-completeness reduction for the permanent ([#321]). -- General auxiliary-input zero knowledge ([#324]); see the GMW theorem above. -- Williams's square-root-space simulation ([#361]) and implicit Tree Evaluation ([#367]), now the - frontier track [#888]. -- Vollmer's generalized-quantifier survey ([#399]): ordered leaf words, explicit uniformity and - input-access models, finite-monoid classification and generalized logical quantifiers. - -[archive]: https://github.com/cslib-community/complexitylib/blob/c89dd48c/ROADMAP.md -[archive-fhe]: https://github.com/cslib-community/complexitylib/blob/c89dd48c/ROADMAP.md#modern-fhe-methods-and-deployment-priorities -[#321]: https://github.com/cslib-community/complexitylib/issues/321 -[#324]: https://github.com/cslib-community/complexitylib/issues/324 -[#361]: https://github.com/cslib-community/complexitylib/issues/361 -[#367]: https://github.com/cslib-community/complexitylib/issues/367 -[#399]: https://github.com/cslib-community/complexitylib/issues/399 -[#456]: https://github.com/cslib-community/complexitylib/issues/456 -[#459]: https://github.com/cslib-community/complexitylib/issues/459 -[#474]: https://github.com/cslib-community/complexitylib/issues/474 -[#486]: https://github.com/cslib-community/complexitylib/issues/486 -[#581]: https://github.com/cslib-community/complexitylib/issues/581 -[#585]: https://github.com/cslib-community/complexitylib/issues/585 -[#608]: https://github.com/cslib-community/complexitylib/issues/608 -[#640]: https://github.com/cslib-community/complexitylib/issues/640 -[#643]: https://github.com/cslib-community/complexitylib/issues/643 -[#645]: https://github.com/cslib-community/complexitylib/issues/645 -[#798]: https://github.com/cslib-community/complexitylib/issues/798 -[#800]: https://github.com/cslib-community/complexitylib/issues/800 -[#862]: https://github.com/cslib-community/complexitylib/issues/862 -[#881]: https://github.com/cslib-community/complexitylib/issues/881 -[#884]: https://github.com/cslib-community/complexitylib/issues/884 -[#886]: https://github.com/cslib-community/complexitylib/issues/886 -[#888]: https://github.com/cslib-community/complexitylib/issues/888 -[#889]: https://github.com/cslib-community/complexitylib/issues/889 -[#890]: https://github.com/cslib-community/complexitylib/issues/890 -[#891]: https://github.com/cslib-community/complexitylib/issues/891 -[#892]: https://github.com/cslib-community/complexitylib/issues/892 -[#893]: https://github.com/cslib-community/complexitylib/issues/893 -[#894]: https://github.com/cslib-community/complexitylib/issues/894 -[#895]: https://github.com/cslib-community/complexitylib/issues/895 -[#896]: https://github.com/cslib-community/complexitylib/issues/896 -[#897]: https://github.com/cslib-community/complexitylib/issues/897 -[#939]: https://github.com/cslib-community/complexitylib/issues/939 -[#949]: https://github.com/cslib-community/complexitylib/issues/949 -[#999]: https://github.com/cslib-community/complexitylib/issues/999 -[#1000]: https://github.com/cslib-community/complexitylib/issues/1000 -[#1001]: https://github.com/cslib-community/complexitylib/issues/1001 -[#1002]: https://github.com/cslib-community/complexitylib/issues/1002 -[#1047]: https://github.com/cslib-community/complexitylib/issues/1047 -[#1079]: https://github.com/cslib-community/complexitylib/issues/1079 -[#1104]: https://github.com/cslib-community/complexitylib/issues/1104 -[#1124]: https://github.com/cslib-community/complexitylib/issues/1124 -[#1155]: https://github.com/cslib-community/complexitylib/issues/1155 -[#1336]: https://github.com/cslib-community/complexitylib/issues/1336 -[#1351]: https://github.com/cslib-community/complexitylib/issues/1351 -[#1363]: https://github.com/cslib-community/complexitylib/issues/1363 -[#1394]: https://github.com/cslib-community/complexitylib/issues/1394 -[#1435]: https://github.com/cslib-community/complexitylib/issues/1435 -[#1438]: https://github.com/cslib-community/complexitylib/issues/1438 -[#1442]: https://github.com/cslib-community/complexitylib/issues/1442 -[#1446]: https://github.com/cslib-community/complexitylib/issues/1446 -[#1447]: https://github.com/cslib-community/complexitylib/issues/1447 -[#1449]: https://github.com/cslib-community/complexitylib/issues/1449 -[#1524]: https://github.com/cslib-community/complexitylib/issues/1524 -[#1572]: https://github.com/cslib-community/complexitylib/issues/1572 +# Roadmap + +The directions Complexitylib aims to cover, grouped by area. This file lists directions only: +status, progress and completion notes live on the linked issues and pull requests, module +docstrings describe what is proved, and the [README](README.md) and the curated formal abstracts +present completed results. A PR edits this file when it opens or closes a direction. + +A direction is complete only when its machine, resource and quantitative conclusions are proved +for the actual algorithms, with explicit models, encodings and assumptions. An abstract algorithm, +a class definition or an assumed reconstruction theorem does not complete it. Search Mathlib, +CSLib and this library before starting, and check upstream work. + +The [pre-rewrite roadmap][archive] is the archive of past detail and design records, including +the FHE industrial context, the first-order design decisions and the repository's history. + +## Programs + +| Program | Issues | +| --- | --- | +| Frontier program: ambitious theorems toward the research frontier | [#897]; its tracks are linked below | +| Metacomplexity: time-bounded Kolmogorov complexity, partial MCSP, hardness magnification | [#798], audit [#884] | +| Plain computability: delete the tidy calculus, keeping a machine-level kernel | [#862], from [#456] | +| Consolidation: duplicates, accurate statements and docs, less churn | [#949], [#939] | +| Fully homomorphic encryption: follow-ups to the instantiated bootstrapping theorem, then modern schemes | [#1104] | +| Boneh–Shoup cryptography tracks; zero-knowledge architecture | [#640], [#643] | +| Crypto frontier tracks: Kilian's succinct arguments, chosen-ciphertext security, signatures from one-way functions | [#999], [#1000], [#1001] | +| OpenAI math TCS results: review Fortnow's rewrites and extract in-scope results | [#1002] | +| A Verso guide with a chapter per area | [#585] | +| Planar SAT and geometric hardness: orthogonal planar 3SAT-3 with explicit grid bounds, parsimonious compilers | [#1079] | +| Randomized Word RAM: broader expected-time rules and simulation bridges | [#1124] | +| Proof automation: predictable polynomial-time certificate search | [#1155] | + +### Landmark theorems, in priority order + +Priorities live in this list and in the tiers of [#897]. + +1. [Goldreich–Micali–Wigderson zero knowledge in full](#cryptographic-landmarks) +2. [Rompel's signature theorem](#cryptographic-landmarks) ([#1001]) +3. [Completeness of oblivious transfer](#cryptographic-landmarks) +4. [Håstad's optimal MAX-E3SAT hardness](#approximation-and-pcp) ([#886]) +5. [Williams's NEXP ⊄ ACC⁰](#circuits-and-algebraic-complexity) ([#889]) +6. [Valiant's VNP-completeness of the permanent](#circuits-and-algebraic-complexity) +7. [The Kabanets–Impagliazzo theorem](#circuits-and-algebraic-complexity) ([#891]) +8. [MIP = NEXP](#interactive-proofs) ([#892]) +9. [Regev's worst-case to average-case reduction](#cryptographic-landmarks) +10. [The Immerman–Vardi theorem](#descriptive-complexity-and-first-order-reductions) + +## Cryptography + +### Cryptographic landmarks + +- **Goldreich–Micali–Wigderson zero knowledge in full.** If one-way functions exist, every + language in NP has a computational zero-knowledge proof in Goldreich's sense (Def. 4.3.10): + for every `x ∈ L` and arbitrary auxiliary input `z`, against probabilistic polynomial-time + verifiers. This needs a bridge between the security premise and a clock independent of an + unbounded auxiliary word, witness-level Levin reductions, and sequential soundness + amplification to negligible error. State it under nonuniformly one-way functions, as + Goldreich does (*Foundations of Cryptography*, Vol. 1, Section 4.4), and derive the + nonuniform (advice-secure) PRG premise of the auxiliary-input theorem from that; uniform + one-wayness is not known to give such a generator. +- **Rompel's signature theorem** ([#1001]). One-way functions imply digital signatures that are + existentially unforgeable under adaptive chosen-message attacks, for arbitrary + polynomial-length messages. Authentication trees from one-time signatures and + collision-resistant hashing are an intermediate development step; for the theorem itself, + universal one-way hash functions replace the collision-resistance assumption. +- **Completeness of oblivious transfer.** In the ideal OT-hybrid model, every polynomial-time + multiparty functionality admits a protocol secure against static malicious corruptions, with + real/ideal simulation and abort, under the standard communication assumptions. +- **Regev's worst-case to average-case reduction.** For suitable `q` and Gaussian noise rate + `α` with `αq > 2√n`, an efficient search-LWE algorithm yields a quantum polynomial-time + algorithm for worst-case GapSVP and SIVP within `Õ(n/α)`. The noise distribution, modulus + restrictions and quantum reduction are part of the theorem. +- **Kilian's succinct arguments** ([#999]). If collision-resistant hash families exist, every + language in NP has an interactive argument with polylogarithmic communication, by committing + to a PCP with Merkle trees; Micali's CS proofs and the BCS transform give the random-oracle + versions. + +### Modern fully homomorphic encryption + +Gentry's bootstrapping is instantiated with GSW ([#486]; formal abstract `gsw-fhe`), including +uniform compactness for the leveled wrapper decryption circuits and level-indexed multi-hop +evaluation. Its remaining follow-up ([#1104]) is Gentry's exact concrete-security loss together +with the `poly(ℓ, d)` running-time clause of his Theorem 4. Then develop these in reviewable +milestones, reusing the finite sampling, gadget algebra, native certificates and game reductions +where their hypotheses match. The [pre-rewrite FHE section][archive-fhe] maps the schemes to +current libraries and workloads. + +1. **Ring arithmetic and RLWE.** Polynomial-ring operations over `(Z/qZ)[X]/(X^d + 1)`, RNS/CRT + conversions, rounding and modulus-switching error bounds, and negacyclic NTTs implementing + ring multiplication, with native certificates. Define the RLWE secret and error laws and + experiment separately; an ordinary-LWE reduction does not give them. +2. **FHEW/TFHE programmable bootstrapping** + ([Ducas–Micciancio](https://eprint.iacr.org/2014/816); + [Chillotti–Gama–Georgieva–Izabachène](https://eprint.iacr.org/2016/870)). Ring-GSW external + products, blind rotation, sample extraction and key switching; prove the bootstrap decrypts + to `f(m)` with its noise, failure, width and runtime bounds, including negacyclic lookup + restrictions and torus representations; floating-point FFT implementations need their own + rounding-error proof. Then the [smaller-key methods of Lee et al.](https://eprint.iacr.org/2022/198) + and batched variants, preserving their exact secret distributions and joint error bounds. +3. **Exact packed BFV/BGV** ([Brakerski–Gentry–Vaikuntanathan](https://eprint.iacr.org/2011/277); + [Fan–Vercauteren](https://eprint.iacr.org/2012/144)). Slot encoding, + multiplication, relinearization, rotations and level management, with leveled evaluation + before a separately proved refresh. Equality modulo the plaintext modulus differs from + ordinary integer equality, which needs a range bound. +4. **Approximate packed CKKS and its bootstrap** + ([Cheon–Kim–Kim–Song](https://eprint.iacr.org/2016/421)), with an explicit + approximate-correctness interface and a norm bound on decoded error covering rescaling, + rounding, key switching and rotations. +5. **Scheme switching and threshold variants**, with explicit range/precision contracts, + evaluation-key assumptions, corruption thresholds, interaction, robustness and + decryption-query access, distinguishing honest-but-curious from malicious security. + +For each scheme, prove chosen-plaintext security for the actual algorithms and public evaluation +keys; keep LWE/RLWE, circular/KDM assumptions and attack-cost estimates distinct; introduce a +decryption-failure game when correctness is not perfect; and require nonvacuous parameter +families and full-input native runtime. + +### Public-key, lattice and symmetric constructions ([#640]) + +- **Sigma protocols and Fiat–Shamir** ([#581]): computational Schnorr signatures under discrete + log against strict PPT forgers; eavesdropping security in the strict-PPT model, with the + statistical-distance term from sampling `ZMod q`. +- **Diffie–Hellman and ElGamal:** arbitrary-key-length standalone symmetric-cipher bridge for + general-cipher Hashed ElGamal (Boneh–Shoup Theorem 11.5; the general-cipher construction, + Game 0/1/2 hybrids, Equation `(11.14)` reductions, and normalized `n`-bit-key `DEMOneTimeINDCPAOn` + / `linearHashKDF` endpoints have landed) ([#1047]). +- **Chosen-ciphertext security** ([#1000]): authenticated encryption by encrypt-then-MAC and + Cramer–Shoup from DDH, with KEM–DEM hybrid encryption ([#881]) as the chosen-plaintext + starting point (Boneh–Shoup Exercise 11.9). +- **Lattices** ([#645]): Fiat–Shamir with aborts and signatures from SIS (Lyubashevsky 2009, + 2012); strict-PPT samplers for integer boxes; identification security under SIS; efficiency + certificates for the simulator and extractor; full interactive-view simulation; SIS for + general moduli; module SIS and module LWE toward Dilithium-style schemes, as theory. +- **General:** reusable adaptive reductions, public-key encryption, universal-instance zero + knowledge, hard-core predicates and hardness amplification, and connections to learning and + average-case hardness. + +### Zero-knowledge proof systems toward libzk ([#643]) + +In order: +- the `PublicCoin → IP` bridge, with a uniform word verifier and adversarial decoding; +- parallel and AND/OR composition, and round-by-round knowledge soundness; +- oracle statements, so a later phase can query an earlier phase's oracle message (virtual + output oracles, as in ArkLib), with knowledge soundness and zero knowledge in oracle worlds; +- efficiency-certified simulators and extractors, and auxiliary-input zero knowledge on the core; +- polynomial commitment and evaluation-query reductions, and the Ligero IOP; +- the BCS transform to non-interactive arguments in the random-oracle model; +- VOLE as an ideal functionality, and VOLE-in-the-head; +- libzk's compositional structure as a composition of these components, without ciphersuites. + +## Complexity classes and reductions + +### Coverage catalogue + +Build an auditable coverage index spanning the [Complexity Zoo](https://complexityzoo.net/Complexity_Zoo) +and the canonical problems and theorems that ECCC papers need, verified against primary sources. +Each entry records its source and aliases, precise model and conventions, Lean declarations, +missing prerequisites and level (definition, partial development, verified theorem). Track +containments, separations, closure properties, complete problems and reductions separately, and +cover decision, function, search, counting, promise, distributional and parameterized problems, +with uniformity, advice, oracle access, error gaps, resources and reduction types explicit. The +index links to canonical definitions rather than keeping a second set. + +### Structure, time and space + +- **PRIMES ∈ P** ([#893]): the polynomial-time AKS algorithm on binary inputs, on top of the + proved criterion and small-`r` bound. +- Time, space and exponential hierarchies; oracle, advice, unambiguous and promise classes; + completeness, padding, self-reducibility, relativization and oracle separations, preserving + each reduction's resource bound. +- **Time hierarchies** ([#1572]): native polynomial padding separates every pair of integer + degrees `1 ≤ a < b`. Remaining targets include the general deterministic `f log f` hierarchy + with its simulation bound ([#800]), noninteger polynomial degrees and finer logarithmic + gaps, and sharp nondeterministic and alternating time hierarchies with certified simulators. +- The space hierarchy for `log n ≤ g n`, which needs a universal simulation that does not hold + its input. +- **Space:** Savitch bounds for general space-constructible functions. +- **Randomized logspace** ([#459], [#474]): RL and BPL, `USTCON ∈ RL`, Nisan's generator, + `BPL ⊆ L²` and Saks–Zhou. +- **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. +- Alternating time and space classes and bounded-alternation characterizations of PH; sharp + bounds for sublinear space, distinct from polynomial-space simulation. + +### Reducibilities and the structure of complete sets + +- **Reducibilities** ([#1351]): logspace Turing reductions, in an oracle model whose query tape is + not charged to the space bound, and their transitivity; the rest of the inclusion hierarchy of + Ladner, Lynch and Selman, and its separations. +- **Sparse and tally sets:** SPARSE and TALLY; `P/poly = P^SPARSE` (Meyer), extending + `PPoly.of_sparse` in `Circuit/Boolean/Sparse.lean`; a sparse set that is NP-hard under many-one + reductions gives `P = NP` (Mahaney), as does a sparse coNP-hard set (Fortune) and a sparse NP-hard + set under bounded truth-table reductions (Ogihara and Watanabe). +- **Berman–Hartmanis:** p-isomorphism and paddability; paddable NP-complete sets are p-isomorphic, + so the standard NP-complete problems are all p-isomorphic to SAT; the isomorphism conjecture as a + hypothesis; every many-one complete set for EXP is complete under one-one, length-increasing + reductions (Berman); and the isomorphism theorem for sets complete under AC⁰ reductions (Agrawal, + Allender and Rudich). +- **Autoreducibility and mitoticity:** for nontrivial sets, `m`-autoreducible equals `m`-mitotic, so + every NP-complete set splits into two NP-complete sets (Glaßer, Pavan, Selman and Zhang); + separations of complexity classes by autoreducibility (Buhrman, Fortnow, van Melkebeek and + Torenvliet). +- **Uniform diagonalization** (Schöning), which generalizes Ladner's theorem. +- **Selectivity and downward self-reducibility:** downward self-reducible sets lie in PSPACE. + P-selective sets (Selman): a P-selective NP-complete set gives `P = NP`, P-selective sets lie in + `P/poly` (Ko), and self-reducible P-selective sets lie in P. +- **Low and high hierarchies in NP** (Schöning): sparse NP sets lie in `Low₂` (Ko and Schöning), and + so does graph isomorphism, via graph non-isomorphism in AM. +- **Immunity:** immune and bi-immune sets, P-bi-immune sets in EXP (Balcázar and Schöning), and + almost-everywhere hardness. + +### Counting and modular classes + +- The Another Solution Problem (ASP) framework: certify polynomial-time forward and inverse + witness maps. The parsimonious reductions to positive exact-one SAT and its planar + three-occurrence variants already preserve counts over occurring variables; the new local + replacements have constant-factor growth and preserve the earlier linear grid sides and + quadratic planar costs. See the [reduction catalogue](guide/ComplexityGuide/NPCompleteness.lean) + for the completed NP-completeness and NP-hardness endpoints. Exact count preservation alone + does not supply the ASP runtime certificates. +- Counting reductions and further oracle results preserving exact multiplicities; permanent + #P-completeness; modular and counting hierarchies; the oracle-algorithm form of Stockmeyer's + approximate counting. +- For composite moduli: the prime-power collapse `Mod_{p^e}P = Mod_pP` and union closure. + Complement closure for moduli that are not prime powers is open, and relativizing proofs + cannot give it (Barrington–Beigel–Rudich), so it is not a library direction. +- Target-relativized `LWPP^A`, `WPP^A` and `SBP^A` with `FP^A` targets; adaptive WPP closure and + UP-oracle containment. +- Native oracle counting machines: efficient encoded oracle checking, response-access costs and + oracle-machine characterizations, with resource bounds uniform over admissible oracles. + +### Interactive proofs + +- **MIP = NEXP** ([#892]): `NEXP ⊆ MIP₂` through the BFL multilinearity test and arithmetization + of exponential-time tableaux; an efficient GKR verifier under log-space uniformity. +- Private-to-public-coin transformations, `AM[k] = AM`, interactive-machine characterizations + of Arthur–Merlin classes, and statistical and perfect zero knowledge. + +### Descriptive complexity and first-order reductions + +Ordered first-order complexity follows Immerman: structures on `{0, …, n-1}` with `n ≥ 2`, FO +means FO(≤, BIT), and `≤_fo`, `≤_fop` and `≤_qfp` keep their standard meanings; the order-free +`FO.Reduces` and `FO.QFReduces` stay as a general layer. Numeric predicates are computed from the +universe size, not supplied as unverified input tables. In dependency order: + +- **Ordered completeness.** SAT, 3SAT, 3COL, independent set, clique and vertex cover + NP-complete under first-order or quantifier-free projections; REACH NL-complete under + first-order reductions. +- **Projections:** projection reducibility on finite structure classes and the standard ordered + machine-encoding bridge. The classical qfp convention excludes BIT (Immerman–Landau, + Definition 3.6), and arbitrary quantifier-free combinations of input relations are not + projections. +- **Circuit Value P-complete under first-order reductions**, via `REACHa → MCVP → CVP` (Immerman, + Theorem 3.26 and Exercise 3.28), with the alternating-logspace simulation of polynomial-time + machines and the translation to `encCircuitValue`. +- Numeric certificates for further restricted-domain families; canonical BIT on a restricted + domain need not be first-order definable. +- The ordered ESO-to-NP direction of Fagin's theorem. +- Order-free: SAT-to-3SAT, graph-property hardness, and bridges to the separately encoded + problems. +- **The Immerman–Vardi theorem:** on ordered finite structures, first-order logic with least + fixed points defines exactly the polynomial-time properties, with the machine side proved for + actual native algorithms in both directions. +- Further logical characterizations on ordered structures: FO(TC) = NL and FO(DTC) = L, + SO-Horn = P (Grädel), and DLOGTIME-uniform AC⁰ = FO(≤, BIT) (Barrington–Immerman–Straubing); + then order-invariant and fixed-point queries and uniform translations between syntax, + structures and algorithms. +- A witness-carrying reduction structure once a client needs one, for example size bounds for + Cook–Levin. + +### Uniform circuit families + +Define logspace and DLOGTIME uniformity with explicit gate-address, direct-connection and input +encodings; certify circuit generation, evaluation and the machine-to-circuit simulations, +preserving size, depth and construction resources; and prove FO with order and BIT equals +DLOGTIME-uniform AC⁰ under explicit conventions. + +### Search, parameterized and fine-grained complexity + +- **TFNP:** CLS, further local search, fixed-point and equilibrium problems. +- **Parameterized:** strongly uniform variants (computable `f`), further explicit polynomial + kernels, the W and A hierarchies, and parameterized lower bounds. +- **Exponential-time SAT** ([#894]): amplify Schöning's random walk, with a time bound, then + PPSZ. Further fine-grained reductions and consequences of ETH, SETH and other named hypotheses. + +### Approximation and PCP + +- **Håstad's optimal MAX-E3SAT hardness** ([#886]): for `0 < ε < 1/8`, NP-hardness of + distinguishing satisfiable E3-CNF formulas from those where every assignment satisfies at most + `7/8 + ε` of the clauses. This needs the reduction to MAX-E3LIN and, through the 4-clause XOR + gadget, to MAX-E3SAT; Raz–Holenstein parallel repetition for small-soundness Label Cover; and + the perfect-completeness reduction. +- The FGLSS clique-hardness reduction and its amplification, retaining the exact gap and + reduction runtime. +- Gap and approximation-preserving reductions, APX/PTAS/FPTAS, inapproximability consequences of + PCP, CSP classifications and relaxation gaps, with assumptions such as the Unique Games + Conjecture stated explicitly. The 2-to-2 games theorem waits for [#886]. +- **A practical PCP construction:** a reasonably runnable construction and its cost on complete + reductions; rotation certificates polynomial in the bit length of a vertex name; machine-level + error reduction and extractors from expander walks, with certified seed and evaluation costs. + +### Proof complexity + +Proof-space lower bounds, Frege, cutting planes, polynomial calculus and sum-of-squares, and +certified DPLL solver runtimes. + +## Circuits and algebraic complexity + +- **Williams's NEXP ⊄ ACC⁰ and Murray–Williams's NQP ⊄ ACC⁰** ([#889]): no polynomial-size, + constant-depth circuits over AND, OR, NOT and fixed-modulus counting gates for some language in + NEXP. +- Uniform and nonuniform NC, AC and TC, circuit-to-machine bridges, algorithm-to-lower-bound + arguments, and barriers such as natural proofs and algebrization. NC simulation of unrestricted + `TCⁱ` needs Muroga's threshold weight-bounding theorem. +- Formula lower bounds beyond Andreev's, following Jukna; Raz–Wigderson's monotone depth bound for + matching on general graphs. +- **An explicit frontier extractor:** port v1's sumset-extractor construction with an explicit + family in `Complexity.P encBits` that discharges the frontier lower bounds' hypotheses. It would + exceed the library's unconditional `3.5 N - o(N)` bound in P, so it needs an independent + correctness review of the frontier core. +- **Weighted threshold representations:** v1's explicit source-reduction family, universal scalar + circuit synthesis, entropy and asymptotic activity bounds, and geometric cutting. +- **Construction runtimes**, certified separately from gate counts: the mass-production + constructors, with constructive choices of information sets and line menus; the + iterated-function preprocessing; and the bit complexity of formula evaluation on the rational + hitting-list matrices. +- **Algebraic complexity** ([#891]): the rest of Limaye–Srinivasan–Tavenas. Proved: simple + circuits (product gates reading distinct wires) of constant product-depth computing iterated + matrix multiplication need superpolynomial size over any field, at `d = ⌊log₂ ⌊log₂ n⌋⌋`, from an + explicit bound with exponent `d ^ (1 / (4 ^ Δ - 1))`. Remaining: their semantic set-multilinear + model; growing `Δ` with exponent `d ^ exp(-O(Δ))`; the `√d` bound at `Δ = 1` (LST Lemma 13 and + Corollary 4); the full range of `d`; and the `0/1`-substitution remark. Also the + Kabanets–Impagliazzo theorem (a deterministic PIT algorithm implies NEXP ⊄ P/poly or the permanent + has no polynomial-size arithmetic circuits); Valiant's VNP-completeness of the permanent over + fields of characteristic other than two; formula and branching-program models, identity testing, + hitting sets, and tensor and rank methods, including partial-derivative measures (Nisan–Wigderson + depth-3 bounds and shifted partials) and the depth-reduction chasms at depth four (Agrawal–Vinay) + and depth three (Gupta–Kamath–Kayal–Saptharishi), with uniform construction costs certified + separately from gate counts. +- **Real-algebraic decision procedures:** port the Sturm–Tarski query evaluator, recursive + evaluator, implicit arithmetic, native space certificates and the ETR upper bound from + [lean-canny](https://github.com/SamuelSchlesinger/lean-canny). + +### Jukna coverage + +Formalize the established results, constructions and worthwhile exercises of +[Jukna's *Boolean Function Complexity* (2012)](https://web.vu.lt/mif/s.jukna/boolean/index.html), +with the author's [errata](https://web.vu.lt/mif/s.jukna/boolean/misprints-BFC.html), auditing +each statement against the numbered result. Organize modules by subject, not chapter. + +| Chapters | Next coverage work | +| --- | --- | +| 1–2: foundations and Boolean analysis | Counting, graph complexity, degree and approximation results; the partial-function circuit upper bounds that use Lemma 1.17. | +| 3–5: communication | Matrix, randomized and multiparty results. | +| 6–10: formulas and circuits | Remaining formula and monotone bounds; span programs and negation complexity. | +| 11–13: depth restrictions and general gates | Depth-three, threshold, entropy, wire and linear-operator bounds. | +| 14–17: trees and branching programs | Bounded-read and bounded-time lower bounds and the remaining tree results. | +| 18–19: proof complexity | Proof-space and cutting-plane lower bounds. | +| 20: barriers and further methods | The book's generator, natural-proof, fusion and indirect lower-bound results. | + +## Pseudorandomness, coding and Boolean analysis + +- **Derandomization from hardness:** the infinitely-often variant, and the EXP ⊄ P/poly endpoint + of Babai, Fortnow, Nisan and Wigderson. +- **Extractors** ([#895]) beyond Chor–Goldreich: two-source extractors for lower min-entropy, + where the `t`-wise independent parts of Chattopadhyay–Zuckerman's Theorem 4.1 are formalized with + exponent `O(d²)` in place of their `3 d + 6` (the almost `t`-wise independent parts are not), with + Tribes as a small instance (coalitions of fewer than `w` bits only), and their resilient functions + (Theorems 3 and 4) and extractor (Theorem 1) are not yet formalized; + condensers, limited independence, explicit expanders and space-bounded generators, with finite + guarantees separate from uniform construction and decoding costs. +- **Codes:** Guruswami–Sudan list decoding and local decoding, keeping interpolation, + list-size/agreement guarantees and decoding runtime as separate obligations; concrete + list-decodable codes for Nisan–Wigderson. +- **Boolean analysis:** hypercontractivity beyond `p ≤ 2 ≤ q` and its applications. + +## Communication, queries, learning and other models + +- **Lifting** ([#890]): applications of deterministic lifting, such as partition-number + separations, and randomized lifting. +- Multiparty protocols, discrepancy and information complexity, direct-sum and direct-product + results, and reductions to streaming, sketching and cell-probe lower bounds. +- **Streaming and sketching:** native time and space bounds for Count-Min, including hash-seed + storage and evaluation; AMS frequency moments, CountSketch, turnstile updates, and heavy-hitter + recovery; communication-to-streaming transfers with explicit state encodings and pass counts. + The first Count-Min milestone is scoped in [#1201](https://github.com/cslib-community/complexitylib/issues/1201). +- **Learning:** machine-certified running times for the LMN and Kushilevitz–Mansour learners; + distributional problems and reductions, samplability, average-to-worst-case connections, + PAC/agnostic/SQ learning, learning lower bounds, compression and refutation. +- **Property and distribution testing:** access models, one/two-sided and tolerant testing, + query and sample bounds, canonical testers and lower bounds. +- **Kolmogorov complexity and metacomplexity** ([#798], [#884]): the near-linear threshold regime + of Liu–Pass Theorem 3.1 (c), hardness for every polynomial `t(n) ≥ (1 + ε)n` rather than above a + polynomial threshold, which needs a rate-1 efficient conditional EP-PRG running in `n + O(n^c)` + multi-tape steps for a constant `c < 1`; prefix-free complexity, + resource-bounded MCSP variants and connections to learning, cryptography and lower bounds; the + circuitized anti-checker with an efficient generator; the subexponential-formula consequence + Theorem 17(ii) and asymptotic parameter instantiations of Theorem 17(iii) and Theorem 5 in + `MCSP/FormulaMagnification.lean`; and hardness results for `MINKT` and `MINcKT` (whose + definitions, conditional `K^t` measure, and `NP` memberships are in `Kolmogorov/MINKT.lean`, + `Kolmogorov/Conditional.lean`, and `Kolmogorov/MINcKT.lean`), as independently scoped + developments; the reverse + multi-tape-to-single-tape simulation and machine-backed Nisan–Wigderson decoding. +- **Quantum computation** ([#896]): BQP ⊆ PP; reusable + circuit, channel and measurement semantics with uniformity and approximation costs, reusing + suitable quantum foundations rather than incompatible models; BQP, QMA and related query, + communication and interactive classes, with amplification, containments and complete + problems. + +[#1459]: https://github.com/cslib-community/complexitylib/issues/1459 +[#1463]: https://github.com/cslib-community/complexitylib/issues/1463 +[#1481]: https://github.com/cslib-community/complexitylib/issues/1481 +[#1482]: https://github.com/cslib-community/complexitylib/issues/1482 + +## Machines, RAM and data structures + +- **Native distinct-position frontend** ([#1482]): initialized signed input produces exact + compact root descriptors, with derived logarithmic width and uniform + `Oκ((n+1) log²(n+2))` charged cost. This frontend is composed into the complete native + preprocessing and the conditional ordinary host described below. +- **Efficient universal simulation** ([#800]): realize the buffered Hennie–Stearns backend + with time `O_M(t log(t + 2))`, together with a source-space-sensitive storage bound. + The current interpreter has a stronger polynomial certificate and a streaming input lookup; + the buffered representation, its native cleanup routines and their amortized bounds remain. +- **Plain computability** ([#862]): complete the plain calculus ([#608]), delete the tidy + calculus, keeping only a machine-level kernel inside the Machines layer, and switch the + abstract bridges to the shared predicate. +- **Logspace automation** ([#1449]): `fun_prop` rules for `ComputableInLogSpace` (composition, + `ite`, word, arithmetic and list primitives), with the one-way transition from logspace to + polynomial time. +- **Certificate APIs and automation** ([#1155]): predictable search, with fixed heartbeat budgets + on regression goals, failures that name the subterm lacking a certificate, and cheap + syntax-directed composition for glue code. Infer encodings for ordinary functional expressions + through constructor, projection, lookup and composition rules; generalize list, bounded-table, + cache and nested-loop invariants where clients need them; keep separate time, workspace and + output-size bounds through composition; apply cumulative streaming contracts to captured + iteration and nested loops; certify PRG stretching with seed length, rounds and generator cost + kept separate; and develop general open-program composition. Use Cook–Levin and GGM as + regression clients. Improve configuration views, tape partitions and handoff identities only + where several clients benefit, keeping machine transitions out of global simp. +- **Native Chan–He components** ([#1446]): native parameter, collision-counting, prime-table, + collision-tested minimum-search and complete two-prime modulus programs have explicit local + fit/frame/cost contracts. The complete native distinct-position frontend ([#1482]) starts from + initialized signed input and produces exact compact root descriptors with derived logarithmic + width and uniform `Oκ((n+1) log²(n+2))` charged cost. The complete native one-node component + ([#1481]) computes all residues/histograms, three compact heavy arrays and exact packed output + in linear time. Native bounded recursive traversal ([#1512](https://github.com/cslib-community/complexitylib/issues/1512)) executes those components with an + explicit stack, exact preorder output, polynomial local fit/frame contracts and charged + ternary-tree bounds. Complete native preprocessing ([#1446]) composes every root from actual + initialized input, returns the exact finite packed instances and derives positive logarithmic + width, polynomial space/intermediates and an `Oκ(n^1.5 log¹³(n+2))` charged bound. +- **Conditional native ordinary host** ([#1559](https://github.com/cslib-community/complexitylib/issues/1559)): + connects actual preprocessing output to caller-safe Convolution calls, proves the existing + distinct-position verdict from `State.init`, derives logarithmic width and every-prefix + polynomial words/space, and charges concrete callee work at the actual packed size. Its finite + callee contract has a proved slow exhaustive native instance; no fast backend or final + subquadratic bound is supplied. The native fast backend, Exact Triangle solver and final + input/time/width/asymptotic composition, including exponent 1.9992, remain under #1394. +- **Mathlib TM2:** sublinear work-space comparisons and a linear-space forward simulation. +- **Generic RAM:** uniform arbitrary-program dispatch and general width-selection and input-fit + theorems; simulating operations by programs over smaller bases; structured programs and time + classes over arbitrary value domains with weighted or bit costs; simulations between instances; + the typed core proposed in [CSLib #1158](https://github.com/leanprover/cslib/pull/1158). +- **Native pruned-query evaluator** ([#1526](https://github.com/cslib-community/complexitylib/issues/1526)): + fixed 96-register code computes distinct root/child queries, stack traversal and signed + accumulators. Initialized flat runs preserve query order and duplicates, input and outside + frame. Setup, sparse clearing, control/index work and halt fit + `8192*(L+Q+1+(L+1)*|leaves(requested queries)|)` instructions, with widths derived from + original magnitude/address bounds. A callable pointer/epoch kernel permits directory reuse. + Query grouping, band/tile composition, sparse-product and Exact Triangle solving, and full + input/time/width/asymptotic composition remain under #1394. + +- **Shared native matrix encoders** ([#1493](https://github.com/cslib-community/complexitylib/issues/1493)): compute all `10^L` leaf values from `7^L` + signed inputs within `240*(L+1)*10^L` standard WordRAM instructions, plus the final halt, + with fixed code/registers and concrete initialized inputs, frames and explicit width bounds. +- **Native shared-band preparation** ([#1523](https://github.com/cslib-community/complexitylib/issues/1523)): computes deterministic subset tables and a valid + layout, prepares padded signed matrix bands, invokes both shared encoders for every band, and + restores the original initialized input. Fixed 96-register code has a charged bound, including + halt, of `4096*(L+1)*(1+N*4^m+2^L+2*numBands*10^L)+1`; at `L=19m`, `N≥(4^m)^18`, it is at most + `81920*(L+1)*N²/(4^m)^(1/18)`. +- **Native wanted-query routing** + ([#1561](https://github.com/cslib-community/complexitylib/issues/1561)): computes the + selected-subset table, tile keys, query codes, counts, prefix offsets and stable scatter from + original raw coordinates. Its initialized run preserves every original occurrence and gives + each tile exactly its finite `wantedStrings` code set, within + `128*(L+1)*(Q+numBands²+2^L+1)` instructions including halt. It uses fixed 96-register code and + an explicit positive sufficient-width threshold. Joining these groups to shared band encoders, + invoking the pruned kernel and reconstructing queried products remain separate native work. +- **Native queried-product composition** + ([#1581](https://github.com/cslib-community/complexitylib/issues/1581)): joins the actual router + and band producers to the pointer/epoch kernel, restores the original input, and reconstructs + every requested signed product in original order. It charges raw-entry conversion, all copies, + dispatch, one shared directory initialization, epochs and reporting. At `L=19m`, `D=4^m`, + `D^18≤N` and occurrence sparsity `Q*2^m≤N²`, its initialized fixed 160-register program uses at + most `4325376*(19m+1)*N²/D^(1/18)` instructions including halt, with sufficient width + `128*clog₂(N+Q+A+2)` for entry bound `A`. The distinct-set sparsity theorem instead retains an + explicit additive charge for all `Q` occurrences. Native sparse-product and Exact Triangle + solving, full input/time/width/asymptotic composition, and the improved Section 4 route to + exponent 1.9992 remain under [#1394]. +- **Word RAM:** an AC⁰ basis; wider-fragment and Turing-machine simulations; + transfer of Anthropic's modular machine and 3SUM/APSP algorithms; direct 3SUM adaptation ([#1394]) + has the Convolution-3SUM/Exact Triangle correctness reduction, a charged native single-instance + matrix builder and the complete finite mathematical Chan–He reduction ([#1435]), including + ordinary distinct-position inputs and explicit size bounds. The finite lopsided matrix/counting + interface ([#1438]) adds padding, detection and signed digit reconstruction. Modular counting + and the selected-prime false-positive bound ([#1442]), finite residue/chunk/piece splitting and + witness recovery ([#1447]) are proved mathematically. The tensor identity, full recursion, + shared encodings and pruned-evaluation core ([#1459](https://github.com/cslib-community/complexitylib/issues/1459)) include exact visited-leaf and passed-set + bounds, with the empty zero-depth root handled explicitly. The finite thin-product component + ([#1463](https://github.com/cslib-community/complexitylib/issues/1463)) proves valid layouts, arbitrary-size padding/tiling, exact queried signed products and + sparse encoding/leaf/passed-set totals. These counts are not native instruction costs. Complete + native preprocessing and the conditional ordinary host with an exhaustive reference callee + are available. Native pruned/sparse-query backend composition, a concrete fast Exact Triangle solver + and the unconditional fast input/time/width/asymptotic bound remain, including the improved + Section 4 route to exponent 1.9992; + size-based width + selection with an input-fit guarantee and explicit overhead; expected-time rules for unbounded + almost-surely terminating structured loops; random-tape equivalence and BPP/RP/ZPP simulation + bridges for the randomized word RAM ([#1124]); transfer of cell-probe lower bounds; and the + Horn-SAT, 2SAT, matching and domino-tiling clients. +- **Caller-safe queried thin products** ([#1618](https://github.com/cslib-community/complexitylib/issues/1618)): + actual memory operations are rebased above a runtime caller base, using a computed source + modulus and charged load normalization. The initialized 163-register program restores local + input, returns an absolute answer pointer and preserves the caller outside a polynomial window. + Setup/return adds at most `2048*(N*D+Q+1)` to three times the original product charge. At + `D=4^m`, `L=19m`, `D^18≤N` and `Q*2^m≤N²`, the full charge including halt is at most + `17301504*(19m+1)*N²/D^(1/18)`. Distinct-set sparsity retains an additive occurrence charge. + Every width at least `256*clog₂(base+N+Q+P+5)` suffices for initialized caller-word bound `P`. + The reserved window is at most `(2*(N+Q+3))^128` and is not bulk-initialized. +- **Prepared ET-record product calls** ([#1626](https://github.com/cslib-community/complexitylib/issues/1626)): + the native adapter reads the computed six-word record, recovers the depth with actual binary + logarithm code, and copies raw matrices and query occurrences above the producer's heap. + It invokes the complete callable product in a separate register bank and preserves the + catalogue, sources and producer registers. The fixed 320-register initialized client runs + the producer and consumes its first record; the caller interface supports every occupied + ordinal. Original polynomial headers/weights give positive logarithmic admission and + polynomial words/address support at every wider width. Adaptation costs `O(nD+Q)` beyond + the actual callee; actual chunks derive `Q*2^m≤n²`, giving the per-record halt-inclusive bound + `34603008*(19m+1)*n²/D^(1/18)` at `D=4^m`, `L=19m`, `D^18≤n`. + The outer instance loop, aggregate subcubic ET bound, backend/witness reconstruction and + the full [#1394] ordinary-3SUM 1.9992 endpoint remain open. +- **Per-record native witness recovery** ([#1639](https://github.com/cslib-community/complexitylib/issues/1639)): + actual initialized producer/product calls now feed real answer/query loads and original signed + weight verification. The fixed 400-register program returns original-coordinate witnesses and + decides zeros in the selected existing chunk/piece. Its actual scan counter satisfies + `S≤falsePositiveCount+returnedFlag`, so unsuccessful records have no successful-scan surplus. + Root decoding, all control and original-weight scans are charged; positive logarithmic width + and polynomial words/support hold at every wider admitted width. The outer instance loop, + aggregate subcubic ET analysis and ordinary-3SUM 1.9992 composition remain open under [#1394]. +- **Conditional Convolution-3SUM host** ([#1524]): the charged native host initializes from the + ordinary input and runs every generated instance, conditional on a concrete solver's execution, + correctness and frame theorem. This completes the conditional host milestone. Integrating the + concrete fast Exact Triangle solver and the full input/time/width/asymptotic reduction remains + open under [#1394]. +- **Stateful algorithms and data structures:** Word-RAM controllers for larger adaptive and + iterative clients, including the two-stack queue and timeline-editing controllers; reusable + allocation and deallocation, shrinking, and live and peak space accounting; ownership and + recursive procedures; model realizations for cell probes, caches and randomness; present and + historical queries for the retroactive interface; deletion in the shared ordered-set interface, + balancing, and heap or RAM representations of search trees; uniform preprocessing and dynamic + updates for fusion trees; deletion and predecessor for van Emde Boas sets; and deallocation, + shrinking and compiled client controllers for the doubling array. +- **A typed-call evaluator** for fixed-program Word-RAM clients, returning the typed observation, + final state and halt-inclusive instruction cost, so the prefix-sum and stack clients no longer + assemble preparation, evaluation and halt accounting by hand. +- **Priority-queue and shortest-path consolidation** ([#1363]): lift operation-count helpers + (`numQueries`, `numInserts`, `numDecreaseKeys`, `numExtractMins`) to `IndexedMinPQ`, parameterize + flat-program operation-sequence execution (`RunsOps`) over `PQCode` in `PriorityQueue/Basic.lean` + so `BinaryHeap` and `FibonacciHeap` share a generic `run_ops_exact` theorem, and factor a common + `ShortestPathSpec p bound` predicate in `Dijkstra/HeapStatement.lean` across `LogLinearSpec`, + `FibonacciSpec`, and `FibonacciLinearSpec`. + +- **Native ExactTriangle preparation** ([#1532](https://github.com/cslib-community/complexitylib/issues/1532)): + initialized execution computes the complete catalogue and raw zero/one instance matrices, + with exact finite readback, logarithmic width, polynomial prefix words and address space, + and a `690n²Dg` charged bound in the source's admissible parameter regime. Prime selection, + solving the prepared instances, product/backend composition and the full #1394 endpoint remain. + +## Coverage and reuse + +The long-term bar is to support formalizing every paper in the +[Electronic Colloquium on Computational Complexity](https://eccc.weizmann.ac.il/colloquium/call_for_papers/), +with the additional work per paper increasingly its new mathematics: standard models, +representation changes, reductions, resource certificates and proof techniques come from the +library. Use a growing, source-pinned sample of papers, including some chosen after an API was +designed, as demanding clients, and promote recurring obligations into shared APIs only after +reuse is demonstrated. + +- **One sensitivity API.** Define the sensitivity measures over any `Fintype` in CSLib and drop + the separate measures of the sensitivity-separation development. +- **Weighted multigraph cuts and `PMF` event bounds** ([#1336]): extract `IsMultigraph` (renamed + e.g. `IsSymmLoopless` or phrased via `Matrix.IsSymm` with zero diagonal), `IsNonTrivialCut`, + `cutWeight`, `minCut`, `rowDeg`, `totalDoubledWeight`, `flattenMatrix`, and `IsContractionOf` from + `Complexity/Algorithms/WordRAM/Karger/Graph.lean` (shared by `KargerMinCut` and `KargerStein`) into + `Complexity/Foundations/Combinatorics/WeightedCut.lean` with a bridge lemma relating `cutWeight W C` + to `(Cslib.Multigraph.edgeCut ends C).card`, and move `toOuterMeasure_bind_ge_mul_ennreal`, + `toOuterMeasure_bind_le_mul_ennreal`, `toOuterMeasure_add_compl_ennreal`, + `toOuterMeasure_compl_le_one_sub`, and `two_branch_hit_prob_ge` into + `Complexity/Probability/PMF/Event.lean` (reusable by `toOuterMeasure_toReal_add_compl`). +- **CNF evaluation certificates:** `fun_prop` certificates for evaluating literals, clauses and + formulas under a polynomial-time assignment, so clients stop passing `CNF.eval`, + `Clause.eval` and `Literal.eval` to `fun_prop` as unfolding hints. +- Factor indicator-expectation identities from PCP's second-moment calculation, and recurring + cardinality and logarithm conversions, once a second client benefits. + +### OpenAI math results ([#1002]) + +Review Lance Fortnow's [expository rewrites](https://github.com/fortnow/AI-Papers) of the OpenAI +math papers and his [TCS catalogue](https://lance.fortnow.com/openai-math-tcs.htm) of the +openai/math release, and extract the results in scope: the headline theorems and also the minor +lemmas useful in their own right. Prove each against Complexitylib's own definitions, from the +cited manuscript's exact statement with its version pinned; a source formalization is a +reference, not evidence. Label unrefereed results as such. The openai/math formalizations are +Apache 2.0, as is Complexitylib, so adaptation with attribution is permitted. The practice is +still to read them for the mathematics and write proofs against Complexitylib's definitions; any +adapted code keeps its Apache 2.0 attribution. The rewrites state no license and are only read +and credited. + +- **Native declared-variable counting** ([#1226](https://github.com/cslib-community/complexitylib/issues/1226)): + `CountingNativeEndpoint.exists_declaredCounter` supplies one native counter for each fixed width, + exact on every valid declared-size/CNF input and zero otherwise. It has + `2^{O_k(interpolationScale N)} * (L + 1)^{O_k(1)}` time and independent polynomial workspace in the + original external encoded length `L`, with no supplier premises. The module derives + subexponential-time `k`-SAT, zero SAT exponents, `¬ETH`, and `¬SETH`. This is the Sections 3–6 and + Appendices A–B interpolation-scale bound, not the sharper Appendix D runtime. Integration, + validation, and the curated formal abstract are tracked in #1226. + +## Documentation and repository + +- **The guide** ([#585]): a chapter per area, including Foundations (bit strings, Fourier + analysis, PMF and `ProbComp`, codes), proof complexity, descriptive complexity, and Word RAM + and algorithms, reusing checked examples. +- **Curated formal abstracts:** improve readability, and compare each changed abstract with its + final source statements. +- **Archive `complexitylib-v1`** once its remaining work is dispositioned. + +## Deferred scopes + +These remain coverage targets but are deferred from the active queue; their issues were closed as +not planned, not as proved. + +- The full VNP-completeness reduction for the permanent ([#321]). +- General auxiliary-input zero knowledge ([#324]); see the GMW theorem above. +- Williams's square-root-space simulation ([#361]) and implicit Tree Evaluation ([#367]), now the + frontier track [#888]. +- Vollmer's generalized-quantifier survey ([#399]): ordered leaf words, explicit uniformity and + input-access models, finite-monoid classification and generalized logical quantifiers. + +[archive]: https://github.com/cslib-community/complexitylib/blob/c89dd48c/ROADMAP.md +[archive-fhe]: https://github.com/cslib-community/complexitylib/blob/c89dd48c/ROADMAP.md#modern-fhe-methods-and-deployment-priorities +[#321]: https://github.com/cslib-community/complexitylib/issues/321 +[#324]: https://github.com/cslib-community/complexitylib/issues/324 +[#361]: https://github.com/cslib-community/complexitylib/issues/361 +[#367]: https://github.com/cslib-community/complexitylib/issues/367 +[#399]: https://github.com/cslib-community/complexitylib/issues/399 +[#456]: https://github.com/cslib-community/complexitylib/issues/456 +[#459]: https://github.com/cslib-community/complexitylib/issues/459 +[#474]: https://github.com/cslib-community/complexitylib/issues/474 +[#486]: https://github.com/cslib-community/complexitylib/issues/486 +[#581]: https://github.com/cslib-community/complexitylib/issues/581 +[#585]: https://github.com/cslib-community/complexitylib/issues/585 +[#608]: https://github.com/cslib-community/complexitylib/issues/608 +[#640]: https://github.com/cslib-community/complexitylib/issues/640 +[#643]: https://github.com/cslib-community/complexitylib/issues/643 +[#645]: https://github.com/cslib-community/complexitylib/issues/645 +[#798]: https://github.com/cslib-community/complexitylib/issues/798 +[#800]: https://github.com/cslib-community/complexitylib/issues/800 +[#862]: https://github.com/cslib-community/complexitylib/issues/862 +[#881]: https://github.com/cslib-community/complexitylib/issues/881 +[#884]: https://github.com/cslib-community/complexitylib/issues/884 +[#886]: https://github.com/cslib-community/complexitylib/issues/886 +[#888]: https://github.com/cslib-community/complexitylib/issues/888 +[#889]: https://github.com/cslib-community/complexitylib/issues/889 +[#890]: https://github.com/cslib-community/complexitylib/issues/890 +[#891]: https://github.com/cslib-community/complexitylib/issues/891 +[#892]: https://github.com/cslib-community/complexitylib/issues/892 +[#893]: https://github.com/cslib-community/complexitylib/issues/893 +[#894]: https://github.com/cslib-community/complexitylib/issues/894 +[#895]: https://github.com/cslib-community/complexitylib/issues/895 +[#896]: https://github.com/cslib-community/complexitylib/issues/896 +[#897]: https://github.com/cslib-community/complexitylib/issues/897 +[#939]: https://github.com/cslib-community/complexitylib/issues/939 +[#949]: https://github.com/cslib-community/complexitylib/issues/949 +[#999]: https://github.com/cslib-community/complexitylib/issues/999 +[#1000]: https://github.com/cslib-community/complexitylib/issues/1000 +[#1001]: https://github.com/cslib-community/complexitylib/issues/1001 +[#1002]: https://github.com/cslib-community/complexitylib/issues/1002 +[#1047]: https://github.com/cslib-community/complexitylib/issues/1047 +[#1079]: https://github.com/cslib-community/complexitylib/issues/1079 +[#1104]: https://github.com/cslib-community/complexitylib/issues/1104 +[#1124]: https://github.com/cslib-community/complexitylib/issues/1124 +[#1155]: https://github.com/cslib-community/complexitylib/issues/1155 +[#1336]: https://github.com/cslib-community/complexitylib/issues/1336 +[#1351]: https://github.com/cslib-community/complexitylib/issues/1351 +[#1363]: https://github.com/cslib-community/complexitylib/issues/1363 +[#1394]: https://github.com/cslib-community/complexitylib/issues/1394 +[#1435]: https://github.com/cslib-community/complexitylib/issues/1435 +[#1438]: https://github.com/cslib-community/complexitylib/issues/1438 +[#1442]: https://github.com/cslib-community/complexitylib/issues/1442 +[#1446]: https://github.com/cslib-community/complexitylib/issues/1446 +[#1447]: https://github.com/cslib-community/complexitylib/issues/1447 +[#1449]: https://github.com/cslib-community/complexitylib/issues/1449 +[#1524]: https://github.com/cslib-community/complexitylib/issues/1524 +[#1572]: https://github.com/cslib-community/complexitylib/issues/1572 diff --git a/UPSTREAM.md b/UPSTREAM.md index b28a37bb7..f84aaa139 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -1,664 +1,664 @@ -# Upstream integration - -Coordination and integration decisions not already recorded in source. - -The authoritative records are: - -* [lakefile.toml](lakefile.toml), [lake-manifest.json](lake-manifest.json) and - [lean-toolchain](lean-toolchain) for pinned versions; -* each module's `-- upstream:` markers for candidate, submitted, vendored or local-only status, - including PR numbers and copied revisions; markers can also identify blocks within a module; -* [Complexity.lean](Complexity.lean) for the module inventory. - -Use `python3 scripts/upstream_sync.py --offline` to list copied dependencies, and -`python3 scripts/upstream_sync.py` to compare recorded revisions and the base pin with upstream. -It checks head revisions, not source equality or unrecorded overlaps. Follow -[AGENTS.md](AGENTS.md#staying-current-with-upstream) for synchronization and removal, and -[CONTRIBUTING.md](CONTRIBUTING.md#upstreaming) for preparing an upstream worktree. -Do not add a ledger row for an ordinary candidate or repeat its source marker here. - -## Coordination before submission - -* **Machine calculus.** Build on Christian Reitwiessner's `crei/tidy-universal` branch and its - component PRs, including #1111, #1112 and #1138. Local `-- vendored:` comments identify required - adaptations. The branch's `IterateWhile` supersedes the version from #1112. Coordinate the - shared encodings and calculus through CSLib issue #611, including the `TapeEncodable` - interface proposed in #590. Check #767 and its split #768 before adding space-to-time - inclusions or overlapping class definitions; reconcile the local encoded, plain-machine - definitions of `P`/`NP` with its `DTIME`-based language classes. The local `DSPACE`, `L`, - `DTIME` and `EXP` (`Classes/Space/Basic`, `Classes/LogSpace`, `Classes/Time`, `Classes/EXP`) - use #767's shapes over encoded instances; once #767 lands, derive them from its - language-indexed classes rather than keeping parallel definitions. The local `PSPACE` - retains its instance-dependent polynomial space bound; - `mem_PSPACE_iff_exists_mem_DSPACE` in `Classes/Space` proves its equivalence to the local - union of `DSPACE (n ^ k)`. A bridge to #767's language-indexed classes is still separate. - Time and space constructibility are in `Classes/Constructible`, independent of hierarchy - theorems. Selected-tape specifications - formerly in `Combinators/StreamFold` and `Encodings/MultiPass` now live beside the plumbing - operations; preserve their exact space bounds when resynchronizing the copied files. - The step-machine layer (`StepMachine/*`, `Encodings/StepMachine*`, `Combinators/StepMachine` - and their helpers) and the blocks marked `@9cce4cc7` in older copies come from a later local - rewrite of the branch that is not yet published; `scripts/upstream_sync.py` reports them - against the published head until it is. The rest of the branch is still copied at `@7f7c0696`. -* **Universal simulation.** The local universal interpreter preserves Christian Reitwiessner’s - description format and execution semantics. Its resource certificates now separate - transition-description size and binary input-head length from the copied configuration; - `Universal/Input` uses the existing native streaming fold to emit a selected input symbol - without repeatedly copying list tails. Retain these local improvements when synchronizing - `Universal/Complexity`, `Bound` and their resource consumers with `crei/tidy-universal`. - This is an extension of the existing interpreter, not a buffered Hennie–Stearns backend. -* **Polynomial bounds.** The tidy calculus's size-parameterized `PolyBounded` adapts CSLib's - `PolynomiallyBounded` and shares its closure API. Coordinate this adaptation with the tidy - branch; preserve it when resynchronizing the vendored files. - `Foundations/Data/Nat/PolynomialBound/Closure.lean` is the proposed next submission following - #1067. -* **Mathlib TM2.** `Computability/Machines/Turing/TM2/*` certifies Mathlib's stack-machine - statements directly through the tidy calculus; it does not depend on the TM0 simulator in - [Complexitylib PR #72](https://github.com/SamuelSchlesinger/complexitylib/pull/72). - Its dependent finite-table helpers (`Encodings/DPi` and `Complexity/PolyTime/DPi` under - `Machines/Turing/MultiTape`) extend the existing table API. Coordinate these helpers with - the machine-calculus work above before upstreaming. The forward bridges preserve polynomial - time and transfer terminating polynomial-stack-space computations, using configuration counting - and binary-clock iteration for space. The reverse compiler in `TM2/Reverse` constructs a - finite-alphabet `FinTM2`, including canonical `haltList` cleanup, with linear time overhead and - total stack length at most input length plus native work space plus output length. - `Reverse/Transfer` proves polynomial-time and PSPACE equivalence under the original binary - encodings; `PolyTime/List` supplies the word conversions and `TM2/Recoding` transfers TM2 - computations across them without decoding the typed embeddings. - These comparisons do not preserve sublinear work-space conventions. -* **Probabilistic programs.** Coordinate the shared `PFunctor.FreeM` API with #1086–#1089 and - typed oracles with #1129. The copied PMF blocks have their own markers and removal instructions - in [Probability/PMF.lean](Complexity/Probability/PMF.lean); remove only the merged blocks. -* **Security games.** The #1068 negligible-bound base is now supplied by the cslib pin. - `Crypto/Negligible.lean` imports it and retains the #1069 `fun_prop` registrations, with - pointwise addition/multiplication wrappers needed by lambda-shaped automation clients; - remove the submitted registrations when #1069 lands. The candidate public helpers - `Negligible.add_fun` and `Negligible.mul_fun` remain until upstream declarations preserve - those full names. `Crypto/Game.lean` retains the unmerged #1069 security-game API. Its two local pointwise/common-mixture advantage bounds (#802) must also be retained when - syncing or removing that copy. - The probability/game stack copies the October 23 upstream refresh (#1062, #1069, #1070, - #1072 and #1129). `Crypto.negligible_iff` and the negligible closure lemmas now come from - the base pin; local PMF additions and negligible automation remain alongside the copied portions. - Exact revisions are recorded in their source markers. - The following hybrid layer is tracked by its own marker in `Crypto/Game/Hybrid.lean`. - `Crypto/Game/Statistical.lean` copies only the Boolean equality from #1072; its remaining - declarations require the arbitrary-type metric replacement in #1065. Until that is in the - base pin, extraction bounds quantify over every randomized test of the full encoded output, - using the existing finite-PMF metric and `Game.Indistinguishable` with all tests admissible. - Coordinate the finite PRG API and its distribution-based refactor in #1071 before adapting - `Crypto/Primitives/PRG/Game`. -* **Query complexity.** Numerical measures and certificates use the merged - `Computability/QueryComplexity` API directly, on `Fin n`/`Bool`. Generic decision trees, - restrictions and Fourier analysis retain arbitrary coordinate/output types; sensitivity - lower bounds there use explicit coordinate sets. The restriction representations agree - definitionally, with `QueryComplexity/Restriction` connecting filling to `Forces`. - Align the remaining tree representation in `Computability/DecisionTree` with #925 and - coordinate its certificate follow-up `D ≤ C₀ C₁`; no second numerical measure API is retained. -* **Word RAM.** `Computability/WordRAM/*` adapt the bounded-word RAM of the Lean formalization - accompanying the tromino undecidability paper, - [`edemaine/lean-trominoes`](https://github.com/edemaine/lean-trominoes) @6d5b36c1 (Apache-2.0), - which is not a CSLib branch. The adapted files retain the source's copyright notice - (lean-trominoes contributors) and credit Erik Demaine alongside the adaptation author in their - `Authors:` lines; coordinate follow-up submissions and attribution with him, - alongside the CSLib algorithm-model work: Shreyas Srinivas's query-combinator - programs (#685, and the register word-RAM models in his Algolean repository) and the merged - query framework (#401), which - count only lifted queries. A deeply embedded machine beneath such front ends answers, for the - RAM, the objection that pure Lean computation is uncharged. The module docstrings record the - deviations from the source. The operations form a basis (`WordRAM/Basis`) over - `Cslib.Circuits.Signature`, and translation between bases (`WordRAM/MapOp`) uses the local - `Circuit/SignatureHom` maps of signatures, so these files depend on that candidate. The - unbounded-register `Computability/RAM`, inspired by - Cook–Reckhow and supporting both unit and logarithmic costs, remains a separate candidate. -* **Generic RAM values.** `Computability/GenericRAM/*` supplies a shared finite-register RAM - interpreter with configurable value, address, configuration and literal types, operation/test - signatures, and fault policies. `WordRAM/Generic` embeds every existing word-operation basis - with exact step, fault, output and instruction-count preservation, including arbitrary initial - states; no existing public WordRAM definition changes. The signed instance follows the - lean-trominoes signed range and its address, move, halt and program-counter checks, retaining - its source credit. The algebraic instances use tagged natural words and exact field values: - only natural words can address memory. Real comparison is mathematical, noncomputable - semantics; prime-field interpretation is executable. The real variant follows the separation - of words and reals in Erickson–van der Hoog–Miltzow, but uses finite named registers, a tagged - memory and checked word arithmetic. These are explicit model choices, not a proved equivalence - with their two-array model. Coordinate this common core with the algorithm-model work above; - the existing Cook–Reckhow-inspired RAM and its logarithmic cost remain separate. - `GenericRAM/Native` separately packages existing native TM `ComputableTidilyInO` witnesses for - encoded scalar primitives, with caller-selected configuration encoding and unrestricted - input-dependent bounds. Its forgetful bridge reuses `IsComputable`; polynomiality is a separate - bound property. `GenericRAM/Native/Nat` supplies a checked bounded-natural addition model; - `WordRAM/Native` supplies complete polynomial-time `NativePrimitives` bundles for - `Basis.standard`, `Basis.additive`, `Basis.arithmetic`, and `Basis.exact` (plus restricted - sub-bases and homomorphic preimages) with unary width and binary operands (#489). - Both reuse Christian Reitwiessner's native arithmetic/encoding calculus. - `GenericRAM/Simulation/{Finite,Turing,Run}` combine these per-symbol certificates with sparse - memory and finite control to simulate single steps and clocked multi-step runs of any fixed - generic-RAM program on native multitape Turing machines - (`Cslib.GenericRAM.FiniteState.transport_inO`, `Cslib.GenericRAM.FiniteState.transport_polyTime` - and `Cslib.GenericRAM.FiniteState.transport_polyTime_of_le_const`). `transport_inO` is - parameterized by explicit time and space bounds; `transport_polyTime` needs a polynomial step - bound (`hStepsBound`) and a polynomial bound on the encoded intermediate run results - (`hOutLen`); `transport_polyTime_of_le_const` derives both from a constant step bound. Uniform - arbitrary-program dispatch and charged configuration selection remain separate. -* **Signed programs on unsigned words.** `WordRAM/Signed/*` reuse `GenericRAM.Signed`'s - range, arithmetic and interpreter. The sign-and-magnitude encoding needs one extra unsigned - bit; the arithmetic blocks use two. `State`, `Operations`, `Compile` and `Input` extend them - to complete programs over the full five-operation signed model (constants, copies, - addition, subtraction, multiplication, Euclidean division/remainder with Lean's total - zero-divisor conventions, memory, signed tests, jumps and halt). Successful source runs of `t` - instructions transfer to one fixed unsigned program at width `w + 2` within `32*t + 5` - instructions, including the five-instruction length header conversion and the final halt. Each - source integer uses one target word, source memory addresses are unchanged, nine scratch - registers precede the source register bank, and each source instruction occupies sixty-four - target code slots. Code labels need not fit a data word in the target model. Run-prefix - theorems preserve the complete source memory and register bank. This is forward correctness, - not reflection of source faults; transfer from Anthropic's modular signed machine and its - 3SUM/APSP algorithms remains separate. -* **Native shared tensor encoder.** `Algorithms/WordRAM/MatrixMultiplication/Schonhage/` implements the complete shared left/right - encoding milestone ([#1493](https://github.com/cslib-community/complexitylib/issues/1493)). - Fixed standard-WordRAM code with 32 registers transforms one coordinate at a time between - two disjoint `10^L` buffers. It starts from `State.init` with a depth header and `7^L` - sign-and-magnitude words, computes all public `encodingL`/`encodingR` values, and preserves - the input and every cell outside the two buffers. Mathlib's `finFunctionFinEquiv` supplies - least-significant-first string numbering. All stages, powers, indices, address arithmetic, - signed add/subtract blocks and the final output copy cost at most `240*(L+1)*10^L` - instructions; halt adds one. Signed operands have magnitude bounded from the original - input by `7^L*A < 2^(w-1)`; the explicit natural bound `L+64+64*10^L` must fit target width - `w+2`. The two extra bits and actual arithmetic-block costs come from the existing native - signed kernels, without a copied source machine or a whole-program simulation hypothesis. - The existing array-copy interface is retained, with a narrow generalization for disjoint - blocks in either address order or identical blocks. - This is the encoder component only; band/tile preparation and full native composition remain - in [#1394](https://github.com/cslib-community/complexitylib/issues/1394). -* **Native shared-band preparation.** The native shared-band preparation component - ([#1523](https://github.com/cslib-community/complexitylib/issues/1523)) computes a deterministic - `Layout` and both subset tables by native mask/popcount scans, computes the square-root block - count by a charged loop, and decodes each band-array index into the original row/column and - thin-dimension coordinates. Real matrix bounds are checked before row-major multiplication. - Each shared row/column band calls the unchanged encoder once; tiles refer to its catalogue - block in the existing leaf order. The fixed 96-register standard code starts from actual - `State.init` scalar/matrix input, charges snapshot and restoration of the complete original - prefix, and preserves arbitrary outside memory. All counter/address fits follow from an - explicit original-parameter word budget; signed fits follow from the original entry bound. - Its complete instruction bound, including halt, is - `81920*(L+1)*N²/(4^m)^(1/18)` when `L=19m` and `N≥(4^m)^18`. Native power, ordered-filter, - finite-digit and row-major list lemmas live in small topic modules; the existing mathematical - layout, matrix and encoding interfaces are reused. No source machine or call/time oracle - is introduced. -* **Direct 3SUM adaptation.** `Algorithms/ThreeSum/Convolution.lean` adapts the explicit - Convolution-3SUM to Exact Triangle correctness reduction from Anthropic's - [`formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp), - `Sec3/Theorem21a/Convolution.lean`. Its reduction uses the arbitrary-matrix predicate in - `Algorithms/ExactTriangle/Defs.lean`, shared with future Exact Triangle solvers. - The three weight matrices use the original formulas; an explicit nonnegative magnitude bound - also handles empty input. The native builder in `Algorithms/WordRAM/ThreeSum/{Cell,Fill,Memory}.lean` - extends the one-weight kernel with index/address calculations and the complete matrix loop. - Given its arguments in registers, it writes one instance in at most `131*t² + 5` instructions - at width `w + 2`, excluding the halt; its theorem identifies the three encoded matrices and - preserves the input. Layout, signed index/filler bounds and target address bounds are explicit. - The existing signed and unsigned APIs were reused; no source machine or separate run semantics - were copied. `Algorithms/WordRAM/ThreeSum/Host/` adds the complete charged native host: ordinary - header initialization, polynomial magnitude computation, square root, consecutive matrix layout, - all `2t` instances and a finite solver program in a disjoint register bank. Its initialized run - theorem connects the actual pre-halt state to the Convolution-3SUM answer and preserves the input. - The host's source-width threshold is `(20κ+125)clog₂(N+2)`; signed kernels use target width `w+2`. - This remains conditional on the supplied solver's native execution/correctness/frame theorem; - it does not establish a fast solver. Fast Exact Triangle and its final native backend/asymptotic - composition remain in - [#1394](https://github.com/cslib-community/complexitylib/issues/1394), including the improved - Section 4 route needed for the published exponent 1.9992. Anthropic's copyright and attribution - are retained; the native proofs and representation choices are local. - Native numeric parameter computation, signed-residue/histogram counting, trial-division prime - tables, collision-tested least-prime scans and complete two-prime modulus selection also use - the existing `WordRAM.Code`/`ExecWithin` APIs. Their explicit local fit, frame and charged-cost - contracts are composed by a fixed 128-register bounded traversal with an explicit seven-word - stack frame and exact preorder packed output. The traversal consumes a root descriptor and - runtime fuel; its polynomial local envelope derives kernel fits and charges every stack, - descriptor and address operation. It does not invoke a Convolution-3SUM solver. Composition - from initialized input through every frontend root is proved in - `Algorithms/WordRAM/ThreeSum/ChanHe/Preprocessing.lean`. One finite 128-register program per - fixed magnitude exponent returns the existing exact finite instance list. All fits derive from - `(1300κ+7865) clog₂(n+2)` physical width; every run prefix uses polynomial words/addresses even - at wider admitted widths. Its charged cost is at most - `Kκ (n+2)(sqrt(n+2)+1)(log₂(n+2)+1)^13`, including the final halt. The complete native - preprocessing milestone is #1446. `Algorithms/WordRAM/ThreeSum/Decision.lean` composes that - actual initialized output with the Convolution host (#1559): raw registers 78/79/116 supply the - actual pointer table, count and common packed length. The caller-owned host reads each signed - payload directly and reserves matrices above twice the preprocessing polynomial word bound, - preserving every future instance. One finite program per fixed exponent and certified finite - Exact Triangle block returns the existing distinct-position verdict, with derived positive - logarithmic physical width, polynomial words/space at every admitted-width prefix, and a fully - charged compositional bound at the actual packed size. `Host.Callee` requires actual finite-code - execution, matrix correctness, logarithmic width and a protected-prefix frame on arbitrary old - scratch memory; `Host.Slow.callee` is a proved exhaustive native instance. This conditional host - does not provide a fast solver. The unconditional native solver/backend and final asymptotic - composition, including exponent 1.9992, remain #1394. These components do not affect the curated - headlines. The caller layout, exhaustive callee and ordinary composition are original; the - underlying finite reductions and builder retain their existing Anthropic credit. - - Bounded API follow-ups from the traversal/preprocessing reviews: merge the stage proof modules - `PreprocessingSnapshot`, `PreprocessingPrimes`, `PreprocessingSetup`, `PreprocessingFinish`, - `PreprocessingInvariant`, `PreprocessingStep` and `PreprocessingLoop` with their controller - consumer `PreprocessingExecute`, or use `import all` to make their proof plumbing private. - This includes `Saved`, `ForestCursor`, `snapshot_exec`, `primes_exec`, `setupTraversal_exec`, - `finish_exec`, `execute_next_root` and `forest_body_exec`; keep the initialized-input, readback, - width and resource endpoints available through `Preprocessing`. Separately, move - `modulus_bounds_of_candidates` from `SelectorBounds` beside `Collisions`' `modulus_bounds` and - derive or cross-reference the stronger bound, migrating `TraversalCall` and selector tests. - Derive `Structured`'s `ExecWithin.loop_invariant` as the `Data`-state case of - `GhostLoop`'s `ExecWithin.loop_invariant_state`, preserving the existing copy/residue/padding/ - search/reservation clients. Resolve the current import direction by keeping both rules in one - loop topic module before changing those clients. - - `Algorithms/ThreeSum/ThreeSets.lean` and `ArrayPacking.lean` provide the generic distinct-position - frontend and three-array packing. `Algorithms/ThreeSum/ChanHe/` now adapts the core mathematical - reduction: minimum-prime searches, collision/heavy-element bounds, light-element arrays, bounded - ternary recursion and composition with the frontend. At polynomial magnitudes it gives explicit - length, count and magnitude bounds; `scanPairs` counts candidate-prime/element pairs and is not - a charged running time. Generic finite-average and prime-counting lemmas live in Foundations. - See milestone [#1435](https://github.com/cslib-community/complexitylib/issues/1435). - `Algorithms/ExactTriangle/Lopsided.lean` adapts the finite unweighted lopsided query/counting - interface from the same pin's `Sec3/MatrixLanguage.lean`: arbitrary-injection zero padding, - common-neighbor counts as product entries, detection, the zero/one converse, and signed products - from `4 * β²` count instances with the original queried edges. Its digit identities live in - `Foundations/Data/Matrix/IntegerBits.lean`, reuse Mathlib's pointwise positive/negative parts, - with descriptive lemmas in `Cslib.Matrix`. The scalar magnitude-bit bound, including zero, - lives in `Foundations/Data/Int/LogBounds.lean`. Count answers use `Set.EqOn`; detection - consumes a direct iff on queried edges. No additional weighted-triangle predicate or source - machine is introduced. - This is the mathematical interface milestone - [#1438](https://github.com/cslib-community/complexitylib/issues/1438), with no sparsity assumption, - preprocessing/time claim or solver. `Algorithms/ExactTriangle/{Weights,Hashing}.lean` now adapts - the modular counting/coefficient identities, minimum-count prime and uniform false-positive - bound from `Sec3/Theorem17/Hashing.lean`, using supplied matrices and the existing decision - predicate. The application-independent `Nat.sqrtPrimeWindow` API lives in - `Foundations/NumberTheory/SqrtPrimeWindow.lean`; its integer-square characterization supports - native enumeration, and splitting and witness recovery reuse it. - The quotient/coefficient API lives in `Foundations/RingTheory/CyclicIntRing.lean`; the uniform - all-window prime-count bound lives in `Foundations/NumberTheory/PrimeWindow.lean`, retaining - the source credit to Mathlib, Patrick Stevens and Bolton Bailey. These are mathematical - statements, not native instructions or an assumed solver. See - [#1442](https://github.com/cslib-community/complexitylib/issues/1442). - `Algorithms/ExactTriangle/{Splitting,Witnesses}.lean` adapts the complete finite splitting and - witness recovery component ([#1447](https://github.com/cslib-community/complexitylib/issues/1447)), - using the existing matrices, lopsided graph and `Set.EqOn` answer API. Computable integer parameters - retain the source's exact real floor/ceiling specifications through checked bridges. - `Algorithms/WordRAM/ExactTriangle/` implements the complete initialized preparation component - ([#1532](https://github.com/cslib-community/complexitylib/issues/1532)). Fixed sixty-four-register - code of at most 641 flat instructions consumes raw `(D,g,n,p)` headers and signed-encoded - A–B, B–C and A–C weights. It computes the residue classes, query chunks and records and writes - both padded instance matrices. These matrices contain raw unsigned zero/one words; a signed - product consumer must charge their conversion. The supplied positive modulus is not selected - by this program. In the source regime `16 ≤ D ≤ n`, `1 ≤ g` and `p` in the square-root prime - window, actual initialized execution including halt costs at most `690n²Dg`. Every sufficiently - wide physical machine has the same output. Under `D,g,p ≤ n` and input magnitudes bounded by - `(n+2)^κ`, sufficient width is at most `(κ+18)clog₂(n+2)`, and every prefix's words and address - support are bounded by `(n+2)^(κ+19)`. The generic width and signed-fit topic modules are reused - unchanged from #1537 and #1552. These are actual execution and fit proofs, without supplied - layout, execution or solver contracts. Product composition #1581 and the full #1394 endpoint - remain separate. - `Algorithms/MatrixMultiplication/Schonhage/` adapts the tensor-recursion core from the same pin's - `Sec2/{Lemma6,Recursion,Lemma7_8,Lemma10}.lean`: the ten-term polynomial identity with explicit - error, full evaluation, shared encodings, pruned values, visited leaves and passed-set totals. - Its pure finite recursion traces are not native machine semantics or charged instruction costs. - The zero-depth empty-query root exception is explicit, with an unconditional root-or-leaf API. - See [#1459](https://github.com/cslib-community/complexitylib/issues/1459). The complete finite - thin-product component ([#1463](https://github.com/cslib-community/complexitylib/issues/1463)) - adapts `Sec2/{Levels,Lemma9,Tiling,Orders,Lemma11,Theorem5}` and needed finite magnitude bounds. - Layouts contain only indexing choices, whose existence is proved; sparse bounds are derived - from exact contributing-leaf counts. Generic function-position counts, ordered gluing, binomial - and finite-sum bounds live in small Foundations modules. Mathlib `Finset.orderEmbOfFin` indexes - both a level set and its complement, and `Finset.piecewise` restricts answers. No source machine, - problem-statement universe or solver/time hypothesis is imported. - The existing signed and unsigned APIs were reused; no source machine or separate run semantics were copied. - The conditional ordinary host and its exhaustive reference callee are complete. Native - pruned/sparse-query backend composition, a concrete fast Exact Triangle solver and the - unconditional fast input/time/width/asymptotic bound remain in - [#1394](https://github.com/cslib-community/complexitylib/issues/1394), including the improved - Section 4 route needed for the published exponent 1.9992. Anthropic's copyright and attribution - are retained; the native proofs and representation choices are local. -* **Native pruned-query evaluator.** The explicit-stack pruned-query evaluator ([#1526](https://github.com/cslib-community/complexitylib/issues/1526)) - is original native code, reusing the finite `Pruned.result`, `childSet` and trace/leaf - quantities from `Sec2/Lemma10.lean`. `PrunedQuery.queryIndex` directly applies the existing - encoder `leafIndex` to `privateLeaf`; its executable output dictionary retains digit nine - for the inner wildcard. Fixed 96-register code computes child lists by actual digit tests, - division and timestamp deduplication, scans requested cells only, and charges the full stack, - signed kernels and reporting. Its complete initialized runs and callable pointer/epoch kernel - have explicit source-magnitude, address and outside-frame contracts. The actual bound including - halt is `8192*(L+Q+1+(L+1)*|leaves(requested queries)|)`. The source Light implementation's - semantics and instruction constants are not reused. - Native wanted-query routing ([#1561](https://github.com/cslib-community/complexitylib/issues/1561)) - uses actual count, prefix and stable-scatter passes after computing the subset table and each - query's tile/code. It reuses `MaskTable.layout`, `outStrOfPos`, `wantedStrings` and the existing - `PrunedQuery.queryIndex`/encoder leaf order; no parallel layout or output-string universe is - added. Primitive counts use Mathlib's `Nat.count` and its prefix/monotonicity APIs directly. - The generic `CountingSort` topic module adds stable positions, their permutation and - bucket-span meaning, without redundant count/offset definitions. The narrow `WordRAM.Sequence` - proof module reassociates existing - structured execution derivations without changing code or charges. Original scalar headers - and raw coordinate pairs are the entire initialized input. Fixed 96-register standard code - computes all tables and grouped arrays, preserves the original input/outside frame, and - charges at most `128*(L+1)*(Q+numBands²+2^L+1)` instructions including halt. Its positive - ceiling-logarithmic admission threshold depends only on the original numerical budget. - Within-tile order is stable input order; no sorted or duplicate-free query premise is needed - by the pruned kernel. - The band catalogue retains the same leaf ordering and canonical signed words. - Native queried-product composition ([#1581](https://github.com/cslib-community/complexitylib/issues/1581)) - now connects those actual programs. It computes all protected buffers and tile arguments, - calls the shared encoders and pruned pointer/epoch kernel, and reconstructs every requested - `Matrix.mul` value in original order, retaining duplicates. Original signed matrices and raw - coordinate pairs are the entire initialized input. Raw nonnegative mode actually doubles - every matrix word before signed encoding, covering the raw 0/1 producer ABI. Copies, - header adaptation, one directory initialization, dispatch, epochs, reporting and restoration - are all charged. Existing `MaskTable.layout`, `wantedStrings`, `encodingL/R`, leaf numbering, - `Pruned.result` and stable-position meanings are reused; the existing `Wanted.tileKey_eq_iff` - lemma is exposed for the concrete tile-correctness proof without changing its statement. - The fixed 160-register program has at most 32768 flat instructions including halt and reads - no physical-width instruction. At `L=19m`, `D=4^m`, `D^18≤N`, `Q*2^m≤N²`, the complete charge - including halt is `4325376*(19m+1)*N²/D^(1/18)`. Distinct-set sparsity retains an additive - occurrence charge instead. The sufficient physical width is `128*clog₂(N+Q+A+2)`; its - magnitude/address budget and allocation have explicit polynomial envelopes in the original - dimensions, request count and entry bound. These are original native execution/resource - proofs, not a transfer of the source Light semantics or instruction constants. Exact Triangle - parameter/instance preparation and outer solving, full ordinary-3SUM composition, and the - improved Section 4 route to exponent 1.9992 remain separate work under #1394. - Caller-safe queried products ([#1618](https://github.com/cslib-community/complexitylib/issues/1618)) - rebase actual memory accesses above a runtime caller base. The generic `WordRAM.MemoryWindow` - compiler charges an addition/load/remainder for each load and an addition/store for each - store, reusing ordinary native `Exec` and `Code`. Its clipped-memory view permits dirty - workspace without bulk initialization; it is a value relation, not independent run semantics. - The original product program is unchanged. Existing `NatClog` (the exact module already - landed in #1469) and `WordRAM.Power` compute the source modulus from original counts; fixed - register normalization and two `WordRAM.ArrayNormalize` span loops are actual charged code. - The callable raw-zero/one thin-regime client uses 163 registers and at most 131072 flat - instructions. It restores local input, returns exact product words at an absolute pointer, - preserves the caller outside its window, and retains the original charged raw-to-signed - doubling. At `D=4^m`, `L=19m`, `D^18≤N`, its standalone charge including halt is at most - three times the original product charge plus `2048*(N*D+Q+1)`. Distinct-set sparsity retains - the additive occurrence charge; `Q*2^m≤N²` gives `17301504*(19m+1)*N²/D^(1/18)`. - The window is at most `(2*(N+Q+3))^128`; this deliberately large polynomial address-space - reservation is not a claim about a tightly packed physical allocation. Every width at least - `256*clog₂(base+N+Q+P+5)` suffices for initialized callers with prefix words bounded by `P`. - The arbitrary dirty-caller interface uses `P` to bound every initial register and memory - word, including words loaded before the actual remainder; native prefix bounds then follow - from that derived physical admission. Initialized scratch is zero, so its `P` only needs - to bound the protected caller prefix. The generic compiler theorem remains separate from - the concrete thin-geometry admission and saving. - The real ET-record/matrix/query adapter is tracked in - [#1626](https://github.com/cslib-community/complexitylib/issues/1626). Its fixed 320-register - native code computes the selected record address, reads all six fields, recovers the exact - `4^m` exponent with existing `NatLog`, stores local original headers and copies the two raw - matrices and interleaved occurrence coordinates above the actual producer heap. Raw zero/one - words are copied unchanged; the callee's existing loop performs charged signed doubling. - The complete 163-register callable bank is renamed together, preserving the producer's low - 64 registers. Memory relocation uses the real callable compiler, never register renaming alone. - `PreparationInput.exec_input` exposes the existing structured initialized derivation so real - caller code can compose it. Its private fit proof stays private; existing `runs_input` reuses - the new rule with unchanged statement, assumptions, exact readback and halt-inclusive budget. - The first-record initialized client supplies no layout/run/time/fit/solver certificate; - actual producer readback derives all source pointer ranges and coordinate meanings, and - actual chunk counts derive occurrence sparsity. The general caller supports every occupied - record ordinal. The per-record charge including halt is at most three times the original - product plus `4096*(nD+Q+1)`, or `34603008*(19m+1)*n²/D^(1/18)` under the admitted geometry. - The original producer word bound supplies caller `P` and heap placement; the complete wider - initialized prefixes have a positive logarithmic width threshold and polynomial words/support - independent of the physical width. Outer parameter/prime selection and instance iteration, - aggregate solving/witness reconstruction and the whole #1394 endpoint remain separate. - The original-weight consumer in - [#1639](https://github.com/cslib-community/complexitylib/issues/1639) composes actual producer, - product and verification code in 400 registers. It reads original interleaved pairs, computes - the piece interval with the native square-root kernel, and invokes the entire renamed - 32-register signed verifier only while searching and on nonzero actual answers. The native - scan counter is at most existing finite false positives plus the returned 0/1 flag. Thus a - failed record has no additive successful-scan charge. Caller memory outside the original - product window and the complete producer bank are framed; widths and all-prefix words/support - are derived from original headers/weights at every sufficiently wide physical word size. - Source Light semantics and costs are not imported or simulated by assumption. The native - verifier returns its last zero, while finite `scanPiece` returns its first; failure and - original-weight soundness agree. The existing decoded queries now expose chunk membership, - nonrepetition and rank order, with an actual initialized client. The general raw writer bound - is placed at `WriteMatrices.entry_le_one`, and shared finite piece coordinates are in the - narrow `ExactTriangle.PieceBounds` module, reused by preparation and recovery. Parameter/prime - choice, the outer ET loop, aggregate savings and the full #1394 endpoint remain open; no curated - abstract changes. - Stage `exec_*` lemmas are internal implementation APIs exported across proof modules; - callers use the flat initialized runs, width theorem or pointer/epoch kernel. The narrow - `WordRAM.ArrayAppend` and `WordRAM.InputArray` topic modules keep append/input dependencies - and interface changes away from the widely imported `Allocation` core. - Post-integration [#1553](https://github.com/cslib-community/complexitylib/issues/1553) includes - replacing pruned `powers` with sibling #1536's generic `Power.program`, preserving the - `4L+10` budget via five retained constant initializations plus its `4L+5` run. Its exact - register map, affected execution/width/cost clients and sibling integration order are - recorded there. This documentation/test repair does not change either sibling's program. -* **Typed RAM upstream core.** [CSLib #1158](https://github.com/leanprover/cslib/pull/1158) at - `beea60a15eddc91d93e92fa5af9fb965c351d0d5` proposes - `Cslib/Computability/Machines/RAM/Basic.lean`, in namespace `Cslib.RAM.Typed`. - This redesigns the local WordRAM/GenericRAM core with a register and word-addressed memory bank - per sort, `Fin (2 ^ w)` words, and absorbing halt/fault states. A halt retains the data without - choosing an output register; `RelSeries` run paths permit terminal padding. Before migrating - local clients, bridge their operation bases, input/output conventions and exact halting costs. - Algorithms, interpretation transfer, native simulations and time-class APIs remain separate - follow-ups. The existing local modules remain candidates, not copies of this submission. -* **Encoded sparse memory.** `Computability/GenericRAM/SparseMemory/*` reuses Mathlib's `AList` - with constant value type and the existing native `ComputableTidilyInO` equality, find and filter - routines. Its certificates expose actual native TM witnesses, parameterized time/space bounds, - and polynomial-time corollaries, and feed the fixed-program step and whole-run simulators in - `GenericRAM/Simulation/{Turing,Run}` (#298, #292). -* **Stateful algorithms prototype.** `Foundations/Semantics/Stateful/*` reuses Quang Dao's merged - `PFunctor.FreeM` for adaptive clients and substitution, adding persistent representation - simulation, interface composition, semantic Hoare rules and separate native-resource - certificates. Its queue and reset-and-replay constructions deliberately count backend calls; - arbitrary pure continuations do not have machine-time certificates. `CostHoare` adds client - operation-cost contracts and separate method-budget composition. The `stateful` Aesop rules - support these contracts. `WordRAM/Controller` adds fixed-code client refinements with charged - preparation, control flow and final halt; the adaptive stack example constructs its empty - representation from the standard input. `WordRAM/Function` gives separate nonuniform and - uniform RAM function-time predicates, with named-certificate reuse by `polytime`. Arbitrary continuation - synthesis, iterative clients and a Turing-machine simulation remain open. - `Foundations/Data/OrderedSet` and `Foundations/Semantics/Stateful/OrderedSet` provide neutral - optional-minimum contracts, a typed finite-set interface and adaptive clients, with no tree or - cost dependency. Bounded-key implementations may specialize insertion admissibility. - `Algorithms/Lean/SearchTree/*` reuses Mathlib's `BinaryTree`, height and node count for an - unbalanced ordered-set realization. Finite-set behavior, strict invariants and height-bounded - node visits are proved, including for the pure deletion operation `erase`; allocation, - comparison implementation and client computation are excluded from that cost model. It is not - a balanced tree or machine-time certificate. - `WordRAM/Dynamic` separately connects - fixed bounded-word operation programs and faithful memory transcripts to those contracts. - The direct shared-heap queue uses the existing FIFO operation signature and native operation - traces, with a hidden two-stack representation, a concrete-register potential, framed storage, - and separately charged initialization. Its `2 * capacity` reservation is explicit; external - client computation and argument delivery are not charged by the operation-history bound. - Coordinate placement and overlap with Srinivas's #685 and the merged #401 (`Algorithms/Lean/Query`) - before upstream submission; #685 is not copied here. This is a prototype of composition and - refinement, not a replacement for their query models or a general cell-probe lower-bound semantics. -* **Earlier review requests.** The withdrawn #1099–#1101 requested abstract word encodings and - textbook sources for FP/poly, P/poly and their machine simulation. The withdrawn #869 and - #871–#875 requested composition above configuration level; use `TransformsTapes`-style specs. - -These are coordination references, not a second record of live PR status. Check the relevant -upstream discussion before starting or submitting overlapping work. - -## Placement decisions not already recorded in source - -Paths below omit `Complexity/` and `.lean`. The default CSLib destination mirrors the source -path. A destination recorded in a source marker takes precedence; the alternatives below need -agreement during upstream review. - -| Local module or declarations | Integration note | -| --- | --- | -| `Computability/Machines/Turing/MultiTape/Encodings/StreamFold` | The candidate `streamFold_sum` generalizes the vendored proof; `streamFold` is its compatibility corollary. When resynchronizing or removing the copy, preserve the summed contract and shared proof until upstream includes them. | -| `Computability/Machines/Turing/MultiTape/Encodings/Length` | Add to the tidy branch's `Encodings/List`. | -| `Computability/Machines/Turing/MultiTape/TapeLemmas` | `exists_visitedByTapeHead_eq_Icc` and `workTapes_runFrom_eq_of_notMem_visitedByTapeHead` were relocated from `Plumbing/Shadow` (#316); upstream with `TapeLemmas`. | -| `Computability/Machines/Turing/MultiTape/Configuration` | `Turing.Action.instFinite` was relocated from `Machine/ChoiceTable` (#354); place beside `Action` in upstream `Configuration`. | -| `Computability/Machines/Turing/MultiTape/Plumbing/ExtendTapesView` | Derive the old `tapeEmb`/`noTapes` lemmas from `viewCfg`; `oneTapeCfg i = viewCfg (tapeEmb i)` by `rfl`. | -| `Computability/Machines/Turing/MultiTape/Probabilistic/IterateWhile` | Its private tape helpers parallel the vendored deterministic `Combinators/IterateWhile`; share them once the tidy branch exposes them. | -| `Computability/Machines/Turing/MultiTape/Plumbing/TransformsTapes` | `runFrom_nop_withState` was relocated from `Combinators/TidyNormalForm` (#354); place beside `runFrom_nop_one` in upstream `Plumbing/TransformsTapes`. | -| `Computability/Machines/Turing/MultiTape/Encodings/Decoder` | Total decoders with tidy certificates; add to the tidy branch's `Encodings` beside `Option`/`Pair`/`List`. | -| `Computability/Machines/Turing/MultiTape/Encodings/NatPower` | Addition to the tidy branch's `Encodings/Nat`: `encNat (2 ^ n)` and its linear clock certificate. | -| `Computability/Circuit/{Unfolding,MapOp}`, `Computability/Circuit/Encoding/MapOp` | Consider integrating into `Circuit/Formula`, `Circuit/Program`, and `Circuit/Encoding`, respectively. | -| `Computability/Circuit/Boolean/FirstOrder` | Consider a descriptive-complexity directory. | -| `Foundations/Data/BitString/{Agree,Block}` | Add to CSLib's `Foundations/Data/BitString`. | -| `Foundations/Data/List/{Fold,Lookup,Counting,Append}` | Fold invariants, lookup and register-file bounds, predicate counts, and append identities including unary headers; `Basic` reexports these topics. Consider Mathlib's `Data/List/GetD` and `Data/List/Basic`. | -| `Foundations/Relation/RelatesWithinSteps` | Bounded reachability helpers; consider Mathlib's `Logic/Relation`. | -| `Foundations/BooleanAnalysis/Fourier` | Consider Mathlib beside `Analysis/Fourier/FiniteAbelian`. | -| `Foundations/BooleanAnalysis/{Bernoulli,CoordinateSampling,Fibers,HarmonicTransform,LightPatterns,MirrorSets}`, `Foundations/Analysis/HarmonicMean` | Consider Mathlib probability APIs; generic finite-type `harmonicMean` is split into `Foundations/Analysis/HarmonicMean` (#317). | -| `Foundations/BooleanAnalysis/HadamardTester` | Consider the PCP hierarchy. | -| `Foundations/Combinatorics/CentralBinomial` | Consider Mathlib's `Data/Nat/Choose/Central` (`Nat.centralBinom_sq_mul_le`) and `Combinatorics/Choose`. | -| `Foundations/Combinatorics/{FiniteCover,Hoeffding,Hypergeometric,PowersetTail,Sunflower}` | Consider Mathlib combinatorics; probabilistic-method lemmas belong near `Combinatorics/Probabilistic`. | -| `Foundations/Data/Finset/Expect` | Product-type expectation helpers extracted from `Foundations/Combinatorics/Expander/Products` (#317); Mathlib candidates. | -| `Foundations/Data/Finset/Markov` | Mathlib candidate: `Algebra/Order/BigOperators/Group/Finset` (`Finset.card_filter_mul_le_sum`, `Finset.mul_card_filter_le_sum`). | -| `Foundations/Data/Finset/OrderGlue` | #1545 resolves the generic naming follow-up from #1496 after native bands stabilized in #1536: order-embedding/subset/complement terminology, Mathlib's `orderEmbOfFin_mem`/`range_orderEmbOfFin` and ordinary substitution in place of five redundant wrappers, and migration of finite tensor clients with checked native band clients. Retain this focused topic module. | -| `Foundations/Data/Matrix/Permanent` | Mathlib candidate: Laplace expansion of the permanent along row and column 0, beside `LinearAlgebra/Matrix/Permanent` (mirroring `Matrix.det_succ_row_zero`). | -| `Foundations/LinearAlgebra/{Rank,Matrix/CutRank,Matrix/Lindsey}` | Mathlib candidates: `LinearAlgebra/Dimension/LinearMap` and `LinearAlgebra/Matrix/Rank`; placement of Lindsey's lemma needs agreement. | -| `Probability/PairwiseIndependentHash/Shift` | Consider integration into `PairwiseIndependentHash`. | -| `Probability/PairwiseIndependentHash/Toeplitz` | Move `Matrix.toeplitz` to Mathlib beside `LinearAlgebra/Matrix/Circulant`. | -| `Probability/ApproximateCounting/Relative` | Extract the natural-number root lemmas to Mathlib. | -| `Foundations/Data/Nat/Bits` | `Nat.foldr_bit_bits` is a candidate for Mathlib's `Data/Nat/Bits`; its local relocation is complete, but upstream integration is still pending. | - -## Deprecation policy and breaking changes - -From `2110aa5a` (2026-10-19), renaming, moving or removing a public declaration already on `main` -left a deprecated alias or theorem at the old full name; the sweep integrated at `7738dcea` added -aliases for the names that changed before the rule existed and restored old binder orders where -they had changed. The library supports no downstream users yet, so #587 deleted every deprecated -declaration, and such changes now update all clients in this repository instead (`AGENTS.md`, -Proof hygiene). This list records earlier changes that had no mechanical successor. - -* **Signature changes under the old name.** `GeneralizedGeography.decide_mem_eq_any` became the - more general `List.decide_mem_eq_any`; `RotationMap.le_edgeExpansion_of_spectralExpansion_le` - takes `V` implicitly; `PolyTimeComplete` takes `{β}` before `C`; `PMF.iid_map` has the opposite - orientation; `eval_toUnbounded_atomsOf` changed its signature. -* **Removed or made private, without a successor of the same shape.** - - Raz–Wigderson helpers: `bobAnswer_aliceMatching`, `aliceGraph_swap_swap`, `bobGraph_swap_swap`, - `exists_run_oneRun`, `run_oneRun_of_disjointness`, `run_oneRun_of_mem`. - - Graph non-isomorphism: `PublicCoin.entry`, `entry_fin`, `value`, `value_fin` (use `matrixBit`) - and `mem_orbit_self`. - - Communication: `PublicCoin.majority` and `le_card_filter_ne_of_majority_ne` (use - `BitString.strictMajorityOn` and `card_filter_eq_le_half_of_strictMajorityOn_ne`); - `ecomplexity_le_succ`, `IsFoolingSet.le_ecomplexity`, - `PublicCoin.Solves.sq_mul_two_pow_le_four_pow`. - - Hamiltonian cycle: `SAT.rowLength`, `rowLength_sub_one`, `diamondArcs`, `literalArcs`, - `diamondGraphArcs`, `diamondGraphVertices`, `mem_diamondGraphArcs`, `mem_diamondGraphVertices` - (use `Diamond.graph`); `Diamond.Follows` and its lemmas are private. - - `SAT.SparseIndependentSet.mem_clauseEdges` and `mem_variableEdges`; the eight - `CookLevin.determined_of_mem_*` lemmas (private); `SharpSAT.getD_idxOf`, - `getD_mem_of_isFirst`, `isFirst_idxOf`; `Crypto.le_length_encPreimagePrefix` and - `UniformLearning.toOuterMeasure_uniformOfFintype_ne` (private); - `UniformLearning.signVal_mul_chi_mem_Icc`. - - Landings of 2026-10-19 before the rule: about 25 `AffineDisperser` names; the natural-proofs - predicates `IsNatural`, `IsLarge`, `IsUseful`, `IsCircuitTest`, `truthTableGenerator` and their - lemmas, moved into the `NaturalProof` namespace; 12 Ladner helpers (among them `boolDecoder`, - `natDecoder`, `ofBits`, `dropFalsesT`); `BakerGillSolovay.unaryLanguage` and three lemmas. -* **Meaning strengthened under the old name.** On 2026-10-20 `Turing.MultiTapeNTM.DecidesInSpace` - started to require that every branch halt (Sipser, Definition 8.1), so `NL` and `NPSPACE` - follow the textbook convention. The former meaning is `Turing.MultiTapeNTM.RecognizesInSpace` - (`DecidesInSpace.recognizesInSpace`); membership proofs now also need a per-instance halting - bound, the second component of `DecidesInSpace` (read back with - `DecidesInSpace.exists_runsInTime`). -* **Word-RAM operation bases.** On 2026-10-21 the word RAM became parametric in a basis of - operations. `Cslib.WordRAM.Op`, `Instr`, `Program` and `Code` take a basis `B` before the register - count, and `DecidableInTime`, `DecidableInTimeAt`, `LinearTime` and `PolynomialTime` take it as - their first explicit argument; the previous instruction set is `Cslib.WordRAM.Basis.standard`. - The constructors `Op.bin` and `Op.width` became definitions of the same names over the standard - basis, so construction is unchanged but pattern matching, `Op.bin.injEq` and `Op.width.injEq` - are gone (match on `Op.alu`). `Op`, `Instr` and `Code` no longer derive `Repr`. The binder `B` of - `Exec.loop`, `ExecWithin.loop` and `ExecWithin.loop_invariant` is now `T`, and the `B : Set β` - binder of the `recode` lemmas in `WordRAM/Time` is now `A'`; named-argument callers must follow. - On the same date `Cslib.Circuits.Signature.Hom` and `Signature.Hom.Preserves` moved, with their - names unchanged, from `Circuit/MapOp` to `Circuit/SignatureHom`, which `Circuit/MapOp` imports. -* **Word-RAM bases with undefined values.** On 2026-10-23 `Cslib.WordRAM.Basis.interp` became - `Option`-valued, and an operation whose value is `none` faults. The field - `Basis.interp_mono_of_not_readsWidth` replaces `Basis.interp_eq_of_not_readsWidth`; it only keeps - values that fit a word. - `Basis.Hom.preserves` is now a field of its own, an equation per word size, operation and - arguments, rather than `Signature.Hom.Preserves`. `StandardOp.eval`, `Basis.standard_interp_bin` - and `Basis.standard_interp_width` wrap their old values in `some`. `Op.eval_alu` now reads - `(B.interp w f (x.regs ∘ args)).bind (x.put w d)`: an undefined value propagates, and the word - check follows for a defined one. `Exec.alu` and `ExecWithin.alu` take the value `v` and the - hypothesis `B.interp w f (x.regs ∘ args) = some v`. `Basis.standard` keeps its semantics; the new - `Basis.exact` faults on `x - y` for `y > x` and on `x / 0` and `x % 0`. -* **Renamed modules.** On 2026-10-20 `Circuit/Boolean/MassProduction/{Sharp,SharpParameters, - SharpStatement}` became `…/{Fine,FineParameters,FineStatement}`, with their `Sharp` declarations - renamed to `Fine`. Imports of the old module paths must be updated: a module stub would have no - upstream counterpart, so none is kept. -* **Withdrawn results of 2026-10-22 (#372).** On 2026-10-22 the maintainer withdrew results merged - that morning without adequate correctness review, deleting them without aliases. An alias would - keep exactly the names being withdrawn, and none of them had been submitted to cslib or used - downstream. - - Regev's reduction (`Crypto/Lattice/LWE.lean`, whole module) and OT completeness - (`Crypto/Multiparty/ObliviousTransfer.lean`, whole module). - - `ACC0.lean`: `SymPlusCompilation`, `symPlusCompilation_zero`, - `exists_fastSymPlusEval_of_symPlusCompilation`, `exists_mem_NEXP_notMem_ACC0`, - `not_NEXP_subset_ACC0`. The duplicate `Cslib.Circuits.Boolean.SigmaP_subset_EXP` and - `SigmaP_subset_NEXP` were also deleted; use `Cslib.Complexity.SigmaP_subset_EXP` and - `SigmaP_subset_NEXP`, now in `Classes/Inclusions`. - - `IdentityTesting.lean`: `kabanets_impagliazzo`, `kabanets_impagliazzo_of_EXP_subset_P`, - `PermPITCertified`, `exists_certified_perm_family_of_PITInt_mem_P_of_mem_VP`, - `NEXP_eq_EXP_of_NEXP_subset_SigmaP`. - - `Hastad.lean`: `hastadSATProb` and its three lemmas, `hastadGraphSATProb`, and - `hastadGraphSATProb_eq_add_hastadGraphLinearCorr`. The SAT conjuncts were dropped from - `hastadGraphProb_longCode_of_satisfies` (now `graphLinearProb_longCode_of_satisfies`) and - `hastad_gapLabelCover_dichotomy` (now `gapLabelCover_dichotomy`). - - The conditional sumset-extractor frontier layer (#391), whose extractor-family hypothesis was - never discharged: `Circuit/Frontier/Extractor.lean` and `Complexity/FrontierExtractor.lean` - (whole modules), and the decimal restatements - `eventually_456249_div_100000_sub_mul_lt_innerSize`, - `circuitLowerBound_456249_div_100000`, `averageCaseBound_456249_div_100000`, - `eventually_456249_div_100000_sub_mul_lt_innerSize_of_nondeterministicDecides` and - `eventually_456249_div_100000_sub_mul_lt_card_of_aggregates`. Combine the exact-`L` theorems - with `Gaussian.circuitCoefficient_ge_456249` instead. - - #392 withdrew Gentry's bootstrapping (`Crypto/Computational/Bootstrapping.lean` and - `Bootstrapping/{HomomorphicEval,KeyChain,KeyEncryption}.lean`, whole modules), whose - `Bootstrappable` interface assumed the content of the theorem. From the Håstad modules it - deleted `GapExactThreeDistinctSAT`, `no_GapExactThreeDistinctSAT_eq_empty`, `xorThreeCNF` and - its five lemmas, `hastadLinearProb_le_of_hastadLinearCorr_le` and the same-day alias - `Cslib.PCP.Hastad.fourierCoeff_sq_signVal_le_one`. The kept long-code, folding and projection - lemmas moved to `Cslib.BitString` in `Foundations/BooleanAnalysis/LongCode` (`IsFolded` - became `IsOdd`), and `Hastad/ExactThreeDistinct` merged into - `PCP/Approximation/MaxExactThreeSAT`, keeping its declaration names; imports of the deleted - module path must be updated. `hastad_gapLabelCover_dichotomy` (now `gapLabelCover_dichotomy`) - now chooses `η` before the label sets. - - Follow-up #390 (same date and policy): the rest of the `ACC0.lean` Williams scaffolding, - `SymPlusCircuit` and its API, `evalMask` and its lemmas, `zetaTransform`, `fastZeta`, - `fastZeta_eq_zetaTransform`, `parity_mem_EXP`, `parity_mem_NEXP`, - `exists_mem_NEXP_notMem_{AC0,AC0Mod,SIZEMod}`, `exists_mem_SIZEMod_of_mem_AC0Mod`, - `NEXP_subset_PPoly_of_NEXP_subset_ACC0`, `PH_eq_SigmaP_two_of_NEXP_subset_ACC0` and - `Kannan.language_mem_SigmaP_two_of_NEXP_subset_ACC0`; the remaining `ACC0` development is - split into `ACC0/Basic` and `ACC0/Simulation`, and `NC_subset_PPoly`, `AC_subset_PPoly` and - `size_majorityDeMorganCircuit_le_sq` moved to `DepthClasses`, `FanInTwo` and `ThresholdNC1`, - all with names unchanged. The module - `Circuit/Arithmetic/IdentityTesting` is deleted. Its Schwartz–Zippel restatements - (`MvPolynomial.card_mul_card_roots_le`, `card_roots_le`, - `exists_eval_ne_zero_of_totalDegree_lt_card`; use Mathlib's - `MvPolynomial.schwartz_zippel_totalDegree`), `encArith*`, `PITInt` and its lemmas, the - arithmetic circuit combinators, the permanent chain (`minorVar` through - `isLaplacePermChain_laplacePermCircuit`), `intPermFamily` and `exists_mem_NEXP_notMem_SIZE` - are deleted. `Matrix.permanent_succ_{column,row}_zero` moved to - `Foundations/Data/Matrix/Permanent`, `Cslib.Circuits.Circuit.eval_polynomial` to - `Arithmetic/VP`, and `permFamily`, `isPFamily_permFamily` and - `totalDegree_permanent_genericMatrix_le` to `Arithmetic/Determinant`, with names unchanged. -* **Two multigraph namespaces.** Since 2026-10-20 the frontier multigraph - (`Cslib.Circuits.Frontier.Multigraph`) defines its simple graph as that of the multigraph given by - its ends (`Cslib.Multigraph`), and its module publicly imports the latter. Their shared base names - `toSimpleGraph`, `toSimpleGraph_adj`, `edgeCut` and `mem_edgeCut` are ambiguous when both - namespaces are visible, for example inside `namespace Cslib.Circuits.Frontier` or with both - opened; use dot notation (`G.toSimpleGraph_adj`) or full names. Upstreaming the frontier - multigraph therefore follows `Foundations/Combinatorics/Multigraph.lean`. - -The [historical ledger](https://github.com/cslib-community/complexitylib/blob/d0e6373970029a91018528463ca7a5ccd0fc9fd1/UPSTREAM.md) -preserves earlier integration notes and per-module follow-up ideas. - -* **Word-RAM word-size contracts (2026-10-22).** Time and encoding arguments now depend on - word size, and an ordinary predicate `allowed : α → ℕ → Prop` admits input/width pairs. - `WordSizeDependentProgram` selects code only by width; the primary decision and function classes - require a fixed register count and uniformly bounded instruction-list length. - `Uniform` variants retain one fixed program, - which may read the width. Existing fixed-code clients prove the uniform variants and can - forget uniformity. The `LogWord` wrappers express the transdichotomous assumption, require a - positive constant, and remain conditional on input fitting. Asymptotic time keeps correctness - at every admitted fitting input - and makes only the runtime estimate eventual, with constants shared by all widths. Width - schedules connect to Mathlib's real-valued big-O after casting. No effective selection of code, - width or encoding, or Turing-machine complexity identification, follows. These breaking local - API changes intentionally retain no deprecated aliases, as authorized by Erik for this redesign. +# Upstream integration + +Coordination and integration decisions not already recorded in source. + +The authoritative records are: + +* [lakefile.toml](lakefile.toml), [lake-manifest.json](lake-manifest.json) and + [lean-toolchain](lean-toolchain) for pinned versions; +* each module's `-- upstream:` markers for candidate, submitted, vendored or local-only status, + including PR numbers and copied revisions; markers can also identify blocks within a module; +* [Complexity.lean](Complexity.lean) for the module inventory. + +Use `python3 scripts/upstream_sync.py --offline` to list copied dependencies, and +`python3 scripts/upstream_sync.py` to compare recorded revisions and the base pin with upstream. +It checks head revisions, not source equality or unrecorded overlaps. Follow +[AGENTS.md](AGENTS.md#staying-current-with-upstream) for synchronization and removal, and +[CONTRIBUTING.md](CONTRIBUTING.md#upstreaming) for preparing an upstream worktree. +Do not add a ledger row for an ordinary candidate or repeat its source marker here. + +## Coordination before submission + +* **Machine calculus.** Build on Christian Reitwiessner's `crei/tidy-universal` branch and its + component PRs, including #1111, #1112 and #1138. Local `-- vendored:` comments identify required + adaptations. The branch's `IterateWhile` supersedes the version from #1112. Coordinate the + shared encodings and calculus through CSLib issue #611, including the `TapeEncodable` + interface proposed in #590. Check #767 and its split #768 before adding space-to-time + inclusions or overlapping class definitions; reconcile the local encoded, plain-machine + definitions of `P`/`NP` with its `DTIME`-based language classes. The local `DSPACE`, `L`, + `DTIME` and `EXP` (`Classes/Space/Basic`, `Classes/LogSpace`, `Classes/Time`, `Classes/EXP`) + use #767's shapes over encoded instances; once #767 lands, derive them from its + language-indexed classes rather than keeping parallel definitions. The local `PSPACE` + retains its instance-dependent polynomial space bound; + `mem_PSPACE_iff_exists_mem_DSPACE` in `Classes/Space` proves its equivalence to the local + union of `DSPACE (n ^ k)`. A bridge to #767's language-indexed classes is still separate. + Time and space constructibility are in `Classes/Constructible`, independent of hierarchy + theorems. Selected-tape specifications + formerly in `Combinators/StreamFold` and `Encodings/MultiPass` now live beside the plumbing + operations; preserve their exact space bounds when resynchronizing the copied files. + The step-machine layer (`StepMachine/*`, `Encodings/StepMachine*`, `Combinators/StepMachine` + and their helpers) and the blocks marked `@9cce4cc7` in older copies come from a later local + rewrite of the branch that is not yet published; `scripts/upstream_sync.py` reports them + against the published head until it is. The rest of the branch is still copied at `@7f7c0696`. +* **Universal simulation.** The local universal interpreter preserves Christian Reitwiessner’s + description format and execution semantics. Its resource certificates now separate + transition-description size and binary input-head length from the copied configuration; + `Universal/Input` uses the existing native streaming fold to emit a selected input symbol + without repeatedly copying list tails. Retain these local improvements when synchronizing + `Universal/Complexity`, `Bound` and their resource consumers with `crei/tidy-universal`. + This is an extension of the existing interpreter, not a buffered Hennie–Stearns backend. +* **Polynomial bounds.** The tidy calculus's size-parameterized `PolyBounded` adapts CSLib's + `PolynomiallyBounded` and shares its closure API. Coordinate this adaptation with the tidy + branch; preserve it when resynchronizing the vendored files. + `Foundations/Data/Nat/PolynomialBound/Closure.lean` is the proposed next submission following + #1067. +* **Mathlib TM2.** `Computability/Machines/Turing/TM2/*` certifies Mathlib's stack-machine + statements directly through the tidy calculus; it does not depend on the TM0 simulator in + [Complexitylib PR #72](https://github.com/SamuelSchlesinger/complexitylib/pull/72). + Its dependent finite-table helpers (`Encodings/DPi` and `Complexity/PolyTime/DPi` under + `Machines/Turing/MultiTape`) extend the existing table API. Coordinate these helpers with + the machine-calculus work above before upstreaming. The forward bridges preserve polynomial + time and transfer terminating polynomial-stack-space computations, using configuration counting + and binary-clock iteration for space. The reverse compiler in `TM2/Reverse` constructs a + finite-alphabet `FinTM2`, including canonical `haltList` cleanup, with linear time overhead and + total stack length at most input length plus native work space plus output length. + `Reverse/Transfer` proves polynomial-time and PSPACE equivalence under the original binary + encodings; `PolyTime/List` supplies the word conversions and `TM2/Recoding` transfers TM2 + computations across them without decoding the typed embeddings. + These comparisons do not preserve sublinear work-space conventions. +* **Probabilistic programs.** Coordinate the shared `PFunctor.FreeM` API with #1086–#1089 and + typed oracles with #1129. The copied PMF blocks have their own markers and removal instructions + in [Probability/PMF.lean](Complexity/Probability/PMF.lean); remove only the merged blocks. +* **Security games.** The #1068 negligible-bound base is now supplied by the cslib pin. + `Crypto/Negligible.lean` imports it and retains the #1069 `fun_prop` registrations, with + pointwise addition/multiplication wrappers needed by lambda-shaped automation clients; + remove the submitted registrations when #1069 lands. The candidate public helpers + `Negligible.add_fun` and `Negligible.mul_fun` remain until upstream declarations preserve + those full names. `Crypto/Game.lean` retains the unmerged #1069 security-game API. Its two local pointwise/common-mixture advantage bounds (#802) must also be retained when + syncing or removing that copy. + The probability/game stack copies the October 23 upstream refresh (#1062, #1069, #1070, + #1072 and #1129). `Crypto.negligible_iff` and the negligible closure lemmas now come from + the base pin; local PMF additions and negligible automation remain alongside the copied portions. + Exact revisions are recorded in their source markers. + The following hybrid layer is tracked by its own marker in `Crypto/Game/Hybrid.lean`. + `Crypto/Game/Statistical.lean` copies only the Boolean equality from #1072; its remaining + declarations require the arbitrary-type metric replacement in #1065. Until that is in the + base pin, extraction bounds quantify over every randomized test of the full encoded output, + using the existing finite-PMF metric and `Game.Indistinguishable` with all tests admissible. + Coordinate the finite PRG API and its distribution-based refactor in #1071 before adapting + `Crypto/Primitives/PRG/Game`. +* **Query complexity.** Numerical measures and certificates use the merged + `Computability/QueryComplexity` API directly, on `Fin n`/`Bool`. Generic decision trees, + restrictions and Fourier analysis retain arbitrary coordinate/output types; sensitivity + lower bounds there use explicit coordinate sets. The restriction representations agree + definitionally, with `QueryComplexity/Restriction` connecting filling to `Forces`. + Align the remaining tree representation in `Computability/DecisionTree` with #925 and + coordinate its certificate follow-up `D ≤ C₀ C₁`; no second numerical measure API is retained. +* **Word RAM.** `Computability/WordRAM/*` adapt the bounded-word RAM of the Lean formalization + accompanying the tromino undecidability paper, + [`edemaine/lean-trominoes`](https://github.com/edemaine/lean-trominoes) @6d5b36c1 (Apache-2.0), + which is not a CSLib branch. The adapted files retain the source's copyright notice + (lean-trominoes contributors) and credit Erik Demaine alongside the adaptation author in their + `Authors:` lines; coordinate follow-up submissions and attribution with him, + alongside the CSLib algorithm-model work: Shreyas Srinivas's query-combinator + programs (#685, and the register word-RAM models in his Algolean repository) and the merged + query framework (#401), which + count only lifted queries. A deeply embedded machine beneath such front ends answers, for the + RAM, the objection that pure Lean computation is uncharged. The module docstrings record the + deviations from the source. The operations form a basis (`WordRAM/Basis`) over + `Cslib.Circuits.Signature`, and translation between bases (`WordRAM/MapOp`) uses the local + `Circuit/SignatureHom` maps of signatures, so these files depend on that candidate. The + unbounded-register `Computability/RAM`, inspired by + Cook–Reckhow and supporting both unit and logarithmic costs, remains a separate candidate. +* **Generic RAM values.** `Computability/GenericRAM/*` supplies a shared finite-register RAM + interpreter with configurable value, address, configuration and literal types, operation/test + signatures, and fault policies. `WordRAM/Generic` embeds every existing word-operation basis + with exact step, fault, output and instruction-count preservation, including arbitrary initial + states; no existing public WordRAM definition changes. The signed instance follows the + lean-trominoes signed range and its address, move, halt and program-counter checks, retaining + its source credit. The algebraic instances use tagged natural words and exact field values: + only natural words can address memory. Real comparison is mathematical, noncomputable + semantics; prime-field interpretation is executable. The real variant follows the separation + of words and reals in Erickson–van der Hoog–Miltzow, but uses finite named registers, a tagged + memory and checked word arithmetic. These are explicit model choices, not a proved equivalence + with their two-array model. Coordinate this common core with the algorithm-model work above; + the existing Cook–Reckhow-inspired RAM and its logarithmic cost remain separate. + `GenericRAM/Native` separately packages existing native TM `ComputableTidilyInO` witnesses for + encoded scalar primitives, with caller-selected configuration encoding and unrestricted + input-dependent bounds. Its forgetful bridge reuses `IsComputable`; polynomiality is a separate + bound property. `GenericRAM/Native/Nat` supplies a checked bounded-natural addition model; + `WordRAM/Native` supplies complete polynomial-time `NativePrimitives` bundles for + `Basis.standard`, `Basis.additive`, `Basis.arithmetic`, and `Basis.exact` (plus restricted + sub-bases and homomorphic preimages) with unary width and binary operands (#489). + Both reuse Christian Reitwiessner's native arithmetic/encoding calculus. + `GenericRAM/Simulation/{Finite,Turing,Run}` combine these per-symbol certificates with sparse + memory and finite control to simulate single steps and clocked multi-step runs of any fixed + generic-RAM program on native multitape Turing machines + (`Cslib.GenericRAM.FiniteState.transport_inO`, `Cslib.GenericRAM.FiniteState.transport_polyTime` + and `Cslib.GenericRAM.FiniteState.transport_polyTime_of_le_const`). `transport_inO` is + parameterized by explicit time and space bounds; `transport_polyTime` needs a polynomial step + bound (`hStepsBound`) and a polynomial bound on the encoded intermediate run results + (`hOutLen`); `transport_polyTime_of_le_const` derives both from a constant step bound. Uniform + arbitrary-program dispatch and charged configuration selection remain separate. +* **Signed programs on unsigned words.** `WordRAM/Signed/*` reuse `GenericRAM.Signed`'s + range, arithmetic and interpreter. The sign-and-magnitude encoding needs one extra unsigned + bit; the arithmetic blocks use two. `State`, `Operations`, `Compile` and `Input` extend them + to complete programs over the full five-operation signed model (constants, copies, + addition, subtraction, multiplication, Euclidean division/remainder with Lean's total + zero-divisor conventions, memory, signed tests, jumps and halt). Successful source runs of `t` + instructions transfer to one fixed unsigned program at width `w + 2` within `32*t + 5` + instructions, including the five-instruction length header conversion and the final halt. Each + source integer uses one target word, source memory addresses are unchanged, nine scratch + registers precede the source register bank, and each source instruction occupies sixty-four + target code slots. Code labels need not fit a data word in the target model. Run-prefix + theorems preserve the complete source memory and register bank. This is forward correctness, + not reflection of source faults; transfer from Anthropic's modular signed machine and its + 3SUM/APSP algorithms remains separate. +* **Native shared tensor encoder.** `Algorithms/WordRAM/MatrixMultiplication/Schonhage/` implements the complete shared left/right + encoding milestone ([#1493](https://github.com/cslib-community/complexitylib/issues/1493)). + Fixed standard-WordRAM code with 32 registers transforms one coordinate at a time between + two disjoint `10^L` buffers. It starts from `State.init` with a depth header and `7^L` + sign-and-magnitude words, computes all public `encodingL`/`encodingR` values, and preserves + the input and every cell outside the two buffers. Mathlib's `finFunctionFinEquiv` supplies + least-significant-first string numbering. All stages, powers, indices, address arithmetic, + signed add/subtract blocks and the final output copy cost at most `240*(L+1)*10^L` + instructions; halt adds one. Signed operands have magnitude bounded from the original + input by `7^L*A < 2^(w-1)`; the explicit natural bound `L+64+64*10^L` must fit target width + `w+2`. The two extra bits and actual arithmetic-block costs come from the existing native + signed kernels, without a copied source machine or a whole-program simulation hypothesis. + The existing array-copy interface is retained, with a narrow generalization for disjoint + blocks in either address order or identical blocks. + This is the encoder component only; band/tile preparation and full native composition remain + in [#1394](https://github.com/cslib-community/complexitylib/issues/1394). +* **Native shared-band preparation.** The native shared-band preparation component + ([#1523](https://github.com/cslib-community/complexitylib/issues/1523)) computes a deterministic + `Layout` and both subset tables by native mask/popcount scans, computes the square-root block + count by a charged loop, and decodes each band-array index into the original row/column and + thin-dimension coordinates. Real matrix bounds are checked before row-major multiplication. + Each shared row/column band calls the unchanged encoder once; tiles refer to its catalogue + block in the existing leaf order. The fixed 96-register standard code starts from actual + `State.init` scalar/matrix input, charges snapshot and restoration of the complete original + prefix, and preserves arbitrary outside memory. All counter/address fits follow from an + explicit original-parameter word budget; signed fits follow from the original entry bound. + Its complete instruction bound, including halt, is + `81920*(L+1)*N²/(4^m)^(1/18)` when `L=19m` and `N≥(4^m)^18`. Native power, ordered-filter, + finite-digit and row-major list lemmas live in small topic modules; the existing mathematical + layout, matrix and encoding interfaces are reused. No source machine or call/time oracle + is introduced. +* **Direct 3SUM adaptation.** `Algorithms/ThreeSum/Convolution.lean` adapts the explicit + Convolution-3SUM to Exact Triangle correctness reduction from Anthropic's + [`formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp), + `Sec3/Theorem21a/Convolution.lean`. Its reduction uses the arbitrary-matrix predicate in + `Algorithms/ExactTriangle/Defs.lean`, shared with future Exact Triangle solvers. + The three weight matrices use the original formulas; an explicit nonnegative magnitude bound + also handles empty input. The native builder in `Algorithms/WordRAM/ThreeSum/{Cell,Fill,Memory}.lean` + extends the one-weight kernel with index/address calculations and the complete matrix loop. + Given its arguments in registers, it writes one instance in at most `131*t² + 5` instructions + at width `w + 2`, excluding the halt; its theorem identifies the three encoded matrices and + preserves the input. Layout, signed index/filler bounds and target address bounds are explicit. + The existing signed and unsigned APIs were reused; no source machine or separate run semantics + were copied. `Algorithms/WordRAM/ThreeSum/Host/` adds the complete charged native host: ordinary + header initialization, polynomial magnitude computation, square root, consecutive matrix layout, + all `2t` instances and a finite solver program in a disjoint register bank. Its initialized run + theorem connects the actual pre-halt state to the Convolution-3SUM answer and preserves the input. + The host's source-width threshold is `(20κ+125)clog₂(N+2)`; signed kernels use target width `w+2`. + This remains conditional on the supplied solver's native execution/correctness/frame theorem; + it does not establish a fast solver. Fast Exact Triangle and its final native backend/asymptotic + composition remain in + [#1394](https://github.com/cslib-community/complexitylib/issues/1394), including the improved + Section 4 route needed for the published exponent 1.9992. Anthropic's copyright and attribution + are retained; the native proofs and representation choices are local. + Native numeric parameter computation, signed-residue/histogram counting, trial-division prime + tables, collision-tested least-prime scans and complete two-prime modulus selection also use + the existing `WordRAM.Code`/`ExecWithin` APIs. Their explicit local fit, frame and charged-cost + contracts are composed by a fixed 128-register bounded traversal with an explicit seven-word + stack frame and exact preorder packed output. The traversal consumes a root descriptor and + runtime fuel; its polynomial local envelope derives kernel fits and charges every stack, + descriptor and address operation. It does not invoke a Convolution-3SUM solver. Composition + from initialized input through every frontend root is proved in + `Algorithms/WordRAM/ThreeSum/ChanHe/Preprocessing.lean`. One finite 128-register program per + fixed magnitude exponent returns the existing exact finite instance list. All fits derive from + `(1300κ+7865) clog₂(n+2)` physical width; every run prefix uses polynomial words/addresses even + at wider admitted widths. Its charged cost is at most + `Kκ (n+2)(sqrt(n+2)+1)(log₂(n+2)+1)^13`, including the final halt. The complete native + preprocessing milestone is #1446. `Algorithms/WordRAM/ThreeSum/Decision.lean` composes that + actual initialized output with the Convolution host (#1559): raw registers 78/79/116 supply the + actual pointer table, count and common packed length. The caller-owned host reads each signed + payload directly and reserves matrices above twice the preprocessing polynomial word bound, + preserving every future instance. One finite program per fixed exponent and certified finite + Exact Triangle block returns the existing distinct-position verdict, with derived positive + logarithmic physical width, polynomial words/space at every admitted-width prefix, and a fully + charged compositional bound at the actual packed size. `Host.Callee` requires actual finite-code + execution, matrix correctness, logarithmic width and a protected-prefix frame on arbitrary old + scratch memory; `Host.Slow.callee` is a proved exhaustive native instance. This conditional host + does not provide a fast solver. The unconditional native solver/backend and final asymptotic + composition, including exponent 1.9992, remain #1394. These components do not affect the curated + headlines. The caller layout, exhaustive callee and ordinary composition are original; the + underlying finite reductions and builder retain their existing Anthropic credit. + + Bounded API follow-ups from the traversal/preprocessing reviews: merge the stage proof modules + `PreprocessingSnapshot`, `PreprocessingPrimes`, `PreprocessingSetup`, `PreprocessingFinish`, + `PreprocessingInvariant`, `PreprocessingStep` and `PreprocessingLoop` with their controller + consumer `PreprocessingExecute`, or use `import all` to make their proof plumbing private. + This includes `Saved`, `ForestCursor`, `snapshot_exec`, `primes_exec`, `setupTraversal_exec`, + `finish_exec`, `execute_next_root` and `forest_body_exec`; keep the initialized-input, readback, + width and resource endpoints available through `Preprocessing`. Separately, move + `modulus_bounds_of_candidates` from `SelectorBounds` beside `Collisions`' `modulus_bounds` and + derive or cross-reference the stronger bound, migrating `TraversalCall` and selector tests. + Derive `Structured`'s `ExecWithin.loop_invariant` as the `Data`-state case of + `GhostLoop`'s `ExecWithin.loop_invariant_state`, preserving the existing copy/residue/padding/ + search/reservation clients. Resolve the current import direction by keeping both rules in one + loop topic module before changing those clients. + + `Algorithms/ThreeSum/ThreeSets.lean` and `ArrayPacking.lean` provide the generic distinct-position + frontend and three-array packing. `Algorithms/ThreeSum/ChanHe/` now adapts the core mathematical + reduction: minimum-prime searches, collision/heavy-element bounds, light-element arrays, bounded + ternary recursion and composition with the frontend. At polynomial magnitudes it gives explicit + length, count and magnitude bounds; `scanPairs` counts candidate-prime/element pairs and is not + a charged running time. Generic finite-average and prime-counting lemmas live in Foundations. + See milestone [#1435](https://github.com/cslib-community/complexitylib/issues/1435). + `Algorithms/ExactTriangle/Lopsided.lean` adapts the finite unweighted lopsided query/counting + interface from the same pin's `Sec3/MatrixLanguage.lean`: arbitrary-injection zero padding, + common-neighbor counts as product entries, detection, the zero/one converse, and signed products + from `4 * β²` count instances with the original queried edges. Its digit identities live in + `Foundations/Data/Matrix/IntegerBits.lean`, reuse Mathlib's pointwise positive/negative parts, + with descriptive lemmas in `Cslib.Matrix`. The scalar magnitude-bit bound, including zero, + lives in `Foundations/Data/Int/LogBounds.lean`. Count answers use `Set.EqOn`; detection + consumes a direct iff on queried edges. No additional weighted-triangle predicate or source + machine is introduced. + This is the mathematical interface milestone + [#1438](https://github.com/cslib-community/complexitylib/issues/1438), with no sparsity assumption, + preprocessing/time claim or solver. `Algorithms/ExactTriangle/{Weights,Hashing}.lean` now adapts + the modular counting/coefficient identities, minimum-count prime and uniform false-positive + bound from `Sec3/Theorem17/Hashing.lean`, using supplied matrices and the existing decision + predicate. The application-independent `Nat.sqrtPrimeWindow` API lives in + `Foundations/NumberTheory/SqrtPrimeWindow.lean`; its integer-square characterization supports + native enumeration, and splitting and witness recovery reuse it. + The quotient/coefficient API lives in `Foundations/RingTheory/CyclicIntRing.lean`; the uniform + all-window prime-count bound lives in `Foundations/NumberTheory/PrimeWindow.lean`, retaining + the source credit to Mathlib, Patrick Stevens and Bolton Bailey. These are mathematical + statements, not native instructions or an assumed solver. See + [#1442](https://github.com/cslib-community/complexitylib/issues/1442). + `Algorithms/ExactTriangle/{Splitting,Witnesses}.lean` adapts the complete finite splitting and + witness recovery component ([#1447](https://github.com/cslib-community/complexitylib/issues/1447)), + using the existing matrices, lopsided graph and `Set.EqOn` answer API. Computable integer parameters + retain the source's exact real floor/ceiling specifications through checked bridges. + `Algorithms/WordRAM/ExactTriangle/` implements the complete initialized preparation component + ([#1532](https://github.com/cslib-community/complexitylib/issues/1532)). Fixed sixty-four-register + code of at most 641 flat instructions consumes raw `(D,g,n,p)` headers and signed-encoded + A–B, B–C and A–C weights. It computes the residue classes, query chunks and records and writes + both padded instance matrices. These matrices contain raw unsigned zero/one words; a signed + product consumer must charge their conversion. The supplied positive modulus is not selected + by this program. In the source regime `16 ≤ D ≤ n`, `1 ≤ g` and `p` in the square-root prime + window, actual initialized execution including halt costs at most `690n²Dg`. Every sufficiently + wide physical machine has the same output. Under `D,g,p ≤ n` and input magnitudes bounded by + `(n+2)^κ`, sufficient width is at most `(κ+18)clog₂(n+2)`, and every prefix's words and address + support are bounded by `(n+2)^(κ+19)`. The generic width and signed-fit topic modules are reused + unchanged from #1537 and #1552. These are actual execution and fit proofs, without supplied + layout, execution or solver contracts. Product composition #1581 and the full #1394 endpoint + remain separate. + `Algorithms/MatrixMultiplication/Schonhage/` adapts the tensor-recursion core from the same pin's + `Sec2/{Lemma6,Recursion,Lemma7_8,Lemma10}.lean`: the ten-term polynomial identity with explicit + error, full evaluation, shared encodings, pruned values, visited leaves and passed-set totals. + Its pure finite recursion traces are not native machine semantics or charged instruction costs. + The zero-depth empty-query root exception is explicit, with an unconditional root-or-leaf API. + See [#1459](https://github.com/cslib-community/complexitylib/issues/1459). The complete finite + thin-product component ([#1463](https://github.com/cslib-community/complexitylib/issues/1463)) + adapts `Sec2/{Levels,Lemma9,Tiling,Orders,Lemma11,Theorem5}` and needed finite magnitude bounds. + Layouts contain only indexing choices, whose existence is proved; sparse bounds are derived + from exact contributing-leaf counts. Generic function-position counts, ordered gluing, binomial + and finite-sum bounds live in small Foundations modules. Mathlib `Finset.orderEmbOfFin` indexes + both a level set and its complement, and `Finset.piecewise` restricts answers. No source machine, + problem-statement universe or solver/time hypothesis is imported. + The existing signed and unsigned APIs were reused; no source machine or separate run semantics were copied. + The conditional ordinary host and its exhaustive reference callee are complete. Native + pruned/sparse-query backend composition, a concrete fast Exact Triangle solver and the + unconditional fast input/time/width/asymptotic bound remain in + [#1394](https://github.com/cslib-community/complexitylib/issues/1394), including the improved + Section 4 route needed for the published exponent 1.9992. Anthropic's copyright and attribution + are retained; the native proofs and representation choices are local. +* **Native pruned-query evaluator.** The explicit-stack pruned-query evaluator ([#1526](https://github.com/cslib-community/complexitylib/issues/1526)) + is original native code, reusing the finite `Pruned.result`, `childSet` and trace/leaf + quantities from `Sec2/Lemma10.lean`. `PrunedQuery.queryIndex` directly applies the existing + encoder `leafIndex` to `privateLeaf`; its executable output dictionary retains digit nine + for the inner wildcard. Fixed 96-register code computes child lists by actual digit tests, + division and timestamp deduplication, scans requested cells only, and charges the full stack, + signed kernels and reporting. Its complete initialized runs and callable pointer/epoch kernel + have explicit source-magnitude, address and outside-frame contracts. The actual bound including + halt is `8192*(L+Q+1+(L+1)*|leaves(requested queries)|)`. The source Light implementation's + semantics and instruction constants are not reused. + Native wanted-query routing ([#1561](https://github.com/cslib-community/complexitylib/issues/1561)) + uses actual count, prefix and stable-scatter passes after computing the subset table and each + query's tile/code. It reuses `MaskTable.layout`, `outStrOfPos`, `wantedStrings` and the existing + `PrunedQuery.queryIndex`/encoder leaf order; no parallel layout or output-string universe is + added. Primitive counts use Mathlib's `Nat.count` and its prefix/monotonicity APIs directly. + The generic `CountingSort` topic module adds stable positions, their permutation and + bucket-span meaning, without redundant count/offset definitions. The narrow `WordRAM.Sequence` + proof module reassociates existing + structured execution derivations without changing code or charges. Original scalar headers + and raw coordinate pairs are the entire initialized input. Fixed 96-register standard code + computes all tables and grouped arrays, preserves the original input/outside frame, and + charges at most `128*(L+1)*(Q+numBands²+2^L+1)` instructions including halt. Its positive + ceiling-logarithmic admission threshold depends only on the original numerical budget. + Within-tile order is stable input order; no sorted or duplicate-free query premise is needed + by the pruned kernel. + The band catalogue retains the same leaf ordering and canonical signed words. + Native queried-product composition ([#1581](https://github.com/cslib-community/complexitylib/issues/1581)) + now connects those actual programs. It computes all protected buffers and tile arguments, + calls the shared encoders and pruned pointer/epoch kernel, and reconstructs every requested + `Matrix.mul` value in original order, retaining duplicates. Original signed matrices and raw + coordinate pairs are the entire initialized input. Raw nonnegative mode actually doubles + every matrix word before signed encoding, covering the raw 0/1 producer ABI. Copies, + header adaptation, one directory initialization, dispatch, epochs, reporting and restoration + are all charged. Existing `MaskTable.layout`, `wantedStrings`, `encodingL/R`, leaf numbering, + `Pruned.result` and stable-position meanings are reused; the existing `Wanted.tileKey_eq_iff` + lemma is exposed for the concrete tile-correctness proof without changing its statement. + The fixed 160-register program has at most 32768 flat instructions including halt and reads + no physical-width instruction. At `L=19m`, `D=4^m`, `D^18≤N`, `Q*2^m≤N²`, the complete charge + including halt is `4325376*(19m+1)*N²/D^(1/18)`. Distinct-set sparsity retains an additive + occurrence charge instead. The sufficient physical width is `128*clog₂(N+Q+A+2)`; its + magnitude/address budget and allocation have explicit polynomial envelopes in the original + dimensions, request count and entry bound. These are original native execution/resource + proofs, not a transfer of the source Light semantics or instruction constants. Exact Triangle + parameter/instance preparation and outer solving, full ordinary-3SUM composition, and the + improved Section 4 route to exponent 1.9992 remain separate work under #1394. + Caller-safe queried products ([#1618](https://github.com/cslib-community/complexitylib/issues/1618)) + rebase actual memory accesses above a runtime caller base. The generic `WordRAM.MemoryWindow` + compiler charges an addition/load/remainder for each load and an addition/store for each + store, reusing ordinary native `Exec` and `Code`. Its clipped-memory view permits dirty + workspace without bulk initialization; it is a value relation, not independent run semantics. + The original product program is unchanged. Existing `NatClog` (the exact module already + landed in #1469) and `WordRAM.Power` compute the source modulus from original counts; fixed + register normalization and two `WordRAM.ArrayNormalize` span loops are actual charged code. + The callable raw-zero/one thin-regime client uses 163 registers and at most 131072 flat + instructions. It restores local input, returns exact product words at an absolute pointer, + preserves the caller outside its window, and retains the original charged raw-to-signed + doubling. At `D=4^m`, `L=19m`, `D^18≤N`, its standalone charge including halt is at most + three times the original product charge plus `2048*(N*D+Q+1)`. Distinct-set sparsity retains + the additive occurrence charge; `Q*2^m≤N²` gives `17301504*(19m+1)*N²/D^(1/18)`. + The window is at most `(2*(N+Q+3))^128`; this deliberately large polynomial address-space + reservation is not a claim about a tightly packed physical allocation. Every width at least + `256*clog₂(base+N+Q+P+5)` suffices for initialized callers with prefix words bounded by `P`. + The arbitrary dirty-caller interface uses `P` to bound every initial register and memory + word, including words loaded before the actual remainder; native prefix bounds then follow + from that derived physical admission. Initialized scratch is zero, so its `P` only needs + to bound the protected caller prefix. The generic compiler theorem remains separate from + the concrete thin-geometry admission and saving. + The real ET-record/matrix/query adapter is tracked in + [#1626](https://github.com/cslib-community/complexitylib/issues/1626). Its fixed 320-register + native code computes the selected record address, reads all six fields, recovers the exact + `4^m` exponent with existing `NatLog`, stores local original headers and copies the two raw + matrices and interleaved occurrence coordinates above the actual producer heap. Raw zero/one + words are copied unchanged; the callee's existing loop performs charged signed doubling. + The complete 163-register callable bank is renamed together, preserving the producer's low + 64 registers. Memory relocation uses the real callable compiler, never register renaming alone. + `PreparationInput.exec_input` exposes the existing structured initialized derivation so real + caller code can compose it. Its private fit proof stays private; existing `runs_input` reuses + the new rule with unchanged statement, assumptions, exact readback and halt-inclusive budget. + The first-record initialized client supplies no layout/run/time/fit/solver certificate; + actual producer readback derives all source pointer ranges and coordinate meanings, and + actual chunk counts derive occurrence sparsity. The general caller supports every occupied + record ordinal. The per-record charge including halt is at most three times the original + product plus `4096*(nD+Q+1)`, or `34603008*(19m+1)*n²/D^(1/18)` under the admitted geometry. + The original producer word bound supplies caller `P` and heap placement; the complete wider + initialized prefixes have a positive logarithmic width threshold and polynomial words/support + independent of the physical width. Outer parameter/prime selection and instance iteration, + aggregate solving/witness reconstruction and the whole #1394 endpoint remain separate. + The original-weight consumer in + [#1639](https://github.com/cslib-community/complexitylib/issues/1639) composes actual producer, + product and verification code in 400 registers. It reads original interleaved pairs, computes + the piece interval with the native square-root kernel, and invokes the entire renamed + 32-register signed verifier only while searching and on nonzero actual answers. The native + scan counter is at most existing finite false positives plus the returned 0/1 flag. Thus a + failed record has no additive successful-scan charge. Caller memory outside the original + product window and the complete producer bank are framed; widths and all-prefix words/support + are derived from original headers/weights at every sufficiently wide physical word size. + Source Light semantics and costs are not imported or simulated by assumption. The native + verifier returns its last zero, while finite `scanPiece` returns its first; failure and + original-weight soundness agree. The existing decoded queries now expose chunk membership, + nonrepetition and rank order, with an actual initialized client. The general raw writer bound + is placed at `WriteMatrices.entry_le_one`, and shared finite piece coordinates are in the + narrow `ExactTriangle.PieceBounds` module, reused by preparation and recovery. Parameter/prime + choice, the outer ET loop, aggregate savings and the full #1394 endpoint remain open; no curated + abstract changes. + Stage `exec_*` lemmas are internal implementation APIs exported across proof modules; + callers use the flat initialized runs, width theorem or pointer/epoch kernel. The narrow + `WordRAM.ArrayAppend` and `WordRAM.InputArray` topic modules keep append/input dependencies + and interface changes away from the widely imported `Allocation` core. + Post-integration [#1553](https://github.com/cslib-community/complexitylib/issues/1553) includes + replacing pruned `powers` with sibling #1536's generic `Power.program`, preserving the + `4L+10` budget via five retained constant initializations plus its `4L+5` run. Its exact + register map, affected execution/width/cost clients and sibling integration order are + recorded there. This documentation/test repair does not change either sibling's program. +* **Typed RAM upstream core.** [CSLib #1158](https://github.com/leanprover/cslib/pull/1158) at + `beea60a15eddc91d93e92fa5af9fb965c351d0d5` proposes + `Cslib/Computability/Machines/RAM/Basic.lean`, in namespace `Cslib.RAM.Typed`. + This redesigns the local WordRAM/GenericRAM core with a register and word-addressed memory bank + per sort, `Fin (2 ^ w)` words, and absorbing halt/fault states. A halt retains the data without + choosing an output register; `RelSeries` run paths permit terminal padding. Before migrating + local clients, bridge their operation bases, input/output conventions and exact halting costs. + Algorithms, interpretation transfer, native simulations and time-class APIs remain separate + follow-ups. The existing local modules remain candidates, not copies of this submission. +* **Encoded sparse memory.** `Computability/GenericRAM/SparseMemory/*` reuses Mathlib's `AList` + with constant value type and the existing native `ComputableTidilyInO` equality, find and filter + routines. Its certificates expose actual native TM witnesses, parameterized time/space bounds, + and polynomial-time corollaries, and feed the fixed-program step and whole-run simulators in + `GenericRAM/Simulation/{Turing,Run}` (#298, #292). +* **Stateful algorithms prototype.** `Foundations/Semantics/Stateful/*` reuses Quang Dao's merged + `PFunctor.FreeM` for adaptive clients and substitution, adding persistent representation + simulation, interface composition, semantic Hoare rules and separate native-resource + certificates. Its queue and reset-and-replay constructions deliberately count backend calls; + arbitrary pure continuations do not have machine-time certificates. `CostHoare` adds client + operation-cost contracts and separate method-budget composition. The `stateful` Aesop rules + support these contracts. `WordRAM/Controller` adds fixed-code client refinements with charged + preparation, control flow and final halt; the adaptive stack example constructs its empty + representation from the standard input. `WordRAM/Function` gives separate nonuniform and + uniform RAM function-time predicates, with named-certificate reuse by `polytime`. Arbitrary continuation + synthesis, iterative clients and a Turing-machine simulation remain open. + `Foundations/Data/OrderedSet` and `Foundations/Semantics/Stateful/OrderedSet` provide neutral + optional-minimum contracts, a typed finite-set interface and adaptive clients, with no tree or + cost dependency. Bounded-key implementations may specialize insertion admissibility. + `Algorithms/Lean/SearchTree/*` reuses Mathlib's `BinaryTree`, height and node count for an + unbalanced ordered-set realization. Finite-set behavior, strict invariants and height-bounded + node visits are proved, including for the pure deletion operation `erase`; allocation, + comparison implementation and client computation are excluded from that cost model. It is not + a balanced tree or machine-time certificate. + `WordRAM/Dynamic` separately connects + fixed bounded-word operation programs and faithful memory transcripts to those contracts. + The direct shared-heap queue uses the existing FIFO operation signature and native operation + traces, with a hidden two-stack representation, a concrete-register potential, framed storage, + and separately charged initialization. Its `2 * capacity` reservation is explicit; external + client computation and argument delivery are not charged by the operation-history bound. + Coordinate placement and overlap with Srinivas's #685 and the merged #401 (`Algorithms/Lean/Query`) + before upstream submission; #685 is not copied here. This is a prototype of composition and + refinement, not a replacement for their query models or a general cell-probe lower-bound semantics. +* **Earlier review requests.** The withdrawn #1099–#1101 requested abstract word encodings and + textbook sources for FP/poly, P/poly and their machine simulation. The withdrawn #869 and + #871–#875 requested composition above configuration level; use `TransformsTapes`-style specs. + +These are coordination references, not a second record of live PR status. Check the relevant +upstream discussion before starting or submitting overlapping work. + +## Placement decisions not already recorded in source + +Paths below omit `Complexity/` and `.lean`. The default CSLib destination mirrors the source +path. A destination recorded in a source marker takes precedence; the alternatives below need +agreement during upstream review. + +| Local module or declarations | Integration note | +| --- | --- | +| `Computability/Machines/Turing/MultiTape/Encodings/StreamFold` | The candidate `streamFold_sum` generalizes the vendored proof; `streamFold` is its compatibility corollary. When resynchronizing or removing the copy, preserve the summed contract and shared proof until upstream includes them. | +| `Computability/Machines/Turing/MultiTape/Encodings/Length` | Add to the tidy branch's `Encodings/List`. | +| `Computability/Machines/Turing/MultiTape/TapeLemmas` | `exists_visitedByTapeHead_eq_Icc` and `workTapes_runFrom_eq_of_notMem_visitedByTapeHead` were relocated from `Plumbing/Shadow` (#316); upstream with `TapeLemmas`. | +| `Computability/Machines/Turing/MultiTape/Configuration` | `Turing.Action.instFinite` was relocated from `Machine/ChoiceTable` (#354); place beside `Action` in upstream `Configuration`. | +| `Computability/Machines/Turing/MultiTape/Plumbing/ExtendTapesView` | Derive the old `tapeEmb`/`noTapes` lemmas from `viewCfg`; `oneTapeCfg i = viewCfg (tapeEmb i)` by `rfl`. | +| `Computability/Machines/Turing/MultiTape/Probabilistic/IterateWhile` | Its private tape helpers parallel the vendored deterministic `Combinators/IterateWhile`; share them once the tidy branch exposes them. | +| `Computability/Machines/Turing/MultiTape/Plumbing/TransformsTapes` | `runFrom_nop_withState` was relocated from `Combinators/TidyNormalForm` (#354); place beside `runFrom_nop_one` in upstream `Plumbing/TransformsTapes`. | +| `Computability/Machines/Turing/MultiTape/Encodings/Decoder` | Total decoders with tidy certificates; add to the tidy branch's `Encodings` beside `Option`/`Pair`/`List`. | +| `Computability/Machines/Turing/MultiTape/Encodings/NatPower` | Addition to the tidy branch's `Encodings/Nat`: `encNat (2 ^ n)` and its linear clock certificate. | +| `Computability/Circuit/{Unfolding,MapOp}`, `Computability/Circuit/Encoding/MapOp` | Consider integrating into `Circuit/Formula`, `Circuit/Program`, and `Circuit/Encoding`, respectively. | +| `Computability/Circuit/Boolean/FirstOrder` | Consider a descriptive-complexity directory. | +| `Foundations/Data/BitString/{Agree,Block}` | Add to CSLib's `Foundations/Data/BitString`. | +| `Foundations/Data/List/{Fold,Lookup,Counting,Append}` | Fold invariants, lookup and register-file bounds, predicate counts, and append identities including unary headers; `Basic` reexports these topics. Consider Mathlib's `Data/List/GetD` and `Data/List/Basic`. | +| `Foundations/Relation/RelatesWithinSteps` | Bounded reachability helpers; consider Mathlib's `Logic/Relation`. | +| `Foundations/BooleanAnalysis/Fourier` | Consider Mathlib beside `Analysis/Fourier/FiniteAbelian`. | +| `Foundations/BooleanAnalysis/{Bernoulli,CoordinateSampling,Fibers,HarmonicTransform,LightPatterns,MirrorSets}`, `Foundations/Analysis/HarmonicMean` | Consider Mathlib probability APIs; generic finite-type `harmonicMean` is split into `Foundations/Analysis/HarmonicMean` (#317). | +| `Foundations/BooleanAnalysis/HadamardTester` | Consider the PCP hierarchy. | +| `Foundations/Combinatorics/CentralBinomial` | Consider Mathlib's `Data/Nat/Choose/Central` (`Nat.centralBinom_sq_mul_le`) and `Combinatorics/Choose`. | +| `Foundations/Combinatorics/{FiniteCover,Hoeffding,Hypergeometric,PowersetTail,Sunflower}` | Consider Mathlib combinatorics; probabilistic-method lemmas belong near `Combinatorics/Probabilistic`. | +| `Foundations/Data/Finset/Expect` | Product-type expectation helpers extracted from `Foundations/Combinatorics/Expander/Products` (#317); Mathlib candidates. | +| `Foundations/Data/Finset/Markov` | Mathlib candidate: `Algebra/Order/BigOperators/Group/Finset` (`Finset.card_filter_mul_le_sum`, `Finset.mul_card_filter_le_sum`). | +| `Foundations/Data/Finset/OrderGlue` | #1545 resolves the generic naming follow-up from #1496 after native bands stabilized in #1536: order-embedding/subset/complement terminology, Mathlib's `orderEmbOfFin_mem`/`range_orderEmbOfFin` and ordinary substitution in place of five redundant wrappers, and migration of finite tensor clients with checked native band clients. Retain this focused topic module. | +| `Foundations/Data/Matrix/Permanent` | Mathlib candidate: Laplace expansion of the permanent along row and column 0, beside `LinearAlgebra/Matrix/Permanent` (mirroring `Matrix.det_succ_row_zero`). | +| `Foundations/LinearAlgebra/{Rank,Matrix/CutRank,Matrix/Lindsey}` | Mathlib candidates: `LinearAlgebra/Dimension/LinearMap` and `LinearAlgebra/Matrix/Rank`; placement of Lindsey's lemma needs agreement. | +| `Probability/PairwiseIndependentHash/Shift` | Consider integration into `PairwiseIndependentHash`. | +| `Probability/PairwiseIndependentHash/Toeplitz` | Move `Matrix.toeplitz` to Mathlib beside `LinearAlgebra/Matrix/Circulant`. | +| `Probability/ApproximateCounting/Relative` | Extract the natural-number root lemmas to Mathlib. | +| `Foundations/Data/Nat/Bits` | `Nat.foldr_bit_bits` is a candidate for Mathlib's `Data/Nat/Bits`; its local relocation is complete, but upstream integration is still pending. | + +## Deprecation policy and breaking changes + +From `2110aa5a` (2026-10-19), renaming, moving or removing a public declaration already on `main` +left a deprecated alias or theorem at the old full name; the sweep integrated at `7738dcea` added +aliases for the names that changed before the rule existed and restored old binder orders where +they had changed. The library supports no downstream users yet, so #587 deleted every deprecated +declaration, and such changes now update all clients in this repository instead (`AGENTS.md`, +Proof hygiene). This list records earlier changes that had no mechanical successor. + +* **Signature changes under the old name.** `GeneralizedGeography.decide_mem_eq_any` became the + more general `List.decide_mem_eq_any`; `RotationMap.le_edgeExpansion_of_spectralExpansion_le` + takes `V` implicitly; `PolyTimeComplete` takes `{β}` before `C`; `PMF.iid_map` has the opposite + orientation; `eval_toUnbounded_atomsOf` changed its signature. +* **Removed or made private, without a successor of the same shape.** + - Raz–Wigderson helpers: `bobAnswer_aliceMatching`, `aliceGraph_swap_swap`, `bobGraph_swap_swap`, + `exists_run_oneRun`, `run_oneRun_of_disjointness`, `run_oneRun_of_mem`. + - Graph non-isomorphism: `PublicCoin.entry`, `entry_fin`, `value`, `value_fin` (use `matrixBit`) + and `mem_orbit_self`. + - Communication: `PublicCoin.majority` and `le_card_filter_ne_of_majority_ne` (use + `BitString.strictMajorityOn` and `card_filter_eq_le_half_of_strictMajorityOn_ne`); + `ecomplexity_le_succ`, `IsFoolingSet.le_ecomplexity`, + `PublicCoin.Solves.sq_mul_two_pow_le_four_pow`. + - Hamiltonian cycle: `SAT.rowLength`, `rowLength_sub_one`, `diamondArcs`, `literalArcs`, + `diamondGraphArcs`, `diamondGraphVertices`, `mem_diamondGraphArcs`, `mem_diamondGraphVertices` + (use `Diamond.graph`); `Diamond.Follows` and its lemmas are private. + - `SAT.SparseIndependentSet.mem_clauseEdges` and `mem_variableEdges`; the eight + `CookLevin.determined_of_mem_*` lemmas (private); `SharpSAT.getD_idxOf`, + `getD_mem_of_isFirst`, `isFirst_idxOf`; `Crypto.le_length_encPreimagePrefix` and + `UniformLearning.toOuterMeasure_uniformOfFintype_ne` (private); + `UniformLearning.signVal_mul_chi_mem_Icc`. + - Landings of 2026-10-19 before the rule: about 25 `AffineDisperser` names; the natural-proofs + predicates `IsNatural`, `IsLarge`, `IsUseful`, `IsCircuitTest`, `truthTableGenerator` and their + lemmas, moved into the `NaturalProof` namespace; 12 Ladner helpers (among them `boolDecoder`, + `natDecoder`, `ofBits`, `dropFalsesT`); `BakerGillSolovay.unaryLanguage` and three lemmas. +* **Meaning strengthened under the old name.** On 2026-10-20 `Turing.MultiTapeNTM.DecidesInSpace` + started to require that every branch halt (Sipser, Definition 8.1), so `NL` and `NPSPACE` + follow the textbook convention. The former meaning is `Turing.MultiTapeNTM.RecognizesInSpace` + (`DecidesInSpace.recognizesInSpace`); membership proofs now also need a per-instance halting + bound, the second component of `DecidesInSpace` (read back with + `DecidesInSpace.exists_runsInTime`). +* **Word-RAM operation bases.** On 2026-10-21 the word RAM became parametric in a basis of + operations. `Cslib.WordRAM.Op`, `Instr`, `Program` and `Code` take a basis `B` before the register + count, and `DecidableInTime`, `DecidableInTimeAt`, `LinearTime` and `PolynomialTime` take it as + their first explicit argument; the previous instruction set is `Cslib.WordRAM.Basis.standard`. + The constructors `Op.bin` and `Op.width` became definitions of the same names over the standard + basis, so construction is unchanged but pattern matching, `Op.bin.injEq` and `Op.width.injEq` + are gone (match on `Op.alu`). `Op`, `Instr` and `Code` no longer derive `Repr`. The binder `B` of + `Exec.loop`, `ExecWithin.loop` and `ExecWithin.loop_invariant` is now `T`, and the `B : Set β` + binder of the `recode` lemmas in `WordRAM/Time` is now `A'`; named-argument callers must follow. + On the same date `Cslib.Circuits.Signature.Hom` and `Signature.Hom.Preserves` moved, with their + names unchanged, from `Circuit/MapOp` to `Circuit/SignatureHom`, which `Circuit/MapOp` imports. +* **Word-RAM bases with undefined values.** On 2026-10-23 `Cslib.WordRAM.Basis.interp` became + `Option`-valued, and an operation whose value is `none` faults. The field + `Basis.interp_mono_of_not_readsWidth` replaces `Basis.interp_eq_of_not_readsWidth`; it only keeps + values that fit a word. + `Basis.Hom.preserves` is now a field of its own, an equation per word size, operation and + arguments, rather than `Signature.Hom.Preserves`. `StandardOp.eval`, `Basis.standard_interp_bin` + and `Basis.standard_interp_width` wrap their old values in `some`. `Op.eval_alu` now reads + `(B.interp w f (x.regs ∘ args)).bind (x.put w d)`: an undefined value propagates, and the word + check follows for a defined one. `Exec.alu` and `ExecWithin.alu` take the value `v` and the + hypothesis `B.interp w f (x.regs ∘ args) = some v`. `Basis.standard` keeps its semantics; the new + `Basis.exact` faults on `x - y` for `y > x` and on `x / 0` and `x % 0`. +* **Renamed modules.** On 2026-10-20 `Circuit/Boolean/MassProduction/{Sharp,SharpParameters, + SharpStatement}` became `…/{Fine,FineParameters,FineStatement}`, with their `Sharp` declarations + renamed to `Fine`. Imports of the old module paths must be updated: a module stub would have no + upstream counterpart, so none is kept. +* **Withdrawn results of 2026-10-22 (#372).** On 2026-10-22 the maintainer withdrew results merged + that morning without adequate correctness review, deleting them without aliases. An alias would + keep exactly the names being withdrawn, and none of them had been submitted to cslib or used + downstream. + - Regev's reduction (`Crypto/Lattice/LWE.lean`, whole module) and OT completeness + (`Crypto/Multiparty/ObliviousTransfer.lean`, whole module). + - `ACC0.lean`: `SymPlusCompilation`, `symPlusCompilation_zero`, + `exists_fastSymPlusEval_of_symPlusCompilation`, `exists_mem_NEXP_notMem_ACC0`, + `not_NEXP_subset_ACC0`. The duplicate `Cslib.Circuits.Boolean.SigmaP_subset_EXP` and + `SigmaP_subset_NEXP` were also deleted; use `Cslib.Complexity.SigmaP_subset_EXP` and + `SigmaP_subset_NEXP`, now in `Classes/Inclusions`. + - `IdentityTesting.lean`: `kabanets_impagliazzo`, `kabanets_impagliazzo_of_EXP_subset_P`, + `PermPITCertified`, `exists_certified_perm_family_of_PITInt_mem_P_of_mem_VP`, + `NEXP_eq_EXP_of_NEXP_subset_SigmaP`. + - `Hastad.lean`: `hastadSATProb` and its three lemmas, `hastadGraphSATProb`, and + `hastadGraphSATProb_eq_add_hastadGraphLinearCorr`. The SAT conjuncts were dropped from + `hastadGraphProb_longCode_of_satisfies` (now `graphLinearProb_longCode_of_satisfies`) and + `hastad_gapLabelCover_dichotomy` (now `gapLabelCover_dichotomy`). + - The conditional sumset-extractor frontier layer (#391), whose extractor-family hypothesis was + never discharged: `Circuit/Frontier/Extractor.lean` and `Complexity/FrontierExtractor.lean` + (whole modules), and the decimal restatements + `eventually_456249_div_100000_sub_mul_lt_innerSize`, + `circuitLowerBound_456249_div_100000`, `averageCaseBound_456249_div_100000`, + `eventually_456249_div_100000_sub_mul_lt_innerSize_of_nondeterministicDecides` and + `eventually_456249_div_100000_sub_mul_lt_card_of_aggregates`. Combine the exact-`L` theorems + with `Gaussian.circuitCoefficient_ge_456249` instead. + - #392 withdrew Gentry's bootstrapping (`Crypto/Computational/Bootstrapping.lean` and + `Bootstrapping/{HomomorphicEval,KeyChain,KeyEncryption}.lean`, whole modules), whose + `Bootstrappable` interface assumed the content of the theorem. From the Håstad modules it + deleted `GapExactThreeDistinctSAT`, `no_GapExactThreeDistinctSAT_eq_empty`, `xorThreeCNF` and + its five lemmas, `hastadLinearProb_le_of_hastadLinearCorr_le` and the same-day alias + `Cslib.PCP.Hastad.fourierCoeff_sq_signVal_le_one`. The kept long-code, folding and projection + lemmas moved to `Cslib.BitString` in `Foundations/BooleanAnalysis/LongCode` (`IsFolded` + became `IsOdd`), and `Hastad/ExactThreeDistinct` merged into + `PCP/Approximation/MaxExactThreeSAT`, keeping its declaration names; imports of the deleted + module path must be updated. `hastad_gapLabelCover_dichotomy` (now `gapLabelCover_dichotomy`) + now chooses `η` before the label sets. + - Follow-up #390 (same date and policy): the rest of the `ACC0.lean` Williams scaffolding, + `SymPlusCircuit` and its API, `evalMask` and its lemmas, `zetaTransform`, `fastZeta`, + `fastZeta_eq_zetaTransform`, `parity_mem_EXP`, `parity_mem_NEXP`, + `exists_mem_NEXP_notMem_{AC0,AC0Mod,SIZEMod}`, `exists_mem_SIZEMod_of_mem_AC0Mod`, + `NEXP_subset_PPoly_of_NEXP_subset_ACC0`, `PH_eq_SigmaP_two_of_NEXP_subset_ACC0` and + `Kannan.language_mem_SigmaP_two_of_NEXP_subset_ACC0`; the remaining `ACC0` development is + split into `ACC0/Basic` and `ACC0/Simulation`, and `NC_subset_PPoly`, `AC_subset_PPoly` and + `size_majorityDeMorganCircuit_le_sq` moved to `DepthClasses`, `FanInTwo` and `ThresholdNC1`, + all with names unchanged. The module + `Circuit/Arithmetic/IdentityTesting` is deleted. Its Schwartz–Zippel restatements + (`MvPolynomial.card_mul_card_roots_le`, `card_roots_le`, + `exists_eval_ne_zero_of_totalDegree_lt_card`; use Mathlib's + `MvPolynomial.schwartz_zippel_totalDegree`), `encArith*`, `PITInt` and its lemmas, the + arithmetic circuit combinators, the permanent chain (`minorVar` through + `isLaplacePermChain_laplacePermCircuit`), `intPermFamily` and `exists_mem_NEXP_notMem_SIZE` + are deleted. `Matrix.permanent_succ_{column,row}_zero` moved to + `Foundations/Data/Matrix/Permanent`, `Cslib.Circuits.Circuit.eval_polynomial` to + `Arithmetic/VP`, and `permFamily`, `isPFamily_permFamily` and + `totalDegree_permanent_genericMatrix_le` to `Arithmetic/Determinant`, with names unchanged. +* **Two multigraph namespaces.** Since 2026-10-20 the frontier multigraph + (`Cslib.Circuits.Frontier.Multigraph`) defines its simple graph as that of the multigraph given by + its ends (`Cslib.Multigraph`), and its module publicly imports the latter. Their shared base names + `toSimpleGraph`, `toSimpleGraph_adj`, `edgeCut` and `mem_edgeCut` are ambiguous when both + namespaces are visible, for example inside `namespace Cslib.Circuits.Frontier` or with both + opened; use dot notation (`G.toSimpleGraph_adj`) or full names. Upstreaming the frontier + multigraph therefore follows `Foundations/Combinatorics/Multigraph.lean`. + +The [historical ledger](https://github.com/cslib-community/complexitylib/blob/d0e6373970029a91018528463ca7a5ccd0fc9fd1/UPSTREAM.md) +preserves earlier integration notes and per-module follow-up ideas. + +* **Word-RAM word-size contracts (2026-10-22).** Time and encoding arguments now depend on + word size, and an ordinary predicate `allowed : α → ℕ → Prop` admits input/width pairs. + `WordSizeDependentProgram` selects code only by width; the primary decision and function classes + require a fixed register count and uniformly bounded instruction-list length. + `Uniform` variants retain one fixed program, + which may read the width. Existing fixed-code clients prove the uniform variants and can + forget uniformity. The `LogWord` wrappers express the transdichotomous assumption, require a + positive constant, and remain conditional on input fitting. Asymptotic time keeps correctness + at every admitted fitting input + and makes only the runtime estimate eventual, with constants shared by all widths. Width + schedules connect to Mathlib's real-valued big-O after casting. No effective selection of code, + width or encoding, or Turing-machine complexity identification, follows. These breaking local + API changes intentionally retain no deprecated aliases, as authorized by Erik for this redesign. From 2e388dc940594ccf683e73326b350daa39db18dc Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 29 Oct 2026 17:07:07 -0400 Subject: [PATCH 70/73] doc(ExactTriangle): state native code bound inclusively Address PR1661 inline review4417867636: replace fewer than by at most to match the existing <=133120 theorem. Exact byte comparison against d4b049362 proves this is the only changed line; all other tracked bytes, definitions, proofs, attributes, imports and tests are unchanged. Diff check passes. No Lean examples introduced and no Lean rebuild needed for this documentation-only correction. Existing technical approvals are retained for unchanged code; supported-rc2 integration validation and delta review after PR1653 remain required. --- .../Algorithms/WordRAM/ExactTriangle/RecordWitness/Flat.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Flat.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Flat.lean index 247834a07..7ca9c2f25 100644 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Flat.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Flat.lean @@ -26,7 +26,7 @@ open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle open Cslib.Algorithms.MatrixMultiplication.Schonhage open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage -/-- The complete initialized standard-native program uses 400 fixed registers, fewer than +/-- The complete initialized standard-native program uses 400 fixed registers, at most 133120 flat instruction slots including halt, and never inspects physical word width. -/ theorem entryProgram_fixed : (entryProgram.program Reg.found).length ≤ 133120 ∧ ¬(entryProgram.program Reg.found).ReadsWidth := by From 9ed49005c9f6ce362d033affa2317a0758f7d7ae Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 29 Oct 2026 18:11:08 -0400 Subject: [PATCH 71/73] feat(ExactTriangle): execute the native fixed-prime catalogue Source/MCP checkpoint for #1662 under the still-open #1394 endpoint. Owner/session: codex-three-sum-continuation-t3-3dda8289, personal implementation on work/three-sum-native-fixed-prime-et-20261029 above d4b0493621c25ff403e8e63101cb23993756c5f9. One actual producer execution feeds all computed occupied records to the complete existing 400-register product/witness program in a fixed416 bank. The actual initialized same-state prefix Runs/halt verdict is equivalent to original HasZeroTriangle, with original coordinates retained on success. Dirty scratch is reused with protected producer readback/current-state framing; the old producer external frame is not asserted to survive. Actual ordinal coverage and query nonrepetition inject distinct failed occurrences into existing global failedScans. Total native scans satisfy S<=falsePositiveCount+actual returnedFlag, with at most one successful return. All copy/call/return/root/query/index/loop/halt costs are actual native Code. Premises: original signed list length3n^2 and magnitude<=A, D=4^m>=16, n>=D^18, g>=1, p in Nat.sqrtPrimeWindow D, and every physical width at least the original-input admission. No layout/run/time/solver/scan certificate is an algorithm input. Code has416 registers, <=133152 flat slots including halt, and no width read. The threshold is positive, at most (257kappa+5909)clog2(n+2) when D,g,p<=n and A<=(n+2)^kappa. All actual wider prefixes have original-input polynomial words/support independent of their physical width. The original signed verifier and 320/163-bank product ABI remain unchanged; raw01 doubles only in that callee. Aggregate charged bound: preparation690n^2Dg separately; products at most 138412032(19m+1)n^3g/D^(1/18), plus4ng(5sqrtD+8queryCap+61), and one (42pieceSize+28)(F(p)+1)+7 global scan/controller/halt reserve. The actual counter retains F(p)+actualFlag, not +1 for each failed record. Pinned source anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5: Programs/Sec3/Theorem17/Host/{Loop,FailedScans,InstanceCount}.lean, with existing local finite Splitting/Witnesses. Credit retained. The native chunk-major catalogue is prepared once and later product calls stop after success; the source Light loop uses piece-major visits and per-round matrix writes with an arbitrary solver. No source/native trace equivalence or Light simulation is assumed. The native code and execution charges here are original. Existing finite/native interfaces are reused throughout. Targeted validation ONLY: Lean MCP clean on the final umbrella and all three new tests; concrete native setup/return/guard VM cases, fully admitted positive and negative original-input same-state runs at valid positive geometry, and real producer dirty-heap regression distinguishing readback from the old external frame. Source proof modules were checked through MCP while implementing. No new sorry/custom axiom/native_decide or resource override was introduced. This records no final hygiene/lint audit yet. ROOT retains the sole import/global/static slot. Canonical import generation, ALL SIX together, full static/source/identity/quote/name audits, compatibility merge and fresh independent whole-layer review remain pending; this is NOT a final validated or approved candidate. No PR opened or review commissioned. The exact supplier doc-only2e388dc9 fix is reserved for the later coordinated compatibility merge; published1661 and other ancestor branches are untouched. No toolchain/main churn was imported. All new files have mode100644. No abstract/catalogue/rendering impact: global parameter/prime selection, unrestricted small geometry, the final aggregate source exponent and ordinary distinct-position3SUM1.9992 remain open under #1394. Root owns those global host/composition seams. AI-assisted implementation by the named worker. --- .../ExactTriangle/FailureAccounting.lean | 86 ++++++ .../WordRAM/ExactTriangle/FixedPrime.lean | 38 +++ .../ExactTriangle/FixedPrime/Accounting.lean | 115 ++++++++ .../ExactTriangle/FixedPrime/Controller.lean | 87 ++++++ .../ExactTriangle/FixedPrime/Entry.lean | 137 +++++++++ .../ExactTriangle/FixedPrime/Flat.lean | 94 ++++++ .../ExactTriangle/FixedPrime/Indexing.lean | 91 ++++++ .../ExactTriangle/FixedPrime/Loop.lean | 271 ++++++++++++++++++ .../ExactTriangle/FixedPrime/Potential.lean | 75 +++++ .../ExactTriangle/FixedPrime/Program.lean | 107 +++++++ .../ExactTriangle/FixedPrime/Record.lean | 219 ++++++++++++++ .../FixedPrime/SourceBounds.lean | 83 ++++++ .../ExactTriangle/FixedPrime/Step.lean | 143 +++++++++ .../ExactTriangle/FixedPrime/TimeBound.lean | 119 ++++++++ .../ExactTriangle/FixedPrime/Width.lean | 141 +++++++++ .../ExactTriangle/PreparationLocality.lean | 114 ++++++++ .../RecordWitness/FailureCount.lean | 71 +++++ .../ExactTriangle/RecordWitness/Failures.lean | 24 +- ...dRAMExactTriangleFixedPrimeController.lean | 66 +++++ .../WordRAMExactTriangleFixedPrimeInput.lean | 156 ++++++++++ ...rdRAMExactTrianglePreparationLocality.lean | 67 +++++ ROADMAP.md | 9 + UPSTREAM.md | 19 ++ 23 files changed, 2311 insertions(+), 21 deletions(-) create mode 100644 Complexity/Algorithms/ExactTriangle/FailureAccounting.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Accounting.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Controller.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Entry.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Flat.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Indexing.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Loop.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Potential.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Program.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Record.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/SourceBounds.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Step.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/TimeBound.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Width.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/PreparationLocality.lean create mode 100644 Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/FailureCount.lean create mode 100644 ComplexityTests/WordRAMExactTriangleFixedPrimeController.lean create mode 100644 ComplexityTests/WordRAMExactTriangleFixedPrimeInput.lean create mode 100644 ComplexityTests/WordRAMExactTrianglePreparationLocality.lean diff --git a/Complexity/Algorithms/ExactTriangle/FailureAccounting.lean b/Complexity/Algorithms/ExactTriangle/FailureAccounting.lean new file mode 100644 index 000000000..1ef3e3c28 --- /dev/null +++ b/Complexity/Algorithms/ExactTriangle/FailureAccounting.lean @@ -0,0 +1,86 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.ExactTriangle.Witnesses +public import Mathlib.Data.Nat.Count +import Mathlib.Data.List.GetD + +/-! +# One false-positive budget for distinct record/query occurrences + +Lists within distinct existing instances inject into the existing global `failedScans` set. +Consequently their failed accepted occurrences share one `falsePositiveCount` bound. This +finite bridge does not multiply that bound by the number of records. A native caller must +derive list membership, nonrepetition and answers from its actual execution/readback. + +The counting argument adapts `Host.FailedScans.sum_fails_le` from +`anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`, using the existing local +finite instances and failed-scan API rather than a second host-data model. +-/ + +public section + +namespace Cslib.Algorithms.ExactTriangle + +/-- Failed accepted occurrences across distinct records inject into the single global +failed-scan set. Lists may be partial chunks, but each occurrence list must be repetition-free. -/ +theorem sum_failed_occurrences_le {n D g p : ℕ} + (wAB wBC wAC : Fin n → Fin n → ℤ) (hp : p ≠ 0) + (ans : InstanceIndex p → Fin n × Fin n → Bool) + (hans : AnswersCorrect wAB wBC wAC D g p ans) + (I : Finset (InstanceIndex p)) (hI : I ⊆ instanceIndices wAB D g p) + (queries : InstanceIndex p → List (Fin n × Fin n)) (default : Fin n × Fin n) + (hnodup : ∀ idx ∈ I, (queries idx).Nodup) + (hquery : ∀ idx ∈ I, ∀ q ∈ queries idx, q ∈ (lopInstance wAB wBC wAC D g p idx).W) : + (∑ idx ∈ I, Nat.count (fun j => ans idx ((queries idx).getD j default) = true ∧ + scanPiece wAB wBC wAC D g idx.2.2 + ((queries idx).getD j default).1 ((queries idx).getD j default).2 = none) + (queries idx).length) ≤ falsePositiveCount wAB wBC wAC p := by + classical + let P := fun idx j => ans idx ((queries idx).getD j default) = true ∧ + scanPiece wAB wBC wAC D g idx.2.2 + ((queries idx).getD j default).1 ((queries idx).getD j default).2 = none + let occurrences : Finset (Σ _ : InstanceIndex p, ℕ) := + I.sigma fun idx => (Finset.range (queries idx).length).filter (P idx) + let failures := failedScans wAB wBC wAC D g p ans + have mem_occurrence (z : Σ _ : InstanceIndex p, ℕ) (hz : z ∈ occurrences) : + z.1 ∈ I ∧ z.2 < (queries z.1).length ∧ P z.1 z.2 := by + simpa only [occurrences, Finset.mem_sigma, Finset.mem_filter, Finset.mem_range, + and_assoc] using hz + have get_eq (idx : InstanceIndex p) (j : ℕ) (hj : j < (queries idx).length) : + (queries idx).getD j default = (queries idx).get ⟨j, hj⟩ := by + rw [List.getD_eq_getElem _ _ hj, List.get_eq_getElem] + let f : {z // z ∈ occurrences} → {s // s ∈ failures} := fun z => + ⟨(z.val.1, (queries z.val.1).getD z.val.2 default), by + obtain ⟨hidx, hj, hacc, hnone⟩ := mem_occurrence z.val z.prop + apply Finset.mem_filter.mpr + refine ⟨mem_acceptedPairs.mpr ⟨hI hidx, ?_, hacc⟩, hnone⟩ + exact hquery _ hidx _ (get_eq _ _ hj ▸ List.get_mem _ _)⟩ + have hf : Function.Injective f := by + rintro ⟨⟨idx, j⟩, hx⟩ ⟨⟨idx', j'⟩, hy⟩ hxy + have he : idx = idx' := congrArg (fun s => s.val.1) hxy + subst idx' + obtain ⟨hidx, hj, -⟩ := mem_occurrence ⟨idx, j⟩ hx + obtain ⟨-, hj', -⟩ := mem_occurrence ⟨idx, j'⟩ hy + have hpair := congrArg (fun s => s.val.2) hxy + change (queries idx).getD j default = (queries idx).getD j' default at hpair + rw [get_eq idx j hj, get_eq idx j' hj'] at hpair + have hpos := List.nodup_iff_injective_get.mp (hnodup idx hidx) hpair + exact Subtype.ext (Sigma.ext rfl (heq_of_eq (congrArg Fin.val hpos))) + have hcard : (∑ idx ∈ I, Nat.count (P idx) (queries idx).length) = occurrences.card := by + simp only [occurrences, Finset.card_sigma, Nat.count_eq_card_filter_range] + change (∑ idx ∈ I, Nat.count (P idx) (queries idx).length) ≤ _ + rw [hcard] + calc + _ = Fintype.card {z // z ∈ occurrences} := (Fintype.card_coe _).symm + _ ≤ Fintype.card {s // s ∈ failures} := Fintype.card_le_of_injective f hf + _ = failures.card := Fintype.card_coe _ + _ ≤ falsePositiveCount wAB wBC wAC p := card_failedScans_le hp hans + +end Cslib.Algorithms.ExactTriangle diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime.lean new file mode 100644 index 000000000..40bff2d99 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime.lean @@ -0,0 +1,38 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.SourceBounds +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.TimeBound + +/-! +# Complete native ExactTriangle decision at a fixed admitted prime + +`FixedPrime.runs_input` executes one concrete 416-register standard-native program from the +original signed input, with actual preparation once, all computed catalogue records and +original-weight witness verification. Its same-state prefix/halt verdict is equivalent to +original `HasZeroTriangle`; a successful state retains the original coordinates. + +`FixedPrime.timeBound_le` derives all-instance queried-product charges and one global +false-positive scan budget. Preparation costs at most `690n²Dg`, product charges at most +`138412032(19m+1)n³g/D^(1/18)`, with explicit additional root/query/controller and original +scan terms. The total actual scans satisfy `S ≤ F(p)+returnedFlag`: empty/failed records do +not each receive a successful-scan allowance. The runtime order is the producer's actual +chunk-major order, without a precomputed record list or a clean-workspace premise. + +All initialized results require `D=4^m≥16`, `n≥D^18`, `g≥1`, the stated prime window and the +original signed magnitude bound. The original-input threshold is positive, logarithmic for +polynomial weights and bounded scalar headers, and works at every wider physical width. +`FixedPrime.bounded_reachable_original` derives polynomial words and zero support above an +original-input envelope independent of that larger width. The code never reads word width, +has at most 133152 flat slots including halt, and performs no whole-window clear or snapshot. + +Global parameter/prime selection, unrestricted small geometry and the complete aggregate +source exponent remain separate. This component does not complete ordinary 3SUM 1.9992. +The stage execution lemmas in the submodules are internal implementation interfaces. +-/ diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Accounting.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Accounting.lean new file mode 100644 index 000000000..43ca14ea9 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Accounting.lean @@ -0,0 +1,115 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.ExactTriangle.FailureAccounting +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Indexing +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Failures + +/-! +# Global accounting for actual catalogue query blocks + +Each block is decoded from the real protected producer memory, in its original rank order. +The mathematical products are those computed by the concrete native product; their answer +correctness is proved, not assumed. Failed occurrences in distinct records therefore share +one global false-positive budget. These value lemmas supply the native loop invariant. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Existing original-weight witness condition restricted to one existing chunk and piece. -/ +@[expose] def recordHit {m n g p : ℕ} (values : ℕ → ℤ) (idx : InstanceIndex p) : Prop := + ∃ q ∈ chunkOf (fun u v : Fin n => values (pairIndex (u, v))) (D m) p idx.1 idx.2.1, + ∃ c ∈ piece n (D m) g idx.2.2, + weightSum (fun u v : Fin n => values (pairIndex (u, v))) + (fun b c => values (n * n + pairIndex (b, c))) + (fun u c => values (2 * (n * n) + pairIndex (u, c))) q.1 q.2 c = 0 + +/-- Real per-record product, root, query and controller charges, excluding interval scans. +The constant includes the outer loop's two control instructions. -/ +@[expose] noncomputable def recordBase {m n p : ℕ} (hn : 0 < n) + (values : ℕ → ℤ) (mem : ℕ → ℕ) (idx : InstanceIndex p) : ℕ := + let Q := RecordProduct.recordQueryCount n (D m) p values idx + let qs := RecordProduct.decodedQueries hn mem (RecordProduct.recordQueries n (D m) p idx) Q + RecordProduct.timeBound (m := m) qs + 5 * Nat.sqrt (D m) + 8 * Q + 61 + +/-- Number of accepted queries without an original witness in one actual producer block. +This is a value specification for charged native scans, not an executable scan oracle. -/ +@[expose] noncomputable def recordFailures {m n g p : ℕ} (hn : 0 < n) + (values : ℕ → ℤ) (mem : ℕ → ℕ) (idx : InstanceIndex p) : ℕ := + let qs := RecordProduct.decodedQueries hn mem (RecordProduct.recordQueries n (D m) p idx) + (RecordProduct.recordQueryCount n (D m) p values idx) + let default : Fin n × Fin n := (⟨0, hn⟩, ⟨0, hn⟩) + Nat.count (fun i => + (RecordProduct.leftMatrix m n g p values idx * RecordProduct.rightMatrix m n g p values idx) + (qs.getD i default).1 (qs.getD i default).2 ≠ 0 ∧ + scanPiece (fun a b => values (pairIndex (a, b))) + (fun b c => values (n * n + pairIndex (b, c))) + (fun a c => values (2 * (n * n) + pairIndex (a, c))) (D m) g idx.2.2 + (qs.getD i default).1 (qs.getD i default).2 = none) qs.length + +/-- Every original query occurs at most once in each actual chunk. -/ +theorem actual_queries_chunk {m n g p : ℕ} (hn : 0 < n) (values : ℕ → ℤ) + {initial a : Data 64} (h : Instances.Output n (D m) g p values initial a) + (hD : 16 ≤ D m) (hDn : D m ≤ n) (hp : p ≠ 0) (idx : InstanceIndex p) + (hidx : idx ∈ instanceIndices (fun u v : Fin n => values (pairIndex (u, v))) (D m) g p) : + let qs := RecordProduct.decodedQueries hn a.mem (RecordProduct.recordQueries n (D m) p idx) + (RecordProduct.recordQueryCount n (D m) p values idx) + qs.Nodup ∧ qs.toFinset = chunkOf + (fun u v : Fin n => values (pairIndex (u, v))) (D m) p idx.1 idx.2.1 := by + let wAB := fun u v : Fin n => values (pairIndex (u, v)) + have hc := QuerySpec.card_residueClass wAB hp + (fun q => (values q % (p : ℤ)).toNat) (by intro pair; rfl) idx.1 + obtain ⟨-, hnodup, hset⟩ := RecordProduct.decodedQueries_chunkOf values wAB hn hp + (by intro u v; rfl) (by omega) hDn h idx.1 idx.2.1 + ((mem_instanceIndices wAB idx).mp hidx).1 + rw [hc] at hnodup hset + exact ⟨hnodup, hset⟩ + +/-- Add distinct actual records before applying the global bound; no per-record false-positive +allowance is duplicated. The real product's detection theorem supplies answer correctness. -/ +theorem sum_recordFailures_le {m n g p : ℕ} (hn : 0 < n) (values : ℕ → ℤ) + {initial a : Data 64} (h : Instances.Output n (D m) g p values initial a) + (hD : 16 ≤ D m) (hDn : D m ≤ n) (hg : 1 ≤ g) + (hp : p ∈ Nat.sqrtPrimeWindow (D m)) + (I : Finset (InstanceIndex p)) + (hI : I ⊆ instanceIndices (fun u v : Fin n => values (pairIndex (u, v))) (D m) g p) : + (∑ idx ∈ I, recordFailures (m := m) (g := g) hn values a.mem idx) ≤ + falsePositiveCount (fun u v : Fin n => values (pairIndex (u, v))) + (fun b c => values (n * n + pairIndex (b, c))) + (fun u c => values (2 * (n * n) + pairIndex (u, c))) p := by + classical + let wAB := fun u v : Fin n => values (pairIndex (u, v)) + let wBC := fun b c : Fin n => values (n * n + pairIndex (b, c)) + let wAC := fun u c : Fin n => values (2 * (n * n) + pairIndex (u, c)) + let queries := fun idx => RecordProduct.decodedQueries hn a.mem + (RecordProduct.recordQueries n (D m) p idx) + (RecordProduct.recordQueryCount n (D m) p values idx) + let ans := fun (idx : InstanceIndex p) (q : Fin n × Fin n) => decide + ((RecordProduct.leftMatrix m n g p values idx * + RecordProduct.rightMatrix m n g p values idx) q.1 q.2 ≠ 0) + have hp0 := (Nat.mem_sqrtPrimeWindow_iff_sq.mp hp).1.ne_zero + have hans : AnswersCorrect wAB wBC wAC (D m) g p ans := + RecordProduct.product_answers_correct values wAB wBC wAC hD hg hp + (by intro b c; rfl) (by intro u c; rfl) + have hquery (idx) (hi : idx ∈ I) := + actual_queries_chunk hn values h hD hDn hp0 idx (hI hi) + have hbound := sum_failed_occurrences_le wAB wBC wAC hp0 ans hans I hI queries + (⟨0, hn⟩, ⟨0, hn⟩) (fun idx hi => (hquery idx hi).1) (by + intro idx hi q hq + change q ∈ chunkOf wAB (D m) p idx.1 idx.2.1 + rw [← (hquery idx hi).2, List.mem_toFinset] + exact hq) + simpa only [recordFailures, queries, ans, decide_eq_true_eq] using hbound + +end Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Controller.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Controller.lean new file mode 100644 index 000000000..08decae1a --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Controller.lean @@ -0,0 +1,87 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Program + +/-! +# Charged catalogue-controller setup and return + +The occupied count is read from the real producer bank. The return code adds the actual +callee scan count, retains its actual unsigned flag, advances the visited prefix and closes +the loop only after success. These internal rules use concrete data instructions; neither +the scan count nor the verdict is supplied as an execution oracle. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime + +open Cslib.WordRAM + +-- These closed checks cover only the short controller and its finite register bank. +set_option maxRecDepth 4096 in +private theorem setup_preserves : ∀ i : Fin 416, i.val < 400 → setup.Preserves i := by + decide + +-- The return branch writes four controller registers and no part of the callee bank. +set_option maxRecDepth 4096 in +private theorem receive_preserves : ∀ i : Fin 416, + i.val < 400 ∨ i = Reg.one → receive.Preserves i := by decide + +/-- Read the actual occupied count, with all five initialization instructions charged. -/ +theorem exec_setup {w : ℕ} (x : Data 416) (h1 : Fits w 1) : + ∃ y, ExecWithin w setup x y 5 ∧ y.mem = x.mem ∧ + y.regs Reg.index = 0 ∧ + y.regs Reg.limit = x.regs (recordBank + (RecordWitness.productBank (RecordProduct.producerBank Instances.produced))) ∧ + y.regs Reg.found = 0 ∧ y.regs Reg.scans = 0 ∧ + y.regs Reg.one = 1 ∧ + ∀ j : Fin 416, j.val < 400 → y.regs j = x.regs j := by + let M := x.regs (recordBank + (RecordWitness.productBank (RecordProduct.producerBank Instances.produced))) + let y := x.set Reg.index 0 |>.set Reg.limit M |>.set Reg.found 0 |>.set Reg.scans 0 + |>.set Reg.one 1 + have he : ExecWithin w setup x y 5 := by + ram_eval [setup, y, M, h1, Reg.index, Reg.limit, Reg.found, Reg.scans, Reg.one, + recordBank, RecordWitness.productBank, RecordProduct.producerBank, + Instances.produced, Fin.castLE] + refine ⟨y, he, rfl, ?_, ?_, ?_, ?_, ?_, fun j hj => + he.regs_eq (setup_preserves j hj)⟩ + all_goals simp +decide [y, M, Reg.index, Reg.limit, Reg.found, Reg.scans, Reg.one] + +/-- The native return needs six instructions in either branch, including control. Its limit +is shortened exactly when the actual callee flag equals one. The complete callee bank survives. -/ +theorem exec_receive {w i limit S performed flag : ℕ} (x : Data 416) + (hi : x.regs Reg.index = i) (hl : x.regs Reg.limit = limit) + (hs : x.regs Reg.scans = S) + (hp : x.regs (recordBank RecordWitness.Reg.scans) = performed) + (hf : x.regs (recordBank RecordWitness.Reg.found) = flag) + (h1 : x.regs Reg.one = 1) (hS : Fits w (S + performed)) (hI : Fits w (i + 1)) : + ∃ y, ExecWithin w receive x y 6 ∧ y.mem = x.mem ∧ + y.regs Reg.index = i + 1 ∧ y.regs Reg.limit = (if flag = 1 then i + 1 else limit) ∧ + y.regs Reg.scans = S + performed ∧ y.regs Reg.found = flag ∧ + ∀ j : Fin 416, j.val < 400 ∨ j = Reg.one → + y.regs j = x.regs j := by + dsimp only [Reg.index, Reg.limit, Reg.scans, Reg.one, recordBank, + RecordWitness.Reg.found, RecordWitness.Reg.scans, Fin.castLE] at hi hl hs hp hf h1 + let y := x.set Reg.scans (S + performed) |>.set Reg.found flag |>.set Reg.index (i + 1) + |>.set Reg.limit (if flag = 1 then i + 1 else limit) + have he : ExecWithin w receive x y 6 := by + by_cases hflag : flag = 1 + · ram_eval [receive, y, hi, hl, hs, hp, hf, h1, hS, hI, hflag, + Reg.index, Reg.limit, Reg.found, Reg.scans, Reg.one, recordBank, + RecordWitness.Reg.found, RecordWitness.Reg.scans, Fin.castLE] + · ram_eval [receive, y, hi, hl, hs, hp, hf, h1, hS, hI, hflag, + Reg.index, Reg.limit, Reg.found, Reg.scans, Reg.one, recordBank, + RecordWitness.Reg.found, RecordWitness.Reg.scans, Fin.castLE] + refine ⟨y, he, rfl, ?_, ?_, ?_, ?_, fun j hj => + he.regs_eq (receive_preserves j hj)⟩ + all_goals simp +decide [y, Reg.index, Reg.limit, Reg.found, Reg.scans] + +end Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Entry.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Entry.lean new file mode 100644 index 000000000..0f0379d01 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Entry.lean @@ -0,0 +1,137 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Loop + +/-! +# Initialized fixed-prime ExactTriangle computation + +The original input alone supplies scalar headers and canonical signed weights. The producer +runs once, its actual heap and occupied records feed the complete native traversal, and the +returned flag decides original ExactTriangle across every record. All fits follow from the +original numerical admission; no intermediate producer, layout or solver result is an input. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +/-- One real preparation followed by the entire actual fixed-prime traversal. Its structured +bound excludes the final halt, and retains the preparation charge separately from record work. +The physical width may be any larger admitted width. -/ +theorem exec_input {w m n g p A : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hD : 16 ≤ D m) + (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) (hN : D m ^ 18 ≤ n) + (hv : ∀ z ∈ values, z.natAbs ≤ A) + (hW : admissionWidth n (D m) g p A ≤ w + 2) : + let V := fun q => values.getD q 0 + let wAB := fun u v : Fin n => V (pairIndex (u, v)) + let wBC := fun b c : Fin n => V (n * n + pairIndex (b, c)) + let wAC := fun u c : Fin n => V (2 * (n * n) + pairIndex (u, c)) + let hn : 0 < n := lt_of_lt_of_le (by omega : 0 < D m) + ((Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN) + InputFits (w + 2) (PreparationInput.input n (D m) g p values) ∧ + ∃ a : Data 64, + ExecWithin (w + 2) Instances.program + (.init (PreparationInput.input n (D m) g p values)) a + (RecordProduct.preparationTime n (D m) g p values - 1) ∧ + Instances.Output n (D m) g p V + (.init (PreparationInput.input n (D m) g p values)) a ∧ + ∃ y, ExecWithin (w + 2) program + (.init (PreparationInput.input n (D m) g p values)) y + (RecordProduct.preparationTime n (D m) g p values + + (∑ idx ∈ instanceIndices wAB (D m) g p, recordBase (m := m) hn V a.mem idx) + + (42 * pieceSize (D m) g + 28) * (falsePositiveCount wAB wBC wAC p + 1) + 6) ∧ + y.Bounded (w + 2) ∧ + (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ + y.regs Reg.scans ≤ falsePositiveCount wAB wBC wAC p + y.regs Reg.found ∧ + (y.regs Reg.found = 1 ↔ HasZeroTriangle wAB wBC wAC) ∧ + (y.regs Reg.found = 1 → + y.regs (recordBank RecordWitness.Reg.witnessA) < n ∧ + y.regs (recordBank RecordWitness.Reg.witnessB) < n ∧ + y.regs (recordBank RecordWitness.Reg.witnessC) < n ∧ + V (y.regs (recordBank RecordWitness.Reg.witnessA) * n + + y.regs (recordBank RecordWitness.Reg.witnessB)) + + V (n * n + (y.regs (recordBank RecordWitness.Reg.witnessB) * n + + y.regs (recordBank RecordWitness.Reg.witnessC))) + + V (2 * (n * n) + (y.regs (recordBank RecordWitness.Reg.witnessA) * n + + y.regs (recordBank RecordWitness.Reg.witnessC))) = 0) ∧ + (∀ u, u < a.regs Instances.heap ∨ + a.regs Instances.heap + CallableProduct.windowSize n (n * n) ≤ u → y.mem u = a.mem u) ∧ + ∀ j : Fin 64, y.regs + (recordBank (RecordWitness.productBank (RecordProduct.producerBank j))) = a.regs j := by + let V := fun q => values.getD q 0 + have hDn : D m ≤ n := (Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN + have hp0 := (Nat.mem_sqrtPrimeWindow_iff_sq.mp hp).1.pos + have hw : PreparationInput.width n (D m) g p A ≤ w + 2 := by + have hr := record_admitted hW + unfold RecordWitness.admissionWidth RecordProduct.admissionWidth at hr + omega + obtain ⟨a, ha, hout, hinput, -⟩ := PreparationInput.exec_input values hlen + (by omega) hDn hg hp0 hv hw + have ha' : ExecWithin (w + 2) Instances.program + (.init (PreparationInput.input n (D m) g p values)) a + (RecordProduct.preparationTime n (D m) g p values - 1) := ha + have hf : Function.Injective RecordProduct.producerBank := by + intro i j h + exact Fin.ext (congrArg (fun k : Fin 320 => k.val) h) + have hf' : Function.Injective RecordWitness.productBank := by + intro i j h + exact Fin.ext (congrArg (fun k : Fin 400 => k.val) h) + have hf'' : Function.Injective recordBank := by + intro i j h + exact Fin.ext (congrArg (fun k : Fin 416 => k.val) h) + let x₀ : Data 320 := .init (PreparationInput.input n (D m) g p values) + let b := a.extend RecordProduct.producerBank x₀ + have hb : ExecWithin (w + 2) RecordProduct.produce x₀ b + (RecordProduct.preparationTime n (D m) g p values - 1) := ha'.rename hf + let b₀ : Data 400 := .init (PreparationInput.input n (D m) g p values) + let c := b.extend RecordWitness.productBank b₀ + have hc : ExecWithin (w + 2) RecordWitness.produce b₀ c + (RecordProduct.preparationTime n (D m) g p values - 1) := hb.rename hf' + let c₀ : Data 416 := .init (PreparationInput.input n (D m) g p values) + let x := c.extend recordBank c₀ + have hx : ExecWithin (w + 2) produce c₀ x + (RecordProduct.preparationTime n (D m) g p values - 1) := hc.rename hf'' + have bank (j : Fin 64) : x.regs + (recordBank (RecordWitness.productBank (RecordProduct.producerBank j))) = a.regs j := + (hf''.extend_apply _ _ _).trans ((hf'.extend_apply _ _ _).trans (hf.extend_apply _ _ _)) + have mem : x.mem = a.mem := rfl + have heap : a.regs Instances.heap ≤ PreparationInput.wordBound n (D m) g p A := by + obtain ⟨t, ht, he⟩ := ha' + have hr : Runs (w + 2) PreparationInput.flatProgram + (State.init (PreparationInput.input n (D m) g p values)) ⟨Instances.program.size, a⟩ t := by + simpa only [Nat.zero_add, State.init] using he.runs (pc := 0) + (p := PreparationInput.flatProgram) (Program.Contains.self _).append_left + exact ((PreparationInput.bounded_reachable values hlen (by omega) hDn hg hp0 hv hw + ⟨Instances.program.size, a⟩ t hr).1 Instances.heap).le + obtain ⟨y, hy, yf, ys, yiff, yw, frame, regs⟩ := + exec_traverse values hlen hD hg hp hN hv hW a x hout bank (fun _ _ => rfl) heap + have hpos : 1 ≤ RecordProduct.preparationTime n (D m) g p values := by + dsimp only [RecordProduct.preparationTime, Instances.timeBound] + omega + have hex : ExecWithin (w + 2) program + (.init (PreparationInput.input n (D m) g p values)) y + (RecordProduct.preparationTime n (D m) g p values + + (∑ idx ∈ instanceIndices (fun u v : Fin n => V (pairIndex (u, v))) (D m) g p, + recordBase (m := m) (lt_of_lt_of_le (by omega : 0 < D m) hDn) V a.mem idx) + + (42 * pieceSize (D m) g + 28) * + (falsePositiveCount (fun u v : Fin n => V (pairIndex (u, v))) + (fun b c => V (n * n + pairIndex (b, c))) + (fun u c => V (2 * (n * n) + pairIndex (u, c))) p + 1) + 6) := + (hx.seq hy).mono (by dsimp only [V]; omega) + refine ⟨hinput, a, ha', hout, y, hex, hex.bounded (Data.Bounded.init hinput), + yf, ys, yiff, yw, ?_, regs⟩ + simpa only [mem] using frame + +end Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Flat.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Flat.lean new file mode 100644 index 000000000..f75a94fec --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Flat.lean @@ -0,0 +1,94 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Entry + +/-! +# Initialized halting fixed-prime ExactTriangle decision + +The actual fixed 416-register standard-native program decides the original signed input at +every admitted physical width. Its prefix witness and halt share the same state, which retains +the original-coordinate witness on success and a single global failure-scan bound. Global +parameter/prime selection and the final source exponent are not supplied by this component. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +/-- Actual one-time preparation, all occupied record charges, one global failed-scan budget, +and all controller/loop/halt work. No whole-window initialization is charged or performed. -/ +@[expose] noncomputable def timeBound {m n : ℕ} (hn : 0 < n) (g p : ℕ) + (values : List ℤ) (mem : ℕ → ℕ) : ℕ := + let V := fun q => values.getD q 0 + let wAB := fun u v : Fin n => V (pairIndex (u, v)) + let wBC := fun b c : Fin n => V (n * n + pairIndex (b, c)) + let wAC := fun u c : Fin n => V (2 * (n * n) + pairIndex (u, c)) + RecordProduct.preparationTime n (D m) g p values + + (∑ idx ∈ instanceIndices wAB (D m) g p, recordBase (m := m) hn V mem idx) + + (42 * pieceSize (D m) g + 28) * (falsePositiveCount wAB wBC wAC p + 1) + 7 + +/-- Same-state prefix and charged halt, with exact original positive and negative ET decisions. +All geometric, prime, original-input and every-wide-width premises are explicit. -/ +theorem runs_input {W m n g p A : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hD : 16 ≤ D m) + (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) (hN : D m ^ 18 ≤ n) + (hv : ∀ z ∈ values, z.natAbs ≤ A) (hW : admissionWidth n (D m) g p A ≤ W) : + let V := fun q => values.getD q 0 + let wAB := fun u v : Fin n => V (pairIndex (u, v)) + let wBC := fun b c : Fin n => V (n * n + pairIndex (b, c)) + let wAC := fun u c : Fin n => V (2 * (n * n) + pairIndex (u, c)) + let hn : 0 < n := lt_of_lt_of_le (by omega : 0 < D m) + ((Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN) + InputFits W (PreparationInput.input n (D m) g p values) ∧ + ∃ a : Data 64, + ExecWithin W Instances.program (.init (PreparationInput.input n (D m) g p values)) a + (RecordProduct.preparationTime n (D m) g p values - 1) ∧ + Instances.Output n (D m) g p V (.init (PreparationInput.input n (D m) g p values)) a ∧ + ∃ y t, t + 1 ≤ timeBound (m := m) hn g p values a.mem ∧ + Runs W (program.program Reg.found) + (State.init (PreparationInput.input n (D m) g p values)) ⟨program.size, y⟩ t ∧ + Executes W (program.program Reg.found) + (State.init (PreparationInput.input n (D m) g p values)) (y.regs Reg.found) (t + 1) ∧ + y.Bounded W ∧ (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ + y.regs Reg.scans ≤ falsePositiveCount wAB wBC wAC p + y.regs Reg.found ∧ + (y.regs Reg.found = 1 ↔ HasZeroTriangle wAB wBC wAC) ∧ + (y.regs Reg.found = 1 → ∃ u v c : Fin n, + y.regs (recordBank RecordWitness.Reg.witnessA) = u.val ∧ + y.regs (recordBank RecordWitness.Reg.witnessB) = v.val ∧ + y.regs (recordBank RecordWitness.Reg.witnessC) = c.val ∧ + weightSum wAB wBC wAC u v c = 0) ∧ + (∀ u, u < a.regs Instances.heap ∨ + a.regs Instances.heap + CallableProduct.windowSize n (n * n) ≤ u → y.mem u = a.mem u) ∧ + ∀ j : Fin 64, y.regs + (recordBank (RecordWitness.productBank (RecordProduct.producerBank j))) = a.regs j := by + have htwo : 2 ≤ W := by + have hr := record_admitted hW + unfold RecordWitness.admissionWidth at hr + omega + have heq : W - 2 + 2 = W := Nat.sub_add_cancel htwo + have h := exec_input (w := W - 2) values hlen hD hg hp hN hv + (by simpa only [heq] using hW) + rw [heq] at h + obtain ⟨hi, a, ha, hout, y, he, yb, yf, ys, yiff, yw, frame, regs⟩ := h + obtain ⟨t, ht, hex⟩ := he + refine ⟨hi, a, ha, hout, y, t, ?_, ?_, ?_, yb, yf, ys, yiff, ?_, frame, regs⟩ + · simpa only [timeBound, Nat.add_assoc] using Nat.add_le_add_right ht 1 + · simpa only [Nat.zero_add, State.init] using hex.runs (pc := 0) + (p := program.program Reg.found) (Program.Contains.self _).append_left + · simpa only [State.init] using hex.executes Reg.found + · intro hf + obtain ⟨ha, hb, hc, hz⟩ := yw hf + exact ⟨⟨_, ha⟩, ⟨_, hb⟩, ⟨_, hc⟩, rfl, rfl, rfl, hz⟩ + +end Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Indexing.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Indexing.lean new file mode 100644 index 000000000..fd74b2c48 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Indexing.lean @@ -0,0 +1,91 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Readback +public import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationLocality + +/-! +# The actual occupied ordinals name the existing finite instances + +The producer's real counter supplies the range, and its existing dependent-sum readback +supplies the unique residue/chunk/piece for each ordinal. These are consequences of output, +not a second enumeration or a list supplied to the native controller. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle + +/-- Every occupied runtime ordinal has exactly one existing finite meaning. -/ +theorem occupied_index_unique {n D g p i : ℕ} (values : ℕ → ℤ) + {initial a : Data 64} (h : Instances.Output n D g p values initial a) + (hD : 16 ≤ D) (hDn : D ≤ n) (hg : 1 ≤ g) (hp : p ≠ 0) + (hi : i < a.regs Instances.produced) : + ∃! idx : InstanceIndex p, + idx ∈ instanceIndices (fun u v : Fin n => values (pairIndex (u, v))) D g p ∧ + RecordProduct.recordOrdinal n D g p values idx = i := by + have hc := h.2.2.2.2.1 + rw [hc] at hi + obtain ⟨idx, hidx, huniq⟩ := PreparationSpec.index_unique values + (fun u v : Fin n => values (pairIndex (u, v))) hp (by intro u v; rfl) + hD hDn hg i hi + refine ⟨idx, ⟨hidx.1, hidx.2.symm⟩, fun other hother => ?_⟩ + exact huniq other ⟨hother.1, hother.2.symm⟩ + +/-- The actual occupied range enumerates every existing instance exactly once. This is a +proof-side interpretation of the native ordinal, not a table consumed by the program. -/ +theorem exists_occupied_order {n D g p : ℕ} (values : ℕ → ℤ) + {initial a : Data 64} (h : Instances.Output n D g p values initial a) + (hD : 16 ≤ D) (hDn : D ≤ n) (hg : 1 ≤ g) (hp : p ≠ 0) : + ∃ f : Fin (a.regs Instances.produced) → InstanceIndex p, + Function.Injective f ∧ + (∀ i, f i ∈ instanceIndices + (fun u v : Fin n => values (pairIndex (u, v))) D g p ∧ + RecordProduct.recordOrdinal n D g p values (f i) = i.val) ∧ + ∀ idx ∈ instanceIndices (fun u v : Fin n => values (pairIndex (u, v))) D g p, + ∃ i, f i = idx := by + classical + let I := instanceIndices (fun u v : Fin n => values (pairIndex (u, v))) D g p + have he (i : Fin (a.regs Instances.produced)) : ∃ idx, idx ∈ I ∧ + RecordProduct.recordOrdinal n D g p values idx = i.val := + (occupied_index_unique values h hD hDn hg hp i.isLt).exists + choose f hf using he + have hinj : Function.Injective f := by + intro i j hij + apply Fin.ext + exact (hf i).2.symm.trans ((congrArg + (RecordProduct.recordOrdinal n D g p values) hij).trans (hf j).2) + let f' : Fin (a.regs Instances.produced) → I := fun i => ⟨f i, (hf i).1⟩ + have hinj' : Function.Injective f' := fun _ _ hij => + hinj (congrArg Subtype.val hij) + have hcard : Fintype.card (Fin (a.regs Instances.produced)) = Fintype.card I := by + simpa only [Fintype.card_fin, Fintype.card_coe, I] using + PreparationSpec.count_eq_card values _ hp (by intro u v; rfl) h + have hsurj := ((Fintype.bijective_iff_injective_and_card f').mpr ⟨hinj', hcard⟩).2 + refine ⟨f, hinj, hf, ?_⟩ + intro idx hidx + obtain ⟨i, hi⟩ := hsurj ⟨idx, hidx⟩ + exact ⟨i, congrArg Subtype.val hi⟩ + +/-- Original interleaved query readback survives changes outside its protected span. -/ +theorem decodedQueries_eq_of_eqOn {n q Q heap : ℕ} (hn : 0 < n) + (x y : ℕ → ℕ) (h : Set.EqOn x y (Set.Iio heap)) (hspan : q + 2 * Q ≤ heap) : + RecordProduct.decodedQueries hn x q Q = RecordProduct.decodedQueries hn y q Q := by + unfold RecordProduct.decodedQueries + apply congrArg List.ofFn + funext i + apply Prod.ext <;> apply Fin.ext + · change x (q + 2 * i.val) % n = y (q + 2 * i.val) % n + rw [h (by have := i.isLt; change q + 2 * i.val < heap; omega)] + · change x (q + 2 * i.val + 1) % n = y (q + 2 * i.val + 1) % n + rw [h (by have := i.isLt; change q + 2 * i.val + 1 < heap; omega)] + +end Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Loop.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Loop.lean new file mode 100644 index 000000000..8283536cb --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Loop.lean @@ -0,0 +1,271 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Step +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Potential + +/-! +# Complete traversal of the actual prepared catalogue + +Every guarded iteration delivers the next occupied ordinal to the concrete record program. +The invariant counts only the visited prefix: distinct failures share one global budget and +the actual successful-return flags sum to at most one. Scratch is reused above the producer +heap; protected readback and the genuine original producer execution are kept separate. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +attribute [local instance] Classical.propDecidable + +private structure Invariant {m n g p : ℕ} (V : ℕ → ℤ) (a : Data 64) + (start : Data 416) (order : ℕ → InstanceIndex p) (hn : 0 < n) (x : Data 416) : Prop where + bank : ∀ j : Fin 64, x.regs + (recordBank (RecordWitness.productBank (RecordProduct.producerBank j))) = a.regs j + protected_mem : Set.EqOn x.mem a.mem (Set.Iio (a.regs Instances.heap)) + frame : ∀ u, u < a.regs Instances.heap ∨ + a.regs Instances.heap + CallableProduct.windowSize n (n * n) ≤ u → x.mem u = start.mem u + index_le : x.regs Reg.index ≤ a.regs Instances.produced + one : x.regs Reg.one = 1 + flag : x.regs Reg.found = 0 ∨ x.regs Reg.found = 1 + limit : x.regs Reg.limit = + (if x.regs Reg.found = 1 then x.regs Reg.index else a.regs Instances.produced) + scans_failed : x.regs Reg.scans ≤ + (∑ j ∈ Finset.range (x.regs Reg.index), + recordFailures (m := m) (g := g) hn V a.mem (order j)) + x.regs Reg.found + scans_coarse : x.regs Reg.scans ≤ x.regs Reg.index * (n * n + 1) + searched : Nat.count (fun j => recordHit (m := m) (n := n) (g := g) V (order j)) + (x.regs Reg.index) = x.regs Reg.found + witness : x.regs Reg.found = 1 → + x.regs (recordBank RecordWitness.Reg.witnessA) < n ∧ + x.regs (recordBank RecordWitness.Reg.witnessB) < n ∧ + x.regs (recordBank RecordWitness.Reg.witnessC) < n ∧ + V (x.regs (recordBank RecordWitness.Reg.witnessA) * n + + x.regs (recordBank RecordWitness.Reg.witnessB)) + + V (n * n + (x.regs (recordBank RecordWitness.Reg.witnessB) * n + + x.regs (recordBank RecordWitness.Reg.witnessC))) + + V (2 * (n * n) + (x.regs (recordBank RecordWitness.Reg.witnessA) * n + + x.regs (recordBank RecordWitness.Reg.witnessC))) = 0 + +private theorem window_mono {n Q : ℕ} (hQ : Q ≤ n * n) : + CallableProduct.windowSize n Q ≤ CallableProduct.windowSize n (n * n) := by + unfold CallableProduct.windowSize CallableProduct.sourceWidth + exact Nat.pow_le_pow_right (by decide) + (Nat.mul_le_mul_left 128 (Nat.clog_mono_right 2 (by omega))) + +/-- Run all occupied records, stopping at the first original witness. The original producer +readback is a caller premise; the initialized theorem constructs it by running the producer +once. No intermediate layout, answer, scan order or execution contract is an algorithm input. -/ +theorem exec_traverse {w m n g p A : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hD : 16 ≤ D m) + (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) (hN : D m ^ 18 ≤ n) + (hv : ∀ z ∈ values, z.natAbs ≤ A) + (hW : admissionWidth n (D m) g p A ≤ w + 2) + (a : Data 64) (x : Data 416) + (hout : Instances.Output n (D m) g p (fun q => values.getD q 0) + (.init (PreparationInput.input n (D m) g p values)) a) + (hbank : ∀ j : Fin 64, + x.regs (recordBank (RecordWitness.productBank (RecordProduct.producerBank j))) = a.regs j) + (hmem : Set.EqOn x.mem a.mem (Set.Iio (a.regs Instances.heap))) + (hheap : a.regs Instances.heap ≤ PreparationInput.wordBound n (D m) g p A) : + let V := fun q => values.getD q 0 + let wAB := fun u v : Fin n => V (pairIndex (u, v)) + let wBC := fun b c : Fin n => V (n * n + pairIndex (b, c)) + let wAC := fun u c : Fin n => V (2 * (n * n) + pairIndex (u, c)) + let hn : 0 < n := lt_of_lt_of_le (by omega : 0 < D m) + ((Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN) + ∃ y, ExecWithin (w + 2) traverse x y + ((∑ idx ∈ instanceIndices wAB (D m) g p, recordBase (m := m) hn V a.mem idx) + + (42 * pieceSize (D m) g + 28) * (falsePositiveCount wAB wBC wAC p + 1) + 7) ∧ + (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ + y.regs Reg.scans ≤ falsePositiveCount wAB wBC wAC p + y.regs Reg.found ∧ + (y.regs Reg.found = 1 ↔ HasZeroTriangle wAB wBC wAC) ∧ + (y.regs Reg.found = 1 → + y.regs (recordBank RecordWitness.Reg.witnessA) < n ∧ + y.regs (recordBank RecordWitness.Reg.witnessB) < n ∧ + y.regs (recordBank RecordWitness.Reg.witnessC) < n ∧ + V (y.regs (recordBank RecordWitness.Reg.witnessA) * n + + y.regs (recordBank RecordWitness.Reg.witnessB)) + + V (n * n + (y.regs (recordBank RecordWitness.Reg.witnessB) * n + + y.regs (recordBank RecordWitness.Reg.witnessC))) + + V (2 * (n * n) + (y.regs (recordBank RecordWitness.Reg.witnessA) * n + + y.regs (recordBank RecordWitness.Reg.witnessC))) = 0) ∧ + (∀ u, u < a.regs Instances.heap ∨ + a.regs Instances.heap + CallableProduct.windowSize n (n * n) ≤ u → y.mem u = x.mem u) ∧ + ∀ j : Fin 64, y.regs + (recordBank (RecordWitness.productBank (RecordProduct.producerBank j))) = a.regs j := by + classical + let V := fun q => values.getD q 0 + let wAB := fun u v : Fin n => V (pairIndex (u, v)) + let wBC := fun b c : Fin n => V (n * n + pairIndex (b, c)) + let wAC := fun u c : Fin n => V (2 * (n * n) + pairIndex (u, c)) + have hDn : D m ≤ n := (Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN + have hn : 0 < n := lt_of_lt_of_le (by omega) hDn + have hp0 := (Nat.mem_sqrtPrimeWindow_iff_sq.mp hp).1.ne_zero + let M := a.regs Instances.produced + let I := instanceIndices wAB (D m) g p + obtain ⟨f, -, hf, fsurj⟩ := exists_occupied_order V hout hD hDn hg hp0 + let order : ℕ → InstanceIndex p := fun i => + if hi : i < M then f ⟨i, hi⟩ else (⟨0, Nat.pos_of_ne_zero hp0⟩, 0, 0) + have order_at (i : ℕ) (hi : i < M) : order i = f ⟨i, hi⟩ := dite_eq_left hi + have order_mem (i : ℕ) (hi : i < M) : order i ∈ I := by + rw [order_at i hi] + exact (hf _).1 + have order_ord (i : ℕ) (hi : i < M) : + RecordProduct.recordOrdinal n (D m) g p V (order i) = i := by + rw [order_at i hi] + exact (hf _).2 + have order_inj : Set.InjOn order (Finset.range M) := by + intro i hi j hj hij + exact (order_ord i (Finset.mem_range.mp hi)).symm.trans + ((congrArg (RecordProduct.recordOrdinal n (D m) g p V) hij).trans + (order_ord j (Finset.mem_range.mp hj))) + have image_eq : (Finset.range M).image order = I := by + ext idx + simp only [Finset.mem_image, Finset.mem_range] + constructor + · rintro ⟨i, hi, rfl⟩ + exact order_mem i hi + · intro hi + obtain ⟨i, hi⟩ := fsurj idx hi + exact ⟨i.val, i.isLt, (order_at i.val i.isLt).trans hi⟩ + have sum_eq (charge : InstanceIndex p → ℕ) : + (∑ j ∈ Finset.range M, charge (order j)) = ∑ idx ∈ I, charge idx := by + rw [← image_eq, Finset.sum_image order_inj] + let B := fun i => recordBase (m := m) hn V a.mem (order i) + let F := fun i => recordFailures (m := m) (g := g) hn V a.mem (order i) + let C := 42 * pieceSize (D m) g + 28 + have hF : (∑ j ∈ Finset.range M, F j) ≤ falsePositiveCount wAB wBC wAC p := by + rw [show (∑ j ∈ Finset.range M, F j) = + ∑ idx ∈ I, recordFailures (m := m) (g := g) hn V a.mem idx from + sum_eq (recordFailures (m := m) (g := g) hn V a.mem)] + exact sum_recordFailures_le hn V hout hD hDn hg hp I (fun _ hi => hi) + let Inv := Invariant (m := m) (g := g) V a x order hn + let Φ := fun z : Data 416 => remainingCharge B F C M (z.regs Reg.index) (z.regs Reg.found) + have body : ∀ z, Inv z → (Cond.lt Reg.index Reg.limit).Holds z → + ∃ y T, ExecWithin (w + 2) step z y T ∧ Inv y ∧ T + 2 + Φ y ≤ Φ z := by + intro z hz hc + have hguard : z.regs Reg.index < z.regs Reg.limit := hc + have before : z.regs Reg.found = 0 := by + rcases hz.flag with h | h + · exact h + · have hlim := hz.limit + rw [h, ite_eq_left rfl] at hlim + omega + have lim : z.regs Reg.limit = M := by + simpa only [before, zero_ne_one, ite_false] using hz.limit + have hi : z.regs Reg.index < M := by omega + obtain ⟨y, S, T, he, hT, yi, yl, yo, ys, yf, hS, hcoarse, yhit, yw, ym, ybank⟩ := + exec_step values hlen hD hg hp hN hv hW a z hout hz.bank hz.protected_mem hheap rfl hi lim + hz.one hz.scans_coarse (order (z.regs Reg.index)) (order_mem _ hi) (order_ord _ hi) + have newflag : y.regs Reg.found ≤ 1 := by rcases yf with h | h <;> omega + have yInv : Inv y := by + refine ⟨ybank, ?_, ?_, ?_, yo, yf, ?_, ?_, ?_, ?_, yw⟩ + · intro u hu + exact (ym u (Or.inl hu)).trans (hz.protected_mem hu) + · intro u hu + have houtside : u < a.regs Instances.heap ∨ a.regs Instances.heap + + CallableProduct.windowSize n + (RecordProduct.recordQueryCount n (D m) p V (order (z.regs Reg.index))) ≤ u := by + rcases hu with h | h + · exact Or.inl h + · exact Or.inr (le_trans (Nat.add_le_add_left + (window_mono (recordQueryCount_le n (D m) p V _)) _) h) + exact (ym u houtside).trans (hz.frame u hu) + · rw [yi] + exact Nat.succ_le_of_lt hi + · simpa only [yi] using yl + · have hs := hz.scans_failed + rw [before, Nat.add_zero] at hs + rw [ys, yi, Finset.sum_range_succ] + change z.regs Reg.scans + S ≤ + (∑ j ∈ Finset.range (z.regs Reg.index), F j) + F (z.regs Reg.index) + y.regs Reg.found + change S ≤ F (z.regs Reg.index) + y.regs Reg.found at hS + change z.regs Reg.scans ≤ ∑ j ∈ Finset.range (z.regs Reg.index), F j at hs + omega + · rw [ys, yi, Nat.add_mul, Nat.one_mul] + exact Nat.add_le_add hz.scans_coarse hcoarse + · rw [yi, Nat.count_succ] + have searched := hz.searched + rw [before] at searched + rw [searched, Nat.zero_add] + rcases yf with h | h + · rw [ite_eq_right (by intro hh; have := yhit.mpr hh; omega), h] + · rw [ite_eq_left (yhit.mp h), h] + refine ⟨y, T, he, yInv, ?_⟩ + have paid := remainingCharge_step (base := B) (failures := F) (C := C) hi hT hS newflag + simpa only [Φ, yi, before] using paid + have h1 : Fits (w + 2) 1 := (counter_fits hW).of_le (by unfold counterEnvelope; omega) + obtain ⟨s, hs, sm, si, sl, sf, ss, so, sbank⟩ := exec_setup x h1 + have sInv : Inv s := by + refine ⟨fun j => (sbank _ (by change j.val < 400; omega)).trans (hbank j), ?_, ?_, ?_, + so, Or.inl sf, ?_, ?_, ?_, ?_, ?_⟩ + · intro u hu + exact (congrFun sm u).trans (hmem hu) + · intro u _ + exact congrFun sm u + · rw [si] + exact Nat.zero_le _ + · rw [sf, ite_eq_right (by decide : ¬(0 : ℕ) = 1), sl, hbank] + · simp only [si, ss, sf, Finset.range_zero, Finset.sum_empty, Nat.zero_add, le_refl] + · simp only [ss, si, Nat.zero_mul, le_refl] + · simp only [si, sf, Nat.count_zero] + · intro h + omega + obtain ⟨y, hy, hIy, hcy⟩ := ExecWithin.loop_potential Inv Φ body sInv + have hbudget : 5 + (Φ s + 2) ≤ + (∑ idx ∈ I, recordBase (m := m) hn V a.mem idx) + + C * (falsePositiveCount wAB wBC wAC p + 1) + 7 := by + dsimp only [Φ, remainingCharge] + rw [si, sf] + simp only [Finset.range_zero, Finset.sum_empty, Nat.sub_zero] + have hmul := Nat.mul_le_mul_left C (Nat.add_le_add_right hF 1) + rw [sum_eq (recordBase (m := m) hn V a.mem)] + omega + have hex : ExecWithin (w + 2) traverse x y + ((∑ idx ∈ I, recordBase (m := m) hn V a.mem idx) + + C * (falsePositiveCount wAB wBC wAC p + 1) + 7) := (hs.seq hy).mono hbudget + refine ⟨y, hex, hIy.flag, ?_, ?_, hIy.witness, hIy.frame, hIy.bank⟩ + · exact hIy.scans_failed.trans (Nat.add_le_add_right + ((sum_prefix_le F hIy.index_le).trans hF) _) + · constructor + · intro hf + obtain ⟨ha, hb, hc, hw⟩ := hIy.witness hf + exact ⟨⟨_, ha⟩, ⟨_, hb⟩, ⟨_, hc⟩, hw⟩ + · intro hzero + by_contra hnot + have hf : y.regs Reg.found = 0 := by rcases hIy.flag with h | h <;> omega + have hlim : y.regs Reg.limit = M := by + simpa only [hf, zero_ne_one, ite_false] using hIy.limit + have hi : y.regs Reg.index = M := by + change ¬ y.regs Reg.index < y.regs Reg.limit at hcy + have := hIy.index_le + omega + obtain ⟨u, v, c, hw⟩ := hzero + have hans := RecordProduct.product_answers_correct V wAB wBC wAC hD hg hp + (by intro b c; rfl) (by intro u c; rfl) + obtain ⟨rho, k, piece, hacc, hc⟩ := exists_mem_acceptedPairs hD hDn hg hp0 hans hw + obtain ⟨hidx, hquery, -⟩ := mem_acceptedPairs.mp hacc + obtain ⟨j, hj⟩ := fsurj (rho, k, piece) hidx + have hh : recordHit (m := m) (n := n) (g := g) V (order j.val) := by + rw [order_at j.val j.isLt, hj] + exact ⟨(u, v), hquery, c, hc, hw⟩ + have hpositive := Nat.count_strict_mono (p := fun j => + recordHit (m := m) (n := n) (g := g) V (order j)) hh j.isLt + have searched := hIy.searched + rw [hf, hi] at searched + rw [searched] at hpositive + exact Nat.not_lt_zero _ hpositive + +end Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Potential.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Potential.lean new file mode 100644 index 000000000..450568fe2 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Potential.lean @@ -0,0 +1,75 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Algebra.BigOperators.Group.Finset.Defs +import Mathlib.Algebra.BigOperators.Group.Finset.Basic +import Mathlib.Algebra.Order.BigOperators.Group.Finset +import Mathlib.Tactic.Ring + +/-! +# Paying for the native catalogue traversal + +The remaining potential reserves one successful scan in total. A failed call consumes only +its own distinct failed occurrences; a successful call consumes that reserve and terminates +the traversal. These internal arithmetic lemmas do not provide an execution or cost oracle. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime + +/-- One actual return consumes its failed occurrences and, only on success, the single reserve. +The base charge includes this iteration's real call/controller/loop overhead. -/ +theorem pay_return {T base C scans failures flag restBase restFailures : ℕ} + (hT : T + 2 ≤ base + C * scans) (hscans : scans ≤ failures + flag) + (hflag : flag ≤ 1) : + T + 2 + (restBase + C * (restFailures + 1 - flag)) ≤ + base + restBase + C * (failures + restFailures + 1) := by + have he : failures + flag + (restFailures + 1 - flag) = + failures + restFailures + 1 := by omega + have hpaid := Nat.mul_le_mul_left C hscans + calc + _ ≤ base + C * (failures + flag) + + (restBase + C * (restFailures + 1 - flag)) := by omega + _ = base + restBase + C * (failures + flag + (restFailures + 1 - flag)) := by ring + _ = _ := by rw [he] + +/-- Nonnegative charges in a visited prefix are bounded by those in the complete range. -/ +theorem sum_prefix_le (charge : ℕ → ℕ) {i M : ℕ} (hi : i ≤ M) : + ∑ j ∈ Finset.range i, charge j ≤ ∑ j ∈ Finset.range M, charge j := + Finset.sum_le_sum_of_subset (Finset.range_mono hi) + +/-- Reserve potential for a concrete finite sequence of base and failed-occurrence charges. -/ +@[expose] def remainingCharge (base failures : ℕ → ℕ) (C M i flag : ℕ) : ℕ := + ((∑ j ∈ Finset.range M, base j) - (∑ j ∈ Finset.range i, base j)) + + C * (((∑ j ∈ Finset.range M, failures j) - + (∑ j ∈ Finset.range i, failures j)) + 1 - flag) + +/-- The actual per-call bound decreases the potential by the charged body and loop work. +The loop invariant establishes zero prior success whenever its guard still holds. -/ +theorem remainingCharge_step {base failures : ℕ → ℕ} {C M i T scans flag : ℕ} + (hi : i < M) (hT : T + 2 ≤ base i + C * scans) + (hscans : scans ≤ failures i + flag) (hflag : flag ≤ 1) : + T + 2 + remainingCharge base failures C M (i + 1) flag ≤ + remainingCharge base failures C M i 0 := by + have hB := sum_prefix_le base (Nat.succ_le_of_lt hi) + have hF := sum_prefix_le failures (Nat.succ_le_of_lt hi) + rw [Finset.sum_range_succ] at hB hF + have eqB : (∑ j ∈ Finset.range M, base j) - (∑ j ∈ Finset.range i, base j) = + base i + ((∑ j ∈ Finset.range M, base j) - + ((∑ j ∈ Finset.range i, base j) + base i)) := by omega + have eqF : (∑ j ∈ Finset.range M, failures j) - (∑ j ∈ Finset.range i, failures j) = + failures i + ((∑ j ∈ Finset.range M, failures j) - + ((∑ j ∈ Finset.range i, failures j) + failures i)) := by omega + unfold remainingCharge + rw [Finset.sum_range_succ, Finset.sum_range_succ, eqB, eqF] + simpa only [Nat.sub_zero, Nat.add_assoc] using pay_return hT hscans hflag + +end Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Program.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Program.lean new file mode 100644 index 000000000..062e51944 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Program.lean @@ -0,0 +1,107 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness + +/-! +# Native traversal of the occupied ExactTriangle catalogue + +The producer runs once. The controller visits actual occupied ordinals and invokes the complete +400-register record-product/witness code. A success closes the loop immediately; no later +product or original-weight scan is called. Five disjoint controller registers are used, +and no catalogue/window snapshot or clearing operation is introduced. + +The control adapts the fixed-prime part of `Host.Loop` at +`anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5`. The existing native producer +uses chunk-major order and prepares all matrices first; the source uses piece-major rounds and +writes each pair of matrices in its round. The code and native charges here are original. +These stage definitions are implementation interfaces, not a parameter/prime-selection host. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime + +open Cslib.WordRAM + +/-- The complete record-product/witness bank moves together, including both internal banks. -/ +@[expose] def recordBank : Fin 400 → Fin 416 := Fin.castLE (by decide) + +namespace Reg + +/-- Number of actual occupied records visited. -/ +abbrev index : Fin 416 := 400 +/-- Original occupied count, shortened to the visited prefix only after success. -/ +abbrev limit : Fin 416 := 401 +/-- Raw unsigned original-witness verdict. -/ +abbrev found : Fin 416 := 402 +/-- Total actual original-weight scans across all visited records. -/ +abbrev scans : Fin 416 := 403 +/-- Constant one outside the callee bank. -/ +abbrev one : Fin 416 := 404 +end Reg + +/-- Execute the actual producer once, without changing its finite instruction graph. -/ +@[expose] def produce : Code .standard 416 := RecordWitness.produce.rename recordBank + +open Reg in +/-- Read the actual producer count and initialize the native controller. -/ +@[expose] def setup : Code .standard 416 := + let count := recordBank + (RecordWitness.productBank (RecordProduct.producerBank Instances.produced)) + ram[ index ← #0; limit ← count; found ← #0; scans ← #0; one ← #1 ] + +open Reg in +/-- Deliver the actual ordinal to the concrete callee. -/ +@[expose] def select : Code .standard 416 := + .op (.copy (recordBank (RecordWitness.productBank RecordProduct.Reg.ordinal)) index) + +/-- Actual product computation, descriptor reading and original signed-weight verification. -/ +@[expose] def record : Code .standard 416 := RecordWitness.program.rename recordBank + +open Reg in +/-- Accumulate the actual scan counter and close the loop exactly on a successful return. -/ +@[expose] def receive : Code .standard 416 := + let returned := recordBank RecordWitness.Reg.found + let performed := recordBank RecordWitness.Reg.scans + ram[ scans ← scans + performed; found ← returned; index ← index + one; + if found = one then [ limit ← index ] else [ limit ← limit ] ] + +/-- One actual occupied record, with all argument/return/controller work in native code. -/ +@[expose] def step : Code .standard 416 := .seq select (.seq record receive) + +/-- Traverse the computed catalogue from a genuine prepared state. -/ +@[expose] def traverse : Code .standard 416 := + .seq setup (.loop (.lt Reg.index Reg.limit) step) + +/-- Complete original-input fixed-prime solver: one preparation and the actual native loop. -/ +@[expose] def program : Code .standard 416 := .seq produce traverse + +/-- The whole fixed-prime controller is one standard-native program, with a fixed register +bank and at most 133152 flat slots including halt. It never reads the physical word width. -/ +theorem program_fixed : (program.program Reg.found).length ≤ 133152 ∧ + ¬(program.program Reg.found).ReadsWidth := by + constructor + · simp only [Code.program, List.length_append, Code.length_assemble, + List.length_singleton, program, produce, traverse, setup, step, select, record, + receive, RecordWitness.produce, RecordProduct.produce, RecordWitness.program, + Code.size, Code.size_rename] + have hp := Instances.program_fixed.1 + have hr := RecordProduct.program_fixed.1 + have hc := RecordWitness.consume_fixed.1 + omega + · apply Code.not_readsWidth_program + simp only [program, produce, traverse, setup, step, select, record, receive, + RecordWitness.produce, RecordProduct.produce, RecordWitness.program, + Code.ReadsWidth, Code.readsWidth_rename, Op.ReadsWidth, Op.bin, reduceCtorEq, + false_or, or_false] + exact not_or.mpr ⟨Instances.program_fixed.2, + not_or.mpr ⟨RecordProduct.program_fixed.2, RecordWitness.consume_fixed.2⟩⟩ + +end Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Record.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Record.lean new file mode 100644 index 000000000..ec4b0c690 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Record.lean @@ -0,0 +1,219 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Accounting +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Controller +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Width +import Complexity.Computability.WordRAM.RegisterFrame + +/-! +# An actual record call after dirty workspace reuse + +Protected producer words and registers retain their original meaning even after earlier +products have used the workspace. The caller reuses the actual full 400-register code and +derives its finite failure/success semantics from real output words. No producer rerun, clean +workspace, intermediate solver execution or scan-count certificate is assumed. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +private theorem recordBank_injective : Function.Injective recordBank := by + intro i j h + exact Fin.ext (congrArg (fun k : Fin 416 => k.val) h) + +/-- Execute the real product and verifier from protected producer readback. The explicit heap +bound is supplied by the actual one-time producer's prefix theorem in the initialized client. +All other record/counter fits follow from original input and the numerical admission. -/ +theorem exec_record {w m n g p A : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hD : 16 ≤ D m) + (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) (hN : D m ^ 18 ≤ n) + (hv : ∀ z ∈ values, z.natAbs ≤ A) + (hW : admissionWidth n (D m) g p A ≤ w + 2) + (a : Data 64) (x : Data 416) + (hout : Instances.Output n (D m) g p (fun q => values.getD q 0) + (.init (PreparationInput.input n (D m) g p values)) a) + (hbank : ∀ j : Fin 64, + x.regs (recordBank (RecordWitness.productBank (RecordProduct.producerBank j))) = a.regs j) + (hmem : Set.EqOn x.mem a.mem (Set.Iio (a.regs Instances.heap))) + (hheap : a.regs Instances.heap ≤ PreparationInput.wordBound n (D m) g p A) + (idx : InstanceIndex p) + (hidx : idx ∈ instanceIndices + (fun u v : Fin n => values.getD (pairIndex (u, v)) 0) (D m) g p) + (hi : x.regs (recordBank (RecordWitness.productBank RecordProduct.Reg.ordinal)) = + RecordProduct.recordOrdinal n (D m) g p (fun q => values.getD q 0) idx) : + let V := fun q => values.getD q 0 + let hn : 0 < n := lt_of_lt_of_le (by omega : 0 < D m) + ((Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN) + let Q := RecordProduct.recordQueryCount n (D m) p V idx + let qs := RecordProduct.decodedQueries hn a.mem (RecordProduct.recordQueries n (D m) p idx) Q + ∃ y S, ExecWithin (w + 2) record x y + (RecordProduct.timeBound (m := m) qs + 5 * Nat.sqrt (D m) + 8 * Q + + (42 * pieceSize (D m) g + 28) * S + 52) ∧ + y.regs (recordBank RecordWitness.Reg.scans) = S ∧ + (y.regs (recordBank RecordWitness.Reg.found) = 0 ∨ + y.regs (recordBank RecordWitness.Reg.found) = 1) ∧ + S ≤ recordFailures (m := m) (g := g) hn V a.mem idx + + y.regs (recordBank RecordWitness.Reg.found) ∧ + S ≤ n * n + 1 ∧ + (y.regs (recordBank RecordWitness.Reg.found) = 1 ↔ + ∃ q ∈ chunkOf (fun u v : Fin n => V (pairIndex (u, v))) (D m) p idx.1 idx.2.1, + ∃ c ∈ piece n (D m) g idx.2.2, + V (pairIndex (q.1, q.2)) + V (n * n + pairIndex (q.2, c)) + + V (2 * (n * n) + pairIndex (q.1, c)) = 0) ∧ + (y.regs (recordBank RecordWitness.Reg.found) = 1 → + y.regs (recordBank RecordWitness.Reg.witnessA) < n ∧ + y.regs (recordBank RecordWitness.Reg.witnessB) < n ∧ + y.regs (recordBank RecordWitness.Reg.witnessC) < n ∧ + V (y.regs (recordBank RecordWitness.Reg.witnessA) * n + + y.regs (recordBank RecordWitness.Reg.witnessB)) + + V (n * n + (y.regs (recordBank RecordWitness.Reg.witnessB) * n + + y.regs (recordBank RecordWitness.Reg.witnessC))) + + V (2 * (n * n) + (y.regs (recordBank RecordWitness.Reg.witnessA) * n + + y.regs (recordBank RecordWitness.Reg.witnessC))) = 0) ∧ + (∀ u, u < a.regs Instances.heap ∨ + a.regs Instances.heap + CallableProduct.windowSize n Q ≤ u → y.mem u = x.mem u) ∧ + (∀ j : Fin 64, y.regs + (recordBank (RecordWitness.productBank (RecordProduct.producerBank j))) = a.regs j) ∧ + ∀ j : Fin 416, 400 ≤ j.val → y.regs j = x.regs j := by + let V := fun q => values.getD q 0 + let AB := fun u v : ℕ => V (u * n + v) + let BC := fun v c : ℕ => V (n * n + (v * n + c)) + let AC := fun u c : ℕ => V (2 * (n * n) + (u * n + c)) + let wAB := fun u v : Fin n => V (pairIndex (u, v)) + let wBC := fun v c : Fin n => V (n * n + pairIndex (v, c)) + let wAC := fun u c : Fin n => V (2 * (n * n) + pairIndex (u, c)) + have hDn : D m ≤ n := (Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN + have hn : 0 < n := lt_of_lt_of_le (by omega) hDn + have hp0 := (Nat.mem_sqrtPrimeWindow_iff_sq.mp hp).1.ne_zero + let z := x.comap recordBank + let z₀ := (z.comap RecordWitness.productBank).comap RecordProduct.producerBank + have zr : z₀.regs = a.regs := funext hbank + have zheap : z.regs (RecordWitness.productBank 42) = a.regs Instances.heap := hbank 42 + have hcurrent : Instances.Output n (D m) g p V z₀ z₀ := + hout.reframe V zr hmem + obtain ⟨hend, -, hspan, -, -, hcoords, hprefix⟩ := + RecordWitness.record_view V _ a hout hD hDn hg hp0 idx hidx + have mem_eq (u : ℕ) (hu : u < a.regs Instances.heap) : z.mem u = a.mem u := hmem hu + have header (u : ℕ) (hu : u < 5 + 3 * (n * n)) : z₀.mem u = + (Data.init (PreparationInput.input n (D m) g p values) : Data 64).mem u := + (mem_eq u (lt_of_lt_of_le hu hend)).trans (hprefix u hu) + have original (q : ℕ) (hq : q < 3 * (n * n)) : z.mem (5 + q) = Signed.encode (V q) := + (header (5 + q) (by omega)).trans (PreparationInput.input_word n (D m) g p values q) + have hmatrix : ∀ u < n, ∀ v < n, + z.mem (5 + (u * n + v)) = Signed.encode (AB u v) ∧ + z.mem (5 + n * n + (u * n + v)) = Signed.encode (BC u v) ∧ + z.mem (5 + n * n + n * n + (u * n + v)) = Signed.encode (AC u v) := by + intro u hu v hv' + have hrow := Nat.mul_le_mul_right n (Nat.succ_le_of_lt hu) + rw [Nat.succ_mul] at hrow + refine ⟨original _ (by omega), ?_, ?_⟩ + · simpa only [Nat.add_assoc] using original (n * n + (u * n + v)) (by omega) + · have he : 5 + n * n + n * n + (u * n + v) = + 5 + (2 * (n * n) + (u * n + v)) := by omega + rw [he] + exact original _ (by omega) + have hmag : ∀ u < n, ∀ v < n, + (AB u v).natAbs ≤ A ∧ (BC u v).natAbs ≤ A ∧ (AC u v).natAbs ≤ A := by + intro u hu v hv' + have hrow := Nat.mul_le_mul_right n (Nat.succ_le_of_lt hu) + rw [Nat.succ_mul] at hrow + exact ⟨RecordWitness.original_magnitude values hlen hv (by omega), + RecordWitness.original_magnitude values hlen hv (by omega), + RecordWitness.original_magnitude values hlen hv (by omega)⟩ + let q := RecordProduct.recordQueries n (D m) p idx + let Q := RecordProduct.recordQueryCount n (D m) p V idx + let qs := RecordProduct.decodedQueries hn a.mem q Q + have hQ : Q ≤ n ^ 2 := (recordQueryCount_le n (D m) p V idx).trans_eq (pow_two n).symm + have hlenQ : qs.length = Q := RecordProduct.decodedQueries_length hn a.mem q Q + have hqs : RecordProduct.decodedQueries hn z.mem q Q = qs := + decodedQueries_eq_of_eqOn hn z.mem a.mem hmem hspan + have admitted := record_admitted hW + obtain ⟨r, S, he, rs, rf, hS, riff, answers, rwit, rframe, rregs⟩ := + RecordWitness.exec_of_output V AB BC AC z₀ z hcurrent hD hg hp0 hN + ((header 1 (by omega)).trans rfl) ((header 2 (by omega)).trans rfl) + ((header 3 (by omega)).trans rfl) idx hidx hi hmatrix hmag + (RecordWitness.signed_fits admitted) (by + rw [zheap] + exact RecordWitness.physical_fits hheap hQ hN admitted) + dsimp only [q, Q] at hqs + rw [hqs] at he answers + have hcoordinate (i : Fin qs.length) : + z.mem (q + 2 * i.val) = (qs.get i).1.val ∧ + z.mem (q + 2 * i.val + 1) = (qs.get i).2.val := by + have hj : i.val < Q := by simpa only [hlenQ] using i.isLt + have hv := RecordProduct.decodedQueries_get_val hn a.mem q Q i + (hcoords i.val hj).1 (hcoords i.val hj).2 + exact ⟨(mem_eq _ (by omega)).trans hv.1.symm, + (mem_eq _ (by omega)).trans hv.2.symm⟩ + have hfail := RecordWitness.native_failed_count_le V AB BC AC wAB wBC wAC hD hg + (by intro u v; rfl) (by intro u v; rfl) (by intro u v; rfl) idx qs + (⟨0, hn⟩, ⟨0, hn⟩) (fun j => z.mem (q + 2 * j)) + (fun j => z.mem (q + 2 * j + 1)) hcoordinate r.mem + (r.regs (RecordWitness.productBank RecordProduct.Reg.result)) answers + rw [hlenQ] at hfail + have hsuccess := RecordWitness.actual_success_iff V AB BC AC wAB wBC wAC hD hg hp + (by intro u v; rfl) (by intro u v; rfl) (by intro u v; rfl) + (by intro v c; rfl) (by intro u c; rfl) idx qs + (actual_queries_chunk hn V hout hD hDn hp0 idx hidx).2 + (fun j => z.mem (q + 2 * j)) (fun j => z.mem (q + 2 * j + 1)) + hcoordinate r.mem (r.regs (RecordWitness.productBank RecordProduct.Reg.result)) answers + rw [hlenQ] at hsuccess + let y := r.extend recordBank x + have yr (j : Fin 400) : y.regs (recordBank j) = r.regs j := + recordBank_injective.extend_apply _ _ j + have hex : ExecWithin (w + 2) record x y + (RecordProduct.timeBound (m := m) qs + 5 * Nat.sqrt (D m) + 8 * Q + + (42 * pieceSize (D m) g + 28) * S + 52) := by + apply (he.rename recordBank_injective).mono + have ht := Nat.mul_le_mul_right S (show + 42 * min (pieceSize (D m) g) (n - idx.2.2 * pieceSize (D m) g) + 28 ≤ + 42 * pieceSize (D m) g + 28 by omega) + omega + refine ⟨y, S, hex, (yr _).trans rs, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · simpa only [yr] using rf + · have hh := hS.trans (Nat.add_le_add_right hfail _) + simpa only [recordFailures, qs, q, Q, RecordProduct.decodedQueries_length, + yr, V, wAB, wBC, wAC] using hh + · have hc : Nat.count (fun j => + r.mem (r.regs (RecordWitness.productBank RecordProduct.Reg.result) + j) ≠ 0 ∧ + ∀ c : Fin (min (pieceSize (D m) g) (n - idx.2.2 * pieceSize (D m) g)), + AB (z.mem (q + 2 * j)) (z.mem (q + 2 * j + 1)) + + BC (z.mem (q + 2 * j + 1)) (idx.2.2 * pieceSize (D m) g + c.val) + + AC (z.mem (q + 2 * j)) (idx.2.2 * pieceSize (D m) g + c.val) ≠ 0) Q ≤ Q := + Nat.count_le _ + have hflag : r.regs RecordWitness.Reg.found ≤ 1 := by rcases rf with h | h <;> omega + have hQ' := recordQueryCount_le n (D m) p V idx + have hcS : S ≤ Q + r.regs RecordWitness.Reg.found := + hS.trans (Nat.add_le_add_right hc _) + omega + · simpa only [yr, wAB, wBC, wAC, weightSum] using riff.trans hsuccess + · intro hf + obtain ⟨ha, hb, hlo, hhi, hz, -⟩ := rwit ((yr _).symm.trans hf) + have hlen := min_le_right (pieceSize (D m) g) (n - idx.2.2 * pieceSize (D m) g) + exact ⟨by simpa only [yr] using ha, by simpa only [yr] using hb, + by simp only [yr]; omega, by simpa only [yr] using hz⟩ + · intro u hu + exact rframe u (by simpa only [zheap] using hu) + · intro j + exact (yr _).trans ((rregs _ (by change j.val < 65; omega)).trans (hbank j)) + · intro j hj + apply hex.regs_eq + apply Code.preserves_rename_of_forall_ne + intro i hij + have hv := congrArg (fun k : Fin 416 => k.val) hij + change i.val = j.val at hv + omega + +end Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/SourceBounds.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/SourceBounds.lean new file mode 100644 index 000000000..bbe40d1b9 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/SourceBounds.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Flat +import Complexity.Foundations.Data.Nat.Log + +/-! +# Original-input bounds for every fixed-prime native prefix + +The admission remains logarithmic for polynomial original weights and bounded scalar headers. +The actual minimum-width halting run controls all wider prefixes, including dirty loads and +all reused-workspace calls. The support envelope is independent of the physical width, and +does not instruct the machine to clear or snapshot that range. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime + +open Cslib.WordRAM +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- A fixed original-input bound for all prefix words and memory support. -/ +@[expose] def wordBound (n D g p A : ℕ) : ℕ := 2 ^ admissionWidth n D g p A + +/-- The all-record controller retains an explicit polynomial envelope for each weight exponent. -/ +theorem wordBound_le_pow {n D g p A κ : ℕ} (hn : 1 ≤ n) + (hD : D ≤ n) (hg : g ≤ n) (hp : p ≤ n) (hA : A ≤ (n + 2) ^ κ) : + wordBound n D g p A ≤ (n + 2) ^ (514 * κ + 11818) := by + let b := n + 2 + have hb : 2 ≤ b := by dsimp only [b]; omega + have hbase : 2 ^ Nat.clog 2 b ≤ b ^ 2 := + (Nat.pow_clog_le_mul (by decide : 1 < (2 : ℕ)) (by omega : b ≠ 0)).trans + (by nlinarith only [hb]) + have hw := admissionWidth_le_log hn hD hg hp hA + change 2 ^ admissionWidth n D g p A ≤ _ + calc + _ ≤ 2 ^ ((257 * κ + 5909) * Nat.clog 2 b) := Nat.pow_le_pow_right (by decide) hw + _ = (2 ^ Nat.clog 2 b) ^ (257 * κ + 5909) := by rw [← Nat.pow_mul, Nat.mul_comm] + _ ≤ (b ^ 2) ^ (257 * κ + 5909) := Nat.pow_le_pow_left hbase _ + _ = b ^ (514 * κ + 11818) := by + rw [← Nat.pow_mul] + exact congrArg (fun j : ℕ => b ^ j) (by omega : 2 * (257 * κ + 5909) = 514 * κ + 11818) + +/-- Every actually reached state at every admitted wider width has the same original-input +polynomial word/support envelope. The concrete minimum-width run, not an assumed execution +certificate, supplies the comparison run. -/ +theorem bounded_reachable_original {W m n g p A k : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hD : 16 ≤ D m) + (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) (hN : D m ^ 18 ≤ n) + (hv : ∀ z ∈ values, z.natAbs ≤ A) (hW : admissionWidth n (D m) g p A ≤ W) + {s : State 416} (hr : Runs W (program.program Reg.found) + (State.init (PreparationInput.input n (D m) g p values)) s k) : + (∀ i, s.data.regs i < wordBound n (D m) g p A) ∧ + (∀ a, s.data.mem a < wordBound n (D m) g p A) ∧ + ∀ a, wordBound n (D m) g p A ≤ a → s.data.mem a = 0 := by + obtain ⟨hi, a, ha, hout, y, t, ht, hpref, he, -⟩ := + runs_input values hlen hD hg hp hN hv le_rfl + have hlow := Runs.prefix_at_smaller_width program_fixed.2 hW hr he + have hs := hlow.bounded (Data.Bounded.init hi) + refine ⟨hs.1, hs.2, ?_⟩ + intro a ha + rw [hlow.mem_above (Data.Bounded.init hi) ha] + cases a with + | zero => + change 2 ^ admissionWidth n (D m) g p A ≤ 0 at ha + have := Nat.two_pow_pos (admissionWidth n (D m) g p A) + omega + | succ a => + change (PreparationInput.input n (D m) g p values).getD a 0 = 0 + apply List.getD_eq_default + have hlen := hi.1 + dsimp only [Fits] at hlen + change 2 ^ admissionWidth n (D m) g p A ≤ a + 1 at ha + omega + +end Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Step.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Step.lean new file mode 100644 index 000000000..afc37d1d5 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Step.lean @@ -0,0 +1,143 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Record + +/-! +# Charged ordinal delivery, concrete call and native return + +This internal step theorem composes the actual copy, unchanged full record computation and +native scan/flag/counter return. The selected finite index is proved from the runtime ordinal; +it is not an additional input field or a supplied algorithm. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage +open Cslib.Algorithms.WordRAM.MatrixMultiplication.Schonhage + +/-- Execute one real occupied ordinal. The instruction bound includes every argument/return +instruction; its two outer-loop instructions appear in `recordBase`. -/ +theorem exec_step {w m n g p A i : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hD : 16 ≤ D m) + (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) (hN : D m ^ 18 ≤ n) + (hv : ∀ z ∈ values, z.natAbs ≤ A) + (hW : admissionWidth n (D m) g p A ≤ w + 2) + (a : Data 64) (x : Data 416) + (hout : Instances.Output n (D m) g p (fun q => values.getD q 0) + (.init (PreparationInput.input n (D m) g p values)) a) + (hbank : ∀ j : Fin 64, + x.regs (recordBank (RecordWitness.productBank (RecordProduct.producerBank j))) = a.regs j) + (hmem : Set.EqOn x.mem a.mem (Set.Iio (a.regs Instances.heap))) + (hheap : a.regs Instances.heap ≤ PreparationInput.wordBound n (D m) g p A) + (hi : x.regs Reg.index = i) (hiM : i < a.regs Instances.produced) + (hl : x.regs Reg.limit = a.regs Instances.produced) (h1 : x.regs Reg.one = 1) + (hscans : x.regs Reg.scans ≤ i * (n * n + 1)) + (idx : InstanceIndex p) + (hidx : idx ∈ instanceIndices + (fun u v : Fin n => values.getD (pairIndex (u, v)) 0) (D m) g p) + (hord : RecordProduct.recordOrdinal n (D m) g p (fun q => values.getD q 0) idx = i) : + let V := fun q => values.getD q 0 + let hn : 0 < n := lt_of_lt_of_le (by omega : 0 < D m) + ((Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN) + let Q := RecordProduct.recordQueryCount n (D m) p V idx + ∃ y S T, ExecWithin (w + 2) step x y T ∧ + T + 2 ≤ recordBase (m := m) hn V a.mem idx + + (42 * pieceSize (D m) g + 28) * S ∧ + y.regs Reg.index = i + 1 ∧ + y.regs Reg.limit = (if y.regs Reg.found = 1 then i + 1 else a.regs Instances.produced) ∧ + y.regs Reg.one = 1 ∧ + y.regs Reg.scans = x.regs Reg.scans + S ∧ + (y.regs Reg.found = 0 ∨ y.regs Reg.found = 1) ∧ + S ≤ recordFailures (m := m) (g := g) hn V a.mem idx + y.regs Reg.found ∧ + S ≤ n * n + 1 ∧ + (y.regs Reg.found = 1 ↔ recordHit (m := m) (n := n) (g := g) V idx) ∧ + (y.regs Reg.found = 1 → + y.regs (recordBank RecordWitness.Reg.witnessA) < n ∧ + y.regs (recordBank RecordWitness.Reg.witnessB) < n ∧ + y.regs (recordBank RecordWitness.Reg.witnessC) < n ∧ + V (y.regs (recordBank RecordWitness.Reg.witnessA) * n + + y.regs (recordBank RecordWitness.Reg.witnessB)) + + V (n * n + (y.regs (recordBank RecordWitness.Reg.witnessB) * n + + y.regs (recordBank RecordWitness.Reg.witnessC))) + + V (2 * (n * n) + (y.regs (recordBank RecordWitness.Reg.witnessA) * n + + y.regs (recordBank RecordWitness.Reg.witnessC))) = 0) ∧ + (∀ u, u < a.regs Instances.heap ∨ + a.regs Instances.heap + CallableProduct.windowSize n Q ≤ u → y.mem u = x.mem u) ∧ + ∀ j : Fin 64, y.regs + (recordBank (RecordWitness.productBank (RecordProduct.producerBank j))) = a.regs j := by + let V := fun q => values.getD q 0 + let hn : 0 < n := lt_of_lt_of_le (by omega : 0 < D m) + ((Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN) + let dst := recordBank (RecordWitness.productBank RecordProduct.Reg.ordinal) + let z := x.set dst i + have hselect : ExecWithin (w + 2) select x z 1 := by + simpa only [select, hi, dst, z] using + (ExecWithin.copy (B := .standard) (w := w + 2) (x := x) (d := dst) (s := Reg.index)) + have zbank (j : Fin 64) : + z.regs (recordBank (RecordWitness.productBank (RecordProduct.producerBank j))) = + a.regs j := by + change Function.update x.regs dst i + (recordBank (RecordWitness.productBank (RecordProduct.producerBank j))) = a.regs j + rw [Function.update_of_ne (by + intro h + have hv := congrArg (fun k : Fin 416 => k.val) h + change j.val = 64 at hv + omega)] + exact hbank j + have control (j : Fin 416) (hj : 400 ≤ j.val) : z.regs j = x.regs j := by + change Function.update x.regs dst i j = x.regs j + apply Function.update_of_ne + intro h + have hv := congrArg (fun k : Fin 416 => k.val) h + change j.val = 64 at hv + omega + obtain ⟨r, S, hr, rs, rf, hS, hcoarse, hit, wit, frame, bank, ctrl⟩ := + exec_record values hlen hD hg hp hN hv hW a z hout zbank hmem hheap idx hidx (by + change Function.update x.regs dst i dst = _ + simpa using hord.symm) + have rc (j : Fin 416) (hj : 400 ≤ j.val) : r.regs j = x.regs j := + (ctrl j hj).trans (control j hj) + have hfits := return_fits hW + (produced_le V hout hD ((Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN) hg) + hiM hscans hcoarse + obtain ⟨y, hy, ym, yi, yl, ys, yf, ypres⟩ := exec_receive r + ((rc Reg.index (by decide)).trans hi) ((rc Reg.limit (by decide)).trans hl) + (rc Reg.scans (by decide)) rs rfl ((rc Reg.one (by decide)).trans h1) hfits.1 hfits.2 + have yflag : y.regs Reg.found = r.regs (recordBank RecordWitness.Reg.found) := yf + have ylow (j : Fin 400) : y.regs (recordBank j) = r.regs (recordBank j) := + ypres _ (Or.inl (by change j.val < 400; exact j.isLt)) + let T := RecordProduct.timeBound (m := m) + (RecordProduct.decodedQueries hn a.mem (RecordProduct.recordQueries n (D m) p idx) + (RecordProduct.recordQueryCount n (D m) p V idx)) + + 5 * Nat.sqrt (D m) + 8 * RecordProduct.recordQueryCount n (D m) p V idx + + (42 * pieceSize (D m) g + 28) * S + 59 + have hex : ExecWithin (w + 2) step x y T := (hselect.seq (hr.seq hy)).mono (by + dsimp only [T, V] + omega) + refine ⟨y, S, T, hex, ?_, yi, ?_, ?_, ys, ?_, ?_, hcoarse, ?_, ?_, ?_, ?_⟩ + · dsimp only [recordBase, T, V] + omega + · rw [yflag] + exact yl + · exact (ypres _ (Or.inr rfl)).trans ((rc Reg.one (by decide)).trans h1) + · simpa only [yflag] using rf + · simpa only [yflag] using hS + · simpa only [yflag, recordHit, weightSum] using hit + · intro hf + simpa only [ylow] using wit (yflag ▸ hf) + · intro u hu + exact (congrFun ym u).trans (frame u hu) + · intro j + exact (ylow _).trans (bank j) + +end Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/TimeBound.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/TimeBound.lean new file mode 100644 index 000000000..725b4bb1e --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/TimeBound.lean @@ -0,0 +1,119 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Flat +import Mathlib.Tactic.Positivity + +/-! +# Aggregate native fixed-prime charges + +The actual producer chunk cardinalities derive occurrence sparsity for every real product. +Summing over the existing finite instances gives an aggregate product saving, with the +one-time preparation and all root/query/controller costs separate. Failed original-weight +scans use one global `F(p)+1` reserve, not that reserve per instance. The fixed prime is still +an input: this does not select parameters or establish the final ET/3SUM source exponent. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- The existing finite family bounds the sum of actual queried-product charges, including a +standalone halt for each term as a conservative upper bound. Every occurrence bound is derived +from the real chunk count; there is no supplied sparse execution or solver premise. -/ +theorem products_time_le {m n g p : ℕ} (hn : 0 < n) (values : ℕ → ℤ) (mem : ℕ → ℕ) + (hD : 16 ≤ D m) (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) (hN : D m ^ 18 ≤ n) : + let I := instanceIndices (fun u v : Fin n => values (pairIndex (u, v))) (D m) g p + let qs := fun idx => RecordProduct.decodedQueries hn mem + (RecordProduct.recordQueries n (D m) p idx) + (RecordProduct.recordQueryCount n (D m) p values idx) + ((∑ idx ∈ I, (RecordProduct.timeBound (m := m) (qs idx) + 1) : ℕ) : ℝ) ≤ + 138412032 * ((19 * m + 1 : ℕ) : ℝ) * (n : ℝ) ^ 3 * g / + (D m : ℝ) ^ (1 / 18 : ℝ) := by + let I := instanceIndices (fun u v : Fin n => values (pairIndex (u, v))) (D m) g p + let qs := fun idx => RecordProduct.decodedQueries hn mem + (RecordProduct.recordQueries n (D m) p idx) + (RecordProduct.recordQueryCount n (D m) p values idx) + let R : ℝ := 34603008 * ((19 * m + 1 : ℕ) : ℝ) * (n : ℝ) ^ 2 / + (D m : ℝ) ^ (1 / 18 : ℝ) + have hr (idx : InstanceIndex p) : + ((RecordProduct.timeBound (m := m) (qs idx) + 1 : ℕ) : ℝ) ≤ R := by + apply RecordProduct.thin_timeBound_sparse _ hN + rw [RecordProduct.decodedQueries_length] + exact (Nat.mul_le_mul_right (2 ^ m) (min_le_left _ _)).trans + (RecordProduct.queryCap_mul_two_pow_le n m) + have hc : (I.card : ℝ) ≤ 4 * (n : ℝ) * g := by + exact card_instanceIndices_le _ hD + ((Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN) hg hp + calc + _ = ∑ idx ∈ I, ((RecordProduct.timeBound (m := m) (qs idx) + 1 : ℕ) : ℝ) := by + rw [Nat.cast_sum] + _ ≤ ∑ _idx ∈ I, R := Finset.sum_le_sum (fun idx _ => hr idx) + _ = I.card * R := by rw [Finset.sum_const, nsmul_eq_mul] + _ ≤ (4 * (n : ℝ) * g) * R := mul_le_mul_of_nonneg_right hc (by dsimp only [R]; positivity) + _ = _ := by dsimp only [R]; ring + +/-- Complete halt-inclusive charged bound, retaining the one-time producer and the single +global failed-scan reserve. Root, query, ordinal, call, return and loop overheads are explicit. +In particular this is not a prime-selection or an unconditional aggregate exponent theorem. -/ +theorem timeBound_le {m n g p : ℕ} (hn : 0 < n) (values : List ℤ) (mem : ℕ → ℕ) + (hD : 16 ≤ D m) (hg : 1 ≤ g) (hp : p ∈ Nat.sqrtPrimeWindow (D m)) (hN : D m ^ 18 ≤ n) : + let V := fun q => values.getD q 0 + let F := falsePositiveCount (fun u v : Fin n => V (pairIndex (u, v))) + (fun b c => V (n * n + pairIndex (b, c))) + (fun u c => V (2 * (n * n) + pairIndex (u, c))) p + ((timeBound (m := m) hn g p values mem : ℕ) : ℝ) ≤ + 690 * (n : ℝ) ^ 2 * D m * g + + 138412032 * ((19 * m + 1 : ℕ) : ℝ) * (n : ℝ) ^ 3 * g / + (D m : ℝ) ^ (1 / 18 : ℝ) + + (4 * (n : ℝ) * g) * ((5 * Nat.sqrt (D m) + 8 * queryCap n (D m) + 61 : ℕ) : ℝ) + + (((42 * pieceSize (D m) g + 28) * (F + 1) + 7 : ℕ) : ℝ) := by + let V := fun q => values.getD q 0 + let I := instanceIndices (fun u v : Fin n => V (pairIndex (u, v))) (D m) g p + let qs := fun idx => RecordProduct.decodedQueries hn mem + (RecordProduct.recordQueries n (D m) p idx) + (RecordProduct.recordQueryCount n (D m) p V idx) + let extra := 5 * Nat.sqrt (D m) + 8 * queryCap n (D m) + 61 + have hDn : D m ≤ n := (Nat.le_self_pow (by decide : 18 ≠ 0) (D m)).trans hN + have hprep : (RecordProduct.preparationTime n (D m) g p values : ℝ) ≤ + 690 * (n : ℝ) ^ 2 * D m * g := by + have hnat : RecordProduct.preparationTime n (D m) g p values ≤ 690 * n ^ 2 * D m * g := by + simpa only [RecordProduct.preparationTime, pow_two] using + PreparationBounds.computed_timeBound_le values hD hDn hg hp + exact_mod_cast hnat + have hterms : (∑ idx ∈ I, recordBase (m := m) hn V mem idx) ≤ + (∑ idx ∈ I, (RecordProduct.timeBound (m := m) (qs idx) + 1)) + I.card * extra := by + rw [show I.card * extra = ∑ _idx ∈ I, extra by simp, ← Finset.sum_add_distrib] + apply Finset.sum_le_sum + intro idx _ + have hQ : RecordProduct.recordQueryCount n (D m) p V idx ≤ queryCap n (D m) := + min_le_left _ _ + dsimp only [recordBase, extra, qs] + omega + have hc : (I.card : ℝ) ≤ 4 * (n : ℝ) * g := card_instanceIndices_le _ hD hDn hg hp + have hextra := mul_le_mul_of_nonneg_right hc (Nat.cast_nonneg extra : (0 : ℝ) ≤ extra) + have hprod := products_time_le hn V mem hD hg hp hN + have hcast := (Nat.cast_le (α := ℝ)).mpr hterms + push_cast at hcast hprod + have hbase : ((∑ idx ∈ I, recordBase (m := m) hn V mem idx : ℕ) : ℝ) ≤ + 138412032 * ((19 * m + 1 : ℕ) : ℝ) * (n : ℝ) ^ 3 * g / + (D m : ℝ) ^ (1 / 18 : ℝ) + (4 * (n : ℝ) * g) * extra := by + dsimp only [I, qs] at hcast hprod ⊢ + push_cast + linarith only [hcast, hextra, hprod] + dsimp only [timeBound] + push_cast + dsimp only [I, V, extra] at hbase hprep ⊢ + push_cast at hbase + linarith only [hbase, hprep] + +end Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Width.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Width.lean new file mode 100644 index 000000000..f95a3685f --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Width.lean @@ -0,0 +1,141 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Indexing +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.SourceBounds + +/-! +# Original-input bounds for the catalogue controller + +The controller's counters fit from original scalar dimensions. A coarse polynomial counter +bound is used only for physical admission; charged scan accounting uses the single global +false-positive count instead. No intermediate counter fit is supplied by the caller. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle + +/-- A polynomial envelope for ordinal and accumulated scan-counter arithmetic. -/ +@[expose] def counterEnvelope (n p : ℕ) : ℕ := + (p * n * n * n + 1) * (n * n + 1) + n + 64 + +/-- Original-input threshold for the unchanged record code and its new controller. -/ +@[expose] def admissionWidth (n D g p A : ℕ) : ℕ := + max (RecordWitness.admissionWidth n D g p A) (Nat.clog 2 (counterEnvelope n p) + 1) + +/-- The original-input physical threshold is positive even before imposing source geometry. -/ +theorem admissionWidth_pos (n D g p A : ℕ) : 0 < admissionWidth n D g p A := by + unfold admissionWidth + omega + +/-- The real occupied count has a polynomial bound without inspecting a workspace. -/ +theorem produced_le {n D g p : ℕ} (values : ℕ → ℤ) + {initial a : Data 64} (h : Instances.Output n D g p values initial a) + (hD : 16 ≤ D) (hDn : D ≤ n) (hg : 1 ≤ g) : + a.regs Instances.produced ≤ p * n * n * n := by + let C := fun rho => Nat.count (fun q => (values q % (p : ℤ)).toNat = rho) (n * n) + have hK : ChunkCatalogue.before C (queryCap n D) p ≤ p * (n * n) := + ChunkCatalogue.before_le (one_le_queryCap (by omega) hDn) + (fun _ _ => Nat.count_le _) (le_refl p) + have hH : numPieces n D g ≤ n := + (Nat.ceilDiv_le_iff (pieceSize_pos hD hg)).mpr + (Nat.le_mul_of_pos_right n (pieceSize_pos hD hg)) + have hc := h.2.2.2.2.1 + change a.regs Instances.produced = ChunkCatalogue.before C (queryCap n D) p * + numPieces n D g at hc + rw [hc] + exact (Nat.mul_le_mul hK hH).trans_eq (by ring) + +/-- All record query-occurrence counts are at most the number of original pairs. -/ +theorem recordQueryCount_le (n D p : ℕ) (values : ℕ → ℤ) (idx : InstanceIndex p) : + RecordProduct.recordQueryCount n D p values idx ≤ n * n := by + unfold RecordProduct.recordQueryCount + exact (min_le_right _ _).trans ((Nat.sub_le _ _).trans (Nat.count_le _)) + +/-- Every larger admitted physical width contains the complete accumulated-counter bound. -/ +theorem counter_fits {W n D g p A : ℕ} (hW : admissionWidth n D g p A ≤ W) : + Fits W (counterEnvelope n p) := by + have hc : Nat.clog 2 (counterEnvelope n p) < W := by + unfold admissionWidth at hW + omega + exact (Nat.le_pow_clog (by decide : 1 < (2 : ℕ)) _).trans_lt + (Nat.pow_lt_pow_right (by decide : 1 < (2 : ℕ)) hc) + +/-- The controller threshold retains the original concrete callee admission. -/ +theorem record_admitted {W n D g p A : ℕ} (hW : admissionWidth n D g p A ≤ W) : + RecordWitness.admissionWidth n D g p A ≤ W := (le_max_left _ _).trans hW + +/-- Before each actual return, both counters fit solely from original dimensions and the +coarse visited-prefix bounds. This is not the charged false-positive estimate. -/ +theorem return_fits {W n D g p A M i S performed : ℕ} + (hW : admissionWidth n D g p A ≤ W) (hM : M ≤ p * n * n * n) + (hi : i < M) (hS : S ≤ i * (n * n + 1)) (hp : performed ≤ n * n + 1) : + Fits W (S + performed) ∧ Fits W (i + 1) := by + have hsum : S + performed ≤ (i + 1) * (n * n + 1) := by + rw [Nat.add_mul, Nat.one_mul] + omega + have hnext : i + 1 ≤ p * n * n * n + 1 := by omega + have hprod := Nat.mul_le_mul_right (n * n + 1) hnext + have hlinear : i + 1 ≤ (p * n * n * n + 1) * (n * n + 1) := + hnext.trans (Nat.le_mul_of_pos_right _ (by omega)) + exact ⟨(counter_fits hW).of_le (by unfold counterEnvelope; omega), + (counter_fits hW).of_le (by unfold counterEnvelope; omega)⟩ + +/-- The additional counters do not increase the existing original-input logarithmic constant. +All source-regime bounds remain explicit; this is not a bound for arbitrary scalar headers. -/ +theorem admissionWidth_le_log {n D g p A κ : ℕ} (hn : 1 ≤ n) + (hD : D ≤ n) (hg : g ≤ n) (hp : p ≤ n) (hA : A ≤ (n + 2) ^ κ) : + admissionWidth n D g p A ≤ (257 * κ + 5909) * Nat.clog 2 (n + 2) := by + let b := n + 2 + have hb : 2 ≤ b := by dsimp only [b]; omega + have hb1 : 1 ≤ b := by omega + have hnB : n ≤ b := by dsimp only [b]; omega + have hfour : p * n * n * n ≤ b ^ 4 := by + calc + _ ≤ b * b * b * b := Nat.mul_le_mul + (Nat.mul_le_mul (Nat.mul_le_mul (hp.trans hnB) hnB) hnB) hnB + _ = b ^ 4 := by ring + have htwo : n * n ≤ b ^ 2 := by simpa only [pow_two] using Nat.mul_le_mul hnB hnB + have h4pos : 1 ≤ b ^ 4 := Nat.one_le_pow _ _ hb1 + have h2pos : 1 ≤ b ^ 2 := Nat.one_le_pow _ _ hb1 + have hleft : p * n * n * n + 1 ≤ b ^ 5 := by + calc + _ ≤ 2 * b ^ 4 := by omega + _ ≤ b * b ^ 4 := Nat.mul_le_mul_right _ hb + _ = b ^ 5 := by ring + have hright : n * n + 1 ≤ b ^ 3 := by + calc + _ ≤ 2 * b ^ 2 := by omega + _ ≤ b * b ^ 2 := Nat.mul_le_mul_right _ hb + _ = b ^ 3 := by ring + have hprod : (p * n * n * n + 1) * (n * n + 1) ≤ b ^ 8 := + (Nat.mul_le_mul hleft hright).trans_eq (by ring) + have hn8 : n ≤ b ^ 8 := hnB.trans (Nat.le_self_pow (by decide) b) + have hc : 64 ≤ b ^ 8 := (show 64 ≤ (2 : ℕ) ^ 8 by decide).trans + (Nat.pow_le_pow_left hb 8) + have he : counterEnvelope n p ≤ b ^ 10 := by + calc + _ ≤ 3 * b ^ 8 := by unfold counterEnvelope; omega + _ ≤ b ^ 2 * b ^ 8 := Nat.mul_le_mul_right _ + ((show 3 ≤ (2 : ℕ) ^ 2 by decide).trans (Nat.pow_le_pow_left hb 2)) + _ = b ^ 10 := by ring + have hlogBound := he.trans (Nat.pow_le_pow_left + (Nat.le_pow_clog (by decide : 1 < (2 : ℕ)) b) 10) + rw [← Nat.pow_mul] at hlogBound + have hlog := Nat.clog_le_of_le_pow hlogBound + have hpos : 1 ≤ Nat.clog 2 b := Nat.clog_pos (by decide) (by omega : 1 < b) + have hcounter : Nat.clog 2 (counterEnvelope n p) + 1 ≤ + (257 * κ + 5909) * Nat.clog 2 b := by + nlinarith only [hlog, hpos, Nat.zero_le (κ * Nat.clog 2 b)] + exact max_le (RecordWitness.admissionWidth_le_log hn hD hg hp hA) hcounter + +end Cslib.Algorithms.WordRAM.ExactTriangle.FixedPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/PreparationLocality.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/PreparationLocality.lean new file mode 100644 index 000000000..8b1b5611b --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/PreparationLocality.lean @@ -0,0 +1,114 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.Instances + +/-! +# Prepared catalogue readback after workspace reuse + +Every catalogue, query, record and matrix word described by `Instances.Output` lies below its +heap boundary. A consumer may change the workspace above that boundary while preserving all +those words and the producer registers. The readback contract can then be framed against the +current state. This does not assert that the producer ran again, or that its original external +frame survived: the original producer execution and its initial state remain separate facts. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.Instances + +open Cslib.WordRAM Cslib.Algorithms.ExactTriangle + +/-- Transport the actual prepared readback through changes above the heap. The new frame is +relative to the current state, not to the original producer input. No execution is asserted. -/ +theorem Output.reframe {n D g p : ℕ} (values : ℕ → ℤ) {initial y z : Data 64} + (h : Output n D g p values initial y) (hregs : z.regs = y.regs) + (hmem : Set.EqOn z.mem y.mem (Set.Iio (y.regs heap))) : + Output n D g p values z z := by + let N := n * n + let R := fun q => (values q % (p : ℤ)).toNat + let C := fun rho => Nat.count (fun q => R q = rho) N + let K := ChunkCatalogue.before C (queryCap n D) p + let H := numPieces n D g + let B := pieceSize D g + let desc := 5 + 6 * N + p + 2 * N * p + let out := desc + 3 * K + let stop := out + (K * H) * (6 + 2 * (n * D)) + obtain ⟨hn, hD, hp, ho, hc, hh, hr, hcounts, hqueries, hdesc, hblocks, -⟩ := h + change y.regs heap = stop at hh + have mem_eq (a : ℕ) (ha : a < stop) : z.mem a = y.mem a := + hmem (by simpa only [Set.mem_Iio, hh] using ha) + have hdescOut : desc ≤ out := by dsimp only [out]; omega + have houtStop : out ≤ stop := Nat.le_add_right _ _ + have desc_lt (i : ℕ) (hi : i < K) : desc + 3 * i + 2 < stop := by + have hm := Nat.mul_le_mul_left 3 (Nat.succ_le_of_lt hi) + rw [Nat.mul_succ] at hm + dsimp only [out] at houtStop + omega + refine ⟨hregs ▸ hn, hregs ▸ hD, hregs ▸ hp, hregs ▸ ho, hregs ▸ hc, + hregs ▸ hh, ?_, ?_, ?_, ?_, ?_, fun _ _ => rfl⟩ + · intro q hq + rw [mem_eq _ (by dsimp only [stop, out, desc]; omega)] + exact hr q hq + · intro rho hrho + rw [mem_eq _ (by dsimp only [stop, out, desc]; omega)] + exact hcounts rho hrho + · intro rho hrho q hq he + have hcount : Nat.count (fun k => R k = rho) q ≤ q := Nat.count_le _ + have hm := Nat.mul_le_mul_left (2 * N) (Nat.succ_le_of_lt hrho) + rw [Nat.mul_succ] at hm + have hbound : 5 + 6 * N + p + 2 * N * rho + + 2 * Nat.count (fun k => R k = rho) q + 1 < stop := by + dsimp only [stop, out, desc] + omega + rw [mem_eq (5 + 6 * N + p + 2 * N * rho + + 2 * Nat.count (fun k => R k = rho) q) (by omega), + mem_eq _ hbound] + exact hqueries rho hrho q hq he + · intro rho hrho k hk + have hi := ChunkCatalogue.before_add_lt C (queryCap n D) hrho hk + have hb := desc_lt (ChunkCatalogue.before C (queryCap n D) rho + k) hi + rw [mem_eq (desc + 3 * (ChunkCatalogue.before C (queryCap n D) rho + k)) + (by omega), + mem_eq (desc + 3 * (ChunkCatalogue.before C (queryCap n D) rho + k) + 1) + (by omega), mem_eq _ hb] + exact hdesc rho hrho k hk + · intro i hi + change i < K * H at hi + have hH : 0 < H := by + by_contra hzero + have hz : H = 0 := by omega + rw [hz, Nat.mul_zero] at hi + omega + have hchunk : i / H < K := (Nat.div_lt_iff_lt_mul hH).mpr hi + have hd := desc_lt (i / H) hchunk + have hm := Nat.mul_le_mul_right (6 + 2 * (n * D)) (Nat.succ_le_of_lt hi) + rw [Nat.succ_mul] at hm + have hb : out + i * (6 + 2 * (n * D)) + (6 + 2 * (n * D)) ≤ stop := by + dsimp only [stop] + omega + obtain ⟨hrecord, hleft, hright⟩ := hblocks i hi + dsimp only + rw [mem_eq _ (by omega : desc + 3 * (i / H) < stop), + mem_eq _ (by omega : desc + 3 * (i / H) + 1 < stop), mem_eq _ hd] + refine ⟨?_, ?_, ?_⟩ + · intro j + change z.mem (out + i * (6 + 2 * (n * D)) + j.val) = _ + rw [mem_eq _ (by have hj : j.val < 6 := j.isLt; omega)] + exact hrecord j + · intro j + change z.mem (out + i * (6 + 2 * (n * D)) + 6 + j.val) = _ + rw [mem_eq _ (by have hj : j.val < n * D := j.isLt; omega)] + exact hleft j + · intro j + change z.mem (out + i * (6 + 2 * (n * D)) + 6 + n * D + j.val) = _ + rw [mem_eq _ (by have hj : j.val < n * D := j.isLt; omega)] + exact hright j + +end Cslib.Algorithms.WordRAM.ExactTriangle.Instances diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/FailureCount.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/FailureCount.lean new file mode 100644 index 000000000..41d59078c --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/FailureCount.lean @@ -0,0 +1,71 @@ +/- +Copyright (c) 2026 Anthropic, PBC, Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anthropic, PBC, Erik Demaine +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Detection +public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Finite + +/-! +# Actual failed-query words and finite failed occurrences + +This value/readback rule keeps the per-record occurrence count before applying any global +false-positive bound. An all-record controller can therefore add these distinct counts once +and inject their union into the existing global failed scans. No execution is assumed here; +actual native calls prove the answer-word and coordinate premises. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +/-- Actual failed accepted queries are bounded by their finite occurrence count, retaining +the record's identity for a later global sum. Repetition assumptions are not needed here. -/ +theorem native_failed_count_le {m n g p : ℕ} (values : ℕ → ℤ) + (AB BC AC : ℕ → ℕ → ℤ) (wAB wBC wAC : Fin n → Fin n → ℤ) + (hD : 16 ≤ D m) (hg : 1 ≤ g) + (hAB : ∀ u v : Fin n, AB u.val v.val = wAB u v) + (hBC : ∀ u v : Fin n, BC u.val v.val = wBC u v) + (hAC : ∀ u v : Fin n, AC u.val v.val = wAC u v) + (idx : InstanceIndex p) (qs : List (Fin n × Fin n)) (default : Fin n × Fin n) + (a b : ℕ → ℕ) (hcoords : ∀ i : Fin qs.length, + a i.val = (qs.get i).1.val ∧ b i.val = (qs.get i).2.val) + (mem : ℕ → ℕ) (out : ℕ) + (hanswer : ∀ i : Fin qs.length, mem (out + i.val) = Signed.encode + ((RecordProduct.leftMatrix m n g p values idx * + RecordProduct.rightMatrix m n g p values idx) (qs.get i).1 (qs.get i).2)) : + let first := idx.2.2 * pieceSize (D m) g + let length := min (pieceSize (D m) g) (n - first) + Nat.count (fun i => mem (out + i) ≠ 0 ∧ + ∀ c : Fin length, AB (a i) (b i) + BC (b i) (first + c.val) + + AC (a i) (first + c.val) ≠ 0) qs.length ≤ + Nat.count (fun i => + (RecordProduct.leftMatrix m n g p values idx * + RecordProduct.rightMatrix m n g p values idx) + (qs.getD i default).1 (qs.getD i default).2 ≠ 0 ∧ + scanPiece wAB wBC wAC (D m) g idx.2.2 + (qs.getD i default).1 (qs.getD i default).2 = none) qs.length := by + apply Nat.count_mono_left + intro i hi hn + let j : Fin qs.length := ⟨i, hi⟩ + have hget : qs.getD i default = qs.get j := by + rw [List.getD_eq_getElem _ _ hi, List.get_eq_getElem] + rw [hget] + constructor + · intro hz + apply hn.1 + rw [hanswer j, hz] + rfl + · apply (WitnessScan.fails_iff_scanPiece_eq_none AB BC AC wAB wBC wAC + hD hg hAB hBC hAC (qs.get j).1 (qs.get j).2).mp + obtain ⟨ha, hb⟩ := hcoords j + simpa only [j, ha, hb] using hn.2 + +end Cslib.Algorithms.WordRAM.ExactTriangle.RecordWitness diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Failures.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Failures.lean index 5618c23a4..6691c7ba5 100644 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Failures.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/RecordWitness/Failures.lean @@ -7,8 +7,7 @@ Authors: Anthropic, PBC, Erik Demaine module -- upstream: candidate -public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordProduct.Detection -public import Complexity.Algorithms.WordRAM.ExactTriangle.WitnessScan.Finite +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.FailureCount /-! # Original false positives bound the actual native failed occurrences @@ -50,27 +49,10 @@ theorem native_failed_occurrences_le {m n g p : ℕ} (values : ℕ → ℤ) Nat.count (fun i => mem (out + i) ≠ 0 ∧ ∀ c : Fin length, AB (a i) (b i) + BC (b i) (first + c.val) + AC (a i) (first + c.val) ≠ 0) qs.length ≤ falsePositiveCount wAB wBC wAC p := by - apply (Nat.count_mono_left (q := fun i => - (RecordProduct.leftMatrix m n g p values idx * RecordProduct.rightMatrix m n g p values idx) - (qs.getD i default).1 (qs.getD i default).2 ≠ 0 ∧ - scanPiece wAB wBC wAC (D m) g idx.2.2 - (qs.getD i default).1 (qs.getD i default).2 = none) ?_).trans + exact (native_failed_count_le values AB BC AC wAB wBC wAC hD hg hAB hBC hAC + idx qs default a b hcoords mem out hanswer).trans (RecordProduct.failed_occurrences_le values wAB wBC wAC hD hg hp hvaluesBC hvaluesAC idx hidx qs default hqs hquery) - intro i hi hn - let j : Fin qs.length := ⟨i, hi⟩ - have hget : qs.getD i default = qs.get j := by - rw [List.getD_eq_getElem _ _ hi, List.get_eq_getElem] - rw [hget] - constructor - · intro hz - apply hn.1 - rw [hanswer j, hz] - rfl - · apply (WitnessScan.fails_iff_scanPiece_eq_none AB BC AC wAB wBC wAC - hD hg hAB hBC hAC (qs.get j).1 (qs.get j).2).mp - obtain ⟨ha, hb⟩ := hcoords j - simpa only [j, ha, hb] using hn.2 /-- The actual native success condition is exactly an original zero triangle in the selected existing chunk and piece. The nonzero-product condition is derived for every genuine zero. -/ diff --git a/ComplexityTests/WordRAMExactTriangleFixedPrimeController.lean b/ComplexityTests/WordRAMExactTriangleFixedPrimeController.lean new file mode 100644 index 000000000..cc228f78e --- /dev/null +++ b/ComplexityTests/WordRAMExactTriangleFixedPrimeController.lean @@ -0,0 +1,66 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +-- upstream: local-only (tests) + +import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Controller +import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Potential + +/-! +# Concrete native catalogue controller regressions + +These executions check actual counter delivery, failed/successful returns and the real loop's +exit guard. They are controller fixtures, not supplied-callee/full-solver execution examples. +The initialized valid-geometry solver clients are checked in their separate test module. +-/ + +set_option linter.hashCommand false + +namespace ComplexityTests.WordRAMExactTriangleFixedPrimeController + +open Cslib.WordRAM +open Cslib.Algorithms.WordRAM.ExactTriangle + +private def fixture (flag : ℕ) : Data 416 := + { regs := fun j => + if j.val = 40 then 7 else if j.val = 392 then flag else if j.val = 396 then 3 + else if j.val = 400 then 2 else if j.val = 401 then 7 else if j.val = 403 then 5 + else if j.val = 404 then 1 else 99 + mem := fun a => if a = 17 then 123 else 0 } + +private def observe (code : Code .standard 416) (flag : ℕ) : Option (List ℕ × ℕ) := + (code.eval 16 0 (fixture flag)).map fun (y, t) => + ([y.regs FixedPrime.Reg.index, y.regs FixedPrime.Reg.limit, + y.regs FixedPrime.Reg.found, y.regs FixedPrime.Reg.scans, y.regs FixedPrime.Reg.one, + y.regs 392, y.mem 17], t) + +#guard observe FixedPrime.setup 1 == some ([0, 7, 0, 0, 1, 1, 123], 5) +#guard observe FixedPrime.select 0 == some ([2, 7, 99, 5, 1, 0, 123], 1) +#guard observe FixedPrime.receive 0 == some ([3, 7, 0, 8, 1, 0, 123], 6) +#guard observe FixedPrime.receive 1 == some ([3, 3, 1, 8, 1, 1, 123], 5) + +-- A successful return closes the actual loop immediately, even with zero body fuel. The +-- embedded body is the real record program, not a substitute solver. +private def stopped : Option (ℕ × ℕ) := do + let (y, _) ← FixedPrime.receive.eval 16 0 (fixture 1) + let (z, t) ← (Code.loop (.lt FixedPrime.Reg.index FixedPrime.Reg.limit) + FixedPrime.step).eval 16 0 y + pure (z.regs FixedPrime.Reg.scans, t) + +#guard stopped == some (8, 2) + +-- A failed record leaves more work and therefore cannot exit with zero body fuel. +private def pending : Option (Data 416 × ℕ) := do + let (y, _) ← FixedPrime.receive.eval 16 0 (fixture 0) + (Code.loop (.lt FixedPrime.Reg.index FixedPrime.Reg.limit) FixedPrime.step).eval 16 0 y + +#guard pending.isNone + +-- The single reserve pays exactly one actual success and none for an unsuccessful return. +example : 20 + 2 + (11 + 7 * (5 + 1 - 1)) ≤ 8 + 11 + 7 * (1 + 5 + 1) := + FixedPrime.pay_return (by decide) (by decide : 2 ≤ 1 + 1) (by decide) + +end ComplexityTests.WordRAMExactTriangleFixedPrimeController diff --git a/ComplexityTests/WordRAMExactTriangleFixedPrimeInput.lean b/ComplexityTests/WordRAMExactTriangleFixedPrimeInput.lean new file mode 100644 index 000000000..c058b56ee --- /dev/null +++ b/ComplexityTests/WordRAMExactTriangleFixedPrimeInput.lean @@ -0,0 +1,156 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +-- upstream: local-only (tests) + +import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime +import Mathlib.Data.List.GetD + +/-! +# Fully admitted original-input fixed-prime decisions + +Both clients retain the actual same-state prefix and halt at valid positive thin geometry. +The all-one negative input has modular false positives at prime three, but no original zero; +its unsuccessful records collectively receive no additive successful-scan allowance. These +kernel proofs do not evaluate the enormous catalogue or encoder through the interpreter. +-/ + +namespace ComplexityTests.WordRAMExactTriangleFixedPrimeInput + +open Cslib.WordRAM Cslib.Matrix Cslib.Algorithms.ExactTriangle +open Cslib.Algorithms.WordRAM.ExactTriangle +open Cslib.Algorithms.MatrixMultiplication.Schonhage + +private def side : ℕ := 16 ^ 18 +private def weights (N : ℕ) (z : ℤ) : List ℤ := List.replicate (3 * (N * N)) z +private def width (N A : ℕ) : ℕ := FixedPrime.admissionWidth N (D 2) 1 3 A + +private theorem prime : 3 ∈ Nat.sqrtPrimeWindow (D 2) := + Nat.mem_sqrtPrimeWindow_iff_sq.mpr ⟨by decide, by decide, by decide⟩ + +/-- The complete actual program returns one and an original witness on all-zero input. -/ +private theorem initialized_zero (N : ℕ) (hgeo : D 2 ^ 18 ≤ N) : ∃ y : Data 416, ∃ t : ℕ, + Runs (width N 0) (FixedPrime.program.program FixedPrime.Reg.found) + (State.init (PreparationInput.input N (D 2) 1 3 (weights N 0))) + ⟨FixedPrime.program.size, y⟩ t ∧ + Executes (width N 0) (FixedPrime.program.program FixedPrime.Reg.found) + (State.init (PreparationInput.input N (D 2) 1 3 (weights N 0))) 1 (t + 1) ∧ + y.regs FixedPrime.Reg.found = 1 ∧ y.regs FixedPrime.Reg.scans ≤ 1 ∧ + ∃ u v c : Fin N, + y.regs (FixedPrime.recordBank RecordWitness.Reg.witnessA) = u.val ∧ + y.regs (FixedPrime.recordBank RecordWitness.Reg.witnessB) = v.val ∧ + y.regs (FixedPrime.recordBank RecordWitness.Reg.witnessC) = c.val := by + have hmag : ∀ z ∈ weights N 0, z.natAbs ≤ 0 := by + intro z hz + have he := List.eq_of_mem_replicate hz + subst z + decide + obtain ⟨hi, a, ha, hout, y, t, ht, hr, he, yb, yf, ys, yiff, yw, frame, regs⟩ := + FixedPrime.runs_input (m := 2) (n := N) (g := 1) (p := 3) (A := 0) + (W := width N 0) (weights N 0) (by simp only [weights, List.length_replicate]) + (by decide) (by decide) prime hgeo hmag le_rfl + let V := fun q => (weights N 0).getD q 0 + have hval (q : ℕ) : V q = 0 := List.getElem?_getD_replicate_default_eq _ _ _ + have hzero : HasZeroTriangle (fun u v : Fin N => V (pairIndex (u, v))) + (fun b c => V (N * N + pairIndex (b, c))) + (fun u c => V (2 * (N * N) + pairIndex (u, c))) := by + let v : Fin N := ⟨0, lt_of_lt_of_le (by decide : 0 < D 2 ^ 18) hgeo⟩ + exact ⟨v, v, v, by simp only [hval, zero_add]⟩ + have hf : y.regs FixedPrime.Reg.found = 1 := yiff.mpr hzero + have hF : falsePositiveCount (fun u v : Fin N => V (pairIndex (u, v))) + (fun b c => V (N * N + pairIndex (b, c))) + (fun u c => V (2 * (N * N) + pairIndex (u, c))) 3 = 0 := by + simp only [falsePositiveCount, IsFalsePositive, weightSum, hval, zero_add, + ne_eq, not_true_eq_false, false_and, Finset.filter_false, Finset.card_empty] + obtain ⟨u, v, c, hu, hv, hc, _⟩ := yw hf + refine ⟨y, t, hr, ?_, hf, ?_, u, v, c, hu, hv, hc⟩ + · simpa only [hf] using he + · have hs : y.regs FixedPrime.Reg.scans ≤ + falsePositiveCount (fun u v : Fin N => V (pairIndex (u, v))) + (fun b c => V (N * N + pairIndex (b, c))) + (fun u c => V (2 * (N * N) + pairIndex (u, c))) 3 + + y.regs FixedPrime.Reg.found := ys + simpa only [hF, hf, zero_add] using hs + +/-- Even with nonzero modular candidates, exhaustion of every actual record returns zero +exactly. The total scan bound is the single global false-positive count, with no extra one. -/ +private theorem initialized_one (N : ℕ) (hgeo : D 2 ^ 18 ≤ N) : + let V := fun q => (weights N 1).getD q 0 + let F := falsePositiveCount (fun u v : Fin N => V (pairIndex (u, v))) + (fun b c => V (N * N + pairIndex (b, c))) + (fun u c => V (2 * (N * N) + pairIndex (u, c))) 3 + ∃ y : Data 416, ∃ t : ℕ, + Runs (width N 1) (FixedPrime.program.program FixedPrime.Reg.found) + (State.init (PreparationInput.input N (D 2) 1 3 (weights N 1))) + ⟨FixedPrime.program.size, y⟩ t ∧ + Executes (width N 1) (FixedPrime.program.program FixedPrime.Reg.found) + (State.init (PreparationInput.input N (D 2) 1 3 (weights N 1))) 0 (t + 1) ∧ + y.regs FixedPrime.Reg.found = 0 ∧ y.regs FixedPrime.Reg.scans ≤ F := by + have hmag : ∀ z ∈ weights N 1, z.natAbs ≤ 1 := by + intro z hz + have he := List.eq_of_mem_replicate hz + subst z + decide + obtain ⟨hi, a, ha, hout, y, t, ht, hr, he, yb, yf, ys, yiff, yw, frame, regs⟩ := + FixedPrime.runs_input (m := 2) (n := N) (g := 1) (p := 3) (A := 1) + (W := width N 1) (weights N 1) (by simp only [weights, List.length_replicate]) + (by decide) (by decide) prime hgeo hmag le_rfl + let V := fun q => (weights N 1).getD q 0 + have hval (q : ℕ) (hq : q < 3 * (N * N)) : V q = 1 := + List.getD_replicate (1 : ℤ) hq + have pair_bound (u v : Fin N) : pairIndex (u, v) < N * N := by + have hu := Nat.mul_le_mul_right N (Nat.succ_le_of_lt u.isLt) + rw [Nat.succ_mul] at hu + change u.val * N + v.val < N * N + have hv := v.isLt + omega + have hn : ¬ HasZeroTriangle (fun u v : Fin N => V (pairIndex (u, v))) + (fun b c => V (N * N + pairIndex (b, c))) + (fun u c => V (2 * (N * N) + pairIndex (u, c))) := by + rintro ⟨u, v, c, hz⟩ + change V (pairIndex (u, v)) + V (N * N + pairIndex (v, c)) + + V (2 * (N * N) + pairIndex (u, c)) = 0 at hz + rw [hval (pairIndex (u, v)) (by have := pair_bound u v; omega), + hval (N * N + pairIndex (v, c)) (by have := pair_bound v c; omega), + hval (2 * (N * N) + pairIndex (u, c)) (by have := pair_bound u c; omega)] at hz + norm_num at hz + have hf : y.regs FixedPrime.Reg.found = 0 := by + rcases yf with h | h + · exact h + · exact False.elim (hn (yiff.mp h)) + refine ⟨y, t, hr, ?_, hf, ?_⟩ + · simpa only [hf] using he + · simpa only [hf, Nat.add_zero] using ys + + +example : ∃ y : Data 416, ∃ t : ℕ, + Runs (width side 0) (FixedPrime.program.program FixedPrime.Reg.found) + (State.init (PreparationInput.input side (D 2) 1 3 (weights side 0))) + ⟨FixedPrime.program.size, y⟩ t ∧ + Executes (width side 0) (FixedPrime.program.program FixedPrime.Reg.found) + (State.init (PreparationInput.input side (D 2) 1 3 (weights side 0))) 1 (t + 1) ∧ + y.regs FixedPrime.Reg.found = 1 ∧ y.regs FixedPrime.Reg.scans ≤ 1 ∧ + ∃ u v c : Fin side, + y.regs (FixedPrime.recordBank RecordWitness.Reg.witnessA) = u.val ∧ + y.regs (FixedPrime.recordBank RecordWitness.Reg.witnessB) = v.val ∧ + y.regs (FixedPrime.recordBank RecordWitness.Reg.witnessC) = c.val := + initialized_zero side (by decide) + +example : + let V := fun q => (weights side 1).getD q 0 + let F := falsePositiveCount (fun u v : Fin side => V (pairIndex (u, v))) + (fun b c => V (side * side + pairIndex (b, c))) + (fun u c => V (2 * (side * side) + pairIndex (u, c))) 3 + ∃ y : Data 416, ∃ t : ℕ, + Runs (width side 1) (FixedPrime.program.program FixedPrime.Reg.found) + (State.init (PreparationInput.input side (D 2) 1 3 (weights side 1))) + ⟨FixedPrime.program.size, y⟩ t ∧ + Executes (width side 1) (FixedPrime.program.program FixedPrime.Reg.found) + (State.init (PreparationInput.input side (D 2) 1 3 (weights side 1))) 0 (t + 1) ∧ + y.regs FixedPrime.Reg.found = 0 ∧ y.regs FixedPrime.Reg.scans ≤ F := + initialized_one side (by decide) + +end ComplexityTests.WordRAMExactTriangleFixedPrimeInput diff --git a/ComplexityTests/WordRAMExactTrianglePreparationLocality.lean b/ComplexityTests/WordRAMExactTrianglePreparationLocality.lean new file mode 100644 index 000000000..5dc732785 --- /dev/null +++ b/ComplexityTests/WordRAMExactTrianglePreparationLocality.lean @@ -0,0 +1,67 @@ +/- +Copyright (c) 2026 Erik Demaine. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Erik Demaine +-/ + +-- upstream: local-only (tests) + +import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationLocality +import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationInput + +/-! +# Actual prepared readback survives a dirty heap cell + +This small producer-only client distinguishes protected readback from the producer's original +external frame. The full thin-solver clients retain their valid positive geometry elsewhere. +-/ + +namespace ComplexityTests.WordRAMExactTrianglePreparationLocality + +open Cslib.WordRAM +open Cslib.Algorithms.WordRAM.ExactTriangle + +example : ∃ a z : Data 64, + ExecWithin (PreparationInput.width 1 1 1 2 0) Instances.program + (.init (PreparationInput.input 1 1 1 2 [0, 0, 0])) a + (Instances.timeBound 1 1 2 1 1 - 1) ∧ + Instances.Output 1 1 1 2 (fun q => ([0, 0, 0] : List ℤ).getD q 0) z z ∧ + z.mem (a.regs Instances.heap) = 77 ∧ + ¬ Instances.Output 1 1 1 2 (fun q => ([0, 0, 0] : List ℤ).getD q 0) + (.init (PreparationInput.input 1 1 1 2 [0, 0, 0])) z := by + obtain ⟨a, ha, hout, _, _⟩ := PreparationInput.exec_input (n := 1) (D := 1) (g := 1) + (p := 2) (A := 0) (W := PreparationInput.width 1 1 1 2 0) [0, 0, 0] + (by decide) (by decide) (by decide) (by decide) (by decide) + (by intro v hv; simp only [List.mem_cons, List.not_mem_nil, or_false] at hv + rcases hv with h | h | h <;> subst v <;> decide) le_rfl + let z := a.write (a.regs Instances.heap) 77 + have hmem : Set.EqOn z.mem a.mem (Set.Iio (a.regs Instances.heap)) := by + intro u hu + change Function.update a.mem (a.regs Instances.heap) 77 u = a.mem u + exact Function.update_of_ne (by change u < a.regs Instances.heap at hu; omega) _ _ + have hnew := hout.reframe (z := z) (fun q => ([0, 0, 0] : List ℤ).getD q 0) rfl hmem + have dirty : z.mem (a.regs Instances.heap) = 77 := by + simp only [z, Data.write_mem, Function.update_self] + have hheap : 17 ≤ a.regs Instances.heap := by + have hh := hout.2.2.2.2.2.1 + change a.regs Instances.heap = _ + _ at hh + omega + have old_zero : (Data.init (PreparationInput.input 1 1 1 2 [0, 0, 0]) : Data 64).mem + (a.regs Instances.heap) = 0 := by + cases he : a.regs Instances.heap with + | zero => omega + | succ u => + change (PreparationInput.input 1 1 1 2 [0, 0, 0]).getD u 0 = 0 + apply List.getD_eq_default + have hu : 7 ≤ u := by omega + exact hu + refine ⟨a, z, ?_, hnew, dirty, ?_⟩ + · convert ha using 1 + decide + · intro hbad + have he := hbad.2.2.2.2.2.2.2.2.2.2.2 (a.regs Instances.heap) + (Or.inr hout.2.2.2.2.2.1.ge) + rw [dirty, old_zero] at he + contradiction + +end ComplexityTests.WordRAMExactTrianglePreparationLocality diff --git a/ROADMAP.md b/ROADMAP.md index 1eb96901d..cffd38165 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -551,6 +551,15 @@ each statement against the numbered result. Organize modules by subject, not cha Root decoding, all control and original-weight scans are charged; positive logarithmic width and polynomial words/support hold at every wider admitted width. The outer instance loop, aggregate subcubic ET analysis and ordinary-3SUM 1.9992 composition remain open under [#1394]. +- **Fixed-prime native catalogue decision** ([#1662](https://github.com/cslib-community/complexitylib/issues/1662)): + the source/MCP continuation runs real preparation once and the actual product/witness callee + over every computed occupied ordinal, stopping on the first original witness. Its initialized + same-state prefix/halt verdict is equivalent to original ET, with dirty-workspace reuse and + one global `S≤F(p)+returnedFlag` scan budget. All-instance product savings, all native control + charges, original-input logarithmic admission and polynomial wider-prefix words/support are + derived. Final validation and independent review remain pending. Global parameter/prime + selection, unrestricted small geometry, the complete aggregate source exponent and the + ordinary-3SUM 1.9992 endpoint remain open under [#1394]. - **Conditional Convolution-3SUM host** ([#1524]): the charged native host initializes from the ordinary input and runs every generated instance, conditional on a concrete solver's execution, correctness and frame theorem. This completes the conditional host milestone. Integrating the diff --git a/UPSTREAM.md b/UPSTREAM.md index f84aaa139..e77e62bc8 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -421,6 +421,25 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he narrow `ExactTriangle.PieceBounds` module, reused by preparation and recovery. Parameter/prime choice, the outer ET loop, aggregate savings and the full #1394 endpoint remain open; no curated abstract changes. + The bounded fixed-prime continuation + [#1662](https://github.com/cslib-community/complexitylib/issues/1662) runs the producer once + and traverses every actual occupied ordinal using the full 400-register callee in a fixed + 416-register program. Its initialized verdict decides original ET across all records; + protected readback survives dirty scratch via `Instances.Output.reframe`, which reframes + against the current state without asserting preservation of the old external frame. + Distinct record/query occurrences inject into the existing global `failedScans`; actual + total scans satisfy `S≤F(p)+returnedFlag`, with only one possible successful-return flag. + Native preparation, products, root/query/controller/loop/halt and original verification + costs are charged. Aggregate product work is bounded by + `138412032*(19m+1)*n³*g/D^(1/18)` under the unchanged positive thin geometry and prime window; + one-time `690n²Dg`, explicit query/root/controller work and one global failed-scan term remain + separate. The actual program has original-input logarithmic admission and polynomial + all-prefix words/support independently of every larger physical width. This is a source/MCP + checkpoint pending final validation and review, not a parameter/prime-selection host or a + completed aggregate source exponent. The source's Light host uses piece-major rounds and + per-round matrix construction; the native producer prepares the chunk-major catalogue once + and later products stop after success. No source-trace equivalence or Light simulation is + assumed. The full ordinary-3SUM 1.9992 objective remains open under #1394; no abstract changes. Stage `exec_*` lemmas are internal implementation APIs exported across proof modules; callers use the flat initialized runs, width theorem or pointer/epoch kernel. The narrow `WordRAM.ArrayAppend` and `WordRAM.InputArray` topic modules keep append/input dependencies From 52b94d0edb302985567898ca8a6a72ea3b52d0e5 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 29 Oct 2026 21:02:11 -0400 Subject: [PATCH 72/73] chore(ExactTriangle): validate fixed-prime native solver on supported rc2 Complete #1662 validation of source checkpoint 9ed49005c9f6ce362d033affa2317a0758f7d7ae. Normal merges pin only rc2 dependency 0d38523e96f8b431aeadc86c548c9994714f9b00 and supplier doc head 2e388dc940594ccf683e73326b350daa39db18dc. No ancestor remote edit, rebase or force. Add 17 canonical aggregate imports and update owned scope prose. TESTED SOURCE 359a46afcdf8a1bccbd46cd1381c4cefb087d3ea: 359a46afcdf8a1bccbd46cd1381c4cefb087d3ea Toolchain leanprover/lean4:v4.36.0-rc2; cslib 8a8ad7489cdccb007ad7279bfb9d03bbda2229ad; mathlib 9d581dfff2e41de989072ea63abf3daf69311520. All SIX passed together on this exact final tree, four compiler threads, lake --no-cache, downloads and automatic cache restoration disabled during gates: - lake --no-cache build --wfail --iofail: PASS (5427.86s). - lake --no-cache exe mk_all --check: PASS (11.33s). - lake --no-cache test --wfail --iofail: PASS (2077.58s). - lake --no-cache lint: PASS (37.98s). - lake --no-cache exe lint-style: PASS (20.01s). - lake --no-cache exe checkInitImports: PASS (35.53s). Final static/source audits PASS: full hygiene --check-simp; fresh pinned-base 49-library/6-test environment and changed-file lints; strict PR-body names (all 5); whole base/staged/unstaged diff --check; source pin/blob/quote checks; exact source/supplier identities; modes and abstract no-impact. The 49-library/6-test comparison against pinned 0d includes unchanged supplier payload. Own scope is 18 library modules/3 new tests; final own comparison awaits integrated supported-rc2 supplier. Actual strict test log builds all three new suites, affected supplier tests, Smoke and global Axioms. No extra whole-test or Axioms rerun. All 21 own Lean Git blobs exactly match 9ed. Supplier Lean blobs match 2e except the intentional #1662 RecordWitness.Failures factorization through new FailureCount, whose existing public statement is retained. No child rc2 proof/API/program port was required. Six wider native-certificate/randomized-Karger differences are exactly pinned 0d; the seventh native-path difference is the supplier Flat doc fix. No other deterministic ET/compiler/program source changed. Cold bootstrap first failed over readonly old outputs; only verified own output directories were preserved, then 9081 artifacts restored into private caches. Logs are retained. One fixed standard native 416-register Code, <=133152 flat slots including halt, no width reads. Actual preparation runs once; all occupied ordinals invoke the concrete 400/320/163 product/witness banks, reuse dirty scratch and stop on original weight success. Same-y State.init prefix Runs and charged halt decide both original ET outcomes, retaining original witnesses and protected catalogue/source/high frames. No supplied layout/list/run/time/sparse/solver/witness oracle. Actual global S<=falsePositiveCount+returnedFlag: no extra one per unsuccessful record. Premises: original signed list length 3*n*n, magnitudes<=A, D=4^m>=16, n>=D^18, g>=1, p in Nat.sqrtPrimeWindow D, every physical W>=derived positive admission. Encoding: length, raw D/g/n/p, canonical signed AB/BC/AC; raw 0/1 matrices undergo the existing single charged doubling. Concrete product calls use L=19m. If 1<=n, D/g/p<=n, A<=(n+2)^k, admission is <=(257*k+5909)*clog2(n+2). Every wider-prefix word/support is bounded independently of physical W by (n+2)^(514*k+11818), including dirty loads. Halt-inclusive charge: 690*n^2*D*g +138412032*(19*m+1)*n^3*g/D^(1/18) +4*n*g*(5*sqrt(D)+8*queryCap(n,D)+61) +(42*pieceSize(D,g)+28)*(F(p)+1)+7. Finite instances give <=4*n*g records; actual queries derive Q*2^m<=n^2. Preparation is once; copies/roots/queries/scans/calls/control are charged. Fixed F(p), parameter/prime selection, unrestricted small geometry, final aggregate exponent and ordinary 3SUM 1.9992 remain OPEN under #1394. Tests distinguish native controller/short-circuit/dirty-frame fixtures from fully admitted positive/negative initialized Runs at n=16^18; no huge VM run or weakened full-solver geometry. Source credit: Anthropic PBC and Erik Demaine; original native implementation and proof composition by Erik Demaine with Codex AI assistance. Pinned source: anthropics/formal-math@16a5ce41304fcdaea4e0be7c132e1c361dc26da5, 3sum-apsp/ThreeSumApsp/Programs/Sec3/Theorem17/Host/Loop.lean:11-31, FailedScans.lean:84-97, InstanceCount.lean:1-40. Files equal pinned Git blobs. Checked quote at FailedScans:89-97: "distinct scans contain distinct false positives, so there are at most F(p) failed scans". Source Light uses piece-major visits, per-round writes and arbitrary solver; native chunk-major preparation once/early product termination differs. No trace equivalence, simulation or source-time oracle. No curated abstract/catalogue/rendering impact or headline promotion. New locality/global-failure APIs, factored native failure bridge and full actual controller/initialized execution/width/cost need fresh whole-layer nonauthor review. Supplier reviews apply only to unchanged source. Root arranges review and dependency routing before publication. No PR/child review/watch/approval/merge or new agent. Local detailed packet: .lake/codex-three-sum-continuation/fixed-prime-1662/rc2-validation/. This commit records tested tree/commands/results/mapping publicly; issue #1662 links it. Validation summary SHA256: 1c4b0f0a34c4e95ec2ed2b71878273772aa5c99bf6b311bc3e135f37f8070035 Static summary SHA256: 56560f6f9ff688a15b7cffda653a8d1459e6a7dc69ca7168c1b7749a53e708fe --- Complexity.lean | 17 +++++++++++++++++ ROADMAP.md | 8 ++++---- UPSTREAM.md | 6 +++--- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Complexity.lean b/Complexity.lean index ea6e35732..935cce58d 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1,6 +1,7 @@ module -- shake: keep-all --deprecated_module: ignore public import Complexity.Algorithms.ExactTriangle.Defs +public import Complexity.Algorithms.ExactTriangle.FailureAccounting public import Complexity.Algorithms.ExactTriangle.Hashing public import Complexity.Algorithms.ExactTriangle.Lopsided public import Complexity.Algorithms.ExactTriangle.PieceBounds @@ -94,6 +95,20 @@ public import Complexity.Algorithms.WordRAM.ExactTriangle.Catalogue public import Complexity.Algorithms.WordRAM.ExactTriangle.ChunkCatalogue public import Complexity.Algorithms.WordRAM.ExactTriangle.Chunks public import Complexity.Algorithms.WordRAM.ExactTriangle.Classes +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Accounting +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Controller +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Entry +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Flat +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Indexing +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Loop +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Potential +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Program +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Record +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.SourceBounds +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Step +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.TimeBound +public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Width public import Complexity.Algorithms.WordRAM.ExactTriangle.InstancePair public import Complexity.Algorithms.WordRAM.ExactTriangle.Instances public import Complexity.Algorithms.WordRAM.ExactTriangle.MatrixSpec @@ -101,6 +116,7 @@ public import Complexity.Algorithms.WordRAM.ExactTriangle.Parameters public import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationAdjacency public import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationBounds public import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationInput +public import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationLocality public import Complexity.Algorithms.WordRAM.ExactTriangle.PreparationSpec public import Complexity.Algorithms.WordRAM.ExactTriangle.QueryScan public import Complexity.Algorithms.WordRAM.ExactTriangle.QuerySpec @@ -127,6 +143,7 @@ public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Consume public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Entry public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Exec +public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.FailureCount public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Failures public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Flat public import Complexity.Algorithms.WordRAM.ExactTriangle.RecordWitness.Header diff --git a/ROADMAP.md b/ROADMAP.md index cffd38165..d087bc159 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -552,14 +552,14 @@ each statement against the numbered result. Organize modules by subject, not cha and polynomial words/support hold at every wider admitted width. The outer instance loop, aggregate subcubic ET analysis and ordinary-3SUM 1.9992 composition remain open under [#1394]. - **Fixed-prime native catalogue decision** ([#1662](https://github.com/cslib-community/complexitylib/issues/1662)): - the source/MCP continuation runs real preparation once and the actual product/witness callee + the native continuation runs real preparation once and the actual product/witness callee over every computed occupied ordinal, stopping on the first original witness. Its initialized same-state prefix/halt verdict is equivalent to original ET, with dirty-workspace reuse and one global `S≤F(p)+returnedFlag` scan budget. All-instance product savings, all native control charges, original-input logarithmic admission and polynomial wider-prefix words/support are - derived. Final validation and independent review remain pending. Global parameter/prime - selection, unrestricted small geometry, the complete aggregate source exponent and the - ordinary-3SUM 1.9992 endpoint remain open under [#1394]. + derived. Validation, independent review and integration are tracked in #1662. Global + parameter/prime selection, unrestricted small geometry, the complete aggregate source exponent + and the ordinary-3SUM 1.9992 endpoint remain open under [#1394]. - **Conditional Convolution-3SUM host** ([#1524]): the charged native host initializes from the ordinary input and runs every generated instance, conditional on a concrete solver's execution, correctness and frame theorem. This completes the conditional host milestone. Integrating the diff --git a/UPSTREAM.md b/UPSTREAM.md index e77e62bc8..773a9cb55 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -434,9 +434,9 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he `138412032*(19m+1)*n³*g/D^(1/18)` under the unchanged positive thin geometry and prime window; one-time `690n²Dg`, explicit query/root/controller work and one global failed-scan term remain separate. The actual program has original-input logarithmic admission and polynomial - all-prefix words/support independently of every larger physical width. This is a source/MCP - checkpoint pending final validation and review, not a parameter/prime-selection host or a - completed aggregate source exponent. The source's Light host uses piece-major rounds and + all-prefix words/support independently of every larger physical width. Validation and + integration are tracked in #1662; this is not a parameter/prime-selection host or a completed + aggregate source exponent. The source's Light host uses piece-major rounds and per-round matrix construction; the native producer prepares the chunk-major catalogue once and later products stop after success. No source-trace equivalence or Light simulation is assumed. The full ordinary-3SUM 1.9992 objective remains open under #1394; no abstract changes. From 3fcbf731febb690b8b6c2693a0ff963722ae1255 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Thu, 29 Oct 2026 21:31:31 -0400 Subject: [PATCH 73/73] doc(ExactTriangle): describe controller return cost as an upper bound Address PR1700 inline4419611235: say at most six instructions, matching the existing ExecWithin bound and the five/six instruction branches. Exact byte comparison against3cda568b confirms one comment line changed and every other byte, definition, proof, attribute, import and test is unchanged. Scoped and full parent-to-head whitespace checks pass. No Lean example or meaning change; no local rebuild repeated for this doc-only correction. Existing independent code approvals carry across the exact requested wording change, with final hosted checks still required. --- .../Algorithms/WordRAM/ExactTriangle/FixedPrime/Controller.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Controller.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Controller.lean index 08decae1a..29bca5009 100644 --- a/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Controller.lean +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/FixedPrime/Controller.lean @@ -55,7 +55,7 @@ theorem exec_setup {w : ℕ} (x : Data 416) (h1 : Fits w 1) : he.regs_eq (setup_preserves j hj)⟩ all_goals simp +decide [y, M, Reg.index, Reg.limit, Reg.found, Reg.scans, Reg.one] -/-- The native return needs six instructions in either branch, including control. Its limit +/-- The native return needs at most six instructions, including control. Its limit is shortened exactly when the actual callee flag equals one. The complete callee bank survives. -/ theorem exec_receive {w i limit S performed flag : ℕ} (x : Data 416) (hi : x.regs Reg.index = i) (hl : x.regs Reg.limit = limit)