diff --git a/Complexity.lean b/Complexity.lean index 53250ff44e..37503b8351 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1,5 +1,7 @@ module -- shake: keep-all --deprecated_module: ignore +public import Complexity.Algorithms.ExactTriangle.CountPrime +public import Complexity.Algorithms.ExactTriangle.CountPrime.Extraction public import Complexity.Algorithms.ExactTriangle.Defs public import Complexity.Algorithms.ExactTriangle.FailureAccounting public import Complexity.Algorithms.ExactTriangle.Hashing @@ -92,6 +94,10 @@ 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.MatrixMultiplication.Strassen.Coefficients +public import Complexity.Algorithms.MatrixMultiplication.Strassen.Correctness +public import Complexity.Algorithms.MatrixMultiplication.Strassen.Defs +public import Complexity.Algorithms.MatrixMultiplication.Strassen.Phases public import Complexity.Algorithms.Reachability.BreadthFirst public import Complexity.Algorithms.Reachability.InductiveCounting public import Complexity.Algorithms.Reachability.InductiveCounting.Bounds @@ -151,6 +157,34 @@ 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.CountPrime +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Access +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Bounds +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.CoefficientMatrices +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Exec +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.ExecReady +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Flat +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Input +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Layout +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Logarithm +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Matrix +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.MatrixCell +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.MatrixReadback +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.MatrixResidue +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Monomial +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Product +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Program +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.ResidueArray +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.ResidueArrays +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Scan +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.ScanCell +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.ScanReadback +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Setup +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Solve +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.SourceBounds +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Spread +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.TimeBound +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Width public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Accounting public import Complexity.Algorithms.WordRAM.ExactTriangle.FixedPrime.Controller @@ -399,6 +433,35 @@ public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Schonhage.Wante 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.MatrixMultiplication.Strassen.Accumulate +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.ArithmeticForm +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Bounds +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Child +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Control +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.CopyForm +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Dispatch +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Entry +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Exec +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Finish +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Form +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Frame +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Initialize +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Leaf +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.LeafStep +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Next +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Pending +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Prepare +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Program +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Progress +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Push +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Quadrant +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.QuarterUpdate +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Receive +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Restore +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Resume +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.State +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Traversal +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Zero public import Complexity.Algorithms.WordRAM.NatClog public import Complexity.Algorithms.WordRAM.NatGcd public import Complexity.Algorithms.WordRAM.NatLog @@ -2431,6 +2494,9 @@ public import Complexity.Computability.WordRAM.Randomized.UniformLoopExit 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.ArrayArithmetic.Body +public import Complexity.Computability.WordRAM.Signed.ArrayArithmetic.Exec +public import Complexity.Computability.WordRAM.Signed.ArrayArithmetic.Program public import Complexity.Computability.WordRAM.Signed.Comparison public import Complexity.Computability.WordRAM.Signed.Compile public import Complexity.Computability.WordRAM.Signed.Encoding @@ -3025,6 +3091,7 @@ public import Complexity.Foundations.Data.Nat.Factorial public import Complexity.Foundations.Data.Nat.Fields public import Complexity.Foundations.Data.Nat.IntermediateValue public import Complexity.Foundations.Data.Nat.Log +public import Complexity.Foundations.Data.Nat.Morton public import Complexity.Foundations.Data.Nat.OddMulMod public import Complexity.Foundations.Data.Nat.OfDigits public import Complexity.Foundations.Data.Nat.PolynomialBound.Closure diff --git a/Complexity/Algorithms/ExactTriangle/CountPrime.lean b/Complexity/Algorithms/ExactTriangle/CountPrime.lean new file mode 100644 index 0000000000..d1ce9bf2e8 --- /dev/null +++ b/Complexity/Algorithms/ExactTriangle/CountPrime.lean @@ -0,0 +1,147 @@ +/- +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.MatrixMultiplication.Strassen.Correctness + +/-! +# Modular triangle counts from the actual seven-product coefficient recursion + +The coefficient arrays contain zero padding and one monomial for each real edge residue. +`CountPrime.count` applies the executable finite Strassen recursion and selects one product +coefficient for each original AB pair. It equals the existing `countZeroMod`, including +modular false positives. This is a mathematical specification and correctness bridge for +the native counter, not a native execution or running-time theorem. + +The construction selectively adapts Spec/Sec3/Theorem17/Strassen.lean, the `matPList`, +`matQList`, `countBy` and `countOf_eq` sections, at revision +16a5ce41304fcdaea4e0be7c132e1c361dc26da5. Existing `matP`, `matQ`, cyclic coefficients and +modular triangle counts are reused; no parallel triangle problem or polynomial ring is added. +-/ + +public section + +namespace Cslib.Algorithms.ExactTriangle.CountPrime + +open Cslib.CyclicIntRing Cslib.Algorithms.MatrixMultiplication.Strassen + +/-- Native P coefficient specification, including true zero padding outside the original side. -/ +@[expose] def pCoefficients {n : ℕ} (p : ℕ) (wAC : Fin n → Fin n → ℤ) (i : ℕ) : ℤ := + if h : Nat.Morton.row (i / p) < n ∧ Nat.Morton.column (i / p) < n then + if i % p = (wAC ⟨_, h.1⟩ ⟨_, h.2⟩ % (p : ℤ)).toNat then 1 else 0 + else 0 + +/-- Native Q coefficient specification; the original BC coordinates are transposed. -/ +@[expose] def qCoefficients {n : ℕ} (p : ℕ) (wBC : Fin n → Fin n → ℤ) (i : ℕ) : ℤ := + if h : Nat.Morton.row (i / p) < n ∧ Nat.Morton.column (i / p) < n then + if i % p = (wBC ⟨_, h.2⟩ ⟨_, h.1⟩ % (p : ℤ)).toNat then 1 else 0 + else 0 + +/-- Every actual monomial coefficient, including padding, has magnitude at most one. -/ +theorem pCoefficients_natAbs_le_one {n : ℕ} (p : ℕ) (wAC : Fin n → Fin n → ℤ) (i : ℕ) : + (pCoefficients p wAC i).natAbs ≤ 1 := by + unfold pCoefficients + split_ifs <;> decide + +/-- The transposed monomial matrix has the same unit coefficient bound. -/ +theorem qCoefficients_natAbs_le_one {n : ℕ} (p : ℕ) (wBC : Fin n → Fin n → ℤ) (i : ℕ) : + (qCoefficients p wBC i).natAbs ≤ 1 := by + unfold qCoefficients + split_ifs <;> decide + +private noncomputable def pad {n p : ℕ} (M : Matrix (Fin n) (Fin n) (CyclicIntRing p)) + (a b : ℕ) : CyclicIntRing p := if h : a < n ∧ b < n then M ⟨a, h.1⟩ ⟨b, h.2⟩ else 0 + +private theorem pCoefficients_eq {n p : ℕ} (hp : p ≠ 0) (wAC : Fin n → Fin n → ℤ) : + pCoefficients p wAC = coefficients hp (pad (matP wAC p)) := by + funext i + by_cases h : Nat.Morton.row (i / p) < n ∧ Nat.Morton.column (i / p) < n + · simp only [pCoefficients, coefficients, pad, dite_eq_left h, matP, Matrix.of_apply, + coeff_x_pow] + rw [Nat.mod_eq_of_lt (show (wAC ⟨_, h.1⟩ ⟨_, h.2⟩ % (p : ℤ)).toNat < p from + Int.natMod_lt hp)] + · simp only [pCoefficients, coefficients, pad, dite_eq_right h, map_zero] + +private theorem qCoefficients_eq {n p : ℕ} (hp : p ≠ 0) (wBC : Fin n → Fin n → ℤ) : + qCoefficients p wBC = coefficients hp (pad (matQ wBC p)) := by + funext i + by_cases h : Nat.Morton.row (i / p) < n ∧ Nat.Morton.column (i / p) < n + · simp only [qCoefficients, coefficients, pad, dite_eq_left h, matQ, Matrix.of_apply, + coeff_x_pow] + rw [Nat.mod_eq_of_lt (show (wBC ⟨_, h.2⟩ ⟨_, h.1⟩ % (p : ℤ)).toNat < p from + Int.natMod_lt hp)] + · simp only [qCoefficients, coefficients, pad, dite_eq_right h, map_zero] + +private theorem sum_pad {n p N : ℕ} (hN : n ≤ N) + (M K : Matrix (Fin n) (Fin n) (CyclicIntRing p)) (a b : Fin n) : + ∑ c ∈ Finset.range N, pad M a c * pad K c b = (M * K) a b := by + obtain ⟨k, rfl⟩ := Nat.exists_eq_add_of_le hN + have ht : ∑ c ∈ Finset.range k, pad M a (n + c) * pad K (n + c) b = 0 := + Finset.sum_eq_zero fun c _ => by simp [pad] + rw [Finset.sum_range_add, ht, add_zero, Matrix.mul_apply, Finset.sum_range] + exact Finset.sum_congr rfl fun c _ => by simp [pad] + +/-- Every original pair and residue in the executable finite Strassen coefficient array +has exactly the existing cyclic matrix-product meaning, including power-of-two padding. -/ +theorem multiply_entry_eq_coeff {n p k : ℕ} (hp : p ≠ 0) (hn : n ≤ 2 ^ k) + (wBC wAC : Fin n → Fin n → ℤ) (a b : Fin n) (r : ℕ) (hr : r < p) : + multiply p k (pCoefficients p wAC) (qCoefficients p wBC) + (Nat.Morton.index a b * p + r) = + CyclicIntRing.coeff hp r ((matP wAC p * matQ wBC p) a b) := by + have hi := Nat.Morton.index_lt (a.isLt.trans_le hn) (b.isLt.trans_le hn) + have hmul := Nat.mul_le_mul_right p (Nat.succ_le_of_lt hi) + rw [Nat.succ_mul] at hmul + have hidx : Nat.Morton.index a b * p + r < 4 ^ k * p := by omega + rw [multiply_eq_coefficients hp k (pad (matP wAC p)) (pad (matQ wBC p)) + (pCoefficients p wAC) (qCoefficients p wBC) + (fun i _ => congrFun (pCoefficients_eq hp wAC) i) + (fun i _ => congrFun (qCoefficients_eq hp wBC) i) hidx, + coefficients_at hp _ a b hr, sum_pad hn] + +/-- The selected real product coefficient is a nonnegative count of at most n middle +vertices. Native signed-to-unsigned extraction uses this bound before every sum. -/ +theorem multiply_entry_bounds {n p k : ℕ} (hp : p ≠ 0) (hn : n ≤ 2 ^ k) + (wBC wAC : Fin n → Fin n → ℤ) (a b : Fin n) (r : ℕ) (hr : r < p) : + 0 ≤ multiply p k (pCoefficients p wAC) (qCoefficients p wBC) + (Nat.Morton.index a b * p + r) ∧ + multiply p k (pCoefficients p wAC) (qCoefficients p wBC) + (Nat.Morton.index a b * p + r) ≤ (n : ℤ) := by + classical + rw [multiply_entry_eq_coeff hp hn wBC wAC a b r hr, + coeff_matP_mul_matQ wBC wAC hp a b r hr] + have hc := Finset.card_le_card (Finset.filter_subset (s := Finset.univ) + (p := fun c : Fin n => wAC a c + wBC b c ≡ (r : ℤ) [ZMOD (p : ℤ)])) + simp only [Finset.card_univ, Fintype.card_fin] at hc + constructor + · positivity + · exact_mod_cast hc + +/-- Select the (-AB)-residue coefficient for each original pair after the real seven-product +finite recursion. The depth controls zero padding; it is not a supplied multiplication. -/ +@[expose] def count {n : ℕ} (p k : ℕ) (wAB wBC wAC : Fin n → Fin n → ℤ) : ℤ := + ∑ a : Fin n, ∑ b : Fin n, + multiply p k (pCoefficients p wAC) (qCoefficients p wBC) + (Nat.Morton.index a b * p + ((-wAB a b) % (p : ℤ)).toNat) + +/-- The finite coefficient algorithm counts exactly the existing modular-zero predicate, +for every nonzero modulus and every power-of-two padding covering the input. -/ +theorem count_eq {n p k : ℕ} (hp : p ≠ 0) (hn : n ≤ 2 ^ k) + (wAB wBC wAC : Fin n → Fin n → ℤ) : + count p k wAB wBC wAC = (countZeroMod wAB wBC wAC p : ℤ) := by + have hc := falsePositiveCount_add_zeroCount_eq_sum_coeff wAB wBC wAC hp + rw [← countZeroMod_eq] at hc + rw [count, hc] + apply Finset.sum_congr rfl + intro a _ + apply Finset.sum_congr rfl + intro b _ + have hr : ((-wAB a b) % (p : ℤ)).toNat < p := Int.natMod_lt hp + exact multiply_entry_eq_coeff hp hn wBC wAC a b _ hr + +end Cslib.Algorithms.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/ExactTriangle/CountPrime/Extraction.lean b/Complexity/Algorithms/ExactTriangle/CountPrime/Extraction.lean new file mode 100644 index 0000000000..a693d2227b --- /dev/null +++ b/Complexity/Algorithms/ExactTriangle/CountPrime/Extraction.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.ExactTriangle.CountPrime + +/-! +# Nonnegative selected coefficients for modular triangle counting + +These bridges justify the real counter's unsigned extraction and summation from the existing +cyclic product specification. They do not assume a matrix multiplication or native execution. +-/ + +public section + +namespace Cslib.Algorithms.ExactTriangle.CountPrime + +open Cslib.Algorithms.MatrixMultiplication.Strassen + +/-- The unsigned value extracted from one actual original-coordinate product coefficient. -/ +@[expose] def pairCoefficient {n : ℕ} (p k : ℕ) + (wAB wBC wAC : Fin n → Fin n → ℤ) (a b : Fin n) : ℕ := + (multiply p k (pCoefficients p wAC) (qCoefficients p wBC) + (Nat.Morton.index a b * p + ((-wAB a b) % (p : ℤ)).toNat)).natAbs + +/-- Nonnegativity makes signed decoding by division by two an exact natural count. -/ +theorem pairCoefficient_cast {n p k : ℕ} (hp : p ≠ 0) (hn : n ≤ 2 ^ k) + (wAB wBC wAC : Fin n → Fin n → ℤ) (a b : Fin n) : + (pairCoefficient p k wAB wBC wAC a b : ℤ) = + multiply p k (pCoefficients p wAC) (qCoefficients p wBC) + (Nat.Morton.index a b * p + ((-wAB a b) % (p : ℤ)).toNat) := by + exact Int.natAbs_of_nonneg + (multiply_entry_bounds hp hn wBC wAC a b _ (Int.natMod_lt hp)).1 + +/-- Each selected coefficient counts at most the original side many middle vertices. -/ +theorem pairCoefficient_le {n p k : ℕ} (hp : p ≠ 0) (hn : n ≤ 2 ^ k) + (wAB wBC wAC : Fin n → Fin n → ℤ) (a b : Fin n) : + pairCoefficient p k wAB wBC wAC a b ≤ n := by + have he := pairCoefficient_cast hp hn wAB wBC wAC a b + have hb := (multiply_entry_bounds hp hn wBC wAC a b + (((-wAB a b) % (p : ℤ)).toNat) (Int.natMod_lt hp)).2 + omega + +/-- All selected original-coordinate coefficients sum to the existing modular triangle count. -/ +theorem sum_pairCoefficient {n p k : ℕ} (hp : p ≠ 0) (hn : n ≤ 2 ^ k) + (wAB wBC wAC : Fin n → Fin n → ℤ) : + ∑ a : Fin n, ∑ b : Fin n, pairCoefficient p k wAB wBC wAC a b = + countZeroMod wAB wBC wAC p := by + apply Int.natCast_inj.mp + push_cast + simp_rw [pairCoefficient_cast hp hn] + exact count_eq hp hn wAB wBC wAC + +/-- The selected counts in the exact original row-major order traversed by native code. +Out-of-range entries are zero, so empty input requires no arbitrary coordinate witness. -/ +@[expose] def rowMajorCoefficient {n : ℕ} (p k : ℕ) + (wAB wBC wAC : Fin n → Fin n → ℤ) (i : ℕ) : ℕ := + if h : i < n * n then + let q := finProdFinEquiv.symm (⟨i, h⟩ : Fin (n * n)) + pairCoefficient p k wAB wBC wAC q.1 q.2 + else 0 + +/-- The real row-major coefficient bound supplies the native prefix-accumulator admission. -/ +theorem rowMajorCoefficient_le {n p k i : ℕ} (hp : p ≠ 0) (hn : n ≤ 2 ^ k) + (wAB wBC wAC : Fin n → Fin n → ℤ) : + rowMajorCoefficient p k wAB wBC wAC i ≤ n := by + unfold rowMajorCoefficient + split_ifs + · exact pairCoefficient_le hp hn wAB wBC wAC _ _ + · omega + +/-- Summation in the actual native scan order preserves the existing modular count. -/ +theorem sum_rowMajorCoefficient {n p k : ℕ} (hp : p ≠ 0) (hn : n ≤ 2 ^ k) + (wAB wBC wAC : Fin n → Fin n → ℤ) : + ∑ i ∈ Finset.range (n * n), rowMajorCoefficient p k wAB wBC wAC i = + countZeroMod wAB wBC wAC p := by + rw [Finset.sum_range] + simp only [rowMajorCoefficient, Fin.isLt, dite_true] + rw [← Equiv.sum_comp (finProdFinEquiv : Fin n × Fin n ≃ Fin (n * n))] + rw [Fintype.sum_prod_type] + change (∑ a : Fin n, ∑ b : Fin n, pairCoefficient p k wAB wBC wAC + (finProdFinEquiv.symm (finProdFinEquiv (a, b))).1 + (finProdFinEquiv.symm (finProdFinEquiv (a, b))).2) = _ + simpa only [Equiv.symm_apply_apply] using sum_pairCoefficient hp hn wAB wBC wAC + +end Cslib.Algorithms.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/MatrixMultiplication/Strassen/Coefficients.lean b/Complexity/Algorithms/MatrixMultiplication/Strassen/Coefficients.lean new file mode 100644 index 0000000000..9d0a4cdc02 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Strassen/Coefficients.lean @@ -0,0 +1,78 @@ +/- +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.Strassen.Defs +public import Complexity.Foundations.Data.Nat.Morton + +/-! +# Morton coefficient blocks for the existing cyclic integer ring + +This is a mathematical readback specification, not an algorithm input. It relates actual +contiguous coefficient quarters to the existing quotient-ring matrices. The coefficient +ring and native signed encoding are unchanged. The quadrant argument adapts the matrix +layout part of pinned Spec/Sec3/Theorem17/Strassen.lean at revision +16a5ce41304fcdaea4e0be7c132e1c361dc26da5, using functions instead of list padding. +-/ + +public section + +namespace Cslib.Algorithms.MatrixMultiplication.Strassen + +open Cslib.CyclicIntRing + +/-- Flatten a cyclic-ring matrix into contiguous coefficient blocks in Morton order. -/ +@[expose] noncomputable def coefficients {p : ℕ} (hp : p ≠ 0) + (M : ℕ → ℕ → CyclicIntRing p) (i : ℕ) : ℤ := + coeff hp (i % p) (M (Nat.Morton.row (i / p)) (Nat.Morton.column (i / p))) + +/-- Ring addition becomes pointwise signed coefficient addition. -/ +theorem coefficients_add {p : ℕ} (hp : p ≠ 0) (M N : ℕ → ℕ → CyclicIntRing p) : + coefficients hp (M + N) = coefficients hp M + coefficients hp N := by + funext i + simp only [coefficients, Pi.add_apply, map_add] + +/-- Ring subtraction becomes pointwise signed coefficient subtraction. -/ +theorem coefficients_sub {p : ℕ} (hp : p ≠ 0) (M N : ℕ → ℕ → CyclicIntRing p) : + coefficients hp (M - N) = coefficients hp M - coefficients hp N := by + funext i + simp only [coefficients, Pi.sub_apply, map_sub] + +/-- Four ring-matrix quadrants in the same order as their contiguous native arrays. -/ +@[expose] def submatrix {R : Type*} (k t : ℕ) (M : ℕ → ℕ → R) : ℕ → ℕ → R := + fun a b => M (a + t / 2 * 2 ^ k) (b + t % 2 * 2 ^ k) + +/-- The native quarter pointer selects exactly the corresponding ring-matrix quadrant. -/ +theorem quarter_coefficients {p : ℕ} (hp : p ≠ 0) (k : ℕ) + (M : ℕ → ℕ → CyclicIntRing p) {t i : ℕ} (ht : t < 4) (hi : i < 4 ^ k * p) : + quarter (4 ^ k * p) t (coefficients hp M) i = + coefficients hp (submatrix k t M) i := by + have hm : t * (4 ^ k * p) + i = p * (t * 4 ^ k) + i := by ring + have hz : i / p < 4 ^ k := (Nat.div_lt_iff_lt_mul (Nat.pos_of_ne_zero hp)).mpr hi + have hrc := Nat.Morton.row_column_quadrant ht hz + simp only [quarter, coefficients, hm, + Nat.mul_add_div (Nat.pos_of_ne_zero hp), Nat.mul_add_mod] + rw [hrc.1, hrc.2] + rfl + +/-- A side-one matrix is one actual cyclic coefficient block. -/ +theorem coefficients_of_lt {p i : ℕ} (hp : p ≠ 0) (M : ℕ → ℕ → CyclicIntRing p) + (hi : i < p) : coefficients hp M i = coeff hp i (M 0 0) := by + simp only [coefficients, Nat.div_eq_of_lt hi, Nat.mod_eq_of_lt hi, + Nat.Morton.row_zero, Nat.Morton.column_zero] + +/-- An actual Morton coefficient address reads exactly the requested ring entry and degree. -/ +theorem coefficients_at {p r : ℕ} (hp : p ≠ 0) (M : ℕ → ℕ → CyclicIntRing p) + (a b : ℕ) (hr : r < p) : + coefficients hp M (Nat.Morton.index a b * p + r) = coeff hp r (M a b) := by + have hm : Nat.Morton.index a b * p + r = p * Nat.Morton.index a b + r := by ring + simp only [coefficients, hm, Nat.mul_add_div (Nat.pos_of_ne_zero hp), Nat.mul_add_mod, + Nat.div_eq_of_lt hr, Nat.mod_eq_of_lt hr, Nat.add_zero, + Nat.Morton.row_index, Nat.Morton.column_index] + +end Cslib.Algorithms.MatrixMultiplication.Strassen diff --git a/Complexity/Algorithms/MatrixMultiplication/Strassen/Correctness.lean b/Complexity/Algorithms/MatrixMultiplication/Strassen/Correctness.lean new file mode 100644 index 0000000000..0c1ba34ab0 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Strassen/Correctness.lean @@ -0,0 +1,147 @@ +/- +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.Strassen.Coefficients +import Mathlib.Tactic.IntervalCases + +/-! +# Correctness of the seven-product cyclic coefficient recursion + +The finite recursion computes coefficients of ordinary matrix multiplication over the +existing cyclic integer quotient. Its inputs need agree with the matrix only inside the +occupied native coefficient blocks. There is no assumed multiplication implementation. + +The block identity adapts pinned Spec/Sec3/Theorem17/Strassen.lean, lines 105–126, at revision +16a5ce41304fcdaea4e0be7c132e1c361dc26da5. This theorem is mathematical; the independent native +controller execution proof must additionally account for its actual forms, stack and calls. +-/ + +public section + +namespace Cslib.Algorithms.MatrixMultiplication.Strassen + +open Cslib.CyclicIntRing + +private theorem sum_two_pow_succ {R : Type*} [AddCommMonoid R] (k : ℕ) (f : ℕ → R) : + ∑ c ∈ Finset.range (2 ^ (k + 1)), f c = + ∑ c ∈ Finset.range (2 ^ k), f c + ∑ c ∈ Finset.range (2 ^ k), f (c + 2 ^ k) := by + rw [pow_succ, Nat.mul_two, Finset.sum_range_add] + simp only [Nat.add_comm] + +/-- Every occupied coefficient of the seven-product recursion is the corresponding +coefficient of the ordinary ring-matrix product. -/ +theorem multiply_eq_coefficients {p : ℕ} (hp : p ≠ 0) (k : ℕ) + (M N : ℕ → ℕ → CyclicIntRing p) (a b : ℕ → ℤ) + (hA : ∀ i < 4 ^ k * p, a i = coefficients hp M i) + (hB : ∀ i < 4 ^ k * p, b i = coefficients hp N i) + {i : ℕ} (hi : i < 4 ^ k * p) : + multiply p k a b i = + coefficients hp (fun r s => ∑ c ∈ Finset.range (2 ^ k), M r c * N c s) i := by + induction k generalizing M N a b i with + | zero => + have hip : i < p := by simpa using hi + calc + _ = convolution p (fun j => coeff hp j (M 0 0)) + (fun j => coeff hp j (N 0 0)) i := by + apply convolution_congr hip + · intro j hj + exact (hA j (by simpa using hj)).trans (coefficients_of_lt hp M hj) + · intro j hj + exact (hB j (by simpa using hj)).trans (coefficients_of_lt hp N hj) + _ = coeff hp i (M 0 0 * N 0 0) := (coeff_mul_eq_convolution hp _ _ hip).symm + _ = _ := by rw [coefficients_of_lt hp _ hip]; simp + | succ k ih => + let q := 4 ^ k * p + let j := i % q + have hq : 0 < q := Nat.mul_pos (by positivity) (Nat.pos_of_ne_zero hp) + have hs : 4 ^ (k + 1) * p = 4 * q := by dsimp [q]; rw [pow_succ]; ring + have hqeq : 4 ^ k * p = q := rfl + have hj : j < q := Nat.mod_lt _ hq + have ht : i / q < 4 := (Nat.div_lt_iff_lt_mul hq).mpr (by simpa only [← hs] using hi) + generalize htdef : i / q = t at ht + have he : t * q + j = i := by + dsimp only [j] + rw [← htdef, Nat.mul_comm] + exact Nat.div_add_mod i q + have hqa (t : ℕ) (ht : t < 4) (s : ℕ) (hs' : s < q) : + quarter q t a s = coefficients hp (submatrix k t M) s := by + have hm := Nat.mul_le_mul_right q (show t ≤ 3 by omega) + exact (hA (t * q + s) (by rw [hs]; omega)).trans + (quarter_coefficients hp k M ht hs') + have hqb (t : ℕ) (ht : t < 4) (s : ℕ) (hs' : s < q) : + quarter q t b s = coefficients hp (submatrix k t N) s := by + have hm := Nat.mul_le_mul_right q (show t ≤ 3 by omega) + exact (hB (t * q + s) (by rw [hs]; omega)).trans + (quarter_coefficients hp k N ht hs') + have h₁ := ih (submatrix k 0 M + submatrix k 3 M) (submatrix k 0 N + submatrix k 3 N) + (quarter q 0 a + quarter q 3 a) (quarter q 0 b + quarter q 3 b) + (fun s hs' => by + rw [coefficients_add] + exact congrArg₂ (· + ·) (hqa 0 (by decide) s hs') (hqa 3 (by decide) s hs')) + (fun s hs' => by + rw [coefficients_add] + exact congrArg₂ (· + ·) (hqb 0 (by decide) s hs') (hqb 3 (by decide) s hs')) hj + have h₂ := ih (submatrix k 2 M + submatrix k 3 M) (submatrix k 0 N) + (quarter q 2 a + quarter q 3 a) (quarter q 0 b) + (fun s hs' => by + rw [coefficients_add] + exact congrArg₂ (· + ·) (hqa 2 (by decide) s hs') (hqa 3 (by decide) s hs')) + (hqb 0 (by decide)) hj + have h₃ := ih (submatrix k 0 M) (submatrix k 1 N - submatrix k 3 N) + (quarter q 0 a) (quarter q 1 b - quarter q 3 b) (hqa 0 (by decide)) + (fun s hs' => by + rw [coefficients_sub] + exact congrArg₂ (· - ·) (hqb 1 (by decide) s hs') (hqb 3 (by decide) s hs')) hj + have h₄ := ih (submatrix k 3 M) (submatrix k 2 N - submatrix k 0 N) + (quarter q 3 a) (quarter q 2 b - quarter q 0 b) (hqa 3 (by decide)) + (fun s hs' => by + rw [coefficients_sub] + exact congrArg₂ (· - ·) (hqb 2 (by decide) s hs') (hqb 0 (by decide) s hs')) hj + have h₅ := ih (submatrix k 0 M + submatrix k 1 M) (submatrix k 3 N) + (quarter q 0 a + quarter q 1 a) (quarter q 3 b) + (fun s hs' => by + rw [coefficients_add] + exact congrArg₂ (· + ·) (hqa 0 (by decide) s hs') (hqa 1 (by decide) s hs')) + (hqb 3 (by decide)) hj + have h₆ := ih (submatrix k 2 M - submatrix k 0 M) (submatrix k 0 N + submatrix k 1 N) + (quarter q 2 a - quarter q 0 a) (quarter q 0 b + quarter q 1 b) + (fun s hs' => by + rw [coefficients_sub] + exact congrArg₂ (· - ·) (hqa 2 (by decide) s hs') (hqa 0 (by decide) s hs')) + (fun s hs' => by + rw [coefficients_add] + exact congrArg₂ (· + ·) (hqb 0 (by decide) s hs') (hqb 1 (by decide) s hs')) hj + have h₇ := ih (submatrix k 1 M - submatrix k 3 M) (submatrix k 2 N + submatrix k 3 N) + (quarter q 1 a - quarter q 3 a) (quarter q 2 b + quarter q 3 b) + (fun s hs' => by + rw [coefficients_sub] + exact congrArg₂ (· - ·) (hqa 1 (by decide) s hs') (hqa 3 (by decide) s hs')) + (fun s hs' => by + rw [coefficients_add] + exact congrArg₂ (· + ·) (hqb 2 (by decide) s hs') (hqb 3 (by decide) s hs')) hj + have hR : coefficients hp + (fun r s => ∑ c ∈ Finset.range (2 ^ (k + 1)), M r c * N c s) i = + coefficients hp (submatrix k t + (fun r s => ∑ c ∈ Finset.range (2 ^ (k + 1)), M r c * N c s)) j := by + rw [← he] + exact quarter_coefficients hp k _ ht hj + have hjeq : i % q = j := rfl + rw [hR] + simp only [multiply, hqeq, assemble, htdef, hjeq, Pi.add_apply, Pi.sub_apply] + interval_cases t <;> simp only [h₁, h₂, h₃, h₄, h₅, h₆, h₇] + all_goals + simp only [coefficients, submatrix, Pi.add_apply, Pi.sub_apply, + Nat.reduceDiv, Nat.reduceMod, Nat.zero_mul, Nat.one_mul, Nat.add_zero, + ← map_add, ← map_sub] + congr 1 + rw [sum_two_pow_succ] + simp only [← Finset.sum_add_distrib, ← Finset.sum_sub_distrib] + exact Finset.sum_congr rfl fun c _ => by ring + +end Cslib.Algorithms.MatrixMultiplication.Strassen diff --git a/Complexity/Algorithms/MatrixMultiplication/Strassen/Defs.lean b/Complexity/Algorithms/MatrixMultiplication/Strassen/Defs.lean new file mode 100644 index 0000000000..6a340e6a67 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Strassen/Defs.lean @@ -0,0 +1,137 @@ +/- +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.RingTheory.CyclicIntRing.Coefficients + +/-! +# Seven-product recursion on cyclic coefficient arrays + +`multiply` is the finite coefficient specification of the native explicit-stack algorithm. +It uses the existing cyclic convolution, seven recursive products, and the ordinary Strassen +signs. The arrays are functions on natural indices; only their first `4^k*p` entries matter. +No execution or resource contract is bundled into this mathematical definition. + +The recursion adapts Spec/Sec3/Theorem17/Strassen.lean, lines 81–96, at Anthropic formal-math +revision 16a5ce41304fcdaea4e0be7c132e1c361dc26da5. The coefficient bound below also controls +the native intermediate operand forms and partial output accumulations. +-/ + +public section + +namespace Cslib.Algorithms.MatrixMultiplication.Strassen + +open Cslib.CyclicIntRing + +/-- Read one contiguous quarter of a coefficient array. -/ +@[expose] def quarter (q t : ℕ) (a : ℕ → ℤ) : ℕ → ℤ := fun i => a (t * q + i) + +/-- Join four equal-length output quarters; only the first `4*q` entries are specified. -/ +@[expose] def assemble (q : ℕ) (a b c d : ℕ → ℤ) (i : ℕ) : ℤ := + match i / q with + | 0 => a (i % q) + | 1 => b (i % q) + | 2 => c (i % q) + | _ => d (i % q) + +/-- The actual seven products, with cyclic convolution at scalar matrix leaves. -/ +@[expose] def multiply (p : ℕ) : ℕ → (ℕ → ℤ) → (ℕ → ℤ) → ℕ → ℤ + | 0, a, b => convolution p a b + | k + 1, a, b => + let q := 4 ^ k * p + let a₀ := quarter q 0 a; let a₁ := quarter q 1 a + let a₂ := quarter q 2 a; let a₃ := quarter q 3 a + let b₀ := quarter q 0 b; let b₁ := quarter q 1 b + let b₂ := quarter q 2 b; let b₃ := quarter q 3 b + let m₁ := multiply p k (a₀ + a₃) (b₀ + b₃) + let m₂ := multiply p k (a₂ + a₃) b₀ + let m₃ := multiply p k a₀ (b₁ - b₃) + let m₄ := multiply p k a₃ (b₂ - b₀) + let m₅ := multiply p k (a₀ + a₁) b₃ + let m₆ := multiply p k (a₂ - a₀) (b₀ + b₁) + let m₇ := multiply p k (a₁ - a₃) (b₂ + b₃) + assemble q (m₁ + m₄ - m₅ + m₇) (m₃ + m₅) (m₂ + m₄) (m₁ - m₂ + m₃ + m₆) + +/-- A quarter read remains within the original matrix block. -/ +theorem quarter_natAbs_le {q A : ℕ} {a : ℕ → ℤ} + (ha : ∀ i < 4 * q, (a i).natAbs ≤ A) {t : ℕ} (ht : t < 4) + {i : ℕ} (hi : i < q) : (quarter q t a i).natAbs ≤ A := by + apply ha + have hm := Nat.mul_le_mul_right q (show t ≤ 3 by omega) + omega + +/-- Every returned coefficient is bounded using only the original two input envelopes. +The factor `16^k` deliberately also covers all four-term output accumulations. -/ +theorem multiply_natAbs_le {p A B : ℕ} (k : ℕ) (a b : ℕ → ℤ) + (ha : ∀ i < 4 ^ k * p, (a i).natAbs ≤ A) + (hb : ∀ i < 4 ^ k * p, (b i).natAbs ≤ B) {i : ℕ} (hi : i < 4 ^ k * p) : + (multiply p k a b i).natAbs ≤ p * (A * B) * 16 ^ k := by + induction k generalizing A B a b i with + | zero => + simpa only [multiply, convolution, pow_zero, Nat.one_mul, Nat.mul_one] using + partialConvolution_natAbs_le (by simpa using hi) (le_refl p) + (by simpa using ha) (by simpa using hb) + | succ k ih => + let q := 4 ^ k * p + have hq : 0 < q := by + have hp : 0 < p := by by_contra hc; simp [Nat.eq_zero_of_not_pos hc] at hi + exact Nat.mul_pos (by positivity) hp + have hs : 4 ^ (k + 1) * p = 4 * q := by dsimp [q]; rw [pow_succ]; ring + have hqa (t : ℕ) (ht : t < 4) (j : ℕ) (hj : j < q) : + (quarter q t a j).natAbs ≤ A := quarter_natAbs_le (by simpa only [← hs] using ha) ht hj + have hqb (t : ℕ) (ht : t < 4) (j : ℕ) (hj : j < q) : + (quarter q t b j).natAbs ≤ B := quarter_natAbs_le (by simpa only [← hs] using hb) ht hj + have hchild (u v : ℕ → ℤ) + (hu : ∀ j < q, (u j).natAbs ≤ 2 * A) + (hv : ∀ j < q, (v j).natAbs ≤ 2 * B) : + (multiply p k u v (i % q)).natAbs ≤ p * ((2 * A) * (2 * B)) * 16 ^ k := + ih u v hu hv (Nat.mod_lt _ hq) + have ha₀ (j : ℕ) (hj : j < q) := hqa 0 (by decide) j hj + have ha₁ (j : ℕ) (hj : j < q) := hqa 1 (by decide) j hj + have ha₂ (j : ℕ) (hj : j < q) := hqa 2 (by decide) j hj + have ha₃ (j : ℕ) (hj : j < q) := hqa 3 (by decide) j hj + have hb₀ (j : ℕ) (hj : j < q) := hqb 0 (by decide) j hj + have hb₁ (j : ℕ) (hj : j < q) := hqb 1 (by decide) j hj + have hb₂ (j : ℕ) (hj : j < q) := hqb 2 (by decide) j hj + have hb₃ (j : ℕ) (hj : j < q) := hqb 3 (by decide) j hj + have hsum {C : ℕ} {u v : ℤ} (hu : u.natAbs ≤ C) (hv : v.natAbs ≤ C) : + (u + v).natAbs ≤ 2 * C := (Int.natAbs_add_le _ _).trans (by omega) + have hsub {C : ℕ} {u v : ℤ} (hu : u.natAbs ≤ C) (hv : v.natAbs ≤ C) : + (u - v).natAbs ≤ 2 * C := (Int.natAbs_sub_le _ _).trans (by omega) + have h₁ := hchild (quarter q 0 a + quarter q 3 a) (quarter q 0 b + quarter q 3 b) + (fun j hj => hsum (ha₀ j hj) (ha₃ j hj)) (fun j hj => hsum (hb₀ j hj) (hb₃ j hj)) + have h₂ := hchild (quarter q 2 a + quarter q 3 a) (quarter q 0 b) + (fun j hj => hsum (ha₂ j hj) (ha₃ j hj)) (fun j hj => (hb₀ j hj).trans (by omega)) + have h₃ := hchild (quarter q 0 a) (quarter q 1 b - quarter q 3 b) + (fun j hj => (ha₀ j hj).trans (by omega)) (fun j hj => hsub (hb₁ j hj) (hb₃ j hj)) + have h₄ := hchild (quarter q 3 a) (quarter q 2 b - quarter q 0 b) + (fun j hj => (ha₃ j hj).trans (by omega)) (fun j hj => hsub (hb₂ j hj) (hb₀ j hj)) + have h₅ := hchild (quarter q 0 a + quarter q 1 a) (quarter q 3 b) + (fun j hj => hsum (ha₀ j hj) (ha₁ j hj)) (fun j hj => (hb₃ j hj).trans (by omega)) + have h₆ := hchild (quarter q 2 a - quarter q 0 a) (quarter q 0 b + quarter q 1 b) + (fun j hj => hsub (ha₂ j hj) (ha₀ j hj)) (fun j hj => hsum (hb₀ j hj) (hb₁ j hj)) + have h₇ := hchild (quarter q 1 a - quarter q 3 a) (quarter q 2 b + quarter q 3 b) + (fun j hj => hsub (ha₁ j hj) (ha₃ j hj)) (fun j hj => hsum (hb₂ j hj) (hb₃ j hj)) + have hqeq : 4 ^ k * p = q := rfl + simp only [multiply, hqeq, assemble, Pi.add_apply, Pi.sub_apply] + split <;> + have htotal : p * (A * B) * 16 ^ (k + 1) = + 4 * (p * ((2 * A) * (2 * B)) * 16 ^ k) := by rw [pow_succ]; ring + all_goals rw [htotal] + · exact ((Int.natAbs_add_le _ _).trans + (Nat.add_le_add ((Int.natAbs_sub_le _ _).trans + (Nat.add_le_add ((Int.natAbs_add_le _ _).trans (Nat.add_le_add h₁ h₄)) h₅)) h₇)).trans + (by omega) + · exact (Int.natAbs_add_le _ _).trans ((Nat.add_le_add h₃ h₅).trans (by omega)) + · exact (Int.natAbs_add_le _ _).trans ((Nat.add_le_add h₂ h₄).trans (by omega)) + · exact ((Int.natAbs_add_le _ _).trans + (Nat.add_le_add ((Int.natAbs_add_le _ _).trans + (Nat.add_le_add ((Int.natAbs_sub_le _ _).trans (Nat.add_le_add h₁ h₂)) h₃)) h₆)).trans + (by omega) + +end Cslib.Algorithms.MatrixMultiplication.Strassen diff --git a/Complexity/Algorithms/MatrixMultiplication/Strassen/Phases.lean b/Complexity/Algorithms/MatrixMultiplication/Strassen/Phases.lean new file mode 100644 index 0000000000..154d9ec8c2 --- /dev/null +++ b/Complexity/Algorithms/MatrixMultiplication/Strassen/Phases.lean @@ -0,0 +1,165 @@ +/- +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.Strassen.Defs +import Mathlib.Tactic.FinCases +import Mathlib.Tactic.IntervalCases + +/-! +# Partial Strassen accumulators for the native traversal proof + +The seven operand forms and their output signs give a pointwise prefix invariant for the +explicit native stack. They are ordinary finite mathematics, not a supplied answer or run. +Every partialValue quadrant has at most four signed child contributions, including cancellation. +The definitions below serve internal execution proofs, rather than another matrix-product API. + +The forms/signs are the same Strassen identities adapted in `Strassen.Defs`, from pinned +Spec/Sec3/Theorem17/Strassen.lean at revision 16a5ce41304fcdaea4e0be7c132e1c361dc26da5. +-/ + +public section + +namespace Cslib.Algorithms.MatrixMultiplication.Strassen + +/-- First actual mathematical operand of each of the seven native recursive children. -/ +@[expose] def leftOperand (p k : ℕ) (a : ℕ → ℤ) : Fin 7 → ℕ → ℤ + | 0 => quarter (4 ^ k * p) 0 a + quarter (4 ^ k * p) 3 a + | 1 => quarter (4 ^ k * p) 2 a + quarter (4 ^ k * p) 3 a + | 2 => quarter (4 ^ k * p) 0 a + | 3 => quarter (4 ^ k * p) 3 a + | 4 => quarter (4 ^ k * p) 0 a + quarter (4 ^ k * p) 1 a + | 5 => quarter (4 ^ k * p) 2 a - quarter (4 ^ k * p) 0 a + | 6 => quarter (4 ^ k * p) 1 a - quarter (4 ^ k * p) 3 a + +/-- Second actual mathematical operand of each native child. -/ +@[expose] def rightOperand (p k : ℕ) (b : ℕ → ℤ) : Fin 7 → ℕ → ℤ + | 0 => quarter (4 ^ k * p) 0 b + quarter (4 ^ k * p) 3 b + | 1 => quarter (4 ^ k * p) 0 b + | 2 => quarter (4 ^ k * p) 1 b - quarter (4 ^ k * p) 3 b + | 3 => quarter (4 ^ k * p) 2 b - quarter (4 ^ k * p) 0 b + | 4 => quarter (4 ^ k * p) 3 b + | 5 => quarter (4 ^ k * p) 0 b + quarter (4 ^ k * p) 1 b + | 6 => quarter (4 ^ k * p) 2 b + quarter (4 ^ k * p) 3 b + +/-- The already-defined finite seven-product recursion on one actual child operand pair. -/ +@[expose] def childProduct (p k : ℕ) (a b : ℕ → ℤ) (t : Fin 7) : ℕ → ℤ := + multiply p k (leftOperand p k a t) (rightOperand p k b t) + +/-- Contribution of a returned child to a selected output quadrant. -/ +@[expose] def contribution : Fin 7 → Fin 4 → ℤ + | 0, 0 | 0, 3 => 1 + | 1, 2 => 1 + | 1, 3 => -1 + | 2, 1 | 2, 3 => 1 + | 3, 0 | 3, 2 => 1 + | 4, 0 => -1 + | 4, 1 => 1 + | 5, 3 => 1 + | 6, 0 => 1 + | _, _ => 0 + +/-- Signed partial output after the first completed children have been accumulated. -/ +@[expose] def partialValue (p k completed : ℕ) (a b : ℕ → ℤ) (quadrant : Fin 4) (r : ℕ) : ℤ := + ∑ t : Fin 7, if t.val < completed then contribution t quadrant * childProduct p k a b t r + else 0 + +/-- No child has contributed immediately after the actual native clear. -/ +@[simp] theorem partialValue_zero (p k : ℕ) (a b : ℕ → ℤ) (quadrant : Fin 4) (r : ℕ) : + partialValue p k 0 a b quadrant r = 0 := by simp [partialValue] + +/-- One returned child changes the partial sum by exactly its fixed signed contribution. -/ +theorem partialValue_succ (p k : ℕ) (completed : Fin 7) (a b : ℕ → ℤ) + (quadrant : Fin 4) (r : ℕ) : + partialValue p k (completed.val + 1) a b quadrant r = + partialValue p k completed.val a b quadrant r + + contribution completed quadrant * childProduct p k a b completed r := by + classical + have hs : ∑ t : Fin 7, (if t = completed then + contribution completed quadrant * childProduct p k a b completed r else 0) = + contribution completed quadrant * childProduct p k a b completed r := by simp + rw [← hs, partialValue, partialValue, ← Finset.sum_add_distrib] + apply Finset.sum_congr rfl + intro t _ + by_cases he : t = completed + · subst t + simp + · have hv : t.val ≠ completed.val := fun h => he (Fin.ext h) + have hlt : t.val < completed.val + 1 ↔ t.val < completed.val := by omega + simp only [he, ite_false, add_zero, hlt] + +/-- Each output quarter has at most four contributions at every intermediate stage. -/ +theorem contribution_weight_le_four (completed : ℕ) (hc : completed ≤ 7) (q : Fin 4) : + (∑ t : Fin 7, if t.val < completed then (contribution t q).natAbs else 0) ≤ 4 := by + interval_cases completed <;> fin_cases q <;> decide + +/-- Every partial accumulator fits the four-term envelope, without assuming a sparse result. -/ +theorem partialValue_natAbs_le {p k completed r C : ℕ} (a b : ℕ → ℤ) (q : Fin 4) + (hc : completed ≤ 7) (hchild : ∀ t, (childProduct p k a b t r).natAbs ≤ C) : + (partialValue p k completed a b q r).natAbs ≤ 4 * C := by + calc + _ ≤ ∑ t : Fin 7, (if t.val < completed then + contribution t q * childProduct p k a b t r else 0).natAbs := Int.natAbs_sum_le _ _ + _ ≤ ∑ t : Fin 7, (if t.val < completed then (contribution t q).natAbs else 0) * C := by + apply Finset.sum_le_sum + intro t _ + split_ifs <;> simp only [Int.natAbs_mul, Int.natAbs_zero, zero_mul, le_refl] + exact Nat.mul_le_mul_left _ (hchild t) + _ = (∑ t : Fin 7, if t.val < completed then (contribution t q).natAbs else 0) * C := + (Finset.sum_mul _ _ _).symm + _ ≤ 4 * C := Nat.mul_le_mul_right _ (contribution_weight_le_four completed hc q) + +/-- Each concrete operand form has at most twice the original coefficient magnitude. -/ +theorem operands_natAbs_le {p k A r : ℕ} (a : ℕ → ℤ) (t : Fin 7) + (ha : ∀ j < 4 ^ (k + 1) * p, (a j).natAbs ≤ A) (hr : r < 4 ^ k * p) : + (leftOperand p k a t r).natAbs ≤ 2 * A ∧ + (rightOperand p k a t r).natAbs ≤ 2 * A := by + have hs : 4 ^ (k + 1) * p = 4 * (4 ^ k * p) := by rw [pow_succ]; ring + have hq (s : ℕ) (hs' : s < 4) : (quarter (4 ^ k * p) s a r).natAbs ≤ A := + quarter_natAbs_le (by simpa only [← hs] using ha) hs' hr + have h₀ := hq 0 (by decide) + have h₁ := hq 1 (by decide) + have h₂ := hq 2 (by decide) + have h₃ := hq 3 (by decide) + fin_cases t <;> constructor <;> simp only [leftOperand, rightOperand, Pi.add_apply, Pi.sub_apply] + all_goals first + | exact (Int.natAbs_add_le _ _).trans (by omega) + | omega + +/-- Child coefficient bounds come from the already-proved finite recursion, rather than +an assumed child result bound. -/ +theorem childProduct_natAbs_le {p k A B r : ℕ} (a b : ℕ → ℤ) (t : Fin 7) + (ha : ∀ j < 4 ^ (k + 1) * p, (a j).natAbs ≤ A) + (hb : ∀ j < 4 ^ (k + 1) * p, (b j).natAbs ≤ B) (hr : r < 4 ^ k * p) : + (childProduct p k a b t r).natAbs ≤ p * ((2 * A) * (2 * B)) * 16 ^ k := + multiply_natAbs_le k _ _ (fun _ hj => (operands_natAbs_le a t ha hj).1) + (fun _ hj => (operands_natAbs_le b t hb hj).2) hr + +/-- The four completed output expressions are exactly the usual Strassen combinations. -/ +theorem partialValue_seven (p k : ℕ) (a b : ℕ → ℤ) (q : Fin 4) (r : ℕ) : + partialValue p k 7 a b q r = match q with + | 0 => childProduct p k a b 0 r + childProduct p k a b 3 r - childProduct p k a b 4 r + + childProduct p k a b 6 r + | 1 => childProduct p k a b 2 r + childProduct p k a b 4 r + | 2 => childProduct p k a b 1 r + childProduct p k a b 3 r + | 3 => childProduct p k a b 0 r - childProduct p k a b 1 r + childProduct p k a b 2 r + + childProduct p k a b 5 r := by + fin_cases q <;> norm_num [partialValue, contribution, Fin.sum_univ_succ] <;> ring + +/-- The completed native-style partial invariant agrees with the existing finite multiply +specification on every real coefficient of each output quadrant. -/ +theorem multiply_eq_partialValue {p k r : ℕ} (hp : 0 < p) (a b : ℕ → ℤ) + (t : Fin 4) (hr : r < 4 ^ k * p) : + multiply p (k + 1) a b (t.val * (4 ^ k * p) + r) = partialValue p k 7 a b t r := by + have hq : 0 < 4 ^ k * p := Nat.mul_pos (by positivity) hp + have hm : t.val * (4 ^ k * p) + r = (4 ^ k * p) * t.val + r := by ring + rw [multiply, assemble, hm, Nat.mul_add_div hq, Nat.div_eq_of_lt hr, Nat.add_zero, + Nat.mul_add_mod, Nat.mod_eq_of_lt hr, partialValue_seven] + fin_cases t <;> rfl + +end Cslib.Algorithms.MatrixMultiplication.Strassen diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime.lean new file mode 100644 index 0000000000..45cd007e4e --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime.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.ExactTriangle.CountPrime.SourceBounds +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.TimeBound + +/-! +# Complete native cyclic-Strassen modular triangle counter + +One fixed 128-register standard WordRAM program counts the existing modular-zero triangles +from raw `(n,p)` headers and three canonical signed matrices. It computes residues, the +Morton table, padding and both cyclic-monomial matrices, executes the actual seven-product +recursion with an explicit stack, then reads and sums the selected coefficients. Its raw +count is register 70; caller arguments are registers 64 through 69. The callable variant takes +runtime matrix/workspace pointers and preserves original arrays and outside memory. + +## Main results + +* `exec`: actual complete caller execution from original signed arrays, allowing dirty scratch. +* `runs_input_subcubic`: actual initialized prefix and charged halt, exact raw count and + `3704*(p+1)^2*(n+1)^2.81` instruction bound. +* `admissionWidth_le_log`: if `p,A≤(n+2)^κ`, physical width + `(κ+24)*clog₂(n+2)` is sufficient and the original input fits. +* `bounded_reachable_polynomial`: every admitted wider prefix has words and memory support + bounded by `(n+2)^(κ+25)`, independently of the selected physical width. + +The n exponent is subcubic and the runtime p factor remains explicit. This is the concrete +counter prerequisite for prime selection, not prime enumeration/argmin, an aggregate +ExactTriangle solver or the ordinary 3SUM exponent 1.9992 endpoint. + +## References + +Selectively adapts the algorithm in Programs/Sec3/Theorem17/Hashing/{CountPrime,RingOps, +Strassen,StrassenTime,ZOrder,Count}.lean and its finite specification at +[Anthropic formal-math revision 16a5ce41304fcdaea4e0be7c132e1c361dc26da5](https://github.com/anthropics/formal-math/tree/16a5ce41304fcdaea4e0be7c132e1c361dc26da5/3sum-apsp/ThreeSumApsp). +Credit is retained in adapted source modules. Native implementation, frames, fits, execution +and instruction constants are independently proved against existing standard WordRAM APIs; +no Light semantics or assumed machine simulation is imported. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM + +/-- One actual initialized finite native program, exact modular count and charged subcubic +execution. The prefix and halting result belong to the same state. All setup, ring arithmetic, +indexing, copying, recursion/control/return, scan and halt instructions are included. +The modulus and original-weight bound determine admission; no intermediate certificate is input. -/ +theorem runs_input_subcubic {W n p A : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hp : 0 < p) + (hv : ∀ z ∈ values, z.natAbs ≤ A) (hW : admissionWidth n p A ≤ W) : + InputFits W (inputWords n p values) ∧ + ∃ y t, Runs W (entryProgram.program Reg.result) (State.init (inputWords n p values)) + ⟨entryProgram.size, y⟩ t ∧ + Executes W (entryProgram.program Reg.result) (State.init (inputWords n p values)) + (y.regs Reg.result) (t + 1) ∧ + ((t + 1 : ℕ) : ℝ) ≤ 3704 * (p + 1 : ℝ) ^ 2 * (n + 1 : ℝ) ^ (281 / 100 : ℝ) ∧ + y.regs Reg.result = Cslib.Algorithms.ExactTriangle.countZeroMod + (fun α β : Fin n => values.getD (α.val * n + β.val) 0) + (fun α β : Fin n => values.getD (n * n + α.val * n + β.val) 0) + (fun α β : Fin n => values.getD (2 * (n * n) + α.val * n + β.val) 0) p ∧ + y.Bounded W ∧ + ∀ d, d < 3 + 3 * (n * n) ∨ memoryEnd n p (Nat.clog 2 n) (3 + 3 * (n * n)) ≤ d → + y.mem d = (Data.init (inputWords n p values) : Data 128).mem d := by + obtain ⟨hi, y, t, ht, run, halt, result, bounded, frame⟩ := runs_input values hlen hp hv hW + have hcast : ((t + 1 : ℕ) : ℝ) ≤ ((cost n p + 10 : ℕ) : ℝ) := by exact_mod_cast ht + exact ⟨hi, y, t, run, halt, hcast.trans (cost_le_subcubic n p), result, bounded, frame⟩ + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Access.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Access.lean new file mode 100644 index 0000000000..1939465c24 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Access.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.ExactTriangle.CountPrime.Spread + +/-! +# Charged native modular-counter matrix accesses + +The Morton block reads two actual words from the previously computed spread table. The +residue block computes the actual row-major address, transposing BC when requested. These +are internal stage contracts; the composed counter supplies the table through its proved +construction and the residues through the existing native signed-remainder loop. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM + +/-- Six actual instructions read the table and compute the Morton index. The input row and +column and the original table pointer are preserved. -/ +theorem exec_morton {w k : ℕ} (x : Data 128) (h2 : x.regs Reg.two = 2) + (hr : x.regs Reg.row < 2 ^ k) (hc : x.regs Reg.column < 2 ^ k) + (htable : ∀ j < 2 ^ k, x.mem (x.regs Reg.spread + j) = Nat.Morton.spread j) + (hw : Fits w (x.regs Reg.spread + 4 ^ k + 2 ^ k)) : + ∃ y, ExecWithin w morton x y 6 ∧ y.mem = x.mem ∧ + y.regs Reg.position = Nat.Morton.index (x.regs Reg.row) (x.regs Reg.column) ∧ + ∀ i, i ≠ Reg.address → i ≠ Reg.rowSpread → i ≠ Reg.columnSpread → + i ≠ Reg.position → y.regs i = x.regs i := by + have hreadr := htable _ hr + have hreadc := htable _ hc + have hi := Nat.Morton.index_lt hr hc + have hsum : Fits w + (Nat.Morton.spread (x.regs Reg.row) * 2 + Nat.Morton.spread (x.regs Reg.column)) := + hw.of_le (by unfold Nat.Morton.index at hi; omega) + have hprod : Fits w (Nat.Morton.spread (x.regs Reg.row) * 2) := hw.of_le (by + unfold Nat.Morton.index at hi + omega) + have haddrR : Fits w (x.regs Reg.spread + x.regs Reg.row) := hw.of_le (by omega) + have haddrC : Fits w (x.regs Reg.spread + x.regs Reg.column) := hw.of_le (by omega) + refine ⟨?y, ?run, ?mem, ?position, ?regs⟩ + case run => + ram_eval [morton, h2, hreadr, hreadc, hsum, hprod, haddrR, haddrC, + Reg.address, Reg.spread, Reg.rowSpread, Reg.columnSpread, Reg.position, + Reg.row, Reg.column, Reg.two] + case mem => rfl + case position => simp only [Data.set, Function.update_self, Nat.Morton.index, Nat.mul_comm] + case regs => + intro i ha hr hc hp + simp only [Data.set, Function.update_of_ne ha, Function.update_of_ne hr, + Function.update_of_ne hc, Function.update_of_ne hp] + +/-- Four actual instructions perform an admitted original-matrix residue read. The +transpose is fixed code selection, while both coordinates and the pointer are runtime data. -/ +theorem exec_readResidue {w : ℕ} (transpose : Bool) (x : Data 128) + (hr : x.regs Reg.row < x.regs Reg.side) (hc : x.regs Reg.column < x.regs Reg.side) + (hw : Fits w (x.regs Reg.residueSource + x.regs Reg.side * x.regs Reg.side)) : + ∃ y, ExecWithin w (readResidue transpose) x y 4 ∧ y.mem = x.mem ∧ + y.regs Reg.residue = x.mem (x.regs Reg.residueSource + + if transpose then x.regs Reg.column * x.regs Reg.side + x.regs Reg.row + else x.regs Reg.row * x.regs Reg.side + x.regs Reg.column) ∧ + ∀ i, i ≠ Reg.address → i ≠ Reg.temporary → i ≠ Reg.residue → y.regs i = x.regs i := by + have hrow := Nat.mul_le_mul_right (x.regs Reg.side) (Nat.succ_le_of_lt hr) + have hcol := Nat.mul_le_mul_right (x.regs Reg.side) (Nat.succ_le_of_lt hc) + rw [Nat.succ_mul] at hrow hcol + have hpr : Fits w (x.regs Reg.row * x.regs Reg.side) := hw.of_le (by omega) + have hpc : Fits w (x.regs Reg.column * x.regs Reg.side) := hw.of_le (by omega) + have hir : Fits w (x.regs Reg.row * x.regs Reg.side + x.regs Reg.column) := + hw.of_le (by omega) + have hic : Fits w (x.regs Reg.column * x.regs Reg.side + x.regs Reg.row) := + hw.of_le (by omega) + have har : Fits w (x.regs Reg.residueSource + + (x.regs Reg.row * x.regs Reg.side + x.regs Reg.column)) := hw.of_le (by omega) + have hac : Fits w (x.regs Reg.residueSource + + (x.regs Reg.column * x.regs Reg.side + x.regs Reg.row)) := hw.of_le (by omega) + cases transpose with + | false => + refine ⟨?y, ?run, ?mem, ?residue, ?regs⟩ + case run => + ram_eval [readResidue, hpr, hir, har, Reg.row, Reg.column, + Reg.side, Reg.residueSource, Reg.address, Reg.temporary, Reg.residue] + case mem => rfl + case residue => rfl + case regs => + intro i ha ht hr + simp only [Data.set, Function.update_of_ne ha, Function.update_of_ne ht, + Function.update_of_ne hr] + | true => + refine ⟨?yTrue, ?runTrue, ?memTrue, ?residueTrue, ?regsTrue⟩ + case runTrue => + ram_eval [readResidue, hpc, hic, hac, Reg.row, Reg.column, + Reg.side, Reg.residueSource, Reg.address, Reg.temporary, Reg.residue] + case memTrue => rfl + case residueTrue => rfl + case regsTrue => + intro i ha ht hr + simp only [Data.set, Function.update_of_ne ha, Function.update_of_ne ht, + Function.update_of_ne hr] + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Bounds.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Bounds.lean new file mode 100644 index 0000000000..8da1009e63 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Bounds.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.Algorithms.WordRAM.ExactTriangle.CountPrime.Setup +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.State + +/-! +# Original-parameter native modular-counter budgets + +These numerical expressions account for actual native arithmetic, memory and recursion. +They contain no supplied execution, multiplication, count or running-time certificate. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +/-- The original entry/modulus bounds and the proved recursive signed magnitude envelope. -/ +@[expose] def magnitudeBudget (p k A : ℕ) : ℕ := A + p + 2 * (p + 4) * 16 ^ k + 1 + +/-- The workspace ends after the actual root/descendant frame stack. -/ +@[expose] def memoryEnd (n p k scratch : ℕ) : ℕ := + scratch + 3 * (n * n) + 2 ^ k + 4 * (4 ^ k * p) + 7 * (k + 1) + +/-- Three real residue calls, table/two matrix writers, actual Strassen and the count scan. -/ +@[expose] def readyCost (n p k : ℕ) : ℕ := + Strassen.traceBudget p k + (14 * p + 48) * 4 ^ k + 11 * (2 ^ k - 1) + + 91 * (n * n) + 4 * k + 81 + +/-- Complete caller execution, including its actual logarithm and layout setup, before halt. -/ +@[expose] def cost (n p : ℕ) : ℕ := readyCost n p (Nat.clog 2 n) + 9 * Nat.clog 2 n + 31 + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/CoefficientMatrices.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/CoefficientMatrices.lean new file mode 100644 index 0000000000..cdc15f25e9 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/CoefficientMatrices.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.ExactTriangle.CountPrime.Setup +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.MatrixReadback + +/-! +# Computed spread table and both real cyclic coefficient matrices + +The table is constructed once by actual code and reused by both charged padded writers. +The returned P/Q words agree with the existing mathematical specification, including the BC +transpose and every padding zero. Memory below the table, including all residue arrays and +original caller data, is preserved. The complete counter supplies residues through native code. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM Cslib.WordRAM.Signed + +private theorem spread_keeps_metadata (j : Fin 128) (hj : MetadataRegister j) : + spreadTable.Preserves j := by decide +kernel +revert + +private theorem matrix_keeps_metadata (transpose : Bool) (j : Fin 128) + (hj : MetadataRegister j) : + (matrix (if transpose then Reg.residueBC else Reg.residueAC) + (if transpose then Reg.matrixQ else Reg.matrixP) transpose).Preserves j := by + cases transpose <;> decide +kernel +revert + +/-- Build the actual table and both coefficient matrices from the three-matrix residue prefix. +No table, monomial array, padding layout or matrix-result certificate is an algorithm input. -/ +theorem exec_coefficientMatrices {w n p k scratch a b c : ℕ} (x : Data 128) + (wBC wAC : Fin n → Fin n → ℤ) (hp : 0 < p) + (ready : CounterRegisters n p k scratch a b c x) + (hBC : ∀ u v : Fin n, x.mem (x.regs Reg.residueBC + u.val * n + v.val) = + (wBC u v % (p : ℤ)).toNat) + (hAC : ∀ u v : Fin n, x.mem (x.regs Reg.residueAC + u.val * n + v.val) = + (wAC u v % (p : ℤ)).toNat) + (hw : Fits w (addressBudget n p k scratch a b c)) : + ∃ y, ExecWithin w coefficientMatrices x y + ((14 * p + 48) * 4 ^ k + 11 * (2 ^ k - 1) + 15) ∧ + CounterRegisters n p k scratch a b c y ∧ + (∀ j < 2 ^ k, y.mem (x.regs Reg.spread + j) = Nat.Morton.spread j) ∧ + (∀ i < 4 ^ k * p, y.mem (x.regs Reg.matrixP + i) = encode + (Cslib.Algorithms.ExactTriangle.CountPrime.pCoefficients p wAC i)) ∧ + (∀ i < 4 ^ k * p, y.mem (x.regs Reg.matrixQ + i) = encode + (Cslib.Algorithms.ExactTriangle.CountPrime.qCoefficients p wBC i)) ∧ + ∀ d, d < x.regs Reg.spread ∨ x.regs Reg.matrixR ≤ d → y.mem d = x.mem d := by + have h4 : 0 < 4 ^ k := by positivity + have h2 : 0 < 2 ^ k := by positivity + have harea : 0 < 4 ^ k * p := Nat.mul_pos h4 hp + have pair_lt (u v : Fin n) : u.val * n + v.val < n * n := by + have h := (finProdFinEquiv (u, v)).isLt + change v.val + n * u.val < n * n at h + simpa only [Nat.add_comm, Nat.mul_comm] using h + have hTP : x.regs Reg.spread + 2 ^ k = x.regs Reg.matrixP := by + rw [ready.layout.spread_eq, ready.layout.matrixP_eq] + have hPQ : x.regs Reg.matrixP + 4 ^ k * p = x.regs Reg.matrixQ := by + rw [ready.layout.matrixP_eq, ready.layout.matrixQ_eq] + have hQR : x.regs Reg.matrixQ + 4 ^ k * p = x.regs Reg.matrixR := by + rw [ready.layout.matrixQ_eq, ready.layout.matrixR_eq] + omega + have hBCbefore : x.regs Reg.residueBC + n * n ≤ x.regs Reg.spread := by + rw [ready.layout.residueBC_eq, ready.layout.spread_eq] + omega + have hACbefore : x.regs Reg.residueAC + n * n ≤ x.regs Reg.spread := by + rw [ready.layout.residueAC_eq, ready.layout.spread_eq] + omega + obtain ⟨s, tableRun, table, tableFrame, _⟩ := exec_spreadTable x ready.layout.paddedSide_eq + ready.zero ready.one ready.two ready.four (by + rw [ready.layout.spread_eq] + exact hw.of_le (by unfold addressBudget; omega)) + have keep1 (j : Fin 128) (hj : MetadataRegister j) : s.regs j = x.regs j := + tableRun.regs_eq (spread_keeps_metadata j hj) + have ready1 := ready.congr keep1 + obtain ⟨t, leftRun, left, leftFrame, _⟩ := exec_matrix_coefficients + Reg.residueAC Reg.matrixP false s wAC hp (by decide) + ready1.layout.side_eq ready1.layout.modulus_eq ready1.layout.paddedSide_eq + ready1.layout.paddedCells_eq (keep1 _ (by decide)) (keep1 _ (by decide)) + (keep1 _ (by decide)) ready1.zero ready1.one ready1.two + (by omega) (by omega) table + (by + intro u v + rw [tableFrame _ (Or.inl (by have := pair_lt u v; omega))] + exact hAC u v) + (by + rw [ready.layout.matrixP_eq, ready.layout.spread_eq, ready.layout.residueAC_eq] + exact hw.of_le (by unfold addressBudget; omega)) + have keep2 (j : Fin 128) (hj : MetadataRegister j) : t.regs j = x.regs j := + (leftRun.regs_eq (matrix_keeps_metadata false j hj)).trans (keep1 j hj) + have ready2 := ready.congr keep2 + obtain ⟨y, rightRun, right, rightFrame, _⟩ := exec_matrix_coefficients + Reg.residueBC Reg.matrixQ true t wBC hp (by decide) + ready2.layout.side_eq ready2.layout.modulus_eq ready2.layout.paddedSide_eq + ready2.layout.paddedCells_eq (keep2 _ (by decide)) (keep2 _ (by decide)) + (keep2 _ (by decide)) ready2.zero ready2.one ready2.two + (by omega) (by omega) + (by + intro j hj + rw [leftFrame _ (Or.inl (by omega))] + exact table j hj) + (by + intro u v + rw [leftFrame _ (Or.inl (by have := pair_lt u v; omega)), + tableFrame _ (Or.inl (by have := pair_lt u v; omega))] + exact hBC u v) + (by + rw [ready.layout.matrixQ_eq, ready.layout.spread_eq, ready.layout.residueBC_eq] + exact hw.of_le (by unfold addressBudget; omega)) + have keep3 (j : Fin 128) (hj : MetadataRegister j) : y.regs j = x.regs j := + (rightRun.regs_eq (matrix_keeps_metadata true j hj)).trans (keep2 j hj) + refine ⟨y, (tableRun.seq (leftRun.seq rightRun)).mono (by nlinarith), + ready.congr keep3, ?_, ?_, right, ?_⟩ + · intro j hj + rw [rightFrame _ (Or.inl (by omega)), leftFrame _ (Or.inl (by omega))] + exact table j hj + · intro i hi + rw [rightFrame _ (Or.inl (by omega))] + exact left i hi + · intro d hd + rw [rightFrame _ (hd.imp (fun h => by omega) (fun h => by omega)), + leftFrame _ (hd.imp (fun h => by omega) (fun h => by omega)), + tableFrame _ (hd.imp id (fun h => by omega))] + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Exec.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Exec.lean new file mode 100644 index 0000000000..9259e07933 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Exec.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.CountPrime.ExecReady + +/-! +# Complete native modular counter from original caller data + +The fixed 128-register program computes its logarithm, padding, layout, residues, table, +coefficient matrices, real seven-product recursion and selected count. No intermediate +layout, table, matrix, execution or time certificate is an argument. Original signed arrays +may alias each other below the dirty workspace. Its outside memory and persistent original +parameters are preserved. Bounds refer solely to the original scalars, pointers and weights. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Complete actual caller execution from ordinary runtime arguments and original signed +array words, with raw count, computed metadata, dirty-workspace frame and all native costs. +The final halt is excluded here and is charged by initialized flat clients. -/ +theorem exec {w n p scratch a b c A : ℕ} (x : Data 128) (u v z : ℕ → ℤ) + (hp0 : 0 < p) (hn : x.regs Reg.side = n) (hp : x.regs Reg.modulus = p) + (hAB : x.regs Reg.inputAB = a) (hBC : x.regs Reg.inputBC = b) + (hAC : x.regs Reg.inputAC = c) (hs : x.regs Reg.scratch = scratch) + (ha : a + n * n ≤ scratch) (hb : b + n * n ≤ scratch) (hc : c + n * n ≤ scratch) + (hu : ∀ i < n * n, (u i).natAbs ≤ A) + (hv : ∀ i < n * n, (v i).natAbs ≤ A) + (hz : ∀ i < n * n, (z i).natAbs ≤ A) + (hinU : ∀ i < n * n, x.mem (a + i) = encode (u i)) + (hinV : ∀ i < n * n, x.mem (b + i) = encode (v i)) + (hinZ : ∀ i < n * n, x.mem (c + i) = encode (z i)) + (hmag : GenericRAM.Signed.Fits w (magnitudeBudget p (Nat.clog 2 n) A : ℤ)) + (hw : Fits (w + 2) (addressBudget n p (Nat.clog 2 n) scratch a b c)) : + ∃ y, ExecWithin (w + 2) program x y (cost n p) ∧ + CounterRegisters n p (Nat.clog 2 n) scratch a b c y ∧ + y.regs Reg.result = Cslib.Algorithms.ExactTriangle.countZeroMod + (fun α β : Fin n => u (α.val * n + β.val)) + (fun α β : Fin n => v (α.val * n + β.val)) + (fun α β : Fin n => z (α.val * n + β.val)) p ∧ + ∀ d, d < scratch ∨ memoryEnd n p (Nat.clog 2 n) scratch ≤ d → y.mem d = x.mem d := by + let k := Nat.clog 2 n + have h2 : 0 < 2 ^ k := by positivity + have h4 : 0 < 4 ^ k := by positivity + have harea : 0 < 4 ^ k * p := Nat.mul_pos h4 hp0 + obtain ⟨x0, setupRun, ready, memory⟩ := exec_setup x hp0 hn hp hAB hBC hAC hs + (hw.of_le (by unfold addressBudget layoutWordBound; dsimp only [k] at *; omega)) + obtain ⟨y, tailRun, finalRegisters, result, frame⟩ := exec_ready x0 u v z hp0 + (Nat.le_pow_clog (by decide : 1 < (2 : ℕ)) n) ready ha hb hc hu hv hz + (by simpa only [memory] using hinU) (by simpa only [memory] using hinV) + (by simpa only [memory] using hinZ) hmag hw + refine ⟨y, (setupRun.seq tailRun).mono (by unfold cost; omega), finalRegisters, result, ?_⟩ + intro d hd + exact (frame d hd).trans (congrFun memory d) + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/ExecReady.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/ExecReady.lean new file mode 100644 index 0000000000..306e496a96 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/ExecReady.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.ExactTriangle.CountPrime.Bounds +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.ResidueArrays +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.CoefficientMatrices +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Solve +public import Complexity.Computability.GenericRAM.Signed.Fits + +/-! +# Complete actual counter tail from original signed arrays + +Only the register metadata has been established by setup. Every residue, table, coefficient +matrix and product is constructed by real code here. The final scan returns the existing +modular count. The original arrays and all caller memory outside the explicit workspace are +preserved, including when the workspace starts dirty. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Compose the actual residue/matrix/seven-product/extraction pipeline. This internal theorem +consumes setup's computed register meaning, not any intermediate table, matrix or run oracle. -/ +theorem exec_ready {w n p k scratch a b c A : ℕ} (x : Data 128) + (u v z : ℕ → ℤ) (hp : 0 < p) (hn : n ≤ 2 ^ k) + (ready : CounterRegisters n p k scratch a b c x) + (ha : a + n * n ≤ scratch) (hb : b + n * n ≤ scratch) (hc : c + n * n ≤ scratch) + (hu : ∀ i < n * n, (u i).natAbs ≤ A) + (hv : ∀ i < n * n, (v i).natAbs ≤ A) + (hz : ∀ i < n * n, (z i).natAbs ≤ A) + (hinU : ∀ i < n * n, x.mem (a + i) = encode (u i)) + (hinV : ∀ i < n * n, x.mem (b + i) = encode (v i)) + (hinZ : ∀ i < n * n, x.mem (c + i) = encode (z i)) + (hmag : GenericRAM.Signed.Fits w (magnitudeBudget p k A : ℤ)) + (hw : Fits (w + 2) (addressBudget n p k scratch a b c)) : + ∃ y, ExecWithin (w + 2) + (.seq residueArrays (.seq coefficientMatrices (.seq product count))) x y + (readyCost n p k) ∧ + CounterRegisters n p k scratch a b c y ∧ + y.regs Reg.result = Cslib.Algorithms.ExactTriangle.countZeroMod + (fun α β : Fin n => u (α.val * n + β.val)) + (fun α β : Fin n => v (α.val * n + β.val)) + (fun α β : Fin n => z (α.val * n + β.val)) p ∧ + ∀ d, d < scratch ∨ memoryEnd n p k scratch ≤ d → y.mem d = x.mem d := by + have h16 : 0 < 16 ^ k := by positivity + have h4 : 0 < 4 ^ k := by positivity + have h2 : 0 < 2 ^ k := by positivity + have harea : 0 < 4 ^ k * p := Nat.mul_pos h4 hp + have hA : A ≤ magnitudeBudget p k A := by unfold magnitudeBudget; omega + have hP : p ≤ magnitudeBudget p k A := by unfold magnitudeBudget; omega + have hM : 2 * (p + 4) * 16 ^ k ≤ magnitudeBudget p k A := by + unfold magnitudeBudget + omega + have modFit : GenericRAM.Signed.Fits w (p : ℤ) := + hmag.of_natAbs_le (by simpa only [Int.natAbs_natCast] using hP) + obtain ⟨x1, residueRun, ready1, resAB, resBC, resAC, residueFrame⟩ := exec_residueArrays x + u v z hp ready ha hb hc modFit + (fun i hi => hmag.of_natAbs_le ((hu i hi).trans hA)) + (fun i hi => hmag.of_natAbs_le ((hv i hi).trans hA)) + (fun i hi => hmag.of_natAbs_le ((hz i hi).trans hA)) hinU hinV hinZ hw + let wAB := fun α β : Fin n => u (α.val * n + β.val) + let wBC := fun α β : Fin n => v (α.val * n + β.val) + let wAC := fun α β : Fin n => z (α.val * n + β.val) + have pair_lt (α β : Fin n) : α.val * n + β.val < n * n := by + have h := (finProdFinEquiv (α, β)).isLt + change β.val + n * α.val < n * n at h + simpa only [Nat.add_comm, Nat.mul_comm] using h + obtain ⟨x2, matrixRun, ready2, table, left, right, matrixFrame⟩ := + exec_coefficientMatrices x1 wBC wAC hp ready1 + (by + intro α β + simpa only [ready1.layout.residueBC_eq, Nat.add_assoc, wBC] using + resBC _ (pair_lt α β)) + (by + intro α β + simpa only [ready1.layout.residueAC_eq, Nat.add_assoc, wAC] using + resAC _ (pair_lt α β)) hw + obtain ⟨y, solveRun, ready3, result, solveFrame⟩ := exec_product_count x2 + wAB wBC wAC hp hn ready2 + (by + intro α β + rw [ready2.layout.residueAB_eq] + rw [matrixFrame _ (Or.inl (by + rw [ready1.layout.spread_eq] + have := pair_lt α β + omega))] + simpa only [Nat.add_assoc, wAB] using resAB _ (pair_lt α β)) + (by simpa only [ready2.layout.spread_eq, ready1.layout.spread_eq] using table) + (by simpa only [ready2.layout.matrixP_eq, ready1.layout.matrixP_eq] using left) + (by simpa only [ready2.layout.matrixQ_eq, ready1.layout.matrixQ_eq] using right) + (hmag.of_natAbs_le (by simpa only [Int.natAbs_natCast] using hM)) hw + refine ⟨y, (residueRun.seq (matrixRun.seq solveRun)).mono (by + unfold readyCost + nlinarith), ready3, result, ?_⟩ + intro d hd + have hs : d < x2.regs Reg.matrixR ∨ + x2.regs Reg.productScratch + 4 ^ k * p + 7 * (k + 1) ≤ d := by + rw [ready2.layout.matrixR_eq, ready2.layout.productScratch_eq] + unfold memoryEnd at hd + rcases hd with hd | hd + · exact Or.inl (by omega) + · exact Or.inr (by omega) + have hm : d < x1.regs Reg.spread ∨ x1.regs Reg.matrixR ≤ d := by + rw [ready1.layout.spread_eq, ready1.layout.matrixR_eq] + unfold memoryEnd at hd + rcases hd with hd | hd + · exact Or.inl (by omega) + · exact Or.inr (by omega) + rw [solveFrame d hs, matrixFrame d hm] + exact residueFrame d (hd.imp id (by intro h; unfold memoryEnd at h; omega)) + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Flat.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Flat.lean new file mode 100644 index 0000000000..74758cec8f --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Flat.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.CountPrime.Width +public import Complexity.Computability.WordRAM.Width +import Mathlib.Data.List.GetD + +/-! +# Actual initialized halting modular counter and every-wide-width prefixes + +The fixed standard-native program starts from the original header and signed matrix words. +Its prefix witness, raw count and charged halt refer to the same actual final memory. Every +wider admitted prefix agrees with a minimum-admission execution; the resulting word/support +bounds depend only on original inputs and not on the chosen physical width. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM + +/-- Complete initialized flat execution, exact raw count and all setup/recursion/halt charges. -/ +theorem runs_input {W n p A : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hp : 0 < p) + (hv : ∀ z ∈ values, z.natAbs ≤ A) (hW : admissionWidth n p A ≤ W) : + InputFits W (inputWords n p values) ∧ + ∃ y t, t + 1 ≤ cost n p + 10 ∧ + Runs W (entryProgram.program Reg.result) (State.init (inputWords n p values)) + ⟨entryProgram.size, y⟩ t ∧ + Executes W (entryProgram.program Reg.result) (State.init (inputWords n p values)) + (y.regs Reg.result) (t + 1) ∧ + y.regs Reg.result = Cslib.Algorithms.ExactTriangle.countZeroMod + (fun α β : Fin n => values.getD (α.val * n + β.val) 0) + (fun α β : Fin n => values.getD (n * n + α.val * n + β.val) 0) + (fun α β : Fin n => values.getD (2 * (n * n) + α.val * n + β.val) 0) p ∧ + y.Bounded W ∧ + ∀ d, d < 3 + 3 * (n * n) ∨ memoryEnd n p (Nat.clog 2 n) (3 + 3 * (n * n)) ≤ d → + y.mem d = (Data.init (inputWords n p values) : Data 128).mem d := by + obtain ⟨hi, y, he, result, bounded, frame⟩ := exec_input values hlen hp hv hW + obtain ⟨t, ht, run⟩ := he + refine ⟨hi, y, t, by omega, ?_, ?_, result, bounded, frame⟩ + · simpa only [State.init, Nat.zero_add] using run.runs (pc := 0) + (p := entryProgram.program Reg.result) (Program.Contains.self _).append_left + · simpa only [State.init] using run.executes Reg.result + +private theorem pow_width_lt (n p A : ℕ) : + 2 ^ admissionWidth n p A < wordBound n p A := by + let E := envelope n p A + let k := Nat.clog 2 E + have hB : 0 < addressBudget n p (Nat.clog 2 n) (3 + 3 * (n * n)) 3 (3 + n * n) + (3 + 2 * (n * n)) := by unfold addressBudget; positivity + have hE : 1 < E := by dsimp only [E, envelope]; omega + have hkpos : 0 < k := Nat.clog_pos (by decide) hE + have hk : k.pred + 1 = k := Nat.succ_pred_eq_of_pos hkpos + have hpred : 2 ^ k.pred < E := Nat.pow_pred_clog_lt_self (by decide) hE + change 2 ^ (k + 3) < 16 * E + calc + _ = 2 ^ k * 8 := by rw [Nat.pow_add] + _ = 2 ^ (k.pred + 1) * 8 := congrArg (fun j => 2 ^ j * 8) hk.symm + _ = (2 ^ k.pred * 2) * 8 := by rw [Nat.pow_succ] + _ < 16 * E := by omega + +/-- Every successful prefix has original-input bounded words and zero memory beyond the same +width-independent support envelope. The premise is an actual program prefix, not a supplied +execution/correctness certificate used by the algorithm. -/ +theorem bounded_reachable {W n p A : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hp : 0 < p) + (hv : ∀ z ∈ values, z.natAbs ≤ A) (hW : admissionWidth n p A ≤ W) + (s : State 128) (steps : ℕ) + (hr : Runs W (entryProgram.program Reg.result) (State.init (inputWords n p values)) s steps) : + (∀ i, s.data.regs i < wordBound n p A) ∧ + (∀ a, s.data.mem a < wordBound n p A) ∧ + ∀ a, wordBound n p A ≤ a → s.data.mem a = 0 := by + obtain ⟨hi, _, _, _, _, halt, _⟩ := runs_input values hlen hp hv le_rfl + have low := Runs.prefix_at_smaller_width program_fixed.2 hW hr halt + have bounded := low.bounded_init hi + have hword := pow_width_lt n p A + refine ⟨fun i => (bounded.1 i).trans hword, fun a => (bounded.2 a).trans hword, ?_⟩ + intro a ha + have above : 2 ^ admissionWidth n p A ≤ a := hword.le.trans ha + rw [low.mem_above (.init hi) above] + cases a with + | zero => have := Nat.two_pow_pos (admissionWidth n p A); omega + | succ a => + change (inputWords n p values).getD a 0 = 0 + apply List.getD_eq_default + have hl := hi.1 + dsimp only [Fits] at hl + omega + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Input.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Input.lean new file mode 100644 index 0000000000..444046f8b3 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Input.lean @@ -0,0 +1,130 @@ +/- +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.CountPrime.Exec +import Mathlib.Data.List.GetD + +/-! +# Initialized native modular counting + +The ordinary input is raw `(n,p)` followed by three canonical signed, row-major matrices. +The real nine-instruction header computes every original pointer and workspace start. The +complete counter then constructs and counts its actual cyclic product. No table, padding +parameter or intermediate fit certificate occurs in the input. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Raw `(n,p)` followed by canonical signed AB, BC and AC payloads in row-major order. -/ +@[expose] def inputWords (n p : ℕ) (values : List ℤ) : List ℕ := + [n, p] ++ values.map encode + +/-- The exact original payload word, including the common zero default beyond the input. -/ +theorem input_word (n p : ℕ) (values : List ℤ) (q : ℕ) : + (Data.init (inputWords n p values) : Data 128).mem (3 + q) = encode (values.getD q 0) := by + rw [show 3 + q = (2 + q) + 1 by omega, Data.init_mem_succ] + rw [inputWords, List.getD_append_right _ _ _ _ (by simp)] + simp only [List.length_cons, List.length_nil, Nat.reduceAdd, Nat.add_sub_cancel_left] + exact List.getD_map values 0 encode + +/-- The actual header reads only the two original scalar words and computes all array bases. -/ +theorem exec_header {W n p : ℕ} (x : Data 128) (hn : x.mem 1 = n) (hp : x.mem 2 = p) + (hw : Fits W (3 + 3 * (n * n) + 8)) : + ∃ y, ExecWithin W input x y 9 ∧ y.mem = x.mem ∧ + y.regs Reg.side = n ∧ y.regs Reg.modulus = p ∧ + y.regs Reg.inputAB = 3 ∧ y.regs Reg.inputBC = 3 + n * n ∧ + y.regs Reg.inputAC = 3 + 2 * (n * n) ∧ y.regs Reg.scratch = 3 + 3 * (n * n) := by + let z := (((((((((x.set Reg.address 1).set Reg.side n).set Reg.address 2).set + Reg.modulus p).set Reg.cells (n * n)).set Reg.inputAB 3).set + Reg.inputBC (3 + n * n)).set Reg.inputAC (3 + n * n + n * n)).set + Reg.scratch (3 + n * n + n * n + n * n)) + have run : ExecWithin W input x z 9 := by + ram_eval [input, z, hn, hp, hw.of_le (by omega : 1 ≤ _), + hw.of_le (by omega : 2 ≤ _), hw.of_le (by omega : 3 ≤ _), + hw.of_le (by omega : n * n ≤ _), hw.of_le (by omega : 3 + n * n ≤ _), + hw.of_le (by omega : 3 + n * n + n * n ≤ _), + hw.of_le (by omega : 3 + n * n + n * n + n * n ≤ _), + Reg.address, Reg.side, Reg.modulus, Reg.cells, Reg.inputAB, Reg.inputBC, + Reg.inputAC, Reg.scratch] + refine ⟨z, run, rfl, ?_, ?_, ?_, ?_, ?_, ?_⟩ + all_goals simp +decide [z, Reg.address, Reg.side, Reg.modulus, Reg.cells, + Reg.inputAB, Reg.inputBC, Reg.inputAC, Reg.scratch, Nat.succ_mul, Nat.add_assoc] + +/-- Initialized actual structured execution with original-parameter numerical admission. +Setup, all real computation and the input header are charged; the final halt is excluded. -/ +theorem exec_input_at {w n p A : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hp : 0 < p) + (hv : ∀ z ∈ values, z.natAbs ≤ A) + (hmag : GenericRAM.Signed.Fits w (magnitudeBudget p (Nat.clog 2 n) A : ℤ)) + (hw : Fits (w + 2) (addressBudget n p (Nat.clog 2 n) (3 + 3 * (n * n)) + 3 (3 + n * n) (3 + 2 * (n * n)))) : + InputFits (w + 2) (inputWords n p values) ∧ + ∃ y, ExecWithin (w + 2) entryProgram (.init (inputWords n p values)) y (cost n p + 9) ∧ + y.regs Reg.result = Cslib.Algorithms.ExactTriangle.countZeroMod + (fun α β : Fin n => values.getD (α.val * n + β.val) 0) + (fun α β : Fin n => values.getD (n * n + α.val * n + β.val) 0) + (fun α β : Fin n => values.getD (2 * (n * n) + α.val * n + β.val) 0) p ∧ + y.Bounded (w + 2) ∧ + ∀ d, d < 3 + 3 * (n * n) ∨ memoryEnd n p (Nat.clog 2 n) (3 + 3 * (n * n)) ≤ d → + y.mem d = (Data.init (inputWords n p values) : Data 128).mem d := by + let k := Nat.clog 2 n + have h4 : 0 < 4 ^ k := by positivity + have h2 : 0 < 2 ^ k := by positivity + have harea : 0 < 4 ^ k * p := Nat.mul_pos h4 hp + have hA : A ≤ magnitudeBudget p k A := by unfold magnitudeBudget; omega + have original (q : ℕ) (hq : q < 3 * (n * n)) : (values.getD q 0).natAbs ≤ A := by + have hq' : q < values.length := by omega + have hm : values.getD q 0 ∈ values := by + rw [List.getD_eq_getElem _ _ hq'] + exact List.getElem_mem hq' + exact hv _ hm + have hi : InputFits (w + 2) (inputWords n p values) := by + refine ⟨?_, ?_⟩ + · simpa only [inputWords, List.length_append, List.length_cons, List.length_nil, + List.length_map, hlen, Nat.reduceAdd] using + hw.of_le (by unfold addressBudget; dsimp only [k] at *; omega : + 2 + 3 * (n * n) ≤ _) + · intro v hm + rcases List.mem_append.mp hm with hm | hm + · simp only [List.mem_cons, List.not_mem_nil, or_false] at hm + rcases hm with rfl | rfl + · exact hw.of_le (by unfold addressBudget; dsimp only [k] at *; omega) + · exact hw.of_le (by unfold addressBudget; dsimp only [k] at *; omega) + · obtain ⟨z, hz, rfl⟩ := List.mem_map.mp hm + exact (encode_fits (hmag.of_natAbs_le ((hv z hz).trans hA))).mono (by omega) + obtain ⟨x0, headerRun, memory, hn, hpReg, hAB, hBC, hAC, hs⟩ := exec_header (n := n) (p := p) + (.init (inputWords n p values)) (by rfl) (by rfl) + (hw.of_le (by unfold addressBudget; dsimp only [k] at *; omega)) + obtain ⟨y, counterRun, _, result, frame⟩ := exec x0 + (fun i => values.getD i 0) (fun i => values.getD (n * n + i) 0) + (fun i => values.getD (2 * (n * n) + i) 0) + hp hn hpReg hAB hBC hAC hs (by omega) (by omega) (by omega) + (fun i hi => original i (by omega)) (fun i hi => original (n * n + i) (by omega)) + (fun i hi => original (2 * (n * n) + i) (by omega)) + (by intro i hi; rw [memory]; exact input_word n p values i) + (by + intro i hi + rw [memory] + simpa only [Nat.add_assoc] using input_word n p values (n * n + i)) + (by + intro i hi + rw [memory] + simpa only [Nat.add_assoc] using input_word n p values (2 * (n * n) + i)) hmag hw + have full : ExecWithin (w + 2) entryProgram (.init (inputWords n p values)) y + (cost n p + 9) := (headerRun.seq counterRun).mono (by omega) + refine ⟨hi, y, full, ?_, full.bounded (.init hi), ?_⟩ + · simpa only [Nat.add_assoc] using result + · intro d hd + exact (frame d hd).trans (congrFun memory d) + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Layout.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Layout.lean new file mode 100644 index 0000000000..7d6925c40e --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Layout.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.ExactTriangle.CountPrime.Program + +/-! +# Actual native CountPrime workspace calculation + +The existing native power routine computes the padded side from the computed depth. +Ordinary arithmetic instructions then calculate every matrix/residue/spread/workspace +pointer. The register assertions below describe the actual resulting state; no layout +object is supplied to the program. All intermediate fits follow from one original-parameter +numerical envelope. This module performs no allocation, clearing or memory copy. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM + +/-- Original-parameter physical envelope for real layout arithmetic and power evaluation. -/ +@[expose] def layoutWordBound (n p k scratch : ℕ) : ℕ := + scratch + 3 * (n * n) + 2 ^ k + 4 * (4 ^ k * p) + k + 2 * p + 16 + +/-- The actual metadata words computed by the native layout routine. This internal +postcondition contains no execution, time or multiplication contract. -/ +structure LayoutRegisters (n p k scratch : ℕ) (x : Data 128) : Prop where + /-- Original side survives the power call and all pointer arithmetic. -/ + side_eq : x.regs Reg.side = n + /-- Original modulus survives. -/ + modulus_eq : x.regs Reg.modulus = p + /-- Computed depth survives. -/ + depth_eq : x.regs Reg.depth = k + /-- Original workspace base survives. -/ + scratch_eq : x.regs Reg.scratch = scratch + /-- Actual native power result. -/ + paddedSide_eq : x.regs Reg.paddedSide = 2 ^ k + /-- Original matrix length. -/ + cells_eq : x.regs Reg.cells = n * n + /-- Padded cyclic-coefficient matrix length. -/ + area_eq : x.regs Reg.area = 4 ^ k * p + /-- Padded scalar-matrix length. -/ + paddedCells_eq : x.regs Reg.paddedCells = 4 ^ k + /-- Canonical signed positive modulus word. -/ + encodedModulus_eq : x.regs Reg.encodedModulus = 2 * p + /-- First actual residue block. -/ + residueAB_eq : x.regs Reg.residueAB = scratch + /-- Second actual residue block. -/ + residueBC_eq : x.regs Reg.residueBC = scratch + n * n + /-- Third actual residue block. -/ + residueAC_eq : x.regs Reg.residueAC = scratch + 2 * (n * n) + /-- Actual spread table. -/ + spread_eq : x.regs Reg.spread = scratch + 3 * (n * n) + /-- Actual first coefficient matrix. -/ + matrixP_eq : x.regs Reg.matrixP = scratch + 3 * (n * n) + 2 ^ k + /-- Actual second coefficient matrix. -/ + matrixQ_eq : x.regs Reg.matrixQ = scratch + 3 * (n * n) + 2 ^ k + 4 ^ k * p + /-- Actual product coefficient matrix. -/ + matrixR_eq : x.regs Reg.matrixR = scratch + 3 * (n * n) + 2 ^ k + 2 * (4 ^ k * p) + /-- Reusable native Strassen scratch begins after all three coefficient matrices. -/ + productScratch_eq : x.regs Reg.productScratch = + scratch + 3 * (n * n) + 2 ^ k + 3 * (4 ^ k * p) + +/-- Registers actually written by layout, including the complete low power bank. -/ +@[expose] def LayoutLocal (i : Fin 128) : Prop := i.val < 5 ∨ i = Reg.paddedSide ∨ + i = Reg.cells ∨ i = Reg.paddedCells ∨ i = Reg.area ∨ i = Reg.encodedModulus ∨ + i = Reg.residueAB ∨ i = Reg.residueBC ∨ i = Reg.residueAC ∨ i = Reg.spread ∨ + i = Reg.matrixP ∨ i = Reg.matrixQ ∨ i = Reg.matrixR ∨ i = Reg.productScratch + +instance (i : Fin 128) : Decidable (LayoutLocal i) := + inferInstanceAs (Decidable (i.val < 5 ∨ i = Reg.paddedSide ∨ + i = Reg.cells ∨ i = Reg.paddedCells ∨ i = Reg.area ∨ i = Reg.encodedModulus ∨ + i = Reg.residueAB ∨ i = Reg.residueBC ∨ i = Reg.residueAC ∨ i = Reg.spread ∨ + i = Reg.matrixP ∨ i = Reg.matrixQ ∨ i = Reg.matrixR ∨ i = Reg.productScratch)) + +/-- Compute every layout word through actual power/arithmetic instructions. All memory and +original argument registers survive; every intermediate fit follows from original n,p,k,scratch. -/ +theorem exec_layout {w n p k scratch : ℕ} (x : Data 128) + (hp : 0 < p) (hn : x.regs Reg.side = n) (hpr : x.regs Reg.modulus = p) + (hk : x.regs Reg.depth = k) (hs : x.regs Reg.scratch = scratch) + (h2 : x.regs Reg.two = 2) (hw : Fits w (layoutWordBound n p k scratch)) : + ∃ y, ExecWithin w layout x y (4 * k + 20) ∧ LayoutRegisters n p k scratch y ∧ + y.mem = x.mem ∧ ∀ i, ¬LayoutLocal i → y.regs i = x.regs i := by + let N := 2 ^ k + let area := 4 ^ k * p + let z := (x.set Reg.low2 2).set Reg.low3 k + have hargs : ExecWithin (B := .standard) w + (ram[ Reg.low2 ← Reg.two; Reg.low3 ← Reg.depth ]) x z 2 := by + ram_eval [z, h2, hk, Reg.low2, Reg.low3, Reg.two, Reg.depth] + let f : Fin 5 → Fin 128 := Fin.castLE (by decide) + obtain ⟨s, he, value, _, _, memory, _, _⟩ := Power.exec 2 k (by decide) (z.comap f) + (by simp +decide [z, f, Data.comap, Power.base, Reg.low2, Reg.low3]) + (by simp [z, f, Data.comap, Power.exponent, Reg.low3]) + (hw.of_le (by unfold layoutWordBound; omega)) + let t := s.extend f z + have hcall : ExecWithin w (Power.program.rename f) z t (4 * k + 5) := + he.rename (Fin.castLE_injective _) + have tvalue : t.regs Reg.low0 = N := by + change Function.extend f s.regs z.regs (f Power.value) = N + rw [(Fin.castLE_injective _).extend_apply] + exact value + have thigh (i : Fin 128) (hi : 5 ≤ i.val) : t.regs i = x.regs i := by + have ht : t.regs i = z.regs i := by + apply Function.extend_apply' + rintro ⟨j, hj⟩ + have hv := congrArg Fin.val hj + change j.val = i.val at hv + omega + simpa only [z, Data.set, + Function.update_of_ne (show i ≠ Reg.low2 by intro he; subst i; contradiction), + Function.update_of_ne (show i ≠ Reg.low3 by intro he; subst i; contradiction)] using ht + have tn := (thigh Reg.side (by decide)).trans hn + have tp := (thigh Reg.modulus (by decide)).trans hpr + have ts := (thigh Reg.scratch (by decide)).trans hs + have tt := (thigh Reg.two (by decide)).trans h2 + have hsquare : N * N = 4 ^ k := by dsimp only [N]; rw [← mul_pow]; rfl + have harea : 4 ^ k ≤ area := by + have hm := Nat.mul_le_mul_left (4 ^ k) (Nat.succ_le_of_lt hp) + simpa only [Nat.mul_one] using hm + have fit (v : ℕ) (hv : v ≤ layoutWordBound n p k scratch) : Fits w v := hw.of_le hv + refine ⟨?y, ?run, ?fields, ?memory, ?regs⟩ + case run => + have tailRun : ExecWithin w (ram[ + Reg.paddedSide ← Reg.low0; Reg.cells ← Reg.side * Reg.side; + Reg.paddedCells ← Reg.paddedSide * Reg.paddedSide; + Reg.area ← Reg.paddedCells * Reg.modulus; Reg.encodedModulus ← Reg.modulus * Reg.two; + Reg.residueAB ← Reg.scratch; Reg.residueBC ← Reg.residueAB + Reg.cells; + Reg.residueAC ← Reg.residueBC + Reg.cells; Reg.spread ← Reg.residueAC + Reg.cells; + Reg.matrixP ← Reg.spread + Reg.paddedSide; Reg.matrixQ ← Reg.matrixP + Reg.area; + Reg.matrixR ← Reg.matrixQ + Reg.area; + Reg.productScratch ← Reg.matrixR + Reg.area ]) t ?y 13 := by + ram_eval [area, tvalue, tn, tp, ts, tt, hsquare, + fit (n * n) (by unfold layoutWordBound; omega), + fit (4 ^ k) (by unfold layoutWordBound; omega), + fit area (by unfold layoutWordBound; dsimp [area]; omega), + fit (p * 2) (by unfold layoutWordBound; omega), + fit (scratch + n * n) (by unfold layoutWordBound; omega), + fit (scratch + n * n + n * n) (by unfold layoutWordBound; omega), + fit (scratch + n * n + n * n + n * n) (by unfold layoutWordBound; omega), + fit (scratch + n * n + n * n + n * n + N) (by unfold layoutWordBound; omega), + fit (scratch + n * n + n * n + n * n + N + area) + (by unfold layoutWordBound; dsimp [area]; omega), + fit (scratch + n * n + n * n + n * n + N + area + area) + (by unfold layoutWordBound; dsimp [area]; omega), + fit (scratch + n * n + n * n + n * n + N + area + area + area) + (by unfold layoutWordBound; dsimp [area]; omega), + Reg.paddedSide, Reg.low0, Reg.cells, Reg.side, Reg.paddedCells, Reg.area, + Reg.modulus, Reg.encodedModulus, Reg.two, Reg.residueAB, Reg.scratch, + Reg.residueBC, Reg.residueAC, Reg.spread, Reg.matrixP, Reg.matrixQ, Reg.matrixR, + Reg.productScratch] + obtain ⟨nSteps, hnSteps, argsRun⟩ := hargs + obtain ⟨mSteps, hmSteps, restRun⟩ := hcall.seq tailRun + cases argsRun with + | seq h₁ h₂ => exact ⟨_, by omega, .seq h₁ (.seq h₂ restRun)⟩ + case fields => + constructor <;> simp +decide [Data.set, thigh, hn, hpr, hk, hs, N, + Reg.side, Reg.modulus, Reg.depth, Reg.scratch, Reg.paddedSide, Reg.cells, + Reg.paddedCells, Reg.area, Reg.encodedModulus, Reg.residueAB, Reg.residueBC, + Reg.residueAC, Reg.spread, Reg.matrixP, Reg.matrixQ, Reg.matrixR, Reg.productScratch, + Nat.add_assoc, Nat.mul_two, two_mul, show 3 * (n * n) = n * n + n * n + n * n by omega, + show 3 * (4 ^ k * p) = 4 ^ k * p + 4 ^ k * p + 4 ^ k * p by omega] + case memory => exact memory + case regs => + intro i hi + have hn (j : Fin 128) (hj : LayoutLocal j) : i ≠ j := by + intro he + subst i + exact hi hj + have hval : 5 ≤ i.val := by + have hh : ¬i.val < 5 := fun h => hi (Or.inl h) + omega + simp only [Data.set, + Function.update_of_ne (hn Reg.paddedSide (by decide)), + Function.update_of_ne (hn Reg.cells (by decide)), + Function.update_of_ne (hn Reg.paddedCells (by decide)), + Function.update_of_ne (hn Reg.area (by decide)), + Function.update_of_ne (hn Reg.encodedModulus (by decide)), + Function.update_of_ne (hn Reg.residueAB (by decide)), + Function.update_of_ne (hn Reg.residueBC (by decide)), + Function.update_of_ne (hn Reg.residueAC (by decide)), + Function.update_of_ne (hn Reg.spread (by decide)), + Function.update_of_ne (hn Reg.matrixP (by decide)), + Function.update_of_ne (hn Reg.matrixQ (by decide)), + Function.update_of_ne (hn Reg.matrixR (by decide)), + Function.update_of_ne (hn Reg.productScratch (by decide))] + exact thigh i hval + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Logarithm.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Logarithm.lean new file mode 100644 index 0000000000..c2dfb0b453 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Logarithm.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.CountPrime.Program + +/-! +# Computed native CountPrime depth + +The actual runtime original side is copied into the existing native ceiling-logarithm +routine. Four constants, the call, and copying its result into the counter metadata are +charged. No depth/logarithm certificate or precomputed padding parameter is supplied. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM + +/-- Compute the real ceiling binary logarithm and establish the four native constants. +All original counter argument registers and memory are preserved. -/ +theorem exec_logarithm {w : ℕ} (x : Data 128) + (hw : Fits w (x.regs Reg.side + Nat.clog 2 (x.regs Reg.side) + 8)) : + ∃ y, ExecWithin w logarithm x y (5 * Nat.clog 2 (x.regs Reg.side) + 11) ∧ + y.regs Reg.depth = Nat.clog 2 (x.regs Reg.side) ∧ + y.regs Reg.zero = 0 ∧ y.regs Reg.one = 1 ∧ y.regs Reg.two = 2 ∧ y.regs Reg.four = 4 ∧ + y.mem = x.mem ∧ + ∀ i, 4 ≤ i.val → i ≠ Reg.depth → i ≠ Reg.zero → i ≠ Reg.one → + i ≠ Reg.two → i ≠ Reg.four → y.regs i = x.regs i := by + let K := Nat.clog 2 (x.regs Reg.side) + let z := ((((x.set Reg.zero 0).set Reg.one 1).set Reg.two 2).set Reg.four 4).set + Reg.low0 (x.regs Reg.side) + have hargs : ExecWithin (B := .standard) w + (ram[ Reg.zero ← #0; Reg.one ← #1; Reg.two ← #2; Reg.four ← #4; + Reg.low0 ← Reg.side ]) x z 5 := by + ram_eval [z, hw.of_le (show 0 ≤ _ by omega), hw.of_le (show 1 ≤ _ by omega), + hw.of_le (show 2 ≤ _ by omega), hw.of_le (show 4 ≤ _ by omega), + Reg.zero, Reg.one, Reg.two, Reg.four, Reg.low0, Reg.side] + let f : Fin 4 → Fin 128 := Fin.castLE (by decide) + have harg : (z.comap f).regs NatClog.arg = x.regs Reg.side := by + simp [z, f, Data.comap, NatClog.arg, Reg.low0] + obtain ⟨s, hs, result, memory, _, _⟩ := NatClog.exec (z.comap f) + (hw.of_le (by omega)) + (by rw [harg]; exact hw.of_le (by omega)) + (by rw [harg]; exact hw.of_le (by omega)) + let t := s.extend f z + have hcall : ExecWithin w (NatClog.program.rename f) z t (5 * K + 5) := by + simpa only [harg] using hs.rename (Fin.castLE_injective _) + have tresult : t.regs Reg.low1 = K := by + change Function.extend f s.regs z.regs (f NatClog.result) = K + rw [(Fin.castLE_injective _).extend_apply] + simpa only [harg] using result + have tm : t.mem = x.mem := memory + have thigh (i : Fin 128) (hi : 4 ≤ i.val) : t.regs i = z.regs i := by + apply Function.extend_apply' + rintro ⟨j, hj⟩ + have hv := congrArg Fin.val hj + change j.val = i.val at hv + omega + let y := t.set Reg.depth K + have hlast : ExecWithin (B := .standard) w (ram[ Reg.depth ← Reg.low1 ]) t y 1 := by + simpa only [y, tresult] using (ExecWithin.copy (B := .standard) (w := w) + (d := Reg.depth) (s := Reg.low1) (x := t)) + have he : ExecWithin w logarithm x y (5 * K + 11) := by + obtain ⟨n, hn, argsRun⟩ := hargs + obtain ⟨m, hm, restRun⟩ := hcall.seq hlast + cases argsRun with + | seq h₁ rest₁ => cases rest₁ with + | seq h₂ rest₂ => cases rest₂ with + | seq h₃ rest₃ => cases rest₃ with + | seq h₄ h₅ => + exact ⟨_, by omega, .seq h₁ (.seq h₂ (.seq h₃ (.seq h₄ (.seq h₅ restRun))))⟩ + refine ⟨y, he, ?_, ?_, ?_, ?_, ?_, tm, ?_⟩ + · rfl + · simp only [y, Data.set, Function.update_of_ne (show Reg.zero ≠ Reg.depth by decide), + thigh Reg.zero (by decide)] + simp +decide [z, Reg.zero, Reg.one, Reg.two, Reg.four, Reg.low0] + · simp only [y, Data.set, Function.update_of_ne (show Reg.one ≠ Reg.depth by decide), + thigh Reg.one (by decide)] + simp +decide [z, Reg.zero, Reg.one, Reg.two, Reg.four, Reg.low0] + · simp only [y, Data.set, Function.update_of_ne (show Reg.two ≠ Reg.depth by decide), + thigh Reg.two (by decide)] + simp +decide [z, Reg.zero, Reg.one, Reg.two, Reg.four, Reg.low0] + · simp only [y, Data.set, Function.update_of_ne (show Reg.four ≠ Reg.depth by decide), + thigh Reg.four (by decide)] + simp +decide [z, Reg.zero, Reg.one, Reg.two, Reg.four, Reg.low0] + · intro i hi hd h0 h1 h2 h4 + simp only [y, Data.set, Function.update_of_ne hd, thigh i hi, z, + Function.update_of_ne (show i ≠ Reg.low0 by intro he; subst i; contradiction), + Function.update_of_ne h0, Function.update_of_ne h1, + Function.update_of_ne h2, Function.update_of_ne h4] + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Matrix.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Matrix.lean new file mode 100644 index 0000000000..c8109a8fc0 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Matrix.lean @@ -0,0 +1,190 @@ +/- +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.CountPrime.MatrixCell + +/-! +# Complete native padded monomial matrix construction + +The actual cell loop visits row-major coordinates, derives each distinct Morton block from +the computed spread table, and writes all p coefficients. The loop invariant protects prior +blocks using injectivity of the existing Morton projections. Original source/table words +are protected below the destination, which may start dirty. Every padding store is charged. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM + +private def cellPosition (N j : ℕ) : ℕ := Nat.Morton.index (j / N) (j % N) + +private def cellKey (transpose : Bool) (n p N source : ℕ) (m : ℕ → ℕ) (j : ℕ) : ℕ := + if j / N < n ∧ j % N < n then m (source + + if transpose then j % N * n + j / N else j / N * n + j % N) else p + +private theorem cellPosition_injective (N : ℕ) : Function.Injective (cellPosition N) := by + intro i j he + have hr := congrArg Nat.Morton.row he + have hc := congrArg Nat.Morton.column he + simp only [cellPosition, Nat.Morton.row_index] at hr + simp only [cellPosition, Nat.Morton.column_index] at hc + calc + i = N * (i / N) + i % N := (Nat.div_add_mod i N).symm + _ = N * (j / N) + j % N := by rw [hr, hc] + _ = j := Nat.div_add_mod j N + +private theorem cellPosition_lt {k j : ℕ} (hj : j < 4 ^ k) : cellPosition (2 ^ k) j < 4 ^ k := by + have hN : 0 < 2 ^ k := by positivity + have hsq : 2 ^ k * 2 ^ k = 4 ^ k := by rw [← mul_pow]; rfl + exact Nat.Morton.index_lt ((Nat.div_lt_iff_lt_mul hN).mpr (by simpa only [hsq] using hj)) + (Nat.mod_lt _ hN) + +/-- Build every padded matrix cell using the actual loop and computed Morton table. +The raw source is read only at admitted original coordinates, with actual BC transposition. +The numerical source/table separation is derived from the counter's real layout in composition. -/ +theorem exec_matrix {w n p k source destination table : ℕ} (src dst : Fin 128) + (transpose : Bool) (x : Data 128) (hdstReg : dst ≠ Reg.residueSource) + (hn : x.regs Reg.side = n) (hp : x.regs Reg.modulus = p) + (hN : x.regs Reg.paddedSide = 2 ^ k) (harea : x.regs Reg.paddedCells = 4 ^ k) + (hsrc : x.regs src = source) (hdst : x.regs dst = destination) (ht : x.regs Reg.spread = table) + (h0 : x.regs Reg.zero = 0) (h1 : x.regs Reg.one = 1) (h2 : x.regs Reg.two = 2) + (hsourceBefore : source + n * n ≤ destination) (htableBefore : table + 2 ^ k ≤ destination) + (htable : ∀ j < 2 ^ k, x.mem (table + j) = Nat.Morton.spread j) + (hw : Fits w (destination + 4 ^ k * p + table + 4 ^ k + 2 ^ k + + source + n * n + p + 16)) : + ∃ y, ExecWithin w (matrix src dst transpose) x y ((7 * p + 24) * 4 ^ k + 5) ∧ + (∀ j < 4 ^ k, ∀ r < p, + y.mem (destination + Nat.Morton.index (j / 2 ^ k) (j % 2 ^ k) * p + r) = + if r = (if j / 2 ^ k < n ∧ j % 2 ^ k < n then x.mem (source + + if transpose then j % 2 ^ k * n + j / 2 ^ k else j / 2 ^ k * n + j % 2 ^ k) + else p) then 2 else 0) ∧ + (∀ d, d < destination ∨ destination + 4 ^ k * p ≤ d → y.mem d = x.mem d) ∧ + ∀ r, ¬MatrixLocal r → r ≠ Reg.residueSource → r ≠ Reg.destination → y.regs r = x.regs r := by + let N := 2 ^ k + let start := ((x.set Reg.residueSource source).set Reg.destination destination).set Reg.index 0 + have startRegs (r : Fin 128) (hr : ¬MatrixLocal r) + (hs : r ≠ Reg.residueSource) (hd : r ≠ Reg.destination) : start.regs r = x.regs r := by + simp only [start, Data.set, Function.update_of_ne hs, Function.update_of_ne hd, + Function.update_of_ne (show r ≠ Reg.index by intro he; subst r; exact hr (by decide))] + let I (y : Data 128) : Prop := + (∀ r, ¬MatrixLocal r → y.regs r = start.regs r) ∧ + y.regs Reg.index ≤ 4 ^ k ∧ + (∀ j < y.regs Reg.index, ∀ r < p, + y.mem (destination + cellPosition N j * p + r) = + if r = cellKey transpose n p N source x.mem j then 2 else 0) ∧ + ∀ d, d < destination ∨ destination + 4 ^ k * p ≤ d → y.mem d = x.mem d + have body (y : Data 128) (hy : I y) (hc : (Cond.lt Reg.index Reg.paddedCells).Holds y) : + ∃ z, ExecWithin w (matrixStep transpose) y z (7 * p + 22) ∧ I z ∧ + 4 ^ k - z.regs Reg.index < 4 ^ k - y.regs Reg.index := by + obtain ⟨hr, hi, values, memory⟩ := hy + have keep (r : Fin 128) (hnr : ¬MatrixLocal r) + (hs : r ≠ Reg.residueSource) (hd : r ≠ Reg.destination) : y.regs r = x.regs r := + (hr r hnr).trans (startRegs r hnr hs hd) + have ysource : y.regs Reg.residueSource = source := by + simpa +decide [start, Reg.residueSource, Reg.destination, Reg.index] using + hr Reg.residueSource (by decide) + have ydestination : y.regs Reg.destination = destination := by + simpa +decide [start, Reg.destination, Reg.index] using hr Reg.destination (by decide) + have yarea : y.regs Reg.paddedCells = 4 ^ k := + (keep _ (by decide) (by decide) (by decide)).trans harea + have hlt : y.regs Reg.index < 4 ^ k := by simpa only [Cond.holds_lt, yarea] using hc + obtain ⟨z, he, zi, newValues, cellFrame, zr⟩ := exec_matrixStep transpose y + ((keep _ (by decide) (by decide) (by decide)).trans hn) + ((keep _ (by decide) (by decide) (by decide)).trans hp) + ((keep _ (by decide) (by decide) (by decide)).trans hN) rfl hlt + ysource ydestination ((keep _ (by decide) (by decide) (by decide)).trans ht) + ((keep _ (by decide) (by decide) (by decide)).trans h0) + ((keep _ (by decide) (by decide) (by decide)).trans h1) + ((keep _ (by decide) (by decide) (by decide)).trans h2) + (by + intro j hj + rw [memory _ (Or.inl (by omega))] + exact htable j hj) hw + have keyFrame : cellKey transpose n p N source y.mem (y.regs Reg.index) = + cellKey transpose n p N source x.mem (y.regs Reg.index) := by + unfold cellKey + split + · rename_i hguard + have hm₁ := Nat.mul_le_mul_right n (Nat.succ_le_of_lt hguard.1) + have hm₂ := Nat.mul_le_mul_right n (Nat.succ_le_of_lt hguard.2) + rw [Nat.succ_mul] at hm₁ hm₂ + apply memory + cases transpose <;> simp only [Bool.false_eq_true, ite_false, ite_true] <;> omega + · rfl + refine ⟨z, he, ⟨?_, by rw [zi]; omega, ?_, ?_⟩, by rw [zi]; omega⟩ + · intro r hnr + exact (zr r hnr).trans (hr r hnr) + · intro j hj r hrp + rw [zi] at hj + by_cases hjnew : j = y.regs Reg.index + · subst j + have hv := newValues r hrp + change z.mem (destination + cellPosition N (y.regs Reg.index) * p + r) = + (if r = cellKey transpose n p N source y.mem (y.regs Reg.index) then 2 else 0) at hv + simpa only [keyFrame] using hv + · have hprior : j < y.regs Reg.index := by omega + have hne : cellPosition N j ≠ cellPosition N (y.regs Reg.index) := + fun h => hjnew (cellPosition_injective N h) + have sep : destination + cellPosition N j * p + r < + destination + cellPosition N (y.regs Reg.index) * p ∨ + destination + cellPosition N (y.regs Reg.index) * p + p ≤ + destination + cellPosition N j * p + r := by + rcases lt_or_gt_of_ne hne with hlt' | hgt' + · have hm := Nat.mul_le_mul_right p (Nat.succ_le_of_lt hlt') + rw [Nat.succ_mul] at hm + omega + · have hm := Nat.mul_le_mul_right p (Nat.succ_le_of_lt hgt') + rw [Nat.succ_mul] at hm + omega + rw [cellFrame _ sep] + exact values j hprior r hrp + · intro d hd + have hm := Nat.mul_le_mul_right p (Nat.succ_le_of_lt (cellPosition_lt hlt)) + rw [Nat.succ_mul] at hm + change cellPosition N (y.regs Reg.index) * p + p ≤ 4 ^ k * p at hm + rw [cellFrame _ (by change d < destination + cellPosition N (y.regs Reg.index) * p ∨ + destination + cellPosition N (y.regs Reg.index) * p + p ≤ d; omega)] + exact memory d hd + have hinit : I start := by + refine ⟨fun _ _ => rfl, ?_, ?_, fun _ _ => rfl⟩ + · change 0 ≤ 4 ^ k + omega + · intro j hj + change j < 0 at hj + omega + obtain ⟨y, hy, hIy, hstop⟩ := ExecWithin.loop_invariant I + (fun y => 4 ^ k - y.regs Reg.index) body hinit + obtain ⟨hr, hi, values, memory⟩ := hIy + have harea' : y.regs Reg.paddedCells = 4 ^ k := + (hr Reg.paddedCells (by decide)).trans + ((startRegs _ (by decide) (by decide) (by decide)).trans harea) + have hindex : y.regs Reg.index = 4 ^ k := by + simp only [Cond.holds_lt, harea'] at hstop + omega + have hargs : ExecWithin (B := .standard) w + (ram[ Reg.residueSource ← src; Reg.destination ← dst; Reg.index ← #0 ]) x start 3 := by + ram_eval [start, hsrc, hdst, hdstReg, Reg.residueSource, Reg.destination, Reg.index] + have he : ExecWithin w (matrix src dst transpose) x y ((7 * p + 24) * 4 ^ k + 5) := by + obtain ⟨m, hm, argsRun⟩ := hargs + obtain ⟨nSteps, hnSteps, loopRun⟩ := hy + cases argsRun with + | seq h₁ rest => cases rest with + | seq h₂ h₃ => + refine ⟨_, ?_, .seq h₁ (.seq h₂ (.seq h₃ loopRun))⟩ + have hstart : start.regs Reg.index = 0 := by simp [start] + rw [hstart, Nat.sub_zero] at hnSteps + nlinarith + refine ⟨y, he, ?_, memory, ?_⟩ + · simpa only [hindex, cellPosition, cellKey, N] using values + · intro r hnr hs hd + exact (hr r hnr).trans (startRegs r hnr hs hd) + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/MatrixCell.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/MatrixCell.lean new file mode 100644 index 0000000000..77bfdb4481 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/MatrixCell.lean @@ -0,0 +1,161 @@ +/- +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.CountPrime.MatrixResidue +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Monomial + +/-! +# Actual native padded coefficient-matrix cell + +One cell executes its runtime division/remainder, actual spread-table reads and Morton +address arithmetic, both padding guards, real residue read when admitted, every coefficient +write and index increment. The table is constructed by the preceding native spread stage; +this internal execution theorem describes its current-state meaning for composition. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM + +/-- Exactly the row/column/scan/coefficient/address temporaries used by a matrix cell. -/ +@[expose] def MatrixLocal (i : Fin 128) : Prop := 87 ≤ i.val ∧ i.val ≤ 97 + +instance (i : Fin 128) : Decidable (MatrixLocal i) := + inferInstanceAs (Decidable (87 ≤ i.val ∧ i.val ≤ 97)) + +private theorem matrixStep_preserves (transpose : Bool) (i : Fin 128) (hi : ¬MatrixLocal i) : + (matrixStep transpose).Preserves i := by + cases transpose <;> decide +kernel +revert + +/-- Construct one complete padded p-coefficient cell from actual current source/table words. +All address, division, comparison, coefficient and control fits follow from a numerical +envelope; no matrix cell, residue answer, table lookup or running time is supplied by an oracle. -/ +theorem exec_matrixStep {w n p k source destination table i : ℕ} + (transpose : Bool) (x : Data 128) + (hn : x.regs Reg.side = n) (hp : x.regs Reg.modulus = p) + (hN : x.regs Reg.paddedSide = 2 ^ k) (hindex : x.regs Reg.index = i) (hi : i < 4 ^ k) + (hsrc : x.regs Reg.residueSource = source) (hdst : x.regs Reg.destination = destination) + (ht : x.regs Reg.spread = table) + (h0 : x.regs Reg.zero = 0) (h1 : x.regs Reg.one = 1) (h2 : x.regs Reg.two = 2) + (htable : ∀ j < 2 ^ k, x.mem (table + j) = Nat.Morton.spread j) + (hw : Fits w (destination + 4 ^ k * p + table + 4 ^ k + 2 ^ k + + source + n * n + p + 16)) : + ∃ y, ExecWithin w (matrixStep transpose) x y (7 * p + 22) ∧ + y.regs Reg.index = i + 1 ∧ + (∀ j < p, y.mem (destination + Nat.Morton.index (i / 2 ^ k) (i % 2 ^ k) * p + j) = + if j = (if i / 2 ^ k < n ∧ i % 2 ^ k < n then x.mem (source + + if transpose then i % 2 ^ k * n + i / 2 ^ k else i / 2 ^ k * n + i % 2 ^ k) + else p) then 2 else 0) ∧ + (∀ d, (d < destination + Nat.Morton.index (i / 2 ^ k) (i % 2 ^ k) * p ∨ + destination + Nat.Morton.index (i / 2 ^ k) (i % 2 ^ k) * p + p ≤ d) → + y.mem d = x.mem d) ∧ + ∀ r, ¬MatrixLocal r → y.regs r = x.regs r := by + let N := 2 ^ k + let row := i / N + let col := i % N + let position := Nat.Morton.index row col + have hpos : 0 < N := by positivity + have hsquare : N * N = 4 ^ k := by dsimp only [N]; rw [← mul_pow]; rfl + have hrlt : row < N := (Nat.div_lt_iff_lt_mul hpos).mpr (by simpa only [hsquare] using hi) + have hclt : col < N := Nat.mod_lt i hpos + have hzlt : position < 4 ^ k := Nat.Morton.index_lt hrlt hclt + have hmul := Nat.mul_le_mul_right p (Nat.succ_le_of_lt hzlt) + rw [Nat.succ_mul] at hmul + have fit (a : ℕ) (ha : a ≤ destination + 4 ^ k * p + table + 4 ^ k + N + + source + n * n + p + 16) : Fits w a := hw.of_le ha + let z := (x.set Reg.row row).set Reg.column col + have coordinates : ExecWithin (B := .standard) w + (ram[ Reg.row ← Reg.index / Reg.paddedSide; Reg.column ← Reg.index % Reg.paddedSide ]) + x z 2 := by + ram_eval [z, row, col, hindex, hN, N, fit row (by omega), fit col (by omega), + Reg.row, Reg.column, Reg.index, Reg.paddedSide] + have zmeta (r : Fin 128) (hr : ¬MatrixLocal r) : z.regs r = x.regs r := by + simp only [z, Data.set, + Function.update_of_ne (show r ≠ Reg.row by intro he; subst r; exact hr (by decide)), + Function.update_of_ne (show r ≠ Reg.column by intro he; subst r; exact hr (by decide))] + obtain ⟨s, mortonRun, sm, spos, sr⟩ := exec_morton (k := k) z + ((zmeta Reg.two (by decide)).trans h2) + (by simpa +decide [z, Reg.row, Reg.column] using hrlt) + (by simpa +decide [z, Reg.column] using hclt) + (by simpa only [show z.mem = x.mem from rfl, zmeta Reg.spread (by decide), ht] using htable) + (by rw [zmeta Reg.spread (by decide), ht]; exact hw.of_le (by omega)) + have smeta (r : Fin 128) (hr : ¬MatrixLocal r) : s.regs r = x.regs r := + (sr r (by intro he; subst r; exact hr (by decide)) + (by intro he; subst r; exact hr (by decide)) + (by intro he; subst r; exact hr (by decide)) + (by intro he; subst r; exact hr (by decide))).trans (zmeta r hr) + have sposition : s.regs Reg.position = position := by + simpa +decide [z, Reg.row, Reg.column] using spos + have srow : s.regs Reg.row = row := + (sr Reg.row (by decide) (by decide) (by decide) (by decide)).trans (by + simp +decide [z, Reg.row, Reg.column]) + have scol : s.regs Reg.column = col := + (sr Reg.column (by decide) (by decide) (by decide) (by decide)).trans (by + simp +decide [z, Reg.column]) + have sindex : s.regs Reg.index = i := + (sr Reg.index (by decide) (by decide) (by decide) (by decide)).trans (by + simpa +decide [z, Reg.index, Reg.row, Reg.column] using hindex) + let block := destination + position * p + let t := (s.set Reg.block (position * p)).set Reg.block block + have blockRun : ExecWithin (B := .standard) w + (ram[ Reg.block ← Reg.position * Reg.modulus; + Reg.block ← Reg.destination + Reg.block ]) s t 2 := by + ram_eval [t, block, sposition, (smeta Reg.modulus (by decide)).trans hp, + (smeta Reg.destination (by decide)).trans hdst, + fit (position * p) (by omega), fit block (by dsimp [block]; omega), + Reg.block, Reg.position, Reg.modulus, Reg.destination] + have tmeta (r : Fin 128) (hr : ¬MatrixLocal r) : t.regs r = x.regs r := by + simpa only [t, Data.set, + Function.update_of_ne (show r ≠ Reg.block by intro he; subst r; exact hr (by decide))] + using smeta r hr + have trow : t.regs Reg.row = row := by simpa +decide [t, Reg.row, Reg.block] using srow + have tcol : t.regs Reg.column = col := by simpa +decide [t, Reg.column, Reg.block] using scol + have tindex : t.regs Reg.index = i := by simpa +decide [t, Reg.index, Reg.block] using sindex + have tm : t.mem = x.mem := sm + obtain ⟨r, selectRun, rm, key, rr⟩ := exec_matrixResidue transpose t (by + rw [tmeta Reg.residueSource (by decide), hsrc, tmeta Reg.side (by decide), hn, + tmeta Reg.modulus (by decide), hp] + exact hw.of_le (by omega)) + have rmeta (j : Fin 128) (hj : ¬MatrixLocal j) : r.regs j = x.regs j := + (rr j (by intro he; subst j; exact hj (by decide)) + (by intro he; subst j; exact hj (by decide)) + (by intro he; subst j; exact hj (by decide))).trans (tmeta j hj) + have rblock : r.regs Reg.block = block := + (rr Reg.block (by decide) (by decide) (by decide)).trans (by simp [t]) + have rindex : r.regs Reg.index = i := + (rr Reg.index (by decide) (by decide) (by decide)).trans tindex + have rmod : r.regs Reg.modulus = p := (rmeta Reg.modulus (by decide)).trans hp + obtain ⟨s', writeRun, values, memory, regs⟩ := exec_writeCoefficients r + ((rmeta Reg.zero (by decide)).trans h0) ((rmeta Reg.one (by decide)).trans h1) + ((rmeta Reg.two (by decide)).trans h2) + (by rw [rblock, rmod]; exact fit _ (by dsimp [block]; omega)) + have s'index : s'.regs Reg.index = i := + (regs Reg.index (by decide) (by decide)).trans rindex + have s'one : s'.regs Reg.one = 1 := + (regs Reg.one (by decide) (by decide)).trans ((rmeta Reg.one (by decide)).trans h1) + let y := s'.set Reg.index (i + 1) + have increment : ExecWithin (B := .standard) w + (ram[ Reg.index ← Reg.index + Reg.one ]) s' y 1 := by + ram_eval [y, s'index, s'one, fit (i + 1) (by omega)] + have he : ExecWithin w (matrixStep transpose) x y (7 * p + 22) := + (coordinates.seq (mortonRun.seq (blockRun.seq (selectRun.seq (writeRun.seq increment))))).mono + (by rw [rmod]; omega) + refine ⟨y, he, ?_, ?_, ?_, fun j hj => he.regs_eq (matrixStep_preserves transpose j hj)⟩ + · rfl + · intro j hj + have hv := values j (by simpa only [rmod] using hj) + simpa only [y, Data.set_mem, rblock, block, key, trow, tcol, + tmeta Reg.side (by decide), hn, tmeta Reg.residueSource (by decide), hsrc, + tmeta Reg.modulus (by decide), hp, tm, rm] using hv + · intro d hd + exact (memory d (by simpa only [rblock, rmod, block] using hd)).trans (by rw [rm, tm]) + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/MatrixReadback.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/MatrixReadback.lean new file mode 100644 index 0000000000..c6b8866b2b --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/MatrixReadback.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.CountPrime.Matrix +public import Complexity.Algorithms.ExactTriangle.CountPrime + +/-! +# Native CountPrime matrices agree with the existing coefficient specification + +Actual residue readback and the preceding native table construction feed the real padded +matrix loop. Every resulting word is the canonical encoding of the existing P/Q coefficient +at that exact Morton/coefficient index. No alternative matrix or triangle definition is +introduced. Both padding and the actual BC transpose are included. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- The complete computed native matrix agrees with existing P coefficients, or Q +coefficients for the real transposed branch, at every occupied coefficient index. -/ +theorem exec_matrix_coefficients {w n p k source destination table : ℕ} + (src dst : Fin 128) (transpose : Bool) (x : Data 128) (weights : Fin n → Fin n → ℤ) + (hpositive : 0 < p) (hdstReg : dst ≠ Reg.residueSource) + (hn : x.regs Reg.side = n) (hp : x.regs Reg.modulus = p) + (hN : x.regs Reg.paddedSide = 2 ^ k) (harea : x.regs Reg.paddedCells = 4 ^ k) + (hsrc : x.regs src = source) (hdst : x.regs dst = destination) (ht : x.regs Reg.spread = table) + (h0 : x.regs Reg.zero = 0) (h1 : x.regs Reg.one = 1) (h2 : x.regs Reg.two = 2) + (hsourceBefore : source + n * n ≤ destination) (htableBefore : table + 2 ^ k ≤ destination) + (htable : ∀ j < 2 ^ k, x.mem (table + j) = Nat.Morton.spread j) + (hresidue : ∀ a b : Fin n, x.mem (source + a.val * n + b.val) = + (weights a b % (p : ℤ)).toNat) + (hw : Fits w (destination + 4 ^ k * p + table + 4 ^ k + 2 ^ k + + source + n * n + p + 16)) : + ∃ y, ExecWithin w (matrix src dst transpose) x y ((7 * p + 24) * 4 ^ k + 5) ∧ + (∀ i < 4 ^ k * p, y.mem (destination + i) = encode + (if transpose then Cslib.Algorithms.ExactTriangle.CountPrime.qCoefficients p weights i + else Cslib.Algorithms.ExactTriangle.CountPrime.pCoefficients p weights i)) ∧ + (∀ d, d < destination ∨ destination + 4 ^ k * p ≤ d → y.mem d = x.mem d) ∧ + ∀ r, ¬MatrixLocal r → r ≠ Reg.residueSource → r ≠ Reg.destination → y.regs r = x.regs r := by + obtain ⟨y, he, values, memory, regs⟩ := exec_matrix src dst transpose x hdstReg + hn hp hN harea hsrc hdst ht h0 h1 h2 hsourceBefore htableBefore htable hw + refine ⟨y, he, ?_, memory, regs⟩ + intro i hi + let cell := i / p + let a := Nat.Morton.row cell + let b := Nat.Morton.column cell + let N := 2 ^ k + have hcell : cell < 4 ^ k := (Nat.div_lt_iff_lt_mul hpositive).mpr hi + obtain ⟨ha, hb⟩ := Nat.Morton.row_column_lt hcell + change a < N at ha + change b < N at hb + have hNpos : 0 < N := by positivity + have hsquare : N * N = 4 ^ k := by dsimp only [N]; rw [← mul_pow]; rfl + let j := a * N + b + have hj : j < 4 ^ k := by + have hm := Nat.mul_le_mul_right N (Nat.succ_le_of_lt ha) + rw [Nat.succ_mul] at hm + dsimp only [j] + rw [← hsquare] + omega + have hjDiv : j / N = a := by + dsimp only [j] + rw [Nat.mul_comm a N, Nat.mul_add_div hNpos, Nat.div_eq_of_lt hb, Nat.add_zero] + have hjMod : j % N = b := by + dsimp only [j] + rw [Nat.mul_comm a N, Nat.mul_add_mod, Nat.mod_eq_of_lt hb] + have hpos : Nat.Morton.index a b = cell := Nat.Morton.index_row_column cell + have hidx : cell * p + i % p = i := by + simpa only [cell, Nat.mul_comm] using Nat.div_add_mod i p + have hr : i % p < p := Nat.mod_lt i hpositive + have hv := values j hj (i % p) hr + rw [hjDiv, hjMod, hpos, Nat.add_assoc, hidx] at hv + cases transpose with + | false => + change y.mem (destination + i) = encode + (Cslib.Algorithms.ExactTriangle.CountPrime.pCoefficients p weights i) + unfold Cslib.Algorithms.ExactTriangle.CountPrime.pCoefficients + change y.mem (destination + i) = encode (if h : a < n ∧ b < n then + if i % p = (weights ⟨a, h.1⟩ ⟨b, h.2⟩ % (p : ℤ)).toNat then 1 else 0 else 0) + by_cases h : a < n ∧ b < n + · rw [dite_eq_left h] + simp only [h, and_self, ite_true, Bool.false_eq_true, ite_false] at hv + rw [← Nat.add_assoc, hresidue ⟨a, h.1⟩ ⟨b, h.2⟩] at hv + rw [hv] + split_ifs <;> rfl + · rw [dite_eq_right h] + simp only [h, ite_false, show i % p ≠ p by omega] at hv + exact hv + | true => + change y.mem (destination + i) = encode + (Cslib.Algorithms.ExactTriangle.CountPrime.qCoefficients p weights i) + unfold Cslib.Algorithms.ExactTriangle.CountPrime.qCoefficients + change y.mem (destination + i) = encode (if h : a < n ∧ b < n then + if i % p = (weights ⟨b, h.2⟩ ⟨a, h.1⟩ % (p : ℤ)).toNat then 1 else 0 else 0) + by_cases h : a < n ∧ b < n + · rw [dite_eq_left h] + simp only [h, and_self, ite_true] at hv + rw [← Nat.add_assoc, hresidue ⟨b, h.2⟩ ⟨a, h.1⟩] at hv + rw [hv] + split_ifs <;> rfl + · rw [dite_eq_right h] + simp only [h, ite_false, show i % p ≠ p by omega] at hv + exact hv + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/MatrixResidue.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/MatrixResidue.lean new file mode 100644 index 0000000000..c3e47e3bdc --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/MatrixResidue.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.ExactTriangle.CountPrime.Access + +/-! +# Actual guarded CountPrime residue reads + +Both native coordinate comparisons run before a real row-major source read. A padded cell +uses the runtime modulus as an out-of-range key, causing the subsequent actual coefficient +loop to write zeros. BC transposition is part of the real address computation. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM + +/-- Execute both real padding guards and the admitted source read, or select the padding +sentinel. Even invalid coordinates never cause an unchecked source-memory access. -/ +theorem exec_matrixResidue {w : ℕ} (transpose : Bool) (x : Data 128) + (hw : Fits w (x.regs Reg.residueSource + x.regs Reg.side * x.regs Reg.side + + x.regs Reg.modulus)) : + ∃ y, ExecWithin w (matrixResidue transpose) x y 8 ∧ y.mem = x.mem ∧ + y.regs Reg.residue = + (if x.regs Reg.row < x.regs Reg.side ∧ x.regs Reg.column < x.regs Reg.side then + x.mem (x.regs Reg.residueSource + + if transpose then x.regs Reg.column * x.regs Reg.side + x.regs Reg.row + else x.regs Reg.row * x.regs Reg.side + x.regs Reg.column) + else x.regs Reg.modulus) ∧ + ∀ i, i ≠ Reg.address → i ≠ Reg.temporary → i ≠ Reg.residue → y.regs i = x.regs i := by + by_cases hr : x.regs Reg.row < x.regs Reg.side + · by_cases hc : x.regs Reg.column < x.regs Reg.side + · obtain ⟨y, hy, hm, hv, hregs⟩ := exec_readResidue transpose x hr hc (hw.of_le (by omega)) + have he : ExecWithin w (matrixResidue transpose) x y 8 := + (ExecWithin.branch_pos (by simpa only [Cond.holds_lt] using hr) + (ExecWithin.branch_pos (by simpa only [Cond.holds_lt] using hc) hy)).mono (by omega) + refine ⟨y, he, hm, ?_, hregs⟩ + simpa only [hr, hc, and_self, ite_true] using hv + · let y := x.set Reg.residue (x.regs Reg.modulus) + have hset : ExecWithin (B := .standard) w (ram[ Reg.residue ← Reg.modulus ]) x y 1 := + .copy + have he : ExecWithin w (matrixResidue transpose) x y 8 := + (ExecWithin.branch_pos (by simpa only [Cond.holds_lt] using hr) + (ExecWithin.branch_neg (by simpa only [Cond.holds_lt] using hc) hset)).mono (by omega) + refine ⟨y, he, rfl, ?_, ?_⟩ + · simp only [y, Data.set, Function.update_self, hc, and_false, ite_false] + · intro i _ _ hi + exact Function.update_of_ne hi _ _ + · let y := x.set Reg.residue (x.regs Reg.modulus) + have hset : ExecWithin (B := .standard) w (ram[ Reg.residue ← Reg.modulus ]) x y 1 := .copy + have he : ExecWithin w (matrixResidue transpose) x y 8 := + (ExecWithin.branch_neg (by simpa only [Cond.holds_lt] using hr) hset).mono (by omega) + refine ⟨y, he, rfl, ?_, ?_⟩ + · simp only [y, Data.set, Function.update_self, hr, false_and, ite_false] + · intro i _ _ hi + exact Function.update_of_ne hi _ _ + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Monomial.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Monomial.lean new file mode 100644 index 0000000000..cc93f451a8 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Monomial.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.ExactTriangle.CountPrime.Program + +/-! +# Actual canonical signed monomial writes + +Each of the p coefficients is stored, so dirty destination words do not become implicit +padding. An out-of-range key (in particular the native padding sentinel p) writes a zero +block. The raw unsigned word two is exactly `Signed.encode 1`; there is no extra conversion +or free ring-coefficient operation. This is an internal counter-stage execution API. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM + +private theorem writeCoefficient_preserves (i : Fin 128) : + i ≠ Reg.coefficient → i ≠ Reg.address → writeCoefficient.Preserves i := by + decide +kernel +revert + +/-- A whole dirty p-word destination becomes the canonical coefficient block, with every +test, address calculation and store charged. The key may be any unsigned word. -/ +theorem exec_writeCoefficients {w : ℕ} (x : Data 128) + (h0 : x.regs Reg.zero = 0) (h1 : x.regs Reg.one = 1) (h2 : x.regs Reg.two = 2) + (hw : Fits w (x.regs Reg.block + x.regs Reg.modulus + 4)) : + ∃ y, ExecWithin w writeCoefficients x y (7 * x.regs Reg.modulus + 3) ∧ + (∀ j < x.regs Reg.modulus, y.mem (x.regs Reg.block + j) = + if j = x.regs Reg.residue then 2 else 0) ∧ + (∀ a, a < x.regs Reg.block ∨ x.regs Reg.block + x.regs Reg.modulus ≤ a → + y.mem a = x.mem a) ∧ + ∀ i, i ≠ Reg.coefficient → i ≠ Reg.address → y.regs i = x.regs i := by + let I (y : Data 128) : Prop := + (∀ i, i ≠ Reg.coefficient → i ≠ Reg.address → y.regs i = x.regs i) ∧ + y.regs Reg.coefficient ≤ x.regs Reg.modulus ∧ + (∀ j < y.regs Reg.coefficient, y.mem (x.regs Reg.block + j) = + if j = x.regs Reg.residue then 2 else 0) ∧ + ∀ a, ¬(x.regs Reg.block ≤ a ∧ a < x.regs Reg.block + y.regs Reg.coefficient) → + y.mem a = x.mem a + have hbody (y : Data 128) (hy : I y) + (hc : (Cond.lt Reg.coefficient Reg.modulus).Holds y) : + ∃ z, ExecWithin w writeCoefficient y z 5 ∧ I z ∧ + x.regs Reg.modulus - z.regs Reg.coefficient < + x.regs Reg.modulus - y.regs Reg.coefficient := by + obtain ⟨hr, hi, hv, hf⟩ := hy + have hmod := hr Reg.modulus (by decide) (by decide) + have hbase := hr Reg.block (by decide) (by decide) + have hres := hr Reg.residue (by decide) (by decide) + have hy0 := (hr Reg.zero (by decide) (by decide)).trans h0 + have hy1 := (hr Reg.one (by decide) (by decide)).trans h1 + have hy2 := (hr Reg.two (by decide) (by decide)).trans h2 + have hlt : y.regs Reg.coefficient < x.regs Reg.modulus := by + simpa only [Cond.holds_lt, hmod] using hc + have ha : Fits w (y.regs Reg.block + y.regs Reg.coefficient) := hw.of_le (by + rw [hbase]; omega) + have hn : Fits w (y.regs Reg.coefficient + 1) := hw.of_le (by omega) + let z := ((y.set Reg.address (y.regs Reg.block + y.regs Reg.coefficient)).write + (y.regs Reg.block + y.regs Reg.coefficient) + (if y.regs Reg.coefficient = y.regs Reg.residue then 2 else 0)).set + Reg.coefficient (y.regs Reg.coefficient + 1) + have he : ExecWithin w writeCoefficient y z 5 := by + let before := y.set Reg.address (y.regs Reg.block + y.regs Reg.coefficient) + let middle := before.write (y.regs Reg.block + y.regs Reg.coefficient) + (if y.regs Reg.coefficient = y.regs Reg.residue then 2 else 0) + have hfirst : ExecWithin (B := .standard) w + (ram[ Reg.address ← Reg.block + Reg.coefficient ]) y before 1 := .bin ha + have hcase : ExecWithin (B := .standard) w + (.branch (.eq Reg.coefficient Reg.residue) + (ram[ *Reg.address ← Reg.two ]) (ram[ *Reg.address ← Reg.zero ])) before middle 3 := by + apply ExecWithin.branch + · intro hc' + have h : y.regs Reg.coefficient = y.regs Reg.residue := by + simpa +decide [before, Cond.holds_eq, Reg.address, Reg.coefficient, + Reg.residue] using hc' + simpa +decide [middle, before, h, hy2, Reg.address, Reg.two] using + (ExecWithin.store (B := .standard) (w := w) (a := Reg.address) (s := Reg.two) + (x := before)) + · intro hc' + have h : y.regs Reg.coefficient ≠ y.regs Reg.residue := by + simpa +decide [before, Cond.holds_eq, Reg.address, Reg.coefficient, + Reg.residue] using hc' + simpa +decide [middle, before, h, hy0, Reg.address, Reg.zero] using + (ExecWithin.store (B := .standard) (w := w) (a := Reg.address) (s := Reg.zero) + (x := before)) + have hlast : ExecWithin (B := .standard) w + (ram[ Reg.coefficient ← Reg.coefficient + Reg.one ]) middle z 1 := by + ram_eval [middle, before, z, hy1, hn, Reg.address, Reg.coefficient, Reg.one] + exact hfirst.seq (hcase.seq hlast) + refine ⟨z, he, ⟨?_, ?_, ?_, ?_⟩, ?_⟩ + · intro i hc ha + exact (he.regs_eq (writeCoefficient_preserves i hc ha)).trans (hr i hc ha) + · change y.regs Reg.coefficient + 1 ≤ x.regs Reg.modulus + omega + · intro j hj + change j < y.regs Reg.coefficient + 1 at hj + change Function.update y.mem (y.regs Reg.block + y.regs Reg.coefficient) + (if y.regs Reg.coefficient = y.regs Reg.residue then 2 else 0) + (x.regs Reg.block + j) = _ + by_cases heq : j = y.regs Reg.coefficient + · subst j + rw [hbase, Function.update_self, hres] + · rw [Function.update_of_ne (by rw [hbase]; omega)] + exact hv j (by omega) + · intro a ha + change ¬(x.regs Reg.block ≤ a ∧ a < x.regs Reg.block + (y.regs Reg.coefficient + 1)) at ha + change Function.update y.mem (y.regs Reg.block + y.regs Reg.coefficient) + (if y.regs Reg.coefficient = y.regs Reg.residue then 2 else 0) a = x.mem a + rw [Function.update_of_ne (by rw [hbase]; omega)] + exact hf a (by omega) + · change x.regs Reg.modulus - (y.regs Reg.coefficient + 1) < + x.regs Reg.modulus - y.regs Reg.coefficient + omega + let y := x.set Reg.coefficient 0 + have hinit : ExecWithin (B := .standard) w (ram[ Reg.coefficient ← #0 ]) x y 1 := + .const (fits_zero w) + have hI : I y := by + refine ⟨?_, ?_, ?_, ?_⟩ + · intro i hc ha + exact Function.update_of_ne hc _ _ + · change 0 ≤ x.regs Reg.modulus + omega + · intro j hj + change j < 0 at hj + omega + · intro a ha + rfl + obtain ⟨z, hz, hIz, hstop⟩ := ExecWithin.loop_invariant I + (fun y => x.regs Reg.modulus - y.regs Reg.coefficient) hbody hI + obtain ⟨hr, hi, hv, hf⟩ := hIz + have hi' : z.regs Reg.coefficient = x.regs Reg.modulus := by + have hm := hr Reg.modulus (by decide) (by decide) + simp only [Cond.holds_lt, hm] at hstop + omega + refine ⟨z, (hinit.seq hz).mono (by change 1 + + ((x.regs Reg.modulus - 0) * (5 + 2) + 2) ≤ _; omega), ?_, ?_, hr⟩ + · simpa only [hi'] using hv + · intro a ha + exact hf a (by omega) + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Product.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Product.lean new file mode 100644 index 0000000000..7ae32d805f --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Product.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.ExactTriangle.CountPrime.Program +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Exec + +/-! +# Actual low-bank CountPrime multiplication call + +The counter copies six real arguments and invokes the complete native 64-register Strassen +program in its low bank. Its 64-and-above metadata survives the existing native register +renaming semantics. Monomial inputs have coefficient magnitude at most one; all recursive +signed fits therefore follow from the explicit original cyclic-length/depth envelope. + +This is an internal stage contract. The complete counter derives its monomial readback and +pointer separation from preceding native setup and matrix construction, rather than taking +a layout or multiplication certificate as an algorithm input. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Invoke the actual complete native Strassen program, charging argument copies, all +coefficient arithmetic/recursion and loop exit. No multiplication/run/time contract is assumed. -/ +theorem exec_product {w p k : ℕ} (x : Data 128) (u v : ℕ → ℤ) + (hp : 0 < p) (hpr : x.regs Reg.modulus = p) (hkr : x.regs Reg.depth = k) + (hPQ : x.regs Reg.matrixP + 4 ^ k * p ≤ x.regs Reg.matrixQ) + (hQR : x.regs Reg.matrixQ + 4 ^ k * p ≤ x.regs Reg.matrixR) + (hRS : x.regs Reg.matrixR + 4 ^ k * p ≤ x.regs Reg.productScratch) + (hu : ∀ i < 4 ^ k * p, (u i).natAbs ≤ 1) + (hv : ∀ i < 4 ^ k * p, (v i).natAbs ≤ 1) + (hleft : ∀ i < 4 ^ k * p, x.mem (x.regs Reg.matrixP + i) = encode (u i)) + (hright : ∀ i < 4 ^ k * p, x.mem (x.regs Reg.matrixQ + i) = encode (v i)) + (hmag : GenericRAM.Signed.Fits w ((2 * (p + 4) * 16 ^ k : ℕ) : ℤ)) + (haddr : Fits (w + 2) (Strassen.addressBound (x.regs Reg.productScratch + 4 ^ k * p) + (x.regs Reg.productScratch + 4 ^ k * p) k)) : + ∃ y, ExecWithin (w + 2) product x y (Strassen.traceBudget p k + 4 * k + 38) ∧ + (∀ i < 4 ^ k * p, y.mem (x.regs Reg.matrixR + i) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.multiply p k u v i)) ∧ + (∀ d, ¬Strassen.NodeWrites p k (x.regs Reg.matrixR) (x.regs Reg.productScratch) + (x.regs Reg.productScratch + 4 ^ k * p) (x.regs Reg.productScratch + 4 ^ k * p) d → + y.mem d = x.mem d) ∧ + ∀ i, 64 ≤ i.val → y.regs i = x.regs i := by + let z := (((((x.set Reg.low32 (x.regs Reg.matrixP)).set Reg.low33 (x.regs Reg.matrixQ)).set + Reg.low34 (x.regs Reg.matrixR)).set Reg.low35 (x.regs Reg.productScratch)).set + Reg.low36 k).set Reg.low46 p + have hargs : ExecWithin (B := .standard) (w + 2) + (ram[ Reg.low32 ← Reg.matrixP; Reg.low33 ← Reg.matrixQ; Reg.low34 ← Reg.matrixR; + Reg.low35 ← Reg.productScratch; Reg.low36 ← Reg.depth; + Reg.low46 ← Reg.modulus ]) x z 6 := by + ram_eval [z, hpr, hkr, Reg.low32, Reg.low33, Reg.low34, Reg.low35, Reg.low36, Reg.low46, + Reg.matrixP, Reg.matrixQ, Reg.matrixR, Reg.productScratch, Reg.depth, Reg.modulus] + let f : Fin 64 → Fin 128 := Fin.castLE (by decide) + have ha : (z.comap f).regs Strassen.Reg.inputA = x.regs Reg.matrixP := by + simp +decide [z, f, Data.comap, Strassen.Reg.inputA, Reg.low32, Reg.low33, + Reg.low34, Reg.low35, Reg.low36, Reg.low46] + have hb : (z.comap f).regs Strassen.Reg.inputB = x.regs Reg.matrixQ := by + simp +decide [z, f, Data.comap, Strassen.Reg.inputB, Reg.low33, + Reg.low34, Reg.low35, Reg.low36, Reg.low46] + have ho : (z.comap f).regs Strassen.Reg.output = x.regs Reg.matrixR := by + simp +decide [z, f, Data.comap, Strassen.Reg.output, + Reg.low34, Reg.low35, Reg.low36, Reg.low46] + have hs : (z.comap f).regs Strassen.Reg.scratch = x.regs Reg.productScratch := by + simp +decide [z, f, Data.comap, Strassen.Reg.scratch, Reg.low35, Reg.low36, Reg.low46] + have hk : (z.comap f).regs Strassen.Reg.depth = k := by + simp +decide [z, f, Data.comap, Strassen.Reg.depth, Reg.low36, Reg.low46] + have hpp : (z.comap f).regs Strassen.Reg.modulus = p := by + simp [z, f, Data.comap, Strassen.Reg.modulus, Reg.low46] + have hmag' : GenericRAM.Signed.Fits w (Strassen.magnitudeBound p 1 1 k : ℤ) := by + convert hmag using 1 + simp [Strassen.magnitudeBound, + CyclicConvolution.magnitudeBound, Nat.add_assoc, Nat.add_comm, Nat.add_left_comm] + obtain ⟨s, he, result⟩ := Strassen.exec (z.comap f) u v hp hpp hk ha hb ho hs + (by omega) (by omega) hRS (Or.inl (by omega)) (Or.inl hQR) + hu hv hleft hright hmag' haddr + let y := s.extend f z + have hcall : ExecWithin (w + 2) (Strassen.program.rename f) z y + (Strassen.traceBudget p k + 4 * k + 32) := he.rename (Fin.castLE_injective _) + have hproduct : ExecWithin (w + 2) product x y + (Strassen.traceBudget p k + 4 * k + 38) := by + obtain ⟨n, hn, argsRun⟩ := hargs + obtain ⟨m, hm, callRun⟩ := hcall + cases argsRun with + | seq h₁ rest₁ => cases rest₁ with + | seq h₂ rest₂ => cases rest₂ with + | seq h₃ rest₃ => cases rest₃ with + | seq h₄ rest₄ => cases rest₄ with + | seq h₅ h₆ => + exact ⟨_, by omega, .seq h₁ (.seq h₂ (.seq h₃ (.seq h₄ (.seq h₅ (.seq h₆ callRun)))))⟩ + refine ⟨y, hproduct, ?_, ?_, ?_⟩ + · exact result.values + · exact result.outside + · intro i hi + have hn : ¬∃ j, f j = i := by + rintro ⟨j, hj⟩ + have hv := congrArg Fin.val hj + change j.val = i.val at hv + omega + have hyi : y.regs i = z.regs i := Function.extend_apply' _ _ _ hn + apply hyi.trans + simp only [z, Data.set, + Function.update_of_ne (show i ≠ Reg.low32 by intro he; subst i; contradiction), + Function.update_of_ne (show i ≠ Reg.low33 by intro he; subst i; contradiction), + Function.update_of_ne (show i ≠ Reg.low34 by intro he; subst i; contradiction), + Function.update_of_ne (show i ≠ Reg.low35 by intro he; subst i; contradiction), + Function.update_of_ne (show i ≠ Reg.low36 by intro he; subst i; contradiction), + Function.update_of_ne (show i ≠ Reg.low46 by intro he; subst i; contradiction)] + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Program.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Program.lean new file mode 100644 index 0000000000..24771b97b5 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/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.Strassen.Program +public import Complexity.Algorithms.WordRAM.ThreeSum.ChanHe.Residues +public import Complexity.Algorithms.WordRAM.NatClog + +/-! +# Concrete native modular triangle-counting code + +The program computes three actual unsigned residue arrays, a spread table by its binary +recurrence, two zero-padded cyclic-monomial matrices, the actual seven-product Strassen +product, and the selected coefficient sum. It contains no triple-enumeration loop and takes +no table, layout, multiplication, result or execution certificate as input. + +Pointers, dimensions and modulus are unsigned words; the original three weight arrays hold +canonical signed words. Monomial coefficients are written directly as canonical signed +zero or one. The existing residue loop explicitly decodes its nonnegative remainders. The +last scan explicitly decodes the nonnegative product coefficients before unsigned addition. + +The source algorithm is Programs/Sec3/Theorem17/Hashing/CountPrime.lean at pinned revision +16a5ce41304fcdaea4e0be7c132e1c361dc26da5. This native adaptation uses the genuine standard +division/remainder instructions and the existing signed remainder kernel instead of Light's +bit-doubling residue table. This module defines code; `Exec`, `Flat`, +`SourceBounds` and `TimeBound` prove the complete native correctness, initialized execution, +logarithmic admission, width-independent prefix bounds and charged subcubic resource claims. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM + +namespace Reg + +/-- Low helper-bank registers, reused only between completed calls. -/ +abbrev low0 : Fin 128 := 0 +/-- Low helper result / argument register. -/ +abbrev low1 : Fin 128 := 1 +/-- Low helper power-base register. -/ +abbrev low2 : Fin 128 := 2 +/-- Low helper power-exponent register. -/ +abbrev low3 : Fin 128 := 3 +/-- Low residue input pointer. -/ +abbrev low9 : Fin 128 := 9 +/-- Low residue output pointer. -/ +abbrev low10 : Fin 128 := 10 +/-- Low residue length. -/ +abbrev low11 : Fin 128 := 11 +/-- Low signed modulus. -/ +abbrev low12 : Fin 128 := 12 +/-- First low Strassen matrix pointer. -/ +abbrev low32 : Fin 128 := 32 +/-- Second low Strassen matrix pointer. -/ +abbrev low33 : Fin 128 := 33 +/-- Low Strassen output pointer. -/ +abbrev low34 : Fin 128 := 34 +/-- Low Strassen scratch pointer. -/ +abbrev low35 : Fin 128 := 35 +/-- Low Strassen matrix depth. -/ +abbrev low36 : Fin 128 := 36 +/-- Low Strassen coefficient length. -/ +abbrev low46 : Fin 128 := 46 +/-- Original side length. -/ +abbrev side : Fin 128 := 64 +/-- Positive runtime modulus. -/ +abbrev modulus : Fin 128 := 65 +/-- Original canonical signed AB matrix. -/ +abbrev inputAB : Fin 128 := 66 +/-- Original canonical signed BC matrix, indexed by (b,c). -/ +abbrev inputBC : Fin 128 := 67 +/-- Original canonical signed AC matrix. -/ +abbrev inputAC : Fin 128 := 68 +/-- Beginning of caller-disjoint dirty workspace. -/ +abbrev scratch : Fin 128 := 69 +/-- Raw unsigned modular-zero count. -/ +abbrev result : Fin 128 := 70 +/-- Raw / canonical signed zero. -/ +abbrev zero : Fin 128 := 71 +/-- Raw unsigned one. -/ +abbrev one : Fin 128 := 72 +/-- Raw unsigned two, also the canonical signed word for one. -/ +abbrev two : Fin 128 := 73 +/-- Raw unsigned four. -/ +abbrev four : Fin 128 := 74 +/-- Actual computed ceiling binary logarithm of the side. -/ +abbrev depth : Fin 128 := 75 +/-- Padded power-of-two side. -/ +abbrev paddedSide : Fin 128 := 76 +/-- Original matrix word count. -/ +abbrev cells : Fin 128 := 77 +/-- Padded coefficient word count. -/ +abbrev area : Fin 128 := 78 +/-- First unsigned residue block. -/ +abbrev residueAB : Fin 128 := 79 +/-- Second unsigned residue block. -/ +abbrev residueBC : Fin 128 := 80 +/-- Third unsigned residue block. -/ +abbrev residueAC : Fin 128 := 81 +/-- Actually computed binary-to-base-four spread table. -/ +abbrev spread : Fin 128 := 82 +/-- First zero-padded monomial matrix. -/ +abbrev matrixP : Fin 128 := 83 +/-- Second zero-padded monomial matrix, with BC transposed. -/ +abbrev matrixQ : Fin 128 := 84 +/-- Actual signed-coefficient product. -/ +abbrev matrixR : Fin 128 := 85 +/-- Reused scalar/recursive multiplication scratch, followed by explicit frames. -/ +abbrev productScratch : Fin 128 := 86 +/-- Outer table, matrix or pair index. -/ +abbrev index : Fin 128 := 87 +/-- Inner coefficient index. -/ +abbrev coefficient : Fin 128 := 88 +/-- Computed row. -/ +abbrev row : Fin 128 := 89 +/-- Computed column. -/ +abbrev column : Fin 128 := 90 +/-- Computed Morton index. -/ +abbrev position : Fin 128 := 91 +/-- Spread of the current row. -/ +abbrev rowSpread : Fin 128 := 92 +/-- Spread of the current column. -/ +abbrev columnSpread : Fin 128 := 93 +/-- Base of one coefficient block. -/ +abbrev block : Fin 128 := 94 +/-- Actual unsigned residue, or the out-of-range padding sentinel p. -/ +abbrev residue : Fin 128 := 95 +/-- Actual indexed address. -/ +abbrev address : Fin 128 := 96 +/-- Arithmetic / loaded-word temporary. -/ +abbrev temporary : Fin 128 := 97 +/-- Canonical signed encoding of the positive modulus. -/ +abbrev encodedModulus : Fin 128 := 98 +/-- Current unsigned residue-array pointer. -/ +abbrev residueSource : Fin 128 := 99 +/-- Current coefficient-matrix pointer. -/ +abbrev destination : Fin 128 := 100 +/-- Number of padded matrix cells. -/ +abbrev paddedCells : Fin 128 := 101 + +end Reg + +/-- Real constant initialization and the existing native ceiling logarithm call. -/ +@[expose] def logarithm : Code .standard 128 := ram[ + Reg.zero ← #0; Reg.one ← #1; Reg.two ← #2; Reg.four ← #4; + Reg.low0 ← Reg.side; ⟪NatClog.program.rename (Fin.castLE (by decide))⟫; + Reg.depth ← Reg.low1 ] + +/-- Runtime powers, lengths and every actual workspace pointer. -/ +@[expose] def layout : Code .standard 128 := ram[ + Reg.low2 ← Reg.two; Reg.low3 ← Reg.depth; + ⟪Power.program.rename (Fin.castLE (by decide))⟫; Reg.paddedSide ← Reg.low0; + Reg.cells ← Reg.side * Reg.side; + Reg.paddedCells ← Reg.paddedSide * Reg.paddedSide; + Reg.area ← Reg.paddedCells * Reg.modulus; + Reg.encodedModulus ← Reg.modulus * Reg.two; + Reg.residueAB ← Reg.scratch; Reg.residueBC ← Reg.residueAB + Reg.cells; + Reg.residueAC ← Reg.residueBC + Reg.cells; Reg.spread ← Reg.residueAC + Reg.cells; + Reg.matrixP ← Reg.spread + Reg.paddedSide; Reg.matrixQ ← Reg.matrixP + Reg.area; + Reg.matrixR ← Reg.matrixQ + Reg.area; Reg.productScratch ← Reg.matrixR + Reg.area ] + +/-- Call the existing actual signed-residue array loop on one original matrix. -/ +@[expose] def residues (src dst : Fin 128) : Code .standard 128 := + .seq (ram[ Reg.low9 ← src; Reg.low10 ← dst; Reg.low11 ← Reg.cells; + Reg.low12 ← Reg.encodedModulus ]) + (ThreeSum.ChanHe.Residues.program.rename (Fin.castLE (by decide))) + +/-- Initialize the first actual spread-table word and its next unwritten index. -/ +@[expose] def spreadSetup : Code .standard 128 := ram[ + Reg.address ← Reg.spread; *Reg.address ← Reg.zero; Reg.index ← #1 ] + +/-- Compute one spread-table entry from its strictly earlier half-index entry. -/ +@[expose] def spreadStep : Code .standard 128 := ram[ + Reg.temporary ← Reg.index / Reg.two; Reg.address ← Reg.spread + Reg.temporary; + Reg.temporary ← *Reg.address; Reg.temporary ← Reg.temporary * Reg.four; + Reg.position ← Reg.index % Reg.two; Reg.temporary ← Reg.temporary + Reg.position; + Reg.address ← Reg.spread + Reg.index; *Reg.address ← Reg.temporary; + Reg.index ← Reg.index + Reg.one ] + +/-- Compute the whole spread table; no table is supplied by the caller. -/ +@[expose] def spreadTable : Code .standard 128 := + .seq spreadSetup (.loop (.lt Reg.index Reg.paddedSide) spreadStep) + +/-- Load actual row/column spread words and interleave their positions. -/ +@[expose] def morton : Code .standard 128 := ram[ + Reg.address ← Reg.spread + Reg.row; Reg.rowSpread ← *Reg.address; + Reg.address ← Reg.spread + Reg.column; Reg.columnSpread ← *Reg.address; + Reg.position ← Reg.rowSpread * Reg.two; Reg.position ← Reg.position + Reg.columnSpread ] + +/-- Compute the true row-major source position, with the actual BC transpose when requested. -/ +@[expose] def readResidue (transpose : Bool) : Code .standard 128 := + .seq (if transpose then + ram[ Reg.temporary ← Reg.column * Reg.side; Reg.temporary ← Reg.temporary + Reg.row ] + else ram[ Reg.temporary ← Reg.row * Reg.side; + Reg.temporary ← Reg.temporary + Reg.column ]) + (ram[ Reg.address ← Reg.residueSource + Reg.temporary; Reg.residue ← *Reg.address ]) + +/-- Store one actual canonical signed monomial coefficient and advance its index. -/ +@[expose] def writeCoefficient : Code .standard 128 := ram[ + Reg.address ← Reg.block + Reg.coefficient; + if Reg.coefficient = Reg.residue then [ *Reg.address ← Reg.two ] + else [ *Reg.address ← Reg.zero ]; + Reg.coefficient ← Reg.coefficient + Reg.one ] + +/-- Write one whole actual monomial coefficient block, including each padding zero. -/ +@[expose] def writeCoefficients : Code .standard 128 := + .seq (ram[ Reg.coefficient ← #0 ]) + (.loop (.lt Reg.coefficient Reg.modulus) writeCoefficient) + +/-- Guard the real residue read and use the out-of-range key p for every padded cell. -/ +@[expose] def matrixResidue (transpose : Bool) : Code .standard 128 := ram[ + if Reg.row < Reg.side then [ + if Reg.column < Reg.side then [ ⟪readResidue transpose⟫ ] + else [ Reg.residue ← Reg.modulus ] ] + else [ Reg.residue ← Reg.modulus ] ] + +/-- One real padded matrix cell: compute coordinates/Morton address, select the admitted +residue or padding sentinel, write every coefficient, and advance the original cell index. -/ +@[expose] def matrixStep (transpose : Bool) : Code .standard 128 := + .seq (ram[ Reg.row ← Reg.index / Reg.paddedSide; + Reg.column ← Reg.index % Reg.paddedSide ]) + (.seq morton (.seq (ram[ Reg.block ← Reg.position * Reg.modulus; + Reg.block ← Reg.destination + Reg.block ]) + (.seq (matrixResidue transpose) (.seq writeCoefficients + (ram[ Reg.index ← Reg.index + Reg.one ]))))) + +/-- Create a complete zero-padded cyclic matrix from real residue words. -/ +@[expose] def matrix (src dst : Fin 128) (transpose : Bool) : Code .standard 128 := ram[ + Reg.residueSource ← src; Reg.destination ← dst; Reg.index ← #0; + while Reg.index < Reg.paddedCells do [ ⟪matrixStep transpose⟫ ] ] + +/-- Invoke the actual fixed Strassen controller in the low 64-register bank. -/ +@[expose] def product : Code .standard 128 := ram[ + Reg.low32 ← Reg.matrixP; Reg.low33 ← Reg.matrixQ; Reg.low34 ← Reg.matrixR; + Reg.low35 ← Reg.productScratch; Reg.low36 ← Reg.depth; Reg.low46 ← Reg.modulus; + ⟪Strassen.program.rename (Fin.castLE (by decide))⟫ ] + +/-- One actual selected-coefficient read and unsigned count accumulation. -/ +@[expose] def countStep : Code .standard 128 := + .seq (ram[ Reg.row ← Reg.index / Reg.side; Reg.column ← Reg.index % Reg.side ]) + (.seq morton (.seq (ram[ + Reg.address ← Reg.residueAB + Reg.index; Reg.residue ← *Reg.address; + if Reg.residue = Reg.zero then [ Reg.residue ← #0 ] + else [ Reg.residue ← Reg.modulus - Reg.residue ] ]) + (ram[ Reg.block ← Reg.position * Reg.modulus; + Reg.address ← Reg.matrixR + Reg.block; Reg.address ← Reg.address + Reg.residue; + Reg.temporary ← *Reg.address; Reg.temporary ← Reg.temporary / Reg.two; + Reg.result ← Reg.result + Reg.temporary; Reg.index ← Reg.index + Reg.one ]))) + +/-- Read and add exactly one coefficient for each original AB pair. -/ +@[expose] def count : Code .standard 128 := ram[ + Reg.result ← #0; Reg.index ← #0; + while Reg.index < Reg.cells do [ ⟪countStep⟫ ] ] + +/-- Compute the actual logarithm and all runtime layout metadata. -/ +@[expose] def setup : Code .standard 128 := .seq logarithm layout + +/-- Compute all three original residue matrices by actual signed-remainder calls. -/ +@[expose] def residueArrays : Code .standard 128 := ram[ + ⟪residues Reg.inputAB Reg.residueAB⟫; ⟪residues Reg.inputBC Reg.residueBC⟫; + ⟪residues Reg.inputAC Reg.residueAC⟫ ] + +/-- Compute the spread table and both complete padded coefficient matrices. -/ +@[expose] def coefficientMatrices : Code .standard 128 := ram[ + ⟪spreadTable⟫; ⟪matrix Reg.residueAC Reg.matrixP false⟫; + ⟪matrix Reg.residueBC Reg.matrixQ true⟫ ] + +/-- Concrete runtime-parameter counter; every multiplication is actual native code. -/ +@[expose] def program : Code .standard 128 := ram[ + ⟪setup⟫; + ⟪residueArrays⟫; ⟪coefficientMatrices⟫; + ⟪product⟫; ⟪count⟫ ] + +/-- Ordinary initialized input: raw `(n,p)`, followed by the three canonical signed arrays. -/ +@[expose] def input : Code .standard 128 := ram[ + Reg.address ← #1; Reg.side ← *Reg.address; Reg.address ← #2; Reg.modulus ← *Reg.address; + Reg.cells ← Reg.side * Reg.side; Reg.inputAB ← #3; + Reg.inputBC ← Reg.inputAB + Reg.cells; Reg.inputAC ← Reg.inputBC + Reg.cells; + Reg.scratch ← Reg.inputAC + Reg.cells ] + +/-- Fixed initialized counter code, independent of dimensions, modulus and physical width. -/ +@[expose] def entryProgram : Code .standard 128 := .seq input program + +/-- The complete counter is one finite standard native program with no word-width reads. -/ +theorem program_fixed : (entryProgram.program Reg.result).length ≤ 32768 ∧ + ¬(entryProgram.program Reg.result).ReadsWidth := by + constructor + · decide +kernel + · apply Code.not_readsWidth_program + decide +kernel + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/ResidueArray.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/ResidueArray.lean new file mode 100644 index 0000000000..cc1a83407d --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/ResidueArray.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.CountPrime.Program + +/-! +# Real counter residue-array calls + +Four charged argument copies invoke the existing signed-remainder loop. Every resulting +residue is an unsigned key; canonical signed decoding is performed inside the actual callee. +The original high-bank metadata and all memory outside the destination are preserved. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Complete actual residue call for a matrix, with source and destination parameters in the +protected metadata bank. No residue array is supplied to the algorithm. -/ +theorem exec_residues {w n p a b : ℕ} (src dst : Fin 128) (x : Data 128) + (hsrc : 64 ≤ src.val) (hdst : 64 ≤ dst.val) (values : ℕ → ℤ) + (hp : 0 < p) (hc : x.regs Reg.cells = n * n) + (ha : x.regs src = a) (hb : x.regs dst = b) + (hm : x.regs Reg.encodedModulus = encode (p : ℤ)) + (hmod : GenericRAM.Signed.Fits w (p : ℤ)) + (hv : ∀ i < n * n, GenericRAM.Signed.Fits w (values i)) + (haf : Fits (w + 2) (a + n * n)) (hbf : Fits (w + 2) (b + n * n)) + (hdisjoint : a + n * n ≤ b) + (hinput : ∀ i < n * n, x.mem (a + i) = encode (values i)) : + ∃ y, ExecWithin (w + 2) (residues src dst) x y (23 * (n * n) + 8) ∧ + (∀ i < n * n, y.mem (b + i) = (values i % (p : ℤ)).toNat) ∧ + (∀ d, d < b ∨ b + n * n ≤ d → y.mem d = x.mem d) ∧ + ∀ j, 16 ≤ j.val → y.regs j = x.regs j := by + let z := (((x.set Reg.low9 a).set Reg.low10 b).set Reg.low11 (n * n)).set + Reg.low12 (encode (p : ℤ)) + have args : ExecWithin (B := .standard) (w + 2) + (ram[ Reg.low9 ← src; Reg.low10 ← dst; Reg.low11 ← Reg.cells; + Reg.low12 ← Reg.encodedModulus ]) x z 4 := by + have hs : src ≠ Reg.low9 := by intro he; subst src; contradiction + have hd9 : dst ≠ Reg.low9 := by intro he; subst dst; contradiction + have hd10 : dst ≠ Reg.low10 := by intro he; subst dst; contradiction + ram_eval [z, ha, hb, hc, hm, hs, hd9, hd10, Reg.low9, Reg.low10, Reg.low11, Reg.low12, + Reg.cells, Reg.encodedModulus] + let f : Fin 16 → Fin 128 := Fin.castLE (by decide) + have za : (z.comap f).regs ThreeSum.ChanHe.Residues.src = a := by + simp +decide [z, f, Data.comap, ThreeSum.ChanHe.Residues.src, + Reg.low9, Reg.low10, Reg.low11, Reg.low12] + have zb : (z.comap f).regs ThreeSum.ChanHe.Residues.dst = b := by + simp +decide [z, f, Data.comap, ThreeSum.ChanHe.Residues.dst, + Reg.low10, Reg.low11, Reg.low12] + have zl : (z.comap f).regs ThreeSum.ChanHe.Residues.len = n * n := by + simp +decide [z, f, Data.comap, ThreeSum.ChanHe.Residues.len, Reg.low11, Reg.low12] + have zp : (z.comap f).regs ThreeSum.ChanHe.Residues.modulus = encode (p : ℤ) := by + simp [z, f, Data.comap, ThreeSum.ChanHe.Residues.modulus, Reg.low12] + obtain ⟨s, he, memory, _⟩ := ThreeSum.ChanHe.Residues.exec (z.comap f) values hp hmod zp + (by simpa only [zl] using hv) (by simpa only [za, zl] using haf) + (by simpa only [zb, zl] using hbf) (by simpa only [za, zb, zl] using hdisjoint) + (by simpa only [za, zl, show (z.comap f).mem = x.mem from rfl] using hinput) + let y := s.extend f z + have call : ExecWithin (w + 2) + (ThreeSum.ChanHe.Residues.program.rename f) z y (23 * (n * n) + 4) := by + simpa only [zl] using he.rename (Fin.castLE_injective _) + have full : ExecWithin (w + 2) (residues src dst) x y (23 * (n * n) + 8) := + (args.seq call).mono (by omega) + have ym : y.mem = ThreeSum.ChanHe.Residues.memory x.mem values p b (n * n) := by + change s.mem = _ + simpa only [zl, zb, show (z.comap f).mem = x.mem from rfl] using memory + refine ⟨y, full, ?_, ?_, ?_⟩ + · intro i hi + rw [ym] + simp only [ThreeSum.ChanHe.Residues.memory, show b ≤ b + i ∧ b + i < b + n * n by omega, + and_self, ite_true, Nat.add_sub_cancel_left] + · intro d hd + rw [ym] + simp only [ThreeSum.ChanHe.Residues.memory, show ¬(b ≤ d ∧ d < b + n * n) by omega, + ite_false] + · intro j hj + have hyj : y.regs j = z.regs j := by + apply Function.extend_apply' + rintro ⟨q, heq⟩ + have heqv := congrArg Fin.val heq + change q.val = j.val at heqv + omega + apply hyj.trans + simp only [z, Data.set, + Function.update_of_ne (show j ≠ Reg.low9 by intro h; subst j; contradiction), + Function.update_of_ne (show j ≠ Reg.low10 by intro h; subst j; contradiction), + Function.update_of_ne (show j ≠ Reg.low11 by intro h; subst j; contradiction), + Function.update_of_ne (show j ≠ Reg.low12 by intro h; subst j; contradiction)] + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/ResidueArrays.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/ResidueArrays.lean new file mode 100644 index 0000000000..06751c48e2 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/ResidueArrays.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 +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Setup +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.ResidueArray + +/-! +# Complete three-matrix native residue prefix + +The three genuine calls preserve all original arrays below the computed workspace and each +other's already written output. All argument copies, signed remainder, decoding, stores and +loop controls are charged. This is the first memory-producing stage of the complete counter. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Compute all original unsigned residue words using only actual caller matrix data. -/ +theorem exec_residueArrays {w n p k scratch a b c : ℕ} (x : Data 128) + (u v z : ℕ → ℤ) (hp : 0 < p) (ready : CounterRegisters n p k scratch a b c x) + (ha : a + n * n ≤ scratch) (hb : b + n * n ≤ scratch) (hc : c + n * n ≤ scratch) + (hmod : GenericRAM.Signed.Fits w (p : ℤ)) + (hu : ∀ i < n * n, GenericRAM.Signed.Fits w (u i)) + (hv : ∀ i < n * n, GenericRAM.Signed.Fits w (v i)) + (hz : ∀ i < n * n, GenericRAM.Signed.Fits w (z i)) + (hinU : ∀ i < n * n, x.mem (a + i) = encode (u i)) + (hinV : ∀ i < n * n, x.mem (b + i) = encode (v i)) + (hinZ : ∀ i < n * n, x.mem (c + i) = encode (z i)) + (hw : Fits (w + 2) (addressBudget n p k scratch a b c)) : + ∃ y, ExecWithin (w + 2) residueArrays x y (69 * (n * n) + 24) ∧ + CounterRegisters n p k scratch a b c y ∧ + (∀ i < n * n, y.mem (scratch + i) = (u i % (p : ℤ)).toNat) ∧ + (∀ i < n * n, y.mem (scratch + n * n + i) = (v i % (p : ℤ)).toNat) ∧ + (∀ i < n * n, y.mem (scratch + 2 * (n * n) + i) = (z i % (p : ℤ)).toNat) ∧ + ∀ d, d < scratch ∨ scratch + 3 * (n * n) ≤ d → y.mem d = x.mem d := by + have h4pos : 0 < 4 ^ k := by positivity + have h2pos : 0 < 2 ^ k := by positivity + have fit (d : ℕ) (hd : d ≤ addressBudget n p k scratch a b c) : Fits (w + 2) d := hw.of_le hd + have encoded (s : Data 128) (hs : CounterRegisters n p k scratch a b c s) : + s.regs Reg.encodedModulus = encode (p : ℤ) := by + rw [hs.layout.encodedModulus_eq] + simp [encode] + obtain ⟨x1, run1, val1, frame1, regs1⟩ := exec_residues Reg.inputAB Reg.residueAB x + (by decide) (by decide) u hp ready.layout.cells_eq ready.inputAB ready.layout.residueAB_eq + (encoded x ready) hmod hu (fit _ (by unfold addressBudget; omega)) + (fit _ (by unfold addressBudget; omega)) ha hinU + have meta1 : CounterRegisters n p k scratch a b c x1 := ready.congr + (fun j hj => regs1 j (by unfold MetadataRegister at hj; omega)) + obtain ⟨x2, run2, val2, frame2, regs2⟩ := exec_residues Reg.inputBC Reg.residueBC x1 + (by decide) (by decide) v hp meta1.layout.cells_eq meta1.inputBC meta1.layout.residueBC_eq + (encoded x1 meta1) hmod hv (fit _ (by unfold addressBudget; omega)) + (fit _ (by unfold addressBudget; omega)) (by omega) + (by intro i hi; rw [frame1 _ (Or.inl (by omega))]; exact hinV i hi) + have meta2 : CounterRegisters n p k scratch a b c x2 := meta1.congr + (fun j hj => regs2 j (by unfold MetadataRegister at hj; omega)) + obtain ⟨y, run3, val3, frame3, regs3⟩ := exec_residues Reg.inputAC Reg.residueAC x2 + (by decide) (by decide) z hp meta2.layout.cells_eq meta2.inputAC meta2.layout.residueAC_eq + (encoded x2 meta2) hmod hz (fit _ (by unfold addressBudget; omega)) + (fit _ (by unfold addressBudget; omega)) (by omega) + (by + intro i hi + rw [frame2 _ (Or.inl (by omega)), frame1 _ (Or.inl (by omega))] + exact hinZ i hi) + refine ⟨y, (run1.seq (run2.seq run3)).mono (by omega), + meta2.congr (fun j hj => regs3 j (by unfold MetadataRegister at hj; omega)), ?_, ?_, val3, ?_⟩ + · intro i hi + rw [frame3 _ (Or.inl (by omega)), frame2 _ (Or.inl (by omega))] + exact val1 i hi + · intro i hi + rw [frame3 _ (Or.inl (by omega))] + exact val2 i hi + · intro d hd + rw [frame3 _ (hd.imp (fun h => by omega) (fun h => by omega)), + frame2 _ (hd.imp (fun h => by omega) (fun h => by omega)), + frame1 _ (hd.imp id (fun h => by omega))] + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Scan.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Scan.lean new file mode 100644 index 0000000000..03079b2000 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Scan.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.CountPrime.ScanCell + +/-! +# Complete native selected-coefficient scan + +The invariant counts exactly the original row-major prefix and preserves all memory. The +numeric accumulator bound is derived by summing the nonnegative coefficient bound, rather +than assuming that the final answer fits. This is an internal composition contract; the +counter derives its residue and product readback from its actual preceding executions. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM Cslib.WordRAM.Signed + +private theorem scan_sum_le {n t : ℕ} (values : ℕ → ℕ) + (hv : ∀ i < t, values i ≤ n) : ∑ i ∈ Finset.range t, values i ≤ t * n := by + calc + _ ≤ ∑ _i ∈ Finset.range t, n := Finset.sum_le_sum fun i hi => hv i (Finset.mem_range.mp hi) + _ = t * n := by simp + +/-- Execute the complete original-pair scan, including initialization, every coefficient +load/decoding/addition, all loop controls and exit. No memory word is changed. -/ +theorem exec_count {w n p k : ℕ} (x : Data 128) (keys values : ℕ → ℕ) + (hn : x.regs Reg.side = n) (hp : x.regs Reg.modulus = p) + (hc : x.regs Reg.cells = n * n) (hpadding : n ≤ 2 ^ k) + (hkeys : ∀ i < n * n, keys i < p) (hbound : ∀ i < n * n, values i ≤ n) + (hresidues : ∀ i < n * n, x.mem (x.regs Reg.residueAB + i) = keys i) + (hproduct : ∀ i < n * n, + x.mem (x.regs Reg.matrixR + Nat.Morton.index (i / n) (i % n) * p + + (if keys i = 0 then 0 else p - keys i)) = encode (values i : ℤ)) + (h0 : x.regs Reg.zero = 0) (h1 : x.regs Reg.one = 1) (h2 : x.regs Reg.two = 2) + (htable : ∀ j < 2 ^ k, x.mem (x.regs Reg.spread + j) = Nat.Morton.spread j) + (hw : Fits w (x.regs Reg.spread + x.regs Reg.residueAB + x.regs Reg.matrixR + + 4 ^ k * p + 4 ^ k + 2 ^ k + n * n * n + n * n + p + 16)) : + ∃ y, ExecWithin w count x y (22 * (n * n) + 4) ∧ y.mem = x.mem ∧ + y.regs Reg.result = ∑ i ∈ Finset.range (n * n), values i ∧ + ∀ j, ¬ScanLocal j → y.regs j = x.regs j := by + let I (y : Data 128) : Prop := + (∀ j, ¬ScanLocal j → y.regs j = x.regs j) ∧ y.mem = x.mem ∧ + y.regs Reg.index ≤ n * n ∧ + y.regs Reg.result = ∑ i ∈ Finset.range (y.regs Reg.index), values i + have body (y : Data 128) (hy : I y) (hcond : (Cond.lt Reg.index Reg.cells).Holds y) : + ∃ z, ExecWithin w countStep y z 20 ∧ I z ∧ + n * n - z.regs Reg.index < n * n - y.regs Reg.index := by + obtain ⟨regs, memory, index, result⟩ := hy + have keep (j : Fin 128) (hj : ¬ScanLocal j) := regs j hj + have hlt : y.regs Reg.index < n * n := by + simpa only [Cond.holds_lt, keep Reg.cells (by decide), hc] using hcond + obtain ⟨z, he, zm, zi, zr, keepz⟩ := exec_countStep y + ((keep _ (by decide)).trans hn) ((keep _ (by decide)).trans hp) rfl hlt hpadding + (by rw [memory, keep Reg.residueAB (by decide)]; exact hresidues _ hlt) + (hkeys _ hlt) + (by rw [memory, keep Reg.matrixR (by decide)]; exact hproduct _ hlt) + (hbound _ hlt) result + (scan_sum_le values (fun i hi => hbound i (hi.trans hlt))) + ((keep _ (by decide)).trans h0) ((keep _ (by decide)).trans h1) + ((keep _ (by decide)).trans h2) + (by rw [memory, keep Reg.spread (by decide)]; exact htable) + (by simpa only [keep Reg.spread (by decide), keep Reg.residueAB (by decide), + keep Reg.matrixR (by decide)] using hw) + refine ⟨z, he, ⟨fun j hj => (keepz j hj).trans (regs j hj), zm.trans memory, + by omega, ?_⟩, by omega⟩ + rw [zi, Finset.sum_range_succ, zr] + let start := (x.set Reg.result 0).set Reg.index 0 + have init : ExecWithin (B := .standard) w + (ram[ Reg.result ← #0; Reg.index ← #0 ]) x start 2 := by + ram_eval [start, fits_zero w] + have startI : I start := by + refine ⟨?_, rfl, by change 0 ≤ n * n; omega, ?_⟩ + · intro j hj + have hi : j ≠ Reg.index := by intro h; subst j; exact hj (by decide) + have hr : j ≠ Reg.result := by intro h; subst j; exact hj (by decide) + simp only [start, Data.set, Function.update_of_ne hi, Function.update_of_ne hr] + · change 0 = ∑ i ∈ Finset.range 0, values i + simp + obtain ⟨y, run, hy, stopped⟩ := ExecWithin.loop_invariant I + (fun y => n * n - y.regs Reg.index) body startI + obtain ⟨regs, memory, index, result⟩ := hy + have ended : y.regs Reg.index = n * n := by + simp only [Cond.holds_lt, regs Reg.cells (by decide), hc] at stopped + omega + have full : ExecWithin w count x y (22 * (n * n) + 4) := by + obtain ⟨i, hi, he⟩ := init + obtain ⟨j, hj, hf⟩ := run + cases he with + | seq ha hb => + exact ⟨_, by change j ≤ (n * n - 0) * (20 + 2) + 2 at hj; omega, + .seq ha (.seq hb hf)⟩ + exact ⟨y, full, memory, by simpa only [ended] using result, regs⟩ + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/ScanCell.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/ScanCell.lean new file mode 100644 index 0000000000..7852cb510b --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/ScanCell.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.CountPrime.Access +public import Complexity.Computability.WordRAM.Signed.Encoding + +/-! +# Actual selected-coefficient read and accumulation + +This internal stage reads the computed AB residue, computes its additive inverse modulo p, +loads the actual cyclic-product coefficient and decodes its nonnegative canonical word. +The complete counter derives this readback from its real residue and Strassen executions. +No instruction performs a free coefficient lookup, signed decoding or summation. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- The scan modifies only its result and the shared coordinate/address temporaries. -/ +@[expose] def ScanLocal (j : Fin 128) : Prop := j = Reg.result ∨ 87 ≤ j.val ∧ j.val ≤ 97 + +instance (j : Fin 128) : Decidable (ScanLocal j) := + inferInstanceAs (Decidable (j = Reg.result ∨ 87 ≤ j.val ∧ j.val ≤ 97)) + +private theorem countStep_preserves (j : Fin 128) (hj : ¬ScanLocal j) : + countStep.Preserves j := by decide +kernel +revert + +/-- One actual selected coefficient, including the real zero/nonzero residue branch and +signed-to-unsigned division. The sum and all addresses fit by the displayed numerical bound. -/ +theorem exec_countStep {w n p k i r value total : ℕ} (x : Data 128) + (hn : x.regs Reg.side = n) (hp : x.regs Reg.modulus = p) + (hi : x.regs Reg.index = i) (hlt : i < n * n) (hpadding : n ≤ 2 ^ k) + (hr : x.mem (x.regs Reg.residueAB + i) = r) (hrlt : r < p) + (hvalue : x.mem (x.regs Reg.matrixR + Nat.Morton.index (i / n) (i % n) * p + + (if r = 0 then 0 else p - r)) = encode (value : ℤ)) + (hbound : value ≤ n) (hresult : x.regs Reg.result = total) (htotal : total ≤ i * n) + (h0 : x.regs Reg.zero = 0) (h1 : x.regs Reg.one = 1) (h2 : x.regs Reg.two = 2) + (htable : ∀ j < 2 ^ k, x.mem (x.regs Reg.spread + j) = Nat.Morton.spread j) + (hw : Fits w (x.regs Reg.spread + x.regs Reg.residueAB + x.regs Reg.matrixR + + 4 ^ k * p + 4 ^ k + 2 ^ k + n * n * n + n * n + p + 16)) : + ∃ y, ExecWithin w countStep x y 20 ∧ y.mem = x.mem ∧ + y.regs Reg.index = i + 1 ∧ y.regs Reg.result = total + value ∧ + ∀ j, ¬ScanLocal j → y.regs j = x.regs j := by + have hnpos : 0 < n := by nlinarith + have hrow : i / n < n := (Nat.div_lt_iff_lt_mul hnpos).mpr hlt + have hcol : i % n < n := Nat.mod_lt _ hnpos + have hposition := Nat.Morton.index_lt (hrow.trans_le hpadding) (hcol.trans_le hpadding) + have hprod := Nat.mul_le_mul_right p (Nat.succ_le_of_lt hposition) + rw [Nat.succ_mul] at hprod + let key := if r = 0 then 0 else p - r + have hkey : key < p := by dsimp only [key]; split_ifs <;> omega + have hsum : total + value ≤ n * n * n := by + have he := Nat.mul_le_mul_right n (Nat.succ_le_of_lt hlt) + rw [Nat.succ_mul] at he + omega + have fit (a : ℕ) (ha : a ≤ x.regs Reg.spread + x.regs Reg.residueAB + + x.regs Reg.matrixR + 4 ^ k * p + 4 ^ k + 2 ^ k + n * n * n + n * n + p + 16) : + Fits w a := hw.of_le ha + let z := (x.set Reg.row (i / n)).set Reg.column (i % n) + have coordinates : ExecWithin (B := .standard) w + (ram[ Reg.row ← Reg.index / Reg.side; Reg.column ← Reg.index % Reg.side ]) x z 2 := by + ram_eval [z, hi, hn, fit (i / n) (by omega), fit (i % n) (by omega), + Reg.row, Reg.column, Reg.index, Reg.side] + have zregs (j : Fin 128) (hj : j ≠ Reg.row) (hc : j ≠ Reg.column) : + z.regs j = x.regs j := by simp only [z, Data.set, Function.update_of_ne hj, + Function.update_of_ne hc] + obtain ⟨s, hm, sm, sp, sr⟩ := exec_morton (k := k) z + ((zregs _ (by decide) (by decide)).trans h2) + (by simpa +decide [z, Reg.row, Reg.column] using hrow.trans_le hpadding) + (by simpa +decide [z, Reg.column] using hcol.trans_le hpadding) + (by simpa only [show z.mem = x.mem from rfl, zregs Reg.spread (by decide) (by decide)] + using htable) + (by rw [zregs Reg.spread (by decide) (by decide)]; exact fit _ (by omega)) + have keep (j : Fin 128) (ha : j ≠ Reg.address) (hb : j ≠ Reg.rowSpread) + (hc : j ≠ Reg.columnSpread) (hd : j ≠ Reg.position) + (he : j ≠ Reg.row) (hf : j ≠ Reg.column) : s.regs j = x.regs j := + (sr j ha hb hc hd).trans (zregs j he hf) + have spos : s.regs Reg.position = Nat.Morton.index (i / n) (i % n) := by + simpa +decide [z, Reg.row, Reg.column] using sp + have si := (keep Reg.index (by decide) (by decide) (by decide) (by decide) + (by decide) (by decide)).trans hi + have sa := keep Reg.residueAB (by decide) (by decide) (by decide) (by decide) + (by decide) (by decide) + let t := ((s.set Reg.address (x.regs Reg.residueAB + i)).set Reg.residue r).set Reg.residue key + have read : ExecWithin (B := .standard) w + (ram[ Reg.address ← Reg.residueAB + Reg.index; Reg.residue ← *Reg.address; + if Reg.residue = Reg.zero then [ Reg.residue ← #0 ] + else [ Reg.residue ← Reg.modulus - Reg.residue ] ]) s t 5 := by + have smod := (keep Reg.modulus (by decide) (by decide) (by decide) (by decide) + (by decide) (by decide)).trans hp + have szero := (keep Reg.zero (by decide) (by decide) (by decide) (by decide) + (by decide) (by decide)).trans h0 + by_cases hz : r = 0 + · ram_eval [t, z, key, hz, si, sa, sm, hr, szero, smod, + fit (x.regs Reg.residueAB + i) (by omega), fits_zero w, + Reg.residueAB, Reg.index, Reg.address, Reg.residue, Reg.zero, Reg.modulus] + · ram_eval [t, z, key, hz, si, sa, sm, hr, szero, smod, + fit (x.regs Reg.residueAB + i) (by omega), fit (p - r) (by omega), + Reg.residueAB, Reg.index, Reg.address, Reg.residue, Reg.zero, Reg.modulus] + let position := Nat.Morton.index (i / n) (i % n) + let addr := x.regs Reg.matrixR + position * p + key + let y := (((((((t.set Reg.block (position * p)).set Reg.address + (x.regs Reg.matrixR + position * p)).set Reg.address addr).set Reg.temporary + (encode (value : ℤ))).set Reg.temporary value).set Reg.result (total + value)).set + Reg.index (i + 1)) + have suffix : ExecWithin (B := .standard) w + (ram[ Reg.block ← Reg.position * Reg.modulus; + Reg.address ← Reg.matrixR + Reg.block; Reg.address ← Reg.address + Reg.residue; + Reg.temporary ← *Reg.address; Reg.temporary ← Reg.temporary / Reg.two; + Reg.result ← Reg.result + Reg.temporary; Reg.index ← Reg.index + Reg.one ]) t y 7 := by + have kv : encode (value : ℤ) / 2 = value := by simp [encode_div_two] + ram_eval [t, y, z, key, position, addr, spos, sm, hvalue, kv, si, + keep Reg.modulus (by decide) (by decide) (by decide) (by decide) (by decide) (by decide), hp, + keep Reg.matrixR (by decide) (by decide) (by decide) (by decide) (by decide) (by decide), + keep Reg.two (by decide) (by decide) (by decide) (by decide) (by decide) (by decide), h2, + keep Reg.one (by decide) (by decide) (by decide) (by decide) (by decide) (by decide), h1, + keep Reg.result (by decide) (by decide) (by decide) (by decide) + (by decide) (by decide), hresult, + fit (position * p) (by dsimp [position]; omega), + fit (x.regs Reg.matrixR + position * p) (by dsimp [position]; omega), + fit addr (by dsimp [addr, position]; omega), fit value (by omega), + fit (total + value) (by omega), fit (i + 1) (by omega), + Reg.block, Reg.position, Reg.modulus, Reg.matrixR, Reg.address, Reg.residue, + Reg.temporary, Reg.two, Reg.result, Reg.index, Reg.one] + have he : ExecWithin w countStep x y 20 := coordinates.seq (hm.seq (read.seq suffix)) + exact ⟨y, he, sm, rfl, rfl, fun j hj => he.regs_eq (countStep_preserves j hj)⟩ + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/ScanReadback.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/ScanReadback.lean new file mode 100644 index 0000000000..fe43b6de3c --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/ScanReadback.lean @@ -0,0 +1,95 @@ +/- +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.CountPrime.Scan +public import Complexity.Algorithms.ExactTriangle.CountPrime.Extraction + +/-! +# Actual selected-product scan yields the existing modular triangle count + +The signed original weights determine the residue keys and the actual seven-product +coefficient array. Nonnegative coefficient bounds justify native unsigned decoding and every +prefix sum. The caller counter constructs these arrays with its proved native stages; they +are not additional algorithm inputs or an assumed multiplication/counting implementation. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Scan actual computed residue/product readback and obtain precisely the existing modular +triangle count, with original row-major coordinates, zero padding and both residue branches. -/ +theorem exec_count_coefficients {w n p k : ℕ} (x : Data 128) + (wAB wBC wAC : Fin n → Fin n → ℤ) (hpositive : 0 < p) (hpadding : n ≤ 2 ^ k) + (hn : x.regs Reg.side = n) (hp : x.regs Reg.modulus = p) + (hc : x.regs Reg.cells = n * n) + (hresidue : ∀ a b : Fin n, x.mem (x.regs Reg.residueAB + a.val * n + b.val) = + (wAB a b % (p : ℤ)).toNat) + (hproduct : ∀ i < 4 ^ k * p, x.mem (x.regs Reg.matrixR + i) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.multiply p k + (Cslib.Algorithms.ExactTriangle.CountPrime.pCoefficients p wAC) + (Cslib.Algorithms.ExactTriangle.CountPrime.qCoefficients p wBC) i)) + (h0 : x.regs Reg.zero = 0) (h1 : x.regs Reg.one = 1) (h2 : x.regs Reg.two = 2) + (htable : ∀ j < 2 ^ k, x.mem (x.regs Reg.spread + j) = Nat.Morton.spread j) + (hw : Fits w (x.regs Reg.spread + x.regs Reg.residueAB + x.regs Reg.matrixR + + 4 ^ k * p + 4 ^ k + 2 ^ k + n * n * n + n * n + p + 16)) : + ∃ y, ExecWithin w count x y (22 * (n * n) + 4) ∧ y.mem = x.mem ∧ + y.regs Reg.result = Cslib.Algorithms.ExactTriangle.countZeroMod wAB wBC wAC p ∧ + ∀ j, ¬ScanLocal j → y.regs j = x.regs j := by + let keys (i : ℕ) : ℕ := if h : i < n * n then + let q := finProdFinEquiv.symm (⟨i, h⟩ : Fin (n * n)) + (wAB q.1 q.2 % (p : ℤ)).toNat else 0 + let values := Cslib.Algorithms.ExactTriangle.CountPrime.rowMajorCoefficient p k wAB wBC wAC + have keys_at (i : ℕ) (hi : i < n * n) : keys i = + (wAB (finProdFinEquiv.symm (⟨i, hi⟩ : Fin (n * n))).1 + (finProdFinEquiv.symm (⟨i, hi⟩ : Fin (n * n))).2 % (p : ℤ)).toNat := by + simp only [keys, dite_eq_left hi] + have residue (i : ℕ) (hi : i < n * n) : + x.mem (x.regs Reg.residueAB + i) = keys i := by + let q := finProdFinEquiv.symm (⟨i, hi⟩ : Fin (n * n)) + have index : q.1.val * n + q.2.val = i := by + change i / n * n + i % n = i + simpa only [Nat.mul_comm] using Nat.div_add_mod i n + rw [keys_at i hi] + change x.mem (x.regs Reg.residueAB + i) = (wAB q.1 q.2 % (p : ℤ)).toNat + simpa only [Nat.add_assoc, index] using hresidue q.1 q.2 + have product (i : ℕ) (hi : i < n * n) : + x.mem (x.regs Reg.matrixR + Nat.Morton.index (i / n) (i % n) * p + + (if keys i = 0 then 0 else p - keys i)) = encode (values i : ℤ) := by + let q := finProdFinEquiv.symm (⟨i, hi⟩ : Fin (n * n)) + have qr : q.1.val = i / n := rfl + have qc : q.2.val = i % n := rfl + have key : keys i = (wAB q.1 q.2 % (p : ℤ)).toNat := keys_at i hi + have val : values i = Cslib.Algorithms.ExactTriangle.CountPrime.pairCoefficient + p k wAB wBC wAC q.1 q.2 := by + simp only [values, Cslib.Algorithms.ExactTriangle.CountPrime.rowMajorCoefficient, + dite_eq_left hi] + rfl + have hidx := Nat.Morton.index_lt (q.1.isLt.trans_le hpadding) (q.2.isLt.trans_le hpadding) + have hr : ((-wAB q.1 q.2) % (p : ℤ)).toNat < p := Int.natMod_lt hpositive.ne' + have hb := Nat.mul_le_mul_right p (Nat.succ_le_of_lt hidx) + rw [Nat.succ_mul] at hb + have entry := hproduct (Nat.Morton.index q.1 q.2 * p + + ((-wAB q.1 q.2) % (p : ℤ)).toNat) ((Nat.add_lt_add_left hr _).trans_le hb) + rw [← Cslib.Algorithms.ExactTriangle.CountPrime.pairCoefficient_cast + hpositive.ne' hpadding wAB wBC wAC q.1 q.2] at entry + simpa only [Nat.add_assoc, qr, qc, val, key, Int.toNat_neg_emod hpositive] using entry + obtain ⟨y, he, memory, result, regs⟩ := exec_count x keys values hn hp hc hpadding + (by intro i hi; rw [keys_at i hi]; exact Int.natMod_lt hpositive.ne') + (fun _ _ => Cslib.Algorithms.ExactTriangle.CountPrime.rowMajorCoefficient_le + hpositive.ne' hpadding wAB wBC wAC) + residue product h0 h1 h2 htable hw + refine ⟨y, he, memory, ?_, regs⟩ + rw [result] + exact Cslib.Algorithms.ExactTriangle.CountPrime.sum_rowMajorCoefficient + hpositive.ne' hpadding wAB wBC wAC + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Setup.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Setup.lean new file mode 100644 index 0000000000..3c964c802b --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Setup.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.ExactTriangle.CountPrime.Layout +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Logarithm + +/-! +# Computed counter metadata and native setup + +The actual logarithm and power routines determine the padding and every pointer. The internal +register predicate records that execution's resulting words; it is not an algorithm argument +or a supplied layout. Later native stages preserve these metadata registers. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM + +/-- Original scalar/pointer envelope for actual address, index, length and count arithmetic. +The signed arithmetic magnitude envelope is accounted for separately. -/ +@[expose] def addressBudget (n p k scratch a b c : ℕ) : ℕ := + 32 * (a + b + c + scratch + n * n * n + n * n + 4 ^ k * p + 4 ^ k + + 2 ^ k + k + p + n + 4) + +/-- Exactly the persistent counter metadata, excluding its mutable raw result. -/ +@[expose] def MetadataRegister (j : Fin 128) : Prop := + 64 ≤ j.val ∧ (j.val ≤ 86 ∨ j.val = 98 ∨ j.val = 101) ∧ j ≠ Reg.result + +instance (j : Fin 128) : Decidable (MetadataRegister j) := + inferInstanceAs + (Decidable (64 ≤ j.val ∧ (j.val ≤ 86 ∨ j.val = 98 ∨ j.val = 101) ∧ j ≠ Reg.result)) + +/-- The actual computed layout, protected original pointers and initialized constants. -/ +structure CounterRegisters (n p k scratch a b c : ℕ) (x : Data 128) : Prop where + /-- Computed powers, matrix lengths and all workspace pointers. -/ + layout : LayoutRegisters n p k scratch x + /-- Original AB pointer. -/ + inputAB : x.regs Reg.inputAB = a + /-- Original BC pointer. -/ + inputBC : x.regs Reg.inputBC = b + /-- Original AC pointer. -/ + inputAC : x.regs Reg.inputAC = c + /-- Actual constant zero. -/ + zero : x.regs Reg.zero = 0 + /-- Actual constant one. -/ + one : x.regs Reg.one = 1 + /-- Raw two, also the canonical signed word for one. -/ + two : x.regs Reg.two = 2 + /-- Actual constant four. -/ + four : x.regs Reg.four = 4 + +/-- Native stages that leave the persistent metadata unchanged retain its computed meaning. -/ +theorem CounterRegisters.congr {n p k scratch a b c : ℕ} {x y : Data 128} + (h : CounterRegisters n p k scratch a b c x) + (keep : ∀ j, MetadataRegister j → y.regs j = x.regs j) : + CounterRegisters n p k scratch a b c y := by + rcases h with ⟨L, ha, hb, hc, h0, h1, h2, h4⟩ + refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · cases L + constructor <;> rwa [keep _ (by decide)] + all_goals rwa [keep _ (by decide)] + +/-- Execute the actual depth calculation and layout, with every argument, constant, power and +pointer instruction charged. The input contains no padding-depth or layout certificate. -/ +theorem exec_setup {W n p scratch a b c : ℕ} (x : Data 128) (hp0 : 0 < p) + (hn : x.regs Reg.side = n) (hp : x.regs Reg.modulus = p) + (ha : x.regs Reg.inputAB = a) (hb : x.regs Reg.inputBC = b) + (hc : x.regs Reg.inputAC = c) (hs : x.regs Reg.scratch = scratch) + (hw : Fits W (layoutWordBound n p (Nat.clog 2 n) scratch + n + Nat.clog 2 n + 8)) : + ∃ y, ExecWithin W setup x y (9 * Nat.clog 2 n + 31) ∧ + CounterRegisters n p (Nat.clog 2 n) scratch a b c y ∧ y.mem = x.mem := by + let k := Nat.clog 2 n + obtain ⟨s, logRun, depth, h0, h1, h2, h4, memory, keep⟩ := exec_logarithm x + (by rw [hn]; exact hw.of_le (by omega)) + have sreg (j : Fin 128) (hj : MetadataRegister j) (hd : j ≠ Reg.depth) + (hz : j ≠ Reg.zero) (ho : j ≠ Reg.one) (ht : j ≠ Reg.two) + (hf : j ≠ Reg.four) : s.regs j = x.regs j := + keep j (by unfold MetadataRegister at hj; omega) hd hz ho ht hf + have sk : s.regs Reg.depth = k := by simpa only [hn] using depth + obtain ⟨y, layoutRun, L, ym, yr⟩ := exec_layout s hp0 + ((sreg _ (by decide) (by decide) (by decide) (by decide) (by decide) (by decide)).trans hn) + ((sreg _ (by decide) (by decide) (by decide) (by decide) (by decide) (by decide)).trans hp) + sk + ((sreg _ (by decide) (by decide) (by decide) (by decide) (by decide) (by decide)).trans hs) + h2 (hw.of_le (by dsimp only [k]; omega)) + have preserved (j : Fin 128) (hj : ¬LayoutLocal j) : y.regs j = s.regs j := yr j hj + refine ⟨y, ?_, ⟨L, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩, ym.trans memory⟩ + · exact (logRun.seq layoutRun).mono (by rw [hn]; dsimp only [k]; omega) + · exact (preserved _ (by decide)).trans + ((sreg _ (by decide) (by decide) (by decide) (by decide) (by decide) (by decide)).trans ha) + · exact (preserved _ (by decide)).trans + ((sreg _ (by decide) (by decide) (by decide) (by decide) (by decide) (by decide)).trans hb) + · exact (preserved _ (by decide)).trans + ((sreg _ (by decide) (by decide) (by decide) (by decide) (by decide) (by decide)).trans hc) + · exact (preserved _ (by decide)).trans h0 + · exact (preserved _ (by decide)).trans h1 + · exact (preserved _ (by decide)).trans h2 + · exact (preserved _ (by decide)).trans h4 + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Solve.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Solve.lean new file mode 100644 index 0000000000..f65e3075cf --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Solve.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 +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Setup +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Product +public import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.ScanReadback + +/-! +# Actual cyclic Strassen product followed by native coefficient counting + +The actual seven-product program runs in the low bank and protects the computed residue and +spread prefix. Its proved coefficient output feeds the real scan, which returns the existing +modular triangle count. This internal composition theorem assumes the preceding producer's +ordinary array meaning, never a multiplication, execution, count or time oracle. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM Cslib.WordRAM.Signed + +private theorem metadata_not_scan (j : Fin 128) (hj : MetadataRegister j) : ¬ScanLocal j := by + decide +kernel +revert + +/-- Invoke the concrete native Strassen controller and count its actual computed coefficients. +The entire original/residue/table prefix and all memory beyond the explicit scratch/stack +window survive; dirty product and scratch words require no clearing hypothesis. -/ +theorem exec_product_count {w n p k scratch a b c : ℕ} (x : Data 128) + (wAB wBC wAC : Fin n → Fin n → ℤ) (hp : 0 < p) (hn : n ≤ 2 ^ k) + (ready : CounterRegisters n p k scratch a b c x) + (hAB : ∀ u v : Fin n, x.mem (x.regs Reg.residueAB + u.val * n + v.val) = + (wAB u v % (p : ℤ)).toNat) + (htable : ∀ j < 2 ^ k, x.mem (x.regs Reg.spread + j) = Nat.Morton.spread j) + (hleft : ∀ i < 4 ^ k * p, x.mem (x.regs Reg.matrixP + i) = encode + (Cslib.Algorithms.ExactTriangle.CountPrime.pCoefficients p wAC i)) + (hright : ∀ i < 4 ^ k * p, x.mem (x.regs Reg.matrixQ + i) = encode + (Cslib.Algorithms.ExactTriangle.CountPrime.qCoefficients p wBC i)) + (hmag : GenericRAM.Signed.Fits w ((2 * (p + 4) * 16 ^ k : ℕ) : ℤ)) + (hw : Fits (w + 2) (addressBudget n p k scratch a b c)) : + ∃ y, ExecWithin (w + 2) (.seq product count) x y + (Strassen.traceBudget p k + 4 * k + 22 * (n * n) + 42) ∧ + CounterRegisters n p k scratch a b c y ∧ + y.regs Reg.result = Cslib.Algorithms.ExactTriangle.countZeroMod wAB wBC wAC p ∧ + ∀ d, d < x.regs Reg.matrixR ∨ + x.regs Reg.productScratch + 4 ^ k * p + 7 * (k + 1) ≤ d → y.mem d = x.mem d := by + have h4 : 0 < 4 ^ k := by positivity + have h2 : 0 < 2 ^ k := by positivity + have harea : 0 < 4 ^ k * p := Nat.mul_pos h4 hp + have hPQ : x.regs Reg.matrixP + 4 ^ k * p ≤ x.regs Reg.matrixQ := by + rw [ready.layout.matrixP_eq, ready.layout.matrixQ_eq] + have hQR : x.regs Reg.matrixQ + 4 ^ k * p ≤ x.regs Reg.matrixR := by + rw [ready.layout.matrixQ_eq, ready.layout.matrixR_eq] + omega + have hRS : x.regs Reg.matrixR + 4 ^ k * p ≤ x.regs Reg.productScratch := by + rw [ready.layout.matrixR_eq, ready.layout.productScratch_eq] + omega + obtain ⟨s, productRun, productValues, productFrame, productRegs⟩ := exec_product x + (Cslib.Algorithms.ExactTriangle.CountPrime.pCoefficients p wAC) + (Cslib.Algorithms.ExactTriangle.CountPrime.qCoefficients p wBC) + hp ready.layout.modulus_eq ready.layout.depth_eq hPQ hQR hRS + (fun i _ => Cslib.Algorithms.ExactTriangle.CountPrime.pCoefficients_natAbs_le_one p wAC i) + (fun i _ => Cslib.Algorithms.ExactTriangle.CountPrime.qCoefficients_natAbs_le_one p wBC i) + hleft hright hmag (by + unfold Strassen.addressBound + rw [ready.layout.productScratch_eq] + exact hw.of_le (by unfold addressBudget; omega)) + have keep1 (j : Fin 128) (hj : MetadataRegister j) : s.regs j = x.regs j := + productRegs j hj.1 + have ready1 := ready.congr keep1 + have before (d : ℕ) (hd : d < x.regs Reg.matrixR) : s.mem d = x.mem d := by + apply productFrame d + unfold Strassen.NodeWrites + omega + have pair_lt (u v : Fin n) : u.val * n + v.val < n * n := by + have h := (finProdFinEquiv (u, v)).isLt + change v.val + n * u.val < n * n at h + simpa only [Nat.add_comm, Nat.mul_comm] using h + obtain ⟨y, scanRun, scanMemory, result, scanRegs⟩ := exec_count_coefficients s + wAB wBC wAC hp hn ready1.layout.side_eq ready1.layout.modulus_eq ready1.layout.cells_eq + (by + intro u v + rw [keep1 Reg.residueAB (by decide), before _ (by + rw [ready.layout.residueAB_eq, ready.layout.matrixR_eq] + have := pair_lt u v + omega)] + exact hAB u v) + (by simpa only [keep1 Reg.matrixR (by decide)] using productValues) + ready1.zero ready1.one ready1.two + (by + intro j hj + rw [keep1 Reg.spread (by decide), before _ (by + rw [ready.layout.spread_eq, ready.layout.matrixR_eq] + omega)] + exact htable j hj) + (by + rw [keep1 Reg.spread (by decide), keep1 Reg.residueAB (by decide), + keep1 Reg.matrixR (by decide), ready.layout.spread_eq, ready.layout.residueAB_eq, + ready.layout.matrixR_eq] + exact hw.of_le (by unfold addressBudget; omega)) + refine ⟨y, (productRun.seq scanRun).mono (by omega), + ready1.congr (fun j hj => scanRegs j (metadata_not_scan j hj)), result, ?_⟩ + intro d hd + rw [scanMemory] + apply productFrame d + unfold Strassen.NodeWrites + omega + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/SourceBounds.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/SourceBounds.lean new file mode 100644 index 0000000000..b27d729b3d --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/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.CountPrime.Flat +import Complexity.Foundations.Data.Nat.Log +import Mathlib.Tactic.Linarith +import Mathlib.Tactic.Ring + +/-! +# Original-input logarithmic width and polynomial wider prefixes + +When the original modulus and weights are polynomially bounded in n+2, the concrete counter +admits logarithmic physical width and every wider execution has polynomial words and memory +support, independently of that chosen width. The modulus bound is explicit; these are not +uniform bounds for arbitrarily large runtime p relative to n. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM + +private theorem padding_le (n : ℕ) : 2 ^ Nat.clog 2 n ≤ (n + 2) ^ 2 := by + have hm := Nat.pow_le_pow_right (by decide : 1 ≤ (2 : ℕ)) + (Nat.clog_mono_right 2 (by omega : n ≤ n + 1)) + have hp := Nat.pow_clog_le_mul (by decide : 1 < (2 : ℕ)) (by omega : n + 1 ≠ 0) + have hb : 2 * (n + 1) ≤ (n + 2) ^ 2 := by nlinarith + exact (hm.trans hp).trans hb + +private theorem envelope_le_pow {n p A κ : ℕ} + (hp : p ≤ (n + 2) ^ κ) (hA : A ≤ (n + 2) ^ κ) : + envelope n p A ≤ (n + 2) ^ (κ + 21) := by + let b := n + 2 + let k := Nat.clog 2 n + let e := κ + 12 + change p ≤ b ^ κ at hp + change A ≤ b ^ κ at hA + have hb : 2 ≤ b := by dsimp only [b]; omega + have hb1 : 1 ≤ b := by omega + have hn : n ≤ b := by dsimp only [b]; omega + have hk : k ≤ n := Nat.clog_le_of_le_pow (Nat.lt_two_pow_self.le) + have hκ : 1 ≤ b ^ κ := Nat.one_le_pow _ _ hb1 + have htwo : 2 ^ k ≤ b ^ 2 := padding_le n + have hfour : 4 ^ k ≤ b ^ 4 := by + calc + _ = (2 ^ k) ^ 2 := by rw [pow_two, ← mul_pow]; rfl + _ ≤ (b ^ 2) ^ 2 := Nat.pow_le_pow_left htwo _ + _ = b ^ 4 := by rw [← pow_mul] + have hsixteen : 16 ^ k ≤ b ^ 8 := by + change (2 ^ 4) ^ k ≤ b ^ 8 + rw [← pow_mul, Nat.mul_comm 4 k, pow_mul] + exact (Nat.pow_le_pow_left htwo 4).trans_eq (by rw [← pow_mul]) + have hN2 : n * n ≤ b ^ e := by + calc + _ ≤ b ^ 2 := by simpa only [pow_two] using Nat.mul_le_mul hn hn + _ ≤ b ^ e := Nat.pow_le_pow_right hb1 (by dsimp only [e]; omega) + have hN3 : n * n * n ≤ b ^ e := by + calc + _ ≤ b ^ 3 := by + simpa only [pow_succ, pow_zero, one_mul] using (Nat.mul_le_mul (Nat.mul_le_mul hn hn) hn) + _ ≤ b ^ e := Nat.pow_le_pow_right hb1 (by dsimp only [e]; omega) + have hnE : n ≤ b ^ e := hn.trans (Nat.le_self_pow (by dsimp only [e]; omega) b) + have hkE : k ≤ b ^ e := hk.trans hnE + have hpE : p ≤ b ^ e := hp.trans (Nat.pow_le_pow_right hb1 (by dsimp only [e]; omega)) + have hAE : A ≤ b ^ e := hA.trans (Nat.pow_le_pow_right hb1 (by dsimp only [e]; omega)) + have hfourE : 4 ^ k ≤ b ^ e := hfour.trans + (Nat.pow_le_pow_right hb1 (by dsimp only [e]; omega)) + have htwoE : 2 ^ k ≤ b ^ e := htwo.trans + (Nat.pow_le_pow_right hb1 (by dsimp only [e]; omega)) + have harea : 4 ^ k * p ≤ b ^ e := by + calc + _ ≤ b ^ 4 * b ^ κ := Nat.mul_le_mul hfour hp + _ = b ^ (κ + 4) := by rw [← pow_add, Nat.add_comm] + _ ≤ b ^ e := Nat.pow_le_pow_right hb1 (by dsimp only [e]; omega) + have hunit : 1 ≤ b ^ e := Nat.one_le_pow _ _ hb1 + have hconst : 16 ≤ b ^ e := (show 16 ≤ (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 hB : addressBudget n p k (3 + 3 * (n * n)) 3 (3 + n * n) + (3 + 2 * (n * n)) ≤ 480 * b ^ e := by + unfold addressBudget + nlinarith only [hN2, hN3, hnE, hkE, hpE, hfourE, htwoE, harea, hconst] + have hcoeff : 10 ≤ b ^ 4 := (show 10 ≤ (2 : ℕ) ^ 4 by decide).trans + (Nat.pow_le_pow_left hb 4) + have hmag : 2 * (p + 4) * 16 ^ k ≤ b ^ e := by + calc + _ ≤ (10 * b ^ κ) * b ^ 8 := Nat.mul_le_mul (by omega) hsixteen + _ = 10 * b ^ (κ + 8) := by rw [pow_add]; ring + _ ≤ b ^ 4 * b ^ (κ + 8) := Nat.mul_le_mul_right _ hcoeff + _ = b ^ e := by rw [← pow_add]; congr 1; dsimp only [e]; omega + have hE : envelope n p A ≤ 485 * b ^ e := by + unfold envelope magnitudeBudget + change addressBudget n p k (3 + 3 * (n * n)) 3 (3 + n * n) + (3 + 2 * (n * n)) + (A + p + 2 * (p + 4) * 16 ^ k + 1) + 1 ≤ _ + omega + have h485 : 485 ≤ b ^ 9 := (show 485 ≤ (2 : ℕ) ^ 9 by decide).trans + (Nat.pow_le_pow_left hb 9) + calc + envelope n p A ≤ 485 * b ^ e := hE + _ ≤ b ^ 9 * b ^ e := Nat.mul_le_mul_right _ h485 + _ = b ^ (κ + 21) := by rw [← pow_add]; congr 1; dsimp only [e]; omega + +/-- Polynomial original moduli and weights give a positive logarithmic physical threshold, +including input fit and both signed-kernel extension bits. Empty matrices are included. -/ +theorem admissionWidth_le_log {n p A κ : ℕ} + (hp : p ≤ (n + 2) ^ κ) (hA : A ≤ (n + 2) ^ κ) : + admissionWidth n p A ≤ (κ + 24) * Nat.clog 2 (n + 2) := by + have hpow := (envelope_le_pow hp hA).trans + (Nat.pow_le_pow_left (Nat.le_pow_clog (by decide : 1 < (2 : ℕ)) (n + 2)) (κ + 21)) + rw [← pow_mul] at hpow + have hc := Nat.clog_le_of_le_pow hpow + have hpos : 1 ≤ Nat.clog 2 (n + 2) := Nat.clog_pos (by decide) (by omega) + unfold admissionWidth + nlinarith only [hc, hpos] + +/-- The same original hypotheses bound words and memory support by one polynomial independent +of the admitted physical width. -/ +theorem wordBound_le_pow {n p A κ : ℕ} + (hp : p ≤ (n + 2) ^ κ) (hA : A ≤ (n + 2) ^ κ) : + wordBound n p A ≤ (n + 2) ^ (κ + 25) := by + have hc : 16 ≤ (n + 2) ^ 4 := (show 16 ≤ (2 : ℕ) ^ 4 by decide).trans + (Nat.pow_le_pow_left (by omega : 2 ≤ n + 2) 4) + calc + _ ≤ (n + 2) ^ 4 * (n + 2) ^ (κ + 21) := Nat.mul_le_mul hc (envelope_le_pow hp hA) + _ = _ := by rw [← pow_add]; congr 1; omega + +/-- Every actual prefix at any sufficiently wide source-regime width has polynomial words +and support, not merely a bound by that physical width's own word universe. -/ +theorem bounded_reachable_polynomial {W n p A κ : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hp0 : 0 < p) + (hv : ∀ z ∈ values, z.natAbs ≤ A) + (hp : p ≤ (n + 2) ^ κ) (hA : A ≤ (n + 2) ^ κ) + (hW : (κ + 24) * Nat.clog 2 (n + 2) ≤ W) + (s : State 128) (steps : ℕ) + (hr : Runs W (entryProgram.program Reg.result) (State.init (inputWords n p values)) s steps) : + (∀ i, s.data.regs i < (n + 2) ^ (κ + 25)) ∧ + (∀ a, s.data.mem a < (n + 2) ^ (κ + 25)) ∧ + ∀ a, (n + 2) ^ (κ + 25) ≤ a → s.data.mem a = 0 := by + obtain ⟨regs, memory, support⟩ := bounded_reachable values hlen hp0 hv + ((admissionWidth_le_log hp hA).trans hW) s steps hr + have hb := wordBound_le_pow hp hA + exact ⟨fun i => (regs i).trans_le hb, fun a => (memory a).trans_le hb, + fun a ha => support a (hb.trans ha)⟩ + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Spread.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Spread.lean new file mode 100644 index 0000000000..08024c373d --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Spread.lean @@ -0,0 +1,161 @@ +/- +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.CountPrime.Program +public import Complexity.Foundations.Data.Nat.Morton + +/-! +# Computed native Morton spread table + +The table may start dirty. Each entry is read only after an earlier native iteration has +written it, and its value follows the existing digit recurrence. Every division, remainder, +index calculation, load, store and loop instruction is charged. This is an internal stage +execution API; the complete counter must derive its constants, padded side and fit premises. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM + +/-- Only the scan and arithmetic/address temporaries change while constructing the table. -/ +@[expose] def SpreadLocal (i : Fin 128) : Prop := + i = Reg.index ∨ i = Reg.address ∨ i = Reg.temporary ∨ i = Reg.position + +instance (i : Fin 128) : Decidable (SpreadLocal i) := + inferInstanceAs (Decidable + (i = Reg.index ∨ i = Reg.address ∨ i = Reg.temporary ∨ i = Reg.position)) + +/-- Construct all `2^k` actual spread words in at most `11*(2^k-1)+5` instructions, +preserving the caller memory outside exactly that table. -/ +theorem exec_spreadTable {w k : ℕ} (x : Data 128) + (hside : x.regs Reg.paddedSide = 2 ^ k) + (h0 : x.regs Reg.zero = 0) (h1 : x.regs Reg.one = 1) + (h2 : x.regs Reg.two = 2) (h4 : x.regs Reg.four = 4) + (hw : Fits w (x.regs Reg.spread + 4 ^ k + 2 ^ k + 8)) : + ∃ y, ExecWithin w spreadTable x y (11 * (2 ^ k - 1) + 5) ∧ + (∀ j < 2 ^ k, y.mem (x.regs Reg.spread + j) = Nat.Morton.spread j) ∧ + (∀ a, a < x.regs Reg.spread ∨ x.regs Reg.spread + 2 ^ k ≤ a → y.mem a = x.mem a) ∧ + ∀ i, ¬SpreadLocal i → y.regs i = x.regs i := by + have hpow : 0 < 4 ^ k := by positivity + have hsidePos : 0 < 2 ^ k := by positivity + let I (y : Data 128) : Prop := + (∀ i, ¬SpreadLocal i → y.regs i = x.regs i) ∧ + 1 ≤ y.regs Reg.index ∧ y.regs Reg.index ≤ 2 ^ k ∧ + (∀ j < y.regs Reg.index, y.mem (x.regs Reg.spread + j) = Nat.Morton.spread j) ∧ + ∀ a, ¬(x.regs Reg.spread ≤ a ∧ a < x.regs Reg.spread + y.regs Reg.index) → + y.mem a = x.mem a + have hbody (y : Data 128) (hy : I y) (hc : (Cond.lt Reg.index Reg.paddedSide).Holds y) : + ∃ z, ExecWithin w spreadStep y z 9 ∧ I z ∧ + 2 ^ k - z.regs Reg.index < 2 ^ k - y.regs Reg.index := by + obtain ⟨hr, hlo, hhi, hv, hf⟩ := hy + have hs := (hr Reg.paddedSide (by decide)).trans hside + have hi : y.regs Reg.index < 2 ^ k := by simpa only [Cond.holds_lt, hs] using hc + have hbase := hr Reg.spread (by decide) + have hy1 := (hr Reg.one (by decide)).trans h1 + have hy2 := (hr Reg.two (by decide)).trans h2 + have hy4 := (hr Reg.four (by decide)).trans h4 + have hread : y.mem (y.regs Reg.spread + y.regs Reg.index / 2) = + Nat.Morton.spread (y.regs Reg.index / 2) := by + rw [hbase] + apply hv + omega + have hvalue := Nat.Morton.spread_lt hi + have hrec : Nat.Morton.spread (y.regs Reg.index / 2) * 4 + y.regs Reg.index % 2 = + Nat.Morton.spread (y.regs Reg.index) := by + rw [Nat.Morton.spread_eq (y.regs Reg.index)] + omega + have hquot : Fits w (y.regs Reg.index / 2) := hw.of_le (by omega) + have hreadaddr : Fits w (y.regs Reg.spread + y.regs Reg.index / 2) := hw.of_le (by + rw [hbase]; omega) + have hprod : Fits w (Nat.Morton.spread (y.regs Reg.index / 2) * 4) := + hw.of_le (by omega) + have hrem : Fits w (y.regs Reg.index % 2) := hw.of_le (by omega) + have hsum : Fits w (Nat.Morton.spread (y.regs Reg.index)) := hw.of_le (by omega) + have hwrite : Fits w (y.regs Reg.spread + y.regs Reg.index) := hw.of_le (by + rw [hbase]; omega) + have hinc : Fits w (y.regs Reg.index + 1) := hw.of_le (by omega) + refine ⟨?z, ?run, ?inv, ?decrease⟩ + case run => + ram_eval [spreadStep, hy1, hy2, hy4, hread, hquot, hreadaddr, hprod, hrem, + hrec, hsum, hwrite, hinc, Reg.index, Reg.address, Reg.temporary, Reg.position, + Reg.spread, Reg.one, Reg.two, Reg.four, Fin.mk.injEq] + case inv => + refine ⟨?_, ?_, ?_, ?_, ?_⟩ + · intro i hni + have hne : i ≠ Reg.index ∧ i ≠ Reg.address ∧ i ≠ Reg.temporary ∧ + i ≠ Reg.position := by simpa only [SpreadLocal, not_or] using hni + simpa only [Data.set, Data.write, Function.update_of_ne hne.1, + Function.update_of_ne hne.2.1, Function.update_of_ne hne.2.2.1, + Function.update_of_ne hne.2.2.2] using hr i hni + · change 1 ≤ y.regs Reg.index + 1 + omega + · change y.regs Reg.index + 1 ≤ 2 ^ k + omega + · intro j hj + change j < y.regs Reg.index + 1 at hj + change Function.update y.mem (y.regs Reg.spread + y.regs Reg.index) + (Nat.Morton.spread (y.regs Reg.index)) (x.regs Reg.spread + j) = _ + by_cases he : j = y.regs Reg.index + · subst j + rw [hbase, Function.update_self] + · rw [Function.update_of_ne (by rw [hbase]; omega)] + exact hv j (by omega) + · intro a ha + change ¬(x.regs Reg.spread ≤ a ∧ a < x.regs Reg.spread + (y.regs Reg.index + 1)) at ha + change Function.update y.mem (y.regs Reg.spread + y.regs Reg.index) + (Nat.Morton.spread (y.regs Reg.index)) a = x.mem a + rw [Function.update_of_ne (by rw [hbase]; omega)] + apply hf + omega + case decrease => + change 2 ^ k - (y.regs Reg.index + 1) < 2 ^ k - y.regs Reg.index + omega + have hfit1 : Fits w 1 := hw.of_le (by omega) + let y := + (((x.set Reg.address (x.regs Reg.spread)).write (x.regs Reg.spread) 0).set Reg.index 1) + have hinit : ExecWithin w spreadSetup x y + 3 := by ram_eval [spreadSetup, y, h0, hfit1, Reg.address, Reg.spread, Reg.zero, Reg.index] + have hI : I y := by + refine ⟨?_, ?_, ?_, ?_, ?_⟩ + · intro i hi + have hi' : i ≠ Reg.address ∧ i ≠ Reg.index := by + simp only [SpreadLocal, not_or] at hi + tauto + simp only [y, Data.set, Data.write, Function.update_of_ne hi'.1, + Function.update_of_ne hi'.2] + · change 1 ≤ 1 + exact le_rfl + · change 1 ≤ 2 ^ k + omega + · intro j hj + change j < 1 at hj + obtain rfl : j = 0 := by omega + simp only [y, Nat.add_zero, Data.set, Data.write, Function.update_self, + Nat.Morton.spread_zero] + · intro a ha + change ¬(x.regs Reg.spread ≤ a ∧ a < x.regs Reg.spread + 1) at ha + change Function.update x.mem (x.regs Reg.spread) 0 a = x.mem a + rw [Function.update_of_ne (show a ≠ x.regs Reg.spread by omega)] + obtain ⟨z, hz, hIz, hstop⟩ := ExecWithin.loop_invariant I + (fun y => 2 ^ k - y.regs Reg.index) hbody hI + have hrun := hinit.seq hz + have hbound : 3 + ((2 ^ k - 1) * (9 + 2) + 2) ≤ 11 * (2 ^ k - 1) + 5 := by omega + obtain ⟨hr, hlo, hhi, hv, hf⟩ := hIz + have hi : z.regs Reg.index = 2 ^ k := by + have hs := (hr Reg.paddedSide (by decide)).trans hside + simp only [Cond.holds_lt, hs] at hstop + omega + refine ⟨z, hrun.mono hbound, ?_, ?_, hr⟩ + · simpa only [hi] using hv + · intro a ha + exact hf a (by omega) + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/TimeBound.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/TimeBound.lean new file mode 100644 index 0000000000..dea8614858 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/TimeBound.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.ExactTriangle.CountPrime.Flat +import Complexity.Foundations.Data.Nat.Log +import Mathlib.Analysis.SpecialFunctions.Pow.Real +import Mathlib.Tactic.Linarith +import Mathlib.Tactic.NormNum +import Mathlib.Tactic.Ring + +/-! +# Charged subcubic native modular-counter time + +The actual initialized execution, including halt, is bounded by a quadratic polynomial in +the runtime cyclic length p times seven to the computed ceiling-logarithmic matrix depth. +The real-power consequence has n exponent 2.81, strictly below three. Dependence on p remains +explicit; this does not claim subcubic time after arbitrary growth of p with n, nor complete +prime selection or the full ExactTriangle/3SUM endpoint. + +All constants bound the proved native execution. They do not inherit the Light machine's +ring-operation constants from the pinned source. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +/-- Every actual initialized setup, memory/coefficient operation, recursion, scan and halt +fits this native seven-to-depth budget. -/ +theorem cost_le_seven_pow (n p : ℕ) : + cost n p + 10 ≤ (42 * p * p + 254 * p + 463) * 7 ^ Nat.clog 2 n := by + let k := Nat.clog 2 n + have hn : n ≤ 2 ^ k := Nat.le_pow_clog (by decide) n + have hn2 : n * n ≤ 4 ^ k := by + calc + _ ≤ 2 ^ k * 2 ^ k := Nat.mul_le_mul hn hn + _ = 4 ^ k := by rw [← mul_pow]; rfl + have h4 : 4 ^ k ≤ 7 ^ k := Nat.pow_le_pow_left (by decide) _ + have h2 : 2 ^ k ≤ 7 ^ k := Nat.pow_le_pow_left (by decide) _ + have hN := Nat.mul_le_mul_left 91 (hn2.trans h4) + have hsub := Nat.mul_le_mul_left 11 ((Nat.sub_le (2 ^ k) 1).trans h2) + have hk := Nat.mul_le_mul_left 13 (Nat.lt_pow_self (n := k) (by decide : 1 < 7)).le + have hunit : 1 ≤ 7 ^ k := Nat.one_le_pow _ _ (by decide) + have hc := Nat.mul_le_mul_left 122 hunit + have hmat := Nat.mul_le_mul_left (14 * p + 48) h4 + have ht := Strassen.traceBudget_potential_le p k + have htrace : Strassen.traceBudget p k ≤ (42 * p * p + 240 * p + 178) * 7 ^ k := + ((Nat.le_add_right (Strassen.traceBudget p k) (231 * p * 4 ^ k)).trans + (Nat.le_add_right _ 150)).trans ht + change Strassen.traceBudget p k + (14 * p + 48) * 4 ^ k + 11 * (2 ^ k - 1) + + 91 * (n * n) + 4 * k + 81 + 9 * k + 31 + 10 ≤ _ + nlinarith only [htrace, hmat, hN, hsub, hk, hc] + +/-- A simple quadratic p envelope for the complete actual initialized instruction charge. -/ +theorem cost_le_square (n p : ℕ) : + cost n p + 10 ≤ 463 * (p + 1) ^ 2 * 7 ^ Nat.clog 2 n := by + have hc : 42 * p * p + 254 * p + 463 ≤ 463 * (p + 1) ^ 2 := by nlinarith + exact (cost_le_seven_pow n p).trans (Nat.mul_le_mul_right _ hc) + +private theorem seven_le_two_rpow : (7 : ℝ) ≤ (2 : ℝ) ^ (281 / 100 : ℝ) := by + have seed : (7 : ℕ) ^ 100 ≤ 2 ^ 281 := by decide +kernel + have seedR : (7 : ℝ) ^ 100 ≤ (2 : ℝ) ^ 281 := by exact_mod_cast seed + have power : ((2 : ℝ) ^ (281 / 100 : ℝ)) ^ 100 = (2 : ℝ) ^ 281 := by + rw [← Real.rpow_mul_natCast (by norm_num)] + norm_num + have h : (7 : ℝ) ^ 100 ≤ ((2 : ℝ) ^ (281 / 100 : ℝ)) ^ 100 := by + rw [power] + exact seedR + apply (Real.rpow_le_rpow_iff (by norm_num : 0 ≤ (7 : ℝ)) + (by positivity) (by norm_num : 0 < (100 : ℝ))).mp + change (7 : ℝ) ^ ((100 : ℕ) : ℝ) ≤ + ((2 : ℝ) ^ (281 / 100 : ℝ)) ^ ((100 : ℕ) : ℝ) + simpa only [Real.rpow_natCast] using h + +private theorem seven_pow_clog_le (n : ℕ) : + (7 : ℝ) ^ Nat.clog 2 n ≤ 8 * (n + 1 : ℝ) ^ (281 / 100 : ℝ) := by + let k := Nat.clog 2 n + have pad : 2 ^ k ≤ 2 * (n + 1) := + (Nat.pow_le_pow_right (by decide : 1 ≤ (2 : ℕ)) + (Nat.clog_mono_right 2 (by omega : n ≤ n + 1))).trans + (Nat.pow_clog_le_mul (by decide) (by omega : n + 1 ≠ 0)) + have padR : (2 : ℝ) ^ k ≤ 2 * (n + 1 : ℝ) := by exact_mod_cast pad + have h7 := pow_le_pow_left₀ (by norm_num : 0 ≤ (7 : ℝ)) seven_le_two_rpow k + have reordered : ((2 : ℝ) ^ (281 / 100 : ℝ)) ^ k = + ((2 : ℝ) ^ k) ^ (281 / 100 : ℝ) := by + rw [← Real.rpow_mul_natCast (by norm_num), + mul_comm (281 / 100 : ℝ) (k : ℝ), Real.rpow_natCast_mul (by norm_num)] + have height : (2 : ℝ) ^ (281 / 100 : ℝ) ≤ 8 := by + have h := Real.rpow_le_rpow_of_exponent_le (by norm_num : 1 ≤ (2 : ℝ)) + (by norm_num : (281 / 100 : ℝ) ≤ 3) + convert h using 1; norm_num + calc + (7 : ℝ) ^ k ≤ ((2 : ℝ) ^ k) ^ (281 / 100 : ℝ) := by simpa only [reordered] using h7 + _ ≤ (2 * (n + 1 : ℝ)) ^ (281 / 100 : ℝ) := + Real.rpow_le_rpow (by positivity) padR (by norm_num) + _ = (2 : ℝ) ^ (281 / 100 : ℝ) * (n + 1 : ℝ) ^ (281 / 100 : ℝ) := + Real.mul_rpow (by norm_num) (by positivity) + _ ≤ 8 * (n + 1 : ℝ) ^ (281 / 100 : ℝ) := + mul_le_mul_of_nonneg_right height (by positivity) + +/-- The actual initialized native counter has subcubic n exponent 2.81 and quadratic p +factor, with every ring coefficient operation charged. The bound includes the final halt. -/ +theorem cost_le_subcubic (n p : ℕ) : + ((cost n p + 10 : ℕ) : ℝ) ≤ + 3704 * (p + 1 : ℝ) ^ 2 * (n + 1 : ℝ) ^ (281 / 100 : ℝ) := by + have hc : ((cost n p + 10 : ℕ) : ℝ) ≤ + 463 * (p + 1 : ℝ) ^ 2 * (7 : ℝ) ^ Nat.clog 2 n := by + exact_mod_cast cost_le_square n p + calc + _ ≤ 463 * (p + 1 : ℝ) ^ 2 * (7 : ℝ) ^ Nat.clog 2 n := hc + _ ≤ 463 * (p + 1 : ℝ) ^ 2 * (8 * (n + 1 : ℝ) ^ (281 / 100 : ℝ)) := + mul_le_mul_of_nonneg_left (seven_pow_clog_le n) (by positivity) + _ = _ := by ring + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Width.lean b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Width.lean new file mode 100644 index 0000000000..2c012f0aa4 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/ExactTriangle/CountPrime/Width.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.CountPrime.Input + +/-! +# Original-input physical admission for the complete native counter + +A positive ceiling-logarithm threshold depends only on the original side, runtime modulus +and weight bound. Every larger physical width executes the same actual initialized program. +No intermediate fit, padding, table or execution certificate is an input hypothesis. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +open Cslib.WordRAM + +/-- The original-input envelope dominates all native signed arithmetic and address budgets. -/ +@[expose] def envelope (n p A : ℕ) : ℕ := + addressBudget n p (Nat.clog 2 n) (3 + 3 * (n * n)) 3 (3 + n * n) + (3 + 2 * (n * n)) + magnitudeBudget p (Nat.clog 2 n) A + 1 + +/-- Positive sufficient physical width, including both bits required by native signed kernels. -/ +@[expose] def admissionWidth (n p A : ℕ) : ℕ := Nat.clog 2 (envelope n p A) + 3 + +/-- Width-independent word and support envelope for every admitted wider execution. -/ +@[expose] def wordBound (n p A : ℕ) : ℕ := 16 * envelope n p A + +/-- The sufficient width is positive for every original dimension, including empty matrices. -/ +theorem admissionWidth_pos (n p A : ℕ) : 0 < admissionWidth n p A := by + unfold admissionWidth + omega + +private theorem fits_at_width (n p A : ℕ) : + GenericRAM.Signed.Fits (Nat.clog 2 (envelope n p A) + 1) + (magnitudeBudget p (Nat.clog 2 n) A : ℤ) ∧ + Fits (admissionWidth n p A) + (addressBudget n p (Nat.clog 2 n) (3 + 3 * (n * n)) 3 (3 + n * n) + (3 + 2 * (n * n))) := by + let E := envelope n p A + let k := Nat.clog 2 E + have hp : E ≤ 2 ^ k := Nat.le_pow_clog (by decide) E + have hm : magnitudeBudget p (Nat.clog 2 n) A < 2 ^ k := + (show magnitudeBudget p (Nat.clog 2 n) A < E by dsimp only [E, envelope]; omega).trans_le hp + have hb : addressBudget n p (Nat.clog 2 n) (3 + 3 * (n * n)) 3 (3 + n * n) + (3 + 2 * (n * n)) < 2 ^ k := + (show addressBudget n p (Nat.clog 2 n) (3 + 3 * (n * n)) 3 (3 + n * n) + (3 + 2 * (n * n)) < E by dsimp only [E, envelope]; omega).trans_le hp + refine ⟨⟨by omega, ?_, ?_⟩, (show Fits k _ from hb).mono (by + dsimp only [admissionWidth, k, E]; omega)⟩ + · simp only [Nat.add_sub_cancel] + exact (neg_nonpos.mpr (by positivity)).trans (Int.natCast_nonneg _) + · simp only [Nat.add_sub_cancel] + exact_mod_cast hm + +/-- Complete initialized structured execution at every admitted width, with original input fit, +exact raw modular count, a bounded actual final state and the caller-memory frame. -/ +theorem exec_input {W n p A : ℕ} (values : List ℤ) + (hlen : values.length = 3 * (n * n)) (hp : 0 < p) + (hv : ∀ z ∈ values, z.natAbs ≤ A) (hW : admissionWidth n p A ≤ W) : + InputFits W (inputWords n p values) ∧ + ∃ y, ExecWithin W entryProgram (.init (inputWords n p values)) y (cost n p + 9) ∧ + y.regs Reg.result = Cslib.Algorithms.ExactTriangle.countZeroMod + (fun α β : Fin n => values.getD (α.val * n + β.val) 0) + (fun α β : Fin n => values.getD (n * n + α.val * n + β.val) 0) + (fun α β : Fin n => values.getD (2 * (n * n) + α.val * n + β.val) 0) p ∧ + y.Bounded W ∧ + ∀ d, d < 3 + 3 * (n * n) ∨ memoryEnd n p (Nat.clog 2 n) (3 + 3 * (n * n)) ≤ d → + y.mem d = (Data.init (inputWords n p values) : Data 128).mem d := by + obtain ⟨hmag, haddr⟩ := fits_at_width n p A + obtain ⟨hi, y, he, result, _, frame⟩ := exec_input_at values hlen hp hv hmag + (by simpa only [Nat.add_assoc, admissionWidth] using haddr) + have hwidth : Nat.clog 2 (envelope n p A) + 1 + 2 ≤ W := by + simpa only [Nat.add_assoc, admissionWidth] using hW + have noWidth : ¬entryProgram.ReadsWidth := by decide +kernel + have hew := he.mono_width noWidth hwidth + have hii := hi.mono hwidth + exact ⟨hii, y, hew, result, hew.bounded (.init hii), frame⟩ + +end Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Accumulate.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Accumulate.lean new file mode 100644 index 0000000000..c502db7cfc --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Accumulate.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.Strassen.Quadrant +public import Complexity.Computability.WordRAM.Signed.ArrayArithmetic.Exec + +/-! +# Native in-place Strassen product accumulation + +The actual selected output quarter is both the first input and destination of the existing +signed vector loop. Its exact-alias theorem is essential here. The child product buffer is +disjoint; dirty words outside this one output quarter retain their previous values. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Add/subtract one actual child result in place, charging the selected-quadrant address, +all three argument copies, and the complete signed vector loop. -/ +theorem exec_accumulate {w q t A B : ℕ} (subtract : Bool) (x : Data 64) (u v : ℕ → ℤ) + (hq : x.regs Reg.quarter = q) (ht : t < 4) + (hbound : GenericRAM.Signed.Fits w ((A + B : ℕ) : ℤ)) + (hu : ∀ j < q, (u j).natAbs ≤ A) (hv : ∀ j < q, (v j).natAbs ≤ B) + (hout : ∀ j < q, x.mem (x.regs Reg.output + t * q + j) = encode (u j)) + (hprod : ∀ j < q, x.mem (x.regs Reg.product + j) = encode (v j)) + (hw : Fits (w + 2) (x.regs Reg.output + 4 * q + x.regs Reg.product + q + 8)) + (hsep : x.regs Reg.product + q ≤ x.regs Reg.output ∨ + x.regs Reg.output + 4 * q ≤ x.regs Reg.product) : + ∃ y, ExecWithin (w + 2) (accumulate t subtract) x y (26 * q + 10) ∧ + (∀ j < q, y.mem (x.regs Reg.output + t * q + j) = + encode (if subtract then u j - v j else u j + v j)) ∧ + (∀ a, a < x.regs Reg.output + t * q ∨ x.regs Reg.output + t * q + q ≤ a → + y.mem a = x.mem a) ∧ + ∀ i, 32 ≤ i.val → i ≠ Reg.temporary → y.regs i = x.regs i := by + have hqfit : Fits (w + 2) (x.regs Reg.output + 4 * x.regs Reg.quarter + 4) := by + rw [hq] + exact hw.of_le (by omega) + obtain ⟨s, hs, hsm, hsrc, hsr⟩ := exec_quadrant Reg.output Reg.operandA x (by decide) ht hqfit + have hhigh (i : Fin 64) (hi : 32 ≤ i.val) (htemp : i ≠ Reg.temporary) : + s.regs i = x.regs i := hsr i (by intro he; subst i; contradiction) htemp + have hsq := (hhigh Reg.quarter (by decide) (by decide)).trans hq + have hsp := hhigh Reg.product (by decide) (by decide) + have hsa : s.regs Reg.operandA = x.regs Reg.output + t * q := by + simpa only [hq] using hsrc + let z := ((s.set Reg.operandB (x.regs Reg.product)).set Reg.target + (x.regs Reg.output + t * q)).set Reg.length q + have hargs : ExecWithin (B := .standard) (w + 2) + (ram[ Reg.operandB ← Reg.product; Reg.target ← Reg.operandA; Reg.length ← Reg.quarter ]) + s z 3 := by + ram_eval [z, hsp, hsa, hsq, Reg.operandB, Reg.target, Reg.operandA, Reg.length] + let f : Fin 16 → Fin 64 := Fin.castLE (by decide) + have hza : (z.comap f).regs Signed.ArrayArithmetic.Reg.inputA = x.regs Reg.output + t * q := by + simpa +decide [z, f, Data.comap, Reg.operandA, Reg.operandB, Reg.target, Reg.length, + Signed.ArrayArithmetic.Reg.inputA] using hsa + have hzb : (z.comap f).regs Signed.ArrayArithmetic.Reg.inputB = x.regs Reg.product := by + simp +decide [z, f, Data.comap, Reg.operandB, Reg.target, Reg.length, + Signed.ArrayArithmetic.Reg.inputB] + have hzo : (z.comap f).regs Signed.ArrayArithmetic.Reg.output = x.regs Reg.output + t * q := by + simp +decide [z, f, Data.comap, Reg.operandB, Reg.target, Reg.length, + Signed.ArrayArithmetic.Reg.output] + have hzl : (z.comap f).regs Signed.ArrayArithmetic.Reg.length = q := by + simp [z, f, Data.comap, Reg.length, Signed.ArrayArithmetic.Reg.length] + have hzm : (z.comap f).mem = x.mem := hsm + have hquarter : t * q + q ≤ 4 * q := by + have hmul := Nat.mul_le_mul_right q (Nat.succ_le_of_lt ht) + simpa only [Nat.succ_mul] using hmul + obtain ⟨v', hv', values, frame, _⟩ := Signed.ArrayArithmetic.exec subtract (z.comap f) u v + hzl hbound hu hv + (by intro j hj; rw [hzm, hza]; exact hout j hj) + (by intro j hj; rw [hzm, hzb]; exact hprod j hj) + (by rw [hza]; exact hw.of_le (by omega)) + (by rw [hzb]; exact hw.of_le (by omega)) + (by rw [hzo]; exact hw.of_le (by omega)) + (Or.inr (Or.inr (hza.trans hzo.symm))) + (by + rw [hzb, hzo] + rcases hsep with h | h + · exact Or.inl (by omega) + · exact Or.inr (Or.inl (by omega))) + let y := v'.extend f z + have hvector : ExecWithin (w + 2) + ((Signed.ArrayArithmetic.program subtract).rename f) z y (26 * q + 4) := + hv'.rename (Fin.castLE_injective _) + have he : ExecWithin (w + 2) (accumulate t subtract) x y (26 * q + 10) := by + exact (hs.seq (hargs.seq hvector)).mono (by omega) + refine ⟨y, he, ?_, ?_, ?_⟩ + · intro j hj + simpa only [y, Data.extend, hzo] using values j hj + · intro a ha + simpa only [y, Data.extend, hzm] using frame a (by simpa only [hzo] using ha) + · intro i hi htemp + have hn : ¬∃ j, f j = i := by + rintro ⟨j, hj⟩ + have hj' := congrArg Fin.val hj + change j.val = i.val at hj' + omega + have hyi : y.regs i = z.regs i := Function.extend_apply' _ _ _ hn + apply hyi.trans + have hib : i ≠ Reg.operandB := by intro h; subst i; contradiction + have hit : i ≠ Reg.target := by intro h; subst i; contradiction + have hil : i ≠ Reg.length := by intro h; subst i; contradiction + simpa only [z, Data.set, Function.update_of_ne hib, Function.update_of_ne hit, + Function.update_of_ne hil] using hhigh i hi htemp + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/ArithmeticForm.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/ArithmeticForm.lean new file mode 100644 index 0000000000..47083ee55f --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/ArithmeticForm.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.Strassen.Quadrant +public import Complexity.Computability.WordRAM.Signed.ArrayArithmetic.Exec + +/-! +# Actual two-quadrant Strassen operand forms + +Both source pointers are computed by native quadrant address blocks. The existing signed +array loop then reads, adds/subtracts and stores every coefficient. Original matrix bounds +imply operand fits; the output may be dirty. This internal composition lemma does not assume +the recursively computed matrix product or its execution/time contract. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Materialize a signed sum/difference of two actual matrix quarters in a disjoint buffer, +charging all address multiplication, argument copies and vector instructions. -/ +theorem exec_arithmeticForm {w q t₁ t₂ A : ℕ} (base dst : Fin 64) (sign : ℤ) + (x : Data 64) (u : ℕ → ℤ) + (hb : 32 ≤ base.val) (hd : 32 ≤ dst.val) + (hbt : base ≠ Reg.temporary) (hdt : dst ≠ Reg.temporary) + (hq : x.regs Reg.quarter = q) (ht₁ : t₁ < 4) (ht₂ : t₂ < 4) (hsign : sign ≠ 0) + (hbound : GenericRAM.Signed.Fits w ((A + A : ℕ) : ℤ)) + (hu : ∀ j < 4 * q, (u j).natAbs ≤ A) + (hinput : ∀ j < 4 * q, x.mem (x.regs base + j) = encode (u j)) + (hw : Fits (w + 2) (x.regs base + 4 * q + x.regs dst + q + 8)) + (hsep : x.regs base + 4 * q ≤ x.regs dst ∨ x.regs dst + q ≤ x.regs base) : + ∃ y, ExecWithin (w + 2) (form base dst t₁ t₂ sign) x y (26 * q + 12) ∧ + (∀ j < q, y.mem (x.regs dst + j) = encode + (if sign < 0 then u (t₁ * q + j) - u (t₂ * q + j) + else u (t₁ * q + j) + u (t₂ * q + j))) ∧ + (∀ a, a < x.regs dst ∨ x.regs dst + q ≤ a → y.mem a = x.mem a) ∧ + ∀ i, 32 ≤ i.val → i ≠ Reg.temporary → y.regs i = x.regs i := by + have hqfit : Fits (w + 2) (x.regs base + 4 * x.regs Reg.quarter + 4) := by + rw [hq] + exact hw.of_le (by omega) + obtain ⟨r, hr, hrmem, hra, hrr⟩ := exec_quadrant base Reg.operandA x hbt ht₁ hqfit + have hrhigh (i : Fin 64) (hi : 32 ≤ i.val) (htemp : i ≠ Reg.temporary) : + r.regs i = x.regs i := hrr i (by intro he; subst i; contradiction) htemp + have hrq := (hrhigh Reg.quarter (by decide) (by decide)).trans hq + have hrbase := hrhigh base hb hbt + obtain ⟨s, hs, hsmem, hsb, hsr⟩ := exec_quadrant base Reg.operandB r hbt ht₂ + (by simpa only [hrbase, hrq, hq] using hqfit) + have hshigh (i : Fin 64) (hi : 32 ≤ i.val) (htemp : i ≠ Reg.temporary) : + s.regs i = x.regs i := + (hsr i (by intro he; subst i; contradiction) htemp).trans (hrhigh i hi htemp) + have hsq := (hshigh Reg.quarter (by decide) (by decide)).trans hq + have hsd := hshigh dst hd hdt + have hsa : s.regs Reg.operandA = x.regs base + t₁ * q := by + rw [hsr Reg.operandA (by decide) (by decide), hra, hq] + have hsb' : s.regs Reg.operandB = x.regs base + t₂ * q := by + simpa only [hrbase, hrq] using hsb + have hmem : s.mem = x.mem := hsmem.trans hrmem + let z := (s.set Reg.target (x.regs dst)).set Reg.length q + have hargs : ExecWithin (B := .standard) (w + 2) + (ram[ Reg.target ← dst; Reg.length ← Reg.quarter ]) s z 2 := by + ram_eval [z, hsq, hsd, Reg.target, Reg.length] + let f : Fin 16 → Fin 64 := Fin.castLE (by decide) + have hza : (z.comap f).regs Signed.ArrayArithmetic.Reg.inputA = x.regs base + t₁ * q := by + simpa +decide [z, f, Data.comap, Reg.target, Reg.length, Reg.operandA, + Signed.ArrayArithmetic.Reg.inputA] using hsa + have hzb : (z.comap f).regs Signed.ArrayArithmetic.Reg.inputB = x.regs base + t₂ * q := by + simpa +decide [z, f, Data.comap, Reg.target, Reg.length, Reg.operandB, + Signed.ArrayArithmetic.Reg.inputB] using hsb' + have hzo : (z.comap f).regs Signed.ArrayArithmetic.Reg.output = x.regs dst := by + simp +decide [z, f, Data.comap, Reg.target, Reg.length, Signed.ArrayArithmetic.Reg.output] + have hzl : (z.comap f).regs Signed.ArrayArithmetic.Reg.length = q := by + simp [z, f, Data.comap, Reg.length, Signed.ArrayArithmetic.Reg.length] + have hzm : (z.comap f).mem = x.mem := hmem + have hquarter (t : ℕ) (ht : t < 4) : t * q + q ≤ 4 * q := by + have hmul := Nat.mul_le_mul_right q (Nat.succ_le_of_lt ht) + simpa only [Nat.succ_mul] using hmul + have hq₁ := hquarter t₁ ht₁ + have hq₂ := hquarter t₂ ht₂ + obtain ⟨v, hv, values, frame, _⟩ := Signed.ArrayArithmetic.exec + (decide (sign < 0)) (z.comap f) (fun j => u (t₁ * q + j)) (fun j => u (t₂ * q + j)) + hzl hbound + (fun j hj => hu _ (by omega)) (fun j hj => hu _ (by omega)) + (by intro j hj; rw [hzm, hza, Nat.add_assoc]; exact hinput _ (by omega)) + (by intro j hj; rw [hzm, hzb, Nat.add_assoc]; exact hinput _ (by omega)) + (by rw [hza]; exact hw.of_le (by omega)) + (by rw [hzb]; exact hw.of_le (by omega)) + (by rw [hzo]; exact hw.of_le (by omega)) + (by + rw [hza, hzo] + rcases hsep with h | h + · exact Or.inl (by omega) + · exact Or.inr (Or.inl (by omega))) + (by + rw [hzb, hzo] + rcases hsep with h | h + · exact Or.inl (by omega) + · exact Or.inr (Or.inl (by omega))) + let y := v.extend f z + have hvector : ExecWithin (w + 2) + ((Signed.ArrayArithmetic.program (decide (sign < 0))).rename f) z y (26 * q + 4) := + hv.rename (Fin.castLE_injective _) + have he : ExecWithin (w + 2) (form base dst t₁ t₂ sign) x y (26 * q + 12) := by + simpa only [form, ite_eq_right hsign, f] using + (hr.seq (hs.seq (hargs.seq hvector))).mono + (by omega : 3 + (3 + (2 + (26 * q + 4))) ≤ 26 * q + 12) + refine ⟨y, he, ?_, ?_, ?_⟩ + · intro j hj + simpa only [y, Data.extend, hzo, decide_eq_true_eq] using values j hj + · intro a ha + simpa only [y, Data.extend, hzm] using frame a (by simpa only [hzo] using ha) + · intro i hi htemp + have hn : ¬∃ j, f j = i := by + rintro ⟨j, hj⟩ + have hj' := congrArg Fin.val hj + change j.val = i.val at hj' + omega + have hyi : y.regs i = z.regs i := Function.extend_apply' _ _ _ hn + apply hyi.trans + have hit : i ≠ Reg.target := by intro h; subst i; contradiction + have hil : i ≠ Reg.length := by intro h; subst i; contradiction + simpa only [z, Data.set, Function.update_of_ne hit, Function.update_of_ne hil] using + hshigh i hi htemp + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Bounds.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Bounds.lean new file mode 100644 index 0000000000..dc4459a05e --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Bounds.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.MatrixMultiplication.Strassen.Phases +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.CyclicConvolution.Coefficient + +/-! +# Original-parameter bounds for native Strassen composition + +The magnitude envelope covers native scalar convolution, operand forms, recursive children +and partial output additions. The pointer envelope covers current/descendant frames and +coefficient buffers. These arithmetic lemmas derive intermediate admission facts; they are +not an execution, multiplication or time certificate supplied to the program. + +The factor two above the actual scalar-kernel envelope deliberately leaves room to bound +an accumulated quarter and its next child separately. The resulting `16^k` growth is +polynomial in the padded matrix side, as required for later logarithmic-width admission. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +/-- An input-derived envelope for every signed arithmetic operand in a depth-k node. -/ +@[expose] def magnitudeBound (p A B k : ℕ) : ℕ := + 2 * CyclicConvolution.magnitudeBound p A B * 16 ^ k + +/-- A uniform address envelope for the current node and all its actual descendant frames. +`frames` is the first stack address; coefficient scratch ends before it. -/ +@[expose] def addressBound (frames frame k : ℕ) : ℕ := frames + frame + 7 * (k + 1) + 16 + +/-- There is always positive signed slack, even for zero original coefficient arrays. -/ +theorem magnitudeBound_pos (p A B k : ℕ) : 0 < magnitudeBound p A B k := by + unfold magnitudeBound CyclicConvolution.magnitudeBound + positivity + +private theorem scalar_bound_left (p A B : ℕ) : A ≤ CyclicConvolution.magnitudeBound p A B := by + unfold CyclicConvolution.magnitudeBound + omega + +private theorem scalar_bound_right (p A B : ℕ) : B ≤ CyclicConvolution.magnitudeBound p A B := by + unfold CyclicConvolution.magnitudeBound + omega + +/-- Original coefficient operands fit the node envelope at every depth. -/ +theorem inputs_le_magnitudeBound (p A B k : ℕ) : + A ≤ magnitudeBound p A B k ∧ B ≤ magnitudeBound p A B k := by + have hp : 1 ≤ 16 ^ k := by + have h : 0 < 16 ^ k := by positivity + omega + have hm := Nat.mul_le_mul_left (2 * CyclicConvolution.magnitudeBound p A B) hp + simp only [Nat.mul_one] at hm + exact ⟨(scalar_bound_left p A B).trans (by unfold magnitudeBound; omega), + (scalar_bound_right p A B).trans (by unfold magnitudeBound; omega)⟩ + +/-- Both native operand-form sums have enough signed range. -/ +theorem forms_le_magnitudeBound (p A B k : ℕ) : + A + A ≤ magnitudeBound p A B k ∧ B + B ≤ magnitudeBound p A B k := by + have hp : 1 ≤ 16 ^ k := by + have h : 0 < 16 ^ k := by positivity + omega + have hm := Nat.mul_le_mul_left (2 * CyclicConvolution.magnitudeBound p A B) hp + simp only [Nat.mul_one] at hm + have ha := scalar_bound_left p A B + have hb := scalar_bound_right p A B + constructor <;> unfold magnitudeBound <;> omega + +/-- At scalar leaves the original cyclic kernel admission follows from the node admission. -/ +theorem scalar_le_magnitudeBound (p A B : ℕ) : + CyclicConvolution.magnitudeBound p A B ≤ magnitudeBound p A B 0 := by + simp only [magnitudeBound, pow_zero, Nat.mul_one] + omega + +/-- Every child's envelope follows from the original parent's, including doubled input forms. -/ +theorem child_magnitudeBound_le (p A B k : ℕ) : + magnitudeBound p (2 * A) (2 * B) k ≤ magnitudeBound p A B (k + 1) := by + have hscalar : CyclicConvolution.magnitudeBound p (2 * A) (2 * B) ≤ + 4 * CyclicConvolution.magnitudeBound p A B := by + unfold CyclicConvolution.magnitudeBound + nlinarith [Nat.zero_le ((p + 1) * (A * B))] + have hm := Nat.mul_le_mul_right (16 ^ k) + (show 2 * CyclicConvolution.magnitudeBound p (2 * A) (2 * B) ≤ + 32 * CyclicConvolution.magnitudeBound p A B from by omega) + convert hm using 1 + · rfl + · unfold magnitudeBound + rw [pow_succ] + ring + +/-- Four previous child contributions and one next contribution fit without relying on +an unproved cancellation or sparsity bound. -/ +theorem accumulation_le_magnitudeBound (p A B k : ℕ) : + 5 * (p * ((2 * A) * (2 * B)) * 16 ^ k) ≤ magnitudeBound p A B (k + 1) := by + have hscalar : p * (A * B) ≤ CyclicConvolution.magnitudeBound p A B := by + unfold CyclicConvolution.magnitudeBound + nlinarith [Nat.zero_le (A * B)] + have hm := Nat.mul_le_mul_right (16 ^ k) + (show 20 * (p * (A * B)) ≤ 32 * CyclicConvolution.magnitudeBound p A B from by omega) + convert hm using 1 + · ring + · unfold magnitudeBound + rw [pow_succ] + ring + +/-- The actual child-buffer advancement and its child size exactly fill the parent scratch span. -/ +theorem child_scratch_end (scratch p k : ℕ) : + scratch + 3 * (4 ^ k * p) + 4 ^ k * p = scratch + 4 ^ (k + 1) * p := by + rw [pow_succ] + ring + +/-- An actual seven-word push leaves the numerical descendant address envelope unchanged. -/ +theorem addressBound_child (frames frame k : ℕ) : + addressBound frames (frame + 7) k = addressBound frames frame (k + 1) := by + unfold addressBound + ring + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Child.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Child.lean new file mode 100644 index 0000000000..2dc8f8c472 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Child.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.Strassen.State +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Push + +/-! +# Computed Strassen child buffers + +All six child fields below are produced by the actual `locateChild` block. The layout +assertions are internal loop invariants derived from its numerical result, never a +precomputed layout parameter supplied to the program. The common parent header and every +earlier frame word survive this read-only stage. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM + +/-- Exact child words computed by the native six-instruction buffer calculation. -/ +structure ChildBuffers (p k scratch : ℕ) (x : Data 64) : Prop where + /-- Runtime quarter length. -/ + quarter_eq : x.regs Reg.quarter = 4 ^ k * p + /-- Actual first form buffer. -/ + formA_eq : x.regs Reg.formA = scratch + /-- Actual second form buffer. -/ + formB_eq : x.regs Reg.formB = scratch + 4 ^ k * p + /-- Actual child output buffer. -/ + product_eq : x.regs Reg.product = scratch + 2 * (4 ^ k * p) + /-- Remaining scratch begins after the three real buffers. -/ + scratch_eq : x.regs Reg.childScratch = scratch + 3 * (4 ^ k * p) + /-- Actual child depth. -/ + depth_eq : x.regs Reg.childDepth = k + +/-- The low-bank operand kernels preserve every computed child-buffer register. -/ +theorem ChildBuffers.of_regs_eq {p k scratch : ℕ} {x y : Data 64} + (h : ChildBuffers p k scratch x) + (hr : ∀ i, 50 ≤ i.val → i.val < 56 → y.regs i = x.regs i) : + ChildBuffers p k scratch y := + ⟨(hr _ (by decide) (by decide)).trans h.quarter_eq, + (hr _ (by decide) (by decide)).trans h.formA_eq, + (hr _ (by decide) (by decide)).trans h.formB_eq, + (hr _ (by decide) (by decide)).trans h.product_eq, + (hr _ (by decide) (by decide)).trans h.scratch_eq, + (hr _ (by decide) (by decide)).trans h.depth_eq⟩ + +/-- The native block derives all child buffer words and preserves the parent header/phase, +using only the original node address envelope for every arithmetic fit. -/ +theorem exec_locateChild_header {w p k a b out scratch frames frame : ℕ} + (x : Data 64) (hx : NodeHeader p (k + 1) a b out scratch frames frame x) + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (haddr : Fits w (addressBound frames frame (k + 1))) : + ∃ y, ExecWithin w locateChild x y 6 ∧ + NodeHeader p (k + 1) a b out scratch frames frame y ∧ + ChildBuffers p k scratch y ∧ y.mem = x.mem ∧ + ∀ i, i.val < 50 → y.regs i = x.regs i := by + have hsize : 4 ^ (k + 1) * p = 4 * (4 ^ k * p) := by rw [pow_succ]; ring + have hq : x.regs Reg.size / 4 = 4 ^ k * p := by rw [hx.size_eq, hsize]; omega + have hbefore := hl.scratch_before + obtain ⟨y, hy, hm, hquarter, ha, hb, hp, hs, hd, hr⟩ := exec_locateChild x hx.one_eq hx.four_eq + (by + rw [hx.scratch_eq, hx.size_eq, hx.depth_eq] + exact haddr.of_le (by unfold addressBound; omega)) + have hhigh (i : Fin 64) (hi : i.val < 50) : y.regs i = x.regs i := + hr i (by intro he; subst i; contradiction) (by intro he; subst i; contradiction) + (by intro he; subst i; contradiction) (by intro he; subst i; contradiction) + (by intro he; subst i; contradiction) (by intro he; subst i; contradiction) + refine ⟨y, hy, hx.of_regs_eq (fun i _ hi => hhigh i hi), ?_, hm, hhigh⟩ + refine ⟨hquarter.trans hq, ?_, ?_, ?_, ?_, ?_⟩ + · simpa only [hx.scratch_eq] using ha + · simpa only [hx.scratch_eq, hq] using hb + · simpa only [hx.scratch_eq, hq] using hp + · simpa only [hx.scratch_eq, hq] using hs + · simpa only [hx.depth_eq, Nat.add_sub_cancel_right] using hd + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Control.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Control.lean new file mode 100644 index 0000000000..5a1ca758f6 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Control.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.Strassen.Frame + +/-! +# Native Strassen clearing and stack returns + +These internal execution lemmas reuse the existing `zeroFill_exec` implementation. The +clearing footprint is one current output matrix, never a whole caller catalogue or window. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM + +/-- Clear the actual current output, deriving its end pointer and charging every store. +Only the two cursor registers change. -/ +theorem exec_clear {w : ℕ} (x : Data 64) (h0 : x.regs Reg.zero = 0) + (h1 : x.regs Reg.one = 1) (hw : Fits w (x.regs Reg.output + x.regs Reg.size)) : + ∃ y, ExecWithin w clear x y (4 * x.regs Reg.size + 4) ∧ + y.mem = WordRAM.clear x.mem (x.regs Reg.output) (x.regs Reg.size) ∧ + ∀ i, i ≠ Reg.address → i ≠ Reg.index → y.regs i = x.regs i := by + let z := (x.set Reg.address (x.regs Reg.output)).set Reg.index + (x.regs Reg.output + x.regs Reg.size) + have hsetup : ExecWithin w + (ram[ Reg.address ← Reg.output; Reg.index ← Reg.output + Reg.size ]) x z 2 := by + ram_eval [z, hw, Reg.address, Reg.index, Reg.output, Reg.size] + have he := zeroFill_exec (x := z) (w := w) + (cur := Reg.address) (stop := Reg.index) (zero := Reg.zero) (one := Reg.one) + (by decide) (by decide) (by decide) + (by simpa +decide [z, Reg.address, Reg.index, Reg.zero] using h0) + (by simpa +decide [z, Reg.address, Reg.index, Reg.one] using h1) + (by simp +decide [z, Reg.address, Reg.index]) + (by simpa +decide [z, Reg.address, Reg.index] using hw) + have hcount : z.regs Reg.index - z.regs Reg.address = x.regs Reg.size := by + simp +decide [z, Reg.index, Reg.address] + let y : Data 64 := ⟨Function.update z.regs Reg.address (z.regs Reg.index), + WordRAM.clear z.mem (z.regs Reg.address) (z.regs Reg.index - z.regs Reg.address)⟩ + refine ⟨y, ?_, ?_, ?_⟩ + · exact (hsetup.seq he.within).mono (by rw [hcount]; omega) + · dsimp only [y] + rw [hcount] + rfl + · intro i ha hi + simp only [y, z, Data.set, Function.update_of_ne ha, Function.update_of_ne hi] + +/-- Return to the parent or finish the root in three instructions. +The subtraction is charged, and its fit follows from the actual frame pointer. -/ +theorem exec_pop {w : ℕ} (x : Data 64) (h7 : x.regs Reg.frameWidth = 7) + (hw : Fits w (x.regs Reg.frame)) : + ∃ y, ExecWithin w pop x y 3 ∧ y.mem = x.mem ∧ + y.regs Reg.frame = + (if x.regs Reg.frames < x.regs Reg.frame then x.regs Reg.frame - 7 + else x.regs Reg.frame) ∧ + y.regs Reg.active = + (if x.regs Reg.frames < x.regs Reg.frame then x.regs Reg.active else 0) ∧ + ∀ i, i ≠ Reg.frame → i ≠ Reg.active → y.regs i = x.regs i := by + by_cases hc : x.regs Reg.frames < x.regs Reg.frame + · have hd : Fits w (x.regs Reg.frame - 7) := hw.of_le (by omega) + let y := x.set Reg.frame (x.regs Reg.frame - 7) + have he : ExecWithin w pop x y 3 := by ram_eval [pop, y, hc, h7, hd] + refine ⟨y, he, rfl, ?_, ?_, ?_⟩ + · simp +decide [y, hc, Reg.frame] + · simp +decide [y, hc, Reg.frame, Reg.active] + · intro i hf ha + simp only [y, Data.set, Function.update_of_ne hf] + · let y := x.set Reg.active 0 + have he : ExecWithin w pop x y 3 := + .branch_neg (by simpa only [Cond.holds_lt] using hc) (.const (fits_zero w)) + refine ⟨y, he, rfl, ?_, ?_, ?_⟩ + · simp +decide [y, hc, Reg.frame, Reg.active] + · simp +decide [y, hc, Reg.frame, Reg.active] + · intro i hf ha + simp only [y, Data.set, Function.update_of_ne ha] + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/CopyForm.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/CopyForm.lean new file mode 100644 index 0000000000..87560b173a --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/CopyForm.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.MatrixMultiplication.Strassen.Quadrant + +/-! +# Actual single-quadrant Strassen operand forms + +The zero-sign form is a real call to the existing copying loop, not a pointer or layout +oracle. Both disjoint address orders are admitted. The source is one of the current matrix +quarters and the destination is the actual caller-provided child operand buffer. +These are internal stage interfaces for composing the full explicit-stack proof. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM + +/-- Copy one real quadrant, including its computed source pointer, argument setup and every +native copy-loop instruction. All high controller fields except the offset temporary survive. -/ +theorem exec_copyForm {w q t : ℕ} (base dst : Fin 64) (x : Data 64) + (hd : 32 ≤ dst.val) + (hbt : base ≠ Reg.temporary) (hdt : dst ≠ Reg.temporary) + (hq : x.regs Reg.quarter = q) (ht : t < 4) + (hw : Fits w (x.regs base + 4 * q + x.regs dst + q + 8)) + (hsep : x.regs base + 4 * q ≤ x.regs dst ∨ x.regs dst + q ≤ x.regs base) : + ∃ y, ExecWithin w (form base dst t 0 0) x y (7 * q + 9) ∧ + (∀ j < q, y.mem (x.regs dst + j) = x.mem (x.regs base + t * q + j)) ∧ + (∀ a, a < x.regs dst ∨ x.regs dst + q ≤ a → y.mem a = x.mem a) ∧ + ∀ i, 32 ≤ i.val → i ≠ Reg.temporary → y.regs i = x.regs i := by + have hqfit : Fits w (x.regs base + 4 * x.regs Reg.quarter + 4) := by + rw [hq] + exact hw.of_le (by omega) + obtain ⟨s, hs, hsm, hsrc, hsr⟩ := exec_quadrant base Reg.copySource x hbt ht hqfit + have hhigh (i : Fin 64) (hi : 32 ≤ i.val) (htemp : i ≠ Reg.temporary) : + s.regs i = x.regs i := hsr i (by intro he; subst i; contradiction) htemp + have hsq := (hhigh Reg.quarter (by decide) (by decide)).trans hq + have hsd := hhigh dst hd hdt + have hss : s.regs Reg.copySource = x.regs base + t * q := by simpa only [hq] using hsrc + let z := (s.set Reg.copyTarget (x.regs dst)).set Reg.copyLength q + have hargs : ExecWithin (B := .standard) w + (ram[ Reg.copyTarget ← dst; Reg.copyLength ← Reg.quarter ]) s z 2 := by + ram_eval [z, hsq, hsd, Reg.copyTarget, Reg.copyLength] + let f : Fin 7 → Fin 64 := Fin.castLE (by decide) + have hzs : (z.comap f).regs ArrayCopy.src = x.regs base + t * q := by + simpa +decide [z, f, Data.comap, Reg.copyTarget, Reg.copyLength, + Reg.copySource, ArrayCopy.src] using hss + have hzd : (z.comap f).regs ArrayCopy.dst = x.regs dst := by + simp +decide [z, f, Data.comap, Reg.copyTarget, Reg.copyLength, ArrayCopy.dst] + have hzl : (z.comap f).regs ArrayCopy.len = q := by + simp [z, f, Data.comap, Reg.copyLength, ArrayCopy.len] + have hquarter : t * q + q ≤ 4 * q := by + have hmul := Nat.mul_le_mul_right q (Nat.succ_le_of_lt ht) + simpa only [Nat.succ_mul] using hmul + obtain ⟨u, hu, hum, _, _, _⟩ := ArrayCopy.exec_of_separated_or_eq (z.comap f) + (hw.of_le (by omega)) + (by rw [hzs, hzl]; exact hw.of_le (by omega)) + (by rw [hzd, hzl]; exact hw.of_le (by omega)) + (by + rw [hzs, hzd, hzl] + rcases hsep with h | h + · exact Or.inl (by omega) + · exact Or.inr (Or.inl (by omega))) + let y := u.extend f z + have hcopy : ExecWithin w (ArrayCopy.program.rename f) z y (7 * q + 4) := by + simpa only [hzl] using hu.rename (Fin.castLE_injective _) + have he : ExecWithin w (form base dst t 0 0) x y (7 * q + 9) := by + simpa only [form, ite_true, f] using + (hs.seq (hargs.seq hcopy)).mono (by omega : 3 + (2 + (7 * q + 4)) ≤ 7 * q + 9) + have hym : y.mem = ArrayCopy.copyPrefix x.mem (x.regs base + t * q) (x.regs dst) q := by + rw [hzs, hzd, hzl] at hum + have hzm : z.mem = x.mem := hsm + simpa only [y, Data.extend, Data.comap_mem, hzm] using hum + refine ⟨y, he, ?_, ?_, ?_⟩ + · intro j hj + rw [hym] + simpa only [Nat.add_assoc] using ArrayCopy.copyPrefix_read (mem := x.mem) + (src := x.regs base + t * q) (dst := x.regs dst) hj + · intro a ha + rw [hym] + exact ArrayCopy.copyPrefix_outside (by omega) + · intro i hi htemp + have hn : ¬∃ j, f j = i := by + rintro ⟨j, hj⟩ + have hv := congrArg Fin.val hj + change j.val = i.val at hv + omega + have hyi : y.regs i = z.regs i := Function.extend_apply' _ _ _ hn + apply hyi.trans + have hi1 : i ≠ Reg.copyTarget := by intro h; subst i; contradiction + have hi2 : i ≠ Reg.copyLength := by intro h; subst i; contradiction + simpa only [z, Data.set, Function.update_of_ne hi1, Function.update_of_ne hi2] using + hhigh i hi htemp + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Dispatch.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Dispatch.lean new file mode 100644 index 0000000000..7d19f2b5c5 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Dispatch.lean @@ -0,0 +1,253 @@ +/- +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.Strassen.Program +import Mathlib.Tactic.FinCases + +/-! +# Charged native Strassen phase dispatch + +This internal proof follows every actual comparison and tag constant in the fixed branch +chain. The selected stage executes ordinary native code; no uncharged table dispatch is +introduced. Positive branches cost one instruction, negative branches two. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM + +/-- Comparisons and tag constants executed before the selected real stage. -/ +@[expose] def dispatchBudget (phase : Fin 8) : ℕ := + if phase.val < 7 then 3 * phase.val + 1 else 20 + +/-- The tag value actually assigned by the branch chain before entering a stage. -/ +@[expose] def dispatchState (phase : Fin 8) (x : Data 64) : Data 64 := + if phase.val = 0 then x else x.set Reg.tag (min phase.val 6) + +/-- The dispatch prefix only writes the tag register. -/ +theorem dispatchState_regs (phase : Fin 8) (x : Data 64) (i : Fin 64) (hi : i ≠ Reg.tag) : + (dispatchState phase x).regs i = x.regs i := by + unfold dispatchState + split + · rfl + · exact Function.update_of_ne hi _ _ + +/-- Comparisons and tag constants never change memory. -/ +theorem dispatchState_mem (phase : Fin 8) (x : Data 64) : (dispatchState phase x).mem = x.mem := by + unfold dispatchState + split <;> rfl + +/-- Follow the actual phase chain, charging every failed comparison and tag assignment. +The stage premise is an internal composition step, discharged by the native stage lemmas. -/ +theorem exec_dispatch {w T : ℕ} (phase : Fin 8) (x y : Data 64) + (hzero : x.regs Reg.zero = 0) (hphase : x.regs Reg.phase = phase.val) + (hfit : Fits w 6) + (hstage : ExecWithin w (stage phase) (dispatchState phase x) y T) : + ExecWithin w dispatch x y (T + dispatchBudget phase) := by + fin_cases phase + all_goals simp only [dispatchBudget] + all_goals norm_num + all_goals unfold dispatch + all_goals change x.regs (38 : Fin 64) = _ at hphase + all_goals change x.regs (49 : Fin 64) = 0 at hzero + case «0» => + have hc : (Cond.eq Reg.phase Reg.zero).Holds x := by + change x.regs (38 : Fin 64) = x.regs (49 : Fin 64) + exact hphase.trans hzero.symm + exact .branch_pos hc (by simpa [dispatchState, stage] using hstage) + case «1» => + let s1 := x.set Reg.tag 1 + have hstart : ExecWithin w (stage 1) s1 y T := by + simpa [dispatchState, stage, s1, Data.set, Function.update_idem] using hstage + have hc0 : ¬(Cond.eq Reg.phase Reg.zero).Holds x := by + norm_num [Cond.holds_eq, s1, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, hzero] + have hc1 : (Cond.eq Reg.phase Reg.tag).Holds s1 := by + norm_num [Cond.holds_eq, s1, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, hzero] + exact (ExecWithin.branch_neg hc0 ((ExecWithin.const (x := x) (d := Reg.tag) (v := 1) + (hfit.of_le (by decide))).seq (ExecWithin.branch_pos hc1 hstart))).mono (by omega) + case «2» => + let s1 := x.set Reg.tag 1 + let s2 := s1.set Reg.tag 2 + have hstart : ExecWithin w (stage 2) s2 y T := by + simpa [dispatchState, stage, s1, s2, Data.set, Function.update_idem] using hstage + have hc0 : ¬(Cond.eq Reg.phase Reg.zero).Holds x := by + norm_num [Cond.holds_eq, s1, s2, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, hzero] + have hc1 : ¬(Cond.eq Reg.phase Reg.tag).Holds s1 := by + norm_num [Cond.holds_eq, s1, s2, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, hzero] + have hc2 : (Cond.eq Reg.phase Reg.tag).Holds s2 := by + norm_num [Cond.holds_eq, s1, s2, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, hzero] + exact (ExecWithin.branch_neg hc0 ((ExecWithin.const (x := x) (d := Reg.tag) (v := 1) + (hfit.of_le (by decide))).seq (ExecWithin.branch_neg hc1 ((ExecWithin.const (x := s1) (d := + Reg.tag) (v := 2) (hfit.of_le (by decide))).seq (ExecWithin.branch_pos hc2 hstart))))).mono + (by omega) + case «3» => + let s1 := x.set Reg.tag 1 + let s2 := s1.set Reg.tag 2 + let s3 := s2.set Reg.tag 3 + have hstart : ExecWithin w (stage 3) s3 y T := by + simpa [dispatchState, stage, s1, s2, s3, Data.set, Function.update_idem] using hstage + have hc0 : ¬(Cond.eq Reg.phase Reg.zero).Holds x := by + norm_num [Cond.holds_eq, s1, s2, s3, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, hzero] + have hc1 : ¬(Cond.eq Reg.phase Reg.tag).Holds s1 := by + norm_num [Cond.holds_eq, s1, s2, s3, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, hzero] + have hc2 : ¬(Cond.eq Reg.phase Reg.tag).Holds s2 := by + norm_num [Cond.holds_eq, s1, s2, s3, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, hzero] + have hc3 : (Cond.eq Reg.phase Reg.tag).Holds s3 := by + norm_num [Cond.holds_eq, s1, s2, s3, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, hzero] + exact (ExecWithin.branch_neg hc0 ((ExecWithin.const (x := x) (d := Reg.tag) (v := 1) + (hfit.of_le (by decide))).seq (ExecWithin.branch_neg hc1 ((ExecWithin.const (x := s1) (d := + Reg.tag) (v := 2) (hfit.of_le (by decide))).seq (ExecWithin.branch_neg hc2 + ((ExecWithin.const (x := s2) (d := Reg.tag) (v := 3) (hfit.of_le (by decide))).seq + (ExecWithin.branch_pos hc3 hstart))))))).mono (by omega) + case «4» => + let s1 := x.set Reg.tag 1 + let s2 := s1.set Reg.tag 2 + let s3 := s2.set Reg.tag 3 + let s4 := s3.set Reg.tag 4 + have hstart : ExecWithin w (stage 4) s4 y T := by + simpa [dispatchState, stage, s1, s2, s3, s4, Data.set, Function.update_idem] using hstage + have hc0 : ¬(Cond.eq Reg.phase Reg.zero).Holds x := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, + hzero] + have hc1 : ¬(Cond.eq Reg.phase Reg.tag).Holds s1 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, + hzero] + have hc2 : ¬(Cond.eq Reg.phase Reg.tag).Holds s2 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, + hzero] + have hc3 : ¬(Cond.eq Reg.phase Reg.tag).Holds s3 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, + hzero] + have hc4 : (Cond.eq Reg.phase Reg.tag).Holds s4 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, + hzero] + exact (ExecWithin.branch_neg hc0 ((ExecWithin.const (x := x) (d := Reg.tag) (v := 1) + (hfit.of_le (by decide))).seq (ExecWithin.branch_neg hc1 ((ExecWithin.const (x := s1) (d := + Reg.tag) (v := 2) (hfit.of_le (by decide))).seq (ExecWithin.branch_neg hc2 + ((ExecWithin.const (x := s2) (d := Reg.tag) (v := 3) (hfit.of_le (by decide))).seq + (ExecWithin.branch_neg hc3 ((ExecWithin.const (x := s3) (d := Reg.tag) (v := 4) (hfit.of_le + (by decide))).seq (ExecWithin.branch_pos hc4 hstart))))))))).mono (by omega) + case «5» => + let s1 := x.set Reg.tag 1 + let s2 := s1.set Reg.tag 2 + let s3 := s2.set Reg.tag 3 + let s4 := s3.set Reg.tag 4 + let s5 := s4.set Reg.tag 5 + have hstart : ExecWithin w (stage 5) s5 y T := by + simpa [dispatchState, stage, s1, s2, s3, s4, s5, Data.set, Function.update_idem] using hstage + have hc0 : ¬(Cond.eq Reg.phase Reg.zero).Holds x := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, + hzero] + have hc1 : ¬(Cond.eq Reg.phase Reg.tag).Holds s1 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, + hzero] + have hc2 : ¬(Cond.eq Reg.phase Reg.tag).Holds s2 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, + hzero] + have hc3 : ¬(Cond.eq Reg.phase Reg.tag).Holds s3 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, + hzero] + have hc4 : ¬(Cond.eq Reg.phase Reg.tag).Holds s4 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, + hzero] + have hc5 : (Cond.eq Reg.phase Reg.tag).Holds s5 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, Data.set, Reg.phase, Reg.zero, Reg.tag, hphase, + hzero] + exact (ExecWithin.branch_neg hc0 ((ExecWithin.const (x := x) (d := Reg.tag) (v := 1) + (hfit.of_le (by decide))).seq (ExecWithin.branch_neg hc1 ((ExecWithin.const (x := s1) (d := + Reg.tag) (v := 2) (hfit.of_le (by decide))).seq (ExecWithin.branch_neg hc2 + ((ExecWithin.const (x := s2) (d := Reg.tag) (v := 3) (hfit.of_le (by decide))).seq + (ExecWithin.branch_neg hc3 ((ExecWithin.const (x := s3) (d := Reg.tag) (v := 4) (hfit.of_le + (by decide))).seq (ExecWithin.branch_neg hc4 ((ExecWithin.const (x := s4) (d := Reg.tag) (v + := 5) (hfit.of_le (by decide))).seq (ExecWithin.branch_pos hc5 hstart))))))))))).mono (by + omega) + case «6» => + let s1 := x.set Reg.tag 1 + let s2 := s1.set Reg.tag 2 + let s3 := s2.set Reg.tag 3 + let s4 := s3.set Reg.tag 4 + let s5 := s4.set Reg.tag 5 + let s6 := s5.set Reg.tag 6 + have hstart : ExecWithin w (stage 6) s6 y T := by + simpa [dispatchState, stage, s1, s2, s3, s4, s5, s6, Data.set, Function.update_idem] using + hstage + have hc0 : ¬(Cond.eq Reg.phase Reg.zero).Holds x := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, s6, Data.set, Reg.phase, Reg.zero, Reg.tag, + hphase, hzero] + have hc1 : ¬(Cond.eq Reg.phase Reg.tag).Holds s1 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, s6, Data.set, Reg.phase, Reg.zero, Reg.tag, + hphase, hzero] + have hc2 : ¬(Cond.eq Reg.phase Reg.tag).Holds s2 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, s6, Data.set, Reg.phase, Reg.zero, Reg.tag, + hphase, hzero] + have hc3 : ¬(Cond.eq Reg.phase Reg.tag).Holds s3 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, s6, Data.set, Reg.phase, Reg.zero, Reg.tag, + hphase, hzero] + have hc4 : ¬(Cond.eq Reg.phase Reg.tag).Holds s4 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, s6, Data.set, Reg.phase, Reg.zero, Reg.tag, + hphase, hzero] + have hc5 : ¬(Cond.eq Reg.phase Reg.tag).Holds s5 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, s6, Data.set, Reg.phase, Reg.zero, Reg.tag, + hphase, hzero] + have hc6 : (Cond.eq Reg.phase Reg.tag).Holds s6 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, s6, Data.set, Reg.phase, Reg.zero, Reg.tag, + hphase, hzero] + exact (ExecWithin.branch_neg hc0 ((ExecWithin.const (x := x) (d := Reg.tag) (v := 1) + (hfit.of_le (by decide))).seq (ExecWithin.branch_neg hc1 ((ExecWithin.const (x := s1) (d := + Reg.tag) (v := 2) (hfit.of_le (by decide))).seq (ExecWithin.branch_neg hc2 + ((ExecWithin.const (x := s2) (d := Reg.tag) (v := 3) (hfit.of_le (by decide))).seq + (ExecWithin.branch_neg hc3 ((ExecWithin.const (x := s3) (d := Reg.tag) (v := 4) (hfit.of_le + (by decide))).seq (ExecWithin.branch_neg hc4 ((ExecWithin.const (x := s4) (d := Reg.tag) (v + := 5) (hfit.of_le (by decide))).seq (ExecWithin.branch_neg hc5 ((ExecWithin.const (x := s5) + (d := Reg.tag) (v := 6) (hfit.of_le (by decide))).seq (ExecWithin.branch_pos hc6 + hstart))))))))))))).mono (by omega) + case «7» => + let s1 := x.set Reg.tag 1 + let s2 := s1.set Reg.tag 2 + let s3 := s2.set Reg.tag 3 + let s4 := s3.set Reg.tag 4 + let s5 := s4.set Reg.tag 5 + let s6 := s5.set Reg.tag 6 + have hstart : ExecWithin w (stage 7) s6 y T := by + simpa [dispatchState, stage, s1, s2, s3, s4, s5, s6, Data.set, Function.update_idem] using + hstage + have hc0 : ¬(Cond.eq Reg.phase Reg.zero).Holds x := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, s6, Data.set, Reg.phase, Reg.zero, Reg.tag, + hphase, hzero] + have hc1 : ¬(Cond.eq Reg.phase Reg.tag).Holds s1 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, s6, Data.set, Reg.phase, Reg.zero, Reg.tag, + hphase, hzero] + have hc2 : ¬(Cond.eq Reg.phase Reg.tag).Holds s2 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, s6, Data.set, Reg.phase, Reg.zero, Reg.tag, + hphase, hzero] + have hc3 : ¬(Cond.eq Reg.phase Reg.tag).Holds s3 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, s6, Data.set, Reg.phase, Reg.zero, Reg.tag, + hphase, hzero] + have hc4 : ¬(Cond.eq Reg.phase Reg.tag).Holds s4 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, s6, Data.set, Reg.phase, Reg.zero, Reg.tag, + hphase, hzero] + have hc5 : ¬(Cond.eq Reg.phase Reg.tag).Holds s5 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, s6, Data.set, Reg.phase, Reg.zero, Reg.tag, + hphase, hzero] + have hc6 : ¬(Cond.eq Reg.phase Reg.tag).Holds s6 := by + norm_num [Cond.holds_eq, s1, s2, s3, s4, s5, s6, Data.set, Reg.phase, Reg.zero, Reg.tag, + hphase, hzero] + exact (ExecWithin.branch_neg hc0 ((ExecWithin.const (x := x) (d := Reg.tag) (v := 1) + (hfit.of_le (by decide))).seq (ExecWithin.branch_neg hc1 ((ExecWithin.const (x := s1) (d := + Reg.tag) (v := 2) (hfit.of_le (by decide))).seq (ExecWithin.branch_neg hc2 + ((ExecWithin.const (x := s2) (d := Reg.tag) (v := 3) (hfit.of_le (by decide))).seq + (ExecWithin.branch_neg hc3 ((ExecWithin.const (x := s3) (d := Reg.tag) (v := 4) (hfit.of_le + (by decide))).seq (ExecWithin.branch_neg hc4 ((ExecWithin.const (x := s4) (d := Reg.tag) (v + := 5) (hfit.of_le (by decide))).seq (ExecWithin.branch_neg hc5 ((ExecWithin.const (x := s5) + (d := Reg.tag) (v := 6) (hfit.of_le (by decide))).seq (ExecWithin.branch_neg hc6 + (hstart)))))))))))))).mono (by omega) + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Entry.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Entry.lean new file mode 100644 index 0000000000..8bc9e7206f --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Entry.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.Strassen.Zero +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Prepare +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Control +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Dispatch + +/-! +# Actual positive-depth Strassen entry + +The first parent stage clears only its current output and materializes the first child. +Original coefficient arrays and older frames are protected by the derived native footprint. +Together with the scalar case, this is an internal stage composed by `Strassen.Traversal`. +The modular triangle counter still requires its own complete pipeline composition. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- The first real parent stage clears its output and computes both first-child operands. +No clean output or preformed child input is required. -/ +theorem exec_stage_zero {w p k A B a b out scratch frames frame : ℕ} + (x : Data 64) (u v : ℕ → ℤ) + (hx : NodeHeader p (k + 1) a b out scratch frames frame x) + (hc : ChildBuffers p k scratch x) + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (hu : ∀ i < 4 ^ (k + 1) * p, (u i).natAbs ≤ A) + (hv : ∀ i < 4 ^ (k + 1) * p, (v i).natAbs ≤ B) + (hleft : ∀ i < 4 ^ (k + 1) * p, x.mem (a + i) = encode (u i)) + (hright : ∀ i < 4 ^ (k + 1) * p, x.mem (b + i) = encode (v i)) + (hmag : GenericRAM.Signed.Fits w (magnitudeBound p A B (k + 1) : ℤ)) + (haddr : Fits (w + 2) (addressBound frames frame (k + 1))) : + ∃ y, ExecWithin (w + 2) (stage 0) x y (68 * (4 ^ k * p) + 54) ∧ + NodeReady p k scratch (scratch + 4 ^ k * p) (scratch + 2 * (4 ^ k * p)) + (scratch + 3 * (4 ^ k * p)) frames (frame + 7) y ∧ + (∀ j < 4 ^ k * p, y.mem (scratch + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.leftOperand p k u 0 j)) ∧ + (∀ j < 4 ^ k * p, y.mem (scratch + 4 ^ k * p + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.rightOperand p k v 0 j)) ∧ + (∀ j < 4 ^ (k + 1) * p, y.mem (out + j) = 0) ∧ + y.mem (frame + 6) = 1 ∧ + ∀ d, (d < out ∨ out + 4 ^ (k + 1) * p ≤ d) → + (d < scratch ∨ scratch + 2 * (4 ^ k * p) ≤ d) → + (d < frame + 6 ∨ frame + 14 ≤ d) → y.mem d = x.mem d := by + let q := 4 ^ k * p + have hsize : 4 ^ (k + 1) * p = 4 * q := by dsimp [q]; rw [pow_succ]; ring + have ho := hl.output_before + have hs := hl.scratch_before + have hf := hl.frames_before + obtain ⟨s, he, hm, hr⟩ := exec_clear x hx.zero_eq hx.one_eq + (by rw [hx.output_eq, hx.size_eq]; exact haddr.of_le (by unfold addressBound; omega)) + have sh : NodeHeader p (k + 1) a b out scratch frames frame s := hx.of_regs_eq_cursors hr + have sc : ChildBuffers p k scratch s := hc.of_regs_eq (fun i hi hi' => + hr i (by intro he; subst i; contradiction) (by intro he; subst i; contradiction)) + have houtside (d : ℕ) (hd : d < out ∨ out + 4 ^ (k + 1) * p ≤ d) : s.mem d = x.mem d := by + rw [hm, hx.output_eq, hx.size_eq] + exact WordRAM.clear_of_not_owns (by simp only [Block.Owns]; omega) + obtain ⟨y, hy, ready, leftValues, rightValues, parentPhase, memory⟩ := exec_prepareChild 0 + s u v sh sc hl hu hv + (by + intro i hi + rw [houtside _ ?_] + · exact hleft i hi + · rcases hl.left_disjoint with h | h <;> omega) + (by + intro i hi + rw [houtside _ ?_] + · exact hright i hi + · rcases hl.right_disjoint with h | h <;> omega) + hmag haddr + refine ⟨y, (he.seq hy).mono (by rw [hx.size_eq, hsize]; omega), ready, + leftValues, rightValues, ?_, parentPhase, ?_⟩ + · intro j hj + rw [memory _ (Or.inl (by omega)) (Or.inl (by omega)), hm, hx.output_eq, hx.size_eq] + exact WordRAM.clear_of_owns (by simpa only [Block.Owns] using And.intro (by omega) (by omega)) + · intro d hd hds hdf + exact (memory d hds hdf).trans (houtside d hd) + +/-- The actual first positive-depth controller iteration includes frame loading, depth +comparison, computed child pointers, phase dispatch, clearing and both operand loops. -/ +theorem exec_step_entry {w p k A B a b out scratch frames frame : ℕ} + (x : Data 64) (u v : ℕ → ℤ) + (hx : NodeReady p (k + 1) a b out scratch frames frame x) + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (hu : ∀ i < 4 ^ (k + 1) * p, (u i).natAbs ≤ A) + (hv : ∀ i < 4 ^ (k + 1) * p, (v i).natAbs ≤ B) + (hleft : ∀ i < 4 ^ (k + 1) * p, x.mem (a + i) = encode (u i)) + (hright : ∀ i < 4 ^ (k + 1) * p, x.mem (b + i) = encode (v i)) + (hmag : GenericRAM.Signed.Fits w (magnitudeBound p A B (k + 1) : ℤ)) + (haddr : Fits (w + 2) (addressBound frames frame (k + 1))) : + ∃ y, ExecWithin (w + 2) step x y (68 * (4 ^ k * p) + 77) ∧ + NodeReady p k scratch (scratch + 4 ^ k * p) (scratch + 2 * (4 ^ k * p)) + (scratch + 3 * (4 ^ k * p)) frames (frame + 7) y ∧ + (∀ j < 4 ^ k * p, y.mem (scratch + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.leftOperand p k u 0 j)) ∧ + (∀ j < 4 ^ k * p, y.mem (scratch + 4 ^ k * p + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.rightOperand p k v 0 j)) ∧ + (∀ j < 4 ^ (k + 1) * p, y.mem (out + j) = 0) ∧ + y.mem (frame + 6) = 1 ∧ + ∀ d, (d < out ∨ out + 4 ^ (k + 1) * p ≤ d) → + (d < scratch ∨ scratch + 2 * (4 ^ k * p) ≤ d) → + (d < frame + 6 ∨ frame + 14 ≤ d) → y.mem d = x.mem d := by + obtain ⟨s, hs, sm, sr⟩ := exec_loadCurrent x hx.one_eq + (by rw [hx.frame_eq]; exact haddr.of_le (by unfold addressBound; omega)) + (by simpa only [hx.frame_eq] using hx.frame_words) + have sh : NodeHeader p (k + 1) a b out scratch frames frame s := + hx.toNodeHeader.of_regs_eq_cursors (fun i hi _ => sr i hi) + obtain ⟨r, hr, rh, rc, rm, rr⟩ := exec_locateChild_header s sh hl haddr + have rphase : r.regs Reg.phase = 0 := + (rr Reg.phase (by decide)).trans ((sr Reg.phase (by decide)).trans hx.phase_eq) + obtain ⟨y, hy, ready, valuesA, valuesB, zeros, parentPhase, memory⟩ := + exec_stage_zero r u v rh rc hl hu hv + (by simpa only [rm, sm] using hleft) (by simpa only [rm, sm] using hright) hmag haddr + have hd : ExecWithin (w + 2) dispatch r y (68 * (4 ^ k * p) + 54 + dispatchBudget 0) := + exec_dispatch 0 r y rh.zero_eq rphase + (haddr.of_le (by unfold addressBound; omega)) + (by simpa [dispatchState] using hy) + have hc : ¬(Cond.eq Reg.depth Reg.zero).Holds s := by + simp only [Cond.holds_eq, sh.depth_eq, sh.zero_eq] + omega + have he : ExecWithin (w + 2) step x y (68 * (4 ^ k * p) + 77) := + (hs.seq (ExecWithin.branch_neg hc (hr.seq hd))).mono (by + norm_num [dispatchBudget] + omega) + refine ⟨y, he, ready, valuesA, valuesB, zeros, parentPhase, ?_⟩ + intro d hd hds hdf + simpa only [rm, sm] using memory d hd hds hdf + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Exec.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Exec.lean new file mode 100644 index 0000000000..abcfb76929 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Exec.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.Strassen.Traversal + +/-! +# Complete native cyclic-coefficient Strassen execution + +The actual fixed program computes its root power/size and frame address, initializes the +dirty root frame, executes all seven-product recursion through the explicit native stack, +and exits the real controller loop. Callers supply ordinary pointers and canonical signed +coefficient arrays with numerical separation and fits. No layout, multiplication, execution +or time certificate is an algorithm argument. The result agrees with the existing finite +Strassen recursion, whose separate correctness theorem identifies cyclic-ring matrix product. + +This caller-safe kernel does not yet compose CountPrime matrix preparation, coefficient +extraction, initialized original-weight input, or its final original-input width envelope. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Execute the complete native Strassen program from its actual runtime arguments. +Output and recursive workspace may be dirty. The bound includes setup and loop exit, +while a final flat halt, when appended by a caller, costs one additional instruction. -/ +theorem exec {w p k A B a b out scratch : ℕ} (x : Data 64) (u v : ℕ → ℤ) + (hp : 0 < p) (hpr : x.regs Reg.modulus = p) (hkr : x.regs Reg.depth = k) + (har : x.regs Reg.inputA = a) (hbr : x.regs Reg.inputB = b) + (hor : x.regs Reg.output = out) (hsr : x.regs Reg.scratch = scratch) + (ha : a + 4 ^ k * p ≤ scratch) (hb : b + 4 ^ k * p ≤ scratch) + (ho : out + 4 ^ k * p ≤ scratch) + (hdisjA : a + 4 ^ k * p ≤ out ∨ out + 4 ^ k * p ≤ a) + (hdisjB : b + 4 ^ k * p ≤ out ∨ out + 4 ^ k * p ≤ b) + (hu : ∀ i < 4 ^ k * p, (u i).natAbs ≤ A) + (hv : ∀ i < 4 ^ k * p, (v i).natAbs ≤ B) + (hleft : ∀ i < 4 ^ k * p, x.mem (a + i) = encode (u i)) + (hright : ∀ i < 4 ^ k * p, x.mem (b + i) = encode (v i)) + (hmag : GenericRAM.Signed.Fits w (magnitudeBound p A B k : ℤ)) + (haddr : Fits (w + 2) (addressBound (scratch + 4 ^ k * p) (scratch + 4 ^ k * p) k)) : + ∃ y, ExecWithin (w + 2) program x y (traceBudget p k + 4 * k + 32) ∧ + NodeResult p k out scratch (scratch + 4 ^ k * p) (scratch + 4 ^ k * p) u v x y := by + let frames := scratch + 4 ^ k * p + have hpow : 4 ^ k ≤ 4 ^ k * p := by + have hm := Nat.mul_le_mul_left (4 ^ k) (Nat.succ_le_of_lt hp) + simpa only [Nat.mul_one] using hm + have hsetupFit : Fits (w + 2) (x.regs Reg.scratch + x.regs Reg.modulus * + 4 ^ x.regs Reg.depth + 4 ^ x.regs Reg.depth + x.regs Reg.depth + 16) := by + rw [hsr, hpr, hkr, Nat.mul_comm p] + exact haddr.of_le (by unfold addressBound; omega) + obtain ⟨s, setupRun, ready, setupFrame⟩ := exec_setup_ready x hsetupFit + have ready' : NodeReady p k a b out scratch frames frames s := by + simpa only [hpr, hkr, har, hbr, hor, hsr] using ready + have layout : NodeLayout p k a b out scratch frames frames := + ⟨hp, ha, hb, ho, Nat.le_refl _, Nat.le_refl _, hdisjA, hdisjB⟩ + have smem (d : ℕ) (hd : d < frames ∨ frames + 7 ≤ d) : s.mem d = x.mem d := + setupFrame d (by simpa only [ready'.frame_eq] using hd) + obtain ⟨y, result, continuation⟩ := exec_ready k s u v ready' layout hu hv + (by intro i hi; rw [smem _ (Or.inl (by omega))]; exact hleft i hi) + (by intro i hi; rw [smem _ (Or.inl (by omega))]; exact hright i hi) hmag haddr + have hexit : ¬(Cond.lt Reg.zero Reg.active).Holds y := by + simp only [Cond.holds_lt, result.zero_eq, result.active_eq, lt_self_iff_false, ite_false] + decide + have hcontroller := continuation y 2 (ExecWithin.loop_exit hexit) + have he : ExecWithin (w + 2) program x y (traceBudget p k + 4 * k + 32) := + (setupRun.seq hcontroller).mono (by rw [hkr]; omega) + refine ⟨y, he, { result with outside := ?_ }⟩ + intro d hd + rw [result.outside d hd] + apply smem + have hframes : ¬(frames ≤ d ∧ d < frames + 7 * (k + 1)) := fun h => hd (Or.inr (Or.inr h)) + omega + +/-- The proved native instruction count is polynomial in the cyclic coefficient length +and grows as seven to the matrix depth. This bound concerns the actual execution in `exec`. -/ +theorem exec_budget_le (p k : ℕ) : traceBudget p k + 4 * k + 32 ≤ + (42 * p * p + 240 * p + 178) * 7 ^ k + 4 * k + 32 := by + have h := traceBudget_potential_le p k + omega + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Finish.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Finish.lean new file mode 100644 index 0000000000..1ff543406a --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Finish.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.Strassen.Receive +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Control + +/-! +# Native final Strassen child return + +The last actual product adds into the first output quarter, then the real parent/root pop +returns. The completed four partial sums equal the existing finite Strassen product on +every coefficient. This closes one parent's final stage, not the entire recursive traversal. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- The actual seventh return stage produces every parent coefficient and performs the +real parent/root pop. All inputs to this lemma are internal current-state invariants. -/ +theorem exec_stage_finish {w p k A B a b out scratch frames frame : ℕ} + (x : Data 64) (u v : ℕ → ℤ) + (hx : NodeHeader p (k + 1) a b out scratch frames frame x) + (hc : ChildBuffers p k scratch x) + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (hu : ∀ i < 4 ^ (k + 1) * p, (u i).natAbs ≤ A) + (hv : ∀ i < 4 ^ (k + 1) * p, (v i).natAbs ≤ B) + (hout : ∀ s j, j < 4 ^ k * p → x.mem (out + s.val * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue p k 6 u v s j)) + (hprod : ∀ j < 4 ^ k * p, x.mem (scratch + 2 * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.childProduct p k u v 6 j)) + (hmag : GenericRAM.Signed.Fits w (magnitudeBound p A B (k + 1) : ℤ)) + (haddr : Fits (w + 2) (addressBound frames frame (k + 1))) : + ∃ y, ExecWithin (w + 2) (stage 7) x y (26 * (4 ^ k * p) + 13) ∧ + NodeResult p (k + 1) out scratch frames frame u v x y := by + obtain ⟨s, hs, sh, _, outputValues, _, memory⟩ := exec_returnedUpdates 6 x u v + hx hc hl hu hv hout hprod hmag haddr + obtain ⟨y, hy, hm, hframe, hactive, hr⟩ := exec_pop s sh.frameWidth_eq + (by rw [sh.frame_eq]; exact haddr.of_le (by unfold addressBound; omega)) + have he := exec_stage_return 6 hs hy + have he' : ExecWithin (w + 2) (stage 7) x y (26 * (4 ^ k * p) + 13) := by + simpa [receiveBudget] using he.mono (show receiveBudget 6 (4 ^ k * p) + 3 ≤ + 26 * (4 ^ k * p) + 13 by norm_num [receiveBudget]) + refine ⟨y, he', ?_⟩ + refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · intro i hi + let q := 4 ^ k * p + have hq : 0 < q := Nat.mul_pos (by positivity) hl.modulus_pos + have hsize : 4 ^ (k + 1) * p = 4 * q := by dsimp [q]; rw [pow_succ]; ring + let t : Fin 4 := ⟨i / q, (Nat.div_lt_iff_lt_mul hq).mpr (by simpa only [hsize] using hi)⟩ + have hmidx : t.val * q + i % q = i := by + simpa only [t, Nat.mul_comm] using Nat.div_add_mod i q + have hmod : i % q < q := Nat.mod_lt i hq + rw [hm] + have hv' := outputValues t (i % q) hmod + have hp' := Cslib.Algorithms.MatrixMultiplication.Strassen.multiply_eq_partialValue + hl.modulus_pos u v t hmod + change s.mem (out + t.val * q + i % q) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue p k 7 u v t (i % q)) at hv' + rw [Nat.add_assoc, hmidx, ← hp', hmidx] at hv' + exact hv' + · intro d hd + rw [hm] + apply memory d + have hn : ¬(out ≤ d ∧ d < out + 4 ^ (k + 1) * p) := fun h => hd (Or.inl h) + omega + · exact (hr Reg.output (by decide) (by decide)).trans sh.output_eq + · exact (hr Reg.modulus (by decide) (by decide)).trans sh.modulus_eq + · exact (hr Reg.frames (by decide) (by decide)).trans sh.frames_eq + · simpa only [sh.frames_eq, sh.frame_eq] using hframe + · simpa only [sh.frames_eq, sh.frame_eq, sh.active_eq] using hactive + · exact (hr Reg.zero (by decide) (by decide)).trans sh.zero_eq + · exact (hr Reg.one (by decide) (by decide)).trans sh.one_eq + · exact (hr Reg.four (by decide) (by decide)).trans sh.four_eq + · exact (hr Reg.frameWidth (by decide) (by decide)).trans sh.frameWidth_eq + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Form.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Form.lean new file mode 100644 index 0000000000..9311ef21aa --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Form.lean @@ -0,0 +1,58 @@ +/- +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.Strassen.CopyForm +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.ArithmeticForm + +/-! +# Uniform execution of actual Strassen operand forms + +The zero-sign branch executes the proved native copying loop; the other branches execute +the proved signed array arithmetic loop. This internal helper derives its execution from +those real programs. No form values, run or cost certificate is supplied by the caller. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- The actual form code has one uniform bound for copying, addition and subtraction. -/ +theorem exec_form {w q t₁ t₂ A : ℕ} (base dst : Fin 64) (sign : ℤ) (x : Data 64) (u : ℕ → ℤ) + (hb : 32 ≤ base.val) (hd : 32 ≤ dst.val) + (hbt : base ≠ Reg.temporary) (hdt : dst ≠ Reg.temporary) + (hq : x.regs Reg.quarter = q) (ht₁ : t₁ < 4) (ht₂ : t₂ < 4) + (hbound : GenericRAM.Signed.Fits w ((A + A : ℕ) : ℤ)) + (hu : ∀ j < 4 * q, (u j).natAbs ≤ A) + (hinput : ∀ j < 4 * q, x.mem (x.regs base + j) = encode (u j)) + (hw : Fits (w + 2) (x.regs base + 4 * q + x.regs dst + q + 8)) + (hsep : x.regs base + 4 * q ≤ x.regs dst ∨ x.regs dst + q ≤ x.regs base) : + ∃ y, ExecWithin (w + 2) (form base dst t₁ t₂ sign) x y (26 * q + 12) ∧ + (∀ j < q, y.mem (x.regs dst + j) = encode + (if sign = 0 then u (t₁ * q + j) else if sign < 0 then + u (t₁ * q + j) - u (t₂ * q + j) else u (t₁ * q + j) + u (t₂ * q + j))) ∧ + (∀ a, a < x.regs dst ∨ x.regs dst + q ≤ a → y.mem a = x.mem a) ∧ + ∀ i, 32 ≤ i.val → i ≠ Reg.temporary → y.regs i = x.regs i := by + by_cases hs : sign = 0 + · obtain ⟨y, hy, values, frame, regs⟩ := exec_copyForm base dst x hd hbt hdt hq ht₁ hw hsep + have he : ExecWithin (w + 2) (form base dst t₁ t₂ sign) x y (26 * q + 12) := by + simpa only [form, hs, ite_true] using hy.mono (by omega : 7 * q + 9 ≤ 26 * q + 12) + have hquarter : t₁ * q + q ≤ 4 * q := by + have hm := Nat.mul_le_mul_right q (Nat.succ_le_of_lt ht₁) + simpa only [Nat.succ_mul] using hm + refine ⟨y, he, ?_, frame, regs⟩ + intro j hj + rw [values j hj, Nat.add_assoc, hinput _ (by omega)] + simp only [hs, ite_true] + · obtain ⟨y, hy, values, frame, regs⟩ := + exec_arithmeticForm base dst sign x u hb hd hbt hdt hq ht₁ ht₂ hs hbound hu hinput hw hsep + exact ⟨y, hy, by simpa only [ite_eq_right hs] using values, frame, regs⟩ + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Frame.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Frame.lean new file mode 100644 index 0000000000..4f04850de1 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Frame.lean @@ -0,0 +1,136 @@ +/- +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.Strassen.Program +import Mathlib.Tactic.FinCases + +/-! +# Actual seven-word Strassen stack access + +The frame lemmas read and write actual memory. They expose only internal composition APIs +for the native controller proof; a caller does not supply a frame layout or execution oracle. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM + +/-- The fixed order of the seven register fields stored in an explicit frame. -/ +@[expose] def field : Fin 7 → Fin 64 + | 0 => Reg.inputA + | 1 => Reg.inputB + | 2 => Reg.output + | 3 => Reg.scratch + | 4 => Reg.depth + | 5 => Reg.size + | 6 => Reg.phase + +/-- Store every current field, charge all seven stores and all seven address instructions, +and preserve memory outside exactly this frame. -/ +theorem exec_storeFrame {w : ℕ} (x : Data 64) (h1 : x.regs Reg.one = 1) + (hfit : Fits w (x.regs Reg.frame + 7)) : + ∃ y, ExecWithin w storeFrame x y 14 ∧ + (∀ j : Fin 7, y.mem (x.regs Reg.frame + j.val) = x.regs (field j)) ∧ + (∀ a, a < x.regs Reg.frame ∨ x.regs Reg.frame + 7 ≤ a → y.mem a = x.mem a) ∧ + ∀ i, i ≠ Reg.address → y.regs i = x.regs i := by + have hf (k : ℕ) (hk : k ≤ 7) : Fits w (x.regs Reg.frame + k) := hfit.of_le (by omega) + refine ⟨?y, ?run, ?values, ?frame, ?regs⟩ + case run => + ram_eval [storeFrame, h1, hf 1 (by decide), hf 2 (by decide), hf 3 (by decide), + hf 4 (by decide), hf 5 (by decide), hf 6 (by decide), + Reg.address, Reg.one, Reg.frame, Reg.inputA, Reg.inputB, Reg.output, Reg.scratch, + Reg.depth, Reg.size, Reg.phase, Nat.add_assoc] + case values => + intro j + fin_cases j <;> simp +decide [field, Data.write, Data.set, + Reg.frame, Reg.inputA, Reg.inputB, Reg.output, Reg.scratch, + Reg.depth, Reg.size, Reg.phase] + case frame => + intro a ha + simp only [Data.write, Data.set] + simp only [Function.update_of_ne (show a ≠ x.regs Reg.frame by omega), + Function.update_of_ne (show a ≠ x.regs Reg.frame + 1 by omega), + Function.update_of_ne (show a ≠ x.regs Reg.frame + 2 by omega), + Function.update_of_ne (show a ≠ x.regs Reg.frame + 3 by omega), + Function.update_of_ne (show a ≠ x.regs Reg.frame + 4 by omega), + Function.update_of_ne (show a ≠ x.regs Reg.frame + 5 by omega), + Function.update_of_ne (show a ≠ x.regs Reg.frame + 6 by omega)] + case regs => + intro i hi + simp only [Data.write, Data.set, Function.update_of_ne hi] + +/-- Registers loaded by the actual frame-read block, including its address temporary. -/ +@[expose] def LoadLocal (i : Fin 64) : Prop := i = Reg.address ∨ 32 ≤ i.val ∧ i.val ≤ 38 + +instance (i : Fin 64) : Decidable (LoadLocal i) := + inferInstanceAs (Decidable (i = Reg.address ∨ 32 ≤ i.val ∧ i.val ≤ 38)) + +/-- Read every field from the actual current frame in fourteen instructions, without stores. -/ +theorem exec_loadFrame {w : ℕ} (x : Data 64) (h1 : x.regs Reg.one = 1) + (hfit : Fits w (x.regs Reg.frame + 7)) : + ∃ y, ExecWithin w loadFrame x y 14 ∧ y.mem = x.mem ∧ + (∀ j : Fin 7, y.regs (field j) = x.mem (x.regs Reg.frame + j.val)) ∧ + ∀ i, ¬LoadLocal i → y.regs i = x.regs i := by + have hf (k : ℕ) (hk : k ≤ 7) : Fits w (x.regs Reg.frame + k) := hfit.of_le (by omega) + refine ⟨?y, ?run, ?memory, ?values, ?regs⟩ + case run => + ram_eval [loadFrame, h1, hf 1 (by decide), hf 2 (by decide), hf 3 (by decide), + hf 4 (by decide), hf 5 (by decide), hf 6 (by decide), + Reg.address, Reg.one, Reg.frame, Reg.inputA, Reg.inputB, Reg.output, Reg.scratch, + Reg.depth, Reg.size, Reg.phase, Nat.add_assoc] + case memory => rfl + case values => + intro j + fin_cases j <;> simp +decide [field, Data.set, + Reg.frame, Reg.inputA, Reg.inputB, Reg.output, Reg.scratch, + Reg.depth, Reg.size, Reg.phase] + case regs => + intro i hi + have hn (j : Fin 64) (hj : 32 ≤ j.val ∧ j.val ≤ 38) : i ≠ j := by + intro he + subst i + exact hi (Or.inr hj) + have ha : i ≠ (48 : Fin 64) := fun he => hi (Or.inl he) + simp only [Data.set, Function.update_of_ne ha, Function.update_of_ne (hn 32 (by decide)), + Function.update_of_ne (hn 33 (by decide)), Function.update_of_ne (hn 34 (by decide)), + Function.update_of_ne (hn 35 (by decide)), Function.update_of_ne (hn 36 (by decide)), + Function.update_of_ne (hn 37 (by decide)), Function.update_of_ne (hn 38 (by decide))] + +/-- Loading a frame just stored by the controller restores the same current parameters. +This assertion is produced by native setup/push and maintained by the node proof. -/ +theorem exec_loadCurrent {w : ℕ} (x : Data 64) (h1 : x.regs Reg.one = 1) + (hfit : Fits w (x.regs Reg.frame + 7)) + (hframe : ∀ j : Fin 7, x.mem (x.regs Reg.frame + j.val) = x.regs (field j)) : + ∃ y, ExecWithin w loadFrame x y 14 ∧ y.mem = x.mem ∧ + ∀ i, i ≠ Reg.address → y.regs i = x.regs i := by + obtain ⟨y, hy, hm, values, regs⟩ := exec_loadFrame x h1 hfit + have hfields (j : Fin 7) : y.regs (field j) = x.regs (field j) := + (values j).trans (hframe j) + refine ⟨y, hy, hm, ?_⟩ + intro i hi + by_cases hc : LoadLocal i + · have hcases : i = field 0 ∨ i = field 1 ∨ i = field 2 ∨ i = field 3 ∨ + i = field 4 ∨ i = field 5 ∨ i = field 6 := by + simp only [LoadLocal, Reg.address, Fin.ext_iff] at hc hi + simp only [field, Reg.inputA, Reg.inputB, Reg.output, Reg.scratch, + Reg.depth, Reg.size, Reg.phase, Fin.ext_iff] + omega + rcases hcases with rfl | rfl | rfl | rfl | rfl | rfl | rfl + · exact hfields 0 + · exact hfields 1 + · exact hfields 2 + · exact hfields 3 + · exact hfields 4 + · exact hfields 5 + · exact hfields 6 + · exact regs i hc + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Initialize.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Initialize.lean new file mode 100644 index 0000000000..440e11d105 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Initialize.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 +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Frame +import Mathlib.Tactic.FinCases + +/-! +# Actual initialized Strassen root and power call + +The existing native `Power.program` computes `4^L`; a real multiplication by p computes the +coefficient-array length. Actual additions locate the root frame, and all its fields are +stored. Only original numeric parameters occur in the admission bound. No power, stack +layout, initialized scratch or intermediate execution certificate is supplied. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM + +private theorem locateRoot_preserves (i : Fin 64) : + i ≠ Reg.size → i ≠ Reg.frames → i ≠ Reg.frame → i ≠ Reg.phase → i ≠ Reg.active → + locateRoot.Preserves i := by decide +kernel +revert + +/-- Compute the runtime length, constants and explicit root frame in at most `4L+30` +instructions, including the actual power call and every frame store. -/ +theorem exec_setup {w : ℕ} (x : Data 64) + (hw : Fits w (x.regs Reg.scratch + x.regs Reg.modulus * 4 ^ x.regs Reg.depth + + 4 ^ x.regs Reg.depth + x.regs Reg.depth + 16)) : + ∃ y, ExecWithin w setup x y (4 * x.regs Reg.depth + 30) ∧ + y.regs Reg.size = x.regs Reg.modulus * 4 ^ x.regs Reg.depth ∧ + y.regs Reg.frames = x.regs Reg.scratch + x.regs Reg.modulus * 4 ^ x.regs Reg.depth ∧ + y.regs Reg.frame = y.regs Reg.frames ∧ y.regs Reg.phase = 0 ∧ y.regs Reg.active = 1 ∧ + y.regs Reg.zero = 0 ∧ y.regs Reg.one = 1 ∧ y.regs Reg.four = 4 ∧ + y.regs Reg.frameWidth = 7 ∧ + (∀ i, i ∈ [Reg.inputA, Reg.inputB, Reg.output, Reg.scratch, Reg.depth, Reg.modulus] → + y.regs i = x.regs i) ∧ + (∀ j : Fin 7, y.mem (y.regs Reg.frame + j.val) = y.regs (field j)) ∧ + ∀ a, a < y.regs Reg.frame ∨ y.regs Reg.frame + 7 ≤ a → y.mem a = x.mem a := by + have hpow : 0 < 4 ^ x.regs Reg.depth := by positivity + have h7 : Fits w 7 := hw.of_le (by omega) + have h4 : Fits w 4 := hw.of_le (by omega) + have h1 : Fits w 1 := hw.of_le (by omega) + let r := (((((x.set Reg.zero 0).set Reg.one 1).set Reg.four 4).set Reg.frameWidth 7).set + Reg.powerBase 4).set Reg.powerExponent (x.regs Reg.depth) + have hr : ExecWithin w powerArguments x r 6 := by + ram_eval [powerArguments, r, h1, h4, h7, Reg.zero, Reg.one, Reg.four, Reg.frameWidth, + Reg.powerBase, Reg.powerExponent, Reg.depth] + let f : Fin 5 → Fin 64 := Fin.castLE (by decide) + obtain ⟨t, ht, hvalue, _, _, htm, _, _⟩ := Power.exec 4 (x.regs Reg.depth) (by decide) + (r.comap f) + (by simp +decide [r, f, Data.comap, Power.base, Reg.powerBase, Reg.powerExponent]) + (by simp [r, f, Data.comap, Power.exponent, Reg.powerExponent]) + (hw.of_le (by omega)) + let s := t.extend f r + have hpower : ExecWithin w (Power.program.rename f) r s (4 * x.regs Reg.depth + 5) := + ht.rename (Fin.castLE_injective _) + have hhigh (i : Fin 64) (hi : 32 ≤ i.val) : s.regs i = r.regs i := by + have hn : ¬∃ j, f j = i := by + rintro ⟨j, hj⟩ + have hj' := congrArg Fin.val hj + change j.val = i.val at hj' + omega + exact Function.extend_apply' _ _ _ hn + have hsvalue : s.regs Reg.powerValue = 4 ^ x.regs Reg.depth := by + change (s.comap f).regs Power.value = _ + rw [Data.comap_extend (Fin.castLE_injective _)] + exact hvalue + have hsmod : s.regs Reg.modulus = x.regs Reg.modulus := by + simpa +decide [r, Reg.zero, Reg.one, Reg.four, Reg.frameWidth, + Reg.powerBase, Reg.powerExponent, Reg.modulus] using hhigh Reg.modulus (by decide) + have hsscratch : s.regs Reg.scratch = x.regs Reg.scratch := by + simpa +decide [r, Reg.zero, Reg.one, Reg.four, Reg.frameWidth, + Reg.powerBase, Reg.powerExponent, Reg.scratch] using hhigh Reg.scratch (by decide) + have hsmem : s.mem = x.mem := htm + have hsize : Fits w (4 ^ x.regs Reg.depth * x.regs Reg.modulus) := by + rw [Nat.mul_comm] + exact hw.of_le (by omega) + have hframe : Fits w (x.regs Reg.scratch + 4 ^ x.regs Reg.depth * x.regs Reg.modulus) := by + rw [Nat.mul_comm (4 ^ x.regs Reg.depth)] + exact hw.of_le (by omega) + let root := x.regs Reg.scratch + 4 ^ x.regs Reg.depth * x.regs Reg.modulus + let u := ((((s.set Reg.size (4 ^ x.regs Reg.depth * x.regs Reg.modulus)).set Reg.frames + root).set Reg.frame root).set Reg.phase 0).set Reg.active 1 + have hu : ExecWithin w locateRoot s u 5 := by + ram_eval [locateRoot, u, root, hsvalue, hsmod, hsscratch, hsize, hframe, h1, + Reg.powerValue, Reg.modulus, Reg.size, Reg.frames, Reg.frame, Reg.scratch, + Reg.phase, Reg.active] + have hur (i : Fin 64) (hsize : i ≠ Reg.size) (hframes : i ≠ Reg.frames) + (hframe : i ≠ Reg.frame) (hphase : i ≠ Reg.phase) (hactive : i ≠ Reg.active) : + u.regs i = s.regs i := + hu.regs_eq (locateRoot_preserves i hsize hframes hframe hphase hactive) + have hu1 : u.regs Reg.one = 1 := by + rw [hur Reg.one (by decide) (by decide) (by decide) (by decide) (by decide), + hhigh Reg.one (by decide)] + simp +decide [r, Reg.one, Reg.zero, Reg.four, Reg.frameWidth, + Reg.powerBase, Reg.powerExponent] + have huframe : u.regs Reg.frame = root := by + simp +decide [u, Reg.size, Reg.frames, Reg.frame, Reg.phase, Reg.active] + obtain ⟨y, hy, values, frame, regs⟩ := exec_storeFrame u hu1 (by + rw [huframe] + dsimp only [root] + rw [Nat.mul_comm (4 ^ x.regs Reg.depth)] + exact hw.of_le (by omega)) + have he : ExecWithin w setup x y (4 * x.regs Reg.depth + 30) := + (hr.seq (hpower.seq (hu.seq hy))).mono (by omega) + have hyframe : y.regs Reg.frame = root := (regs Reg.frame (by decide)).trans huframe + have hyframes : y.regs Reg.frames = root := by + rw [regs Reg.frames (by decide)] + simp +decide [u, Reg.frames, Reg.frame, Reg.phase, Reg.active] + have hparams (i : Fin 64) + (hi : i ∈ [Reg.inputA, Reg.inputB, Reg.output, Reg.scratch, Reg.depth, Reg.modulus]) : + y.regs i = x.regs i := by + simp only [List.mem_cons, List.not_mem_nil, or_false] at hi + rcases hi with rfl | rfl | rfl | rfl | rfl | rfl <;> + rw [regs _ (by decide), hur _ (by decide) (by decide) (by decide) (by decide) (by decide), + hhigh _ (by decide)] <;> + simp +decide [r, Reg.zero, Reg.one, Reg.four, Reg.frameWidth, Reg.powerBase, + Reg.powerExponent, Reg.inputA, Reg.inputB, Reg.output, Reg.scratch, Reg.depth, Reg.modulus] + refine ⟨y, he, ?_, ?_, hyframe.trans hyframes.symm, ?_, ?_, ?_, ?_, ?_, ?_, hparams, ?_, ?_⟩ + · rw [regs Reg.size (by decide)] + simp +decide [u, Reg.size, Reg.frames, Reg.frame, Reg.phase, Reg.active, Nat.mul_comm] + · simpa only [root, Nat.mul_comm] using hyframes + · rw [regs Reg.phase (by decide)] + simp +decide [u, Reg.phase, Reg.active] + · rw [regs Reg.active (by decide)] + simp [u] + · rw [regs Reg.zero (by decide), + hur Reg.zero (by decide) (by decide) (by decide) (by decide) (by decide), + hhigh Reg.zero (by decide)] + simp +decide [r, Reg.zero, Reg.one, Reg.four, Reg.frameWidth, + Reg.powerBase, Reg.powerExponent] + · exact (regs Reg.one (by decide)).trans hu1 + · rw [regs Reg.four (by decide), + hur Reg.four (by decide) (by decide) (by decide) (by decide) (by decide), + hhigh Reg.four (by decide)] + simp +decide [r, Reg.zero, Reg.one, Reg.four, Reg.frameWidth, + Reg.powerBase, Reg.powerExponent] + · rw [regs Reg.frameWidth (by decide), + hur Reg.frameWidth (by decide) (by decide) (by decide) (by decide) (by decide), + hhigh Reg.frameWidth (by decide)] + simp +decide [r, Reg.zero, Reg.one, Reg.four, Reg.frameWidth, + Reg.powerBase, Reg.powerExponent] + · intro j + rw [regs Reg.frame (by decide), values j, regs (field j) (by fin_cases j <;> decide)] + · intro a ha + rw [frame a (by simpa only [hyframe, huframe] using ha)] + change s.mem a = x.mem a + exact congrFun hsmem a + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Leaf.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Leaf.lean new file mode 100644 index 0000000000..dab9c24d97 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Leaf.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.Strassen.Control +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.CyclicConvolution.Exec + +/-! +# Charged native Strassen scalar leaves + +The leaf transition copies real controller arguments into the low helper bank, executes the +actual cyclic kernel, and returns through the actual explicit stack. Its numerical premises +concern only original coefficient envelopes, pointers and the current frame. This internal +composition API assumes no product answer, run or cost certificate. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM Cslib.WordRAM.Signed Cslib.CyclicIntRing + +/-- A scalar leaf, including four argument copies, all coefficient operations, and return. +Both input arrays are preserved. The frame and active flag implement the real parent return. -/ +theorem exec_leaf {w p A B : ℕ} (x : Data 64) (u v : ℕ → ℤ) + (hlen : x.regs Reg.modulus = p) + (hbound : GenericRAM.Signed.Fits w (CyclicConvolution.magnitudeBound p A B : ℤ)) + (hu : ∀ i < p, (u i).natAbs ≤ A) (hv : ∀ i < p, (v i).natAbs ≤ B) + (hA : ∀ i < p, x.mem (x.regs Reg.inputA + i) = encode (u i)) + (hB : ∀ i < p, x.mem (x.regs Reg.inputB + i) = encode (v i)) + (h2p : Fits (w + 2) (2 * p)) + (ha : Fits (w + 2) (x.regs Reg.inputA + p)) + (hb : Fits (w + 2) (x.regs Reg.inputB + p)) + (hout : Fits (w + 2) (x.regs Reg.output + p)) + (hdisjA : x.regs Reg.inputA + p ≤ x.regs Reg.output ∨ + x.regs Reg.output + p ≤ x.regs Reg.inputA) + (hdisjB : x.regs Reg.inputB + p ≤ x.regs Reg.output ∨ + x.regs Reg.output + p ≤ x.regs Reg.inputB) + (h7 : x.regs Reg.frameWidth = 7) (hf : Fits (w + 2) (x.regs Reg.frame)) : + ∃ y, ExecWithin (w + 2) leaf x y (42 * p * p + 9 * p + 11) ∧ + (∀ r < p, y.mem (x.regs Reg.output + r) = encode (convolution p u v r)) ∧ + (∀ a, a < x.regs Reg.output ∨ x.regs Reg.output + p ≤ a → y.mem a = x.mem a) ∧ + y.regs Reg.frame = + (if x.regs Reg.frames < x.regs Reg.frame then x.regs Reg.frame - 7 + else x.regs Reg.frame) ∧ + y.regs Reg.active = + (if x.regs Reg.frames < x.regs Reg.frame then x.regs Reg.active else 0) ∧ + ∀ i, 32 ≤ i.val → i ≠ Reg.frame → i ≠ Reg.active → y.regs i = x.regs i := by + let z := (((x.set Reg.operandA (x.regs Reg.inputA)).set Reg.operandB + (x.regs Reg.inputB)).set Reg.target (x.regs Reg.output)).set Reg.length p + have hargs : ExecWithin (B := .standard) (w + 2) + (ram[ Reg.operandA ← Reg.inputA; Reg.operandB ← Reg.inputB; + Reg.target ← Reg.output; Reg.length ← Reg.modulus ]) x z 4 := by + ram_eval [z, hlen, Reg.operandA, Reg.operandB, Reg.target, Reg.length, + Reg.inputA, Reg.inputB, Reg.output, Reg.modulus] + let f : Fin 32 → Fin 64 := Fin.castLE (by decide) + have hzm : z.mem = x.mem := rfl + have hza : (z.comap f).regs CyclicConvolution.Reg.inputA = x.regs Reg.inputA := by + simp +decide [z, f, Data.comap, Reg.operandA, Reg.operandB, Reg.target, Reg.length, + CyclicConvolution.Reg.inputA] + have hzb : (z.comap f).regs CyclicConvolution.Reg.inputB = x.regs Reg.inputB := by + simp +decide [z, f, Data.comap, Reg.operandA, Reg.operandB, Reg.target, Reg.length, + CyclicConvolution.Reg.inputB] + have hzo : (z.comap f).regs CyclicConvolution.Reg.output = x.regs Reg.output := by + simp +decide [z, f, Data.comap, Reg.operandA, Reg.operandB, Reg.target, Reg.length, + CyclicConvolution.Reg.output] + obtain ⟨t, ht, values, frame, _⟩ := CyclicConvolution.exec (z.comap f) u v + (by simp [z, f, Data.comap, Reg.length, CyclicConvolution.Reg.length]) hbound hu hv + (by simpa only [Data.comap_mem, hzm, hza] using hA) + (by simpa only [Data.comap_mem, hzm, hzb] using hB) h2p + (by simpa only [hza] using ha) (by simpa only [hzb] using hb) + (by simpa only [hzo] using hout) (by simpa only [hza, hzo] using hdisjA) + (by simpa only [hzb, hzo] using hdisjB) + let s := t.extend f z + have hkernel : ExecWithin (w + 2) + (CyclicConvolution.program.rename f) z s (42 * p * p + 9 * p + 4) := + ht.rename (Fin.castLE_injective _) + have hs (i : Fin 64) (hi : 32 ≤ i.val) : s.regs i = x.regs i := by + have hn : ¬∃ j, f j = i := by + rintro ⟨j, hj⟩ + have he := congrArg Fin.val hj + change j.val = i.val at he + omega + have he : s.regs i = z.regs i := Function.extend_apply' _ _ _ hn + apply he.trans + simp only [z, Data.set, + Function.update_of_ne (show i ≠ Reg.length by intro h; subst i; contradiction), + Function.update_of_ne (show i ≠ Reg.target by intro h; subst i; contradiction), + Function.update_of_ne (show i ≠ Reg.operandB by intro h; subst i; contradiction), + Function.update_of_ne (show i ≠ Reg.operandA by intro h; subst i; contradiction)] + obtain ⟨y, hy, hym, hyf, hya, hyr⟩ := exec_pop s + ((hs Reg.frameWidth (by decide)).trans h7) + (by simpa only [hs Reg.frame (by decide)] using hf) + have he : ExecWithin (w + 2) leaf x y (42 * p * p + 9 * p + 11) := + (hargs.seq (hkernel.seq hy)).mono (by omega) + refine ⟨y, he, ?_, ?_, ?_, ?_, ?_⟩ + · intro r hr + rw [hym] + simpa only [s, Data.extend, Data.comap_mem, hzo] using values r hr + · intro a ha' + rw [hym] + simpa only [s, Data.extend, Data.comap_mem, hzm] using frame a (by simpa only [hzo] using ha') + · simpa only [hs Reg.frame (by decide), hs Reg.frames (by decide)] using hyf + · simpa only [hs Reg.frame (by decide), hs Reg.frames (by decide), + hs Reg.active (by decide)] using hya + · intro i hi hframe hactive + exact (hyr i hframe hactive).trans (hs i hi) + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/LeafStep.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/LeafStep.lean new file mode 100644 index 0000000000..a00afdb685 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/LeafStep.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.Strassen.Leaf + +/-! +# Actual leaf iteration of the explicit Strassen controller + +This composes the real frame loads, the depth comparison, cyclic coefficient multiplication +and real parent/root return. The internal frame equality is established by native setup or +push; the complete counter does not accept it as input data or an assumed execution contract. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM Cslib.WordRAM.Signed Cslib.CyclicIntRing + +/-- A loaded scalar frame executes a complete native controller step, including the actual +depth dispatch and every frame read. All original coefficient/magnitude premises are explicit. -/ +theorem exec_step_leaf {w p A B : ℕ} (x : Data 64) (u v : ℕ → ℤ) + (h0 : x.regs Reg.zero = 0) (h1 : x.regs Reg.one = 1) + (h7 : x.regs Reg.frameWidth = 7) (hd : x.regs Reg.depth = 0) + (hframe : ∀ j : Fin 7, x.mem (x.regs Reg.frame + j.val) = x.regs (field j)) + (hptr : Fits (w + 2) (x.regs Reg.frame + 7)) + (hlen : x.regs Reg.modulus = p) + (hbound : GenericRAM.Signed.Fits w (CyclicConvolution.magnitudeBound p A B : ℤ)) + (hu : ∀ i < p, (u i).natAbs ≤ A) (hv : ∀ i < p, (v i).natAbs ≤ B) + (hA : ∀ i < p, x.mem (x.regs Reg.inputA + i) = encode (u i)) + (hB : ∀ i < p, x.mem (x.regs Reg.inputB + i) = encode (v i)) + (h2p : Fits (w + 2) (2 * p)) + (ha : Fits (w + 2) (x.regs Reg.inputA + p)) + (hb : Fits (w + 2) (x.regs Reg.inputB + p)) + (hout : Fits (w + 2) (x.regs Reg.output + p)) + (hdisjA : x.regs Reg.inputA + p ≤ x.regs Reg.output ∨ + x.regs Reg.output + p ≤ x.regs Reg.inputA) + (hdisjB : x.regs Reg.inputB + p ≤ x.regs Reg.output ∨ + x.regs Reg.output + p ≤ x.regs Reg.inputB) : + ∃ y, ExecWithin (w + 2) step x y (42 * p * p + 9 * p + 26) ∧ + (∀ i < p, y.mem (x.regs Reg.output + i) = encode (convolution p u v i)) ∧ + (∀ a, a < x.regs Reg.output ∨ x.regs Reg.output + p ≤ a → y.mem a = x.mem a) ∧ + y.regs Reg.frame = + (if x.regs Reg.frames < x.regs Reg.frame then x.regs Reg.frame - 7 + else x.regs Reg.frame) ∧ + y.regs Reg.active = + (if x.regs Reg.frames < x.regs Reg.frame then x.regs Reg.active else 0) ∧ + ∀ i, 32 ≤ i.val → i ≠ Reg.address → i ≠ Reg.frame → i ≠ Reg.active → + y.regs i = x.regs i := by + obtain ⟨s, hs, hm, hr⟩ := exec_loadCurrent x h1 hptr hframe + obtain ⟨y, hy, values, frame, hyf, hya, hyr⟩ := exec_leaf s u v + ((hr Reg.modulus (by decide)).trans hlen) hbound hu hv + (by simpa only [hm, hr Reg.inputA (by decide)] using hA) + (by simpa only [hm, hr Reg.inputB (by decide)] using hB) h2p + (by simpa only [hr Reg.inputA (by decide)] using ha) + (by simpa only [hr Reg.inputB (by decide)] using hb) + (by simpa only [hr Reg.output (by decide)] using hout) + (by simpa only [hr Reg.inputA (by decide), hr Reg.output (by decide)] using hdisjA) + (by simpa only [hr Reg.inputB (by decide), hr Reg.output (by decide)] using hdisjB) + ((hr Reg.frameWidth (by decide)).trans h7) + (by rw [hr Reg.frame (by decide)]; exact hptr.of_le (by omega)) + have he : ExecWithin (w + 2) step x y (42 * p * p + 9 * p + 26) := by + have hc : (Cond.eq Reg.depth Reg.zero).Holds s := by + simp only [Cond.holds_eq, hr Reg.depth (by decide), hr Reg.zero (by decide), hd, h0] + exact (hs.seq (.branch_pos hc hy)).mono (by omega) + refine ⟨y, he, ?_, ?_, ?_, ?_, ?_⟩ + · simpa only [hr Reg.output (by decide)] using values + · simpa only [hr Reg.output (by decide), hm] using frame + · simpa only [hr Reg.frames (by decide), hr Reg.frame (by decide)] using hyf + · simpa only [hr Reg.frames (by decide), hr Reg.frame (by decide), + hr Reg.active (by decide)] using hya + · intro i hi haddr hframe hactive + exact (hyr i hi hframe hactive).trans (hr i haddr) + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Next.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Next.lean new file mode 100644 index 0000000000..acce4d3dc3 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Next.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.Strassen.Receive +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Prepare + +/-! +# Native Strassen transition to the next child + +The actual return stage first accumulates the current child's real product, then constructs +both next operands and pushes the complete next frame. Original inputs, earlier frames and +the new partial output survive by composing the real footprints of those operations. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Each of the first six actual return stages advances the output partial sum and computes +the next real recursive child. The seventh child returns through a separate final-pop case. -/ +theorem exec_stage_next {w p k A B a b out scratch frames frame : ℕ} + (phase : Fin 6) (x : Data 64) (u v : ℕ → ℤ) + (hx : NodeHeader p (k + 1) a b out scratch frames frame x) + (hc : ChildBuffers p k scratch x) + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (hu : ∀ i < 4 ^ (k + 1) * p, (u i).natAbs ≤ A) + (hv : ∀ i < 4 ^ (k + 1) * p, (v i).natAbs ≤ B) + (hleft : ∀ i < 4 ^ (k + 1) * p, x.mem (a + i) = encode (u i)) + (hright : ∀ i < 4 ^ (k + 1) * p, x.mem (b + i) = encode (v i)) + (hout : ∀ s j, j < 4 ^ k * p → x.mem (out + s.val * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue p k phase.val u v s j)) + (hprod : ∀ j < 4 ^ k * p, x.mem (scratch + 2 * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.childProduct + p k u v ⟨phase.val, by omega⟩ j)) + (hmag : GenericRAM.Signed.Fits w (magnitudeBound p A B (k + 1) : ℤ)) + (haddr : Fits (w + 2) (addressBound frames frame (k + 1))) : + ∃ y, ExecWithin (w + 2) (stage ⟨phase.val + 1, by omega⟩) x y + (receiveBudget ⟨phase.val, by omega⟩ (4 ^ k * p) + 52 * (4 ^ k * p) + 50) ∧ + NodeReady p k scratch (scratch + 4 ^ k * p) (scratch + 2 * (4 ^ k * p)) + (scratch + 3 * (4 ^ k * p)) frames (frame + 7) y ∧ + (∀ j < 4 ^ k * p, y.mem (scratch + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.leftOperand + p k u ⟨phase.val + 1, by omega⟩ j)) ∧ + (∀ j < 4 ^ k * p, y.mem (scratch + 4 ^ k * p + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.rightOperand + p k v ⟨phase.val + 1, by omega⟩ j)) ∧ + (∀ s j, j < 4 ^ k * p → y.mem (out + s.val * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue + p k (phase.val + 1) u v s j)) ∧ + y.mem (frame + 6) = phase.val + 2 ∧ + ∀ d, (d < out ∨ out + 4 ^ (k + 1) * p ≤ d) → + (d < scratch ∨ scratch + 2 * (4 ^ k * p) ≤ d) → + (d < frame + 6 ∨ frame + 14 ≤ d) → y.mem d = x.mem d := by + let current : Fin 7 := ⟨phase.val, by omega⟩ + let next : Fin 7 := ⟨phase.val + 1, by omega⟩ + have hsize : 4 ^ (k + 1) * p = 4 * (4 ^ k * p) := by rw [pow_succ]; ring + have ho := hl.output_before + have hs := hl.scratch_before + have hf := hl.frames_before + obtain ⟨s, he, sh, sc, outputValues, productValues, outputFrame⟩ := + exec_returnedUpdates current x u v hx hc hl hu hv hout hprod hmag haddr + obtain ⟨y, hy, ready, leftValues, rightValues, parentPhase, memory⟩ := exec_prepareChild next + s u v sh sc hl hu hv + (by + intro i hi + rw [outputFrame _ ?_] + · exact hleft i hi + · rcases hl.left_disjoint with h | h <;> omega) + (by + intro i hi + rw [outputFrame _ ?_] + · exact hright i hi + · rcases hl.right_disjoint with h | h <;> omega) + hmag haddr + have hafter : afterReturn current = prepareChild next := by + fin_cases phase <;> rfl + have hstage := exec_stage_return current he (hafter.symm ▸ hy) + refine ⟨y, hstage.mono (by dsimp only [current]; omega), ready, + leftValues, rightValues, ?_, ?_, ?_⟩ + · intro t j hj + have ht := Nat.mul_le_mul_right (4 ^ k * p) (Nat.succ_le_of_lt t.isLt) + rw [Nat.succ_mul] at ht + rw [hsize] at ho + rw [memory _ (Or.inl (by omega)) (Or.inl (by omega)), outputValues t j hj] + · simpa only [next, Nat.add_assoc] using parentPhase + · intro d hd hds hdf + exact (memory d hds hdf).trans (outputFrame d hd) + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Pending.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Pending.lean new file mode 100644 index 0000000000..7bd21f4353 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Pending.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.Strassen.Entry +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Restore + +/-! +# Actual pending-child invariant for native Strassen + +The first child invariant follows from the actual complete positive-depth iteration. Later +children use the same invariant after actual return, accumulation, operand production and +push. It records only mathematical meanings of current memory/registers, not executions, +time bounds or data supplied to the program. The controller induction must derive every +instance from its actual preceding execution. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Current native child and protected parent data, after both form loops and frame push. -/ +structure PendingChild (p k a b out scratch frames frame : ℕ) (phase : Fin 7) + (u v : ℕ → ℤ) (parent child : Data 64) : Prop where + /-- Entire child frame really exists and is ready for the controller. -/ + ready : NodeReady p k scratch (scratch + 4 ^ k * p) (scratch + 2 * (4 ^ k * p)) + (scratch + 3 * (4 ^ k * p)) frames (frame + 7) child + /-- Actual first formed operand. -/ + left : ∀ j < 4 ^ k * p, child.mem (scratch + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.leftOperand p k u phase j) + /-- Actual second formed operand. -/ + right : ∀ j < 4 ^ k * p, child.mem (scratch + 4 ^ k * p + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.rightOperand p k v phase j) + /-- Original parent first input remains readable. -/ + original_left : ∀ j < 4 ^ (k + 1) * p, child.mem (a + j) = encode (u j) + /-- Original parent second input remains readable. -/ + original_right : ∀ j < 4 ^ (k + 1) * p, child.mem (b + j) = encode (v j) + /-- Earlier returned children have already contributed to the real parent output. -/ + partial_words : ∀ s j, j < 4 ^ k * p → child.mem (out + s.val * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue p k phase.val u v s j) + /-- Parent fields are protected and its phase word was actually updated by the push. -/ + saved : ∀ j : Fin 7, child.mem (frame + j.val) = + if j.val = 6 then phase.val + 1 else parent.regs (field j) + +/-- Every descendant write is within the parent's complete output/scratch/frame footprint. -/ +theorem NodeWrites.child_subset {p k a b out scratch frames frame d : ℕ} + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (h : NodeWrites p k (scratch + 2 * (4 ^ k * p)) (scratch + 3 * (4 ^ k * p)) + frames (frame + 7) d) : NodeWrites p (k + 1) out scratch frames frame d := by + have hs := hl.scratch_before + have hsize : 4 ^ (k + 1) * p = 4 * (4 ^ k * p) := by rw [pow_succ]; ring + rw [hsize] at hs + unfold NodeWrites at h ⊢ + omega + +/-- The three concrete stage-write envelopes all lie inside the parent footprint. -/ +theorem NodeLayout.outside_stage {p k a b out scratch frames frame d : ℕ} + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (h : ¬NodeWrites p (k + 1) out scratch frames frame d) : + (d < out ∨ out + 4 ^ (k + 1) * p ≤ d) ∧ + (d < scratch ∨ scratch + 2 * (4 ^ k * p) ≤ d) ∧ + (d < frame + 6 ∨ frame + 14 ≤ d) := by + have hs := hl.scratch_before + have hsize : 4 ^ (k + 1) * p = 4 * (4 ^ k * p) := by rw [pow_succ]; ring + rw [hsize] at hs + unfold NodeWrites at h + omega + +/-- A completed child preserves the original parent arrays and its earlier partial sum. +Its own returned values are exactly the current finite child product. -/ +theorem PendingChild.after_result {p k a b out scratch frames frame : ℕ} + {phase : Fin 7} {u v : ℕ → ℤ} {parent child returned : Data 64} + (h : PendingChild p k a b out scratch frames frame phase u v parent child) + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (hr : NodeResult p k (scratch + 2 * (4 ^ k * p)) (scratch + 3 * (4 ^ k * p)) + frames (frame + 7) + (Cslib.Algorithms.MatrixMultiplication.Strassen.leftOperand p k u phase) + (Cslib.Algorithms.MatrixMultiplication.Strassen.rightOperand p k v phase) child returned) : + (∀ j < 4 ^ (k + 1) * p, returned.mem (a + j) = encode (u j)) ∧ + (∀ j < 4 ^ (k + 1) * p, returned.mem (b + j) = encode (v j)) ∧ + (∀ s j, j < 4 ^ k * p → returned.mem (out + s.val * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue p k phase.val u v s j)) ∧ + (∀ j < 4 ^ k * p, returned.mem (scratch + 2 * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.childProduct p k u v phase j)) := by + have ha := hl.left_before + have hb := hl.right_before + have ho := hl.output_before + have hs := hl.scratch_before + have hf := hl.frames_before + have hsize : 4 ^ (k + 1) * p = 4 * (4 ^ k * p) := by rw [pow_succ]; ring + have hbelow (d : ℕ) (hd : d < scratch) : returned.mem d = child.mem d := by + apply hr.outside + unfold NodeWrites + omega + refine ⟨?_, ?_, ?_, ?_⟩ + · intro j hj + rw [hbelow _ (by omega)] + exact h.original_left j hj + · intro j hj + rw [hbelow _ (by omega)] + exact h.original_right j hj + · intro t j hj + have ht := Nat.mul_le_mul_right (4 ^ k * p) (Nat.succ_le_of_lt t.isLt) + rw [Nat.succ_mul, ← hsize] at ht + rw [hbelow _ (by omega)] + exact h.partial_words t j hj + · exact hr.values + +/-- The actual first controller iteration establishes the first pending child from original +parent coefficient arrays and its actual freshly written frame, even with dirty output. -/ +theorem exec_first_pending {w p k A B a b out scratch frames frame : ℕ} + (x : Data 64) (u v : ℕ → ℤ) + (hx : NodeReady p (k + 1) a b out scratch frames frame x) + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (hu : ∀ i < 4 ^ (k + 1) * p, (u i).natAbs ≤ A) + (hv : ∀ i < 4 ^ (k + 1) * p, (v i).natAbs ≤ B) + (hleft : ∀ i < 4 ^ (k + 1) * p, x.mem (a + i) = encode (u i)) + (hright : ∀ i < 4 ^ (k + 1) * p, x.mem (b + i) = encode (v i)) + (hmag : GenericRAM.Signed.Fits w (magnitudeBound p A B (k + 1) : ℤ)) + (haddr : Fits (w + 2) (addressBound frames frame (k + 1))) : + ∃ y, ExecWithin (w + 2) step x y (68 * (4 ^ k * p) + 77) ∧ + PendingChild p k a b out scratch frames frame 0 u v x y ∧ + (∀ d, ¬NodeWrites p (k + 1) out scratch frames frame d → y.mem d = x.mem d) := by + have ha := hl.left_before + have hb := hl.right_before + have ho := hl.output_before + have hs := hl.scratch_before + have hf := hl.frames_before + have hsize : 4 ^ (k + 1) * p = 4 * (4 ^ k * p) := by rw [pow_succ]; ring + obtain ⟨y, he, ready, leftValues, rightValues, zeros, parentPhase, memory⟩ := + exec_step_entry x u v hx hl hu hv hleft hright hmag haddr + refine ⟨y, he, ⟨ready, leftValues, rightValues, ?_, ?_, ?_, ?_⟩, ?_⟩ + · intro j hj + rw [memory _ ?_ (Or.inl (by omega)) (Or.inl (by omega))] + · exact hleft j hj + · rcases hl.left_disjoint with h | h <;> omega + · intro j hj + rw [memory _ ?_ (Or.inl (by omega)) (Or.inl (by omega))] + · exact hright j hj + · rcases hl.right_disjoint with h | h <;> omega + · intro t j hj + have ht := Nat.mul_le_mul_right (4 ^ k * p) (Nat.succ_le_of_lt t.isLt) + rw [Nat.succ_mul] at ht + rw [Nat.add_assoc, zeros _ (by rw [hsize]; omega)] + change 0 = encode (Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue p k 0 u v t j) + rw [Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue_zero] + rfl + · intro j + by_cases heq : j.val = 6 + · have hj : j = 6 := Fin.ext heq + subst j + simpa using parentPhase + · rw [ite_eq_right heq, memory _ (Or.inr (by omega)) (Or.inr (by rw [hsize] at hs; omega)) + (Or.inl (by omega))] + exact hx.frame_words j + · intro d hd + obtain ⟨ho', hs', hf'⟩ := hl.outside_stage hd + exact memory d ho' hs' hf' + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Prepare.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Prepare.lean new file mode 100644 index 0000000000..a1307ec67a --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Prepare.lean @@ -0,0 +1,178 @@ +/- +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.Strassen.Child +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Form +import Mathlib.Tactic.FinCases + +/-! +# Real Strassen operand production and child entry + +Both operand arrays are actually copied or arithmetically formed, then the native push +writes the child's entire frame. Child readiness and its canonical input words are derived +from these executions. No form, layout, multiplication, run or time certificate is supplied +to the program. This internal stage preserves the parent output and older frames through +the explicit footprint stated below. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Produce two actual operand forms and a complete child frame. Every form, argument, +pointer, store and push instruction is charged, including single-quadrant copies. -/ +theorem exec_prepare {w p k A B a b out scratch frames frame a₁ a₂ b₁ b₂ : ℕ} + (phase : Fin 7) (asign bsign : ℤ) (x : Data 64) (u v : ℕ → ℤ) + (hx : NodeHeader p (k + 1) a b out scratch frames frame x) + (hc : ChildBuffers p k scratch x) + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (ha₁ : a₁ < 4) (ha₂ : a₂ < 4) (hb₁ : b₁ < 4) (hb₂ : b₂ < 4) + (hu : ∀ i < 4 ^ (k + 1) * p, (u i).natAbs ≤ A) + (hv : ∀ i < 4 ^ (k + 1) * p, (v i).natAbs ≤ B) + (hleft : ∀ i < 4 ^ (k + 1) * p, x.mem (a + i) = encode (u i)) + (hright : ∀ i < 4 ^ (k + 1) * p, x.mem (b + i) = encode (v i)) + (hmag : GenericRAM.Signed.Fits w (magnitudeBound p A B (k + 1) : ℤ)) + (haddr : Fits (w + 2) (addressBound frames frame (k + 1))) : + ∃ y, ExecWithin (w + 2) + (prepare (phase.val + 1) a₁ a₂ asign b₁ b₂ bsign) x y (52 * (4 ^ k * p) + 50) ∧ + NodeReady p k scratch (scratch + 4 ^ k * p) (scratch + 2 * (4 ^ k * p)) + (scratch + 3 * (4 ^ k * p)) frames (frame + 7) y ∧ + (∀ j < 4 ^ k * p, y.mem (scratch + j) = encode + (if asign = 0 then u (a₁ * (4 ^ k * p) + j) else if asign < 0 then + u (a₁ * (4 ^ k * p) + j) - u (a₂ * (4 ^ k * p) + j) + else u (a₁ * (4 ^ k * p) + j) + u (a₂ * (4 ^ k * p) + j))) ∧ + (∀ j < 4 ^ k * p, y.mem (scratch + 4 ^ k * p + j) = encode + (if bsign = 0 then v (b₁ * (4 ^ k * p) + j) else if bsign < 0 then + v (b₁ * (4 ^ k * p) + j) - v (b₂ * (4 ^ k * p) + j) + else v (b₁ * (4 ^ k * p) + j) + v (b₂ * (4 ^ k * p) + j))) ∧ + y.mem (frame + 6) = phase.val + 1 ∧ + ∀ q, (q < scratch ∨ scratch + 2 * (4 ^ k * p) ≤ q) → + (q < frame + 6 ∨ frame + 14 ≤ q) → y.mem q = x.mem q := by + let q := 4 ^ k * p + have hsize : 4 ^ (k + 1) * p = 4 * q := by dsimp [q]; rw [pow_succ]; ring + have habefore : a + 4 * q ≤ scratch := by simpa only [hsize] using hl.left_before + have hbbefore : b + 4 * q ≤ scratch := by simpa only [hsize] using hl.right_before + have hsbefore : scratch + 4 * q ≤ frames := by simpa only [hsize] using hl.scratch_before + have hfbefore := hl.frames_before + have hfitA : GenericRAM.Signed.Fits w ((A + A : ℕ) : ℤ) := hmag.of_natAbs_le (by + rw [Int.natAbs_natCast] + exact (forms_le_magnitudeBound p A B (k + 1)).1) + have hfitB : GenericRAM.Signed.Fits w ((B + B : ℕ) : ℤ) := hmag.of_natAbs_le (by + rw [Int.natAbs_natCast] + exact (forms_le_magnitudeBound p A B (k + 1)).2) + obtain ⟨s, hs, leftValues, leftFrame, leftRegs⟩ := exec_form Reg.inputA Reg.formA asign x u + (by decide) (by decide) (by decide) (by decide) hc.quarter_eq ha₁ ha₂ hfitA + (by simpa only [hsize] using hu) + (by simpa only [hx.inputA_eq, hsize] using hleft) + (by rw [hx.inputA_eq, hc.formA_eq]; exact haddr.of_le (by unfold addressBound; omega)) + (Or.inl (by rw [hx.inputA_eq, hc.formA_eq]; exact habefore)) + have hsh : NodeHeader p (k + 1) a b out scratch frames frame s := + hx.of_regs_eq (fun i hi hi' => leftRegs i hi (by intro he; subst i; contradiction)) + have hsc : ChildBuffers p k scratch s := + hc.of_regs_eq (fun i hi hi' => leftRegs i (by omega) (by intro he; subst i; contradiction)) + have hright' (i : ℕ) (hi : i < 4 * q) : s.mem (s.regs Reg.inputB + i) = encode (v i) := by + rw [hsh.inputB_eq, leftFrame _ (Or.inl (by rw [hc.formA_eq]; omega))] + exact hright i (by simpa only [hsize] using hi) + obtain ⟨r, hr, rightValues, rightFrame, rightRegs⟩ := exec_form Reg.inputB Reg.formB bsign s v + (by decide) (by decide) (by decide) (by decide) hsc.quarter_eq hb₁ hb₂ hfitB + (by simpa only [hsize] using hv) hright' + (by rw [hsh.inputB_eq, hsc.formB_eq]; exact haddr.of_le (by unfold addressBound; omega)) + (Or.inl (by rw [hsh.inputB_eq, hsc.formB_eq]; omega)) + have hrh : NodeHeader p (k + 1) a b out scratch frames frame r := + hsh.of_regs_eq (fun i hi hi' => rightRegs i hi (by intro he; subst i; contradiction)) + have hrc : ChildBuffers p k scratch r := + hsc.of_regs_eq (fun i hi hi' => rightRegs i (by omega) (by intro he; subst i; contradiction)) + let next : Fin 8 := ⟨phase.val + 1, by omega⟩ + obtain ⟨y, hy, hyf, hya, hyb, hyo, hys, hyd, hysz, hyp, hywords, hyparent, + hyframe, hyregs⟩ := exec_push next r hrh.one_eq hrh.frameWidth_eq + (by rw [hrh.frame_eq]; exact haddr.of_le (by unfold addressBound; omega)) + have he : ExecWithin (w + 2) + (prepare (phase.val + 1) a₁ a₂ asign b₁ b₂ bsign) x y (52 * q + 50) := + (hs.seq (hr.seq hy)).mono (by omega) + have hyframe' : y.regs Reg.frame = frame + 7 := by simpa only [hrh.frame_eq] using hyf + have hready : NodeReady p k scratch (scratch + q) (scratch + 2 * q) (scratch + 3 * q) + frames (frame + 7) y := { + inputA_eq := hya.trans hrc.formA_eq + inputB_eq := hyb.trans hrc.formB_eq + output_eq := hyo.trans hrc.product_eq + scratch_eq := hys.trans hrc.scratch_eq + modulus_eq := (hyregs Reg.modulus (by decide)).trans hrh.modulus_eq + depth_eq := hyd.trans hrc.depth_eq + size_eq := hysz.trans hrc.quarter_eq + frames_eq := (hyregs Reg.frames (by decide)).trans hrh.frames_eq + frame_eq := hyframe' + phase_eq := hyp + active_eq := (hyregs Reg.active (by decide)).trans hrh.active_eq + zero_eq := (hyregs Reg.zero (by decide)).trans hrh.zero_eq + one_eq := (hyregs Reg.one (by decide)).trans hrh.one_eq + four_eq := (hyregs Reg.four (by decide)).trans hrh.four_eq + frameWidth_eq := (hyregs Reg.frameWidth (by decide)).trans hrh.frameWidth_eq + frame_words := by simpa only [hyframe'] using hywords } + refine ⟨y, he, hready, ?_, ?_, ?_, ?_⟩ + · intro j hj + rw [hyframe _ (Or.inl (by rw [hrh.frame_eq]; omega)), + rightFrame _ (Or.inl (by rw [hsc.formB_eq]; change scratch + j < scratch + q; omega))] + simpa only [hc.formA_eq] using leftValues j hj + · intro j hj + rw [hyframe _ (Or.inl (by + rw [hrh.frame_eq] + change scratch + q + j < frame + 6 + omega))] + simpa only [hsc.formB_eq] using rightValues j hj + · simpa only [hrh.frame_eq, next] using hyparent + · intro d hd hdf + rw [hyframe _ (by simpa only [hrh.frame_eq] using hdf), + rightFrame _ (by rw [hsc.formB_eq]; change d < scratch + q ∨ scratch + q + q ≤ d; omega), + leftFrame _ (by rw [hc.formA_eq]; change d < scratch ∨ scratch + q ≤ d; omega)] + +/-- Each static child dispatch produces exactly the operand pair of the existing finite +Strassen recurrence. Its readiness and input meanings follow from the two real form loops +and frame push; no precomputed operand array is assumed. -/ +theorem exec_prepareChild {w p k A B a b out scratch frames frame : ℕ} + (phase : Fin 7) (x : Data 64) (u v : ℕ → ℤ) + (hx : NodeHeader p (k + 1) a b out scratch frames frame x) + (hc : ChildBuffers p k scratch x) + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (hu : ∀ i < 4 ^ (k + 1) * p, (u i).natAbs ≤ A) + (hv : ∀ i < 4 ^ (k + 1) * p, (v i).natAbs ≤ B) + (hleft : ∀ i < 4 ^ (k + 1) * p, x.mem (a + i) = encode (u i)) + (hright : ∀ i < 4 ^ (k + 1) * p, x.mem (b + i) = encode (v i)) + (hmag : GenericRAM.Signed.Fits w (magnitudeBound p A B (k + 1) : ℤ)) + (haddr : Fits (w + 2) (addressBound frames frame (k + 1))) : + ∃ y, ExecWithin (w + 2) (prepareChild phase) x y (52 * (4 ^ k * p) + 50) ∧ + NodeReady p k scratch (scratch + 4 ^ k * p) (scratch + 2 * (4 ^ k * p)) + (scratch + 3 * (4 ^ k * p)) frames (frame + 7) y ∧ + (∀ j < 4 ^ k * p, y.mem (scratch + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.leftOperand p k u phase j)) ∧ + (∀ j < 4 ^ k * p, y.mem (scratch + 4 ^ k * p + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.rightOperand p k v phase j)) ∧ + y.mem (frame + 6) = phase.val + 1 ∧ + ∀ q, (q < scratch ∨ scratch + 2 * (4 ^ k * p) ≤ q) → + (q < frame + 6 ∨ frame + 14 ≤ q) → y.mem q = x.mem q := by + fin_cases phase + all_goals simp only [prepareChild] + all_goals first + | exact exec_prepare 0 1 1 x u v hx hc hl (by decide) (by decide) (by decide) + (by decide) hu hv hleft hright hmag haddr + | exact exec_prepare 1 1 0 x u v hx hc hl (by decide) (by decide) (by decide) + (by decide) hu hv hleft hright hmag haddr + | exact exec_prepare 2 0 (-1) x u v hx hc hl (by decide) (by decide) (by decide) + (by decide) hu hv hleft hright hmag haddr + | exact exec_prepare 3 0 (-1) x u v hx hc hl (by decide) (by decide) (by decide) + (by decide) hu hv hleft hright hmag haddr + | exact exec_prepare 4 1 0 x u v hx hc hl (by decide) (by decide) (by decide) + (by decide) hu hv hleft hright hmag haddr + | exact exec_prepare 5 (-1) 1 x u v hx hc hl (by decide) (by decide) (by decide) + (by decide) hu hv hleft hright hmag haddr + | exact exec_prepare 6 (-1) 1 x u v hx hc hl (by decide) (by decide) (by decide) + (by decide) hu hv hleft hright hmag haddr + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Program.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Program.lean new file mode 100644 index 0000000000..8ba8830c46 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Program.lean @@ -0,0 +1,288 @@ +/- +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.CyclicConvolution.Program +public import Complexity.Computability.WordRAM.Signed.ArrayArithmetic.Program +public import Complexity.Computability.WordRAM.Copy +public import Complexity.Computability.WordRAM.Power + +/-! +# Native cyclic-coefficient Strassen stack + +The fixed controller traverses an explicit seven-word frame stack. Each nonleaf creates the +seven actual operand forms, calls the same controller on the child, and accumulates the +returned coefficient array into its output quadrants. Leaves execute the native quadratic +cyclic-convolution kernel. Every form, accumulation, copy, frame access and dispatch is code +on the standard unsigned machine, using the existing signed arithmetic kernels. + +Inputs are runtime pointers to Morton-ordered coefficient arrays, the coefficient length, +a matrix depth, an output and a scratch base. The setup computes the actual array length. +The scratch layout uses three child-sized arrays at each level, followed by child scratch; +the explicit frames start after one root-array length of scratch. Output and scratch may +start dirty. This module defines code; its definition alone supplies neither correctness nor +a running-time theorem. + +The seven products adapt the pinned Anthropic source Spec/Sec3/Theorem17/Strassen.lean and +Programs/Sec3/Theorem17/Hashing/Strassen.lean at revision +16a5ce41304fcdaea4e0be7c132e1c361dc26da5. The native iterative stack is original code. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM + +namespace Reg + +/-- Low-bank result of the existing natural-power routine. -/ +abbrev powerValue : Fin 64 := 0 +/-- Low-bank base of the existing natural-power routine. -/ +abbrev powerBase : Fin 64 := 2 +/-- Low-bank exponent of the existing natural-power routine. -/ +abbrev powerExponent : Fin 64 := 3 + +/-- Low-bank arguments of the native copying kernel. -/ +abbrev copySource : Fin 64 := 0 +/-- Low-bank destination of the native copying kernel. -/ +abbrev copyTarget : Fin 64 := 1 +/-- Low-bank word count of the native copying kernel. -/ +abbrev copyLength : Fin 64 := 2 +/-- Low-bank first operand of signed array kernels. -/ +abbrev operandA : Fin 64 := 9 +/-- Low-bank second operand of signed array kernels. -/ +abbrev operandB : Fin 64 := 10 +/-- Low-bank destination of signed array kernels. -/ +abbrev target : Fin 64 := 11 +/-- Low-bank coefficient count of signed array kernels. -/ +abbrev length : Fin 64 := 12 + +/-- Current first matrix pointer. -/ +abbrev inputA : Fin 64 := 32 + +/-- Current second matrix pointer. -/ +abbrev inputB : Fin 64 := 33 + +/-- Current output pointer. -/ +abbrev output : Fin 64 := 34 + +/-- Current scratch pointer. -/ +abbrev scratch : Fin 64 := 35 + +/-- Current matrix depth. -/ +abbrev depth : Fin 64 := 36 + +/-- Current matrix length in coefficient words. -/ +abbrev size : Fin 64 := 37 + +/-- Zero on entry; one through seven after the corresponding child returns. -/ +abbrev phase : Fin 64 := 38 + +/-- Current explicit frame pointer. -/ +abbrev frame : Fin 64 := 39 + +/-- Root explicit frame pointer. -/ +abbrev frames : Fin 64 := 40 + +/-- One until the root returns. -/ +abbrev active : Fin 64 := 41 + +/-- Constant one. -/ +abbrev one : Fin 64 := 42 + +/-- Constant four. -/ +abbrev four : Fin 64 := 44 + +/-- Constant seven, the number of words per frame. -/ +abbrev frameWidth : Fin 64 := 45 + +/-- Runtime cyclic coefficient length, preserved across all children. -/ +abbrev modulus : Fin 64 := 46 + +/-- Initialization and clearing loop counter. -/ +abbrev index : Fin 64 := 47 + +/-- Frame and output address temporary. -/ +abbrev address : Fin 64 := 48 + +/-- Canonical signed zero and raw unsigned zero. -/ +abbrev zero : Fin 64 := 49 + +/-- Coefficient length of a child quadrant. -/ +abbrev quarter : Fin 64 := 50 + +/-- First child operand buffer. -/ +abbrev formA : Fin 64 := 51 + +/-- Second child operand buffer. -/ +abbrev formB : Fin 64 := 52 + +/-- Child product buffer. -/ +abbrev product : Fin 64 := 53 + +/-- Start of scratch available to the child. -/ +abbrev childScratch : Fin 64 := 54 + +/-- Computed child matrix depth. -/ +abbrev childDepth : Fin 64 := 55 + +/-- Quarter-offset arithmetic temporary. -/ +abbrev temporary : Fin 64 := 56 + +/-- Constant phase-dispatch temporary. -/ +abbrev tag : Fin 64 := 57 + +end Reg + +/-- Compute the address of a statically selected contiguous quadrant. -/ +@[expose] def quadrant (base dst : Fin 64) (q : ℕ) : Code .standard 64 := + if q = 0 then ram[ dst ← base ] else + ram[ Reg.temporary ← #q; Reg.temporary ← Reg.temporary * Reg.quarter; + dst ← base + Reg.temporary ] + +/-- Materialize an operand form, including a real copy in the single-quadrant case. -/ +@[expose] def form (base dst : Fin 64) (q₁ q₂ : ℕ) (sign : ℤ) : Code .standard 64 := + if sign = 0 then + .seq (quadrant base Reg.copySource q₁) + (.seq (ram[ Reg.copyTarget ← dst; Reg.copyLength ← Reg.quarter ]) + (ArrayCopy.program.rename (Fin.castLE (by decide)))) + else + .seq (quadrant base Reg.operandA q₁) (.seq (quadrant base Reg.operandB q₂) + (.seq (ram[ Reg.target ← dst; Reg.length ← Reg.quarter ]) + ((Signed.ArrayArithmetic.program (sign < 0)).rename (Fin.castLE (by decide))))) + +/-- Add or subtract the actual child product into one output quadrant. -/ +@[expose] def accumulate (q : ℕ) (subtract : Bool) : Code .standard 64 := + .seq (quadrant Reg.output Reg.operandA q) + (.seq (ram[ Reg.operandB ← Reg.product; Reg.target ← Reg.operandA; + Reg.length ← Reg.quarter ]) + ((Signed.ArrayArithmetic.program subtract).rename (Fin.castLE (by decide)))) + +/-- Store the seven current frame fields at the computed frame address. -/ +@[expose] def storeFrame : Code .standard 64 := ram[ + Reg.address ← Reg.frame; *Reg.address ← Reg.inputA; + Reg.address ← Reg.address + Reg.one; *Reg.address ← Reg.inputB; + Reg.address ← Reg.address + Reg.one; *Reg.address ← Reg.output; + Reg.address ← Reg.address + Reg.one; *Reg.address ← Reg.scratch; + 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.phase ] + +/-- Read every field of the actual current frame. -/ +@[expose] def loadFrame : Code .standard 64 := ram[ + Reg.address ← Reg.frame; Reg.inputA ← *Reg.address; + Reg.address ← Reg.address + Reg.one; Reg.inputB ← *Reg.address; + Reg.address ← Reg.address + Reg.one; Reg.output ← *Reg.address; + Reg.address ← Reg.address + Reg.one; Reg.scratch ← *Reg.address; + Reg.address ← Reg.address + Reg.one; Reg.depth ← *Reg.address; + Reg.address ← Reg.address + Reg.one; Reg.size ← *Reg.address; + Reg.address ← Reg.address + Reg.one; Reg.phase ← *Reg.address ] + +/-- Derive all child-buffer pointers from the loaded parent frame. -/ +@[expose] def locateChild : Code .standard 64 := ram[ + Reg.quarter ← Reg.size / Reg.four; + Reg.formA ← Reg.scratch; Reg.formB ← Reg.formA + Reg.quarter; + Reg.product ← Reg.formB + Reg.quarter; + Reg.childScratch ← Reg.product + Reg.quarter; + Reg.childDepth ← Reg.depth - Reg.one ] + +/-- One actual store per output coefficient; no global workspace clearing. -/ +@[expose] def clear : Code .standard 64 := + .seq (ram[ Reg.address ← Reg.output; Reg.index ← Reg.output + Reg.size ]) + (zeroFill Reg.address Reg.index Reg.zero Reg.one) + +/-- Save the parent's return phase and prepare the real child fields in registers. -/ +@[expose] def pushArguments (phase : ℕ) : Code .standard 64 := ram[ + Reg.tag ← #6; Reg.address ← Reg.frame + Reg.tag; + Reg.phase ← #phase; *Reg.address ← Reg.phase; + Reg.frame ← Reg.frame + Reg.frameWidth; + Reg.inputA ← Reg.formA; Reg.inputB ← Reg.formB; Reg.output ← Reg.product; + Reg.scratch ← Reg.childScratch; Reg.depth ← Reg.childDepth; Reg.size ← Reg.quarter; + Reg.phase ← #0 ] + +/-- Save the parent's return phase, create a child frame, then let the controller enter it. -/ +@[expose] def push (phase : ℕ) : Code .standard 64 := .seq (pushArguments phase) storeFrame + +/-- Return to a real parent frame, or finish when the root has returned. -/ +@[expose] def pop : Code .standard 64 := ram[ + if Reg.frames < Reg.frame then [ Reg.frame ← Reg.frame - Reg.frameWidth ] + else [ Reg.active ← #0 ] ] + +/-- A recursive child is dispatched only after both actual operand forms have been stored. -/ +@[expose] def prepare (phase a₁ a₂ : ℕ) (asign : ℤ) + (b₁ b₂ : ℕ) (bsign : ℤ) : Code .standard 64 := + .seq (form Reg.inputA Reg.formA a₁ a₂ asign) + (.seq (form Reg.inputB Reg.formB b₁ b₂ bsign) (push phase)) + +/-- The seven actual child calls, in the same order as the mathematical product recurrence. -/ +@[expose] def prepareChild : Fin 7 → Code .standard 64 + | 0 => prepare 1 0 3 1 0 3 1 + | 1 => prepare 2 2 3 1 0 0 0 + | 2 => prepare 3 0 0 0 1 3 (-1) + | 3 => prepare 4 3 3 0 2 0 (-1) + | 4 => prepare 5 0 1 1 3 3 0 + | 5 => prepare 6 2 0 (-1) 0 1 1 + | 6 => prepare 7 1 3 (-1) 2 3 1 + +/-- Execute the corresponding returned-child accumulation and prepare the next product. -/ +@[expose] def stage : Fin 8 → Code .standard 64 + | 0 => .seq clear (prepareChild 0) + | 1 => .seq (accumulate 0 false) (.seq (accumulate 3 false) (prepareChild 1)) + | 2 => .seq (accumulate 2 false) (.seq (accumulate 3 true) (prepareChild 2)) + | 3 => .seq (accumulate 1 false) (.seq (accumulate 3 false) (prepareChild 3)) + | 4 => .seq (accumulate 0 false) (.seq (accumulate 2 false) (prepareChild 4)) + | 5 => .seq (accumulate 0 true) (.seq (accumulate 1 false) (prepareChild 5)) + | 6 => .seq (accumulate 3 false) (prepareChild 6) + | 7 => .seq (accumulate 0 false) pop + +/-- Static seven-way branch chain reads the phase stored by the actual push. -/ +@[expose] def dispatch : Code .standard 64 := ram[ + if Reg.phase = Reg.zero then [ ⟪stage 0⟫ ] else [ + Reg.tag ← #1; if Reg.phase = Reg.tag then [ ⟪stage 1⟫ ] else [ + Reg.tag ← #2; if Reg.phase = Reg.tag then [ ⟪stage 2⟫ ] else [ + Reg.tag ← #3; if Reg.phase = Reg.tag then [ ⟪stage 3⟫ ] else [ + Reg.tag ← #4; if Reg.phase = Reg.tag then [ ⟪stage 4⟫ ] else [ + Reg.tag ← #5; if Reg.phase = Reg.tag then [ ⟪stage 5⟫ ] else [ + Reg.tag ← #6; if Reg.phase = Reg.tag then [ ⟪stage 6⟫ ] else [ + ⟪stage 7⟫ ] ] ] ] ] ] ] ] + +/-- A scalar leaf uses the real cyclic-convolution program, then returns through the stack. -/ +@[expose] def leaf : Code .standard 64 := + .seq (ram[ Reg.operandA ← Reg.inputA; Reg.operandB ← Reg.inputB; + Reg.target ← Reg.output; Reg.length ← Reg.modulus ]) + (.seq (CyclicConvolution.program.rename (Fin.castLE (by decide))) pop) + +/-- One explicit stack iteration; it never invokes a recursive semantic evaluator. -/ +@[expose] def step : Code .standard 64 := ram[ + ⟪loadFrame⟫; + if Reg.depth = Reg.zero then [ ⟪leaf⟫ ] else [ ⟪locateChild⟫; ⟪dispatch⟫ ] ] + +/-- Fixed controller constants and actual runtime arguments to the existing power routine. -/ +@[expose] def powerArguments : Code .standard 64 := ram[ + Reg.zero ← #0; Reg.one ← #1; Reg.four ← #4; Reg.frameWidth ← #7; + Reg.powerBase ← Reg.four; Reg.powerExponent ← Reg.depth ] + +/-- Compute the coefficient-array length and actual root-frame address after the power call. -/ +@[expose] def locateRoot : Code .standard 64 := ram[ + Reg.size ← Reg.powerValue * Reg.modulus; + Reg.frames ← Reg.scratch + Reg.size; Reg.frame ← Reg.frames; + Reg.phase ← #0; Reg.active ← #1 ] + +/-- Actual runtime powers, stack placement and complete dirty-root frame initialization. -/ +@[expose] def setup : Code .standard 64 := + .seq powerArguments (.seq (Power.program.rename (Fin.castLE (by decide))) + (.seq locateRoot storeFrame)) + +/-- Fixed finite native code independent of coefficient length, depth and word size. -/ +@[expose] def controller : Code .standard 64 := .loop (.lt Reg.zero Reg.active) step + +/-- Root initialization followed by the actual explicit-stack traversal. -/ +@[expose] def program : Code .standard 64 := .seq setup controller + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Progress.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Progress.lean new file mode 100644 index 0000000000..4279b71b5b --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Progress.lean @@ -0,0 +1,125 @@ +/- +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.Strassen.QuarterUpdate + +/-! +# Actual partial Strassen accumulations + +The native return stage updates at most two quarters. During those real coefficient loops, +each output quarter contains either its previous partial sum or its next partial sum. +The mask below is a proof invariant, not data supplied to the program. Both alternatives +have the same four-contribution bound, so each actual signed addition admits its operands +without assuming cancellation or a bound on a future machine result. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- A quarter has either received the current child or retains its previous partial sum. -/ +@[expose] def progressValue (p k : ℕ) (phase : Fin 7) (done : Fin 4 → Bool) + (u v : ℕ → ℤ) (s : Fin 4) (r : ℕ) : ℤ := + if done s then + Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue p k (phase.val + 1) u v s r + else Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue p k phase.val u v s r + +/-- Quarters with zero contribution need no native write to reach the next partial sum. -/ +theorem progressValue_eq_next (p k : ℕ) (phase : Fin 7) (done : Fin 4 → Bool) + (u v : ℕ → ℤ) (s : Fin 4) (j : ℕ) + (hzero : done s = false → + Cslib.Algorithms.MatrixMultiplication.Strassen.contribution phase s = 0) : + progressValue p k phase done u v s j = + Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue p k (phase.val + 1) u v s j := by + cases hd : done s + · simp only [progressValue, hd, Bool.false_eq_true, ite_false, + Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue_succ, hzero hd, + zero_mul, add_zero] + · simp only [progressValue, hd, ite_true] + +/-- Both sides of the intra-stage invariant inherit the proved four-contribution bound. -/ +theorem progressValue_natAbs_le {p k A B j : ℕ} (phase : Fin 7) (done : Fin 4 → Bool) + (u v : ℕ → ℤ) (s : Fin 4) + (hu : ∀ i < 4 ^ (k + 1) * p, (u i).natAbs ≤ A) + (hv : ∀ i < 4 ^ (k + 1) * p, (v i).natAbs ≤ B) (hj : j < 4 ^ k * p) : + (progressValue p k phase done u v s j).natAbs ≤ + 4 * (p * ((2 * A) * (2 * B)) * 16 ^ k) := by + have hchild := fun t => + Cslib.Algorithms.MatrixMultiplication.Strassen.childProduct_natAbs_le u v t hu hv hj + unfold progressValue + split + · exact Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue_natAbs_le + u v s (by omega) hchild + · exact Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue_natAbs_le + u v s (by omega) hchild + +/-- One actual returned-child update advances exactly one not-yet-updated output quarter. +The child product and complete high-bank header survive the real in-place loop. -/ +theorem exec_accumulate_progress {w p k A B a b out scratch frames frame : ℕ} + (phase : Fin 7) (done : Fin 4 → Bool) (t : Fin 4) (subtract : Bool) + (x : Data 64) (u v : ℕ → ℤ) + (hx : NodeHeader p (k + 1) a b out scratch frames frame x) + (hc : ChildBuffers p k scratch x) + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (hu : ∀ i < 4 ^ (k + 1) * p, (u i).natAbs ≤ A) + (hv : ∀ i < 4 ^ (k + 1) * p, (v i).natAbs ≤ B) + (hnot : done t = false) + (hsign : Cslib.Algorithms.MatrixMultiplication.Strassen.contribution phase t = + if subtract then -1 else 1) + (hout : ∀ s j, j < 4 ^ k * p → x.mem (out + s.val * (4 ^ k * p) + j) = + encode (progressValue p k phase done u v s j)) + (hprod : ∀ j < 4 ^ k * p, x.mem (scratch + 2 * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.childProduct p k u v phase j)) + (hmag : GenericRAM.Signed.Fits w (magnitudeBound p A B (k + 1) : ℤ)) + (haddr : Fits (w + 2) (addressBound frames frame (k + 1))) : + ∃ y, ExecWithin (w + 2) (accumulate t.val subtract) x y (26 * (4 ^ k * p) + 10) ∧ + NodeHeader p (k + 1) a b out scratch frames frame y ∧ + ChildBuffers p k scratch y ∧ + (∀ s j, j < 4 ^ k * p → y.mem (out + s.val * (4 ^ k * p) + j) = + encode (progressValue p k phase (Function.update done t true) u v s j)) ∧ + (∀ j < 4 ^ k * p, y.mem (scratch + 2 * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.childProduct p k u v phase j)) ∧ + ∀ d, (d < out + t.val * (4 ^ k * p) ∨ + out + t.val * (4 ^ k * p) + 4 ^ k * p ≤ d) → y.mem d = x.mem d := by + let C := p * ((2 * A) * (2 * B)) * 16 ^ k + have hfit : GenericRAM.Signed.Fits w ((4 * C + C : ℕ) : ℤ) := + hmag.of_natAbs_le (by + rw [Int.natAbs_natCast] + have hm := accumulation_le_magnitudeBound p A B k + change 4 * C + C ≤ magnitudeBound p A B (k + 1) + omega) + obtain ⟨y, hy, yh, yc, values, memory⟩ := exec_accumulate_quarters t subtract x + (progressValue p k phase done u v) + (Cslib.Algorithms.MatrixMultiplication.Strassen.childProduct p k u v phase) + hx hc hl (fun s j hj => progressValue_natAbs_le phase done u v s hu hv hj) + (fun j hj => Cslib.Algorithms.MatrixMultiplication.Strassen.childProduct_natAbs_le + u v phase hu hv hj) hout hprod hfit haddr + refine ⟨y, hy, yh, yc, ?_, ?_, memory⟩ + · intro s j hj + rw [values s j hj] + congr 1 + by_cases he : s = t + · subst s + simp only [ite_true, progressValue, hnot, Bool.false_eq_true, ite_false, + Function.update_self, + Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue_succ, hsign] + cases subtract <;> simp [sub_eq_add_neg] + · simp only [he, ite_false, progressValue, Function.update_of_ne he] + · intro j hj + rw [memory _ (Or.inr ?_), hprod j hj] + have ht : t.val * (4 ^ k * p) + 4 ^ k * p ≤ 4 * (4 ^ k * p) := by + have hm := Nat.mul_le_mul_right (4 ^ k * p) (Nat.succ_le_of_lt t.isLt) + simpa only [Nat.succ_mul] using hm + have ho := hl.output_before + rw [pow_succ] at ho + nlinarith + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Push.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Push.lean new file mode 100644 index 0000000000..1be1375279 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Push.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.MatrixMultiplication.Strassen.Frame +import Mathlib.Tactic.FinCases + +/-! +# Native Strassen child layout and explicit frame push + +The six buffer calculations and the parent/child frame stores are actual native execution. +The frame readback is proved from those stores; it is not supplied as an algorithm input. +Only one parent phase cell and seven newly pushed frame words change memory. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM + +/-- Six native arithmetic/copy instructions derive every child buffer and depth. -/ +theorem exec_locateChild {w : ℕ} (x : Data 64) (h1 : x.regs Reg.one = 1) + (h4 : x.regs Reg.four = 4) + (hw : Fits w (x.regs Reg.scratch + x.regs Reg.size + x.regs Reg.depth + 4)) : + ∃ y, ExecWithin w locateChild x y 6 ∧ y.mem = x.mem ∧ + y.regs Reg.quarter = x.regs Reg.size / 4 ∧ + y.regs Reg.formA = x.regs Reg.scratch ∧ + y.regs Reg.formB = x.regs Reg.scratch + x.regs Reg.size / 4 ∧ + y.regs Reg.product = x.regs Reg.scratch + 2 * (x.regs Reg.size / 4) ∧ + y.regs Reg.childScratch = x.regs Reg.scratch + 3 * (x.regs Reg.size / 4) ∧ + y.regs Reg.childDepth = x.regs Reg.depth - 1 ∧ + ∀ i, i ≠ Reg.quarter → i ≠ Reg.formA → i ≠ Reg.formB → i ≠ Reg.product → + i ≠ Reg.childScratch → i ≠ Reg.childDepth → y.regs i = x.regs i := by + have hq : Fits w (x.regs Reg.size / 4) := hw.of_le (by omega) + have hf : Fits w (x.regs Reg.scratch + x.regs Reg.size / 4) := hw.of_le (by omega) + have hp : Fits w (x.regs Reg.scratch + x.regs Reg.size / 4 + x.regs Reg.size / 4) := + hw.of_le (by omega) + have hs : Fits w (x.regs Reg.scratch + x.regs Reg.size / 4 + + x.regs Reg.size / 4 + x.regs Reg.size / 4) := hw.of_le (by omega) + have hd : Fits w (x.regs Reg.depth - 1) := hw.of_le (by omega) + refine ⟨?locatedState, ?locateRun, ?locateMem, ?locatedQuarter, ?locatedA, ?locatedB, + ?locatedProduct, ?locatedScratch, ?locatedDepth, ?locatedRegs⟩ + case locateRun => + ram_eval [locateChild, h1, h4, hq, hf, hp, hs, hd, Reg.quarter, Reg.formA, + Reg.formB, Reg.product, Reg.childScratch, Reg.childDepth, Reg.one, Reg.four, + Reg.size, Reg.depth, Reg.scratch] + case locateMem => rfl + case locatedQuarter => rfl + case locatedA => rfl + case locatedB => rfl + case locatedProduct => + change x.regs Reg.scratch + x.regs Reg.size / 4 + x.regs Reg.size / 4 = + x.regs Reg.scratch + 2 * (x.regs Reg.size / 4) + omega + case locatedScratch => + change x.regs Reg.scratch + x.regs Reg.size / 4 + x.regs Reg.size / 4 + + x.regs Reg.size / 4 = x.regs Reg.scratch + 3 * (x.regs Reg.size / 4) + omega + case locatedDepth => rfl + case locatedRegs => + intro i hq ha hb hp hs hd + simp only [Data.set, Function.update_of_ne hq, Function.update_of_ne ha, + Function.update_of_ne hb, Function.update_of_ne hp, Function.update_of_ne hs, + Function.update_of_ne hd] + +/-- Only current frame fields, its pointer, address scratch and dispatch tag change on push. -/ +@[expose] def PushLocal (i : Fin 64) : Prop := + (32 ≤ i.val ∧ i.val ≤ 39) ∨ i = Reg.address ∨ i = Reg.tag + +instance (i : Fin 64) : Decidable (PushLocal i) := + inferInstanceAs (Decidable ((32 ≤ i.val ∧ i.val ≤ 39) ∨ i = Reg.address ∨ i = Reg.tag)) + +private theorem push_preserves (phase : Fin 8) (i : Fin 64) : + ¬PushLocal i → (push phase.val).Preserves i := by decide +kernel +revert + +/-- Push a real child in twenty-six instructions, preserving the old parent fields except +its phase and writing the entire new frame. All child fields come from actual controller words. -/ +theorem exec_push {w : ℕ} (phase : Fin 8) (x : Data 64) + (h1 : x.regs Reg.one = 1) (h7 : x.regs Reg.frameWidth = 7) + (hw : Fits w (x.regs Reg.frame + 14)) : + ∃ y, ExecWithin w (push phase.val) x y 26 ∧ + y.regs Reg.frame = x.regs Reg.frame + 7 ∧ + y.regs Reg.inputA = x.regs Reg.formA ∧ y.regs Reg.inputB = x.regs Reg.formB ∧ + y.regs Reg.output = x.regs Reg.product ∧ y.regs Reg.scratch = x.regs Reg.childScratch ∧ + y.regs Reg.depth = x.regs Reg.childDepth ∧ y.regs Reg.size = x.regs Reg.quarter ∧ + y.regs Reg.phase = 0 ∧ + (∀ j : Fin 7, y.mem (y.regs Reg.frame + j.val) = y.regs (field j)) ∧ + y.mem (x.regs Reg.frame + 6) = phase.val ∧ + (∀ a, a < x.regs Reg.frame + 6 ∨ x.regs Reg.frame + 14 ≤ a → y.mem a = x.mem a) ∧ + ∀ i, ¬PushLocal i → y.regs i = x.regs i := by + have h6 : Fits w 6 := hw.of_le (by omega) + have hphase : Fits w phase.val := hw.of_le (by omega) + have hparent : Fits w (x.regs Reg.frame + 6) := hw.of_le (by omega) + have hchild : Fits w (x.regs Reg.frame + 7) := hw.of_le (by omega) + let r := x.write (x.regs Reg.frame + 6) phase.val + let s := (((((((((((r.set Reg.tag 6).set Reg.address (x.regs Reg.frame + 6)).set + Reg.phase phase.val).set Reg.frame (x.regs Reg.frame + 7)).set Reg.inputA + (x.regs Reg.formA)).set Reg.inputB (x.regs Reg.formB)).set Reg.output + (x.regs Reg.product)).set Reg.scratch (x.regs Reg.childScratch)).set Reg.depth + (x.regs Reg.childDepth)).set Reg.size (x.regs Reg.quarter)).set Reg.phase 0) + have hpre : ExecWithin w (pushArguments phase.val) x s 12 := by + ram_eval [pushArguments, s, r, h1, h7, h6, hphase, hparent, hchild, + Reg.tag, Reg.address, Reg.phase, Reg.frame, Reg.inputA, Reg.inputB, + Reg.output, Reg.scratch, Reg.depth, Reg.size, Reg.formA, Reg.formB, + Reg.product, Reg.childScratch, Reg.childDepth, Reg.quarter, Reg.frameWidth, + Reg.one, Fin.mk.injEq] + have hsframe : s.regs Reg.frame = x.regs Reg.frame + 7 := by + simp +decide [s, r, Reg.tag, Reg.address, Reg.phase, Reg.frame, Reg.inputA, Reg.inputB, + Reg.output, Reg.scratch, Reg.depth, Reg.size] + obtain ⟨y, hy, values, frame, regs⟩ := exec_storeFrame s + (by simpa +decide [s, r, Reg.tag, Reg.address, Reg.phase, Reg.frame, + Reg.inputA, Reg.inputB, Reg.output, Reg.scratch, Reg.depth, Reg.size, Reg.one] using h1) + (by simpa only [hsframe, Nat.add_assoc] using hw) + have he : ExecWithin w (push phase.val) x y 26 := hpre.seq hy + have hyframe : y.regs Reg.frame = x.regs Reg.frame + 7 := + (regs Reg.frame (by decide)).trans hsframe + have hfields (j : Fin 7) : y.regs (field j) = s.regs (field j) := by + apply regs + fin_cases j <;> decide + refine ⟨y, he, hyframe, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, + fun i hi => he.regs_eq (push_preserves phase i hi)⟩ + · simpa +decide [field, s, r, Reg.tag, Reg.address, Reg.phase, Reg.frame, Reg.inputA, Reg.inputB, + Reg.output, Reg.scratch, Reg.depth, Reg.size] using hfields 0 + · simpa +decide [field, s, r, Reg.tag, Reg.address, Reg.phase, Reg.frame, Reg.inputA, Reg.inputB, + Reg.output, Reg.scratch, Reg.depth, Reg.size] using hfields 1 + · simpa +decide [field, s, r, Reg.tag, Reg.address, Reg.phase, Reg.frame, Reg.inputA, Reg.inputB, + Reg.output, Reg.scratch, Reg.depth, Reg.size] using hfields 2 + · simpa +decide [field, s, r, Reg.tag, Reg.address, Reg.phase, Reg.frame, Reg.inputA, Reg.inputB, + Reg.output, Reg.scratch, Reg.depth, Reg.size] using hfields 3 + · simpa +decide [field, s, r, Reg.tag, Reg.address, Reg.phase, Reg.frame, Reg.inputA, Reg.inputB, + Reg.output, Reg.scratch, Reg.depth, Reg.size] using hfields 4 + · simpa +decide [field, s, r, Reg.tag, Reg.address, Reg.phase, Reg.frame, Reg.inputA, Reg.inputB, + Reg.output, Reg.scratch, Reg.depth, Reg.size] using hfields 5 + · simpa +decide [field, s, r, Reg.tag, Reg.address, Reg.phase, Reg.frame, Reg.inputA, Reg.inputB, + Reg.output, Reg.scratch, Reg.depth, Reg.size] using hfields 6 + · intro j + rw [hyframe, ← hsframe, values j, hfields j] + · rw [frame _ (Or.inl (by rw [hsframe]; omega))] + exact Function.update_self _ _ _ + · intro a ha + rw [frame _ (by rw [hsframe]; omega)] + change Function.update x.mem (x.regs Reg.frame + 6) phase.val a = x.mem a + exact Function.update_of_ne (by omega) _ _ + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Quadrant.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Quadrant.lean new file mode 100644 index 0000000000..b3f344e417 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Quadrant.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.Strassen.Program + +/-! +# Charged quadrant-pointer computation + +The controller's matrix-pointer fields are in the high bank, and the computed helper +arguments are in the low bank. The static quadrant number selects finite code; its runtime +offset multiplication and address addition are ordinary charged instructions. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM + +/-- Compute one of the four actual contiguous-quadrant pointers, preserving memory and +every register except the chosen low argument and the offset temporary. -/ +theorem exec_quadrant {w t : ℕ} (base dst : Fin 64) (x : Data 64) + (hbt : base ≠ Reg.temporary) (ht : t < 4) + (hw : Fits w (x.regs base + 4 * x.regs Reg.quarter + 4)) : + ∃ y, ExecWithin w (quadrant base dst t) x y 3 ∧ y.mem = x.mem ∧ + y.regs dst = x.regs base + t * x.regs Reg.quarter ∧ + ∀ i, i ≠ dst → i ≠ Reg.temporary → y.regs i = x.regs i := by + have hqt : Reg.quarter ≠ Reg.temporary := by decide + have hprod : Fits w (t * x.regs Reg.quarter) := hw.of_le (by + have hmul := Nat.mul_le_mul_right (x.regs Reg.quarter) (show t ≤ 3 by omega) + omega) + have hsum : Fits w (x.regs base + t * x.regs Reg.quarter) := hw.of_le (by + have hmul := Nat.mul_le_mul_right (x.regs Reg.quarter) (show t ≤ 3 by omega) + omega) + have htag : Fits w t := hw.of_le (by omega) + by_cases ht0 : t = 0 + · let y := x.set dst (x.regs base) + have he : ExecWithin w (quadrant base dst t) x y 1 := by + simpa only [quadrant, ht0, ite_true, y] using + (ExecWithin.copy (B := .standard) (w := w) (d := dst) (s := base) (x := x)) + refine ⟨y, he.mono (by decide), rfl, ?_, ?_⟩ + · simp only [y, Data.set, Function.update_self, ht0, Nat.zero_mul, Nat.add_zero] + · intro i hi htemp + exact Function.update_of_ne hi _ _ + · let y := ((x.set Reg.temporary t).set Reg.temporary + (t * x.regs Reg.quarter)).set dst (x.regs base + t * x.regs Reg.quarter) + have he : ExecWithin w (quadrant base dst t) x y 3 := by + ram_eval [quadrant, ht0, y, htag, hprod, hsum, hbt, hqt, Reg.temporary, Reg.quarter] + refine ⟨y, he, rfl, ?_, ?_⟩ + · simp only [y, Data.set, Function.update_self] + · intro i hi htemp + simp only [y, Data.set, Function.update_of_ne hi, Function.update_of_ne htemp] + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/QuarterUpdate.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/QuarterUpdate.lean new file mode 100644 index 0000000000..267a1e89c2 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/QuarterUpdate.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.Strassen.Child +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Accumulate + +/-! +# Native Strassen output-quarter update + +The in-place coefficient loop updates one real output quarter. The other three quarters, +the returned child product, original inputs and older frames survive through its exact +footprint. This is an internal execution lemma for the controller induction. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- One actual accumulation updates its selected quarter and preserves all other quarters. +Header and child-buffer facts are derived from the actual register frame theorem. -/ +theorem exec_accumulate_quarters {w p k a b out scratch frames frame A B : ℕ} + (t : Fin 4) (subtract : Bool) (x : Data 64) (u : Fin 4 → ℕ → ℤ) (v : ℕ → ℤ) + (hx : NodeHeader p (k + 1) a b out scratch frames frame x) + (hc : ChildBuffers p k scratch x) + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (hu : ∀ s j, j < 4 ^ k * p → (u s j).natAbs ≤ A) + (hv : ∀ j < 4 ^ k * p, (v j).natAbs ≤ B) + (hout : ∀ s j, j < 4 ^ k * p → + x.mem (out + s.val * (4 ^ k * p) + j) = encode (u s j)) + (hprod : ∀ j < 4 ^ k * p, x.mem (scratch + 2 * (4 ^ k * p) + j) = encode (v j)) + (hmag : GenericRAM.Signed.Fits w ((A + B : ℕ) : ℤ)) + (haddr : Fits (w + 2) (addressBound frames frame (k + 1))) : + ∃ y, ExecWithin (w + 2) (accumulate t.val subtract) x y (26 * (4 ^ k * p) + 10) ∧ + NodeHeader p (k + 1) a b out scratch frames frame y ∧ + ChildBuffers p k scratch y ∧ + (∀ s j, j < 4 ^ k * p → y.mem (out + s.val * (4 ^ k * p) + j) = + encode (if s = t then if subtract then u s j - v j else u s j + v j else u s j)) ∧ + ∀ d, (d < out + t.val * (4 ^ k * p) ∨ + out + t.val * (4 ^ k * p) + 4 ^ k * p ≤ d) → y.mem d = x.mem d := by + let q := 4 ^ k * p + have hsize : 4 ^ (k + 1) * p = 4 * q := by dsimp [q]; rw [pow_succ]; ring + have ho : out + 4 * q ≤ scratch := by simpa only [hsize] using hl.output_before + have hs : scratch + 4 * q ≤ frames := by simpa only [hsize] using hl.scratch_before + have hf := hl.frames_before + obtain ⟨y, hy, values, frameMem, regs⟩ := exec_accumulate subtract x (u t) v + hc.quarter_eq t.isLt hmag (hu t) hv + (by simpa only [hx.output_eq] using hout t) + (by simpa only [hc.product_eq] using hprod) + (by rw [hx.output_eq, hc.product_eq]; exact haddr.of_le (by unfold addressBound; omega)) + (Or.inr (by rw [hx.output_eq, hc.product_eq]; omega)) + refine ⟨y, hy, hx.of_regs_eq (fun i hi _ => regs i hi (by + intro he; subst i; contradiction)), hc.of_regs_eq (fun i hi _ => regs i (by omega) (by + intro he; subst i; contradiction)), ?_, ?_⟩ + · intro s j hj + by_cases he : s = t + · subst s + simpa only [hx.output_eq, ite_true] using values j hj + · have hne : s.val ≠ t.val := fun h => he (Fin.ext h) + have hsep : out + s.val * q + j < out + t.val * q ∨ + out + t.val * q + q ≤ out + s.val * q + j := by + rcases lt_or_gt_of_ne hne with hlt | hgt + · have hm := Nat.mul_le_mul_right q (Nat.succ_le_of_lt hlt) + rw [Nat.succ_mul] at hm + omega + · have hm := Nat.mul_le_mul_right q (Nat.succ_le_of_lt hgt) + rw [Nat.succ_mul] at hm + omega + rw [frameMem _ (by simpa only [hx.output_eq] using hsep), hout s j hj, ite_eq_right he] + · intro d hd + exact frameMem d (by simpa only [hx.output_eq] using hd) + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Receive.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Receive.lean new file mode 100644 index 0000000000..3957a2ce9a --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Receive.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.Strassen.Progress +import Mathlib.Tactic.FinCases + +/-! +# Native returned-child partial sums + +A returned Strassen child contributes to one or two real output quarters. These internal +lemmas compose the actual in-place coefficient loops, derive the next partial sum and +preserve the returned child product. Their footprints protect original inputs and frames. +Every coefficient operation is charged; zero-contribution quarters require no write. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- The literal accumulation prefix of each actual return stage, before its next push/pop. +The stage composition theorem accounts for the source's right-associated sequencing. -/ +@[expose] def returnedUpdates : Fin 7 → Code .standard 64 + | 0 => .seq (accumulate 0 false) (accumulate 3 false) + | 1 => .seq (accumulate 2 false) (accumulate 3 true) + | 2 => .seq (accumulate 1 false) (accumulate 3 false) + | 3 => .seq (accumulate 0 false) (accumulate 2 false) + | 4 => .seq (accumulate 0 true) (accumulate 1 false) + | 5 => accumulate 3 false + | 6 => accumulate 0 false + +/-- Five children require two accumulations; the last two require one each. -/ +@[expose] def receiveBudget (phase : Fin 7) (q : ℕ) : ℕ := + if phase.val < 5 then 52 * q + 20 else 26 * q + 10 + +/-- The actual continuation after a returned child's contributions: next child or parent pop. -/ +@[expose] def afterReturn : Fin 7 → Code .standard 64 + | 0 => prepareChild 1 + | 1 => prepareChild 2 + | 2 => prepareChild 3 + | 3 => prepareChild 4 + | 4 => prepareChild 5 + | 5 => prepareChild 6 + | 6 => pop + +private theorem seq_right {w U T : ℕ} {c₁ c₂ c₃ : Code .standard 64} {x y z : Data 64} + (h₁ : ExecWithin w (.seq c₁ c₂) x y U) (h₂ : ExecWithin w c₃ y z T) : + ExecWithin w (.seq c₁ (.seq c₂ c₃)) x z (U + T) := by + obtain ⟨m, hm, he⟩ := h₁ + obtain ⟨n, hn, hf⟩ := h₂ + cases he with + | seq ha hb => + exact ⟨_, by omega, .seq ha (.seq hb hf)⟩ + +/-- Reassemble the exact right-associated source stage from its real returned-product +updates and next push/pop. Sequencing changes no charged instruction count. -/ +theorem exec_stage_return {w U T : ℕ} (phase : Fin 7) {x y z : Data 64} + (hupdates : ExecWithin w (returnedUpdates phase) x y U) + (hafter : ExecWithin w (afterReturn phase) y z T) : + ExecWithin w (stage ⟨phase.val + 1, by omega⟩) x z (U + T) := by + fin_cases phase + all_goals first + | exact seq_right hupdates hafter + | exact hupdates.seq hafter + +/-- A child with one nonzero output contribution needs exactly one native coefficient loop. -/ +theorem exec_receive_one {w p k A B a b out scratch frames frame : ℕ} + (phase : Fin 7) (t : Fin 4) (subtract : Bool) (x : Data 64) (u v : ℕ → ℤ) + (hx : NodeHeader p (k + 1) a b out scratch frames frame x) + (hc : ChildBuffers p k scratch x) + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (hu : ∀ i < 4 ^ (k + 1) * p, (u i).natAbs ≤ A) + (hv : ∀ i < 4 ^ (k + 1) * p, (v i).natAbs ≤ B) + (hsign : Cslib.Algorithms.MatrixMultiplication.Strassen.contribution phase t = + if subtract then -1 else 1) + (hzero : ∀ s, s ≠ t → + Cslib.Algorithms.MatrixMultiplication.Strassen.contribution phase s = 0) + (hout : ∀ s j, j < 4 ^ k * p → x.mem (out + s.val * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue p k phase.val u v s j)) + (hprod : ∀ j < 4 ^ k * p, x.mem (scratch + 2 * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.childProduct p k u v phase j)) + (hmag : GenericRAM.Signed.Fits w (magnitudeBound p A B (k + 1) : ℤ)) + (haddr : Fits (w + 2) (addressBound frames frame (k + 1))) : + ∃ y, ExecWithin (w + 2) (accumulate t.val subtract) x y (26 * (4 ^ k * p) + 10) ∧ + NodeHeader p (k + 1) a b out scratch frames frame y ∧ + ChildBuffers p k scratch y ∧ + (∀ s j, j < 4 ^ k * p → y.mem (out + s.val * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue + p k (phase.val + 1) u v s j)) ∧ + (∀ j < 4 ^ k * p, y.mem (scratch + 2 * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.childProduct p k u v phase j)) ∧ + ∀ d, (d < out ∨ out + 4 ^ (k + 1) * p ≤ d) → y.mem d = x.mem d := by + obtain ⟨y, hy, yh, yc, values, productValues, memory⟩ := exec_accumulate_progress + phase (fun _ => false) t subtract x u v hx hc hl hu hv rfl hsign + (by simpa only [progressValue, Bool.false_eq_true, ite_false] using hout) hprod hmag haddr + refine ⟨y, hy, yh, yc, ?_, productValues, ?_⟩ + · intro s j hj + rw [values s j hj, progressValue_eq_next] + intro hd + apply hzero s + intro he + subst s + simp only [Function.update_self] at hd + contradiction + · intro d hd + apply memory d + have ht := Nat.mul_le_mul_right (4 ^ k * p) (Nat.succ_le_of_lt t.isLt) + rw [Nat.succ_mul] at ht + have hsize : 4 ^ (k + 1) * p = 4 * (4 ^ k * p) := by rw [pow_succ]; ring + rw [hsize] at hd + omega + +/-- A child with two distinct nonzero output contributions executes both actual loops. +The second loop uses the first loop's real final state, product and register frame. -/ +theorem exec_receive_two {w p k A B a b out scratch frames frame : ℕ} + (phase : Fin 7) (t₁ t₂ : Fin 4) (subtract₁ subtract₂ : Bool) + (x : Data 64) (u v : ℕ → ℤ) + (hx : NodeHeader p (k + 1) a b out scratch frames frame x) + (hc : ChildBuffers p k scratch x) + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (hu : ∀ i < 4 ^ (k + 1) * p, (u i).natAbs ≤ A) + (hv : ∀ i < 4 ^ (k + 1) * p, (v i).natAbs ≤ B) + (hne : t₂ ≠ t₁) + (hsign₁ : Cslib.Algorithms.MatrixMultiplication.Strassen.contribution phase t₁ = + if subtract₁ then -1 else 1) + (hsign₂ : Cslib.Algorithms.MatrixMultiplication.Strassen.contribution phase t₂ = + if subtract₂ then -1 else 1) + (hzero : ∀ s, s ≠ t₁ → s ≠ t₂ → + Cslib.Algorithms.MatrixMultiplication.Strassen.contribution phase s = 0) + (hout : ∀ s j, j < 4 ^ k * p → x.mem (out + s.val * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue p k phase.val u v s j)) + (hprod : ∀ j < 4 ^ k * p, x.mem (scratch + 2 * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.childProduct p k u v phase j)) + (hmag : GenericRAM.Signed.Fits w (magnitudeBound p A B (k + 1) : ℤ)) + (haddr : Fits (w + 2) (addressBound frames frame (k + 1))) : + ∃ y, ExecWithin (w + 2) (.seq (accumulate t₁.val subtract₁) + (accumulate t₂.val subtract₂)) x y (52 * (4 ^ k * p) + 20) ∧ + NodeHeader p (k + 1) a b out scratch frames frame y ∧ + ChildBuffers p k scratch y ∧ + (∀ s j, j < 4 ^ k * p → y.mem (out + s.val * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue + p k (phase.val + 1) u v s j)) ∧ + (∀ j < 4 ^ k * p, y.mem (scratch + 2 * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.childProduct p k u v phase j)) ∧ + ∀ d, (d < out ∨ out + 4 ^ (k + 1) * p ≤ d) → y.mem d = x.mem d := by + obtain ⟨s, hs, sh, sc, values₁, product₁, memory₁⟩ := exec_accumulate_progress + phase (fun _ => false) t₁ subtract₁ x u v hx hc hl hu hv rfl hsign₁ + (by simpa only [progressValue, Bool.false_eq_true, ite_false] using hout) hprod hmag haddr + obtain ⟨y, hy, yh, yc, values₂, product₂, memory₂⟩ := exec_accumulate_progress + phase (Function.update (fun _ => false) t₁ true) t₂ subtract₂ s u v sh sc hl hu hv + (by simp only [Function.update_of_ne hne]) hsign₂ values₁ product₁ hmag haddr + refine ⟨y, (hs.seq hy).mono (by omega), yh, yc, ?_, product₂, ?_⟩ + · intro t j hj + rw [values₂ t j hj, progressValue_eq_next] + intro hd + apply hzero t + · intro he + subst t + simp only [Function.update_of_ne (Ne.symm hne), Function.update_self] at hd + contradiction + · intro he + subst t + simp only [Function.update_self] at hd + contradiction + · intro d hd + have ht₁ := Nat.mul_le_mul_right (4 ^ k * p) (Nat.succ_le_of_lt t₁.isLt) + have ht₂ := Nat.mul_le_mul_right (4 ^ k * p) (Nat.succ_le_of_lt t₂.isLt) + rw [Nat.succ_mul] at ht₁ ht₂ + have hsize : 4 ^ (k + 1) * p = 4 * (4 ^ k * p) := by rw [pow_succ]; ring + rw [hsize] at hd + exact (memory₂ d (by omega)).trans (memory₁ d (by omega)) + +/-- All seven literal return prefixes advance the existing finite partial-sum invariant. +The exact twelve coefficient loops, rather than fourteen uniform worst cases, are charged. -/ +theorem exec_returnedUpdates {w p k A B a b out scratch frames frame : ℕ} + (phase : Fin 7) (x : Data 64) (u v : ℕ → ℤ) + (hx : NodeHeader p (k + 1) a b out scratch frames frame x) + (hc : ChildBuffers p k scratch x) + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (hu : ∀ i < 4 ^ (k + 1) * p, (u i).natAbs ≤ A) + (hv : ∀ i < 4 ^ (k + 1) * p, (v i).natAbs ≤ B) + (hout : ∀ s j, j < 4 ^ k * p → x.mem (out + s.val * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue p k phase.val u v s j)) + (hprod : ∀ j < 4 ^ k * p, x.mem (scratch + 2 * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.childProduct p k u v phase j)) + (hmag : GenericRAM.Signed.Fits w (magnitudeBound p A B (k + 1) : ℤ)) + (haddr : Fits (w + 2) (addressBound frames frame (k + 1))) : + ∃ y, ExecWithin (w + 2) (returnedUpdates phase) x y (receiveBudget phase (4 ^ k * p)) ∧ + NodeHeader p (k + 1) a b out scratch frames frame y ∧ + ChildBuffers p k scratch y ∧ + (∀ s j, j < 4 ^ k * p → y.mem (out + s.val * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.partialValue + p k (phase.val + 1) u v s j)) ∧ + (∀ j < 4 ^ k * p, y.mem (scratch + 2 * (4 ^ k * p) + j) = encode + (Cslib.Algorithms.MatrixMultiplication.Strassen.childProduct p k u v phase j)) ∧ + ∀ d, (d < out ∨ out + 4 ^ (k + 1) * p ≤ d) → y.mem d = x.mem d := by + fin_cases phase + all_goals simp only [returnedUpdates, receiveBudget] + all_goals first + | exact exec_receive_two 0 0 3 false false x u v hx hc hl hu hv + (by decide) (by decide) (by decide) (by decide) hout hprod hmag haddr + | exact exec_receive_two 1 2 3 false true x u v hx hc hl hu hv + (by decide) (by decide) (by decide) (by decide) hout hprod hmag haddr + | exact exec_receive_two 2 1 3 false false x u v hx hc hl hu hv + (by decide) (by decide) (by decide) (by decide) hout hprod hmag haddr + | exact exec_receive_two 3 0 2 false false x u v hx hc hl hu hv + (by decide) (by decide) (by decide) (by decide) hout hprod hmag haddr + | exact exec_receive_two 4 0 1 true false x u v hx hc hl hu hv + (by decide) (by decide) (by decide) (by decide) hout hprod hmag haddr + | exact exec_receive_one 5 3 false x u v hx hc hl hu hv + (by decide) (by decide) hout hprod hmag haddr + | exact exec_receive_one 6 0 false x u v hx hc hl hu hv + (by decide) (by decide) hout hprod hmag haddr + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Restore.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Restore.lean new file mode 100644 index 0000000000..2de5d0f81f --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Restore.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.Strassen.State +import Mathlib.Tactic.FinCases + +/-! +# Actual parent-frame restoration after a Strassen child + +The child footprint protects the seven earlier parent words. The real fourteen-instruction +load then restores all parent parameters and the saved return phase. In the final induction, +the child result is established recursively for the same native controller, and these saved +words come from the actual push. Neither is an additional argument to the program. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM + +/-- Every earlier parent word is outside the child's actual write footprint. -/ +theorem NodeResult.parent_frame {p k a b out scratch frames frame : ℕ} + {u v : ℕ → ℤ} {x y : Data 64} + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (h : NodeResult p k (scratch + 2 * (4 ^ k * p)) (scratch + 3 * (4 ^ k * p)) + frames (frame + 7) u v x y) (j : Fin 7) : + y.mem (frame + j.val) = x.mem (frame + j.val) := by + apply h.outside + have hs := hl.scratch_before + have hf := hl.frames_before + have hsize : 4 ^ (k + 1) * p = 4 * (4 ^ k * p) := by rw [pow_succ]; ring + rw [hsize] at hs + unfold NodeWrites + omega + +/-- Read the real saved parent frame after the actual child return. Every parent parameter, +return phase and frame-readback fact follows from the charged loads and protected words. -/ +theorem exec_restore_parent {w p k a b out scratch frames frame : ℕ} + (phase : Fin 7) (parent child returned : Data 64) (u v : ℕ → ℤ) + (hp : NodeHeader p (k + 1) a b out scratch frames frame parent) + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (hchild : NodeResult p k (scratch + 2 * (4 ^ k * p)) (scratch + 3 * (4 ^ k * p)) + frames (frame + 7) u v child returned) + (hwords : ∀ j : Fin 7, child.mem (frame + j.val) = + if j.val = 6 then phase.val + 1 else parent.regs (field j)) + (haddr : Fits w (addressBound frames frame (k + 1))) : + ∃ y, ExecWithin w loadFrame returned y 14 ∧ y.mem = returned.mem ∧ + NodeHeader p (k + 1) a b out scratch frames frame y ∧ + y.regs Reg.phase = phase.val + 1 ∧ + (∀ j : Fin 7, y.mem (frame + j.val) = y.regs (field j)) ∧ + ∀ i, ¬LoadLocal i → y.regs i = returned.regs i := by + have hf := hl.frames_before + have hlt : frames < frame + 7 := by omega + have hreturn : returned.regs Reg.frame = frame := by + simpa only [hlt, ite_true, Nat.add_sub_cancel_right] using hchild.frame_eq + obtain ⟨y, hy, hm, fields, regs⟩ := exec_loadFrame returned hchild.one_eq + (by rw [hreturn]; exact haddr.of_le (by unfold addressBound; omega)) + have hfields (j : Fin 7) : y.regs (field j) = + if j.val = 6 then phase.val + 1 else parent.regs (field j) := by + rw [fields j, hreturn, hchild.parent_frame hl j, hwords j] + have hyphase : y.regs Reg.phase = phase.val + 1 := by + simpa [field] using hfields 6 + have hyheader : NodeHeader p (k + 1) a b out scratch frames frame y := { + inputA_eq := by simpa [field, hp.inputA_eq] using hfields 0 + inputB_eq := by simpa [field, hp.inputB_eq] using hfields 1 + output_eq := by simpa [field, hp.output_eq] using hfields 2 + scratch_eq := by simpa [field, hp.scratch_eq] using hfields 3 + depth_eq := by simpa [field, hp.depth_eq] using hfields 4 + size_eq := by simpa [field, hp.size_eq] using hfields 5 + modulus_eq := (regs Reg.modulus (by decide)).trans hchild.modulus_eq + frames_eq := (regs Reg.frames (by decide)).trans hchild.frames_eq + frame_eq := (regs Reg.frame (by decide)).trans hreturn + active_eq := (regs Reg.active (by decide)).trans (by + simpa only [hlt, ite_true] using hchild.active_eq) + zero_eq := (regs Reg.zero (by decide)).trans hchild.zero_eq + one_eq := (regs Reg.one (by decide)).trans hchild.one_eq + four_eq := (regs Reg.four (by decide)).trans hchild.four_eq + frameWidth_eq := (regs Reg.frameWidth (by decide)).trans hchild.frameWidth_eq } + refine ⟨y, hy, hm, hyheader, hyphase, ?_, regs⟩ + intro j + rw [hm, ← hreturn, fields j] + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Resume.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Resume.lean new file mode 100644 index 0000000000..024081b0b2 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Resume.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 +-/ + +module +-- upstream: candidate + +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Pending +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Next +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Finish + +/-! +# Real parent resumption after a native Strassen child + +These internal induction steps consume the proved semantic result of the same native child +controller. They actually reload the protected parent frame, recompute child pointers, +dispatch the saved phase, update the output and prepare the next child or return. The child +result is a recursive proof postcondition, never an extra program input or assumed solver. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- The next actual parent iteration derives the next pending child from the previous +actual child return. Every load, comparison, tag, accumulation, form and push is charged. -/ +theorem exec_step_resume_next {w p k A B a b out scratch frames frame : ℕ} + (phase : Fin 6) (parent child returned : Data 64) (u v : ℕ → ℤ) + (hp : NodeHeader p (k + 1) a b out scratch frames frame parent) + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (pending : PendingChild p k a b out scratch frames frame ⟨phase.val, by omega⟩ + u v parent child) + (hchild : NodeResult p k (scratch + 2 * (4 ^ k * p)) (scratch + 3 * (4 ^ k * p)) + frames (frame + 7) + (Cslib.Algorithms.MatrixMultiplication.Strassen.leftOperand p k u ⟨phase.val, by omega⟩) + (Cslib.Algorithms.MatrixMultiplication.Strassen.rightOperand p k v ⟨phase.val, by omega⟩) + child returned) + (hu : ∀ i < 4 ^ (k + 1) * p, (u i).natAbs ≤ A) + (hv : ∀ i < 4 ^ (k + 1) * p, (v i).natAbs ≤ B) + (hmag : GenericRAM.Signed.Fits w (magnitudeBound p A B (k + 1) : ℤ)) + (haddr : Fits (w + 2) (addressBound frames frame (k + 1))) : + ∃ y, ExecWithin (w + 2) step returned y + (receiveBudget ⟨phase.val, by omega⟩ (4 ^ k * p) + 52 * (4 ^ k * p) + 92) ∧ + PendingChild p k a b out scratch frames frame ⟨phase.val + 1, by omega⟩ u v parent y ∧ + (∀ d, ¬NodeWrites p (k + 1) out scratch frames frame d → y.mem d = returned.mem d) := by + let current : Fin 7 := ⟨phase.val, by omega⟩ + let next : Fin 7 := ⟨phase.val + 1, by omega⟩ + let selected : Fin 8 := ⟨phase.val + 1, by omega⟩ + have ha := hl.left_before + have hb := hl.right_before + have ho := hl.output_before + have hs := hl.scratch_before + have hf := hl.frames_before + have hsize : 4 ^ (k + 1) * p = 4 * (4 ^ k * p) := by rw [pow_succ]; ring + obtain ⟨originalA, originalB, partialWords, productWords⟩ := pending.after_result hl hchild + obtain ⟨s, load, sm, sh, sphase, _, _⟩ := exec_restore_parent current parent child returned + _ _ hp hl hchild pending.saved haddr + obtain ⟨r, locate, rh, rc, rm, rr⟩ := exec_locateChild_header s sh hl haddr + let z := dispatchState selected r + have zm : z.mem = returned.mem := (dispatchState_mem selected r).trans (rm.trans sm) + have zh : NodeHeader p (k + 1) a b out scratch frames frame z := + rh.of_regs_eq (fun i _ hi => dispatchState_regs selected r i (by + intro he; subst i; contradiction)) + have zc : ChildBuffers p k scratch z := + rc.of_regs_eq (fun i _ hi => dispatchState_regs selected r i (by + intro he; subst i; contradiction)) + obtain ⟨y, stageRun, ready, leftValues, rightValues, outputs, parentPhase, memory⟩ := + exec_stage_next phase z u v zh zc hl hu hv + (by simpa only [zm] using originalA) (by simpa only [zm] using originalB) + (by simpa only [zm] using partialWords) (by simpa only [zm] using productWords) hmag haddr + have rphase : r.regs Reg.phase = selected.val := + (rr Reg.phase (by decide)).trans sphase + have dispatched := exec_dispatch selected r y rh.zero_eq rphase + (haddr.of_le (by unfold addressBound; omega)) stageRun + have depthTest : ¬(Cond.eq Reg.depth Reg.zero).Holds s := by + simp only [Cond.holds_eq, sh.depth_eq, sh.zero_eq] + omega + have dispatch_le : dispatchBudget selected ≤ 20 := by + unfold dispatchBudget + split <;> omega + have he : ExecWithin (w + 2) step returned y + (receiveBudget current (4 ^ k * p) + 52 * (4 ^ k * p) + 92) := + (load.seq (ExecWithin.branch_neg depthTest (locate.seq dispatched))).mono (by + dsimp only [current] at * + omega) + refine ⟨y, he, ⟨ready, leftValues, rightValues, ?_, ?_, outputs, ?_⟩, ?_⟩ + · intro j hj + rw [memory _ ?_ (Or.inl (by omega)) (Or.inl (by omega)), zm] + · exact originalA j hj + · rcases hl.left_disjoint with h | h <;> omega + · intro j hj + rw [memory _ ?_ (Or.inl (by omega)) (Or.inl (by omega)), zm] + · exact originalB j hj + · rcases hl.right_disjoint with h | h <;> omega + · intro j + by_cases heq : j.val = 6 + · have hj : j = 6 := Fin.ext heq + subst j + simpa [next] using parentPhase + · rw [ite_eq_right heq, memory _ (Or.inr (by omega)) + (Or.inr (by rw [hsize] at hs; omega)) (Or.inl (by omega)), zm, + hchild.parent_frame hl j, pending.saved j, ite_eq_right heq] + · intro d hd + obtain ⟨ho', hs', hf'⟩ := hl.outside_stage hd + simpa only [zm] using memory d ho' hs' hf' + +/-- The last actual parent iteration consumes the seventh returned child and returns the +complete product. Its frame/flag and every outside word are the actual final-state ones. -/ +theorem exec_step_resume_finish {w p k A B a b out scratch frames frame : ℕ} + (parent child returned : Data 64) (u v : ℕ → ℤ) + (hp : NodeHeader p (k + 1) a b out scratch frames frame parent) + (hl : NodeLayout p (k + 1) a b out scratch frames frame) + (pending : PendingChild p k a b out scratch frames frame 6 u v parent child) + (hchild : NodeResult p k (scratch + 2 * (4 ^ k * p)) (scratch + 3 * (4 ^ k * p)) + frames (frame + 7) + (Cslib.Algorithms.MatrixMultiplication.Strassen.leftOperand p k u 6) + (Cslib.Algorithms.MatrixMultiplication.Strassen.rightOperand p k v 6) child returned) + (hu : ∀ i < 4 ^ (k + 1) * p, (u i).natAbs ≤ A) + (hv : ∀ i < 4 ^ (k + 1) * p, (v i).natAbs ≤ B) + (hmag : GenericRAM.Signed.Fits w (magnitudeBound p A B (k + 1) : ℤ)) + (haddr : Fits (w + 2) (addressBound frames frame (k + 1))) : + ∃ y, ExecWithin (w + 2) step returned y (26 * (4 ^ k * p) + 55) ∧ + NodeResult p (k + 1) out scratch frames frame u v returned y := by + obtain ⟨_, _, partialWords, productWords⟩ := pending.after_result hl hchild + obtain ⟨s, load, sm, sh, sphase, _, _⟩ := exec_restore_parent 6 parent child returned + _ _ hp hl hchild pending.saved haddr + obtain ⟨r, locate, rh, rc, rm, rr⟩ := exec_locateChild_header s sh hl haddr + let z := dispatchState 7 r + have zm : z.mem = returned.mem := (dispatchState_mem 7 r).trans (rm.trans sm) + have zh : NodeHeader p (k + 1) a b out scratch frames frame z := + rh.of_regs_eq (fun i _ hi => dispatchState_regs 7 r i (by + intro he; subst i; contradiction)) + have zc : ChildBuffers p k scratch z := + rc.of_regs_eq (fun i _ hi => dispatchState_regs 7 r i (by + intro he; subst i; contradiction)) + obtain ⟨y, stageRun, result⟩ := exec_stage_finish z u v zh zc hl hu hv + (by simpa [zm] using partialWords) (by simpa only [zm] using productWords) hmag haddr + have rphase : r.regs Reg.phase = 7 := (rr Reg.phase (by decide)).trans sphase + have dispatched := exec_dispatch 7 r y rh.zero_eq rphase + (haddr.of_le (by unfold addressBound; omega)) stageRun + have depthTest : ¬(Cond.eq Reg.depth Reg.zero).Holds s := by + simp only [Cond.holds_eq, sh.depth_eq, sh.zero_eq] + omega + have he : ExecWithin (w + 2) step returned y (26 * (4 ^ k * p) + 55) := + (load.seq (ExecWithin.branch_neg depthTest (locate.seq dispatched))).mono (by + norm_num [dispatchBudget] + omega) + refine ⟨y, he, { result with outside := ?_ }⟩ + intro d hd + simpa only [zm] using result.outside d hd + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/State.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/State.lean new file mode 100644 index 0000000000..2a2037de40 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/State.lean @@ -0,0 +1,236 @@ +/- +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.Strassen.Initialize +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Bounds + +/-! +# Internal explicit-stack Strassen invariants + +The program takes ordinary pointers, depth and coefficient length. These structures describe +its actual intermediate state for the execution induction. They contain no run, result or +time oracle. Root readiness follows from the native setup theorem; child readiness is +derived from the actual operand writes and frame push. The final caller interface must +compose those derivations from original input data, rather than require these invariants. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM + +/-- Numerical separation maintained by the actual Strassen buffers and frame stack. -/ +structure NodeLayout (p k a b out scratch frames frame : ℕ) : Prop where + /-- Scalar cyclic ring length is positive. -/ + modulus_pos : 0 < p + /-- The complete first input is protected below current scratch. -/ + left_before : a + 4 ^ k * p ≤ scratch + /-- The complete second input is protected below current scratch. -/ + right_before : b + 4 ^ k * p ≤ scratch + /-- The output also ends before current scratch. -/ + output_before : out + 4 ^ k * p ≤ scratch + /-- Recursive coefficient buffers end before the actual stack base. -/ + scratch_before : scratch + 4 ^ k * p ≤ frames + /-- Older frames precede the current frame. -/ + frames_before : frames ≤ frame + /-- The output is disjoint from the first input. -/ + left_disjoint : a + 4 ^ k * p ≤ out ∨ out + 4 ^ k * p ≤ a + /-- The output is disjoint from the second input. -/ + right_disjoint : b + 4 ^ k * p ≤ out ∨ out + 4 ^ k * p ≤ b + +/-- The actual three-buffer advancement gives the child all separation properties. -/ +theorem NodeLayout.child {p k a b out scratch frames frame : ℕ} + (h : NodeLayout p (k + 1) a b out scratch frames frame) : + NodeLayout p k scratch (scratch + 4 ^ k * p) (scratch + 2 * (4 ^ k * p)) + (scratch + 3 * (4 ^ k * p)) frames (frame + 7) := by + refine ⟨h.modulus_pos, ?_, ?_, ?_, ?_, ?_, Or.inl ?_, Or.inl ?_⟩ + · omega + · omega + · omega + · simpa only [child_scratch_end] using h.scratch_before + · have hf := h.frames_before + omega + · omega + · omega + +/-- Current matrix parameters and fixed controller constants, before phase-specific work. -/ +structure NodeHeader (p k a b out scratch frames frame : ℕ) (x : Data 64) : Prop where + /-- First input pointer. -/ + inputA_eq : x.regs Reg.inputA = a + /-- Second input pointer. -/ + inputB_eq : x.regs Reg.inputB = b + /-- Output pointer. -/ + output_eq : x.regs Reg.output = out + /-- Current scratch pointer. -/ + scratch_eq : x.regs Reg.scratch = scratch + /-- Actual runtime cyclic length. -/ + modulus_eq : x.regs Reg.modulus = p + /-- Current depth. -/ + depth_eq : x.regs Reg.depth = k + /-- Actual computed coefficient-array length. -/ + size_eq : x.regs Reg.size = 4 ^ k * p + /-- First real frame address. -/ + frames_eq : x.regs Reg.frames = frames + /-- Current real frame address. -/ + frame_eq : x.regs Reg.frame = frame + /-- Current traversal is active. -/ + active_eq : x.regs Reg.active = 1 + /-- Raw/canonical zero constant. -/ + zero_eq : x.regs Reg.zero = 0 + /-- Counter constant. -/ + one_eq : x.regs Reg.one = 1 + /-- Quadrant radix. -/ + four_eq : x.regs Reg.four = 4 + /-- Real frame width. -/ + frameWidth_eq : x.regs Reg.frameWidth = 7 + +/-- Reuse the common header after a real helper call preserving its high register bank. -/ +theorem NodeHeader.of_regs_eq {p k a b out scratch frames frame : ℕ} {x y : Data 64} + (h : NodeHeader p k a b out scratch frames frame x) + (hr : ∀ i, 32 ≤ i.val → i.val < 50 → y.regs i = x.regs i) : + NodeHeader p k a b out scratch frames frame y := + ⟨(hr _ (by decide) (by decide)).trans h.inputA_eq, + (hr _ (by decide) (by decide)).trans h.inputB_eq, + (hr _ (by decide) (by decide)).trans h.output_eq, + (hr _ (by decide) (by decide)).trans h.scratch_eq, + (hr _ (by decide) (by decide)).trans h.modulus_eq, + (hr _ (by decide) (by decide)).trans h.depth_eq, + (hr _ (by decide) (by decide)).trans h.size_eq, + (hr _ (by decide) (by decide)).trans h.frames_eq, + (hr _ (by decide) (by decide)).trans h.frame_eq, + (hr _ (by decide) (by decide)).trans h.active_eq, + (hr _ (by decide) (by decide)).trans h.zero_eq, + (hr _ (by decide) (by decide)).trans h.one_eq, + (hr _ (by decide) (by decide)).trans h.four_eq, + (hr _ (by decide) (by decide)).trans h.frameWidth_eq⟩ + +/-- Frame loads and output clearing may change the two cursors, which are not parameters +of the current node. All actual header fields are preserved. -/ +theorem NodeHeader.of_regs_eq_cursors {p k a b out scratch frames frame : ℕ} {x y : Data 64} + (h : NodeHeader p k a b out scratch frames frame x) + (hr : ∀ i, i ≠ Reg.address → i ≠ Reg.index → y.regs i = x.regs i) : + NodeHeader p k a b out scratch frames frame y := + ⟨(hr _ (by decide) (by decide)).trans h.inputA_eq, + (hr _ (by decide) (by decide)).trans h.inputB_eq, + (hr _ (by decide) (by decide)).trans h.output_eq, + (hr _ (by decide) (by decide)).trans h.scratch_eq, + (hr _ (by decide) (by decide)).trans h.modulus_eq, + (hr _ (by decide) (by decide)).trans h.depth_eq, + (hr _ (by decide) (by decide)).trans h.size_eq, + (hr _ (by decide) (by decide)).trans h.frames_eq, + (hr _ (by decide) (by decide)).trans h.frame_eq, + (hr _ (by decide) (by decide)).trans h.active_eq, + (hr _ (by decide) (by decide)).trans h.zero_eq, + (hr _ (by decide) (by decide)).trans h.one_eq, + (hr _ (by decide) (by decide)).trans h.four_eq, + (hr _ (by decide) (by decide)).trans h.frameWidth_eq⟩ + +/-- Actual current fields and frame words at a newly entered root or pushed child. -/ +structure NodeReady (p k a b out scratch frames frame : ℕ) (x : Data 64) : Prop + extends NodeHeader p k a b out scratch frames frame x where + /-- Entry dispatch phase. -/ + phase_eq : x.regs Reg.phase = 0 + /-- Words actually written by native root setup or child push. -/ + frame_words : ∀ j : Fin 7, x.mem (frame + j.val) = x.regs (field j) + +/-- Only current output, recursive coefficient scratch and current/descendant frames may +change during this node. Older parent frames are protected. -/ +@[expose] def NodeWrites (p k out scratch frames frame a : ℕ) : Prop := + (out ≤ a ∧ a < out + 4 ^ k * p) ∨ (scratch ≤ a ∧ a < frames) ∨ + (frame ≤ a ∧ a < frame + 7 * (k + 1)) + +/-- Semantic/memory result of a node return, independent of an execution or time claim. -/ +structure NodeResult (p k out scratch frames frame : ℕ) + (u v : ℕ → ℤ) (x y : Data 64) : Prop where + /-- Every actual result coefficient agrees with the existing finite product. -/ + values : ∀ i < 4 ^ k * p, y.mem (out + i) = + Signed.encode (Cslib.Algorithms.MatrixMultiplication.Strassen.multiply p k u v i) + /-- Actual preserved caller/parent memory. -/ + outside : ∀ a, ¬NodeWrites p k out scratch frames frame a → y.mem a = x.mem a + /-- The current output pointer is the returned native address. -/ + output_eq : y.regs Reg.output = out + /-- The cyclic length is preserved. -/ + modulus_eq : y.regs Reg.modulus = p + /-- Root stack address is preserved. -/ + frames_eq : y.regs Reg.frames = frames + /-- The actual return is a parent pop or the unchanged root pointer. -/ + frame_eq : y.regs Reg.frame = if frames < frame then frame - 7 else frame + /-- Only root return clears the actual traversal flag. -/ + active_eq : y.regs Reg.active = if frames < frame then 1 else 0 + /-- Raw/canonical zero survives all low-bank helper calls. -/ + zero_eq : y.regs Reg.zero = 0 + /-- Counter constant survives. -/ + one_eq : y.regs Reg.one = 1 + /-- Quadrant radix survives. -/ + four_eq : y.regs Reg.four = 4 + /-- Frame width survives. -/ + frameWidth_eq : y.regs Reg.frameWidth = 7 + +/-- Native instruction budget established by the subsequent controller composition proof. +Its definition alone supplies no execution or source Light instruction-count theorem. -/ +@[expose] def traceBudget (p : ℕ) : ℕ → ℕ + | 0 => 42 * p * p + 9 * p + 28 + | k + 1 => 7 * traceBudget p k + 692 * (4 ^ k * p) + 900 + +/-- Arithmetic bound for the native budget. The strengthened potential pays the linear +quarter-size work; the actual execution theorem is proved separately in `Strassen.Exec`. -/ +theorem traceBudget_potential_le (p k : ℕ) : + traceBudget p k + 231 * p * 4 ^ k + 150 ≤ (42 * p * p + 240 * p + 178) * 7 ^ k := by + induction k with + | zero => simp only [traceBudget, pow_zero, Nat.mul_one]; omega + | succ k ih => + have he : traceBudget p (k + 1) + 231 * p * 4 ^ (k + 1) + 150 + p * 4 ^ k = + 7 * (traceBudget p k + 231 * p * 4 ^ k + 150) := by + rw [traceBudget, pow_succ] + ring + calc + _ ≤ traceBudget p (k + 1) + 231 * p * 4 ^ (k + 1) + 150 + p * 4 ^ k := + Nat.le_add_right _ _ + _ = _ := he + _ ≤ 7 * ((42 * p * p + 240 * p + 178) * 7 ^ k) := Nat.mul_le_mul_left _ ih + _ = _ := by rw [pow_succ]; ring + +/-- The actual native setup constructs root readiness, rather than taking it as a premise. -/ +theorem exec_setup_ready {w : ℕ} (x : Data 64) + (hw : Fits w (x.regs Reg.scratch + x.regs Reg.modulus * 4 ^ x.regs Reg.depth + + 4 ^ x.regs Reg.depth + x.regs Reg.depth + 16)) : + ∃ y, ExecWithin w setup x y (4 * x.regs Reg.depth + 30) ∧ + NodeReady (x.regs Reg.modulus) (x.regs Reg.depth) (x.regs Reg.inputA) + (x.regs Reg.inputB) (x.regs Reg.output) (x.regs Reg.scratch) + (x.regs Reg.scratch + 4 ^ x.regs Reg.depth * x.regs Reg.modulus) + (x.regs Reg.scratch + 4 ^ x.regs Reg.depth * x.regs Reg.modulus) y ∧ + ∀ a, a < y.regs Reg.frame ∨ y.regs Reg.frame + 7 ≤ a → y.mem a = x.mem a := by + obtain ⟨y, hy, hsize, hframes, hframe, hphase, hactive, hzero, hone, hfour, hwidth, + hparams, hfields, hmem⟩ := exec_setup x hw + have hf : y.regs Reg.frame = + x.regs Reg.scratch + 4 ^ x.regs Reg.depth * x.regs Reg.modulus := by + simpa only [Nat.mul_comm] using hframe.trans hframes + refine ⟨y, hy, { + inputA_eq := hparams _ (by decide) + inputB_eq := hparams _ (by decide) + output_eq := hparams _ (by decide) + scratch_eq := hparams _ (by decide) + modulus_eq := hparams _ (by decide) + depth_eq := hparams _ (by decide) + size_eq := ?_ + frames_eq := ?_ + frame_eq := hf + phase_eq := hphase + active_eq := hactive + zero_eq := hzero + one_eq := hone + four_eq := hfour + frameWidth_eq := hwidth + frame_words := ?_ }, hmem⟩ + · simpa only [Nat.mul_comm] using hsize + · simpa only [Nat.mul_comm] using hframes + · simpa only [hf] using hfields + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Traversal.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Traversal.lean new file mode 100644 index 0000000000..8b95ebff55 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Traversal.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.Strassen.Resume + +/-! +# Native Strassen traversal induction + +The explicit native controller executes all seven children, real parent resumptions and +the final pop. The proof inducts on matrix depth and the actual remaining child phases. +Every recursive execution comes from that induction; there is no assumed multiplication, +execution or time field in the final result. Root setup and initialized counter composition +must separately derive the entry invariants from original input data. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- Internal remaining-work budget, including each real child and its parent return step. +The five double updates and two single updates account for exactly twelve coefficient loops. -/ +@[expose] def suffixBudget (p k : ℕ) : Fin 7 → ℕ + | 0 => 7 * traceBudget p k + 624 * (4 ^ k * p) + 731 + | 1 => 6 * traceBudget p k + 520 * (4 ^ k * p) + 617 + | 2 => 5 * traceBudget p k + 416 * (4 ^ k * p) + 503 + | 3 => 4 * traceBudget p k + 312 * (4 ^ k * p) + 389 + | 4 => 3 * traceBudget p k + 208 * (4 ^ k * p) + 275 + | 5 => 2 * traceBudget p k + 104 * (4 ^ k * p) + 161 + | 6 => traceBudget p k + 26 * (4 ^ k * p) + 57 + +private theorem suffixBudget_next (p k : ℕ) (phase : Fin 6) : + traceBudget p k + (receiveBudget ⟨phase.val, by omega⟩ (4 ^ k * p) + + 52 * (4 ^ k * p) + 92) + 2 + suffixBudget p k ⟨phase.val + 1, by omega⟩ = + suffixBudget p k ⟨phase.val, by omega⟩ := by + fin_cases phase <;> norm_num [suffixBudget, receiveBudget] <;> omega + +private abbrev Continues (w : ℕ) (x y : Data 64) (T : ℕ) : Prop := + ∀ z N, ExecWithin w controller y z N → ExecWithin w controller x z (T + N) + +private theorem continues_trans {w U T : ℕ} {x y z : Data 64} + (hxy : Continues w x y U) (hyz : Continues w y z T) : Continues w x z (U + T) := by + intro s N hs + exact (hxy s _ (hyz s N hs)).mono (by omega) + +private theorem continues_step {w T : ℕ} {x y : Data 64} + (hc : (Cond.lt Reg.zero Reg.active).Holds x) (he : ExecWithin w step x y T) : + Continues w x y (T + 2) := by + intro z N hz + exact (ExecWithin.loop_iter hc he hz).mono (by omega) + +/-- Complete native execution of one ready node, including every recursively executed +child, stack access, phase decision, operand loop, accumulation and return. The continuation +form charges the surrounding real loop while allowing a nonroot child to return to its parent. -/ +theorem exec_ready (k : ℕ) {w p A B a b out scratch frames frame : ℕ} + (x : Data 64) (u v : ℕ → ℤ) (hx : NodeReady p k a b out scratch frames frame x) + (hl : NodeLayout p k a b out scratch frames frame) + (hu : ∀ i < 4 ^ k * p, (u i).natAbs ≤ A) + (hv : ∀ i < 4 ^ k * p, (v i).natAbs ≤ B) + (hleft : ∀ i < 4 ^ k * p, x.mem (a + i) = encode (u i)) + (hright : ∀ i < 4 ^ k * p, x.mem (b + i) = encode (v i)) + (hmag : GenericRAM.Signed.Fits w (magnitudeBound p A B k : ℤ)) + (haddr : Fits (w + 2) (addressBound frames frame k)) : + ∃ y, NodeResult p k out scratch frames frame u v x y ∧ + ∀ z T, ExecWithin (w + 2) controller y z T → + ExecWithin (w + 2) controller x z (traceBudget p k + T) := by + induction k generalizing A B a b out scratch frame x u v with + | zero => + exact exec_ready_zero x u v hx hl + (by simpa only [pow_zero, Nat.one_mul] using hu) + (by simpa only [pow_zero, Nat.one_mul] using hv) + (by simpa only [pow_zero, Nat.one_mul] using hleft) + (by simpa only [pow_zero, Nat.one_mul] using hright) hmag haddr + | succ k ih => + have childFits : GenericRAM.Signed.Fits w (magnitudeBound p (2 * A) (2 * B) k : ℤ) := + hmag.of_natAbs_le (by + rw [Int.natAbs_natCast] + exact child_magnitudeBound_le p A B k) + have childAddr : Fits (w + 2) (addressBound frames (frame + 7) k) := by + simpa only [addressBound_child] using haddr + have childLayout := hl.child + have remaining : ∀ remainingCount : ℕ, ∀ phase : Fin 7, + 6 - phase.val = remainingCount → ∀ child : Data 64, + PendingChild p k a b out scratch frames frame phase u v x child → + ∃ y, NodeResult p (k + 1) out scratch frames frame u v child y ∧ + Continues (w + 2) child y (suffixBudget p k phase) := by + intro remainingCount + induction remainingCount with + | zero => + intro phase hphase child pending + have hphase' : phase = 6 := Fin.ext (by omega) + subst phase + obtain ⟨returned, childResult, childExecution⟩ := ih child _ _ pending.ready childLayout + (fun j hj => (Cslib.Algorithms.MatrixMultiplication.Strassen.operands_natAbs_le + u 6 hu hj).1) + (fun j hj => (Cslib.Algorithms.MatrixMultiplication.Strassen.operands_natAbs_le + v 6 hv hj).2) pending.left pending.right childFits childAddr + obtain ⟨y, stepRun, finalResult⟩ := exec_step_resume_finish x child returned u v + hx.toNodeHeader hl pending childResult hu hv hmag haddr + have active : (Cond.lt Reg.zero Reg.active).Holds returned := by + have hf := hl.frames_before + simp only [Cond.holds_lt, childResult.zero_eq, childResult.active_eq] + rw [ite_eq_left (by omega)] + decide + have cont := continues_trans childExecution (continues_step active stepRun) + refine ⟨y, { finalResult with outside := ?_ }, ?_⟩ + · intro d hd + exact (finalResult.outside d hd).trans + (childResult.outside d (fun h => hd (NodeWrites.child_subset hl h))) + · simpa [suffixBudget, Nat.add_assoc] using cont + | succ remainingCount inductionRemaining => + intro phase hphase child pending + have hlt : phase.val < 6 := by omega + let current : Fin 6 := ⟨phase.val, hlt⟩ + let next : Fin 7 := ⟨phase.val + 1, by omega⟩ + obtain ⟨returned, childResult, childExecution⟩ := ih child _ _ pending.ready childLayout + (fun j hj => (Cslib.Algorithms.MatrixMultiplication.Strassen.operands_natAbs_le + u phase hu hj).1) + (fun j hj => (Cslib.Algorithms.MatrixMultiplication.Strassen.operands_natAbs_le + v phase hv hj).2) pending.left pending.right childFits childAddr + obtain ⟨nextChild, stepRun, nextPending, stepOutside⟩ := exec_step_resume_next current + x child returned u v hx.toNodeHeader hl pending childResult hu hv hmag haddr + obtain ⟨y, result, rest⟩ := inductionRemaining next (by dsimp [next]; omega) + nextChild nextPending + have active : (Cond.lt Reg.zero Reg.active).Holds returned := by + have hf := hl.frames_before + simp only [Cond.holds_lt, childResult.zero_eq, childResult.active_eq] + rw [ite_eq_left (by omega)] + decide + have cont := continues_trans childExecution + (continues_trans (continues_step active stepRun) rest) + refine ⟨y, { result with outside := ?_ }, ?_⟩ + · intro d hd + exact ((result.outside d hd).trans (stepOutside d hd)).trans + (childResult.outside d (fun h => hd (NodeWrites.child_subset hl h))) + · intro z T hz + have hc := cont z T hz + apply hc.mono + have hb := suffixBudget_next p k current + have heq : (⟨phase.val, by omega⟩ : Fin 7) = phase := Fin.ext rfl + dsimp only [current, next] at * + rw [heq] at hb hc + rw [heq] + omega + obtain ⟨first, firstStep, firstPending, firstOutside⟩ := + exec_first_pending x u v hx hl hu hv hleft hright hmag haddr + obtain ⟨y, result, rest⟩ := remaining 6 0 (by decide) first firstPending + have active : (Cond.lt Reg.zero Reg.active).Holds x := by + simp only [Cond.holds_lt, hx.zero_eq, hx.active_eq] + decide + have cont := continues_trans (continues_step active firstStep) rest + refine ⟨y, { result with outside := ?_ }, ?_⟩ + · intro d hd + exact (result.outside d hd).trans (firstOutside d hd) + · intro z T hz + exact (cont z T hz).mono (by simp only [traceBudget, suffixBudget]; omega) + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Zero.lean b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Zero.lean new file mode 100644 index 0000000000..65f72c3e74 --- /dev/null +++ b/Complexity/Algorithms/WordRAM/MatrixMultiplication/Strassen/Zero.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.Strassen.State +public import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.LeafStep + +/-! +# Base case of the actual native Strassen traversal induction + +A ready scalar frame executes its real native leaf iteration and returns to the parent or +finishes the root. The continuation form accounts for the enclosing native loop overhead; +the final root theorem closes it with the proved zero active flag and the charged loop exit. +All arithmetic and frame fits follow from original scalar/pointer envelopes. +-/ + +public section + +namespace Cslib.Algorithms.WordRAM.Strassen + +open Cslib.WordRAM Cslib.WordRAM.Signed + +/-- The all-coefficient native scalar node, with its actual return and complete footprint. +No multiplication or execution field occurs in its hypotheses. -/ +theorem exec_ready_zero {w p A B a b out scratch frames frame : ℕ} + (x : Data 64) (u v : ℕ → ℤ) (hx : NodeReady p 0 a b out scratch frames frame x) + (hl : NodeLayout p 0 a b out scratch frames frame) + (hu : ∀ i < p, (u i).natAbs ≤ A) (hv : ∀ i < p, (v i).natAbs ≤ B) + (hleft : ∀ i < p, x.mem (a + i) = encode (u i)) + (hright : ∀ i < p, x.mem (b + i) = encode (v i)) + (hmag : GenericRAM.Signed.Fits w (magnitudeBound p A B 0 : ℤ)) + (haddr : Fits (w + 2) (addressBound frames frame 0)) : + ∃ y, NodeResult p 0 out scratch frames frame u v x y ∧ + ∀ z T, ExecWithin (w + 2) controller y z T → + ExecWithin (w + 2) controller x z (traceBudget p 0 + T) := by + have hs : scratch + p ≤ frames := by simpa only [pow_zero, Nat.one_mul] using hl.scratch_before + have ha : a + p ≤ scratch := by simpa only [pow_zero, Nat.one_mul] using hl.left_before + have hb : b + p ≤ scratch := by simpa only [pow_zero, Nat.one_mul] using hl.right_before + have ho : out + p ≤ scratch := by simpa only [pow_zero, Nat.one_mul] using hl.output_before + have hf := hl.frames_before + have hcyclic : GenericRAM.Signed.Fits w (CyclicConvolution.magnitudeBound p A B : ℤ) := + hmag.of_natAbs_le (by + rw [Int.natAbs_natCast] + exact scalar_le_magnitudeBound p A B) + obtain ⟨y, hy, values, outside, hyframe, hyactive, hyregs⟩ := exec_step_leaf x u v + hx.zero_eq hx.one_eq hx.frameWidth_eq hx.depth_eq + (by simpa only [hx.frame_eq] using hx.frame_words) + (by rw [hx.frame_eq]; exact haddr.of_le (by unfold addressBound; omega)) + hx.modulus_eq hcyclic hu hv + (by simpa only [hx.inputA_eq] using hleft) + (by simpa only [hx.inputB_eq] using hright) + (haddr.of_le (by unfold addressBound; omega)) + (by rw [hx.inputA_eq]; exact haddr.of_le (by unfold addressBound; omega)) + (by rw [hx.inputB_eq]; exact haddr.of_le (by unfold addressBound; omega)) + (by rw [hx.output_eq]; exact haddr.of_le (by unfold addressBound; omega)) + (by simpa only [hx.inputA_eq, hx.output_eq, pow_zero, Nat.one_mul] using hl.left_disjoint) + (by simpa only [hx.inputB_eq, hx.output_eq, pow_zero, Nat.one_mul] using hl.right_disjoint) + refine ⟨y, ?_, ?_⟩ + · refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · simpa only [hx.output_eq, pow_zero, Nat.one_mul, + Cslib.Algorithms.MatrixMultiplication.Strassen.multiply] using values + · intro q hq + have hno : ¬(out ≤ q ∧ q < out + p) := by + intro h + exact hq (Or.inl (by simpa only [pow_zero, Nat.one_mul] using h)) + exact outside q (by rw [hx.output_eq]; omega) + · exact (hyregs Reg.output (by decide) (by decide) (by decide) (by decide)).trans hx.output_eq + · exact (hyregs Reg.modulus (by decide) (by decide) (by decide) (by decide)).trans hx.modulus_eq + · exact (hyregs Reg.frames (by decide) (by decide) (by decide) (by decide)).trans hx.frames_eq + · simpa only [hx.frames_eq, hx.frame_eq] using hyframe + · simpa only [hx.frames_eq, hx.frame_eq, hx.active_eq] using hyactive + · exact (hyregs Reg.zero (by decide) (by decide) (by decide) (by decide)).trans hx.zero_eq + · exact (hyregs Reg.one (by decide) (by decide) (by decide) (by decide)).trans hx.one_eq + · exact (hyregs Reg.four (by decide) (by decide) (by decide) (by decide)).trans hx.four_eq + · exact (hyregs Reg.frameWidth (by decide) (by decide) (by decide) (by decide)).trans + hx.frameWidth_eq + · intro z T hz + have hc : (Cond.lt Reg.zero Reg.active).Holds x := by + simp only [Cond.holds_lt, hx.zero_eq, hx.active_eq] + decide + exact (ExecWithin.loop_iter hc hy hz).mono (by simp only [traceBudget]; omega) + +end Cslib.Algorithms.WordRAM.Strassen diff --git a/Complexity/Computability/WordRAM/Signed/ArrayArithmetic/Body.lean b/Complexity/Computability/WordRAM/Signed/ArrayArithmetic/Body.lean new file mode 100644 index 0000000000..02d0132f24 --- /dev/null +++ b/Complexity/Computability/WordRAM/Signed/ArrayArithmetic/Body.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.Computability.WordRAM.Signed.ArrayArithmetic.Program + +/-! +# Charged native pointwise signed array operations + +Both operands are read before the output store, so exact input/output aliases are valid. +The enclosing array loop is responsible for proving that earlier stores preserve unread +input cells. The numerical premises here concern only the two original values and addresses. +-/ + +public section + +namespace Cslib.WordRAM.Signed.ArrayArithmetic + +open Cslib.WordRAM Cslib.WordRAM.Signed + +private theorem exec_arithmetic {w : ℕ} (subtract : Bool) (x : Data 16) (a b : ℤ) + (ha : GenericRAM.Signed.Fits w a) (hb : GenericRAM.Signed.Fits w b) + (hla : x.regs left = encode a) (hlb : x.regs right = encode b) : + ∃ y, ExecWithin (w + 2) (arithmetic subtract) x y 17 ∧ + y.regs result = encode (if subtract then a - b else a + b) ∧ Frame x y := by + cases subtract with + | false => + obtain ⟨y, he, hr, hf⟩ := Signed.exec_add ha hb x hla hlb + exact ⟨y, he.mono (by decide), hr, hf⟩ + | true => + obtain ⟨y, he, hr, hf⟩ := Signed.exec_subtract ha hb x hla hlb + exact ⟨y, he, hr, hf⟩ + +/-- The arithmetic workspace, index and address are the only body modifications. -/ +@[expose] def BodyLocal (i : Fin 16) : Prop := + i.val < 9 ∨ i = Reg.index ∨ i = Reg.address + +instance (i : Fin 16) : Decidable (BodyLocal i) := + inferInstanceAs (Decidable (i.val < 9 ∨ i = Reg.index ∨ i = Reg.address)) + +private theorem body_preserves (subtract : Bool) (i : Fin 16) : + ¬BodyLocal i → (body subtract).Preserves i := by + cases subtract <;> decide +kernel +revert + +/-- One complete pointwise operation costs at most twenty-four native instructions. -/ +theorem exec_body {w : ℕ} (subtract : Bool) (x : Data 16) (a b : ℤ) + (ha : GenericRAM.Signed.Fits w a) (hb : GenericRAM.Signed.Fits w b) + (hA : x.mem (x.regs Reg.inputA + x.regs Reg.index) = encode a) + (hB : x.mem (x.regs Reg.inputB + x.regs Reg.index) = encode b) + (haddrA : Fits (w + 2) (x.regs Reg.inputA + x.regs Reg.index)) + (haddrB : Fits (w + 2) (x.regs Reg.inputB + x.regs Reg.index)) + (haddrOut : Fits (w + 2) (x.regs Reg.output + x.regs Reg.index)) + (hnext : Fits (w + 2) (x.regs Reg.index + 1)) (hone : x.regs Reg.one = 1) : + ∃ y, ExecWithin (w + 2) (body subtract) x y 24 ∧ + y.regs Reg.index = x.regs Reg.index + 1 ∧ + y.mem = Function.update x.mem (x.regs Reg.output + x.regs Reg.index) + (encode (if subtract then a - b else a + b)) ∧ + ∀ i, ¬BodyLocal i → y.regs i = x.regs i := by + let z := (((x.set Reg.address (x.regs Reg.inputA + x.regs Reg.index)).set left + (encode a)).set Reg.address (x.regs Reg.inputB + x.regs Reg.index)).set right (encode b) + have hz : ExecWithin (w + 2) load x z 4 := by + ram_eval [load, z, hA, hB, haddrA, haddrB, Reg.inputA, Reg.inputB, Reg.index, + Reg.address, left, right, Fin.mk.injEq] + obtain ⟨t, ht, htresult, htframe⟩ := exec_arithmetic subtract z a b ha hb + (by simp +decide [z, left, right, Reg.address]) (by simp [z]) + have hprefix : ExecWithin (w + 2) (.seq load (arithmetic subtract)) x t 21 := hz.seq ht + have hri : t.regs Reg.index = x.regs Reg.index := + hprefix.regs_eq (by cases subtract <;> decide) + have hro : t.regs Reg.output = x.regs Reg.output := + hprefix.regs_eq (by cases subtract <;> decide) + have hr1 : t.regs Reg.one = 1 := + (hprefix.regs_eq (by cases subtract <;> decide)).trans hone + let y := ((t.set Reg.address (x.regs Reg.output + x.regs Reg.index)).write + (x.regs Reg.output + x.regs Reg.index) + (encode (if subtract then a - b else a + b))).set Reg.index (x.regs Reg.index + 1) + have hfinish : ExecWithin (w + 2) finish t y 3 := by + ram_eval [finish, y, hri, hro, hr1, htresult, haddrOut, hnext, Reg.address, + Reg.index, Reg.output, Reg.one, result, Fin.mk.injEq] + have he : ExecWithin (w + 2) (body subtract) x y 24 := hz.seq (ht.seq hfinish) + refine ⟨y, he, by simp [y], ?_, fun i hi => he.regs_eq (body_preserves subtract i hi)⟩ + simpa only [y, Data.set_mem, Data.write_mem, z] using + congrArg (fun mem => Function.update mem (x.regs Reg.output + x.regs Reg.index) + (encode (if subtract then a - b else a + b))) htframe.1 + +end Cslib.WordRAM.Signed.ArrayArithmetic diff --git a/Complexity/Computability/WordRAM/Signed/ArrayArithmetic/Exec.lean b/Complexity/Computability/WordRAM/Signed/ArrayArithmetic/Exec.lean new file mode 100644 index 0000000000..6aaee2f266 --- /dev/null +++ b/Complexity/Computability/WordRAM/Signed/ArrayArithmetic/Exec.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.Computability.WordRAM.Signed.ArrayArithmetic.Body +public import Complexity.Computability.WordRAM.Signed.Fits + +/-! +# Complete native signed array arithmetic with exact aliases + +Each input is either separated from the output or has the identical base. Earlier writes +then preserve every unread source entry. This supports both disjoint Strassen operand forms +and in-place additions/subtractions into output quadrants. +-/ + +public section + +namespace Cslib.WordRAM.Signed.ArrayArithmetic + +open Cslib.WordRAM Cslib.WordRAM.Signed + +private theorem init_preserves (i : Fin 16) : + 9 ≤ i.val ∧ i.val ≤ 12 → + (ram[ Reg.index ← #0; Reg.one ← #1 ] : Code .standard 16).Preserves i := by + decide +kernel +revert + +/-- Actual native array addition/subtraction, including setup and all loop/access work. +The source-width envelope concerns only original input magnitudes. Dirty output and exact +input/output aliases are permitted. The bound excludes the final halt. -/ +theorem exec {w n A B : ℕ} (subtract : Bool) (x : Data 16) (u v : ℕ → ℤ) + (hlen : x.regs Reg.length = n) (hbound : GenericRAM.Signed.Fits w ((A + B : ℕ) : ℤ)) + (hu : ∀ i < n, (u i).natAbs ≤ A) (hv : ∀ i < n, (v i).natAbs ≤ B) + (hA : ∀ i < n, x.mem (x.regs Reg.inputA + i) = encode (u i)) + (hB : ∀ i < n, x.mem (x.regs Reg.inputB + i) = encode (v i)) + (ha : Fits (w + 2) (x.regs Reg.inputA + n)) + (hb : Fits (w + 2) (x.regs Reg.inputB + n)) + (hout : Fits (w + 2) (x.regs Reg.output + n)) + (hsepA : x.regs Reg.inputA + n ≤ x.regs Reg.output ∨ + x.regs Reg.output + n ≤ x.regs Reg.inputA ∨ x.regs Reg.inputA = x.regs Reg.output) + (hsepB : x.regs Reg.inputB + n ≤ x.regs Reg.output ∨ + x.regs Reg.output + n ≤ x.regs Reg.inputB ∨ x.regs Reg.inputB = x.regs Reg.output) : + ∃ y, ExecWithin (w + 2) (program subtract) x y (26 * n + 4) ∧ + (∀ i < n, y.mem (x.regs Reg.output + i) = + encode (if subtract then u i - v i else u i + v i)) ∧ + (∀ a, a < x.regs Reg.output ∨ x.regs Reg.output + n ≤ a → y.mem a = x.mem a) ∧ + ∀ 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 Reg.one = 1 ∧ + y.regs Reg.index ≤ n ∧ + (∀ i < y.regs Reg.index, y.mem (x.regs Reg.output + i) = + encode (if subtract then u i - v i else u i + v i)) ∧ + ∀ a, ¬(x.regs Reg.output ≤ a ∧ a < x.regs Reg.output + y.regs Reg.index) → + y.mem a = x.mem a + have hbody (y : Data 16) (hy : I y) (hc : (Cond.lt Reg.index Reg.length).Holds y) : + ∃ z, ExecWithin (w + 2) (body subtract) y z 24 ∧ I z ∧ + n - z.regs Reg.index < n - y.regs Reg.index := by + obtain ⟨hregs, h1, hi, hvalues, hframe⟩ := hy + have hl := (hregs Reg.length (by decide)).trans hlen + have hlt : y.regs Reg.index < n := by simpa only [Cond.holds_lt, hl] using hc + have hra := hregs Reg.inputA (by decide) + have hrb := hregs Reg.inputB (by decide) + have hro := hregs Reg.output (by decide) + have readA : y.mem (y.regs Reg.inputA + y.regs Reg.index) = encode (u (y.regs Reg.index)) := by + rw [hra, hframe _ (by rcases hsepA with h | h | h <;> omega)] + exact hA _ hlt + have readB : y.mem (y.regs Reg.inputB + y.regs Reg.index) = encode (v (y.regs Reg.index)) := by + rw [hrb, hframe _ (by rcases hsepB with h | h | h <;> omega)] + exact hB _ hlt + obtain ⟨z, hz, hzi, hzmem, hzregs⟩ := + exec_body subtract y (u (y.regs Reg.index)) (v (y.regs Reg.index)) + (hbound.of_natAbs_le ((hu _ hlt).trans (by omega))) + (hbound.of_natAbs_le ((hv _ hlt).trans (by omega))) readA readB + (by rw [hra]; exact ha.of_le (by omega)) + (by rw [hrb]; exact hb.of_le (by omega)) + (by rw [hro]; exact hout.of_le (by omega)) (hout.of_le (by omega)) h1 + have hm : z.mem = Function.update y.mem (x.regs Reg.output + y.regs Reg.index) + (encode (if subtract then u (y.regs Reg.index) - v (y.regs Reg.index) + else u (y.regs Reg.index) + v (y.regs Reg.index))) := by + simpa only [hro] using hzmem + have hpres (i : Fin 16) (hip : 9 ≤ i.val ∧ i.val ≤ 12) : + z.regs i = x.regs i := by + have hnl : ¬BodyLocal i := by + simp only [BodyLocal, Reg.index, Reg.address, Fin.ext_iff] + omega + exact (hzregs i hnl).trans (hregs i hip) + refine ⟨z, hz, ⟨hpres, ?_, ?_, ?_, ?_⟩, ?_⟩ + · exact (hzregs Reg.one (by decide)).trans h1 + · rw [hzi] + omega + · intro i hip + rw [hzi] at hip + rw [hm] + by_cases he : i = y.regs Reg.index + · subst i + simp + · rw [Function.update_of_ne (by omega)] + exact hvalues i (by omega) + · intro a ha' + have hne : a ≠ x.regs Reg.output + y.regs Reg.index := by + intro he + apply ha' + rw [he, hzi] + omega + rw [hm, Function.update_of_ne hne] + apply hframe + intro h + apply ha' + rw [hzi] + omega + · rw [hzi] + omega + let x₀ := (x.set Reg.index 0).set Reg.one 1 + have h1 : Fits (w + 2) 1 := (show Fits 2 1 from by decide).mono (by omega) + have hinit : ExecWithin (B := .standard) (w + 2) + (ram[ Reg.index ← #0; Reg.one ← #1 ]) x x₀ 2 := by ram_eval [x₀, h1] + have hx₀ : I x₀ := by + refine ⟨fun i hi => hinit.regs_eq (init_preserves i hi), by simp [x₀], ?_, ?_, ?_⟩ + · simp +decide [x₀, Reg.one, Reg.index] + · simp +decide [x₀, Reg.one, Reg.index] + · intro _ _ + rfl + obtain ⟨y, hy, hyI, hstop⟩ := + ExecWithin.loop_invariant I (fun y => n - y.regs Reg.index) hbody hx₀ + obtain ⟨hregs, _, hi, hvalues, hframe⟩ := hyI + have hl := (hregs Reg.length (by decide)).trans hlen + have hend : y.regs Reg.index = n := by + simp only [Cond.holds_lt, hl] at hstop + omega + have he : ExecWithin (w + 2) (program subtract) x y (26 * n + 4) := + (hinit.seq hy).mono (by simp +decide [x₀, Reg.one, Reg.index]; omega) + refine ⟨y, he, by simpa only [hend] using hvalues, ?_, hregs⟩ + intro a ha' + apply hframe + rw [hend] + omega + +/-- Both closed operation choices use fixed standard code and never read physical width. -/ +theorem program_fixed (subtract : Bool) : + ((program subtract).program Reg.output).length ≤ 128 ∧ + ¬((program subtract).program Reg.output).ReadsWidth := by + cases subtract <;> constructor + · decide +kernel + · apply Code.not_readsWidth_program + decide +kernel + · decide +kernel + · apply Code.not_readsWidth_program + decide +kernel + +end Cslib.WordRAM.Signed.ArrayArithmetic diff --git a/Complexity/Computability/WordRAM/Signed/ArrayArithmetic/Program.lean b/Complexity/Computability/WordRAM/Signed/ArrayArithmetic/Program.lean new file mode 100644 index 0000000000..663db8fa63 --- /dev/null +++ b/Complexity/Computability/WordRAM/Signed/ArrayArithmetic/Program.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.Computability.WordRAM.Signed.Arithmetic + +/-! +# Native pointwise signed array addition and subtraction + +These fixed loops perform the vector forms and accumulations needed by native Strassen. +The Boolean selects a fixed add or subtract block when constructing the code; every array +index, load, signed operation, store and loop instruction remains native and charged. +Parameters are ordinary absolute runtime pointers and a length. +-/ + +public section + +namespace Cslib.WordRAM.Signed.ArrayArithmetic + +namespace Reg + +/-- First canonical signed array. -/ +abbrev inputA : Fin 16 := 9 +/-- Second canonical signed array. -/ +abbrev inputB : Fin 16 := 10 +/-- Output array, which may exactly alias an input. -/ +abbrev output : Fin 16 := 11 +/-- Runtime length of all three arrays. -/ +abbrev length : Fin 16 := 12 +/-- Current pointwise index. -/ +abbrev index : Fin 16 := 13 +/-- Load/store address temporary. -/ +abbrev address : Fin 16 := 14 +/-- Constant one. -/ +abbrev one : Fin 16 := 15 + +end Reg + +/-- Select a real native signed arithmetic block. -/ +@[expose] def arithmetic (subtract : Bool) : Code .standard 16 := + if subtract then Signed.subtract else Signed.add + +/-- Compute both operand addresses and read the actual words. -/ +@[expose] def load : Code .standard 16 := ram[ + Reg.address ← Reg.inputA + Reg.index; Signed.left ← *Reg.address; + Reg.address ← Reg.inputB + Reg.index; Signed.right ← *Reg.address ] + +/-- Write the result and advance the index after both reads have completed. -/ +@[expose] def finish : Code .standard 16 := ram[ + Reg.address ← Reg.output + Reg.index; *Reg.address ← Signed.result; + Reg.index ← Reg.index + Reg.one ] + +/-- One pointwise operation, including all array access work. -/ +@[expose] def body (subtract : Bool) : Code .standard 16 := + .seq load (.seq (arithmetic subtract) finish) + +/-- Fixed finite code for the entire array operation. -/ +@[expose] def program (subtract : Bool) : Code .standard 16 := + .seq (ram[ Reg.index ← #0; Reg.one ← #1 ]) + (.loop (.lt Reg.index Reg.length) (body subtract)) + +end Cslib.WordRAM.Signed.ArrayArithmetic diff --git a/Complexity/Foundations/Data/Int/Residue.lean b/Complexity/Foundations/Data/Int/Residue.lean index eee6215f92..8edb7425d3 100644 --- a/Complexity/Foundations/Data/Int/Residue.lean +++ b/Complexity/Foundations/Data/Int/Residue.lean @@ -16,6 +16,7 @@ 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_neg_emod`: compute the natural residue of a negated integer from its residue. Bounds on natural residues use Mathlib's existing `Int.natMod_lt`. @@ -36,4 +37,20 @@ theorem natCast_toNat_emod {p : ℕ} (hp : 0 < p) (u : ℤ) : ((u % (p : ℤ)).toNat : ℤ) = u % (p : ℤ) := Int.toNat_of_nonneg (Int.emod_nonneg _ (by omega)) +/-- Natural residues of negated integers use a zero test followed by natural subtraction. -/ +theorem toNat_neg_emod {p : ℕ} (hp : 0 < p) (z : ℤ) : + ((-z) % (p : ℤ)).toNat = + if (z % (p : ℤ)).toNat = 0 then 0 else p - (z % (p : ℤ)).toNat := by + have hnonneg := Int.emod_nonneg z (show (p : ℤ) ≠ 0 by omega) + have hlt := Int.emod_lt_of_pos z (show (0 : ℤ) < p by omega) + rw [Int.neg_emod] + by_cases h : (p : ℤ) ∣ z + · simp only [h, ite_true, Int.emod_eq_zero_of_dvd h, Int.toNat_zero] + · have hn : (z % (p : ℤ)).toNat ≠ 0 := by + intro hz + have he : z % (p : ℤ) = 0 := by omega + exact h (Int.dvd_of_emod_eq_zero he) + simp only [h, ite_false, hn, Int.natAbs_natCast] + omega + end Int diff --git a/Complexity/Foundations/Data/Nat/Morton.lean b/Complexity/Foundations/Data/Nat/Morton.lean new file mode 100644 index 0000000000..286beabc02 --- /dev/null +++ b/Complexity/Foundations/Data/Nat/Morton.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.Init +public import Mathlib.Data.Nat.Digits.Lemmas + +/-! +# Morton indexing for square matrices + +`Nat.Morton.index a b` interleaves the binary row and column digits, with the row digit in +the higher bit of each pair. Its inverse projections are `row` and `column`. For a square +of side `2^k`, the four contiguous blocks of length `4^k` are exactly its four quadrants. + +These elementary digit identities adapt Spec/Sec3/Theorem17/ZOrder.lean, lines 39–151, at +anthropics/formal-math revision 16a5ce41304fcdaea4e0be7c132e1c361dc26da5. The native counter +will compute the spread table in memory; these definitions do not supply a table oracle. +-/ + +public section + +namespace Nat.Morton + +/-- Move each binary digit to an even position, preserving its least-significant-first order. -/ +@[expose] def spread (i : ℕ) : ℕ := Nat.ofDigits 4 (Nat.digits 2 i) + +/-- Interleaved row and column number, with row bits above column bits. -/ +@[expose] def index (a b : ℕ) : ℕ := 2 * spread a + spread b + +/-- Recover row bits from a Morton position. -/ +@[expose] def row (z : ℕ) : ℕ := Nat.ofDigits 2 ((Nat.digits 4 z).map (· / 2)) + +/-- Recover column bits from a Morton position. -/ +@[expose] def column (z : ℕ) : ℕ := Nat.ofDigits 2 ((Nat.digits 4 z).map (· % 2)) + +/-- The native spread-table recurrence needs only an earlier entry and the last binary bit. -/ +theorem spread_eq (i : ℕ) : spread i = i % 2 + 4 * spread (i / 2) := by + rcases Nat.eq_zero_or_pos i with rfl | hi + · simp [spread] + · unfold spread + rw [Nat.digits_eq_cons_digits_div (by norm_num) hi.ne', Nat.ofDigits_cons] + +/-- No bits are set in the spread of zero. -/ +@[simp] theorem spread_zero : spread 0 = 0 := by simp [spread] + +/-- Extract the last row bit and recurse on the remaining base-four digits. -/ +theorem row_eq (z : ℕ) : row z = z % 4 / 2 + 2 * row (z / 4) := by + rcases Nat.eq_zero_or_pos z with rfl | hz + · simp [row] + · unfold row + rw [Nat.digits_eq_cons_digits_div (by norm_num) hz.ne', List.map_cons, Nat.ofDigits_cons] + +/-- Extract the last column bit and recurse on the remaining base-four digits. -/ +theorem column_eq (z : ℕ) : column z = z % 4 % 2 + 2 * column (z / 4) := by + rcases Nat.eq_zero_or_pos z with rfl | hz + · simp [column] + · unfold column + rw [Nat.digits_eq_cons_digits_div (by norm_num) hz.ne', List.map_cons, Nat.ofDigits_cons] + +/-- Position zero has row zero. -/ +@[simp] theorem row_zero : row 0 = 0 := by simp [row] + +/-- Position zero has column zero. -/ +@[simp] theorem column_zero : column 0 = 0 := by simp [column] + +/-- The paired last binary bits form the last base-four digit. -/ +theorem index_eq (a b : ℕ) : + index a b = (2 * (a % 2) + b % 2) + 4 * index (a / 2) (b / 2) := by + unfold index + rw [spread_eq a, spread_eq b] + ring + +/-- A side-`2^k` square occupies exactly the first `4^k` Morton positions. -/ +theorem index_lt {k a b : ℕ} (ha : a < 2 ^ k) (hb : b < 2 ^ k) : index a b < 4 ^ k := by + induction k generalizing a b with + | zero => + obtain rfl : a = 0 := by simpa using ha + obtain rfl : b = 0 := by simpa using hb + simp [index] + | succ k ih => + rw [pow_succ] at ha hb + have h := ih (a := a / 2) (b := b / 2) (by omega) (by omega) + rw [index_eq, pow_succ] + omega + +/-- Each computed spread-table entry fits in the corresponding base-four square span. -/ +theorem spread_lt {k i : ℕ} (hi : i < 2 ^ k) : spread i < 4 ^ k := by + have h := index_lt hi (show 0 < 2 ^ k by positivity) + simp only [index, spread_zero, Nat.add_zero] at h + omega + +private theorem row_column_index (a b : ℕ) : row (index a b) = a ∧ column (index a b) = b := by + induction hm : a + b using Nat.strong_induction_on generalizing a b with + | _ m ih => + rcases Nat.eq_zero_or_pos (a + b) with h0 | hpos + · obtain ⟨rfl, rfl⟩ : a = 0 ∧ b = 0 := by omega + simp [index] + · obtain ⟨hr, hc⟩ := ih (a / 2 + b / 2) (by omega) (a / 2) (b / 2) rfl + rw [row_eq, column_eq, index_eq] + generalize index (a / 2) (b / 2) = z at hr hc + rw [show (2 * (a % 2) + b % 2 + 4 * z) / 4 = z by omega, hr, hc] + omega + +/-- Row projection inverts the interleaved position. -/ +@[simp] theorem row_index (a b : ℕ) : row (index a b) = a := (row_column_index a b).1 + +/-- Column projection inverts the interleaved position. -/ +@[simp] theorem column_index (a b : ℕ) : column (index a b) = b := (row_column_index a b).2 + +/-- Re-interleaving the projections recovers every original position. -/ +@[simp] theorem index_row_column (z : ℕ) : index (row z) (column z) = z := by + induction z using Nat.strong_induction_on with + | _ z ih => + rcases Nat.eq_zero_or_pos z with rfl | hz + · simp [index] + · have h := ih (z / 4) (by omega) + rw [index_eq, row_eq z, column_eq z] + generalize row (z / 4) = a at h + generalize column (z / 4) = b at h + rw [show (z % 4 / 2 + 2 * a) / 2 = a by omega, + show (z % 4 % 2 + 2 * b) / 2 = b by omega, h] + omega + +/-- Reading a contiguous quadrant is the same as shifting its row and column by half a side. -/ +theorem row_column_quadrant {k t z : ℕ} (ht : t < 4) (hz : z < 4 ^ k) : + row (t * 4 ^ k + z) = row z + t / 2 * 2 ^ k ∧ + column (t * 4 ^ k + z) = column z + t % 2 * 2 ^ k := by + induction k generalizing z with + | zero => + obtain rfl : z = 0 := by simpa using hz + rw [row_eq, column_eq, pow_zero, pow_zero, Nat.mul_one, Nat.add_zero, + Nat.mod_eq_of_lt ht, Nat.div_eq_of_lt ht, row_zero, column_zero] + omega + | succ k ih => + obtain ⟨hr, hc⟩ := ih (z := z / 4) (by rw [pow_succ] at hz; omega) + have hp : t * 4 ^ (k + 1) = 4 * (t * 4 ^ k) := by rw [pow_succ]; ring + rw [row_eq, column_eq, row_eq z, column_eq z, hp, + show (4 * (t * 4 ^ k) + z) % 4 = z % 4 by omega, + show (4 * (t * 4 ^ k) + z) / 4 = t * 4 ^ k + z / 4 by omega, hr, hc, pow_succ 2] + constructor <;> ring + +/-- Projection of an admitted Morton position remains inside the corresponding square. -/ +theorem row_column_lt {k z : ℕ} (hz : z < 4 ^ k) : row z < 2 ^ k ∧ column z < 2 ^ k := by + induction k generalizing z with + | zero => + obtain rfl : z = 0 := by simpa using hz + simp + | succ k ih => + have h := ih (z := z / 4) (by rw [pow_succ] at hz; omega) + rw [row_eq, column_eq, pow_succ] + omega + +end Nat.Morton diff --git a/Complexity/Foundations/RingTheory/CyclicIntRing/Coefficients.lean b/Complexity/Foundations/RingTheory/CyclicIntRing/Coefficients.lean index 0a8c128516..96af3ddf65 100644 --- a/Complexity/Foundations/RingTheory/CyclicIntRing/Coefficients.lean +++ b/Complexity/Foundations/RingTheory/CyclicIntRing/Coefficients.lean @@ -69,6 +69,16 @@ theorem partialConvolution_natAbs_le {p r k A B : ℕ} {u v : ℕ → ℤ} exact Nat.mul_le_mul (hu i hip) (hv _ (subIndex_lt hr hip)) _ = k * (A * B) := by simp +/-- A cyclic coefficient depends only on the two original coefficient blocks. -/ +theorem convolution_congr {p r : ℕ} (hr : r < p) {u v u' v' : ℕ → ℤ} + (hu : ∀ i < p, u i = u' i) (hv : ∀ i < p, v i = v' i) : + convolution p u v r = convolution p u' v' r := by + unfold convolution partialConvolution + apply Finset.sum_congr rfl + intro i hi + have hip := Finset.mem_range.mp hi + rw [hu i hip, hv _ (subIndex_lt hr hip)] + variable {p : ℕ} /-- Quotient representatives have no coefficients at degrees at least p. -/ diff --git a/ComplexityTests/WordRAMCountPrime.lean b/ComplexityTests/WordRAMCountPrime.lean new file mode 100644 index 0000000000..b736071610 --- /dev/null +++ b/ComplexityTests/WordRAMCountPrime.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.ExactTriangle.CountPrime +import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime.Program + +/-! +# Complete native modular-count execution regressions + +The actual initialized code computes residues, tables, padding, every Strassen product and +the coefficient sum. Triple enumeration appears only in one independent test expectation; +the tested program contains the seven-product recursive matrix multiplication instead. +These interpreter examples do not supply the general native correctness or resource theorem. +-/ + +-- Deliberately execute the real native interpreter for complete counter examples. +set_option linter.hashCommand false + +namespace ComplexityTests.WordRAMCountPrime + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +private def weights (xs : List ℤ) : List ℕ := xs.map encode + +private def initial (n p : ℕ) (a b c : List ℤ) : Data 128 := + .init ([n, p] ++ weights a ++ weights b ++ weights c ++ List.replicate 256 99 ++ [777]) + +-- A genuinely signed zero-weight triangle; the complete leaf uses modulus three. +#guard match entryProgram.eval 20 20000 (initial 1 3 [-2] [1] [1]) with + | some (y, _) => y.regs Reg.result == 1 && y.mem 3 == encode (-2) && + y.mem 4 == encode 1 && y.mem 5 == encode 1 && y.mem 262 == 777 + | none => false + +-- A modular false positive counts for p=3 but not p=5; the integer sum is nonzero. +#guard match entryProgram.eval 20 20000 (initial 1 3 [-2] [1] [4]) with + | some (y, _) => y.regs Reg.result == 1 + | none => false + +#guard match entryProgram.eval 20 20000 (initial 1 5 [-2] [1] [4]) with + | some (y, _) => y.regs Reg.result == 0 + | none => false + +private def a : List ℤ := [-1, 0, 2, -3] +private def b : List ℤ := [1, -2, 3, 0] +private def c : List ℤ := [0, 4, -2, 1] + +private def expected : ℕ := ((List.range 2).flatMap fun i => (List.range 2).flatMap + fun j => (List.range 2).map fun k => (a[i * 2 + j]! + b[j * 2 + k]! + c[i * 2 + k]!) % 2) + |>.count 0 + +-- Independent finite semantics agree on the asymmetric signed example; neither computation +-- is an assumption passed to the native program above or below. +example : Cslib.Algorithms.ExactTriangle.CountPrime.count 2 1 + (fun i j : Fin 2 => a[i.val * 2 + j.val]!) + (fun i j : Fin 2 => b[i.val * 2 + j.val]!) + (fun i j : Fin 2 => c[i.val * 2 + j.val]!) = 4 ∧ + Cslib.Algorithms.ExactTriangle.countZeroMod + (fun i j : Fin 2 => a[i.val * 2 + j.val]!) + (fun i j : Fin 2 => b[i.val * 2 + j.val]!) + (fun i j : Fin 2 => c[i.val * 2 + j.val]!) 2 = 4 := by decide + +example : Nat.Morton.index 1 2 = 6 ∧ Nat.Morton.index 2 1 = 9 ∧ + Nat.Morton.row 15 = 3 ∧ Nat.Morton.column 15 = 3 := by decide + +-- Asymmetric BC verifies that the native construction really performs the required transpose. +#guard match entryProgram.eval 20 100000 (initial 2 2 a b c) with + | some (y, _) => y.regs Reg.result == expected && + y.mem 3 == encode (-1) && y.mem 14 == encode 1 && y.mem 271 == 777 + | none => false + +-- Side three forces actual zero padding to side four and two explicit recursion levels. +#guard match entryProgram.eval 24 400000 + (initial 3 2 (List.replicate 9 (-1)) (List.replicate 9 1) (List.replicate 9 0)) with + | some (y, _) => y.regs Reg.result == 27 && y.mem 286 == 777 + | none => false + +-- No pairs exist for side zero; the actual power, padding and leaf paths still terminate. +#guard match entryProgram.eval 20 20000 (initial 0 2 [] [] []) with + | some (y, _) => y.regs Reg.result == 0 && y.mem 259 == 777 + | none => false + +end ComplexityTests.WordRAMCountPrime diff --git a/ComplexityTests/WordRAMCountPrimeExecution.lean b/ComplexityTests/WordRAMCountPrimeExecution.lean new file mode 100644 index 0000000000..56c0f048b9 --- /dev/null +++ b/ComplexityTests/WordRAMCountPrimeExecution.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 +-/ + +-- upstream: local-only (tests) + +import Complexity.Algorithms.WordRAM.ExactTriangle.CountPrime + +/-! +# Fully admitted complete modular-counter clients + +Every witness below is the same actual State.init prefix and halting state. These kernel +proof clients complement the interpreter's dirty-workspace, padding and transpose regressions. +The resource predicates are discharged from original inputs, not assumed as certificates. +-/ + +namespace ComplexityTests.WordRAMCountPrimeExecution + +open Cslib.WordRAM +open Cslib.Algorithms.WordRAM.ExactTriangle.CountPrime + +private def truth (n p : ℕ) (values : List ℤ) : ℕ := + Cslib.Algorithms.ExactTriangle.countZeroMod + (fun α β : Fin n => values.getD (α.val * n + β.val) 0) + (fun α β : Fin n => values.getD (n * n + α.val * n + β.val) 0) + (fun α β : Fin n => values.getD (2 * (n * n) + α.val * n + β.val) 0) p + +-- A nonzero original sum is counted modulo three. The derived width is met exactly. +example : ∃ y : Data 128, ∃ t, t + 1 ≤ 736 ∧ + Runs (admissionWidth 1 3 4) (entryProgram.program Reg.result) + (State.init (inputWords 1 3 [-2, 1, 4])) ⟨entryProgram.size, y⟩ t ∧ + Executes (admissionWidth 1 3 4) (entryProgram.program Reg.result) + (State.init (inputWords 1 3 [-2, 1, 4])) 1 (t + 1) ∧ + y.regs Reg.result = 1 ∧ y.mem 3 = 5 := by + obtain ⟨_, y, t, ht, run, halt, result, _, frame⟩ := + runs_input (W := admissionWidth 1 3 4) (n := 1) (p := 3) (A := 4) + [-2, 1, 4] (by decide) (by decide) (by decide) le_rfl + change y.regs Reg.result = truth 1 3 [-2, 1, 4] at result + rw [show truth 1 3 [-2, 1, 4] = 1 by decide] at result + refine ⟨y, t, ?_, run, ?_, result, ?_⟩ + · simpa only [show cost 1 3 + 10 = 736 by decide] using ht + · simpa only [result] using halt + · exact frame 3 (Or.inl (by decide)) + +-- The same signed data has no modular-zero triangle modulo five. +example : ∃ y : Data 128, ∃ t, t + 1 ≤ 1454 ∧ + Runs (admissionWidth 1 5 4) (entryProgram.program Reg.result) + (State.init (inputWords 1 5 [-2, 1, 4])) ⟨entryProgram.size, y⟩ t ∧ + Executes (admissionWidth 1 5 4) (entryProgram.program Reg.result) + (State.init (inputWords 1 5 [-2, 1, 4])) 0 (t + 1) ∧ y.mem 3 = 5 := by + obtain ⟨_, y, t, ht, run, halt, result, _, frame⟩ := + runs_input (W := admissionWidth 1 5 4) (n := 1) (p := 5) (A := 4) + [-2, 1, 4] (by decide) (by decide) (by decide) le_rfl + change y.regs Reg.result = truth 1 5 [-2, 1, 4] at result + rw [show truth 1 5 [-2, 1, 4] = 0 by decide] at result + exact ⟨y, t, by simpa only [show cost 1 5 + 10 = 1454 by decide] using ht, + run, by simpa only [result] using halt, frame 3 (Or.inl (by decide))⟩ + +private def asymmetric : List ℤ := + [-1, 0, 2, -3, 1, -2, 3, 0, 0, 4, -2, 1] + +-- Positive recursion depth, an asymmetric BC transpose and signed nonzero coefficients. +example : ∃ y : Data 128, ∃ t, t + 1 ≤ 4596 ∧ + Runs (admissionWidth 2 2 4) (entryProgram.program Reg.result) + (State.init (inputWords 2 2 asymmetric)) ⟨entryProgram.size, y⟩ t ∧ + Executes (admissionWidth 2 2 4) (entryProgram.program Reg.result) + (State.init (inputWords 2 2 asymmetric)) 4 (t + 1) ∧ + y.mem 0 = 14 ∧ y.mem 3 = 3 := by + obtain ⟨_, y, t, ht, run, halt, result, _, frame⟩ := + runs_input (W := admissionWidth 2 2 4) (n := 2) (p := 2) (A := 4) + asymmetric (by decide) (by decide) (by decide) le_rfl + change y.regs Reg.result = truth 2 2 asymmetric at result + rw [show truth 2 2 asymmetric = 4 by decide] at result + exact ⟨y, t, by simpa only [show cost 2 2 + 10 = 4596 by decide] using ht, + run, by simpa only [result] using halt, frame 0 (Or.inl (by decide)), + frame 3 (Or.inl (by decide))⟩ + +private def aliasedCaller : Data 128 := + (((((Data.init ([3] ++ List.replicate 30 99 ++ [777])).set Reg.side 1).set + Reg.modulus 3).set Reg.inputAB 1).set Reg.inputBC 1).set Reg.inputAC 1 |>.set Reg.scratch 2 + +-- Original arrays may share a source address; scratch is dirty and its outside sentinel survives. +example : ∃ y : Data 128, ExecWithin 12 program aliasedCaller y 726 ∧ + y.regs Reg.result = 1 ∧ y.mem 1 = 3 ∧ y.mem 32 = 777 := by + have read : ∀ i < 1 * 1, aliasedCaller.mem (1 + i) = Signed.encode (-1) := by + intro i hi + have he : i = 0 := by omega + subst i + rfl + have side : aliasedCaller.regs Reg.side = 1 := by rfl + have modulus : aliasedCaller.regs Reg.modulus = 3 := by rfl + have ab : aliasedCaller.regs Reg.inputAB = 1 := by rfl + have bc : aliasedCaller.regs Reg.inputBC = 1 := by rfl + have ac : aliasedCaller.regs Reg.inputAC = 1 := by rfl + have space : aliasedCaller.regs Reg.scratch = 2 := by rfl + obtain ⟨y, he, _, result, frame⟩ := exec (w := 10) (n := 1) (p := 3) (scratch := 2) + (a := 1) (b := 1) (c := 1) (A := 1) aliasedCaller + (fun _ => -1) (fun _ => -1) (fun _ => -1) (by decide) side modulus ab bc ac space + (by decide) (by decide) (by decide) + (by intro i hi; decide) (by intro i hi; decide) (by intro i hi; decide) + read read read (by decide) (by decide) + change y.regs Reg.result = Cslib.Algorithms.ExactTriangle.countZeroMod + (fun _ _ : Fin 1 => -1) (fun _ _ : Fin 1 => -1) (fun _ _ : Fin 1 => -1) 3 at result + rw [show Cslib.Algorithms.ExactTriangle.countZeroMod + (fun _ _ : Fin 1 => -1) (fun _ _ : Fin 1 => -1) (fun _ _ : Fin 1 => -1) 3 = 1 by decide] + at result + exact ⟨y, by simpa only [show cost 1 3 = 726 by decide] using he, + result, frame 1 (Or.inl (by decide)), frame 32 (Or.inr (by decide))⟩ + +-- Modulus one is admitted even though it is not prime; every original triple is modular-zero. +example : ∃ y : Data 128, ∃ t, t + 1 ≤ 354 ∧ + Runs (admissionWidth 1 1 4) (entryProgram.program Reg.result) + (State.init (inputWords 1 1 [-2, 1, 4])) ⟨entryProgram.size, y⟩ t ∧ + Executes (admissionWidth 1 1 4) (entryProgram.program Reg.result) + (State.init (inputWords 1 1 [-2, 1, 4])) 1 (t + 1) := by + obtain ⟨_, y, t, ht, run, halt, result, _, _⟩ := + runs_input (W := admissionWidth 1 1 4) (n := 1) (p := 1) (A := 4) + [-2, 1, 4] (by decide) (by decide) (by decide) le_rfl + change y.regs Reg.result = truth 1 1 [-2, 1, 4] at result + rw [show truth 1 1 [-2, 1, 4] = 1 by decide] at result + exact ⟨y, t, by simpa only [show cost 1 1 + 10 = 354 by decide] using ht, + run, by simpa only [result] using halt⟩ + +-- The actual empty-input path still computes powers, padding and the scalar product. +example : ∃ y : Data 128, ∃ t, t + 1 ≤ 412 ∧ + Runs (admissionWidth 0 2 0) (entryProgram.program Reg.result) + (State.init (inputWords 0 2 [])) ⟨entryProgram.size, y⟩ t ∧ + Executes (admissionWidth 0 2 0) (entryProgram.program Reg.result) + (State.init (inputWords 0 2 [])) 0 (t + 1) := by + obtain ⟨_, y, t, ht, run, halt, result, _, _⟩ := + runs_input (W := admissionWidth 0 2 0) (n := 0) (p := 2) (A := 0) + [] (by decide) (by decide) (by decide) le_rfl + change y.regs Reg.result = truth 0 2 [] at result + rw [show truth 0 2 [] = 0 by decide] at result + exact ⟨y, t, by simpa only [show cost 0 2 + 10 = 412 by decide] using ht, + run, by simpa only [result] using halt⟩ + +-- The explicit source regime is admitted without any intermediate word/layout hypothesis. +example : admissionWidth 2 2 4 ≤ 25 * Nat.clog 2 4 ∧ wordBound 2 2 4 ≤ 4 ^ 26 := by + exact ⟨admissionWidth_le_log (κ := 1) (by decide) (by decide), + wordBound_le_pow (κ := 1) (by decide) (by decide)⟩ + +-- Native time really has a subcubic n exponent; the runtime p factor is retained. +example : (281 / 100 : ℝ) < 3 ∧ + ((cost 2 2 + 10 : ℕ) : ℝ) ≤ 3704 * (2 + 1 : ℝ) ^ 2 * + (2 + 1 : ℝ) ^ (281 / 100 : ℝ) := by + exact ⟨by norm_num, cost_le_subcubic 2 2⟩ + +end ComplexityTests.WordRAMCountPrimeExecution diff --git a/ComplexityTests/WordRAMSignedArrayArithmetic.lean b/ComplexityTests/WordRAMSignedArrayArithmetic.lean new file mode 100644 index 0000000000..176fd5f57e --- /dev/null +++ b/ComplexityTests/WordRAMSignedArrayArithmetic.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 +-/ + +-- upstream: local-only (tests) + +import Complexity.Computability.WordRAM.Signed.ArrayArithmetic.Exec +import Mathlib.Tactic.IntervalCases + +/-! +# Real signed-array operations used by Strassen + +The interpreter executes actual argument setup and the complete native loop. The checked +client also discharges every generic execution premise for nonzero signed inputs and an +in-place destination. These are coefficient-array operations, not a matrix/counting endpoint. +-/ + +set_option linter.hashCommand false + +namespace ComplexityTests.WordRAMSignedArrayArithmetic + +open Cslib.WordRAM Cslib.WordRAM.Signed Cslib.WordRAM.Signed.ArrayArithmetic + +private def setup (a b dst n : ℕ) : Code .standard 16 := ram[ + Reg.inputA ← #a; Reg.inputB ← #b; Reg.output ← #dst; Reg.length ← #n ] + +private def complete (subtract : Bool) (a b dst n : ℕ) : Code .standard 16 := + .seq (setup a b dst n) (program subtract) + +-- In-place addition into the first operand: [-3,2]+[5,-7]=[2,-5]. +#guard match (complete false 1 3 1 2).eval 8 100 (.init [7, 4, 10, 15, 123]) with + | some (y, t) => y.mem 1 == encode 2 && y.mem 2 == encode (-5) && + y.mem 3 == 10 && y.mem 4 == 15 && y.mem 5 == 123 && t ≤ 60 + | none => false + +-- In-place subtraction into the second operand still reads its original future entries. +#guard match (complete true 1 3 3 2).eval 8 100 (.init [7, 4, 10, 15, 123]) with + | some (y, t) => y.mem 3 == encode (-8) && y.mem 4 == encode 9 && + y.mem 1 == 7 && y.mem 2 == 4 && y.mem 5 == 123 && t ≤ 60 + | none => false + +-- All three bases may coincide; canonical subtraction produces zero. +#guard match (complete true 1 1 1 2).eval 8 100 (.init [7, 4, 123]) with + | some (y, _) => y.mem 1 == 0 && y.mem 2 == 0 && y.mem 3 == 123 + | none => false + +-- A non-identical partial overlap is rejected by the contract. +example : ¬((1 : ℕ) + 2 ≤ 2 ∨ 2 + 2 ≤ 1 ∨ 1 = 2) := by decide + +private def input : List ℕ := [7, 4, 10, 15, 123] +private def u (i : ℕ) : ℤ := if i = 0 then -3 else 2 +private def v (i : ℕ) : ℤ := if i = 0 then 5 else -7 + +example : InputFits 8 input ∧ ∃ y t, t + 1 ≤ 61 ∧ + Runs 8 ((complete false 1 3 1 2).program Reg.output) (State.init input) + ⟨(complete false 1 3 1 2).size, y⟩ t ∧ + Executes 8 ((complete false 1 3 1 2).program Reg.output) (State.init input) 1 (t + 1) ∧ + y.mem 1 = encode 2 ∧ y.mem 2 = encode (-5) ∧ y.mem 5 = 123 := by + have hi : InputFits 8 input := by decide + let x : Data 16 := ((((Data.init input).set Reg.inputA 1).set Reg.inputB 3).set + Reg.output 1).set Reg.length 2 + have hsetup : ExecWithin 8 (setup 1 3 1 2) (.init input) x 4 := by + ram_eval [setup, x, Reg.inputA, Reg.inputB, Reg.output, Reg.length] + obtain ⟨y, hy, values, frame, regs⟩ := exec (w := 6) (n := 2) (A := 3) (B := 7) + false x u v (by simp [x]) (by decide) + (by intro i hip; interval_cases i <;> decide) + (by intro i hip; interval_cases i <;> decide) + (by intro i hip; interval_cases i <;> decide) + (by intro i hip; interval_cases i <;> decide) + (by decide) (by decide) (by decide) (Or.inr (Or.inr (by decide))) (Or.inr (Or.inl (by decide))) + have he : ExecWithin 8 (complete false 1 3 1 2) (.init input) y 60 := hsetup.seq hy + have hxout : x.regs Reg.output = 1 := by decide + have hyout : y.regs Reg.output = 1 := (regs Reg.output (by decide)).trans hxout + have h0 : y.mem 1 = encode 2 := by + simpa only [hxout, Nat.reduceAdd, Bool.false_eq_true, ↓reduceIte, + show u 0 + v 0 = 2 from by decide] using values 0 (by decide) + have h1 : y.mem 2 = encode (-5) := by + simpa only [hxout, Nat.reduceAdd, Bool.false_eq_true, ↓reduceIte, + show u 1 + v 1 = -5 from by decide] using values 1 (by decide) + have hs : y.mem 5 = 123 := (frame 5 (Or.inr (by decide))).trans (by decide) + obtain ⟨t, ht, hex⟩ := he + refine ⟨hi, y, t, by omega, ?_, ?_, h0, h1, hs⟩ + · simpa only [Nat.zero_add, State.init] using hex.runs (pc := 0) + (p := (complete false 1 3 1 2).program Reg.output) (Program.Contains.self _).append_left + · simpa only [State.init, hyout] using hex.executes Reg.output + +end ComplexityTests.WordRAMSignedArrayArithmetic diff --git a/ComplexityTests/WordRAMStrassen.lean b/ComplexityTests/WordRAMStrassen.lean new file mode 100644 index 0000000000..4be1fe24b6 --- /dev/null +++ b/ComplexityTests/WordRAMStrassen.lean @@ -0,0 +1,244 @@ +/- +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.Strassen.Leaf +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Initialize +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.LeafStep +import Complexity.Algorithms.WordRAM.MatrixMultiplication.Strassen.Exec +import Mathlib.Tactic.IntervalCases + +/-! +# Native explicit-stack Strassen execution regressions + +These run actual finite native code. They check signed scalar leaves, all seven products at +positive depth, and a two-level cyclic-coefficient matrix with dirty scratch. They do not +replace the general execution or resource proof, and do not claim a complete triangle counter. +-/ + +-- These commands deliberately execute the real standard WordRAM interpreter. +set_option linter.hashCommand false + +namespace ComplexityTests.WordRAMStrassen + +open Cslib.WordRAM Cslib.WordRAM.Signed +open Cslib.Algorithms.WordRAM.Strassen + +private def arguments (a b out scratch p depth : ℕ) : Code .standard 64 := ram[ + Reg.inputA ← #a; Reg.inputB ← #b; Reg.output ← #out; Reg.scratch ← #scratch; + Reg.modulus ← #p; Reg.depth ← #depth ] + +private def complete (a b out scratch p depth : ℕ) : Code .standard 64 := + .seq (arguments a b out scratch p depth) program + +-- All seven child products contribute, with negative intermediate values and dirty output. +-- [-1 2; 3 -4] [5 -6; 7 8] = [9 22; -13 -50]. +#guard match (complete 1 5 9 13 1 1).eval 16 10000 + (.init ([3, 4, 6, 9, 10, 13, 14, 16] ++ List.replicate 30 99 ++ [123])) with + | some (y, _) => y.mem 9 == encode 9 && y.mem 10 == encode 22 && + y.mem 11 == encode (-13) && y.mem 12 == encode (-50) && + y.mem 1 == 3 && y.mem 8 == 16 && y.mem 39 == 123 && y.regs Reg.active == 0 + | none => false + +-- The depth-zero path still uses actual cyclic coefficient multiplication. +#guard match (complete 1 4 7 10 3 0).eval 16 2000 + (.init ([2, 5, 6, 9, 10, 4] ++ List.replicate 14 99 ++ [123])) with + | some (y, _) => y.mem 7 == encode 7 && y.mem 8 == encode 19 && + y.mem 9 == encode (-20) && y.mem 21 == 123 && y.regs Reg.active == 0 + | none => false + +private def matrixA : List ℕ := (List.range 32).map fun i => encode ((i : ℤ) - 17) + +private def identityB : List ℕ := (List.range 32).map fun i => + if i % 2 = 0 ∧ i / 2 ∈ [0, 3, 12, 15] then encode 1 else 0 + +-- The Morton-ordered 4×4 identity over Z[X]/(X²-1) leaves all 32 signed coefficients intact. +-- Both recursion levels, frame reuse, all operand-copy cases and canonical cancellations run. +#guard match (complete 1 33 65 97 2 2).eval 24 200000 + (.init (matrixA ++ identityB ++ List.replicate 100 99 ++ [123])) with + | some (y, _) => (List.range 32).all (fun i => + y.mem (65 + i) == matrixA[i]! && y.mem (1 + i) == matrixA[i]!) && + y.mem 165 == 123 && y.regs Reg.active == 0 + | none => false + +private def leafArguments : Code .standard 64 := ram[ + Reg.inputA ← #1; Reg.inputB ← #2; Reg.output ← #3; Reg.modulus ← #1; + Reg.frame ← #10; Reg.frames ← #10; Reg.frameWidth ← #7; Reg.active ← #1 ] + +private def leafRun : Code .standard 64 := .seq leafArguments leaf + +private def leafInput : List ℕ := [7, 4, 99, 123] + +-- A kernel-checked native ABI seam: the same actual initialized prefix/halting state +-- contains the signed result and sentinel, and the root return clears the real active flag. +example : InputFits 10 leafInput ∧ ∃ y t, t + 1 ≤ 71 ∧ + Runs 10 (leafRun.program Reg.output) (State.init leafInput) ⟨leafRun.size, y⟩ t ∧ + Executes 10 (leafRun.program Reg.output) (State.init leafInput) 3 (t + 1) ∧ + y.Bounded 10 ∧ y.mem 3 = encode (-6) ∧ y.mem 4 = 123 ∧ y.regs Reg.active = 0 := by + have hi : InputFits 10 leafInput := by decide + let x₀ := (((Data.init leafInput).set Reg.inputA 1).set Reg.inputB 2).set Reg.output 3 + let x := ((((x₀.set Reg.modulus 1).set Reg.frame 10).set Reg.frames 10).set + Reg.frameWidth 7).set Reg.active 1 + have ha : ExecWithin 10 leafArguments (.init leafInput) x 8 := by + ram_eval [leafArguments, x, x₀, Reg.inputA, Reg.inputB, Reg.output, Reg.modulus, + Reg.frame, Reg.frames, Reg.frameWidth, Reg.active] + obtain ⟨y, hy, hv, hf, _, hflag, hr⟩ := exec_leaf (w := 8) (p := 1) (A := 3) (B := 2) + x (fun _ => -3) (fun _ => 2) (by decide) (by decide) + (by intro i hi'; decide) (by intro i hi'; decide) + (by + intro i hi' + obtain rfl : i = 0 := by omega + decide) + (by + intro i hi' + obtain rfl : i = 0 := by omega + decide) + (by decide) (by decide) (by decide) (by decide) + (Or.inl (by decide)) (Or.inl (by decide)) (by decide) (by decide) + have he : ExecWithin 10 leafRun (.init leafInput) y 70 := ha.seq hy + have hp : y.regs Reg.output = 3 := (hr Reg.output (by decide) (by decide) (by decide)).trans + (by decide) + have hxout : x.regs Reg.output = 3 := by decide + have hvalue : y.mem 3 = encode (-6) := by + simpa only [hxout, Nat.add_zero, show Cslib.CyclicIntRing.convolution 1 + (fun _ => -3) (fun _ => 2) 0 = -6 from by decide] using hv 0 (by decide) + have hsentinel : y.mem 4 = 123 := (hf 4 (Or.inr (by decide))).trans (by decide) + have hactive : y.regs Reg.active = 0 := by + simpa only [show x.regs Reg.frames = 10 from by decide, + show x.regs Reg.frame = 10 from by decide, Nat.lt_irrefl, ite_false] using hflag + have hyb : y.Bounded 10 := he.bounded (Data.Bounded.init hi) + obtain ⟨t, ht, hex⟩ := he + refine ⟨hi, y, t, by omega, ?_, ?_, hyb, + hvalue, hsentinel, hactive⟩ + · simpa only [Nat.zero_add, State.init] using hex.runs (pc := 0) + (p := leafRun.program Reg.output) (Program.Contains.self _).append_left + · simpa only [State.init, hp] using hex.executes Reg.output + +-- The complete fixed controller also has a fully admitted initialized scalar execution: +-- actual Power call, computed frame placement, all frame stores/loads, depth dispatch, +-- coefficient computation, root return, loop exit and halt refer to the same state. +example : ∃ y t, t + 1 ≤ 118 ∧ + Runs 10 ((complete 1 2 3 10 1 0).program Reg.output) (State.init leafInput) + ⟨(complete 1 2 3 10 1 0).size, y⟩ t ∧ + Executes 10 ((complete 1 2 3 10 1 0).program Reg.output) (State.init leafInput) 3 (t + 1) ∧ + y.Bounded 10 ∧ y.mem 3 = encode (-6) ∧ y.mem 4 = 123 := by + let x₀ := (((Data.init leafInput).set Reg.inputA 1).set Reg.inputB 2).set Reg.output 3 + let x := ((x₀.set Reg.scratch 10).set Reg.modulus 1).set Reg.depth 0 + have hargs : ExecWithin 10 (arguments 1 2 3 10 1 0) (.init leafInput) x 6 := by + ram_eval [arguments, x, x₀, Reg.inputA, Reg.inputB, Reg.output, + Reg.scratch, Reg.modulus, Reg.depth] + obtain ⟨s, hs, _, hsframes, hsframe, _, hsactive, hszero, hsone, _, hswidth, + hsparams, hsfields, hsmem⟩ := exec_setup (w := 10) x (by decide) + have hsa : s.regs Reg.inputA = 1 := (hsparams Reg.inputA (by decide)).trans (by decide) + have hsb : s.regs Reg.inputB = 2 := (hsparams Reg.inputB (by decide)).trans (by decide) + have hso : s.regs Reg.output = 3 := (hsparams Reg.output (by decide)).trans (by decide) + have hsp : s.regs Reg.modulus = 1 := (hsparams Reg.modulus (by decide)).trans (by decide) + have hsd : s.regs Reg.depth = 0 := (hsparams Reg.depth (by decide)).trans (by decide) + have hsf : s.regs Reg.frame = 11 := by + rw [hsframe, hsframes] + decide + have hsm (a : ℕ) (ha : a < 11) : s.mem a = x.mem a := + hsmem a (Or.inl (by simpa only [hsf] using ha)) + obtain ⟨y, hy, values, frame, _, hyactive, hyregs⟩ := exec_step_leaf + (w := 8) (p := 1) (A := 3) (B := 2) s (fun _ => -3) (fun _ => 2) + hszero hsone hswidth hsd hsfields (by rw [hsf]; decide) hsp + (by decide) (by intro i hi; decide) (by intro i hi; decide) + (by + intro i hi + obtain rfl : i = 0 := by omega + rw [hsa, Nat.add_zero, hsm 1 (by decide)] + decide) + (by + intro i hi + obtain rfl : i = 0 := by omega + rw [hsb, Nat.add_zero, hsm 2 (by decide)] + decide) + (by decide) (by rw [hsa]; decide) (by rw [hsb]; decide) (by rw [hso]; decide) + (Or.inl (by omega)) (Or.inl (by omega)) + have hy0 : y.regs Reg.zero = 0 := + (hyregs Reg.zero (by decide) (by decide) (by decide) (by decide)).trans hszero + have hyflag : y.regs Reg.active = 0 := by + simpa only [hsframe, Nat.lt_irrefl, ite_false] using hyactive + have hloop : ExecWithin 10 (.loop (.lt Reg.zero Reg.active) step) s y 81 := by + have hexit : ExecWithin 10 (.loop (.lt Reg.zero Reg.active) step) y y 2 := + .loop_exit (by simp only [Cond.holds_lt, hy0, hyflag, Nat.lt_irrefl, not_false_eq_true]) + exact .loop_iter (by simp only [Cond.holds_lt, hszero, hsactive]; decide) hy hexit + have he : ExecWithin 10 (complete 1 2 3 10 1 0) (.init leafInput) y 117 := + hargs.seq (hs.seq hloop) + have hyout : y.regs Reg.output = 3 := + (hyregs Reg.output (by decide) (by decide) (by decide) (by decide)).trans hso + have hvalue : y.mem 3 = encode (-6) := by + simpa only [hso, Nat.add_zero, show Cslib.CyclicIntRing.convolution 1 + (fun _ => -3) (fun _ => 2) 0 = -6 from by decide] using values 0 (by decide) + have hsentinel : y.mem 4 = 123 := + (frame 4 (Or.inr (by omega))).trans ((hsm 4 (by decide)).trans (by decide)) + have hyb := he.bounded (Data.Bounded.init (show InputFits 10 leafInput from by decide)) + obtain ⟨t, ht, hex⟩ := he + refine ⟨y, t, by omega, ?_, ?_, hyb, hvalue, hsentinel⟩ + · simpa only [Nat.zero_add, State.init] using hex.runs (pc := 0) + (p := (complete 1 2 3 10 1 0).program Reg.output) (Program.Contains.self _).append_left + · simpa only [State.init, hyout] using hex.executes Reg.output + +private def stackInput : List ℕ := + [3, 4, 6, 9, 10, 13, 14, 16] ++ List.replicate 30 99 ++ [123] + +private def leftCoefficients (i : ℕ) : ℤ := ([-1, 2, 3, -4] : List ℤ).getD i 0 +private def rightCoefficients (i : ℕ) : ℤ := ([5, -6, 7, 8] : List ℤ).getD i 0 + +-- A fully admitted positive-depth client of the general execution theorem, rather than +-- an interpreter-only regression: all seven children execute in the same initialized run. +-- Every premise is discharged from these original signed arrays and concrete pointers. +example : ∃ y t, t + 1 ≤ 2188 ∧ + Runs 18 ((complete 1 5 9 13 1 1).program Reg.output) (State.init stackInput) + ⟨(complete 1 5 9 13 1 1).size, y⟩ t ∧ + Executes 18 ((complete 1 5 9 13 1 1).program Reg.output) (State.init stackInput) 9 (t + 1) ∧ + y.Bounded 18 ∧ y.mem 9 = encode 9 ∧ y.mem 10 = encode 22 ∧ + y.mem 11 = encode (-13) ∧ y.mem 12 = encode (-50) ∧ y.mem 39 = 123 := by + let x₀ := (((Data.init stackInput).set Reg.inputA 1).set Reg.inputB 5).set Reg.output 9 + let x := ((x₀.set Reg.scratch 13).set Reg.modulus 1).set Reg.depth 1 + have hargs : ExecWithin 18 (arguments 1 5 9 13 1 1) (.init stackInput) x 6 := by + ram_eval [arguments, x, x₀, Reg.inputA, Reg.inputB, Reg.output, + Reg.scratch, Reg.modulus, Reg.depth] + obtain ⟨y, hy, result⟩ := exec (w := 16) (p := 1) (k := 1) (A := 4) (B := 8) + (a := 1) (b := 5) (out := 9) (scratch := 13) x leftCoefficients rightCoefficients + (by decide) (by decide) (by decide) (by decide) (by decide) (by decide) (by decide) + (by decide) (by decide) (by decide) (Or.inl (by decide)) (Or.inl (by decide)) + (by intro i hi; have hi' : i < 4 := hi; interval_cases i <;> decide) + (by intro i hi; have hi' : i < 4 := hi; interval_cases i <;> decide) + (by intro i hi; have hi' : i < 4 := hi; interval_cases i <;> decide) + (by intro i hi; have hi' : i < 4 := hi; interval_cases i <;> decide) + (by decide) (by decide) + have he : ExecWithin 18 (complete 1 5 9 13 1 1) (.init stackInput) y 2187 := + (hargs.seq hy).mono (by decide) + have hv₀ : y.mem 9 = encode 9 := by + simpa only [Nat.add_zero, show Cslib.Algorithms.MatrixMultiplication.Strassen.multiply + 1 1 leftCoefficients rightCoefficients 0 = 9 from by decide] using result.values 0 (by decide) + have hv₁ : y.mem 10 = encode 22 := by + simpa only [show 9 + 1 = 10 from rfl, + show Cslib.Algorithms.MatrixMultiplication.Strassen.multiply + 1 1 leftCoefficients rightCoefficients 1 = 22 from by decide] using + result.values 1 (by decide) + have hv₂ : y.mem 11 = encode (-13) := by + simpa only [show 9 + 2 = 11 from rfl, + show Cslib.Algorithms.MatrixMultiplication.Strassen.multiply + 1 1 leftCoefficients rightCoefficients 2 = -13 from by decide] using + result.values 2 (by decide) + have hv₃ : y.mem 12 = encode (-50) := by + simpa only [show 9 + 3 = 12 from rfl, + show Cslib.Algorithms.MatrixMultiplication.Strassen.multiply + 1 1 leftCoefficients rightCoefficients 3 = -50 from by decide] using + result.values 3 (by decide) + have hsentinel : y.mem 39 = 123 := + (result.outside 39 (by unfold NodeWrites; decide)).trans (by decide) + have hyb := he.bounded (Data.Bounded.init (show InputFits 18 stackInput from by decide)) + obtain ⟨t, ht, hex⟩ := he + refine ⟨y, t, by omega, ?_, ?_, hyb, hv₀, hv₁, hv₂, hv₃, hsentinel⟩ + · simpa only [Nat.zero_add, State.init] using hex.runs (pc := 0) + (p := (complete 1 5 9 13 1 1).program Reg.output) (Program.Contains.self _).append_left + · simpa only [State.init, result.output_eq] using hex.executes Reg.output + +end ComplexityTests.WordRAMStrassen diff --git a/ROADMAP.md b/ROADMAP.md index fbebb1d669..fe25ae1ca5 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -602,12 +602,16 @@ reusing graph and matroid foundations and the shared data-structure interfaces. 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. -- **Direct 3SUM adaptation** ([#1394]): global parameter and prime selection, including the - native cyclic-Strassen modular counter ([#1701]); unrestricted small geometry; the aggregate +- **Direct 3SUM adaptation** ([#1394]): global parameter and prime selection, using the + concrete native cyclic-Strassen modular counter ([#1701]); unrestricted small geometry; the aggregate subcubic Exact Triangle bound and source exponent; and the unconditional ordinary-3SUM input/time/width/asymptotic composition, including the improved Section 4 route to exponent 1.9992. These build on the native Chan–He preprocessing, queried thin products, fixed-prime - Exact Triangle solving and the conditional Convolution-3SUM host. + Exact Triangle solving and the conditional Convolution-3SUM host. The counter computes its + own residues, padding/table, seven-product recursion and exact raw modular count, within + `3704*(p+1)²*(n+1)^2.81` instructions including halt. Its source-regime admission and + width-independent prefix bounds are proved; selecting the actual minimum-count prime and + the global endpoint remain separate. - **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 diff --git a/UPSTREAM.md b/UPSTREAM.md index 77596d4fe6..808bf14799 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -295,7 +295,22 @@ Do not add a ledger row for an ordinary candidate or repeat its source marker he array multiplication in `Algorithms/WordRAM/MatrixMultiplication/CyclicConvolution/`. Source indexing/quotient mathematics adapt pinned Anthropic `RingOps.lean`/`Cyclic.lean`; the native execution, frame and `42p² + 9p + 4` pre-halt instruction bound are proved directly. - Full Strassen/modular-counter composition, fast prime selection and #1394 remain open. + The concrete counter continues in `Algorithms/WordRAM/ExactTriangle/CountPrime/` and the + native explicit-stack `Algorithms/WordRAM/MatrixMultiplication/Strassen/` component, using + original native pointwise signed array operations under `Computability/WordRAM/Signed/`. + It selectively adapts the pinned `CountPrime`, `Strassen`, `ZOrder` and `Count` algorithms; + actual standard WordRAM setup/residues/matrices/seven-product recursion/extraction are proved + from original signed arrays. The 128-register ABI uses raw arguments 64..69 and result 70; + initialized input is raw `(n,p)` plus three canonical signed row-major arrays. The existing + `ExactTriangle.countZeroMod`, cyclic quotient and Morton APIs retain their meanings. + Native cost, including halt, is at most `3704*(p+1)²*(n+1)^2.81`; all p-dependent coefficient + arithmetic is charged, rather than importing Light's ring-operation constants. If the + original modulus and weight bound satisfy `p,A≤(n+2)^κ`, physical width + `(κ+24)*clog₂(n+2)` suffices and every wider prefix has words/support below + `(n+2)^(κ+25)`. The generic caller allows dirty scratch and original-array aliasing below + its workspace. `Int.toNat_neg_emod` is kept in the existing integer-residue topic module. + This completes the counter's mathematical/native component, with validation/review tracked + in #1701. Native prime selection (#2098), global parameter/host composition and #1394 remain open. `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