From c33195005eae3e6da0e33ad648b13d3cefaf9b42 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 17:41:28 -0400 Subject: [PATCH 001/129] feat(circuit/frontier): Boolean tensor networks and their contraction Tensor networks over a commutative semiring as the weighted form of the frontier constraint networks (shared Multigraph and locality), with the contraction Z, subdivision by equality tensors (preserves Z, bipartite, keeps degree bounds), explicit tables with ordered ports and size measures, the write-out theorem, and the 0-1 bridge from constraint networks. Tracking: #1833 (T1), #1538. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 3 + .../Circuit/Frontier/TensorNetwork.lean | 251 ++++++++++++++++++ .../Frontier/TensorNetwork/Constraint.lean | 81 ++++++ .../Frontier/TensorNetwork/Explicit.lean | 210 +++++++++++++++ .../Circuit/Frontier/TensorNetwork.lean | 116 ++++++++ 5 files changed, 661 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork.lean create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/Constraint.lean create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/Explicit.lean create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetwork.lean diff --git a/Complexity.lean b/Complexity.lean index 3942e2e28b..3e53d5172a 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -857,6 +857,9 @@ public import Complexity.Computability.Circuit.Frontier.Projection public import Complexity.Computability.Circuit.Frontier.Rectangle public import Complexity.Computability.Circuit.Frontier.Reduction public import Complexity.Computability.Circuit.Frontier.Sweep +public import Complexity.Computability.Circuit.Frontier.TensorNetwork +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness public import Complexity.Computability.Circuit.Frontier.Unconditional public import Complexity.Computability.Circuit.Frontier.Witness diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork.lean new file mode 100644 index 0000000000..cf45df7f82 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork.lean @@ -0,0 +1,251 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.Multigraph +public import Mathlib.Algebra.BigOperators.Group.Finset.Basic +public import Mathlib.Combinatorics.SimpleGraph.Bipartite +public import Mathlib.Basic.Finite.Sum +public import Mathlib.Data.Fintype.Pi +import Mathlib.Algebra.BigOperators.GroupWithZero.Finset +import Mathlib.Algebra.BigOperators.Ring.Finset + +/-! +# Tensor networks and their contraction + +A *tensor network* over a commutative semiring `R` and an alphabet `U` is a multigraph whose +edges carry values in `U`, with a *tensor* at each vertex `v`: a value `N.tensor v x ∈ R` for each +assignment `x : E → U` of values to the edges, which depends only on the values on the edges at +`v`. Its *contraction* sums, over all assignments, the product of the tensors: + + `Z(N) = ∑ x : E → U, ∏ v, N.tensor v x`. + +A *Boolean* tensor network has `U = Bool`, one bit per edge. The usual presentation gives the +tensor at `v` as a function `F_v` of the bits on the ordered list `δ(v)` of edges at `v`, with +`Z = ∑ x ∏ v F_v (x|δ(v))`. Here the tensor is a function of the whole assignment, constrained to +read only the edges at `v` (`TensorNetwork.tensor_congr`); the ordered presentation, with +explicit tables, is `Cslib.Circuits.Frontier.ExplicitTensorNetwork`, and +`Cslib.Circuits.Frontier.TensorNetwork.contraction_toExplicit` shows that the two contractions +agree. Parallel edges are distinct elements of `E` and so give distinct ports. + +A tensor network is the weighted form of a constraint network +(`Cslib.Circuits.Frontier.Network`) on a fixed input: the two share the multigraph and the +locality condition, and a constraint becomes its `0`–`1` indicator tensor, whose contraction +counts the satisfying assignments +(`Cslib.Circuits.Frontier.Network.contraction_toTensorNetwork`). + +**Subdivision** (`TensorNetwork.subdivide`). Splitting every edge `e` into two edges joined at a +new vertex that carries the *equality tensor* `[y₁ = y₂]` preserves the contraction +(`TensorNetwork.contraction_subdivide`): each assignment of the old network extends in exactly one +way to an assignment of the new one with nonzero weight. The subdivided network is bipartite +(`TensorNetwork.isBipartite_subdivide`), with the old vertices on one side and the new ones on the +other, and it keeps the degree bound of a loopless network (`TensorNetwork.maxDegreeLE_subdivide`). +It has twice as many edges and one new vertex per old edge. + +## Main definitions + +* `Cslib.Circuits.Frontier.Multigraph.IsBipartite`: the vertices split into two sides and every + edge joins the two sides. +* `Cslib.Circuits.Frontier.TensorNetwork`: a tensor network. +* `Cslib.Circuits.Frontier.TensorNetwork.contraction`: its contraction `Z`. +* `Cslib.Circuits.Frontier.TensorNetwork.subdivide`: subdivision of every edge by an equality + tensor. + +## Main results + +* `Cslib.Circuits.Frontier.Multigraph.isBipartite_iff`: a multigraph is bipartite iff it has no + loops and its underlying simple graph is bipartite (`SimpleGraph.IsBipartite`). +* `Cslib.Circuits.Frontier.TensorNetwork.contraction_subdivide`: subdivision preserves the + contraction. +* `Cslib.Circuits.Frontier.TensorNetwork.isBipartite_subdivide`: a subdivided network is + bipartite. +* `Cslib.Circuits.Frontier.TensorNetwork.maxDegreeLE_subdivide`: subdividing a loopless network of + maximum degree at most `d ≥ 2` gives maximum degree at most `d`. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Appendix D.1 (p. 53) and + the subdivision step of Appendix D.2 (p. 54). +-/ + +@[expose] public section + +namespace Cslib.Circuits.Frontier + +open Set + +/-! ### Bipartite multigraphs -/ + +namespace Multigraph + +variable {V E : Type*} (G : Multigraph V E) + +/-- A multigraph is *bipartite* when its vertices split into two sides, `side v = true` and +`side v = false`, and every edge joins the two sides. A bipartite multigraph has no loops +(`Multigraph.IsBipartite.loopless`). -/ +def IsBipartite : Prop := + ∃ side : V → Bool, ∀ e, side (G.src e) ≠ side (G.tgt e) + +variable {G} + +/-- A bipartite multigraph has no loops. -/ +theorem IsBipartite.loopless (h : G.IsBipartite) : G.Loopless := by + obtain ⟨side, hside⟩ := h + exact fun e he => hside e (congrArg side he) + +/-- A multigraph is bipartite exactly when it has no loops and its underlying simple graph is +bipartite in the sense of Mathlib (`SimpleGraph.IsBipartite`, a `2`-colouring). -/ +theorem isBipartite_iff : G.IsBipartite ↔ G.Loopless ∧ G.toSimpleGraph.IsBipartite := by + constructor + · intro h + refine ⟨h.loopless, ?_⟩ + obtain ⟨side, hside⟩ := h + have hcol : G.toSimpleGraph.Coloring Bool := SimpleGraph.Coloring.mk side fun {u v} huv => by + obtain ⟨-, e, he⟩ := toSimpleGraph_adj.mp huv + rcases Sym2.eq_iff.mp he with ⟨hu, hv⟩ | ⟨hu, hv⟩ + · exact hu ▸ hv ▸ hside e + · exact hu ▸ hv ▸ (hside e).symm + simpa using hcol.colorable + · rintro ⟨hloop, ⟨c⟩⟩ + refine ⟨fun v => decide (c v = 0), fun e he => ?_⟩ + have hadj : G.toSimpleGraph.Adj (G.src e) (G.tgt e) := toSimpleGraph_adj.mpr ⟨hloop e, e, rfl⟩ + have key : ∀ a b : Fin 2, a ≠ b → decide (a = 0) ≠ decide (b = 0) := by decide + exact key _ _ (c.valid hadj) he + +end Multigraph + +/-! ### Tensor networks -/ + +/-- A *tensor network* over the commutative semiring `R` with alphabet `U`: a multigraph on the +vertices `V` and edges `E`, whose edges carry values in `U`, and at each vertex a *tensor*, a value +in `R` for each assignment of values to the edges, that sees only the values on the edges at that +vertex. A *Boolean* tensor network has `U = Bool`. -/ +structure TensorNetwork (R U V E : Type*) extends Multigraph V E where + /-- The tensor at a vertex: its value on an assignment of values to the edges. -/ + tensor : V → (E → U) → R + /-- Tensors are local: the tensor at `v` sees only the values on the edges incident to `v`. -/ + tensor_congr : ∀ v x y, (∀ e, src e = v ∨ tgt e = v → x e = y e) → tensor v x = tensor v y + +namespace TensorNetwork + +variable {R U V E : Type*} + +section Contraction + +variable [CommSemiring R] [Fintype U] [Fintype V] [Fintype E] [DecidableEq E] + +/-- The *contraction* of a tensor network: the sum over all assignments of values to the edges of +the product of the tensors at all vertices. -/ +def contraction (N : TensorNetwork R U V E) : R := + ∑ x : E → U, ∏ v, N.tensor v x + +end Contraction + +/-! ### Subdivision by equality tensors -/ + +section Subdivide + +variable [DecidableEq U] [DecidableEq V] [Zero R] [One R] (N : TensorNetwork R U V E) + +/-- The *subdivision* of a tensor network by equality tensors. Each edge `e` becomes a new vertex +`Sum.inr e` and two edges: `Sum.inl e`, from the source of `e`, and `Sum.inr e`, from its target, +both ending at `Sum.inr e`. The new vertex carries the *equality tensor*, one when its two edges +carry the same value and zero otherwise. An old vertex reads the value of `e` on the half of `e` +at its end. -/ +def subdivide : TensorNetwork R U (V ⊕ E) (E ⊕ E) where + src := Sum.elim (fun e => .inl (N.src e)) (fun e => .inl (N.tgt e)) + tgt := Sum.elim .inr .inr + tensor + | .inl v, y => N.tensor v fun e => if N.src e = v then y (.inl e) else y (.inr e) + | .inr e, y => if y (.inl e) = y (.inr e) then 1 else 0 + tensor_congr := by + rintro (v | e) y y' h + · refine N.tensor_congr v _ _ fun e he => ?_ + by_cases hs : N.src e = v + · simp only [hs, ite_true] + exact h (.inl e) (Or.inl (by simp [hs])) + · simp only [hs, ite_false] + exact h (.inr e) (Or.inl (by simp [he.resolve_left hs])) + · simp only + rw [h (.inl e) (Or.inr rfl), h (.inr e) (Or.inr rfl)] + +@[simp] theorem subdivide_src_inl (e : E) : N.subdivide.src (.inl e) = .inl (N.src e) := rfl + +@[simp] theorem subdivide_src_inr (e : E) : N.subdivide.src (.inr e) = .inl (N.tgt e) := rfl + +@[simp] theorem subdivide_tgt_inl (e : E) : N.subdivide.tgt (.inl e) = .inr e := rfl + +@[simp] theorem subdivide_tgt_inr (e : E) : N.subdivide.tgt (.inr e) = .inr e := rfl + +/-- The tensor at an old vertex of the subdivision. -/ +theorem subdivide_tensor_inl (v : V) (y : E ⊕ E → U) : + N.subdivide.tensor (.inl v) y = + N.tensor v fun e => if N.src e = v then y (.inl e) else y (.inr e) := rfl + +/-- The equality tensor at a new vertex of the subdivision. -/ +theorem subdivide_tensor_inr (e : E) (y : E ⊕ E → U) : + N.subdivide.tensor (.inr e) y = if y (.inl e) = y (.inr e) then 1 else 0 := rfl + +/-- **Subdivision is bipartite.** Every edge of the subdivision joins an old vertex to a new one. -/ +theorem isBipartite_subdivide : N.subdivide.IsBipartite := + ⟨Sum.isLeft, fun e => by cases e <;> simp⟩ + +/-- **Subdivision preserves the contraction.** An assignment of the subdivision has nonzero +weight only when the two halves of every edge carry the same value, and such assignments are +exactly the assignments of the original network. -/ +theorem contraction_subdivide {R : Type*} [CommSemiring R] [Fintype U] [Fintype V] [Fintype E] + [DecidableEq E] (N : TensorNetwork R U V E) : N.subdivide.contraction = N.contraction := by + classical + -- Split an assignment of the subdivision into its values `a` on the source halves and `b` on + -- the target halves. + rw [contraction, contraction, ← (Equiv.sumArrowEquivProdArrow E E U).symm.sum_comp, + Fintype.sum_prod_type] + refine Finset.sum_congr rfl fun a _ => ?_ + simp only [Fintype.prod_sum_type, subdivide_tensor_inl, subdivide_tensor_inr, + Equiv.sumArrowEquivProdArrow_symm_apply_inl, Equiv.sumArrowEquivProdArrow_symm_apply_inr] + rw [Finset.sum_eq_single a] + · -- Equal halves: every equality tensor is one, and the old vertices read `a`. + simp only [ite_self, ite_true, Finset.prod_const_one, mul_one] + · -- Unequal halves on some edge `e`: the equality tensor at `e` vanishes. + intro b _ hb + obtain ⟨e, he⟩ := Function.ne_iff.mp hb + exact mul_eq_zero_of_right _ + (Finset.prod_eq_zero (Finset.mem_univ e) (ite_eq_right fun h => he h.symm)) + · exact fun h => absurd (Finset.mem_univ a) h + +/-- The edges at an old vertex of the subdivision are the halves of its old edges at it. -/ +theorem incidenceSet_subdivide_inl (v : V) : + N.subdivide.incidenceSet (.inl v) = + Sum.inl '' {e | N.src e = v} ∪ Sum.inr '' {e | N.tgt e = v} := by + ext (e | e) <;> simp [Multigraph.mem_incidenceSet] + +/-- The edges at a new vertex of the subdivision are the two halves of its edge. -/ +theorem incidenceSet_subdivide_inr (e : E) : + N.subdivide.incidenceSet (.inr e) = {.inl e, .inr e} := by + ext (e' | e') <;> simp [Multigraph.mem_incidenceSet, eq_comm] + +/-- **Subdivision keeps the degree bound.** In a loopless network, the edges at a vertex are those +with it as source and those with it as target, and they are the halves at its old vertex in the +subdivision; a new vertex has degree two. -/ +theorem maxDegreeLE_subdivide [Finite E] {d : ℕ} (hloop : N.Loopless) + (hd : N.MaxDegreeLE d) (h2 : 2 ≤ d) : N.subdivide.MaxDegreeLE d := by + rintro (v | e) + · rw [incidenceSet_subdivide_inl, ncard_union_eq _ (toFinite _) (toFinite _), + ncard_image_of_injective _ Sum.inl_injective, ncard_image_of_injective _ Sum.inr_injective, + ← ncard_union_eq _ (toFinite _) (toFinite _)] + · exact hd v + · exact disjoint_left.mpr fun e hs ht => hloop e (hs.trans ht.symm) + · exact disjoint_left.mpr fun _ ⟨_, _, h⟩ ⟨_, _, h'⟩ => Sum.inl_ne_inr (h.trans h'.symm) + · rw [incidenceSet_subdivide_inr, ncard_pair Sum.inl_ne_inr] + exact h2 + +end Subdivide + +end TensorNetwork + +end Cslib.Circuits.Frontier diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Constraint.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Constraint.lean new file mode 100644 index 0000000000..08c80a6649 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Constraint.lean @@ -0,0 +1,81 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.Network +public import Complexity.Computability.Circuit.Frontier.TensorNetwork +import Mathlib.Algebra.BigOperators.GroupWithZero.Finset +import Mathlib.Algebra.BigOperators.Ring.Finset + +/-! +# Constraint networks as `0`–`1` tensor networks + +A constraint network (`Cslib.Circuits.Frontier.Network`) on a fixed input is a tensor network +whose tensors take only the values zero and one: the tensor at `v` is the indicator of the +constraint at `v` (`Network.toTensorNetwork`). The two share the multigraph, and the locality of +the constraints is the locality of the tensors. The contraction of this tensor network is the +number of satisfying assignments (`Network.contraction_toTensorNetwork`), so over `ℕ` it is +nonzero exactly at the accepted inputs (`Network.mem_accepted_iff_contraction_ne_zero`). + +A general tensor network is thus the weighted form of a constraint network, and the frontier +constraint networks of circuits (`Cslib.Circuits.Frontier.Compiler.network`) are special cases. + +## Main definitions + +* `Cslib.Circuits.Frontier.Network.toTensorNetwork`: the indicator tensor network of a constraint + network on an input. + +## Main results + +* `Cslib.Circuits.Frontier.Network.contraction_toTensorNetwork`: its contraction counts the + satisfying assignments. +* `Cslib.Circuits.Frontier.Network.mem_accepted_iff_contraction_ne_zero`: an input is accepted iff + the contraction over `ℕ` is nonzero. +-/ + +@[expose] public section + +namespace Cslib.Circuits.Frontier.Network + +variable {U ι V E : Type*} (R : Type*) (N : Network U ι V E) + +open Classical in +/-- The `0`–`1` tensor network of a constraint network on the input `x`: the same multigraph, with +the tensor at `v` equal to one where the constraint at `v` holds and to zero elsewhere. -/ +noncomputable def toTensorNetwork [Zero R] [One R] (x : ι → U) : TensorNetwork R U V E where + toMultigraph := N.toMultigraph + tensor v α := if N.Constraint v x α then 1 else 0 + tensor_congr v α β h := by + have hiff : N.Constraint v x α ↔ N.Constraint v x β := + ⟨N.constraint_congr v x x α β (fun _ _ => rfl) h, + N.constraint_congr v x x β α (fun _ _ => rfl) fun e he => (h e he).symm⟩ + simp only [hiff] + +/-- The tensor network of a constraint network has the same multigraph. -/ +@[simp] theorem toMultigraph_toTensorNetwork [Zero R] [One R] (x : ι → U) : + (N.toTensorNetwork R x).toMultigraph = N.toMultigraph := + rfl + +/-- **The contraction counts satisfying assignments.** The contraction of the `0`–`1` tensor +network of a constraint network on the input `x` is the number of assignments of values to the +edges that satisfy every constraint on `x`. -/ +theorem contraction_toTensorNetwork [CommSemiring R] [Fintype U] [Fintype V] [Fintype E] + [DecidableEq E] (x : ι → U) : + (N.toTensorNetwork R x).contraction = ({α | N.Satisfies x α} : Set (E → U)).ncard := by + classical + simp only [TensorNetwork.contraction, toTensorNetwork, Fintype.prod_boole, Finset.sum_boole, + Set.ncard_eq_toFinset_card', Set.toFinset_ofPred, Satisfies] + +/-- Over `ℕ`, the contraction is nonzero exactly at the accepted inputs. -/ +theorem mem_accepted_iff_contraction_ne_zero [Fintype U] [Fintype V] [Fintype E] [DecidableEq E] + (x : ι → U) : x ∈ N.accepted ↔ (N.toTensorNetwork ℕ x).contraction ≠ 0 := by + rw [contraction_toTensorNetwork, Nat.cast_id, Ne, Set.ncard_eq_zero (Set.toFinite _), + ← Ne, ← Set.nonempty_iff_ne_empty] + rfl + +end Cslib.Circuits.Frontier.Network diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Explicit.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Explicit.lean new file mode 100644 index 0000000000..28f5dd3f54 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Explicit.lean @@ -0,0 +1,210 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork +import Mathlib.Algebra.BigOperators.Fin +import Mathlib.Data.List.GetD + +/-! +# Explicit Boolean tensor networks + +An *explicit* Boolean tensor network (`ExplicitTensorNetwork R`) is the data an algorithm reads: +the number of edges, numbered `0, 1, …`, and a list of vertices, each given by + +* its ordered list of *ports*, the numbers of the edges at the vertex, and +* its *table*: the value of its tensor when port `i` carries the bit `bs[i]` is the entry at + position `tableIndex bs`, the number whose binary digit `i` is `bs[i]`. + +So a vertex with `k` ports has a table of `2 ^ k` entries. The contraction of an explicit network +(`ExplicitTensorNetwork.contraction`) sums, over all assignments of bits to the edges, the product +of the table entries at the bits on the ports. It needs no well-formedness: a port naming no edge +reads `false`, and a position past the end of a table has entry zero. + +Its size measures are the number of edges, the number of vertices, the maximum number of ports of +a vertex (`ExplicitTensorNetwork.maxDegree`) and the total length of the tables +(`ExplicitTensorNetwork.tableLength`). + +**Writing a network out** (`TensorNetwork.toExplicit`). Given numberings of the vertices and the +edges of a Boolean tensor network on finite types, list the edges at each vertex in increasing +order and tabulate its tensor on them. The contraction is unchanged +(`TensorNetwork.contraction_toExplicit`). + +## Main definitions + +* `Cslib.Circuits.Frontier.tableIndex`: the position of a list of bits in a table. +* `Cslib.Circuits.Frontier.ExplicitTensorNetwork`: an explicit Boolean tensor network. +* `Cslib.Circuits.Frontier.ExplicitTensorNetwork.entry`, + `Cslib.Circuits.Frontier.ExplicitTensorNetwork.portBits`: table lookup and the bits on a list + of ports. +* `Cslib.Circuits.Frontier.ExplicitTensorNetwork.contraction`: the contraction. +* `Cslib.Circuits.Frontier.ExplicitTensorNetwork.maxDegree`, + `Cslib.Circuits.Frontier.ExplicitTensorNetwork.tableLength`: size measures. +* `Cslib.Circuits.Frontier.TensorNetwork.explicitPorts`, + `Cslib.Circuits.Frontier.TensorNetwork.explicitTable`, + `Cslib.Circuits.Frontier.TensorNetwork.toExplicit`: writing a Boolean tensor network out. + +## Main results + +* `Cslib.Circuits.Frontier.testBit_tableIndex`, `Cslib.Circuits.Frontier.tableIndex_lt`: the + digits and range of a table position. +* `Cslib.Circuits.Frontier.ExplicitTensorNetwork.maxDegree_le_iff`: the degree bound. +* `Cslib.Circuits.Frontier.TensorNetwork.entry_explicitTable`: a written-out table evaluates the + tensor. +* `Cslib.Circuits.Frontier.TensorNetwork.contraction_toExplicit`: writing a network out preserves + its contraction. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Appendix D.1 (p. 53). +-/ + +@[expose] public section + +namespace Cslib.Circuits.Frontier + +/-- The position of a list of bits in a table: the number whose binary digit `i` is `bs[i]`, so +that the bits are read little-endian. -/ +def tableIndex (bs : List Bool) : ℕ := + (BitVec.ofBoolListLE bs).toNat + +/-- Binary digit `i` of the position of `bs` is `bs[i]`, or `false` past the end of `bs`. -/ +@[simp] theorem testBit_tableIndex (bs : List Bool) (i : ℕ) : + (tableIndex bs).testBit i = bs.getD i false := by + rw [tableIndex, BitVec.testBit_toNat, BitVec.getLsbD_ofBoolListLE] + +/-- A list of `k` bits has a position below `2 ^ k`. -/ +theorem tableIndex_lt (bs : List Bool) : tableIndex bs < 2 ^ bs.length := + BitVec.isLt _ + +/-- An *explicit Boolean tensor network* over `R`: the number of edges, numbered from zero, and a +list of vertices. A vertex is given by its ordered list of *ports*, the numbers of its edges, and +its *table*, whose entry at position `tableIndex bs` is the value of its tensor when port `i` +carries the bit `bs[i]`. -/ +structure ExplicitTensorNetwork (R : Type*) where + /-- The number of edges; the edges are numbered `0, …, numEdges - 1`. -/ + numEdges : ℕ + /-- The vertices, each given by its ports and its table. -/ + vertices : List (List ℕ × List R) + +namespace ExplicitTensorNetwork + +variable {R : Type*} (d : ExplicitTensorNetwork R) + +/-- The entry of a table at a list of bits. A position past the end of the table has entry +zero. -/ +def entry [Zero R] (table : List R) (bs : List Bool) : R := + table.getD (tableIndex bs) 0 + +/-- The bits on a list of ports under an assignment of bits to the edges. A port naming no edge +reads `false`. -/ +def portBits (x : Fin d.numEdges → Bool) (ports : List ℕ) : List Bool := + ports.map fun i => if h : i < d.numEdges then x ⟨i, h⟩ else false + +/-- The *contraction* of an explicit network: the sum over all assignments of bits to the edges of +the product, over the vertices, of the table entries at the bits on their ports. -/ +def contraction [CommSemiring R] : R := + ∑ x : Fin d.numEdges → Bool, (d.vertices.map fun v => entry v.2 (d.portBits x v.1)).prod + +/-- The *maximum degree*: the largest number of ports of a vertex, or zero without vertices. -/ +def maxDegree : ℕ := + (d.vertices.map fun v => v.1.length).foldr max 0 + +/-- The total length of the tables. -/ +def tableLength : ℕ := + (d.vertices.map fun v => v.2.length).sum + +private theorem foldr_max_le_iff (l : List ℕ) (k : ℕ) : l.foldr max 0 ≤ k ↔ ∀ n ∈ l, n ≤ k := by + induction l with + | nil => simp + | cons a l ih => simp [ih] + +/-- The maximum degree is at most `k` exactly when every vertex has at most `k` ports. -/ +theorem maxDegree_le_iff {k : ℕ} : d.maxDegree ≤ k ↔ ∀ v ∈ d.vertices, v.1.length ≤ k := by + rw [maxDegree, foldr_max_le_iff] + constructor + · exact fun h v hv => h _ (List.mem_map_of_mem hv) + · rintro h _ hn + obtain ⟨v, hv, rfl⟩ := List.mem_map.mp hn + exact h v hv + +/-- Every vertex has at most `maxDegree` ports. -/ +theorem length_le_maxDegree {v : List ℕ × List R} (hv : v ∈ d.vertices) : + v.1.length ≤ d.maxDegree := + d.maxDegree_le_iff.mp le_rfl v hv + +end ExplicitTensorNetwork + +/-! ### Writing a tensor network out -/ + +namespace TensorNetwork + +variable {R V E : Type*} [DecidableEq V] {n m : ℕ} (N : TensorNetwork R Bool V E) (ev : V ≃ Fin n) + (ee : E ≃ Fin m) + +/-- The ports of a vertex when the edges are numbered by `ee`: the numbers of the edges at the +vertex, in increasing order. -/ +def explicitPorts (v : V) : List (Fin m) := + (List.finRange m).filter fun j => N.src (ee.symm j) = v ∨ N.tgt (ee.symm j) = v + +/-- An edge is a port of `v` exactly when it is incident to `v`. -/ +@[simp] theorem mem_explicitPorts {v : V} {j : Fin m} : + j ∈ N.explicitPorts ee v ↔ N.src (ee.symm j) = v ∨ N.tgt (ee.symm j) = v := by + simp [explicitPorts] + +/-- The table of a vertex: its tensor at each list of bits on its ports, by position. -/ +def explicitTable (v : V) : List R := + (List.range (2 ^ (N.explicitPorts ee v).length)).map fun j => + N.tensor v fun e => j.testBit ((N.explicitPorts ee v).idxOf (ee e)) + +/-- **A written-out table evaluates the tensor.** At the bits that an assignment puts on the ports +of `v`, the table of `v` has the value of the tensor at that assignment. -/ +theorem entry_explicitTable [Zero R] (v : V) (x : Fin m → Bool) : + ExplicitTensorNetwork.entry (N.explicitTable ee v) ((N.explicitPorts ee v).map x) = + N.tensor v (x ∘ ee) := by + set ps := N.explicitPorts ee v + have hlt : tableIndex (ps.map x) < (N.explicitTable ee v).length := by + simpa [explicitTable, ps] using tableIndex_lt (ps.map x) + rw [ExplicitTensorNetwork.entry, List.getD_eq_getElem _ _ hlt] + simp only [explicitTable, List.getElem_map, List.getElem_range] + refine N.tensor_congr v _ _ fun e he => ?_ + have hmem : ee e ∈ ps := (N.mem_explicitPorts ee).mpr (by simpa using he) + have hidx := List.idxOf_lt_length_of_mem hmem + rw [testBit_tableIndex, List.getD_eq_getElem _ _ (by simpa using hidx), List.getElem_map, + List.getElem_idxOf hidx, Function.comp_apply] + +/-- A Boolean tensor network on finite types written out as an explicit network, numbering the +vertices by `ev` and the edges by `ee`: vertex `i` is `ev.symm i`, with its ports in increasing +order and its table. -/ +def toExplicit : ExplicitTensorNetwork R where + numEdges := m + vertices := List.ofFn fun i => + ((N.explicitPorts ee (ev.symm i)).map Fin.val, N.explicitTable ee (ev.symm i)) + +/-- **Writing a network out preserves its contraction.** -/ +theorem contraction_toExplicit [CommSemiring R] [Fintype V] [Fintype E] [DecidableEq E] : + (N.toExplicit ev ee).contraction = N.contraction := by + rw [ExplicitTensorNetwork.contraction, contraction] + refine Fintype.sum_equiv (ee.symm.arrowCongr (Equiv.refl Bool)) _ _ fun x => ?_ + have hv : (N.toExplicit ev ee).vertices = List.ofFn fun i => + ((N.explicitPorts ee (ev.symm i)).map Fin.val, N.explicitTable ee (ev.symm i)) := rfl + rw [hv, List.map_ofFn, List.prod_ofFn, ← ev.symm.prod_comp] + refine Finset.prod_congr rfl fun i _ => ?_ + -- Every port names an edge, so the bits on the ports are those of the assignment. + have hbits : (N.toExplicit ev ee).portBits x ((N.explicitPorts ee (ev.symm i)).map Fin.val) = + (N.explicitPorts ee (ev.symm i)).map x := by + unfold ExplicitTensorNetwork.portBits + rw [List.map_map] + exact List.map_congr_left fun j _ => dite_eq_left j.isLt + simp only [Function.comp_apply] + rw [hbits, entry_explicitTable] + rfl + +end TensorNetwork + +end Cslib.Circuits.Frontier diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetwork.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetwork.lean new file mode 100644 index 0000000000..5be31a11e9 --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetwork.lean @@ -0,0 +1,116 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit +public import Mathlib.Data.ZMod.Defs + +/-! +# Tests for tensor networks + +Hand-checked contractions of small explicit Boolean tensor networks (two parallel edges, and +four-cycles of equality and negation tensors over `ZMod 8`), a tensor network on finite types +written out and subdivided, and a constraint network counted by its indicator tensor network. +-/ + +@[expose] public section + +namespace ComplexityTests.Computability.Circuit.Frontier.TensorNetwork + +open Cslib.Circuits.Frontier + +/-! ### Explicit networks -/ + +/-- Two vertices joined by two parallel edges `0` and `1`, with the tables `A = (1, 2, 3, 4)` and +`B = (5, 6, 7, 8)`, read at positions `x₀ + 2 x₁`. -/ +def parallel : ExplicitTensorNetwork ℤ := + ⟨2, [([0, 1], [1, 2, 3, 4]), ([0, 1], [5, 6, 7, 8])]⟩ + +/-- By hand: `Z = ∑ᵢ Aᵢ Bᵢ = 1·5 + 2·6 + 3·7 + 4·8 = 70`. -/ +example : parallel.contraction = 70 := by decide + +example : parallel.maxDegree = 2 ∧ parallel.tableLength = 8 := by decide + +/-- The equality tensor on two ports. -/ +def eqTable : List (ZMod 8) := [1, 0, 0, 1] + +/-- The negation tensor on two ports: its two bits differ. -/ +def neTable : List (ZMod 8) := [0, 1, 1, 0] + +/-- A bipartite four-cycle `r₀ - c₀ - r₁ - c₁ - r₀` on the edges `0, 1, 2, 3`, with the table `t` +at `c₁` and equality tensors elsewhere. -/ +def cycle (t : List (ZMod 8)) : ExplicitTensorNetwork (ZMod 8) := + ⟨4, [([3, 0], eqTable), ([0, 1], eqTable), ([1, 2], eqTable), ([2, 3], t)]⟩ + +/-- With equality tensors all around, an assignment contributes exactly when all four bits are +equal: `Z = 2`. -/ +example : (cycle eqTable).contraction = 2 := by decide + +/-- One negation on a cycle forces an odd number of bit changes around it, which is impossible: +`Z = 0`. -/ +example : (cycle neTable).contraction = 0 := by decide + +/-! ### A network on finite types, written out and subdivided -/ + +/-- The table `A` or `B` of `parallel` as a tensor on assignments to `Fin 2`. -/ +def table (v : Fin 2) (x : Fin 2 → Bool) : ℤ := + [[1, 2, 3, 4], [5, 6, 7, 8]][v]!.getD (tableIndex [x 0, x 1]) 0 + +/-- The network `parallel` on `Fin 2` vertices and `Fin 2` edges, both edges from `0` to `1`. +Every edge is at both vertices, so the tensors are trivially local. -/ +def parallelNetwork : TensorNetwork ℤ Bool (Fin 2) (Fin 2) where + src _ := 0 + tgt _ := 1 + tensor := table + tensor_congr v x y h := by + have hxy : x = y := funext fun e => h e (by fin_cases v <;> simp) + rw [hxy] + +/-- Writing the network out gives `parallel` up to the order of its data, and the same +contraction. -/ +example : parallelNetwork.contraction = 70 := by + rw [← parallelNetwork.contraction_toExplicit (Equiv.refl _) (Equiv.refl _)] + decide + +/-- Subdividing both edges by equality tensors gives a bipartite network with the same +contraction and degree bound. -/ +example : parallelNetwork.subdivide.contraction = 70 ∧ parallelNetwork.subdivide.IsBipartite ∧ + parallelNetwork.subdivide.MaxDegreeLE 2 := by + refine ⟨?_, parallelNetwork.isBipartite_subdivide, + parallelNetwork.maxDegreeLE_subdivide (fun e => by simp [parallelNetwork]) (fun v => ?_) + le_rfl⟩ + · rw [TensorNetwork.contraction_subdivide, + ← parallelNetwork.contraction_toExplicit (Equiv.refl _) (Equiv.refl _)] + decide + · exact (Set.ncard_le_card _).trans (by simp) + +/-! ### Constraint networks -/ + +/-- Two vertices joined by two parallel edges, each requiring its two edges to agree. -/ +def agree : Network Bool Empty (Fin 2) (Fin 2) where + src _ := 0 + tgt _ := 1 + site := Empty.elim + Constraint _ _ α := α 0 = α 1 + constraint_congr v _ _ α β _ h hα := by + have h' : ∀ e, α e = β e := fun e => h e (by fin_cases v <;> simp) + rw [← h' 0, ← h' 1] + exact hα + +/-- The indicator tensor network of `agree` counts its two satisfying assignments. -/ +example : (agree.toTensorNetwork ℕ Empty.elim).contraction = 2 := by + rw [Network.contraction_toTensorNetwork, Nat.cast_id] + have hsat : {α | agree.Satisfies Empty.elim α} = {fun _ => false, fun _ => true} := by + ext α + simp only [Network.Satisfies, agree, Set.mem_ofPred_eq, forall_const, Set.mem_insert_iff, + Set.mem_singleton_iff, funext_iff, Fin.forall_fin_two] + cases α 0 <;> cases α 1 <;> simp + rw [hsat, Set.ncard_pair (fun h => Bool.false_ne_true (congrFun h 0))] + +end ComplexityTests.Computability.Circuit.Frontier.TensorNetwork From ae638974f0c2f9120d89832435883fcb9cdd17ae Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 17:52:13 -0400 Subject: [PATCH 002/129] feat(circuit/frontier): deformed contraction and the Cauchy kernel Start of Theorem D.1 of OpenAI's ETH manuscript (section D.4.3, (D.7)-(D.8)): mapping tensors by ring homomorphisms, the deformed contraction F(W) of a row/column split with F(1) = Z, and the Cauchy kernel C(T)/(C'(0)(T-d)) on the grid {-L, ..., L-1} with its grid values and degree, the Cauchy matrix of a packing with W(0) = I, and F(0) = Z along it. Tracking: #1538 (T2.1), #1833. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Frontier/TensorNetwork/Deformation.lean | 299 ++++++++++++++++++ .../Frontier/TensorNetworkDeformation.lean | 57 ++++ 3 files changed, 357 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/Deformation.lean create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkDeformation.lean diff --git a/Complexity.lean b/Complexity.lean index 3e53d5172a..8ea2c2b140 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -859,6 +859,7 @@ public import Complexity.Computability.Circuit.Frontier.Reduction public import Complexity.Computability.Circuit.Frontier.Sweep public import Complexity.Computability.Circuit.Frontier.TensorNetwork public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness public import Complexity.Computability.Circuit.Frontier.Unconditional diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Deformation.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Deformation.lean new file mode 100644 index 0000000000..c5aa9c025f --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Deformation.lean @@ -0,0 +1,299 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork +public import Mathlib.Data.Matrix.Mul +public import Mathlib.LinearAlgebra.Lagrange +public import Mathlib.Order.Interval.Finset.Defs +public import Mathlib.Data.Int.Interval +import Mathlib.Algebra.BigOperators.Ring.Finset +import Mathlib.Algebra.Polynomial.BigOperators + +/-! +# Deformed contractions and the Cauchy kernel + +This file starts the proof of the Boolean tensor contraction theorem of [OpenAI2026ETH] +(Theorem D.1, §D.1, p. 53) with the deformation of §D.4.3 (p. 59). + +**Deformed contraction** (`TensorNetwork.deformedContraction`). Split the vertices of a Boolean +tensor network into *rows* (`side v = true`) and *columns*. Give each edge `e` a *row bit* `Y e`, +read by the row tensors, and a *column bit* `X e`, read by the column tensors. For a matrix `W` +indexed by the edges, the deformed contraction is + + `F(W) = ∑ X Y, A(Y) B(X) ∏ e, l_{Y e}((W X) e)`, + +where `A` and `B` are the products of the row and column tensors, `l_false z = 1 - z` and +`l_true z = z` (the manuscript's (D.8), with `l₀`, `l₁`). At the identity matrix the product of +the affine factors is the indicator of `X = Y`, so `F(1)` is the contraction +(`TensorNetwork.deformedContraction_one`). Deforming `W` away from the identity lets the +interactions between groups of ports be expanded separately; that expansion is later work. + +**The Cauchy kernel** (`cauchyKernel`). On the grid `S = {-L, …, L - 1}`, with +`C(T) = ∏_{t ∈ S} (T - t)`, the kernel at `d ∈ S` is the polynomial `C(T) / (C'(0) (T - d))` of +(D.7). It is one at `T = 0` when `d = 0` and vanishes at every other grid point +(`eval_zero_cauchyKernel`, `eval_cauchyKernel_of_ne`). The *Cauchy matrix* of a packing of the +edges (`cauchyMatrix`), with entry `cauchyKernel L (slot e' - slot e)` between edges of the same +packet and zero between packets, is therefore the identity at `T = 0` when every edge has its own +slot in its packet (`cauchyMatrix_eval_zero`), and the deformed contraction along it evaluates to +the contraction at zero (`eval_zero_deformedContraction_cauchyMatrix`). + +## Main definitions + +* `Cslib.Circuits.Frontier.TensorNetwork.map`: a tensor network with its tensors mapped by a + ring homomorphism. +* `Cslib.Circuits.Frontier.TensorNetwork.affineFactor`: the affine factors `l_b`. +* `Cslib.Circuits.Frontier.TensorNetwork.deformedContraction`: the deformed contraction `F(W)`. +* `Cslib.Circuits.Frontier.cauchyGrid`, `Cslib.Circuits.Frontier.cauchyKernel`, + `Cslib.Circuits.Frontier.cauchyMatrix`: the grid, the Cauchy kernel and the Cauchy matrix of a + packing. + +## Main results + +* `Cslib.Circuits.Frontier.TensorNetwork.contraction_map`: contraction commutes with ring + homomorphisms, e.g. reduction of integer entries modulo `2 ^ P`. +* `Cslib.Circuits.Frontier.TensorNetwork.deformedContraction_one`: `F(1) = Z`. +* `Cslib.Circuits.Frontier.TensorNetwork.deformedContraction_map`: deformed contraction commutes + with ring homomorphisms. +* `Cslib.Circuits.Frontier.eval_zero_cauchyKernel`, + `Cslib.Circuits.Frontier.eval_cauchyKernel_of_ne`: values of the kernel on the grid. +* `Cslib.Circuits.Frontier.natDegree_cauchyKernel`: its degree `2 L - 1`. +* `Cslib.Circuits.Frontier.cauchyMatrix_eval_zero`: the Cauchy matrix is the identity at zero. +* `Cslib.Circuits.Frontier.TensorNetwork.eval_zero_deformedContraction_cauchyMatrix`: the + polynomial `F(T)` along the Cauchy matrix has value `Z` at `T = 0`. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.3, (D.7)–(D.8), + p. 59. +-/ + +@[expose] public section + +namespace Cslib.Circuits.Frontier + +open Polynomial + +namespace TensorNetwork + +variable {R S U V E : Type*} + +/-! ### Mapping the tensors -/ + +section Map + +variable [CommSemiring R] [CommSemiring S] (f : R →+* S) + +/-- The tensor network with every tensor entry mapped by the ring homomorphism `f`. -/ +def map (N : TensorNetwork R U V E) : TensorNetwork S U V E where + toMultigraph := N.toMultigraph + tensor v x := f (N.tensor v x) + tensor_congr v x y h := by rw [N.tensor_congr v x y h] + +@[simp] theorem map_tensor (N : TensorNetwork R U V E) (v : V) (x : E → U) : + (N.map f).tensor v x = f (N.tensor v x) := + rfl + +@[simp] theorem toMultigraph_map (N : TensorNetwork R U V E) : + (N.map f).toMultigraph = N.toMultigraph := + rfl + +/-- **Contraction commutes with ring homomorphisms.** For instance, contracting integer tensors +and reducing modulo `2 ^ P` is contracting the reduced tensors. -/ +theorem contraction_map [Fintype U] [Fintype V] [Fintype E] [DecidableEq E] + (N : TensorNetwork R U V E) : (N.map f).contraction = f N.contraction := by + simp only [contraction, map_tensor, map_sum, map_prod] + +end Map + +/-! ### Deformed contraction -/ + +section Deformed + +variable [CommRing R] [Fintype V] [Fintype E] [DecidableEq E] + +/-- The affine factor `l_b`: `l_b z = z` for `b = true` and `l_b z = 1 - z` for `b = false`. At a +bit `z`, it is the indicator of `z = b`. -/ +def affineFactor (b : Bool) (z : R) : R := + if b then z else 1 - z + +/-- At a bit, the affine factor is the indicator of equality. -/ +theorem affineFactor_bit (b c : Bool) : + affineFactor b (if c then (1 : R) else 0) = if b = c then 1 else 0 := by + cases b <;> cases c <;> simp [affineFactor] + +/-- The *deformed contraction* of a Boolean tensor network whose vertices are split into rows +(`side v = true`) and columns, along a matrix `W` indexed by the edges: every edge carries a row +bit, read by the row tensors, and a column bit, read by the column tensors, joined through the +affine factors of `W`. -/ +def deformedContraction (N : TensorNetwork R Bool V E) (side : V → Bool) (W : Matrix E E R) : R := + ∑ X : E → Bool, ∑ Y : E → Bool, + (∏ v ∈ Finset.univ.filter (side · = true), N.tensor v Y) * + (∏ v ∈ Finset.univ.filter (side · = false), N.tensor v X) * + ∏ e, affineFactor (Y e) (W.mulVec (fun e' => if X e' then 1 else 0) e) + +/-- **The deformed contraction at the identity is the contraction.** The affine factors of the +identity matrix force the row bits to equal the column bits. -/ +theorem deformedContraction_one (N : TensorNetwork R Bool V E) (side : V → Bool) : + N.deformedContraction side 1 = N.contraction := by + classical + simp only [deformedContraction, Matrix.one_mulVec, affineFactor_bit, Fintype.prod_boole, + ← funext_iff, mul_ite, mul_one, mul_zero, Finset.sum_ite_eq', Finset.mem_univ, ite_true] + refine Finset.sum_congr rfl fun X _ => ?_ + have h := Finset.prod_filter_mul_prod_filter_not Finset.univ (fun v => side v = true) + (fun v => N.tensor v X) + simp only [Bool.not_eq_true] at h + exact h + +/-- Deformed contraction commutes with ring homomorphisms. -/ +theorem deformedContraction_map {S : Type*} [CommRing S] (f : R →+* S) + (N : TensorNetwork R Bool V E) (side : V → Bool) (W : Matrix E E R) : + (N.map f).deformedContraction side (W.map f) = f (N.deformedContraction side W) := by + simp only [deformedContraction, map_sum, map_mul, map_prod, map_tensor] + refine Finset.sum_congr rfl fun X _ => Finset.sum_congr rfl fun Y _ => ?_ + congr 1 + refine Finset.prod_congr rfl fun e _ => ?_ + have hv : (W.map f).mulVec (fun e' => if X e' then (1 : S) else 0) = + fun e => f (W.mulVec (fun e' => if X e' then (1 : R) else 0) e) := by + ext e + simp only [Matrix.mulVec, dotProduct, Matrix.map_apply, map_sum, map_mul, apply_ite f, + map_one, map_zero] + rw [hv] + cases Y e <;> simp [affineFactor] + +end Deformed + +end TensorNetwork + +/-! ### The Cauchy kernel -/ + +/-- The interpolation grid `S = {-L, …, L - 1}` of a packet with `L` slots. -/ +def cauchyGrid (L : ℕ) : Finset ℤ := + Finset.Ico (-L : ℤ) L + +theorem mem_cauchyGrid {L : ℕ} {t : ℤ} : t ∈ cauchyGrid L ↔ -(L : ℤ) ≤ t ∧ t < L := + Finset.mem_Ico + +/-- The difference of two slots of a packet lies on the grid. -/ +theorem sub_mem_cauchyGrid {L : ℕ} {x y : ℕ} (hx : x < L) (hy : y < L) : + (y : ℤ) - x ∈ cauchyGrid L := by + rw [mem_cauchyGrid] + omega + +/-- The *Cauchy kernel* at `d` on the grid of `L` slots: `C(T) / (C'(0) (T - d))`, where +`C(T) = ∏_{t ∈ S} (T - t)`. For `d ∈ S` it is the polynomial `C'(0)⁻¹ ∏_{t ∈ S, t ≠ d} (T - t)` +of degree `2 L - 1`; here `C'(0) = ∏_{t ∈ S, t ≠ 0} (0 - t)`. -/ +noncomputable def cauchyKernel (L : ℕ) (d : ℤ) : ℚ[X] := + C ((Lagrange.nodal ((cauchyGrid L).erase 0) (Int.cast : ℤ → ℚ)).eval 0)⁻¹ * + Lagrange.nodal ((cauchyGrid L).erase d) (Int.cast : ℤ → ℚ) + +/-- The normalization is `C'(0)`, the derivative of `C(T) = ∏_{t ∈ S} (T - t)` at zero. -/ +theorem eval_zero_nodal_erase_zero {L : ℕ} (hL : 0 < L) : + (Lagrange.nodal ((cauchyGrid L).erase 0) (Int.cast : ℤ → ℚ)).eval 0 = + (derivative (Lagrange.nodal (cauchyGrid L) (Int.cast : ℤ → ℚ))).eval 0 := by + have h0 : (0 : ℤ) ∈ cauchyGrid L := by rw [mem_cauchyGrid]; omega + have := Lagrange.eval_nodal_derivative_eval_node_eq (v := (Int.cast : ℤ → ℚ)) h0 + simpa using this.symm + +/-- `C'(0)` is nonzero: the grid has distinct points. -/ +theorem eval_zero_nodal_erase_zero_ne_zero (L : ℕ) : + (Lagrange.nodal ((cauchyGrid L).erase 0) (Int.cast : ℤ → ℚ)).eval 0 ≠ 0 := by + rw [Lagrange.eval_nodal, Finset.prod_ne_zero_iff] + intro t ht + have : t ≠ 0 := Finset.ne_of_mem_erase ht + simpa [sub_eq_zero, eq_comm] using this + +/-- On the grid, the kernel has degree `2 L - 1`, so a deformed contraction along a Cauchy matrix +has degree at most `M (2 L - 1)` in `T` for `M` edges. -/ +theorem natDegree_cauchyKernel {L : ℕ} {d : ℤ} (hd : d ∈ cauchyGrid L) : + (cauchyKernel L d).natDegree = 2 * L - 1 := by + rw [cauchyKernel, natDegree_C_mul (inv_ne_zero (eval_zero_nodal_erase_zero_ne_zero L)), + Lagrange.natDegree_nodal, Finset.card_erase_of_mem hd, cauchyGrid, Int.card_Ico] + omega + +/-- **The kernel at zero.** The kernel at `d = 0` is one at `T = 0`. -/ +theorem eval_zero_cauchyKernel_zero (L : ℕ) : (cauchyKernel L 0).eval 0 = 1 := by + rw [cauchyKernel, eval_mul, eval_C] + exact inv_mul_cancel₀ (eval_zero_nodal_erase_zero_ne_zero L) + +/-- **The kernel vanishes at the other grid points.** The kernel at `d` vanishes at every grid +point `t ≠ d`. -/ +theorem eval_cauchyKernel_of_ne {L : ℕ} {d t : ℤ} (ht : t ∈ cauchyGrid L) (htd : t ≠ d) : + (cauchyKernel L d).eval (t : ℚ) = 0 := by + rw [cauchyKernel, eval_mul, Lagrange.eval_nodal_at_node (Finset.mem_erase.mpr ⟨htd, ht⟩), + mul_zero] + +/-- The values of the kernel at `T = 0`: one at `d = 0`, and zero at every `d ≠ 0`, because then +`T = 0` is a root of one of its factors. -/ +theorem eval_zero_cauchyKernel {L : ℕ} (hL : 0 < L) (d : ℤ) : + (cauchyKernel L d).eval 0 = if d = 0 then 1 else 0 := by + split_ifs with h + · rw [h, eval_zero_cauchyKernel_zero] + · have h0 : (0 : ℤ) ∈ cauchyGrid L := by rw [mem_cauchyGrid]; omega + simpa using eval_cauchyKernel_of_ne h0 (Ne.symm h) + +/-- The *Cauchy matrix* of a packing of the edges into packets of `L` slots: between edges `e` +and `e'` of the same packet, the Cauchy kernel at the difference `slot e' - slot e` of their +slots; zero between different packets (the manuscript's (D.7)). -/ +noncomputable def cauchyMatrix {E P : Type*} [DecidableEq P] (L : ℕ) (packet : E → P) + (slot : E → ℕ) : Matrix E E ℚ[X] := + fun e e' => if packet e = packet e' then cauchyKernel L ((slot e' : ℤ) - slot e) else 0 + +/-- **The Cauchy matrix is the identity at zero** when every edge has its own slot in its packet, +below `L`. -/ +theorem cauchyMatrix_eval_zero {E P : Type*} [DecidableEq E] [DecidableEq P] {L : ℕ} + {packet : E → P} {slot : E → ℕ} (hslot : ∀ e, slot e < L) + (hinj : ∀ e e', packet e = packet e' → slot e = slot e' → e = e') : + (cauchyMatrix L packet slot).map (eval 0) = 1 := by + ext e e' + have hL : 0 < L := lt_of_le_of_lt (Nat.zero_le _) (hslot e) + simp only [Matrix.map_apply, cauchyMatrix, Matrix.one_apply] + split_ifs with hp he he + · subst he; simp [eval_zero_cauchyKernel_zero] + · rw [eval_zero_cauchyKernel hL, ite_eq_right] + intro h + exact he (hinj e e' hp (by omega)) + · subst he; exact absurd rfl hp + · exact eval_zero + +namespace TensorNetwork + +variable {R V E P : Type*} [CommRing R] [Algebra ℚ R] [Fintype V] [Fintype E] [DecidableEq E] + [DecidableEq P] + +/-- **The deformation along the Cauchy matrix starts at the contraction.** Over a `ℚ`-algebra, +the deformed contraction along the Cauchy matrix of a packing is a polynomial `F(T)` whose value at +`T = 0` is the contraction, when every edge has its own slot in its packet. -/ +theorem eval_zero_deformedContraction_cauchyMatrix (N : TensorNetwork R Bool V E) + (side : V → Bool) {L : ℕ} {packet : E → P} {slot : E → ℕ} (hslot : ∀ e, slot e < L) + (hinj : ∀ e e', packet e = packet e' → slot e = slot e' → e = e') : + ((N.map Polynomial.C).deformedContraction side + ((cauchyMatrix L packet slot).map (Polynomial.mapRingHom (algebraMap ℚ R)))).eval 0 = + N.contraction := by + have h := deformedContraction_map (Polynomial.evalRingHom (0 : R)) (N.map Polynomial.C) side + ((cauchyMatrix L packet slot).map (Polynomial.mapRingHom (algebraMap ℚ R))) + rw [coe_evalRingHom] at h + rw [← h] + have hN : (N.map Polynomial.C).map (Polynomial.evalRingHom (0 : R)) = N := by + cases N + simp [map] + have hW : ((cauchyMatrix L packet slot).map (Polynomial.mapRingHom (algebraMap ℚ R))).map + (eval 0) = 1 := by + rw [Matrix.map_map] + have : (eval (0 : R) ∘ ⇑(Polynomial.mapRingHom (algebraMap ℚ R))) = + (algebraMap ℚ R) ∘ (eval 0) := by + ext p + simp only [Function.comp_apply, coe_mapRingHom, eval_map, eval₂_at_zero, + coeff_zero_eq_eval_zero] + rw [this, ← Matrix.map_map, cauchyMatrix_eval_zero hslot hinj, + Matrix.map_one _ (map_zero _) (map_one _)] + rw [hN, hW, deformedContraction_one] + +end TensorNetwork + +end Cslib.Circuits.Frontier diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkDeformation.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkDeformation.lean new file mode 100644 index 0000000000..1f1322b399 --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkDeformation.lean @@ -0,0 +1,57 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation + +/-! +# Tests for deformed contractions and the Cauchy kernel + +A one-edge network deformed along a `1 × 1` matrix, checked by hand against the expansion of +`F(W)`, and the Cauchy kernel of a packet with one slot, `C(T) = T (T + 1)`. +-/ + +@[expose] public section + +namespace ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.Deformation + +open Cslib.Circuits.Frontier Polynomial + +/-- A row vertex `0` and a column vertex `1` joined by one edge, with tensors `A = (1, 2)` and +`B = (3, 5)` on its bit. -/ +def oneEdge : TensorNetwork ℤ Bool (Fin 2) (Fin 1) where + src _ := 0 + tgt _ := 1 + tensor v x := if v = 0 then (if x 0 then 2 else 1) else (if x 0 then 5 else 3) + tensor_congr v x y h := by rw [h 0 (by fin_cases v <;> simp)] + +/-- The rows: vertex `0`. -/ +def side (v : Fin 2) : Bool := v = 0 + +/-- By hand, `F(w) = A₀B₀ + A₀B₁ (1 - w) + A₁B₁ w`; at `w = 2` this is `3 - 5 + 20 = 18`. -/ +example : oneEdge.deformedContraction side (fun _ _ => 2) = 18 := by decide + +/-- At `w = 1` it is the contraction `A₀B₀ + A₁B₁ = 13`. -/ +example : oneEdge.deformedContraction side 1 = 13 ∧ oneEdge.contraction = 13 := by decide + +/-- Contraction commutes with reduction modulo `8`: `13 ≡ 5`. -/ +example : (oneEdge.map (Int.castRingHom (ZMod 8))).contraction = 5 := by + rw [TensorNetwork.contraction_map] + decide + +/-- With one slot the grid is `{-1, 0}`, `C'(0) = 1`, and the kernel at `-1` is `T`, whose value +at `T = 1` is `1`; the kernel at `0` is `T + 1`. -/ +example : (cauchyKernel 1 (-1)).eval 1 = 1 ∧ (cauchyKernel 1 0).eval 1 = 2 := by + have hS : cauchyGrid 1 = {-1, 0} := by decide + have h0 : ({-1, 0} : Finset ℤ).erase 0 = {-1} := by decide + have h1 : ({-1, 0} : Finset ℤ).erase (-1) = {0} := by decide + simp only [cauchyKernel, hS, h0, h1, eval_mul, eval_C, Lagrange.eval_nodal, + Finset.prod_singleton] + norm_num + +end ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.Deformation From b06c2112f2badd9381e157200e2880137e5a13db Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 18:19:33 -0400 Subject: [PATCH 003/129] refactor(circuit/frontier): index explicit tables by BitString.lsbValue Address the #1844 review: replace the duplicate public `tableIndex` encoding and its lemmas by the existing `Cslib.BitString.lsbValue` and `lsbValue_lt`. Add the missing digit lemma `BitString.testBit_lsbValue` next to `lsbValue` in `BitString/Counter.lean`, and update the table lookup, its proof and the tests. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/Explicit.lean | 37 ++++++------------- .../Foundations/Data/BitString/Counter.lean | 11 ++++++ .../Circuit/Frontier/TensorNetwork.lean | 2 +- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Explicit.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Explicit.lean index 28f5dd3f54..051a95b90a 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Explicit.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Explicit.lean @@ -8,6 +8,7 @@ module -- upstream: candidate public import Complexity.Computability.Circuit.Frontier.TensorNetwork +public import Complexity.Foundations.Data.BitString.Counter import Mathlib.Algebra.BigOperators.Fin import Mathlib.Data.List.GetD @@ -19,7 +20,8 @@ the number of edges, numbered `0, 1, …`, and a list of vertices, each given by * its ordered list of *ports*, the numbers of the edges at the vertex, and * its *table*: the value of its tensor when port `i` carries the bit `bs[i]` is the entry at - position `tableIndex bs`, the number whose binary digit `i` is `bs[i]`. + position `BitString.lsbValue bs`, the number whose binary digit `i` is `bs[i]` + (`BitString.testBit_lsbValue`). So a vertex with `k` ports has a table of `2 ^ k` entries. The contraction of an explicit network (`ExplicitTensorNetwork.contraction`) sums, over all assignments of bits to the edges, the product @@ -37,7 +39,6 @@ order and tabulate its tensor on them. The contraction is unchanged ## Main definitions -* `Cslib.Circuits.Frontier.tableIndex`: the position of a list of bits in a table. * `Cslib.Circuits.Frontier.ExplicitTensorNetwork`: an explicit Boolean tensor network. * `Cslib.Circuits.Frontier.ExplicitTensorNetwork.entry`, `Cslib.Circuits.Frontier.ExplicitTensorNetwork.portBits`: table lookup and the bits on a list @@ -51,8 +52,6 @@ order and tabulate its tensor on them. The contraction is unchanged ## Main results -* `Cslib.Circuits.Frontier.testBit_tableIndex`, `Cslib.Circuits.Frontier.tableIndex_lt`: the - digits and range of a table position. * `Cslib.Circuits.Frontier.ExplicitTensorNetwork.maxDegree_le_iff`: the degree bound. * `Cslib.Circuits.Frontier.TensorNetwork.entry_explicitTable`: a written-out table evaluates the tensor. @@ -68,24 +67,10 @@ order and tabulate its tensor on them. The contraction is unchanged namespace Cslib.Circuits.Frontier -/-- The position of a list of bits in a table: the number whose binary digit `i` is `bs[i]`, so -that the bits are read little-endian. -/ -def tableIndex (bs : List Bool) : ℕ := - (BitVec.ofBoolListLE bs).toNat - -/-- Binary digit `i` of the position of `bs` is `bs[i]`, or `false` past the end of `bs`. -/ -@[simp] theorem testBit_tableIndex (bs : List Bool) (i : ℕ) : - (tableIndex bs).testBit i = bs.getD i false := by - rw [tableIndex, BitVec.testBit_toNat, BitVec.getLsbD_ofBoolListLE] - -/-- A list of `k` bits has a position below `2 ^ k`. -/ -theorem tableIndex_lt (bs : List Bool) : tableIndex bs < 2 ^ bs.length := - BitVec.isLt _ - /-- An *explicit Boolean tensor network* over `R`: the number of edges, numbered from zero, and a list of vertices. A vertex is given by its ordered list of *ports*, the numbers of its edges, and -its *table*, whose entry at position `tableIndex bs` is the value of its tensor when port `i` -carries the bit `bs[i]`. -/ +its *table*, whose entry at position `BitString.lsbValue bs` is the value of its tensor when +port `i` carries the bit `bs[i]`. -/ structure ExplicitTensorNetwork (R : Type*) where /-- The number of edges; the edges are numbered `0, …, numEdges - 1`. -/ numEdges : ℕ @@ -96,10 +81,10 @@ namespace ExplicitTensorNetwork variable {R : Type*} (d : ExplicitTensorNetwork R) -/-- The entry of a table at a list of bits. A position past the end of the table has entry -zero. -/ +/-- The entry of a table at a list of bits, read little-endian (`BitString.lsbValue`). A position +past the end of the table has entry zero. -/ def entry [Zero R] (table : List R) (bs : List Bool) : R := - table.getD (tableIndex bs) 0 + table.getD (BitString.lsbValue bs) 0 /-- The bits on a list of ports under an assignment of bits to the edges. A port naming no edge reads `false`. -/ @@ -168,14 +153,14 @@ theorem entry_explicitTable [Zero R] (v : V) (x : Fin m → Bool) : ExplicitTensorNetwork.entry (N.explicitTable ee v) ((N.explicitPorts ee v).map x) = N.tensor v (x ∘ ee) := by set ps := N.explicitPorts ee v - have hlt : tableIndex (ps.map x) < (N.explicitTable ee v).length := by - simpa [explicitTable, ps] using tableIndex_lt (ps.map x) + have hlt : BitString.lsbValue (ps.map x) < (N.explicitTable ee v).length := by + simpa [explicitTable, ps] using BitString.lsbValue_lt (ps.map x) rw [ExplicitTensorNetwork.entry, List.getD_eq_getElem _ _ hlt] simp only [explicitTable, List.getElem_map, List.getElem_range] refine N.tensor_congr v _ _ fun e he => ?_ have hmem : ee e ∈ ps := (N.mem_explicitPorts ee).mpr (by simpa using he) have hidx := List.idxOf_lt_length_of_mem hmem - rw [testBit_tableIndex, List.getD_eq_getElem _ _ (by simpa using hidx), List.getElem_map, + rw [BitString.testBit_lsbValue, List.getD_eq_getElem _ _ (by simpa using hidx), List.getElem_map, List.getElem_idxOf hidx, Function.comp_apply] /-- A Boolean tensor network on finite types written out as an explicit network, numbering the diff --git a/Complexity/Foundations/Data/BitString/Counter.lean b/Complexity/Foundations/Data/BitString/Counter.lean index d13e8137f0..40319f05b8 100644 --- a/Complexity/Foundations/Data/BitString/Counter.lean +++ b/Complexity/Foundations/Data/BitString/Counter.lean @@ -35,6 +35,7 @@ their candidates in this way, storing only the current one. * `Cslib.BitString.iterate_increment`, `Cslib.BitString.iterate_increment_lsbValue`: counting from zero enumerates the words of a length. * `Cslib.BitString.lsbValue_eq_foldr_bit`: `lsbValue` is a right fold of `Nat.bit`. +* `Cslib.BitString.testBit_lsbValue`: binary digit `i` of `lsbValue w` is the bit `w[i]`. -/ @[expose] public section @@ -154,4 +155,14 @@ theorem lsbValue_eq_foldr_bit (w : List Bool) : lsbValue w = w.foldr Nat.bit 0 : | nil => rfl | cons b w ih => rw [lsbValue, List.foldr_cons, ← ih, Nat.bit_val, Nat.add_comm] +/-- Binary digit `i` of the value of a word is its bit `i`, or `false` past its end. -/ +@[simp] theorem testBit_lsbValue : ∀ (w : List Bool) (i : ℕ), + (lsbValue w).testBit i = w.getD i false + | [], _ => by simp [lsbValue] + | b :: w, i => by + rw [lsbValue_eq_foldr_bit, List.foldr_cons, ← lsbValue_eq_foldr_bit] + cases i with + | zero => simp + | succ i => simp [Nat.testBit_bit_succ, testBit_lsbValue w i] + end Cslib.BitString diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetwork.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetwork.lean index 5be31a11e9..27d867afd5 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetwork.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetwork.lean @@ -60,7 +60,7 @@ example : (cycle neTable).contraction = 0 := by decide /-- The table `A` or `B` of `parallel` as a tensor on assignments to `Fin 2`. -/ def table (v : Fin 2) (x : Fin 2 → Bool) : ℤ := - [[1, 2, 3, 4], [5, 6, 7, 8]][v]!.getD (tableIndex [x 0, x 1]) 0 + [[1, 2, 3, 4], [5, 6, 7, 8]][v]!.getD (Cslib.BitString.lsbValue [x 0, x 1]) 0 /-- The network `parallel` on `Fin 2` vertices and `Fin 2` edges, both edges from `0` to `1`. Every edge is at both vertices, so the tensors are trivially local. -/ From fa3aa4aa09b498b2e78b1d7b77c23420be455a66 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 18:26:26 -0400 Subject: [PATCH 004/129] feat(foundations): Galois rings, ring Lagrange interpolation and the p-adic valuation of Q[X]/(f) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit T2.2 of #1538 (Theorem D.1 of OpenAI2026ETH, §D.4.1, pp. 57-58). - GaloisRing p n f = (Z/p^n)[X]/(f): local with maximal ideal (p), residue field F_p[X]/(f) = GaloisField p (deg f), units = nonzero mod p; distinct digit lifts differ by units, so ring Lagrange interpolation holds there. - Lagrange.ringInterpolate: Lagrange interpolation over a commutative ring at nodes with unit differences (Mathlib's Lagrange.interpolate needs a field). - AdjoinRoot.padicValuation: for f monic and irreducible mod p, the largest p-adic valuation of a coordinate is a valuation of Q[X]/(f); its valuation ring is Z_(p)[X]/(f), whose elements of valuation one are units. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 3 + .../Data/Polynomial/RingLagrange.lean | 154 ++++++++ .../Padics/AdjoinRootValuation.lean | 303 +++++++++++++++ .../Foundations/RingTheory/GaloisRing.lean | 351 ++++++++++++++++++ .../Data/Polynomial/RingLagrange.lean | 54 +++ .../Padics/AdjoinRootValuation.lean | 98 +++++ .../Foundations/RingTheory/GaloisRing.lean | 111 ++++++ 7 files changed, 1074 insertions(+) create mode 100644 Complexity/Foundations/Data/Polynomial/RingLagrange.lean create mode 100644 Complexity/Foundations/NumberTheory/Padics/AdjoinRootValuation.lean create mode 100644 Complexity/Foundations/RingTheory/GaloisRing.lean create mode 100644 ComplexityTests/Foundations/Data/Polynomial/RingLagrange.lean create mode 100644 ComplexityTests/Foundations/NumberTheory/Padics/AdjoinRootValuation.lean create mode 100644 ComplexityTests/Foundations/RingTheory/GaloisRing.lean diff --git a/Complexity.lean b/Complexity.lean index 1cd97a4651..7ae579ac43 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2679,6 +2679,7 @@ public import Complexity.Foundations.Data.Polynomial.CoeffNorm1 public import Complexity.Foundations.Data.Polynomial.HermiteInterpolation public import Complexity.Foundations.Data.Polynomial.ParityAmplifier public import Complexity.Foundations.Data.Polynomial.ParityAmplifierDegree +public import Complexity.Foundations.Data.Polynomial.RingLagrange public import Complexity.Foundations.Data.Polynomial.SignQueries public import Complexity.Foundations.Data.Polynomial.StagedJetArithmetic public import Complexity.Foundations.Data.Rat.Basic @@ -2748,6 +2749,7 @@ public import Complexity.Foundations.NumberTheory.AKS.Criterion public import Complexity.Foundations.NumberTheory.AKS.Introspective public import Complexity.Foundations.NumberTheory.AKS.SmallR public import Complexity.Foundations.NumberTheory.Harmonic +public import Complexity.Foundations.NumberTheory.Padics.AdjoinRootValuation public import Complexity.Foundations.NumberTheory.PrimeCounting public import Complexity.Foundations.NumberTheory.PrimeWindow public import Complexity.Foundations.NumberTheory.SqrtPrimeWindow @@ -2777,6 +2779,7 @@ public import Complexity.Foundations.Relation.InductiveCounting public import Complexity.Foundations.Relation.RelatesWithinSteps public import Complexity.Foundations.RingTheory.CyclicIntRing public import Complexity.Foundations.RingTheory.CyclicIntRing.Coefficients +public import Complexity.Foundations.RingTheory.GaloisRing public import Complexity.Foundations.Semantics.Stateful.Amortized public import Complexity.Foundations.Semantics.Stateful.Basic public import Complexity.Foundations.Semantics.Stateful.Composition diff --git a/Complexity/Foundations/Data/Polynomial/RingLagrange.lean b/Complexity/Foundations/Data/Polynomial/RingLagrange.lean new file mode 100644 index 0000000000..2ee13b0100 --- /dev/null +++ b/Complexity/Foundations/Data/Polynomial/RingLagrange.lean @@ -0,0 +1,154 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate (Mathlib, `Mathlib/LinearAlgebra/Lagrange.lean`) + +public import Complexity.Init +public import Mathlib.LinearAlgebra.Lagrange + +/-! +# Lagrange interpolation over a commutative ring + +Mathlib's `Lagrange.interpolate` works over a field. Over a commutative ring `R`, interpolation at +nodes `v i`, `i ∈ s`, still works when the nodes have *unit differences*: every `v i - v j` with +`i ≠ j` is a unit. Galois rings such as `(ℤ / 2 ^ p)[U] / (f)` are the motivating example; they +are not domains, but lifts of distinct residue-field elements differ by units. + +The Lagrange basis polynomial at `i` is `∏_{j ∈ s, j ≠ i} (X - v j) / (v i - v j)`, where the +division is multiplication by `Ring.inverse (v i - v j)` (`Lagrange.ringBasis`). It is one at +`v i` and zero at the other nodes, so `Lagrange.ringInterpolate s v r = ∑ i ∈ s, r i • basis i` +takes the value `r i` at `v i`. A polynomial of degree less than `#s` that vanishes at all nodes is +zero, because the factors `X - v i` are pairwise coprime; hence every polynomial of degree less +than `#s` is the interpolant of its values (`Lagrange.eq_ringInterpolate`). Over a field these +are Mathlib's `Lagrange.basis` and `Lagrange.interpolate` +(`Lagrange.ringInterpolate_eq_interpolate`). + +## Main definitions + +* `Lagrange.ringBasis`: the Lagrange basis polynomial, with `Ring.inverse` for division. +* `Lagrange.ringInterpolate`: Lagrange interpolation over a commutative ring, as a linear map. + +## Main results + +* `Lagrange.eval_ringInterpolate_at_node`: the interpolant takes the prescribed values. +* `Lagrange.degree_ringInterpolate_lt`: it has degree less than the number of nodes. +* `Lagrange.eq_zero_of_degree_lt_of_eval_eq_zero`: uniqueness at nodes with unit differences. +* `Lagrange.eq_ringInterpolate`: a polynomial of degree less than `#s` is the interpolant of its + values. +* `Lagrange.ringInterpolate_eq_interpolate`: over a field, this is `Lagrange.interpolate`. +-/ + +public section + +namespace Lagrange + +open Polynomial Finset + +variable {R : Type*} [CommRing R] {ι : Type*} [DecidableEq ι] {s : Finset ι} {v : ι → R} + {i j : ι} + +/-- The Lagrange basis polynomial `∏_{j ∈ s, j ≠ i} (X - v j) / (v i - v j)` over a commutative +ring, where division by `v i - v j` is multiplication by `Ring.inverse (v i - v j)`. When the nodes +have unit differences it is one at `v i` and zero at the other nodes. -/ +@[expose] noncomputable def ringBasis (s : Finset ι) (v : ι → R) (i : ι) : R[X] := + ∏ j ∈ s.erase i, C (Ring.inverse (v i - v j)) * (X - C (v j)) + +/-- **Lagrange interpolation over a commutative ring**: `∑ i ∈ s, r i • ringBasis s v i`. At nodes +with unit differences, it is the unique polynomial of degree less than `#s` taking the value `r i` +at `v i`. -/ +@[expose] noncomputable def ringInterpolate (s : Finset ι) (v : ι → R) : (ι → R) →ₗ[R] R[X] where + toFun r := ∑ i ∈ s, C (r i) * ringBasis s v i + map_add' r r' := by + simp only [Pi.add_apply, C_add, add_mul, sum_add_distrib] + map_smul' c r := by + simp only [Pi.smul_apply, smul_eq_mul, C_mul, RingHom.id_apply, smul_sum, mul_assoc, + smul_eq_C_mul] + +theorem ringInterpolate_apply (s : Finset ι) (v : ι → R) (r : ι → R) : + ringInterpolate s v r = ∑ i ∈ s, C (r i) * ringBasis s v i := + rfl + +/-- The basis polynomial at `i` is one at its own node. -/ +theorem eval_ringBasis_self (hvs : (s : Set ι).Pairwise fun i j => IsUnit (v i - v j)) + (hi : i ∈ s) : (ringBasis s v i).eval (v i) = 1 := by + rw [ringBasis, eval_prod] + refine prod_eq_one fun j hj => ?_ + obtain ⟨hji, hj⟩ := mem_erase.mp hj + rw [eval_mul, eval_C, eval_sub, eval_X, eval_C] + exact Ring.inverse_mul_cancel _ (hvs hi hj (Ne.symm hji)) + +/-- The basis polynomial at `i` vanishes at every other node. -/ +theorem eval_ringBasis_of_ne (hij : i ≠ j) (hj : j ∈ s) : (ringBasis s v i).eval (v j) = 0 := by + rw [ringBasis, eval_prod] + refine prod_eq_zero (mem_erase.mpr ⟨Ne.symm hij, hj⟩) ?_ + simp only [eval_mul, eval_sub, eval_X, eval_C, sub_self, mul_zero] + +theorem natDegree_ringBasis_le (s : Finset ι) (v : ι → R) (i : ι) : + (ringBasis s v i).natDegree ≤ #(s.erase i) := by + refine (natDegree_prod_le _ _).trans + ((sum_le_sum fun j _ => ?_).trans_eq (card_eq_sum_ones _).symm) + exact (natDegree_C_mul_le _ _).trans (natDegree_X_sub_C_le _) + +/-- The basis polynomial at a node has degree less than the number of nodes. -/ +theorem degree_ringBasis_lt (hi : i ∈ s) : (ringBasis s v i).degree < #s := by + refine (degree_le_natDegree).trans_lt ?_ + have h := natDegree_ringBasis_le s v i + rw [card_erase_of_mem hi] at h + have hs : 0 < #s := card_pos.mpr ⟨i, hi⟩ + exact_mod_cast (show (ringBasis s v i).natDegree < #s by omega) + +/-- The interpolant has degree less than the number of nodes. -/ +theorem degree_ringInterpolate_lt (r : ι → R) : (ringInterpolate s v r).degree < #s := by + rw [← mem_degreeLT, ringInterpolate_apply] + refine Submodule.sum_mem _ fun i hi => ?_ + rw [← smul_eq_C_mul] + exact Submodule.smul_mem _ _ (mem_degreeLT.mpr (degree_ringBasis_lt hi)) + +/-- **The interpolant takes the prescribed values** at nodes with unit differences. -/ +theorem eval_ringInterpolate_at_node (hvs : (s : Set ι).Pairwise fun i j => IsUnit (v i - v j)) + (r : ι → R) (hi : i ∈ s) : (ringInterpolate s v r).eval (v i) = r i := by + rw [ringInterpolate_apply, eval_finsetSum, sum_eq_single_of_mem i hi, eval_mul, eval_C, + eval_ringBasis_self hvs hi, mul_one] + intro j _ hji + rw [eval_mul, eval_ringBasis_of_ne hji hi, mul_zero] + +omit [DecidableEq ι] in +/-- **Uniqueness of interpolation over a commutative ring.** A polynomial of degree less than +`#s` that vanishes at nodes with unit differences is zero: the factors `X - v i` are pairwise +coprime, so their product, of degree `#s`, divides it. -/ +theorem eq_zero_of_degree_lt_of_eval_eq_zero + (hvs : (s : Set ι).Pairwise fun i j => IsUnit (v i - v j)) {f : R[X]} (hf : f.degree < #s) + (h0 : ∀ i ∈ s, f.eval (v i) = 0) : f = 0 := by + nontriviality R + by_contra hne + have hdvd : nodal s v ∣ f := by + rw [nodal] + refine prod_dvd_of_coprime (fun i hi j hj hij => ?_) fun i hi => ?_ + · exact isCoprime_X_sub_C_of_isUnit_sub (hvs hi hj hij) + · exact dvd_iff_isRoot.mpr (h0 i hi) + exact nodal_monic.not_dvd_of_degree_lt hne (by rwa [degree_nodal]) hdvd + +/-- **Every polynomial of degree less than `#s` is the interpolant of its values** at nodes with +unit differences. -/ +theorem eq_ringInterpolate (hvs : (s : Set ι).Pairwise fun i j => IsUnit (v i - v j)) + {f : R[X]} (hf : f.degree < #s) : f = ringInterpolate s v (fun i => f.eval (v i)) := by + refine sub_eq_zero.mp (eq_zero_of_degree_lt_of_eval_eq_zero hvs + ((degree_sub_le _ _).trans_lt (max_lt hf (degree_ringInterpolate_lt _))) fun i hi => ?_) + rw [eval_sub, eval_ringInterpolate_at_node hvs _ hi, sub_self] + +/-- Over a field, `ringBasis` is Mathlib's `Lagrange.basis`. -/ +theorem ringBasis_eq_basis {F : Type*} [Field F] (s : Finset ι) (v : ι → F) (i : ι) : + ringBasis s v i = Lagrange.basis s v i := by + simp only [ringBasis, Lagrange.basis, basisDivisor, Ring.inverse_eq_inv'] + +/-- Over a field, `ringInterpolate` is Mathlib's `Lagrange.interpolate`. -/ +theorem ringInterpolate_eq_interpolate {F : Type*} [Field F] (s : Finset ι) (v : ι → F) : + ringInterpolate s v = interpolate s v := by + ext r : 1 + simp only [ringInterpolate_apply, interpolate_apply, ringBasis_eq_basis] + +end Lagrange diff --git a/Complexity/Foundations/NumberTheory/Padics/AdjoinRootValuation.lean b/Complexity/Foundations/NumberTheory/Padics/AdjoinRootValuation.lean new file mode 100644 index 0000000000..707dad117b --- /dev/null +++ b/Complexity/Foundations/NumberTheory/Padics/AdjoinRootValuation.lean @@ -0,0 +1,303 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate (Mathlib, `Mathlib/NumberTheory/Padics/`) + +public import Complexity.Init +public import Mathlib.NumberTheory.Padics.PadicNumbers +public import Mathlib.RingTheory.AdjoinRoot +public import Mathlib.RingTheory.Valuation.Integers +import Complexity.Foundations.RingTheory.GaloisRing +import Mathlib.Algebra.GCDMonoid.IntegrallyClosed +import Mathlib.Algebra.Polynomial.Eval.Irreducible +import Mathlib.RingTheory.Localization.Integral +import Mathlib.RingTheory.Polynomial.Content +import Mathlib.RingTheory.Polynomial.GaussLemma + +/-! +# The `p`-adic valuation of `ℚ[X] / (f)` for `f` irreducible modulo `p` + +Let `p` be a prime and `f` a monic integer polynomial that is irreducible modulo `p`. Then `f` is +irreducible over `ℚ` (`AdjoinRoot.irreducible_map_rat`), so `K = ℚ[X] / (f)` is a field. Give each +element of `K` its coordinates in the basis `1, X, …, X ^ (deg f - 1)`, and let `v x` be the +largest `p`-adic valuation `Rat.padicValuation p` of a coordinate; written additively, `v x` is the +least `p`-adic order of a coordinate. This file proves that `v` is a valuation of `K` +(`AdjoinRoot.padicValuation`): it is multiplicative and ultrametric. + +Multiplicativity is the substantial point. Every element of `K` is a rational multiple +`r • x₀` of the image `x₀` of an element of `ℤ[X] / (f)` that is nonzero modulo `p`, and such +`x₀` have `v x₀ = 1`. Reduction modulo `p` is a ring map into the field `𝔽_p[X] / (f)`, so a +product of two such elements is again nonzero modulo `p`. + +The valuation ring `O = {x | v x ≤ 1}` of `v` consists of the elements whose coordinates have no +`p` in their denominators, that is `ℤ_(p)[X] / (f)` (`AdjoinRoot.mem_integer_padicValuation_iff`), +and an element of `O` with `v x = 1`, one that is nonzero modulo `p`, is a unit of `O` +(`AdjoinRoot.isUnit_of_padicValuation_eq_one`). + +This is the 2-adic valuation `v₂` of the coefficient field `K = ℚ[U] / (f)` of [OpenAI2026ETH] +(§D.4.1, p. 57), with `v₂ x = n` corresponding to `v x = exp (-n)`. + +## Main definitions + +* `AdjoinRoot.padicValuation`: the `p`-adic valuation of `ℚ[X] / (f)`. + +## Main results + +* `AdjoinRoot.irreducible_map_rat`: `f` is irreducible over `ℚ`. +* `AdjoinRoot.padicValuation_apply`: `v x` is the largest valuation of a coordinate of `x`. +* `AdjoinRoot.padicValuation_algebraMap`: `v` extends `Rat.padicValuation p`. +* `AdjoinRoot.padicValuation_map_eq_one_iff`: an element of `ℤ[X] / (f)` has valuation one + exactly when it is nonzero modulo `p`. +* `AdjoinRoot.mem_integer_padicValuation_iff`: the valuation ring is `ℤ_(p)[X] / (f)`. +* `AdjoinRoot.isUnit_of_padicValuation_eq_one`: elements of valuation one are units of it. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.1, p. 57. +-/ + +public section + +open Polynomial +open scoped WithZero + +namespace AdjoinRoot + +variable (p : ℕ) [hp : Fact p.Prime] {f : ℤ[X]} + +/-- **Irreducible modulo `p` implies irreducible over `ℚ`**, for monic `f`. -/ +theorem irreducible_map_rat (hf : f.Monic) + (hirr : Irreducible (f.map (Int.castRingHom (ZMod p)))) : + Irreducible (f.map (Int.castRingHom ℚ)) := + (hf.irreducible_iff_irreducible_map_fraction_map (K := ℚ)).mp + (Monic.irreducible_of_irreducible_map _ _ hf hirr) + +variable {p} + +/-- The reduced representative has degree below `deg g`. -/ +private theorem degree_modByMonicHom_lt {R : Type*} [CommRing R] [Nontrivial R] {g : R[X]} + (hg : g.Monic) (x : AdjoinRoot g) : (modByMonicHom hg x).degree < g.degree := by + obtain ⟨Q, rfl⟩ := mk_surjective x + rw [modByMonicHom_mk] + exact degree_modByMonic_lt _ hg + +variable (p) in +/-- The largest `p`-adic valuation of a coordinate of `x` in the basis `1, X, …, X ^ (deg f - 1)`. +-/ +private noncomputable def coordVal (hf : f.Monic) (x : AdjoinRoot (f.map (Int.castRingHom ℚ))) : + ℤᵐ⁰ := + (Finset.range f.natDegree).sup fun i => + Rat.padicValuation p ((modByMonicHom (hf.map (Int.castRingHom ℚ)) x).coeff i) + +private theorem coordVal_smul_le (hf : f.Monic) (r : ℚ) + (x : AdjoinRoot (f.map (Int.castRingHom ℚ))) : + coordVal p hf (r • x) ≤ Rat.padicValuation p r * coordVal p hf x := by + refine Finset.sup_le fun i hi => ?_ + rw [LinearMap.map_smul_of_tower, coeff_smul, smul_eq_mul, map_mul] + exact mul_le_mul_right (Finset.le_sup (f := fun i => Rat.padicValuation p + ((modByMonicHom (hf.map (Int.castRingHom ℚ)) x).coeff i)) hi) _ + +/-- The coordinates scale with rational scalars. -/ +private theorem coordVal_smul (hf : f.Monic) (r : ℚ) + (x : AdjoinRoot (f.map (Int.castRingHom ℚ))) : + coordVal p hf (r • x) = Rat.padicValuation p r * coordVal p hf x := by + refine le_antisymm (coordVal_smul_le hf r x) ?_ + rcases eq_or_ne r 0 with rfl | hr + · simp + have h := coordVal_smul_le (p := p) hf r⁻¹ (r • x) + rw [inv_smul_smul₀ hr, map_inv₀] at h + calc + Rat.padicValuation p r * coordVal p hf x + ≤ Rat.padicValuation p r * ((Rat.padicValuation p r)⁻¹ * coordVal p hf (r • x)) := + mul_le_mul_right h _ + _ = coordVal p hf (r • x) := by + rw [← mul_assoc, mul_inv_cancel₀ ((Rat.padicValuation p).ne_zero_iff.mpr hr), one_mul] + +/-- The image of `ℤ[X] / (f)` in `ℚ[X] / (f)`. -/ +private noncomputable abbrev toRat (f : ℤ[X]) : + AdjoinRoot f →+* AdjoinRoot (f.map (Int.castRingHom ℚ)) := + map (Int.castRingHom ℚ) f _ dvd_rfl + +/-- Reduction of `ℤ[X] / (f)` modulo `p`. -/ +private noncomputable abbrev toZMod (p : ℕ) (f : ℤ[X]) : + AdjoinRoot f →+* AdjoinRoot (f.map (Int.castRingHom (ZMod p))) := + map (Int.castRingHom (ZMod p)) f _ dvd_rfl + +/-- The coordinates of the image of `ℤ[X] / (f)` are the integer coordinates. -/ +private theorem modByMonicHom_toRat (hf : f.Monic) (o : AdjoinRoot f) : + modByMonicHom (hf.map (Int.castRingHom ℚ)) (toRat f o) = + (modByMonicHom hf o).map (Int.castRingHom ℚ) := by + obtain ⟨Q, rfl⟩ := mk_surjective o + rw [toRat, map_mk, modByMonicHom_mk, modByMonicHom_mk, map_modByMonic _ hf] + +/-- **Images of `ℤ[X] / (f)` have valuation one exactly when they are nonzero modulo `p`**, and at +most one in any case. -/ +private theorem coordVal_toRat_eq_one_iff (hf : f.Monic) (o : AdjoinRoot f) : + coordVal p hf (toRat f o) ≤ 1 ∧ + (coordVal p hf (toRat f o) = 1 ↔ toZMod p f o ≠ 0) := by + set Q := modByMonicHom hf o + have hQdeg : ∀ i, f.natDegree ≤ i → Q.coeff i = 0 := fun i hi => + coeff_eq_zero_of_degree_lt ((degree_modByMonicHom_lt hf o).trans_le + ((degree_le_natDegree).trans (by exact_mod_cast hi))) + have hval : ∀ i, Rat.padicValuation p ((modByMonicHom (hf.map (Int.castRingHom ℚ)) + (toRat f o)).coeff i) = Int.padicValuation p (Q.coeff i) := fun i => by + rw [modByMonicHom_toRat, coeff_map, eq_intCast, Rat.padicValuation_cast] + have hle : coordVal p hf (toRat f o) ≤ 1 := + Finset.sup_le fun i _ => (hval i).trans_le (Int.padicValuation_le_one p _) + refine ⟨hle, ?_⟩ + rw [Ne, toZMod, map_eq_zero_iff _ hf dvd_rfl rfl] + simp only [eq_intCast, ZMod.intCast_zmod_eq_zero_iff_dvd] + constructor + · intro h1 hdvd + have hlt : coordVal p hf (toRat f o) < 1 := by + refine (Finset.sup_lt_iff (bot_lt_iff_ne_bot.mpr (by simp))).mpr fun i _ => ?_ + rw [hval] + exact Int.padicValuation_lt_one_iff.mpr (hdvd i) + exact hlt.ne h1 + · intro hndvd + obtain ⟨i, hi⟩ := not_forall.mp hndvd + have hie : i < f.natDegree := by + by_contra hie + exact hi (by rw [hQdeg i (not_lt.mp hie)]; exact dvd_zero _) + refine le_antisymm hle ?_ + rw [← Int.padicValuation_eq_one_iff.mpr hi, ← hval] + exact Finset.le_sup (f := fun i => Rat.padicValuation p + ((modByMonicHom (hf.map (Int.castRingHom ℚ)) (toRat f o)).coeff i)) (Finset.mem_range.mpr hie) + +/-- **Every element is a rational multiple of an integral element that is nonzero modulo `p`.** +Clear the denominators of the coordinates and divide by the content. -/ +private theorem exists_eq_smul_toRat (hf : f.Monic) + (hirr : Irreducible (f.map (Int.castRingHom (ZMod p)))) + (x : AdjoinRoot (f.map (Int.castRingHom ℚ))) : + ∃ r : ℚ, ∃ o : AdjoinRoot f, toZMod p f o ≠ 0 ∧ x = r • toRat f o := by + have := Fact.mk hirr + set P := modByMonicHom (hf.map (Int.castRingHom ℚ)) x + have hx : mk _ P = x := mk_leftInverse _ x + rcases eq_or_ne P 0 with hP | hP + · refine ⟨0, 1, by rw [map_one]; exact one_ne_zero, ?_⟩ + rw [zero_smul, ← hx, hP, map_zero] + obtain ⟨b, hb, hQ₀'⟩ := IsLocalization.integerNormalization_spec (nonZeroDivisors ℤ) P + set Q₀ := IsLocalization.integerNormalization (nonZeroDivisors ℤ) P + have hb0 : (b : ℚ) ≠ 0 := by exact_mod_cast nonZeroDivisors.ne_zero hb + have hQ₀ : Q₀.map (Int.castRingHom ℚ) = C (b : ℚ) * P := by + rw [← algebraMap_int_eq, hQ₀', ← Int.cast_smul_eq_zsmul ℚ, smul_eq_C_mul] + have hQ₀ne : Q₀ ≠ 0 := by + rintro h + rw [h, Polynomial.map_zero] at hQ₀ + exact mul_ne_zero (C_ne_zero.mpr hb0) hP hQ₀.symm + set Q₁ := Q₀.primPart + have hdeg : Q₁.degree < (f.map (Int.castRingHom (ZMod p))).degree := by + rw [hf.degree_map, degree_eq_natDegree (primPart_ne_zero Q₀), natDegree_primPart] + have hPdeg : P.degree < f.degree := + (hf.degree_map (Int.castRingHom ℚ)) ▸ degree_modByMonicHom_lt (hf.map _) x + have : Q₀.degree < f.degree := by + refine lt_of_le_of_lt ?_ hPdeg + rw [← degree_map_eq_of_injective (RingHom.injective_int (Int.castRingHom ℚ)) Q₀, hQ₀] + exact degree_mul_le_of_le (degree_C_le) le_rfl |>.trans (by simp) + rwa [degree_eq_natDegree hQ₀ne] at this + have hred : (Q₁.map (Int.castRingHom (ZMod p))) ≠ 0 := by + intro h0 + have hdvd : C (p : ℤ) ∣ Q₁ := (C_dvd_iff_dvd_coeff _ _).mpr fun i => by + rw [← ZMod.intCast_zmod_eq_zero_iff_dvd, ← eq_intCast (Int.castRingHom (ZMod p)), ← coeff_map, + h0, coeff_zero] + have hu := isPrimitive_primPart Q₀ (p : ℤ) hdvd + rw [Int.isUnit_iff_natAbs_eq, Int.natAbs_natCast] at hu + exact hp.out.one_lt.ne' hu + refine ⟨Q₀.content / b, mk f Q₁, ?_, ?_⟩ + · rw [toZMod, map_mk, Ne, mk_eq_zero] + exact (hf.map _).not_dvd_of_degree_lt hred ((degree_map_le).trans_lt hdeg) + · rw [toRat, map_mk, ← hx, Algebra.smul_def, algebraMap_eq, ← mk_C, ← map_mul] + congr 1 + have h := congrArg (Polynomial.map (Int.castRingHom ℚ)) (eq_C_content_mul_primPart Q₀) + rw [Polynomial.map_mul, map_C, hQ₀, eq_intCast] at h + calc + P = C (b : ℚ)⁻¹ * (C (b : ℚ) * P) := by + rw [← mul_assoc, ← C_mul, inv_mul_cancel₀ hb0, C_1, one_mul] + _ = C ((Q₀.content : ℚ) / b) * Q₁.map (Int.castRingHom ℚ) := by + rw [h, ← mul_assoc, ← C_mul, div_eq_inv_mul] + +variable (p) in +/-- **The `p`-adic valuation of `ℚ[X] / (f)`**, for `f` monic and irreducible modulo `p`: the +largest `p`-adic valuation of a coordinate in the basis `1, X, …, X ^ (deg f - 1)`. It extends +`Rat.padicValuation p` (`AdjoinRoot.padicValuation_algebraMap`). -/ +noncomputable def padicValuation (hf : f.Monic) + (hirr : Irreducible (f.map (Int.castRingHom (ZMod p)))) : + Valuation (AdjoinRoot (f.map (Int.castRingHom ℚ))) ℤᵐ⁰ where + toFun := coordVal p hf + map_zero' := by + simp only [coordVal, map_zero, coeff_zero] + exact Finset.sup_bot _ + map_one' := by + have := (coordVal_toRat_eq_one_iff (p := p) hf 1).2.mpr (by + have := Fact.mk hirr + rw [map_one]; exact one_ne_zero) + rwa [map_one] at this + map_mul' x y := by + have := Fact.mk hirr + obtain ⟨r, o, ho, rfl⟩ := exists_eq_smul_toRat hf hirr x + obtain ⟨r', o', ho', rfl⟩ := exists_eq_smul_toRat hf hirr y + rw [smul_mul_smul_comm, ← map_mul, coordVal_smul, coordVal_smul, coordVal_smul, map_mul, + (coordVal_toRat_eq_one_iff hf _).2.mpr (by rw [map_mul]; exact mul_ne_zero ho ho'), + (coordVal_toRat_eq_one_iff hf _).2.mpr ho, (coordVal_toRat_eq_one_iff hf _).2.mpr ho', + mul_one, mul_one, mul_one] + map_add_le_max' x y := by + refine Finset.sup_le fun i hi => ?_ + rw [map_add, coeff_add] + refine ((Rat.padicValuation p).map_add _ _).trans (max_le_max ?_ ?_) <;> + exact Finset.le_sup (f := fun i => Rat.padicValuation p _) hi + +variable (hf : f.Monic) (hirr : Irreducible (f.map (Int.castRingHom (ZMod p)))) + +/-- The valuation is the largest `p`-adic valuation of a coordinate. -/ +theorem padicValuation_apply (x : AdjoinRoot (f.map (Int.castRingHom ℚ))) : + padicValuation p hf hirr x = (Finset.range f.natDegree).sup fun i => + Rat.padicValuation p ((modByMonicHom (hf.map (Int.castRingHom ℚ)) x).coeff i) := by + simp only [padicValuation] + rfl + +/-- **The valuation extends the `p`-adic valuation of `ℚ`.** -/ +theorem padicValuation_algebraMap (r : ℚ) : + padicValuation p hf hirr (algebraMap ℚ _ r) = Rat.padicValuation p r := by + rw [Algebra.algebraMap_eq_smul_one, ← mul_one (Rat.padicValuation p r), + ← (padicValuation p hf hirr).map_one] + exact coordVal_smul hf r 1 + +/-- **An element of `ℤ[X] / (f)` has valuation one exactly when it is nonzero modulo `p`.** -/ +theorem padicValuation_map_eq_one_iff (o : AdjoinRoot f) : + padicValuation p hf hirr (map (Int.castRingHom ℚ) f _ dvd_rfl o) = 1 ↔ + map (Int.castRingHom (ZMod p)) f _ dvd_rfl o ≠ 0 := + (coordVal_toRat_eq_one_iff hf o).2 + +/-- Elements of `ℤ[X] / (f)` have valuation at most one. -/ +theorem padicValuation_map_le_one (o : AdjoinRoot f) : + padicValuation p hf hirr (map (Int.castRingHom ℚ) f _ dvd_rfl o) ≤ 1 := + (coordVal_toRat_eq_one_iff hf o).1 + +/-- **The valuation ring is `ℤ_(p)[X] / (f)`**: the elements whose coordinates have no `p` in their +denominators. -/ +theorem mem_integer_padicValuation_iff (x : AdjoinRoot (f.map (Int.castRingHom ℚ))) : + x ∈ (padicValuation p hf hirr).integer ↔ + ∀ i, Rat.padicValuation p ((modByMonicHom (hf.map (Int.castRingHom ℚ)) x).coeff i) ≤ 1 := by + rw [Valuation.mem_integer_iff, padicValuation_apply, Finset.sup_le_iff] + refine ⟨fun h i => ?_, fun h i _ => h i⟩ + rcases lt_or_ge i f.natDegree with hi | hi + · exact h i (Finset.mem_range.mpr hi) + · rw [coeff_eq_zero_of_degree_lt, map_zero] + · exact zero_le + refine (degree_modByMonicHom_lt (hf.map _) x).trans_le ?_ + rw [hf.degree_map] + exact (degree_le_natDegree).trans (by exact_mod_cast hi) + +/-- **Elements of the valuation ring that are nonzero modulo `p` are units of it**: an element of +valuation one has an inverse of valuation one. -/ +theorem isUnit_of_padicValuation_eq_one {x : AdjoinRoot (f.map (Int.castRingHom ℚ))} + (hx : x ∈ (padicValuation p hf hirr).integer) (h1 : padicValuation p hf hirr x = 1) : + IsUnit (⟨x, hx⟩ : (padicValuation p hf hirr).integer) := by + have := Fact.mk (irreducible_map_rat p hf hirr) + exact (Valuation.integer.integers _).isUnit_iff_valuation_eq_one.mpr h1 + +end AdjoinRoot diff --git a/Complexity/Foundations/RingTheory/GaloisRing.lean b/Complexity/Foundations/RingTheory/GaloisRing.lean new file mode 100644 index 0000000000..da9bc2de1e --- /dev/null +++ b/Complexity/Foundations/RingTheory/GaloisRing.lean @@ -0,0 +1,351 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate (Mathlib, Galois rings) + +public import Complexity.Init +public import Complexity.Foundations.Data.Polynomial.RingLagrange +public import Mathlib.FieldTheory.Finite.GaloisField +public import Mathlib.RingTheory.AdjoinRoot +public import Mathlib.RingTheory.LocalRing.ResidueField.Defs +import Mathlib.RingTheory.LocalRing.ResidueField.Basic +import Mathlib.RingTheory.Polynomial.Nilpotent + +/-! +# Galois rings + +For a prime `p`, an exponent `n ≠ 0` and an integer polynomial `f` that is monic and irreducible +modulo `p`, the *Galois ring* `GaloisRing p n f = (ℤ / p ^ n)[X] / (f)` is a local ring. Its maximal +ideal is `(p)`, and reduction modulo `p` identifies its residue field with `𝔽_p[X] / (f)`, the +finite field with `p ^ deg f` elements. In particular an element is a unit exactly when it is +nonzero modulo `p`. + +The *digit lifts* `∑ i, c i * X ^ i` with digits `c i ∈ {0, …, p - 1}` reduce to the `p ^ deg f` +distinct residue-field elements, so any two of them differ by a unit, and Lagrange interpolation +over the ring holds at any set of them (`GaloisRing.eq_ringInterpolate_digitLift`). + +This is the coefficient ring of the Boolean tensor contraction of [OpenAI2026ETH] (§D.4.1, +pp. 57–58), which takes `p = 2`, a degree `e = 4 ⌈log₂ s⌉` and the precision `n`; the manuscript's +binary-coordinate lifts are the digit lifts for `p = 2`. + +The argument is that of a surjection onto a field with nilpotent kernel +(`RingHom.isUnit_iff_ne_zero_of_isNilpotent`): reduction modulo `p` has kernel `(p)`, and +`p ^ n = 0`. The manuscript states the unit criterion for `ℤ_(2)[U] / (f)` and proves it with the +determinant of the multiplication matrix; at a finite precision the nilpotent kernel suffices. The +statement over `ℤ_(p)` is `AdjoinRoot.isUnit_of_padicValuation_eq_one`. + +## Main definitions + +* `GaloisRing`: the ring `(ℤ / p ^ n)[X] / (f)`. +* `GaloisRing.residue`: reduction modulo `p`, onto `𝔽_p[X] / (f)`. +* `GaloisRing.digitLift`: the lift `∑ i, c i * X ^ i` of a digit vector. + +## Main results + +* `RingHom.isLocalHom_of_isNilpotent`: a surjection with nilpotent kernel is a local + homomorphism. +* `AdjoinRoot.map_eq_zero_iff`: an element of `R[X] / (g)` vanishes in `S[X] / (g)` exactly when + the coefficients of its reduced representative do. +* `GaloisRing.ker_residue`: the kernel of reduction modulo `p` is `(p)`. +* `GaloisRing.isUnit_iff`: an element is a unit exactly when it is nonzero modulo `p`. +* `GaloisRing.instIsLocalRing`, `GaloisRing.maximalIdeal_eq`: the ring is local with maximal ideal + `(p)`. +* `GaloisRing.residueFieldEquiv`, `GaloisRing.algEquivGaloisField`: its residue field is + `𝔽_p[X] / (f) ≃ GaloisField p (deg f)`. +* `GaloisRing.isUnit_digitLift_sub`: distinct digit lifts differ by a unit. +* `GaloisRing.eq_ringInterpolate_digitLift`: Lagrange interpolation at digit lifts. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.1, pp. 57–58. +-/ + +public section + +open Polynomial IsLocalRing + +/-! ### Surjections with nilpotent kernel -/ + +namespace RingHom + +variable {R S : Type*} [CommRing R] [CommRing S] + +/-- A surjective ring homomorphism whose kernel consists of nilpotent elements is local: if `ψ x` +is a unit then `x y = 1 + n` for some `y` and nilpotent `n`. -/ +theorem isLocalHom_of_isNilpotent (ψ : R →+* S) (hψ : Function.Surjective ψ) + (hnil : ∀ x, ψ x = 0 → IsNilpotent x) : IsLocalHom ψ where + map_nonunit x hx := by + obtain ⟨u, hu⟩ := hx + obtain ⟨y, hy⟩ := hψ ↑u⁻¹ + have hn : IsNilpotent (x * y - 1) := + hnil _ (by rw [map_sub, map_mul, map_one, hy, ← hu, Units.mul_inv, sub_self]) + have h1 : IsUnit (x * y) := by + simpa only [add_sub_cancel] using hn.isUnit_one_add + exact isUnit_of_mul_isUnit_left h1 + +/-- Over a surjection onto a field with nilpotent kernel, an element is a unit exactly when its +image is nonzero. -/ +theorem isUnit_iff_ne_zero_of_isNilpotent {F : Type*} [Field F] (ψ : R →+* F) + (hψ : Function.Surjective ψ) (hnil : ∀ x, ψ x = 0 → IsNilpotent x) {x : R} : + IsUnit x ↔ ψ x ≠ 0 := by + have := ψ.isLocalHom_of_isNilpotent hψ hnil + exact ⟨fun h => (h.map ψ).ne_zero, fun h => isUnit_of_map_unit ψ x (Ne.isUnit h)⟩ + +end RingHom + +/-! ### Reducing the coefficients of `R[X] / (g)` -/ + +namespace AdjoinRoot + +variable {R S : Type*} [CommRing R] [CommRing S] (φ : R →+* S) {g : R[X]} + +/-- Reducing the coefficients maps the class of `P` to the class of `P.map φ`. -/ +theorem map_mk (q : S[X]) (h : q ∣ g.map φ) (P : R[X]) : + map φ g q h (mk g P) = mk q (P.map φ) := by + have : (map φ g q h).comp (mk g) = (mk q).comp (mapRingHom φ) := by + refine Polynomial.ringHom_ext (fun a => ?_) ?_ + · simp only [RingHom.comp_apply, mk_C, map_of, coe_mapRingHom, Polynomial.map_C] + · simp only [RingHom.comp_apply, mk_X, map_root, coe_mapRingHom, Polynomial.map_X] + exact congrArg (fun F : R[X] →+* AdjoinRoot q => F P) this + +/-- Reducing the coefficients is surjective when `φ` is. -/ +theorem map_surjective_of_surjective (q : S[X]) (h : q ∣ g.map φ) + (hφ : Function.Surjective φ) : Function.Surjective (map φ g q h) := by + intro y + obtain ⟨Q, rfl⟩ := mk_surjective y + obtain ⟨P, rfl⟩ := Polynomial.map_surjective φ hφ Q + exact ⟨mk g P, map_mk φ q h P⟩ + +/-- **An element vanishes after reducing the coefficients** of a monic `g` exactly when the +coefficients of its representative of degree below `deg g` do: that representative reduces to a +polynomial of degree below `deg (g.map φ)`, which `g.map φ` divides only when it is zero. -/ +theorem map_eq_zero_iff [Nontrivial S] (hg : g.Monic) {q : S[X]} (h : q ∣ g.map φ) + (hq : q = g.map φ) (x : AdjoinRoot g) : + map φ g q h x = 0 ↔ ∀ i, φ ((modByMonicHom hg x).coeff i) = 0 := by + subst hq + have := φ.domain_nontrivial + obtain ⟨Q, rfl⟩ := mk_surjective x + rw [modByMonicHom_mk] + set P := Q %ₘ g + have hx : mk g P = mk g Q := by + have := mk_leftInverse hg (mk g Q) + rwa [modByMonicHom_mk] at this + have hdeg : (P.map φ).degree < (g.map φ).degree := by + rw [hg.degree_map] + exact degree_map_le.trans_lt (degree_modByMonic_lt _ hg) + rw [← hx, map_mk, mk_eq_zero] + constructor + · intro hdvd i + by_contra hne + have h0 : P.map φ ≠ 0 := fun h0 => hne (by rw [← coeff_map, h0, coeff_zero]) + exact (hg.map φ).not_dvd_of_degree_lt h0 hdeg hdvd + · intro h + have : P.map φ = 0 := Polynomial.ext fun i => by rw [coeff_map, h, coeff_zero] + rw [this] + exact dvd_zero _ + +end AdjoinRoot + +/-! ### Galois rings -/ + +/-- The ring `(ℤ / p ^ n)[X] / (f)` presented by an integer polynomial `f`. For a prime `p`, +`n ≠ 0` and `f` monic and irreducible modulo `p`, it is the *Galois ring* of characteristic +`p ^ n` and rank `deg f`: a local ring with maximal ideal `(p)` and residue field +`𝔽_{p ^ deg f}`. -/ +abbrev GaloisRing (p n : ℕ) (f : ℤ[X]) : Type := + AdjoinRoot (f.map (Int.castRingHom (ZMod (p ^ n)))) + +namespace GaloisRing + +variable (p n : ℕ) (f : ℤ[X]) + +/-- Reducing integer coefficients modulo `m` and then modulo a divisor `d` of `m` is reducing +modulo `d`. -/ +theorem map_castHom_map {d m : ℕ} (h : d ∣ m) : + (f.map (Int.castRingHom (ZMod m))).map (ZMod.castHom h (ZMod d)) = + f.map (Int.castRingHom (ZMod d)) := by + rw [Polynomial.map_map] + congr 1 + exact RingHom.ext_int _ _ + +/-- The *digit lift* `∑ i, c i * X ^ i` of a digit vector `c`, with each digit `c i ∈ ZMod p` read +as an integer in `{0, …, p - 1}`. For `p = 2` these are the binary-coordinate lifts of +[OpenAI2026ETH] (§D.4.1, p. 58). -/ +@[expose] noncomputable def digitLift (c : Fin f.natDegree → ZMod p) : GaloisRing p n f := + ∑ i, ((c i).val : GaloisRing p n f) * AdjoinRoot.root _ ^ (i : ℕ) + +variable [NeZero n] + +/-- Reduction modulo `p`, from `(ℤ / p ^ n)[X] / (f)` onto `𝔽_p[X] / (f)`. -/ +noncomputable def residue : GaloisRing p n f →+* AdjoinRoot (f.map (Int.castRingHom (ZMod p))) := + AdjoinRoot.map (ZMod.castHom (dvd_pow_self p (NeZero.ne n)) (ZMod p)) _ _ + (map_castHom_map f _).symm.dvd + +@[simp] +theorem residue_mk (P : (ZMod (p ^ n))[X]) : + residue p n f (AdjoinRoot.mk _ P) = + AdjoinRoot.mk _ (P.map (ZMod.castHom (dvd_pow_self p (NeZero.ne n)) (ZMod p))) := + AdjoinRoot.map_mk _ _ _ P + +@[simp] +theorem residue_root : residue p n f (AdjoinRoot.root _) = AdjoinRoot.root _ := + AdjoinRoot.map_root _ _ _ _ + +@[simp] +theorem residue_of (a : ZMod (p ^ n)) : + residue p n f (AdjoinRoot.of _ a) = + AdjoinRoot.of _ (ZMod.castHom (dvd_pow_self p (NeZero.ne n)) (ZMod p) a) := + AdjoinRoot.map_of _ _ _ _ _ + +theorem residue_surjective : Function.Surjective (residue p n f) := + AdjoinRoot.map_surjective_of_surjective _ _ _ (ZMod.castHom_surjective _) + +variable {p n f} [Fact p.Prime] + +variable (p n f) in +/-- A digit lift reduces to `∑ i, c i * X ^ i` modulo `p`. -/ +theorem residue_digitLift (c : Fin f.natDegree → ZMod p) : + residue p n f (digitLift p n f c) = AdjoinRoot.mk _ (∑ i, C (c i) * X ^ (i : ℕ)) := by + simp only [digitLift, map_sum, map_mul, map_pow, map_natCast, residue_root, AdjoinRoot.mk_C, + AdjoinRoot.mk_X] + refine Finset.sum_congr rfl fun i _ => ?_ + rw [← map_natCast (AdjoinRoot.of _), ZMod.natCast_zmod_val] + +/-- Digit lifts reduce to distinct elements modulo `p`: the coordinates `1, X, …, X ^ (deg f - 1)` +are independent in `𝔽_p[X] / (f)` for monic `f`. -/ +theorem residue_digitLift_injective (hf : f.Monic) : + Function.Injective fun c : Fin f.natDegree → ZMod p => residue p n f (digitLift p n f c) := by + intro c c' h + simp only [residue_digitLift] at h + rw [AdjoinRoot.mk_eq_mk, ← Finset.sum_sub_distrib] at h + set P : (ZMod p)[X] := ∑ i, (C (c i) * X ^ (i : ℕ) - C (c' i) * X ^ (i : ℕ)) + have hcoeff : ∀ i : Fin f.natDegree, P.coeff i = c i - c' i := fun i => by + simp only [P, finsetSum_coeff, coeff_sub, coeff_C_mul, coeff_X_pow, mul_ite, mul_one, + mul_zero] + rw [Finset.sum_eq_single i (fun j _ hji => ?_) (by simp), ite_eq_left rfl, ite_eq_left rfl] + rw [ite_eq_right fun h => hji (Fin.ext h.symm), ite_eq_right fun h => hji (Fin.ext h.symm), + sub_zero] + have hP : P = 0 := by + by_contra h0 + refine (hf.map (Int.castRingHom (ZMod p))).not_dvd_of_degree_lt h0 ?_ h + rw [hf.degree_map, degree_eq_natDegree hf.ne_zero] + refine (degree_sum_le _ _).trans_lt + ((Finset.sup_lt_iff (WithBot.bot_lt_coe _)).mpr fun i _ => ?_) + refine (degree_sub_le _ _).trans_lt (max_lt ?_ ?_) <;> + exact (degree_C_mul_X_pow_le _ _).trans_lt (by exact_mod_cast i.isLt) + funext i + have := hcoeff i + rw [hP, coeff_zero] at this + exact (sub_eq_zero.mp this.symm) + + +/-- An element of `ℤ / p ^ n` that vanishes modulo `p` is a multiple of `p`. -/ +private theorem dvd_of_castHom_eq_zero {a : ZMod (p ^ n)} + (ha : ZMod.castHom (dvd_pow_self p (NeZero.ne n)) (ZMod p) a = 0) : (p : ZMod (p ^ n)) ∣ a := by + obtain ⟨k, rfl⟩ := ZMod.natCast_zmod_surjective a + rw [map_natCast, ZMod.natCast_eq_zero_iff] at ha + obtain ⟨m, rfl⟩ := ha + exact ⟨m, by push_cast; ring⟩ + +/-- **The kernel of reduction modulo `p` is `(p)`**, when `f` is monic. -/ +theorem ker_residue (hf : f.Monic) : + RingHom.ker (residue p n f) = Ideal.span {(p : GaloisRing p n f)} := by + ext x + rw [RingHom.mem_ker, Ideal.mem_span_singleton] + constructor + · intro hx + rw [residue, AdjoinRoot.map_eq_zero_iff _ (hf.map _) _ + (map_castHom_map f (dvd_pow_self p (NeZero.ne n))).symm] at hx + obtain ⟨Q, hQ⟩ := (C_dvd_iff_dvd_coeff (p : ZMod (p ^ n)) _).mpr fun i => + dvd_of_castHom_eq_zero (hx i) + refine ⟨AdjoinRoot.mk _ Q, ?_⟩ + rw [← AdjoinRoot.mk_leftInverse (hf.map _) x, hQ, map_mul, AdjoinRoot.mk_C, map_natCast] + · rintro ⟨y, rfl⟩ + rw [map_mul, map_natCast, ← map_natCast (AdjoinRoot.of _), ZMod.natCast_self, map_zero, + zero_mul] + +/-- `p ^ n = 0`, so elements that vanish modulo `p` are nilpotent. -/ +theorem isNilpotent_of_residue_eq_zero (hf : f.Monic) {x : GaloisRing p n f} + (hx : residue p n f x = 0) : IsNilpotent x := by + rw [← RingHom.mem_ker, ker_residue hf, Ideal.mem_span_singleton] at hx + obtain ⟨y, rfl⟩ := hx + refine ⟨n, ?_⟩ + rw [mul_pow, ← Nat.cast_pow, ← map_natCast (AdjoinRoot.of _), ZMod.natCast_self, map_zero, + zero_mul] + +variable (hf : f.Monic) (hirr : Irreducible (f.map (Int.castRingHom (ZMod p)))) +include hf hirr + +/-- **Units are the elements that are nonzero modulo `p`.** -/ +theorem isUnit_iff {x : GaloisRing p n f} : IsUnit x ↔ residue p n f x ≠ 0 := + have := Fact.mk hirr + (residue p n f).isUnit_iff_ne_zero_of_isNilpotent (residue_surjective p n f) fun _ => + isNilpotent_of_residue_eq_zero hf + +/-- **Distinct digit lifts differ by a unit.** -/ +theorem isUnit_digitLift_sub {c c' : Fin f.natDegree → ZMod p} (h : c ≠ c') : + IsUnit (digitLift p n f c - digitLift p n f c') := by + rw [isUnit_iff hf hirr, map_sub, sub_ne_zero] + exact fun h' => h (residue_digitLift_injective hf h') + +/-- **Lagrange interpolation at digit lifts.** A polynomial over a Galois ring of degree less than +`#s`, for a set `s` of digit vectors, is the interpolant of its values at their lifts; the lifts +have unit differences. -/ +theorem eq_ringInterpolate_digitLift {s : Finset (Fin f.natDegree → ZMod p)} + {P : (GaloisRing p n f)[X]} (hP : P.degree < s.card) : + P = Lagrange.ringInterpolate s (digitLift p n f) (fun c => P.eval (digitLift p n f c)) := + Lagrange.eq_ringInterpolate (fun _ _ _ _ h => isUnit_digitLift_sub hf hirr h) hP + +/-- **A Galois ring is local.** -/ +theorem isLocalRing : IsLocalRing (GaloisRing p n f) := + have := Fact.mk hirr + have := (residue p n f).isLocalHom_of_isNilpotent (residue_surjective p n f) fun _ => + isNilpotent_of_residue_eq_zero hf + (residue p n f).domain_isLocalRing + +omit hirr in +/-- `𝔽_p[X] / (f)` has `p ^ deg f` elements. -/ +theorem natCard_adjoinRoot : + Nat.card (AdjoinRoot (f.map (Int.castRingHom (ZMod p)))) = p ^ f.natDegree := by + let b := (AdjoinRoot.powerBasis' (hf.map (Int.castRingHom (ZMod p)))).basis + rw [Nat.card_congr b.equivFun.toEquiv, Nat.card_fun, Nat.card_zmod, Nat.card_eq_fintype_card, + Fintype.card_fin, AdjoinRoot.powerBasis'_dim, hf.natDegree_map] + +end GaloisRing + +/-! ### The residue field -/ + +namespace GaloisRing + +variable {p n : ℕ} {f : ℤ[X]} [NeZero n] [Fact p.Prime] [hf : Fact f.Monic] + [hirr : Fact (Irreducible (f.map (Int.castRingHom (ZMod p))))] + +instance instIsLocalRing : IsLocalRing (GaloisRing p n f) := + isLocalRing hf.out hirr.out + +/-- **The maximal ideal of a Galois ring is `(p)`.** -/ +theorem maximalIdeal_eq : maximalIdeal (GaloisRing p n f) = Ideal.span {(p : GaloisRing p n f)} := + by rw [← ker_residue hf.out, IsLocalRing.ker_eq_maximalIdeal _ (residue_surjective p n f)] + +/-- **The residue field of a Galois ring** is `𝔽_p[X] / (f)`, by reduction modulo `p`. -/ +noncomputable def residueFieldEquiv : + ResidueField (GaloisRing p n f) ≃+* AdjoinRoot (f.map (Int.castRingHom (ZMod p))) := + (Ideal.quotEquivOfEq (IsLocalRing.ker_eq_maximalIdeal _ (residue_surjective p n f)).symm).trans + (RingHom.quotientKerEquivOfSurjective (residue_surjective p n f)) + +@[simp] +theorem residueFieldEquiv_residue (x : GaloisRing p n f) : + residueFieldEquiv (IsLocalRing.residue _ x) = residue p n f x := by + simp only [residueFieldEquiv] + rfl + +/-- **The residue field is the Galois field** with `p ^ deg f` elements. -/ +noncomputable def algEquivGaloisField : + AdjoinRoot (f.map (Int.castRingHom (ZMod p))) ≃ₐ[ZMod p] GaloisField p f.natDegree := + GaloisField.algEquivGaloisField p f.natDegree (natCard_adjoinRoot hf.out) + +end GaloisRing diff --git a/ComplexityTests/Foundations/Data/Polynomial/RingLagrange.lean b/ComplexityTests/Foundations/Data/Polynomial/RingLagrange.lean new file mode 100644 index 0000000000..94333bd0bc --- /dev/null +++ b/ComplexityTests/Foundations/Data/Polynomial/RingLagrange.lean @@ -0,0 +1,54 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Foundations.Data.Polynomial.RingLagrange +import Mathlib.Data.ZMod.Basic +import Mathlib.Tactic.FinCases + +/-! Tests for Lagrange interpolation over a commutative ring. -/ + +namespace ComplexityTests.RingLagrange + +open Polynomial Lagrange + +/-! ### Interpolation over `ZMod 8`, which is not a domain -/ + +/-- The nodes `0` and `1` of `ZMod 8` differ by a unit. -/ +theorem pairwise_isUnit : + ((Finset.univ : Finset (Fin 2)) : Set (Fin 2)).Pairwise + fun i j => IsUnit ((![0, 1] : Fin 2 → ZMod 8) i - ![0, 1] j) := by + intro i _ j _ hij + fin_cases i <;> fin_cases j + · exact absurd rfl hij + · simp + · simp + · exact absurd rfl hij + +/-- The interpolant of the values `3, 5` at `0, 1` takes those values. -/ +example : (ringInterpolate Finset.univ (![0, 1] : Fin 2 → ZMod 8) ![3, 5]).eval 0 = 3 ∧ + (ringInterpolate Finset.univ (![0, 1] : Fin 2 → ZMod 8) ![3, 5]).eval 1 = 5 := + ⟨eval_ringInterpolate_at_node pairwise_isUnit _ (Finset.mem_univ (0 : Fin 2)), + eval_ringInterpolate_at_node pairwise_isUnit _ (Finset.mem_univ (1 : Fin 2))⟩ + +/-- A linear polynomial over `ZMod 8` is the interpolant of its values at `0` and `1`. -/ +example : (C 3 + C 2 * X : (ZMod 8)[X]) = + ringInterpolate Finset.univ (![0, 1] : Fin 2 → ZMod 8) + (fun i => (C 3 + C 2 * X : (ZMod 8)[X]).eval (![0, 1] i)) := + eq_ringInterpolate pairwise_isUnit ((degree_add_le _ _).trans_lt (max_lt + (degree_C_le.trans_lt (by decide)) ((degree_C_mul_X_le _).trans_lt (by decide)))) + +/-! ### Unit differences are needed -/ + +/-- Over `ZMod 4`, the nodes `0` and `2` differ by the non-unit `2`, and the nonzero polynomial +`2 X` of degree below two vanishes at both: uniqueness fails without unit differences. -/ +example : (C 2 * X : (ZMod 4)[X]).eval 0 = 0 ∧ (C 2 * X : (ZMod 4)[X]).eval 2 = 0 ∧ + (C 2 * X : (ZMod 4)[X]) ≠ 0 := by + refine ⟨by simp, by simp only [eval_mul, eval_C, eval_X]; decide, fun h => ?_⟩ + have := congrArg (coeff · 1) h + simp only [coeff_C_mul, coeff_X_one, mul_one, coeff_zero] at this + exact absurd this (by decide) + +end ComplexityTests.RingLagrange diff --git a/ComplexityTests/Foundations/NumberTheory/Padics/AdjoinRootValuation.lean b/ComplexityTests/Foundations/NumberTheory/Padics/AdjoinRootValuation.lean new file mode 100644 index 0000000000..1219f44ba2 --- /dev/null +++ b/ComplexityTests/Foundations/NumberTheory/Padics/AdjoinRootValuation.lean @@ -0,0 +1,98 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Foundations.NumberTheory.Padics.AdjoinRootValuation +import Mathlib.Algebra.Field.ZMod +import Mathlib.Algebra.Polynomial.SpecificDegree +import Mathlib.Tactic.ComputeDegree +import Mathlib.Tactic.FinCases + +/-! Tests for the 2-adic valuation of `K = ℚ[U] / (U ^ 2 + U + 1)`, the case `e = 2` of the +coefficient field of [OpenAI2026ETH], §D.4.1. -/ + +namespace ComplexityTests.AdjoinRootValuation + +open Polynomial +open scoped WithZero + +/-- `U ^ 2 + U + 1`, irreducible modulo 2. -/ +noncomputable def f : ℤ[X] := X ^ 2 + X + 1 + +theorem f_monic : f.Monic := by unfold f; monicity! + +theorem f_irreducible : Irreducible (f.map (Int.castRingHom (ZMod 2))) := by + unfold f + simp only [Polynomial.map_add, Polynomial.map_pow, Polynomial.map_X, Polynomial.map_one] + refine Polynomial.irreducible_of_degree_le_three_of_not_isRoot ?_ ?_ + · have : (X ^ 2 + X + 1 : (ZMod 2)[X]).natDegree = 2 := by compute_degree! + simp [this] + · intro x hx + rw [IsRoot.def, eval_add, eval_add, eval_pow, eval_X, eval_one] at hx + fin_cases x <;> revert hx <;> decide + +/-- The field `K = ℚ[U] / (U ^ 2 + U + 1)`. -/ +abbrev K : Type := AdjoinRoot (f.map (Int.castRingHom ℚ)) + +/-- The 2-adic valuation of `K`. -/ +noncomputable abbrev v : Valuation K ℤᵐ⁰ := AdjoinRoot.padicValuation 2 f_monic f_irreducible + +/-- The map `ℤ[U] / (f) → K`. -/ +noncomputable abbrev ι : AdjoinRoot f →+* K := AdjoinRoot.map (Int.castRingHom ℚ) f _ dvd_rfl + +instance : Fact (Irreducible (f.map (Int.castRingHom (ZMod 2)))) := ⟨f_irreducible⟩ + +/-- `U ^ 2 + U + 1 = 0` modulo `2`. -/ +theorem root_sq_add_root_add_one : + (AdjoinRoot.root (f.map (Int.castRingHom (ZMod 2)))) ^ 2 + AdjoinRoot.root _ + 1 = 0 := by + rw [← AdjoinRoot.mk_X, ← map_pow, ← map_add, ← map_one (AdjoinRoot.mk _), ← map_add, + AdjoinRoot.mk_eq_zero] + exact dvd_of_eq (by simp [f]) + +/-- `U` is nonzero modulo `2`: otherwise `f(0) = 1` would vanish. -/ +theorem root_ne_zero_mod_two : + AdjoinRoot.map (Int.castRingHom (ZMod 2)) f _ dvd_rfl (AdjoinRoot.root f) ≠ 0 := by + rw [AdjoinRoot.map_root] + intro h + have := root_sq_add_root_add_one + rw [h] at this + simp at this + +/-- `1 + U` is nonzero modulo `2`: it is `U²`, the square of a nonzero element of a field. -/ +theorem one_add_root_ne_zero_mod_two : + AdjoinRoot.map (Int.castRingHom (ZMod 2)) f _ dvd_rfl (1 + AdjoinRoot.root f) ≠ 0 := by + have hroot := root_ne_zero_mod_two + rw [AdjoinRoot.map_root] at hroot + rw [map_add, map_one, AdjoinRoot.map_root] + intro h1 + have h := root_sq_add_root_add_one + rw [add_assoc, add_comm (AdjoinRoot.root _), h1, add_zero] at h + exact pow_ne_zero 2 hroot h + +/-! ### The valuation on concrete elements -/ + +theorem padicValRat_two : padicValRat 2 (2 : ℚ) = 1 := by + exact_mod_cast padicValRat.self (p := 2) one_lt_two + +/-- `v (1 / 2) = exp 1`: the valuation extends that of `ℚ`. -/ +example : v (algebraMap ℚ K (1 / 2)) = WithZero.exp 1 := by + rw [AdjoinRoot.padicValuation_algebraMap] + simp [Rat.padicValuation, padicValRat_two] + +/-- `v (2 U) = v 2 * v U = exp (-1)`, by multiplicativity: `U` is nonzero modulo `2`. -/ +example : v (algebraMap ℚ K 2 * ι (AdjoinRoot.root f)) = WithZero.exp (-1) := by + rw [map_mul, AdjoinRoot.padicValuation_algebraMap, + (AdjoinRoot.padicValuation_map_eq_one_iff f_monic f_irreducible _).mpr root_ne_zero_mod_two, + mul_one] + simp [Rat.padicValuation, padicValRat_two] + +/-- `1 + U` is integral and nonzero modulo `2`, hence a unit of the valuation ring. -/ +example : ∃ h : ι (1 + AdjoinRoot.root f) ∈ v.integer, IsUnit (⟨_, h⟩ : v.integer) := + have h := AdjoinRoot.padicValuation_map_le_one f_monic f_irreducible (1 + AdjoinRoot.root f) + ⟨h, AdjoinRoot.isUnit_of_padicValuation_eq_one f_monic f_irreducible h + ((AdjoinRoot.padicValuation_map_eq_one_iff f_monic f_irreducible _).mpr + one_add_root_ne_zero_mod_two)⟩ + +end ComplexityTests.AdjoinRootValuation diff --git a/ComplexityTests/Foundations/RingTheory/GaloisRing.lean b/ComplexityTests/Foundations/RingTheory/GaloisRing.lean new file mode 100644 index 0000000000..c11fcc94d8 --- /dev/null +++ b/ComplexityTests/Foundations/RingTheory/GaloisRing.lean @@ -0,0 +1,111 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Foundations.RingTheory.GaloisRing +import Mathlib.Algebra.Polynomial.SpecificDegree +import Mathlib.Tactic.ComputeDegree +import Mathlib.Tactic.FinCases + +/-! Tests for Galois rings, on `(ℤ / 8)[U] / (U ^ 2 + U + 1)`: the case `p = 2`, `e = 2` and +precision `3` of the coefficient ring of [OpenAI2026ETH], §D.4.1. -/ + +namespace ComplexityTests.GaloisRing + +open Polynomial IsLocalRing + +/-- `U ^ 2 + U + 1`, irreducible modulo 2. -/ +noncomputable def f : ℤ[X] := X ^ 2 + X + 1 + +theorem f_monic : f.Monic := by unfold f; monicity! + +theorem f_natDegree : f.natDegree = 2 := by unfold f; compute_degree! + +theorem f_irreducible : Irreducible (f.map (Int.castRingHom (ZMod 2))) := by + unfold f + simp only [Polynomial.map_add, Polynomial.map_pow, Polynomial.map_X, Polynomial.map_one] + refine Polynomial.irreducible_of_degree_le_three_of_not_isRoot ?_ ?_ + · have : (X ^ 2 + X + 1 : (ZMod 2)[X]).natDegree = 2 := by compute_degree! + simp [this] + · intro x hx + rw [IsRoot.def, eval_add, eval_add, eval_pow, eval_X, eval_one] at hx + fin_cases x <;> revert hx <;> decide + +instance : Fact f.Monic := ⟨f_monic⟩ + +instance : Fact (Irreducible (f.map (Int.castRingHom (ZMod 2)))) := ⟨f_irreducible⟩ + +/-- The ring `(ℤ / 8)[U] / (U ^ 2 + U + 1)`. -/ +abbrev A : Type := GaloisRing 2 3 f + +/-- The root `U`. -/ +noncomputable abbrev U : A := AdjoinRoot.root _ + +/-! ### Locality and units -/ + +/-- The residue field has characteristic two. -/ +theorem two_eq_zero : (2 : AdjoinRoot (f.map (Int.castRingHom (ZMod 2)))) = 0 := by + rw [← map_ofNat (AdjoinRoot.of _), show (OfNat.ofNat 2 : ZMod 2) = 0 by decide, map_zero] + +/-- The ring is local. -/ +example : IsLocalRing A := inferInstance + +/-- `2` is not a unit: it vanishes modulo `2`. -/ +example : ¬IsUnit (2 : A) := by + rw [GaloisRing.isUnit_iff f_monic f_irreducible, map_ofNat, not_not] + exact two_eq_zero + +/-- `1 + 2 U` is a unit: it is one modulo `2`. -/ +example : IsUnit (1 + 2 * U) := by + rw [GaloisRing.isUnit_iff f_monic f_irreducible] + simp only [map_add, map_mul, map_one, map_ofNat, two_eq_zero, zero_mul, add_zero] + exact one_ne_zero + +/-- `2` generates the maximal ideal. -/ +example : (2 : A) ∈ maximalIdeal A := by + rw [GaloisRing.maximalIdeal_eq] + exact_mod_cast Ideal.mem_span_singleton_self (2 : A) + +/-- The residue field has `2 ^ 2 = 4` elements. -/ +example : Nat.card (AdjoinRoot (f.map (Int.castRingHom (ZMod 2)))) = 4 := by + rw [GaloisRing.natCard_adjoinRoot f_monic, f_natDegree] + norm_num + +/-! ### Digit lifts and interpolation -/ + +/-- The digit vector `(0, 1)`. -/ +def c₀₁ : Fin f.natDegree → ZMod 2 := fun i => if (i : ℕ) = 1 then 1 else 0 + +/-- The digit vector `(1, 0)`. -/ +def c₁₀ : Fin f.natDegree → ZMod 2 := fun i => if (i : ℕ) = 0 then 1 else 0 + +/-- The lift of `(0, 1)` is `U`, and the lift of `(1, 0)` is `1`. -/ +theorem digitLift_c₀₁ : + GaloisRing.digitLift 2 3 f c₀₁ = U ∧ GaloisRing.digitLift 2 3 f c₁₀ = 1 := by + simp only [GaloisRing.digitLift, Fin.sum_univ_eq_sum_range + (fun i => (((if i = 1 then 1 else 0 : ZMod 2)).val : A) * AdjoinRoot.root _ ^ i), + Fin.sum_univ_eq_sum_range + (fun i => (((if i = 0 then 1 else 0 : ZMod 2)).val : A) * AdjoinRoot.root _ ^ i), + c₀₁, c₁₀, f_natDegree, Finset.sum_range_succ, Finset.sum_range_zero] + exact ⟨by simp [ZMod.val_one]; rfl, by simp [ZMod.val_one]⟩ + +/-- `U - 1` is a unit, as the difference of two distinct digit lifts. -/ +example : IsUnit (U - 1) := by + rw [← digitLift_c₀₁.1, ← digitLift_c₀₁.2] + refine GaloisRing.isUnit_digitLift_sub f_monic f_irreducible fun h => ?_ + have := congrFun h ⟨1, by rw [f_natDegree]; norm_num⟩ + simp [c₀₁, c₁₀] at this + +/-- There are `4` digit vectors, so a cubic over the ring is the interpolant of its values at the +four digit lifts. -/ +example (P : A[X]) (hP : P.natDegree ≤ 3) : + P = Lagrange.ringInterpolate Finset.univ (GaloisRing.digitLift 2 3 f) + (fun c => P.eval (GaloisRing.digitLift 2 3 f c)) := by + refine GaloisRing.eq_ringInterpolate_digitLift f_monic f_irreducible + ((degree_le_natDegree).trans_lt ?_) + rw [Finset.card_univ, Fintype.card_fun, ZMod.card, Fintype.card_fin, f_natDegree] + exact_mod_cast (show P.natDegree < 2 ^ 2 by omega) + +end ComplexityTests.GaloisRing From 31ff8a6ae3d937e6263f8f29045eb1005a0cf90d Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 18:40:37 -0400 Subject: [PATCH 005/129] feat(circuit/frontier): Lemma D.4, integral local coordinates of the Cauchy kernel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit T2.3 of #1538 (OpenAI2026ETH, §D.4.3, Lemma D.4, pp. 59-60), stacked on #1845. - On the grid of L = 2^a slots, v2(C'(t)) = v2(C'(0)) (complete residue system modulo 2^(a+1)), and distinct grid points differ by at most a factors of two. - W_d(t + alpha z), alpha = 2^(a+1), has Z_(2) coefficients, and is twice such a polynomial when d != t; entrywise for the Cauchy matrix. - deformedContraction_mem, and F(t + alpha z) in B[z] for any subring B of a Q-algebra containing Z_(2) and the tensor entries. - Rat.padicIntegerToZModPow: reduction of Z_(p) modulo p^n, through Z_[p]. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 2 + .../TensorNetwork/IntegralCoordinates.lean | 365 ++++++++++++++++++ .../Padics/RatIntegerReduction.lean | 74 ++++ .../TensorNetworkIntegralCoordinates.lean | 110 ++++++ 4 files changed, 551 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/IntegralCoordinates.lean create mode 100644 Complexity/Foundations/NumberTheory/Padics/RatIntegerReduction.lean create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkIntegralCoordinates.lean diff --git a/Complexity.lean b/Complexity.lean index 8ea2c2b140..ffc52e5b23 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -861,6 +861,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness public import Complexity.Computability.Circuit.Frontier.Unconditional public import Complexity.Computability.Circuit.Frontier.Witness @@ -2748,6 +2749,7 @@ public import Complexity.Foundations.NumberTheory.AKS.Criterion public import Complexity.Foundations.NumberTheory.AKS.Introspective public import Complexity.Foundations.NumberTheory.AKS.SmallR public import Complexity.Foundations.NumberTheory.Harmonic +public import Complexity.Foundations.NumberTheory.Padics.RatIntegerReduction public import Complexity.Foundations.NumberTheory.PrimeCounting public import Complexity.Foundations.NumberTheory.PrimeWindow public import Complexity.Foundations.NumberTheory.SqrtPrimeWindow diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/IntegralCoordinates.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/IntegralCoordinates.lean new file mode 100644 index 0000000000..d44097932c --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/IntegralCoordinates.lean @@ -0,0 +1,365 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation +public import Mathlib.Algebra.Polynomial.Lifts +public import Mathlib.NumberTheory.Padics.PadicNumbers +public import Mathlib.RingTheory.Valuation.Integers +import Mathlib.NumberTheory.Padics.PadicVal.Basic +import Mathlib.Tactic.FieldSimp +import Mathlib.Tactic.LinearCombination + +/-! +# Integral local coordinates for the Cauchy kernel + +This file proves Lemma D.4 of [OpenAI2026ETH] (§D.4.3, p. 59; proof on p. 60): near each grid +point the Cauchy kernel has `2`-integral coefficients. Let `L = 2 ^ a`, so that the grid +`S = {-L, …, L - 1}` is a complete residue system modulo `α = 2 ^ (a + 1)`. For `t, d ∈ S`, the +polynomial `z ↦ W_d(t + α z)` obtained from the Cauchy kernel `W_d = cauchyKernel L d` has +coefficients in `ℤ_(2)`, the rationals with odd denominator +(`Cslib.Circuits.Frontier.cauchyKernel_comp_mem_lifts`); when `d ≠ t` it is moreover divisible by +`2` (`Cslib.Circuits.Frontier.exists_cauchyKernel_comp_eq_two_mul`). Here `ℤ_(2)` is the valuation +ring `(Rat.padicValuation 2).integer`, and "coefficients in `ℤ_(2)`" is membership in +`Polynomial.lifts` of its inclusion. + +The proof is the manuscript's. Since `S` is a complete residue system modulo `2 ^ (a + 1)`, the +nonzero differences `t - q`, `q ∈ S`, have the same `2`-adic valuations for every `t`; so +`C'(t) = ∏_{q ≠ t} (t - q)` has the valuation of `C'(0)` +(`Cslib.Circuits.Frontier.padicValuation_prod_sub_eq`). Each factor `t - q + α z` with `q ≠ t` is +`(t - q) (1 + α / (t - q) z)` with `α / (t - q)` divisible by `2`, and the factor `q = t` is +`α z`. + +Entrywise, the Cauchy matrix has the same property +(`Cslib.Circuits.Frontier.cauchyMatrix_comp_mem_lifts`). Consequently, for tensors with entries in +a subring `B` of a `ℚ`-algebra `R` that contains `ℤ_(2)`, the deformed contraction `F(T)` along the +Cauchy matrix satisfies `F(t + α z) ∈ B[z]` +(`Cslib.Circuits.Frontier.TensorNetwork.deformedContraction_cauchyMatrix_comp_mem_liftsRing`): the +manuscript's `F(t + α z) ∈ A[z]` with `B = O`. Coefficients in `ℤ_(2)` reduce modulo `2 ^ p` +through `Rat.padicIntegerToZModPow`. The algorithmic half of the lemma, evaluating +`W_xy(t + α ζ) mod 2 ^ p` in polynomial time, belongs to the runtime development. + +## Main results + +* `Cslib.Circuits.Frontier.padicValuation_prod_sub_eq`: `v₂(C'(t)) = v₂(C'(0))` on the grid. +* `Cslib.Circuits.Frontier.cauchyKernel_comp_mem_lifts`: `W_d(t + α z)` has `2`-integral + coefficients. +* `Cslib.Circuits.Frontier.exists_cauchyKernel_comp_eq_two_mul`: it is divisible by `2` unless + `d = t`. +* `Cslib.Circuits.Frontier.cauchyMatrix_comp_mem_lifts`: the same for the Cauchy matrix. +* `Cslib.Circuits.Frontier.TensorNetwork.deformedContraction_mem`: a deformed contraction lies in + any subring containing the tensor and matrix entries. +* `Cslib.Circuits.Frontier.TensorNetwork.deformedContraction_cauchyMatrix_comp_mem_liftsRing`: + `F(t + α z)` has coefficients in any subring containing `ℤ_(2)` and the tensor entries. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.3, Lemma D.4, + pp. 59–60. +-/ + +public section + +namespace Cslib.Circuits.Frontier + +open Polynomial +open scoped WithZero + +/-! ### Two-adic valuations on the grid -/ + +/-- The `2`-adic valuation of a nonzero integer. -/ +private theorem padicValuation_intCast {x : ℤ} (hx : x ≠ 0) : + Rat.padicValuation 2 (x : ℚ) = WithZero.exp (-(padicValInt 2 x : ℤ)) := by + simp [Rat.padicValuation, hx, padicValRat.of_int] + +/-- The `2`-adic valuation of a power of two. -/ +theorem padicValuation_two_pow (k : ℕ) : + Rat.padicValuation 2 ((2 : ℚ) ^ k) = WithZero.exp (-(k : ℤ)) := by + have h2 : Rat.padicValuation 2 (2 : ℚ) = WithZero.exp (-1) := by + exact_mod_cast Rat.padicValuation_self 2 + rw [map_pow, h2, ← WithZero.exp_nsmul, nsmul_eq_mul, mul_neg, mul_one] + +/-- **A nonzero integer below `2 ^ (a + 1)` in absolute value has at most `a` factors of two.** -/ +theorem padicValuation_two_pow_le {a : ℕ} {x : ℤ} (hx : x ≠ 0) (hlt : |x| < 2 ^ (a + 1)) : + Rat.padicValuation 2 ((2 : ℚ) ^ a) ≤ Rat.padicValuation 2 (x : ℚ) := by + rw [padicValuation_two_pow, padicValuation_intCast hx, WithZero.exp_le_exp, neg_le_neg_iff] + by_contra h + have hdvd : (2 : ℤ) ^ (a + 1) ∣ x := by + have := (padicValInt_dvd_iff (p := 2) (a + 1) x).mpr (Or.inr (by omega)) + exact_mod_cast this + exact absurd (Int.le_of_dvd (abs_pos.mpr hx) ((dvd_abs _ _).mpr hdvd)) (not_le.mpr hlt) + +/-- Adding `2 ^ (a + 1)` to a nonzero integer below it in absolute value keeps its `2`-adic +valuation. -/ +theorem padicValuation_add_two_pow {a : ℕ} {x : ℤ} (hx : x ≠ 0) (hlt : |x| < 2 ^ (a + 1)) : + Rat.padicValuation 2 ((x : ℚ) + 2 ^ (a + 1)) = Rat.padicValuation 2 (x : ℚ) := by + refine (Rat.padicValuation 2).map_add_eq_of_lt_left (lt_of_lt_of_le ?_ + (padicValuation_two_pow_le hx hlt)) + rw [padicValuation_two_pow, padicValuation_two_pow, WithZero.exp_lt_exp] + omega + +/-- **The grid is a complete residue system.** For `t` on the grid of `L = 2 ^ a` slots, the +nonzero differences `t - q`, `q ∈ S`, have the valuations of `1, …, 2 L - 1`: send `q` to the +residue of `t - q` modulo `2 L`. -/ +theorem padicValuation_prod_sub_eq_prod_Ioo {a : ℕ} {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) : + Rat.padicValuation 2 (∏ q ∈ (cauchyGrid (2 ^ a)).erase t, ((t : ℚ) - q)) = + ∏ r ∈ Finset.Ioo (0 : ℤ) (2 ^ (a + 1)), Rat.padicValuation 2 (r : ℚ) := by + rw [map_prod] + set L : ℤ := 2 ^ a with hL + have hL2 : (2 : ℤ) ^ (a + 1) = 2 * L := by rw [hL, pow_succ, mul_comm] + have hLpos : 0 < L := by positivity + rw [mem_cauchyGrid] at ht + simp only [cauchyGrid, Nat.cast_pow, Nat.cast_ofNat, ← hL] at ht ⊢ + rw [hL2] + refine Finset.prod_nbij' (fun q => if q < t then t - q else t - q + 2 * L) + (fun r => if -L ≤ t - r then t - r else t - r + 2 * L) ?_ ?_ ?_ ?_ ?_ + · intro q hq + simp only [Finset.mem_erase, Finset.mem_Ico, Finset.mem_Ioo] at hq ⊢ + split_ifs <;> omega + · intro r hr + simp only [Finset.mem_erase, Finset.mem_Ico, Finset.mem_Ioo] at hr ⊢ + split_ifs <;> omega + · intro q hq + simp only [Finset.mem_erase, Finset.mem_Ico] at hq + split_ifs <;> omega + · intro r hr + simp only [Finset.mem_Ioo] at hr + split_ifs <;> omega + · intro q hq + simp only [Finset.mem_erase, Finset.mem_Ico] at hq + have hne : t - q ≠ 0 := by omega + have habs : |t - q| < 2 ^ (a + 1) := by rw [hL2, abs_lt]; omega + have h2L : (2 : ℚ) * (L : ℚ) = 2 ^ (a + 1) := by exact_mod_cast hL2.symm + have hadd := padicValuation_add_two_pow hne habs + push_cast at hadd + split_ifs + · push_cast; rfl + · push_cast + rw [h2L] + exact hadd.symm + +/-- **`v₂(C'(t)) = v₂(C'(0))` on the grid** of `L = 2 ^ a` slots, where +`C'(t) = ∏_{q ∈ S, q ≠ t} (t - q)`. -/ +theorem padicValuation_prod_sub_eq {a : ℕ} {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) : + Rat.padicValuation 2 (∏ q ∈ (cauchyGrid (2 ^ a)).erase t, ((t : ℚ) - q)) = + Rat.padicValuation 2 (∏ q ∈ (cauchyGrid (2 ^ a)).erase 0, ((0 : ℚ) - q)) := by + have h0 : (0 : ℤ) ∈ cauchyGrid (2 ^ a) := by + have : (0 : ℤ) < ((2 ^ a : ℕ) : ℤ) := by positivity + rw [mem_cauchyGrid] + exact ⟨by linarith, this⟩ + rw [padicValuation_prod_sub_eq_prod_Ioo ht] + exact_mod_cast (padicValuation_prod_sub_eq_prod_Ioo h0).symm + +/-- Distinct grid points of `L = 2 ^ a` slots differ by at most `a` factors of two. -/ +theorem padicValuation_two_pow_le_sub {a : ℕ} {t q : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (hq : q ∈ cauchyGrid (2 ^ a)) (hne : q ≠ t) : + Rat.padicValuation 2 ((2 : ℚ) ^ a) ≤ Rat.padicValuation 2 ((t : ℚ) - q) := by + rw [mem_cauchyGrid] at ht hq + push_cast at ht hq + have hlt : |t - q| < 2 ^ (a + 1) := by + rw [pow_succ, abs_lt] + constructor <;> linarith + exact_mod_cast padicValuation_two_pow_le (sub_ne_zero.mpr hne.symm) hlt + +/-! ### The kernel in local coordinates -/ + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +private theorem C_mem_lifts_of_le_one {c : ℚ} (hc : Rat.padicValuation 2 c ≤ 1) : + C c ∈ lifts (ℤ₍₂₎).subtype := + C_mem_lifts (ℤ₍₂₎).subtype (⟨c, hc⟩ : ℤ₍₂₎) + +/-- A shifted factor `t + α z - q` with `q ≠ t` is `(t - q) (1 + α / (t - q) z)`. -/ +private theorem shift_factor {t q α : ℚ} (h : t - q ≠ 0) : + C t + C α * X - C q = C (t - q) * (1 + C (α / (t - q)) * X) := by + rw [mul_add, mul_one, ← mul_assoc, ← C_mul, mul_div_cancel₀ _ h, C_sub] + ring + +/-- The kernel in local coordinates, as a product of the shifted factors. -/ +private theorem cauchyKernel_comp (L : ℕ) (d : ℤ) (t α : ℚ) : + (cauchyKernel L d).comp (C t + C α * X) = + C ((∏ q ∈ (cauchyGrid L).erase 0, ((0 : ℚ) - q))⁻¹) * + ∏ q ∈ (cauchyGrid L).erase d, (C t + C α * X - C (q : ℚ)) := by + rw [cauchyKernel, Lagrange.eval_nodal, mul_comp, C_comp, Lagrange.nodal, prod_comp] + simp only [sub_comp, X_comp, C_comp] + +/-- The unit factors `1 + α / (t - q) z` with `q ≠ t` are integral. -/ +private theorem prod_one_add_mem_lifts {a : ℕ} {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (s : Finset ℤ) (hs : s ⊆ (cauchyGrid (2 ^ a)).erase t) : + ∏ q ∈ s, (1 + C ((2 : ℚ) ^ (a + 1) / ((t : ℚ) - q)) * X) ∈ lifts (ℤ₍₂₎).subtype := by + refine Subsemiring.prod_mem _ fun q hq => ?_ + obtain ⟨hqt, hqS⟩ := Finset.mem_erase.mp (hs hq) + refine add_mem (one_mem _) (mul_mem (C_mem_lifts_of_le_one ?_) (X_mem_lifts _)) + have hle := padicValuation_two_pow_le_sub ht hqS hqt + have hne : ((t : ℚ) - q) ≠ 0 := sub_ne_zero.mpr (by exact_mod_cast hqt.symm) + rw [map_div₀, div_le_one₀ ((Rat.padicValuation 2).pos_iff.mpr hne)] + refine le_trans ?_ hle + rw [padicValuation_two_pow, padicValuation_two_pow, WithZero.exp_le_exp] + omega + +/-- A product of differences `t - q` over points `q ≠ t` is nonzero. -/ +private theorem prod_sub_ne_zero {t : ℤ} {s : Finset ℤ} (hs : t ∉ s) : + ∏ q ∈ s, ((t : ℚ) - q) ≠ 0 := + Finset.prod_ne_zero_iff.mpr fun q hq => + sub_ne_zero.mpr fun h => hs (by rw [show t = q by exact_mod_cast h]; exact hq) + +/-- Shifted factors at points `q ≠ t` split into a constant and the unit factors. -/ +private theorem prod_shift {t : ℤ} (α : ℚ) {s : Finset ℤ} (hs : t ∉ s) : + ∏ q ∈ s, (C (t : ℚ) + C α * X - C (q : ℚ)) = + C (∏ q ∈ s, ((t : ℚ) - q)) * ∏ q ∈ s, (1 + C (α / ((t : ℚ) - q)) * X) := by + calc + ∏ q ∈ s, (C (t : ℚ) + C α * X - C (q : ℚ)) = + ∏ q ∈ s, (C ((t : ℚ) - q) * (1 + C (α / ((t : ℚ) - q)) * X)) := + Finset.prod_congr rfl fun (q : ℤ) hq => shift_factor + (sub_ne_zero.mpr fun h => hs (by rw [show t = q by exact_mod_cast h]; exact hq)) + _ = _ := by rw [Finset.prod_mul_distrib, map_prod] + +/-- **Lemma D.4, divisibility.** Away from its own node, `d ≠ t`, the Cauchy kernel in local +coordinates `z ↦ W_d(t + α z)`, `α = 2 ^ (a + 1)`, is twice a polynomial with `2`-integral +coefficients: the factor `α z` survives, and `α / (t - d)` is divisible by `2`. -/ +theorem exists_cauchyKernel_comp_eq_two_mul {a : ℕ} {t d : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (hd : d ∈ cauchyGrid (2 ^ a)) (hdt : d ≠ t) : + ∃ P ∈ lifts (ℤ₍₂₎).subtype, + (cauchyKernel (2 ^ a) d).comp (C (t : ℚ) + C ((2 : ℚ) ^ (a + 1)) * X) = C 2 * P := by + have htd : t ∈ (cauchyGrid (2 ^ a)).erase d := Finset.mem_erase.mpr ⟨hdt.symm, ht⟩ + set s := ((cauchyGrid (2 ^ a)).erase d).erase t with hs_def + have hts : t ∉ s := Finset.notMem_erase t _ + have hs : s ⊆ (cauchyGrid (2 ^ a)).erase t := fun q hq => by + simp only [s, Finset.mem_erase] at hq ⊢ + exact ⟨hq.1, hq.2.2⟩ + have htd' : ((t : ℚ) - d) ≠ 0 := sub_ne_zero.mpr (by exact_mod_cast hdt.symm) + have hdt' : d ∈ (cauchyGrid (2 ^ a)).erase t := Finset.mem_erase.mpr ⟨hdt, hd⟩ + have hCt : ∏ q ∈ (cauchyGrid (2 ^ a)).erase t, ((t : ℚ) - q) = + ((t : ℚ) - d) * ∏ q ∈ s, ((t : ℚ) - q) := by + rw [hs_def, Finset.erase_right_comm] + exact (Finset.mul_prod_erase _ (fun q : ℤ => (t : ℚ) - q) hdt').symm + obtain ⟨C₀, hC₀def⟩ : ∃ C₀ : ℚ, C₀ = ∏ q ∈ (cauchyGrid (2 ^ a)).erase 0, ((0 : ℚ) - q) := + ⟨_, rfl⟩ + have hC₀ : C₀ ≠ 0 := by + have := prod_sub_ne_zero (Finset.notMem_erase (0 : ℤ) (cauchyGrid (2 ^ a))) + push_cast at this + rwa [hC₀def] + obtain ⟨c, hc⟩ : ∃ c : ℚ, c = C₀⁻¹ * (2 ^ (a + 1) * ∏ q ∈ s, ((t : ℚ) - q)) := ⟨_, rfl⟩ + refine ⟨C (c / 2) * X * ∏ q ∈ s, (1 + C ((2 : ℚ) ^ (a + 1) / ((t : ℚ) - q)) * X), ?_, ?_⟩ + · refine mul_mem (mul_mem (C_mem_lifts_of_le_one ?_) (X_mem_lifts _)) + (prod_one_add_mem_lifts ht s hs) + have hc2 : c / 2 = + 2 ^ a * (C₀⁻¹ * ∏ q ∈ (cauchyGrid (2 ^ a)).erase t, ((t : ℚ) - q)) / ((t : ℚ) - d) := by + rw [hc, hCt, pow_succ] + field_simp + have hval : + Rat.padicValuation 2 (C₀⁻¹ * ∏ q ∈ (cauchyGrid (2 ^ a)).erase t, ((t : ℚ) - q)) = 1 := by + rw [map_mul, map_inv₀, padicValuation_prod_sub_eq ht, ← hC₀def, + inv_mul_cancel₀ ((Rat.padicValuation 2).ne_zero_iff.mpr hC₀)] + rw [hc2, map_div₀, map_mul, hval, mul_one, + div_le_one₀ ((Rat.padicValuation 2).pos_iff.mpr htd')] + exact padicValuation_two_pow_le_sub ht hd hdt + · rw [cauchyKernel_comp, ← Finset.mul_prod_erase _ _ htd, prod_shift _ hts, ← hC₀def, + show C (t : ℚ) + C ((2 : ℚ) ^ (a + 1)) * X - C (t : ℚ) = C ((2 : ℚ) ^ (a + 1)) * X by ring] + have h2 : C (2 : ℚ) * C (c / 2) = C c := by rw [← C_mul, mul_div_cancel₀ _ two_ne_zero] + have hcC : C c = C C₀⁻¹ * (C ((2 : ℚ) ^ (a + 1)) * C (∏ q ∈ s, ((t : ℚ) - q))) := by + rw [hc, C_mul, C_mul] + linear_combination + (-(X * ∏ q ∈ s, (1 + C ((2 : ℚ) ^ (a + 1) / ((t : ℚ) - q)) * X))) * h2 - + (X * ∏ q ∈ s, (1 + C ((2 : ℚ) ^ (a + 1) / ((t : ℚ) - q)) * X)) * hcC + +/-- **Lemma D.4, integrality.** For grid points `t, d` of `L = 2 ^ a` slots, the Cauchy kernel in +local coordinates `z ↦ W_d(t + α z)`, `α = 2 ^ (a + 1)`, has coefficients in `ℤ_(2)`. At its own +node, `d = t`, its constant term is `C'(t) / C'(0)`, of valuation one, and the other factors are +`1 + α / (t - q) z`. -/ +theorem cauchyKernel_comp_mem_lifts {a : ℕ} {t d : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (hd : d ∈ cauchyGrid (2 ^ a)) : + (cauchyKernel (2 ^ a) d).comp (C (t : ℚ) + C ((2 : ℚ) ^ (a + 1)) * X) ∈ + lifts (ℤ₍₂₎).subtype := by + rcases eq_or_ne d t with rfl | hdt + · have hts : d ∉ (cauchyGrid (2 ^ a)).erase d := Finset.notMem_erase d _ + rw [cauchyKernel_comp, prod_shift _ hts, ← mul_assoc, ← C_mul] + refine mul_mem (C_mem_lifts_of_le_one (le_of_eq ?_)) (prod_one_add_mem_lifts ht _ subset_rfl) + have hC₀ := prod_sub_ne_zero (Finset.notMem_erase (0 : ℤ) (cauchyGrid (2 ^ a))) + push_cast at hC₀ + rw [map_mul, map_inv₀, padicValuation_prod_sub_eq ht, + inv_mul_cancel₀ ((Rat.padicValuation 2).ne_zero_iff.mpr hC₀)] + · obtain ⟨P, hP, h⟩ := exists_cauchyKernel_comp_eq_two_mul ht hd hdt + rw [h] + refine mul_mem (C_mem_lifts_of_le_one ?_) hP + have h2 := padicValuation_two_pow 1 + rw [pow_one] at h2 + rw [h2, ← WithZero.exp_zero, WithZero.exp_le_exp] + norm_num + +/-! ### The Cauchy matrix and the deformed contraction -/ + +/-- **Lemma D.4 for the Cauchy matrix.** For a packing into packets of `2 ^ a` slots, every entry +of the Cauchy matrix in local coordinates `z ↦ W(t + α z)` has `2`-integral coefficients. -/ +theorem cauchyMatrix_comp_mem_lifts {E P : Type*} [DecidableEq P] {a : ℕ} {packet : E → P} + {slot : E → ℕ} (hslot : ∀ e, slot e < 2 ^ a) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (e e' : E) : + (cauchyMatrix (2 ^ a) packet slot e e').comp (C (t : ℚ) + C ((2 : ℚ) ^ (a + 1)) * X) ∈ + lifts (ℤ₍₂₎).subtype := by + unfold cauchyMatrix + split_ifs + · exact cauchyKernel_comp_mem_lifts ht (sub_mem_cauchyGrid (hslot e) (hslot e')) + · rw [zero_comp] + exact zero_mem _ + +namespace TensorNetwork + +variable {R V E : Type*} [CommRing R] [Fintype V] [Fintype E] [DecidableEq E] + +/-- A deformed contraction lies in every subring that contains the tensor entries and the matrix +entries: it is a polynomial expression in them. -/ +theorem deformedContraction_mem (N : TensorNetwork R Bool V E) (side : V → Bool) + (W : Matrix E E R) (T : Subring R) (hN : ∀ v x, N.tensor v x ∈ T) + (hW : ∀ e e', W e e' ∈ T) : N.deformedContraction side W ∈ T := by + unfold deformedContraction + refine T.sum_mem fun X _ => T.sum_mem fun Y _ => T.mul_mem (T.mul_mem + (T.prod_mem fun v _ => hN v Y) (T.prod_mem fun v _ => hN v X)) (T.prod_mem fun e _ => ?_) + have hv : W.mulVec (fun e' => if X e' then (1 : R) else 0) e ∈ T := by + unfold Matrix.mulVec dotProduct + refine T.sum_mem fun e' _ => T.mul_mem (hW e e') ?_ + dsimp only + split_ifs + exacts [T.one_mem, T.zero_mem] + unfold affineFactor + split_ifs + exacts [hv, T.sub_mem T.one_mem hv] + +/-- **Lemma D.4 for the deformed contraction.** Let `R` be a `ℚ`-algebra and `B` a subring of `R` +containing `ℤ_(2)`, and let the tensors have entries in `B`. Along the Cauchy matrix of a packing +into packets of `2 ^ a` slots, the deformed contraction `F(T)` satisfies `F(t + α z) ∈ B[z]` at +every grid point `t`, with `α = 2 ^ (a + 1)`. For the manuscript, `R = K`, `B = O` and `F(0)` is +the contraction (`eval_zero_deformedContraction_cauchyMatrix`). -/ +theorem deformedContraction_cauchyMatrix_comp_mem_liftsRing [Algebra ℚ R] {P : Type*} + [DecidableEq P] (N : TensorNetwork R Bool V E) (side : V → Bool) {a : ℕ} {packet : E → P} + {slot : E → ℕ} (hslot : ∀ e, slot e < 2 ^ a) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (B : Subring R) (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ R c ∈ B) (hN : ∀ v x, N.tensor v x ∈ B) : + ((N.map Polynomial.C).deformedContraction side + ((cauchyMatrix (2 ^ a) packet slot).map (Polynomial.mapRingHom (algebraMap ℚ R)))).comp + (C (t : R) + C (2 ^ (a + 1)) * X) ∈ liftsRing B.subtype := by + have hq : (C (t : R) + C (2 ^ (a + 1)) * X : R[X]) = + (C (t : ℚ) + C ((2 : ℚ) ^ (a + 1)) * X).map (algebraMap ℚ R) := by + rw [Polynomial.map_add, Polynomial.map_mul, Polynomial.map_C, Polynomial.map_C, + Polynomial.map_X] + exact congrArg₂ (· + ·) (congrArg C (map_intCast _ t).symm) + (congrArg (C · * X) (by rw [map_pow, map_ofNat])) + rw [← coe_compRingHom_apply, ← deformedContraction_map] + refine deformedContraction_mem _ side _ _ (fun v x => ?_) (fun e e' => ?_) + · rw [map_tensor, map_tensor, coe_compRingHom_apply, C_comp, ← lifts_iff_liftsRing] + exact C_mem_lifts B.subtype ⟨_, hN v x⟩ + · rw [Matrix.map_apply, Matrix.map_apply, coe_compRingHom_apply, coe_mapRingHom, hq, + ← map_comp, ← lifts_iff_liftsRing, lifts_iff_coeff_lifts] + intro n + obtain ⟨c, hc⟩ := (lifts_iff_coeff_lifts _).mp + (cauchyMatrix_comp_mem_lifts (packet := packet) hslot ht e e') n + rw [coeff_map, ← hc] + exact ⟨⟨_, hB c c.2⟩, rfl⟩ + +end TensorNetwork + +end Cslib.Circuits.Frontier diff --git a/Complexity/Foundations/NumberTheory/Padics/RatIntegerReduction.lean b/Complexity/Foundations/NumberTheory/Padics/RatIntegerReduction.lean new file mode 100644 index 0000000000..044fad57b9 --- /dev/null +++ b/Complexity/Foundations/NumberTheory/Padics/RatIntegerReduction.lean @@ -0,0 +1,74 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate (Mathlib, `Mathlib/NumberTheory/Padics/RingHoms.lean`) + +public import Complexity.Init +public import Mathlib.NumberTheory.Padics.RingHoms +public import Mathlib.RingTheory.Valuation.Integers + +/-! +# Reducing `p`-integral rationals modulo `p ^ n` + +The rationals whose denominator is prime to `p` form the valuation ring `ℤ_(p)` of +`Rat.padicValuation p`. It embeds in the `p`-adic integers (`Rat.padicIntegerToPadicInt`), so +it has reductions modulo every power of `p` (`Rat.padicIntegerToZModPow`). On `a / b` with `b` +prime to `p`, the reduction is `a * b⁻¹`: it is characterized by +`Rat.padicIntegerToZModPow_mul_den`. + +This reduces the integral local coordinates of the Cauchy kernel of [OpenAI2026ETH] (Lemma D.4, +pp. 59–60), whose coefficients lie in `ℤ_(2)`, modulo the working precision `2 ^ p`. + +## Main definitions + +* `Rat.padicIntegerToPadicInt`: the inclusion `ℤ_(p) → ℤ_[p]`. +* `Rat.padicIntegerToZModPow`: reduction `ℤ_(p) → ZMod (p ^ n)`. + +## Main results + +* `Rat.padicIntegerToZModPow_mul_den`: the reduction of `x` times the denominator of `x` is the + numerator of `x`. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.3, pp. 59–60. +-/ + +public section + +namespace Rat + +variable (p : ℕ) [Fact p.Prime] + +/-- The inclusion of the `p`-integral rationals `ℤ_(p)` in the `p`-adic integers. -/ +noncomputable def padicIntegerToPadicInt : (Rat.padicValuation p).integer →+* ℤ_[p] where + toFun x := ⟨(x : ℚ), Padic.norm_rat_le_one (Rat.padicValuation_le_one_iff.mp x.2)⟩ + map_one' := Subtype.ext (by simp) + map_mul' x y := Subtype.ext (by push_cast; rfl) + map_zero' := Subtype.ext (by simp) + map_add' x y := Subtype.ext (by push_cast; rfl) + +@[simp] +theorem coe_padicIntegerToPadicInt (x : (Rat.padicValuation p).integer) : + ((padicIntegerToPadicInt p x : ℤ_[p]) : ℚ_[p]) = ((x : ℚ) : ℚ_[p]) := by + simp only [padicIntegerToPadicInt] + rfl + +/-- Reduction of the `p`-integral rationals modulo `p ^ n`. -/ +noncomputable def padicIntegerToZModPow (n : ℕ) : (Rat.padicValuation p).integer →+* ZMod (p ^ n) := + (PadicInt.toZModPow n).comp (padicIntegerToPadicInt p) + +/-- **The reduction of `a / b` is `a * b⁻¹`**: multiplied by the denominator it gives the +numerator. -/ +theorem padicIntegerToZModPow_mul_den (n : ℕ) (x : (Rat.padicValuation p).integer) : + padicIntegerToZModPow p n x * ((x : ℚ).den : ZMod (p ^ n)) = ((x : ℚ).num : ZMod (p ^ n)) := by + have h : x * ((x : ℚ).den : (Rat.padicValuation p).integer) = + ((x : ℚ).num : (Rat.padicValuation p).integer) := + Subtype.ext (by push_cast; exact Rat.mul_den_eq_num _) + simpa only [map_mul, map_natCast, map_intCast] using congrArg (padicIntegerToZModPow p n) h + +end Rat diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkIntegralCoordinates.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkIntegralCoordinates.lean new file mode 100644 index 0000000000..998c69fca1 --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkIntegralCoordinates.lean @@ -0,0 +1,110 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates +import Complexity.Foundations.NumberTheory.Padics.RatIntegerReduction + +/-! +# Tests for the integral local coordinates of the Cauchy kernel + +With `L = 2` slots the grid is `{-2, -1, 0, 1}`, `C'(0) = -2` and `α = 4`. The kernel at `1` is +`-(T + 2) (T + 1) T / 2`, whose leading coefficient `-1/2` is not `2`-integral; in the local +coordinate `T = 1 + 4 z` it becomes `2`-integral (Lemma D.4). +-/ + +namespace ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates + +open Cslib.Circuits.Frontier Polynomial +open scoped WithZero + +/-- The grid of two slots. -/ +theorem cauchyGrid_two : cauchyGrid 2 = {-2, -1, 0, 1} := by decide + +/-- The leading coefficient of the kernel is `C'(0)⁻¹ = -1/2`. -/ +theorem coeff_cauchyKernel_two_one : (cauchyKernel 2 1).coeff 3 = -1 / 2 := by + have hmem : (1 : ℤ) ∈ cauchyGrid 2 := by decide + have hdeg := natDegree_cauchyKernel hmem + have hlead : (cauchyKernel 2 1).leadingCoeff = + ((Lagrange.nodal ((cauchyGrid 2).erase 0) (Int.cast : ℤ → ℚ)).eval 0)⁻¹ := by + rw [cauchyKernel, leadingCoeff_mul, leadingCoeff_C, Lagrange.nodal_monic.leadingCoeff, + mul_one] + rw [leadingCoeff, hdeg] at hlead + rw [show 3 = 2 * 2 - 1 from rfl, hlead, Lagrange.eval_nodal, cauchyGrid_two] + rw [show ({-2, -1, 0, 1} : Finset ℤ).erase 0 = {-2, -1, 1} by decide] + simp only [Finset.prod_insert (by decide : (-2 : ℤ) ∉ ({-1, 1} : Finset ℤ)), + Finset.prod_insert (by decide : (-1 : ℤ) ∉ ({1} : Finset ℤ)), Finset.prod_singleton] + norm_num + +theorem padicValRat_two : padicValRat 2 (2 : ℚ) = 1 := by + exact_mod_cast padicValRat.self (p := 2) one_lt_two + +/-- **The kernel itself is not `2`-integral**: its leading coefficient `-1/2` has a `2` in the +denominator. -/ +example : cauchyKernel 2 1 ∉ lifts (Rat.padicValuation 2).integer.subtype := by + rw [lifts_iff_coeff_lifts] + intro h + obtain ⟨c, hc⟩ := h 3 + have hle := c.2 + rw [Valuation.mem_integer_iff] at hle + change Rat.padicValuation 2 (c : ℚ) ≤ 1 at hle + have hv : Rat.padicValuation 2 (-1 / 2 : ℚ) = WithZero.exp 1 := by + rw [neg_div, Valuation.map_neg] + simp [Rat.padicValuation, padicValRat_two] + rw [show (c : ℚ) = -1 / 2 by rw [← coeff_cauchyKernel_two_one]; exact hc, hv, + ← WithZero.exp_zero, WithZero.exp_le_exp] at hle + exact absurd hle (by norm_num) + +/-- **In the local coordinate `T = 1 + 4 z` it is `2`-integral** (Lemma D.4, with `a = 1`). -/ +example : + (cauchyKernel 2 1).comp (C 1 + C 4 * X) ∈ lifts (Rat.padicValuation 2).integer.subtype := by + have h := cauchyKernel_comp_mem_lifts (a := 1) (t := 1) (d := 1) (by decide) (by decide) + norm_num at h + exact h + +/-- Away from its own node the kernel in local coordinates is divisible by `2`. -/ +example : ∃ P ∈ lifts (Rat.padicValuation 2).integer.subtype, + (cauchyKernel 2 0).comp (C 1 + C 4 * X) = C 2 * P := by + have h := exists_cauchyKernel_comp_eq_two_mul (a := 1) (t := 1) (d := 0) (by decide) + (by decide) (by decide) + norm_num at h + exact h + +/-! ### Reduction modulo `2 ^ p` -/ + +/-- `1 / 3 ≡ 3 (mod 8)`, since `3 * 3 = 9 ≡ 1`. -/ +example (h : Rat.padicValuation 2 ((3 : ℚ)⁻¹) ≤ 1) : + Rat.padicIntegerToZModPow 2 3 ⟨(3 : ℚ)⁻¹, h⟩ = 3 := by + have h3 := Rat.padicIntegerToZModPow_mul_den 2 3 ⟨(3 : ℚ)⁻¹, h⟩ + have hden : ((3 : ℚ)⁻¹).den = 3 := by simp + have hnum : ((3 : ℚ)⁻¹).num = 1 := by simp + simp only [hden, hnum, Nat.cast_ofNat, Int.cast_one] at h3 + have h9 : (3 * 3 : ZMod (2 ^ 3)) = 1 := by decide + calc Rat.padicIntegerToZModPow 2 3 ⟨(3 : ℚ)⁻¹, h⟩ + = Rat.padicIntegerToZModPow 2 3 ⟨(3 : ℚ)⁻¹, h⟩ * (3 * 3) := by rw [h9, mul_one] + _ = 3 := by rw [← mul_assoc, h3, one_mul] + +/-! ### The deformed contraction -/ + +/-- A row vertex `0` and a column vertex `1` joined by one edge, with integer tensors. -/ +def oneEdge : TensorNetwork ℚ Bool (Fin 2) (Fin 1) where + src _ := 0 + tgt _ := 1 + tensor v x := if v = 0 then (if x 0 then 2 else 1) else (if x 0 then 5 else 3) + tensor_congr v x y h := by rw [h 0 (by fin_cases v <;> simp)] + +/-- With one slot (`a = 0`, `α = 2`), `F(-1 + 2 z)` has `2`-integral coefficients. -/ +example : + ((oneEdge.map Polynomial.C).deformedContraction (fun v => v = 0) + ((cauchyMatrix (2 ^ 0) (fun _ : Fin 1 => ()) (fun _ => 0)).map + (Polynomial.mapRingHom (algebraMap ℚ ℚ)))).comp + (C ((-1 : ℤ) : ℚ) + C (2 ^ (0 + 1)) * X) ∈ + liftsRing (Rat.padicValuation 2).integer.subtype := + TensorNetwork.deformedContraction_cauchyMatrix_comp_mem_liftsRing _ _ (fun _ => by norm_num) + (by decide) _ (fun c hc => hc) fun v x => by + simp only [oneEdge] + split_ifs <;> exact (Rat.padicValuation_le_one_iff.mpr (by decide)) + +end ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates From 3d383101a8009cd08e47489327f9146d83b48a90 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 18:44:25 -0400 Subject: [PATCH 006/129] refactor(foundations): move integer valuation bounds to RatValuation General p-adic facts used by Lemma D.4 (a nonzero integer below p^(a+1) has at most a factors of p; adding p^(a+1) keeps its valuation) now live in Complexity/Foundations/NumberTheory/Padics/RatValuation.lean for every prime p, next to the reduction of Z_(p) modulo p^n. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 2 +- .../TensorNetwork/IntegralCoordinates.lean | 42 ++---- .../Padics/RatIntegerReduction.lean | 74 ----------- .../NumberTheory/Padics/RatValuation.lean | 123 ++++++++++++++++++ .../TensorNetworkIntegralCoordinates.lean | 2 +- 5 files changed, 139 insertions(+), 104 deletions(-) delete mode 100644 Complexity/Foundations/NumberTheory/Padics/RatIntegerReduction.lean create mode 100644 Complexity/Foundations/NumberTheory/Padics/RatValuation.lean diff --git a/Complexity.lean b/Complexity.lean index ffc52e5b23..09b1a7b72d 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2749,7 +2749,7 @@ public import Complexity.Foundations.NumberTheory.AKS.Criterion public import Complexity.Foundations.NumberTheory.AKS.Introspective public import Complexity.Foundations.NumberTheory.AKS.SmallR public import Complexity.Foundations.NumberTheory.Harmonic -public import Complexity.Foundations.NumberTheory.Padics.RatIntegerReduction +public import Complexity.Foundations.NumberTheory.Padics.RatValuation public import Complexity.Foundations.NumberTheory.PrimeCounting public import Complexity.Foundations.NumberTheory.PrimeWindow public import Complexity.Foundations.NumberTheory.SqrtPrimeWindow diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/IntegralCoordinates.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/IntegralCoordinates.lean index d44097932c..be7ad962d0 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/IntegralCoordinates.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/IntegralCoordinates.lean @@ -11,7 +11,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformatio public import Mathlib.Algebra.Polynomial.Lifts public import Mathlib.NumberTheory.Padics.PadicNumbers public import Mathlib.RingTheory.Valuation.Integers -import Mathlib.NumberTheory.Padics.PadicVal.Basic +import Complexity.Foundations.NumberTheory.Padics.RatValuation import Mathlib.Tactic.FieldSimp import Mathlib.Tactic.LinearCombination @@ -41,8 +41,10 @@ a subring `B` of a `ℚ`-algebra `R` that contains `ℤ_(2)`, the deformed contr Cauchy matrix satisfies `F(t + α z) ∈ B[z]` (`Cslib.Circuits.Frontier.TensorNetwork.deformedContraction_cauchyMatrix_comp_mem_liftsRing`): the manuscript's `F(t + α z) ∈ A[z]` with `B = O`. Coefficients in `ℤ_(2)` reduce modulo `2 ^ p` -through `Rat.padicIntegerToZModPow`. The algorithmic half of the lemma, evaluating -`W_xy(t + α ζ) mod 2 ^ p` in polynomial time, belongs to the runtime development. +through `Rat.padicIntegerToZModPow`; the valuations of integers that the proof uses are +`Rat.padicValuation_pow_le_of_abs_lt` and `Rat.padicValuation_add_pow_of_abs_lt`. The algorithmic +half of the lemma, evaluating `W_xy(t + α ζ) mod 2 ^ p` in polynomial time, belongs to the +runtime development. ## Main results @@ -72,36 +74,20 @@ open scoped WithZero /-! ### Two-adic valuations on the grid -/ -/-- The `2`-adic valuation of a nonzero integer. -/ -private theorem padicValuation_intCast {x : ℤ} (hx : x ≠ 0) : - Rat.padicValuation 2 (x : ℚ) = WithZero.exp (-(padicValInt 2 x : ℤ)) := by - simp [Rat.padicValuation, hx, padicValRat.of_int] - /-- The `2`-adic valuation of a power of two. -/ -theorem padicValuation_two_pow (k : ℕ) : +private theorem padicValuation_two_pow (k : ℕ) : Rat.padicValuation 2 ((2 : ℚ) ^ k) = WithZero.exp (-(k : ℤ)) := by - have h2 : Rat.padicValuation 2 (2 : ℚ) = WithZero.exp (-1) := by - exact_mod_cast Rat.padicValuation_self 2 - rw [map_pow, h2, ← WithZero.exp_nsmul, nsmul_eq_mul, mul_neg, mul_one] + exact_mod_cast Rat.padicValuation_pow_self 2 k -/-- **A nonzero integer below `2 ^ (a + 1)` in absolute value has at most `a` factors of two.** -/ -theorem padicValuation_two_pow_le {a : ℕ} {x : ℤ} (hx : x ≠ 0) (hlt : |x| < 2 ^ (a + 1)) : +private theorem padicValuation_two_pow_le {a : ℕ} {x : ℤ} (hx : x ≠ 0) + (hlt : |x| < 2 ^ (a + 1)) : Rat.padicValuation 2 ((2 : ℚ) ^ a) ≤ Rat.padicValuation 2 (x : ℚ) := by - rw [padicValuation_two_pow, padicValuation_intCast hx, WithZero.exp_le_exp, neg_le_neg_iff] - by_contra h - have hdvd : (2 : ℤ) ^ (a + 1) ∣ x := by - have := (padicValInt_dvd_iff (p := 2) (a + 1) x).mpr (Or.inr (by omega)) - exact_mod_cast this - exact absurd (Int.le_of_dvd (abs_pos.mpr hx) ((dvd_abs _ _).mpr hdvd)) (not_le.mpr hlt) - -/-- Adding `2 ^ (a + 1)` to a nonzero integer below it in absolute value keeps its `2`-adic -valuation. -/ -theorem padicValuation_add_two_pow {a : ℕ} {x : ℤ} (hx : x ≠ 0) (hlt : |x| < 2 ^ (a + 1)) : + exact_mod_cast Rat.padicValuation_pow_le_of_abs_lt (p := 2) hx (by exact_mod_cast hlt) + +private theorem padicValuation_add_two_pow {a : ℕ} {x : ℤ} (hx : x ≠ 0) + (hlt : |x| < 2 ^ (a + 1)) : Rat.padicValuation 2 ((x : ℚ) + 2 ^ (a + 1)) = Rat.padicValuation 2 (x : ℚ) := by - refine (Rat.padicValuation 2).map_add_eq_of_lt_left (lt_of_lt_of_le ?_ - (padicValuation_two_pow_le hx hlt)) - rw [padicValuation_two_pow, padicValuation_two_pow, WithZero.exp_lt_exp] - omega + exact_mod_cast Rat.padicValuation_add_pow_of_abs_lt (p := 2) hx (by exact_mod_cast hlt) /-- **The grid is a complete residue system.** For `t` on the grid of `L = 2 ^ a` slots, the nonzero differences `t - q`, `q ∈ S`, have the valuations of `1, …, 2 L - 1`: send `q` to the diff --git a/Complexity/Foundations/NumberTheory/Padics/RatIntegerReduction.lean b/Complexity/Foundations/NumberTheory/Padics/RatIntegerReduction.lean deleted file mode 100644 index 044fad57b9..0000000000 --- a/Complexity/Foundations/NumberTheory/Padics/RatIntegerReduction.lean +++ /dev/null @@ -1,74 +0,0 @@ -/- -Copyright (c) 2026 Samuel Schlesinger. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Samuel Schlesinger --/ - -module --- upstream: candidate (Mathlib, `Mathlib/NumberTheory/Padics/RingHoms.lean`) - -public import Complexity.Init -public import Mathlib.NumberTheory.Padics.RingHoms -public import Mathlib.RingTheory.Valuation.Integers - -/-! -# Reducing `p`-integral rationals modulo `p ^ n` - -The rationals whose denominator is prime to `p` form the valuation ring `ℤ_(p)` of -`Rat.padicValuation p`. It embeds in the `p`-adic integers (`Rat.padicIntegerToPadicInt`), so -it has reductions modulo every power of `p` (`Rat.padicIntegerToZModPow`). On `a / b` with `b` -prime to `p`, the reduction is `a * b⁻¹`: it is characterized by -`Rat.padicIntegerToZModPow_mul_den`. - -This reduces the integral local coordinates of the Cauchy kernel of [OpenAI2026ETH] (Lemma D.4, -pp. 59–60), whose coefficients lie in `ℤ_(2)`, modulo the working precision `2 ^ p`. - -## Main definitions - -* `Rat.padicIntegerToPadicInt`: the inclusion `ℤ_(p) → ℤ_[p]`. -* `Rat.padicIntegerToZModPow`: reduction `ℤ_(p) → ZMod (p ^ n)`. - -## Main results - -* `Rat.padicIntegerToZModPow_mul_den`: the reduction of `x` times the denominator of `x` is the - numerator of `x`. - -## References - -* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.3, pp. 59–60. --/ - -public section - -namespace Rat - -variable (p : ℕ) [Fact p.Prime] - -/-- The inclusion of the `p`-integral rationals `ℤ_(p)` in the `p`-adic integers. -/ -noncomputable def padicIntegerToPadicInt : (Rat.padicValuation p).integer →+* ℤ_[p] where - toFun x := ⟨(x : ℚ), Padic.norm_rat_le_one (Rat.padicValuation_le_one_iff.mp x.2)⟩ - map_one' := Subtype.ext (by simp) - map_mul' x y := Subtype.ext (by push_cast; rfl) - map_zero' := Subtype.ext (by simp) - map_add' x y := Subtype.ext (by push_cast; rfl) - -@[simp] -theorem coe_padicIntegerToPadicInt (x : (Rat.padicValuation p).integer) : - ((padicIntegerToPadicInt p x : ℤ_[p]) : ℚ_[p]) = ((x : ℚ) : ℚ_[p]) := by - simp only [padicIntegerToPadicInt] - rfl - -/-- Reduction of the `p`-integral rationals modulo `p ^ n`. -/ -noncomputable def padicIntegerToZModPow (n : ℕ) : (Rat.padicValuation p).integer →+* ZMod (p ^ n) := - (PadicInt.toZModPow n).comp (padicIntegerToPadicInt p) - -/-- **The reduction of `a / b` is `a * b⁻¹`**: multiplied by the denominator it gives the -numerator. -/ -theorem padicIntegerToZModPow_mul_den (n : ℕ) (x : (Rat.padicValuation p).integer) : - padicIntegerToZModPow p n x * ((x : ℚ).den : ZMod (p ^ n)) = ((x : ℚ).num : ZMod (p ^ n)) := by - have h : x * ((x : ℚ).den : (Rat.padicValuation p).integer) = - ((x : ℚ).num : (Rat.padicValuation p).integer) := - Subtype.ext (by push_cast; exact Rat.mul_den_eq_num _) - simpa only [map_mul, map_natCast, map_intCast] using congrArg (padicIntegerToZModPow p n) h - -end Rat diff --git a/Complexity/Foundations/NumberTheory/Padics/RatValuation.lean b/Complexity/Foundations/NumberTheory/Padics/RatValuation.lean new file mode 100644 index 0000000000..3c249ac985 --- /dev/null +++ b/Complexity/Foundations/NumberTheory/Padics/RatValuation.lean @@ -0,0 +1,123 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate (Mathlib, `Mathlib/NumberTheory/Padics/RingHoms.lean`) + +public import Complexity.Init +public import Mathlib.NumberTheory.Padics.RingHoms +public import Mathlib.RingTheory.Valuation.Integers +import Mathlib.NumberTheory.Padics.PadicVal.Basic + +/-! +# The `p`-adic valuation of rationals: integers and reduction modulo `p ^ n` + +Two facts about `Rat.padicValuation p`. First, valuations of integers below `p ^ (a + 1)`: such +a nonzero integer has at most `a` factors of `p` (`Rat.padicValuation_pow_le_of_abs_lt`), so adding +`p ^ (a + 1)` does not change its valuation (`Rat.padicValuation_add_pow_of_abs_lt`). + +Second, reduction modulo `p ^ n`. The rationals whose denominator is prime to `p` form the +valuation ring `ℤ_(p)` of `Rat.padicValuation p`. It embeds in the `p`-adic integers +(`Rat.padicIntegerToPadicInt`), so it has reductions modulo every power of `p` +(`Rat.padicIntegerToZModPow`). On `a / b` with `b` prime to `p`, the reduction is `a * b⁻¹`: it +is characterized by `Rat.padicIntegerToZModPow_mul_den`. + +Both serve the integral local coordinates of the Cauchy kernel of [OpenAI2026ETH] (Lemma D.4, +pp. 59–60): the first shows that its grid is a complete residue system for valuations, and the +second reduces its `ℤ_(2)` coefficients modulo the working precision `2 ^ p`. + +## Main definitions + +* `Rat.padicIntegerToPadicInt`: the inclusion `ℤ_(p) → ℤ_[p]`. +* `Rat.padicIntegerToZModPow`: reduction `ℤ_(p) → ZMod (p ^ n)`. + +## Main results + +* `Rat.padicValuation_pow_self`: `v (p ^ k) = exp (-k)`. +* `Rat.padicValuation_pow_le_of_abs_lt`: a nonzero integer below `p ^ (a + 1)` in absolute value + has valuation at least `v (p ^ a)`. +* `Rat.padicValuation_add_pow_of_abs_lt`: adding `p ^ (a + 1)` to it keeps its valuation. +* `Rat.padicIntegerToZModPow_mul_den`: the reduction of `x` times the denominator of `x` is the + numerator of `x`. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.3, pp. 59–60. +-/ + +public section + +open scoped WithZero + +namespace Rat + +variable (p : ℕ) [Fact p.Prime] + +/-! ### Valuations of integers -/ + +/-- The `p`-adic valuation of a nonzero integer. -/ +theorem padicValuation_intCast_of_ne_zero {x : ℤ} (hx : x ≠ 0) : + Rat.padicValuation p (x : ℚ) = WithZero.exp (-(padicValInt p x : ℤ)) := by + simp [Rat.padicValuation, hx, padicValRat.of_int] + +/-- The `p`-adic valuation of a power of `p`. -/ +theorem padicValuation_pow_self (k : ℕ) : + Rat.padicValuation p ((p : ℚ) ^ k) = WithZero.exp (-(k : ℤ)) := by + rw [map_pow, Rat.padicValuation_self, ← WithZero.exp_nsmul, nsmul_eq_mul, mul_neg, mul_one] + +variable {p} + +/-- **A nonzero integer below `p ^ (a + 1)` in absolute value has at most `a` factors of `p`.** -/ +theorem padicValuation_pow_le_of_abs_lt {a : ℕ} {x : ℤ} (hx : x ≠ 0) + (hlt : |x| < (p : ℤ) ^ (a + 1)) : + Rat.padicValuation p ((p : ℚ) ^ a) ≤ Rat.padicValuation p (x : ℚ) := by + rw [padicValuation_pow_self, padicValuation_intCast_of_ne_zero p hx, WithZero.exp_le_exp, + neg_le_neg_iff] + by_contra h + have hdvd : (p : ℤ) ^ (a + 1) ∣ x := (padicValInt_dvd_iff (a + 1) x).mpr (Or.inr (by omega)) + exact absurd (Int.le_of_dvd (abs_pos.mpr hx) ((dvd_abs _ _).mpr hdvd)) (not_le.mpr hlt) + +/-- **Adding `p ^ (a + 1)` to a nonzero integer below it in absolute value keeps its valuation.** -/ +theorem padicValuation_add_pow_of_abs_lt {a : ℕ} {x : ℤ} (hx : x ≠ 0) + (hlt : |x| < (p : ℤ) ^ (a + 1)) : + Rat.padicValuation p ((x : ℚ) + (p : ℚ) ^ (a + 1)) = Rat.padicValuation p (x : ℚ) := by + refine (Rat.padicValuation p).map_add_eq_of_lt_left (lt_of_lt_of_le ?_ + (padicValuation_pow_le_of_abs_lt hx hlt)) + rw [padicValuation_pow_self, padicValuation_pow_self, WithZero.exp_lt_exp] + omega + +variable (p) + +/-! ### Reduction modulo `p ^ n` -/ + +/-- The inclusion of the `p`-integral rationals `ℤ_(p)` in the `p`-adic integers. -/ +noncomputable def padicIntegerToPadicInt : (Rat.padicValuation p).integer →+* ℤ_[p] where + toFun x := ⟨(x : ℚ), Padic.norm_rat_le_one (Rat.padicValuation_le_one_iff.mp x.2)⟩ + map_one' := Subtype.ext (by simp) + map_mul' x y := Subtype.ext (by push_cast; rfl) + map_zero' := Subtype.ext (by simp) + map_add' x y := Subtype.ext (by push_cast; rfl) + +@[simp] +theorem coe_padicIntegerToPadicInt (x : (Rat.padicValuation p).integer) : + ((padicIntegerToPadicInt p x : ℤ_[p]) : ℚ_[p]) = ((x : ℚ) : ℚ_[p]) := by + simp only [padicIntegerToPadicInt] + rfl + +/-- Reduction of the `p`-integral rationals modulo `p ^ n`. -/ +noncomputable def padicIntegerToZModPow (n : ℕ) : (Rat.padicValuation p).integer →+* ZMod (p ^ n) := + (PadicInt.toZModPow n).comp (padicIntegerToPadicInt p) + +/-- **The reduction of `a / b` is `a * b⁻¹`**: multiplied by the denominator it gives the +numerator. -/ +theorem padicIntegerToZModPow_mul_den (n : ℕ) (x : (Rat.padicValuation p).integer) : + padicIntegerToZModPow p n x * ((x : ℚ).den : ZMod (p ^ n)) = ((x : ℚ).num : ZMod (p ^ n)) := by + have h : x * ((x : ℚ).den : (Rat.padicValuation p).integer) = + ((x : ℚ).num : (Rat.padicValuation p).integer) := + Subtype.ext (by push_cast; exact Rat.mul_den_eq_num _) + simpa only [map_mul, map_natCast, map_intCast] using congrArg (padicIntegerToZModPow p n) h + +end Rat diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkIntegralCoordinates.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkIntegralCoordinates.lean index 998c69fca1..65ad05b7a1 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkIntegralCoordinates.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkIntegralCoordinates.lean @@ -5,7 +5,7 @@ Authors: Samuel Schlesinger -/ import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates -import Complexity.Foundations.NumberTheory.Padics.RatIntegerReduction +import Complexity.Foundations.NumberTheory.Padics.RatValuation /-! # Tests for the integral local coordinates of the Cauchy kernel From adeeddc47d7dce6dc8f3da3a1866df592de1c22d Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 19:10:47 -0400 Subject: [PATCH 007/129] feat(circuit/frontier): mode expansion and the child identity (D.16) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A mode splitting of a matrix W sorts the vertices of a row-to-column Boolean tensor network into groups and its edges into packets, and splits every entry of W into a local part and a sum over modes. Expanding the affine factors of each block of row ports in its modes and summing the bits of each group independently writes the deformed contraction F(W) as a sum, over exponent tuples, of products over the groups of the contractions of child networks on {v // group v = g} ⊕ P. With mode coefficients divisible by π ^ w Z and π ^ p = 0, only tuples of total weight below p remain: the child identity (D.16) of OpenAI2026ETH, §D.4.5–§D.4.6, pp. 62–63. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Frontier/TensorNetwork/ModeExpansion.lean | 684 ++++++++++++++++++ .../Frontier/TensorNetworkModeExpansion.lean | 103 +++ 3 files changed, 788 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/ModeExpansion.lean create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkModeExpansion.lean diff --git a/Complexity.lean b/Complexity.lean index 8ea2c2b140..72282f4bf5 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -861,6 +861,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness public import Complexity.Computability.Circuit.Frontier.Unconditional public import Complexity.Computability.Circuit.Frontier.Witness diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ModeExpansion.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ModeExpansion.lean new file mode 100644 index 0000000000..422460a8ec --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ModeExpansion.lean @@ -0,0 +1,684 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation +public import Mathlib.Data.Fintype.Option +import Mathlib.Algebra.BigOperators.Ring.Finset +import Mathlib.Data.Fintype.BigOperators + +/-! +# Modes and the child networks of a deformed contraction + +This file proves the child identity (D.16) of [OpenAI2026ETH] (§D.4.5–§D.4.6, pp. 62–63), the +step of the proof of the Boolean tensor contraction theorem (Theorem D.1) that turns one +deformed contraction into a sum of products of smaller contractions. + +**Setting.** Let `N` be a Boolean tensor network whose edges run from *rows* (their sources) to +*columns* (their targets), and let `W` be a matrix indexed by the edges. The deformed contraction +(`TensorNetwork.deformedContraction`, the manuscript's (D.8)) is + + `F(W) = ∑ X Y, A(Y) B(X) ∏ e, l_{Y e}((W X) e)`. + +A *mode splitting* (`TensorNetwork.ModeSplitting`) sorts the vertices on both sides into +*groups* and the edges into *packets*, and splits every entry of `W` as + + `W e e' = localPart e e' + ∑ Z, coeff e Z * form Z e'`, + +where the local part only joins a row port to a column port of the same packet and group, and +each *mode* `Z` has a packet, a *target* group, whose row ports carry its coefficients, and a +*source* group, whose column ports its linear form `L_Z(X) = ∑ e', form Z e' X e'` reads. In the +manuscript the modes are the terms of the geometric expansion (D.12) of the crossing entries of +the Cauchy matrix; here they are arbitrary. + +**Mode expansion.** Each affine factor `l_{Y e}((W X) e)` is affine in the mode values, so the +product of the affine factors of the row ports of one packet and group (a *block*) expands as a +polynomial in the modes targeted at that block. Its coefficient at the exponents `β` +(`ModeSplitting.blockCoeff`) sums, over the ways to pick the local term or one mode in each +factor with the multiplicities `β`, the product of the picked coefficients. + +**Children** (`ModeSplitting.child`). For a tuple of exponents `β` and a group `g`, the child +network has the vertices of group `g` from both sides of `N`, with their tensors, and one new +vertex per packet. Its edges are the ports of group `g`: an edge from the row of `e` to the packet +of `e` for each row port `e` of group `g`, carrying the row bit `Y e`, and one from the column of +`e` to the packet of `e` for each column port of group `g`, carrying the column bit `X e`. The +tensor at a packet (`ModeSplitting.packetTensor`) is the coefficient at `β` for that packet and +group times `∏ L_Z(X) ^ β Z` over the modes of that packet with source `g`. So children are +tensor networks on `{v // group v = g} ⊕ P`, bipartite with the old vertices as rows +(`ModeSplitting.isBipartite_child`). + +**The child identity** (`ModeSplitting.deformedContraction_eq_sum_prod_contraction_child`, the +manuscript's (D.16)). If the coefficient of each mode `Z` is divisible by `π ^ w Z` with +`w Z ≥ 1`, and `π ^ p = 0`, then + + `F(W) = ∑ β ∈ modeBudget w p, ∏ g, Z_{g, β}`, + +where `Z_{g, β}` is the contraction of the child of group `g` and `modeBudget w p` is the set of +tuples with `∑ Z, w Z * β Z < p` (the manuscript's (D.14)). The manuscript works modulo `2 ^ p_t` +with `π = 2` and `w Z = b + 1`; here the ring itself has `π ^ p = 0`, as in `A / 2 ^ p_t`, and a +congruence in a larger ring follows by mapping to its quotient +(`TensorNetwork.deformedContraction_map`). Without truncation, the same expansion gives +`F(W) = ∑ β, ∏ g, Z_{g, β}` over all tuples with entries at most the number of edges, in every +commutative ring (`ModeSplitting.deformedContraction_eq_sum_prod_contraction_child_of_le`). + +The proof is the manuscript's: distribute the product of the affine factors over the blocks, +expand each block in its modes, substitute the mode values, and sum the bits of each group +independently. The construction of the modes from the Cauchy kernel ((D.12)–(D.13)), the count +(D.15) of the tuples, and the size bounds on the children are separate steps. + +## Main definitions + +* `TensorNetwork.affineSign`: the sign of the variable part of an affine factor. +* `TensorNetwork.ModeSplitting`: groups, packets, and the splitting of a matrix into a local part + and modes. +* `TensorNetwork.modeBudget`: the tuples of exponents of total weight below `p`. +* `TensorNetwork.ModeSplitting.blockCoeff`: the coefficient of a mode monomial in the product of + the affine factors of a block. +* `TensorNetwork.ModeSplitting.packetTensor`: the tensor at a packet vertex of a child. +* `TensorNetwork.ModeSplitting.child`: the child network of a group. + +## Main results + +* `TensorNetwork.affineFactor_add`: `l_b (u + w) = l_b u ± w`. +* `TensorNetwork.mem_modeBudget`: membership in the budget. +* `TensorNetwork.ModeSplitting.packetTensor_congr`: a packet tensor reads only the ports of its + packet and group. +* `TensorNetwork.ModeSplitting.pow_dvd_blockCoeff`: the coefficient of a mode monomial is + divisible by `π` to its total weight. +* `TensorNetwork.ModeSplitting.isBipartite_child`, `TensorNetwork.ModeSplitting.card_edges_child`: + children are bipartite, with one edge per port of their group. +* `TensorNetwork.ModeSplitting.deformedContraction_eq_sum_prod_contraction_child_of_le`: the + exact expansion of `F(W)` into children. +* `TensorNetwork.ModeSplitting.deformedContraction_eq_sum_prod_contraction_child`: the child + identity (D.16). + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.5–§D.4.6, + (D.14) and (D.16), pp. 62–63. +-/ + +public section + +namespace Cslib.Circuits.Frontier + +open Finset + +namespace TensorNetwork + +variable {R V E : Type*} [CommRing R] + +/-! ### Affine factors of a sum -/ + +/-- The sign of the variable part of an affine factor: `affineSign true = 1` and +`affineSign false = -1`, so that `l_b (u + w) = l_b u + affineSign b * w`. -/ +@[expose] def affineSign (b : Bool) : R := + if b then 1 else -1 + +/-- An affine factor of a sum: `l_b (u + w) = l_b u + affineSign b * w`. -/ +theorem affineFactor_add (b : Bool) (u w : R) : + affineFactor b (u + w) = affineFactor b u + affineSign b * w := by + cases b <;> simp only [affineFactor, affineSign, Bool.false_eq_true, ite_false, ite_true] <;> + ring + +/-! ### Mode splittings -/ + +/-- A *mode splitting* (§D.4.5 of [OpenAI2026ETH]) of the deformed contraction of a Boolean tensor +network `N` along a matrix `W`. The rows of `N` (`side v = true`) are the sources of its edges and +the columns their targets. The vertices on both sides are sorted into *groups* and the edges into +*packets*, and every entry of `W` splits into a *local part*, which joins a row port to a column +port of the same packet and group, and a sum over the *modes* `Z`, each contributing +`coeff e Z * form Z e'`. A mode has a packet, a *target* group, the only row ports of its packet +whose forms contain it, and a *source* group, the only column ports of its packet that its linear +form reads. -/ +structure ModeSplitting (N : TensorNetwork R Bool V E) (W : Matrix E E R) (G P M : Type*) + [Fintype M] where + /-- The side of a vertex: `true` for rows, `false` for columns. -/ + side : V → Bool + /-- The group of a vertex. -/ + group : V → G + /-- The packet of an edge. -/ + packet : E → P + /-- The local part of the matrix. -/ + localPart : Matrix E E R + /-- The packet of a mode. -/ + modePacket : M → P + /-- The source group of a mode: its form reads the column ports of this group. -/ + source : M → G + /-- The target group of a mode: it occurs in the forms of the row ports of this group. -/ + target : M → G + /-- The coefficient of a mode in the form of a row port. -/ + coeff : E → M → R + /-- The linear form in the column bits that a mode stands for. -/ + form : M → E → R + /-- Edges start at rows. -/ + side_src : ∀ e, side (N.src e) = true + /-- Edges end at columns. -/ + side_tgt : ∀ e, side (N.tgt e) = false + /-- The splitting of the matrix. -/ + apply_eq : ∀ e e', W e e' = localPart e e' + ∑ Z, coeff e Z * form Z e' + /-- The local part joins ports of the same packet and group. -/ + localPart_ne_zero : ∀ e e', localPart e e' ≠ 0 → + packet e' = packet e ∧ group (N.tgt e') = group (N.src e) + /-- A mode occurs only in the forms of the row ports of its packet and target group. -/ + coeff_ne_zero : ∀ e Z, coeff e Z ≠ 0 → modePacket Z = packet e ∧ target Z = group (N.src e) + /-- A mode reads only the column ports of its packet and source group. -/ + form_ne_zero : ∀ Z e, form Z e ≠ 0 → modePacket Z = packet e ∧ source Z = group (N.tgt e) + +/-- The tuples of exponents `β` of total weight `∑ Z, w Z * β Z` below `p`, the index set of the +manuscript's (D.14). -/ +@[expose] def modeBudget {M : Type*} [Fintype M] [DecidableEq M] (w : M → ℕ) (p : ℕ) : + Finset (M → ℕ) := + {β ∈ Fintype.piFinset fun _ => range p | ∑ Z, w Z * β Z < p} + +/-- For positive weights, the budget holds exactly the tuples of total weight below `p`. -/ +theorem mem_modeBudget {M : Type*} [Fintype M] [DecidableEq M] {w : M → ℕ} (hw : ∀ Z, 0 < w Z) + {p : ℕ} {β : M → ℕ} : β ∈ modeBudget w p ↔ ∑ Z, w Z * β Z < p := by + simp only [modeBudget, mem_filter, Fintype.mem_piFinset, mem_range, and_iff_right_iff_imp] + intro h Z + calc β Z ≤ w Z * β Z := Nat.le_mul_of_pos_left _ (hw Z) + _ ≤ ∑ Z, w Z * β Z := single_le_sum (fun Z _ => Nat.zero_le (w Z * β Z)) (mem_univ Z) + _ < p := h + +namespace ModeSplitting + +variable {G P M : Type*} [Fintype M] {N : TensorNetwork R Bool V E} {W : Matrix E E R} + (S : ModeSplitting N W G P M) + +/-- The block of a row port: its packet and the group of its row. -/ +@[expose] def rowBlock (e : E) : P × G := + (S.packet e, S.group (N.src e)) + +/-- The block of a mode: its packet and its target group. -/ +@[expose] def targetBlock (Z : M) : P × G := + (S.modePacket Z, S.target Z) + +/-- The source block of a mode: its packet and its source group. -/ +@[expose] def sourceBlock (Z : M) : P × G := + (S.modePacket Z, S.source Z) + +section Forms + +variable [Fintype E] + +/-- The local form of a row port on the column bits `X`. -/ +@[expose] def localForm (X : E → Bool) (e : E) : R := + ∑ e', S.localPart e e' * if X e' then 1 else 0 + +/-- The value `L_Z(X) = ∑ e', form Z e' X e'` of a mode on the column bits `X`. -/ +@[expose] def modeValue (X : E → Bool) (Z : M) : R := + ∑ e', S.form Z e' * if X e' then 1 else 0 + +/-- The terms of the expansion of the affine factor of a row port: the local term, and the +coefficient of each mode. -/ +@[expose] def choiceTerm (Y X : E → Bool) (e : E) : Option M → R + | none => affineFactor (Y e) (S.localForm X e) + | some Z => affineSign (Y e) * S.coeff e Z + +theorem localForm_congr {X X' : E → Bool} {e : E} + (h : ∀ e', S.packet e' = S.packet e → S.group (N.tgt e') = S.group (N.src e) → X e' = X' e') : + S.localForm X e = S.localForm X' e := by + refine sum_congr rfl fun e' _ => ?_ + by_cases h0 : S.localPart e e' = 0 + · rw [h0, zero_mul, zero_mul] + · obtain ⟨hp, hg⟩ := S.localPart_ne_zero e e' h0 + rw [h e' hp hg] + +theorem modeValue_congr {X X' : E → Bool} {Z : M} + (h : ∀ e, S.packet e = S.modePacket Z → S.group (N.tgt e) = S.source Z → X e = X' e) : + S.modeValue X Z = S.modeValue X' Z := by + refine sum_congr rfl fun e _ => ?_ + by_cases h0 : S.form Z e = 0 + · rw [h0, zero_mul, zero_mul] + · obtain ⟨hp, hg⟩ := S.form_ne_zero Z e h0 + rw [h e hp.symm hg.symm] + +/-- The form of a row port splits into its local form and its modes. -/ +theorem mulVec_apply (X : E → Bool) (e : E) : + W.mulVec (fun e' => if X e' then 1 else 0) e = + S.localForm X e + ∑ Z, S.coeff e Z * S.modeValue X Z := by + simp only [Matrix.mulVec, dotProduct, S.apply_eq, add_mul, sum_add_distrib, localForm, + modeValue, mul_sum, sum_mul] + rw [sum_comm (s := univ) (t := univ) (f := fun e' Z => S.coeff e Z * S.form Z e' * _)] + simp only [mul_assoc] + +/-- The affine factor of a row port as the sum of its terms, each times the value of its mode. -/ +theorem affineFactor_mulVec (Y X : E → Bool) (e : E) : + affineFactor (Y e) (W.mulVec (fun e' => if X e' then 1 else 0) e) = + ∑ o : Option M, S.choiceTerm Y X e o * o.elim 1 (S.modeValue X) := by + rw [S.mulVec_apply, affineFactor_add, Fintype.sum_option, mul_sum] + simp only [choiceTerm, Option.elim, mul_one, mul_assoc] + +end Forms + +/-! ### Coefficients of mode monomials -/ + +section Coefficients + +variable [Fintype E] [DecidableEq E] [DecidableEq P] [DecidableEq G] [DecidableEq M] + +/-- The coefficient of the mode monomial with exponents `β` in the product of the affine factors +of the row ports of the block `k`. It sums, over the ways `φ` to pick in each factor either its +local term or one of the modes targeted at `k`, each mode `Z` of `k` picked `β Z` times, the +product of the picked terms. It depends only on the exponents of the modes of `k`. -/ +@[expose] def blockCoeff (k : P × G) (β : M → ℕ) (Y X : E → Bool) : R := + ∑ φ : {e // S.rowBlock e = k} → Option M, + if (∀ e Z, φ e = some Z → S.targetBlock Z = k) ∧ + ∀ Z, S.targetBlock Z = k → #{e | φ e = some Z} = β Z then + ∏ e, S.choiceTerm Y X e.1 (φ e) + else 0 + +/-- The tensor at the vertex of packet `q` in the child of group `g`: the coefficient of the mode +monomial `β` for the row ports of `q` in group `g`, times `L_Z(X) ^ β Z` for the modes of `q` with +source `g`. -/ +@[expose] def packetTensor (β : M → ℕ) (q : P) (g : G) (Y X : E → Bool) : R := + S.blockCoeff (q, g) β Y X * ∏ Z with S.sourceBlock Z = (q, g), S.modeValue X Z ^ β Z + +/-- A block coefficient depends only on the exponents of the modes of its block. -/ +theorem blockCoeff_congr_exponents {k : P × G} {β β' : M → ℕ} + (h : ∀ Z, S.targetBlock Z = k → β Z = β' Z) (Y X : E → Bool) : + S.blockCoeff k β Y X = S.blockCoeff k β' Y X := by + refine sum_congr rfl fun φ _ => ?_ + refine if_congr (and_congr Iff.rfl (forall_congr' fun Z => imp_congr_right fun hZ => ?_)) rfl + rfl + rw [h Z hZ] + +/-- **A packet tensor is local:** it reads only the row and column ports of its packet and +group. -/ +theorem packetTensor_congr (β : M → ℕ) (q : P) (g : G) {Y Y' X X' : E → Bool} + (hY : ∀ e, S.packet e = q → S.group (N.src e) = g → Y e = Y' e) + (hX : ∀ e, S.packet e = q → S.group (N.tgt e) = g → X e = X' e) : + S.packetTensor β q g Y X = S.packetTensor β q g Y' X' := by + unfold packetTensor blockCoeff + congr 1 + · refine sum_congr rfl fun φ _ => if_congr Iff.rfl (prod_congr rfl fun e _ => ?_) rfl + obtain ⟨e, he⟩ := e + simp only [rowBlock, Prod.mk.injEq] at he + have hl : S.localForm X e = S.localForm X' e := S.localForm_congr fun e' hp hg => + hX e' (hp.trans he.1) (hg.trans he.2) + cases φ ⟨e, _⟩ <;> simp only [choiceTerm, hY e he.1 he.2, hl] + · refine prod_congr rfl fun Z hZ => ?_ + simp only [sourceBlock, mem_filter, mem_univ, true_and, Prod.mk.injEq] at hZ + rw [S.modeValue_congr fun e hp hg => hX e (hp.trans hZ.1) (hg.trans hZ.2)] + +/-- A choice of terms with exponent `β Z` above the number of edges never exists, so the block +coefficient of such a mode's block vanishes. -/ +theorem blockCoeff_eq_zero_of_card_lt {Z : M} {β : M → ℕ} (hZ : Fintype.card E < β Z) + (Y X : E → Bool) : S.blockCoeff (S.targetBlock Z) β Y X = 0 := by + refine sum_eq_zero fun φ _ => ite_eq_right fun h => ?_ + have hle : #{e | φ e = some Z} ≤ Fintype.card E := + (card_filter_le _ _).trans (by rw [card_univ]; exact Fintype.card_subtype_le _) + rw [h.2 Z rfl] at hle + omega + +omit [DecidableEq E] in +/-- The weight of a picked term: zero for the local term and `w Z` for the mode `Z`. -/ +private theorem sum_elim_weight {k : P × G} (w : M → ℕ) (φ : {e // S.rowBlock e = k} → Option M) : + ∑ e, (φ e).elim 0 w = ∑ Z, #{e | φ e = some Z} * w Z := by + rw [← sum_fiberwise' univ φ (fun o => o.elim 0 w)] + simp only [sum_const, smul_eq_mul, Fintype.sum_option, Option.elim, mul_zero, zero_add] + +/-- **The coefficient of a mode monomial is divisible by `π` to its total weight**, when the +coefficient of each mode `Z` is divisible by `π ^ w Z`. -/ +theorem pow_dvd_blockCoeff (w : M → ℕ) {π : R} (hdiv : ∀ e Z, π ^ w Z ∣ S.coeff e Z) + (k : P × G) (β : M → ℕ) (Y X : E → Bool) : + π ^ (∑ Z with S.targetBlock Z = k, w Z * β Z) ∣ S.blockCoeff k β Y X := by + refine dvd_sum fun φ _ => ?_ + split_ifs with h + · have hexp : ∑ Z with S.targetBlock Z = k, w Z * β Z = ∑ e, (φ e).elim 0 w := by + rw [sum_elim_weight, sum_filter] + refine sum_congr rfl fun Z _ => ?_ + split_ifs with hZ + · rw [h.2 Z hZ, mul_comm] + · have : #{e | φ e = some Z} = 0 := card_eq_zero.mpr (filter_eq_empty_iff.mpr + fun e _ he => hZ (h.1 e Z he)) + rw [this, zero_mul] + rw [hexp, ← prod_pow_eq_pow_sum] + refine prod_dvd_prod_of_dvd _ _ fun e _ => ?_ + cases φ e with + | none => simp only [Option.elim, pow_zero, one_dvd] + | some Z => exact Dvd.dvd.mul_left (hdiv e.1 Z) _ + · exact dvd_zero _ + +/-- The product of the block coefficients vanishes when the total weight reaches `p`, if +`π ^ p = 0`. -/ +theorem prod_blockCoeff_eq_zero (w : M → ℕ) {π : R} (hdiv : ∀ e Z, π ^ w Z ∣ S.coeff e Z) + {p : ℕ} (hπ : π ^ p = 0) [Fintype P] [Fintype G] {β : M → ℕ} + (hβ : p ≤ ∑ Z, w Z * β Z) (Y X : E → Bool) : ∏ k, S.blockCoeff k β Y X = 0 := by + have hdvd : π ^ (∑ Z, w Z * β Z) ∣ ∏ k, S.blockCoeff k β Y X := by + rw [← sum_fiberwise univ S.targetBlock (fun Z => w Z * β Z), ← prod_pow_eq_pow_sum] + exact prod_dvd_prod_of_dvd _ _ fun k _ => S.pow_dvd_blockCoeff w hdiv k β Y X + rw [← Nat.add_sub_cancel' hβ, pow_add, hπ, zero_mul] at hdvd + exact zero_dvd_iff.mp hdvd + +end Coefficients + +/-! ### The mode expansion -/ + +section Expansion + +variable [Fintype E] [DecidableEq E] [DecidableEq P] [DecidableEq G] [DecidableEq M] + +omit [DecidableEq E] in +/-- The product of the values of the picked modes, the local term counting as one. -/ +private theorem prod_elim_modeValue {k : P × G} (φ : {e // S.rowBlock e = k} → Option M) + (X : E → Bool) : + ∏ e, (φ e).elim 1 (S.modeValue X) = ∏ Z, S.modeValue X Z ^ #{e | φ e = some Z} := by + rw [← prod_fiberwise' univ φ (fun o => o.elim 1 (S.modeValue X))] + simp only [prod_const, Fintype.prod_option, Option.elim, one_pow, one_mul] + +/-- The exponent tuples supported on the modes of one block, with entries at most `n`. -/ +private abbrev blockBox (n : ℕ) (k : P × G) : Finset (M → ℕ) := + {β ∈ Fintype.piFinset fun _ => range (n + 1) | ∀ Z, S.targetBlock Z ≠ k → β Z = 0} + +/-- **Mode expansion of one block.** The product of the affine factors of the row ports of a +block is a polynomial in the values of the modes targeted at it, with the block coefficients. -/ +private theorem prod_block (Y X : E → Bool) (k : P × G) : + ∏ e : {e // S.rowBlock e = k}, + affineFactor (Y e) (W.mulVec (fun e' => if X e' then 1 else 0) e) = + ∑ β ∈ S.blockBox (Fintype.card E) k, S.blockCoeff k β Y X * ∏ Z, S.modeValue X Z ^ β Z := by + simp only [S.affineFactor_mulVec, Fintype.prod_sum, prod_mul_distrib, S.prod_elim_modeValue, + blockCoeff, sum_mul, ite_mul, zero_mul] + rw [sum_comm] + refine sum_congr rfl fun φ _ => ?_ + set c : M → ℕ := fun Z => #{e | φ e = some Z} with hc + by_cases hv : ∀ e Z, φ e = some Z → S.targetBlock Z = k + · rw [sum_eq_single c] + · rw [ite_eq_left ⟨hv, fun Z _ => rfl⟩] + · intro β hβ hne + refine ite_eq_right fun h => hne (funext fun Z => ?_) + by_cases hZ : S.targetBlock Z = k + · exact (h.2 Z hZ).symm + · rw [(mem_filter.mp hβ).2 Z hZ] + exact (card_eq_zero.mpr (filter_eq_empty_iff.mpr fun e _ he => hZ (hv e Z he))).symm + · intro hn + refine absurd ?_ hn + simp only [mem_filter, Fintype.mem_piFinset, mem_range] + refine ⟨fun Z => Nat.lt_succ_of_le ((card_filter_le _ _).trans ?_), fun Z hZ => ?_⟩ + · rw [card_univ] + exact Fintype.card_subtype_le _ + · exact card_eq_zero.mpr (filter_eq_empty_iff.mpr fun e _ he => hZ (hv e Z he)) + · push Not at hv + obtain ⟨e, Z, he, hZ⟩ := hv + rw [sum_eq_zero fun β _ => ite_eq_right fun h => hZ (h.1 e Z he)] + refine mul_eq_zero_of_left (prod_eq_zero (mem_univ e) ?_) _ + rw [he, choiceTerm] + refine mul_eq_zero_of_right _ (by_contra fun h0 => hZ ?_) + obtain ⟨hp, hg⟩ := S.coeff_ne_zero e.1 Z h0 + rw [targetBlock, hp, hg] + exact e.2 + +omit [Fintype E] [DecidableEq E] in +/-- Sums over the tuples supported on each block combine into one sum over all tuples. -/ +private theorem prod_sum_blockBox (n : ℕ) [Fintype P] [Fintype G] (f : P × G → (M → ℕ) → R) : + ∏ k, ∑ β ∈ S.blockBox n k, f k β = + ∑ β ∈ Fintype.piFinset (fun _ : M => range (n + 1)), + ∏ k, f k (fun Z => if S.targetBlock Z = k then β Z else 0) := by + rw [prod_univ_sum] + refine sum_nbij' (fun x Z => x (S.targetBlock Z) Z) + (fun β k Z => if S.targetBlock Z = k then β Z else 0) ?_ ?_ ?_ ?_ ?_ + · intro x hx + simp only [Fintype.mem_piFinset, mem_filter] at hx ⊢ + exact fun Z => (hx (S.targetBlock Z)).1 Z + · intro β hβ + simp only [Fintype.mem_piFinset, mem_filter, mem_range] at hβ ⊢ + refine fun k => ⟨fun Z => ?_, fun Z hZ => ite_eq_right hZ⟩ + split_ifs + exacts [hβ Z, Nat.succ_pos n] + · intro x hx + simp only [Fintype.mem_piFinset, mem_filter] at hx + funext k Z + split_ifs with h + · subst h + rfl + · exact ((hx k).2 Z h).symm + · intro β _ + funext Z + simp only [ite_true] + · intro x hx + simp only [Fintype.mem_piFinset, mem_filter] at hx + refine prod_congr rfl fun k _ => congrArg (f k) (funext fun Z => ?_) + split_ifs with h + · subst h + rfl + · exact (hx k).2 Z h + +/-- **Mode expansion.** The product of all affine factors is a sum over exponent tuples of the +product of the block coefficients times the mode monomial. -/ +private theorem prod_affineFactor_eq_sum [Fintype P] [Fintype G] (Y X : E → Bool) : + ∏ e, affineFactor (Y e) (W.mulVec (fun e' => if X e' then 1 else 0) e) = + ∑ β ∈ Fintype.piFinset (fun _ : M => range (Fintype.card E + 1)), + (∏ k, S.blockCoeff k β Y X) * ∏ Z, S.modeValue X Z ^ β Z := by + rw [← Fintype.prod_fiberwise S.rowBlock] + simp only [S.prod_block, S.prod_sum_blockBox] + refine sum_congr rfl fun β _ => ?_ + rw [prod_mul_distrib] + congr 1 + · exact prod_congr rfl fun k _ => S.blockCoeff_congr_exponents (fun Z hZ => ite_eq_left hZ) Y X + · rw [prod_comm] + refine prod_congr rfl fun Z _ => ?_ + simp only [pow_ite, pow_zero] + exact Fintype.prod_ite_eq (S.targetBlock Z) _ + +/-- **Truncated mode expansion.** When `π ^ p = 0`, only the tuples in the budget remain. -/ +private theorem prod_affineFactor_eq_sum_modeBudget [Fintype P] [Fintype G] (w : M → ℕ) + (hw : ∀ Z, 0 < w Z) {π : R} {p : ℕ} (hπ : π ^ p = 0) (hdiv : ∀ e Z, π ^ w Z ∣ S.coeff e Z) + (Y X : E → Bool) : + ∏ e, affineFactor (Y e) (W.mulVec (fun e' => if X e' then 1 else 0) e) = + ∑ β ∈ modeBudget w p, (∏ k, S.blockCoeff k β Y X) * ∏ Z, S.modeValue X Z ^ β Z := by + rw [S.prod_affineFactor_eq_sum] + set box := Fintype.piFinset (fun _ : M => range (Fintype.card E + 1)) + rw [← sum_subset (inter_subset_left (s₁ := box) (s₂ := modeBudget w p)), + ← sum_subset (inter_subset_right (s₁ := box) (s₂ := modeBudget w p))] + · intro β hβ hn + have hbox : β ∉ box := fun h => hn (mem_inter.mpr ⟨h, hβ⟩) + simp only [box, Fintype.mem_piFinset, mem_range, not_forall, not_lt] at hbox + obtain ⟨Z, hZ⟩ := hbox + have hZ' : Fintype.card E < β Z := by omega + rw [prod_eq_zero (i := S.targetBlock Z) (mem_univ _) + (S.blockCoeff_eq_zero_of_card_lt hZ' Y X), zero_mul] + · intro β hβ hn + have hb : β ∉ modeBudget w p := fun h => hn (mem_inter.mpr ⟨hβ, h⟩) + rw [mem_modeBudget hw, not_lt] at hb + rw [S.prod_blockCoeff_eq_zero w hdiv hπ hb, zero_mul] + +end Expansion + +/-! ### Child networks -/ + +section Child + +variable [Fintype E] [DecidableEq E] [DecidableEq P] [DecidableEq G] [DecidableEq M] + [DecidableEq V] + +/-- The *child network* of the group `g` for the exponents `β` (§D.4.6). Its vertices are the +vertices of group `g`, from both sides, and one vertex per packet. For every row port `e` of group +`g` it has an edge from the row of `e` to the packet of `e`, and for every column port of group +`g` an edge from the column of `e` to the packet of `e`. A row keeps its tensor, reading the row +bits on its edges, and a column keeps its tensor, reading the column bits; the tensor at a packet +is `packetTensor`. -/ +@[expose] def child (β : M → ℕ) (g : G) : + TensorNetwork R Bool ({v // S.group v = g} ⊕ P) + ({e // S.group (N.src e) = g} ⊕ {e // S.group (N.tgt e) = g}) where + src := Sum.elim (fun e => .inl ⟨N.src e.1, e.2⟩) (fun e => .inl ⟨N.tgt e.1, e.2⟩) + tgt := Sum.elim (fun e => .inr (S.packet e.1)) (fun e => .inr (S.packet e.1)) + tensor + | .inl v, y => + if S.side v.1 then + N.tensor v.1 fun e => if h : N.src e = v.1 then y (.inl ⟨e, h ▸ v.2⟩) else false + else + N.tensor v.1 fun e => if h : N.tgt e = v.1 then y (.inr ⟨e, h ▸ v.2⟩) else false + | .inr q, y => + S.packetTensor β q g (fun e => if h : S.group (N.src e) = g then y (.inl ⟨e, h⟩) else false) + (fun e => if h : S.group (N.tgt e) = g then y (.inr ⟨e, h⟩) else false) + tensor_congr := by + rintro (v | q) y y' h + · have hs : ∀ e (he : N.src e = v.1), y (.inl ⟨e, he ▸ v.2⟩) = y' (.inl ⟨e, he ▸ v.2⟩) := by + intro e he + exact h _ (Or.inl (congrArg Sum.inl (Subtype.ext he))) + have ht : ∀ e (he : N.tgt e = v.1), y (.inr ⟨e, he ▸ v.2⟩) = y' (.inr ⟨e, he ▸ v.2⟩) := by + intro e he + exact h _ (Or.inl (congrArg Sum.inl (Subtype.ext he))) + simp only + split_ifs + · congr 1 + funext e + split_ifs with he + exacts [hs e he, rfl] + · congr 1 + funext e + split_ifs with he + exacts [ht e he, rfl] + · refine S.packetTensor_congr β q g (fun e hq hg => ?_) (fun e hq hg => ?_) + · simp only [hg, dite_true] + exact h _ (Or.inr (by simp only [Sum.elim_inl, hq])) + · simp only [hg, dite_true] + exact h _ (Or.inr (by simp only [Sum.elim_inr, hq])) + +@[simp] theorem child_src_inl (β : M → ℕ) (g : G) (e : {e // S.group (N.src e) = g}) : + (S.child β g).src (.inl e) = .inl ⟨N.src e.1, e.2⟩ := + rfl + +@[simp] theorem child_src_inr (β : M → ℕ) (g : G) (e : {e // S.group (N.tgt e) = g}) : + (S.child β g).src (.inr e) = .inl ⟨N.tgt e.1, e.2⟩ := + rfl + +@[simp] theorem child_tgt_inl (β : M → ℕ) (g : G) (e : {e // S.group (N.src e) = g}) : + (S.child β g).tgt (.inl e) = .inr (S.packet e.1) := + rfl + +@[simp] theorem child_tgt_inr (β : M → ℕ) (g : G) (e : {e // S.group (N.tgt e) = g}) : + (S.child β g).tgt (.inr e) = .inr (S.packet e.1) := + rfl + +/-- **Children are bipartite**, with the old vertices on one side and the packets on the other; +every edge runs from an old vertex to a packet. -/ +theorem isBipartite_child (β : M → ℕ) (g : G) : (S.child β g).IsBipartite := + ⟨Sum.isLeft, by rintro (e | e) <;> simp⟩ + +omit [DecidableEq E] [DecidableEq P] [DecidableEq M] [DecidableEq V] in +/-- A child has one edge for each port of its group. -/ +theorem card_edges_child (g : G) : + Fintype.card ({e // S.group (N.src e) = g} ⊕ {e // S.group (N.tgt e) = g}) = + #{e | S.group (N.src e) = g} + #{e | S.group (N.tgt e) = g} := by + rw [Fintype.card_sum, Fintype.card_subtype, Fintype.card_subtype] + +/-- The bits of the ports of group `g`, read off the row bits `Y` and the column bits `X`. -/ +private abbrev groupBits (Y X : E → Bool) (g : G) : + {e // S.group (N.src e) = g} ⊕ {e // S.group (N.tgt e) = g} → Bool := + Sum.elim (fun e => Y e.1) (fun e => X e.1) + +/-- On the bits of its group, an old vertex of a child reads the row bits if it is a row and the +column bits if it is a column. -/ +private theorem child_tensor_inl_groupBits (β : M → ℕ) (g : G) (v : {v // S.group v = g}) + (Y X : E → Bool) : + (S.child β g).tensor (.inl v) (S.groupBits Y X g) = + if S.side v.1 then N.tensor v.1 Y else N.tensor v.1 X := by + simp only [child] + split_ifs with hv + · refine N.tensor_congr v.1 _ _ fun e he => ?_ + by_cases hs : N.src e = v.1 + · simp only [hs, dite_true, Sum.elim_inl] + · have ht : N.tgt e = v.1 := he.resolve_left hs + have := S.side_tgt e + rw [ht, hv] at this + exact absurd this (by decide) + · refine N.tensor_congr v.1 _ _ fun e he => ?_ + by_cases ht : N.tgt e = v.1 + · simp only [ht, dite_true, Sum.elim_inr] + · have hs : N.src e = v.1 := he.resolve_right ht + have := S.side_src e + rw [hs] at this + exact absurd this hv + +/-- On the bits of its group, a packet of a child reads the row and column bits. -/ +private theorem child_tensor_inr_groupBits (β : M → ℕ) (g : G) (q : P) (Y X : E → Bool) : + (S.child β g).tensor (.inr q) (S.groupBits Y X g) = S.packetTensor β q g Y X := by + simp only [child] + refine S.packetTensor_congr β q g (fun e _ hg => ?_) (fun e _ hg => ?_) + · simp only [hg, dite_true, Sum.elim_inl] + · simp only [hg, dite_true, Sum.elim_inr] + +/-- **The weight of an assignment factors over the groups.** For every tuple `β`, the term of +the expanded deformed contraction at the row bits `Y` and column bits `X` is the product, over the +groups, of the weights of the bits of each group in its child. -/ +private theorem weight_eq_prod_child [Fintype V] [Fintype P] [Fintype G] (β : M → ℕ) + (Y X : E → Bool) : + (∏ v ∈ univ.filter (S.side · = true), N.tensor v Y) * + (∏ v ∈ univ.filter (S.side · = false), N.tensor v X) * + ((∏ k, S.blockCoeff k β Y X) * ∏ Z, S.modeValue X Z ^ β Z) = + ∏ g, ∏ x, (S.child β g).tensor x (S.groupBits Y X g) := by + simp only [Fintype.prod_sum_type, S.child_tensor_inl_groupBits, S.child_tensor_inr_groupBits, + prod_mul_distrib] + rw [Fintype.prod_fiberwise S.group (fun v => if S.side v then N.tensor v Y else N.tensor v X), + prod_ite] + simp only [Bool.not_eq_true] + have hk : ∏ k, S.blockCoeff k β Y X = ∏ g, ∏ q, S.blockCoeff (q, g) β Y X := by + rw [Fintype.prod_prod_type, prod_comm] + have hZ : ∏ Z, S.modeValue X Z ^ β Z = + ∏ g, ∏ q, ∏ Z with S.sourceBlock Z = (q, g), S.modeValue X Z ^ β Z := by + rw [prod_comm, ← Fintype.prod_prod_type (f := fun k => ∏ Z with S.sourceBlock Z = k, + S.modeValue X Z ^ β Z), prod_fiberwise] + simp only [packetTensor, prod_mul_distrib, hk, hZ] + +/-- Splitting the row and column bits by group. -/ +private def splitBits : + (E → Bool) × (E → Bool) ≃ + ∀ g, ({e // S.group (N.src e) = g} ⊕ {e // S.group (N.tgt e) = g} → Bool) where + toFun YX g := S.groupBits YX.1 YX.2 g + invFun y := (fun e => y _ (.inl ⟨e, rfl⟩), fun e => y _ (.inr ⟨e, rfl⟩)) + left_inv _ := rfl + right_inv y := by + funext g c + rcases c with ⟨e, rfl⟩ | ⟨e, rfl⟩ <;> rfl + +/-- **The sum over the bits factors over the groups**: for each tuple `β`, the sum over all row +and column bits of the expanded term is the product of the contractions of the children. -/ +private theorem sum_weight_eq_prod_contraction_child [Fintype V] [Fintype P] [Fintype G] + (β : M → ℕ) : + ∑ X : E → Bool, ∑ Y : E → Bool, + (∏ v ∈ univ.filter (S.side · = true), N.tensor v Y) * + (∏ v ∈ univ.filter (S.side · = false), N.tensor v X) * + ((∏ k, S.blockCoeff k β Y X) * ∏ Z, S.modeValue X Z ^ β Z) = + ∏ g, (S.child β g).contraction := by + simp only [S.weight_eq_prod_child, contraction] + rw [Fintype.prod_sum, sum_comm, ← Fintype.sum_prod_type'] + exact Fintype.sum_equiv S.splitBits _ _ fun _ => rfl + +/-- **The exact child expansion.** In every commutative ring, the deformed contraction is the sum, +over the exponent tuples with entries at most the number of edges, of the products over the +groups of the contractions of the children. -/ +theorem deformedContraction_eq_sum_prod_contraction_child_of_le [Fintype V] [Fintype P] + [Fintype G] : + N.deformedContraction S.side W = + ∑ β ∈ Fintype.piFinset (fun _ : M => range (Fintype.card E + 1)), + ∏ g, (S.child β g).contraction := by + simp only [deformedContraction, S.prod_affineFactor_eq_sum, mul_sum] + exact (sum_congr rfl fun X _ => sum_comm).trans + (sum_comm.trans (sum_congr rfl fun β _ => S.sum_weight_eq_prod_contraction_child β)) + +/-- **The child identity** ((D.16) of [OpenAI2026ETH]). If the coefficient of every mode `Z` is +divisible by `π ^ w Z`, with positive weights, and `π ^ p = 0`, then the deformed contraction is +the sum, over the exponent tuples of total weight below `p`, of the products over the groups of +the contractions of the children. In the manuscript, `π = 2`, `p = p_t` and the ring is +`A / 2 ^ p_t`. -/ +theorem deformedContraction_eq_sum_prod_contraction_child [Fintype V] [Fintype P] [Fintype G] + (w : M → ℕ) (hw : ∀ Z, 0 < w Z) {π : R} {p : ℕ} (hπ : π ^ p = 0) + (hdiv : ∀ e Z, π ^ w Z ∣ S.coeff e Z) : + N.deformedContraction S.side W = ∑ β ∈ modeBudget w p, ∏ g, (S.child β g).contraction := by + simp only [deformedContraction, S.prod_affineFactor_eq_sum_modeBudget w hw hπ hdiv, mul_sum] + exact (sum_congr rfl fun X _ => sum_comm).trans + (sum_comm.trans (sum_congr rfl fun β _ => S.sum_weight_eq_prod_contraction_child β)) + +end Child + +end ModeSplitting + +end TensorNetwork + +end Cslib.Circuits.Frontier diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkModeExpansion.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkModeExpansion.lean new file mode 100644 index 0000000000..227e628e6f --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkModeExpansion.lean @@ -0,0 +1,103 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion + +/-! +# Tests for the mode expansion and the child identity + +Two edges `0 : 0 → 2` and `1 : 1 → 3`, from the rows `0, 1` to the columns `2, 3`, in one packet. +Vertex `v` is in group `v % 2`, so edge `e` joins two vertices of group `e`, and the entries of the +matrix `W = !![1, 2; 2, 3]` off the diagonal cross between the groups. Over `ZMod 4` they become two +modes of weight one, since `2 ∣ 2` and `2 ^ 2 = 0`: mode `Z` reads the column bit of edge `1 - Z` +and enters the form of row port `Z`. The child identity (D.16) then writes the deformed +contraction as a sum over the three tuples `β` of total weight below two. +-/ + +@[expose] public section + +namespace ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion + +open Cslib.Circuits.Frontier TensorNetwork + +/-- The edge at a vertex: vertices `0` and `2` are the ends of edge `0`, and `1` and `3` of +edge `1`. -/ +def edgeAt (v : Fin 4) : Fin 2 := ⟨v.val % 2, Nat.mod_lt _ (by decide)⟩ + +/-- The two-edge network, with tensor `(1, v + 1)` on the bit of the edge at `v`. -/ +def net : TensorNetwork (ZMod 4) Bool (Fin 4) (Fin 2) where + src e := ⟨e.val, by omega⟩ + tgt e := ⟨e.val + 2, by omega⟩ + tensor v x := if x (edgeAt v) then (v.val + 1 : ZMod 4) else 1 + tensor_congr v x y h := by rw [h (edgeAt v) (by fin_cases v <;> decide)] + +/-- The matrix: the identity on the diagonal, and `2` between the groups. -/ +def W : Matrix (Fin 2) (Fin 2) (ZMod 4) := !![1, 2; 2, 3] + +/-- The splitting of `W` into its diagonal and two modes. -/ +def S : net.ModeSplitting W (Fin 2) Unit (Fin 2) where + side v := decide (v.val < 2) + group := edgeAt + packet _ := () + localPart e e' := if e = e' then W e e else 0 + modePacket _ := () + source Z := Z + 1 + target Z := Z + coeff e Z := if e = Z then W Z (Z + 1) else 0 + form Z e' := if e' = Z + 1 then 1 else 0 + side_src := by decide + side_tgt := by decide + apply_eq e e' := by fin_cases e <;> fin_cases e' <;> decide + localPart_ne_zero e e' := by fin_cases e <;> fin_cases e' <;> decide + coeff_ne_zero e Z := by fin_cases e <;> fin_cases Z <;> decide + form_ne_zero Z e := by fin_cases Z <;> fin_cases e <;> decide + +/-- Both modes have coefficients divisible by `2`. -/ +theorem two_dvd_coeff (e Z : Fin 2) : 2 ^ 1 ∣ S.coeff e Z := by + have h : S.coeff e Z = 2 * (if e = Z then 1 else 0) := by + fin_cases e <;> fin_cases Z <;> decide + rw [h, pow_one] + exact dvd_mul_right _ _ + +/-- **The child identity** for this splitting, with weights one and `2 ^ 2 = 0` in `ZMod 4`. -/ +theorem deformedContraction_eq : + net.deformedContraction S.side W = + ∑ β ∈ modeBudget (fun _ : Fin 2 => 1) 2, ∏ g, (S.child β g).contraction := + S.deformedContraction_eq_sum_prod_contraction_child _ (fun _ => Nat.one_pos) + (by decide : (2 : ZMod 4) ^ 2 = 0) two_dvd_coeff + +/-- The budget holds the three tuples of total weight below two. -/ +example : modeBudget (fun _ : Fin 2 => 1) 2 = {![0, 0], ![1, 0], ![0, 1]} := by decide + +/-- The deformed contraction is `2`. -/ +example : net.deformedContraction S.side W = 2 := by decide + +/-- Without modes, the child of group `0` contracts to `1 + 3 = 0` and that of group `1` to `1`. +With mode `0`, whose coefficient `2` enters the row form of group `0`, the child of group `0` +vanishes. -/ +example : (S.child ![0, 0] 0).contraction = 0 ∧ (S.child ![0, 0] 1).contraction = 1 ∧ + (S.child ![1, 0] 0).contraction = 0 := by + decide + +/-- With mode `1`, the child of group `0` carries the mode value `L(X) = X₀` on its packet and +contracts to `3`, and the child of group `1` carries the coefficient `±2` and contracts to `2`. So +the only surviving term of (D.16) is `3 * 2 = 2`, the deformed contraction. -/ +example : (S.child ![0, 1] 0).contraction = 3 ∧ (S.child ![0, 1] 1).contraction = 2 := by + decide + +/-- The children are bipartite. -/ +example (β : Fin 2 → ℕ) (g : Fin 2) : (S.child β g).IsBipartite := S.isBipartite_child β g + +/-- Each child has the row port and the column port of its group. -/ +example (g : Fin 2) : + Fintype.card ({e // S.group (net.src e) = g} ⊕ {e // S.group (net.tgt e) = g}) = 2 := by + rw [S.card_edges_child] + fin_cases g <;> decide + +end ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion From 05c80d0e48f19f7c39322f9a4d2e8886dd8bcb55 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 19:29:38 -0400 Subject: [PATCH 008/129] feat(circuit/frontier): modes of the Cauchy matrix, the geometric expansion (D.12) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Cauchy kernel in local coordinates over Z_(2) (Lemma D.4) relates the kernels at d0 and d0 + 2^(i+1) j exactly once 2^v is cancelled, so in a ring with 2^p = 0 the factor 1 - rho j is inverted by its geometric series: W_d(T) = sum_{b Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 3 +- .../Frontier/TensorNetwork/CauchyModes.lean | 633 ++++++++++++++++++ .../Frontier/TensorNetworkCauchyModes.lean | 91 +++ 3 files changed, 726 insertions(+), 1 deletion(-) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/CauchyModes.lean create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkCauchyModes.lean diff --git a/Complexity.lean b/Complexity.lean index b11988bdb5..db420cdf23 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -858,11 +858,12 @@ public import Complexity.Computability.Circuit.Frontier.Rectangle public import Complexity.Computability.Circuit.Frontier.Reduction public import Complexity.Computability.Circuit.Frontier.Sweep public import Complexity.Computability.Circuit.Frontier.TensorNetwork +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CauchyModes public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness public import Complexity.Computability.Circuit.Frontier.Unconditional public import Complexity.Computability.Circuit.Frontier.Witness diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/CauchyModes.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/CauchyModes.lean new file mode 100644 index 0000000000..77494386cd --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/CauchyModes.lean @@ -0,0 +1,633 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion +public import Mathlib.NumberTheory.Padics.PadicVal.Defs +import Mathlib.Algebra.Ring.GeomSum +import Mathlib.NumberTheory.Padics.PadicVal.Basic +import Mathlib.RingTheory.Nilpotent.Basic +import Mathlib.Tactic.LinearCombination + +/-! +# The geometric expansion of the Cauchy kernel + +This file proves the geometric expansion (D.12) of [OpenAI2026ETH] (§D.4.5, p. 62), which turns +the crossing entries of the Cauchy matrix near a grid point into the modes of +`Cslib.Circuits.Frontier.TensorNetwork.ModeSplitting`. + +**Local kernels** (`localKernel`). By Lemma D.4 (`cauchyKernel_comp_mem_lifts`), the Cauchy kernel +`W_d` on the grid of `L = 2 ^ a` slots has `2`-integral coefficients in local coordinates +`z ↦ W_d(t + α z)`, `α = 2 ^ (a + 1)`. `localKernel a t d` is this polynomial over `ℤ_(2)`, and for +a ring homomorphism `κ : ℤ_(2) → R` and `ζ ∈ R`, `kernelValue κ ζ a t d` is its value +`W_d(t + α ζ)` in `R`. + +**The expansion.** Fix `t` and two grid points `d₀` and `d = d₀ + 2 ^ (i + 1) j` with +`2 ^ (i + 1) ∤ t - d₀`, `i ≤ a`. All kernels are `C(T) / (C'(0) (T - d))`, so +`W_d(T) (T - d) = W_{d₀}(T) (T - d₀)`. Write `t - d₀ = 2 ^ v u₀` with `u₀` odd and `v ≤ i`; dividing +by `2 ^ v` in `ℤ_(2)[z]` makes + + `W_d(t + α z) (u₀ + 2 ^ (a + 1 - v) z - 2 ^ (i + 1 - v) j)` equal to + `W_{d₀}(t + α z) (u₀ + 2 ^ (a + 1 - v) z)` + +(`localKernel_mul_eq`). In a ring `R` with `2 ^ p = 0`, `U = u₀ + 2 ^ (a + 1 - v) ζ` is a unit, and +with the *cross ratio* `ρ = 2 ^ (i + 1 - v) U⁻¹ = 2 ^ (i + 1) / (T - d₀)` (`crossRatio`) the factor +`1 - ρ j` is inverted by its geometric series, since `2 ∣ ρ`: + + `W_d(T) = ∑_{b < p} W_{d₀}(T) ρ ^ b j ^ b` (`kernelValue_eq_sum`), + +the manuscript's (D.12), there written with the remainder beyond `b + 1 < p_t` dropped modulo +`2 ^ p_t`. When `d₀ ≠ t`, `W_{d₀}(T)` is divisible by `2` (Lemma D.4), so the coefficient of `j ^ b` +is divisible by `2 ^ (b + 1)` (`two_pow_dvd_kernelValue_mul_crossRatio_pow`): the weight +`w_Z = b + 1` of the mode. + +**The modes of a packing** (`TensorNetwork.cauchyModeSplitting`). Pack the edges into packets of +`2 ^ a` slots, oriented in coordinate `i` as in Lemma D.3 (2): each packet `q` has a pair of groups, +and digit `i` of an edge's slot says which of them is its row group. For a grid point `t` of +valuation `i`, a row slot `x` and a column slot `y` of a packet with two different groups lie in +different groups exactly when their digits `i` agree, and then `v₂(t - y₀ + x) ≤ i` for the tile +`y₀ = y mod 2 ^ (i + 1)` (`not_dvd_sub_sub_of_testBit_eq`). So the Cauchy matrix at `T = t + α ζ` +(`TensorNetwork.cauchyValueMatrix`) splits into its local entries and one mode per packet, tile and +`b < p`, of weight `b + 1`, and the child identity of +`Cslib.Circuits.Frontier.TensorNetwork.ModeSplitting` applies +(`TensorNetwork.deformedContraction_cauchyValueMatrix_eq_sum`). The manuscript's modes are those +with `b + 1 < p_t`; the one with `b + 1 = p_t` has weight `p_t` and drops out of (D.14). + +## Main definitions + +* `Cslib.Circuits.Frontier.localKernel`: the Cauchy kernel in local coordinates over `ℤ_(2)`. +* `Cslib.Circuits.Frontier.kernelValue`: its value at `z = ζ` in a `ℤ_(2)`-algebra. +* `Cslib.Circuits.Frontier.crossRatio`: the ratio `2 ^ (i + 1) / (T - d₀)`. +* `Cslib.Circuits.Frontier.TensorNetwork.cauchyValueMatrix`: the Cauchy matrix of a packing at + `T = t + α ζ`. +* `Cslib.Circuits.Frontier.TensorNetwork.cauchyModeCoeff`, + `Cslib.Circuits.Frontier.TensorNetwork.cauchyModeForm`: the coefficients and forms of its modes. +* `Cslib.Circuits.Frontier.TensorNetwork.cauchyModeSplitting`: its mode splitting. + +## Main results + +* `Cslib.Circuits.Frontier.map_localKernel`: the local kernel is the composed Cauchy kernel. +* `Cslib.Circuits.Frontier.exists_localKernel_eq_two_mul`: away from its node it is divisible by + `2`. +* `Cslib.Circuits.Frontier.localKernel_mul_eq`: the exact relation between the kernels at `d₀` + and `d₀ + 2 ^ (i + 1) j`. +* `Cslib.Circuits.Frontier.kernelValue_eq_sum`: the geometric expansion (D.12). +* `Cslib.Circuits.Frontier.two_pow_dvd_kernelValue_mul_crossRatio_pow`: its coefficients are + divisible by `2 ^ (b + 1)`. +* `Cslib.Circuits.Frontier.not_dvd_sub_sub_of_testBit_eq`: a row and a column tile with the same + digit `i` give `v₂(t - y₀ + x) ≤ i`. +* `Cslib.Circuits.Frontier.TensorNetwork.two_pow_dvd_cauchyModeCoeff`: the modes have weight + `b + 1`. +* `Cslib.Circuits.Frontier.TensorNetwork.deformedContraction_cauchyValueMatrix_eq_sum`: the child + identity (D.16) for the Cauchy matrix of a packing. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Lemma D.3 (2) (p. 58), + Lemma D.4 (pp. 59–60), and §D.4.5–§D.4.6, (D.12)–(D.16) (pp. 62–63). +-/ + +public section + +namespace Cslib.Circuits.Frontier + +open Polynomial Finset + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +/-! ### Local kernels over `ℤ_(2)` -/ + +/-- The Cauchy kernel `W_d` on the grid of `2 ^ a` slots in local coordinates +`z ↦ W_d(t + α z)`, `α = 2 ^ (a + 1)`, as a polynomial over `ℤ_(2)` (Lemma D.4 of +[OpenAI2026ETH]); zero when `t` or `d` is off the grid. -/ +noncomputable def localKernel (a : ℕ) (t d : ℤ) : ℤ₍₂₎[X] := + if h : t ∈ cauchyGrid (2 ^ a) ∧ d ∈ cauchyGrid (2 ^ a) then + ((mem_lifts _).mp (cauchyKernel_comp_mem_lifts h.1 h.2)).choose + else 0 + +/-- The local kernel is the Cauchy kernel composed with `t + α z`. -/ +theorem map_localKernel {a : ℕ} {t d : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (hd : d ∈ cauchyGrid (2 ^ a)) : + (localKernel a t d).map (ℤ₍₂₎).subtype = + (cauchyKernel (2 ^ a) d).comp (C (t : ℚ) + C ((2 : ℚ) ^ (a + 1)) * X) := by + rw [localKernel, dite_eq_left ⟨ht, hd⟩] + exact ((mem_lifts _).mp (cauchyKernel_comp_mem_lifts ht hd)).choose_spec + +private theorem map_subtype_injective : + Function.Injective (Polynomial.map (ℤ₍₂₎).subtype) := + Polynomial.map_injective _ Subtype.val_injective + +/-- **Lemma D.4, divisibility, over `ℤ_(2)`.** Away from its node, the local kernel is twice a +polynomial over `ℤ_(2)`. -/ +theorem exists_localKernel_eq_two_mul {a : ℕ} {t d : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (hd : d ∈ cauchyGrid (2 ^ a)) (hdt : d ≠ t) : ∃ Q : ℤ₍₂₎[X], localKernel a t d = C 2 * Q := by + obtain ⟨P, hP, h⟩ := exists_cauchyKernel_comp_eq_two_mul ht hd hdt + obtain ⟨Q, rfl⟩ := (mem_lifts _).mp hP + refine ⟨Q, map_subtype_injective ?_⟩ + rw [map_localKernel ht hd, h, Polynomial.map_mul, Polynomial.map_C] + congr 2 + +/-- Every kernel on the grid is `C(T) / C'(0)` divided by `T - d`. -/ +private theorem cauchyKernel_mul_X_sub {L : ℕ} {d : ℤ} (hd : d ∈ cauchyGrid L) : + cauchyKernel L d * (X - C (d : ℚ)) = + C ((Lagrange.nodal ((cauchyGrid L).erase 0) (Int.cast : ℤ → ℚ)).eval 0)⁻¹ * + Lagrange.nodal (cauchyGrid L) (Int.cast : ℤ → ℚ) := by + rw [cauchyKernel, Lagrange.nodal_eq_mul_nodal_erase hd] + ring + +/-- **The kernels of a tile.** For grid points `d₀` and `d = d₀ + 2 ^ (i + 1) j`, with +`t - d₀ = 2 ^ v u₀` and `v ≤ i ≤ a`, the local kernels satisfy +`W_d (u₀ + 2 ^ (a + 1 - v) z - 2 ^ (i + 1 - v) j) = W_{d₀} (u₀ + 2 ^ (a + 1 - v) z)`: both sides +are `C(t + α z) / (C'(0) 2 ^ v)`. -/ +theorem localKernel_mul_eq {a i v : ℕ} {t d₀ d j u₀ : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (hd₀ : d₀ ∈ cauchyGrid (2 ^ a)) (hd : d ∈ cauchyGrid (2 ^ a)) + (hdd : d = d₀ + 2 ^ (i + 1) * j) (htd : t - d₀ = 2 ^ v * u₀) (hvi : v ≤ i) (hia : i ≤ a) : + localKernel a t d * (C (u₀ : ℤ₍₂₎) + C ((2 : ℤ₍₂₎) ^ (a + 1 - v)) * X - + C ((2 ^ (i + 1 - v) * j : ℤ) : ℤ₍₂₎)) = + localKernel a t d₀ * (C (u₀ : ℤ₍₂₎) + C ((2 : ℤ₍₂₎) ^ (a + 1 - v)) * X) := by + refine map_subtype_injective ?_ + have hmap₁ : (C (u₀ : ℤ₍₂₎) + C ((2 : ℤ₍₂₎) ^ (a + 1 - v)) * X).map ℤ₍₂₎.subtype = + C (u₀ : ℚ) + C ((2 : ℚ) ^ (a + 1 - v)) * X := by + rw [Polynomial.map_add, Polynomial.map_mul, Polynomial.map_C, Polynomial.map_C, + Polynomial.map_X, map_intCast, map_pow, map_ofNat] + have hmap₂ : (C ((2 ^ (i + 1 - v) * j : ℤ) : ℤ₍₂₎)).map ℤ₍₂₎.subtype = + C ((2 : ℚ) ^ (i + 1 - v) * j) := by + rw [Polynomial.map_C, map_intCast] + push_cast + rfl + rw [Polynomial.map_mul, Polynomial.map_mul, Polynomial.map_sub, hmap₁, hmap₂, + map_localKernel ht hd, map_localKernel ht hd₀] + set Y : ℚ[X] := C (t : ℚ) + C ((2 : ℚ) ^ (a + 1)) * X + -- Multiply both sides by `2 ^ v`, which turns the linear factors into `Y - d` and `Y - d₀`. + have h1 : (2 : ℚ) ^ v * u₀ = t - d₀ := by exact_mod_cast htd.symm + have h2 : (2 : ℚ) ^ v * 2 ^ (a + 1 - v) = 2 ^ (a + 1) := by + rw [← pow_add, Nat.add_sub_cancel' (by omega)] + have h3 : (2 : ℚ) ^ v * (2 ^ (i + 1 - v) * j) = d - d₀ := by + rw [← mul_assoc, ← pow_add, Nat.add_sub_cancel' (by omega)] + exact_mod_cast (sub_eq_of_eq_add' hdd).symm + have hYd : C ((2 : ℚ) ^ v) * (C (u₀ : ℚ) + C (2 ^ (a + 1 - v)) * X - + C (2 ^ (i + 1 - v) * (j : ℚ))) = Y - C (d : ℚ) := by + rw [mul_sub, mul_add, ← C_mul, ← mul_assoc, ← C_mul, ← C_mul, h1, h2, h3] + simp only [Y, C_sub] + ring + have hYd₀ : C ((2 : ℚ) ^ v) * (C (u₀ : ℚ) + C (2 ^ (a + 1 - v)) * X) = Y - C (d₀ : ℚ) := by + rw [mul_add, ← C_mul, ← mul_assoc, ← C_mul, h1, h2] + simp only [Y, C_sub] + ring + have hcomp : ∀ e ∈ cauchyGrid (2 ^ a), + (cauchyKernel (2 ^ a) e).comp Y * (Y - C (e : ℚ)) = + (C ((Lagrange.nodal ((cauchyGrid (2 ^ a)).erase 0) (Int.cast : ℤ → ℚ)).eval 0)⁻¹ * + Lagrange.nodal (cauchyGrid (2 ^ a)) (Int.cast : ℤ → ℚ)).comp Y := by + intro e he + rw [← cauchyKernel_mul_X_sub he, mul_comp, sub_comp, X_comp, C_comp] + have h2v : C ((2 : ℚ) ^ v) ≠ 0 := C_ne_zero.mpr (pow_ne_zero _ two_ne_zero) + refine mul_left_cancel₀ h2v ?_ + rw [mul_left_comm, hYd, mul_left_comm, hYd₀, hcomp d hd, hcomp d₀ hd₀] + +/-! ### Values in a ring where `2` is nilpotent -/ + +variable {R : Type*} [CommRing R] + +/-- The value `W_d(t + α ζ)` of the Cauchy kernel at `T = t + α ζ`, `α = 2 ^ (a + 1)`, in a ring `R` +receiving `ℤ_(2)` through `κ`. -/ +@[expose] noncomputable def kernelValue (κ : ℤ₍₂₎ →+* R) (ζ : R) (a : ℕ) (t d : ℤ) : R := + (localKernel a t d).eval₂ κ ζ + +/-- The *cross ratio* `ρ = 2 ^ (i + 1) / (T - d₀)` at `T = t + α ζ`: with `t - d₀ = 2 ^ v u₀`, `u₀` +odd, it is `2 ^ (i + 1 - v)` times the inverse of `u₀ + 2 ^ (a + 1 - v) ζ`. -/ +@[expose] noncomputable def crossRatio (a i : ℕ) (t d₀ : ℤ) (ζ : R) : R := + 2 ^ (i + 1 - padicValInt 2 (t - d₀)) * + Ring.inverse ((((t - d₀) / 2 ^ padicValInt 2 (t - d₀) : ℤ) : R) + + 2 ^ (a + 1 - padicValInt 2 (t - d₀)) * ζ) + +/-- An integer not divisible by `2 ^ (i + 1)` is `2 ^ v` times an odd integer, with `v ≤ i`: +`v` is its `2`-adic valuation. -/ +private theorem two_adic_split {n : ℤ} {i : ℕ} (hn : ¬ (2 : ℤ) ^ (i + 1) ∣ n) : + padicValInt 2 n ≤ i ∧ Odd (n / 2 ^ padicValInt 2 n) ∧ + n = 2 ^ padicValInt 2 n * (n / 2 ^ padicValInt 2 n) := by + have hn0 : n ≠ 0 := by rintro rfl; exact hn (dvd_zero _) + have hdvd : (2 : ℤ) ^ padicValInt 2 n ∣ n := by exact_mod_cast padicValInt_dvd (p := 2) n + have hsplit := (Int.mul_ediv_cancel' hdvd).symm + refine ⟨?_, ?_, hsplit⟩ + · by_contra h + exact hn (by exact_mod_cast (padicValInt_dvd_iff (p := 2) (i + 1) n).mpr (Or.inr (by omega))) + · rw [← Int.not_even_iff_odd, even_iff_two_dvd] + rintro ⟨c, hc⟩ + have : (2 : ℤ) ^ (padicValInt 2 n + 1) ∣ n := ⟨c, by + calc n = 2 ^ padicValInt 2 n * (n / 2 ^ padicValInt 2 n) := hsplit + _ = 2 ^ padicValInt 2 n * (2 * c) := by rw [hc] + _ = 2 ^ (padicValInt 2 n + 1) * c := by ring⟩ + have h := (padicValInt_dvd_iff (p := 2) (padicValInt 2 n + 1) n).mp (by exact_mod_cast this) + omega + +/-- In a ring where `2` is nilpotent, `1 + 2 x` is a unit. -/ +private theorem isUnit_one_add_two_mul {p : ℕ} (hp : (2 : R) ^ p = 0) (x : R) : + IsUnit (1 + 2 * x) := + IsNilpotent.isUnit_one_add ⟨p, by rw [mul_pow, hp, zero_mul]⟩ + +/-- **The geometric expansion (D.12).** Let `2 ^ p = 0` in `R`, and let `d₀` and +`d = d₀ + 2 ^ (i + 1) j` be grid points of `2 ^ a` slots with `2 ^ (i + 1) ∤ t - d₀` and `i ≤ a`. +Then +`W_d(T) = ∑_{b < p} W_{d₀}(T) ρ ^ b j ^ b` at `T = t + α ζ`, with `ρ = crossRatio a i t d₀ ζ`. -/ +theorem kernelValue_eq_sum (κ : ℤ₍₂₎ →+* R) (ζ : R) {p a i : ℕ} (hp : (2 : R) ^ p = 0) + {t d₀ d j : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) (hd₀ : d₀ ∈ cauchyGrid (2 ^ a)) + (hd : d ∈ cauchyGrid (2 ^ a)) (hdd : d = d₀ + 2 ^ (i + 1) * j) + (hcross : ¬ (2 : ℤ) ^ (i + 1) ∣ t - d₀) (hia : i ≤ a) : + kernelValue κ ζ a t d = + ∑ b ∈ range p, kernelValue κ ζ a t d₀ * crossRatio a i t d₀ ζ ^ b * (j : R) ^ b := by + obtain ⟨hvi, hodd, hsplit⟩ := two_adic_split hcross + set v := padicValInt 2 (t - d₀) + set u₀ := (t - d₀) / 2 ^ v + have h : kernelValue κ ζ a t d * ((u₀ : R) + 2 ^ (a + 1 - v) * ζ - 2 ^ (i + 1 - v) * j) = + kernelValue κ ζ a t d₀ * ((u₀ : R) + 2 ^ (a + 1 - v) * ζ) := by + have h := congrArg (eval₂ κ ζ) (localKernel_mul_eq ht hd₀ hd hdd hsplit hvi hia) + simp only [eval₂_mul, eval₂_sub, eval₂_add, eval₂_C, eval₂_X] at h + simp only [map_intCast, map_pow, map_ofNat] at h + push_cast at h + exact h + -- The unit `U = u₀ + 2 ^ (a + 1 - v) ζ`. + set U : R := (u₀ : R) + 2 ^ (a + 1 - v) * ζ + have hU : IsUnit U := by + obtain ⟨c, hc⟩ := hodd + have : U = 1 + 2 * ((c : R) + 2 ^ (a - v) * ζ) := by + simp only [U, hc] + rw [show a + 1 - v = a - v + 1 by omega, pow_succ] + push_cast + ring + rw [this] + exact isUnit_one_add_two_mul hp _ + set ρ := crossRatio a i t d₀ ζ + have hρ : ρ = 2 ^ (i + 1 - v) * Ring.inverse U := rfl + have hUρ : U * ρ = 2 ^ (i + 1 - v) := by + rw [hρ, mul_left_comm, Ring.mul_inverse_cancel U hU, mul_one] + -- Cancel `U`: `W_d (1 - ρ j) = W_{d₀}`. + have hcancel : kernelValue κ ζ a t d * (1 - ρ * j) = kernelValue κ ζ a t d₀ := by + have h' : kernelValue κ ζ a t d * (1 - ρ * j) * U = kernelValue κ ζ a t d₀ * U := by + linear_combination h - (kernelValue κ ζ a t d * j) * hUρ + exact hU.mul_right_cancel h' + -- The geometric series inverts `1 - ρ j`, as `(ρ j) ^ p = 0`. + have hnil : (ρ * j) ^ p = 0 := by + have : ρ = 2 * (2 ^ (i - v) * Ring.inverse U) := by + rw [hρ, show i + 1 - v = i - v + 1 by omega, pow_succ] + ring + rw [this, mul_assoc, mul_pow, hp, zero_mul] + have hgeom := mul_neg_geom_sum (ρ * (j : R)) p + rw [hnil, sub_zero] at hgeom + calc kernelValue κ ζ a t d + = kernelValue κ ζ a t d * ((1 - ρ * j) * ∑ b ∈ range p, (ρ * (j : R)) ^ b) := by + rw [hgeom, mul_one] + _ = kernelValue κ ζ a t d₀ * ∑ b ∈ range p, (ρ * (j : R)) ^ b := by rw [← mul_assoc, hcancel] + _ = _ := by + rw [mul_sum] + refine sum_congr rfl fun b _ => ?_ + rw [mul_pow, mul_assoc] + +/-- **The coefficients of (D.12) have weight `b + 1`.** When `d₀ ≠ t`, the coefficient +`W_{d₀}(T) ρ ^ b` of `j ^ b` is divisible by `2 ^ (b + 1)`. -/ +theorem two_pow_dvd_kernelValue_mul_crossRatio_pow (κ : ℤ₍₂₎ →+* R) (ζ : R) {a i : ℕ} + {t d₀ : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) (hd₀ : d₀ ∈ cauchyGrid (2 ^ a)) + (hcross : ¬ (2 : ℤ) ^ (i + 1) ∣ t - d₀) (b : ℕ) : + (2 : R) ^ (b + 1) ∣ kernelValue κ ζ a t d₀ * crossRatio a i t d₀ ζ ^ b := by + have hd₀t : d₀ ≠ t := by rintro rfl; exact hcross (by rw [sub_self]; exact dvd_zero _) + obtain ⟨Q, hQ⟩ := exists_localKernel_eq_two_mul ht hd₀ hd₀t + have hvi := (two_adic_split hcross).1 + have hk : kernelValue κ ζ a t d₀ = 2 * Q.eval₂ κ ζ := by + rw [kernelValue, hQ, eval₂_mul, eval₂_C, map_ofNat] + have hρ : crossRatio a i t d₀ ζ = 2 * (2 ^ (i - padicValInt 2 (t - d₀)) * + Ring.inverse ((((t - d₀) / 2 ^ padicValInt 2 (t - d₀) : ℤ) : R) + + 2 ^ (a + 1 - padicValInt 2 (t - d₀)) * ζ)) := by + rw [crossRatio, show i + 1 - padicValInt 2 (t - d₀) = i - padicValInt 2 (t - d₀) + 1 by + omega, pow_succ] + ring + rw [hk, hρ, mul_pow, pow_succ'] + exact mul_dvd_mul (dvd_mul_right _ _) (dvd_mul_right _ _) + +/-! ### The modes of a packing -/ + +/-- A row and a column slot with the same binary digit `i`, at a grid point `t` of valuation `i`: +`t - (y₀ - x)` is not divisible by `2 ^ (i + 1)`, for the residue `y₀ < 2 ^ (i + 1)` of the column +slot. (Otherwise `y₀ ≡ x + 2 ^ i` modulo `2 ^ (i + 1)`, which flips digit `i`.) -/ +theorem not_dvd_sub_sub_of_testBit_eq {t : ℤ} {i x y₀ : ℕ} (hti : (2 : ℤ) ^ i ∣ t) + (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) (hy₀ : y₀ < 2 ^ (i + 1)) (hbit : x.testBit i = y₀.testBit i) : + ¬ (2 : ℤ) ^ (i + 1) ∣ t - ((y₀ : ℤ) - x) := by + rintro ⟨n, hn⟩ + obtain ⟨m, rfl⟩ := hti + have hm : ¬ (2 : ℤ) ∣ m := fun ⟨c, hc⟩ => hti' ⟨c, by rw [hc, pow_succ]; ring⟩ + obtain ⟨c, rfl⟩ : ∃ c, m = 2 * c + 1 := ⟨m / 2, by omega⟩ + rw [Nat.testBit_eq_decide_div_mod_eq, Nat.testBit_eq_decide_div_mod_eq, decide_eq_decide] at hbit + obtain ⟨K, hK⟩ : ∃ K, 2 ^ i = K := ⟨_, rfl⟩ + have hKpos : 0 < K := hK ▸ by positivity + have hpow : (2 : ℤ) ^ i = K := by exact_mod_cast hK + have hpow' : (2 : ℤ) ^ (i + 1) = 2 * K := by rw [pow_succ, hpow, mul_comm] + have hy₀' : y₀ < K * 2 := by rw [← hK, ← pow_succ]; exact hy₀ + rw [hK] at hbit + rw [hpow', hpow] at hn + -- Digit `i` of both is `h`; write `x = K (2 q + h) + lx` and `y₀ = K h + ly`. + have hy₀K : y₀ / K < 2 := Nat.div_lt_of_lt_mul hy₀' + have hh : x / K % 2 = y₀ / K := by + have h1 := Nat.mod_lt (x / K) two_pos + have h2 : y₀ / K % 2 = y₀ / K := Nat.mod_eq_of_lt hy₀K + by_cases hx1 : x / K % 2 = 1 + · have := hbit.mp hx1 + omega + · have := mt hbit.mpr hx1 + omega + have hx := Nat.div_add_mod x K + have hx2 := Nat.div_add_mod (x / K) 2 + have hy := Nat.div_add_mod y₀ K + have hlx := Nat.mod_lt x hKpos + have hly := Nat.mod_lt y₀ hKpos + obtain ⟨lx, hlxd⟩ : ∃ lx, x % K = lx := ⟨_, rfl⟩ + obtain ⟨ly, hlyd⟩ : ∃ ly, y₀ % K = ly := ⟨_, rfl⟩ + obtain ⟨q, hqd⟩ : ∃ q, x / K / 2 = q := ⟨_, rfl⟩ + obtain ⟨h, hhd⟩ : ∃ h, y₀ / K = h := ⟨_, rfl⟩ + rw [hlxd] at hx hlx + rw [hlyd] at hy hly + rw [hqd, hh, hhd] at hx2 + rw [hhd] at hy + rw [← hx2] at hx + have hxZ : (x : ℤ) = K * (2 * q + h) + lx := by exact_mod_cast hx.symm + have hyZ : (y₀ : ℤ) = K * h + ly := by exact_mod_cast hy.symm + have hlx' : (lx : ℤ) < K := by exact_mod_cast hlx + have hly' : (ly : ℤ) < K := by exact_mod_cast hly + have hK' : (0 : ℤ) < K := by exact_mod_cast hKpos + have hlx0 : (0 : ℤ) ≤ lx := Int.natCast_nonneg lx + have hly0 : (0 : ℤ) ≤ ly := Int.natCast_nonneg ly + -- Then `0 < K + lx - ly < 2 K` would be a multiple of `2 K`. + rw [hxZ, hyZ] at hn + have hw : (K : ℤ) + lx - ly = 2 * K * (n - c - q) := by linear_combination hn + rcases le_or_gt (n - c - q) 0 with h0 | h0 + · have : (2 * K : ℤ) * (n - c - q) ≤ 0 := mul_nonpos_of_nonneg_of_nonpos (by positivity) h0 + linarith + · have : (2 * K : ℤ) * 1 ≤ 2 * K * (n - c - q) := + mul_le_mul_of_nonneg_left (by omega) (by positivity) + linarith + +namespace TensorNetwork + +variable {V E G P : Type*} + +/-- The Cauchy matrix of a packing at `T = t + α ζ`, `α = 2 ^ (a + 1)`: between two edges of the +same packet, the Cauchy kernel at the difference of their slots, and zero between packets (the +manuscript's (D.7) at `T`). -/ +@[expose] noncomputable def cauchyValueMatrix [DecidableEq P] (κ : ℤ₍₂₎ →+* R) (ζ : R) (a : ℕ) + (t : ℤ) (packet : E → P) (slot : E → ℕ) : Matrix E E R := + fun e e' => if packet e = packet e' then kernelValue κ ζ a t ((slot e' : ℤ) - slot e) else 0 + +section Modes + +variable [Fintype E] [Fintype P] [DecidableEq G] [DecidableEq P] {N : TensorNetwork R Bool V E} + {group : V → G} {packet : E → P} {slot : E → ℕ} {sym : P → G × G} {i : ℕ} + +omit [CommRing R] [Fintype E] [Fintype P] [DecidableEq G] [DecidableEq P] in +private theorem group_src_eq + (horient : ∀ e, (group (N.src e), group (N.tgt e)) = + if (slot e).testBit i then (sym (packet e)).swap else sym (packet e)) (e : E) : + group (N.src e) = if (slot e).testBit i then (sym (packet e)).2 else (sym (packet e)).1 := by + have h := congrArg Prod.fst (horient e) + split_ifs at h ⊢ <;> exact h + +omit [CommRing R] [Fintype E] [Fintype P] [DecidableEq G] [DecidableEq P] in +private theorem group_tgt_eq + (horient : ∀ e, (group (N.src e), group (N.tgt e)) = + if (slot e).testBit i then (sym (packet e)).swap else sym (packet e)) (e : E) : + group (N.tgt e) = if (slot e).testBit i then (sym (packet e)).1 else (sym (packet e)).2 := by + have h := congrArg Prod.snd (horient e) + split_ifs at h ⊢ <;> exact h + +/-- The coefficient of the mode `Z = (q, y₀, b)` in the row form of `e`: for a row of the packet +`q` whose two groups differ, with digit `i` of its slot `x` equal to that of `y₀`, it is +`W_{y₀ - x}(T) ρ ^ b`, the coefficient of `j ^ b` in (D.12); otherwise zero. -/ +@[expose] noncomputable def cauchyModeCoeff (κ : ℤ₍₂₎ →+* R) (ζ : R) (a i : ℕ) (t : ℤ) + (packet : E → P) (slot : E → ℕ) (sym : P → G × G) {p : ℕ} (e : E) + (Z : P × Fin (2 ^ (i + 1)) × Fin p) : R := + if packet e = Z.1 ∧ (sym Z.1).1 ≠ (sym Z.1).2 ∧ (slot e).testBit i = (Z.2.1 : ℕ).testBit i then + kernelValue κ ζ a t ((Z.2.1 : ℤ) - slot e) * + crossRatio a i t ((Z.2.1 : ℤ) - slot e) ζ ^ (Z.2.2 : ℕ) + else 0 + +/-- The form of the mode `Z = (q, y₀, b)` at the column `e'`: for a column of the packet `q` whose +two groups differ, in slot `y = y₀ + 2 ^ (i + 1) j` of the tile `y₀`, it is `j ^ b`; otherwise +zero. -/ +@[expose] noncomputable def cauchyModeForm (R : Type*) [CommRing R] (i : ℕ) (packet : E → P) + (slot : E → ℕ) (sym : P → G × G) {p : ℕ} (Z : P × Fin (2 ^ (i + 1)) × Fin p) (e' : E) : R := + if packet e' = Z.1 ∧ (sym Z.1).1 ≠ (sym Z.1).2 ∧ slot e' % 2 ^ (i + 1) = Z.2.1 then + ((slot e' / 2 ^ (i + 1) : ℕ) : R) ^ (Z.2.2 : ℕ) + else 0 + +omit [Fintype E] in +/-- The Cauchy matrix at `T` is its local part plus its modes: the geometric expansion (D.12) on +the crossing entries. -/ +private theorem cauchyValueMatrix_eq_add_sum + (horient : ∀ e, (group (N.src e), group (N.tgt e)) = + if (slot e).testBit i then (sym (packet e)).swap else sym (packet e)) + {a : ℕ} (hslot : ∀ e, slot e < 2 ^ a) (hia : i < a) (κ : ℤ₍₂₎ →+* R) (ζ : R) {p : ℕ} + (hp : (2 : R) ^ p = 0) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) (hti : (2 : ℤ) ^ i ∣ t) + (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) (e e' : E) : + cauchyValueMatrix κ ζ a t packet slot e e' = + (if group (N.tgt e') = group (N.src e) then cauchyValueMatrix κ ζ a t packet slot e e' + else 0) + + ∑ Z : P × Fin (2 ^ (i + 1)) × Fin p, + cauchyModeCoeff κ ζ a i t packet slot sym e Z * + cauchyModeForm R i packet slot sym Z e' := by + have hsrc := group_src_eq horient e + have htgt := group_tgt_eq horient e' + -- A mode term is nonzero only at the packet of both edges, when its groups differ, at the digit + -- of `e` and the tile of `e'`. + have hterm : ∀ Z : P × Fin (2 ^ (i + 1)) × Fin p, + cauchyModeCoeff κ ζ a i t packet slot sym e Z * cauchyModeForm R i packet slot sym Z e' ≠ 0 → + packet e = Z.1 ∧ packet e' = Z.1 ∧ (sym Z.1).1 ≠ (sym Z.1).2 ∧ + (slot e).testBit i = (Z.2.1 : ℕ).testBit i ∧ slot e' % 2 ^ (i + 1) = Z.2.1 := by + intro Z hZ + have hc := left_ne_zero_of_mul hZ + have hf := right_ne_zero_of_mul hZ + unfold cauchyModeCoeff at hc + unfold cauchyModeForm at hf + obtain ⟨h1, h2, h3⟩ := not_not.mp fun h => hc (ite_eq_right h) + obtain ⟨h4, -, h5⟩ := not_not.mp fun h => hf (ite_eq_right h) + exact ⟨h1, h4, h2, h3, h5⟩ + have hbit' : ∀ y₀ : ℕ, slot e' % 2 ^ (i + 1) = y₀ → (slot e').testBit i = y₀.testBit i := by + rintro y₀ rfl + rw [Nat.testBit_mod_two_pow] + simp + by_cases hloc : group (N.tgt e') = group (N.src e) + · -- Local entries: no mode term survives. + rw [ite_eq_left hloc, left_eq_add] + refine sum_eq_zero fun Z _ => by_contra fun hZ => ?_ + obtain ⟨h1, h2, h3, h4, h5⟩ := hterm Z hZ + have hb : (slot e').testBit i = (slot e).testBit i := (hbit' _ h5).trans h4.symm + rw [htgt, hsrc, hb, h2, h1] at hloc + split_ifs at hloc + exacts [h3 hloc, h3 hloc.symm] + rw [ite_eq_right hloc, zero_add] + by_cases hq : packet e = packet e' + · -- Crossing entries of one packet: the geometric expansion. + have hσ : (sym (packet e)).1 ≠ (sym (packet e)).2 := by + intro hσ + apply hloc + rw [htgt, hsrc, ← hq] + split_ifs <;> simp only [hσ] + have hb : (slot e).testBit i = (slot e').testBit i := by + by_contra hb + apply hloc + rw [htgt, hsrc, ← hq] + rcases Bool.eq_false_or_eq_true ((slot e).testBit i) with h1 | h1 <;> + rcases Bool.eq_false_or_eq_true ((slot e').testBit i) with h2 | h2 <;> + simp only [h1, h2, ite_true, Bool.false_eq_true, ite_false, not_true_eq_false] at hb ⊢ + have h2a : 2 ^ (i + 1) ≤ 2 ^ a := Nat.pow_le_pow_right two_pos hia + have hy₀lt : slot e' % 2 ^ (i + 1) < 2 ^ (i + 1) := Nat.mod_lt _ (by positivity) + obtain ⟨y₀, hy₀⟩ : ∃ y₀ : Fin (2 ^ (i + 1)), (y₀ : ℕ) = slot e' % 2 ^ (i + 1) := + ⟨⟨_, hy₀lt⟩, rfl⟩ + have hx' : ((slot e : ℕ) : ℤ) < 2 ^ a := by exact_mod_cast hslot e + have hy₀' : ((y₀ : ℕ) : ℤ) < 2 ^ a := by + rw [hy₀] + exact_mod_cast lt_of_lt_of_le hy₀lt h2a + have hy' : ((slot e' : ℕ) : ℤ) < 2 ^ a := by exact_mod_cast hslot e' + rw [Fintype.sum_prod_type, sum_eq_single (packet e), Fintype.sum_prod_type, + sum_eq_single y₀] + · have hcoef : ∀ b : Fin p, + cauchyModeCoeff κ ζ a i t packet slot sym e (packet e, y₀, b) * + cauchyModeForm R i packet slot sym (packet e, y₀, b) e' = + kernelValue κ ζ a t ((y₀ : ℕ) - slot e) * + crossRatio a i t ((y₀ : ℕ) - slot e) ζ ^ (b : ℕ) * + (((slot e' / 2 ^ (i + 1) : ℕ) : ℤ) : R) ^ (b : ℕ) := by + intro b + unfold cauchyModeCoeff cauchyModeForm + rw [ite_eq_left ⟨rfl, hσ, hb.trans (hbit' _ hy₀.symm)⟩, + ite_eq_left ⟨hq.symm, hσ, hy₀.symm⟩, Int.cast_natCast] + simp only [hcoef] + rw [Fin.sum_univ_eq_sum_range (fun b => kernelValue κ ζ a t ((y₀ : ℕ) - slot e) * + crossRatio a i t ((y₀ : ℕ) - slot e) ζ ^ b * + (((slot e' / 2 ^ (i + 1) : ℕ) : ℤ) : R) ^ b) p, + cauchyValueMatrix, ite_eq_left hq] + have hdiv := Nat.mod_add_div (slot e') (2 ^ (i + 1)) + refine kernelValue_eq_sum κ ζ hp ht ?_ ?_ ?_ + (not_dvd_sub_sub_of_testBit_eq hti hti' y₀.isLt (hb.trans (hbit' _ hy₀.symm))) hia.le + · rw [mem_cauchyGrid] + push_cast + constructor <;> omega + · rw [mem_cauchyGrid] + push_cast + constructor <;> omega + · have : ((slot e' : ℕ) : ℤ) = ((y₀ : ℕ) : ℤ) + + 2 ^ (i + 1) * ((slot e' / 2 ^ (i + 1) : ℕ) : ℤ) := by + rw [hy₀] + exact_mod_cast hdiv.symm + rw [this] + ring + · intro y₀' _ hne + refine sum_eq_zero fun b _ => by_contra fun hZ => hne ?_ + exact Fin.ext ((hterm _ hZ).2.2.2.2.symm.trans hy₀.symm) + · exact fun h => absurd (mem_univ _) h + · intro q' _ hne + refine sum_eq_zero fun Z _ => by_contra fun hZ => hne ?_ + exact (hterm _ hZ).1.symm + · exact fun h => absurd (mem_univ _) h + · -- Different packets: the entry and every mode term vanish. + rw [cauchyValueMatrix, ite_eq_right hq] + refine (sum_eq_zero fun Z _ => by_contra fun hZ => hq ?_).symm + obtain ⟨h1, h2, -⟩ := hterm Z hZ + exact h1.trans h2.symm + +/-- **The modes of the Cauchy matrix (§D.4.5).** Let the edges of `N` be packed into packets of +`2 ^ a` slots, with an orientation in coordinate `i < a`: each packet `q` has a pair of groups +`sym q`, and an edge in slot `x` of `q` runs from group `(sym q).1` to group `(sym q).2` when digit +`i` of `x` is `0`, and the other way when it is `1`. Let `t` be a grid point of valuation `i` and +`2 ^ p = 0` in `R`. Then the Cauchy matrix at `T = t + α ζ` has a mode splitting: entries between +the same group are local, and for each packet `q` whose two groups differ, each *tile* `y₀` of the +column slots `y ≡ y₀ (mod 2 ^ (i + 1))` and each `b < p` there is a mode with coefficient +`W_{y₀ - x}(T) ρ ^ b` in the row form of slot `x` (`cauchyModeCoeff`) and form `∑ j ^ b X_y` over +the columns `y = y₀ + 2 ^ (i + 1) j` of the tile (`cauchyModeForm`): the manuscript's (D.12). -/ +noncomputable def cauchyModeSplitting (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) + (htgt : ∀ e, side (N.tgt e) = false) + (horient : ∀ e, (group (N.src e), group (N.tgt e)) = + if (slot e).testBit i then (sym (packet e)).swap else sym (packet e)) + {a : ℕ} (hslot : ∀ e, slot e < 2 ^ a) (hia : i < a) (κ : ℤ₍₂₎ →+* R) (ζ : R) {p : ℕ} + (hp : (2 : R) ^ p = 0) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) (hti : (2 : ℤ) ^ i ∣ t) + (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) : + N.ModeSplitting (cauchyValueMatrix κ ζ a t packet slot) G P + (P × Fin (2 ^ (i + 1)) × Fin p) where + side := side + group := group + packet := packet + localPart e e' := + if group (N.tgt e') = group (N.src e) then cauchyValueMatrix κ ζ a t packet slot e e' else 0 + modePacket Z := Z.1 + source Z := if (Z.2.1 : ℕ).testBit i then (sym Z.1).1 else (sym Z.1).2 + target Z := if (Z.2.1 : ℕ).testBit i then (sym Z.1).2 else (sym Z.1).1 + coeff := cauchyModeCoeff κ ζ a i t packet slot sym + form := cauchyModeForm R i packet slot sym + side_src := hsrc + side_tgt := htgt + apply_eq := cauchyValueMatrix_eq_add_sum horient hslot hia κ ζ hp ht hti hti' + localPart_ne_zero e e' h := by + simp only [ne_eq, ite_eq_right_iff, not_forall, cauchyValueMatrix] at h + obtain ⟨hg, hq, -⟩ := h + exact ⟨hq.symm, hg⟩ + coeff_ne_zero e Z h := by + unfold cauchyModeCoeff at h + obtain ⟨hq, -, hb⟩ := not_not.mp fun hc => h (ite_eq_right hc) + refine ⟨hq.symm, ?_⟩ + rw [group_src_eq horient e, hb, hq] + form_ne_zero Z e h := by + unfold cauchyModeForm at h + obtain ⟨hq, -, hy⟩ := not_not.mp fun hc => h (ite_eq_right hc) + refine ⟨hq.symm, ?_⟩ + have hb : (slot e).testBit i = (Z.2.1 : ℕ).testBit i := by + rw [← hy, Nat.testBit_mod_two_pow] + simp + rw [group_tgt_eq horient e, hb, hq] + +omit [Fintype E] [Fintype P] in +/-- **The modes have weight `b + 1`:** the coefficient of the mode `(q, y₀, b)` is divisible by +`2 ^ (b + 1)`, so the child identity (D.16) applies with these weights. -/ +theorem two_pow_dvd_cauchyModeCoeff {a : ℕ} (hslot : ∀ e, slot e < 2 ^ a) (hia : i < a) + (κ : ℤ₍₂₎ →+* R) (ζ : R) {p : ℕ} {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (hti : (2 : ℤ) ^ i ∣ t) (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) (e : E) + (Z : P × Fin (2 ^ (i + 1)) × Fin p) : + (2 : R) ^ ((Z.2.2 : ℕ) + 1) ∣ cauchyModeCoeff κ ζ a i t packet slot sym e Z := by + unfold cauchyModeCoeff + split_ifs with h + · have h2a : 2 ^ (i + 1) ≤ 2 ^ a := Nat.pow_le_pow_right two_pos hia + have hx : ((slot e : ℕ) : ℤ) < 2 ^ a := by exact_mod_cast hslot e + have hy₀ : ((Z.2.1 : ℕ) : ℤ) < 2 ^ a := by exact_mod_cast lt_of_lt_of_le Z.2.1.isLt h2a + refine two_pow_dvd_kernelValue_mul_crossRatio_pow κ ζ ht ?_ + (not_dvd_sub_sub_of_testBit_eq hti hti' Z.2.1.isLt h.2.2) _ + rw [mem_cauchyGrid] + push_cast + constructor <;> omega + · exact dvd_zero _ + +/-- **The child identity for the Cauchy matrix** ((D.16) of [OpenAI2026ETH] at an interpolation +value). With `2 ^ p = 0` in `R`, the deformed contraction along the Cauchy matrix of a packing at +`T = t + α ζ`, for a grid point `t` of valuation `i`, is the sum over the exponent tuples of total +weight `∑ (b + 1) β_{(q, y₀, b)} < p` of the products over the groups of the contractions of the +children of `cauchyModeSplitting`. -/ +theorem deformedContraction_cauchyValueMatrix_eq_sum [Fintype V] [DecidableEq V] [Fintype G] + [DecidableEq E] (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) + (htgt : ∀ e, side (N.tgt e) = false) + (horient : ∀ e, (group (N.src e), group (N.tgt e)) = + if (slot e).testBit i then (sym (packet e)).swap else sym (packet e)) + {a : ℕ} (hslot : ∀ e, slot e < 2 ^ a) (hia : i < a) (κ : ℤ₍₂₎ →+* R) (ζ : R) {p : ℕ} + (hp : (2 : R) ^ p = 0) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) (hti : (2 : ℤ) ^ i ∣ t) + (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) : + N.deformedContraction side (cauchyValueMatrix κ ζ a t packet slot) = + ∑ β ∈ modeBudget (fun Z : P × Fin (2 ^ (i + 1)) × Fin p => (Z.2.2 : ℕ) + 1) p, + ∏ g, ((cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').child β + g).contraction := + (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti + hti').deformedContraction_eq_sum_prod_contraction_child _ (fun _ => Nat.succ_pos _) hp + fun e Z => two_pow_dvd_cauchyModeCoeff hslot hia κ ζ ht hti hti' e Z + +end Modes + +end TensorNetwork + +end Cslib.Circuits.Frontier diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkCauchyModes.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkCauchyModes.lean new file mode 100644 index 0000000000..eb5731330c --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkCauchyModes.lean @@ -0,0 +1,91 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CauchyModes +public import Complexity.Foundations.NumberTheory.Padics.RatValuation + +/-! +# Tests for the modes of the Cauchy kernel + +One packet of two slots (`a = 1`), oriented in coordinate `i = 0`, with groups `false` and `true`: +edge `0`, in slot `0`, runs from row `0` of group `false` to column `2` of group `true`, and edge +`1`, in slot `1`, from row `1` of group `true` to column `3` of group `false`. At the grid point +`t = 1`, of valuation `0`, over `ℤ / 4` (`p = 2`), the Cauchy matrix at `T = 1 + 4 ζ` has four +modes `(packet, tile, b)`: the tiles are `y₀ ∈ {0, 1}` and `b < 2`, of weight `b + 1`. +-/ + +@[expose] public section + +namespace ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.CauchyModes + +open Cslib.Circuits.Frontier TensorNetwork + +/-- The two-edge network over `ℤ / 4`, with tensor `(1, v + 1)` on the bit of the edge at `v`. -/ +def net : TensorNetwork (ZMod (2 ^ 2)) Bool (Fin 4) (Fin 2) where + src e := ⟨e.val, by omega⟩ + tgt e := ⟨e.val + 2, by omega⟩ + tensor v x := if x ⟨v.val % 2, Nat.mod_lt _ (by decide)⟩ then (v.val + 1 : ZMod (2 ^ 2)) else 1 + tensor_congr v x y h := by rw [h _ (by fin_cases v <;> decide)] + +/-- The rows `0, 1`. -/ +def side (v : Fin 4) : Bool := decide (v.val < 2) + +/-- Groups: rows `0` and column `3` are `false`, row `1` and column `2` are `true`. -/ +def group (v : Fin 4) : Bool := decide (v = 1 ∨ v = 2) + +/-- Edge `e` sits in slot `e` of the single packet. -/ +def slot (e : Fin 2) : ℕ := e.val + +/-- The packet's pair of groups, oriented as at slot `0`. -/ +def sym (_ : Unit) : Bool × Bool := (false, true) + +theorem horient (e : Fin 2) : (group (net.src e), group (net.tgt e)) = + if (slot e).testBit 0 then (sym ((fun _ => ()) e)).swap else sym ((fun _ => ()) e) := by + fin_cases e <;> decide + +/-- Reduction of `ℤ_(2)` modulo `4`. -/ +noncomputable abbrev κ := Rat.padicIntegerToZModPow 2 2 + +/-- **The child identity (D.16)** at `T = 1 + 4 ζ`: the deformed contraction along the Cauchy matrix +is a sum over the mode tuples of total weight below two of products of child contractions. -/ +example (ζ : ZMod (2 ^ 2)) : + net.deformedContraction side (cauchyValueMatrix κ ζ 1 1 (fun _ => ()) slot) = + ∑ β ∈ modeBudget (fun Z : Unit × Fin (2 ^ (0 + 1)) × Fin 2 => (Z.2.2 : ℕ) + 1) 2, + ∏ g, ((cauchyModeSplitting (sym := sym) (i := 0) side (by decide) (by decide) horient + (a := 1) (fun e => by fin_cases e <;> decide) (by decide) κ ζ (p := 2) (by decide) + (t := 1) (by decide) (by decide) (by decide)).child β g).contraction := + deformedContraction_cauchyValueMatrix_eq_sum side (by decide) (by decide) horient + (fun e => by fin_cases e <;> decide) (by decide) κ ζ (by decide) (by decide) (by decide) + (by decide) + +/-- The budget keeps the zero tuple and the two weight-one modes `b = 0`. -/ +example : + (modeBudget (fun Z : Unit × Fin (2 ^ (0 + 1)) × Fin 2 => (Z.2.2 : ℕ) + 1) 2).card = 3 := by + decide + +/-- Column `1`, in slot `1`, is in tile `1` with `j = 0`, so the mode `((), 1, 0)` reads it with +coefficient `j ^ 0 = 1`, and the mode of tile `0` does not read it. -/ +example : cauchyModeForm (ZMod (2 ^ 2)) 0 (fun _ : Fin 2 => ()) slot sym (p := 2) ((), 1, 0) 1 = 1 ∧ + cauchyModeForm (ZMod (2 ^ 2)) 0 (fun _ : Fin 2 => ()) slot sym (p := 2) ((), 0, 0) 1 = 0 := by + decide + +/-- Row `0` has digit `0`, so it only carries the modes of tile `0`, whose columns cross from the +other group. -/ +example (ζ : ZMod (2 ^ 2)) : + cauchyModeCoeff κ ζ 1 0 1 (fun _ : Fin 2 => ()) slot sym (p := 2) 0 ((), 1, 0) = 0 := by + simp [cauchyModeCoeff, slot] + +/-- A crossing coefficient has weight `b + 1`. -/ +example (ζ : ZMod (2 ^ 2)) : + (2 : ZMod (2 ^ 2)) ^ 2 ∣ cauchyModeCoeff κ ζ 1 0 1 (fun _ : Fin 2 => ()) slot sym (p := 2) 0 + ((), 0, 1) := + two_pow_dvd_cauchyModeCoeff (fun e => by fin_cases e <;> decide) (by decide) κ ζ (by decide) + (by decide) (by decide) 0 ((), 0, 1) + +end ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.CauchyModes From f6826a6c5efd2a0383251a4fe5caa9a985d3ae7c Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:07:13 -0400 Subject: [PATCH 009/129] feat(circuit/frontier): local deformation and finite-precision tapered recovery (Lemma D.5) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The evaluation bridge for the deformed contraction F(t + αz) and the precision half of Lemma D.5 of OpenAI2026ETH (§D.4.4, pp. 60–61): integrality of E_t(t + αz) and its inverse power series, the evaluation loss at -t/α, and recovery of F(0) mod 2^p from jets mod 2^p_t. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 5 +- .../TensorNetwork/LocalDeformation.lean | 223 ++++++++ .../TensorNetwork/TaperedRecovery.lean | 475 ++++++++++++++++++ .../TensorNetworkLocalDeformation.lean | 68 +++ .../TensorNetworkTaperedRecovery.lean | 50 ++ 5 files changed, 818 insertions(+), 3 deletions(-) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/LocalDeformation.lean create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/TaperedRecovery.lean create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkLocalDeformation.lean create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkTaperedRecovery.lean diff --git a/Complexity.lean b/Complexity.lean index 8bb600f47e..baecded444 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -869,15 +869,14 @@ public import Complexity.Computability.Circuit.Frontier.Rectangle public import Complexity.Computability.Circuit.Frontier.Reduction public import Complexity.Computability.Circuit.Frontier.Sweep public import Complexity.Computability.Circuit.Frontier.TensorNetwork -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CauchyModes public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.LocalDeformation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.TaperedRecovery public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness public import Complexity.Computability.Circuit.Frontier.Unconditional public import Complexity.Computability.Circuit.Frontier.Witness diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/LocalDeformation.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/LocalDeformation.lean new file mode 100644 index 0000000000..5f497f546d --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/LocalDeformation.lean @@ -0,0 +1,223 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CauchyModes + +/-! +# The deformed contraction in local coordinates + +This file connects the polynomial `F(T)` of (D.8) in [OpenAI2026ETH] (§D.4.3, p. 59) with its +values at the interpolation points of §D.4.4–§D.4.5 (pp. 61–62). + +Let `B` be a commutative ring receiving `ℤ_(2)` through `κ`, and let `N` be a Boolean tensor network +with entries in `B`. For a packing of the edges into packets of `2 ^ a` slots and a grid point `t`, +the *local deformation* (`TensorNetwork.localDeformation`) is the deformed contraction of `N` along +the matrix of local kernels `W_{y - x}(t + α z)` over `B[z]` (`cauchyLocalMatrix`), built from +`Cslib.Circuits.Frontier.localKernel`. It is the manuscript's `F(t + α z) ∈ A[z]`: + +* in a `ℚ`-algebra `R` receiving `B` compatibly with `ℚ`, its image is `F(t + α z)`, where `F(T)` + is the deformed contraction along the Cauchy matrix + (`TensorNetwork.map_localDeformation`), whose value at `0` is the contraction + (`TensorNetwork.eval_zero_deformedContraction_cauchyMatrix`); +* under any ring homomorphism `π : B → R'`, its value at `z = ζ` is the deformed contraction of the + reduced network along the Cauchy matrix at `T = t + α ζ` + (`TensorNetwork.eval₂_localDeformation`), which the child identity + `TensorNetwork.deformedContraction_cauchyValueMatrix_eq_sum` expands when `2 ^ p = 0` in `R'`. + +So the values of the children of §D.4.6 at `ζ` are values of one polynomial over `B`, and +interpolation recovers its reduction. + +## Main definitions + +* `Cslib.Circuits.Frontier.cauchyLocalMatrix`: the Cauchy matrix of a packing in local + coordinates, over `B[z]`. +* `Cslib.Circuits.Frontier.TensorNetwork.localDeformation`: `F(t + α z)` over `B`. + +## Main results + +* `Cslib.Circuits.Frontier.TensorNetwork.map_localDeformation`: its image in a `ℚ`-algebra is + `F(t + α z)`. +* `Cslib.Circuits.Frontier.TensorNetwork.eval₂_localDeformation`: its values are deformed + contractions along `cauchyValueMatrix`. +* `Cslib.Circuits.Frontier.TensorNetwork.natDegree_deformedContraction_le`, + `Cslib.Circuits.Frontier.TensorNetwork.natDegree_localDeformation_le`, + `Cslib.Circuits.Frontier.TensorNetwork.natDegree_deformedContraction_cauchyMatrix_le`: both have + degree at most `M (2 L - 1)` for `M` edges and `L` slots. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.3–§D.4.5, + pp. 59–62. +-/ + +public section + +namespace Cslib.Circuits.Frontier + +open Polynomial + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +variable {B V E P : Type*} [CommRing B] + +/-- The Cauchy matrix of a packing into packets of `2 ^ a` slots in local coordinates +`z ↦ W(t + α z)`, `α = 2 ^ (a + 1)`, over `B[z]` for a ring `B` receiving `ℤ_(2)` through `κ`: +between two edges of the same packet, the local kernel at the difference of their slots, and zero +between packets. -/ +@[expose] noncomputable def cauchyLocalMatrix [DecidableEq P] (κ : ℤ₍₂₎ →+* B) (a : ℕ) (t : ℤ) + (packet : E → P) (slot : E → ℕ) : Matrix E E B[X] := + fun e e' => + if packet e = packet e' then (localKernel a t ((slot e' : ℤ) - slot e)).map κ else 0 + +namespace TensorNetwork + +/-- Mapping the tensors twice is mapping them by the composition. -/ +theorem map_map {R S T U : Type*} [CommSemiring R] [CommSemiring S] [CommSemiring T] + (f : R →+* S) (g : S →+* T) (N : TensorNetwork R U V E) : (N.map f).map g = N.map (g.comp f) := + rfl + +variable [Fintype V] [Fintype E] [DecidableEq E] [DecidableEq P] + +/-- The deformed contraction along the Cauchy matrix of a packing in local coordinates at the grid +point `t`, as a polynomial in `z` over `B`: the manuscript's `F(t + α z) ∈ A[z]`. -/ +@[expose] noncomputable def localDeformation (N : TensorNetwork B Bool V E) (side : V → Bool) + (κ : ℤ₍₂₎ →+* B) (a : ℕ) (t : ℤ) (packet : E → P) (slot : E → ℕ) : B[X] := + (N.map C).deformedContraction side (cauchyLocalMatrix κ a t packet slot) + +/-- **The local deformation is `F(t + α z)`.** In a `ℚ`-algebra `R` receiving `B` through `ι` +compatibly with `ℚ`, the image of the local deformation at a grid point `t` is the deformed +contraction `F(T)` along the Cauchy matrix, composed with `T = t + α z`. -/ +theorem map_localDeformation {R : Type*} [CommRing R] [Algebra ℚ R] (N : TensorNetwork B Bool V E) + (side : V → Bool) (κ : ℤ₍₂₎ →+* B) (ι : B →+* R) + (hκ : ι.comp κ = (algebraMap ℚ R).comp (ℤ₍₂₎).subtype) {a : ℕ} {packet : E → P} + {slot : E → ℕ} (hslot : ∀ e, slot e < 2 ^ a) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) : + (N.localDeformation side κ a t packet slot).map ι = + (((N.map ι).map C).deformedContraction side + ((cauchyMatrix (2 ^ a) packet slot).map (mapRingHom (algebraMap ℚ R)))).comp + (C (t : R) + C (2 ^ (a + 1)) * X) := by + rw [← coe_mapRingHom, localDeformation, ← deformedContraction_map, ← coe_compRingHom_apply, + ← deformedContraction_map, map_map, map_map, map_map] + have hlin : (C (t : ℚ) + C ((2 : ℚ) ^ (a + 1)) * X).map (algebraMap ℚ R) = + C (t : R) + C (2 ^ (a + 1)) * X := by + rw [Polynomial.map_add, Polynomial.map_mul, Polynomial.map_C, Polynomial.map_C, + Polynomial.map_X, map_intCast, map_pow, map_ofNat] + congr 1 + · congr 1 + ext x : 1 + simp only [RingHom.coe_comp, Function.comp_apply, coe_mapRingHom, Polynomial.map_C, + coe_compRingHom_apply, C_comp] + · ext e e' + simp only [Matrix.map_apply, cauchyLocalMatrix, cauchyMatrix, coe_mapRingHom, + coe_compRingHom_apply] + split_ifs with hp + · rw [Polynomial.map_map, hκ, ← Polynomial.map_map, + map_localKernel ht (sub_mem_cauchyGrid (hslot e) (hslot e')), Polynomial.map_comp, hlin] + · rw [Polynomial.map_zero, Polynomial.map_zero, zero_comp] + +/-- **Values of the local deformation.** Under a ring homomorphism `π : B → R'`, the value of the +local deformation at `z = ζ` is the deformed contraction of the reduced network along the Cauchy +matrix at `T = t + α ζ`. -/ +theorem eval₂_localDeformation {R' : Type*} [CommRing R'] (N : TensorNetwork B Bool V E) + (side : V → Bool) (κ : ℤ₍₂₎ →+* B) (π : B →+* R') (ζ : R') (a : ℕ) (t : ℤ) (packet : E → P) + (slot : E → ℕ) : + (N.localDeformation side κ a t packet slot).eval₂ π ζ = + (N.map π).deformedContraction side (cauchyValueMatrix (π.comp κ) ζ a t packet slot) := by + rw [← coe_eval₂RingHom, localDeformation, ← deformedContraction_map, map_map] + congr 1 + · congr 1 + ext x : 1 + simp only [RingHom.coe_comp, Function.comp_apply, coe_eval₂RingHom, eval₂_C] + · ext e e' + simp only [Matrix.map_apply, cauchyLocalMatrix, cauchyValueMatrix, coe_eval₂RingHom] + split_ifs + · rw [eval₂_map] + rfl + · exact eval₂_zero _ _ + +/-! ### Degrees -/ + +/-- **Degree of a deformed contraction.** For tensors that are constants and a matrix whose entries +have degree at most `d`, the deformed contraction has degree at most `d` times the number of +edges: it is a sum of products of one affine factor per edge. -/ +theorem natDegree_deformedContraction_le {R : Type*} [CommRing R] (N : TensorNetwork R Bool V E) + (side : V → Bool) (W : Matrix E E R[X]) {d : ℕ} (hW : ∀ e e', (W e e').natDegree ≤ d) : + ((N.map C).deformedContraction side W).natDegree ≤ Fintype.card E * d := by + unfold deformedContraction + refine natDegree_sum_le_of_forall_le _ _ fun X _ => natDegree_sum_le_of_forall_le _ _ + fun Y _ => ?_ + have hc : ∀ s : Finset V, ∀ x : E → Bool, (∏ v ∈ s, (N.map C).tensor v x).natDegree = 0 := + fun s x => by simp only [map_tensor, ← map_prod, natDegree_C] + have hv : ∀ e, (W.mulVec (fun e' => if X e' then (1 : R[X]) else 0) e).natDegree ≤ d := by + intro e + refine natDegree_sum_le_of_forall_le _ _ fun e' _ => ?_ + dsimp only + split_ifs + · rw [mul_one]; exact hW e e' + · rw [mul_zero, natDegree_zero]; exact Nat.zero_le _ + have hprod : (∏ e, affineFactor (Y e) + (W.mulVec (fun e' => if X e' then (1 : R[X]) else 0) e)).natDegree ≤ Fintype.card E * d := by + refine (natDegree_prod_le _ _).trans ?_ + rw [← smul_eq_mul, ← Finset.card_univ, ← Finset.sum_const] + refine Finset.sum_le_sum fun e _ => ?_ + unfold affineFactor + split_ifs + · exact hv e + · exact (natDegree_sub_le _ _).trans (max_le (by rw [natDegree_one]; exact Nat.zero_le _) + (hv e)) + refine (natDegree_mul_le).trans ?_ + have h2 := natDegree_mul_le (p := ∏ v ∈ Finset.univ.filter (side · = true), (N.map C).tensor v Y) + (q := ∏ v ∈ Finset.univ.filter (side · = false), (N.map C).tensor v X) + rw [hc, hc] at h2 + omega + +/-- On the grid, the local kernel has degree at most `2 L - 1`, `L = 2 ^ a`. -/ +theorem natDegree_localKernel_le {a : ℕ} {t d : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (hd : d ∈ cauchyGrid (2 ^ a)) : (localKernel a t d).natDegree ≤ 2 * 2 ^ a - 1 := by + rw [← natDegree_map_eq_of_injective (f := (ℤ₍₂₎).subtype) Subtype.val_injective, + map_localKernel ht hd] + refine natDegree_comp_le.trans ?_ + rw [natDegree_cauchyKernel hd] + have : (C (t : ℚ) + C ((2 : ℚ) ^ (a + 1)) * X).natDegree ≤ 1 := + (natDegree_add_le _ _).trans (max_le (by rw [natDegree_C]; exact Nat.zero_le _) + ((natDegree_C_mul_le _ _).trans natDegree_X_le)) + calc (2 * 2 ^ a - 1) * (C (t : ℚ) + C ((2 : ℚ) ^ (a + 1)) * X).natDegree + ≤ (2 * 2 ^ a - 1) * 1 := Nat.mul_le_mul_left _ this + _ = 2 * 2 ^ a - 1 := mul_one _ + +/-- The local deformation has degree at most `M (2 L - 1)` for `M` edges. -/ +theorem natDegree_localDeformation_le (N : TensorNetwork B Bool V E) (side : V → Bool) + (κ : ℤ₍₂₎ →+* B) {a : ℕ} {packet : E → P} {slot : E → ℕ} (hslot : ∀ e, slot e < 2 ^ a) + {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) : + (N.localDeformation side κ a t packet slot).natDegree ≤ Fintype.card E * (2 * 2 ^ a - 1) := by + refine natDegree_deformedContraction_le N side _ fun e e' => ?_ + unfold cauchyLocalMatrix + split_ifs + · exact natDegree_map_le.trans + (natDegree_localKernel_le ht (sub_mem_cauchyGrid (hslot e) (hslot e'))) + · rw [natDegree_zero]; exact Nat.zero_le _ + +/-- The deformed contraction `F(T)` along the Cauchy matrix of a packing into packets of `L` slots +has degree at most `M (2 L - 1)` for `M` edges (§D.4.3 of [OpenAI2026ETH], p. 59). -/ +theorem natDegree_deformedContraction_cauchyMatrix_le {R : Type*} [CommRing R] [Algebra ℚ R] + (N : TensorNetwork R Bool V E) (side : V → Bool) {L : ℕ} {packet : E → P} {slot : E → ℕ} + (hslot : ∀ e, slot e < L) : + ((N.map C).deformedContraction side + ((cauchyMatrix L packet slot).map (mapRingHom (algebraMap ℚ R)))).natDegree ≤ + Fintype.card E * (2 * L - 1) := by + refine natDegree_deformedContraction_le N side _ fun e e' => ?_ + simp only [Matrix.map_apply, cauchyMatrix, coe_mapRingHom] + split_ifs + · exact natDegree_map_le.trans + (natDegree_cauchyKernel (sub_mem_cauchyGrid (hslot e) (hslot e'))).le + · rw [Polynomial.map_zero, natDegree_zero]; exact Nat.zero_le _ + +end TensorNetwork + +end Cslib.Circuits.Frontier diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/TaperedRecovery.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/TaperedRecovery.lean new file mode 100644 index 0000000000..4d914e4644 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/TaperedRecovery.lean @@ -0,0 +1,475 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates +public import Complexity.Foundations.Data.Polynomial.TaperedHermite +import Mathlib.Algebra.Polynomial.Eval.Degree +import Mathlib.RingTheory.PowerSeries.Trunc + +/-! +# Tapered Hermite recovery with finite precision + +This file finishes Lemma D.5 of [OpenAI2026ETH] (§D.4.4, pp. 60–61): the value `F(0)` modulo +`2 ^ p` is recovered from the local jets of `F(t + α z)` modulo `2 ^ p_t`. + +**Setting.** Let `K` be a `ℚ`-algebra and `B` a subring of `K` containing `ℤ_(2)`, the rationals +with odd denominator (in the manuscript, `K = ℚ[U] / (f)` and `B = O`). The nodes are the nonzero +points `t` of the grid `S = [-L, L)` of `L = 2 ^ a` slots (`recoveryNodes`), with the tapered +multiplicities `q_t = h max(0, u - v₂ t)` (`taperMult`), and `α = 2 ^ (a + 1)`. From polynomials +`G t ∈ K[z]`, the recovered value (`taperedRecovery`) is the right side of (D.9), + + `∑_t E_t(0) (G t / E_t(t + α z) mod z ^ q_t)(-t / α)`, + +with the Hermite basis `E_t` and quotient of +`Polynomial.eval_eq_sum_hermiteBasis_mul_hermiteQuotient`. For `G t = F(t + α z)` and +`deg F < ∑_t q_t` it is `F(0)` (`eval_zero_eq_taperedRecovery`), the manuscript's (D.9). + +**Precision.** Each `E_t(t + α z)` is a product of powers of `1 + α z / (t - q)`, so it has +coefficients in `ℤ_(2)` and constant term one, and so has the inverse power series +(`hermiteBasis_comp_eq_map`, `coeff_invOfUnit_hermiteBasis_comp_mem`). The quotient of a jet that +is divisible by `2 ^ p_t` is therefore divisible by `2 ^ p_t`. Evaluating a polynomial of degree +at most `q_t` at `-t / α` multiplies by rationals of valuation at least `-q_t (a + 1)`, and `E_t(0)` +has valuation at least `-6 h D_t` with `D_t = 2 ^ (a - v₂ t)` ((D.10), +`Polynomial.neg_le_padicValRat_eval_zero_hermiteBasis_taper`). So if the jets `G t` agree with +`F(t + α z)` modulo `2 ^ p_t` below `z ^ q_t`, where `p + 6 h D_t + q_t (a + 1) ≤ p_t`, the +recovered value agrees with `F(0)` modulo `2 ^ p` (`exists_taperedRecovery_eq_add`). The +manuscript's choice `p_t = p + 8 h D_t + 8 M (a + 1)` satisfies this when `q_t ≤ 4 M` +(`taperMult_le`); the manuscript's multiplicities have enough total weight +(`le_sum_taperMult`). + +Congruences are stated in `K` as `x = y + 2 ^ p * b` with `b ∈ B`. + +## Main definitions + +* `Cslib.Circuits.Frontier.recoveryNodes`: the nonzero points of the grid. +* `Cslib.Circuits.Frontier.taperMult`: the tapered multiplicities `q_t`. +* `Cslib.Circuits.Frontier.taperedRecovery`: the right side of (D.9) for given jets. + +## Main results + +* `Cslib.Circuits.Frontier.le_sum_taperMult`: the total multiplicity is at least `2 ^ a h u`. +* `Cslib.Circuits.Frontier.taperMult_le`: `q_t ≤ 4 M` for `h = ⌈3 M / u⌉` and `u ≤ M`. +* `Cslib.Circuits.Frontier.eval_zero_eq_taperedRecovery`: (D.9). +* `Cslib.Circuits.Frontier.hermiteBasis_comp_eq_map`: `E_t(t + α z)` is integral with constant + term one. +* `Cslib.Circuits.Frontier.exists_taperedRecovery_eq_add`: recovery modulo `2 ^ p` from jets + modulo `2 ^ p_t`. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.4, Lemma D.5, + (D.9)–(D.10), pp. 60–61. +-/ + +public section + +namespace Cslib.Circuits.Frontier + +open Polynomial Finset + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +/-! ### Nodes and multiplicities -/ + +/-- The nodes of tapered Hermite recovery: the nonzero points of the grid of `2 ^ a` slots. -/ +@[expose] def recoveryNodes (a : ℕ) : Finset ℤ := + (cauchyGrid (2 ^ a)).erase 0 + +/-- The tapered multiplicity `q_t = h max(0, u - v₂ t)` of Lemma D.5, with natural +subtraction. -/ +@[expose] def taperMult (u h : ℕ) (t : ℤ) : ℕ := + h * (u - padicValInt 2 t) + +/-- The grid of `2 ^ a` slots is `[-2 ^ a, 2 ^ a)`. -/ +theorem cauchyGrid_two_pow (a : ℕ) : cauchyGrid (2 ^ a) = Ico (-(2 : ℤ) ^ a) (2 ^ a) := by + simp only [cauchyGrid, Nat.cast_pow, Nat.cast_ofNat] + +theorem recoveryNodes_eq (a : ℕ) : recoveryNodes a = (Ico (-(2 : ℤ) ^ a) (2 ^ a)).erase 0 := by + rw [recoveryNodes, cauchyGrid_two_pow] + +/-- **There is enough multiplicity.** The `2 ^ a` odd nodes have multiplicity `h u` each. -/ +theorem le_sum_taperMult (a u h : ℕ) : + 2 ^ a * (h * u) ≤ ∑ t ∈ recoveryNodes a, taperMult u h t := by + have key := sum_Ico_erase_zero_padicValInt_two a fun j => ((h * (u - j) : ℕ) : ℤ) + rw [← recoveryNodes_eq] at key + have hsum : ((∑ t ∈ recoveryNodes a, taperMult u h t : ℕ) : ℤ) = + ∑ j ∈ range (a + 1), 2 ^ (a - j) * ((h * (u - j) : ℕ) : ℤ) := by + rw [← key] + push_cast [taperMult] + rfl + have hle : ((2 ^ a * (h * u) : ℕ) : ℤ) ≤ + ∑ j ∈ range (a + 1), 2 ^ (a - j) * ((h * (u - j) : ℕ) : ℤ) := by + rw [sum_range_succ'] + simp only [Nat.sub_zero, Nat.cast_mul, Nat.cast_pow, Nat.cast_ofNat] + have : 0 ≤ ∑ j ∈ range a, (2 : ℤ) ^ (a - (j + 1)) * ((h : ℤ) * ((u - (j + 1) : ℕ) : ℤ)) := + sum_nonneg fun j _ => by positivity + linarith + exact_mod_cast hle.trans hsum.symm.le + +/-- **Multiplicities are at most `4 M`** when `h = ⌈3 M / u⌉` (any `h ≤ 3 M / u + 1`) and +`u ≤ M`. -/ +theorem taperMult_le {u h M : ℕ} (hh : h * u ≤ 3 * M + u) (huM : u ≤ M) (t : ℤ) : + taperMult u h t ≤ 4 * M := by + unfold taperMult + calc h * (u - padicValInt 2 t) ≤ h * u := Nat.mul_le_mul_left _ (Nat.sub_le _ _) + _ ≤ 4 * M := by omega + +/-! ### The recovered value -/ + +variable {K : Type*} [CommRing K] [Algebra ℚ K] + +/-- The value recovered by tapered Hermite interpolation from the polynomials `G t`: the right +side of (D.9) of [OpenAI2026ETH], `∑_t E_t(0) (G t / E_t(t + α z) mod z ^ q_t)(-t / α)` over the +nonzero grid points `t`, with `α = 2 ^ (a + 1)`. Only the first `q_t` coefficients of `G t` +matter (`Polynomial.hermiteQuotient_trunc`). -/ +noncomputable def taperedRecovery (a u h : ℕ) (G : ℤ → K[X]) : K := + ∑ t ∈ recoveryNodes a, + (hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : K)) (taperMult u h) t).eval 0 * + (hermiteQuotient (recoveryNodes a) (fun q : ℤ => (q : K)) (taperMult u h) (2 ^ (a + 1)) t + (G t)).eval (algebraMap ℚ K (-(t : ℚ) / 2 ^ (a + 1))) + +/-- `2 ^ n` as a unit of a `ℚ`-algebra. -/ +private noncomputable def twoPowUnit (K : Type*) [CommRing K] [Algebra ℚ K] (n : ℕ) : Kˣ := + Units.map (algebraMap ℚ K : ℚ →* K) (Units.mk0 ((2 : ℚ) ^ n) (by positivity)) + +private theorem coe_twoPowUnit (n : ℕ) : ((twoPowUnit K n : Kˣ) : K) = 2 ^ n := by + simp [twoPowUnit, map_pow, map_ofNat] + +private theorem coe_twoPowUnit_inv (n : ℕ) : + ((twoPowUnit K n)⁻¹ : Kˣ) = algebraMap ℚ K ((2 : ℚ) ^ n)⁻¹ := by + simp [twoPowUnit, Units.val_inv_eq_inv_val] + +/-- Distinct integers are units of a `ℚ`-algebra apart. -/ +private theorem pairwise_isUnit_sub (s : Finset ℤ) : + (s : Set ℤ).Pairwise fun i j => IsUnit ((i : K) - (j : K)) := by + intro i _ j _ hij + have h : ((i : ℚ) - j) ≠ 0 := sub_ne_zero.mpr (by exact_mod_cast hij) + have := (isUnit_iff_ne_zero.mpr h).map (algebraMap ℚ K) + simpa using this + +/-- **Hermite reconstruction (D.9)** of [OpenAI2026ETH]: for `deg F < ∑_t q_t`, the recovered value +from the local polynomials `F(t + α z)` is `F(0)`. -/ +theorem eval_zero_eq_taperedRecovery {a u h : ℕ} {F : K[X]} + (hF : F.degree < ↑(∑ t ∈ recoveryNodes a, taperMult u h t)) : + F.eval 0 = taperedRecovery a u h fun t => F.comp (C (t : K) + C (2 ^ (a + 1)) * X) := by + rw [eval_eq_sum_hermiteBasis_mul_hermiteQuotient (pairwise_isUnit_sub _) (twoPowUnit K (a + 1)) + hF 0, taperedRecovery] + refine sum_congr rfl fun t _ => ?_ + rw [coe_twoPowUnit, coe_twoPowUnit_inv, zero_sub, ← map_intCast (algebraMap ℚ K) t, ← map_neg, + ← map_mul] + congr 3 + ring + +/-! ### Integrality of the Hermite basis in local coordinates -/ + +/-- The rationals `α / (t - q)` with `t ≠ q` on the grid, `α = 2 ^ (a + 1)`, lie in `ℤ_(2)`. -/ +private theorem padicValuation_two_pow_div_le_one {a : ℕ} {t q : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (hq : q ∈ cauchyGrid (2 ^ a)) (hne : q ≠ t) : + Rat.padicValuation 2 ((2 : ℚ) ^ (a + 1) / ((t : ℚ) - q)) ≤ 1 := by + have hle := padicValuation_two_pow_le_sub ht hq hne + have hne' : ((t : ℚ) - q) ≠ 0 := sub_ne_zero.mpr (by exact_mod_cast hne.symm) + rw [map_div₀, div_le_one₀ ((Rat.padicValuation 2).pos_iff.mpr hne')] + refine le_trans ?_ hle + rw [pow_succ, map_mul] + exact mul_le_of_le_one_right' (by + rw [show (2 : ℚ) = ((2 : ℕ) : ℚ) by norm_num, Rat.padicValuation_natCast] + exact Int.padicValuation_le_one 2 2) + +/-- The Hermite basis over `ℚ` in local coordinates is a product of powers of +`1 + α / (t - q) z`. -/ +private theorem hermiteBasis_comp_rat (a u h : ℕ) (t : ℤ) : + (hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : ℚ)) (taperMult u h) t).comp + (C (t : ℚ) + C (2 ^ (a + 1)) * X) = + ∏ q ∈ (recoveryNodes a).erase t, + (1 + C ((2 : ℚ) ^ (a + 1) / ((t : ℚ) - q)) * X) ^ taperMult u h q := by + rw [hermiteBasis, Polynomial.prod_comp] + refine prod_congr rfl fun q hq => ?_ + have hqt : ((t : ℚ) - q) ≠ 0 := sub_ne_zero.mpr (by exact_mod_cast (ne_of_mem_erase hq).symm) + rw [pow_comp, mul_comp, C_comp, sub_comp, X_comp, C_comp, Ring.inverse_eq_inv'] + congr 1 + rw [show C (t : ℚ) + C (2 ^ (a + 1)) * X - C (q : ℚ) = + C ((t : ℚ) - q) * (1 + C ((2 : ℚ) ^ (a + 1) / ((t : ℚ) - q)) * X) by + rw [mul_add, mul_one, ← mul_assoc, ← C_mul, mul_div_cancel₀ _ hqt, C_sub]; ring, + ← mul_assoc, ← C_mul, inv_mul_cancel₀ hqt, C_1, one_mul] + +/-- **Lemma D.5, integrality.** For a nonzero grid point `t`, the Hermite basis polynomial in +local coordinates, `E_t(t + α z)`, is the image of a polynomial over `ℤ_(2)` with constant term +one: each factor is a power of `1 + α z / (t - q)` with `α / (t - q)` in `ℤ_(2)`. -/ +theorem hermiteBasis_comp_eq_map (a u h : ℕ) {t : ℤ} (ht : t ∈ recoveryNodes a) : + ∃ E : ℤ₍₂₎[X], E.coeff 0 = 1 ∧ + (hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : K)) (taperMult u h) t).comp + (C (t : K) + C (2 ^ (a + 1)) * X) = + E.map ((algebraMap ℚ K).comp (ℤ₍₂₎).subtype) := by + have htS : t ∈ cauchyGrid (2 ^ a) := mem_of_mem_erase ht + -- each factor is integral + have hfac : ∀ q ∈ (recoveryNodes a).erase t, + (1 + C ((2 : ℚ) ^ (a + 1) / ((t : ℚ) - q)) * X) ∈ lifts (ℤ₍₂₎).subtype := fun q hq => + add_mem (one_mem _) (mul_mem (C_mem_lifts _ (⟨_, padicValuation_two_pow_div_le_one htS + (mem_of_mem_erase (mem_of_mem_erase hq)) (ne_of_mem_erase hq)⟩ : ℤ₍₂₎)) (X_mem_lifts _)) + obtain ⟨E, hE⟩ := (mem_lifts _).mp (Subsemiring.prod_mem _ fun q hq => + Subsemiring.pow_mem _ (hfac q hq) (taperMult u h q)) + have hEq : E.map (ℤ₍₂₎).subtype = (hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : ℚ)) + (taperMult u h) t).comp (C (t : ℚ) + C (2 ^ (a + 1)) * X) := by + rw [hE, hermiteBasis_comp_rat] + refine ⟨E, ?_, ?_⟩ + · apply Subtype.val_injective + have h0 := congrArg (fun P => Polynomial.coeff P 0) hEq + simp only [coeff_map, Subring.coe_subtype] at h0 + rw [h0, coeff_zero_eq_eval_zero, eval_comp, eval_add, eval_C, eval_mul, eval_C, eval_X, + mul_zero, add_zero, eval_hermiteBasis_self (pairwise_isUnit_sub _) ht] + rfl + · have hv : (fun q : ℤ => algebraMap ℚ K (q : ℚ)) = fun q : ℤ => (q : K) := + funext fun q => map_intCast _ q + have hlin : (C (t : ℚ) + C ((2 : ℚ) ^ (a + 1)) * X).map (algebraMap ℚ K) = + C (t : K) + C (2 ^ (a + 1)) * X := by + rw [Polynomial.map_add, Polynomial.map_mul, Polynomial.map_C, Polynomial.map_C, + Polynomial.map_X, map_intCast, map_pow, map_ofNat] + rw [← Polynomial.map_map, hEq, Polynomial.map_comp, + map_hermiteBasis _ (pairwise_isUnit_sub _) ht, hv, hlin] + +/-- The inverse power series of `E_t(t + α z)` is the image of a power series over `ℤ_(2)`. -/ +private theorem invOfUnit_hermiteBasis_comp_eq_map (a u h : ℕ) {t : ℤ} (ht : t ∈ recoveryNodes a) : + ∃ ψ : PowerSeries ℤ₍₂₎, + PowerSeries.invOfUnit (((hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : K)) + (taperMult u h) t).comp (C (t : K) + C (2 ^ (a + 1)) * X) : K[X]) : PowerSeries K) 1 = + PowerSeries.map ((algebraMap ℚ K).comp (ℤ₍₂₎).subtype) ψ := by + obtain ⟨E, hE0, hE⟩ := hermiteBasis_comp_eq_map (K := K) a u h ht + refine ⟨PowerSeries.invOfUnit (E : PowerSeries ℤ₍₂₎) 1, ?_⟩ + set κ := (algebraMap ℚ K).comp (ℤ₍₂₎).subtype + set φ : PowerSeries K := ((E.map κ : K[X]) : PowerSeries K) + have hφ : φ = PowerSeries.map κ (E : PowerSeries ℤ₍₂₎) := Polynomial.polynomial_map_coe + have h1 : φ * PowerSeries.invOfUnit φ 1 = 1 := + PowerSeries.mul_invOfUnit _ _ (by + rw [Polynomial.constantCoeff_coe, coeff_map, hE0, map_one, Units.val_one]) + have h2 : φ * PowerSeries.map κ (PowerSeries.invOfUnit (E : PowerSeries ℤ₍₂₎) 1) = 1 := by + rw [hφ, ← map_mul, PowerSeries.mul_invOfUnit _ _ (by + rw [Polynomial.constantCoeff_coe, hE0, Units.val_one]), map_one] + rw [hE] + calc PowerSeries.invOfUnit φ 1 + = PowerSeries.invOfUnit φ 1 * (φ * PowerSeries.map κ + (PowerSeries.invOfUnit (E : PowerSeries ℤ₍₂₎) 1)) := by rw [h2, mul_one] + _ = PowerSeries.map κ (PowerSeries.invOfUnit (E : PowerSeries ℤ₍₂₎) 1) := by + rw [← mul_assoc, mul_comm _ φ, h1, one_mul] + +/-! ### Congruences modulo powers of two -/ + +private theorem padicValRat_two : padicValRat 2 (2 : ℚ) = 1 := by + exact_mod_cast padicValRat.self (p := 2) one_lt_two + +omit [Algebra ℚ K] in +/-- `x` is `2 ^ n` times an element of `B`. -/ +private def InTwoPow (B : Subring K) (n : ℕ) (x : K) : Prop := + ∃ b ∈ B, x = 2 ^ n * b + +omit [Algebra ℚ K] in +private theorem inTwoPow_zero (B : Subring K) (n : ℕ) : InTwoPow B n (0 : K) := + ⟨0, B.zero_mem, (mul_zero _).symm⟩ + +omit [Algebra ℚ K] in +private theorem InTwoPow.add {B : Subring K} {n : ℕ} {x y : K} (hx : InTwoPow B n x) + (hy : InTwoPow B n y) : InTwoPow B n (x + y) := by + obtain ⟨b, hb, rfl⟩ := hx + obtain ⟨c, hc, rfl⟩ := hy + exact ⟨b + c, B.add_mem hb hc, by ring⟩ + +omit [Algebra ℚ K] in +private theorem InTwoPow.sub {B : Subring K} {n : ℕ} {x y : K} (hx : InTwoPow B n x) + (hy : InTwoPow B n y) : InTwoPow B n (x - y) := by + obtain ⟨b, hb, rfl⟩ := hx + obtain ⟨c, hc, rfl⟩ := hy + exact ⟨b - c, B.sub_mem hb hc, by ring⟩ + +omit [Algebra ℚ K] in +private theorem inTwoPow_sum {B : Subring K} {n : ℕ} {ι : Type*} (s : Finset ι) {f : ι → K} + (hf : ∀ i ∈ s, InTwoPow B n (f i)) : InTwoPow B n (∑ i ∈ s, f i) := by + classical + induction s using Finset.induction_on with + | empty => exact inTwoPow_zero B n + | insert j s hj ih => + rw [sum_insert hj] + exact (hf j (mem_insert_self j s)).add (ih fun i hi => hf i (mem_insert_of_mem hi)) + +omit [Algebra ℚ K] in +private theorem InTwoPow.mul_left {B : Subring K} {n : ℕ} {x y : K} (hx : InTwoPow B n x) + (hy : y ∈ B) : InTwoPow B n (x * y) := by + obtain ⟨b, hb, rfl⟩ := hx + exact ⟨b * y, B.mul_mem hb hy, by ring⟩ + +/-- A rational of valuation at least `-k` maps `2 ^ (n + k) B` into `2 ^ n B`. -/ +private theorem InTwoPow.algebraMap_mul {B : Subring K} + (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) {n k : ℕ} {x : K} (hx : InTwoPow B (n + k) x) + {r : ℚ} (hr : r ≠ 0) (hv : -(k : ℤ) ≤ padicValRat 2 r) : + InTwoPow B n (algebraMap ℚ K r * x) := by + obtain ⟨b, hb, rfl⟩ := hx + have hr2 : r * 2 ^ k ∈ ℤ₍₂₎ := by + have hne : r * 2 ^ k ≠ 0 := mul_ne_zero hr (by positivity) + have hval : 0 ≤ padicValRat 2 (r * 2 ^ k) := by + rw [padicValRat.mul hr (by positivity), padicValRat.pow, padicValRat_two] + omega + change Rat.padicValuation 2 (r * 2 ^ k) ≤ 1 + simp only [Rat.padicValuation, Valuation.coe_mk, MonoidWithZeroHom.coe_mk, ZeroHom.coe_mk, + hne, ↓reduceIte] + rw [← WithZero.exp_zero, WithZero.exp_le_exp] + omega + refine ⟨algebraMap ℚ K (r * 2 ^ k) * b, B.mul_mem (hB _ hr2) hb, ?_⟩ + rw [map_mul, map_pow, map_ofNat, pow_add] + ring + +/-- **The quotient of a jet divisible by `2 ^ n` is divisible by `2 ^ n`**: the inverse power +series of `E_t(t + α z)` is integral. -/ +private theorem inTwoPow_coeff_hermiteQuotient {B : Subring K} + (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) {a u h n : ℕ} {t : ℤ} (ht : t ∈ recoveryNodes a) + {P : K[X]} (hP : ∀ j < taperMult u h t, InTwoPow B n (P.coeff j)) (j : ℕ) : + InTwoPow B n ((hermiteQuotient (recoveryNodes a) (fun q : ℤ => (q : K)) (taperMult u h) + (2 ^ (a + 1)) t P).coeff j) := by + obtain ⟨ψ, hψ⟩ := invOfUnit_hermiteBasis_comp_eq_map (K := K) a u h ht + rw [hermiteQuotient, hψ, PowerSeries.coeff_trunc] + split_ifs with hj + · rw [PowerSeries.coeff_mul] + refine inTwoPow_sum _ fun kl hkl => ?_ + have hk : kl.1 < taperMult u h t := by + have := mem_antidiagonal.mp hkl + omega + rw [Polynomial.coeff_coe, PowerSeries.coeff_map] + exact (hP _ hk).mul_left (hB _ (PowerSeries.coeff kl.2 ψ).2) + · exact inTwoPow_zero B n + +/-- The value of `E_t(0)` in `K` is the image of the rational one. -/ +private theorem eval_zero_hermiteBasis_eq (a u h : ℕ) {t : ℤ} (ht : t ∈ recoveryNodes a) : + (hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : K)) (taperMult u h) t).eval 0 = + algebraMap ℚ K + ((hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : ℚ)) (taperMult u h) t).eval 0) := by + have hv : (fun q : ℤ => algebraMap ℚ K (q : ℚ)) = fun q : ℤ => (q : K) := + funext fun q => map_intCast _ q + rw [← hv, ← map_hermiteBasis _ (pairwise_isUnit_sub _) ht, eval_map, + ← map_zero (algebraMap ℚ K), eval₂_at_apply] + +/-- `E_t(0) ≠ 0`: its factors are `-q / (t - q)` with `q ≠ 0`. -/ +private theorem eval_zero_hermiteBasis_ne_zero (a u h : ℕ) {t : ℤ} : + (hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : ℚ)) (taperMult u h) t).eval 0 ≠ 0 := by + rw [eval_hermiteBasis, prod_ne_zero_iff] + intro q hq + have hq0 : (q : ℚ) ≠ 0 := by exact_mod_cast ne_of_mem_erase (mem_of_mem_erase hq) + have hqt : ((t : ℚ) - q) ≠ 0 := sub_ne_zero.mpr (by exact_mod_cast (ne_of_mem_erase hq).symm) + rw [Ring.inverse_eq_inv'] + exact pow_ne_zero _ (mul_ne_zero (inv_ne_zero hqt) (by rwa [zero_sub, neg_ne_zero])) + +/-- **Lemma D.5, precision** ([OpenAI2026ETH], §D.4.4, pp. 60–61). Let `B` be a subring of the +`ℚ`-algebra `K` containing `ℤ_(2)`, and let `F ∈ K[T]` have degree below the total multiplicity +`∑_t q_t`. Suppose that at every nonzero grid point `t` the polynomial `G t` agrees with +`F(t + α z)` modulo `2 ^ p_t` below `z ^ q_t`, where `p + 6 h D_t + q_t (a + 1) ≤ p_t` and +`D_t = 2 ^ (a - v₂ t)`. Then the recovered value agrees with `F(0)` modulo `2 ^ p`. -/ +theorem exists_taperedRecovery_eq_add (B : Subring K) (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) + {a u h p : ℕ} (pt : ℤ → ℕ) + (hpt : ∀ t ∈ recoveryNodes a, + p + 6 * h * 2 ^ (a - padicValInt 2 t) + taperMult u h t * (a + 1) ≤ pt t) + {F : K[X]} (hF : F.degree < ↑(∑ t ∈ recoveryNodes a, taperMult u h t)) (G : ℤ → K[X]) + (hG : ∀ t ∈ recoveryNodes a, ∀ j < taperMult u h t, ∃ b ∈ B, + (G t).coeff j = (F.comp (C (t : K) + C (2 ^ (a + 1)) * X)).coeff j + 2 ^ pt t * b) : + ∃ b ∈ B, taperedRecovery a u h G = F.eval 0 + 2 ^ p * b := by + suffices hsub : InTwoPow B p (taperedRecovery a u h G - F.eval 0) by + obtain ⟨b, hb, hb'⟩ := hsub + exact ⟨b, hb, by rw [← hb', add_sub_cancel]⟩ + rw [eval_zero_eq_taperedRecovery hF, taperedRecovery, taperedRecovery, ← sum_sub_distrib] + refine inTwoPow_sum _ fun t ht => ?_ + set F' := F.comp (C (t : K) + C (2 ^ (a + 1)) * X) + set J := taperMult u h t + set Q := hermiteQuotient (recoveryNodes a) (fun q : ℤ => (q : K)) (taperMult u h) + (2 ^ (a + 1)) t ((G t - F' : K[X]) : PowerSeries K) + have hQ : ∀ j, InTwoPow B (pt t) (Q.coeff j) := by + refine inTwoPow_coeff_hermiteQuotient hB ht (P := G t - F') (fun j hj => ?_) + obtain ⟨b, hb, hb'⟩ := hG t ht j hj + exact ⟨b, hb, by rw [coeff_sub, hb', add_sub_cancel_left]⟩ + have hQsub : hermiteQuotient (recoveryNodes a) (fun q : ℤ => (q : K)) (taperMult u h) + (2 ^ (a + 1)) t (G t) - hermiteQuotient (recoveryNodes a) (fun q : ℤ => (q : K)) + (taperMult u h) (2 ^ (a + 1)) t F' = Q := by + simp only [Q, hermiteQuotient, Polynomial.coe_sub, sub_mul, map_sub] + have hdeg : Q.natDegree < J + 1 := by + have := degree_hermiteQuotient_lt (recoveryNodes a) (fun q : ℤ => (q : K)) (taperMult u h) + (2 ^ (a + 1)) t ((G t - F' : K[X]) : PowerSeries K) + by_cases hQ0 : Q = 0 + · rw [hQ0, natDegree_zero]; omega + · have := (natDegree_lt_iff_degree_lt hQ0).mpr this + omega + rw [← mul_sub, ← eval_sub, hQsub, eval_eq_sum_range' hdeg, mul_sum, + eval_zero_hermiteBasis_eq a u h ht] + refine inTwoPow_sum _ fun j hj => ?_ + have hjJ : j ≤ J := Nat.lt_succ_iff.mp (mem_range.mp hj) + set E0 := (hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : ℚ)) (taperMult u h) t).eval 0 + have ht0 : (t : ℚ) ≠ 0 := by exact_mod_cast ne_of_mem_erase ht + have hx0 : (-(t : ℚ) / 2 ^ (a + 1)) ≠ 0 := div_ne_zero (neg_ne_zero.mpr ht0) (by positivity) + have hr : E0 * (-(t : ℚ) / 2 ^ (a + 1)) ^ j ≠ 0 := + mul_ne_zero (eval_zero_hermiteBasis_ne_zero a u h) (pow_ne_zero _ hx0) + have hE0 : -(6 * (h : ℤ) * 2 ^ (a - padicValInt 2 t)) ≤ padicValRat 2 E0 := by + have := neg_le_padicValRat_eval_zero_hermiteBasis_taper (a := a) u h + (mem_of_mem_erase (by rw [← recoveryNodes_eq]; exact ht)) (ne_of_mem_erase ht) + rw [← recoveryNodes_eq] at this + exact this + have hxv : -((a : ℤ) + 1) ≤ padicValRat 2 (-(t : ℚ) / 2 ^ (a + 1)) := by + rw [padicValRat.div (neg_ne_zero.mpr ht0) (by positivity), padicValRat.neg, + padicValRat.of_int, padicValRat.pow, padicValRat_two] + push_cast + omega + have hval : -((pt t - p : ℕ) : ℤ) ≤ padicValRat 2 (E0 * (-(t : ℚ) / 2 ^ (a + 1)) ^ j) := by + rw [padicValRat.mul (eval_zero_hermiteBasis_ne_zero a u h) (pow_ne_zero _ hx0), + padicValRat.pow] + have hpt' := hpt t ht + have hJ : (j : ℤ) * padicValRat 2 (-(t : ℚ) / 2 ^ (a + 1)) ≥ -((J : ℤ) * ((a : ℤ) + 1)) := by + have : (j : ℤ) ≤ J := by exact_mod_cast hjJ + nlinarith + have : ((pt t - p : ℕ) : ℤ) = (pt t : ℤ) - p := by omega + push_cast [this] + have hpt'' : (p : ℤ) + 6 * h * 2 ^ (a - padicValInt 2 t) + (J : ℤ) * (a + 1) ≤ pt t := by + exact_mod_cast hpt' + linarith + have hk : p + (pt t - p) = pt t := by have := hpt t ht; omega + have hQj := hQ j + rw [← hk] at hQj + have := hQj.algebraMap_mul hB hr hval + rw [map_mul, map_pow] at this + convert this using 1 + ring + +/-- **Lemma D.5 with the manuscript's parameters.** Let `1 ≤ u ≤ M`, let `h` satisfy +`3 M ≤ h u ≤ 3 M + u` (as `h = ⌈3 M / u⌉` does), and let `F` have degree at most `M (2 L - 1)`, +`L = 2 ^ a`. If the jets `G t` agree with `F(t + α z)` modulo `2 ^ p_t` below `z ^ q_t`, with +`p_t = p + 8 h D_t + 8 M (a + 1)`, then the recovered value agrees with `F(0)` modulo `2 ^ p`. -/ +theorem exists_taperedRecovery_eq_add_of_params (B : Subring K) + (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) {a u h p M : ℕ} (hu : 1 ≤ u) (huM : u ≤ M) + (hh : 3 * M ≤ h * u) (hh' : h * u ≤ 3 * M + u) {F : K[X]} + (hF : F.natDegree ≤ M * (2 * 2 ^ a - 1)) (G : ℤ → K[X]) + (hG : ∀ t ∈ recoveryNodes a, ∀ j < taperMult u h t, ∃ b ∈ B, + (G t).coeff j = (F.comp (C (t : K) + C (2 ^ (a + 1)) * X)).coeff j + + 2 ^ (p + 8 * h * 2 ^ (a - padicValInt 2 t) + 8 * M * (a + 1)) * b) : + ∃ b ∈ B, taperedRecovery a u h G = F.eval 0 + 2 ^ p * b := by + refine exists_taperedRecovery_eq_add B hB + (fun t => p + 8 * h * 2 ^ (a - padicValInt 2 t) + 8 * M * (a + 1)) (fun t _ => ?_) ?_ G hG + · have hJ := taperMult_le hh' huM t + have hD : 6 * h * 2 ^ (a - padicValInt 2 t) ≤ 8 * h * 2 ^ (a - padicValInt 2 t) := + Nat.mul_le_mul_right _ (Nat.mul_le_mul_right _ (by norm_num)) + have hJa : taperMult u h t * (a + 1) ≤ 8 * M * (a + 1) := + Nat.mul_le_mul_right _ (by omega) + omega + · refine (degree_le_natDegree).trans_lt ?_ + have hsum := le_sum_taperMult a u h + have hM : 1 ≤ M := hu.trans huM + have h2a : 1 ≤ 2 ^ a := Nat.one_le_two_pow + have : M * (2 * 2 ^ a - 1) < 2 ^ a * (h * u) := by + have h3 : 2 ^ a * (3 * M) ≤ 2 ^ a * (h * u) := Nat.mul_le_mul_left _ hh + have : M * (2 * 2 ^ a - 1) < 2 ^ a * (3 * M) := by + rw [Nat.mul_sub, mul_one] + have : M * (2 * 2 ^ a) = 2 * (2 ^ a * M) := by ring + have : 2 ^ a * (3 * M) = 3 * (2 ^ a * M) := by ring + have : 1 ≤ 2 ^ a * M := Nat.one_le_iff_ne_zero.mpr (by positivity) + omega + omega + exact_mod_cast (hF.trans_lt (this.trans_le hsum)) + +end Cslib.Circuits.Frontier diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkLocalDeformation.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkLocalDeformation.lean new file mode 100644 index 0000000000..eea99015e2 --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkLocalDeformation.lean @@ -0,0 +1,68 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.LocalDeformation +public import Complexity.Foundations.NumberTheory.Padics.RatValuation + +/-! +# Tests for the deformed contraction in local coordinates + +The two-edge network of the Cauchy-mode tests, with tensors over `ℤ_(2)`: one packet of two +slots (`a = 1`), at the grid point `t = 1`. Its local deformation is a polynomial over `ℤ_(2)` of +degree at most `2 (2 * 2 - 1) = 6`; in `ℚ` it is `F(1 + 4 z)`, and modulo `4` its value at `ζ` is +the deformed contraction along the Cauchy matrix at `T = 1 + 4 ζ`. +-/ + +@[expose] public section + +namespace ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.LocalDeformation + +open Cslib.Circuits.Frontier TensorNetwork Polynomial + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +/-- The two-edge network over `ℤ_(2)`, with tensor `(1, 2)` on the bit of the edge at each +vertex. -/ +def net : TensorNetwork ℤ₍₂₎ Bool (Fin 4) (Fin 2) where + src e := ⟨e.val, by omega⟩ + tgt e := ⟨e.val + 2, by omega⟩ + tensor v x := if x ⟨v.val % 2, Nat.mod_lt _ (by decide)⟩ then 2 else 1 + tensor_congr v x y h := by rw [h _ (by fin_cases v <;> decide)] + +/-- The rows `0, 1`. -/ +def side (v : Fin 4) : Bool := decide (v.val < 2) + +/-- Edge `e` sits in slot `e` of the single packet. -/ +def slot (e : Fin 2) : ℕ := e.val + +theorem hslot (e : Fin 2) : slot e < 2 ^ 1 := by fin_cases e <;> decide + +/-- The degree bound `M (2 L - 1)`. -/ +example : (net.localDeformation side (RingHom.id _) 1 1 (fun _ => ()) slot).natDegree ≤ 6 := + natDegree_localDeformation_le net side (RingHom.id _) hslot (by decide) + +/-- In `ℚ`, the local deformation is `F(1 + 4 z)` for the deformed contraction `F(T)` along the +Cauchy matrix. -/ +example : (net.localDeformation side (RingHom.id _) 1 1 (fun _ => ()) slot).map (ℤ₍₂₎).subtype = + (((net.map (ℤ₍₂₎).subtype).map C).deformedContraction side + ((cauchyMatrix (2 ^ 1) (fun _ => ()) slot).map (mapRingHom (algebraMap ℚ ℚ)))).comp + (C ((1 : ℤ) : ℚ) + C (2 ^ (1 + 1)) * X) := + map_localDeformation net side (RingHom.id _) (ℤ₍₂₎).subtype rfl hslot (by decide) + +/-- Modulo `4`, its value at `ζ` is the deformed contraction along the Cauchy matrix at +`T = 1 + 4 ζ`. -/ +example (ζ : ZMod (2 ^ 2)) : + (net.localDeformation side (RingHom.id _) 1 1 (fun _ => ()) slot).eval₂ + (Rat.padicIntegerToZModPow 2 2) ζ = + (net.map (Rat.padicIntegerToZModPow 2 2)).deformedContraction side + (cauchyValueMatrix (Rat.padicIntegerToZModPow 2 2) ζ 1 1 (fun _ => ()) slot) := + eval₂_localDeformation net side (RingHom.id _) _ ζ 1 1 _ slot + +end ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.LocalDeformation diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkTaperedRecovery.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkTaperedRecovery.lean new file mode 100644 index 0000000000..af21199d2f --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkTaperedRecovery.lean @@ -0,0 +1,50 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.TaperedRecovery + +/-! +# Tests for tapered Hermite recovery with finite precision + +The grid of two slots (`a = 1`) has the nodes `-2, -1, 1`. With `u = 2` and `h = 3`, the odd nodes +have multiplicity `6` and the node `-2`, of valuation one, has multiplicity `3`. The precision +theorem applies over `K = ℚ` with `B = ℤ_(2)`: its hypotheses are met by exact jets. +-/ + +@[expose] public section + +namespace ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.TaperedRecovery + +open Cslib.Circuits.Frontier Polynomial + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +example : recoveryNodes 1 = {-2, -1, 1} := by decide + +example : taperMult 2 3 1 = 6 ∧ taperMult 2 3 (-1) = 6 := by + simp [taperMult, padicValInt] + +example : taperMult 2 3 (-2) = 3 := by + simp [taperMult, padicValInt] + +/-- The total multiplicity of the two odd nodes alone is `2 * 6`. -/ +example : 2 ^ 1 * (3 * 2) ≤ ∑ t ∈ recoveryNodes 1, taperMult 2 3 t := + le_sum_taperMult 1 2 3 + +/-- **Non-vacuity.** For `F = X` (degree `1 = M (2 L - 1)` with `M = 1`, `L = 1`), `u = 1` and +`h = 3`, exact jets recover `F(0)` modulo every `2 ^ p`. -/ +example (p : ℕ) : + ∃ b ∈ ℤ₍₂₎, taperedRecovery 0 1 3 (fun t => (X : ℚ[X]).comp (C (t : ℚ) + C (2 ^ (0 + 1)) * X)) = + (X : ℚ[X]).eval 0 + 2 ^ p * b := + exists_taperedRecovery_eq_add_of_params (K := ℚ) (M := 1) ℤ₍₂₎ (fun c hc => hc) le_rfl le_rfl + (by norm_num) (by norm_num) (by rw [natDegree_X]; norm_num) _ + (fun t _ j _ => ⟨0, zero_mem _, by rw [mul_zero, add_zero]⟩) + +end ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.TaperedRecovery From f9349461854369452a5b74f4698f350ab194f7a5 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:11:30 -0400 Subject: [PATCH 010/129] refactor(foundations): move AdjoinRoot coefficient reduction to its own module `AdjoinRoot.map_mk`, `AdjoinRoot.map_surjective_of_surjective` and `AdjoinRoot.map_eq_zero_iff` move unchanged from `GaloisRing.lean` to the new `Complexity/Foundations/RingTheory/AdjoinRoot.lean`. Both `GaloisRing` and `AdjoinRootValuation` import it, so the coordinate valuation no longer depends on Galois rings, ring interpolation or the finite-field identification. Names are unchanged; `GaloisRing` re-exports the module publicly. Addresses the placement finding of reviewer-codex-edemaine on #1859. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Padics/AdjoinRootValuation.lean | 3 +- .../Foundations/RingTheory/AdjoinRoot.lean | 85 +++++++++++++++++++ .../Foundations/RingTheory/GaloisRing.lean | 57 +------------ 4 files changed, 89 insertions(+), 57 deletions(-) create mode 100644 Complexity/Foundations/RingTheory/AdjoinRoot.lean diff --git a/Complexity.lean b/Complexity.lean index 7ae579ac43..6e48165717 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2777,6 +2777,7 @@ public import Complexity.Foundations.Probability.TrueSampleMeans public import Complexity.Foundations.Quantum.Measurement public import Complexity.Foundations.Relation.InductiveCounting public import Complexity.Foundations.Relation.RelatesWithinSteps +public import Complexity.Foundations.RingTheory.AdjoinRoot public import Complexity.Foundations.RingTheory.CyclicIntRing public import Complexity.Foundations.RingTheory.CyclicIntRing.Coefficients public import Complexity.Foundations.RingTheory.GaloisRing diff --git a/Complexity/Foundations/NumberTheory/Padics/AdjoinRootValuation.lean b/Complexity/Foundations/NumberTheory/Padics/AdjoinRootValuation.lean index 707dad117b..edb4016e1f 100644 --- a/Complexity/Foundations/NumberTheory/Padics/AdjoinRootValuation.lean +++ b/Complexity/Foundations/NumberTheory/Padics/AdjoinRootValuation.lean @@ -11,7 +11,8 @@ public import Complexity.Init public import Mathlib.NumberTheory.Padics.PadicNumbers public import Mathlib.RingTheory.AdjoinRoot public import Mathlib.RingTheory.Valuation.Integers -import Complexity.Foundations.RingTheory.GaloisRing +import Complexity.Foundations.RingTheory.AdjoinRoot +import Mathlib.Algebra.Field.ZMod import Mathlib.Algebra.GCDMonoid.IntegrallyClosed import Mathlib.Algebra.Polynomial.Eval.Irreducible import Mathlib.RingTheory.Localization.Integral diff --git a/Complexity/Foundations/RingTheory/AdjoinRoot.lean b/Complexity/Foundations/RingTheory/AdjoinRoot.lean new file mode 100644 index 0000000000..39bfed75e5 --- /dev/null +++ b/Complexity/Foundations/RingTheory/AdjoinRoot.lean @@ -0,0 +1,85 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate (Mathlib, `Mathlib/RingTheory/AdjoinRoot.lean`) + +public import Complexity.Init +public import Mathlib.RingTheory.AdjoinRoot + +/-! +# Reducing the coefficients of `R[X] / (g)` + +For a ring homomorphism `φ : R →+* S` and `q ∣ g.map φ`, Mathlib's `AdjoinRoot.map φ g q` sends +`R[X] / (g)` to `S[X] / (q)`. This file computes it on classes of polynomials, shows that it is +surjective when `φ` is, and characterizes its kernel when `g` is monic and `q = g.map φ`: an element +vanishes exactly when the coefficients of its reduced representative do. + +These facts are used both for Galois rings (`GaloisRing.residue`, reduction modulo `p`) and for the +`p`-adic valuation of `ℚ[X] / (f)` (`AdjoinRoot.padicValuation`). + +## Main results + +* `AdjoinRoot.map_mk`: the class of `P` maps to the class of `P.map φ`. +* `AdjoinRoot.map_surjective_of_surjective`: reducing the coefficients is surjective when `φ` is. +* `AdjoinRoot.map_eq_zero_iff`: an element of `R[X] / (g)` vanishes in `S[X] / (g.map φ)` exactly + when the coefficients of its reduced representative do. +-/ + +public section + +open Polynomial + +namespace AdjoinRoot + +variable {R S : Type*} [CommRing R] [CommRing S] (φ : R →+* S) {g : R[X]} + +/-- Reducing the coefficients maps the class of `P` to the class of `P.map φ`. -/ +theorem map_mk (q : S[X]) (h : q ∣ g.map φ) (P : R[X]) : + map φ g q h (mk g P) = mk q (P.map φ) := by + have : (map φ g q h).comp (mk g) = (mk q).comp (mapRingHom φ) := by + refine Polynomial.ringHom_ext (fun a => ?_) ?_ + · simp only [RingHom.comp_apply, mk_C, map_of, coe_mapRingHom, Polynomial.map_C] + · simp only [RingHom.comp_apply, mk_X, map_root, coe_mapRingHom, Polynomial.map_X] + exact congrArg (fun F : R[X] →+* AdjoinRoot q => F P) this + +/-- Reducing the coefficients is surjective when `φ` is. -/ +theorem map_surjective_of_surjective (q : S[X]) (h : q ∣ g.map φ) + (hφ : Function.Surjective φ) : Function.Surjective (map φ g q h) := by + intro y + obtain ⟨Q, rfl⟩ := mk_surjective y + obtain ⟨P, rfl⟩ := Polynomial.map_surjective φ hφ Q + exact ⟨mk g P, map_mk φ q h P⟩ + +/-- **An element vanishes after reducing the coefficients** of a monic `g` exactly when the +coefficients of its representative of degree below `deg g` do: that representative reduces to a +polynomial of degree below `deg (g.map φ)`, which `g.map φ` divides only when it is zero. -/ +theorem map_eq_zero_iff [Nontrivial S] (hg : g.Monic) {q : S[X]} (h : q ∣ g.map φ) + (hq : q = g.map φ) (x : AdjoinRoot g) : + map φ g q h x = 0 ↔ ∀ i, φ ((modByMonicHom hg x).coeff i) = 0 := by + subst hq + have := φ.domain_nontrivial + obtain ⟨Q, rfl⟩ := mk_surjective x + rw [modByMonicHom_mk] + set P := Q %ₘ g + have hx : mk g P = mk g Q := by + have := mk_leftInverse hg (mk g Q) + rwa [modByMonicHom_mk] at this + have hdeg : (P.map φ).degree < (g.map φ).degree := by + rw [hg.degree_map] + exact degree_map_le.trans_lt (degree_modByMonic_lt _ hg) + rw [← hx, map_mk, mk_eq_zero] + constructor + · intro hdvd i + by_contra hne + have h0 : P.map φ ≠ 0 := fun h0 => hne (by rw [← coeff_map, h0, coeff_zero]) + exact (hg.map φ).not_dvd_of_degree_lt h0 hdeg hdvd + · intro h + have : P.map φ = 0 := Polynomial.ext fun i => by rw [coeff_map, h, coeff_zero] + rw [this] + exact dvd_zero _ + +end AdjoinRoot diff --git a/Complexity/Foundations/RingTheory/GaloisRing.lean b/Complexity/Foundations/RingTheory/GaloisRing.lean index da9bc2de1e..79a1b2965e 100644 --- a/Complexity/Foundations/RingTheory/GaloisRing.lean +++ b/Complexity/Foundations/RingTheory/GaloisRing.lean @@ -9,8 +9,8 @@ module public import Complexity.Init public import Complexity.Foundations.Data.Polynomial.RingLagrange +public import Complexity.Foundations.RingTheory.AdjoinRoot public import Mathlib.FieldTheory.Finite.GaloisField -public import Mathlib.RingTheory.AdjoinRoot public import Mathlib.RingTheory.LocalRing.ResidueField.Defs import Mathlib.RingTheory.LocalRing.ResidueField.Basic import Mathlib.RingTheory.Polynomial.Nilpotent @@ -48,8 +48,6 @@ statement over `ℤ_(p)` is `AdjoinRoot.isUnit_of_padicValuation_eq_one`. * `RingHom.isLocalHom_of_isNilpotent`: a surjection with nilpotent kernel is a local homomorphism. -* `AdjoinRoot.map_eq_zero_iff`: an element of `R[X] / (g)` vanishes in `S[X] / (g)` exactly when - the coefficients of its reduced representative do. * `GaloisRing.ker_residue`: the kernel of reduction modulo `p` is `(p)`. * `GaloisRing.isUnit_iff`: an element is a unit exactly when it is nonzero modulo `p`. * `GaloisRing.instIsLocalRing`, `GaloisRing.maximalIdeal_eq`: the ring is local with maximal ideal @@ -97,59 +95,6 @@ theorem isUnit_iff_ne_zero_of_isNilpotent {F : Type*} [Field F] (ψ : R →+* F) end RingHom -/-! ### Reducing the coefficients of `R[X] / (g)` -/ - -namespace AdjoinRoot - -variable {R S : Type*} [CommRing R] [CommRing S] (φ : R →+* S) {g : R[X]} - -/-- Reducing the coefficients maps the class of `P` to the class of `P.map φ`. -/ -theorem map_mk (q : S[X]) (h : q ∣ g.map φ) (P : R[X]) : - map φ g q h (mk g P) = mk q (P.map φ) := by - have : (map φ g q h).comp (mk g) = (mk q).comp (mapRingHom φ) := by - refine Polynomial.ringHom_ext (fun a => ?_) ?_ - · simp only [RingHom.comp_apply, mk_C, map_of, coe_mapRingHom, Polynomial.map_C] - · simp only [RingHom.comp_apply, mk_X, map_root, coe_mapRingHom, Polynomial.map_X] - exact congrArg (fun F : R[X] →+* AdjoinRoot q => F P) this - -/-- Reducing the coefficients is surjective when `φ` is. -/ -theorem map_surjective_of_surjective (q : S[X]) (h : q ∣ g.map φ) - (hφ : Function.Surjective φ) : Function.Surjective (map φ g q h) := by - intro y - obtain ⟨Q, rfl⟩ := mk_surjective y - obtain ⟨P, rfl⟩ := Polynomial.map_surjective φ hφ Q - exact ⟨mk g P, map_mk φ q h P⟩ - -/-- **An element vanishes after reducing the coefficients** of a monic `g` exactly when the -coefficients of its representative of degree below `deg g` do: that representative reduces to a -polynomial of degree below `deg (g.map φ)`, which `g.map φ` divides only when it is zero. -/ -theorem map_eq_zero_iff [Nontrivial S] (hg : g.Monic) {q : S[X]} (h : q ∣ g.map φ) - (hq : q = g.map φ) (x : AdjoinRoot g) : - map φ g q h x = 0 ↔ ∀ i, φ ((modByMonicHom hg x).coeff i) = 0 := by - subst hq - have := φ.domain_nontrivial - obtain ⟨Q, rfl⟩ := mk_surjective x - rw [modByMonicHom_mk] - set P := Q %ₘ g - have hx : mk g P = mk g Q := by - have := mk_leftInverse hg (mk g Q) - rwa [modByMonicHom_mk] at this - have hdeg : (P.map φ).degree < (g.map φ).degree := by - rw [hg.degree_map] - exact degree_map_le.trans_lt (degree_modByMonic_lt _ hg) - rw [← hx, map_mk, mk_eq_zero] - constructor - · intro hdvd i - by_contra hne - have h0 : P.map φ ≠ 0 := fun h0 => hne (by rw [← coeff_map, h0, coeff_zero]) - exact (hg.map φ).not_dvd_of_degree_lt h0 hdeg hdvd - · intro h - have : P.map φ = 0 := Polynomial.ext fun i => by rw [coeff_map, h, coeff_zero] - rw [this] - exact dvd_zero _ - -end AdjoinRoot - /-! ### Galois rings -/ /-- The ring `(ℤ / p ^ n)[X] / (f)` presented by an integer polynomial `f`. For a prime `p`, From 5189108242a32270693556cd43044d989591fe1d Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:11:30 -0400 Subject: [PATCH 011/129] doc(foundations): scope GaloisRing to the manuscript's base ring O/(2^n) On p. 57 of the pinned manuscript, O = Z_(2)[U]/(f), A = O[V]/(V^l) and A_p = A/(2^p). `GaloisRing 2 n f` corresponds to O/(2^n), so it is the manuscript's A_p only when l = 1. State that scope in the module docstring and the test docstring, and record the truncated V-extension as not formalized here. Addresses the source-scope finding of reviewer-codex-edemaine on #1859. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity/Foundations/RingTheory/GaloisRing.lean | 10 +++++++--- ComplexityTests/Foundations/RingTheory/GaloisRing.lean | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Complexity/Foundations/RingTheory/GaloisRing.lean b/Complexity/Foundations/RingTheory/GaloisRing.lean index 79a1b2965e..b9f21502ce 100644 --- a/Complexity/Foundations/RingTheory/GaloisRing.lean +++ b/Complexity/Foundations/RingTheory/GaloisRing.lean @@ -28,9 +28,13 @@ The *digit lifts* `∑ i, c i * X ^ i` with digits `c i ∈ {0, …, p - 1}` red distinct residue-field elements, so any two of them differ by a unit, and Lagrange interpolation over the ring holds at any set of them (`GaloisRing.eq_ringInterpolate_digitLift`). -This is the coefficient ring of the Boolean tensor contraction of [OpenAI2026ETH] (§D.4.1, -pp. 57–58), which takes `p = 2`, a degree `e = 4 ⌈log₂ s⌉` and the precision `n`; the manuscript's -binary-coordinate lifts are the digit lifts for `p = 2`. +This is the base coefficient ring of the Boolean tensor contraction of [OpenAI2026ETH] (§D.4.1, +pp. 57–58), with `p = 2`, a degree `e = 4 ⌈log₂ s⌉` and the precision `n`. The manuscript puts +`O = ℤ_(2)[U] / (f)` and `A = O[V] / (V ^ ℓ)`, and computes in `A / (2 ^ n)` (written `A_p` there, +with `p` the precision). `GaloisRing 2 n f` corresponds to `O / (2 ^ n)`, since +`ℤ_(2) / (2 ^ n) ≅ ℤ / 2 ^ n` (that identification is not formalized here). It is therefore the +manuscript's `A / (2 ^ n)` only when `ℓ = 1`; the truncated `V`-extension for general `ℓ` is not +formalized here. The manuscript's binary-coordinate lifts are the digit lifts for `p = 2`. The argument is that of a surjection onto a field with nilpotent kernel (`RingHom.isUnit_iff_ne_zero_of_isNilpotent`): reduction modulo `p` has kernel `(p)`, and diff --git a/ComplexityTests/Foundations/RingTheory/GaloisRing.lean b/ComplexityTests/Foundations/RingTheory/GaloisRing.lean index c11fcc94d8..9a4438a778 100644 --- a/ComplexityTests/Foundations/RingTheory/GaloisRing.lean +++ b/ComplexityTests/Foundations/RingTheory/GaloisRing.lean @@ -10,7 +10,8 @@ import Mathlib.Tactic.ComputeDegree import Mathlib.Tactic.FinCases /-! Tests for Galois rings, on `(ℤ / 8)[U] / (U ^ 2 + U + 1)`: the case `p = 2`, `e = 2` and -precision `3` of the coefficient ring of [OpenAI2026ETH], §D.4.1. -/ +precision `3` of the base coefficient ring `O / (2 ^ 3)` of [OpenAI2026ETH], §D.4.1, which is the +manuscript's `A_3` when `ℓ = 1`. -/ namespace ComplexityTests.GaloisRing From 1e12be93f698fbdbe699e102b5e54cd73d2898e0 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:18:04 -0400 Subject: [PATCH 012/129] feat(circuit/frontier): correctness of one active call, packing bridge and child sizes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One active call of the contraction recursion of OpenAI2026ETH (§D.4.4–§D.4.7, pp. 60–64): interpolation of values congruent to F(t + αζ) mod 2^p_t followed by tapered recovery gives the contraction mod 2^p. Also the Lemma D.3 (2) orientation bridge for PacketPacking.Packing, the child degree and edge bounds of §D.4.6, and field lemmas for cauchyModeSplitting. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 4 +- .../Frontier/TensorNetwork/ActiveCall.lean | 284 ++++++++++++++++++ .../Frontier/TensorNetwork/CauchyModes.lean | 27 ++ .../Frontier/TensorNetwork/ChildSize.lean | 131 ++++++++ .../Frontier/TensorNetworkActiveCall.lean | 109 +++++++ 5 files changed, 554 insertions(+), 1 deletion(-) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/ActiveCall.lean create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildSize.lean create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkActiveCall.lean diff --git a/Complexity.lean b/Complexity.lean index b6d298b92f..8cbfae21cd 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -869,7 +869,9 @@ public import Complexity.Computability.Circuit.Frontier.Rectangle public import Complexity.Computability.Circuit.Frontier.Reduction public import Complexity.Computability.Circuit.Frontier.Sweep public import Complexity.Computability.Circuit.Frontier.TensorNetwork +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ActiveCall public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CauchyModes +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildSize public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit @@ -2822,8 +2824,8 @@ public import Complexity.Foundations.NumberTheory.AKS.Criterion public import Complexity.Foundations.NumberTheory.AKS.Introspective public import Complexity.Foundations.NumberTheory.AKS.SmallR public import Complexity.Foundations.NumberTheory.Harmonic -public import Complexity.Foundations.NumberTheory.Padics.RatValuation public import Complexity.Foundations.NumberTheory.Padics.AdjoinRootValuation +public import Complexity.Foundations.NumberTheory.Padics.RatValuation public import Complexity.Foundations.NumberTheory.PrimeCounting public import Complexity.Foundations.NumberTheory.PrimeWindow public import Complexity.Foundations.NumberTheory.SqrtPrimeWindow diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ActiveCall.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ActiveCall.lean new file mode 100644 index 0000000000..cf9a248fe1 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ActiveCall.lean @@ -0,0 +1,284 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.LocalDeformation +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.TaperedRecovery +public import Complexity.Foundations.Data.Polynomial.RingLagrange +public import Complexity.Foundations.Combinatorics.PacketPacking +public import Mathlib.RingTheory.Ideal.Quotient.Defs +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildSize + +/-! +# One active call of the contraction recursion + +This file proves the correctness of one active call of the recursive contraction algorithm of +[OpenAI2026ETH] (§D.4.4–§D.4.7, pp. 61–64): the contraction modulo `2 ^ p` is recovered from the +contractions of the children modulo the higher precisions `2 ^ p_t`. + +**Setting.** Let `K` be a `ℚ`-algebra and `B` a subring containing `ℤ_(2)` (in the manuscript, +`K = ℚ[U] / (f)` and `B = O`), and let `N` be a Boolean tensor network with entries in `B` whose +edges run from rows to columns. Pack its `M` edges into packets of `L = 2 ^ a` slots, and choose +`n > M (2 L - 1)` interpolation points `ζ k ∈ B` whose differences are units (in the manuscript, +binary-coordinate lifts of residue-field elements). For each nonzero grid point `t` of positive +multiplicity `q_t` and each point `ζ k`, a value `v t k ∈ B` is supplied. + +**Recovery** (`recoverFromValues`). Interpolate the values `v t ·` at the points `ζ` over `B` +(`Lagrange.ringInterpolate`), and apply the tapered Hermite recovery (D.9) to the interpolants +(`Cslib.Circuits.Frontier.taperedRecovery`). + +**Correctness.** If every `v t k` agrees with the local deformation `F(t + α ζ k)` +(`TensorNetwork.localDeformation`) modulo `2 ^ p_t`, `p_t = p + 8 h D_t + 8 M (a + 1)`, then the +interpolant agrees with `F(t + α z)` modulo `2 ^ p_t` (`Lagrange.exists_ringInterpolate_eq_add`), +and Lemma D.5 (`Cslib.Circuits.Frontier.exists_taperedRecovery_eq_add_of_params`) recovers +`F(0) = Z(N)` modulo `2 ^ p` (`TensorNetwork.exists_recoverFromValues_eq_add`). Modulo `2 ^ p_t`, +`F(t + α ζ)` is the deformed contraction of the reduced network along the Cauchy matrix at +`T = t + α ζ` (`TensorNetwork.mk_eval_localDeformation`), which the child identity (D.16), +`TensorNetwork.deformedContraction_cauchyValueMatrix_eq_sum`, writes as the sum over the mode tuples +of the products of the contractions of the children. So the values may be computed from the +children's contractions modulo `2 ^ p_t` (`TensorNetwork.exists_recoverFromValues_eq_add_of_mk`). + +**The packing bridge.** The orientation that the child identity needs is property (2) of +Lemma D.3 for `Cslib.PacketPacking.Packing` (`TensorNetwork.packingGroup_src_tgt`), and the port +counts of Lemma D.3 (3) are the group sizes that bound the children +(`TensorNetwork.card_packingGroup_src`, `TensorNetwork.card_packingGroup_tgt`, with +`TensorNetwork.ModeSplitting.mul_card_edges_child_le`). + +## Main definitions + +* `Cslib.Circuits.Frontier.integerHom`: the inclusion of `ℤ_(2)` into `B`. +* `Cslib.Circuits.Frontier.recoverFromValues`: interpolation followed by tapered recovery. +* `Cslib.Circuits.Frontier.TensorNetwork.packingGroup`: the group of a vertex in coordinate `i` + of a packing. + +## Main results + +* `Lagrange.exists_ringInterpolate_eq_add`: interpolating values that agree with a polynomial + modulo `π` gives a polynomial that agrees with it modulo `π`. +* `Cslib.Circuits.Frontier.TensorNetwork.exists_recoverFromValues_eq_add`: recovery modulo + `2 ^ p` from values modulo `2 ^ p_t`. +* `Cslib.Circuits.Frontier.TensorNetwork.mk_eval_localDeformation`: the values of the local + deformation modulo `2 ^ p_t` are deformed contractions along `cauchyValueMatrix`. +* `Cslib.Circuits.Frontier.TensorNetwork.exists_recoverFromValues_eq_add_of_mk`: correctness of + one active call. +* `Cslib.Circuits.Frontier.TensorNetwork.packingGroup_src_tgt`: the packing bridge, with + `packing_slot_lt`, `packing_slot_injective`, `card_packingGroup_src` and + `card_packingGroup_tgt`. +* `Cslib.Circuits.Frontier.two_pow_padicValInt_dvd_and_not_dvd`: a node of valuation `i` meets + the divisibility hypotheses of the child identity. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Lemma D.3 (p. 58), + §D.4.4–§D.4.7, pp. 60–64. +-/ + +public section + +open Polynomial Finset + +/-! ### Interpolation of congruent values -/ + +namespace Lagrange + +variable {R ι : Type*} [CommRing R] [DecidableEq ι] {s : Finset ι} {v : ι → R} + +/-- **Interpolating congruent values.** At nodes with unit differences, if the values `r i` agree +with a polynomial `f` of degree below `#s` modulo `π`, then so does every coefficient of their +interpolant. -/ +theorem exists_ringInterpolate_eq_add (hvs : (s : Set ι).Pairwise fun i j => IsUnit (v i - v j)) + {f : R[X]} (hf : f.degree < #s) {π : R} {r : ι → R} + (hr : ∀ i ∈ s, ∃ c, r i = f.eval (v i) + π * c) : + ∃ g : R[X], ringInterpolate s v r = f + C π * g := by + choose! c hc using hr + refine ⟨∑ i ∈ s, C (c i) * ringBasis s v i, ?_⟩ + rw [← sub_eq_iff_eq_add'] + calc ringInterpolate s v r - f + = ringInterpolate s v r - ringInterpolate s v (fun i => f.eval (v i)) := by + rw [sub_right_inj]; exact eq_ringInterpolate hvs hf + _ = ringInterpolate s v (r - fun i => f.eval (v i)) := (map_sub _ _ _).symm + _ = ∑ i ∈ s, C (π * c i) * ringBasis s v i := by + rw [ringInterpolate_apply] + refine sum_congr rfl fun i hi => ?_ + rw [Pi.sub_apply, hc i hi, add_sub_cancel_left] + _ = C π * ∑ i ∈ s, C (c i) * ringBasis s v i := by + rw [mul_sum] + exact sum_congr rfl fun i _ => by rw [C_mul, mul_assoc] + +end Lagrange + +namespace Cslib.Circuits.Frontier + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +variable {K : Type*} [CommRing K] [Algebra ℚ K] + +/-- The inclusion of `ℤ_(2)` into a subring `B` of a `ℚ`-algebra containing it. -/ +@[expose] noncomputable def integerHom (B : Subring K) (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) : + ℤ₍₂₎ →+* B := + ((algebraMap ℚ K).comp (ℤ₍₂₎).subtype).codRestrict B fun c => hB c c.2 + +theorem subtype_comp_integerHom (B : Subring K) (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) : + B.subtype.comp (integerHom B hB) = (algebraMap ℚ K).comp (ℤ₍₂₎).subtype := + rfl + +/-- The value recovered at an active call from the values `v t k` at the interpolation points +`ζ k`: interpolate over `B` at each node `t`, and apply tapered Hermite recovery (D.9). -/ +noncomputable def recoverFromValues (B : Subring K) (a u h : ℕ) {n : ℕ} (ζ : Fin n → B) + (v : ℤ → Fin n → B) : K := + taperedRecovery a u h fun t => (Lagrange.ringInterpolate univ ζ (v t)).map B.subtype + +/-- A nonzero integer `t` of valuation `i` is divisible by `2 ^ i` but not by `2 ^ (i + 1)`. -/ +theorem two_pow_padicValInt_dvd_and_not_dvd {t : ℤ} (ht : t ≠ 0) : + (2 : ℤ) ^ padicValInt 2 t ∣ t ∧ ¬ (2 : ℤ) ^ (padicValInt 2 t + 1) ∣ t := by + refine ⟨by exact_mod_cast padicValInt_dvd (p := 2) t, fun h => ?_⟩ + have := (padicValInt_dvd_iff (p := 2) (padicValInt 2 t + 1) t).mp (by exact_mod_cast h) + omega + +namespace TensorNetwork + +variable {V E P : Type*} [Fintype V] [Fintype E] [DecidableEq E] [DecidableEq P] + +/-- **Correctness of recovery from values.** Let `N` have entries in `B`, edges from rows to +columns packed into packets of `2 ^ a` slots, and let `1 ≤ u ≤ M` and `3 M ≤ h u ≤ 3 M + u` for +`M` edges. Let the `n > M (2 L - 1)` points `ζ k` have unit differences. If at every node `t` of +positive multiplicity the values `v t k` agree with `F(t + α ζ k)` modulo `2 ^ p_t`, +`p_t = p + 8 h D_t + 8 M (a + 1)`, then the recovered value agrees with the contraction modulo +`2 ^ p`. -/ +theorem exists_recoverFromValues_eq_add (B : Subring K) + (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) (N : TensorNetwork B Bool V E) (side : V → Bool) + {a : ℕ} {packet : E → P} {slot : E → ℕ} (hslot : ∀ e, slot e < 2 ^ a) + (hinj : ∀ e e', packet e = packet e' → slot e = slot e' → e = e') {u h p : ℕ} (hu : 1 ≤ u) + (huM : u ≤ Fintype.card E) (hh : 3 * Fintype.card E ≤ h * u) + (hh' : h * u ≤ 3 * Fintype.card E + u) {n : ℕ} (ζ : Fin n → B) + (hζ : Pairwise fun k l => IsUnit (ζ k - ζ l)) (hn : Fintype.card E * (2 * 2 ^ a - 1) < n) + (v : ℤ → Fin n → B) + (hv : ∀ t ∈ recoveryNodes a, 0 < taperMult u h t → ∀ k, ∃ c : B, + v t k = (N.localDeformation side (integerHom B hB) a t packet slot).eval (ζ k) + + 2 ^ (p + 8 * h * 2 ^ (a - padicValInt 2 t) + 8 * Fintype.card E * (a + 1)) * c) : + ∃ b ∈ B, recoverFromValues B a u h ζ v = (N.contraction : K) + 2 ^ p * b := by + have hF0 : (((N.map B.subtype).map C).deformedContraction side + ((cauchyMatrix (2 ^ a) packet slot).map (mapRingHom (algebraMap ℚ K)))).eval 0 = + (N.contraction : K) := by + rw [eval_zero_deformedContraction_cauchyMatrix _ side hslot hinj, contraction_map] + rfl + rw [← hF0] + refine exists_taperedRecovery_eq_add_of_params B hB hu huM hh hh' + (natDegree_deformedContraction_cauchyMatrix_le _ side hslot) _ fun t ht j hj => ?_ + have htS : t ∈ cauchyGrid (2 ^ a) := mem_of_mem_erase ht + have hnat : (N.localDeformation side (integerHom B hB) a t packet slot).natDegree < n := + (natDegree_localDeformation_le N side (integerHom B hB) hslot htS).trans_lt hn + have hdeg : (N.localDeformation side (integerHom B hB) a t packet slot).degree < + ↑(#(univ : Finset (Fin n))) := by + rw [card_univ, Fintype.card_fin] + exact (degree_le_natDegree).trans_lt (Nat.cast_lt.mpr hnat) + obtain ⟨g, hg⟩ := Lagrange.exists_ringInterpolate_eq_add (s := univ) (v := ζ) + (fun k _ l _ hkl => hζ hkl) hdeg + (π := 2 ^ (p + 8 * h * 2 ^ (a - padicValInt 2 t) + 8 * Fintype.card E * (a + 1))) + (fun k _ => hv t ht (by omega) k) + refine ⟨(g.coeff j : K), (g.coeff j).2, ?_⟩ + rw [← map_localDeformation N side (integerHom B hB) B.subtype (subtype_comp_integerHom B hB) + hslot htS, coeff_map, coeff_map, hg, coeff_add, coeff_C_mul, map_add, map_mul, map_pow] + rfl + +/-! ### The packing bridge -/ + +/-- The group of a vertex in coordinate `i` of a packing: its row label if it is a row and its +column label if it is a column. -/ +@[expose] def packingGroup {R : Type*} {a r : ℕ} (N : TensorNetwork R Bool V E) (side : V → Bool) + (Q : PacketPacking.Packing N.src N.tgt a r) (i : Fin a) (v : V) : Fin r := + if side v then Q.rowLabel v i else Q.colLabel v i + +omit [Fintype V] [Fintype E] [DecidableEq E] [DecidableEq P] in +/-- **The packing bridge.** For a packing of the edges of a network whose edges run from rows to +columns (Lemma D.3 of [OpenAI2026ETH]), the groups in coordinate `i` are oriented by digit `i` of +the slots (Lemma D.3 (2)): the hypothesis of `TensorNetwork.cauchyModeSplitting`. -/ +theorem packingGroup_src_tgt {R : Type*} {a r : ℕ} (N : TensorNetwork R Bool V E) + (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) (htgt : ∀ e, side (N.tgt e) = false) + (Q : PacketPacking.Packing N.src N.tgt a r) (i : Fin a) (e : E) : + (packingGroup N side Q i (N.src e), packingGroup N side Q i (N.tgt e)) = + if (Q.slot e : ℕ).testBit i then (Q.symbols (Q.packet e) i).swap + else Q.symbols (Q.packet e) i := by + simp only [packingGroup, hsrc, htgt, ↓reduceIte, Bool.false_eq_true] + exact Q.labels_eq e i + +omit [Fintype V] [Fintype E] [DecidableEq E] [DecidableEq P] in +/-- The slots of a packing lie below `2 ^ a`. -/ +theorem packing_slot_lt {R : Type*} {a r : ℕ} {N : TensorNetwork R Bool V E} + (Q : PacketPacking.Packing N.src N.tgt a r) (e : E) : (Q.slot e : ℕ) < 2 ^ a := + (Q.slot e).isLt + +omit [Fintype V] [Fintype E] [DecidableEq E] [DecidableEq P] in +/-- No two edges share a slot of a packet. -/ +theorem packing_slot_injective {R : Type*} {a r : ℕ} {N : TensorNetwork R Bool V E} + (Q : PacketPacking.Packing N.src N.tgt a r) (e e' : E) (hp : Q.packet e = Q.packet e') + (hs : (Q.slot e : ℕ) = Q.slot e') : e = e' := + Q.injective (Prod.ext hp (Fin.ext hs)) + +omit [Fintype V] [DecidableEq E] [DecidableEq P] in +/-- The rows of group `σ` in coordinate `i` of a packing are the rows with symbol `σ`. -/ +theorem card_packingGroup_src {R : Type*} {a r : ℕ} (N : TensorNetwork R Bool V E) + (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) + (Q : PacketPacking.Packing N.src N.tgt a r) (i : Fin a) (σ : Fin r) : + #{e | packingGroup N side Q i (N.src e) = σ} = #{e | Q.rowLabel (N.src e) i = σ} := by + simp only [packingGroup, hsrc, ↓reduceIte] + +omit [Fintype V] [DecidableEq E] [DecidableEq P] in +/-- The columns of group `σ` in coordinate `i` of a packing are the columns with symbol `σ`. -/ +theorem card_packingGroup_tgt {R : Type*} {a r : ℕ} (N : TensorNetwork R Bool V E) + (side : V → Bool) (htgt : ∀ e, side (N.tgt e) = false) + (Q : PacketPacking.Packing N.src N.tgt a r) (i : Fin a) (σ : Fin r) : + #{e | packingGroup N side Q i (N.tgt e) = σ} = #{e | Q.colLabel (N.tgt e) i = σ} := by + simp only [packingGroup, htgt, Bool.false_eq_true, ↓reduceIte] + +/-- **The values of the local deformation modulo `2 ^ p_t`** are deformed contractions of the +reduced network along the Cauchy matrix at `T = t + α ζ` (which the child identity (D.16), +`TensorNetwork.deformedContraction_cauchyValueMatrix_eq_sum`, expands). -/ +theorem mk_eval_localDeformation {B : Type*} [CommRing B] (N : TensorNetwork B Bool V E) + (side : V → Bool) (κ : ℤ₍₂₎ →+* B) (a : ℕ) (t : ℤ) (packet : E → P) (slot : E → ℕ) + (I : Ideal B) (ζ : B) : + Ideal.Quotient.mk I ((N.localDeformation side κ a t packet slot).eval ζ) = + (N.map (Ideal.Quotient.mk I)).deformedContraction side + (cauchyValueMatrix ((Ideal.Quotient.mk I).comp κ) (Ideal.Quotient.mk I ζ) a t packet + slot) := by + rw [← eval₂_hom, eval₂_localDeformation] + +/-- **Correctness of one active call** ([OpenAI2026ETH], §D.4.4–§D.4.7). Under the hypotheses of +`exists_recoverFromValues_eq_add`, suppose that at every node `t` of positive multiplicity, each +value `v t k` reduces modulo `2 ^ p_t` to the deformed contraction of the reduced network along the +Cauchy matrix at `T = t + α ζ k`, `p_t = p + 8 h D_t + 8 M (a + 1)`: by the child identity (D.16), +the sum over the mode tuples of the products of the contractions of the children. Then the +recovered value agrees with the contraction modulo `2 ^ p`. -/ +theorem exists_recoverFromValues_eq_add_of_mk (B : Subring K) + (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) (N : TensorNetwork B Bool V E) (side : V → Bool) + {a : ℕ} {packet : E → P} {slot : E → ℕ} (hslot : ∀ e, slot e < 2 ^ a) + (hinj : ∀ e e', packet e = packet e' → slot e = slot e' → e = e') {u h p : ℕ} (hu : 1 ≤ u) + (huM : u ≤ Fintype.card E) (hh : 3 * Fintype.card E ≤ h * u) + (hh' : h * u ≤ 3 * Fintype.card E + u) {n : ℕ} (ζ : Fin n → B) + (hζ : Pairwise fun k l => IsUnit (ζ k - ζ l)) (hn : Fintype.card E * (2 * 2 ^ a - 1) < n) + (v : ℤ → Fin n → B) + (hv : ∀ t ∈ recoveryNodes a, 0 < taperMult u h t → ∀ k, + let I := Ideal.span {(2 : B) ^ (p + 8 * h * 2 ^ (a - padicValInt 2 t) + + 8 * Fintype.card E * (a + 1))} + Ideal.Quotient.mk I (v t k) = + (N.map (Ideal.Quotient.mk I)).deformedContraction side + (cauchyValueMatrix ((Ideal.Quotient.mk I).comp (integerHom B hB)) + (Ideal.Quotient.mk I (ζ k)) a t packet slot)) : + ∃ b ∈ B, recoverFromValues B a u h ζ v = (N.contraction : K) + 2 ^ p * b := by + refine exists_recoverFromValues_eq_add B hB N side hslot hinj hu huM hh hh' ζ hζ hn v + fun t ht hq k => ?_ + have h1 := hv t ht hq k + simp only at h1 + rw [← mk_eval_localDeformation, Ideal.Quotient.eq, Ideal.mem_span_singleton'] at h1 + obtain ⟨c, hc⟩ := h1 + exact ⟨c, by rw [mul_comm, hc, add_sub_cancel]⟩ + +end TensorNetwork + +end Cslib.Circuits.Frontier diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/CauchyModes.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/CauchyModes.lean index 77494386cd..556464fe44 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/CauchyModes.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/CauchyModes.lean @@ -585,6 +585,33 @@ noncomputable def cauchyModeSplitting (side : V → Bool) (hsrc : ∀ e, side (N simp rw [group_tgt_eq horient e, hb, hq] +section Fields + +variable (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) + (htgt : ∀ e, side (N.tgt e) = false) + (horient : ∀ e, (group (N.src e), group (N.tgt e)) = + if (slot e).testBit i then (sym (packet e)).swap else sym (packet e)) + {a : ℕ} (hslot : ∀ e, slot e < 2 ^ a) (hia : i < a) (κ : ℤ₍₂₎ →+* R) (ζ : R) {p : ℕ} + (hp : (2 : R) ^ p = 0) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) (hti : (2 : ℤ) ^ i ∣ t) + (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) + +omit [Fintype E] in +@[simp] theorem cauchyModeSplitting_side : + (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').side = side := by + unfold cauchyModeSplitting; rfl + +omit [Fintype E] in +@[simp] theorem cauchyModeSplitting_group : + (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').group = group := by + unfold cauchyModeSplitting; rfl + +omit [Fintype E] in +@[simp] theorem cauchyModeSplitting_packet : + (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').packet = packet := by + unfold cauchyModeSplitting; rfl + +end Fields + omit [Fintype E] [Fintype P] in /-- **The modes have weight `b + 1`:** the coefficient of the mode `(q, y₀, b)` is divisible by `2 ^ (b + 1)`, so the child identity (D.16) applies with these weights. -/ diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildSize.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildSize.lean new file mode 100644 index 0000000000..390e7d8943 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildSize.lean @@ -0,0 +1,131 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion +import Mathlib.Data.Fintype.Sum + +/-! +# Sizes of the child networks + +The child networks of a mode splitting (`TensorNetwork.ModeSplitting.child`, §D.4.6 of +[OpenAI2026ETH], p. 63) keep the invariants of the recursion: + +* an old vertex keeps its degree (`ModeSplitting.card_child_src_inl`); +* a packet vertex has one port for each port of its group in its packet + (`ModeSplitting.card_child_tgt_inr`), so at most `2 L` ports when no two edges share a slot of a + packet of `L` slots (`ModeSplitting.card_child_tgt_inr_le`): the manuscript's + "a new packet vertex has at most `2L ⩽ Δ` ports"; +* the child has one edge per port of its group (`ModeSplitting.card_edges_child`), so with at most + `c M / r` ports per group on each side (Lemma D.3 (3)) it has at most `2 c M / r` edges + (`ModeSplitting.mul_card_edges_child_le`): the manuscript's "at most `6M / r < M / 8` edges". + +## Main results + +* `Cslib.Circuits.Frontier.TensorNetwork.ModeSplitting.card_child_src_inl`: old degrees are + unchanged. +* `Cslib.Circuits.Frontier.TensorNetwork.ModeSplitting.card_child_tgt_inr_le`: packet degrees are + at most `2 L`. +* `Cslib.Circuits.Frontier.TensorNetwork.ModeSplitting.mul_card_edges_child_le`: the edge bound. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Lemma D.3 (pp. 58–59) and + §D.4.6 (p. 63). +-/ + +public section + +namespace Cslib.Circuits.Frontier.TensorNetwork.ModeSplitting + +open Finset + +variable {R V E G P M : Type*} [CommRing R] [Fintype M] {N : TensorNetwork R Bool V E} + {W : Matrix E E R} (S : N.ModeSplitting W G P M) [Fintype E] [DecidableEq E] [DecidableEq P] + [DecidableEq G] [DecidableEq M] [DecidableEq V] + +/-- Counting the elements of a sum type with a property. -/ +private theorem card_filter_sum {A B : Type*} [Fintype A] [Fintype B] (p : A ⊕ B → Prop) + [DecidablePred p] : + #{x | p x} = #{a | p (.inl a)} + #{b | p (.inr b)} := by + rw [← Fintype.card_subtype, Fintype.card_congr Equiv.subtypeSum, Fintype.card_sum, + Fintype.card_subtype, Fintype.card_subtype] + +/-- Counting the elements of a subtype with a property. -/ +private theorem card_filter_subtype {A : Type*} [Fintype A] (Q : A → Prop) [DecidablePred Q] + (f : A → Prop) [DecidablePred f] : #{e : {e // Q e} | f e.1} = #{e | Q e ∧ f e} := by + rw [← Fintype.card_subtype, Fintype.card_congr (Equiv.subtypeSubtypeEquivSubtypeInter Q f), + Fintype.card_subtype] + +/-- **Old degrees are unchanged.** In a child, an old vertex has one port for each edge of `N` +at it. -/ +theorem card_child_src_inl (β : M → ℕ) (g : G) (v : {v // S.group v = g}) : + #{x | (S.child β g).src x = .inl v} = #{e | N.src e = v.1} + #{e | N.tgt e = v.1} := by + rw [card_filter_sum] + congr 1 + · simp only [child_src_inl, Sum.inl.injEq, Subtype.ext_iff] + rw [card_filter_subtype (fun e => S.group (N.src e) = g) (fun e => N.src e = v.1)] + exact congrArg card (filter_congr fun e _ => ⟨fun h => h.2, fun h => ⟨h ▸ v.2, h⟩⟩) + · simp only [child_src_inr, Sum.inl.injEq, Subtype.ext_iff] + rw [card_filter_subtype (fun e => S.group (N.tgt e) = g) (fun e => N.tgt e = v.1)] + exact congrArg card (filter_congr fun e _ => ⟨fun h => h.2, fun h => ⟨h ▸ v.2, h⟩⟩) + +/-- Every edge of a child ends at a packet vertex. -/ +theorem card_child_tgt_inl (β : M → ℕ) (g : G) (v : {v // S.group v = g}) : + #{x | (S.child β g).tgt x = .inl v} = 0 := by + rw [card_filter_sum] + simp + +/-- Every edge of a child starts at an old vertex. -/ +theorem card_child_src_inr (β : M → ℕ) (g : G) (q : P) : + #{x | (S.child β g).src x = .inr q} = 0 := by + rw [card_filter_sum] + simp + +/-- A packet vertex of a child has one port for each port of its group in its packet. -/ +theorem card_child_tgt_inr (β : M → ℕ) (g : G) (q : P) : + #{x | (S.child β g).tgt x = .inr q} = + #{e | S.group (N.src e) = g ∧ S.packet e = q} + + #{e | S.group (N.tgt e) = g ∧ S.packet e = q} := by + rw [card_filter_sum] + simp only [child_tgt_inl, child_tgt_inr, Sum.inr.injEq] + rw [card_filter_subtype (fun e => S.group (N.src e) = g) (fun e => S.packet e = q), + card_filter_subtype (fun e => S.group (N.tgt e) = g) (fun e => S.packet e = q)] + +omit [DecidableEq E] [DecidableEq G] [DecidableEq M] [DecidableEq V] in +/-- At most `L` edges lie in a packet of `L` slots when no two share a slot. -/ +private theorem card_packet_le {a : ℕ} {slot : E → ℕ} (hslot : ∀ e, slot e < 2 ^ a) + (hinj : ∀ e e', S.packet e = S.packet e' → slot e = slot e' → e = e') (q : P) + (f : E → Prop) [DecidablePred f] : #{e | f e ∧ S.packet e = q} ≤ 2 ^ a := by + calc #{e | f e ∧ S.packet e = q} ≤ #(range (2 ^ a)) := + card_le_card_of_injOn slot (fun e _ => by simpa using hslot e) fun e he e' he' h => by + simp only [coe_filter, Set.mem_ofPred_eq, mem_univ, true_and] at he he' + exact hinj e e' (he.2.trans he'.2.symm) h + _ = 2 ^ a := card_range _ + +/-- **Packet degrees are at most `2 L`** in a child, for packets of `L = 2 ^ a` slots with no two +edges in the same slot of a packet. -/ +theorem card_child_tgt_inr_le {a : ℕ} {slot : E → ℕ} (hslot : ∀ e, slot e < 2 ^ a) + (hinj : ∀ e e', S.packet e = S.packet e' → slot e = slot e' → e = e') (β : M → ℕ) (g : G) + (q : P) : #{x | (S.child β g).tgt x = .inr q} ≤ 2 * 2 ^ a := by + rw [card_child_tgt_inr, two_mul] + exact add_le_add (S.card_packet_le hslot hinj q _) (S.card_packet_le hslot hinj q _) + +omit [DecidableEq E] [DecidableEq P] [DecidableEq M] [DecidableEq V] in +/-- **The edge bound.** If, in the group `g`, the rows have at most `c M / r` ports and so do the +columns, then the child of `g` has at most `2 c M / r` edges. With Lemma D.3 (3), `c = 3` and +`r = 64`, this is the manuscript's `6M / r < M / 8`. -/ +theorem mul_card_edges_child_le {r c : ℕ} (g : G) + (hrow : r * #{e | S.group (N.src e) = g} ≤ c * Fintype.card E) + (hcol : r * #{e | S.group (N.tgt e) = g} ≤ c * Fintype.card E) : + r * Fintype.card ({e // S.group (N.src e) = g} ⊕ {e // S.group (N.tgt e) = g}) ≤ + 2 * c * Fintype.card E := by + rw [card_edges_child, mul_add] + linarith + +end Cslib.Circuits.Frontier.TensorNetwork.ModeSplitting diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkActiveCall.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkActiveCall.lean new file mode 100644 index 0000000000..dd0a261b1e --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkActiveCall.lean @@ -0,0 +1,109 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ActiveCall + +/-! +# Tests for one active call of the contraction recursion + +* **The packing bridge.** The two-edge network of the Cauchy-mode tests with a hand-made packing + into one packet of two slots (`a = 1`, `r = 2`) satisfies the orientation hypothesis of the + child identity, its groups have one port per side, and the children of a mode splitting along + its packets have packet degree at most `2 L`. +* **Non-vacuity of the active call.** A one-edge network over `ℤ_(2)` (`M = 1`, `a = 0`, so + `M (2 L - 1) = 1`), with the two interpolation points `0` and `1`, `u = 1` and `h = 3`: values + equal to `F(t + α ζ)` recover the contraction modulo every `2 ^ p`. +-/ + +@[expose] public section + +namespace ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.ActiveCall + +open Cslib.Circuits.Frontier TensorNetwork + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +/-! ### The packing bridge -/ + +/-- Edge `e` runs from row `e` to column `e + 2`, with the all-one tensors. -/ +def net : TensorNetwork ℤ Bool (Fin 4) (Fin 2) where + src e := ⟨e.val, by omega⟩ + tgt e := ⟨e.val + 2, by omega⟩ + tensor _ _ := 1 + tensor_congr _ _ _ _ := rfl + +/-- The rows `0, 1`. -/ +def side (v : Fin 4) : Bool := decide (v.val < 2) + +/-- One packet of two slots; edge `e` in slot `e`. Row `1` and column `2` have symbol `1`, so +edge `0` (slot `0`) runs from symbol `0` to `1` and edge `1` (slot `1`) the other way. -/ +def packing : Cslib.PacketPacking.Packing net.src net.tgt 1 2 where + rowLabel v _ := if v = 1 then 1 else 0 + colLabel v _ := if v = 2 then 1 else 0 + numPackets := 1 + packet _ := 0 + slot e := ⟨e.val, by have := e.isLt; simp; omega⟩ + symbols _ _ := (0, 1) + injective e e' h := by fin_cases e <;> fin_cases e' <;> simp_all + labels_eq e i := by fin_cases e <;> fin_cases i <;> decide + +example (e : Fin 2) : + (packingGroup net side packing 0 (net.src e), packingGroup net side packing 0 (net.tgt e)) = + if (packing.slot e : ℕ).testBit 0 then (packing.symbols (packing.packet e) 0).swap + else packing.symbols (packing.packet e) 0 := + packingGroup_src_tgt net side (fun e => by fin_cases e <;> decide) + (fun e => by fin_cases e <;> decide) packing 0 e + +example : Finset.card {e | packingGroup net side packing 0 (net.src e) = 1} = 1 := by + rw [card_packingGroup_src net side (fun e => by fin_cases e <;> decide)] + decide + +/-- **Child invariants.** For any mode splitting of `net` along the packets of `packing`, a packet +vertex of a child has at most `2 L = 4` ports. -/ +example {M : Type} [Fintype M] [DecidableEq M] {W : Matrix (Fin 2) (Fin 2) ℤ} + (S : net.ModeSplitting W (Fin 2) (Fin 1) M) (hS : S.packet = packing.packet) (β : M → ℕ) + (g : Fin 2) (q : Fin 1) : + Finset.card {x | (S.child β g).tgt x = .inr q} ≤ 2 * 2 ^ 1 := + S.card_child_tgt_inr_le (packing_slot_lt packing) + (by rw [hS]; exact packing_slot_injective packing) β g q + +/-! ### Non-vacuity of the active call -/ + +/-- One edge from row `0` to column `1`, with tensor `(1, 2)` at the row and `(1, 1)` at the +column. -/ +def net₁ : TensorNetwork ℤ₍₂₎ Bool (Fin 2) (Fin 1) where + src _ := 0 + tgt _ := 1 + tensor v x := if v = 0 ∧ x 0 then 2 else 1 + tensor_congr v x y h := by rw [h 0 (by fin_cases v <;> simp)] + +/-- The interpolation points `0, 1`, whose difference is a unit. -/ +def ζ (k : Fin 2) : ℤ₍₂₎ := k.val + +theorem hζ : Pairwise fun k l => IsUnit (ζ k - ζ l) := by + intro k l hkl + fin_cases k <;> fin_cases l + · exact absurd rfl hkl + · simp [ζ] + · simp [ζ] + · exact absurd rfl hkl + +/-- Exact values of `F(t + α ζ)` recover the contraction modulo every `2 ^ p`. -/ +example (p : ℕ) : ∃ b ∈ ℤ₍₂₎, + recoverFromValues ℤ₍₂₎ 0 1 3 ζ (fun t k => + (net₁.localDeformation (fun v => decide (v = 0)) (integerHom ℤ₍₂₎ fun _ hc => hc) 0 t + (fun _ => ()) (fun _ => 0)).eval (ζ k)) = + ((net₁.contraction : ℤ₍₂₎) : ℚ) + 2 ^ p * b := + exists_recoverFromValues_eq_add_of_mk (K := ℚ) ℤ₍₂₎ (fun _ hc => hc) net₁ _ + (fun _ => by decide) (fun e e' _ _ => Subsingleton.elim e e') le_rfl le_rfl (by simp) + (by simp) ζ hζ (by simp) _ fun t _ _ k => + mk_eval_localDeformation net₁ _ _ 0 t _ _ _ (ζ k) + +end ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.ActiveCall From 8e242417d972de64f33df2c494274633b213a50a Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:19:14 -0400 Subject: [PATCH 013/129] test: make the tapered recovery test module public Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Circuit/Frontier/TensorNetworkTaperedRecovery.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkTaperedRecovery.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkTaperedRecovery.lean index af21199d2f..58cea84af6 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkTaperedRecovery.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkTaperedRecovery.lean @@ -40,7 +40,7 @@ example : 2 ^ 1 * (3 * 2) ≤ ∑ t ∈ recoveryNodes 1, taperMult 2 3 t := /-- **Non-vacuity.** For `F = X` (degree `1 = M (2 L - 1)` with `M = 1`, `L = 1`), `u = 1` and `h = 3`, exact jets recover `F(0)` modulo every `2 ^ p`. -/ -example (p : ℕ) : +theorem exists_taperedRecovery_X (p : ℕ) : ∃ b ∈ ℤ₍₂₎, taperedRecovery 0 1 3 (fun t => (X : ℚ[X]).comp (C (t : ℚ) + C (2 ^ (0 + 1)) * X)) = (X : ℚ[X]).eval 0 + 2 ^ p * b := exists_taperedRecovery_eq_add_of_params (K := ℚ) (M := 1) ℤ₍₂₎ (fun c hc => hc) le_rfl le_rfl From 02ef13c857b9b8fd1f5aa83e5093c6fcb25e1f24 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:19:45 -0400 Subject: [PATCH 014/129] feat(GenericRAM/Native): depth-first stack recursion with native bounds A generic recursion scheme (leaf test, leaf step, start/next/absorb/finish) run by an explicit stack machine. Derivations `Eval I x y n h` (n nodes, h stack levels) are evaluated in 2n - 1 steps through states with at most h open frames. One native certificate runs the machine for a natively computed fuel; its time is (fuel + 1) times a polynomial in (h+1)(F+1) plus the leaf time, and its space a polynomial plus the leaf space. Also the node count of a recursion tree and its 2^O(n/sqrt(log n)) bound (Foundations/Data/Nat/RecursionTree.lean), for the call count of [OpenAI2026ETH, Appendix D.4.7]. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 2 + .../GenericRAM/Native/StackRecursion.lean | 778 ++++++++++++++++++ .../Foundations/Data/Nat/RecursionTree.lean | 170 ++++ ComplexityTests/StackRecursion.lean | 257 ++++++ 4 files changed, 1207 insertions(+) create mode 100644 Complexity/Computability/GenericRAM/Native/StackRecursion.lean create mode 100644 Complexity/Foundations/Data/Nat/RecursionTree.lean create mode 100644 ComplexityTests/StackRecursion.lean diff --git a/Complexity.lean b/Complexity.lean index 69ba190535..e3af2ca51c 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1457,6 +1457,7 @@ public import Complexity.Computability.GenericRAM.Native.HeavyBranchFold public import Complexity.Computability.GenericRAM.Native.IndexFold public import Complexity.Computability.GenericRAM.Native.MeasurementFold public import Complexity.Computability.GenericRAM.Native.Nat +public import Complexity.Computability.GenericRAM.Native.StackRecursion public import Complexity.Computability.GenericRAM.Native.SubsetPairFold public import Complexity.Computability.GenericRAM.Signed public import Complexity.Computability.GenericRAM.Signed.Fits @@ -2726,6 +2727,7 @@ public import Complexity.Foundations.Data.Nat.Prime public import Complexity.Foundations.Data.Nat.Primrec public import Complexity.Foundations.Data.Nat.Radix public import Complexity.Foundations.Data.Nat.Radix.Measurement +public import Complexity.Foundations.Data.Nat.RecursionTree public import Complexity.Foundations.Data.Nat.ResidueSum public import Complexity.Foundations.Data.Nat.SqrtLog public import Complexity.Foundations.Data.Nat.SqrtRatio diff --git a/Complexity/Computability/GenericRAM/Native/StackRecursion.lean b/Complexity/Computability/GenericRAM/Native/StackRecursion.lean new file mode 100644 index 0000000000..c2fd37dd11 --- /dev/null +++ b/Complexity/Computability/GenericRAM/Native/StackRecursion.lean @@ -0,0 +1,778 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.GenericRAM.Native.Combinators +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.List +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Option +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Length +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListLookup +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeOptionPrimitives + +/-! +# Depth-first recursion with an explicit stack + +A *recursion scheme* (`StackRecursion.Scheme`) evaluates node descriptions `x : X` to values +`y : Y`. A node is either a leaf, evaluated by `leaf`, or a call, which keeps an accumulator +`a : A`: starting from `start x`, it asks `next x a` for its next child, folds the child's value +into the accumulator with `absorb`, and when `next` returns `none` produces its value with +`finish`. Children may depend on the values of earlier children through the accumulator. + +**The machine** (`Scheme.step`). Its state is the stack of open calls, innermost first, each +with its accumulator, and a register holding a pending call or a returned value. Each step +performs one call or one return. A derivation `Scheme.Eval I x y n h`, with `n` nodes and `h` +stack levels, makes the machine reach `y` in `2 * n - 1` steps (`Scheme.Eval.run`), through +states with at most `h` open calls whose nodes, frames and values satisfy the invariant `I` +(`Scheme.Eval.good_iterate`). + +**The native certificate** (`Scheme.certificate`). The leaf step is an arbitrary native +certificate; the other components are polynomial-time. One fixed machine runs the stack machine +for a natively computed number `fuel a` of steps (`NativeCertificate.iterate`) and returns the +register. When `2 * n - 1 ≤ fuel a` the result is `y` (`Scheme.run_eq_some`). If every node, +frame and value on the derivation has encoded length at most `F` and every leaf is evaluated +within time `TL` and space `SL`, the time is at most `fuel a + 1` times +`K ((h + 1) (F + 1)) ^ K + TL` and the workspace at most `K ((h + 1) (F + 1)) ^ K + SL`, up to +the cost of computing the fuel and the root and of the binary clock, for one constant `K` of the +scheme (`Scheme.certificate_le`). Only the stack is kept, so the workspace grows with the depth, +not with the number of nodes. The number of nodes of a recursion tree is bounded in +`Foundations/Data/Nat/RecursionTree.lean`. + +This is the depth-first evaluation of the recursion in the proof of Theorem D.1 of +[OpenAI2026ETH] (Appendix D.4.7, p. 64), which keeps one sum over mode tuples and one product +over groups per stack level. The scheme is generic: the recursion of Appendix D.4 is one +instance, with its leaves evaluated by direct summation. + +## Main definitions + +* `Cslib.GenericRAM.StackRecursion.Scheme`: a recursion scheme. +* `Cslib.GenericRAM.StackRecursion.Invariant`: predicates on the nodes, frames and values met. +* `Cslib.GenericRAM.StackRecursion.Scheme.Loop`, `Cslib.GenericRAM.StackRecursion.Scheme.Eval`: + derivations of the loop of a call and of the value of a node, with their node counts and + stack depths. +* `Cslib.GenericRAM.StackRecursion.Scheme.step`, `Cslib.GenericRAM.StackRecursion.Scheme.run`: + the stack machine and its clocked run. +* `Cslib.GenericRAM.StackRecursion.Scheme.PolyTime`: the polynomial-time components. +* `Cslib.GenericRAM.StackRecursion.Scheme.certificate`: one native machine for the clocked run. + +## Main results + +* `Cslib.GenericRAM.StackRecursion.Scheme.Eval.run`: a derivation with `n` nodes is evaluated in + `2 * n - 1` steps. +* `Cslib.GenericRAM.StackRecursion.Scheme.run_eq_some`: enough fuel gives the value. +* `Cslib.GenericRAM.StackRecursion.Scheme.certificate_le`: time `fuel + 1` times a polynomial in + `(h + 1) * (F + 1)` plus the leaf time, and workspace a polynomial plus the leaf space. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Appendix D.4.7 (p. 64). +-/ + +@[expose] public section + +namespace Cslib.GenericRAM.StackRecursion + +open Turing.MultiTapeTM + +/-- A *recursion scheme*. A node `x` is a leaf when `isLeaf x`, with value `leaf x`. Otherwise +it is a call: its accumulator starts at `start x`; while `next x a` returns a child, the child is +evaluated and its value folded into the accumulator by `absorb`; when `next x a` is `none` the +call returns `finish x a`. -/ +structure Scheme (X A Y : Type) where + /-- Whether a node is a leaf. -/ + isLeaf : X → Bool + /-- The value of a leaf. -/ + leaf : X → Y + /-- The initial accumulator of a call. -/ + start : X → A + /-- The next child of a call with the given accumulator, or `none` when it is done. -/ + next : X → A → Option X + /-- Fold the value of the current child into the accumulator. -/ + absorb : X → A → Y → A + /-- The value of a call from its final accumulator. -/ + finish : X → A → Y + +/-- Predicates on what a derivation meets: the nodes evaluated, the frames pushed (a call with +its accumulator while one of its children is evaluated) and the values returned. -/ +structure Invariant (X A Y : Type) where + /-- Every node evaluated satisfies `node`. -/ + node : X → Prop + /-- Every pushed frame satisfies `frame`. -/ + frame : X → A → Prop + /-- Every returned value satisfies `value`. -/ + value : Y → Prop + +/-- A state of the stack machine: the open calls with their accumulators, innermost first, a +pending call and a returned value. -/ +abbrev State (X A Y : Type) := List (X × A) × Option X × Option Y + +namespace Scheme + +variable {X A Y : Type} (R : Scheme X A Y) + +/-- Continue the call `x` with accumulator `a` above the stack `st`: push its frame and call its +next child, or return its value when it has none. -/ +def resume (x : X) (a : A) (st : List (X × A)) : State X A Y := + match R.next x a with + | none => (st, none, some (R.finish x a)) + | some c => ((x, a) :: st, some c, none) + +/-- **One step of the stack machine.** A pending leaf is evaluated; a pending call is started; a +returned value is absorbed by the innermost open call, which then continues. A value returned +with no open call is final. -/ +def step : State X A Y → State X A Y + | (st, some x, _) => if R.isLeaf x then (st, none, some (R.leaf x)) else R.resume x (R.start x) st + | ((x, a) :: st, none, some y) => R.resume x (R.absorb x a y) st + | σ => σ + +theorem resume_of_next_none {x : X} {a : A} (st : List (X × A)) (h : R.next x a = none) : + R.resume x a st = (st, none, some (R.finish x a)) := by + simp [resume, h] + +theorem resume_of_next_some {x c : X} {a : A} (st : List (X × A)) (h : R.next x a = some c) : + R.resume x a st = ((x, a) :: st, some c, none) := by + simp [resume, h] + +theorem step_leaf {x : X} (st : List (X × A)) (r : Option Y) (h : R.isLeaf x = true) : + R.step (st, some x, r) = (st, none, some (R.leaf x)) := by + simp [step, h] + +theorem step_call {x : X} (st : List (X × A)) (r : Option Y) (h : R.isLeaf x = false) : + R.step (st, some x, r) = R.resume x (R.start x) st := by + simp [step, h] + +theorem step_return (x : X) (a : A) (st : List (X × A)) (y : Y) : + R.step ((x, a) :: st, none, some y) = R.resume x (R.absorb x a y) st := rfl + +/-- A value returned with no open call is final. -/ +@[simp] theorem step_final (y : Y) : R.step ([], none, some y) = ([], none, some y) := rfl + +/-- **The loop of a call.** `R.Loop I x a a' n h`: the call `x`, from accumulator `a`, evaluates +its remaining children with `n` nodes in total and ends with accumulator `a'`, using at most `h` +stack levels above its own frame. Every frame pushed, child evaluated and value returned +satisfies `I`. -/ +inductive Loop (I : Invariant X A Y) : X → A → A → ℕ → ℕ → Prop + /-- The call has no further child. -/ + | done {x : X} {a : A} : R.next x a = none → Loop I x a a 0 0 + /-- The next child is a leaf. -/ + | leaf {x c : X} {a a' : A} {n h : ℕ} : I.frame x a → R.next x a = some c → I.node c → + R.isLeaf c = true → I.value (R.leaf c) → Loop I x (R.absorb x a (R.leaf c)) a' n h → + Loop I x a a' (n + 1) h + /-- The next child is a call, whose own loop ends with accumulator `b`. -/ + | call {x c : X} {a b a' : A} {n₁ h₁ n₂ h₂ : ℕ} : I.frame x a → R.next x a = some c → + I.node c → R.isLeaf c = false → Loop I c (R.start c) b n₁ h₁ → I.value (R.finish c b) → + Loop I x (R.absorb x a (R.finish c b)) a' n₂ h₂ → + Loop I x a a' (n₁ + 1 + n₂) (max (h₁ + 1) h₂) + +/-- **A derivation of the value of a node.** `R.Eval I x y n h`: the node `x` has value `y`, +computed with `n` nodes and at most `h` open calls, and every node, frame and value met satisfies +`I`. A leaf has one node and no open call; a call has one more node and one more level than its +loop. -/ +def Eval (I : Invariant X A Y) (x : X) (y : Y) (n h : ℕ) : Prop := + I.node x ∧ I.value y ∧ + ((R.isLeaf x = true ∧ y = R.leaf x ∧ n = 1 ∧ h = 0) ∨ + (R.isLeaf x = false ∧ ∃ b n' h', R.Loop I x (R.start x) b n' h' ∧ y = R.finish x b ∧ + n = n' + 1 ∧ h = h' + 1)) + +variable {R} {I : Invariant X A Y} + +/-- A leaf is evaluated by `leaf`. -/ +theorem Eval.leaf {x : X} (hx : I.node x) (hl : R.isLeaf x = true) (hv : I.value (R.leaf x)) : + R.Eval I x (R.leaf x) 1 0 := + ⟨hx, hv, .inl ⟨hl, rfl, rfl, rfl⟩⟩ + +/-- A call is evaluated by its loop, from `start`, and `finish`. -/ +theorem Eval.call {x : X} {b : A} {n h : ℕ} (hx : I.node x) (hl : R.isLeaf x = false) + (hloop : R.Loop I x (R.start x) b n h) (hv : I.value (R.finish x b)) : + R.Eval I x (R.finish x b) (n + 1) (h + 1) := + ⟨hx, hv, .inr ⟨hl, b, n, h, hloop, rfl, rfl, rfl⟩⟩ + +/-- **The loop absorbs an evaluated child.** -/ +theorem Loop.child {x c : X} {a a' : A} {y : Y} {n₁ h₁ n₂ h₂ : ℕ} (hf : I.frame x a) + (hn : R.next x a = some c) (hc : R.Eval I c y n₁ h₁) + (hrest : R.Loop I x (R.absorb x a y) a' n₂ h₂) : + R.Loop I x a a' (n₁ + n₂) (max h₁ h₂) := by + obtain ⟨hnode, hval, ⟨hl, rfl, rfl, rfl⟩ | ⟨hl, b, n', h', hloop, rfl, rfl, rfl⟩⟩ := hc + · simpa [Nat.add_comm] using Loop.leaf hf hn hnode hl hval hrest + · exact Loop.call hf hn hnode hl hloop hval hrest + +/-- Every derivation has at least one node. -/ +theorem Eval.one_le {x : X} {y : Y} {n h : ℕ} (he : R.Eval I x y n h) : 1 ≤ n := by + obtain ⟨-, -, ⟨-, -, rfl, -⟩ | ⟨-, -, -, -, -, -, rfl, -⟩⟩ := he <;> omega + +/-! ### The machine follows the derivation -/ + +/-- **A loop is run in two steps per node**, ending with the value of the call returned to the +stack below it. -/ +theorem Loop.run {x : X} {a a' : A} {n h : ℕ} (hl : R.Loop I x a a' n h) (st : List (X × A)) : + R.step^[2 * n] (R.resume x a st) = (st, none, some (R.finish x a')) := by + induction hl generalizing st with + | done hn => exact R.resume_of_next_none st hn + | @leaf x c a a' n h _ hn _ hl _ _ ih => + rw [show 2 * (n + 1) = 2 * n + 1 + 1 by ring] + simp only [Function.iterate_add_apply, Function.iterate_one] + rw [R.resume_of_next_some st hn, R.step_leaf _ _ hl, R.step_return, ih] + | @call x c a b a' n₁ h₁ n₂ h₂ _ hn _ hl _ _ _ ih₁ ih₂ => + rw [show 2 * (n₁ + 1 + n₂) = 2 * n₂ + 1 + 2 * n₁ + 1 by ring] + simp only [Function.iterate_add_apply, Function.iterate_one] + rw [R.resume_of_next_some st hn, R.step_call _ _ hl, ih₁, R.step_return, ih₂] + +/-- **A node with `n` nodes is evaluated in `2 * n - 1` steps**, above any stack. -/ +theorem Eval.run {x : X} {y : Y} {n h : ℕ} (he : R.Eval I x y n h) (st : List (X × A)) : + R.step^[2 * n - 1] (st, some x, none) = (st, none, some y) := by + obtain ⟨-, -, ⟨hl, rfl, rfl, rfl⟩ | ⟨hl, b, n', h', hloop, rfl, rfl, rfl⟩⟩ := he + · exact R.step_leaf st none hl + · rw [show 2 * (n' + 1) - 1 = 2 * n' + 1 by omega, Function.iterate_add_apply, + Function.iterate_one, R.step_call _ _ hl, hloop.run] + +/-! ### The states met -/ + +/-- A state is *good* for `I` and a depth `H` when at most `H` calls are open, every open frame +satisfies `I`, and the register holds either a pending node or a returned value satisfying +`I`. -/ +def Good (I : Invariant X A Y) (H : ℕ) (σ : State X A Y) : Prop := + σ.1.length ≤ H ∧ (∀ f ∈ σ.1, I.frame f.1 f.2) ∧ + ((∃ x, σ.2 = (some x, none) ∧ I.node x) ∨ (∃ y, σ.2 = (none, some y) ∧ I.value y)) + +theorem Good.mono {H H' : ℕ} {σ : State X A Y} (hσ : Good I H σ) (h : H ≤ H') : Good I H' σ := + ⟨hσ.1.trans h, hσ.2⟩ + +private theorem good_push {x : X} {a : A} {st : List (X × A)} {H : ℕ} {r : Option X × Option Y} + (hst : ∀ f ∈ st, I.frame f.1 f.2) (hf : I.frame x a) (hH : st.length + 1 ≤ H) + (hr : (∃ x, r = (some x, none) ∧ I.node x) ∨ (∃ y, r = (none, some y) ∧ I.value y)) : + Good I H ((x, a) :: st, r) := by + refine ⟨by simpa using hH, ?_, hr⟩ + intro f hf' + rcases List.mem_cons.mp hf' with rfl | hf' + · exact hf + · exact hst f hf' + +/-- **The states of a loop are good**, strictly before it returns. -/ +theorem Loop.good {x : X} {a a' : A} {n h : ℕ} (hl : R.Loop I x a a' n h) {st : List (X × A)} + (hst : ∀ f ∈ st, I.frame f.1 f.2) : + ∀ j < 2 * n, Good I (st.length + h + 1) (R.step^[j] (R.resume x a st)) := by + induction hl generalizing st with + | done _ => intro j hj; omega + | @leaf x c a a' n h hf hn hc hl hv _ ih => + intro j hj + rw [R.resume_of_next_some st hn] + rcases j with _ | _ | j + · exact good_push hst hf (by omega) (.inl ⟨c, rfl, hc⟩) + · rw [Function.iterate_one, R.step_leaf _ _ hl] + exact good_push hst hf (by omega) (.inr ⟨_, rfl, hv⟩) + · rw [show j + 1 + 1 = j + 2 by ring, Function.iterate_add_apply, + show R.step^[2] ((x, a) :: st, some c, none) = R.resume x (R.absorb x a (R.leaf c)) st by + simp only [show (2 : ℕ) = 1 + 1 from rfl, Function.iterate_add_apply, + Function.iterate_one] + rw [R.step_leaf _ _ hl, R.step_return]] + exact ih hst j (by omega) + | @call x c a b a' n₁ h₁ n₂ h₂ hf hn hc hl hloop₁ hv _ ih₁ ih₂ => + intro j hj + rw [R.resume_of_next_some st hn] + have hst' : ∀ f ∈ (x, a) :: st, I.frame f.1 f.2 := fun f hf' => by + rcases List.mem_cons.mp hf' with rfl | hf' + · exact hf + · exact hst f hf' + have hbegin : R.step^[1] ((x, a) :: st, some c, none) = R.resume c (R.start c) ((x, a) :: st) := + by rw [Function.iterate_one, R.step_call _ _ hl] + rcases j with _ | j + · exact good_push hst hf (by omega) (.inl ⟨c, rfl, hc⟩) + rcases Nat.lt_or_ge j (2 * n₁) with hj₁ | hj₁ + · rw [show j + 1 = j + 1 from rfl, Function.iterate_add_apply, hbegin] + exact (ih₁ hst' j hj₁).mono (by simp only [List.length_cons]; omega) + obtain ⟨k, rfl⟩ := Nat.exists_eq_add_of_le hj₁ + rw [show 2 * n₁ + k + 1 = k + 2 * n₁ + 1 by ring, Function.iterate_add_apply, + Function.iterate_add_apply, hbegin, hloop₁.run] + rcases k with _ | k + · exact good_push hst hf (by omega) (.inr ⟨_, rfl, hv⟩) + · rw [show k + 1 = k + 1 from rfl, Function.iterate_add_apply, Function.iterate_one, + R.step_return] + exact (ih₂ hst k (by omega)).mono (by omega) + +/-- **The states of an evaluation are good**, up to and including its returned value. -/ +theorem Eval.good {x : X} {y : Y} {n h : ℕ} (he : R.Eval I x y n h) {st : List (X × A)} + (hst : ∀ f ∈ st, I.frame f.1 f.2) : + ∀ j ≤ 2 * n - 1, Good I (st.length + h) (R.step^[j] (st, some x, none)) := by + obtain ⟨hx, hy, ⟨hl, rfl, rfl, rfl⟩ | ⟨hl, b, n', h', hloop, rfl, rfl, rfl⟩⟩ := he + · intro j hj + rcases j with _ | j + · exact ⟨by simp, hst, .inl ⟨x, rfl, hx⟩⟩ + · rw [show j = 0 by omega, Function.iterate_one, R.step_leaf _ _ hl] + exact ⟨by simp, hst, .inr ⟨_, rfl, hy⟩⟩ + · intro j hj + rcases j with _ | j + · exact ⟨by simp, hst, .inl ⟨x, rfl, hx⟩⟩ + rw [Function.iterate_add_apply, Function.iterate_one, R.step_call _ _ hl] + rcases Nat.lt_or_ge j (2 * n') with hj' | hj' + · exact (hloop.good hst j hj').mono (by omega) + · rw [show j = 2 * n' by omega, hloop.run] + exact ⟨by simp, hst, .inr ⟨_, rfl, hy⟩⟩ + +/-- **Every state of a run from an empty stack is good**, including the final state, which no +step changes. -/ +theorem Eval.good_iterate {x : X} {y : Y} {n h : ℕ} (he : R.Eval I x y n h) (j : ℕ) : + Good I h (R.step^[j] ([], some x, none)) := by + rcases le_or_gt j (2 * n - 1) with hj | hj + · simpa using he.good (st := []) (by simp) j hj + · obtain ⟨k, rfl⟩ := Nat.exists_eq_add_of_lt hj + rw [show 2 * n - 1 + k + 1 = (k + 1) + (2 * n - 1) by omega, Function.iterate_add_apply, + he.run, Function.iterate_fixed (R.step_final y)] + exact ⟨by simp, by simp, .inr ⟨y, rfl, he.2.1⟩⟩ + +/-! ### The native machine -/ + +section Native + +variable (R) {encX : X ↪ List Bool} {encA : A ↪ List Bool} {encY : Y ↪ List Bool} + +/-- The encoding of a machine state: the list of open frames, each a pair, then the optional +pending node and the optional returned value. -/ +abbrev encState (encX : X ↪ List Bool) (encA : A ↪ List Bool) (encY : Y ↪ List Bool) : + State X A Y ↪ List Bool := + encPair (encList (encPair encX encA)) (encPair (encOption encX) (encOption encY)) + +/-- **The polynomial-time components** of a scheme: everything but the leaf step. -/ +structure PolyTime (encX : X ↪ List Bool) (encA : A ↪ List Bool) (encY : Y ↪ List Bool) : + Prop where + /-- The leaf test. -/ + isLeaf : ComputableInPolyTime R.isLeaf encX encBool + /-- The initial accumulator. -/ + start : ComputableInPolyTime R.start encX encA + /-- Child generation. -/ + next : ComputableInPolyTime (fun p : X × A => R.next p.1 p.2) (encPair encX encA) + (encOption encX) + /-- Absorbing a child's value. -/ + absorb : ComputableInPolyTime (fun p : (X × A) × Y => R.absorb p.1.1 p.1.2 p.2) + (encPair (encPair encX encA) encY) encA + /-- The value of a call. -/ + finish : ComputableInPolyTime (fun p : X × A => R.finish p.1 p.2) (encPair encX encA) encY + +/-- Whether the register holds a pending leaf. -/ +def isLeafCall (σ : State X A Y) : Bool := σ.2.1.elim false R.isLeaf + +/-- Evaluate a pending node as a leaf, keeping the stack. -/ +def leafStep (σ : State X A Y) : State X A Y := + (σ.1, none, σ.2.1.elim none fun x => some (R.leaf x)) + +/-- Every step but the evaluation of a leaf. -/ +def polyStep (σ : State X A Y) : State X A Y := + σ.2.1.elim + (σ.2.2.elim σ fun y => σ.1.head?.elim σ fun f => R.resume f.1 (R.absorb f.1 f.2 y) σ.1.tail) + (fun x => R.resume x (R.start x) σ.1) + +/-- A step evaluates a leaf, or takes a polynomial-time step. -/ +theorem step_eq_ite (σ : State X A Y) : + R.step σ = if R.isLeafCall σ then R.leafStep σ else R.polyStep σ := by + rcases σ with ⟨_ | ⟨⟨x, a⟩, st⟩, _ | c, _ | y⟩ <;> + simp only [step, isLeafCall, leafStep, polyStep, Option.elim, List.head?, List.tail, + Bool.false_eq_true, ite_false] <;> rfl + +variable {R} + +/-- Continuing a call is polynomial-time. -/ +theorem resume_polyTime (h : R.PolyTime encX encA encY) : + ComputableInPolyTime (fun q : (X × A) × List (X × A) => R.resume q.1.1 q.1.2 q.2) + (encPair (encPair encX encA) (encList (encPair encX encA))) (encState encX encA encY) := by + have hfr := ComputableInPolyTime.fst (encPair encX encA) (encList (encPair encX encA)) + have hst := ComputableInPolyTime.snd (encPair encX encA) (encList (encPair encX encA)) + have hfallback : ComputableInPolyTime + (fun q : (X × A) × List (X × A) => (q.2, (none : Option X), some (R.finish q.1.1 q.1.2))) + (encPair (encPair encX encA) (encList (encPair encX encA))) (encState encX encA encY) := + hst.prod ((ComputableInPolyTime.const none).prod + ((ComputableInPolyTime.optionSome encY).comp (h.finish.comp hfr))) + have hbody : ComputableInPolyTime + (fun p : ((X × A) × List (X × A)) × X => (p.1.1 :: p.1.2, some p.2, (none : Option Y))) + (encPair (encPair (encPair encX encA) (encList (encPair encX encA))) encX) + (encState encX encA encY) := by + have hq := ComputableInPolyTime.fst + (encPair (encPair encX encA) (encList (encPair encX encA))) encX + have hc := ComputableInPolyTime.snd + (encPair (encPair encX encA) (encList (encPair encX encA))) encX + exact (ComputableInPolyTime.cons' (hfr.comp hq) (hst.comp hq)).prod + (((ComputableInPolyTime.optionSome encX).comp hc).prod (ComputableInPolyTime.const none)) + have helim := ComputableInPolyTime.optionElimWithInput + (value := fun q : (X × A) × List (X × A) => R.next q.1.1 q.1.2) + (fallback := fun q => (q.2, (none : Option X), some (R.finish q.1.1 q.1.2))) + (body := fun q c => (q.1 :: q.2, some c, (none : Option Y))) (h.next.comp hfr) hfallback hbody + refine helim.of_enc_eq fun q => ?_ + rcases q with ⟨⟨x, a⟩, st⟩ + change encState encX encA encY (R.resume x a st) = + encState encX encA encY ((R.next x a).elim (st, none, some (R.finish x a)) + fun c => ((x, a) :: st, some c, none)) + rw [resume] + cases R.next x a <;> rfl + +/-- The leaf test on the register is polynomial-time. -/ +theorem isLeafCall_polyTime (h : R.PolyTime encX encA encY) : + ComputableInPolyTime R.isLeafCall (encState encX encA encY) encBool := by + have hreg := (ComputableInPolyTime.fst (encOption encX) (encOption encY)).comp + (ComputableInPolyTime.snd (encList (encPair encX encA)) + (encPair (encOption encX) (encOption encY))) + exact ComputableInPolyTime.optionElimWithInput (body := fun _ x => R.isLeaf x) hreg + (ComputableInPolyTime.const false) + (h.isLeaf.comp (ComputableInPolyTime.snd (encState encX encA encY) encX)) + +/-- Every step but the evaluation of a leaf is polynomial-time. -/ +theorem polyStep_polyTime (h : R.PolyTime encX encA encY) : + ComputableInPolyTime R.polyStep (encState encX encA encY) (encState encX encA encY) := by + have hstack : ComputableInPolyTime (fun σ : State X A Y => σ.1) (encState encX encA encY) + (encList (encPair encX encA)) := + ComputableInPolyTime.fst (encList (encPair encX encA)) + (encPair (encOption encX) (encOption encY)) + have hreg : ComputableInPolyTime (fun σ : State X A Y => σ.2) (encState encX encA encY) + (encPair (encOption encX) (encOption encY)) := + ComputableInPolyTime.snd (encList (encPair encX encA)) + (encPair (encOption encX) (encOption encY)) + have hpend : ComputableInPolyTime (fun σ : State X A Y => σ.2.1) (encState encX encA encY) + (encOption encX) := (ComputableInPolyTime.fst (encOption encX) (encOption encY)).comp hreg + have hret : ComputableInPolyTime (fun σ : State X A Y => σ.2.2) (encState encX encA encY) + (encOption encY) := (ComputableInPolyTime.snd (encOption encX) (encOption encY)).comp hreg + -- A returned value is absorbed by the innermost open frame. + have hframe : ComputableInPolyTime + (fun q : (State X A Y × Y) × (X × A) => + R.resume q.2.1 (R.absorb q.2.1 q.2.2 q.1.2) q.1.1.1.tail) + (encPair (encPair (encState encX encA encY) encY) (encPair encX encA)) + (encState encX encA encY) := by + have hq := ComputableInPolyTime.fst (encPair (encState encX encA encY) encY) + (encPair encX encA) + have hf := ComputableInPolyTime.snd (encPair (encState encX encA encY) encY) + (encPair encX encA) + have hy := (ComputableInPolyTime.snd (encState encX encA encY) encY).comp hq + have hσ := (ComputableInPolyTime.fst (encState encX encA encY) encY).comp hq + have habs := h.absorb.comp (hf.prod hy) + exact (resume_polyTime h).comp + ((((ComputableInPolyTime.fst encX encA).comp hf).prod habs).prod + ((ComputableInPolyTime.tail (encA := encPair encX encA)).comp (hstack.comp hσ))) + have hreturn : ComputableInPolyTime + (fun p : State X A Y × Y => + p.1.1.head?.elim p.1 fun f => R.resume f.1 (R.absorb f.1 f.2 p.2) p.1.1.tail) + (encPair (encState encX encA encY) encY) (encState encX encA encY) := by + have hσ := ComputableInPolyTime.fst (encState encX encA encY) encY + have hhead : ComputableInPolyTime (fun p : State X A Y × Y => p.1.1.head?) + (encPair (encState encX encA encY) encY) (encOption (encPair encX encA)) := + (ComputableInPolyTime.head? (encPair encX encA) (encL := encList (encPair encX encA))).comp + (hstack.comp hσ) + exact ComputableInPolyTime.optionElimWithInput + (value := fun p : State X A Y × Y => p.1.1.head?) (fallback := fun p => p.1) + (body := fun p f => R.resume f.1 (R.absorb f.1 f.2 p.2) p.1.1.tail) hhead hσ hframe + have hnoCall : ComputableInPolyTime + (fun σ : State X A Y => + σ.2.2.elim σ fun y => σ.1.head?.elim σ fun f => R.resume f.1 (R.absorb f.1 f.2 y) σ.1.tail) + (encState encX encA encY) (encState encX encA encY) := + ComputableInPolyTime.optionElimWithInput (value := fun σ : State X A Y => σ.2.2) + (fallback := fun σ => σ) + (body := fun σ y => σ.1.head?.elim σ fun f => R.resume f.1 (R.absorb f.1 f.2 y) σ.1.tail) + hret ComputableInPolyTime.id' hreturn + have hcall : ComputableInPolyTime + (fun p : State X A Y × X => R.resume p.2 (R.start p.2) p.1.1) + (encPair (encState encX encA encY) encX) (encState encX encA encY) := by + have hx : ComputableInPolyTime (fun p : State X A Y × X => p.2) + (encPair (encState encX encA encY) encX) encX := + ComputableInPolyTime.snd (encState encX encA encY) encX + have hst : ComputableInPolyTime (fun p : State X A Y × X => p.1.1) + (encPair (encState encX encA encY) encX) (encList (encPair encX encA)) := + hstack.comp (ComputableInPolyTime.fst (encState encX encA encY) encX) + exact (resume_polyTime h).comp ((hx.prod (h.start.comp hx)).prod hst) + exact ComputableInPolyTime.optionElimWithInput (value := fun σ : State X A Y => σ.2.1) + (fallback := fun σ => σ.2.2.elim σ fun y => + σ.1.head?.elim σ fun f => R.resume f.1 (R.absorb f.1 f.2 y) σ.1.tail) + (body := fun σ x => R.resume x (R.start x) σ.1) hpend hnoCall hcall + +end Native + +section Certificate + +variable {encX : X ↪ List Bool} {encA : A ↪ List Bool} {encY : Y ↪ List Bool} + +theorem stack_polyTime : + ComputableInPolyTime (fun σ : State X A Y => σ.1) (encState encX encA encY) + (encList (encPair encX encA)) := + ComputableInPolyTime.fst _ _ + +theorem pending_polyTime : + ComputableInPolyTime (fun σ : State X A Y => σ.2.1) (encState encX encA encY) + (encOption encX) := + (ComputableInPolyTime.fst (encOption encX) (encOption encY)).comp (ComputableInPolyTime.snd _ _) + +variable (R) in +/-- **The leaf step**: the leaf certificate on the pending node, keeping the stack. -/ +noncomputable def leafStepCertificate (leaf : NativeCertificate R.leaf encX encY) : + NativeCertificate R.leafStep (encState encX encA encY) (encState encX encA encY) := + (NativeCertificate.ofPolyTime stack_polyTime).prod ((NativeCertificate.const none).prod + (NativeCertificate.option (value := fun σ : State X A Y => σ.2.1) (fallback := fun _ => none) + (body := fun p : State X A Y × X => some (R.leaf p.2)) + (NativeCertificate.ofPolyTime pending_polyTime) (NativeCertificate.const none) + (NativeCertificate.some (leaf.comp (NativeCertificate.payload _ encX))))) + +/-- **One step of the machine**: the leaf certificate on a pending leaf, and a polynomial-time +step otherwise. -/ +noncomputable def stepCertificate (h : R.PolyTime encX encA encY) + (leaf : NativeCertificate R.leaf encX encY) : + NativeCertificate R.step (encState encX encA encY) (encState encX encA encY) := + (NativeCertificate.branch (NativeCertificate.ofPolyTime (isLeafCall_polyTime h)) + (leafStepCertificate R leaf) (NativeCertificate.ofPolyTime (polyStep_polyTime h))).congr + fun σ => (R.step_eq_ite σ).symm + +theorem stepCertificate_time (h : R.PolyTime encX encA encY) + (leaf : NativeCertificate R.leaf encX encY) (σ : State X A Y) : + (stepCertificate h leaf).time σ = + (NativeCertificate.ofPolyTime (isLeafCall_polyTime h)).time σ + + if R.isLeafCall σ then (leafStepCertificate R (encA := encA) leaf).time σ + else (NativeCertificate.ofPolyTime (polyStep_polyTime h)).time σ := rfl + +theorem stepCertificate_space (h : R.PolyTime encX encA encY) + (leaf : NativeCertificate R.leaf encX encY) (σ : State X A Y) : + (stepCertificate h leaf).space σ = + (NativeCertificate.ofPolyTime (isLeafCall_polyTime h)).space σ + + if R.isLeafCall σ then (leafStepCertificate R (encA := encA) leaf).space σ + else (NativeCertificate.ofPolyTime (polyStep_polyTime h)).space σ := rfl + +theorem leafStepCertificate_time (leaf : NativeCertificate R.leaf encX encY) (st : List (X × A)) + (x : X) (v : Option Y) : + (leafStepCertificate R (encA := encA) leaf).time (st, some x, v) = + (NativeCertificate.ofPolyTime (stack_polyTime (encX := encX) (encA := encA) + (encY := encY))).time (st, some x, v) + + ((encState encX encA encY (st, some x, v)).length + + (2 * (NativeCertificate.ofPolyTime (pending_polyTime (encX := encX) (encA := encA) + (encY := encY))).time (st, some x, v) + + (encState encX encA encY (st, some x, v)).length + + ((encPair (encState encX encA encY) encX ((st, some x, v), x)).length + + leaf.time x))) := rfl + +theorem leafStepCertificate_space (leaf : NativeCertificate R.leaf encX encY) + (st : List (X × A)) (x : X) (v : Option Y) : + (leafStepCertificate R (encA := encA) leaf).space (st, some x, v) = + (NativeCertificate.ofPolyTime (stack_polyTime (encX := encX) (encA := encA) + (encY := encY))).space (st, some x, v) + + ((encState encX encA encY (st, some x, v)).length + + (2 * (NativeCertificate.ofPolyTime (pending_polyTime (encX := encX) (encA := encA) + (encY := encY))).space (st, some x, v) + + (encState encX encA encY (st, some x, v)).length + + ((encPair (encState encX encA encY) encX ((st, some x, v), x)).length + + leaf.space x))) := rfl + +variable (R) in +/-- The leaf cost of a state: the leaf certificate's bound at a pending leaf, and zero +otherwise. -/ +def leafCost (cost : X → ℕ) (σ : State X A Y) : ℕ := + σ.2.1.elim 0 fun x => if R.isLeaf x then cost x else 0 + +private theorem length_pending_le {σ : State X A Y} {x : X} (hx : σ.2.1 = some x) : + (encX x).length ≤ (encState encX encA encY σ).length := by + rcases σ with ⟨st, r, v⟩ + simp only at hx + subst hx + simp only [length_encPair, encOption_some, List.length_cons] + omega + +/-- **The cost of one step**: a polynomial in the state length, plus the leaf certificate at a +pending leaf. -/ +theorem exists_stepCertificate_le (h : R.PolyTime encX encA encY) + (leaf : NativeCertificate R.leaf encX encY) : + ∃ K : ℕ, ∀ σ : State X A Y, + (stepCertificate h leaf).time σ ≤ + K * ((encState encX encA encY σ).length + 1) ^ K + R.leafCost leaf.time σ ∧ + (stepCertificate h leaf).space σ ≤ + K * ((encState encX encA encY σ).length + 1) ^ K + R.leafCost leaf.space σ := by + have ht := NativeCertificate.ofPolyTime_isPolynomial (isLeafCall_polyTime h) + have hp := NativeCertificate.ofPolyTime_isPolynomial (polyStep_polyTime h) + have hs := NativeCertificate.ofPolyTime_isPolynomial (stack_polyTime (encX := encX) + (encA := encA) (encY := encY)) + have hc := NativeCertificate.ofPolyTime_isPolynomial (pending_polyTime (encX := encX) + (encA := encA) (encY := encY)) + -- Everything but the leaf certificate is polynomial in the state length. + have hT := ((((ht.1.add hp.1).add hs.1).add ((PolyBounded.const 2).mul hc.1)).add + ((PolyBounded.const 6).mul PolyBounded.self)).add (PolyBounded.const 1) + have hS := ((((ht.2.add hp.2).add hs.2).add ((PolyBounded.const 2).mul hc.2)).add + ((PolyBounded.const 6).mul PolyBounded.self)).add (PolyBounded.const 1) + obtain ⟨cT, dT, hcT⟩ := hT.exists_le + obtain ⟨cS, dS, hcS⟩ := hS.exists_le + refine ⟨cT + cS + dT + dS, fun σ => ?_⟩ + have hmono {c d : ℕ} (hc : c ≤ cT + cS + dT + dS) (hd : d ≤ cT + cS + dT + dS) : + c * ((encState encX encA encY σ).length + 1) ^ d ≤ + (cT + cS + dT + dS) * ((encState encX encA encY σ).length + 1) ^ (cT + cS + dT + dS) := + Nat.mul_le_mul hc (Nat.pow_le_pow_right (Nat.succ_pos _) hd) + have hT' := (hcT σ).trans (hmono (by omega) (by omega)) + have hS' := (hcS σ).trans (hmono (by omega) (by omega)) + rcases σ with ⟨st, _ | x, v⟩ + · rw [stepCertificate_time, stepCertificate_space] + simp only [leafCost, isLeafCall, Option.elim, Bool.false_eq_true, ite_false, Nat.add_zero] + constructor <;> omega + · have hx := length_pending_le (encX := encX) (encA := encA) (encY := encY) + (σ := (st, some x, v)) rfl + rw [stepCertificate_time, stepCertificate_space] + by_cases hl : R.isLeaf x = true + · simp only [leafCost, isLeafCall, Option.elim, hl, ite_true] + have hpair := length_encPair (enc₁ := encState encX encA encY) (enc₂ := encX) + ((st, some x, v), x) + dsimp only at hpair + rw [leafStepCertificate_time, leafStepCertificate_space] + constructor <;> omega + · simp only [Bool.not_eq_true] at hl + simp only [leafCost, isLeafCall, Option.elim, hl, Bool.false_eq_true, ite_false, + Nat.add_zero] + constructor <;> omega + +variable (R) in +/-- **The clocked run**: the register after `fuel a` steps from the root `root a` on an empty +stack. -/ +def run {I : Type} (fuel : I → ℕ) (root : I → X) (a : I) : Option Y := + (R.step^[fuel a] ([], some (root a), none)).2.2 + +/-- **Enough fuel gives the value.** -/ +theorem run_eq_some {I : Type} {fuel : I → ℕ} {root : I → X} {Inv : Invariant X A Y} {a : I} + {y : Y} {N H : ℕ} (he : R.Eval Inv (root a) y N H) (hfuel : 2 * N - 1 ≤ fuel a) : + R.run fuel root a = some y := by + obtain ⟨k, hk⟩ := Nat.exists_eq_add_of_le hfuel + rw [run, hk, Nat.add_comm, Function.iterate_add_apply, he.run, + Function.iterate_fixed (R.step_final y)] + +/-- **The native machine** for the clocked run: one fixed machine, from native certificates for +the fuel and the root. -/ +noncomputable def certificate (h : R.PolyTime encX encA encY) + (leaf : NativeCertificate R.leaf encX encY) {I : Type} {encIn : I ↪ List Bool} + {fuel : I → ℕ} {root : I → X} (cn : NativeCertificate fuel encIn encNat) + (croot : NativeCertificate root encIn encX) : + NativeCertificate (R.run fuel root) encIn (encOption encY) := + NativeCertificate.snd (NativeCertificate.snd (NativeCertificate.iterate cn + ((NativeCertificate.const ([] : List (X × A))).prod + ((NativeCertificate.some croot).prod (NativeCertificate.const (none : Option Y)))) + (stepCertificate h leaf))) + +/-- A good state is at most `5 (H + 1) (F + 1)` long when the nodes, frames and values it holds +are at most `F` long. -/ +theorem length_encState_le {Inv : Invariant X A Y} {H F : ℕ} {σ : State X A Y} + (hσ : Good Inv H σ) (hnode : ∀ x, Inv.node x → (encX x).length ≤ F) + (hframe : ∀ x a, Inv.frame x a → (encPair encX encA (x, a)).length ≤ F) + (hvalue : ∀ y, Inv.value y → (encY y).length ≤ F) : + (encState encX encA encY σ).length ≤ 5 * (H + 1) * (F + 1) := by + obtain ⟨hlen, hfr, hreg⟩ := hσ + have hstack : (encList (encPair encX encA) σ.1).length ≤ H * (2 * F + 1) := by + rw [length_encList] + calc (σ.1.map fun f => 2 * (encPair encX encA f).length + 1).sum + ≤ (σ.1.map fun f => 2 * (encPair encX encA f).length + 1).length • (2 * F + 1) := + List.sum_le_length_nsmul _ _ fun n hn => by + obtain ⟨⟨x, a⟩, hf, rfl⟩ := List.mem_map.mp hn + have := hframe x a (hfr _ hf) + omega + _ ≤ H * (2 * F + 1) := by + rw [List.length_map, smul_eq_mul] + exact Nat.mul_le_mul_right _ hlen + have hHF : H * (2 * F + 1) ≤ 2 * H * F + H := by rw [Nat.mul_add, Nat.mul_one]; ring_nf; omega + rcases σ with ⟨st, r, v⟩ + rcases hreg with ⟨x, hr, hx⟩ | ⟨y, hr, hy⟩ <;> simp only [Prod.mk.injEq] at hr <;> + obtain ⟨rfl, rfl⟩ := hr + · have := hnode x hx + simp only [length_encPair, encOption_some, encOption_none, List.length_cons, + List.length_nil] at hstack ⊢ + nlinarith + · have := hvalue y hy + simp only [length_encPair, encOption_some, encOption_none, List.length_cons, + List.length_nil] at hstack ⊢ + nlinarith + +private theorem pow_absorb (K₀ z : ℕ) (hz : 1 ≤ z) : + K₀ * (5 * z + 1) ^ K₀ + 5 * z ≤ (K₀ * 6 ^ K₀ + K₀ + 6) * z ^ (K₀ * 6 ^ K₀ + K₀ + 6) := by + set K := K₀ * 6 ^ K₀ + K₀ + 6 + have h6 : (5 * z + 1) ^ K₀ ≤ 6 ^ K₀ * z ^ K := by + calc (5 * z + 1) ^ K₀ ≤ (6 * z) ^ K₀ := Nat.pow_le_pow_left (by omega) _ + _ = 6 ^ K₀ * z ^ K₀ := Nat.mul_pow _ _ _ + _ ≤ 6 ^ K₀ * z ^ K := Nat.mul_le_mul_left _ (Nat.pow_le_pow_right hz (by omega)) + have hz' : z ≤ z ^ K := by + calc z = z ^ 1 := (pow_one z).symm + _ ≤ z ^ K := Nat.pow_le_pow_right hz (by omega) + calc K₀ * (5 * z + 1) ^ K₀ + 5 * z ≤ K₀ * (6 ^ K₀ * z ^ K) + 5 * z ^ K := + Nat.add_le_add (Nat.mul_le_mul_left _ h6) (Nat.mul_le_mul_left _ hz') + _ = (K₀ * 6 ^ K₀ + 5) * z ^ K := by ring + _ ≤ K * z ^ K := Nat.mul_le_mul_right _ (by omega) + +/-- **The resources of the native machine.** There is one constant `K` such that, for any fuel +and root certificates, if the root has a derivation with `N` nodes and at most `H` open calls +whose nodes, frames and values are at most `F` long, and whose leaves are evaluated by the leaf +certificate within time `TL` and space `SL`, then the time is at most the cost of the fuel and the +root plus `fuel + 1` times `K ((H + 1) (F + 1)) ^ K + TL` (and the cost of the binary clock), and +the workspace is at most the cost of the fuel and the root plus `K ((H + 1) (F + 1)) ^ K + SL`. +The bounds hold for any fuel; with `2 * N - 1 ≤ fuel` the result is the value +(`run_eq_some`). -/ +theorem certificate_le (h : R.PolyTime encX encA encY) + (leaf : NativeCertificate R.leaf encX encY) : + ∃ K : ℕ, ∀ {I : Type} {encIn : I ↪ List Bool} {fuel : I → ℕ} {root : I → X} + (cn : NativeCertificate fuel encIn encNat) (croot : NativeCertificate root encIn encX) + (Inv : Invariant X A Y) (F TL SL : ℕ), + (∀ x, Inv.node x → (encX x).length ≤ F ∧ + (R.isLeaf x = true → leaf.time x ≤ TL ∧ leaf.space x ≤ SL)) → + (∀ x a, Inv.frame x a → (encPair encX encA (x, a)).length ≤ F) → + (∀ y, Inv.value y → (encY y).length ≤ F) → + ∀ (a : I) (y : Y) (N H : ℕ), R.Eval Inv (root a) y N H → + (certificate h leaf cn croot).time a ≤ cn.time a + croot.time a + 2 * (encIn a).length + + (fuel a + 1) * (K * ((H + 1) * (F + 1)) ^ K + TL + + ((encNat (fuel a)).length + 1) ^ 2) ∧ + (certificate h leaf cn croot).space a ≤ cn.space a + croot.space a + + 2 * (encIn a).length + K * ((H + 1) * (F + 1)) ^ K + SL + + (encNat (fuel a)).length := by + obtain ⟨K₀, hK₀⟩ := exists_stepCertificate_le h leaf + refine ⟨K₀ * 6 ^ K₀ + K₀ + 6, ?_⟩ + intro I encIn fuel root cn croot Inv F TL SL hnode hframe hvalue a y N H he + set z := (H + 1) * (F + 1) + have hz : 1 ≤ z := Nat.one_le_iff_ne_zero.mpr (Nat.mul_ne_zero (by omega) (by omega)) + have habsorb := pow_absorb K₀ z hz + -- Every state of the run is good, hence short, and pays the leaf bound only at a leaf. + have hgood := he.good_iterate (R := R) + have hlen (j : ℕ) : (encState encX encA encY (R.step^[j] ([], some (root a), none))).length ≤ + 5 * z := by + have := length_encState_le (hgood j) (fun x hx => (hnode x hx).1) hframe hvalue + rwa [Nat.mul_assoc] at this + clear_value z + have hcost (cost : X → ℕ) (B : ℕ) + (hB : ∀ x, Inv.node x → R.isLeaf x = true → cost x ≤ B) (j : ℕ) : + R.leafCost cost (R.step^[j] ([], some (root a), none)) ≤ B := by + obtain ⟨-, -, ⟨x, hr, hx⟩ | ⟨y', hr, -⟩⟩ := hgood j <;> simp only [leafCost, hr, Option.elim] + · split_ifs with hl + · exact hB x hx hl + · exact Nat.zero_le _ + · exact Nat.zero_le _ + have hround (j : ℕ) : + (stepCertificate h leaf).time (R.step^[j] ([], some (root a), none)) ≤ + K₀ * (5 * z + 1) ^ K₀ + TL ∧ + (stepCertificate h leaf).space (R.step^[j] ([], some (root a), none)) ≤ + K₀ * (5 * z + 1) ^ K₀ + SL := by + obtain ⟨ht, hs⟩ := hK₀ (R.step^[j] ([], some (root a), none)) + have hp : K₀ * ((encState encX encA encY (R.step^[j] ([], some (root a), none))).length + 1) ^ + K₀ ≤ K₀ * (5 * z + 1) ^ K₀ := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_left (by have := hlen j; omega) _) + have hct := hcost leaf.time TL (fun x hx hl => ((hnode x hx).2 hl).1) j + have hcs := hcost leaf.space SL (fun x hx hl => ((hnode x hx).2 hl).2) j + constructor <;> omega + have hmaxT := NativeCertificate.maxRoundTime_le (stepCertificate h leaf) (fuel a) + ([], some (root a), none) (R := K₀ * (5 * z + 1) ^ K₀ + TL) fun j _ => (hround j).1 + have hmaxS := NativeCertificate.maxRoundSpace_le (stepCertificate h leaf) (fuel a) + ([], some (root a), none) (Q := K₀ * (5 * z + 1) ^ K₀ + SL) fun j _ => (hround j).2 + have hmaxL := NativeCertificate.maxStateLength_le + (encPair (encList (encPair encX encA)) (encPair (encOption encX) (encOption encY))) R.step + (fuel a) ([], some (root a), none) (B := 5 * z) fun j _ => hlen j + simp only [certificate, NativeCertificate.snd_time, NativeCertificate.snd_space, + NativeCertificate.iterate_time, NativeCertificate.iterate_space, + NativeCertificate.prod_time, NativeCertificate.prod_space, NativeCertificate.const_time, + NativeCertificate.const_space, NativeCertificate.some_time, NativeCertificate.some_space] + constructor + · have hstep : NativeCertificate.maxRoundTime (stepCertificate h leaf) (fuel a) + ([], some (root a), none) + + NativeCertificate.maxStateLength + (encPair (encList (encPair encX encA)) (encPair (encOption encX) (encOption encY))) + R.step (fuel a) ([], some (root a), none) + ((encNat (fuel a)).length + 1) ^ 2 ≤ + (K₀ * 6 ^ K₀ + K₀ + 6) * z ^ (K₀ * 6 ^ K₀ + K₀ + 6) + TL + + ((encNat (fuel a)).length + 1) ^ 2 := by omega + have := Nat.mul_le_mul_left (fuel a + 1) hstep + omega + · omega + +end Certificate + +end Scheme + +end Cslib.GenericRAM.StackRecursion diff --git a/Complexity/Foundations/Data/Nat/RecursionTree.lean b/Complexity/Foundations/Data/Nat/RecursionTree.lean new file mode 100644 index 0000000000..e5f2982128 --- /dev/null +++ b/Complexity/Foundations/Data/Nat/RecursionTree.lean @@ -0,0 +1,170 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Foundations.Data.Nat.SqrtLog +public import Mathlib.Algebra.BigOperators.Group.Finset.Basic +import Mathlib.Algebra.Order.BigOperators.Group.Finset +import Mathlib.Tactic.IntervalCases +import Mathlib.Tactic.Linarith +import Mathlib.Tactic.Ring + +/-! +# Counting the nodes of a recursion tree + +A recursion whose calls at depth `d` have at most `b d` children, with at most `k` levels below +depth `d`, has at most `Nat.recursionBound b k d` nodes below and including a call at depth `d`: +one for the call, plus `b d` subtrees of one level less (`Nat.recursionBound_succ`). This is at +most `k + 1` times the product of the branching factors (`Nat.recursionBound_le`). + +**Recursion trees of size `2 ^ O(n / √(log n))`.** If a call at depth `j` has at most +`2 ^ (c ⌊log₂ n⌋ + e j)` children, there are at most `⌊log₂ n⌋` levels, and the excess exponents +sum to `O(n / √⌊log₂ n⌋)`, then the tree has `2 ^ O(n / √⌊log₂ n⌋)` nodes +(`Nat.exists_recursionBound_le_two_pow_div_sqrt_log_two`), in the natural-number scale of +`Nat.exists_three_pow_mul_le_two_pow_div_sqrt_log_two`. The polynomial factors `2 ^ (c ⌊log₂ n⌋)` +contribute `2 ^ O(log² n)`, which the scale absorbs. Excess exponents halving with the depth sum to +at most twice the first one (`Nat.sum_div_two_pow_le`). + +This is the call count in the proof of Theorem D.1 of [OpenAI2026ETH] (Appendix D.4.7, p. 64): +a call at depth `d` has `s ^ O(1) exp(O(s / √λ · (√2 / 8) ^ d))` children and there are +`O(log λ)` levels. + +## Main definitions + +* `Nat.recursionBound`: the node bound of a recursion tree with given branching factors. + +## Main results + +* `Nat.recursionBound_le`: at most `k + 1` times the product of the branching factors. +* `Nat.sum_div_two_pow_le`: `∑ j < D, x / 2 ^ j ≤ 2 x`. +* `Nat.exists_recursionBound_le_two_pow_div_sqrt_log_two`: recursion trees of size + `2 ^ O(n / √⌊log₂ n⌋)`. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Appendix D.4.7 (p. 64). +-/ + +public section + +namespace Nat + +open Finset + +/-- The node bound of a recursion tree whose calls at depth `d` have at most `b d` children, +for a call at depth `d` with at most `k` levels below it. -/ +@[expose] def recursionBound (b : ℕ → ℕ) : ℕ → ℕ → ℕ + | 0, _ => 1 + | k + 1, d => 1 + b d * recursionBound b k (d + 1) + +@[simp] theorem recursionBound_zero (b : ℕ → ℕ) (d : ℕ) : recursionBound b 0 d = 1 := rfl + +/-- A call is one node above its children's subtrees. -/ +theorem recursionBound_succ (b : ℕ → ℕ) (k d : ℕ) : + recursionBound b (k + 1) d = 1 + b d * recursionBound b k (d + 1) := rfl + +/-- **The node bound is at most `k + 1` times the product of the branching factors.** -/ +theorem recursionBound_le (b : ℕ → ℕ) (k d : ℕ) : + recursionBound b k d ≤ (k + 1) * ∏ j ∈ range k, max 1 (b (d + j)) := by + induction k generalizing d with + | zero => simp + | succ k ih => + rw [recursionBound_succ, prod_range_succ'] + set P := ∏ j ∈ range k, max 1 (b (d + (j + 1))) + have hP : 1 ≤ P := one_le_prod fun _ _ => le_max_left _ _ + have hm : 1 ≤ max 1 (b d) := le_max_left _ _ + have hb : b d ≤ max 1 (b d) := le_max_right _ _ + have ih' : recursionBound b k (d + 1) ≤ (k + 1) * P := by + simpa only [Nat.add_assoc, Nat.add_comm 1] using ih (d + 1) + simp only [Nat.add_zero] + calc 1 + b d * recursionBound b k (d + 1) ≤ max 1 (b d) * P + max 1 (b d) * ((k + 1) * P) := + Nat.add_le_add (Nat.one_le_iff_ne_zero.mpr (Nat.mul_ne_zero (by omega) (by omega))) + (Nat.mul_le_mul hb ih') + _ = (k + 1 + 1) * (P * max 1 (b d)) := by ring + +/-- **Halving excess exponents**: `∑ j < D, x / 2 ^ j ≤ 2 x`. -/ +theorem sum_div_two_pow_le (x D : ℕ) : ∑ j ∈ range D, x / 2 ^ j ≤ 2 * x := by + induction D generalizing x with + | zero => simp + | succ D ih => + rw [sum_range_succ'] + have h : ∑ j ∈ range D, x / 2 ^ (j + 1) = ∑ j ∈ range D, x / 2 / 2 ^ j := + sum_congr rfl fun j _ => by rw [pow_succ, Nat.mul_comm, Nat.div_div_eq_div_mul] + rw [h, pow_zero, Nat.div_one] + have := ih (x / 2) + have := Nat.div_mul_le_self x 2 + omega + +/-- `L ^ 3 ≤ 8 * 2 ^ L`. -/ +private theorem cube_le_eight_mul_two_pow (L : ℕ) : L ^ 3 ≤ 8 * 2 ^ L := by + have hlarge : ∀ m : ℕ, (m + 9) ^ 3 ≤ 8 * 2 ^ (m + 9) := by + intro m + induction m with + | zero => norm_num + | succ m ih => + have h : (m + 1 + 9) ^ 3 ≤ 2 * (m + 9) ^ 3 := by ring_nf; nlinarith + calc (m + 1 + 9) ^ 3 ≤ 2 * (m + 9) ^ 3 := h + _ ≤ 2 * (8 * 2 ^ (m + 9)) := Nat.mul_le_mul_left 2 ih + _ = 8 * 2 ^ (m + 1 + 9) := by ring + rcases Nat.lt_or_ge L 9 with hL | hL + · interval_cases L <;> norm_num + · obtain ⟨m, rfl⟩ := Nat.exists_eq_add_of_le' hL + exact hlarge m + +/-- The square of the logarithm is `O(n / √⌊log₂ n⌋)`: `⌊log₂ n⌋ ^ 2 ≤ 16 (n / √⌊log₂ n⌋)` from +`n = 2` on, since `⌊log₂ n⌋ ^ 2 √⌊log₂ n⌋ ≤ ⌊log₂ n⌋ ^ 3 ≤ 8 n`. -/ +theorem log_two_sq_le_div_sqrt_log_two {n : ℕ} (hn : 2 ≤ n) : + log 2 n ^ 2 ≤ 16 * (n / sqrt (log 2 n)) := by + set L := log 2 n + have hr : 1 ≤ sqrt L := one_le_sqrt_log_two hn + have hS : 1 ≤ n / sqrt L := one_le_div_sqrt_log_two hn + have hcube : L ^ 2 * sqrt L ≤ 8 * n := + calc L ^ 2 * sqrt L ≤ L ^ 2 * L := Nat.mul_le_mul_left _ (sqrt_le_self L) + _ = L ^ 3 := by ring + _ ≤ 8 * 2 ^ L := cube_le_eight_mul_two_pow L + _ ≤ 8 * n := Nat.mul_le_mul_left 8 (pow_log_le_self 2 (by omega)) + have h1 : L ^ 2 ≤ 8 * n / sqrt L := (Nat.le_div_iff_mul_le hr).2 hcube + have h2 := mul_div_le_mul_div_add 8 n (sqrt L) + omega + +/-- **Recursion trees of size `2 ^ O(n / √⌊log₂ n⌋)`.** For constants `c` and `E` there is `K` +such that, from `n = 2` on: if a call at depth `j < D` has at most `2 ^ (c ⌊log₂ n⌋ + e j)` +children, `D ≤ ⌊log₂ n⌋`, and `∑ j < D, e j ≤ E (n / √⌊log₂ n⌋)`, then +`(D + 1) ∏ j < D, max 1 (b j) ≤ 2 ^ (K n / √⌊log₂ n⌋)`, which bounds the nodes of the recursion +tree by `recursionBound_le`. -/ +theorem exists_recursionBound_le_two_pow_div_sqrt_log_two (c E : ℕ) : + ∃ K, ∀ (n D : ℕ) (b e : ℕ → ℕ), 2 ≤ n → D ≤ log 2 n → + (∀ j < D, b j ≤ 2 ^ (c * log 2 n + e j)) → + ∑ j ∈ range D, e j ≤ E * (n / sqrt (log 2 n)) → + (D + 1) * ∏ j ∈ range D, max 1 (b j) ≤ 2 ^ (K * n / sqrt (log 2 n)) := by + refine ⟨1 + 16 * c + E, fun n D b e hn hD hb he => ?_⟩ + set L := log 2 n + set S := n / sqrt L + have hL : L ≤ S := log_two_le_div_sqrt_log_two n + have hL2 : L ^ 2 ≤ 16 * S := log_two_sq_le_div_sqrt_log_two hn + have hD1 : D + 1 ≤ 2 ^ L := (Nat.add_le_add_right hD 1).trans Nat.lt_two_pow_self + have hprod : ∏ j ∈ range D, max 1 (b j) ≤ 2 ^ (D * (c * L) + ∑ j ∈ range D, e j) := by + calc ∏ j ∈ range D, max 1 (b j) ≤ ∏ j ∈ range D, 2 ^ (c * L + e j) := + prod_le_prod fun j hj => max_le Nat.one_le_two_pow (hb j (mem_range.mp hj)) + _ = 2 ^ (∑ j ∈ range D, (c * L + e j)) := (Finset.prod_pow_eq_pow_sum _ _ _) + _ = 2 ^ (D * (c * L) + ∑ j ∈ range D, e j) := by + rw [sum_add_distrib, sum_const, card_range, smul_eq_mul] + have hexp : L + (D * (c * L) + ∑ j ∈ range D, e j) ≤ (1 + 16 * c + E) * S := by + have h1 : D * (c * L) ≤ c * (16 * S) := + calc D * (c * L) ≤ L * (c * L) := Nat.mul_le_mul_right _ hD + _ = c * L ^ 2 := by ring + _ ≤ c * (16 * S) := Nat.mul_le_mul_left _ hL2 + nlinarith + calc (D + 1) * ∏ j ∈ range D, max 1 (b j) + ≤ 2 ^ L * 2 ^ (D * (c * L) + ∑ j ∈ range D, e j) := Nat.mul_le_mul hD1 hprod + _ = 2 ^ (L + (D * (c * L) + ∑ j ∈ range D, e j)) := (pow_add _ _ _).symm + _ ≤ 2 ^ ((1 + 16 * c + E) * S) := Nat.pow_le_pow_right (by norm_num) hexp + _ ≤ 2 ^ ((1 + 16 * c + E) * n / sqrt L) := + Nat.pow_le_pow_right (by norm_num) (mul_div_le_mul_div_assoc _ _ _) + +end Nat diff --git a/ComplexityTests/StackRecursion.lean b/ComplexityTests/StackRecursion.lean new file mode 100644 index 0000000000..bcca97e434 --- /dev/null +++ b/ComplexityTests/StackRecursion.lean @@ -0,0 +1,257 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.GenericRAM.Native.StackRecursion +import Complexity.Foundations.Data.Nat.RecursionTree +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Binary +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeOptionPrimitives + +/-! # Depth-first stack recursion on a toy scheme + +The halving recursion: a node `x ≥ 2` has two children `x / 2`, a leaf has value `1`, and a call +adds its children's values. The value of `x` is its number of leaves. The tests run the stack +machine, derive `Eval` for every node, instantiate the polynomial-time components (so the +`PolyTime` hypotheses of the native certificate are satisfiable) and check the node-count +arithmetic. -/ + +namespace ComplexityTests.StackRecursion + +open Cslib.GenericRAM Cslib.GenericRAM.StackRecursion Turing.MultiTapeTM + +/-- The halving scheme. The accumulator counts the children done and sums their values. -/ +def halving : Scheme ℕ (ℕ × ℕ) ℕ where + isLeaf x := decide (x < 2) + leaf _ := 1 + start _ := (0, 0) + next x a := if a.1 < 2 then some (x / 2) else none + absorb _ a y := (a.1 + 1, a.2 + y) + finish _ a := a.2 + +/-- The number of leaves below `x`. -/ +def leaves (x : ℕ) : ℕ := if x < 2 then 1 else 2 * leaves (x / 2) +decreasing_by omega + +/-- The number of nodes below `x`. -/ +def nodes (x : ℕ) : ℕ := if x < 2 then 1 else 2 * nodes (x / 2) + 1 +decreasing_by omega + +/-- The number of open calls while evaluating `x`. -/ +def height (x : ℕ) : ℕ := if x < 2 then 0 else height (x / 2) + 1 +decreasing_by omega + +/-- No invariant. -/ +def none : Invariant ℕ (ℕ × ℕ) ℕ := ⟨fun _ => True, fun _ _ => True, fun _ => True⟩ + +-- The machine on `8`: four levels, eight leaves, fifteen nodes, so `2 * 15 - 1 = 29` steps. +example : halving.run (fun _ : Unit => 29) (fun _ => 8) () = some 8 := by decide + +-- One step fewer, the value `4` of the second child is still waiting to be absorbed by the root. +example : halving.run (fun _ : Unit => 28) (fun _ => 8) () = some 4 := by decide + +-- Extra fuel changes nothing: the final state is fixed. +example : halving.run (fun _ : Unit => 100) (fun _ => 8) () = some 8 := by decide + +/-- **Every node has a derivation**, with its leaves as value. -/ +theorem eval_halving (x : ℕ) : halving.Eval none x (leaves x) (nodes x) (height x) := by + induction x using Nat.strong_induction_on with + | _ x ih => + by_cases hx : x < 2 + · have hl : leaves x = 1 := by rw [leaves]; simp [hx] + have hn : nodes x = 1 := by rw [nodes]; simp [hx] + have hh : height x = 0 := by rw [height]; simp [hx] + rw [hl, hn, hh] + exact Scheme.Eval.leaf (R := halving) trivial (decide_eq_true hx) trivial + · have hl : leaves x = leaves (x / 2) + leaves (x / 2) := by rw [leaves]; simp [hx]; ring + have hn : nodes x = (nodes (x / 2) + (nodes (x / 2) + 0)) + 1 := by + rw [nodes]; simp [hx]; ring + have hh : height x = max (height (x / 2)) (max (height (x / 2)) 0) + 1 := by + rw [height]; simp [hx] + have hc := ih (x / 2) (by omega) + have hloop : halving.Loop none x (halving.start x) + (2, 0 + leaves (x / 2) + leaves (x / 2)) (nodes (x / 2) + (nodes (x / 2) + 0)) + (max (height (x / 2)) (max (height (x / 2)) 0)) := + Scheme.Loop.child trivial (by simp [halving]) hc + (Scheme.Loop.child trivial (by simp [halving]) hc (Scheme.Loop.done (by simp [halving]))) + have := Scheme.Eval.call (R := halving) (I := none) trivial (decide_eq_false hx) hloop + trivial + rw [hl, hn, hh] + simpa [halving] using this + +/-- `nodes x + 1 ≤ 2 max x 1`. -/ +theorem nodes_add_one_le (x : ℕ) : nodes x + 1 ≤ 2 * max x 1 := by + induction x using Nat.strong_induction_on with + | _ x ih => + by_cases hx : x < 2 + · rw [nodes]; simp only [hx, ite_true]; omega + · have := ih (x / 2) (by omega) + rw [nodes]; simp only [hx, ite_false] + omega + +/-- `2 * nodes x - 1 ≤ 4 * x + 1`: linear fuel suffices. -/ +theorem two_mul_nodes_le (x : ℕ) : 2 * nodes x - 1 ≤ 4 * x + 1 := by + have := nodes_add_one_le x + omega + +/-- With fuel `4 x + 1`, the machine returns the number of leaves, on every input. -/ +theorem run_halving (x : ℕ) : + halving.run (fun x => 4 * x + 1) id x = some (leaves x) := + Scheme.run_eq_some (eval_halving x) (two_mul_nodes_le x) + +/-- **The polynomial-time components** of the halving scheme, in binary. -/ +theorem halving_polyTime : halving.PolyTime encNat (encPair encNat encNat) encNat where + isLeaf := ComputableInPolyTime.binary_lt' ComputableInPolyTime.id' + (ComputableInPolyTime.const 2) + start := ComputableInPolyTime.const (0, 0) + next := by + have hk : ComputableInPolyTime (fun p : ℕ × (ℕ × ℕ) => p.2.1) + (encPair encNat (encPair encNat encNat)) encNat := + (ComputableInPolyTime.fst encNat encNat).comp (ComputableInPolyTime.snd _ _) + have hhalf : ComputableInPolyTime (fun p : ℕ × (ℕ × ℕ) => p.1 / 2) + (encPair encNat (encPair encNat encNat)) encNat := + (ComputableInPolyTime.binary_shiftRight' (ComputableInPolyTime.fst _ _) + (ComputableInPolyTime.const 1)).of_enc_eq fun p => by + rw [Nat.shiftRight_eq_div_pow, pow_one] + refine (ComputableInPolyTime.ite (ComputableInPolyTime.binary_lt' hk + (ComputableInPolyTime.const 2)) ((ComputableInPolyTime.optionSome encNat).comp hhalf) + (ComputableInPolyTime.const Option.none)).of_enc_eq fun p => ?_ + by_cases h : p.2.1 < 2 + · have h' : p.2.1 ≤ 1 := by omega + simp [halving, h, h'] + · have h' : ¬p.2.1 ≤ 1 := by omega + simp [halving, h, h'] + absorb := by + have hk : ComputableInPolyTime (fun p : (ℕ × (ℕ × ℕ)) × ℕ => p.1.2.1) + (encPair (encPair encNat (encPair encNat encNat)) encNat) encNat := + (ComputableInPolyTime.fst encNat encNat).comp + ((ComputableInPolyTime.snd encNat _).comp (ComputableInPolyTime.fst _ _)) + have hs : ComputableInPolyTime (fun p : (ℕ × (ℕ × ℕ)) × ℕ => p.1.2.2) + (encPair (encPair encNat (encPair encNat encNat)) encNat) encNat := + (ComputableInPolyTime.snd encNat encNat).comp + ((ComputableInPolyTime.snd encNat _).comp (ComputableInPolyTime.fst _ _)) + exact (ComputableInPolyTime.binary_add.comp (hk.prod (ComputableInPolyTime.const 1))).prod + (ComputableInPolyTime.binary_add.comp (hs.prod (ComputableInPolyTime.snd _ _))) + finish := (ComputableInPolyTime.snd encNat encNat).comp (ComputableInPolyTime.snd _ _) + +/-- The fuel `4 x + 1`, in binary. -/ +noncomputable def fuelCertificate : NativeCertificate (fun x : ℕ => 4 * x + 1) encNat encNat := + NativeCertificate.ofPolyTime (ComputableInPolyTime.binary_add.comp + ((ComputableInPolyTime.binary_mul' (ComputableInPolyTime.const 4) + ComputableInPolyTime.id').prod (ComputableInPolyTime.const 1))) + +/-- The root is the input. -/ +noncomputable def rootCertificate : NativeCertificate (id : ℕ → ℕ) encNat encNat := + NativeCertificate.ofPolyTime ComputableInPolyTime.id' + +/-- The leaf step: the constant `1`. -/ +def leafCertificate : NativeCertificate halving.leaf encNat encNat := NativeCertificate.const 1 + +/-- **One fixed machine computes the number of leaves** of every input. -/ +noncomputable def halvingCertificate : + NativeCertificate (halving.run (fun x => 4 * x + 1) id) encNat (encOption encNat) := + Scheme.certificate halving_polyTime leafCertificate fuelCertificate rootCertificate + +/-- The size invariant below a root `x₀`: nodes are at most `x₀`, frames have counted at most two +children and summed at most `x₀ + 1`, and values are at most `x₀ + 1`. -/ +def bounded (x₀ : ℕ) : Invariant ℕ (ℕ × ℕ) ℕ := + ⟨fun y => y ≤ x₀, fun y a => y ≤ x₀ ∧ a.1 ≤ 2 ∧ a.2 ≤ x₀ + 1, fun v => v ≤ x₀ + 1⟩ + +theorem leaves_le_max (x : ℕ) : leaves x ≤ max x 1 := by + induction x using Nat.strong_induction_on with + | _ x ih => + by_cases hx : x < 2 + · rw [leaves]; simp only [hx, ite_true]; omega + · have := ih (x / 2) (by omega) + rw [leaves]; simp only [hx, ite_false] + omega + +theorem leaves_le (x : ℕ) : leaves x ≤ x + 1 := by + have := leaves_le_max x + omega + +/-- **Every node below `x₀` has a derivation keeping the size invariant.** -/ +theorem eval_halving_bounded {x₀ x : ℕ} (hx₀ : x ≤ x₀) : + halving.Eval (bounded x₀) x (leaves x) (nodes x) (height x) := by + induction x using Nat.strong_induction_on with + | _ x ih => + have hlx := leaves_le x + by_cases hx : x < 2 + · have hl : leaves x = 1 := by rw [leaves]; simp [hx] + have hn : nodes x = 1 := by rw [nodes]; simp [hx] + have hh : height x = 0 := by rw [height]; simp [hx] + rw [hl, hn, hh] + exact Scheme.Eval.leaf (R := halving) hx₀ (decide_eq_true hx) + (show 1 ≤ x₀ + 1 by omega) + · have hl : leaves x = leaves (x / 2) + leaves (x / 2) := by rw [leaves]; simp [hx]; ring + have hn : nodes x = (nodes (x / 2) + (nodes (x / 2) + 0)) + 1 := by + rw [nodes]; simp [hx]; ring + have hh : height x = max (height (x / 2)) (max (height (x / 2)) 0) + 1 := by + rw [height]; simp [hx] + have hc := ih (x / 2) (by omega) (by omega) + have hhalf := leaves_le (x / 2) + have hloop : halving.Loop (bounded x₀) x (halving.start x) + (2, 0 + leaves (x / 2) + leaves (x / 2)) (nodes (x / 2) + (nodes (x / 2) + 0)) + (max (height (x / 2)) (max (height (x / 2)) 0)) := + Scheme.Loop.child (show x ≤ x₀ ∧ 0 ≤ 2 ∧ 0 ≤ x₀ + 1 by omega) (by simp [halving]) hc + (Scheme.Loop.child (show x ≤ x₀ ∧ 1 ≤ 2 ∧ 0 + leaves (x / 2) ≤ x₀ + 1 by omega) + (by simp [halving]) hc (Scheme.Loop.done (by simp [halving]))) + have := Scheme.Eval.call (R := halving) (I := bounded x₀) hx₀ (decide_eq_false hx) hloop + (show 0 + leaves (x / 2) + leaves (x / 2) ≤ x₀ + 1 by omega) + rw [hl, hn, hh] + simpa [halving] using this + +private theorem length_encNat_mono {y z : ℕ} (h : y ≤ z) : + (encNat y).length ≤ (encNat z).length := by + simpa [Nat.size_eq_bits_len] using Nat.size_le_size h + +/-- **The resource theorem applies**: with `ℓ` the length of `x + 1` in binary, nodes, frames and +values are at most `F = 3 ℓ + 6` long, and the leaf costs at most `F`, so the time is +`4 x + 2` times a polynomial in `(height x + 1) (F + 1)`, and the workspace is polynomial in it. -/ +theorem halvingCertificate_le : ∃ K : ℕ, ∀ x : ℕ, + halvingCertificate.time x ≤ fuelCertificate.time x + rootCertificate.time x + + 2 * (encNat x).length + (4 * x + 1 + 1) * + (K * ((height x + 1) * (3 * (encNat (x + 1)).length + 6 + 1)) ^ K + + (3 * (encNat (x + 1)).length + 6) + ((encNat (4 * x + 1)).length + 1) ^ 2) ∧ + halvingCertificate.space x ≤ fuelCertificate.space x + rootCertificate.space x + + 2 * (encNat x).length + + K * ((height x + 1) * (3 * (encNat (x + 1)).length + 6 + 1)) ^ K + + (3 * (encNat (x + 1)).length + 6) + (encNat (4 * x + 1)).length := by + obtain ⟨K, hK⟩ := Scheme.certificate_le halving_polyTime leafCertificate + refine ⟨K, fun x => ?_⟩ + have h2 : (encNat 2).length ≤ 2 := by decide + refine hK fuelCertificate rootCertificate (bounded x) (3 * (encNat (x + 1)).length + 6) _ _ + (fun y hy => ⟨?_, fun _ => ⟨?_, ?_⟩⟩) (fun y a ha => ?_) (fun v hv => ?_) x (leaves x) + (nodes x) (height x) (eval_halving_bounded le_rfl) + · have := length_encNat_mono (show y ≤ x + 1 by simp only [bounded] at hy; omega) + omega + · have := length_encNat_mono (show y ≤ x + 1 by simp only [bounded] at hy; omega) + simp only [leafCertificate, NativeCertificate.const_time] + omega + · have := length_encNat_mono (show y ≤ x + 1 by simp only [bounded] at hy; omega) + simp only [leafCertificate, NativeCertificate.const_space] + omega + · obtain ⟨hy, hk, hs⟩ := ha + have h1 := length_encNat_mono (show y ≤ x + 1 by omega) + have h3 := length_encNat_mono (show a.2 ≤ x + 1 by omega) + have h4 := (length_encNat_mono hk).trans h2 + simp only [length_encPair] + omega + · have := length_encNat_mono (show v ≤ x + 1 by simp only [bounded] at hv; omega) + omega + +/-! ### Node-count arithmetic -/ + +-- Binary branching for three levels: `1 + 2 (1 + 2 (1 + 2)) = 15` nodes. +example : Nat.recursionBound (fun _ => 2) 3 0 = 15 := rfl + +-- The product bound: `15 ≤ 4 * 2 ^ 3`. +example : Nat.recursionBound (fun _ => 2) 3 0 ≤ (3 + 1) * ∏ _j ∈ Finset.range 3, max 1 2 := + Nat.recursionBound_le _ 3 0 + +-- Halving excess exponents: `100 + 50 + 25 + 12 + 6 = 193 ≤ 200`. +example : ∑ j ∈ Finset.range 5, 100 / 2 ^ j ≤ 2 * 100 := Nat.sum_div_two_pow_le 100 5 + +end ComplexityTests.StackRecursion From d9722cc3898cd3fa92c7828961b82c768914bc1c Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:21:07 -0400 Subject: [PATCH 015/129] chore: merge main and regenerate Complexity.lean Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Complexity.lean b/Complexity.lean index 0c8c217b24..735fba8eb9 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -869,6 +869,7 @@ public import Complexity.Computability.Circuit.Frontier.Rectangle public import Complexity.Computability.Circuit.Frontier.Reduction public import Complexity.Computability.Circuit.Frontier.Sweep public import Complexity.Computability.Circuit.Frontier.TensorNetwork +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Binarize public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CauchyModes public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation @@ -877,11 +878,6 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCo public import Complexity.Computability.Circuit.Frontier.TensorNetwork.LocalDeformation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion public import Complexity.Computability.Circuit.Frontier.TensorNetwork.TaperedRecovery -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Binarize -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness public import Complexity.Computability.Circuit.Frontier.Unconditional public import Complexity.Computability.Circuit.Frontier.Witness From 99d9e0d69372a439fe89c955398c74361686e888 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:28:05 -0400 Subject: [PATCH 016/129] feat(PolyTime): arithmetic in (Z/2^p)[U]/(f) on coefficient lists Addition, multiplication by U and shift-and-add multiplication modulo 2^p and a monic f given by its lower coefficients, proved correct in AdjoinRoot (f mod 2^p) and certified polynomial-time with p in unary and coordinates in binary. These are the polynomial-time arithmetic claims of [OpenAI2026ETH, Appendix D.4.1] for addition and multiplication (l = 1). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../PolyTime/GaloisRingArithmetic.lean | 446 ++++++++++++++++++ ComplexityTests/GaloisRingArithmetic.lean | 59 +++ 3 files changed, 506 insertions(+) create mode 100644 Complexity/Computability/PolyTime/GaloisRingArithmetic.lean create mode 100644 ComplexityTests/GaloisRingArithmetic.lean diff --git a/Complexity.lean b/Complexity.lean index fdd3b220c6..566b83dcbd 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1985,6 +1985,7 @@ public import Complexity.Computability.PolyTime.CNF.SparsePolyCompile public import Complexity.Computability.PolyTime.CSP.ToCNF public import Complexity.Computability.PolyTime.CommonCountingAccum public import Complexity.Computability.PolyTime.FineGrained.ParameterCheck +public import Complexity.Computability.PolyTime.GaloisRingArithmetic public import Complexity.Computability.PolyTime.GreedyLabelSchedule public import Complexity.Computability.PolyTime.GreedySlotScore public import Complexity.Computability.PolyTime.Int.ExactDiv diff --git a/Complexity/Computability/PolyTime/GaloisRingArithmetic.lean b/Complexity/Computability/PolyTime/GaloisRingArithmetic.lean new file mode 100644 index 0000000000..b46df67660 --- /dev/null +++ b/Complexity/Computability/PolyTime/GaloisRingArithmetic.lean @@ -0,0 +1,446 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Complexity.Computability.Machines.Turing.MultiTape.PolyTime +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.List +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Nat.Bits +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Unary +public import Mathlib.Algebra.BigOperators.Group.Finset.Basic +public import Mathlib.Data.ZMod.Basic +public import Mathlib.RingTheory.AdjoinRoot +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Fold +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Binary +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Length +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryDivision +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListLookup +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListMap +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeLoops +import Mathlib.Algebra.BigOperators.Intervals +import Mathlib.Data.List.GetD +import Mathlib.Data.Nat.Size + +/-! +# Arithmetic modulo `2 ^ p` and a monic polynomial, on coefficient lists + +The coefficient rings of the contraction algorithm of [OpenAI2026ETH] (Appendix D.4.1, p. 57) are +`A_p = (ℤ / 2 ^ p)[U] / (f)` for a monic `f ∈ ℤ[U]` of degree `e`. An element is represented in the +basis `1, U, …, U ^ (e - 1)` by its list of coordinates, each an integer residue modulo `2 ^ p`. +The monic polynomial is given by the list `fl` of its coefficients below the leading one +(`GaloisRingArith.monicOf`), so `e` is the length of `fl`. With `f` irreducible modulo `2`, `A_p` is +the Galois ring `(ℤ / 2 ^ p)[U] / (f)`; nothing here needs irreducibility. + +The value of a coefficient list `c` is `∑ i < e, c i U ^ i` in `A_p` (`GaloisRingArith.val`). + +* **Addition** (`GaloisRingArith.add`) adds coordinates modulo `2 ^ p`. +* **Multiplication by `U`** (`GaloisRingArith.mulX`) shifts the coordinates up and subtracts the top + coordinate times `f`, reducing `U ^ e = -∑ i < e, fl i U ^ i`. +* **Multiplication** (`GaloisRingArith.mul`) is shift-and-add: `c d = ∑ i < e, c i (U ^ i d)`, + accumulating `c i` times the current `U ^ i d` and multiplying it by `U`, `e` times. + +Each operation is correct (`GaloisRingArith.val_add`, `GaloisRingArith.val_mulX`, +`GaloisRingArith.val_mul`) and polynomial-time, with the precision `p` in unary and the +coordinates and the coefficients of `f` in binary (`GaloisRingArith.computableInPolyTime_add`, +`GaloisRingArith.computableInPolyTime_mul`). Their outputs are reduced, with `e` coordinates below +`2 ^ p` (`GaloisRingArith.length_mul`, `GaloisRingArith.lt_of_mem_mul`). These are the +polynomial-time arithmetic claims of Appendix D.4.1 for addition and multiplication, in the case +`ℓ = 1` of the source, where `A = O` has no `V`-coordinates. Inversion of units, which the source +does by Newton lifting, is not covered here. + +## Main definitions + +* `Cslib.GaloisRingArith.monicOf`: the monic polynomial with given lower coefficients. +* `Cslib.GaloisRingArith.val`: the value of a coefficient list. +* `Cslib.GaloisRingArith.add`, `Cslib.GaloisRingArith.mulX`, `Cslib.GaloisRingArith.mul`: addition, + multiplication by `U` and multiplication. + +## Main results + +* `Cslib.GaloisRingArith.val_add`, `Cslib.GaloisRingArith.val_mulX`, + `Cslib.GaloisRingArith.val_mul`: correctness. +* `Cslib.GaloisRingArith.computableInPolyTime_add`, + `Cslib.GaloisRingArith.computableInPolyTime_mulX`, + `Cslib.GaloisRingArith.computableInPolyTime_mul`: polynomial time. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Appendix D.4.1 (p. 57). +-/ + +@[expose] public section + +namespace Cslib.GaloisRingArith + +open Polynomial Finset + +/-- The monic polynomial `U ^ e + ∑ i < e, fl i U ^ i` of degree `e = fl.length`. -/ +noncomputable def monicOf (fl : List ℕ) : ℤ[X] := + X ^ fl.length + ∑ i ∈ range fl.length, C (fl.getD i 0 : ℤ) * X ^ i + +/-- `monicOf fl` is monic, of degree `fl.length`. -/ +theorem monic_monicOf (fl : List ℕ) : (monicOf fl).Monic := by + unfold monicOf + apply monic_X_pow_add + refine (degree_sum_le _ _).trans_lt ?_ + rw [Finset.sup_lt_iff (WithBot.bot_lt_coe _)] + intro i hi + exact (degree_C_mul_X_pow_le _ _).trans_lt (WithBot.coe_lt_coe.mpr (mem_range.mp hi)) + +/-- The ring `A_p = (ℤ / 2 ^ p)[U] / (f)` for `f = monicOf fl`. -/ +abbrev Ring (p : ℕ) (fl : List ℕ) : Type := + AdjoinRoot ((monicOf fl).map (Int.castRingHom (ZMod (2 ^ p)))) + +/-- The value `∑ i < e, c i U ^ i` of a coefficient list in `A_p`, with `e = fl.length`. +Coordinates past `e` are ignored and missing ones are zero. -/ +noncomputable def val (p : ℕ) (fl c : List ℕ) : Ring p fl := + ∑ i ∈ range fl.length, ((c.getD i 0 : ℕ) : Ring p fl) * AdjoinRoot.root _ ^ i + +/-- Coordinatewise sum modulo `2 ^ p`. -/ +def add (p : ℕ) (fl c d : List ℕ) : List ℕ := + (List.range fl.length).map fun i => (c.getD i 0 + d.getD i 0) % 2 ^ p + +/-- Add `k` times `d` to `c`, coordinatewise modulo `2 ^ p`. -/ +def addSmul (p : ℕ) (fl c : List ℕ) (k : ℕ) (d : List ℕ) : List ℕ := + (List.range fl.length).map fun i => (c.getD i 0 + k * d.getD i 0) % 2 ^ p + +/-- Multiplication by `U`: shift the coordinates up and subtract the top coordinate times the +lower coefficients of `f`, modulo `2 ^ p`. -/ +def mulX (p : ℕ) (fl c : List ℕ) : List ℕ := + (List.range fl.length).map fun i => + ((0 :: c).getD i 0 + (2 ^ p - c.getD (fl.length - 1) 0 * fl.getD i 0 % 2 ^ p)) % 2 ^ p + +/-- The zero element, with `e` coordinates. -/ +def zero (fl : List ℕ) : List ℕ := (List.range fl.length).map fun _ => 0 + +/-- One step of shift-and-add multiplication by `c`: from `(U ^ i d, ∑ j < i, c j U ^ j d)`, +add `c i U ^ i d` to the accumulator and multiply the current power by `U`. -/ +def mulStep (p : ℕ) (fl c : List ℕ) (s : List ℕ × List ℕ) (i : ℕ) : List ℕ × List ℕ := + (mulX p fl s.1, addSmul p fl s.2 (c.getD i 0) s.1) + +/-- **Multiplication** by shift-and-add. -/ +def mul (p : ℕ) (fl c d : List ℕ) : List ℕ := + ((List.range fl.length).foldl (mulStep p fl c) (d, zero fl)).2 + +/-! ### Correctness -/ + +section Correctness + +variable {p : ℕ} {fl : List ℕ} + +theorem natCast_two_pow : ((2 ^ p : ℕ) : Ring p fl) = 0 := by + rw [← map_natCast (algebraMap (ZMod (2 ^ p)) (Ring p fl)), ZMod.natCast_self, map_zero] + +theorem natCast_mod_two_pow (x : ℕ) : ((x % 2 ^ p : ℕ) : Ring p fl) = (x : Ring p fl) := by + conv_rhs => rw [← Nat.mod_add_div x (2 ^ p)] + rw [Nat.cast_add, Nat.cast_mul, natCast_two_pow, zero_mul, add_zero] + +/-- The root satisfies `U ^ e = -∑ i < e, fl i U ^ i`. -/ +theorem root_pow_length : + (AdjoinRoot.root _ : Ring p fl) ^ fl.length = + -∑ i ∈ range fl.length, ((fl.getD i 0 : ℕ) : Ring p fl) * AdjoinRoot.root _ ^ i := by + have hmap : (monicOf fl).map (Int.castRingHom (ZMod (2 ^ p))) = X ^ fl.length + + ∑ i ∈ range fl.length, C ((fl.getD i 0 : ℕ) : ZMod (2 ^ p)) * X ^ i := by + simp [monicOf, Polynomial.map_sum] + have h := AdjoinRoot.eval₂_root ((monicOf fl).map (Int.castRingHom (ZMod (2 ^ p)))) + conv at h => lhs; arg 3; rw [hmap] + simp only [eval₂_add, eval₂_finsetSum, eval₂_mul, eval₂_natCast, eval₂_X_pow, + map_natCast] at h + exact eq_neg_of_add_eq_zero_left h + +@[simp] theorem val_zero : val p fl (zero fl) = 0 := by + refine sum_eq_zero fun i hi => ?_ + rw [zero, List.getD_eq_getElem _ _ (by simpa using hi)] + simp + +/-- **Addition is correct.** -/ +theorem val_add (c d : List ℕ) : val p fl (add p fl c d) = val p fl c + val p fl d := by + rw [val, val, val, ← sum_add_distrib] + refine sum_congr rfl fun i hi => ?_ + rw [add, List.getD_eq_getElem _ _ (by simpa using hi)] + simp only [List.getElem_map, List.getElem_range, natCast_mod_two_pow, Nat.cast_add, add_mul] + +/-- **Adding a multiple is correct.** -/ +theorem val_addSmul (c : List ℕ) (k : ℕ) (d : List ℕ) : + val p fl (addSmul p fl c k d) = val p fl c + (k : Ring p fl) * val p fl d := by + rw [val, val, val, mul_sum, ← sum_add_distrib] + refine sum_congr rfl fun i hi => ?_ + rw [addSmul, List.getD_eq_getElem _ _ (by simpa using hi)] + simp only [List.getElem_map, List.getElem_range, natCast_mod_two_pow, Nat.cast_add, + Nat.cast_mul] + ring + +/-- Shifting coordinates and subtracting the top one times the lower coefficients of `f` is +multiplication by a root of `f`, in any commutative ring. -/ +private theorem shift_sub_mul_sum {R : Type*} [CommRing R] (r : R) (cs fs : ℕ → R) (e : ℕ) + (hroot : r ^ (e + 1) = -∑ i ∈ range (e + 1), fs i * r ^ i) : + ∑ i ∈ range (e + 1), ((if i = 0 then 0 else cs (i - 1)) - cs e * fs i) * r ^ i = + r * ∑ i ∈ range (e + 1), cs i * r ^ i := by + simp only [sub_mul, sum_sub_distrib] + rw [sum_range_succ' (fun i => (if i = 0 then 0 else cs (i - 1)) * r ^ i)] + simp only [Nat.add_sub_cancel, Nat.succ_ne_zero, ite_false, ite_true, zero_mul, add_zero] + rw [mul_sum, sum_range_succ (fun i => r * (cs i * r ^ i)), + show r * (cs e * r ^ e) = cs e * r ^ (e + 1) by ring, hroot, mul_neg, mul_sum, + ← sub_eq_add_neg] + congr 1 + · exact sum_congr rfl fun i _ => by ring + · exact sum_congr rfl fun i _ => by ring + +/-- **Multiplication by `U` is correct.** -/ +theorem val_mulX (c : List ℕ) : + val p fl (mulX p fl c) = AdjoinRoot.root _ * val p fl c := by + -- Each coordinate is the shifted coordinate minus the top coordinate times `fl i`. + have hcoord (i : ℕ) (hi : i < fl.length) : (((mulX p fl c).getD i 0 : ℕ) : Ring p fl) = + (if i = 0 then 0 else (c.getD (i - 1) 0 : Ring p fl)) - + (c.getD (fl.length - 1) 0 : Ring p fl) * (fl.getD i 0 : Ring p fl) := by + rw [mulX, List.getD_eq_getElem _ _ (by simpa using hi)] + simp only [List.getElem_map, List.getElem_range, natCast_mod_two_pow, Nat.cast_add] + rw [Nat.cast_sub (Nat.mod_lt _ (Nat.two_pow_pos p)).le, natCast_two_pow, natCast_mod_two_pow, + Nat.cast_mul] + rcases i with _ | i <;> simp [sub_eq_add_neg] + rcases Nat.eq_zero_or_pos fl.length with he | he + · simp [val, he] + obtain ⟨e, he'⟩ := Nat.exists_eq_add_of_lt he + rw [Nat.zero_add] at he' + have hroot := root_pow_length (p := p) (fl := fl) + rw [val, val, sum_congr rfl fun i hi => by rw [hcoord i (mem_range.mp hi)]] + rw [he', Nat.add_sub_cancel] at * + exact shift_sub_mul_sum _ (fun i => (c.getD i 0 : Ring p fl)) (fun i => (fl.getD i 0 : Ring p fl)) + e hroot + +/-- **Multiplication is correct.** -/ +theorem val_mul (c d : List ℕ) : val p fl (mul p fl c d) = val p fl c * val p fl d := by + -- After `j` steps the state is `(U ^ j d, ∑ i < j, c i U ^ i d)`. + have hinv (j : ℕ) : + val p fl ((List.range j).foldl (mulStep p fl c) (d, zero fl)).1 = + AdjoinRoot.root _ ^ j * val p fl d ∧ + val p fl ((List.range j).foldl (mulStep p fl c) (d, zero fl)).2 = + (∑ i ∈ range j, ((c.getD i 0 : ℕ) : Ring p fl) * AdjoinRoot.root _ ^ i) * val p fl d := by + induction j with + | zero => simp + | succ j ih => + rw [List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil, mulStep] + refine ⟨?_, ?_⟩ + · rw [val_mulX, ih.1, pow_succ]; ring + · rw [val_addSmul, ih.1, ih.2, sum_range_succ]; ring + rw [mul, (hinv fl.length).2] + rfl + +end Correctness + +/-! ### Reduced outputs -/ + +@[simp] theorem length_add (p : ℕ) (fl c d : List ℕ) : (add p fl c d).length = fl.length := by + simp [add] + +@[simp] theorem length_addSmul (p : ℕ) (fl c : List ℕ) (k : ℕ) (d : List ℕ) : + (addSmul p fl c k d).length = fl.length := by + simp [addSmul] + +@[simp] theorem length_mulX (p : ℕ) (fl c : List ℕ) : (mulX p fl c).length = fl.length := by + simp [mulX] + +@[simp] theorem length_zero (fl : List ℕ) : (zero fl).length = fl.length := by simp [zero] + +theorem lt_of_mem_add {p : ℕ} {fl c d : List ℕ} {x : ℕ} (hx : x ∈ add p fl c d) : x < 2 ^ p := by + obtain ⟨i, -, rfl⟩ := List.mem_map.mp hx + exact Nat.mod_lt _ (Nat.two_pow_pos p) + +theorem lt_of_mem_addSmul {p : ℕ} {fl c : List ℕ} {k : ℕ} {d : List ℕ} {x : ℕ} + (hx : x ∈ addSmul p fl c k d) : x < 2 ^ p := by + obtain ⟨i, -, rfl⟩ := List.mem_map.mp hx + exact Nat.mod_lt _ (Nat.two_pow_pos p) + +theorem lt_of_mem_mulX {p : ℕ} {fl c : List ℕ} {x : ℕ} (hx : x ∈ mulX p fl c) : x < 2 ^ p := by + obtain ⟨i, -, rfl⟩ := List.mem_map.mp hx + exact Nat.mod_lt _ (Nat.two_pow_pos p) + +theorem length_mul (p : ℕ) (fl c d : List ℕ) : (mul p fl c d).length = fl.length := by + rcases Nat.eq_zero_or_pos fl.length with he | he + · simp [mul, he, zero] + obtain ⟨e, he'⟩ := Nat.exists_eq_add_of_lt he + rw [mul, Nat.zero_add] at * + rw [he', List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil, mulStep, + length_addSmul, he'] + +theorem lt_of_mem_mul {p : ℕ} {fl c d : List ℕ} {x : ℕ} (hx : x ∈ mul p fl c d) : x < 2 ^ p := by + rcases Nat.eq_zero_or_pos fl.length with he | he + · simp [mul, he, zero] at hx + obtain ⟨e, he'⟩ := Nat.exists_eq_add_of_lt he + rw [Nat.zero_add] at he' + rw [mul, he', List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil, + mulStep] at hx + exact lt_of_mem_addSmul hx + +/-! ### Polynomial time -/ + +section PolyTime + +open Turing.MultiTapeTM + +variable {α : Type} {encIn : α ↪ List Bool} + +/-- Reduction modulo `2 ^ p`, with `p` in unary. -/ +@[fun_prop] +theorem computableInPolyTime_mod_two_pow {x p : α → ℕ} + (hx : ComputableInPolyTime x encIn encNat) (hp : ComputableInPolyTime p encIn encUnary) : + ComputableInPolyTime (fun a => x a % 2 ^ p a) encIn encNat := + ComputableInPolyTime.binary_mod.comp (hx.prod (ComputableInPolyTime.binary_two_pow.comp hp)) + +/-- A coordinate, at a unary index. -/ +theorem computableInPolyTime_getD {c : α → List ℕ} {i : α → ℕ} + (hc : ComputableInPolyTime c encIn (encList encNat)) + (hi : ComputableInPolyTime i encIn encUnary) : + ComputableInPolyTime (fun a => (c a).getD (i a) 0) encIn encNat := + (ComputableInPolyTime.listGetD (encA := encNat) 0).comp (hc.prod hi) + +/-- Tabulating `e` coordinates, for a unary `e`. -/ +theorem computableInPolyTime_tabulate {n : α → ℕ} {g : α → ℕ → ℕ} + (hn : ComputableInPolyTime n encIn encUnary) + (hg : ComputableInPolyTime (fun q : α × ℕ => g q.1 q.2) (encPair encIn encUnary) encNat) : + ComputableInPolyTime (fun x => (List.range (n x)).map (g x)) encIn (encList encNat) := + ComputableInPolyTime.mapWithInput (ComputableInPolyTime.range.comp hn) hg + +private theorem length_polyTime {fl : α → List ℕ} + (hfl : ComputableInPolyTime fl encIn (encList encNat)) : + ComputableInPolyTime (fun x => (fl x).length) encIn encUnary := + (ComputableInPolyTime.length_list encNat).comp hfl + +/-- **Addition is polynomial-time**, with the precision in unary. -/ +@[fun_prop] +theorem computableInPolyTime_add {p : α → ℕ} {fl c d : α → List ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hc : ComputableInPolyTime c encIn (encList encNat)) + (hd : ComputableInPolyTime d encIn (encList encNat)) : + ComputableInPolyTime (fun x => add (p x) (fl x) (c x) (d x)) encIn (encList encNat) := by + have h1 := ComputableInPolyTime.fst encIn encUnary + have h2 := ComputableInPolyTime.snd encIn encUnary + exact computableInPolyTime_tabulate (length_polyTime hfl) + (computableInPolyTime_mod_two_pow (ComputableInPolyTime.binary_add.comp + ((computableInPolyTime_getD (hc.comp h1) h2).prod + (computableInPolyTime_getD (hd.comp h1) h2))) (hp.comp h1)) + +/-- **Adding a multiple is polynomial-time**, with the precision in unary. -/ +@[fun_prop] +theorem computableInPolyTime_addSmul {p k : α → ℕ} {fl c d : α → List ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hc : ComputableInPolyTime c encIn (encList encNat)) + (hk : ComputableInPolyTime k encIn encNat) + (hd : ComputableInPolyTime d encIn (encList encNat)) : + ComputableInPolyTime (fun x => addSmul (p x) (fl x) (c x) (k x) (d x)) encIn + (encList encNat) := by + have h1 := ComputableInPolyTime.fst encIn encUnary + have h2 := ComputableInPolyTime.snd encIn encUnary + exact computableInPolyTime_tabulate (length_polyTime hfl) + (computableInPolyTime_mod_two_pow (ComputableInPolyTime.binary_add.comp + ((computableInPolyTime_getD (hc.comp h1) h2).prod + (ComputableInPolyTime.binary_mul' (hk.comp h1) + (computableInPolyTime_getD (hd.comp h1) h2)))) (hp.comp h1)) + +/-- **Multiplication by `U` is polynomial-time**, with the precision in unary. -/ +@[fun_prop] +theorem computableInPolyTime_mulX {p : α → ℕ} {fl c : α → List ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hc : ComputableInPolyTime c encIn (encList encNat)) : + ComputableInPolyTime (fun x => mulX (p x) (fl x) (c x)) encIn (encList encNat) := by + have h1 := ComputableInPolyTime.fst encIn encUnary + have h2 := ComputableInPolyTime.snd encIn encUnary + have hshift := computableInPolyTime_getD + (ComputableInPolyTime.cons' (ComputableInPolyTime.const 0) (hc.comp h1)) h2 + have htop := computableInPolyTime_getD (hc.comp h1) + (ComputableInPolyTime.unary_sub' (length_polyTime (hfl.comp h1)) + (ComputableInPolyTime.const 1)) + have hsub := computableInPolyTime_mod_two_pow + (ComputableInPolyTime.binary_mul' htop (computableInPolyTime_getD (hfl.comp h1) h2)) + (hp.comp h1) + exact computableInPolyTime_tabulate (length_polyTime hfl) + (computableInPolyTime_mod_two_pow (ComputableInPolyTime.binary_add.comp (hshift.prod + (ComputableInPolyTime.binary_sub' (ComputableInPolyTime.binary_two_pow.comp (hp.comp h1)) + hsub))) (hp.comp h1)) + +/-- A reduced coefficient list, with `e` coordinates below `2 ^ p`, is at most +`e (2 p + 1)` long in binary. -/ +theorem length_encList_le {p e : ℕ} {c : List ℕ} (hlen : c.length = e) + (hlt : ∀ x ∈ c, x < 2 ^ p) : (encList encNat c).length ≤ e * (2 * p + 1) := by + rw [length_encList] + calc (c.map fun x => 2 * (encNat x).length + 1).sum + ≤ (c.map fun x => 2 * (encNat x).length + 1).length • (2 * p + 1) := + List.sum_le_length_nsmul _ _ fun n hn => by + obtain ⟨x, hx, rfl⟩ := List.mem_map.mp hn + have : (encNat x).length ≤ p := by + change x.bits.length ≤ p + rw [Nat.size_eq_bits_len] at * + exact (Nat.size_eq_bits_len x ▸ Nat.size_le.mpr (hlt x hx)) + omega + _ = e * (2 * p + 1) := by rw [List.length_map, hlen, smul_eq_mul] + +/-- **Multiplication is polynomial-time**, with the precision in unary. Every intermediate pair is +reduced after its first step, so its length is bounded by the precision and the degree. -/ +@[fun_prop] +theorem computableInPolyTime_mul {p : α → ℕ} {fl c d : α → List ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hc : ComputableInPolyTime c encIn (encList encNat)) + (hd : ComputableInPolyTime d encIn (encList encNat)) : + ComputableInPolyTime (fun x => mul (p x) (fl x) (c x) (d x)) encIn (encList encNat) := by + let encS := encPair (encList encNat) (encList encNat) + have hinit : ComputableInPolyTime (fun x => (d x, zero (fl x))) encIn encS := + hd.prod (computableInPolyTime_tabulate (g := fun _ _ => 0) (length_polyTime hfl) + (ComputableInPolyTime.const 0)) + have hrange : ComputableInPolyTime (fun x => List.range (fl x).length) encIn + (encList encUnary) := + ComputableInPolyTime.range.comp (length_polyTime hfl) + -- The step, on the input, the state and the unary index. + have hstep : ComputableInPolyTime + (fun q : (α × (List ℕ × List ℕ)) × ℕ => mulStep (p q.1.1) (fl q.1.1) (c q.1.1) q.1.2 q.2) + (encPair (encPair encIn encS) encUnary) encS := by + have hq := ComputableInPolyTime.fst (encPair encIn encS) encUnary + have hx := (ComputableInPolyTime.fst encIn encS).comp hq + have hs := (ComputableInPolyTime.snd encIn encS).comp hq + have hi := ComputableInPolyTime.snd (encPair encIn encS) encUnary + have hcur := (ComputableInPolyTime.fst (encList encNat) (encList encNat)).comp hs + have hacc := (ComputableInPolyTime.snd (encList encNat) (encList encNat)).comp hs + exact (computableInPolyTime_mulX (hp.comp hx) (hfl.comp hx) hcur).prod + (computableInPolyTime_addSmul (hp.comp hx) (hfl.comp hx) hacc + (computableInPolyTime_getD (hc.comp hx) hi) hcur) + -- Every step's output is reduced, hence of bounded length. + have hgrowth : PolyBounded (fun x => (encIn x).length) + (fun x => 3 * ((fl x).length * (2 * p x + 1)) + 1) := by + have hpl := (ComputableInPolyTime.polyBounded_length hp) + have hfll := (ComputableInPolyTime.polyBounded_length hfl) + refine (((PolyBounded.const 3).mul (hfll.mul (((PolyBounded.const 2).mul hpl).add + (PolyBounded.const 1)))).add (PolyBounded.const 1)).mono fun x => ?_ + have h1 : p x ≤ (encUnary (p x)).length := by simp + have h2 : (fl x).length ≤ (encList encNat (fl x)).length := by + rw [length_encList] + calc (fl x).length = ((fl x).map fun _ => 1).sum := by simp + _ ≤ ((fl x).map fun a => 2 * (encNat a).length + 1).sum := + List.sum_le_sum fun a _ => by omega + have := Nat.mul_le_mul h2 (Nat.add_le_add_right (Nat.mul_le_mul_left 2 h1) 1) + omega + have hfold := ComputableInPolyTime.foldlWithInput_of_bounded_growth + (encA := encUnary) (encL := encList encUnary) + (step := fun x s i => mulStep (p x) (fl x) (c x) s i) hinit + ⟨hrange, hstep, inferInstance, inferInstance⟩ ⟨hgrowth, fun x s i _ => ?_⟩ + · exact (ComputableInPolyTime.snd (encList encNat) (encList encNat)).comp hfold + · have hcur := length_encList_le (length_mulX (p x) (fl x) s.1) fun _ h => lt_of_mem_mulX h + have hacc := length_encList_le (length_addSmul (p x) (fl x) s.2 ((c x).getD i 0) s.1) + fun _ h => lt_of_mem_addSmul h + simp only [mulStep, encS, length_encPair] + omega + +end PolyTime + +end Cslib.GaloisRingArith diff --git a/ComplexityTests/GaloisRingArithmetic.lean b/ComplexityTests/GaloisRingArithmetic.lean new file mode 100644 index 0000000000..566fca586f --- /dev/null +++ b/ComplexityTests/GaloisRingArithmetic.lean @@ -0,0 +1,59 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.PolyTime.GaloisRingArithmetic + +/-! # Arithmetic in `(ℤ / 2 ^ p)[U] / (f)` on coefficient lists + +Concrete products in the Galois ring `(ℤ / 8)[U] / (U ^ 2 + U + 1)` (`p = 3`, `fl = [1, 1]`), the +correctness statements, and the polynomial-time certificates in closed form, with the precision +in unary and the coordinates in binary. -/ + +namespace ComplexityTests.GaloisRingArithmetic + +open Cslib.GaloisRingArith Turing.MultiTapeTM + +-- `U * U = U ^ 2 = -1 - U`, that is `7 + 7 U` modulo `8`. +example : mulX 3 [1, 1] [0, 1] = [7, 7] := by decide + +-- `(1 + 2 U) (3 + U) = 3 + 7 U + 2 U ^ 2 = 1 + 5 U` modulo `8` and `U ^ 2 + U + 1`. +example : mul 3 [1, 1] [1, 2] [3, 1] = [1, 5] := by decide + +-- Addition reduces each coordinate: `(7 + 5 U) + (3 + 6 U) = 2 + 3 U` modulo `8`. +example : add 3 [1, 1] [7, 5] [3, 6] = [2, 3] := by decide + +-- `U` has order `3` here: `U ^ 3 = 1`. +example : mul 3 [1, 1] [0, 1] (mul 3 [1, 1] [0, 1] [0, 1]) = [1, 0] := by decide + +-- Unreduced inputs are reduced: missing coordinates are zero, extra ones ignored. +example : mul 3 [1, 1] [9] [3, 1, 4] = [3, 1] := by decide + +-- Degree three, `f = U ^ 3 + U + 1`, modulo `16`: `U ^ 2 * U = U ^ 3 = -1 - U`. +example : mulX 4 [1, 1, 0] [0, 0, 1] = [15, 15, 0] := by decide + +/-- The defining polynomial is monic. -/ +example : (monicOf [1, 1]).Monic := monic_monicOf _ + +/-- Multiplication is correct in the ring itself. -/ +example (c d : List ℕ) : val 3 [1, 1] (mul 3 [1, 1] c d) = val 3 [1, 1] c * val 3 [1, 1] d := + val_mul c d + +/-- **Multiplication in polynomial time**, on the ring description `(p, fl)` with `p` in unary +and the two coefficient lists in binary. -/ +example : ComputableInPolyTime + (fun q : (ℕ × List ℕ) × (List ℕ × List ℕ) => mul q.1.1 q.1.2 q.2.1 q.2.2) + (encPair (encPair encUnary (encList encNat)) (encPair (encList encNat) (encList encNat))) + (encList encNat) := by + fun_prop + +/-- **Addition in polynomial time**, in the same encoding. -/ +example : ComputableInPolyTime + (fun q : (ℕ × List ℕ) × (List ℕ × List ℕ) => add q.1.1 q.1.2 q.2.1 q.2.2) + (encPair (encPair encUnary (encList encNat)) (encPair (encList encNat) (encList encNat))) + (encList encNat) := by + fun_prop + +end ComplexityTests.GaloisRingArithmetic From 27d031198052460748576f5c64eee82e3c841af6 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:29:37 -0400 Subject: [PATCH 017/129] doc(GenericRAM/Native): qualify the module path in the stack recursion docstring Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity/Computability/GenericRAM/Native/StackRecursion.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Complexity/Computability/GenericRAM/Native/StackRecursion.lean b/Complexity/Computability/GenericRAM/Native/StackRecursion.lean index c2fd37dd11..fadf0610d0 100644 --- a/Complexity/Computability/GenericRAM/Native/StackRecursion.lean +++ b/Complexity/Computability/GenericRAM/Native/StackRecursion.lean @@ -41,7 +41,7 @@ within time `TL` and space `SL`, the time is at most `fuel a + 1` times the cost of computing the fuel and the root and of the binary clock, for one constant `K` of the scheme (`Scheme.certificate_le`). Only the stack is kept, so the workspace grows with the depth, not with the number of nodes. The number of nodes of a recursion tree is bounded in -`Foundations/Data/Nat/RecursionTree.lean`. +`Complexity/Foundations/Data/Nat/RecursionTree.lean`. This is the depth-first evaluation of the recursion in the proof of Theorem D.1 of [OpenAI2026ETH] (Appendix D.4.7, p. 64), which keeps one sum over mode tuples and one product From 70f4cd51d4fae5ef1cd772bdca7f609ed8894e77 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:34:00 -0400 Subject: [PATCH 018/129] feat(PolyTime/Nat): the clock 2^(K n / sqrt(floor(log2 n))) in polynomial time The fuel of the stack recursion on the scale of ContractionAlgorithm, computed in binary from n in unary. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Complexity/PolyTime/Nat/SqrtLog.lean | 53 +++++++++++++++++++ ComplexityTests/StackRecursion.lean | 14 +++++ 3 files changed, 68 insertions(+) create mode 100644 Complexity/Computability/Machines/Turing/MultiTape/Complexity/PolyTime/Nat/SqrtLog.lean diff --git a/Complexity.lean b/Complexity.lean index e3af2ca51c..c3020b1ba0 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1631,6 +1631,7 @@ public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Poly public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Prime public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Search public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Sqrt +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.SqrtLog public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Option public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Pi public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Plain diff --git a/Complexity/Computability/Machines/Turing/MultiTape/Complexity/PolyTime/Nat/SqrtLog.lean b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/PolyTime/Nat/SqrtLog.lean new file mode 100644 index 0000000000..7b6a566ec2 --- /dev/null +++ b/Complexity/Computability/Machines/Turing/MultiTape/Complexity/PolyTime/Nat/SqrtLog.lean @@ -0,0 +1,53 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Nat.Bits +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Unary +public import Complexity.Computability.Machines.Turing.MultiTape.PolyTime +public import Mathlib.Data.Nat.Log +public import Mathlib.Data.Nat.Sqrt +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.BitString +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Binary +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Div +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Log +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Sqrt +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeNatFrontends + +/-! +# The clock `2 ^ (K n / √⌊log₂ n⌋)` in polynomial time + +For a fixed `K`, the number `2 ^ (K n / √⌊log₂ n⌋)` (in the natural-number scale of +`Complexity/Foundations/Data/Nat/SqrtLog.lean`) is computable in binary in polynomial time from +`n` in unary. Its binary length is at most `K n + 1`. It serves as the fuel of clocked +subexponential algorithms, such as the depth-first recursion of the contraction theorem of +[OpenAI2026ETH] (Appendix D.4.7, p. 64), whose number of calls is `2 ^ O(n / √(log n))`. + +## Main results + +* `Turing.MultiTapeTM.ComputableInPolyTime.unary_two_pow_div_sqrt_log_two`: the clock. +-/ + +@[expose] public section + +namespace Turing.MultiTapeTM.ComputableInPolyTime + +/-- **The clock `2 ^ (K n / √⌊log₂ n⌋)`**, in binary from `n` in unary. -/ +theorem unary_two_pow_div_sqrt_log_two (K : ℕ) : + ComputableInPolyTime (fun n : ℕ => 2 ^ (K * n / Nat.sqrt (Nat.log 2 n))) encUnary encNat := by + have hb := unary_to_binary + have hlog : ComputableInPolyTime (fun n : ℕ => Nat.log 2 n) encUnary encUnary := + binary_log_two.comp hb + have hsq : ComputableInPolyTime (fun n : ℕ => Nat.sqrt (Nat.log 2 n)) encUnary encUnary := + binary_to_unary_bounded (binary_sqrt.comp (hb.comp hlog)) id' + (fun n => (Nat.sqrt_le_self _).trans (Nat.log_le_self 2 n)) + exact binary_two_pow.comp (unary_div.comp ((unary_mul' (const K) id').prod hsq)) + +end Turing.MultiTapeTM.ComputableInPolyTime diff --git a/ComplexityTests/StackRecursion.lean b/ComplexityTests/StackRecursion.lean index bcca97e434..34fbd89200 100644 --- a/ComplexityTests/StackRecursion.lean +++ b/ComplexityTests/StackRecursion.lean @@ -6,6 +6,7 @@ Authors: Samuel Schlesinger import Complexity.Computability.GenericRAM.Native.StackRecursion import Complexity.Foundations.Data.Nat.RecursionTree +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.SqrtLog import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Binary import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryPrimitives import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeOptionPrimitives @@ -254,4 +255,17 @@ example : Nat.recursionBound (fun _ => 2) 3 0 ≤ (3 + 1) * ∏ _j ∈ Finset.ra -- Halving excess exponents: `100 + 50 + 25 + 12 + 6 = 193 ≤ 200`. example : ∑ j ∈ Finset.range 5, 100 / 2 ^ j ≤ 2 * 100 := Nat.sum_div_two_pow_le 100 5 +/-! ### The clock -/ + +-- The fuel `2 ^ (3 n / √⌊log₂ n⌋)` of a subexponential recursion, from `n` in unary. +example : ComputableInPolyTime (fun n : ℕ => 2 ^ (3 * n / Nat.sqrt (Nat.log 2 n))) encUnary + encNat := + ComputableInPolyTime.unary_two_pow_div_sqrt_log_two 3 + +-- At `n = 16`: `⌊log₂ 16⌋ = 4`, `√4 = 2`, so the clock is `2 ^ 24`. +example : 2 ^ (3 * 16 / Nat.sqrt (Nat.log 2 16)) = 2 ^ 24 := by + have h1 : Nat.log 2 16 = 4 := by rw [show (16 : ℕ) = 2 ^ 4 by rfl, Nat.log_pow one_lt_two] + have h2 : Nat.sqrt 4 = 2 := (Nat.eq_sqrt.2 ⟨by norm_num, by norm_num⟩).symm + rw [h1, h2] + end ComplexityTests.StackRecursion From b9caee50441cd14c22ce4d3f0285c1f20bed8e32 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:36:29 -0400 Subject: [PATCH 019/129] feat(PolyTime): unit and powers in (Z/2^p)[U]/(f) Powers c^k by k multiplications, with k in unary (the mode exponents of [OpenAI2026ETH, Appendix D.4.6] are below the precision). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../PolyTime/GaloisRingArithmetic.lean | 93 ++++++++++++++++++- ComplexityTests/GaloisRingArithmetic.lean | 17 ++++ 2 files changed, 106 insertions(+), 4 deletions(-) diff --git a/Complexity/Computability/PolyTime/GaloisRingArithmetic.lean b/Complexity/Computability/PolyTime/GaloisRingArithmetic.lean index b46df67660..8a9bfcd2a5 100644 --- a/Complexity/Computability/PolyTime/GaloisRingArithmetic.lean +++ b/Complexity/Computability/PolyTime/GaloisRingArithmetic.lean @@ -47,6 +47,9 @@ The value of a coefficient list `c` is `∑ i < e, c i U ^ i` in `A_p` (`GaloisR coordinate times `f`, reducing `U ^ e = -∑ i < e, fl i U ^ i`. * **Multiplication** (`GaloisRingArith.mul`) is shift-and-add: `c d = ∑ i < e, c i (U ^ i d)`, accumulating `c i` times the current `U ^ i d` and multiplying it by `U`, `e` times. +* **Powers** (`GaloisRingArith.pow`) `c ^ k` take `k` multiplications from `GaloisRingArith.one`, + with `k` in unary. In the source the exponents of the mode substitutions are below the + precision (Appendix D.4.6, p. 63), so they are polynomial. Each operation is correct (`GaloisRingArith.val_add`, `GaloisRingArith.val_mulX`, `GaloisRingArith.val_mul`) and polynomial-time, with the precision `p` in unary and the @@ -61,16 +64,19 @@ does by Newton lifting, is not covered here. * `Cslib.GaloisRingArith.monicOf`: the monic polynomial with given lower coefficients. * `Cslib.GaloisRingArith.val`: the value of a coefficient list. -* `Cslib.GaloisRingArith.add`, `Cslib.GaloisRingArith.mulX`, `Cslib.GaloisRingArith.mul`: addition, - multiplication by `U` and multiplication. +* `Cslib.GaloisRingArith.add`, `Cslib.GaloisRingArith.mulX`, `Cslib.GaloisRingArith.mul`, + `Cslib.GaloisRingArith.one`, `Cslib.GaloisRingArith.pow`: addition, multiplication by `U`, + multiplication, the unit and powers. ## Main results * `Cslib.GaloisRingArith.val_add`, `Cslib.GaloisRingArith.val_mulX`, - `Cslib.GaloisRingArith.val_mul`: correctness. + `Cslib.GaloisRingArith.val_mul`, `Cslib.GaloisRingArith.val_one`, + `Cslib.GaloisRingArith.val_pow`: correctness. * `Cslib.GaloisRingArith.computableInPolyTime_add`, `Cslib.GaloisRingArith.computableInPolyTime_mulX`, - `Cslib.GaloisRingArith.computableInPolyTime_mul`: polynomial time. + `Cslib.GaloisRingArith.computableInPolyTime_mul`, + `Cslib.GaloisRingArith.computableInPolyTime_pow`: polynomial time. ## References @@ -131,6 +137,13 @@ def mulStep (p : ℕ) (fl c : List ℕ) (s : List ℕ × List ℕ) (i : ℕ) : L def mul (p : ℕ) (fl c d : List ℕ) : List ℕ := ((List.range fl.length).foldl (mulStep p fl c) (d, zero fl)).2 +/-- The unit element `1`, with `e` coordinates. -/ +def one (fl : List ℕ) : List ℕ := (List.range fl.length).map fun i => if i = 0 then 1 else 0 + +/-- **Powers** `c ^ k`, by `k` multiplications; the exponent is read in unary. -/ +def pow (p : ℕ) (fl c : List ℕ) (k : ℕ) : List ℕ := + (List.range k).foldl (fun acc _ => mul p fl acc c) (one fl) + /-! ### Correctness -/ section Correctness @@ -235,6 +248,34 @@ theorem val_mul (c d : List ℕ) : val p fl (mul p fl c d) = val p fl c * val p rw [mul, (hinv fl.length).2] rfl +/-- **The unit is correct.** -/ +@[simp] theorem val_one : val p fl (one fl) = 1 := by + rcases Nat.eq_zero_or_pos fl.length with he | he + · -- The defining polynomial is `1`, so the ring is trivial. + have h1 : (monicOf fl).map (Int.castRingHom (ZMod (2 ^ p))) = 1 := by + simp [monicOf, he] + have h : (1 : Ring p fl) = 0 := by + rw [← map_one (AdjoinRoot.mk ((monicOf fl).map (Int.castRingHom (ZMod (2 ^ p))))), + AdjoinRoot.mk_eq_zero, h1] + simp [val, he, h] + · obtain ⟨e, he'⟩ := Nat.exists_eq_add_of_lt he + rw [Nat.zero_add] at he' + rw [val, he', sum_range_succ'] + simp only [one, he'] + rw [sum_eq_zero fun i hi => by + rw [List.getD_eq_getElem _ _ (by simp at hi ⊢; omega)] + simp] + rw [List.getD_eq_getElem _ _ (by simp)] + simp + +/-- **Powers are correct.** -/ +theorem val_pow (c : List ℕ) (k : ℕ) : val p fl (pow p fl c k) = val p fl c ^ k := by + rw [pow] + induction k with + | zero => simp + | succ k ih => rw [List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil, + val_mul, ih, pow_succ] + end Correctness /-! ### Reduced outputs -/ @@ -441,6 +482,50 @@ theorem computableInPolyTime_mul {p : α → ℕ} {fl c d : α → List ℕ} simp only [mulStep, encS, length_encPair] omega +/-- **Powers are polynomial-time**, with the precision and the exponent in unary. -/ +@[fun_prop] +theorem computableInPolyTime_pow {p k : α → ℕ} {fl c : α → List ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hc : ComputableInPolyTime c encIn (encList encNat)) + (hk : ComputableInPolyTime k encIn encUnary) : + ComputableInPolyTime (fun x => pow (p x) (fl x) (c x) (k x)) encIn (encList encNat) := by + have hone : ComputableInPolyTime (fun x => one (fl x)) encIn (encList encNat) := by + have h2 := ComputableInPolyTime.snd encIn encUnary + refine computableInPolyTime_tabulate (g := fun _ i => if i = 0 then 1 else 0) + (length_polyTime hfl) ?_ + refine (ComputableInPolyTime.ite (encMid := encNat) (c := fun q : α × ℕ => q.2 == 0) + ((ComputableInPolyTime.beq (h2) (ComputableInPolyTime.const 0))) + (ComputableInPolyTime.const 1) (ComputableInPolyTime.const 0)).of_enc_eq fun q => ?_ + by_cases h : q.2 = 0 <;> simp [h] + have hstep : ComputableInPolyTime (fun q : (α × List ℕ) × ℕ => mul (p q.1.1) (fl q.1.1) q.1.2 + (c q.1.1)) (encPair (encPair encIn (encList encNat)) encUnary) (encList encNat) := by + have hq := ComputableInPolyTime.fst (encPair encIn (encList encNat)) encUnary + have hx := (ComputableInPolyTime.fst encIn (encList encNat)).comp hq + have hacc := (ComputableInPolyTime.snd encIn (encList encNat)).comp hq + exact computableInPolyTime_mul (hp.comp hx) (hfl.comp hx) hacc (hc.comp hx) + have hgrowth : PolyBounded (fun x => (encIn x).length) + (fun x => (fl x).length * (2 * p x + 1)) := by + have hpl := (ComputableInPolyTime.polyBounded_length hp) + have hfll := (ComputableInPolyTime.polyBounded_length hfl) + refine (hfll.mul (((PolyBounded.const 2).mul hpl).add (PolyBounded.const 1))).mono + fun x => ?_ + have h1 : p x ≤ (encUnary (p x)).length := by simp + have h2 : (fl x).length ≤ (encList encNat (fl x)).length := by + rw [length_encList] + calc (fl x).length = ((fl x).map fun _ => 1).sum := by simp + _ ≤ ((fl x).map fun a => 2 * (encNat a).length + 1).sum := + List.sum_le_sum fun a _ => by omega + exact Nat.mul_le_mul h2 (Nat.add_le_add_right (Nat.mul_le_mul_left 2 h1) 1) + have hfold := ComputableInPolyTime.foldlWithInput_of_bounded_growth + (encA := encUnary) (encL := encList encUnary) + (step := fun x acc _ => mul (p x) (fl x) acc (c x)) hone + ⟨ComputableInPolyTime.range.comp hk, hstep, inferInstance, inferInstance⟩ + ⟨hgrowth, fun x acc _ _ => ?_⟩ + · exact hfold + · have := length_encList_le (length_mul (p x) (fl x) acc (c x)) fun _ h => lt_of_mem_mul h + omega + end PolyTime end Cslib.GaloisRingArith diff --git a/ComplexityTests/GaloisRingArithmetic.lean b/ComplexityTests/GaloisRingArithmetic.lean index 566fca586f..f0e7710ba4 100644 --- a/ComplexityTests/GaloisRingArithmetic.lean +++ b/ComplexityTests/GaloisRingArithmetic.lean @@ -34,6 +34,16 @@ example : mul 3 [1, 1] [9] [3, 1, 4] = [3, 1] := by decide -- Degree three, `f = U ^ 3 + U + 1`, modulo `16`: `U ^ 2 * U = U ^ 3 = -1 - U`. example : mulX 4 [1, 1, 0] [0, 0, 1] = [15, 15, 0] := by decide +-- The unit, and `U ^ 3 = 1` as a power. +example : one [1, 1] = [1, 0] ∧ pow 3 [1, 1] [0, 1] 3 = [1, 0] := by decide + +-- `(1 + U) ^ 2 = 1 + 2 U + U ^ 2 = U`. +example : pow 3 [1, 1] [1, 1] 2 = [0, 1] := by decide + +/-- Powers are correct. -/ +example (c : List ℕ) (k : ℕ) : val 3 [1, 1] (pow 3 [1, 1] c k) = val 3 [1, 1] c ^ k := + val_pow c k + /-- The defining polynomial is monic. -/ example : (monicOf [1, 1]).Monic := monic_monicOf _ @@ -56,4 +66,11 @@ example : ComputableInPolyTime (encList encNat) := by fun_prop +/-- **Powers in polynomial time**, with the exponent in unary. -/ +example : ComputableInPolyTime + (fun q : (ℕ × List ℕ) × (List ℕ × ℕ) => pow q.1.1 q.1.2 q.2.1 q.2.2) + (encPair (encPair encUnary (encList encNat)) (encPair (encList encNat) encUnary)) + (encList encNat) := by + fun_prop + end ComplexityTests.GaloisRingArithmetic From dc4824e4e3a12d05ae3527e90eb88b985f9c6525 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:41:30 -0400 Subject: [PATCH 020/129] feat(PolyTime): negation, subtraction and Newton inversion in (Z/2^p)[U]/(f) The seed x^(2^e - 2) by e - 1 squarings, then p Newton steps y(2 - xy); correct whenever the seed inverts x modulo 2, which holds for units of a Galois ring. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../PolyTime/GaloisRingArithmetic.lean | 287 +++++++++++++++--- ComplexityTests/GaloisRingArithmetic.lean | 20 ++ 2 files changed, 266 insertions(+), 41 deletions(-) diff --git a/Complexity/Computability/PolyTime/GaloisRingArithmetic.lean b/Complexity/Computability/PolyTime/GaloisRingArithmetic.lean index 8a9bfcd2a5..123a3cfdb2 100644 --- a/Complexity/Computability/PolyTime/GaloisRingArithmetic.lean +++ b/Complexity/Computability/PolyTime/GaloisRingArithmetic.lean @@ -50,6 +50,12 @@ The value of a coefficient list `c` is `∑ i < e, c i U ^ i` in `A_p` (`GaloisR * **Powers** (`GaloisRingArith.pow`) `c ^ k` take `k` multiplications from `GaloisRingArith.one`, with `k` in unary. In the source the exponents of the mode substitutions are below the precision (Appendix D.4.6, p. 63), so they are polynomial. +* **Inversion** (`GaloisRingArith.inv`) starts from the seed `x ^ (2 ^ e - 2)`, a product of `e - 1` + repeated squares (`GaloisRingArith.seed`), and takes `p` Newton steps `y ↦ y (2 - x y)`, each + squaring the defect `x y - 1` (`GaloisRingArith.val_mul_newton`). When the seed inverts `x` + modulo `2`, the result is the inverse (`GaloisRingArith.val_mul_inv`). In a Galois ring, with + `f` irreducible modulo `2`, this holds for every unit, since the residue field has `2 ^ e` + elements; that bridge is not made here. Each operation is correct (`GaloisRingArith.val_add`, `GaloisRingArith.val_mulX`, `GaloisRingArith.val_mul`) and polynomial-time, with the precision `p` in unary and the @@ -57,26 +63,32 @@ coordinates and the coefficients of `f` in binary (`GaloisRingArith.computableIn `GaloisRingArith.computableInPolyTime_mul`). Their outputs are reduced, with `e` coordinates below `2 ^ p` (`GaloisRingArith.length_mul`, `GaloisRingArith.lt_of_mem_mul`). These are the polynomial-time arithmetic claims of Appendix D.4.1 for addition and multiplication, in the case -`ℓ = 1` of the source, where `A = O` has no `V`-coordinates. Inversion of units, which the source -does by Newton lifting, is not covered here. +`ℓ = 1` of the source, where `A = O` has no `V`-coordinates, together with the inversion of units +by inversion modulo two and Newton lifting that the source names (Appendix D.4.1, p. 58). ## Main definitions * `Cslib.GaloisRingArith.monicOf`: the monic polynomial with given lower coefficients. * `Cslib.GaloisRingArith.val`: the value of a coefficient list. * `Cslib.GaloisRingArith.add`, `Cslib.GaloisRingArith.mulX`, `Cslib.GaloisRingArith.mul`, - `Cslib.GaloisRingArith.one`, `Cslib.GaloisRingArith.pow`: addition, multiplication by `U`, - multiplication, the unit and powers. + `Cslib.GaloisRingArith.one`, `Cslib.GaloisRingArith.pow`, `Cslib.GaloisRingArith.neg`, + `Cslib.GaloisRingArith.sub`: addition, multiplication by `U`, multiplication, the unit, powers, + negation and subtraction. +* `Cslib.GaloisRingArith.seed`, `Cslib.GaloisRingArith.newton`, `Cslib.GaloisRingArith.inv`: + inversion. ## Main results * `Cslib.GaloisRingArith.val_add`, `Cslib.GaloisRingArith.val_mulX`, `Cslib.GaloisRingArith.val_mul`, `Cslib.GaloisRingArith.val_one`, - `Cslib.GaloisRingArith.val_pow`: correctness. + `Cslib.GaloisRingArith.val_pow`, `Cslib.GaloisRingArith.val_neg`, + `Cslib.GaloisRingArith.val_sub`, `Cslib.GaloisRingArith.val_seed`, + `Cslib.GaloisRingArith.val_mul_inv`: correctness. * `Cslib.GaloisRingArith.computableInPolyTime_add`, `Cslib.GaloisRingArith.computableInPolyTime_mulX`, `Cslib.GaloisRingArith.computableInPolyTime_mul`, - `Cslib.GaloisRingArith.computableInPolyTime_pow`: polynomial time. + `Cslib.GaloisRingArith.computableInPolyTime_pow`, + `Cslib.GaloisRingArith.computableInPolyTime_inv`: polynomial time. ## References @@ -144,6 +156,33 @@ def one (fl : List ℕ) : List ℕ := (List.range fl.length).map fun i => if i = def pow (p : ℕ) (fl c : List ℕ) (k : ℕ) : List ℕ := (List.range k).foldl (fun acc _ => mul p fl acc c) (one fl) +/-- Negation, coordinatewise modulo `2 ^ p`. -/ +def neg (p : ℕ) (fl c : List ℕ) : List ℕ := + (List.range fl.length).map fun i => (2 ^ p - c.getD i 0 % 2 ^ p) % 2 ^ p + +/-- Subtraction. -/ +def sub (p : ℕ) (fl c d : List ℕ) : List ℕ := add p fl c (neg p fl d) + +/-- One step of the seed of inversion: from `(x ^ 2 ^ j, x ^ (2 ^ (j + 1) - 2))`, square the power +and multiply it in. -/ +def seedStep (p : ℕ) (fl : List ℕ) (s : List ℕ × List ℕ) : List ℕ × List ℕ := + (mul p fl s.1 s.1, mul p fl s.2 (mul p fl s.1 s.1)) + +/-- The seed `x ^ (2 ^ e - 2) = x ^ 2 x ^ 4 ⋯ x ^ (2 ^ (e - 1))` of inversion, by `e - 1` +squarings. In a Galois ring it inverts `x` modulo `2`, since the residue field has `2 ^ e` +elements. -/ +def seed (p : ℕ) (fl x : List ℕ) : List ℕ := + ((List.range (fl.length - 1)).foldl (fun s _ => seedStep p fl s) (x, one fl)).2 + +/-- One Newton step `y (2 - x y)` towards the inverse of `x`. -/ +def newton (p : ℕ) (fl x y : List ℕ) : List ℕ := + mul p fl y (sub p fl (add p fl (one fl) (one fl)) (mul p fl x y)) + +/-- **Inversion**: `p` Newton steps from the seed. It is the inverse of `x` whenever the seed +inverts `x` modulo `2` (`val_mul_inv`), in particular for every unit of a Galois ring. -/ +def inv (p : ℕ) (fl x : List ℕ) : List ℕ := + (List.range p).foldl (fun y _ => newton p fl x y) (seed p fl x) + /-! ### Correctness -/ section Correctness @@ -276,6 +315,78 @@ theorem val_pow (c : List ℕ) (k : ℕ) : val p fl (pow p fl c k) = val p fl c | succ k ih => rw [List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil, val_mul, ih, pow_succ] +/-- **Negation is correct.** -/ +theorem val_neg (c : List ℕ) : val p fl (neg p fl c) = -val p fl c := by + rw [val, val, ← sum_neg_distrib] + refine sum_congr rfl fun i hi => ?_ + rw [neg, List.getD_eq_getElem _ _ (by simpa using hi)] + simp only [List.getElem_map, List.getElem_range, natCast_mod_two_pow] + rw [Nat.cast_sub (Nat.mod_lt _ (Nat.two_pow_pos p)).le, natCast_two_pow, natCast_mod_two_pow] + ring + +/-- **Subtraction is correct.** -/ +theorem val_sub (c d : List ℕ) : val p fl (sub p fl c d) = val p fl c - val p fl d := by + rw [sub, val_add, val_neg, sub_eq_add_neg] + +/-- **The seed is the power `x ^ (2 ^ e - 2)`.** -/ +theorem val_seed (x : List ℕ) : val p fl (seed p fl x) = val p fl x ^ (2 ^ fl.length - 2) := by + have hinv (j : ℕ) : + val p fl ((List.range j).foldl (fun s _ => seedStep p fl s) (x, one fl)).1 = + val p fl x ^ 2 ^ j ∧ + val p fl ((List.range j).foldl (fun s _ => seedStep p fl s) (x, one fl)).2 = + val p fl x ^ (2 ^ (j + 1) - 2) := by + induction j with + | zero => simp + | succ j ih => + rw [List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil, seedStep] + refine ⟨?_, ?_⟩ + · rw [val_mul, ih.1, ← pow_add, ← two_mul, ← pow_succ'] + · rw [val_mul, val_mul, ih.1, ih.2, ← pow_add, ← pow_add] + congr 1 + have : 2 ≤ 2 ^ (j + 1) := by + calc 2 = 2 ^ 1 := rfl + _ ≤ 2 ^ (j + 1) := Nat.pow_le_pow_right (by norm_num) (by omega) + rw [pow_succ 2 (j + 1)] + omega + rw [seed, (hinv _).2] + rcases Nat.eq_zero_or_pos fl.length with he | he + · simp [he] + · rw [Nat.sub_add_cancel he] + +/-- **Newton's step squares the defect**: if `x y = 1 + 2 ^ m w` then +`x (y (2 - x y)) = 1 + 2 ^ (2 m) (-w ^ 2)`. -/ +theorem val_mul_newton (x y : List ℕ) {m : ℕ} {w : Ring p fl} + (h : val p fl x * val p fl y = 1 + 2 ^ m * w) : + val p fl x * val p fl (newton p fl x y) = 1 + 2 ^ (2 * m) * -w ^ 2 := by + rw [newton, val_mul, val_sub, val_add, val_mul, val_one] + have : val p fl x * (val p fl y * (1 + 1 - val p fl x * val p fl y)) = + (val p fl x * val p fl y) * (2 - val p fl x * val p fl y) := by ring + rw [this, h, show (2 : Ring p fl) ^ (2 * m) = 2 ^ m * 2 ^ m by rw [two_mul, pow_add]] + ring + +/-- **Inversion is correct** when the seed inverts `x` modulo `2`. -/ +theorem val_mul_inv (x : List ℕ) {w : Ring p fl} + (h : val p fl x * val p fl x ^ (2 ^ fl.length - 2) = 1 + 2 * w) : + val p fl x * val p fl (inv p fl x) = 1 := by + have hstep (k : ℕ) : ∃ w' : Ring p fl, val p fl x * + val p fl ((List.range k).foldl (fun y _ => newton p fl x y) (seed p fl x)) = + 1 + 2 ^ 2 ^ k * w' := by + induction k with + | zero => exact ⟨w, by rw [List.range_zero, List.foldl_nil, val_seed, h, pow_zero, pow_one]⟩ + | succ k ih => + obtain ⟨w', hw'⟩ := ih + refine ⟨-w' ^ 2, ?_⟩ + rw [List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil, + val_mul_newton _ _ hw', show 2 ^ (k + 1) = 2 * 2 ^ k by ring] + obtain ⟨w', hw'⟩ := hstep p + rw [inv, hw'] + have hp : (2 : Ring p fl) ^ 2 ^ p = 0 := by + have h2p : (2 : Ring p fl) ^ p = 0 := by + rw [show (2 : Ring p fl) ^ p = ((2 ^ p : ℕ) : Ring p fl) by push_cast; rfl, + natCast_two_pow] + exact pow_eq_zero_of_le Nat.lt_two_pow_self.le h2p + rw [hp, zero_mul, add_zero] + end Correctness /-! ### Reduced outputs -/ @@ -427,6 +538,38 @@ theorem length_encList_le {p e : ℕ} {c : List ℕ} (hlen : c.length = e) omega _ = e * (2 * p + 1) := by rw [List.length_map, hlen, smul_eq_mul] +/-- The unit is polynomial-time. -/ +@[fun_prop] +theorem computableInPolyTime_one {fl : α → List ℕ} + (hfl : ComputableInPolyTime fl encIn (encList encNat)) : + ComputableInPolyTime (fun x => one (fl x)) encIn (encList encNat) := by + have h2 := ComputableInPolyTime.snd encIn encUnary + refine computableInPolyTime_tabulate (g := fun _ i => if i = 0 then 1 else 0) + (length_polyTime hfl) ?_ + refine (ComputableInPolyTime.ite (encMid := encNat) (c := fun q : α × ℕ => q.2 == 0) + ((ComputableInPolyTime.beq (h2) (ComputableInPolyTime.const 0))) + (ComputableInPolyTime.const 1) (ComputableInPolyTime.const 0)).of_enc_eq fun q => ?_ + by_cases h : q.2 = 0 <;> simp [h] + +/-- The length bound shared by the folds below: a pair of reduced lists. -/ +private theorem polyBounded_reduced {p : α → ℕ} {fl : α → List ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) : + PolyBounded (fun x => (encIn x).length) + (fun x => 3 * ((fl x).length * (2 * p x + 1)) + 1) := by + have hpl := (ComputableInPolyTime.polyBounded_length hp) + have hfll := (ComputableInPolyTime.polyBounded_length hfl) + refine (((PolyBounded.const 3).mul (hfll.mul (((PolyBounded.const 2).mul hpl).add + (PolyBounded.const 1)))).add (PolyBounded.const 1)).mono fun x => ?_ + have h1 : p x ≤ (encUnary (p x)).length := by simp + have h2 : (fl x).length ≤ (encList encNat (fl x)).length := by + rw [length_encList] + calc (fl x).length = ((fl x).map fun _ => 1).sum := by simp + _ ≤ ((fl x).map fun a => 2 * (encNat a).length + 1).sum := + List.sum_le_sum fun a _ => by omega + have := Nat.mul_le_mul h2 (Nat.add_le_add_right (Nat.mul_le_mul_left 2 h1) 1) + omega + /-- **Multiplication is polynomial-time**, with the precision in unary. Every intermediate pair is reduced after its first step, so its length is bounded by the precision and the degree. -/ @[fun_prop] @@ -457,20 +600,7 @@ theorem computableInPolyTime_mul {p : α → ℕ} {fl c d : α → List ℕ} (computableInPolyTime_addSmul (hp.comp hx) (hfl.comp hx) hacc (computableInPolyTime_getD (hc.comp hx) hi) hcur) -- Every step's output is reduced, hence of bounded length. - have hgrowth : PolyBounded (fun x => (encIn x).length) - (fun x => 3 * ((fl x).length * (2 * p x + 1)) + 1) := by - have hpl := (ComputableInPolyTime.polyBounded_length hp) - have hfll := (ComputableInPolyTime.polyBounded_length hfl) - refine (((PolyBounded.const 3).mul (hfll.mul (((PolyBounded.const 2).mul hpl).add - (PolyBounded.const 1)))).add (PolyBounded.const 1)).mono fun x => ?_ - have h1 : p x ≤ (encUnary (p x)).length := by simp - have h2 : (fl x).length ≤ (encList encNat (fl x)).length := by - rw [length_encList] - calc (fl x).length = ((fl x).map fun _ => 1).sum := by simp - _ ≤ ((fl x).map fun a => 2 * (encNat a).length + 1).sum := - List.sum_le_sum fun a _ => by omega - have := Nat.mul_le_mul h2 (Nat.add_le_add_right (Nat.mul_le_mul_left 2 h1) 1) - omega + have hgrowth := polyBounded_reduced hp hfl have hfold := ComputableInPolyTime.foldlWithInput_of_bounded_growth (encA := encUnary) (encL := encList encUnary) (step := fun x s i => mulStep (p x) (fl x) (c x) s i) hinit @@ -490,33 +620,14 @@ theorem computableInPolyTime_pow {p k : α → ℕ} {fl c : α → List ℕ} (hc : ComputableInPolyTime c encIn (encList encNat)) (hk : ComputableInPolyTime k encIn encUnary) : ComputableInPolyTime (fun x => pow (p x) (fl x) (c x) (k x)) encIn (encList encNat) := by - have hone : ComputableInPolyTime (fun x => one (fl x)) encIn (encList encNat) := by - have h2 := ComputableInPolyTime.snd encIn encUnary - refine computableInPolyTime_tabulate (g := fun _ i => if i = 0 then 1 else 0) - (length_polyTime hfl) ?_ - refine (ComputableInPolyTime.ite (encMid := encNat) (c := fun q : α × ℕ => q.2 == 0) - ((ComputableInPolyTime.beq (h2) (ComputableInPolyTime.const 0))) - (ComputableInPolyTime.const 1) (ComputableInPolyTime.const 0)).of_enc_eq fun q => ?_ - by_cases h : q.2 = 0 <;> simp [h] + have hone := computableInPolyTime_one hfl have hstep : ComputableInPolyTime (fun q : (α × List ℕ) × ℕ => mul (p q.1.1) (fl q.1.1) q.1.2 (c q.1.1)) (encPair (encPair encIn (encList encNat)) encUnary) (encList encNat) := by have hq := ComputableInPolyTime.fst (encPair encIn (encList encNat)) encUnary have hx := (ComputableInPolyTime.fst encIn (encList encNat)).comp hq have hacc := (ComputableInPolyTime.snd encIn (encList encNat)).comp hq exact computableInPolyTime_mul (hp.comp hx) (hfl.comp hx) hacc (hc.comp hx) - have hgrowth : PolyBounded (fun x => (encIn x).length) - (fun x => (fl x).length * (2 * p x + 1)) := by - have hpl := (ComputableInPolyTime.polyBounded_length hp) - have hfll := (ComputableInPolyTime.polyBounded_length hfl) - refine (hfll.mul (((PolyBounded.const 2).mul hpl).add (PolyBounded.const 1))).mono - fun x => ?_ - have h1 : p x ≤ (encUnary (p x)).length := by simp - have h2 : (fl x).length ≤ (encList encNat (fl x)).length := by - rw [length_encList] - calc (fl x).length = ((fl x).map fun _ => 1).sum := by simp - _ ≤ ((fl x).map fun a => 2 * (encNat a).length + 1).sum := - List.sum_le_sum fun a _ => by omega - exact Nat.mul_le_mul h2 (Nat.add_le_add_right (Nat.mul_le_mul_left 2 h1) 1) + have hgrowth := polyBounded_reduced hp hfl have hfold := ComputableInPolyTime.foldlWithInput_of_bounded_growth (encA := encUnary) (encL := encList encUnary) (step := fun x acc _ => mul (p x) (fl x) acc (c x)) hone @@ -526,6 +637,100 @@ theorem computableInPolyTime_pow {p k : α → ℕ} {fl c : α → List ℕ} · have := length_encList_le (length_mul (p x) (fl x) acc (c x)) fun _ h => lt_of_mem_mul h omega +/-- **Negation is polynomial-time**, with the precision in unary. -/ +@[fun_prop] +theorem computableInPolyTime_neg {p : α → ℕ} {fl c : α → List ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hc : ComputableInPolyTime c encIn (encList encNat)) : + ComputableInPolyTime (fun x => neg (p x) (fl x) (c x)) encIn (encList encNat) := by + have h1 := ComputableInPolyTime.fst encIn encUnary + have h2 := ComputableInPolyTime.snd encIn encUnary + exact computableInPolyTime_tabulate (length_polyTime hfl) + (computableInPolyTime_mod_two_pow (ComputableInPolyTime.binary_sub' + (ComputableInPolyTime.binary_two_pow.comp (hp.comp h1)) + (computableInPolyTime_mod_two_pow (computableInPolyTime_getD (hc.comp h1) h2) + (hp.comp h1))) (hp.comp h1)) + +/-- **Subtraction is polynomial-time**, with the precision in unary. -/ +@[fun_prop] +theorem computableInPolyTime_sub {p : α → ℕ} {fl c d : α → List ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hc : ComputableInPolyTime c encIn (encList encNat)) + (hd : ComputableInPolyTime d encIn (encList encNat)) : + ComputableInPolyTime (fun x => sub (p x) (fl x) (c x) (d x)) encIn (encList encNat) := + computableInPolyTime_add hp hfl hc (computableInPolyTime_neg hp hfl hd) + +/-- **A Newton step is polynomial-time**, with the precision in unary. -/ +@[fun_prop] +theorem computableInPolyTime_newton {p : α → ℕ} {fl x y : α → List ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hx : ComputableInPolyTime x encIn (encList encNat)) + (hy : ComputableInPolyTime y encIn (encList encNat)) : + ComputableInPolyTime (fun a => newton (p a) (fl a) (x a) (y a)) encIn (encList encNat) := + computableInPolyTime_mul hp hfl hy (computableInPolyTime_sub hp hfl + (computableInPolyTime_add hp hfl (computableInPolyTime_one hfl) + (computableInPolyTime_one hfl)) (computableInPolyTime_mul hp hfl hx hy)) + +/-- **The seed of inversion is polynomial-time**, with the precision in unary. -/ +@[fun_prop] +theorem computableInPolyTime_seed {p : α → ℕ} {fl x : α → List ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hx : ComputableInPolyTime x encIn (encList encNat)) : + ComputableInPolyTime (fun a => seed (p a) (fl a) (x a)) encIn (encList encNat) := by + let encS := encPair (encList encNat) (encList encNat) + have hstep : ComputableInPolyTime + (fun q : (α × (List ℕ × List ℕ)) × ℕ => seedStep (p q.1.1) (fl q.1.1) q.1.2) + (encPair (encPair encIn encS) encUnary) encS := by + have hq := ComputableInPolyTime.fst (encPair encIn encS) encUnary + have ha := (ComputableInPolyTime.fst encIn encS).comp hq + have hs := (ComputableInPolyTime.snd encIn encS).comp hq + have h1 := (ComputableInPolyTime.fst (encList encNat) (encList encNat)).comp hs + have h2 := (ComputableInPolyTime.snd (encList encNat) (encList encNat)).comp hs + have hsq := computableInPolyTime_mul (hp.comp ha) (hfl.comp ha) h1 h1 + exact hsq.prod (computableInPolyTime_mul (hp.comp ha) (hfl.comp ha) h2 hsq) + have hfold := ComputableInPolyTime.foldlWithInput_of_bounded_growth + (encA := encUnary) (encL := encList encUnary) + (step := fun a s _ => seedStep (p a) (fl a) s) + (hx.prod (computableInPolyTime_one hfl)) + ⟨ComputableInPolyTime.range.comp (ComputableInPolyTime.unary_sub' (length_polyTime hfl) + (ComputableInPolyTime.const 1)), hstep, inferInstance, inferInstance⟩ + ⟨polyBounded_reduced hp hfl, fun a s _ _ => ?_⟩ + · exact (ComputableInPolyTime.snd (encList encNat) (encList encNat)).comp hfold + · have h1 := length_encList_le (length_mul (p a) (fl a) s.1 s.1) fun _ h => lt_of_mem_mul h + have h2 := length_encList_le (length_mul (p a) (fl a) s.2 (mul (p a) (fl a) s.1 s.1)) + fun _ h => lt_of_mem_mul h + simp only [seedStep, length_encPair] + omega + +/-- **Inversion is polynomial-time**, with the precision in unary. -/ +@[fun_prop] +theorem computableInPolyTime_inv {p : α → ℕ} {fl x : α → List ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hx : ComputableInPolyTime x encIn (encList encNat)) : + ComputableInPolyTime (fun a => inv (p a) (fl a) (x a)) encIn (encList encNat) := by + have hstep : ComputableInPolyTime + (fun q : (α × List ℕ) × ℕ => newton (p q.1.1) (fl q.1.1) (x q.1.1) q.1.2) + (encPair (encPair encIn (encList encNat)) encUnary) (encList encNat) := by + have hq := ComputableInPolyTime.fst (encPair encIn (encList encNat)) encUnary + have ha := (ComputableInPolyTime.fst encIn (encList encNat)).comp hq + have hy := (ComputableInPolyTime.snd encIn (encList encNat)).comp hq + exact computableInPolyTime_newton (hp.comp ha) (hfl.comp ha) (hx.comp ha) hy + exact ComputableInPolyTime.foldlWithInput_of_bounded_growth + (encA := encUnary) (encL := encList encUnary) + (step := fun a y _ => newton (p a) (fl a) (x a) y) (computableInPolyTime_seed hp hfl hx) + ⟨ComputableInPolyTime.range.comp hp, hstep, inferInstance, inferInstance⟩ + ⟨polyBounded_reduced hp hfl, fun a y _ _ => by + have := length_encList_le (length_mul (p a) (fl a) y + (sub (p a) (fl a) (add (p a) (fl a) (one (fl a)) (one (fl a))) (mul (p a) (fl a) (x a) y))) + fun _ h => lt_of_mem_mul h + simp only [newton] + omega⟩ + end PolyTime end Cslib.GaloisRingArith diff --git a/ComplexityTests/GaloisRingArithmetic.lean b/ComplexityTests/GaloisRingArithmetic.lean index f0e7710ba4..c3f7705e26 100644 --- a/ComplexityTests/GaloisRingArithmetic.lean +++ b/ComplexityTests/GaloisRingArithmetic.lean @@ -44,6 +44,20 @@ example : pow 3 [1, 1] [1, 1] 2 = [0, 1] := by decide example (c : List ℕ) (k : ℕ) : val 3 [1, 1] (pow 3 [1, 1] c k) = val 3 [1, 1] c ^ k := val_pow c k +-- Inversion of units: `U⁻¹ = U ^ 2 = 7 + 7 U`, and `(1 + 2 U)⁻¹`, `(3 + 5 U)⁻¹` are inverses. +example : inv 3 [1, 1] [0, 1] = [7, 7] := by decide +example : mul 3 [1, 1] [1, 2] (inv 3 [1, 1] [1, 2]) = [1, 0] := by decide +example : mul 3 [1, 1] [3, 5] (inv 3 [1, 1] [3, 5]) = [1, 0] := by decide + +-- `2` is not a unit, and inversion does not invert it. +example : mul 3 [1, 1] [2, 0] (inv 3 [1, 1] [2, 0]) ≠ [1, 0] := by decide + +/-- Inversion is correct when the seed inverts modulo `2`. -/ +example (x : List ℕ) {w : Ring 3 [1, 1]} + (h : val 3 [1, 1] x * val 3 [1, 1] x ^ (2 ^ 2 - 2) = 1 + 2 * w) : + val 3 [1, 1] x * val 3 [1, 1] (inv 3 [1, 1] x) = 1 := + val_mul_inv x h + /-- The defining polynomial is monic. -/ example : (monicOf [1, 1]).Monic := monic_monicOf _ @@ -73,4 +87,10 @@ example : ComputableInPolyTime (encList encNat) := by fun_prop +/-- **Inversion in polynomial time.** -/ +example : ComputableInPolyTime + (fun q : (ℕ × List ℕ) × List ℕ => inv q.1.1 q.1.2 q.2) + (encPair (encPair encUnary (encList encNat)) (encList encNat)) (encList encNat) := by + fun_prop + end ComplexityTests.GaloisRingArithmetic From 5312093045d6432d93242e34e2a13a13954fdef4 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:53:27 -0400 Subject: [PATCH 021/129] refactor(circuit/frontier): packingGroup takes the edge ends directly packingGroup and its lemmas now take a packing of arbitrary edge ends src/tgt instead of a network, so the orientation hypothesis of cauchyModeSplitting can be supplied for N.map f. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/ActiveCall.lean | 75 ++++++++++--------- .../Frontier/TensorNetworkActiveCall.lean | 8 +- 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ActiveCall.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ActiveCall.lean index cf9a248fe1..afa5522d4c 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ActiveCall.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ActiveCall.lean @@ -44,17 +44,16 @@ of the products of the contractions of the children. So the values may be comput children's contractions modulo `2 ^ p_t` (`TensorNetwork.exists_recoverFromValues_eq_add_of_mk`). **The packing bridge.** The orientation that the child identity needs is property (2) of -Lemma D.3 for `Cslib.PacketPacking.Packing` (`TensorNetwork.packingGroup_src_tgt`), and the port +Lemma D.3 for `Cslib.PacketPacking.Packing` (`packingGroup_src_tgt`), and the port counts of Lemma D.3 (3) are the group sizes that bound the children -(`TensorNetwork.card_packingGroup_src`, `TensorNetwork.card_packingGroup_tgt`, with +(`card_packingGroup_src`, `card_packingGroup_tgt`, with `TensorNetwork.ModeSplitting.mul_card_edges_child_le`). ## Main definitions * `Cslib.Circuits.Frontier.integerHom`: the inclusion of `ℤ_(2)` into `B`. * `Cslib.Circuits.Frontier.recoverFromValues`: interpolation followed by tapered recovery. -* `Cslib.Circuits.Frontier.TensorNetwork.packingGroup`: the group of a vertex in coordinate `i` - of a packing. +* `Cslib.Circuits.Frontier.packingGroup`: the group of a vertex in coordinate `i` of a packing. ## Main results @@ -66,7 +65,7 @@ counts of Lemma D.3 (3) are the group sizes that bound the children deformation modulo `2 ^ p_t` are deformed contractions along `cauchyValueMatrix`. * `Cslib.Circuits.Frontier.TensorNetwork.exists_recoverFromValues_eq_add_of_mk`: correctness of one active call. -* `Cslib.Circuits.Frontier.TensorNetwork.packingGroup_src_tgt`: the packing bridge, with +* `Cslib.Circuits.Frontier.packingGroup_src_tgt`: the packing bridge, with `packing_slot_lt`, `packing_slot_injective`, `card_packingGroup_src` and `card_packingGroup_tgt`. * `Cslib.Circuits.Frontier.two_pow_padicValInt_dvd_and_not_dvd`: a node of valuation `i` meets @@ -187,56 +186,64 @@ theorem exists_recoverFromValues_eq_add (B : Subring K) hslot htS, coeff_map, coeff_map, hg, coeff_add, coeff_C_mul, map_add, map_mul, map_pow] rfl +end TensorNetwork + /-! ### The packing bridge -/ -/-- The group of a vertex in coordinate `i` of a packing: its row label if it is a row and its -column label if it is a column. -/ -@[expose] def packingGroup {R : Type*} {a r : ℕ} (N : TensorNetwork R Bool V E) (side : V → Bool) - (Q : PacketPacking.Packing N.src N.tgt a r) (i : Fin a) (v : V) : Fin r := +section PackingBridge + +variable {V E : Type*} {src tgt : E → V} {a r : ℕ} + +/-- The group of a vertex in coordinate `i` of a packing of the edges `src e → tgt e`: its row +label if it is a row and its column label if it is a column. -/ +@[expose] def packingGroup (side : V → Bool) (Q : PacketPacking.Packing src tgt a r) (i : Fin a) + (v : V) : Fin r := if side v then Q.rowLabel v i else Q.colLabel v i -omit [Fintype V] [Fintype E] [DecidableEq E] [DecidableEq P] in -/-- **The packing bridge.** For a packing of the edges of a network whose edges run from rows to -columns (Lemma D.3 of [OpenAI2026ETH]), the groups in coordinate `i` are oriented by digit `i` of -the slots (Lemma D.3 (2)): the hypothesis of `TensorNetwork.cauchyModeSplitting`. -/ -theorem packingGroup_src_tgt {R : Type*} {a r : ℕ} (N : TensorNetwork R Bool V E) - (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) (htgt : ∀ e, side (N.tgt e) = false) - (Q : PacketPacking.Packing N.src N.tgt a r) (i : Fin a) (e : E) : - (packingGroup N side Q i (N.src e), packingGroup N side Q i (N.tgt e)) = +/-- **The packing bridge.** For a packing of edges that run from rows to columns (Lemma D.3 of +[OpenAI2026ETH]), the groups in coordinate `i` are oriented by digit `i` of the slots +(Lemma D.3 (2)): the hypothesis of `TensorNetwork.cauchyModeSplitting`. The edge ends `src` and +`tgt` are arbitrary functions, so the bridge applies to `N.src` and `N.tgt` of any network with +these ends, such as `N.map f`. -/ +theorem packingGroup_src_tgt (side : V → Bool) (hsrc : ∀ e, side (src e) = true) + (htgt : ∀ e, side (tgt e) = false) (Q : PacketPacking.Packing src tgt a r) (i : Fin a) + (e : E) : + (packingGroup side Q i (src e), packingGroup side Q i (tgt e)) = if (Q.slot e : ℕ).testBit i then (Q.symbols (Q.packet e) i).swap else Q.symbols (Q.packet e) i := by simp only [packingGroup, hsrc, htgt, ↓reduceIte, Bool.false_eq_true] exact Q.labels_eq e i -omit [Fintype V] [Fintype E] [DecidableEq E] [DecidableEq P] in /-- The slots of a packing lie below `2 ^ a`. -/ -theorem packing_slot_lt {R : Type*} {a r : ℕ} {N : TensorNetwork R Bool V E} - (Q : PacketPacking.Packing N.src N.tgt a r) (e : E) : (Q.slot e : ℕ) < 2 ^ a := +theorem packing_slot_lt (Q : PacketPacking.Packing src tgt a r) (e : E) : + (Q.slot e : ℕ) < 2 ^ a := (Q.slot e).isLt -omit [Fintype V] [Fintype E] [DecidableEq E] [DecidableEq P] in /-- No two edges share a slot of a packet. -/ -theorem packing_slot_injective {R : Type*} {a r : ℕ} {N : TensorNetwork R Bool V E} - (Q : PacketPacking.Packing N.src N.tgt a r) (e e' : E) (hp : Q.packet e = Q.packet e') - (hs : (Q.slot e : ℕ) = Q.slot e') : e = e' := +theorem packing_slot_injective (Q : PacketPacking.Packing src tgt a r) (e e' : E) + (hp : Q.packet e = Q.packet e') (hs : (Q.slot e : ℕ) = Q.slot e') : e = e' := Q.injective (Prod.ext hp (Fin.ext hs)) -omit [Fintype V] [DecidableEq E] [DecidableEq P] in +variable [Fintype E] + /-- The rows of group `σ` in coordinate `i` of a packing are the rows with symbol `σ`. -/ -theorem card_packingGroup_src {R : Type*} {a r : ℕ} (N : TensorNetwork R Bool V E) - (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) - (Q : PacketPacking.Packing N.src N.tgt a r) (i : Fin a) (σ : Fin r) : - #{e | packingGroup N side Q i (N.src e) = σ} = #{e | Q.rowLabel (N.src e) i = σ} := by +theorem card_packingGroup_src (side : V → Bool) (hsrc : ∀ e, side (src e) = true) + (Q : PacketPacking.Packing src tgt a r) (i : Fin a) (σ : Fin r) : + #{e | packingGroup side Q i (src e) = σ} = #{e | Q.rowLabel (src e) i = σ} := by simp only [packingGroup, hsrc, ↓reduceIte] -omit [Fintype V] [DecidableEq E] [DecidableEq P] in /-- The columns of group `σ` in coordinate `i` of a packing are the columns with symbol `σ`. -/ -theorem card_packingGroup_tgt {R : Type*} {a r : ℕ} (N : TensorNetwork R Bool V E) - (side : V → Bool) (htgt : ∀ e, side (N.tgt e) = false) - (Q : PacketPacking.Packing N.src N.tgt a r) (i : Fin a) (σ : Fin r) : - #{e | packingGroup N side Q i (N.tgt e) = σ} = #{e | Q.colLabel (N.tgt e) i = σ} := by +theorem card_packingGroup_tgt (side : V → Bool) (htgt : ∀ e, side (tgt e) = false) + (Q : PacketPacking.Packing src tgt a r) (i : Fin a) (σ : Fin r) : + #{e | packingGroup side Q i (tgt e) = σ} = #{e | Q.colLabel (tgt e) i = σ} := by simp only [packingGroup, htgt, Bool.false_eq_true, ↓reduceIte] +end PackingBridge + +namespace TensorNetwork + +variable {V E P : Type*} [Fintype V] [Fintype E] [DecidableEq E] [DecidableEq P] + /-- **The values of the local deformation modulo `2 ^ p_t`** are deformed contractions of the reduced network along the Cauchy matrix at `T = t + α ζ` (which the child identity (D.16), `TensorNetwork.deformedContraction_cauchyValueMatrix_eq_sum`, expands). -/ diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkActiveCall.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkActiveCall.lean index dd0a261b1e..d75f5cbbe8 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkActiveCall.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkActiveCall.lean @@ -55,14 +55,14 @@ def packing : Cslib.PacketPacking.Packing net.src net.tgt 1 2 where labels_eq e i := by fin_cases e <;> fin_cases i <;> decide example (e : Fin 2) : - (packingGroup net side packing 0 (net.src e), packingGroup net side packing 0 (net.tgt e)) = + (packingGroup side packing 0 (net.src e), packingGroup side packing 0 (net.tgt e)) = if (packing.slot e : ℕ).testBit 0 then (packing.symbols (packing.packet e) 0).swap else packing.symbols (packing.packet e) 0 := - packingGroup_src_tgt net side (fun e => by fin_cases e <;> decide) + packingGroup_src_tgt side (fun e => by fin_cases e <;> decide) (fun e => by fin_cases e <;> decide) packing 0 e -example : Finset.card {e | packingGroup net side packing 0 (net.src e) = 1} = 1 := by - rw [card_packingGroup_src net side (fun e => by fin_cases e <;> decide)] +example : Finset.card {e | packingGroup side packing 0 (net.src e) = 1} = 1 := by + rw [card_packingGroup_src side (fun e => by fin_cases e <;> decide)] decide /-- **Child invariants.** For any mode splitting of `net` along the packets of `packing`, a packet From c64e111a74ca71665df6828fbb6ab58f4e7a88e3 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:53:27 -0400 Subject: [PATCH 022/129] feat(circuit/frontier): renaming and lifting tensor networks TensorNetwork.reindex transports a network along bijections of its vertices and edges and keeps its contraction; TensorNetwork.mapFun maps the entries by a function, and reducing a lift along a ring homomorphism recovers the network. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/Reindex.lean | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/Reindex.lean diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Reindex.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Reindex.lean new file mode 100644 index 0000000000..306a069d3b --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Reindex.lean @@ -0,0 +1,128 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation + +/-! +# Renaming and lifting tensor networks + +Two operations that keep the contraction of a tensor network under control while its types or its +coefficient ring change. + +* **Renaming** (`TensorNetwork.reindex`). Transport a network along bijections of its vertices + and of its edges. The contraction is unchanged (`TensorNetwork.contraction_reindex`). The + contraction recursion of [OpenAI2026ETH] (§D.4.6–§D.4.7, pp. 63–64) uses it to number the + vertices and edges of a child network by `Fin`. +* **Lifting the entries** (`TensorNetwork.mapFun`). Apply an arbitrary function to every tensor + entry. When the function is a section of a ring homomorphism `f`, reducing the lifted network + along `f` recovers the original (`TensorNetwork.map_mapFun`), so its contraction reduces to the + original contraction (`TensorNetwork.contraction_map_mapFun`). This is the manuscript's + canonical lift of entries specified modulo `2 ^ p` (§D.4.1, p. 58); any section works. + +## Main definitions + +* `Cslib.Circuits.Frontier.TensorNetwork.reindex`: renaming the vertices and edges. +* `Cslib.Circuits.Frontier.TensorNetwork.mapFun`: mapping every entry by a function. + +## Main results + +* `Cslib.Circuits.Frontier.TensorNetwork.contraction_reindex`: renaming preserves the contraction. +* `Cslib.Circuits.Frontier.TensorNetwork.map_mapFun`: reducing a lift along `f` recovers the + network. +* `Cslib.Circuits.Frontier.TensorNetwork.contraction_map_mapFun`: the contraction of a lift + reduces to the contraction. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.1 (p. 58) and + §D.4.6 (p. 63). +-/ + +public section + +namespace Cslib.Circuits.Frontier.TensorNetwork + +variable {R S U V E V' E' : Type*} + +/-! ### Renaming the vertices and edges -/ + +section Reindex + +variable (eV : V ≃ V') (eE : E ≃ E') (N : TensorNetwork R U V E) + +/-- The network transported along bijections `eV` of the vertices and `eE` of the edges: edge +`eE e` runs from `eV (N.src e)` to `eV (N.tgt e)`, and vertex `eV v` carries the tensor of `v`. -/ +@[expose] def reindex : TensorNetwork R U V' E' where + src e := eV (N.src (eE.symm e)) + tgt e := eV (N.tgt (eE.symm e)) + tensor v x := N.tensor (eV.symm v) (x ∘ eE) + tensor_congr v x y h := N.tensor_congr _ _ _ fun e he => h (eE e) (by + simp only [Equiv.symm_apply_apply] + rcases he with he | he + · exact Or.inl (by rw [he, Equiv.apply_symm_apply]) + · exact Or.inr (by rw [he, Equiv.apply_symm_apply])) + +@[simp] theorem reindex_src (e : E') : (N.reindex eV eE).src e = eV (N.src (eE.symm e)) := + rfl + +@[simp] theorem reindex_tgt (e : E') : (N.reindex eV eE).tgt e = eV (N.tgt (eE.symm e)) := + rfl + +@[simp] theorem reindex_tensor (v : V') (x : E' → U) : + (N.reindex eV eE).tensor v x = N.tensor (eV.symm v) (x ∘ eE) := + rfl + +/-- **Renaming preserves the contraction.** -/ +theorem contraction_reindex [CommSemiring R] [Fintype U] [Fintype V] [Fintype E] [DecidableEq E] + [Fintype V'] [Fintype E'] [DecidableEq E'] : + (N.reindex eV eE).contraction = N.contraction := + Fintype.sum_equiv (eE.symm.arrowCongr (Equiv.refl U)) _ _ fun _ => + Fintype.prod_equiv eV.symm _ _ fun _ => rfl + +end Reindex + +/-! ### Lifting the entries -/ + +section MapFun + +/-- The tensor network with every tensor entry mapped by the function `f`. Unlike +`TensorNetwork.map`, `f` need not be a ring homomorphism; a section of a reduction map lifts the +entries. -/ +@[expose] def mapFun (f : R → S) (N : TensorNetwork R U V E) : TensorNetwork S U V E where + toMultigraph := N.toMultigraph + tensor v x := f (N.tensor v x) + tensor_congr v x y h := by rw [N.tensor_congr v x y h] + +@[simp] theorem mapFun_tensor (f : R → S) (N : TensorNetwork R U V E) (v : V) (x : E → U) : + (N.mapFun f).tensor v x = f (N.tensor v x) := + rfl + +@[simp] theorem toMultigraph_mapFun (f : R → S) (N : TensorNetwork R U V E) : + (N.mapFun f).toMultigraph = N.toMultigraph := + rfl + +variable [CommSemiring R] [CommSemiring S] (f : R →+* S) {σ : S → R} + +/-- **Reducing a lift recovers the network**: for a section `σ` of `f`, mapping the entries by +`σ` and then by `f` changes nothing. -/ +theorem map_mapFun (hσ : ∀ x, f (σ x) = x) (N : TensorNetwork S U V E) : + (N.mapFun σ).map f = N := by + obtain ⟨G, T, hT⟩ := N + simp only [map, mapFun, hσ] + +/-- **The contraction of a lift reduces to the contraction**: for a section `σ` of `f`, `f` maps +the contraction of the lifted network to the contraction of the network. -/ +theorem contraction_map_mapFun [Fintype U] [Fintype V] [Fintype E] [DecidableEq E] + (hσ : ∀ x, f (σ x) = x) (N : TensorNetwork S U V E) : + f (N.mapFun σ).contraction = N.contraction := by + rw [← contraction_map, map_mapFun f hσ] + +end MapFun + +end Cslib.Circuits.Frontier.TensorNetwork From 55c7a8fc3faab8ee9e8cce97f5347236d7423578 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:53:27 -0400 Subject: [PATCH 023/129] feat(foundations): digit lifts in Z_(p)[X]/(f) have unit differences MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The O-side counterpart of GaloisRing.isUnit_digitLift_sub: the binary-coordinate lifts of [OpenAI2026ETH] (§D.4.1, p. 58) in the valuation ring of AdjoinRoot.padicValuation differ by units. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Padics/AdjoinRootDigitLift.lean | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 Complexity/Foundations/NumberTheory/Padics/AdjoinRootDigitLift.lean diff --git a/Complexity/Foundations/NumberTheory/Padics/AdjoinRootDigitLift.lean b/Complexity/Foundations/NumberTheory/Padics/AdjoinRootDigitLift.lean new file mode 100644 index 0000000000..9d807df9aa --- /dev/null +++ b/Complexity/Foundations/NumberTheory/Padics/AdjoinRootDigitLift.lean @@ -0,0 +1,117 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate (Mathlib, `Mathlib/NumberTheory/Padics/`) + +public import Complexity.Foundations.NumberTheory.Padics.AdjoinRootValuation +import Complexity.Foundations.RingTheory.AdjoinRoot +import Complexity.Foundations.RingTheory.GaloisRing + +/-! +# Digit lifts in `ℤ_(p)[X] / (f)` + +Let `p` be a prime and `f` a monic integer polynomial that is irreducible modulo `p`, and let +`O = ℤ_(p)[X] / (f)` be the valuation ring of `AdjoinRoot.padicValuation` in `K = ℚ[X] / (f)`. +The *digit lift* of a digit vector `c : Fin (deg f) → ZMod p` is `∑ i, c i * X ^ i` in `O`, with +each digit read as an integer in `{0, …, p - 1}` (`AdjoinRoot.integerDigitLift`). For `p = 2` +these are the binary-coordinate lifts of the residue-field elements of [OpenAI2026ETH] (§D.4.1, +p. 58). + +**Distinct digit lifts differ by a unit of `O`** (`AdjoinRoot.isUnit_integerDigitLift_sub`). The +difference of two lifts is the image of `∑ i, (c i - c' i) * X ^ i ∈ ℤ[X] / (f)`, which is nonzero +modulo `p` because the digit lifts reduce to distinct elements of `𝔽_p[X] / (f)` +(`GaloisRing.residue_digitLift_injective`). So it has valuation one +(`AdjoinRoot.padicValuation_map_eq_one_iff`), and is a unit of the valuation ring +(`AdjoinRoot.isUnit_of_padicValuation_eq_one`). This is the counterpart in `O` of +`GaloisRing.isUnit_digitLift_sub`, and it supplies the interpolation points of the contraction +recursion (`Cslib.Circuits.Frontier.TensorNetwork.exists_contractRec_eq_add`) over `B = O`. + +## Main definitions + +* `AdjoinRoot.integerDigitLift`: the digit lift of a digit vector, in `O`. + +## Main results + +* `AdjoinRoot.coe_integerDigitLift`: the digit lift is `∑ i, c i * X ^ i`. +* `AdjoinRoot.isUnit_integerDigitLift_sub`: distinct digit lifts differ by a unit of `O`. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.1, p. 58. +-/ + +public section + +open Polynomial + +namespace AdjoinRoot + +variable {p : ℕ} [Fact p.Prime] {f : ℤ[X]} (hf : f.Monic) + (hirr : Irreducible (f.map (Int.castRingHom (ZMod p)))) + +variable (p f) in +/-- The integer polynomial `∑ i, c i * X ^ i` of a digit vector, with each digit read as an +integer in `{0, …, p - 1}`. -/ +private noncomputable def digitPoly (c : Fin f.natDegree → ZMod p) : ℤ[X] := + ∑ i, C ((c i).val : ℤ) * X ^ (i : ℕ) + +/-- Modulo `p`, the digit polynomial is `∑ i, c i * X ^ i`. -/ +private theorem map_digitPoly (c : Fin f.natDegree → ZMod p) : + (digitPoly p f c).map (Int.castRingHom (ZMod p)) = ∑ i, C (c i) * X ^ (i : ℕ) := by + simp only [digitPoly, Polynomial.map_sum, Polynomial.map_mul, Polynomial.map_C, + Polynomial.map_pow, Polynomial.map_X] + simp only [eq_intCast, Int.cast_natCast, ZMod.natCast_zmod_val] + +include hf hirr + +/-- The image in `ℚ[X] / (f)` of the class of the digit polynomial lies in the valuation ring. -/ +private theorem map_mk_digitPoly_mem (c : Fin f.natDegree → ZMod p) : + map (Int.castRingHom ℚ) f _ dvd_rfl (mk f (digitPoly p f c)) ∈ + (padicValuation p hf hirr).integer := + padicValuation_map_le_one hf hirr _ + +/-- **The digit lift** of a digit vector `c`: the element `∑ i, c i * X ^ i` of the valuation ring +`O = ℤ_(p)[X] / (f)`, with each digit `c i ∈ ZMod p` read as an integer in `{0, …, p - 1}`. For +`p = 2` these are the binary-coordinate lifts of [OpenAI2026ETH] (§D.4.1, p. 58). -/ +noncomputable def integerDigitLift (c : Fin f.natDegree → ZMod p) : + (padicValuation p hf hirr).integer := + ⟨_, map_mk_digitPoly_mem hf hirr c⟩ + +/-- The digit lift is `∑ i, c i * X ^ i` in `ℚ[X] / (f)`. -/ +theorem coe_integerDigitLift (c : Fin f.natDegree → ZMod p) : + (integerDigitLift hf hirr c : AdjoinRoot (f.map (Int.castRingHom ℚ))) = + ∑ i, ((c i).val : AdjoinRoot (f.map (Int.castRingHom ℚ))) * root _ ^ (i : ℕ) := by + simp only [integerDigitLift, digitPoly, map_sum, map_mul, map_pow, mk_X, map_natCast, map_root] + +/-- **Distinct digit lifts differ by a unit of `O`**: the difference reduces modulo `p` to a +nonzero element of `𝔽_p[X] / (f)`, so it has valuation one. -/ +theorem isUnit_integerDigitLift_sub {c c' : Fin f.natDegree → ZMod p} (h : c ≠ c') : + IsUnit (integerDigitLift hf hirr c - integerDigitLift hf hirr c') := by + have hx : ((integerDigitLift hf hirr c - integerDigitLift hf hirr c' : + (padicValuation p hf hirr).integer) : AdjoinRoot (f.map (Int.castRingHom ℚ))) = + map (Int.castRingHom ℚ) f _ dvd_rfl (mk f (digitPoly p f c - digitPoly p f c')) := by + rw [map_sub, map_sub] + rfl + have h1 : padicValuation p hf hirr + ((integerDigitLift hf hirr c - integerDigitLift hf hirr c' : + (padicValuation p hf hirr).integer) : AdjoinRoot (f.map (Int.castRingHom ℚ))) = 1 := by + rw [hx, padicValuation_map_eq_one_iff hf hirr, map_mk, Polynomial.map_sub, map_digitPoly, + map_digitPoly, map_sub, sub_ne_zero] + intro heq + refine h (GaloisRing.residue_digitLift_injective (n := 1) hf ?_) + simp only [GaloisRing.residue_digitLift] + exact heq + exact isUnit_of_padicValuation_eq_one hf hirr _ h1 + +/-- **Digit lifts are interpolation points**: distinct digit vectors have lifts with unit +differences. -/ +theorem pairwise_isUnit_integerDigitLift_sub : + Pairwise fun c c' : Fin f.natDegree → ZMod p => + IsUnit (integerDigitLift hf hirr c - integerDigitLift hf hirr c') := + fun _ _ h => isUnit_integerDigitLift_sub hf hirr h + +end AdjoinRoot From 9d8168fd914484488dd263f2d7118a0c324ad995 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:53:27 -0400 Subject: [PATCH 024/129] feat(circuit/frontier): the contraction recursion and its correctness (T2.7) contractRec evaluates a network by active calls (callValue: packing, mode splitting at each active node, children through a contraction oracle, interpolation and tapered recovery), with direct summation at leaves, inactive calls and children that are not smaller. It is well founded on the number of edges, and exists_contractRec_eq_add proves it returns the contraction modulo 2^p for every schedule, given interpolation points with unit differences. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 3 + .../Frontier/TensorNetwork/Recursion.lean | 439 ++++++++++++++++++ .../Frontier/TensorNetworkRecursion.lean | 146 ++++++ 3 files changed, 588 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkRecursion.lean diff --git a/Complexity.lean b/Complexity.lean index 84b80a176e..cb21ca7e9e 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -879,6 +879,8 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates public import Complexity.Computability.Circuit.Frontier.TensorNetwork.LocalDeformation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Recursion +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Reindex public import Complexity.Computability.Circuit.Frontier.TensorNetwork.TaperedRecovery public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness public import Complexity.Computability.Circuit.Frontier.Unconditional @@ -2829,6 +2831,7 @@ public import Complexity.Foundations.NumberTheory.AKS.Criterion public import Complexity.Foundations.NumberTheory.AKS.Introspective public import Complexity.Foundations.NumberTheory.AKS.SmallR public import Complexity.Foundations.NumberTheory.Harmonic +public import Complexity.Foundations.NumberTheory.Padics.AdjoinRootDigitLift public import Complexity.Foundations.NumberTheory.Padics.AdjoinRootValuation public import Complexity.Foundations.NumberTheory.Padics.RatValuation public import Complexity.Foundations.NumberTheory.PrimeCounting diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean new file mode 100644 index 0000000000..3c9753220f --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean @@ -0,0 +1,439 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ActiveCall +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Reindex + +/-! +# The contraction recursion and its correctness + +This file assembles the recursive contraction algorithm of [OpenAI2026ETH] (§D.4.7, p. 64) from +its active calls, and proves it correct by induction from the leaves. + +**One node** (`TensorNetwork.nodeSplitting`). For a network whose edges run from rows to columns, +a packing `Q` of its edges into packets of `2 ^ a` slots, and a nonzero grid point `t` of +valuation `i < a`, group the vertices by their symbol in coordinate `i` of `Q` (`packingGroup`). +The packing bridge `packingGroup_src_tgt` orients the groups, so the Cauchy matrix at +`T = t + α ζ` has the mode splitting `TensorNetwork.cauchyModeSplitting` (§D.4.5, p. 62). At a call +with input precision `p`, `TensorNetwork.callSplitting` is this splitting for the network reduced +modulo `2 ^ p_t`, `p_t = p + 8 h D_t + 8 M (a + 1)` (`nodePrecision`). + +**One call from its children.** If every value `v t k` reduces modulo `2 ^ p_t` to the sum over +the mode tuples of the products of the contractions of the children (the right side of (D.16)), +then interpolation and tapered recovery return the contraction modulo `2 ^ p` +(`TensorNetwork.exists_recoverFromValues_eq_add_of_children`). The children's contractions may +come from any *contraction oracle* (`TensorNetwork.ContractionOracle`) that is correct modulo the +precision it is given: a child over `B ⧸ (2 ^ p_t)` is lifted to `B` along a section of the +reduction and renumbered by `Fin` (`TensorNetwork.ContractionOracle.evalLift`), and the oracle's +value then reduces to its contraction (`TensorNetwork.ContractionOracle.mk_evalLift`). The value of +an active node is `TensorNetwork.nodeValue`, the values of a call are `TensorNetwork.callValues`, +and the recovered value is `TensorNetwork.callValue` +(`TensorNetwork.exists_callValue_eq_add`). + +**The recursion** (`TensorNetwork.contractRec`). A call receives a network on `Fin n` vertices +and `Fin m` edges, its sides, its depth `d` and its precision `p`. A *schedule* gives the +parameters `a`, `u`, `h` of a call from its depth and its number of edges (`CallParams`), or none +at a leaf. The call is active when the parameters meet the hypotheses of Lemma D.5 +(`CallParams.Admissible`, with `u ≤ a` and enough interpolation points) and the edges run from +rows to columns. It packs the edges with `PacketPacking.packing` (Lemma D.3, `r = 64`), takes the +first `M (2 L - 1) + 1` of a fixed family of interpolation points, and evaluates each child by a +recursive call at depth `d + 1` and precision `p_t`. A leaf, an inactive call, and a child that +does not have fewer edges than its parent are evaluated by the contraction itself, the direct +summation of the manuscript's leaves. The recursion is well founded on the number of edges. + +**Correctness** (`TensorNetwork.exists_contractRec_eq_add`). For interpolation points with unit +differences, every call returns the contraction modulo `2 ^ p`, for every schedule: the manuscript's +"induction from the leaves" (p. 64) becomes strong induction on the number of edges. So the +parameter schedule (D.4)–(D.6) matters only for the running time, which this file does not treat; +under it, Lemma D.3 (3) and `ModeSplitting.mul_card_edges_child_le` make every child smaller, so the +fallback for children is not taken. In the manuscript's ring `B = O = ℤ_(2)[U] / (f)`, the +binary-coordinate lifts are such points (`AdjoinRoot.isUnit_integerDigitLift_sub`). + +## Main definitions + +* `Cslib.Circuits.Frontier.nodePrecision`: the child precision `p_t`. +* `Cslib.Circuits.Frontier.TensorNetwork.nodeSplitting`, + `Cslib.Circuits.Frontier.TensorNetwork.callSplitting`: the mode splitting at an active node. +* `Cslib.Circuits.Frontier.TensorNetwork.ContractionOracle`: values for child calls, with + `ContractionOracle.Correct` and `ContractionOracle.evalLift`. +* `Cslib.Circuits.Frontier.TensorNetwork.nodeValue`, + `Cslib.Circuits.Frontier.TensorNetwork.callValues`, + `Cslib.Circuits.Frontier.TensorNetwork.callValue`: one active call from an oracle. +* `Cslib.Circuits.Frontier.TensorNetwork.CallParams`: the parameters of a call. +* `Cslib.Circuits.Frontier.TensorNetwork.contractRec`: the recursion. + +## Main results + +* `Cslib.Circuits.Frontier.TensorNetwork.exists_recoverFromValues_eq_add_of_children`: one call is + correct when its values are the sums of the products of the children's contractions. +* `Cslib.Circuits.Frontier.TensorNetwork.exists_callValue_eq_add`: one call is correct for a + correct oracle. +* `Cslib.Circuits.Frontier.TensorNetwork.exists_contractRec_eq_add`, + `Cslib.Circuits.Frontier.TensorNetwork.mk_contractRec`: the recursion returns the contraction + modulo `2 ^ p`. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Lemma D.3 (pp. 58–59), + §D.4.4–§D.4.7, pp. 60–64. +-/ + +public section + +open Polynomial Finset + +namespace Cslib.Circuits.Frontier + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +/-! ### Node precisions -/ + +/-- The precision `p_t = p + 8 h D_t + 8 M (a + 1)`, `D_t = 2 ^ (a - v₂ t)`, that a call of input +precision `p` with `M` edges passes to the children of the node `t` (Lemma D.5). -/ +@[expose] def nodePrecision (M a h p : ℕ) (t : ℤ) : ℕ := + p + 8 * h * 2 ^ (a - padicValInt 2 t) + 8 * M * (a + 1) + +/-- A node of positive multiplicity has valuation below `u`, so below `a` when `u ≤ a`. -/ +theorem padicValInt_lt_of_taperMult_pos {u h a : ℕ} {t : ℤ} (hq : 0 < taperMult u h t) + (hua : u ≤ a) : padicValInt 2 t < a := by + unfold taperMult at hq + have : padicValInt 2 t < u := by + by_contra hc + rw [Nat.sub_eq_zero_of_le (not_lt.mp hc), mul_zero] at hq + exact lt_irrefl 0 hq + omega + +/-- The ideal `(2 ^ n)` of a commutative ring. -/ +abbrev twoPowIdeal (B : Type*) [CommRing B] (n : ℕ) : Ideal B := + Ideal.span {(2 : B) ^ n} + +/-- `2 ^ n = 0` modulo `2 ^ n`. -/ +theorem two_pow_eq_zero_twoPowIdeal (B : Type*) [CommRing B] (n : ℕ) : + (2 : B ⧸ twoPowIdeal B n) ^ n = 0 := by + have : Ideal.Quotient.mk (twoPowIdeal B n) ((2 : B) ^ n) = 0 := + Ideal.Quotient.eq_zero_iff_mem.mpr (Ideal.mem_span_singleton_self _) + rwa [map_pow, map_ofNat] at this + +namespace TensorNetwork + +/-! ### The modes of an active node -/ + +section Node + +variable {R V E : Type*} [CommRing R] [Fintype E] {a r : ℕ} + +/-- **The modes of an active node** (§D.4.5). For a network whose edges run from rows to columns, +a packing `Q` of its edges, and a nonzero grid point `t` of valuation `i < a`, the mode splitting +of the Cauchy matrix at `T = t + α ζ` with the vertices grouped by their symbol in coordinate `i` +of `Q` (`cauchyModeSplitting`, oriented by `packingGroup_src_tgt`). -/ +noncomputable def nodeSplitting (N : TensorNetwork R Bool V E) (side : V → Bool) + (hsrc : ∀ e, side (N.src e) = true) (htgt : ∀ e, side (N.tgt e) = false) + (Q : PacketPacking.Packing N.src N.tgt a r) (κ : ℤ₍₂₎ →+* R) (ζ : R) {p : ℕ} + (hp : (2 : R) ^ p = 0) {t : ℤ} (ht : t ∈ recoveryNodes a) (hia : padicValInt 2 t < a) : + N.ModeSplitting (cauchyValueMatrix κ ζ a t Q.packet fun e => Q.slot e) (Fin r) + (Fin Q.numPackets) (Fin Q.numPackets × Fin (2 ^ (padicValInt 2 t + 1)) × Fin p) := + cauchyModeSplitting (group := packingGroup side Q ⟨_, hia⟩) (packet := Q.packet) + (slot := fun e => Q.slot e) (sym := fun q => Q.symbols q ⟨_, hia⟩) (i := padicValInt 2 t) + side hsrc htgt (packingGroup_src_tgt side hsrc htgt Q ⟨_, hia⟩) (packing_slot_lt Q) hia κ ζ hp + (mem_of_mem_erase ht) (two_pow_padicValInt_dvd_and_not_dvd (ne_of_mem_erase ht)).1 + (two_pow_padicValInt_dvd_and_not_dvd (ne_of_mem_erase ht)).2 + +/-- **The child identity at an active node** ((D.16)): the deformed contraction along the Cauchy +matrix at `T = t + α ζ` is the sum, over the mode tuples of weight below `p`, of the products over +the groups of the contractions of the children of `nodeSplitting`. -/ +theorem deformedContraction_eq_sum_nodeSplitting [Fintype V] [DecidableEq V] [DecidableEq E] + (N : TensorNetwork R Bool V E) (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) + (htgt : ∀ e, side (N.tgt e) = false) (Q : PacketPacking.Packing N.src N.tgt a r) + (κ : ℤ₍₂₎ →+* R) (ζ : R) {p : ℕ} (hp : (2 : R) ^ p = 0) {t : ℤ} (ht : t ∈ recoveryNodes a) + (hia : padicValInt 2 t < a) : + N.deformedContraction side (cauchyValueMatrix κ ζ a t Q.packet fun e => Q.slot e) = + ∑ β ∈ modeBudget + (fun Z : Fin Q.numPackets × Fin (2 ^ (padicValInt 2 t + 1)) × Fin p => (Z.2.2 : ℕ) + 1) p, + ∏ g, ((nodeSplitting N side hsrc htgt Q κ ζ hp ht hia).child β g).contraction := + deformedContraction_cauchyValueMatrix_eq_sum (group := packingGroup side Q ⟨_, hia⟩) + (packet := Q.packet) (slot := fun e => Q.slot e) (sym := fun q => Q.symbols q ⟨_, hia⟩) + (i := padicValInt 2 t) side hsrc htgt (packingGroup_src_tgt side hsrc htgt Q ⟨_, hia⟩) + (packing_slot_lt Q) hia κ ζ hp (mem_of_mem_erase ht) + (two_pow_padicValInt_dvd_and_not_dvd (ne_of_mem_erase ht)).1 + (two_pow_padicValInt_dvd_and_not_dvd (ne_of_mem_erase ht)).2 + +end Node + +/-! ### One call from its children -/ + +section Children + +variable {K : Type*} [CommRing K] [Algebra ℚ K] (B : Subring K) + (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) {V E : Type*} [Fintype V] [DecidableEq V] [Fintype E] + [DecidableEq E] + +/-- The mode splitting at the active node `t` and the point `z` of a call with input precision +`p`: `nodeSplitting` for the network reduced modulo `2 ^ p_t`, at the reduction of `z`. -/ +noncomputable def callSplitting (N : TensorNetwork B Bool V E) (side : V → Bool) + (hsrc : ∀ e, side (N.src e) = true) (htgt : ∀ e, side (N.tgt e) = false) {a r : ℕ} + (Q : PacketPacking.Packing N.src N.tgt a r) (p u h : ℕ) (hua : u ≤ a) {t : ℤ} + (ht : t ∈ recoveryNodes a) (hq : 0 < taperMult u h t) (z : B) : + ModeSplitting + (N.map (Ideal.Quotient.mk (twoPowIdeal B (nodePrecision (Fintype.card E) a h p t)))) + (cauchyValueMatrix + ((Ideal.Quotient.mk (twoPowIdeal B (nodePrecision (Fintype.card E) a h p t))).comp + (integerHom B hB)) + (Ideal.Quotient.mk (twoPowIdeal B (nodePrecision (Fintype.card E) a h p t)) z) a t + Q.packet fun e => Q.slot e) + (Fin r) (Fin Q.numPackets) + (Fin Q.numPackets × Fin (2 ^ (padicValInt 2 t + 1)) × + Fin (nodePrecision (Fintype.card E) a h p t)) := + nodeSplitting (N.map (Ideal.Quotient.mk (twoPowIdeal B (nodePrecision (Fintype.card E) a h p t)))) + side hsrc htgt Q _ _ (two_pow_eq_zero_twoPowIdeal _ _) ht + (padicValInt_lt_of_taperMult_pos hq hua) + +/-- **Correctness of one active call from its children** ([OpenAI2026ETH], §D.4.4–§D.4.7). Let +`N` have entries in `B`, edges from rows to columns, and a packing `Q` into packets of `2 ^ a` +slots. Let `1 ≤ u ≤ M`, `u ≤ a` and `3 M ≤ h u ≤ 3 M + u` for `M` edges, and let the +`n > M (2 L - 1)` points `ζ k` have unit differences. If at every node `t` of positive +multiplicity each value `v t k` reduces modulo `2 ^ p_t` to the sum, over the mode tuples of +weight below `p_t`, of the products over the groups of the contractions of the children of +`callSplitting` at `ζ k` (the right side of (D.16)), then the recovered value agrees with the +contraction modulo `2 ^ p`. -/ +theorem exists_recoverFromValues_eq_add_of_children (N : TensorNetwork B Bool V E) + (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) (htgt : ∀ e, side (N.tgt e) = false) + {a r : ℕ} (Q : PacketPacking.Packing N.src N.tgt a r) {u h p : ℕ} (hu : 1 ≤ u) + (huM : u ≤ Fintype.card E) (hua : u ≤ a) (hh : 3 * Fintype.card E ≤ h * u) + (hh' : h * u ≤ 3 * Fintype.card E + u) {n : ℕ} (ζ : Fin n → B) + (hζ : Pairwise fun k l => IsUnit (ζ k - ζ l)) (hn : Fintype.card E * (2 * 2 ^ a - 1) < n) + (v : ℤ → Fin n → B) + (hv : ∀ t (ht : t ∈ recoveryNodes a) (hq : 0 < taperMult u h t) k, + Ideal.Quotient.mk (twoPowIdeal B (nodePrecision (Fintype.card E) a h p t)) (v t k) = + ∑ β ∈ modeBudget (fun Z : Fin Q.numPackets × Fin (2 ^ (padicValInt 2 t + 1)) × + Fin (nodePrecision (Fintype.card E) a h p t) => (Z.2.2 : ℕ) + 1) + (nodePrecision (Fintype.card E) a h p t), + ∏ g, ((callSplitting B hB N side hsrc htgt Q p u h hua ht hq (ζ k)).child β + g).contraction) : + ∃ b ∈ B, recoverFromValues B a u h ζ v = (N.contraction : K) + 2 ^ p * b := + exists_recoverFromValues_eq_add_of_mk B hB N side (packing_slot_lt Q) + (packing_slot_injective Q) hu huM hh hh' ζ hζ hn v fun t ht hq k => + (hv t ht hq k).trans (deformedContraction_eq_sum_nodeSplitting + (N.map (Ideal.Quotient.mk (twoPowIdeal B (nodePrecision (Fintype.card E) a h p t)))) side + hsrc htgt Q _ _ (two_pow_eq_zero_twoPowIdeal _ _) ht + (padicValInt_lt_of_taperMult_pos hq hua)).symm + +end Children + +/-! ### Contraction oracles -/ + +/-- A *contraction oracle*: a value for every Boolean tensor network on the vertices `Fin n` and the +edges `Fin m`, with its sides, at every precision. A call of the recursion evaluates its children +through an oracle. -/ +abbrev ContractionOracle (B : Type*) : Type _ := + (n m : ℕ) → TensorNetwork B Bool (Fin n) (Fin m) → (Fin n → Bool) → ℕ → B + +namespace ContractionOracle + +variable {B : Type*} [CommRing B] + +/-- An oracle is *correct* when each of its values agrees with the contraction modulo the +precision `2 ^ p` it is given. -/ +def Correct (rec : ContractionOracle B) : Prop := + ∀ n m (N : TensorNetwork B Bool (Fin n) (Fin m)) side p, + ∃ c, rec n m N side p = N.contraction + 2 ^ p * c + +/-- The value of an oracle on a network over `B ⧸ (2 ^ p)` on finite types, at precision `p`: lift +the entries to `B` along a section of the reduction (`TensorNetwork.mapFun`), number the vertices +and edges (`TensorNetwork.reindex`), and query. -/ +noncomputable def evalLift (rec : ContractionOracle B) {V E : Type*} [Fintype V] [Fintype E] + {p : ℕ} (N : TensorNetwork (B ⧸ twoPowIdeal B p) Bool V E) (side : V → Bool) : B := + rec _ _ ((N.mapFun (Function.surjInv Ideal.Quotient.mk_surjective)).reindex + (Fintype.equivFin V) (Fintype.equivFin E)) (side ∘ (Fintype.equivFin V).symm) p + +/-- **A correct oracle evaluates a lifted network**: its value reduces modulo `2 ^ p` to the +contraction. -/ +theorem mk_evalLift {rec : ContractionOracle B} (hrec : rec.Correct) {V E : Type*} [Fintype V] + [Fintype E] [DecidableEq E] {p : ℕ} (N : TensorNetwork (B ⧸ twoPowIdeal B p) Bool V E) + (side : V → Bool) : + Ideal.Quotient.mk (twoPowIdeal B p) (rec.evalLift N side) = N.contraction := by + obtain ⟨c, hc⟩ := hrec _ _ ((N.mapFun (Function.surjInv Ideal.Quotient.mk_surjective)).reindex + (Fintype.equivFin V) (Fintype.equivFin E)) (side ∘ (Fintype.equivFin V).symm) p + have h2 : Ideal.Quotient.mk (twoPowIdeal B p) ((2 : B) ^ p) = 0 := + Ideal.Quotient.eq_zero_iff_mem.mpr (Ideal.mem_span_singleton_self _) + rw [evalLift, hc, map_add, map_mul, h2, zero_mul, add_zero, contraction_reindex, + contraction_map_mapFun _ (Function.surjInv_eq Ideal.Quotient.mk_surjective)] + +end ContractionOracle + +/-! ### One call from an oracle -/ + +section Call + +variable {K : Type*} [CommRing K] [Algebra ℚ K] (B : Subring K) + (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) {V E : Type*} [Fintype V] [DecidableEq V] [Fintype E] + [DecidableEq E] + +/-- **The value at an active node** ((D.16)): the sum, over the mode tuples of weight below `p_t`, +of the products over the groups of the oracle's values on the children of `callSplitting` at `z`. +A child has its old vertices as rows and its packets as columns. -/ +noncomputable def nodeValue (rec : ContractionOracle B) (N : TensorNetwork B Bool V E) + (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) (htgt : ∀ e, side (N.tgt e) = false) + {a r : ℕ} (Q : PacketPacking.Packing N.src N.tgt a r) (p u h : ℕ) (hua : u ≤ a) {t : ℤ} + (ht : t ∈ recoveryNodes a) (hq : 0 < taperMult u h t) (z : B) : B := + ∑ β ∈ modeBudget (fun Z : Fin Q.numPackets × Fin (2 ^ (padicValInt 2 t + 1)) × + Fin (nodePrecision (Fintype.card E) a h p t) => (Z.2.2 : ℕ) + 1) + (nodePrecision (Fintype.card E) a h p t), + ∏ g, rec.evalLift ((callSplitting B hB N side hsrc htgt Q p u h hua ht hq z).child β g) + Sum.isLeft + +/-- For a correct oracle, the value at an active node reduces modulo `2 ^ p_t` to the right side of +(D.16). -/ +theorem mk_nodeValue {rec : ContractionOracle B} (hrec : rec.Correct) + (N : TensorNetwork B Bool V E) (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) + (htgt : ∀ e, side (N.tgt e) = false) {a r : ℕ} (Q : PacketPacking.Packing N.src N.tgt a r) + (p u h : ℕ) (hua : u ≤ a) {t : ℤ} (ht : t ∈ recoveryNodes a) (hq : 0 < taperMult u h t) + (z : B) : + Ideal.Quotient.mk (twoPowIdeal B (nodePrecision (Fintype.card E) a h p t)) + (nodeValue B hB rec N side hsrc htgt Q p u h hua ht hq z) = + ∑ β ∈ modeBudget (fun Z : Fin Q.numPackets × Fin (2 ^ (padicValInt 2 t + 1)) × + Fin (nodePrecision (Fintype.card E) a h p t) => (Z.2.2 : ℕ) + 1) + (nodePrecision (Fintype.card E) a h p t), + ∏ g, ((callSplitting B hB N side hsrc htgt Q p u h hua ht hq z).child β g).contraction := by + simp only [nodeValue, map_sum, map_prod, ContractionOracle.mk_evalLift hrec] + +/-- The values of an active call at the nodes and interpolation points: `nodeValue` at the nodes +of positive multiplicity, and zero elsewhere. -/ +noncomputable def callValues (rec : ContractionOracle B) (N : TensorNetwork B Bool V E) + (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) (htgt : ∀ e, side (N.tgt e) = false) + {a r : ℕ} (Q : PacketPacking.Packing N.src N.tgt a r) (p u h : ℕ) (hua : u ≤ a) {n : ℕ} + (ζ : Fin n → B) (t : ℤ) (k : Fin n) : B := + if hq : t ∈ recoveryNodes a ∧ 0 < taperMult u h t then + nodeValue B hB rec N side hsrc htgt Q p u h hua hq.1 hq.2 (ζ k) + else 0 + +open Classical in +/-- **One active call** (§D.4.7): interpolate and recover (`recoverFromValues`) the values +`callValues` computed from the oracle. The recovered value lies in `B` when the oracle is correct +(`exists_callValue_eq_add`); otherwise the call returns zero. -/ +noncomputable def callValue (rec : ContractionOracle B) (N : TensorNetwork B Bool V E) + (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) (htgt : ∀ e, side (N.tgt e) = false) + {a r : ℕ} (Q : PacketPacking.Packing N.src N.tgt a r) (p u h : ℕ) (hua : u ≤ a) {n : ℕ} + (ζ : Fin n → B) : B := + if hx : recoverFromValues B a u h ζ (callValues B hB rec N side hsrc htgt Q p u h hua ζ) ∈ B then + ⟨_, hx⟩ + else 0 + +/-- **Correctness of one active call** for a correct oracle: under the hypotheses of +`exists_recoverFromValues_eq_add_of_children`, the call returns the contraction modulo `2 ^ p`. -/ +theorem exists_callValue_eq_add {rec : ContractionOracle B} (hrec : rec.Correct) + (N : TensorNetwork B Bool V E) (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) + (htgt : ∀ e, side (N.tgt e) = false) {a r : ℕ} (Q : PacketPacking.Packing N.src N.tgt a r) + {u h p : ℕ} (hu : 1 ≤ u) (huM : u ≤ Fintype.card E) (hua : u ≤ a) + (hh : 3 * Fintype.card E ≤ h * u) (hh' : h * u ≤ 3 * Fintype.card E + u) {n : ℕ} + (ζ : Fin n → B) (hζ : Pairwise fun k l => IsUnit (ζ k - ζ l)) + (hn : Fintype.card E * (2 * 2 ^ a - 1) < n) : + ∃ c : B, callValue B hB rec N side hsrc htgt Q p u h hua ζ = N.contraction + 2 ^ p * c := by + obtain ⟨b, hb, hx⟩ := exists_recoverFromValues_eq_add_of_children B hB N side hsrc htgt Q hu + huM hua hh hh' ζ hζ hn (callValues B hB rec N side hsrc htgt Q p u h hua ζ) + fun t ht hq k => by + rw [callValues, dite_eq_left ⟨ht, hq⟩] + exact mk_nodeValue B hB hrec N side hsrc htgt Q p u h hua ht hq (ζ k) + have hx' : recoverFromValues B a u h ζ (callValues B hB rec N side hsrc htgt Q p u h hua ζ) = + ((N.contraction + 2 ^ p * ⟨b, hb⟩ : B) : K) := by + rw [hx] + push_cast + rfl + refine ⟨⟨b, hb⟩, ?_⟩ + rw [callValue, dite_eq_left (by rw [hx']; exact (N.contraction + 2 ^ p * ⟨b, hb⟩).2)] + exact Subtype.ext hx' + +end Call + +/-! ### The recursion -/ + +/-- The parameters of an active call: packets of `L = 2 ^ a` slots, and the tapered multiplicities +`q_t = h max(0, u - v₂ t)` of Lemma D.5. -/ +structure CallParams where + /-- The packets have `2 ^ a` slots. -/ + a : ℕ + /-- The number of levels of positive multiplicity. -/ + u : ℕ + /-- The step of the multiplicities between levels. -/ + h : ℕ + +/-- The parameters of a call with `M` edges and `n` interpolation points are *admissible* when +they meet the hypotheses of Lemma D.5, `1 ≤ u ≤ M`, `u ≤ a` and `3 M ≤ h u ≤ 3 M + u`, and there +are more than `M (2 L - 1)` points. -/ +@[expose] def CallParams.Admissible (θ : CallParams) (M n : ℕ) : Prop := + 1 ≤ θ.u ∧ θ.u ≤ M ∧ θ.u ≤ θ.a ∧ 3 * M ≤ θ.h * θ.u ∧ θ.h * θ.u ≤ 3 * M + θ.u ∧ + M * (2 * 2 ^ θ.a - 1) < n + +instance (θ : CallParams) (M n : ℕ) : Decidable (θ.Admissible M n) := by + unfold CallParams.Admissible + infer_instance + +section Recursion + +variable {K : Type*} [CommRing K] [Algebra ℚ K] (B : Subring K) + (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) (sched : ℕ → ℕ → Option CallParams) {N₀ : ℕ} + (pts : Fin N₀ → B) + +/-- **The contraction recursion** ([OpenAI2026ETH], §D.4.7). A call at depth `d` on a network `N` +with `m` edges, sides `side` and precision `p` asks the schedule for its parameters. When there +are none, the parameters are not admissible, or an edge does not run from a row to a column, it +returns the contraction (direct summation). Otherwise it is an active call (`callValue`) with the +packing of Lemma D.3 (`r = 64`) and the first `M (2 L - 1) + 1` points of `pts`, whose children +are recursive calls at depth `d + 1`; a child that does not have fewer edges returns its +contraction. -/ +noncomputable def contractRec (d n m : ℕ) (N : TensorNetwork B Bool (Fin n) (Fin m)) + (side : Fin n → Bool) (p : ℕ) : B := + match sched d m with + | none => N.contraction + | some θ => + if hθ : θ.Admissible m N₀ ∧ (∀ e, side (N.src e) = true) ∧ ∀ e, side (N.tgt e) = false then + callValue B hB + (fun n' m' N' side' p' => + if hm : m' < m then contractRec (d + 1) n' m' N' side' p' else N'.contraction) + N side hθ.2.1 hθ.2.2 (PacketPacking.packing N.src N.tgt θ.a 64) p θ.u θ.h hθ.1.2.2.1 + fun k : Fin (m * (2 * 2 ^ θ.a - 1) + 1) => pts (Fin.castLE hθ.1.2.2.2.2.2 k) + else N.contraction +termination_by m + +/-- **Correctness of the recursion** ([OpenAI2026ETH], §D.4.7, p. 64: correctness "by induction +from the leaves"). For interpolation points with unit differences, every call returns the +contraction modulo `2 ^ p`, for every schedule and depth. -/ +theorem exists_contractRec_eq_add (hpts : Pairwise fun k l => IsUnit (pts k - pts l)) (d n m : ℕ) + (N : TensorNetwork B Bool (Fin n) (Fin m)) (side : Fin n → Bool) (p : ℕ) : + ∃ c : B, contractRec B hB sched pts d n m N side p = N.contraction + 2 ^ p * c := by + induction m using Nat.strong_induction_on generalizing d n p with + | _ m ih => + rw [contractRec] + split + · exact ⟨0, by simp⟩ + · rename_i θ _ + split_ifs with hθ + · obtain ⟨hu, huM, hua, hh, hh', hn⟩ := hθ.1 + refine exists_callValue_eq_add B hB (fun n' m' N' side' p' => ?_) N side hθ.2.1 hθ.2.2 _ + hu (by simpa using huM) hua (by simpa using hh) (by simpa using hh') _ + (fun k l hkl => hpts fun h => hkl (Fin.castLE_injective _ h)) (by simp) + split_ifs with hm + · exact ih m' hm (d + 1) n' N' side' p' + · exact ⟨0, by simp⟩ + · exact ⟨0, by simp⟩ + +/-- **The recursion computes the contraction modulo `2 ^ p`.** -/ +theorem mk_contractRec (hpts : Pairwise fun k l => IsUnit (pts k - pts l)) (d n m : ℕ) + (N : TensorNetwork B Bool (Fin n) (Fin m)) (side : Fin n → Bool) (p : ℕ) : + Ideal.Quotient.mk (twoPowIdeal B p) (contractRec B hB sched pts d n m N side p) = + Ideal.Quotient.mk (twoPowIdeal B p) N.contraction := by + obtain ⟨c, hc⟩ := exists_contractRec_eq_add B hB sched pts hpts d n m N side p + rw [hc, Ideal.Quotient.eq, add_sub_cancel_left] + exact Ideal.mul_mem_right _ _ (Ideal.mem_span_singleton_self _) + +end Recursion + +end TensorNetwork + +end Cslib.Circuits.Frontier diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkRecursion.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkRecursion.lean new file mode 100644 index 0000000000..c3b115f8af --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkRecursion.lean @@ -0,0 +1,146 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.Circuit.Frontier.TensorNetwork.Recursion +import Complexity.Foundations.NumberTheory.Padics.AdjoinRootDigitLift +import Mathlib.Algebra.Field.ZMod +import Mathlib.Algebra.Polynomial.SpecificDegree +import Mathlib.Tactic.ComputeDegree +import Mathlib.Tactic.FinCases + +/-! +# Tests for the contraction recursion + +* **Renaming and lifting.** Renaming a two-edge network along a rotation of its vertices and a + swap of its edges keeps its contraction, and the canonical lift of a network over `ℤ / 4` to `ℤ` + (integer representatives in `{0, 1, 2, 3}`) reduces back to it. +* **Interpolation points in `O`.** For `f = U ^ 2 + U + 1`, the four binary-coordinate lifts in + `O = ℤ_(2)[U] / (f)` have unit differences, and `ℤ_(2)` lies in `O`. +* **Correctness of the recursion over `O`**, with those four points and a constant schedule + `a = u = 1`, `h = 3`. On a one-edge network these parameters are admissible (`3 M ≤ h u ≤ 3 M + u` + and `M (2 L - 1) = 3 < 4`), so the root call is active; its children are not smaller and are + evaluated directly. +-/ + +namespace ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.Recursion + +open Polynomial Cslib.Circuits.Frontier TensorNetwork + +/-! ### Renaming and lifting -/ + +/-- Edge `e` runs from row `e` to column `e + 2`; the tensor at `v` is `v + 1` when its edge +carries `true`, and `1` otherwise. -/ +def net : TensorNetwork ℤ Bool (Fin 4) (Fin 2) where + src e := ⟨e.val, by omega⟩ + tgt e := ⟨e.val + 2, by omega⟩ + tensor v x := if x ⟨v.val % 2, by omega⟩ then v.val + 1 else 1 + tensor_congr v x y h := by + rw [h ⟨v.val % 2, by omega⟩ (by fin_cases v <;> decide)] + +/-- Renaming the vertices by a rotation and the edges by a swap keeps the contraction. -/ +example : (net.reindex (finRotate 4) (Equiv.swap 0 1)).contraction = net.contraction := + contraction_reindex _ _ _ + +/-- The same network over `ℤ / 4`. -/ +def net₄ : TensorNetwork (ZMod 4) Bool (Fin 4) (Fin 2) := + net.map (Int.castRingHom (ZMod 4)) + +/-- The canonical lift of `net₄` to `ℤ`, by the integer representatives in `{0, …, 3}`, reduces +to `net₄`, so its contraction reduces to the contraction of `net₄`. -/ +example : (net₄.mapFun fun x => (x.val : ℤ)).map (Int.castRingHom (ZMod 4)) = net₄ := + map_mapFun _ (fun x => by simp) _ + +/-! ### Interpolation points in `O = ℤ_(2)[U] / (U ^ 2 + U + 1)` -/ + +/-- `U ^ 2 + U + 1`, irreducible modulo 2. -/ +noncomputable def f : ℤ[X] := X ^ 2 + X + 1 + +theorem f_monic : f.Monic := by unfold f; monicity! + +theorem f_natDegree : f.natDegree = 2 := by unfold f; compute_degree! + +theorem f_irreducible : Irreducible (f.map (Int.castRingHom (ZMod 2))) := by + unfold f + simp only [Polynomial.map_add, Polynomial.map_pow, Polynomial.map_X, Polynomial.map_one] + refine Polynomial.irreducible_of_degree_le_three_of_not_isRoot ?_ ?_ + · have : (X ^ 2 + X + 1 : (ZMod 2)[X]).natDegree = 2 := by compute_degree! + simp [this] + · intro x hx + rw [IsRoot.def, eval_add, eval_add, eval_pow, eval_X, eval_one] at hx + fin_cases x <;> revert hx <;> decide + +/-- The field `K = ℚ[U] / (U ^ 2 + U + 1)`. -/ +abbrev K : Type := AdjoinRoot (f.map (Int.castRingHom ℚ)) + +/-- The ring `O = ℤ_(2)[U] / (U ^ 2 + U + 1)`, the valuation ring of `K`. -/ +noncomputable abbrev O : Subring K := (AdjoinRoot.padicValuation 2 f_monic f_irreducible).integer + +/-- `ℤ_(2)` lies in `O`: the valuation of `K` extends that of `ℚ`. -/ +theorem hO : ∀ c ∈ Valuation.integer (Rat.padicValuation 2), algebraMap ℚ K c ∈ O := fun c hc => by + rw [Valuation.mem_integer_iff, AdjoinRoot.padicValuation_algebraMap] + exact hc + +/-- There are `2 ^ 2 = 4` digit vectors. -/ +theorem card_digits : Fintype.card (Fin f.natDegree → ZMod 2) = 4 := by + rw [Fintype.card_fun, ZMod.card, Fintype.card_fin, f_natDegree] + norm_num + +/-- The four binary-coordinate lifts `0, 1, U, 1 + U`, in some order. -/ +noncomputable def pts (k : Fin 4) : O := + AdjoinRoot.integerDigitLift f_monic f_irreducible + ((Fintype.equivFin (Fin f.natDegree → ZMod 2)).symm (Fin.cast card_digits.symm k)) + +/-- **The binary-coordinate lifts are interpolation points**: their differences are units of `O`. -/ +theorem pts_pairwise : Pairwise fun k l => IsUnit (pts k - pts l) := fun _ _ hkl => + AdjoinRoot.pairwise_isUnit_integerDigitLift_sub f_monic f_irreducible fun h => + hkl (Fin.cast_injective _ ((Fintype.equivFin _).symm.injective h)) + +/-! ### The recursion over `O` -/ + +/-- The constant schedule `a = u = 1`, `h = 3`. -/ +def sched (_ _ : ℕ) : Option TensorNetwork.CallParams := some ⟨1, 1, 3⟩ + +/-- On one edge with four points the schedule is admissible. -/ +example : (TensorNetwork.CallParams.mk 1 1 3).Admissible 1 4 := by decide + +/-- One edge from row `0` to column `1`, with tensor `(1, 2)` at the row and `(1, 1)` at the +column. -/ +noncomputable def net₁ : TensorNetwork O Bool (Fin 2) (Fin 1) where + src _ := 0 + tgt _ := 1 + tensor v x := if v = 0 ∧ x 0 then 2 else 1 + tensor_congr v x y h := by rw [h 0 (by fin_cases v <;> simp)] + +/-- **The root call is active**: it is the active call of `callValue` with the packing of +Lemma D.3, the four lifts as interpolation points, and recursive calls at depth one for the +children with fewer edges. -/ +example (p : ℕ) : + contractRec O hO sched pts 0 2 1 net₁ (fun v => decide (v = 0)) p = + callValue O hO + (fun n' m' N' side' p' => + if m' < 1 then contractRec O hO sched pts 1 n' m' N' side' p' else N'.contraction) + net₁ (fun v => decide (v = 0)) (fun _ => rfl) (fun _ => rfl) + (Cslib.PacketPacking.packing net₁.src net₁.tgt 1 64) p 1 3 le_rfl pts := by + rw [contractRec.eq_def] + simp only [sched] + rw [dite_eq_left ⟨by decide, fun _ => rfl, fun _ => rfl⟩] + rfl + +/-- **The recursion is correct**: at every precision, the root call returns the contraction of +`net₁` modulo `2 ^ p`. -/ +example (p : ℕ) : + Ideal.Quotient.mk (twoPowIdeal O p) + (contractRec O hO sched pts 0 2 1 net₁ (fun v => decide (v = 0)) p) = + Ideal.Quotient.mk (twoPowIdeal O p) net₁.contraction := + mk_contractRec O hO sched pts pts_pairwise 0 2 1 net₁ _ p + +/-- The recursion is correct for every schedule and every network. -/ +example (sched' : ℕ → ℕ → Option TensorNetwork.CallParams) (n m p : ℕ) + (N : TensorNetwork O Bool (Fin n) (Fin m)) (side : Fin n → Bool) : + ∃ c : O, contractRec O hO sched' pts 0 n m N side p = N.contraction + 2 ^ p * c := + exists_contractRec_eq_add O hO sched' pts pts_pairwise 0 n m N side p + +end ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.Recursion From bffa3cfe4e5d12e668c7cbd21458063402759c10 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:53:49 -0400 Subject: [PATCH 025/129] feat(foundations): count tuples of bounded weight by the partition generating function The number of tuples beta : iota -> N of total weight sum w_i beta_i < p, with at most m coordinates of each positive weight, is at most exp(theta p + m pi^2/(6 theta)) for every theta > 0, hence at most 2^(4 floor(sqrt(m p)) + 4). This is the mode count (D.15) of the Boolean tensor contraction theorem of [OpenAI2026ETH] (#1538). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Combinatorics/WeightBudget.lean | 245 ++++++++++++++++++ .../Combinatorics/WeightBudget.lean | 59 +++++ 3 files changed, 305 insertions(+) create mode 100644 Complexity/Foundations/Combinatorics/WeightBudget.lean create mode 100644 ComplexityTests/Foundations/Combinatorics/WeightBudget.lean diff --git a/Complexity.lean b/Complexity.lean index fdd3b220c6..68fc1497cf 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2594,6 +2594,7 @@ public import Complexity.Foundations.Combinatorics.Sunflower public import Complexity.Foundations.Combinatorics.Sunflower.LowerBound public import Complexity.Foundations.Combinatorics.Sunflower.Pluck public import Complexity.Foundations.Combinatorics.UniformSchedules +public import Complexity.Foundations.Combinatorics.WeightBudget public import Complexity.Foundations.Control.Monad.IsMonadHom.StateT public import Complexity.Foundations.Control.Monad.Memoize public import Complexity.Foundations.Data.BiTape diff --git a/Complexity/Foundations/Combinatorics/WeightBudget.lean b/Complexity/Foundations/Combinatorics/WeightBudget.lean new file mode 100644 index 0000000000..7d742fb6f2 --- /dev/null +++ b/Complexity/Foundations/Combinatorics/WeightBudget.lean @@ -0,0 +1,245 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Analysis.SpecialFunctions.Log.Basic +public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Basic +import Mathlib.Algebra.BigOperators.Ring.Finset +import Mathlib.Analysis.Complex.ExponentialBounds +import Mathlib.Analysis.Real.Pi.Bounds +import Mathlib.Analysis.SpecialFunctions.Log.Deriv +import Mathlib.Analysis.SpecialFunctions.Pow.Real +import Mathlib.Analysis.SpecificLimits.Basic +import Mathlib.Data.Fintype.Pi +import Mathlib.NumberTheory.ZetaValues +import Mathlib.Tactic.FieldSimp +import Mathlib.Tactic.Linarith +import Mathlib.Tactic.Positivity + +/-! +# Counting tuples of bounded weight + +Give each coordinate `i` of a tuple `β : ι → ℕ` a positive weight `w i`, with at most `m` +coordinates of each weight. This file bounds the number of tuples of total weight +`∑ i, w i * β i < p` by the partition generating function: + +* for every `θ > 0` there are at most `exp (θ p + m π² / (6 θ))` of them + (`Cslib.card_le_exp_of_sum_mul_lt`); +* taking `θ = √(m / p)`, there are at most `2 ^ (4 ⌊√(m p)⌋ + 4)` + (`Cslib.card_le_two_pow_of_sum_mul_lt`). + +This is the count (D.15) of the exponent tuples of the modes in the proof of the Boolean tensor +contraction theorem of [OpenAI2026ETH] (§D.4.5, pp. 62–63), where the modes play the coordinates +and their weights are their divisibility exponents. + +**The proof.** Put `x = e^{-θ}`. Every tuple of weight below `p` has `x ^ (weight) ≥ x ^ p`, and the +sum of `x ^ (weight)` over all tuples is at most the generating product `∏ i, (1 - x ^ w i)⁻¹`. +Its logarithm is `∑ i, -log (1 - x ^ w i)`, which is at most `m` times the sum of +`-log (1 - e^{-θ j})` over the distinct weights `j`. Expanding the logarithm and summing the +geometric series bounds that sum (`Cslib.sum_neg_log_one_sub_exp_le`): + + `∑_{j ≥ 1} -log (1 - e^{-θ j}) = ∑_{h ≥ 1} 1 / (h (e^{θ h} - 1)) ≤ (1 / θ) ∑_{h ≥ 1} 1 / h² + = π² / (6 θ)`, + +using `e^y - 1 ≥ y` and `∑ 1 / h² = π² / 6` (`hasSum_zeta_two`). + +## Main results + +* `Cslib.sum_neg_log_one_sub_exp_le`: `∑_{j ∈ s} -log (1 - e^{-θ j}) ≤ π² / (6 θ)` for a finite + set `s` of positive integers. +* `Cslib.card_le_exp_of_sum_mul_lt`: the generating-function bound on the number of tuples. +* `Cslib.card_le_two_pow_of_sum_mul_lt`: its natural-number form `2 ^ (4 ⌊√(m p)⌋ + 4)`. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.5, the count (D.15) + and the estimate following it (pp. 62–63). +-/ + +public section + +namespace Cslib + +open Finset Real + +/-- **The logarithm of the partition generating function.** For `θ > 0` and every finite set +`s` of positive integers, `∑_{j ∈ s} -log (1 - e^{-θ j}) ≤ π² / (6 θ)`. -/ +theorem sum_neg_log_one_sub_exp_le {θ : ℝ} (hθ : 0 < θ) (s : Finset ℕ) (hs : 0 ∉ s) : + ∑ j ∈ s, -log (1 - exp (-(θ * j))) ≤ π ^ 2 / (6 * θ) := by + have hterm : ∀ j ∈ s, HasSum (fun h : ℕ => exp (-(θ * j)) ^ (h + 1) / (h + 1)) + (-log (1 - exp (-(θ * j)))) := by + intro j hj + have hj0 : (0 : ℝ) < j := Nat.cast_pos.2 (Nat.pos_of_ne_zero fun h => hs (h ▸ hj)) + apply Real.hasSum_pow_div_log_of_abs_lt_one + rw [abs_of_pos (exp_pos _), exp_lt_one_iff] + have := mul_pos hθ hj0 + linarith + have hzeta : HasSum (fun h : ℕ => 1 / θ * (1 / ((h : ℝ) + 1) ^ 2)) (1 / θ * (π ^ 2 / 6)) := by + have h := (hasSum_nat_add_iff' 1).mpr hasSum_zeta_two + simp only [range_one, sum_singleton, Nat.cast_zero, ne_eq, OfNat.ofNat_ne_zero, + not_false_eq_true, zero_pow, div_zero, sub_zero, Nat.cast_add, Nat.cast_one] at h + exact h.mul_left _ + have hle := hasSum_le (fun h => ?_) (hasSum_sum hterm) hzeta + · calc _ ≤ 1 / θ * (π ^ 2 / 6) := hle + _ = π ^ 2 / (6 * θ) := by field_simp + -- For fixed `h`, the terms form a geometric series in `q = e^{-θ (h + 1)}`. + set y : ℝ := θ * (h + 1) with hy + have hy0 : 0 < y := by positivity + set q : ℝ := exp (-y) with hq + have hq0 : 0 ≤ q := (exp_pos _).le + have hq1 : q < 1 := exp_lt_one_iff.2 (neg_lt_zero.2 hy0) + have hpow : ∀ j : ℕ, exp (-(θ * j)) ^ (h + 1) = q ^ j := fun j => by + rw [hq, ← exp_nat_mul, ← exp_nat_mul, hy] + push_cast + ring_nf + have hgeo : ∑ j ∈ s, q ^ j ≤ (1 - q)⁻¹ - 1 := by + have := sum_le_hasSum (insert 0 s) (fun j _ => pow_nonneg hq0 j) + (hasSum_geometric_of_lt_one hq0 hq1) + rw [sum_insert hs, pow_zero] at this + linarith + have hkey : q * (1 + y) ≤ 1 := by + calc q * (1 + y) ≤ q * exp y := mul_le_mul_of_nonneg_left (by linarith [add_one_le_exp y]) hq0 + _ = 1 := by rw [hq, ← exp_add, neg_add_cancel, exp_zero] + have hq' : (1 - q)⁻¹ - 1 ≤ 1 / y := by + have h1q : 0 < 1 - q := by linarith + rw [show (1 - q)⁻¹ - 1 = q / (1 - q) by field_simp; ring, div_le_div_iff₀ h1q hy0] + linarith + have hh : (0 : ℝ) < h + 1 := by positivity + calc ∑ j ∈ s, exp (-(θ * j)) ^ (h + 1) / (h + 1) = (∑ j ∈ s, q ^ j) / (h + 1) := by + rw [sum_div] + exact sum_congr rfl fun j _ => by rw [hpow] + _ ≤ (1 / y) / (h + 1) := div_le_div_of_nonneg_right (hgeo.trans hq') hh.le + _ = 1 / θ * (1 / ((h : ℝ) + 1) ^ 2) := by rw [hy]; field_simp + +/-- **Counting tuples of bounded weight by the generating function.** Let every coordinate have a +positive weight, with at most `m` coordinates of each weight. Then for every `θ > 0` there are at +most `exp (θ p + m π² / (6 θ))` tuples of total weight below `p`. -/ +theorem card_le_exp_of_sum_mul_lt {ι : Type*} [Fintype ι] {w : ι → ℕ} (hw : ∀ i, 0 < w i) + {m : ℕ} (hm : ∀ j, #{i | w i = j} ≤ m) {p : ℕ} {S : Finset (ι → ℕ)} + (hS : ∀ β ∈ S, ∑ i, w i * β i < p) {θ : ℝ} (hθ : 0 < θ) : + (#S : ℝ) ≤ exp (θ * p + m * (π ^ 2 / (6 * θ))) := by + classical + set x : ℝ := exp (-θ) with hx + have hx0 : 0 < x := exp_pos _ + have hx1 : x < 1 := exp_lt_one_iff.2 (neg_lt_zero.2 hθ) + -- Every tuple contributes at least `x ^ p` to the generating sum. + have h1 : (#S : ℝ) * x ^ p ≤ ∑ β ∈ S, x ^ (∑ i, w i * β i) := by + calc (#S : ℝ) * x ^ p = ∑ _β ∈ S, x ^ p := by rw [sum_const, nsmul_eq_mul] + _ ≤ _ := sum_le_sum fun β hβ => pow_le_pow_of_le_one hx0.le hx1.le (hS β hβ).le + -- The tuples lie in a box, over which the generating sum factors. + have hsub : S ⊆ Fintype.piFinset fun _ => range p := by + intro β hβ + rw [Fintype.mem_piFinset] + intro i + rw [mem_range] + calc β i ≤ w i * β i := Nat.le_mul_of_pos_left _ (hw i) + _ ≤ ∑ i, w i * β i := single_le_sum (fun i _ => Nat.zero_le (w i * β i)) (mem_univ i) + _ < p := hS β hβ + have h2 : ∑ β ∈ S, x ^ (∑ i, w i * β i) ≤ ∏ i, ∑ b ∈ range p, (x ^ w i) ^ b := by + rw [prod_univ_sum] + calc ∑ β ∈ S, x ^ (∑ i, w i * β i) = ∑ β ∈ S, ∏ i, (x ^ w i) ^ β i := by + refine sum_congr rfl fun β _ => ?_ + rw [← prod_pow_eq_pow_sum] + exact prod_congr rfl fun i _ => pow_mul x (w i) (β i) + _ ≤ ∑ β ∈ Fintype.piFinset fun _ => range p, ∏ i, (x ^ w i) ^ β i := + sum_le_sum_of_subset_of_nonneg hsub fun _ _ _ => prod_nonneg fun _ _ => by positivity + -- Each factor is a truncated geometric series. + have h3 : ∏ i, ∑ b ∈ range p, (x ^ w i) ^ b ≤ exp (∑ i, -log (1 - x ^ w i)) := by + rw [exp_sum] + refine prod_le_prod₀ (fun i _ => sum_nonneg fun _ _ => by positivity) fun i _ => ?_ + have hy0 : 0 ≤ x ^ w i := by positivity + have hy1 : x ^ w i < 1 := pow_lt_one₀ hx0.le hx1 (hw i).ne' + rw [exp_neg, exp_log (by linarith)] + exact sum_le_hasSum _ (fun _ _ => by positivity) (hasSum_geometric_of_lt_one hy0 hy1) + -- Group the coordinates by weight. + have h4 : ∑ i, -log (1 - x ^ w i) ≤ m * (π ^ 2 / (6 * θ)) := by + have hxj : ∀ j : ℕ, x ^ j = exp (-(θ * j)) := fun j => by + rw [hx, ← exp_nat_mul] + ring_nf + have hnn : ∀ j ∈ univ.image w, 0 ≤ -log (1 - x ^ j) := by + intro j hj + obtain ⟨i, -, rfl⟩ := mem_image.1 hj + have : 0 < 1 - x ^ w i := by linarith [pow_lt_one₀ hx0.le hx1 (hw i).ne'] + have : 1 - x ^ w i ≤ 1 := by linarith [pow_pos hx0 (w i)] + linarith [log_nonpos (by linarith) this] + have h0 : 0 ∉ univ.image w := by + simp only [mem_image, mem_univ, true_and, not_exists] + exact fun i h => (hw i).ne' h + rw [sum_comp (fun j => -log (1 - x ^ j)) w] + calc ∑ j ∈ univ.image w, #{i ∈ univ | w i = j} • -log (1 - x ^ j) + ≤ ∑ j ∈ univ.image w, m • -log (1 - x ^ j) := + sum_le_sum fun j hj => nsmul_le_nsmul_left (hnn j hj) (hm j) + _ = m * ∑ j ∈ univ.image w, -log (1 - exp (-(θ * j))) := by + rw [← smul_sum, nsmul_eq_mul] + simp only [hxj] + _ ≤ m * (π ^ 2 / (6 * θ)) := + mul_le_mul_of_nonneg_left (sum_neg_log_one_sub_exp_le hθ _ h0) (Nat.cast_nonneg m) + have hxp : x ^ p * exp (θ * p) = 1 := by + rw [hx, ← exp_nat_mul, ← exp_add] + ring_nf + exact exp_zero + calc (#S : ℝ) = #S * x ^ p * exp (θ * p) := by rw [mul_assoc, hxp, mul_one] + _ ≤ exp (m * (π ^ 2 / (6 * θ))) * exp (θ * p) := + mul_le_mul_of_nonneg_right ((h1.trans h2).trans (h3.trans (exp_le_exp.2 h4))) + (exp_pos _).le + _ = exp (θ * p + m * (π ^ 2 / (6 * θ))) := by rw [← exp_add, add_comm] + +/-- `1 + π² / 6 ≤ 4 log 2`, so `exp ((1 + π² / 6) y) ≤ 16 ^ y`. -/ +private theorem one_add_pi_sq_div_six_le : 1 + π ^ 2 / 6 ≤ 4 * log 2 := by + nlinarith [pi_lt_d2, pi_pos, log_two_gt_d9] + +/-- **Counting tuples of bounded weight, natural-number form.** Let every coordinate have a +positive weight, with at most `m` coordinates of each weight. Then there are at most +`2 ^ (4 ⌊√(m p)⌋ + 4)` tuples of total weight below `p`: the generating-function bound at +`θ = √(m / p)`. -/ +theorem card_le_two_pow_of_sum_mul_lt {ι : Type*} [Fintype ι] {w : ι → ℕ} (hw : ∀ i, 0 < w i) + {m : ℕ} (hm : ∀ j, #{i | w i = j} ≤ m) {p : ℕ} {S : Finset (ι → ℕ)} + (hS : ∀ β ∈ S, ∑ i, w i * β i < p) : #S ≤ 2 ^ (4 * Nat.sqrt (m * p) + 4) := by + classical + rcases Nat.eq_zero_or_pos p with rfl | hp + · have : S = ∅ := eq_empty_of_forall_notMem fun β hβ => Nat.not_lt_zero _ (hS β hβ) + simp [this] + rcases Nat.eq_zero_or_pos m with rfl | hm0 + · have : IsEmpty ι := ⟨fun i => by + have hpos : 0 < #{i' | w i' = w i} := card_pos.2 ⟨i, by simp⟩ + have := hm (w i) + omega⟩ + exact (card_le_one_of_subsingleton S).trans Nat.one_le_two_pow + set n := Nat.sqrt (m * p) + set a : ℝ := √(m : ℝ) with ha + set b : ℝ := √(p : ℝ) with hb + have ha0 : 0 < a := Real.sqrt_pos.2 (Nat.cast_pos.2 hm0) + have hb0 : 0 < b := Real.sqrt_pos.2 (Nat.cast_pos.2 hp) + have ha2 : (m : ℝ) = a ^ 2 := (Real.sq_sqrt (Nat.cast_nonneg m)).symm + have hb2 : (p : ℝ) = b ^ 2 := (Real.sq_sqrt (Nat.cast_nonneg p)).symm + have hexp : a / b * p + m * (π ^ 2 / (6 * (a / b))) = a * b * (1 + π ^ 2 / 6) := by + rw [ha2, hb2] + field_simp + have hab : a * b ≤ n + 1 := by + have hlt : ((m * p : ℕ) : ℝ) < ((n + 1 : ℕ) : ℝ) ^ 2 := by + exact_mod_cast Nat.lt_succ_sqrt' (m * p) + have hsq : (a * b) ^ 2 < ((n : ℝ) + 1) ^ 2 := by + rw [mul_pow, ← ha2, ← hb2] + push_cast at hlt + exact hlt + exact (lt_of_pow_lt_pow_left₀ 2 (by positivity) hsq).le + have hreal : (#S : ℝ) ≤ 2 ^ (4 * n + 4) := by + calc (#S : ℝ) ≤ exp (a / b * p + m * (π ^ 2 / (6 * (a / b)))) := + card_le_exp_of_sum_mul_lt hw hm hS (div_pos ha0 hb0) + _ = exp (a * b * (1 + π ^ 2 / 6)) := by rw [hexp] + _ ≤ exp (((4 * n + 4 : ℕ) : ℝ) * log 2) := by + refine exp_le_exp.2 ?_ + have h1 : (0 : ℝ) ≤ 1 + π ^ 2 / 6 := by positivity + calc a * b * (1 + π ^ 2 / 6) ≤ (n + 1) * (4 * log 2) := + mul_le_mul hab one_add_pi_sq_div_six_le h1 (by positivity) + _ = ((4 * n + 4 : ℕ) : ℝ) * log 2 := by push_cast; ring + _ = 2 ^ (4 * n + 4) := by rw [exp_nat_mul, exp_log two_pos] + exact_mod_cast hreal + +end Cslib diff --git a/ComplexityTests/Foundations/Combinatorics/WeightBudget.lean b/ComplexityTests/Foundations/Combinatorics/WeightBudget.lean new file mode 100644 index 0000000000..294c9e7249 --- /dev/null +++ b/ComplexityTests/Foundations/Combinatorics/WeightBudget.lean @@ -0,0 +1,59 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ +module + +public import Complexity.Foundations.Combinatorics.WeightBudget + +public section + +open Finset Real + +namespace Cslib.Tests.WeightBudget + +/-! ### A concrete budget + +Two coordinates of weights `1` and `2` and budget `p = 5`: the tuples with `β₀ + 2 β₁ < 5` are +the five with `β₁ = 0`, the three with `β₁ = 1` and the one with `β₁ = 2`. -/ + +/-- The weights `1, 2`. -/ +def w (i : Fin 2) : ℕ := i.val + 1 + +/-- The tuples of weight below `5`. -/ +def budget : Finset (Fin 2 → ℕ) := + {β ∈ Fintype.piFinset fun _ => range 5 | ∑ i, w i * β i < 5} + +example : #budget = 9 := by decide + +/-- Each weight occurs once, so `m = 1`: the bound is `2 ^ (4 ⌊√5⌋ + 4) = 2 ^ 12`. -/ +example : #budget ≤ 2 ^ 12 := by + have h := card_le_two_pow_of_sum_mul_lt (w := w) (m := 1) (p := 5) (S := budget) + (fun i => Nat.succ_pos _) (fun j => by + rw [card_le_one] + intro a ha b hb + rw [mem_filter] at ha hb + exact Fin.ext (by simp only [w] at ha hb; omega)) + (fun β hβ => (mem_filter.1 hβ).2) + simpa [(Nat.eq_sqrt.2 ⟨by norm_num, by norm_num⟩ : 2 = Nat.sqrt 5).symm] using h + +/-- The real form at `θ = 1`. -/ +example : (#budget : ℝ) ≤ exp (5 + π ^ 2 / 6) := by + have h := card_le_exp_of_sum_mul_lt (w := w) (m := 1) (p := 5) (S := budget) + (fun i => Nat.succ_pos _) (fun j => by + rw [card_le_one] + intro a ha b hb + rw [mem_filter] at ha hb + exact Fin.ext (by simp only [w] at ha hb; omega)) + (fun β hβ => (mem_filter.1 hβ).2) one_pos + simpa using h + +/-! ### The logarithmic sum -/ + +example : -log (1 - exp (-(1 * ((1 : ℕ) : ℝ)))) + -log (1 - exp (-(1 * ((2 : ℕ) : ℝ)))) ≤ + π ^ 2 / (6 * 1) := by + have h := sum_neg_log_one_sub_exp_le one_pos {1, 2} (by decide) + rwa [sum_pair (by decide)] at h + +end Cslib.Tests.WeightBudget From bbfbba0b54fa647fdf327b2ec6455559155b0797 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:55:43 -0400 Subject: [PATCH 026/129] feat(circuit/frontier): children of an active call have at most M/16 edges under (D.6) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/Recursion.lean | 53 +++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean index 3c9753220f..92f4e74600 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean @@ -50,9 +50,10 @@ summation of the manuscript's leaves. The recursion is well founded on the numbe **Correctness** (`TensorNetwork.exists_contractRec_eq_add`). For interpolation points with unit differences, every call returns the contraction modulo `2 ^ p`, for every schedule: the manuscript's "induction from the leaves" (p. 64) becomes strong induction on the number of edges. So the -parameter schedule (D.4)–(D.6) matters only for the running time, which this file does not treat; -under it, Lemma D.3 (3) and `ModeSplitting.mul_card_edges_child_le` make every child smaller, so the -fallback for children is not taken. In the manuscript's ring `B = O = ℤ_(2)[U] / (f)`, the +parameter schedule (D.4)–(D.6) matters only for the running time, which this file does not treat. +Under the first inequality of (D.6), every child has at most `M / 16` edges +(`TensorNetwork.mul_card_edges_child_callSplitting_le`, from Lemma D.3 (3)), so an active call with +an edge never falls back for its children. In the manuscript's ring `B = O = ℤ_(2)[U] / (f)`, the binary-coordinate lifts are such points (`AdjoinRoot.isUnit_integerDigitLift_sub`). ## Main definitions @@ -74,6 +75,8 @@ binary-coordinate lifts are such points (`AdjoinRoot.isUnit_integerDigitLift_sub correct when its values are the sums of the products of the children's contractions. * `Cslib.Circuits.Frontier.TensorNetwork.exists_callValue_eq_add`: one call is correct for a correct oracle. +* `Cslib.Circuits.Frontier.TensorNetwork.mul_card_edges_child_callSplitting_le`: under (D.6), the + children have at most `M / 16` edges. * `Cslib.Circuits.Frontier.TensorNetwork.exists_contractRec_eq_add`, `Cslib.Circuits.Frontier.TensorNetwork.mk_contractRec`: the recursion returns the contraction modulo `2 ^ p`. @@ -351,6 +354,50 @@ theorem exists_callValue_eq_add {rec : ContractionOracle B} (hrec : rec.Correct) end Call +/-! ### Children are smaller under (D.6) -/ + +section ChildSize + +variable {K : Type*} [CommRing K] [Algebra ℚ K] (B : Subring K) + (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) {V E : Type*} [Fintype V] [DecidableEq V] [FinEnum V] + [Fintype E] [DecidableEq E] [LinearOrder E] + +omit [Fintype V] [DecidableEq E] in +/-- **Children have at most `M / 16` edges** under the size hypothesis `2 Δ D² ≤ M` of (D.6), +`D = 64 ^ (2 a)`, for the packing of Lemma D.3 with `r = 64` and maximum degree `Δ`: each group has +at most `2 M / 64` ports on each side (`PacketPacking.row_ports_packing_le`), and a child has one +edge per port of its group. The source (§D.4.6, p. 63) bounds a child by `6M/r = 3M/32 < M/8` +edges from the `3M/r` port bound of Lemma D.3 (3); `PacketPacking.packing` has the sharper port +bound `2M/r`. -/ +theorem mul_card_edges_child_callSplitting_le (N : TensorNetwork B Bool V E) (side : V → Bool) + (hsrc : ∀ e, side (N.src e) = true) (htgt : ∀ e, side (N.tgt e) = false) {a Δ : ℕ} + (hrow : ∀ v, #{e | N.src e = v} ≤ Δ) (hcol : ∀ w, #{e | N.tgt e = w} ≤ Δ) + (hM : 2 * Δ * (64 ^ (2 * a)) ^ 2 ≤ Fintype.card E) (p u h : ℕ) (hua : u ≤ a) {t : ℤ} + (ht : t ∈ recoveryNodes a) (hq : 0 < taperMult u h t) (z : B) (g : Fin 64) : + 64 * Fintype.card + ({e // (callSplitting B hB N side hsrc htgt (PacketPacking.packing N.src N.tgt a 64) p u h + hua ht hq z).group (N.src e) = g} ⊕ + {e // (callSplitting B hB N side hsrc htgt (PacketPacking.packing N.src N.tgt a 64) p u h + hua ht hq z).group (N.tgt e) = g}) ≤ + 4 * Fintype.card E := by + have hg : (callSplitting B hB N side hsrc htgt (PacketPacking.packing N.src N.tgt a 64) p u h + hua ht hq z).group = + packingGroup side (PacketPacking.packing N.src N.tgt a 64) + ⟨_, padicValInt_lt_of_taperMult_pos hq hua⟩ := by + exact cauchyModeSplitting_group .. + have := ModeSplitting.mul_card_edges_child_le (c := 2) (r := 64) + (callSplitting B hB N side hsrc htgt (PacketPacking.packing N.src N.tgt a 64) p u h hua ht hq z) + g (by + rw [hg] + exact (congrArg (64 * ·) (card_packingGroup_src side hsrc _ _ g)).trans_le + (PacketPacking.row_ports_packing_le N.src N.tgt a 64 hrow hcol hM _ g)) (by + rw [hg] + exact (congrArg (64 * ·) (card_packingGroup_tgt side htgt _ _ g)).trans_le + (PacketPacking.col_ports_packing_le N.src N.tgt a 64 hrow hcol hM _ g)) + exact this.trans_eq (by ring) + +end ChildSize + /-! ### The recursion -/ /-- The parameters of an active call: packets of `L = 2 ^ a` slots, and the tapered multiplicities From 5bea2c18e98885ca91912b0cb285c04d22e7b250 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:59:31 -0400 Subject: [PATCH 027/129] feat(PolyTime): polynomials over (Z/2^p)[U]/(f) and Lagrange interpolation Addition, scaling, multiplication by z - v and Horner evaluation of coefficient-list polynomials, and Lagrange interpolation with Newton inverses of the denominators, proved to take the given values and certified polynomial-time with the precision in unary. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../PolyTime/GaloisRingInterpolation.lean | 594 ++++++++++++++++++ ComplexityTests/GaloisRingInterpolation.lean | 55 ++ 3 files changed, 650 insertions(+) create mode 100644 Complexity/Computability/PolyTime/GaloisRingInterpolation.lean create mode 100644 ComplexityTests/GaloisRingInterpolation.lean diff --git a/Complexity.lean b/Complexity.lean index 566b83dcbd..a79124c541 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1986,6 +1986,7 @@ public import Complexity.Computability.PolyTime.CSP.ToCNF public import Complexity.Computability.PolyTime.CommonCountingAccum public import Complexity.Computability.PolyTime.FineGrained.ParameterCheck public import Complexity.Computability.PolyTime.GaloisRingArithmetic +public import Complexity.Computability.PolyTime.GaloisRingInterpolation public import Complexity.Computability.PolyTime.GreedyLabelSchedule public import Complexity.Computability.PolyTime.GreedySlotScore public import Complexity.Computability.PolyTime.Int.ExactDiv diff --git a/Complexity/Computability/PolyTime/GaloisRingInterpolation.lean b/Complexity/Computability/PolyTime/GaloisRingInterpolation.lean new file mode 100644 index 0000000000..d0d12173f6 --- /dev/null +++ b/Complexity/Computability/PolyTime/GaloisRingInterpolation.lean @@ -0,0 +1,594 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.PolyTime.GaloisRingArithmetic +public import Mathlib.Algebra.Polynomial.BigOperators +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Length +import Complexity.Computability.Machines.Turing.MultiTape.Internal.EqualityPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListLookup +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListMap +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeLoops +import Mathlib.Algebra.BigOperators.Intervals +import Mathlib.Data.List.GetD +import Mathlib.Data.Nat.Size + +/-! +# Polynomials over `(ℤ / 2 ^ p)[U] / (f)` and Lagrange interpolation, on coefficient lists + +Polynomials in `z` over the coefficient ring `A_p = (ℤ / 2 ^ p)[U] / (f)` of +`Complexity/Computability/PolyTime/GaloisRingArithmetic.lean` are lists of coefficients, +constant first, each a coefficient list of `A_p` (`GaloisRingArith.pval`). + +* **Addition** (`GaloisRingArith.padd`), **scaling** (`GaloisRingArith.psmul`) and + **multiplication by `z - v`** (`GaloisRingArith.pmulLinear`). +* **Evaluation** (`GaloisRingArith.peval`) by Horner's rule. +* **Lagrange interpolation** (`GaloisRingArith.interp`) at nodes `v₀, …, v_{n-1}`: the sum of + `y_i (∏_{j ≠ i} (v_i - v_j))⁻¹ ∏_{j ≠ i} (z - v_j)`, with the inverses of + `GaloisRingArith.inv`. When those inverses are correct, for instance at nodes with unit + differences in a Galois ring, the result takes the value `y_i` at `v_i` + (`GaloisRingArith.eval_pval_interp`). It has at most `n + 1` coefficients and degree below `n` + (`GaloisRingArith.pval_interp`); at nodes with unit differences, a polynomial of degree below + `n` is determined by its values there. + +Every operation is polynomial-time, with the precision in unary +(`GaloisRingArith.computableInPolyTime_interp` and the rules before it). + +This is the interpolation step of the contraction algorithm of [OpenAI2026ETH] +(Appendix D.4.4, p. 61), which recovers `F (t + α z)` modulo `2 ^ p_t` from its values at +`M (2 L - 1) + 1` binary-coordinate lifts. + +## Main definitions + +* `Cslib.GaloisRingArith.pval`: the polynomial of a list of coefficient lists. +* `Cslib.GaloisRingArith.padd`, `Cslib.GaloisRingArith.psmul`, `Cslib.GaloisRingArith.pmulLinear`, + `Cslib.GaloisRingArith.peval`: polynomial arithmetic. +* `Cslib.GaloisRingArith.interp`: Lagrange interpolation. + +## Main results + +* `Cslib.GaloisRingArith.pval_padd`, `Cslib.GaloisRingArith.pval_psmul`, + `Cslib.GaloisRingArith.pval_pmulLinear`, `Cslib.GaloisRingArith.peval_eq`: correctness. +* `Cslib.GaloisRingArith.eval_pval_interp`: interpolation takes the given values. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Appendix D.4.4 (p. 61). +-/ + +@[expose] public section + +namespace Cslib.GaloisRingArith + +open Polynomial Finset + +variable {p : ℕ} {fl : List ℕ} + +/-- The polynomial `∑ k, P_k z ^ k` over `A_p` of a list of coefficient lists. -/ +noncomputable def pval (p : ℕ) (fl : List ℕ) (P : List (List ℕ)) : (Ring p fl)[X] := + ∑ k ∈ range P.length, C (val p fl (P.getD k [])) * X ^ k + +/-- The empty coefficient list has value zero. -/ +@[simp] theorem val_nil : val p fl [] = 0 := by simp [val] + +/-- Coefficientwise sum, of length the longer length. -/ +def padd (p : ℕ) (fl : List ℕ) (P Q : List (List ℕ)) : List (List ℕ) := + (List.range (max P.length Q.length)).map fun k => add p fl (P.getD k []) (Q.getD k []) + +/-- Scaling by an element of `A_p`. -/ +def psmul (p : ℕ) (fl c : List ℕ) (P : List (List ℕ)) : List (List ℕ) := + P.map fun a => mul p fl c a + +/-- Multiplication by `z - v`. -/ +def pmulLinear (p : ℕ) (fl v : List ℕ) (P : List (List ℕ)) : List (List ℕ) := + (List.range (P.length + 1)).map fun k => + sub p fl (([] :: P).getD k []) (mul p fl v (P.getD k [])) + +/-- Evaluation at `z` by Horner's rule. -/ +def peval (p : ℕ) (fl : List ℕ) (P : List (List ℕ)) (z : List ℕ) : List ℕ := + P.foldr (fun a acc => add p fl a (mul p fl z acc)) (zero fl) + +/-- Extending a polynomial by zero coefficients does not change it. -/ +theorem pval_eq_sum_of_le (P : List (List ℕ)) {n : ℕ} (h : P.length ≤ n) : + pval p fl P = ∑ k ∈ range n, C (val p fl (P.getD k [])) * X ^ k := by + rw [pval, ← sum_range_add_sum_Ico _ h] + rw [sum_eq_zero (s := Ico _ _) fun k hk => by + rw [List.getD_eq_default _ _ (by simp at hk; omega), val_nil, C_0, zero_mul], add_zero] + +/-- **Addition is correct.** -/ +theorem pval_padd (P Q : List (List ℕ)) : + pval p fl (padd p fl P Q) = pval p fl P + pval p fl Q := by + rw [pval_eq_sum_of_le P (le_max_left P.length Q.length), + pval_eq_sum_of_le Q (le_max_right P.length Q.length), ← sum_add_distrib, pval] + simp only [padd, List.length_map, List.length_range] + refine sum_congr rfl fun k hk => ?_ + rw [List.getD_eq_getElem _ _ (by simpa using hk)] + simp [val_add, add_mul] + +/-- **Scaling is correct.** -/ +theorem pval_psmul (c : List ℕ) (P : List (List ℕ)) : + pval p fl (psmul p fl c P) = C (val p fl c) * pval p fl P := by + rw [pval, pval, mul_sum] + simp only [psmul, List.length_map] + refine sum_congr rfl fun k hk => ?_ + rw [List.getD_eq_getElem _ _ (by simpa using hk), List.getD_eq_getElem _ _ (by simpa using hk)] + simp [val_mul, mul_assoc] + +/-- **Multiplication by `z - v` is correct.** -/ +theorem pval_pmulLinear (v : List ℕ) (P : List (List ℕ)) : + pval p fl (pmulLinear p fl v P) = (X - C (val p fl v)) * pval p fl P := by + have hcoeff (k : ℕ) (hk : k < P.length + 1) : + val p fl ((pmulLinear p fl v P).getD k []) = + val p fl (([] :: P).getD k []) - val p fl v * val p fl (P.getD k []) := by + rw [pmulLinear, List.getD_eq_getElem _ _ (by simpa using hk)] + simp [val_sub, val_mul] + rw [pval, show (pmulLinear p fl v P).length = P.length + 1 by simp [pmulLinear], + sum_congr rfl fun k hk => by rw [hcoeff k (mem_range.mp hk)]] + rw [sub_mul, pval, mul_sum, mul_sum] + simp only [map_sub, map_mul, sub_mul, sum_sub_distrib] + congr 1 + · rw [sum_range_succ'] + simp only [List.getD_cons_zero, val_nil, C_0, zero_mul, add_zero, List.getD_cons_succ] + refine sum_congr rfl fun k _ => by ring + · rw [sum_range_succ, List.getD_eq_default _ _ le_rfl, val_nil, C_0, mul_zero, zero_mul, + add_zero] + refine sum_congr rfl fun k _ => by ring + +/-- **Evaluation is correct.** -/ +theorem peval_eq (P : List (List ℕ)) (z : List ℕ) : + val p fl (peval p fl P z) = (pval p fl P).eval (val p fl z) := by + induction P with + | nil => simp [peval, pval] + | cons a P ih => + rw [peval, List.foldr_cons, ← peval, val_add, val_mul, ih] + simp only [pval, List.length_cons, sum_range_succ', List.getD_cons_succ, List.getD_cons_zero, + eval_add, eval_finsetSum, eval_mul, eval_C, eval_pow, eval_X, pow_zero, mul_one, pow_succ, + mul_sum] + rw [add_comm] + congr 1 + refine sum_congr rfl fun k _ => by ring + +/-! ### Lagrange interpolation -/ + +/-- The numerator `∏_{j < n, j ≠ i} (z - v_j)` of the `i`-th Lagrange basis polynomial. -/ +def basisNum (p : ℕ) (fl : List ℕ) (v : List (List ℕ)) (i : ℕ) : List (List ℕ) := + (List.range v.length).foldl + (fun P j => if j = i then P else pmulLinear p fl (v.getD j []) P) [one fl] + +/-- The denominator `∏_{j < n, j ≠ i} (v_i - v_j)` of the `i`-th Lagrange basis polynomial. -/ +def basisDen (p : ℕ) (fl : List ℕ) (v : List (List ℕ)) (i : ℕ) : List ℕ := + (List.range v.length).foldl + (fun d j => if j = i then d else mul p fl d (sub p fl (v.getD i []) (v.getD j []))) (one fl) + +/-- **Lagrange interpolation** of the values `y` at the nodes `v`. -/ +def interp (p : ℕ) (fl : List ℕ) (v : List (List ℕ)) (y : List (List ℕ)) : List (List ℕ) := + (List.range v.length).foldl (fun P i => + padd p fl P (psmul p fl (mul p fl (y.getD i []) (inv p fl (basisDen p fl v i))) + (basisNum p fl v i))) [] + +theorem pval_basisNum (v : List (List ℕ)) (i : ℕ) : + pval p fl (basisNum p fl v i) = + ∏ j ∈ (range v.length).erase i, (X - C (val p fl (v.getD j []))) := by + have h (m : ℕ) (hm : m ≤ v.length) : pval p fl ((List.range m).foldl + (fun P j => if j = i then P else pmulLinear p fl (v.getD j []) P) [one fl]) = + ∏ j ∈ (range m).erase i, (X - C (val p fl (v.getD j []))) := by + induction m with + | zero => simp [pval] + | succ m ih => + rw [List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil, range_add_one] + by_cases hmi : m = i + · subst hmi + simp only [↓reduceIte] + rw [ih (by omega), erase_insert (by simp), Finset.erase_eq_of_notMem (by simp)] + · simp only [hmi, ↓reduceIte] + rw [pval_pmulLinear, ih (by omega), erase_insert_of_ne hmi, prod_insert (by simp)] + exact h _ le_rfl + +theorem val_basisDen (v : List (List ℕ)) (i : ℕ) : + val p fl (basisDen p fl v i) = + ∏ j ∈ (range v.length).erase i, (val p fl (v.getD i []) - val p fl (v.getD j [])) := by + have h (m : ℕ) : val p fl ((List.range m).foldl + (fun d j => if j = i then d else mul p fl d (sub p fl (v.getD i []) (v.getD j []))) + (one fl)) = ∏ j ∈ (range m).erase i, (val p fl (v.getD i []) - val p fl (v.getD j [])) := by + induction m with + | zero => simp + | succ m ih => + rw [List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil, range_add_one] + by_cases hmi : m = i + · subst hmi + simp only [↓reduceIte] + rw [ih, erase_insert (by simp), Finset.erase_eq_of_notMem (by simp)] + · simp only [hmi, ↓reduceIte] + rw [val_mul, val_sub, ih, erase_insert_of_ne hmi, prod_insert (by simp), mul_comm] + exact h _ + +/-- **Interpolation is the Lagrange formula**, with the computed inverses of the denominators. -/ +theorem pval_interp (v y : List (List ℕ)) : + pval p fl (interp p fl v y) = ∑ i ∈ range v.length, + C (val p fl (y.getD i []) * val p fl (inv p fl (basisDen p fl v i))) * + ∏ j ∈ (range v.length).erase i, (X - C (val p fl (v.getD j []))) := by + have h (m : ℕ) (hm : m ≤ v.length) : pval p fl ((List.range m).foldl (fun P i => + padd p fl P (psmul p fl (mul p fl (y.getD i []) (inv p fl (basisDen p fl v i))) + (basisNum p fl v i))) []) = ∑ i ∈ range m, + C (val p fl (y.getD i []) * val p fl (inv p fl (basisDen p fl v i))) * + ∏ j ∈ (range v.length).erase i, (X - C (val p fl (v.getD j []))) := by + induction m with + | zero => simp [pval] + | succ m ih => + rw [List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil, sum_range_succ, + pval_padd, ih (by omega), pval_psmul, val_mul, pval_basisNum] + exact h _ le_rfl + +/-- **Interpolation takes the given values** at the nodes, given correct inverses of the +denominators. -/ +theorem eval_pval_interp (v y : List (List ℕ)) + (hinv : ∀ i < v.length, val p fl (basisDen p fl v i) * + val p fl (inv p fl (basisDen p fl v i)) = 1) {k : ℕ} (hk : k < v.length) : + (pval p fl (interp p fl v y)).eval (val p fl (v.getD k [])) = val p fl (y.getD k []) := by + rw [pval_interp v y, eval_finsetSum, sum_eq_single k] + · rw [eval_mul, eval_C, eval_prod] + simp only [eval_sub, eval_X, eval_C] + rw [← val_basisDen, mul_assoc, mul_comm (val p fl (inv p fl _)), hinv k hk, mul_one] + · intro i hi hik + rw [eval_mul, eval_prod, prod_eq_zero (i := k) (mem_erase.mpr ⟨Ne.symm hik, mem_range.mpr hk⟩) + (by simp), mul_zero] + · intro hk' + exact absurd (mem_range.mpr hk) hk' + +/-! ### Sizes -/ + +/-- A coefficient list is *small* when it has `e` coordinates below `2 ^ (p + 1)`: reduced +coordinates and the unit both are. -/ +def Small (p : ℕ) (fl c : List ℕ) : Prop := c.length = fl.length ∧ ∀ a ∈ c, a < 2 ^ (p + 1) + +theorem small_of_lt {c : List ℕ} (hlen : c.length = fl.length) (hlt : ∀ a ∈ c, a < 2 ^ p) : + Small p fl c := + ⟨hlen, fun a ha => (hlt a ha).trans (Nat.pow_lt_pow_right (by norm_num) (by omega))⟩ + +theorem small_add (c d : List ℕ) : Small p fl (add p fl c d) := + small_of_lt (length_add p fl c d) fun _ h => lt_of_mem_add h + +theorem small_mul (c d : List ℕ) : Small p fl (mul p fl c d) := + small_of_lt (length_mul p fl c d) fun _ h => lt_of_mem_mul h + +theorem small_sub (c d : List ℕ) : Small p fl (sub p fl c d) := small_add _ _ + +theorem small_one : Small p fl (one fl) := by + refine ⟨by simp [one], fun a ha => ?_⟩ + simp only [one, List.mem_map, List.mem_range] at ha + obtain ⟨i, -, rfl⟩ := ha + have : 1 < 2 ^ (p + 1) := Nat.one_lt_two_pow (by omega) + split <;> omega + +theorem length_encList_le_of_small {c : List ℕ} (h : Small p fl c) : + (Turing.MultiTapeTM.encList Turing.MultiTapeTM.encNat c).length ≤ + fl.length * (2 * (p + 1) + 1) := + length_encList_le h.1 h.2 + +/-- A list of small coefficient lists of length at most `n` is at most +`n (2 e (2 p + 3) + 1)` long. -/ +theorem length_encPoly_le {P : List (List ℕ)} {n : ℕ} (hlen : P.length ≤ n) + (h : ∀ c ∈ P, Small p fl c) : + (Turing.MultiTapeTM.encList (Turing.MultiTapeTM.encList Turing.MultiTapeTM.encNat) P).length ≤ + n * (2 * (fl.length * (2 * (p + 1) + 1)) + 1) := by + rw [Turing.MultiTapeTM.length_encList] + calc (P.map fun c => 2 * (Turing.MultiTapeTM.encList Turing.MultiTapeTM.encNat c).length + 1).sum + ≤ (P.map fun c => 2 * (Turing.MultiTapeTM.encList Turing.MultiTapeTM.encNat c).length + + 1).length • (2 * (fl.length * (2 * (p + 1) + 1)) + 1) := + List.sum_le_length_nsmul _ _ fun m hm => by + obtain ⟨c, hc, rfl⟩ := List.mem_map.mp hm + have := length_encList_le_of_small (h c hc) + omega + _ ≤ n * (2 * (fl.length * (2 * (p + 1) + 1)) + 1) := by + rw [List.length_map, smul_eq_mul] + exact Nat.mul_le_mul_right _ hlen + +@[simp] theorem length_padd (P Q : List (List ℕ)) : + (padd p fl P Q).length = max P.length Q.length := by simp [padd] + +@[simp] theorem length_psmul (c : List ℕ) (P : List (List ℕ)) : + (psmul p fl c P).length = P.length := by simp [psmul] + +@[simp] theorem length_pmulLinear (v : List ℕ) (P : List (List ℕ)) : + (pmulLinear p fl v P).length = P.length + 1 := by simp [pmulLinear] + +theorem small_of_mem_padd {P Q : List (List ℕ)} {c : List ℕ} (h : c ∈ padd p fl P Q) : + Small p fl c := by + obtain ⟨k, -, rfl⟩ := List.mem_map.mp h + exact small_add _ _ + +theorem small_of_mem_psmul {c' : List ℕ} {P : List (List ℕ)} {c : List ℕ} + (h : c ∈ psmul p fl c' P) : Small p fl c := by + obtain ⟨a, -, rfl⟩ := List.mem_map.mp h + exact small_mul _ _ + +theorem small_of_mem_pmulLinear {v : List ℕ} {P : List (List ℕ)} {c : List ℕ} + (h : c ∈ pmulLinear p fl v P) : Small p fl c := by + obtain ⟨k, -, rfl⟩ := List.mem_map.mp h + exact small_sub _ _ + +theorem length_basisNum_le (v : List (List ℕ)) (i : ℕ) : + (basisNum p fl v i).length ≤ v.length + 1 := by + have h (m : ℕ) : ((List.range m).foldl + (fun P j => if j = i then P else pmulLinear p fl (v.getD j []) P) [one fl]).length ≤ + m + 1 := by + induction m with + | zero => simp + | succ m ih => + rw [List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil] + split + · omega + · rw [length_pmulLinear]; omega + exact h _ + +theorem small_of_mem_basisNum {v : List (List ℕ)} {i : ℕ} {c : List ℕ} + (h : c ∈ basisNum p fl v i) : Small p fl c := by + have key (m : ℕ) : ∀ c ∈ (List.range m).foldl + (fun P j => if j = i then P else pmulLinear p fl (v.getD j []) P) [one fl], Small p fl c := by + induction m with + | zero => simp [small_one] + | succ m ih => + rw [List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil] + split + · exact ih + · exact fun c hc => small_of_mem_pmulLinear hc + exact key _ c h + +/-! ### Polynomial time -/ + +section PolyTime + +open Turing.MultiTapeTM + +variable {α : Type} {encIn : α ↪ List Bool} + +private theorem lengthPolyTime {β : Type} {encB : β ↪ List Bool} {l : α → List β} + (hl : ComputableInPolyTime l encIn (encList encB)) : + ComputableInPolyTime (fun x => (l x).length) encIn encUnary := + (ComputableInPolyTime.length_list encB).comp hl + +private theorem getDPolyTime {P : α → List (List ℕ)} {k : α → ℕ} + (hP : ComputableInPolyTime P encIn (encList (encList encNat))) + (hk : ComputableInPolyTime k encIn encUnary) : + ComputableInPolyTime (fun x => (P x).getD (k x) []) encIn (encList encNat) := + (ComputableInPolyTime.listGetD (encA := encList encNat) []).comp (hP.prod hk) + +/-- **Addition of polynomials is polynomial-time**, with the precision in unary. -/ +@[fun_prop] +theorem computableInPolyTime_padd {p : α → ℕ} {fl : α → List ℕ} {P Q : α → List (List ℕ)} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hP : ComputableInPolyTime P encIn (encList (encList encNat))) + (hQ : ComputableInPolyTime Q encIn (encList (encList encNat))) : + ComputableInPolyTime (fun x => padd (p x) (fl x) (P x) (Q x)) encIn + (encList (encList encNat)) := by + have h1 := ComputableInPolyTime.fst encIn encUnary + have h2 := ComputableInPolyTime.snd encIn encUnary + have hmax : ComputableInPolyTime (fun x => max (P x).length (Q x).length) encIn encUnary := + (ComputableInPolyTime.unary_add' (lengthPolyTime hP) + (ComputableInPolyTime.unary_sub' (lengthPolyTime hQ) (lengthPolyTime hP))).of_enc_eq + fun x => by congr 1; omega + exact ComputableInPolyTime.mapWithInput (ComputableInPolyTime.range.comp hmax) + (computableInPolyTime_add (hp.comp h1) (hfl.comp h1) (getDPolyTime (hP.comp h1) h2) + (getDPolyTime (hQ.comp h1) h2)) + +/-- **Scaling a polynomial is polynomial-time**, with the precision in unary. -/ +@[fun_prop] +theorem computableInPolyTime_psmul {p : α → ℕ} {fl c : α → List ℕ} {P : α → List (List ℕ)} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hc : ComputableInPolyTime c encIn (encList encNat)) + (hP : ComputableInPolyTime P encIn (encList (encList encNat))) : + ComputableInPolyTime (fun x => psmul (p x) (fl x) (c x) (P x)) encIn + (encList (encList encNat)) := by + have h1 := ComputableInPolyTime.fst encIn (encList encNat) + have h2 := ComputableInPolyTime.snd encIn (encList encNat) + exact ComputableInPolyTime.mapWithInput hP + (computableInPolyTime_mul (hp.comp h1) (hfl.comp h1) (hc.comp h1) h2) + +/-- **Multiplication by `z - v` is polynomial-time**, with the precision in unary. -/ +@[fun_prop] +theorem computableInPolyTime_pmulLinear {p : α → ℕ} {fl v : α → List ℕ} + {P : α → List (List ℕ)} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hv : ComputableInPolyTime v encIn (encList encNat)) + (hP : ComputableInPolyTime P encIn (encList (encList encNat))) : + ComputableInPolyTime (fun x => pmulLinear (p x) (fl x) (v x) (P x)) encIn + (encList (encList encNat)) := by + have h1 := ComputableInPolyTime.fst encIn encUnary + have h2 := ComputableInPolyTime.snd encIn encUnary + exact ComputableInPolyTime.mapWithInput (ComputableInPolyTime.range.comp + (ComputableInPolyTime.unary_add' (lengthPolyTime hP) (ComputableInPolyTime.const 1))) + (computableInPolyTime_sub (hp.comp h1) (hfl.comp h1) + (getDPolyTime (ComputableInPolyTime.cons' (ComputableInPolyTime.const []) + (hP.comp h1)) h2) + (computableInPolyTime_mul (hp.comp h1) (hfl.comp h1) (hv.comp h1) + (getDPolyTime (hP.comp h1) h2))) + +private theorem polyBounded_small {p : α → ℕ} {fl : α → List ℕ} {n : α → ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hn : PolyBounded (fun x => (encIn x).length) n) : + PolyBounded (fun x => (encIn x).length) + (fun x => n x * (2 * ((fl x).length * (2 * (p x + 1) + 1)) + 1)) := by + have hpl : PolyBounded (fun x => (encIn x).length) p := by + simpa using (ComputableInPolyTime.polyBounded_length hp) + have hfll : PolyBounded (fun x => (encIn x).length) fun x => (fl x).length := + (lengthPolyTime hfl).polyBounded_length.mono fun x => by simp + exact hn.mul (((PolyBounded.const 2).mul (hfll.mul (((PolyBounded.const 2).mul + (hpl.add (PolyBounded.const 1))).add (PolyBounded.const 1)))).add (PolyBounded.const 1)) + +/-- **Evaluation is polynomial-time**, with the precision in unary. -/ +@[fun_prop] +theorem computableInPolyTime_peval {p : α → ℕ} {fl z : α → List ℕ} {P : α → List (List ℕ)} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hP : ComputableInPolyTime P encIn (encList (encList encNat))) + (hz : ComputableInPolyTime z encIn (encList encNat)) : + ComputableInPolyTime (fun x => peval (p x) (fl x) (P x) (z x)) encIn (encList encNat) := by + simp only [peval, List.foldr_eq_foldl_reverse] + have hstep : ComputableInPolyTime + (fun q : (α × List ℕ) × List ℕ => add (p q.1.1) (fl q.1.1) q.2 (mul (p q.1.1) (fl q.1.1) + (z q.1.1) q.1.2)) (encPair (encPair encIn (encList encNat)) (encList encNat)) + (encList encNat) := by + have hq := ComputableInPolyTime.fst (encPair encIn (encList encNat)) (encList encNat) + have ha := (ComputableInPolyTime.fst encIn (encList encNat)).comp hq + have hacc := (ComputableInPolyTime.snd encIn (encList encNat)).comp hq + have hc := ComputableInPolyTime.snd (encPair encIn (encList encNat)) (encList encNat) + exact computableInPolyTime_add (hp.comp ha) (hfl.comp ha) hc + (computableInPolyTime_mul (hp.comp ha) (hfl.comp ha) (hz.comp ha) hacc) + have hzero : ComputableInPolyTime (fun x => zero (fl x)) encIn (encList encNat) := + computableInPolyTime_tabulate (g := fun _ _ => 0) (lengthPolyTime hfl) + (ComputableInPolyTime.const 0) + exact ComputableInPolyTime.foldlWithInput_of_bounded_growth + (encA := encList encNat) (encL := encList (encList encNat)) + (step := fun x acc a => add (p x) (fl x) a (mul (p x) (fl x) (z x) acc)) hzero + ⟨ComputableInPolyTime.listReverseWithInput hP, hstep, inferInstance, inferInstance⟩ + ⟨(polyBounded_small hp hfl (PolyBounded.const 1)), fun x acc a _ => by + have := length_encList_le_of_small (small_add (p := p x) (fl := fl x) a + (mul (p x) (fl x) (z x) acc)) + omega⟩ + +/-- **Lagrange numerators are polynomial-time**, with the precision and the index in unary. -/ +@[fun_prop] +theorem computableInPolyTime_basisNum {p : α → ℕ} {fl : α → List ℕ} {v : α → List (List ℕ)} + {i : α → ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hv : ComputableInPolyTime v encIn (encList (encList encNat))) + (hi : ComputableInPolyTime i encIn encUnary) : + ComputableInPolyTime (fun x => basisNum (p x) (fl x) (v x) (i x)) encIn + (encList (encList encNat)) := by + let encS := encList (encList encNat) + have hstep : ComputableInPolyTime + (fun q : (α × List (List ℕ)) × ℕ => if q.2 = i q.1.1 then q.1.2 + else pmulLinear (p q.1.1) (fl q.1.1) ((v q.1.1).getD q.2 []) q.1.2) + (encPair (encPair encIn encS) encUnary) encS := by + have hq := ComputableInPolyTime.fst (encPair encIn encS) encUnary + have ha := (ComputableInPolyTime.fst encIn encS).comp hq + have hP := (ComputableInPolyTime.snd encIn encS).comp hq + have hj := ComputableInPolyTime.snd (encPair encIn encS) encUnary + refine (ComputableInPolyTime.ite (ComputableInPolyTime.beq hj (hi.comp ha)) hP + (computableInPolyTime_pmulLinear (hp.comp ha) (hfl.comp ha) (getDPolyTime (hv.comp ha) hj) + hP)).of_enc_eq fun q => ?_ + by_cases h : q.2 = i q.1.1 <;> simp [h] + have hfold := ComputableInPolyTime.foldlWith_spec (encA := encUnary) + (encL := encList encUnary) (par := fun x => x) + (step := fun x P j => if j = i x then P else pmulLinear (p x) (fl x) ((v x).getD j []) P) + ComputableInPolyTime.id' (ComputableInPolyTime.range.comp (lengthPolyTime hv)) + (ComputableInPolyTime.cons' (computableInPolyTime_one hfl) (ComputableInPolyTime.const [])) + hstep (fun x consumed P => P.length ≤ consumed.length + 1 ∧ ∀ c ∈ P, Small (p x) (fl x) c) + (fun x => ⟨by simp, by simp [small_one]⟩) + (fun x consumed P j _ hP => by + refine ⟨?_, ?_⟩ + · simp only [List.length_append, List.length_singleton] + split + · omega + · rw [length_pmulLinear]; omega + · split + · exact hP.2 + · exact fun c hc => small_of_mem_pmulLinear hc) + (polyBounded_small hp hfl ((lengthPolyTime hv).polyBounded_length.add (PolyBounded.const 1))) + (fun x consumed P hpre hP => by + have hlen : consumed.length ≤ (v x).length := by + simpa using hpre.length_le + refine (length_encPoly_le (n := (v x).length + 1) (by omega) hP.2).trans ?_ + simp [encUnary_apply]) + exact hfold.1 + +/-- **Lagrange denominators are polynomial-time**, with the precision and the index in unary. -/ +@[fun_prop] +theorem computableInPolyTime_basisDen {p : α → ℕ} {fl : α → List ℕ} {v : α → List (List ℕ)} + {i : α → ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hv : ComputableInPolyTime v encIn (encList (encList encNat))) + (hi : ComputableInPolyTime i encIn encUnary) : + ComputableInPolyTime (fun x => basisDen (p x) (fl x) (v x) (i x)) encIn (encList encNat) := by + have hstep : ComputableInPolyTime + (fun q : (α × List ℕ) × ℕ => if q.2 = i q.1.1 then q.1.2 + else mul (p q.1.1) (fl q.1.1) q.1.2 (sub (p q.1.1) (fl q.1.1) ((v q.1.1).getD (i q.1.1) []) + ((v q.1.1).getD q.2 []))) + (encPair (encPair encIn (encList encNat)) encUnary) (encList encNat) := by + have hq := ComputableInPolyTime.fst (encPair encIn (encList encNat)) encUnary + have ha := (ComputableInPolyTime.fst encIn (encList encNat)).comp hq + have hd := (ComputableInPolyTime.snd encIn (encList encNat)).comp hq + have hj := ComputableInPolyTime.snd (encPair encIn (encList encNat)) encUnary + refine (ComputableInPolyTime.ite (ComputableInPolyTime.beq hj (hi.comp ha)) hd + (computableInPolyTime_mul (hp.comp ha) (hfl.comp ha) hd + (computableInPolyTime_sub (hp.comp ha) (hfl.comp ha) + (getDPolyTime (hv.comp ha) (hi.comp ha)) (getDPolyTime (hv.comp ha) hj)))).of_enc_eq + fun q => ?_ + by_cases h : q.2 = i q.1.1 <;> simp [h] + exact ComputableInPolyTime.foldlWithInput_of_bounded_growth + (encA := encUnary) (encL := encList encUnary) + (step := fun x d j => if j = i x then d + else mul (p x) (fl x) d (sub (p x) (fl x) ((v x).getD (i x) []) ((v x).getD j []))) + (computableInPolyTime_one hfl) + ⟨ComputableInPolyTime.range.comp (lengthPolyTime hv), hstep, inferInstance, inferInstance⟩ + ⟨polyBounded_small hp hfl (PolyBounded.const 1), fun x d j _ => by + split + · omega + · have := length_encList_le_of_small (small_mul (p := p x) (fl := fl x) d + (sub (p x) (fl x) ((v x).getD (i x) []) ((v x).getD j []))) + omega⟩ + +/-- **Lagrange interpolation is polynomial-time**, with the precision in unary and the nodes and +values as lists of coefficient lists. -/ +@[fun_prop] +theorem computableInPolyTime_interp {p : α → ℕ} {fl : α → List ℕ} {v y : α → List (List ℕ)} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hv : ComputableInPolyTime v encIn (encList (encList encNat))) + (hy : ComputableInPolyTime y encIn (encList (encList encNat))) : + ComputableInPolyTime (fun x => interp (p x) (fl x) (v x) (y x)) encIn + (encList (encList encNat)) := by + let encS := encList (encList encNat) + have hstep : ComputableInPolyTime + (fun q : (α × List (List ℕ)) × ℕ => padd (p q.1.1) (fl q.1.1) q.1.2 + (psmul (p q.1.1) (fl q.1.1) (mul (p q.1.1) (fl q.1.1) ((y q.1.1).getD q.2 []) + (inv (p q.1.1) (fl q.1.1) (basisDen (p q.1.1) (fl q.1.1) (v q.1.1) q.2))) + (basisNum (p q.1.1) (fl q.1.1) (v q.1.1) q.2))) + (encPair (encPair encIn encS) encUnary) encS := by + have hq := ComputableInPolyTime.fst (encPair encIn encS) encUnary + have ha := (ComputableInPolyTime.fst encIn encS).comp hq + have hP := (ComputableInPolyTime.snd encIn encS).comp hq + have hj := ComputableInPolyTime.snd (encPair encIn encS) encUnary + have hp' := hp.comp ha + have hfl' := hfl.comp ha + exact computableInPolyTime_padd hp' hfl' hP (computableInPolyTime_psmul hp' hfl' + (computableInPolyTime_mul hp' hfl' (getDPolyTime (hy.comp ha) hj) + (computableInPolyTime_inv hp' hfl' + (computableInPolyTime_basisDen hp' hfl' (hv.comp ha) hj))) + (computableInPolyTime_basisNum hp' hfl' (hv.comp ha) hj)) + have hfold := ComputableInPolyTime.foldlWith_spec (encA := encUnary) + (encL := encList encUnary) (par := fun x => x) + (step := fun x P i => padd (p x) (fl x) P + (psmul (p x) (fl x) (mul (p x) (fl x) ((y x).getD i []) (inv (p x) (fl x) + (basisDen (p x) (fl x) (v x) i))) (basisNum (p x) (fl x) (v x) i))) + ComputableInPolyTime.id' (ComputableInPolyTime.range.comp (lengthPolyTime hv)) + (ComputableInPolyTime.const []) hstep + (fun x _ P => P.length ≤ (v x).length + 1 ∧ ∀ c ∈ P, Small (p x) (fl x) c) + (fun x => ⟨by simp, by simp⟩) + (fun x consumed P j _ hP => by + refine ⟨?_, fun c hc => small_of_mem_padd hc⟩ + rw [length_padd, length_psmul] + exact max_le hP.1 (length_basisNum_le _ _)) + (polyBounded_small hp hfl ((lengthPolyTime hv).polyBounded_length.add (PolyBounded.const 1))) + (fun x consumed P _ hP => by + refine (length_encPoly_le hP.1 hP.2).trans ?_ + simp [encUnary_apply]) + exact hfold.1 + +end PolyTime + +end Cslib.GaloisRingArith diff --git a/ComplexityTests/GaloisRingInterpolation.lean b/ComplexityTests/GaloisRingInterpolation.lean new file mode 100644 index 0000000000..00e7152060 --- /dev/null +++ b/ComplexityTests/GaloisRingInterpolation.lean @@ -0,0 +1,55 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.PolyTime.GaloisRingInterpolation + +/-! # Polynomials over `(ℤ / 2 ^ p)[U] / (f)` and Lagrange interpolation + +Concrete evaluation and interpolation in the Galois ring `(ℤ / 8)[U] / (U ^ 2 + U + 1)`, at the +nodes `0, 1, U`, whose differences `1, U, U - 1` are units; the correctness statements; and the +polynomial-time certificate of interpolation in closed form. -/ + +namespace ComplexityTests.GaloisRingInterpolation + +open Cslib.GaloisRingArith Turing.MultiTapeTM + +/-- The nodes `0, 1, U`. -/ +def nodes : List (List ℕ) := [[0, 0], [1, 0], [0, 1]] + +/-- The values `1, 2, 3 + U`. -/ +def values : List (List ℕ) := [[1, 0], [2, 0], [3, 1]] + +-- Horner evaluation of `1 + U z + z ^ 2` at `z = U`: `1 + U ^ 2 + U ^ 2 = 1 + 2 U ^ 2 = 7 + 6 U`. +example : peval 3 [1, 1] [[1, 0], [0, 1], [1, 0]] [0, 1] = [7, 6] := by decide + +-- `(z - U) (z - 1) = U - (1 + U) z + z ^ 2`, that is `[U, -1 - U, 1]`. +example : pmulLinear 3 [1, 1] [0, 1] [[7, 0], [1, 0]] = [[0, 1], [7, 7], [1, 0]] := by decide + +-- The interpolating polynomial takes the values `1, 2, 3 + U` at `0, 1, U`. +example : (nodes.map fun z => peval 3 [1, 1] (interp 3 [1, 1] nodes values) z) = values := by + decide + +-- It has three coefficients. +example : (interp 3 [1, 1] nodes values).length = 3 := by decide + +/-- Interpolation takes the given values, given correct inverses of the denominators. -/ +example (v y : List (List ℕ)) + (hinv : ∀ i < v.length, val 3 [1, 1] (basisDen 3 [1, 1] v i) * + val 3 [1, 1] (inv 3 [1, 1] (basisDen 3 [1, 1] v i)) = 1) {k : ℕ} (hk : k < v.length) : + (pval 3 [1, 1] (interp 3 [1, 1] v y)).eval (val 3 [1, 1] (v.getD k [])) = + val 3 [1, 1] (y.getD k []) := + eval_pval_interp v y hinv hk + +/-- **Interpolation in polynomial time**, on the ring description `(p, fl)` with `p` in unary, +the nodes and the values. -/ +example : ComputableInPolyTime + (fun q : (ℕ × List ℕ) × (List (List ℕ) × List (List ℕ)) => interp q.1.1 q.1.2 q.2.1 q.2.2) + (encPair (encPair encUnary (encList encNat)) + (encPair (encList (encList encNat)) (encList (encList encNat)))) + (encList (encList encNat)) := by + fun_prop + +end ComplexityTests.GaloisRingInterpolation From c2e5c59cfd9c88e57605d77505457d18b1b85499 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 22:59:37 -0400 Subject: [PATCH 028/129] feat(circuit/frontier): unfolding lemmas for an active call and a leaf of the recursion Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/Recursion.lean | 28 +++++++++++++++++++ .../Frontier/TensorNetworkRecursion.lean | 17 ++++++----- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean index 92f4e74600..26e9824a24 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean @@ -77,6 +77,8 @@ binary-coordinate lifts are such points (`AdjoinRoot.isUnit_integerDigitLift_sub correct oracle. * `Cslib.Circuits.Frontier.TensorNetwork.mul_card_edges_child_callSplitting_le`: under (D.6), the children have at most `M / 16` edges. +* `Cslib.Circuits.Frontier.TensorNetwork.contractRec_eq_callValue`, + `Cslib.Circuits.Frontier.TensorNetwork.contractRec_of_sched_eq_none`: an active call and a leaf. * `Cslib.Circuits.Frontier.TensorNetwork.exists_contractRec_eq_add`, `Cslib.Circuits.Frontier.TensorNetwork.mk_contractRec`: the recursion returns the contraction modulo `2 ^ p`. @@ -448,6 +450,32 @@ noncomputable def contractRec (d n m : ℕ) (N : TensorNetwork B Bool (Fin n) (F else N.contraction termination_by m +variable {B hB sched pts} in +/-- **An active call** of the recursion: when the schedule gives admissible parameters and the edges +run from rows to columns, the call is `callValue` with the packing of Lemma D.3, the first +`M (2 L - 1) + 1` points, and recursive calls at depth `d + 1` for the children with fewer edges. -/ +theorem contractRec_eq_callValue {d n m : ℕ} {N : TensorNetwork B Bool (Fin n) (Fin m)} + {side : Fin n → Bool} {θ : CallParams} (hs : sched d m = some θ) (hθ : θ.Admissible m N₀) + (hsrc : ∀ e, side (N.src e) = true) (htgt : ∀ e, side (N.tgt e) = false) (p : ℕ) : + contractRec B hB sched pts d n m N side p = + callValue B hB + (fun n' m' N' side' p' => + if m' < m then contractRec B hB sched pts (d + 1) n' m' N' side' p' else N'.contraction) + N side hsrc htgt (PacketPacking.packing N.src N.tgt θ.a 64) p θ.u θ.h hθ.2.2.1 + fun k : Fin (m * (2 * 2 ^ θ.a - 1) + 1) => pts (Fin.castLE hθ.2.2.2.2.2 k) := by + rw [contractRec.eq_def] + simp only [hs] + rw [dite_eq_left ⟨hθ, hsrc, htgt⟩] + rfl + +variable {B hB sched pts} in +/-- **A leaf** of the recursion, where the schedule gives no parameters, is evaluated directly. -/ +theorem contractRec_of_sched_eq_none {d n m : ℕ} {N : TensorNetwork B Bool (Fin n) (Fin m)} + {side : Fin n → Bool} (hs : sched d m = none) (p : ℕ) : + contractRec B hB sched pts d n m N side p = N.contraction := by + rw [contractRec.eq_def] + simp only [hs] + /-- **Correctness of the recursion** ([OpenAI2026ETH], §D.4.7, p. 64: correctness "by induction from the leaves"). For interpolation points with unit differences, every call returns the contraction modulo `2 ^ p`, for every schedule and depth. -/ diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkRecursion.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkRecursion.lean index c3b115f8af..69be86417a 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkRecursion.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkRecursion.lean @@ -21,8 +21,8 @@ import Mathlib.Tactic.FinCases `O = ℤ_(2)[U] / (f)` have unit differences, and `ℤ_(2)` lies in `O`. * **Correctness of the recursion over `O`**, with those four points and a constant schedule `a = u = 1`, `h = 3`. On a one-edge network these parameters are admissible (`3 M ≤ h u ≤ 3 M + u` - and `M (2 L - 1) = 3 < 4`), so the root call is active; its children are not smaller and are - evaluated directly. + and `M (2 L - 1) = 3 < 4`), so the root call is active (`contractRec_eq_callValue`); its children + are not smaller and are evaluated directly. Without parameters the call is a leaf. -/ namespace ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.Recursion @@ -123,11 +123,14 @@ example (p : ℕ) : (fun n' m' N' side' p' => if m' < 1 then contractRec O hO sched pts 1 n' m' N' side' p' else N'.contraction) net₁ (fun v => decide (v = 0)) (fun _ => rfl) (fun _ => rfl) - (Cslib.PacketPacking.packing net₁.src net₁.tgt 1 64) p 1 3 le_rfl pts := by - rw [contractRec.eq_def] - simp only [sched] - rw [dite_eq_left ⟨by decide, fun _ => rfl, fun _ => rfl⟩] - rfl + (Cslib.PacketPacking.packing net₁.src net₁.tgt 1 64) p 1 3 le_rfl pts := + contractRec_eq_callValue (θ := ⟨1, 1, 3⟩) rfl (by decide) (fun _ => rfl) (fun _ => rfl) p + +/-- With no parameters, a call is a leaf and returns the contraction. -/ +example (p : ℕ) : + contractRec O hO (fun _ _ => none) pts 0 2 1 net₁ (fun v => decide (v = 0)) p = + net₁.contraction := + contractRec_of_sched_eq_none rfl p /-- **The recursion is correct**: at every precision, the root call returns the contraction of `net₁` modulo `2 ^ p`. -/ From bafb62e4272d58d0e9795ad624a3a42a8cc6f085 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 23:00:47 -0400 Subject: [PATCH 029/129] feat(circuit/frontier): parameter schedule of the contraction recursion, (D.11) and (D.15) The schedule (D.4)-(D.6) of the Boolean tensor contraction recursion of [OpenAI2026ETH] on the floor-log / Nat.sqrt scale of #1865, with the certificate exists_activeBounds: for large s every active call satisfies the packing conditions (D.6), the precision bound (D.11) p_t u <= 34 M D_t, polynomial precisions and depth <= log s - 2. The mode count (D.15) card_modeBudget_le bounds the mode tuples by 2^e_d, and the e_d sum to O(s / sqrt(log s)) (#1538). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Frontier/TensorNetwork/Schedule.lean | 724 ++++++++++++++++++ .../Frontier/TensorNetworkSchedule.lean | 90 +++ 3 files changed, 815 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/Schedule.lean create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkSchedule.lean diff --git a/Complexity.lean b/Complexity.lean index 68fc1497cf..6c584d06fd 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -874,6 +874,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Schedule public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness public import Complexity.Computability.Circuit.Frontier.Unconditional public import Complexity.Computability.Circuit.Frontier.Witness diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Schedule.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Schedule.lean new file mode 100644 index 0000000000..6fa306e299 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Schedule.lean @@ -0,0 +1,724 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion +public import Complexity.Foundations.Data.Nat.SqrtLog +import Complexity.Foundations.Combinatorics.WeightBudget +import Complexity.Foundations.Data.Nat.Asymptotics +import Mathlib.Tactic.Linarith +import Mathlib.Tactic.Ring + +/-! +# The parameter schedule of the tensor contraction recursion + +This file fixes the parameters of the recursion in the proof of the Boolean tensor contraction +theorem (Theorem D.1) of [OpenAI2026ETH] (§D.4.2, (D.4)–(D.6), p. 58) and proves the inequalities +its running time rests on: the conditions of the packing lemma, the precision bound (D.11) +(p. 61) and the count (D.15) of the mode tuples (pp. 62–63). Correctness of the recursion does not +depend on these choices; they bound the number of its calls by `2 ^ O(s / √⌊log₂ s⌋)` (§D.4.7, +p. 64). + +**The schedule.** Fix the size `s` and write `λ = ⌊log₂ s⌋`. + +* A call with at most `H = ⌊s / ⌊√λ⌋⌋` edges is a leaf (`leafSize`). +* At depth `d` the taper is `u_d = ⌊λ / 2 ^ (17 + d)⌋` (`taper`), the manuscript's + `⌊ε λ / 2 ^ d⌋` with `ε = 1 / (128 · 1024) = 2 ^ (-17)`. +* Labels have `a_d = 2 ∑_{j ≤ d} u_j` coordinates (`slotBits`), packets `L = 2 ^ a_d` slots, and + there are `D_arr = 64 ^ (2 a_d)` pairs of labels (`r = 64`). +* The degree bound is `Δ = 2 ^ (⌊9 λ / 10⌋ + 2)` (`degreeBound`), a power of two that is at least + `s ^ (9/10)` (`pow_nine_le_degreeBound_pow_ten`) and at most `2 ^ 20 s ^ 9` in tenth power. +* The node `t` of valuation `i` is recovered from its children at precision + `p_t = p + 8 h 2 ^ (a - i) + 8 M (a + 1)` (`nodePrecision`), as in Lemma D.5, where + `D_t = L / 2 ^ i`. +* Precisions stay below `precisionBound C s d`, which starts at `C s` and grows by + `8 C s (4 · 2 ^ a_d + a_d + 1)` per level. + +**Active calls.** `Active C s d M p` says that a call at depth `d` has `M` edges, with +`16 ^ d M ≤ C s` and `H < M`, and precision `p ≤ precisionBound C s d`. The root is active when +it has more than `H` edges and edges and precision are at most `C s` (`Active.root`), and an +active call's children are active when they have more than `H` edges (`Active.child`): Lemma D.3 +gives them at most `M / 16` edges. + +**The certificate.** For every `C` there is `s₀` such that for all `s ≥ s₀` +(`exists_activeBounds`): + +* precisions up to depth `λ` are at most `s ^ 2`; +* every active call satisfies `ActiveBounds`: its depth is at most `λ - 2`; `1 ≤ u_d ≤ M`; the + conditions (D.6) of the packing lemma, `2 Δ D_arr² ≤ M`, `L D_arr ≤ M` and `2 L ≤ Δ`, in the form + `Cslib.PacketPacking.exists_packing` takes them; `M (2 L - 1) + 1 ≤ 2 ^ (4 λ)` interpolation + points; and, for each node `t` with `q_t > 0` and `h = ⌈3 M / u⌉`, the precision bound (D.11) + `p_t u_d ≤ 34 M D_t` together with `p_t ≤ precisionBound C s (d + 1)`. + +From (D.11), the mode count (D.15) gives at most `2 ^ e_d` mode tuples with +`e_d = 4 ⌊15360 C s / (⌊√λ⌋ 8 ^ d)⌋ + 4` (`card_modeBudget_le`, `branchExponent`), and the +exponents sum to `O(s / √⌊log₂ s⌋)` over the levels (`sum_branchExponent_le`). With the factor +`2 ^ (2 λ)` for the nodes, interpolation points and groups (`ActiveBounds.branching_le`), a call at +depth `d` has at most `2 ^ (2 λ + e_d)` children. These are the hypotheses of a recursion tree with +`2 ^ O(s / √⌊log₂ s⌋)` nodes. + +**Differences from the source.** We use `λ = ⌊log₂ s⌋` and `H = ⌊s / ⌊√λ⌋⌋`, the scale of the +consumer interface of #1833, where the manuscript has `⌈log₂ s⌉` and `⌈s / ⌊√λ⌋⌉`; we take +`Δ = 2 ^ (⌊9 λ / 10⌋ + 2)` instead of `⌈s ^ (9/10)⌉`; and we prove the weaker lower bound +`u_d ≥ ⌊√λ⌋ / (2 ^ 17 C)` instead of `u_d ≥ λ ^ (4/5)`, which suffices for (D.11). The manuscript's +precision bound `p ≤ s λ^{O(1)} 2 ^ (a_{d-1})` is `precisionBound_le`. + +## Main definitions + +* `leafSize`, `taper`, `prefixBits`, `slotBits`, `degreeBound`, `nodePrecision`, + `precisionBound`, `branchExponent`: the schedule. +* `Active`: an active call within the schedule. +* `ActiveBounds`: the inequalities that hold at an active call for large `s`. + +## Main results + +* `exists_activeBounds`: the parameter certificate. +* `card_modeBudget_le`: the mode count (D.15) at an active call. +* `sum_branchExponent_le`: the excess exponents sum to `O(s / √⌊log₂ s⌋)`. +* `precisionBound_le`: the precision bound `p ≤ C s 2 ^ (a_{d-1}) (1 + d (8 λ + 40))`. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.2 (D.4)–(D.6) + (p. 58), Lemma D.5 and (D.11) (pp. 60–61), (D.13)–(D.15) (pp. 62–63) and §D.4.7 (p. 64). +-/ + +public section + +namespace Cslib.Circuits.Frontier.ContractionSchedule + +open Finset Filter + +/-! ### The schedule -/ + +/-- The leaf size `H = ⌊s / ⌊√⌊log₂ s⌋⌋⌋`: calls with at most `H` edges are evaluated by direct +summation. -/ +@[expose] def leafSize (s : ℕ) : ℕ := s / Nat.sqrt (Nat.log 2 s) + +/-- The taper `u_d = ⌊⌊log₂ s⌋ / 2 ^ (17 + d)⌋` at depth `d`, the manuscript's `⌊ε λ / 2 ^ d⌋` +with `ε = 2 ^ (-17)`. -/ +@[expose] def taper (s d : ℕ) : ℕ := Nat.log 2 s / 2 ^ (17 + d) + +/-- `2 ∑_{j < d} u_j`, the manuscript's `a_{d - 1}`. -/ +@[expose] def prefixBits (s d : ℕ) : ℕ := 2 * ∑ j ∈ range d, taper s j + +/-- The number `a_d = 2 ∑_{j ≤ d} u_j` of label coordinates at depth `d`: packets have `2 ^ a_d` +slots. -/ +@[expose] def slotBits (s d : ℕ) : ℕ := prefixBits s (d + 1) + +/-- The degree bound `Δ = 2 ^ (⌊9 ⌊log₂ s⌋ / 10⌋ + 2)`, a power of two at least `s ^ (9/10)`. -/ +@[expose] def degreeBound (s : ℕ) : ℕ := 2 ^ (9 * Nat.log 2 s / 10 + 2) + +/-- The precision `p_t = p + 8 h 2 ^ (a - i) + 8 M (a + 1)` at which a node `t` of valuation `i` +is recovered by Lemma D.5, for input precision `p`, `h = ⌈3 M / u⌉` and `2 ^ a` slots. -/ +@[expose] def nodePrecision (p h a M i : ℕ) : ℕ := p + 8 * h * 2 ^ (a - i) + 8 * M * (a + 1) + +/-- The precision schedule: `C s` at the root, growing by `8 C s (4 · 2 ^ a_d + a_d + 1)` from +depth `d` to `d + 1`. -/ +@[expose] def precisionBound (C s : ℕ) : ℕ → ℕ + | 0 => C * s + | d + 1 => precisionBound C s d + 8 * C * s * (4 * 2 ^ slotBits s d + slotBits s d + 1) + +/-- The excess exponent `e_d = 4 ⌊15360 C s / (⌊√⌊log₂ s⌋⌋ 8 ^ d)⌋ + 4` of the mode count at +depth `d`. -/ +@[expose] def branchExponent (C s d : ℕ) : ℕ := + 4 * (15360 * C * s / (Nat.sqrt (Nat.log 2 s) * 8 ^ d)) + 4 + +/-- An *active* call at depth `d` of the recursion for size `s` and constant `C`: it has `M` +edges, at most `C s / 16 ^ d` and more than the leaf size, and precision `p` within the +schedule. -/ +structure Active (C s d M p : ℕ) : Prop where + /-- Each level divides the number of edges by at least `16`. -/ + edges_le : 16 ^ d * M ≤ C * s + /-- The call is not a leaf. -/ + leafSize_lt : leafSize s < M + /-- The precision is within the schedule. -/ + precision_le : p ≤ precisionBound C s d + +/-- The root is active when it is not a leaf and its edges and precision are at most `C s`. -/ +theorem Active.root {C s M p : ℕ} (hM : M ≤ C * s) (hH : leafSize s < M) (hp : p ≤ C * s) : + Active C s 0 M p := + ⟨by simpa using hM, hH, hp⟩ + +/-- A child of an active call at depth `d` with at most a sixteenth of its edges, precision +within the schedule and more than the leaf size is an active call at depth `d + 1`. -/ +theorem Active.child {C s d M p M' p' : ℕ} (h : Active C s d M p) (hM : 16 * M' ≤ M) + (hH : leafSize s < M') (hp : p' ≤ precisionBound C s (d + 1)) : Active C s (d + 1) M' p' := + ⟨by rw [pow_succ, mul_assoc]; exact (Nat.mul_le_mul_left _ hM).trans h.edges_le, hH, hp⟩ + +/-! ### Arithmetic of the schedule -/ + +theorem slotBits_eq (s d : ℕ) : slotBits s d = prefixBits s d + 2 * taper s d := by + simp only [slotBits, prefixBits, sum_range_succ, mul_add] + +theorem prefixBits_le_slotBits (s d : ℕ) : prefixBits s d ≤ slotBits s d := by + rw [slotBits_eq] + exact Nat.le_add_right _ _ + +/-- `∑_{j < d} ⌊y / 2 ^ (j + 1)⌋ ≤ y`. -/ +private theorem sum_div_two_pow_succ_le (y d : ℕ) : ∑ j ∈ range d, y / 2 ^ (j + 1) ≤ y := by + induction d generalizing y with + | zero => simp + | succ d ih => + rw [sum_range_succ'] + have h : ∑ j ∈ range d, y / 2 ^ (j + 1 + 1) = ∑ j ∈ range d, y / 2 / 2 ^ (j + 1) := + sum_congr rfl fun j _ => by rw [Nat.div_div_eq_div_mul, ← pow_succ'] + rw [h] + have := ih (y / 2) + simp only [zero_add, pow_one] + omega + +/-- **`a ≤ 4 ε λ`**: `2 ∑_{j < d} u_j ≤ ⌊λ / 2 ^ 15⌋`. -/ +theorem prefixBits_le (s d : ℕ) : prefixBits s d ≤ Nat.log 2 s / 32768 := by + have h : ∑ j ∈ range d, taper s j ≤ Nat.log 2 s / 65536 := by + have : ∀ j, taper s j = Nat.log 2 s / 65536 / 2 ^ (j + 1) := fun j => by + rw [taper, Nat.div_div_eq_div_mul, show 17 + j = 16 + (j + 1) by omega, pow_add] + rfl + simp only [this] + exact sum_div_two_pow_succ_le _ d + unfold prefixBits + omega + +theorem slotBits_le (s d : ℕ) : slotBits s d ≤ Nat.log 2 s / 32768 := + prefixBits_le s (d + 1) + +/-- **The degree bound is at least `s ^ (9/10)`**: `s ^ 9 ≤ Δ ^ 10`. -/ +theorem pow_nine_le_degreeBound_pow_ten (s : ℕ) : s ^ 9 ≤ degreeBound s ^ 10 := by + have hs : s < 2 ^ (Nat.log 2 s + 1) := Nat.lt_pow_succ_log_self one_lt_two s + calc s ^ 9 ≤ (2 ^ (Nat.log 2 s + 1)) ^ 9 := Nat.pow_le_pow_left hs.le 9 + _ = 2 ^ (9 * Nat.log 2 s + 9) := by rw [← pow_mul]; ring_nf + _ ≤ 2 ^ (10 * (9 * Nat.log 2 s / 10 + 2)) := Nat.pow_le_pow_right two_pos (by omega) + _ = degreeBound s ^ 10 := by rw [degreeBound, ← pow_mul, mul_comm] + +/-- A degree `x` with `x ^ 10 ≤ s ^ 9` is at most the degree bound. -/ +theorem le_degreeBound_of_pow_ten_le {x s : ℕ} (h : x ^ 10 ≤ s ^ 9) : x ≤ degreeBound s := + (Nat.pow_le_pow_iff_left (by norm_num)).1 (h.trans (pow_nine_le_degreeBound_pow_ten s)) + +/-- **The degree bound is at most `2 ^ 2 s ^ (9/10)`**: `Δ ^ 10 ≤ 2 ^ 20 s ^ 9`. -/ +theorem degreeBound_pow_ten_le {s : ℕ} (hs : s ≠ 0) : degreeBound s ^ 10 ≤ 2 ^ 20 * s ^ 9 := by + have h2 : 2 ^ Nat.log 2 s ≤ s := Nat.pow_log_le_self 2 hs + calc degreeBound s ^ 10 = 2 ^ (10 * (9 * Nat.log 2 s / 10) + 20) := by + rw [degreeBound, ← pow_mul]; ring_nf + _ ≤ 2 ^ (9 * Nat.log 2 s + 20) := Nat.pow_le_pow_right two_pos (by omega) + _ = 2 ^ 20 * (2 ^ Nat.log 2 s) ^ 9 := by rw [← pow_mul, ← pow_add]; ring_nf + _ ≤ 2 ^ 20 * s ^ 9 := Nat.mul_le_mul_left _ (Nat.pow_le_pow_left h2 9) + +/-- **The precision bound** of (D.11), `p ≤ s λ^{O(1)} 2 ^ (a_{d - 1})`: +`precisionBound C s d ≤ C s 2 ^ (a_{d - 1}) (1 + d (8 λ + 40))`. -/ +theorem precisionBound_le (C s d : ℕ) : + precisionBound C s d ≤ C * s * 2 ^ prefixBits s d * (1 + d * (8 * Nat.log 2 s + 40)) := by + induction d with + | zero => simp [precisionBound, prefixBits] + | succ d ih => + set n := Nat.log 2 s + set a := slotBits s d + have ha : a ≤ n := (slotBits_le s d).trans (Nat.div_le_self _ _) + have hpow : 2 ^ prefixBits s d ≤ 2 ^ a := + Nat.pow_le_pow_right two_pos (prefixBits_le_slotBits s d) + have h1 : 1 ≤ 2 ^ a := Nat.one_le_two_pow + have hstep : 8 * (4 * 2 ^ a + a + 1) ≤ 2 ^ a * (8 * n + 40) := by + have : a + 1 ≤ 2 ^ a * (n + 1) := by nlinarith + nlinarith + have hP : precisionBound C s d ≤ C * s * 2 ^ a * (1 + d * (8 * n + 40)) := + ih.trans (Nat.mul_le_mul_right _ (Nat.mul_le_mul_left _ hpow)) + calc precisionBound C s (d + 1) + = precisionBound C s d + C * s * (8 * (4 * 2 ^ a + a + 1)) := by + rw [precisionBound]; ring + _ ≤ C * s * 2 ^ a * (1 + d * (8 * n + 40)) + C * s * (2 ^ a * (8 * n + 40)) := + Nat.add_le_add hP (Nat.mul_le_mul_left _ hstep) + _ = C * s * 2 ^ prefixBits s (d + 1) * (1 + (d + 1) * (8 * n + 40)) := by + rw [show prefixBits s (d + 1) = a from rfl]; ring + +/-! ### Active calls -/ + +section Active + +variable {C s d M p : ℕ} + +theorem Active.one_le_edges (h : Active C s d M p) : 1 ≤ M := + Nat.one_le_iff_ne_zero.2 fun h0 => by have := h.leafSize_lt; omega + +theorem Active.edges_le_mul (h : Active C s d M p) : M ≤ C * s := + (Nat.le_mul_of_pos_left M (Nat.pow_pos (by norm_num))).trans h.edges_le + +theorem Active.one_le_const (h : Active C s d M p) : 1 ≤ C := by + have := h.one_le_edges.trans h.edges_le_mul + rcases Nat.eq_zero_or_pos C with rfl | hC + · simp at this + · exact hC + +/-- At an active call `s < M ⌊√λ⌋`, since `⌊s / ⌊√λ⌋⌋ < M`. -/ +theorem Active.lt_mul_sqrt (h : Active C s d M p) (hs : 2 ≤ s) : + s < M * Nat.sqrt (Nat.log 2 s) := + (Nat.div_lt_iff_lt_mul (Nat.one_le_sqrt_log_two hs)).1 h.leafSize_lt + +/-- **The depth bound**: `16 ^ d < C ⌊√λ⌋` at an active call. -/ +theorem Active.sixteen_pow_lt (h : Active C s d M p) (hs : 2 ≤ s) : + 16 ^ d < C * Nat.sqrt (Nat.log 2 s) := by + have h1 := h.lt_mul_sqrt hs + have h2 : 16 ^ d * s < C * Nat.sqrt (Nat.log 2 s) * s := + calc 16 ^ d * s < 16 ^ d * (M * Nat.sqrt (Nat.log 2 s)) := + Nat.mul_lt_mul_of_pos_left h1 (Nat.pow_pos (by norm_num)) + _ = 16 ^ d * M * Nat.sqrt (Nat.log 2 s) := by ring + _ ≤ C * s * Nat.sqrt (Nat.log 2 s) := Nat.mul_le_mul_right _ h.edges_le + _ = C * Nat.sqrt (Nat.log 2 s) * s := by ring + exact Nat.lt_of_mul_lt_mul_right h2 + +/-- **The taper stays large**: `u_d ≥ ⌊√λ⌋ / (2 ^ 17 C)` at an active call. -/ +theorem Active.le_taper (h : Active C s d M p) (hs : 2 ≤ s) : + Nat.sqrt (Nat.log 2 s) / (2 ^ 17 * C) ≤ taper s d := by + set n := Nat.log 2 s + set σ := Nat.sqrt n + have hσ : 1 ≤ σ := Nat.one_le_sqrt_log_two hs + have hC := h.one_le_const + have h2d : 2 ^ d ≤ C * σ := + (Nat.pow_le_pow_left (by norm_num : 2 ≤ 16) d).trans (h.sixteen_pow_lt hs).le + calc σ / (2 ^ 17 * C) = σ * σ / (2 ^ 17 * C * σ) := (Nat.mul_div_mul_right _ _ hσ).symm + _ ≤ n / (2 ^ 17 * C * σ) := Nat.div_le_div_right (Nat.sqrt_le n) + _ ≤ n / (2 ^ 17 * 2 ^ d) := by + refine Nat.div_le_div_left ?_ (by positivity) + rw [mul_assoc] + exact Nat.mul_le_mul_left _ h2d + _ = taper s d := by rw [taper, pow_add] + +end Active + +/-- `⌊√λ⌋ ≤ 2 ^ (10 + d) ⌊√u_d⌋` once `u_d ≥ 1`: the taper loses at most a factor `2 ^ (17 + d)` +against `λ`. -/ +theorem sqrt_log_two_le_mul_sqrt_taper {s d : ℕ} (hu : 1 ≤ taper s d) : + Nat.sqrt (Nat.log 2 s) ≤ 2 ^ (10 + d) * Nat.sqrt (taper s d) := by + set n := Nat.log 2 s + set u := taper s d + set y := Nat.sqrt u + have hy : 1 ≤ y := Nat.sqrt_pos.2 hu + have hn : n < (u + 1) * 2 ^ (17 + d) := by + have := Nat.lt_div_mul_add (a := n) (b := 2 ^ (17 + d)) (by positivity) + simpa only [u, taper, add_mul, one_mul] using this + have hu1 : u + 1 ≤ 4 * y ^ 2 := by + have := Nat.lt_succ_sqrt' u + nlinarith + have hsq : Nat.sqrt n ^ 2 < (2 ^ (10 + d) * y) ^ 2 := by + calc Nat.sqrt n ^ 2 ≤ n := Nat.sqrt_le' n + _ < (u + 1) * 2 ^ (17 + d) := hn + _ ≤ 4 * y ^ 2 * 2 ^ (17 + d) := Nat.mul_le_mul_right _ hu1 + _ = 2 ^ (19 + d) * y ^ 2 := by rw [show 19 + d = 2 + (17 + d) by omega, pow_add]; ring + _ ≤ 2 ^ (20 + 2 * d) * y ^ 2 := Nat.mul_le_mul_right _ (Nat.pow_le_pow_right two_pos + (by omega)) + _ = (2 ^ (10 + d) * y) ^ 2 := by rw [mul_pow, ← pow_mul]; ring_nf + exact ((Nat.pow_lt_pow_iff_left (by norm_num)).1 hsq).le + +/-! ### The certificate -/ + +/-- The inequalities that hold at an active call at depth `d` with `M` edges and precision `p`, for +`s` large enough. Write `u = taper s d`, `a = slotBits s d`, `L = 2 ^ a`, `Δ = degreeBound s` and +`λ = ⌊log₂ s⌋`. -/ +structure ActiveBounds (C s d M p : ℕ) : Prop where + /-- The call and its children lie in the first `λ` levels. -/ + depth_add_two_le : d + 2 ≤ Nat.log 2 s + /-- `1 ≤ u`, as Lemma D.5 requires. -/ + one_le_taper : 1 ≤ taper s d + /-- `u ≤ M`, as Lemma D.5 requires. -/ + taper_le_edges : taper s d ≤ M + /-- (D.6): `2 Δ D_arr² ≤ M`. -/ + degree_mul_pairs_sq_le : 2 * degreeBound s * (64 ^ (2 * slotBits s d)) ^ 2 ≤ M + /-- (D.6): `L D_arr ≤ M`. -/ + slots_mul_pairs_le : 2 ^ slotBits s d * 64 ^ (2 * slotBits s d) ≤ M + /-- (D.6): `2 L ≤ Δ`, so packet vertices respect the degree bound. -/ + two_mul_slots_le : 2 * 2 ^ slotBits s d ≤ degreeBound s + /-- There are `2 ^ (4 λ)` residue-field lifts for the `M (2 L - 1) + 1` interpolation points. -/ + points_le : M * (2 * 2 ^ slotBits s d - 1) + 1 ≤ 2 ^ (4 * Nat.log 2 s) + /-- The nodes, interpolation points and `64` groups contribute a factor `2 ^ (2 λ)` to the + branching. -/ + branching_le : + 2 * 2 ^ slotBits s d * (M * (2 * 2 ^ slotBits s d - 1) + 1) * 64 ≤ 2 ^ (2 * Nat.log 2 s) + /-- The children's precisions are within the schedule. -/ + nodePrecision_le : ∀ i h, i < taper s d → h * taper s d ≤ 3 * M + taper s d → + nodePrecision p h (slotBits s d) M i ≤ precisionBound C s (d + 1) + /-- **(D.11)**: `p_t u ≤ 34 M D_t`. -/ + nodePrecision_mul_taper_le : ∀ i h, i < taper s d → h * taper s d ≤ 3 * M + taper s d → + nodePrecision p h (slotBits s d) M i * taper s d ≤ 34 * M * 2 ^ (slotBits s d - i) + +/-! #### Arithmetic lemmas -/ + +private theorem one_add_mul_le_sq (n : ℕ) : 1 + n * (8 * n + 40) ≤ 49 * (n + 1) ^ 2 := by + nlinarith + +private theorem one_add_mul_le_sq' {n : ℕ} (hn : 1 ≤ n) : 1 + n * (8 * n + 40) ≤ 49 * n ^ 2 := by + nlinarith + +/-- The children's precisions are within the schedule. -/ +private theorem nodePrecision_le_of {p P h u M C s a i : ℕ} (hp : p ≤ P) + (hh : h * u ≤ 3 * M + u) (hu : 1 ≤ u) (huM : u ≤ M) (hM : M ≤ C * s) : + nodePrecision p h a M i ≤ P + 8 * C * s * (4 * 2 ^ a + a + 1) := by + have hh4 : h ≤ 4 * M := by + have : h ≤ h * u := Nat.le_mul_of_pos_right h hu + omega + have hDt : 2 ^ (a - i) ≤ 2 ^ a := Nat.pow_le_pow_right two_pos (Nat.sub_le a i) + calc nodePrecision p h a M i = p + 8 * h * 2 ^ (a - i) + 8 * M * (a + 1) := rfl + _ ≤ P + 8 * (4 * (C * s)) * 2 ^ a + 8 * (C * s) * (a + 1) := + Nat.add_le_add (Nat.add_le_add hp (Nat.mul_le_mul + (Nat.mul_le_mul_left 8 (hh4.trans (Nat.mul_le_mul_left 4 hM))) hDt)) + (Nat.mul_le_mul_right _ (Nat.mul_le_mul_left 8 hM)) + _ = P + 8 * C * s * (4 * 2 ^ a + a + 1) := by ring + +/-- The inherited precision is absorbed by the taper: `p u ≤ M 2 ^ (a_{d-1} + u)`. -/ +private theorem mul_taper_le_of {p C s A d n σ u M : ℕ} + (hp : p ≤ C * s * 2 ^ A * (1 + d * (8 * n + 40))) (hsM : s ≤ M * σ) (hσ : σ ≤ n) + (hd : d ≤ n) (hu : u ≤ n) (hn : 1 ≤ n) (hpow : 49 * C * n ^ 4 ≤ 2 ^ u) : + p * u ≤ M * 2 ^ (A + u) := by + have h0 : d * (8 * n + 40) ≤ n * (8 * n + 40) := Nat.mul_le_mul_right _ hd + have h1 : 1 + d * (8 * n + 40) ≤ 49 * n ^ 2 := (Nat.add_le_add_left h0 1).trans + (one_add_mul_le_sq' hn) + have h2 : σ * u ≤ n ^ 2 := by rw [sq]; exact Nat.mul_le_mul hσ hu + have hfac : C * σ * (1 + d * (8 * n + 40)) * u ≤ 49 * C * n ^ 4 := + calc C * σ * (1 + d * (8 * n + 40)) * u = C * (σ * u) * (1 + d * (8 * n + 40)) := by ring + _ ≤ C * n ^ 2 * (49 * n ^ 2) := Nat.mul_le_mul (Nat.mul_le_mul_left _ h2) h1 + _ = 49 * C * n ^ 4 := by ring + calc p * u ≤ C * s * 2 ^ A * (1 + d * (8 * n + 40)) * u := Nat.mul_le_mul_right _ hp + _ ≤ C * (M * σ) * 2 ^ A * (1 + d * (8 * n + 40)) * u := + Nat.mul_le_mul_right _ (Nat.mul_le_mul_right _ (Nat.mul_le_mul_right _ + (Nat.mul_le_mul_left C hsM))) + _ = M * 2 ^ A * (C * σ * (1 + d * (8 * n + 40)) * u) := by ring + _ ≤ M * 2 ^ A * 2 ^ u := Nat.mul_le_mul_left _ (hfac.trans hpow) + _ = M * 2 ^ (A + u) := by rw [mul_assoc, ← pow_add] + +/-- The evaluation loss is absorbed by the taper: `8 (a + 1) u ≤ 2 ^ u`. -/ +private theorem eight_mul_le_two_pow {a u n C : ℕ} (hC : 1 ≤ C) (hn : 1 ≤ n) (ha : a ≤ n) + (hu : u ≤ n) (hpow : 49 * C * n ^ 4 ≤ 2 ^ u) : 8 * (a + 1) * u ≤ 2 ^ u := by + have h1 : 8 * (a + 1) * u ≤ 16 * n ^ 2 := by + calc 8 * (a + 1) * u ≤ 8 * (n + n) * n := + Nat.mul_le_mul (Nat.mul_le_mul_left 8 (Nat.add_le_add ha hn)) hu + _ = 16 * n ^ 2 := by ring + have h2 : n ^ 2 ≤ n ^ 4 := Nat.pow_le_pow_right hn (by norm_num) + have h3 : 16 * n ^ 4 ≤ 49 * C * n ^ 4 := + Nat.mul_le_mul_right _ (by omega) + omega + +/-- **(D.11)** from its ingredients: `p_t u ≤ 34 M D_t`. -/ +private theorem nodePrecision_mul_le_of {p h a M i u A : ℕ} (hpu : p * u ≤ M * 2 ^ (A + u)) + (ha : a = A + 2 * u) (hi : i < u) (hh : h * u ≤ 3 * M + u) (huM : u ≤ M) + (h8 : 8 * (a + 1) * u ≤ 2 ^ u) : + nodePrecision p h a M i * u ≤ 34 * M * 2 ^ (a - i) := by + have hDt : 2 ^ (A + u) ≤ 2 ^ (a - i) := Nat.pow_le_pow_right two_pos (by omega) + have hu2 : 2 ^ u ≤ 2 ^ (a - i) := Nat.pow_le_pow_right two_pos (by omega) + have hhu : h * u ≤ 4 * M := by omega + calc nodePrecision p h a M i * u + = p * u + 8 * (h * u) * 2 ^ (a - i) + M * (8 * (a + 1) * u) := by + rw [nodePrecision]; ring + _ ≤ M * 2 ^ (a - i) + 8 * (4 * M) * 2 ^ (a - i) + M * 2 ^ (a - i) := + Nat.add_le_add (Nat.add_le_add (hpu.trans (Nat.mul_le_mul_left _ hDt)) + (Nat.mul_le_mul_right _ (Nat.mul_le_mul_left 8 hhu))) + (Nat.mul_le_mul_left _ (h8.trans hu2)) + _ = 34 * M * 2 ^ (a - i) := by ring + +/-- `64 ^ (2 a) = 2 ^ (12 a)`. -/ +private theorem sixtyFour_pow (a : ℕ) : 64 ^ (2 * a) = 2 ^ (12 * a) := by + rw [show (64 : ℕ) = 2 ^ 6 by norm_num, ← pow_mul]; ring_nf + +/-- A power of two is below `M` when it is below `s / 2 ^ (n / 20)`, since `s < M σ` and +`σ ≤ 2 ^ (n / 20)`. -/ +private theorem two_pow_lt_of {k n s M σ : ℕ} (hk : k + n / 20 ≤ n) (hσ : σ ≤ 2 ^ (n / 20)) + (h2n : 2 ^ n ≤ s) (hsM : s < M * σ) : 2 ^ k < M := by + have : 2 ^ k * σ < M * σ := + calc 2 ^ k * σ ≤ 2 ^ k * 2 ^ (n / 20) := Nat.mul_le_mul_left _ hσ + _ = 2 ^ (k + n / 20) := (pow_add _ _ _).symm + _ ≤ 2 ^ n := Nat.pow_le_pow_right two_pos hk + _ ≤ s := h2n + _ < M * σ := hsM + exact Nat.lt_of_mul_lt_mul_right this + +/-- The interpolation points: `M (2 L - 1) + 1 ≤ 2 ^ (n / 8 + n + a + 2)`. -/ +private theorem points_le_of {M a C s n : ℕ} (hM1 : 1 ≤ M) (hM : M ≤ C * s) + (hC : C ≤ 2 ^ (n / 8)) (hs : s < 2 ^ (n + 1)) : + M * (2 * 2 ^ a - 1) + 1 ≤ 2 ^ (n / 8 + n + a + 2) := by + have h1 : M * (2 * 2 ^ a - 1) + 1 ≤ M * (2 * 2 ^ a) := by + have h2a : 1 ≤ 2 * 2 ^ a := by have := Nat.one_le_two_pow (n := a); omega + rw [Nat.mul_sub, mul_one] + have : M ≤ M * (2 * 2 ^ a) := Nat.le_mul_of_pos_right M (by omega) + omega + calc M * (2 * 2 ^ a - 1) + 1 ≤ M * (2 * 2 ^ a) := h1 + _ ≤ C * s * (2 * 2 ^ a) := Nat.mul_le_mul_right _ hM + _ ≤ 2 ^ (n / 8) * 2 ^ (n + 1) * (2 * 2 ^ a) := + Nat.mul_le_mul_right _ (Nat.mul_le_mul hC hs.le) + _ = 2 ^ (n / 8 + n + a + 2) := by + rw [← pow_succ', ← pow_add, ← pow_add]; ring_nf + +/-- The asymptotic inequalities behind the certificate, in `n = ⌊log₂ s⌋`. -/ +private theorem eventually_large (C : ℕ) (hC : 1 ≤ C) : ∀ᶠ n : ℕ in atTop, + 200 ≤ n ∧ 49 * C * (Nat.sqrt n + 1) ^ 8 ≤ 2 ^ (Nat.sqrt n / (2 ^ 17 * C)) ∧ + C * (n + 1) ^ 1 ≤ 2 ^ (n / 8) ∧ 8 * (n + 1) ^ 1 ≤ 2 ^ (n / 20) ∧ + 49 * C * (n + 1) ^ 2 ≤ 2 ^ (n / 2) := by + have hsqrt : Tendsto Nat.sqrt atTop atTop := + tendsto_atTop_atTop.2 fun b => ⟨b * b, fun n hn => Nat.le_sqrt.2 hn⟩ + filter_upwards [eventually_ge_atTop 200, + hsqrt.eventually (Nat.eventually_mul_add_one_pow_le_pow_div (49 * C) 8 (2 ^ 17 * C) + one_lt_two (by positivity)), + Nat.eventually_mul_add_one_pow_le_pow_div C 1 8 one_lt_two (by norm_num), + Nat.eventually_mul_add_one_pow_le_pow_div 8 1 20 one_lt_two (by norm_num), + Nat.eventually_mul_add_one_pow_le_pow_div (49 * C) 2 2 one_lt_two (by norm_num)] + with n h1 h2 h3 h4 h5 + exact ⟨h1, h2, h3, h4, h5⟩ + +/-! #### The certificate -/ + +/-- Precisions up to depth `⌊log₂ s⌋` are at most `s ^ 2` once `49 C (λ + 1)² ≤ 2 ^ (λ / 2)`. -/ +private theorem precisionBound_le_sq_of {C s d : ℕ} (hd : d ≤ Nat.log 2 s) (hs : s ≠ 0) + (h : 49 * C * (Nat.log 2 s + 1) ^ 2 ≤ 2 ^ (Nat.log 2 s / 2)) : + precisionBound C s d ≤ s ^ 2 := by + set n := Nat.log 2 s + have h2n : 2 ^ n ≤ s := Nat.pow_log_le_self 2 hs + have hpow : 2 ^ prefixBits s d ≤ 2 ^ (n / 32768) := + Nat.pow_le_pow_right two_pos (prefixBits_le s d) + have hfac : C * (1 + d * (8 * n + 40)) ≤ 49 * C * (n + 1) ^ 2 := by + have h1 : d * (8 * n + 40) ≤ n * (8 * n + 40) := Nat.mul_le_mul_right _ hd + have h2 := one_add_mul_le_sq n + calc C * (1 + d * (8 * n + 40)) ≤ C * (49 * (n + 1) ^ 2) := + Nat.mul_le_mul_left _ (by omega) + _ = 49 * C * (n + 1) ^ 2 := by ring + calc precisionBound C s d ≤ C * s * 2 ^ prefixBits s d * (1 + d * (8 * n + 40)) := + precisionBound_le C s d + _ = s * (C * (1 + d * (8 * n + 40))) * 2 ^ prefixBits s d := by ring + _ ≤ s * 2 ^ (n / 2) * 2 ^ (n / 32768) := + Nat.mul_le_mul (Nat.mul_le_mul_left _ (hfac.trans h)) hpow + _ = s * 2 ^ (n / 2 + n / 32768) := by rw [mul_assoc, ← pow_add] + _ ≤ s * 2 ^ n := Nat.mul_le_mul_left _ (Nat.pow_le_pow_right two_pos (by omega)) + _ ≤ s * s := Nat.mul_le_mul_left _ h2n + _ = s ^ 2 := (sq s).symm + +/-- The certificate at one active call, from the asymptotic inequalities at `λ = ⌊log₂ s⌋`. -/ +private theorem activeBounds_of {C s d M p : ℕ} (hA : Active C s d M p) + (h200 : 200 ≤ Nat.log 2 s) + (hA1 : 49 * C * (Nat.sqrt (Nat.log 2 s) + 1) ^ 8 ≤ + 2 ^ (Nat.sqrt (Nat.log 2 s) / (2 ^ 17 * C))) + (hA2 : C * (Nat.log 2 s + 1) ≤ 2 ^ (Nat.log 2 s / 8)) + (hA3 : 8 * (Nat.log 2 s + 1) ≤ 2 ^ (Nat.log 2 s / 20)) : + ActiveBounds C s d M p := by + have hs0 : s ≠ 0 := by + rintro rfl + simp at h200 + have hC := hA.one_le_const + have hM1 := hA.one_le_edges + have hMCs := hA.edges_le_mul + have h2n : 2 ^ Nat.log 2 s ≤ s := Nat.pow_log_le_self 2 hs0 + have hsn : s < 2 ^ (Nat.log 2 s + 1) := Nat.lt_pow_succ_log_self one_lt_two s + have hs2 : 2 ≤ s := (Nat.pow_le_pow_right two_pos (by omega : 1 ≤ Nat.log 2 s)).trans h2n + have hsM := hA.lt_mul_sqrt hs2 + have h16 := hA.sixteen_pow_lt hs2 + have hσn : Nat.sqrt (Nat.log 2 s) ≤ Nat.log 2 s := Nat.sqrt_le_self _ + have hnσ : Nat.log 2 s < (Nat.sqrt (Nat.log 2 s) + 1) ^ 2 := Nat.lt_succ_sqrt' _ + have hσpow : Nat.sqrt (Nat.log 2 s) ≤ 2 ^ (Nat.log 2 s / 20) := by omega + have hCpow : C ≤ 2 ^ (Nat.log 2 s / 8) := (Nat.le_mul_of_pos_right C (by omega)).trans hA2 + have haA : slotBits s d = prefixBits s d + 2 * taper s d := slotBits_eq s d + have ha : slotBits s d ≤ Nat.log 2 s / 32768 := slotBits_le s d + have han : slotBits s d ≤ Nat.log 2 s := ha.trans (Nat.div_le_self _ _) + -- The depth. + have hdepth : d + 2 ≤ Nat.log 2 s := by + have h1 : 2 ^ (4 * d) < 2 ^ (Nat.log 2 s / 8) := by + calc 2 ^ (4 * d) = 16 ^ d := by rw [pow_mul]; norm_num + _ < C * Nat.sqrt (Nat.log 2 s) := h16 + _ ≤ C * (Nat.log 2 s + 1) := Nat.mul_le_mul_left _ (by omega) + _ ≤ 2 ^ (Nat.log 2 s / 8) := hA2 + have := (Nat.pow_lt_pow_iff_right one_lt_two).1 h1 + omega + -- The taper. + have hpowu : 49 * C * Nat.log 2 s ^ 4 ≤ 2 ^ taper s d := by + have hn4 : Nat.log 2 s ^ 4 ≤ (Nat.sqrt (Nat.log 2 s) + 1) ^ 8 := by + calc Nat.log 2 s ^ 4 ≤ ((Nat.sqrt (Nat.log 2 s) + 1) ^ 2) ^ 4 := + Nat.pow_le_pow_left hnσ.le 4 + _ = (Nat.sqrt (Nat.log 2 s) + 1) ^ 8 := by rw [← pow_mul] + calc 49 * C * Nat.log 2 s ^ 4 ≤ 49 * C * (Nat.sqrt (Nat.log 2 s) + 1) ^ 8 := + Nat.mul_le_mul_left _ hn4 + _ ≤ 2 ^ (Nat.sqrt (Nat.log 2 s) / (2 ^ 17 * C)) := hA1 + _ ≤ 2 ^ taper s d := Nat.pow_le_pow_right two_pos (hA.le_taper hs2) + have hu1 : 1 ≤ taper s d := by + by_contra hu0 + have h0 : taper s d = 0 := by omega + rw [h0, pow_zero] at hpowu + have : 1 ≤ Nat.log 2 s ^ 4 := Nat.one_le_pow _ _ (by omega) + have : 49 ≤ 49 * C * Nat.log 2 s ^ 4 := + calc 49 = 49 * 1 * 1 := rfl + _ ≤ 49 * C * Nat.log 2 s ^ 4 := Nat.mul_le_mul (Nat.mul_le_mul_left _ hC) this + omega + have hun : taper s d ≤ Nat.log 2 s := Nat.div_le_self _ _ + have huM : taper s d ≤ M := by + have := Nat.log_two_le_div_sqrt_log_two s + have := hA.leafSize_lt + simp only [leafSize] at this + omega + have hpu : p * taper s d ≤ M * 2 ^ (prefixBits s d + taper s d) := + mul_taper_le_of (hA.precision_le.trans (precisionBound_le C s d)) hsM.le hσn + (by omega) hun (by omega) hpowu + have h8 : 8 * (slotBits s d + 1) * taper s d ≤ 2 ^ taper s d := + eight_mul_le_two_pow hC (by omega) han hun hpowu + have hpoints := points_le_of (a := slotBits s d) hM1 hMCs hCpow hsn + refine ⟨hdepth, hu1, huM, ?_, ?_, ?_, ?_, ?_, fun i h _ hh => ?_, fun i h hi hh => ?_⟩ + · -- `2 Δ D_arr² ≤ M`. + have := two_pow_lt_of (k := 3 + 9 * Nat.log 2 s / 10 + 24 * slotBits s d) (by omega) + hσpow h2n hsM + rw [degreeBound, sixtyFour_pow] + calc 2 * 2 ^ (9 * Nat.log 2 s / 10 + 2) * (2 ^ (12 * slotBits s d)) ^ 2 + = 2 ^ (3 + 9 * Nat.log 2 s / 10 + 24 * slotBits s d) := by + rw [← pow_mul, ← pow_succ', ← pow_add]; ring_nf + _ ≤ M := this.le + · -- `L D_arr ≤ M`. + have := two_pow_lt_of (k := 13 * slotBits s d) (by omega) hσpow h2n hsM + rw [sixtyFour_pow, ← pow_add] + calc 2 ^ (slotBits s d + 12 * slotBits s d) = 2 ^ (13 * slotBits s d) := by ring_nf + _ ≤ M := this.le + · -- `2 L ≤ Δ`. + rw [degreeBound, ← pow_succ'] + exact Nat.pow_le_pow_right two_pos (by omega) + · -- Interpolation points. + exact hpoints.trans (Nat.pow_le_pow_right two_pos (by omega)) + · -- The polynomial branching factor. + calc 2 * 2 ^ slotBits s d * (M * (2 * 2 ^ slotBits s d - 1) + 1) * 64 + ≤ 2 * 2 ^ slotBits s d * 2 ^ (Nat.log 2 s / 8 + Nat.log 2 s + slotBits s d + 2) * 64 := + Nat.mul_le_mul_right _ (Nat.mul_le_mul_left _ hpoints) + _ = 2 ^ (Nat.log 2 s / 8 + Nat.log 2 s + 2 * slotBits s d + 9) := by + rw [show (64 : ℕ) = 2 ^ 6 by norm_num, ← pow_succ', ← pow_add, ← pow_add]; ring_nf + _ ≤ 2 ^ (2 * Nat.log 2 s) := Nat.pow_le_pow_right two_pos (by omega) + · -- Children's precisions. + exact nodePrecision_le_of hA.precision_le hh hu1 huM hMCs + · -- (D.11). + exact nodePrecision_mul_le_of hpu haA hi hh huM h8 + +/-- **The parameter certificate.** For every `C` there is `s₀` such that for all `s ≥ s₀` the +precisions up to depth `⌊log₂ s⌋` are at most `s ^ 2`, and every active call satisfies +`ActiveBounds`. -/ +theorem exists_activeBounds (C : ℕ) : ∃ s₀, ∀ s, s₀ ≤ s → + (∀ d ≤ Nat.log 2 s, precisionBound C s d ≤ s ^ 2) ∧ + ∀ d M p, Active C s d M p → ActiveBounds C s d M p := by + rcases Nat.eq_zero_or_pos C with rfl | hC + · refine ⟨0, fun s _ => ⟨fun d _ => ?_, fun d M p h => absurd h.one_le_const (by norm_num)⟩⟩ + have := precisionBound_le 0 s d + simp only [zero_mul] at this + exact this.trans (Nat.zero_le _) + obtain ⟨n₀, hn₀⟩ := eventually_atTop.1 (eventually_large C hC) + refine ⟨2 ^ n₀, fun s hs => ?_⟩ + have hs0 : s ≠ 0 := by + rintro rfl + have := Nat.one_le_two_pow (n := n₀) + omega + obtain ⟨h200, hA1, hA2, hA3, hA5⟩ := hn₀ (Nat.log 2 s) (Nat.le_log_of_pow_le one_lt_two hs) + simp only [pow_one] at hA2 hA3 + exact ⟨fun d hd => precisionBound_le_sq_of hd hs0 hA5, + fun d M p hA => activeBounds_of hA h200 hA1 hA2 hA3⟩ + +/-! ### The mode count -/ + +/-- **The mode count (D.15)** at an active call: if the node `t` of valuation `i ≤ a` satisfies +the precision bound (D.11) `p_t u ≤ 34 M D_t` with `u ≥ 1`, and the packing has `#P 2 ^ a ≤ 3 M` +packets (Lemma D.3 (1)), then there are at most `2 ^ e_d` tuples of exponents of the modes +`P × Fin (2 ^ (i + 1)) × Fin p_t` (packet, tile, `b`) of weight `b + 1` below `p_t`. There are +`#P 2 ^ (i + 1) ≤ 6 M / D_t` modes of each weight (D.13). -/ +theorem card_modeBudget_le {C s d M p : ℕ} (hA : Active C s d M p) (hs : 2 ≤ s) + (hu : 1 ≤ taper s d) {i h : ℕ} (hi : i ≤ slotBits s d) + (hpt : nodePrecision p h (slotBits s d) M i * taper s d ≤ + 34 * M * 2 ^ (slotBits s d - i)) + {P : Type*} [Fintype P] [DecidableEq P] + (hP : Fintype.card P * 2 ^ slotBits s d ≤ 3 * M) : + #(TensorNetwork.modeBudget + (fun Z : P × Fin (2 ^ (i + 1)) × Fin (nodePrecision p h (slotBits s d) M i) => + (Z.2.2 : ℕ) + 1) + (nodePrecision p h (slotBits s d) M i)) ≤ 2 ^ branchExponent C s d := by + set a := slotBits s d + set pt := nodePrecision p h a M i + set u := taper s d + set m := Fintype.card P * 2 ^ (i + 1) + set σ := Nat.sqrt (Nat.log 2 s) + -- At most `m` modes of each weight. + have hfib : ∀ j, #{Z : P × Fin (2 ^ (i + 1)) × Fin pt | (Z.2.2 : ℕ) + 1 = j} ≤ m := by + intro j + calc #{Z : P × Fin (2 ^ (i + 1)) × Fin pt | (Z.2.2 : ℕ) + 1 = j} + ≤ #(univ : Finset (P × Fin (2 ^ (i + 1)))) := by + refine card_le_card_of_injOn (fun Z => (Z.1, Z.2.1)) (fun _ _ => mem_univ _) ?_ + intro Z hZ Z' hZ' hZZ + have hZ := (mem_filter.1 (mem_coe.1 hZ)).2 + have hZ' := (mem_filter.1 (mem_coe.1 hZ')).2 + simp only [Prod.mk.injEq] at hZZ + exact Prod.ext hZZ.1 (Prod.ext hZZ.2 (Fin.ext (by omega))) + _ = m := by simp [m] + have hcard := card_le_two_pow_of_sum_mul_lt (fun _ => Nat.succ_pos _) hfib + (S := TensorNetwork.modeBudget _ pt) fun β hβ => + (TensorNetwork.mem_modeBudget (fun _ => Nat.succ_pos _)).1 hβ + refine hcard.trans (Nat.pow_le_pow_right two_pos ?_) + -- `⌊√(m p_t)⌋ ⌊√u⌋ ≤ 15 M`. + set x := Nat.sqrt (m * pt) + set y := Nat.sqrt u + have hmD : m * 2 ^ (a - i) ≤ 6 * M := by + have : m * 2 ^ (a - i) = 2 * (Fintype.card P * 2 ^ a) := by + rw [show a = i + (a - i) by omega, Nat.add_sub_cancel_left] + simp only [m, pow_succ, pow_add] + ring + omega + have hxy : x * y ≤ 15 * M := by + have h1 : (x * y) ^ 2 ≤ m * pt * u := by + rw [mul_pow] + exact Nat.mul_le_mul (Nat.sqrt_le' _) (Nat.sqrt_le' _) + have h2 : m * pt * u ≤ 204 * M ^ 2 := + calc m * pt * u = m * (pt * u) := by ring + _ ≤ m * (34 * M * 2 ^ (a - i)) := Nat.mul_le_mul_left _ hpt + _ = 34 * M * (m * 2 ^ (a - i)) := by ring + _ ≤ 34 * M * (6 * M) := Nat.mul_le_mul_left _ hmD + _ = 204 * M ^ 2 := by ring + have h3 : (x * y) ^ 2 ≤ (15 * M) ^ 2 := by + rw [mul_pow (15 : ℕ)] + omega + exact (Nat.pow_le_pow_iff_left (by norm_num)).1 h3 + have hσ : σ ≤ 2 ^ (10 + d) * y := sqrt_log_two_le_mul_sqrt_taper hu + have hσ1 : 1 ≤ σ := Nat.one_le_sqrt_log_two hs + have hx : x ≤ 15360 * C * s / (σ * 8 ^ d) := by + rw [Nat.le_div_iff_mul_le (by positivity)] + calc x * (σ * 8 ^ d) ≤ x * (2 ^ (10 + d) * y * 8 ^ d) := + Nat.mul_le_mul_left _ (Nat.mul_le_mul_right _ hσ) + _ = x * y * 2 ^ 10 * (2 ^ d * 8 ^ d) := by rw [pow_add]; ring + _ ≤ 15 * M * 2 ^ 10 * 16 ^ d := by + rw [← mul_pow] + exact Nat.mul_le_mul_right _ (Nat.mul_le_mul_right _ hxy) + _ = 15360 * (16 ^ d * M) := by ring + _ ≤ 15360 * (C * s) := Nat.mul_le_mul_left _ hA.edges_le + _ = 15360 * C * s := by ring + change 4 * x + 4 ≤ 4 * (15360 * C * s / (σ * 8 ^ d)) + 4 + omega + +/-- **The excess exponents sum to `O(s / √⌊log₂ s⌋)`**: over at most `⌊log₂ s⌋` levels, +`∑_d e_d ≤ (245760 C + 4) ⌊s / ⌊√⌊log₂ s⌋⌋⌋`. The terms decrease geometrically, as in the +manuscript's `(√2 / 8) ^ d` (§D.4.7, p. 64). -/ +theorem sum_branchExponent_le (C : ℕ) {s D : ℕ} (hs : 2 ≤ s) (hD : D ≤ Nat.log 2 s) : + ∑ d ∈ range D, branchExponent C s d ≤ + (245760 * C + 4) * (s / Nat.sqrt (Nat.log 2 s)) := by + set σ := Nat.sqrt (Nat.log 2 s) + set S := s / σ + set X := 15360 * C * s / σ + have hσ1 : 1 ≤ σ := Nat.one_le_sqrt_log_two hs + have hS1 : 1 ≤ S := Nat.one_le_div_sqrt_log_two hs + have hDS : D ≤ S := hD.trans (Nat.log_two_le_div_sqrt_log_two s) + have hX : X ≤ 15360 * C * S + 15360 * C := Nat.mul_div_le_mul_div_add _ _ _ + have hterm : ∀ d, 15360 * C * s / (σ * 8 ^ d) ≤ X / 2 ^ d := fun d => by + rw [← Nat.div_div_eq_div_mul] + exact Nat.div_le_div_left (Nat.pow_le_pow_left (by norm_num) d) (by positivity) + have hgeo : ∑ d ∈ range D, X / 2 ^ d ≤ 2 * X := by + have h := sum_div_two_pow_succ_le (2 * X) D + have heq : ∀ d, 2 * X / 2 ^ (d + 1) = X / 2 ^ d := fun d => by + rw [pow_succ', Nat.mul_div_mul_left X (2 ^ d) two_pos] + simpa only [heq] using h + calc ∑ d ∈ range D, branchExponent C s d + = 4 * ∑ d ∈ range D, 15360 * C * s / (σ * 8 ^ d) + 4 * D := by + simp only [branchExponent, sum_add_distrib, ← mul_sum, sum_const, card_range, + smul_eq_mul] + ring + _ ≤ 4 * (2 * X) + 4 * S := + Nat.add_le_add (Nat.mul_le_mul_left _ ((sum_le_sum fun d _ => hterm d).trans hgeo)) + (Nat.mul_le_mul_left _ hDS) + _ ≤ (245760 * C + 4) * S := by + have hCS : C ≤ C * S := Nat.le_mul_of_pos_right C hS1 + have h1 : 15360 * C * S = 15360 * (C * S) := by ring + have h2 : (245760 * C + 4) * S = 245760 * (C * S) + 4 * S := by ring + omega + +end Cslib.Circuits.Frontier.ContractionSchedule diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkSchedule.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkSchedule.lean new file mode 100644 index 0000000000..ef6a4eedd9 --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkSchedule.lean @@ -0,0 +1,90 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Schedule + +/-! +# Tests for the parameter schedule of the contraction recursion + +Concrete values of the schedule at `s = 2 ^ 10`, an active call there, and a nonvacuity check of +the certificate: some root call satisfies `ActiveBounds`. +-/ + +@[expose] public section + +namespace ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.Schedule + +open Cslib.Circuits.Frontier.ContractionSchedule + +/-! ### Values at `s = 2 ^ 10`, where `⌊log₂ s⌋ = 10` and `⌊√10⌋ = 3` -/ + +theorem log_two_1024 : Nat.log 2 1024 = 10 := by + rw [show (1024 : ℕ) = 2 ^ 10 by norm_num, Nat.log_pow one_lt_two] + +theorem sqrt_10 : Nat.sqrt 10 = 3 := + (Nat.eq_sqrt.2 ⟨by norm_num, by norm_num⟩).symm + +example : leafSize 1024 = 341 := by + rw [leafSize, log_two_1024, sqrt_10] + +/-- `ε λ` is far below one at this size, so the taper and the labels are trivial. -/ +example (d : ℕ) : taper 1024 d = 0 := by + rw [taper, log_two_1024] + exact Nat.div_eq_of_lt (lt_of_lt_of_le (by norm_num) (Nat.pow_le_pow_right two_pos + (Nat.le_add_right 17 d))) + +example : degreeBound 1024 = 2 ^ 11 := by + rw [degreeBound, log_two_1024] + +example : precisionBound 3 1024 0 = 3072 := rfl + +/-- One level adds `8 C s (4 · 2 ^ a_0 + a_0 + 1)` with `a_0 = 0`. -/ +example : precisionBound 3 1024 1 = 3072 + 8 * 3 * 1024 * 5 := by + have h : slotBits 1024 0 = 0 := by + simp [slotBits, prefixBits, taper, log_two_1024] + simp [precisionBound, h] + +example : nodePrecision 5 2 3 7 1 = 5 + 8 * 2 * 4 + 8 * 7 * 4 := rfl + +/-- `Δ ^ 10 ≥ s ^ 9` and `Δ ^ 10 ≤ 2 ^ 20 s ^ 9`. -/ +example : 1024 ^ 9 ≤ degreeBound 1024 ^ 10 ∧ degreeBound 1024 ^ 10 ≤ 2 ^ 20 * 1024 ^ 9 := + ⟨pow_nine_le_degreeBound_pow_ten 1024, degreeBound_pow_ten_le (by norm_num)⟩ + +/-! ### Active calls -/ + +/-- A root call with `1000` edges and precision `5` at `s = 1024`, `C = 1`. -/ +example : Active 1 1024 0 1000 5 := + Active.root (by norm_num) (by rw [show leafSize 1024 = 341 by + rw [leafSize, log_two_1024, sqrt_10]]; norm_num) (by norm_num) + +/-- A child with `62` edges is not active at `s = 1024`: it is a leaf. -/ +example : ¬ Active 1 1024 1 62 5 := fun h => by + have := h.leafSize_lt + rw [show leafSize 1024 = 341 by rw [leafSize, log_two_1024, sqrt_10]] at this + omega + +/-- **The certificate is not vacuous**: for each `C ≥ 1` some root call satisfies +`ActiveBounds`. -/ +example (C : ℕ) (hC : 1 ≤ C) : ∃ s M p, ActiveBounds C s 0 M p := by + obtain ⟨s₀, h⟩ := exists_activeBounds C + set s := max s₀ 16 + have hs : 16 ≤ s := le_max_right _ _ + have hlog : 4 ≤ Nat.log 2 s := Nat.le_log_of_pow_le one_lt_two (by simpa using hs) + have hσ : 2 ≤ Nat.sqrt (Nat.log 2 s) := Nat.le_sqrt.2 (by omega) + refine ⟨s, s, 0, (h s (le_max_left _ _)).2 0 s 0 (Active.root ?_ ?_ (Nat.zero_le _))⟩ + · exact Nat.le_mul_of_pos_left s hC + · exact Nat.div_lt_self (by omega) (by omega) + +/-- The excess exponents at `s = 1024` and `C = 1` over two levels. -/ +example : ∑ d ∈ Finset.range 2, branchExponent 1 1024 d ≤ 245764 * (1024 / Nat.sqrt 10) := by + have := sum_branchExponent_le 1 (s := 1024) (D := 2) (by norm_num) + (by rw [log_two_1024]; norm_num) + rwa [log_two_1024] at this + +end ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.Schedule From 5328bbefbd02e8d0cd232cdcf7280e706ee3bff2 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 23:01:23 -0400 Subject: [PATCH 030/129] feat(circuit/frontier): fields of the active-node splitting; expose oracle correctness Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/Recursion.lean | 42 +++++++++++++++---- .../Frontier/TensorNetworkRecursion.lean | 4 ++ 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean index 26e9824a24..a969b3f831 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean @@ -73,6 +73,9 @@ binary-coordinate lifts are such points (`AdjoinRoot.isUnit_integerDigitLift_sub * `Cslib.Circuits.Frontier.TensorNetwork.exists_recoverFromValues_eq_add_of_children`: one call is correct when its values are the sums of the products of the children's contractions. +* `Cslib.Circuits.Frontier.TensorNetwork.callSplitting_side`, + `Cslib.Circuits.Frontier.TensorNetwork.callSplitting_group`, + `Cslib.Circuits.Frontier.TensorNetwork.callSplitting_packet`: the fields of the splitting. * `Cslib.Circuits.Frontier.TensorNetwork.exists_callValue_eq_add`: one call is correct for a correct oracle. * `Cslib.Circuits.Frontier.TensorNetwork.mul_card_edges_child_callSplitting_le`: under (D.6), the @@ -199,6 +202,34 @@ noncomputable def callSplitting (N : TensorNetwork B Bool V E) (side : V → Boo side hsrc htgt Q _ _ (two_pow_eq_zero_twoPowIdeal _ _) ht (padicValInt_lt_of_taperMult_pos hq hua) +section Fields + +variable (N : TensorNetwork B Bool V E) (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) + (htgt : ∀ e, side (N.tgt e) = false) {a r : ℕ} (Q : PacketPacking.Packing N.src N.tgt a r) + (p u h : ℕ) (hua : u ≤ a) {t : ℤ} (ht : t ∈ recoveryNodes a) (hq : 0 < taperMult u h t) (z : B) + +omit [Fintype V] [DecidableEq V] [DecidableEq E] in +/-- The rows of the splitting at an active node are the rows of the call. -/ +@[simp] theorem callSplitting_side : + (callSplitting B hB N side hsrc htgt Q p u h hua ht hq z).side = side := + cauchyModeSplitting_side .. + +omit [Fintype V] [DecidableEq V] [DecidableEq E] in +/-- The groups at an active node of valuation `i` are the symbols in coordinate `i` of the +packing. -/ +@[simp] theorem callSplitting_group : + (callSplitting B hB N side hsrc htgt Q p u h hua ht hq z).group = + packingGroup side Q ⟨_, padicValInt_lt_of_taperMult_pos hq hua⟩ := + cauchyModeSplitting_group .. + +omit [Fintype V] [DecidableEq V] [DecidableEq E] in +/-- The packets of the splitting at an active node are the packets of the packing. -/ +@[simp] theorem callSplitting_packet : + (callSplitting B hB N side hsrc htgt Q p u h hua ht hq z).packet = Q.packet := + cauchyModeSplitting_packet .. + +end Fields + /-- **Correctness of one active call from its children** ([OpenAI2026ETH], §D.4.4–§D.4.7). Let `N` have entries in `B`, edges from rows to columns, and a packing `Q` into packets of `2 ^ a` slots. Let `1 ≤ u ≤ M`, `u ≤ a` and `3 M ≤ h u ≤ 3 M + u` for `M` edges, and let the @@ -245,7 +276,7 @@ variable {B : Type*} [CommRing B] /-- An oracle is *correct* when each of its values agrees with the contraction modulo the precision `2 ^ p` it is given. -/ -def Correct (rec : ContractionOracle B) : Prop := +@[expose] def Correct (rec : ContractionOracle B) : Prop := ∀ n m (N : TensorNetwork B Bool (Fin n) (Fin m)) side p, ∃ c, rec n m N side p = N.contraction + 2 ^ p * c @@ -382,18 +413,13 @@ theorem mul_card_edges_child_callSplitting_le (N : TensorNetwork B Bool V E) (si {e // (callSplitting B hB N side hsrc htgt (PacketPacking.packing N.src N.tgt a 64) p u h hua ht hq z).group (N.tgt e) = g}) ≤ 4 * Fintype.card E := by - have hg : (callSplitting B hB N side hsrc htgt (PacketPacking.packing N.src N.tgt a 64) p u h - hua ht hq z).group = - packingGroup side (PacketPacking.packing N.src N.tgt a 64) - ⟨_, padicValInt_lt_of_taperMult_pos hq hua⟩ := by - exact cauchyModeSplitting_group .. have := ModeSplitting.mul_card_edges_child_le (c := 2) (r := 64) (callSplitting B hB N side hsrc htgt (PacketPacking.packing N.src N.tgt a 64) p u h hua ht hq z) g (by - rw [hg] + rw [callSplitting_group] exact (congrArg (64 * ·) (card_packingGroup_src side hsrc _ _ g)).trans_le (PacketPacking.row_ports_packing_le N.src N.tgt a 64 hrow hcol hM _ g)) (by - rw [hg] + rw [callSplitting_group] exact (congrArg (64 * ·) (card_packingGroup_tgt side htgt _ _ g)).trans_le (PacketPacking.col_ports_packing_le N.src N.tgt a 64 hrow hcol hM _ g)) exact this.trans_eq (by ring) diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkRecursion.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkRecursion.lean index 69be86417a..76bfcec667 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkRecursion.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkRecursion.lean @@ -100,6 +100,10 @@ theorem pts_pairwise : Pairwise fun k l => IsUnit (pts k - pts l) := fun _ _ hkl /-! ### The recursion over `O` -/ +/-- Direct contraction is a correct oracle. -/ +example : ContractionOracle.Correct (B := O) fun _ _ N _ _ => N.contraction := + fun _ _ _ _ _ => ⟨0, by simp⟩ + /-- The constant schedule `a = u = 1`, `h = 3`. -/ def sched (_ _ : ℕ) : Option TensorNetwork.CallParams := some ⟨1, 1, 3⟩ From d9a734324a1bf53b70ff5ca89acdb2166b57587b Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 23:02:48 -0400 Subject: [PATCH 031/129] feat(circuit/frontier): mode count at a certified active call ActiveBounds.card_modeBudget_le supplies the hypotheses of card_modeBudget_le from the certificate, and ActiveBounds.two_le. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/Schedule.lean | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Schedule.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Schedule.lean index 6fa306e299..66eafea95a 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Schedule.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Schedule.lean @@ -78,7 +78,8 @@ precision bound `p ≤ s λ^{O(1)} 2 ^ (a_{d-1})` is `precisionBound_le`. ## Main results * `exists_activeBounds`: the parameter certificate. -* `card_modeBudget_le`: the mode count (D.15) at an active call. +* `card_modeBudget_le`, `ActiveBounds.card_modeBudget_le`: the mode count (D.15) at an active + call. * `sum_branchExponent_le`: the excess exponents sum to `O(s / √⌊log₂ s⌋)`. * `precisionBound_le`: the precision bound `p ≤ C s 2 ^ (a_{d-1}) (1 + d (8 λ + 40))`. @@ -686,6 +687,28 @@ theorem card_modeBudget_le {C s d M p : ℕ} (hA : Active C s d M p) (hs : 2 ≤ change 4 * x + 4 ≤ 4 * (15360 * C * s / (σ * 8 ^ d)) + 4 omega +/-- At an active call satisfying the certificate, `s ≥ 2`. -/ +theorem ActiveBounds.two_le {C s d M p : ℕ} (hB : ActiveBounds C s d M p) : 2 ≤ s := by + have h := hB.depth_add_two_le + rcases Nat.lt_or_ge s 2 with hs | hs + · rw [Nat.log_of_lt hs] at h + omega + · exact hs + +/-- **The mode count (D.15) at a certified active call**: for a node of valuation `i < u` and +`h = ⌈3 M / u⌉`, and a packing with `#P 2 ^ a ≤ 3 M` (Lemma D.3 (1)), there are at most `2 ^ e_d` +mode tuples. This is `card_modeBudget_le` with its hypotheses supplied by `ActiveBounds`. -/ +theorem ActiveBounds.card_modeBudget_le {C s d M p : ℕ} (hB : ActiveBounds C s d M p) + (hA : Active C s d M p) {i h : ℕ} (hi : i < taper s d) + (hh : h * taper s d ≤ 3 * M + taper s d) {P : Type*} [Fintype P] [DecidableEq P] + (hP : Fintype.card P * 2 ^ slotBits s d ≤ 3 * M) : + #(TensorNetwork.modeBudget + (fun Z : P × Fin (2 ^ (i + 1)) × Fin (nodePrecision p h (slotBits s d) M i) => + (Z.2.2 : ℕ) + 1) + (nodePrecision p h (slotBits s d) M i)) ≤ 2 ^ branchExponent C s d := + ContractionSchedule.card_modeBudget_le hA hB.two_le hB.one_le_taper + (by have := slotBits_eq s d; omega) (hB.nodePrecision_mul_taper_le i h hi hh) hP + /-- **The excess exponents sum to `O(s / √⌊log₂ s⌋)`**: over at most `⌊log₂ s⌋` levels, `∑_d e_d ≤ (245760 C + 4) ⌊s / ⌊√⌊log₂ s⌋⌋⌋`. The terms decrease geometrically, as in the manuscript's `(√2 / 8) ^ d` (§D.4.7, p. 64). -/ From af73f5bbde72bde81d9bc7fad3cd4bb3ca6678f0 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 23:06:30 -0400 Subject: [PATCH 032/129] feat(circuit/frontier): the contraction recursion has 2^O(s/sqrt(log s)) calls Compose ContractionSchedule.sum_branchExponent_le with Nat.exists_recursionBound_le_two_pow_div_sqrt_log_two. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Frontier/TensorNetwork/ScheduleCount.lean | 52 +++++++++++++++++++ ComplexityTests/ScheduleCount.lean | 25 +++++++++ 3 files changed, 78 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/ScheduleCount.lean create mode 100644 ComplexityTests/ScheduleCount.lean diff --git a/Complexity.lean b/Complexity.lean index 0f8709f8f9..a0f2851d5a 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -875,6 +875,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformatio public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Schedule +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ScheduleCount public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness public import Complexity.Computability.Circuit.Frontier.Unconditional public import Complexity.Computability.Circuit.Frontier.Witness diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ScheduleCount.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ScheduleCount.lean new file mode 100644 index 0000000000..2a989ab9df --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ScheduleCount.lean @@ -0,0 +1,52 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Schedule +public import Complexity.Foundations.Data.Nat.RecursionTree + +/-! +# The number of calls of the contraction recursion + +Under the parameter schedule of `Cslib.Circuits.Frontier.ContractionSchedule`, a call at depth +`d` of the recursion of [OpenAI2026ETH] (Appendix D.4) has at most +`2 ^ (2 ⌊log₂ s⌋ + branchExponent C s d)` children (`ContractionSchedule.exists_activeBounds`, +`ContractionSchedule.ActiveBounds.card_modeBudget_le`), and there are at most +`⌊log₂ s⌋` levels. The excess exponents sum to `O(s / √⌊log₂ s⌋)` +(`ContractionSchedule.sum_branchExponent_le`), so the recursion has `2 ^ O(s / √⌊log₂ s⌋)` calls +(`ContractionSchedule.exists_recursionBound_le`): the count of Appendix D.4.7 (p. 64), in the +natural-number scale of the contraction interface. It bounds the nodes of the depth-first +evaluation (`Nat.recursionBound`), hence the fuel of +`Cslib.GenericRAM.StackRecursion.Scheme.certificate`. + +## Main results + +* `Cslib.Circuits.Frontier.ContractionSchedule.exists_recursionBound_le`: the call count. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Appendix D.4.7 (p. 64). +-/ + +public section + +namespace Cslib.Circuits.Frontier.ContractionSchedule + +/-- **The recursion has `2 ^ O(s / √⌊log₂ s⌋)` calls.** For every `C` there is `K` such that, +from `s = 2` on, a recursion of depth at most `D ≤ ⌊log₂ s⌋` whose calls at depth `d` have at +most `2 ^ (2 ⌊log₂ s⌋ + branchExponent C s d)` children has at most +`2 ^ (K s / √⌊log₂ s⌋)` nodes below its root. -/ +theorem exists_recursionBound_le (C : ℕ) : ∃ K, ∀ s D, 2 ≤ s → D ≤ Nat.log 2 s → + Nat.recursionBound (fun d => 2 ^ (2 * Nat.log 2 s + branchExponent C s d)) D 0 ≤ + 2 ^ (K * s / Nat.sqrt (Nat.log 2 s)) := by + obtain ⟨K, hK⟩ := Nat.exists_recursionBound_le_two_pow_div_sqrt_log_two 2 (245760 * C + 4) + refine ⟨K, fun s D hs hD => (Nat.recursionBound_le _ D 0).trans ?_⟩ + simpa only [Nat.zero_add] using hK s D (fun d => 2 ^ (2 * Nat.log 2 s + branchExponent C s d)) + (branchExponent C s) hs hD (fun _ _ => le_rfl) (sum_branchExponent_le C hs hD) + +end Cslib.Circuits.Frontier.ContractionSchedule diff --git a/ComplexityTests/ScheduleCount.lean b/ComplexityTests/ScheduleCount.lean new file mode 100644 index 0000000000..d9886e70c8 --- /dev/null +++ b/ComplexityTests/ScheduleCount.lean @@ -0,0 +1,25 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.Circuit.Frontier.TensorNetwork.ScheduleCount + +/-! # The call count of the contraction recursion + +The node bound of a recursion with the schedule's branching factors is `2 ^ O(s / √⌊log₂ s⌋)`, +for every constant `C` and every depth up to `⌊log₂ s⌋`. -/ + +namespace ComplexityTests.ScheduleCount + +open Cslib.Circuits.Frontier.ContractionSchedule + +-- At `s = 16` the depth is at most `⌊log₂ 16⌋ = 4`; one constant serves every depth and size. +example (C : ℕ) : ∃ K, ∀ D ≤ Nat.log 2 16, + Nat.recursionBound (fun d => 2 ^ (2 * Nat.log 2 16 + branchExponent C 16 d)) D 0 ≤ + 2 ^ (K * 16 / Nat.sqrt (Nat.log 2 16)) := by + obtain ⟨K, hK⟩ := exists_recursionBound_le C + exact ⟨K, fun D hD => hK 16 D (by norm_num) hD⟩ + +end ComplexityTests.ScheduleCount From 5fef5d3af669f4d4b39c03e442d12594a952bde2 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 23:18:23 -0400 Subject: [PATCH 033/129] feat(circuit/frontier): run the recursion with the parameter schedule of #1908 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The recursion now uses ContractionSchedule.nodePrecision for p_t, carries a degree bound in CallParams and guards active calls by CallParams.Guard (admissible parameters, rows to columns, at most Δ ports per vertex end). Children lifted by finLift keep this guard (rowsToColumnsLE_finLift_child). ScheduledRecursion.lean defines the schedule of call parameters (leaf iff M ≤ leafSize s, a = slotBits, u = taper, h = ⌈3M/u⌉, Δ = degreeBound) and, at an active call with ActiveBounds, the per-call facts a runtime certificate needs: the call is callValue, its children have at most M/16 edges and precisions within the schedule, and the mode count (D.15). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 3 +- .../Frontier/TensorNetwork/Recursion.lean | 323 +++++++++++++----- .../Frontier/TensorNetwork/Reindex.lean | 19 +- .../TensorNetwork/ScheduledRecursion.lean | 206 +++++++++++ .../Frontier/TensorNetworkRecursion.lean | 39 ++- 5 files changed, 505 insertions(+), 85 deletions(-) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/ScheduledRecursion.lean diff --git a/Complexity.lean b/Complexity.lean index ad777cb57f..e4bb6dc63a 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -881,8 +881,9 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.LocalDefor public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Recursion public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Reindex -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.TaperedRecovery public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Schedule +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ScheduledRecursion +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.TaperedRecovery public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness public import Complexity.Computability.Circuit.Frontier.Unconditional public import Complexity.Computability.Circuit.Frontier.Witness diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean index a969b3f831..a6606b51b9 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean @@ -9,6 +9,7 @@ module public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ActiveCall public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Reindex +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Schedule /-! # The contraction recursion and its correctness @@ -22,7 +23,8 @@ valuation `i < a`, group the vertices by their symbol in coordinate `i` of `Q` ( The packing bridge `packingGroup_src_tgt` orients the groups, so the Cauchy matrix at `T = t + α ζ` has the mode splitting `TensorNetwork.cauchyModeSplitting` (§D.4.5, p. 62). At a call with input precision `p`, `TensorNetwork.callSplitting` is this splitting for the network reduced -modulo `2 ^ p_t`, `p_t = p + 8 h D_t + 8 M (a + 1)` (`nodePrecision`). +modulo `2 ^ p_t`, `p_t = p + 8 h D_t + 8 M (a + 1)`: the schedule's +`ContractionSchedule.nodePrecision` at the valuation of `t`, the precision of Lemma D.5. **One call from its children.** If every value `v t k` reduces modulo `2 ^ p_t` to the sum over the mode tuples of the products of the contractions of the children (the right side of (D.16)), @@ -30,22 +32,26 @@ then interpolation and tapered recovery return the contraction modulo `2 ^ p` (`TensorNetwork.exists_recoverFromValues_eq_add_of_children`). The children's contractions may come from any *contraction oracle* (`TensorNetwork.ContractionOracle`) that is correct modulo the precision it is given: a child over `B ⧸ (2 ^ p_t)` is lifted to `B` along a section of the -reduction and renumbered by `Fin` (`TensorNetwork.ContractionOracle.evalLift`), and the oracle's -value then reduces to its contraction (`TensorNetwork.ContractionOracle.mk_evalLift`). The value of +reduction and renumbered by `Fin` (`TensorNetwork.finLift`, +`TensorNetwork.ContractionOracle.evalLift`), and the oracle's value then reduces to its contraction +(`TensorNetwork.ContractionOracle.mk_evalLift`). The value of an active node is `TensorNetwork.nodeValue`, the values of a call are `TensorNetwork.callValues`, and the recovered value is `TensorNetwork.callValue` (`TensorNetwork.exists_callValue_eq_add`). **The recursion** (`TensorNetwork.contractRec`). A call receives a network on `Fin n` vertices and `Fin m` edges, its sides, its depth `d` and its precision `p`. A *schedule* gives the -parameters `a`, `u`, `h` of a call from its depth and its number of edges (`CallParams`), or none -at a leaf. The call is active when the parameters meet the hypotheses of Lemma D.5 -(`CallParams.Admissible`, with `u ≤ a` and enough interpolation points) and the edges run from -rows to columns. It packs the edges with `PacketPacking.packing` (Lemma D.3, `r = 64`), takes the -first `M (2 L - 1) + 1` of a fixed family of interpolation points, and evaluates each child by a -recursive call at depth `d + 1` and precision `p_t`. A leaf, an inactive call, and a child that -does not have fewer edges than its parent are evaluated by the contraction itself, the direct -summation of the manuscript's leaves. The recursion is well founded on the number of edges. +parameters `a`, `u`, `h` and the degree bound `Δ` of a call from its depth and its number of edges +(`CallParams`), or none at a leaf; the manuscript's schedule is +`ContractionSchedule.callParams`. The call is active when its guard holds (`CallParams.Guard`): +the parameters meet the hypotheses of Lemma D.5 (`CallParams.Admissible`, with `u ≤ a` and enough +interpolation points), and the network runs from rows to columns with at most `Δ` ports at each +end of each vertex (`TensorNetwork.RowsToColumnsLE`). It packs the edges with +`PacketPacking.packing` (Lemma D.3, `r = 64`), takes the first `M (2 L - 1) + 1` of a fixed family +of interpolation points, and evaluates each child by a recursive call at depth `d + 1` and precision +`p_t`. A leaf, an inactive call, and a child that does not have fewer edges than its parent are +evaluated by the contraction itself, the direct summation of the manuscript's leaves. The recursion +is well founded on the number of edges. **Correctness** (`TensorNetwork.exists_contractRec_eq_add`). For interpolation points with unit differences, every call returns the contraction modulo `2 ^ p`, for every schedule: the manuscript's @@ -53,20 +59,25 @@ differences, every call returns the contraction modulo `2 ^ p`, for every schedu parameter schedule (D.4)–(D.6) matters only for the running time, which this file does not treat. Under the first inequality of (D.6), every child has at most `M / 16` edges (`TensorNetwork.mul_card_edges_child_callSplitting_le`, from Lemma D.3 (3)), so an active call with -an edge never falls back for its children. In the manuscript's ring `B = O = ℤ_(2)[U] / (f)`, the -binary-coordinate lifts are such points (`AdjoinRoot.isUnit_integerDigitLift_sub`). +an edge never falls back for its children, and when `2 L ≤ Δ` the lifted children again run from +rows to columns within the degree bound (`TensorNetwork.rowsToColumnsLE_finLift_child`). In the +manuscript's ring `B = O = ℤ_(2)[U] / (f)`, the binary-coordinate lifts are such points +(`AdjoinRoot.isUnit_integerDigitLift_sub`). ## Main definitions -* `Cslib.Circuits.Frontier.nodePrecision`: the child precision `p_t`. +* `Cslib.Circuits.Frontier.TensorNetwork.RowsToColumnsLE`: a network from rows to columns with a + degree bound. * `Cslib.Circuits.Frontier.TensorNetwork.nodeSplitting`, `Cslib.Circuits.Frontier.TensorNetwork.callSplitting`: the mode splitting at an active node. +* `Cslib.Circuits.Frontier.TensorNetwork.finLift`: a child lifted to `B` and numbered by `Fin`. * `Cslib.Circuits.Frontier.TensorNetwork.ContractionOracle`: values for child calls, with `ContractionOracle.Correct` and `ContractionOracle.evalLift`. * `Cslib.Circuits.Frontier.TensorNetwork.nodeValue`, `Cslib.Circuits.Frontier.TensorNetwork.callValues`, `Cslib.Circuits.Frontier.TensorNetwork.callValue`: one active call from an oracle. -* `Cslib.Circuits.Frontier.TensorNetwork.CallParams`: the parameters of a call. +* `Cslib.Circuits.Frontier.TensorNetwork.CallParams`: the parameters of a call, with + `CallParams.Admissible` and the guard `CallParams.Guard`. * `Cslib.Circuits.Frontier.TensorNetwork.contractRec`: the recursion. ## Main results @@ -80,6 +91,9 @@ binary-coordinate lifts are such points (`AdjoinRoot.isUnit_integerDigitLift_sub correct oracle. * `Cslib.Circuits.Frontier.TensorNetwork.mul_card_edges_child_callSplitting_le`: under (D.6), the children have at most `M / 16` edges. +* `Cslib.Circuits.Frontier.TensorNetwork.ModeSplitting.rowsToColumnsLE_child`, + `Cslib.Circuits.Frontier.TensorNetwork.rowsToColumnsLE_finLift_child`: the children keep the + degree bound. * `Cslib.Circuits.Frontier.TensorNetwork.contractRec_eq_callValue`, `Cslib.Circuits.Frontier.TensorNetwork.contractRec_of_sched_eq_none`: an active call and a leaf. * `Cslib.Circuits.Frontier.TensorNetwork.exists_contractRec_eq_add`, @@ -98,25 +112,25 @@ open Polynomial Finset namespace Cslib.Circuits.Frontier +open ContractionSchedule (nodePrecision) + /-- The subring `ℤ_(2)` of rationals with odd denominator. -/ local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) /-! ### Node precisions -/ -/-- The precision `p_t = p + 8 h D_t + 8 M (a + 1)`, `D_t = 2 ^ (a - v₂ t)`, that a call of input -precision `p` with `M` edges passes to the children of the node `t` (Lemma D.5). -/ -@[expose] def nodePrecision (M a h p : ℕ) (t : ℤ) : ℕ := - p + 8 * h * 2 ^ (a - padicValInt 2 t) + 8 * M * (a + 1) +/-- A node of positive multiplicity has valuation below `u`. -/ +theorem padicValInt_lt_of_taperMult_pos_left {u h : ℕ} {t : ℤ} (hq : 0 < taperMult u h t) : + padicValInt 2 t < u := by + unfold taperMult at hq + by_contra hc + rw [Nat.sub_eq_zero_of_le (not_lt.mp hc), mul_zero] at hq + exact lt_irrefl 0 hq /-- A node of positive multiplicity has valuation below `u`, so below `a` when `u ≤ a`. -/ theorem padicValInt_lt_of_taperMult_pos {u h a : ℕ} {t : ℤ} (hq : 0 < taperMult u h t) - (hua : u ≤ a) : padicValInt 2 t < a := by - unfold taperMult at hq - have : padicValInt 2 t < u := by - by_contra hc - rw [Nat.sub_eq_zero_of_le (not_lt.mp hc), mul_zero] at hq - exact lt_irrefl 0 hq - omega + (hua : u ≤ a) : padicValInt 2 t < a := + (padicValInt_lt_of_taperMult_pos_left hq).trans_le hua /-- The ideal `(2 ^ n)` of a commutative ring. -/ abbrev twoPowIdeal (B : Type*) [CommRing B] (n : ℕ) : Ideal B := @@ -129,8 +143,35 @@ theorem two_pow_eq_zero_twoPowIdeal (B : Type*) [CommRing B] (n : ℕ) : Ideal.Quotient.eq_zero_iff_mem.mpr (Ideal.mem_span_singleton_self _) rwa [map_pow, map_ofNat] at this +/-- The ideal `(2 ^ p_t)` of the node `t` of a call with `M` edges, packets of `2 ^ a` slots, +multiplicity step `h` and input precision `p`. -/ +abbrev nodeIdeal (B : Type*) [CommRing B] (M a h p : ℕ) (t : ℤ) : Ideal B := + twoPowIdeal B (nodePrecision p h a M (padicValInt 2 t)) + namespace TensorNetwork +/-! ### Networks from rows to columns -/ + +/-- Every edge of `N` runs from a row (`side v = true`) to a column, and every vertex has at most +`Δ` ports at each end: the bipartite form and the degree bound `Δ` of the networks of the +recursion (§D.4.2, p. 58). -/ +structure RowsToColumnsLE {R U V E : Type*} [Fintype E] [DecidableEq V] + (N : TensorNetwork R U V E) (side : V → Bool) (Δ : ℕ) : Prop where + /-- Edges start at rows. -/ + side_src : ∀ e, side (N.src e) = true + /-- Edges end at columns. -/ + side_tgt : ∀ e, side (N.tgt e) = false + /-- Every vertex is the source of at most `Δ` edges. -/ + src_le : ∀ v, #{e | N.src e = v} ≤ Δ + /-- Every vertex is the target of at most `Δ` edges. -/ + tgt_le : ∀ w, #{e | N.tgt e = w} ≤ Δ + +instance {R U : Type*} {n m : ℕ} (N : TensorNetwork R U (Fin n) (Fin m)) (side : Fin n → Bool) + (Δ : ℕ) : Decidable (N.RowsToColumnsLE side Δ) := + decidable_of_iff ((∀ e, side (N.src e) = true) ∧ (∀ e, side (N.tgt e) = false) ∧ + (∀ v, #{e | N.src e = v} ≤ Δ) ∧ ∀ w, #{e | N.tgt e = w} ≤ Δ) + ⟨fun ⟨h₁, h₂, h₃, h₄⟩ => ⟨h₁, h₂, h₃, h₄⟩, fun ⟨h₁, h₂, h₃, h₄⟩ => ⟨h₁, h₂, h₃, h₄⟩⟩ + /-! ### The modes of an active node -/ section Node @@ -189,16 +230,16 @@ noncomputable def callSplitting (N : TensorNetwork B Bool V E) (side : V → Boo (Q : PacketPacking.Packing N.src N.tgt a r) (p u h : ℕ) (hua : u ≤ a) {t : ℤ} (ht : t ∈ recoveryNodes a) (hq : 0 < taperMult u h t) (z : B) : ModeSplitting - (N.map (Ideal.Quotient.mk (twoPowIdeal B (nodePrecision (Fintype.card E) a h p t)))) + (N.map (Ideal.Quotient.mk (nodeIdeal B (Fintype.card E) a h p t))) (cauchyValueMatrix - ((Ideal.Quotient.mk (twoPowIdeal B (nodePrecision (Fintype.card E) a h p t))).comp + ((Ideal.Quotient.mk (nodeIdeal B (Fintype.card E) a h p t)).comp (integerHom B hB)) - (Ideal.Quotient.mk (twoPowIdeal B (nodePrecision (Fintype.card E) a h p t)) z) a t + (Ideal.Quotient.mk (nodeIdeal B (Fintype.card E) a h p t) z) a t Q.packet fun e => Q.slot e) (Fin r) (Fin Q.numPackets) (Fin Q.numPackets × Fin (2 ^ (padicValInt 2 t + 1)) × - Fin (nodePrecision (Fintype.card E) a h p t)) := - nodeSplitting (N.map (Ideal.Quotient.mk (twoPowIdeal B (nodePrecision (Fintype.card E) a h p t)))) + Fin (nodePrecision p h a (Fintype.card E) (padicValInt 2 t))) := + nodeSplitting (N.map (Ideal.Quotient.mk (nodeIdeal B (Fintype.card E) a h p t))) side hsrc htgt Q _ _ (two_pow_eq_zero_twoPowIdeal _ _) ht (padicValInt_lt_of_taperMult_pos hq hua) @@ -246,22 +287,61 @@ theorem exists_recoverFromValues_eq_add_of_children (N : TensorNetwork B Bool V (hζ : Pairwise fun k l => IsUnit (ζ k - ζ l)) (hn : Fintype.card E * (2 * 2 ^ a - 1) < n) (v : ℤ → Fin n → B) (hv : ∀ t (ht : t ∈ recoveryNodes a) (hq : 0 < taperMult u h t) k, - Ideal.Quotient.mk (twoPowIdeal B (nodePrecision (Fintype.card E) a h p t)) (v t k) = + Ideal.Quotient.mk (nodeIdeal B (Fintype.card E) a h p t) (v t k) = ∑ β ∈ modeBudget (fun Z : Fin Q.numPackets × Fin (2 ^ (padicValInt 2 t + 1)) × - Fin (nodePrecision (Fintype.card E) a h p t) => (Z.2.2 : ℕ) + 1) - (nodePrecision (Fintype.card E) a h p t), + Fin (nodePrecision p h a (Fintype.card E) (padicValInt 2 t)) => (Z.2.2 : ℕ) + 1) + (nodePrecision p h a (Fintype.card E) (padicValInt 2 t)), ∏ g, ((callSplitting B hB N side hsrc htgt Q p u h hua ht hq (ζ k)).child β g).contraction) : ∃ b ∈ B, recoverFromValues B a u h ζ v = (N.contraction : K) + 2 ^ p * b := exists_recoverFromValues_eq_add_of_mk B hB N side (packing_slot_lt Q) (packing_slot_injective Q) hu huM hh hh' ζ hζ hn v fun t ht hq k => (hv t ht hq k).trans (deformedContraction_eq_sum_nodeSplitting - (N.map (Ideal.Quotient.mk (twoPowIdeal B (nodePrecision (Fintype.card E) a h p t)))) side + (N.map (Ideal.Quotient.mk (nodeIdeal B (Fintype.card E) a h p t))) side hsrc htgt Q _ _ (two_pow_eq_zero_twoPowIdeal _ _) ht (padicValInt_lt_of_taperMult_pos hq hua)).symm end Children +/-! ### Lifting child networks -/ + +section FinLift + +variable {B : Type*} [CommRing B] {V E : Type*} [Fintype V] [Fintype E] {p : ℕ} + +/-- A network over `B ⧸ (2 ^ p)` on finite types, lifted to `B` along a section of the reduction +(`TensorNetwork.mapFun`) and numbered by `Fin` (`TensorNetwork.reindex`): the network that a child +call receives. -/ +noncomputable def finLift (N : TensorNetwork (B ⧸ twoPowIdeal B p) Bool V E) : + TensorNetwork B Bool (Fin (Fintype.card V)) (Fin (Fintype.card E)) := + (N.mapFun (Function.surjInv Ideal.Quotient.mk_surjective)).reindex (Fintype.equivFin V) + (Fintype.equivFin E) + +/-- The contraction of a lifted network reduces to the contraction. -/ +theorem mk_contraction_finLift [DecidableEq E] (N : TensorNetwork (B ⧸ twoPowIdeal B p) Bool V E) : + Ideal.Quotient.mk (twoPowIdeal B p) (finLift N).contraction = N.contraction := by + rw [finLift, contraction_reindex, + contraction_map_mapFun _ (Function.surjInv_eq Ideal.Quotient.mk_surjective)] + +/-- Lifting keeps a network running from rows to columns, with its degree bound. -/ +theorem RowsToColumnsLE.finLift [DecidableEq V] {N : TensorNetwork (B ⧸ twoPowIdeal B p) Bool V E} + {side : V → Bool} {Δ : ℕ} (h : N.RowsToColumnsLE side Δ) : + (TensorNetwork.finLift N).RowsToColumnsLE (side ∘ (Fintype.equivFin V).symm) Δ where + side_src e := by + simp only [TensorNetwork.finLift, Function.comp_apply, reindex_src, Equiv.symm_apply_apply] + exact h.side_src _ + side_tgt e := by + simp only [TensorNetwork.finLift, Function.comp_apply, reindex_tgt, Equiv.symm_apply_apply] + exact h.side_tgt _ + src_le v := by + rw [TensorNetwork.finLift, card_reindex_src] + exact h.src_le _ + tgt_le w := by + rw [TensorNetwork.finLift, card_reindex_tgt] + exact h.tgt_le _ + +end FinLift + /-! ### Contraction oracles -/ /-- A *contraction oracle*: a value for every Boolean tensor network on the vertices `Fin n` and the @@ -280,13 +360,11 @@ precision `2 ^ p` it is given. -/ ∀ n m (N : TensorNetwork B Bool (Fin n) (Fin m)) side p, ∃ c, rec n m N side p = N.contraction + 2 ^ p * c -/-- The value of an oracle on a network over `B ⧸ (2 ^ p)` on finite types, at precision `p`: lift -the entries to `B` along a section of the reduction (`TensorNetwork.mapFun`), number the vertices -and edges (`TensorNetwork.reindex`), and query. -/ +/-- The value of an oracle on a network over `B ⧸ (2 ^ p)` on finite types, at precision `p`: the +query on the lifted network `finLift N`. -/ noncomputable def evalLift (rec : ContractionOracle B) {V E : Type*} [Fintype V] [Fintype E] {p : ℕ} (N : TensorNetwork (B ⧸ twoPowIdeal B p) Bool V E) (side : V → Bool) : B := - rec _ _ ((N.mapFun (Function.surjInv Ideal.Quotient.mk_surjective)).reindex - (Fintype.equivFin V) (Fintype.equivFin E)) (side ∘ (Fintype.equivFin V).symm) p + rec _ _ (finLift N) (side ∘ (Fintype.equivFin V).symm) p /-- **A correct oracle evaluates a lifted network**: its value reduces modulo `2 ^ p` to the contraction. -/ @@ -294,12 +372,10 @@ theorem mk_evalLift {rec : ContractionOracle B} (hrec : rec.Correct) {V E : Type [Fintype E] [DecidableEq E] {p : ℕ} (N : TensorNetwork (B ⧸ twoPowIdeal B p) Bool V E) (side : V → Bool) : Ideal.Quotient.mk (twoPowIdeal B p) (rec.evalLift N side) = N.contraction := by - obtain ⟨c, hc⟩ := hrec _ _ ((N.mapFun (Function.surjInv Ideal.Quotient.mk_surjective)).reindex - (Fintype.equivFin V) (Fintype.equivFin E)) (side ∘ (Fintype.equivFin V).symm) p + obtain ⟨c, hc⟩ := hrec _ _ (finLift N) (side ∘ (Fintype.equivFin V).symm) p have h2 : Ideal.Quotient.mk (twoPowIdeal B p) ((2 : B) ^ p) = 0 := Ideal.Quotient.eq_zero_iff_mem.mpr (Ideal.mem_span_singleton_self _) - rw [evalLift, hc, map_add, map_mul, h2, zero_mul, add_zero, contraction_reindex, - contraction_map_mapFun _ (Function.surjInv_eq Ideal.Quotient.mk_surjective)] + rw [evalLift, hc, map_add, map_mul, h2, zero_mul, add_zero, mk_contraction_finLift] end ContractionOracle @@ -319,8 +395,8 @@ noncomputable def nodeValue (rec : ContractionOracle B) (N : TensorNetwork B Boo {a r : ℕ} (Q : PacketPacking.Packing N.src N.tgt a r) (p u h : ℕ) (hua : u ≤ a) {t : ℤ} (ht : t ∈ recoveryNodes a) (hq : 0 < taperMult u h t) (z : B) : B := ∑ β ∈ modeBudget (fun Z : Fin Q.numPackets × Fin (2 ^ (padicValInt 2 t + 1)) × - Fin (nodePrecision (Fintype.card E) a h p t) => (Z.2.2 : ℕ) + 1) - (nodePrecision (Fintype.card E) a h p t), + Fin (nodePrecision p h a (Fintype.card E) (padicValInt 2 t)) => (Z.2.2 : ℕ) + 1) + (nodePrecision p h a (Fintype.card E) (padicValInt 2 t)), ∏ g, rec.evalLift ((callSplitting B hB N side hsrc htgt Q p u h hua ht hq z).child β g) Sum.isLeft @@ -331,11 +407,11 @@ theorem mk_nodeValue {rec : ContractionOracle B} (hrec : rec.Correct) (htgt : ∀ e, side (N.tgt e) = false) {a r : ℕ} (Q : PacketPacking.Packing N.src N.tgt a r) (p u h : ℕ) (hua : u ≤ a) {t : ℤ} (ht : t ∈ recoveryNodes a) (hq : 0 < taperMult u h t) (z : B) : - Ideal.Quotient.mk (twoPowIdeal B (nodePrecision (Fintype.card E) a h p t)) + Ideal.Quotient.mk (nodeIdeal B (Fintype.card E) a h p t) (nodeValue B hB rec N side hsrc htgt Q p u h hua ht hq z) = ∑ β ∈ modeBudget (fun Z : Fin Q.numPackets × Fin (2 ^ (padicValInt 2 t + 1)) × - Fin (nodePrecision (Fintype.card E) a h p t) => (Z.2.2 : ℕ) + 1) - (nodePrecision (Fintype.card E) a h p t), + Fin (nodePrecision p h a (Fintype.card E) (padicValInt 2 t)) => (Z.2.2 : ℕ) + 1) + (nodePrecision p h a (Fintype.card E) (padicValInt 2 t)), ∏ g, ((callSplitting B hB N side hsrc htgt Q p u h hua ht hq z).child β g).contraction := by simp only [nodeValue, map_sum, map_prod, ContractionOracle.mk_evalLift hrec] @@ -387,7 +463,51 @@ theorem exists_callValue_eq_add {rec : ContractionOracle B} (hrec : rec.Correct) end Call -/-! ### Children are smaller under (D.6) -/ +/-! ### The children of an active call -/ + +namespace ModeSplitting + +variable {R V E G P M : Type*} [CommRing R] [Fintype M] {N : TensorNetwork R Bool V E} + {W : Matrix E E R} (S : N.ModeSplitting W G P M) [Fintype E] [DecidableEq E] [DecidableEq P] + [DecidableEq G] [DecidableEq M] [DecidableEq V] + +/-- **Children run from rows to columns within the degree bound.** If every vertex of `N` has at +most `Δ` ports at each end, and the packets have `2 ^ a` slots with `2 · 2 ^ a ≤ Δ`, then each +child runs from its old vertices to its packets with at most `Δ` ports at each vertex: old +vertices keep their degrees, and a packet vertex has at most `2 L` ports (§D.4.6, p. 63). -/ +theorem rowsToColumnsLE_child {Δ a : ℕ} {slot : E → ℕ} (hrow : ∀ v, #{e | N.src e = v} ≤ Δ) + (hcol : ∀ w, #{e | N.tgt e = w} ≤ Δ) (hslot : ∀ e, slot e < 2 ^ a) + (hinj : ∀ e e', S.packet e = S.packet e' → slot e = slot e' → e = e') (hL : 2 * 2 ^ a ≤ Δ) + (β : M → ℕ) (g : G) : (S.child β g).RowsToColumnsLE Sum.isLeft Δ where + side_src := by rintro (e | e) <;> rfl + side_tgt := by rintro (e | e) <;> rfl + src_le := by + rintro (v | q) + · rw [S.card_child_src_inl] + by_cases hv : S.side v.1 + · have h0 : #{e | N.tgt e = v.1} = 0 := card_eq_zero.mpr <| filter_eq_empty_iff.mpr + fun e _ he => by + have := S.side_tgt e + rw [he, hv] at this + exact absurd this (by decide) + rw [h0, add_zero] + exact hrow _ + · have h0 : #{e | N.src e = v.1} = 0 := card_eq_zero.mpr <| filter_eq_empty_iff.mpr + fun e _ he => by + have := S.side_src e + rw [he] at this + exact hv this + rw [h0, zero_add] + exact hcol _ + · rw [S.card_child_src_inr] + exact Nat.zero_le _ + tgt_le := by + rintro (v | q) + · rw [S.card_child_tgt_inl] + exact Nat.zero_le _ + · exact (S.card_child_tgt_inr_le hslot hinj β g q).trans hL + +end ModeSplitting section ChildSize @@ -424,12 +544,28 @@ theorem mul_card_edges_child_callSplitting_le (N : TensorNetwork B Bool V E) (si (PacketPacking.col_ports_packing_le N.src N.tgt a 64 hrow hcol hM _ g)) exact this.trans_eq (by ring) +omit [LinearOrder E] [FinEnum V] in +/-- **The lifted children of an active call run from rows to columns within the degree bound** +`Δ`, when the call does and its packets have `2 · 2 ^ a ≤ Δ` slots: the networks the child calls +receive meet the network part of their guard (`CallParams.Guard`). -/ +theorem rowsToColumnsLE_finLift_child (N : TensorNetwork B Bool V E) (side : V → Bool) + (hsrc : ∀ e, side (N.src e) = true) (htgt : ∀ e, side (N.tgt e) = false) {a r : ℕ} + (Q : PacketPacking.Packing N.src N.tgt a r) (p u h : ℕ) (hua : u ≤ a) {t : ℤ} + (ht : t ∈ recoveryNodes a) (hq : 0 < taperMult u h t) (z : B) {Δ : ℕ} + (hrow : ∀ v, #{e | N.src e = v} ≤ Δ) (hcol : ∀ w, #{e | N.tgt e = w} ≤ Δ) + (hL : 2 * 2 ^ a ≤ Δ) (β) (g : Fin r) : + (finLift ((callSplitting B hB N side hsrc htgt Q p u h hua ht hq z).child β g)).RowsToColumnsLE + (Sum.isLeft ∘ (Fintype.equivFin _).symm) Δ := + RowsToColumnsLE.finLift <| ModeSplitting.rowsToColumnsLE_child + (callSplitting B hB N side hsrc htgt Q p u h hua ht hq z) hrow hcol (packing_slot_lt Q) + (by rw [callSplitting_packet]; exact packing_slot_injective Q) hL β g + end ChildSize /-! ### The recursion -/ -/-- The parameters of an active call: packets of `L = 2 ^ a` slots, and the tapered multiplicities -`q_t = h max(0, u - v₂ t)` of Lemma D.5. -/ +/-- The parameters of an active call: packets of `L = 2 ^ a` slots, the tapered multiplicities +`q_t = h max(0, u - v₂ t)` of Lemma D.5, and the degree bound `Δ` of (D.6). -/ structure CallParams where /-- The packets have `2 ^ a` slots. -/ a : ℕ @@ -437,17 +573,47 @@ structure CallParams where u : ℕ /-- The step of the multiplicities between levels. -/ h : ℕ + /-- The degree bound `Δ`. -/ + degree : ℕ /-- The parameters of a call with `M` edges and `n` interpolation points are *admissible* when they meet the hypotheses of Lemma D.5, `1 ≤ u ≤ M`, `u ≤ a` and `3 M ≤ h u ≤ 3 M + u`, and there are more than `M (2 L - 1)` points. -/ -@[expose] def CallParams.Admissible (θ : CallParams) (M n : ℕ) : Prop := - 1 ≤ θ.u ∧ θ.u ≤ M ∧ θ.u ≤ θ.a ∧ 3 * M ≤ θ.h * θ.u ∧ θ.h * θ.u ≤ 3 * M + θ.u ∧ - M * (2 * 2 ^ θ.a - 1) < n - -instance (θ : CallParams) (M n : ℕ) : Decidable (θ.Admissible M n) := by - unfold CallParams.Admissible - infer_instance +structure CallParams.Admissible (θ : CallParams) (M n : ℕ) : Prop where + /-- `1 ≤ u`. -/ + one_le_u : 1 ≤ θ.u + /-- `u ≤ M`. -/ + u_le_edges : θ.u ≤ M + /-- `u ≤ a`. -/ + u_le_a : θ.u ≤ θ.a + /-- `3 M ≤ h u`. -/ + le_h_mul : 3 * M ≤ θ.h * θ.u + /-- `h u ≤ 3 M + u`, as for `h = ⌈3 M / u⌉`. -/ + h_mul_le : θ.h * θ.u ≤ 3 * M + θ.u + /-- There are more than `M (2 L - 1)` interpolation points. -/ + points_lt : M * (2 * 2 ^ θ.a - 1) < n + +instance (θ : CallParams) (M n : ℕ) : Decidable (θ.Admissible M n) := + decidable_of_iff (1 ≤ θ.u ∧ θ.u ≤ M ∧ θ.u ≤ θ.a ∧ 3 * M ≤ θ.h * θ.u ∧ + θ.h * θ.u ≤ 3 * M + θ.u ∧ M * (2 * 2 ^ θ.a - 1) < n) + ⟨fun ⟨h₁, h₂, h₃, h₄, h₅, h₆⟩ => ⟨h₁, h₂, h₃, h₄, h₅, h₆⟩, + fun ⟨h₁, h₂, h₃, h₄, h₅, h₆⟩ => ⟨h₁, h₂, h₃, h₄, h₅, h₆⟩⟩ + +/-- The *guard* of an active call on a network `N` with `m` edges, its sides `side` and `n` +interpolation points: the parameters `θ` are admissible, and `N` runs from rows to columns with +at most `Δ = θ.degree` ports at each end of each vertex, the degree bound under which Lemma D.3 +makes the children smaller (`mul_card_edges_child_callSplitting_le`). Correctness does not use the +degree bound. -/ +structure CallParams.Guard (θ : CallParams) {R : Type*} {n m : ℕ} + (N : TensorNetwork R Bool (Fin n) (Fin m)) (side : Fin n → Bool) (N₀ : ℕ) : Prop + extends N.RowsToColumnsLE side θ.degree where + /-- The parameters are admissible. -/ + admissible : θ.Admissible m N₀ + +instance (θ : CallParams) {R : Type*} {n m : ℕ} (N : TensorNetwork R Bool (Fin n) (Fin m)) + (side : Fin n → Bool) (N₀ : ℕ) : Decidable (θ.Guard N side N₀) := + decidable_of_iff (N.RowsToColumnsLE side θ.degree ∧ θ.Admissible m N₀) + ⟨fun ⟨h₁, h₂⟩ => ⟨h₁, h₂⟩, fun h => ⟨h.toRowsToColumnsLE, h.admissible⟩⟩ section Recursion @@ -457,41 +623,42 @@ variable {K : Type*} [CommRing K] [Algebra ℚ K] (B : Subring K) /-- **The contraction recursion** ([OpenAI2026ETH], §D.4.7). A call at depth `d` on a network `N` with `m` edges, sides `side` and precision `p` asks the schedule for its parameters. When there -are none, the parameters are not admissible, or an edge does not run from a row to a column, it -returns the contraction (direct summation). Otherwise it is an active call (`callValue`) with the -packing of Lemma D.3 (`r = 64`) and the first `M (2 L - 1) + 1` points of `pts`, whose children -are recursive calls at depth `d + 1`; a child that does not have fewer edges returns its -contraction. -/ +are none, or the guard fails (`CallParams.Guard`), it returns the contraction (direct +summation). Otherwise it is an active call (`callValue`) with the packing of Lemma D.3 (`r = 64`) +and the first `M (2 L - 1) + 1` points of `pts`, whose children are recursive calls at depth +`d + 1`; a child that does not have fewer edges returns its contraction. -/ noncomputable def contractRec (d n m : ℕ) (N : TensorNetwork B Bool (Fin n) (Fin m)) (side : Fin n → Bool) (p : ℕ) : B := match sched d m with | none => N.contraction | some θ => - if hθ : θ.Admissible m N₀ ∧ (∀ e, side (N.src e) = true) ∧ ∀ e, side (N.tgt e) = false then + if hθ : θ.Guard N side N₀ then callValue B hB (fun n' m' N' side' p' => if hm : m' < m then contractRec (d + 1) n' m' N' side' p' else N'.contraction) - N side hθ.2.1 hθ.2.2 (PacketPacking.packing N.src N.tgt θ.a 64) p θ.u θ.h hθ.1.2.2.1 - fun k : Fin (m * (2 * 2 ^ θ.a - 1) + 1) => pts (Fin.castLE hθ.1.2.2.2.2.2 k) + N side hθ.side_src hθ.side_tgt (PacketPacking.packing N.src N.tgt θ.a 64) p θ.u θ.h + hθ.admissible.u_le_a + fun k : Fin (m * (2 * 2 ^ θ.a - 1) + 1) => pts (Fin.castLE hθ.admissible.points_lt k) else N.contraction termination_by m variable {B hB sched pts} in -/-- **An active call** of the recursion: when the schedule gives admissible parameters and the edges -run from rows to columns, the call is `callValue` with the packing of Lemma D.3, the first -`M (2 L - 1) + 1` points, and recursive calls at depth `d + 1` for the children with fewer edges. -/ +/-- **An active call** of the recursion: when the schedule gives parameters whose guard holds, the +call is `callValue` with the packing of Lemma D.3, the first `M (2 L - 1) + 1` points, and +recursive calls at depth `d + 1` for the children with fewer edges. -/ theorem contractRec_eq_callValue {d n m : ℕ} {N : TensorNetwork B Bool (Fin n) (Fin m)} - {side : Fin n → Bool} {θ : CallParams} (hs : sched d m = some θ) (hθ : θ.Admissible m N₀) - (hsrc : ∀ e, side (N.src e) = true) (htgt : ∀ e, side (N.tgt e) = false) (p : ℕ) : + {side : Fin n → Bool} {θ : CallParams} (hs : sched d m = some θ) (hθ : θ.Guard N side N₀) + (p : ℕ) : contractRec B hB sched pts d n m N side p = callValue B hB (fun n' m' N' side' p' => if m' < m then contractRec B hB sched pts (d + 1) n' m' N' side' p' else N'.contraction) - N side hsrc htgt (PacketPacking.packing N.src N.tgt θ.a 64) p θ.u θ.h hθ.2.2.1 - fun k : Fin (m * (2 * 2 ^ θ.a - 1) + 1) => pts (Fin.castLE hθ.2.2.2.2.2 k) := by + N side hθ.side_src hθ.side_tgt (PacketPacking.packing N.src N.tgt θ.a 64) p θ.u θ.h + hθ.admissible.u_le_a + fun k : Fin (m * (2 * 2 ^ θ.a - 1) + 1) => pts (Fin.castLE hθ.admissible.points_lt k) := by rw [contractRec.eq_def] simp only [hs] - rw [dite_eq_left ⟨hθ, hsrc, htgt⟩] + rw [dite_eq_left hθ] rfl variable {B hB sched pts} in @@ -515,9 +682,9 @@ theorem exists_contractRec_eq_add (hpts : Pairwise fun k l => IsUnit (pts k - pt · exact ⟨0, by simp⟩ · rename_i θ _ split_ifs with hθ - · obtain ⟨hu, huM, hua, hh, hh', hn⟩ := hθ.1 - refine exists_callValue_eq_add B hB (fun n' m' N' side' p' => ?_) N side hθ.2.1 hθ.2.2 _ - hu (by simpa using huM) hua (by simpa using hh) (by simpa using hh') _ + · obtain ⟨hu, huM, hua, hh, hh', hn⟩ := hθ.admissible + refine exists_callValue_eq_add B hB (fun n' m' N' side' p' => ?_) N side hθ.side_src + hθ.side_tgt _ hu (by simpa using huM) hua (by simpa using hh) (by simpa using hh') _ (fun k l hkl => hpts fun h => hkl (Fin.castLE_injective _ h)) (by simp) split_ifs with hm · exact ih m' hm (d + 1) n' N' side' p' diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Reindex.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Reindex.lean index 306a069d3b..468d7f065f 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Reindex.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Reindex.lean @@ -32,7 +32,8 @@ coefficient ring change. ## Main results -* `Cslib.Circuits.Frontier.TensorNetwork.contraction_reindex`: renaming preserves the contraction. +* `Cslib.Circuits.Frontier.TensorNetwork.contraction_reindex`: renaming preserves the contraction, + and `card_reindex_src`, `card_reindex_tgt` the degrees. * `Cslib.Circuits.Frontier.TensorNetwork.map_mapFun`: reducing a lift along `f` recovers the network. * `Cslib.Circuits.Frontier.TensorNetwork.contraction_map_mapFun`: the contraction of a lift @@ -48,6 +49,8 @@ public section namespace Cslib.Circuits.Frontier.TensorNetwork +open Finset + variable {R S U V E V' E' : Type*} /-! ### Renaming the vertices and edges -/ @@ -78,6 +81,20 @@ variable (eV : V ≃ V') (eE : E ≃ E') (N : TensorNetwork R U V E) (N.reindex eV eE).tensor v x = N.tensor (eV.symm v) (x ∘ eE) := rfl +/-- Renaming keeps the number of edges leaving each vertex. -/ +theorem card_reindex_src [Fintype E] [Fintype E'] [DecidableEq V] [DecidableEq V'] (v : V') : + #{e | (N.reindex eV eE).src e = v} = #{e | N.src e = eV.symm v} := by + refine Finset.card_equiv eE.symm fun e => ?_ + rw [Finset.mem_filter, Finset.mem_filter] + simp [Equiv.eq_symm_apply] + +/-- Renaming keeps the number of edges entering each vertex. -/ +theorem card_reindex_tgt [Fintype E] [Fintype E'] [DecidableEq V] [DecidableEq V'] (v : V') : + #{e | (N.reindex eV eE).tgt e = v} = #{e | N.tgt e = eV.symm v} := by + refine Finset.card_equiv eE.symm fun e => ?_ + rw [Finset.mem_filter, Finset.mem_filter] + simp [Equiv.eq_symm_apply] + /-- **Renaming preserves the contraction.** -/ theorem contraction_reindex [CommSemiring R] [Fintype U] [Fintype V] [Fintype E] [DecidableEq E] [Fintype V'] [Fintype E'] [DecidableEq E'] : diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ScheduledRecursion.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ScheduledRecursion.lean new file mode 100644 index 0000000000..3a70376cc1 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ScheduledRecursion.lean @@ -0,0 +1,206 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Recursion + +/-! +# The contraction recursion with the parameter schedule + +This file runs the contraction recursion (`TensorNetwork.contractRec`) with the parameter schedule +of [OpenAI2026ETH] (§D.4.2, (D.4)–(D.6), p. 58) from `Cslib.Circuits.Frontier.ContractionSchedule`, +so that its correctness and the running-time certificate refer to the same calls. + +**The schedule of call parameters** (`ContractionSchedule.callParams s`). For size `s`, a call at +depth `d` with `M` edges is a leaf when `M ≤ H = leafSize s`. Otherwise it has `a = slotBits s d` +slot bits, taper `u = taper s d`, multiplicity step `h = ⌈3 M / u⌉` (`taperStep`), and degree +bound `Δ = degreeBound s`. Its children are recovered at the precisions `p_t = nodePrecision`. +Correctness of the recursion holds for this schedule as for every other +(`TensorNetwork.exists_contractRec_eq_add`). + +**An active call.** At a call with `Active C s d M p` and `ActiveBounds C s d M p` (the certificate +`exists_activeBounds` gives the second from the first for `s ≥ s₀`) on a network that runs from +rows to columns with at most `Δ` ports at each end, and with at least `2 ^ (4 ⌊log₂ s⌋)` +interpolation points: + +* the guard holds, so the call is the active call `callValue` + (`contractRec_callParams_eq_callValue`); +* every lifted child runs from rows to columns within the degree bound + (`TensorNetwork.rowsToColumnsLE_finLift_child`), has at most `M / 16` edges + (`sixteen_mul_card_edges_child_le`), and is evaluated at a precision within the schedule + (`nodePrecision_le_of_taperMult_pos`), so it is a leaf or an active call at depth `d + 1` + (`Active.child`); +* there are at most `2 ^ e_d` mode tuples at each active node (`card_modeBudget_callParams_le`), and + the nodes, points and groups contribute `2 ^ (2 ⌊log₂ s⌋)` (`ActiveBounds.branching_le`). + +These are the per-call facts that a certificate of the running time instantiates along the +recursion tree. + +## Main definitions + +* `Cslib.Circuits.Frontier.ContractionSchedule.taperStep`: `h = ⌈3 M / u⌉`. +* `Cslib.Circuits.Frontier.ContractionSchedule.callParams`: the schedule of call parameters. + +## Main results + +* `Cslib.Circuits.Frontier.ContractionSchedule.ActiveBounds.admissible`: the parameters of an active + call are admissible. +* `Cslib.Circuits.Frontier.ContractionSchedule.contractRec_callParams_eq_callValue`: an active call. +* `Cslib.Circuits.Frontier.ContractionSchedule.sixteen_mul_card_edges_child_le`, + `Cslib.Circuits.Frontier.ContractionSchedule.nodePrecision_le_of_taperMult_pos`, + `Cslib.Circuits.Frontier.ContractionSchedule.card_modeBudget_callParams_le`: its children and + their number. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.2 (p. 58), + §D.4.4–§D.4.7, pp. 60–64. +-/ + +public section + +open Finset + +namespace Cslib.Circuits.Frontier.ContractionSchedule + +open TensorNetwork + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +/-! ### The schedule of call parameters -/ + +/-- The multiplicity step `h = ⌈3 M / u⌉` of Lemma D.5 at depth `d`, with `u = taper s d`. -/ +@[expose] def taperStep (s d M : ℕ) : ℕ := + (3 * M + taper s d - 1) / taper s d + +theorem le_taperStep_mul {s d M : ℕ} (hu : 1 ≤ taper s d) : + 3 * M ≤ taperStep s d M * taper s d := by + have h1 := Nat.lt_div_mul_add (a := 3 * M + taper s d - 1) (b := taper s d) (by omega) + unfold taperStep + generalize (3 * M + taper s d - 1) / taper s d * taper s d = y at h1 ⊢ + omega + +theorem taperStep_mul_le (s d M : ℕ) : taperStep s d M * taper s d ≤ 3 * M + taper s d := + (Nat.div_mul_le_self _ _).trans (Nat.sub_le _ _) + +/-- **The schedule of call parameters** for size `s`: a call at depth `d` with `M` edges is a leaf +when `M ≤ leafSize s`, and otherwise has `a = slotBits s d`, `u = taper s d`, `h = ⌈3 M / u⌉` and +the degree bound `Δ = degreeBound s`. -/ +@[expose] def callParams (s d M : ℕ) : Option CallParams := + if M ≤ leafSize s then none + else some ⟨slotBits s d, taper s d, taperStep s d M, degreeBound s⟩ + +theorem callParams_of_le {s d M : ℕ} (h : M ≤ leafSize s) : callParams s d M = none := + ite_eq_left h + +theorem callParams_of_lt {s d M : ℕ} (h : leafSize s < M) : + callParams s d M = some ⟨slotBits s d, taper s d, taperStep s d M, degreeBound s⟩ := + ite_eq_right (not_le.mpr h) + +/-- **The parameters of an active call are admissible**, given `2 ^ (4 ⌊log₂ s⌋)` interpolation +points. -/ +theorem ActiveBounds.admissible {C s d M p : ℕ} (hB : ActiveBounds C s d M p) {N₀ : ℕ} + (hN₀ : 2 ^ (4 * Nat.log 2 s) ≤ N₀) : + (CallParams.mk (slotBits s d) (taper s d) (taperStep s d M) (degreeBound s)).Admissible M + N₀ where + one_le_u := hB.one_le_taper + u_le_edges := hB.taper_le_edges + u_le_a := by + have := slotBits_eq s d + simp only + omega + le_h_mul := le_taperStep_mul hB.one_le_taper + h_mul_le := taperStep_mul_le s d M + points_lt := by + have := hB.points_le + simp only + omega + +/-! ### An active call -/ + +section Active + +variable {K : Type*} [CommRing K] [Algebra ℚ K] (B : Subring K) + (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) {N₀ : ℕ} (pts : Fin N₀ → B) + +/-- **An active call of the scheduled recursion.** At an active call (`Active`, `ActiveBounds`) on a +network that runs from rows to columns within the degree bound, with at least +`2 ^ (4 ⌊log₂ s⌋)` interpolation points, the recursion is the active call `callValue` with the +packing of Lemma D.3 at `a = slotBits s d`, `u = taper s d` and `h = ⌈3 M / u⌉`, and recursive +calls at depth `d + 1` for the children. -/ +theorem contractRec_callParams_eq_callValue {C s d n m p : ℕ} + {N : TensorNetwork B Bool (Fin n) (Fin m)} {side : Fin n → Bool} (hA : Active C s d m p) + (hAB : ActiveBounds C s d m p) (hN : N.RowsToColumnsLE side (degreeBound s)) + (hN₀ : 2 ^ (4 * Nat.log 2 s) ≤ N₀) : + contractRec B hB (callParams s) pts d n m N side p = + callValue B hB + (fun n' m' N' side' p' => + if m' < m then contractRec B hB (callParams s) pts (d + 1) n' m' N' side' p' + else N'.contraction) + N side hN.side_src hN.side_tgt (PacketPacking.packing N.src N.tgt (slotBits s d) 64) p + (taper s d) (taperStep s d m) (hAB.admissible hN₀).u_le_a + fun k : Fin (m * (2 * 2 ^ slotBits s d - 1) + 1) => + pts (Fin.castLE (hAB.admissible hN₀).points_lt k) := + contractRec_eq_callValue (callParams_of_lt hA.leafSize_lt) ⟨hN, hAB.admissible hN₀⟩ p + +variable {C s d n m p : ℕ} {N : TensorNetwork B Bool (Fin n) (Fin m)} {side : Fin n → Bool} + +/-- **The children of an active call have at most `M / 16` edges**, as `Active.child` requires +(Lemma D.3 (3) under (D.6), `TensorNetwork.mul_card_edges_child_callSplitting_le`). -/ +theorem sixteen_mul_card_edges_child_le (hAB : ActiveBounds C s d m p) + (hN : N.RowsToColumnsLE side (degreeBound s)) (hua : taper s d ≤ slotBits s d) {t : ℤ} + (ht : t ∈ recoveryNodes (slotBits s d)) (hq : 0 < taperMult (taper s d) (taperStep s d m) t) + (z : B) (g : Fin 64) : + 16 * Fintype.card + ({e // (callSplitting B hB N side hN.side_src hN.side_tgt + (PacketPacking.packing N.src N.tgt (slotBits s d) 64) p (taper s d) (taperStep s d m) + hua ht hq z).group (N.src e) = g} ⊕ + {e // (callSplitting B hB N side hN.side_src hN.side_tgt + (PacketPacking.packing N.src N.tgt (slotBits s d) 64) p (taper s d) (taperStep s d m) + hua ht hq z).group (N.tgt e) = g}) ≤ + m := by + have h := mul_card_edges_child_callSplitting_le B hB N side hN.side_src hN.side_tgt hN.src_le + hN.tgt_le (by rw [Fintype.card_fin]; exact hAB.degree_mul_pairs_sq_le) p (taper s d) + (taperStep s d m) hua ht hq z g + have := Fintype.card_fin m + omega + +/-- **The children of an active call are evaluated within the precision schedule**: a node of +positive multiplicity passes `p_t ≤ precisionBound C s (d + 1)` to its children. -/ +theorem nodePrecision_le_of_taperMult_pos (hAB : ActiveBounds C s d m p) {t : ℤ} + (hq : 0 < taperMult (taper s d) (taperStep s d m) t) : + nodePrecision p (taperStep s d m) (slotBits s d) (Fintype.card (Fin m)) (padicValInt 2 t) ≤ + precisionBound C s (d + 1) := by + rw [Fintype.card_fin] + exact hAB.nodePrecision_le _ _ (padicValInt_lt_of_taperMult_pos_left hq) (taperStep_mul_le s d m) + +omit [Algebra ℚ K] in +/-- **The mode count (D.15) at an active call**: at a node of positive multiplicity, there are at +most `2 ^ e_d` mode tuples for the packing of Lemma D.3, which has at most `3 M / L` packets. -/ +theorem card_modeBudget_callParams_le (hA : Active C s d m p) (hAB : ActiveBounds C s d m p) + (hN : N.RowsToColumnsLE side (degreeBound s)) {t : ℤ} + (hq : 0 < taperMult (taper s d) (taperStep s d m) t) : + #(modeBudget + (fun Z : Fin (PacketPacking.packing N.src N.tgt (slotBits s d) 64).numPackets × + Fin (2 ^ (padicValInt 2 t + 1)) × + Fin (nodePrecision p (taperStep s d m) (slotBits s d) (Fintype.card (Fin m)) + (padicValInt 2 t)) => (Z.2.2 : ℕ) + 1) + (nodePrecision p (taperStep s d m) (slotBits s d) (Fintype.card (Fin m)) + (padicValInt 2 t))) ≤ + 2 ^ branchExponent C s d := by + have hP := PacketPacking.numPackets_packing_mul_le N.src N.tgt (slotBits s d) 64 hN.src_le + hN.tgt_le (by rw [Fintype.card_fin]; exact hAB.degree_mul_pairs_sq_le) + (by rw [Fintype.card_fin]; exact hAB.slots_mul_pairs_le) + rw [Fintype.card_fin] at hP ⊢ + exact hAB.card_modeBudget_le hA (padicValInt_lt_of_taperMult_pos_left hq) + (taperStep_mul_le s d m) (by rwa [Fintype.card_fin]) + +end Active + +end Cslib.Circuits.Frontier.ContractionSchedule diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkRecursion.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkRecursion.lean index 76bfcec667..624befa8db 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkRecursion.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkRecursion.lean @@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Samuel Schlesinger -/ -import Complexity.Computability.Circuit.Frontier.TensorNetwork.Recursion +import Complexity.Computability.Circuit.Frontier.TensorNetwork.ScheduledRecursion import Complexity.Foundations.NumberTheory.Padics.AdjoinRootDigitLift import Mathlib.Algebra.Field.ZMod import Mathlib.Algebra.Polynomial.SpecificDegree @@ -23,6 +23,8 @@ import Mathlib.Tactic.FinCases `a = u = 1`, `h = 3`. On a one-edge network these parameters are admissible (`3 M ≤ h u ≤ 3 M + u` and `M (2 L - 1) = 3 < 4`), so the root call is active (`contractRec_eq_callValue`); its children are not smaller and are evaluated directly. Without parameters the call is a leaf. +* **The scheduled recursion** (`ContractionSchedule.callParams`): small calls are leaves, the + recursion is correct, and for large `s` the parameters of every active call are admissible. -/ namespace ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.Recursion @@ -104,11 +106,11 @@ theorem pts_pairwise : Pairwise fun k l => IsUnit (pts k - pts l) := fun _ _ hkl example : ContractionOracle.Correct (B := O) fun _ _ N _ _ => N.contraction := fun _ _ _ _ _ => ⟨0, by simp⟩ -/-- The constant schedule `a = u = 1`, `h = 3`. -/ -def sched (_ _ : ℕ) : Option TensorNetwork.CallParams := some ⟨1, 1, 3⟩ +/-- The constant schedule `a = u = 1`, `h = 3`, with degree bound `1`. -/ +def sched (_ _ : ℕ) : Option TensorNetwork.CallParams := some ⟨1, 1, 3, 1⟩ /-- On one edge with four points the schedule is admissible. -/ -example : (TensorNetwork.CallParams.mk 1 1 3).Admissible 1 4 := by decide +example : (TensorNetwork.CallParams.mk 1 1 3 1).Admissible 1 4 := by decide /-- One edge from row `0` to column `1`, with tensor `(1, 2)` at the row and `(1, 1)` at the column. -/ @@ -128,7 +130,9 @@ example (p : ℕ) : if m' < 1 then contractRec O hO sched pts 1 n' m' N' side' p' else N'.contraction) net₁ (fun v => decide (v = 0)) (fun _ => rfl) (fun _ => rfl) (Cslib.PacketPacking.packing net₁.src net₁.tgt 1 64) p 1 3 le_rfl pts := - contractRec_eq_callValue (θ := ⟨1, 1, 3⟩) rfl (by decide) (fun _ => rfl) (fun _ => rfl) p + contractRec_eq_callValue (θ := ⟨1, 1, 3, 1⟩) rfl + ⟨⟨fun _ => rfl, fun _ => rfl, fun _ => (Finset.card_filter_le _ _).trans (by simp), + fun _ => (Finset.card_filter_le _ _).trans (by simp)⟩, by decide⟩ p /-- With no parameters, a call is a leaf and returns the contraction. -/ example (p : ℕ) : @@ -150,4 +154,29 @@ example (sched' : ℕ → ℕ → Option TensorNetwork.CallParams) (n m p : ℕ) ∃ c : O, contractRec O hO sched' pts 0 n m N side p = N.contraction + 2 ^ p * c := exists_contractRec_eq_add O hO sched' pts pts_pairwise 0 n m N side p +/-! ### The scheduled recursion -/ + +open Cslib.Circuits.Frontier.ContractionSchedule in +/-- With the parameter schedule, a call with at most `leafSize s` edges is a leaf. -/ +example (s n m p : ℕ) (hm : m ≤ leafSize s) (N : TensorNetwork O Bool (Fin n) (Fin m)) + (side : Fin n → Bool) : + contractRec O hO (callParams s) pts 0 n m N side p = N.contraction := + contractRec_of_sched_eq_none (callParams_of_le hm) p + +open Cslib.Circuits.Frontier.ContractionSchedule in +/-- With the parameter schedule, the recursion is correct. -/ +example (s n m p : ℕ) (N : TensorNetwork O Bool (Fin n) (Fin m)) (side : Fin n → Bool) : + ∃ c : O, contractRec O hO (callParams s) pts 0 n m N side p = N.contraction + 2 ^ p * c := + exists_contractRec_eq_add O hO (callParams s) pts pts_pairwise 0 n m N side p + +open Cslib.Circuits.Frontier.ContractionSchedule in +/-- For large `s`, the parameters of every active call are admissible with `2 ^ (4 ⌊log₂ s⌋)` +points, so its guard reduces to the network running from rows to columns within the degree +bound. -/ +example (C : ℕ) : ∃ s₀, ∀ s, s₀ ≤ s → ∀ d M p, Active C s d M p → + (TensorNetwork.CallParams.mk (slotBits s d) (taper s d) (taperStep s d M) + (degreeBound s)).Admissible M (2 ^ (4 * Nat.log 2 s)) := by + obtain ⟨s₀, hs₀⟩ := exists_activeBounds C + exact ⟨s₀, fun s hs d M p hA => ((hs₀ s hs).2 d M p hA).admissible le_rfl⟩ + end ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.Recursion From 792613291bce8b9d183ff5b14b4ccb2664042655 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 23:21:15 -0400 Subject: [PATCH 034/129] feat(circuit/frontier): direct summation over networks with described tensors Networks whose tensors are given by descriptors and an evaluator, and their direct summation as a stack recursion: one native machine for the contraction modulo 2^P from any native certificate of the evaluator, with time (2^m (k+1) + 1) (poly + 2E) and space poly + ES. This is the leaf step of [OpenAI2026ETH, Appendix D.4.7] with implicit packet tensors. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../TensorNetwork/DescribedContraction.lean | 896 ++++++++++++++++++ ComplexityTests/DescribedContraction.lean | 79 ++ 3 files changed, 976 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean create mode 100644 ComplexityTests/DescribedContraction.lean diff --git a/Complexity.lean b/Complexity.lean index f7c0c0ab92..8217887b32 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -872,6 +872,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Binarize public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DescribedContraction public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DirectContraction public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean new file mode 100644 index 0000000000..fb1320cf92 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean @@ -0,0 +1,896 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit +public import Complexity.Computability.GenericRAM.Native.StackRecursion +public import Complexity.Foundations.Data.BitString.List.Binary +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Words +public import Complexity.Computability.GenericRAM.Native.BooleanFold +public import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListLookup +public import Complexity.Computability.Machines.Turing.MultiTape.Internal.DegreeOptionPrimitives +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Degree.Bounds +import Complexity.Foundations.Data.BitString.Binary +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Binary +import Complexity.Computability.Machines.Turing.MultiTape.Internal.EqualityPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryDivision +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListBinaryLookup +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListMap +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListToBits +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeNatFrontends +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeOptionPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Length +import Mathlib.Algebra.BigOperators.Fin +import Mathlib.Algebra.BigOperators.Intervals +import Mathlib.Data.List.GetD + +/-! +# Contracting a network with described tensors by direct summation + +The leaves of the recursion in the proof of Theorem D.1 of [OpenAI2026ETH] are summed directly +over all Boolean edge assignments (Appendix D.4.7, p. 64), but their tensors are not tables: a +packet vertex has up to `2 L` ports, so its table would be superpolynomial, and its entries are +computed from a description instead (Appendix D.4.6, p. 63). A *described network* +(`DescribedNetwork T`) has, for each vertex, its ports and a descriptor `t : T` of its tensor, +whose entry at the bits `bs` on the ports is `eval t bs` for an evaluator `eval`. Explicit +networks are the case of tables (`DescribedNetwork.ofExplicit`). + +**Direct summation as a stack recursion** (`DescribedNetwork.sumScheme`). The sum over the +`2 ^ m` assignments is a call whose children are the assignments, in binary order; an assignment +is a call whose children are the entries of the vertices at its bits, multiplied modulo `2 ^ P`; +an entry is a leaf, evaluated by the evaluator. The derivation has `2 ^ m (k + 1) + 1` nodes for +`k` vertices and two open calls (`DescribedNetwork.eval_sumRoot`). So a native certificate of the +evaluator, with any bounds, gives one native machine for the contraction modulo `2 ^ P` on every +input (`DescribedNetwork.contractionCertificate`), through +`Cslib.GenericRAM.StackRecursion.Scheme.certificate`. If the evaluator takes time `E`, space `ES` +and outputs at most `Ev` bits on every vertex, the machine takes time `2 ^ m (k + 1) + 1` times +`K (n + Ev + 1) ^ K + 2 E` and space `K (n + Ev + 1) ^ K + ES` on inputs of length `n` +(`DescribedNetwork.contractionCertificate_le`). This is the leaf step of the contraction +algorithm, with implicit packet tensors as descriptors. + +## Main definitions + +* `Cslib.Circuits.Frontier.DescribedNetwork`: a network with described tensors. +* `Cslib.Circuits.Frontier.DescribedNetwork.contraction`: its contraction. +* `Cslib.Circuits.Frontier.DescribedNetwork.sumScheme`: direct summation as a recursion scheme. +* `Cslib.Circuits.Frontier.DescribedNetwork.encDescribed`: the encoding of a described network. +* `Cslib.Circuits.Frontier.DescribedNetwork.contractionCertificate`: one native machine for the + contraction modulo `2 ^ P`. + +## Main results + +* `Cslib.GenericRAM.StackRecursion.Scheme.Loop.counted`: a call whose children are indexed by + a counter. +* `Cslib.Circuits.Frontier.DescribedNetwork.eval_sumRoot`: direct summation evaluates to the + contraction modulo `2 ^ P`, with `2 ^ m (k + 1) + 1` nodes and two open calls. +* `Cslib.Circuits.Frontier.DescribedNetwork.contraction_ofExplicit`: explicit networks. +* `Cslib.Circuits.Frontier.DescribedNetwork.run_sumScheme`: the machine computes the contraction + modulo `2 ^ P`. +* `Cslib.Circuits.Frontier.DescribedNetwork.contractionCertificate_le`: its time and space. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Appendix D.4.6–D.4.7 + (pp. 63–64). +-/ + +@[expose] public section + +namespace Cslib.GenericRAM.StackRecursion.Scheme + +open Finset + +variable {X A Y : Type} {R : Scheme X A Y} {I : Invariant X A Y} + +/-- **A call with counted children.** If, from the accumulators `acc c`, the call `x` asks for the +children `child c` for `c < bound` and then stops, and the children evaluate to `val c`, then its +loop from `acc 0` ends at `acc bound`, with the children's nodes and at most `H` levels. -/ +theorem Loop.counted {x : X} {acc : ℕ → A} {child : ℕ → X} {val : ℕ → Y} {nodes height : ℕ → ℕ} + (bound H : ℕ) (hnext : ∀ c < bound, R.next x (acc c) = some (child c)) + (hdone : R.next x (acc bound) = none) + (habsorb : ∀ c < bound, R.absorb x (acc c) (val c) = acc (c + 1)) + (heval : ∀ c < bound, R.Eval I (child c) (val c) (nodes c) (height c)) + (hframe : ∀ c < bound, I.frame x (acc c)) (hH : ∀ c < bound, height c ≤ H) : + ∃ h ≤ H, R.Loop I x (acc 0) (acc bound) (∑ c ∈ range bound, nodes c) h := by + -- From any start `s ≤ bound`. + have key : ∀ k s, s + k = bound → + ∃ h ≤ H, R.Loop I x (acc s) (acc bound) (∑ c ∈ Ico s bound, nodes c) h := by + intro k + induction k with + | zero => + intro s hs + subst hs + exact ⟨0, Nat.zero_le _, by simpa using Loop.done hdone⟩ + | succ k ih => + intro s hs + obtain ⟨h, hh, hl⟩ := ih (s + 1) (by omega) + have hs' : s < bound := by omega + have hl' := Loop.child (hframe s hs') (hnext s hs') (heval s hs') + (by rw [habsorb s hs']; exact hl) + refine ⟨max (height s) h, max_le (hH s hs') hh, ?_⟩ + rwa [sum_eq_sum_Ico_succ_bot hs'] + simpa using key bound 0 (by omega) + +end Cslib.GenericRAM.StackRecursion.Scheme + +namespace Cslib.Circuits.Frontier + +open Finset Cslib.BitString Cslib.GenericRAM Cslib.GenericRAM.StackRecursion + +/-- A Boolean tensor network with *described* tensors: the number of edges, numbered from zero, +and for each vertex its ordered list of ports and a descriptor of its tensor. -/ +structure DescribedNetwork (T : Type) where + /-- The number of edges; the edges are numbered `0, …, numEdges - 1`. -/ + numEdges : ℕ + /-- The vertices, each given by its ports and the descriptor of its tensor. -/ + vertices : List (List ℕ × T) + +namespace DescribedNetwork + +variable {T : Type} (eval : T → List Bool → ℕ) + +/-- The bits that a word, read as an assignment of bits to the edges, puts on a list of ports; +a port past the end of the word reads `false`. -/ +def wordBits (word : List Bool) (ports : List ℕ) : List Bool := + ports.map fun i => word.getD i false + +/-- The *contraction*: the sum over all assignments of bits to the edges of the product of the +entries of the vertices at the bits on their ports. -/ +def contraction (d : DescribedNetwork T) : ℕ := + ∑ x : Fin d.numEdges → Bool, + (d.vertices.map fun v => eval v.2 (wordBits (List.ofFn x) v.1)).prod + +/-- An explicit network, with its tables as descriptors. -/ +def ofExplicit (d : ExplicitTensorNetwork ℕ) : DescribedNetwork (List ℕ) := + ⟨d.numEdges, d.vertices⟩ + +private theorem getD_ofFn {n : ℕ} (x : Fin n → Bool) (i : ℕ) : + (List.ofFn x).getD i false = if h : i < n then x ⟨i, h⟩ else false := by + by_cases h : i < n + · rw [dite_eq_left h, List.getD_eq_getElem _ _ (by simpa using h), List.getElem_ofFn] + · rw [dite_eq_right h, List.getD_eq_default _ _ (by simp only [List.length_ofFn]; omega)] + +/-- **Explicit networks**: with table lookup as the evaluator, the contraction is that of the +explicit network. -/ +theorem contraction_ofExplicit (d : ExplicitTensorNetwork ℕ) : + (ofExplicit d).contraction ExplicitTensorNetwork.entry = d.contraction := by + refine sum_congr rfl fun x _ => ?_ + simp only [wordBits, ExplicitTensorNetwork.portBits] + congr 1 + refine List.map_congr_left fun v _ => ?_ + congr 2 + funext i + exact getD_ofFn x i + +/-! ### Direct summation as a recursion scheme -/ + +/-- A node of direct summation: its kind (`0` the sum, `1` an assignment, `2` an entry), the +precision and the network, and the current word and vertex index. -/ +abbrev Node (T : Type) := ℕ × ((ℕ × DescribedNetwork T) × (List Bool × ℕ)) + +/-- The entry of a node: the evaluator at the descriptor of its vertex and the bits of its word +on the vertex's ports, or zero for an index past the vertices. -/ +def entryOf (x : Node T) : ℕ := + (x.2.1.2.vertices[x.2.2.2]?).elim 0 fun v => eval v.2 (wordBits x.2.2.1 v.1) + +/-- **Direct summation as a recursion scheme.** The sum (kind `0`) has the `2 ^ m` assignments as +children, adding their values modulo `2 ^ P`; an assignment (kind `1`) has the entries of the +vertices as children, multiplying them modulo `2 ^ P`; an entry (kind `2`) is a leaf. The +accumulator is a counter and a residue. -/ +def sumScheme : Scheme (Node T) (ℕ × ℕ) ℕ where + isLeaf x := x.1 == 2 + leaf := entryOf eval + start x := (0, (if x.1 = 0 then 0 else 1) % 2 ^ x.2.1.1) + next x a := + if x.1 = 0 then + (if a.1 < 2 ^ x.2.1.2.numEdges then some (1, (x.2.1, (lowBits x.2.1.2.numEdges a.1, 0))) + else none) + else if x.1 = 1 then + (if a.1 < x.2.1.2.vertices.length then + some (2, (x.2.1, (x.2.2.1, min x.2.1.2.vertices.length a.1))) else none) + else none + absorb x a y := (a.1 + 1, if x.1 = 0 then (a.2 + y) % 2 ^ x.2.1.1 else a.2 * y % 2 ^ x.2.1.1) + finish _ a := a.2 + +/-- The root of direct summation on `(P, d)`. -/ +def sumRoot (q : ℕ × DescribedNetwork T) : Node T := (0, (q, ([], 0))) + +/-- The weight of a word modulo `2 ^ P`: the product of the entries at its bits, reduced after +each factor. -/ +def weightMod (P : ℕ) (d : DescribedNetwork T) (word : List Bool) : ℕ := + d.vertices.foldl (fun acc v => acc * eval v.2 (wordBits word v.1) % 2 ^ P) (1 % 2 ^ P) + +/-- The shape of a node of direct summation on `q`: it keeps `q`, its kind is at most `2`, its +word has at most `m` bits and its index is at most the number of vertices. -/ +def NodeShape (q : ℕ × DescribedNetwork T) (x : Node T) : Prop := + x.2.1 = q ∧ x.1 ≤ 2 ∧ x.2.2.1.length ≤ q.2.numEdges ∧ x.2.2.2 ≤ q.2.vertices.length + +/-- The invariant of direct summation on `q = (P, d)`: nodes have the shape of `NodeShape`, frames +also have a counter at most `2 ^ m + k` and a residue below `2 ^ P`, and every value is a residue +below `2 ^ P` or an entry of a vertex at the bits of an `m`-bit word. -/ +def sumInvariant (q : ℕ × DescribedNetwork T) : Invariant (Node T) (ℕ × ℕ) ℕ := + ⟨NodeShape q, fun x a => NodeShape q x ∧ a.1 ≤ 2 ^ q.2.numEdges + q.2.vertices.length ∧ + a.2 < 2 ^ q.1, + fun y => y < 2 ^ q.1 ∨ ∃ v ∈ q.2.vertices, ∃ w : List Bool, w.length = q.2.numEdges ∧ + y = eval v.2 (wordBits w v.1)⟩ + +variable {eval} + +/-- An entry is a leaf. -/ +theorem eval_entry (q : ℕ × DescribedNetwork T) {word : List Bool} {v : ℕ} + (hw : word.length = q.2.numEdges) (hv : v < q.2.vertices.length) : + (sumScheme eval).Eval (sumInvariant eval q) (2, (q, (word, v))) + (entryOf eval (2, (q, (word, v)))) 1 0 := by + refine Scheme.Eval.leaf (R := sumScheme eval) ⟨rfl, by omega, hw.le, hv.le⟩ rfl + (.inr ⟨q.2.vertices[v], List.getElem_mem hv, word, hw, ?_⟩) + change (q.2.vertices[v]?).elim 0 _ = _ + rw [List.getElem?_eq_getElem hv] + rfl + +private theorem foldl_take_succ (d : DescribedNetwork T) (P : ℕ) (word : List Bool) (c : ℕ) + (hc : c < d.vertices.length) : + (d.vertices.take (c + 1)).foldl (fun acc v => acc * eval v.2 (wordBits word v.1) % 2 ^ P) + (1 % 2 ^ P) = + (d.vertices.take c).foldl (fun acc v => acc * eval v.2 (wordBits word v.1) % 2 ^ P) + (1 % 2 ^ P) * entryOf eval (2, ((P, d), (word, c))) % 2 ^ P := by + rw [List.take_add_one, List.getElem?_eq_getElem hc, Option.toList_some, List.foldl_append] + simp [entryOf, List.getElem?_eq_getElem hc] + +private theorem foldl_mod_lt {α : Type} (f : α → ℕ) (P : ℕ) (l : List α) : + l.foldl (fun acc v => acc * f v % 2 ^ P) (1 % 2 ^ P) < 2 ^ P := by + have h (a : ℕ) (ha : a < 2 ^ P) : l.foldl (fun acc v => acc * f v % 2 ^ P) a < 2 ^ P := by + induction l generalizing a with + | nil => exact ha + | cons v l ih => exact ih _ (Nat.mod_lt _ (Nat.two_pow_pos P)) + exact h _ (Nat.mod_lt _ (Nat.two_pow_pos P)) + +/-- **An assignment evaluates to the weight of its word**, with one node per vertex plus one +and one open call. -/ +theorem eval_assign (P : ℕ) (d : DescribedNetwork T) {word : List Bool} + (hw : word.length = d.numEdges) : + ∃ h ≤ 1, (sumScheme eval).Eval (sumInvariant eval (P, d)) (1, ((P, d), (word, 0))) + (weightMod eval P d word) (d.vertices.length + 1) h := by + set acc : ℕ → ℕ × ℕ := fun c => (c, (d.vertices.take c).foldl + (fun acc v => acc * eval v.2 (wordBits word v.1) % 2 ^ P) (1 % 2 ^ P)) + have hshape : NodeShape (P, d) ((1, ((P, d), (word, 0))) : Node T) := + ⟨rfl, by omega, hw.le, Nat.zero_le _⟩ + obtain ⟨h, hh, hloop⟩ := Scheme.Loop.counted (R := sumScheme eval) + (I := sumInvariant eval (P, d)) + (x := (1, ((P, d), (word, 0)))) (acc := acc) + (child := fun c => (2, ((P, d), (word, c)))) + (val := fun c => entryOf eval (2, ((P, d), (word, c)))) + (nodes := fun _ => 1) (height := fun _ => 0) d.vertices.length 0 + (fun c hc => by simp [sumScheme, acc, hc, min_eq_right hc.le]) + (by simp [sumScheme, acc]) + (fun c hc => by + simp only [sumScheme, acc, one_ne_zero, ite_false] + rw [foldl_take_succ d P word c hc]) + (fun c hc => eval_entry (P, d) hw hc) + (fun c hc => ⟨hshape, (Nat.le_of_lt hc).trans (Nat.le_add_left _ _), foldl_mod_lt _ _ _⟩) + (fun _ _ => le_rfl) + have hstart : (sumScheme eval).start (1, ((P, d), (word, 0))) = acc 0 := by + simp [sumScheme, acc] + rw [← hstart] at hloop + refine ⟨h + 1, by omega, ?_⟩ + have := Scheme.Eval.call (R := sumScheme eval) (I := sumInvariant eval (P, d)) + (x := (1, ((P, d), (word, 0)))) hshape rfl hloop (.inl (foldl_mod_lt _ _ _)) + simpa [sumScheme, acc, weightMod, List.take_length, Finset.sum_const, Finset.card_range] using + this + +private theorem sum_lowBits (n : ℕ) (g : List Bool → ℕ) : + ∑ j ∈ range (2 ^ n), g (lowBits n j) = ∑ x : Fin n → Bool, g (List.ofFn x) := by + rw [← Fin.sum_univ_eq_sum_range] + exact Fintype.sum_equiv (equivFin n).symm _ _ fun k => by + rw [lowBits_eq_ofFn_equivFin_symm] + +private theorem foldl_mul_mod {α : Type} (f : α → ℕ) (m : ℕ) (l : List α) (a : ℕ) : + l.foldl (fun acc v => acc * f v % m) (a % m) = a * (l.map f).prod % m := by + induction l generalizing a with + | nil => simp + | cons v l ih => + rw [List.foldl_cons, Nat.mod_mul_mod, ih, List.map_cons, List.prod_cons, Nat.mul_assoc] + +private theorem weightMod_eq (P : ℕ) (d : DescribedNetwork T) (word : List Bool) : + weightMod eval P d word = + (d.vertices.map fun v => eval v.2 (wordBits word v.1)).prod % 2 ^ P := by + rw [weightMod, foldl_mul_mod, Nat.one_mul] + +/-- **Direct summation computes the contraction modulo `2 ^ P`**, with `2 ^ m (k + 1) + 1` +nodes for `m` edges and `k` vertices, and at most two open calls. -/ +theorem eval_sumRoot (P : ℕ) (d : DescribedNetwork T) : + ∃ h ≤ 2, (sumScheme eval).Eval (sumInvariant eval (P, d)) (sumRoot (P, d)) + (d.contraction eval % 2 ^ P) (2 ^ d.numEdges * (d.vertices.length + 1) + 1) h := by + set acc : ℕ → ℕ × ℕ := fun c => (c, (∑ j ∈ range c, weightMod eval P d (lowBits d.numEdges j)) % + 2 ^ P) + have hassign (c : ℕ) := eval_assign (eval := eval) P d (length_lowBits d.numEdges c) + choose hgt hhgt heval using hassign + have hshape : NodeShape (P, d) (sumRoot (P, d)) := ⟨rfl, by simp [sumRoot], by simp [sumRoot], + by simp [sumRoot]⟩ + obtain ⟨h, hh, hloop⟩ := Scheme.Loop.counted (R := sumScheme eval) + (I := sumInvariant eval (P, d)) + (x := sumRoot (P, d)) (acc := acc) + (child := fun c => (1, ((P, d), (lowBits d.numEdges c, 0)))) + (val := fun c => weightMod eval P d (lowBits d.numEdges c)) + (nodes := fun _ => d.vertices.length + 1) (height := hgt) (2 ^ d.numEdges) 1 + (fun c hc => by simp [sumScheme, sumRoot, acc, hc]) + (by simp [sumScheme, sumRoot, acc]) + (fun c hc => by simp [sumScheme, sumRoot, acc, sum_range_succ, Nat.add_mod]) + (fun c _ => heval c) + (fun c hc => ⟨hshape, by simp only [acc]; omega, Nat.mod_lt _ (Nat.two_pow_pos P)⟩) + (fun c _ => hhgt c) + have hstart : (sumScheme eval).start (sumRoot (P, d)) = acc 0 := by + simp [sumScheme, sumRoot, acc] + rw [← hstart] at hloop + refine ⟨h + 1, by omega, ?_⟩ + have := Scheme.Eval.call (R := sumScheme eval) (I := sumInvariant eval (P, d)) + (x := sumRoot (P, d)) hshape rfl hloop (.inl (Nat.mod_lt _ (Nat.two_pow_pos P))) + have hval : (sumScheme eval).finish (sumRoot (P, d)) (acc (2 ^ d.numEdges)) = + d.contraction eval % 2 ^ P := by + simp only [sumScheme, acc, contraction] + rw [sum_lowBits d.numEdges (weightMod eval P d)] + simp only [weightMod_eq] + exact (Finset.sum_nat_mod _ _ _).symm + rw [hval, sum_const, card_range, smul_eq_mul] at this + exact this + +/-! ### The native machine -/ + +section Native + +open Turing.MultiTapeTM + +variable (encT : T ↪ List Bool) + +/-- A described network is its number of edges and its list of vertices. -/ +def equivProd : DescribedNetwork T ≃ ℕ × List (List ℕ × T) where + toFun d := (d.numEdges, d.vertices) + invFun p := ⟨p.1, p.2⟩ + left_inv _ := rfl + right_inv _ := rfl + +/-- **The encoding of a described network**: its number of edges in unary, then its vertices, +each the pair of its list of ports in binary and its descriptor. -/ +def encDescribed : DescribedNetwork T ↪ List Bool := + encOfEquiv equivProd (encPair encUnary (encList (encPair (encList encNat) encT))) + +/-- The encoding of an input `(P, d)`: the precision in unary and the network. -/ +abbrev encInput : ℕ × DescribedNetwork T ↪ List Bool := encPair encUnary (encDescribed encT) + +/-- The encoding of a node: its kind in unary, its input, its word and its vertex index in +unary. -/ +abbrev encNode : Node T ↪ List Bool := + encPair encUnary (encPair (encInput encT) (encPair encBits encUnary)) + +/-- The encoding of an accumulator: the counter and the residue, in binary. -/ +abbrev encAcc : ℕ × ℕ ↪ List Bool := encPair encNat encNat + +variable {encT} + +/-- The number of edges of a described network, in unary. -/ +theorem numEdges_polyTime : + ComputableInPolyTime (fun d : DescribedNetwork T => d.numEdges) (encDescribed encT) + encUnary := + (ComputableInPolyTime.fst _ _).comp (ComputableInPolyTime.of_eq (f := equivProd) fun _ => rfl) + +/-- The vertices of a described network. -/ +theorem vertices_polyTime : + ComputableInPolyTime (fun d : DescribedNetwork T => d.vertices) (encDescribed encT) + (encList (encPair (encList encNat) encT)) := + (ComputableInPolyTime.snd _ _).comp (ComputableInPolyTime.of_eq (f := equivProd) fun _ => rfl) + +/-- The kind of a node. -/ +theorem kind_polyTime : ComputableInPolyTime (fun x : Node T => x.1) (encNode encT) + encUnary := ComputableInPolyTime.fst _ _ + +/-- The input `(P, d)` of a node. -/ +theorem input_polyTime : ComputableInPolyTime (fun x : Node T => x.2.1) (encNode encT) + (encInput encT) := (ComputableInPolyTime.fst _ _).comp (ComputableInPolyTime.snd _ _) + +/-- The precision of a node. -/ +theorem precision_polyTime : ComputableInPolyTime (fun x : Node T => x.2.1.1) + (encNode encT) encUnary := (ComputableInPolyTime.fst _ _).comp input_polyTime + +/-- The network of a node. -/ +theorem network_polyTime : ComputableInPolyTime (fun x : Node T => x.2.1.2) + (encNode encT) (encDescribed encT) := (ComputableInPolyTime.snd _ _).comp input_polyTime + +/-- The word of a node. -/ +theorem word_polyTime : ComputableInPolyTime (fun x : Node T => x.2.2.1) + (encNode encT) encBits := + (ComputableInPolyTime.fst _ _).comp (ComputableInPolyTime.snd _ _).snd' + +/-- The vertex index of a node. -/ +theorem index_polyTime : ComputableInPolyTime (fun x : Node T => x.2.2.2) + (encNode encT) encUnary := + (ComputableInPolyTime.snd _ _).comp (ComputableInPolyTime.snd _ _).snd' + +/-- `2 ^ P`, from `P` in unary. -/ +private theorem twoPow {α : Type} {encIn : α ↪ List Bool} {P : α → ℕ} + (hP : ComputableInPolyTime P encIn encUnary) : + ComputableInPolyTime (fun a => 2 ^ P a) encIn encNat := + ComputableInPolyTime.binary_two_pow.comp hP + +/-- `x % 2 ^ P`, from `P` in unary. -/ +private theorem modTwoPow {α : Type} {encIn : α ↪ List Bool} {x P : α → ℕ} + (hx : ComputableInPolyTime x encIn encNat) (hP : ComputableInPolyTime P encIn encUnary) : + ComputableInPolyTime (fun a => x a % 2 ^ P a) encIn encNat := + ComputableInPolyTime.binary_mod.comp (hx.prod (twoPow hP)) + +/-- **The polynomial-time components of direct summation.** -/ +theorem sumScheme_polyTime : (sumScheme eval).PolyTime (encNode encT) encAcc encNat where + isLeaf := ComputableInPolyTime.beq kind_polyTime (ComputableInPolyTime.const 2) + start := by + refine (ComputableInPolyTime.const 0).prod (modTwoPow ?_ precision_polyTime) + refine (ComputableInPolyTime.ite (encMid := encNat) (ComputableInPolyTime.beq kind_polyTime + (ComputableInPolyTime.const 0)) (ComputableInPolyTime.const 0) + (ComputableInPolyTime.const 1)).of_enc_eq fun x => ?_ + by_cases h : x.1 = 0 <;> simp [h] + next := by + have hx := ComputableInPolyTime.fst (encNode encT) encAcc + have ha := ComputableInPolyTime.snd (encNode encT) encAcc + have hk := kind_polyTime.comp hx + have hc := (ComputableInPolyTime.fst encNat encNat).comp ha + have hq := input_polyTime.comp hx + have hd := network_polyTime.comp hx + have hm := numEdges_polyTime.comp hd + have hlen := (ComputableInPolyTime.length_list _).comp (vertices_polyTime.comp hd) + have hassign : ComputableInPolyTime (fun p : Node T × (ℕ × ℕ) => + (if p.2.1 < 2 ^ p.1.2.1.2.numEdges then + some ((1, (p.1.2.1, (lowBits p.1.2.1.2.numEdges p.2.1, 0))) : Node T) else none)) + (encPair (encNode encT) encAcc) (encOption (encNode encT)) := + (ComputableInPolyTime.ite (ComputableInPolyTime.binary_lt' hc (twoPow hm)) + ((ComputableInPolyTime.optionSome (encNode encT)).comp + ((ComputableInPolyTime.const (encOut := encUnary) 1).prod + (hq.prod ((BooleanFold.decoder_polyTime.comp (hm.prod hc)).prod + (ComputableInPolyTime.const (encOut := encUnary) 0))))) + (ComputableInPolyTime.const none)).of_enc_eq fun p => by + by_cases h : p.2.1 < 2 ^ p.1.2.1.2.numEdges <;> simp [h] + have hentry : ComputableInPolyTime (fun p : Node T × (ℕ × ℕ) => + (if p.2.1 < p.1.2.1.2.vertices.length then + some ((2, (p.1.2.1, (p.1.2.2.1, min p.1.2.1.2.vertices.length p.2.1))) : Node T) + else none)) + (encPair (encNode encT) encAcc) (encOption (encNode encT)) := + (ComputableInPolyTime.ite (ComputableInPolyTime.binary_lt' hc + (ComputableInPolyTime.unary_to_binary.comp hlen)) + ((ComputableInPolyTime.optionSome (encNode encT)).comp + ((ComputableInPolyTime.const (encOut := encUnary) 2).prod + (hq.prod ((word_polyTime.comp hx).prod + (ComputableInPolyTime.binary_min_unary' hlen hc))))) + (ComputableInPolyTime.const none)).of_enc_eq fun p => by + by_cases h : p.2.1 < p.1.2.1.2.vertices.length <;> simp [h] + refine (ComputableInPolyTime.ite (ComputableInPolyTime.beq hk (ComputableInPolyTime.const 0)) + hassign (ComputableInPolyTime.ite (ComputableInPolyTime.beq hk + (ComputableInPolyTime.const 1)) hentry (ComputableInPolyTime.const none))).of_enc_eq + fun p => ?_ + by_cases h0 : p.1.1 = 0 + · simp [sumScheme, h0] + · by_cases h1 : p.1.1 = 1 <;> simp [sumScheme, h0, h1] + absorb := by + have hx := (ComputableInPolyTime.fst (encNode encT) encAcc).comp + (ComputableInPolyTime.fst (encPair (encNode encT) encAcc) encNat) + have ha := (ComputableInPolyTime.snd (encNode encT) encAcc).comp + (ComputableInPolyTime.fst (encPair (encNode encT) encAcc) encNat) + have hy := ComputableInPolyTime.snd (encPair (encNode encT) encAcc) encNat + have hc := (ComputableInPolyTime.fst encNat encNat).comp ha + have hr := (ComputableInPolyTime.snd encNat encNat).comp ha + have hP := precision_polyTime.comp hx + refine (ComputableInPolyTime.binary_add.comp (hc.prod (ComputableInPolyTime.const 1))).prod + ((ComputableInPolyTime.ite (ComputableInPolyTime.beq (kind_polyTime.comp hx) + (ComputableInPolyTime.const 0)) + (modTwoPow (ComputableInPolyTime.binary_add.comp (hr.prod hy)) hP) + (modTwoPow (ComputableInPolyTime.binary_mul' hr hy) hP)).of_enc_eq fun q => ?_) + by_cases h : q.1.1.1 = 0 <;> simp [h] + finish := (ComputableInPolyTime.snd encNat encNat).comp (ComputableInPolyTime.snd _ _) + +/-- The descriptor of a vertex and the bits of the node's word on its ports. -/ +theorem query_polyTime : + ComputableInPolyTime (fun p : Node T × (List ℕ × T) => (p.2.2, wordBits p.1.2.2.1 p.2.1)) + (encPair (encNode encT) (encPair (encList encNat) encT)) (encPair encT encBits) := by + have hv := ComputableInPolyTime.snd (encNode encT) (encPair (encList encNat) encT) + have hports := (ComputableInPolyTime.fst (encList encNat) encT).comp hv + have hword := word_polyTime.comp (ComputableInPolyTime.fst (encNode encT) + (encPair (encList encNat) encT)) + let E := encPair (encNode encT) (encPair (encList encNat) encT) + have hbit : ComputableInPolyTime (fun z : (Node T × (List ℕ × T)) × ℕ => + z.1.1.2.2.1.getD z.2 false) (encPair E encNat) encBool := + ComputableInPolyTime.listGetD_binary (encA := encBool) (encL := encBits) false + (hword.comp (ComputableInPolyTime.fst E encNat)) (ComputableInPolyTime.snd E encNat) + have hbits := ComputableInPolyTime.listToBits.comp + (ComputableInPolyTime.mapWithInput (f := fun p i => p.1.2.2.1.getD i false) hports hbit) + exact ((ComputableInPolyTime.snd (encList encNat) encT).comp hv).prod hbits + +/-- The vertex of a node, if its index is in range. -/ +noncomputable def vertexCertificate : + NativeCertificate (fun x : Node T => x.2.1.2.vertices[x.2.2.2]?) (encNode encT) + (encOption (encPair (encList encNat) encT)) := + NativeCertificate.ofPolyTime (ComputableInPolyTime.getElem? + (vertices_polyTime.comp network_polyTime) index_polyTime) + +/-- The query to the evaluator: the descriptor and the bits on the ports. -/ +noncomputable def queryCertificate : + NativeCertificate (fun p : Node T × (List ℕ × T) => (p.2.2, wordBits p.1.2.2.1 p.2.1)) + (encPair (encNode encT) (encPair (encList encNat) encT)) (encPair encT encBits) := + NativeCertificate.ofPolyTime query_polyTime + +variable (eval) in +/-- **The leaf step**: the evaluator at the descriptor of the node's vertex and its bits, from a +native certificate of the evaluator with any bounds. -/ +noncomputable def leafCertificate + (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) + encNat) : + NativeCertificate (sumScheme eval).leaf (encNode encT) encNat := + NativeCertificate.option (value := fun x : Node T => x.2.1.2.vertices[x.2.2.2]?) + (fallback := fun _ => 0) (body := fun p => eval p.2.2 (wordBits p.1.2.2.1 p.2.1)) + vertexCertificate (NativeCertificate.const 0) (ev.comp queryCertificate) + +/-- The time of the leaf step: the vertex lookup, and the query and the evaluator at a vertex. -/ +theorem leafCertificate_time + (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) + encNat) (x : Node T) : + (leafCertificate eval ev).time x = 2 * (vertexCertificate (encT := encT)).time x + + (encNode encT x).length + + (x.2.1.2.vertices[x.2.2.2]?).elim (encNode encT x).length + fun v => (queryCertificate (encT := encT)).time (x, v) + + ev.time (v.2, wordBits x.2.2.1 v.1) := rfl + +/-- The space of the leaf step. -/ +theorem leafCertificate_space + (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) + encNat) (x : Node T) : + (leafCertificate eval ev).space x = 2 * (vertexCertificate (encT := encT)).space x + + (encNode encT x).length + + (x.2.1.2.vertices[x.2.2.2]?).elim (encNode encT x).length + fun v => (queryCertificate (encT := encT)).space (x, v) + + ev.space (v.2, wordBits x.2.2.1 v.1) := rfl + +/-- The number of steps of direct summation: `2 (2 ^ m (k + 1) + 1) - 1`. -/ +def fuel (q : ℕ × DescribedNetwork T) : ℕ := + 2 * (2 ^ q.2.numEdges * (q.2.vertices.length + 1)) + 1 + +/-- The fuel in polynomial time. -/ +theorem fuel_polyTime : ComputableInPolyTime (fuel (T := T)) (encInput encT) encNat := by + have hd := ComputableInPolyTime.snd encUnary (encDescribed encT) + have hm := numEdges_polyTime.comp hd + have hlen := (ComputableInPolyTime.length_list _).comp (vertices_polyTime.comp hd) + exact ComputableInPolyTime.binary_add.comp ((ComputableInPolyTime.binary_mul' + (ComputableInPolyTime.const 2) (ComputableInPolyTime.binary_mul' (twoPow hm) + (ComputableInPolyTime.unary_to_binary.comp (ComputableInPolyTime.unary_add' hlen + (ComputableInPolyTime.const 1))))).prod (ComputableInPolyTime.const 1)) + +/-- The root in polynomial time. -/ +theorem sumRoot_polyTime : ComputableInPolyTime (sumRoot (T := T)) (encInput encT) + (encNode encT) := + (ComputableInPolyTime.const 0).prod (ComputableInPolyTime.id'.prod + (ComputableInPolyTime.const ([], 0))) + +variable (eval) in +/-- **One native machine for direct summation over described networks**, from a native +certificate of the evaluator. It returns `some (d.contraction eval % 2 ^ P)` +(`run_sumScheme`). -/ +noncomputable def sumCertificate + (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) + encNat) : + NativeCertificate ((sumScheme eval).run fuel sumRoot) (encInput encT) (encOption encNat) := + Scheme.certificate sumScheme_polyTime (leafCertificate eval ev) + (NativeCertificate.ofPolyTime fuel_polyTime) (NativeCertificate.ofPolyTime sumRoot_polyTime) + +/-- **The machine computes the contraction modulo `2 ^ P`.** -/ +theorem run_sumScheme (q : ℕ × DescribedNetwork T) : + (sumScheme eval).run fuel sumRoot q = some (q.2.contraction eval % 2 ^ q.1) := by + obtain ⟨h, -, he⟩ := eval_sumRoot (eval := eval) q.1 q.2 + exact Scheme.run_eq_some he (by simp [fuel]; omega) + +variable (eval) in +/-- **Direct summation over described networks, as a native certificate** for the contraction +modulo `2 ^ P` on every input `(P, d)`, from a native certificate of the evaluator. -/ +noncomputable def contractionCertificate + (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) + encNat) : + NativeCertificate (fun q : ℕ × DescribedNetwork T => q.2.contraction eval % 2 ^ q.1) + (encInput encT) encNat := + (NativeCertificate.post (sumCertificate eval ev) + (ComputableInDegree.getD (enc₁ := encNat) (enc := encOption encNat) 0).inO_one).congr + fun q => by simp [run_sumScheme] + +/-- The time is that of the stack machine. -/ +@[simp] theorem contractionCertificate_time + (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) + encNat) : + (contractionCertificate eval ev).time = (sumCertificate eval ev).time := rfl + +/-- The space is that of the stack machine. -/ +@[simp] theorem contractionCertificate_space + (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) + encNat) : + (contractionCertificate eval ev).space = (sumCertificate eval ev).space := rfl + +/-! ### Resources -/ + +/-- The number of edges and the number of vertices are at most the encoded length. -/ +theorem length_numEdges_vertices_le (d : DescribedNetwork T) : + d.numEdges + d.vertices.length ≤ (encDescribed encT d).length := by + have h := length_le_length_encList (encPair (encList encNat) encT) d.vertices + change d.numEdges + d.vertices.length ≤ + (encPair encUnary (encList (encPair (encList encNat) encT)) (d.numEdges, d.vertices)).length + simp only [length_encPair, encUnary_apply, List.length_replicate] + omega + +/-- A node of the right shape is at most `5 n + 7` long, for `n` the length of the input. -/ +theorem length_encNode_le {q : ℕ × DescribedNetwork T} {x : Node T} (hx : NodeShape q x) : + (encNode encT x).length ≤ 5 * (encInput encT q).length + 7 := by + obtain ⟨hq, hk, hw, hv⟩ := hx + obtain ⟨k, ⟨P, d⟩, w, v⟩ := x + simp only at hq hk hw hv + subst hq + dsimp only at hw hv + have hd := length_numEdges_vertices_le (encT := encT) d + simp only [length_encPair, encUnary_apply, List.length_replicate, encBits_apply] + omega + +private theorem term_le {c a b d D z : ℕ} (hd : d ≤ D) : + c * (a * z + b) ^ d ≤ c * (a + b) ^ d * (z + 1) ^ D := + calc c * (a * z + b) ^ d ≤ c * ((a + b) * (z + 1)) ^ d := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_left (by nlinarith) _) + _ = c * (a + b) ^ d * (z + 1) ^ d := by rw [Nat.mul_pow]; ring + _ ≤ c * (a + b) ^ d * (z + 1) ^ D := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_right (Nat.succ_pos _) hd) + +private theorem length_encNat_lt {y P : ℕ} (h : y < 2 ^ P) : (encNat y).length ≤ P := by + have h' := Nat.size_le.mpr h + rw [← Nat.size_eq_bits_len] at h' + exact h' + +/-- **The resources of direct summation over described networks.** There is `K` such that, for +an input `(P, d)` of length `n` with `m` edges and `k` vertices, if the evaluator takes time at +most `E` and space at most `ES` and outputs at most `Ev` bits on every vertex and every list of +bits on its ports, then the time is at most `2 ^ m (k + 1) + 1` times +`K (n + Ev + 1) ^ K + 2 E`, and the space at most `K (n + Ev + 1) ^ K + ES`. -/ +theorem contractionCertificate_le + (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) + encNat) : + ∃ K : ℕ, ∀ (q : ℕ × DescribedNetwork T) (E ES Ev : ℕ), + (∀ v ∈ q.2.vertices, ∀ bs : List Bool, bs.length = v.1.length → + ev.time (v.2, bs) ≤ E ∧ ev.space (v.2, bs) ≤ ES ∧ (encNat (eval v.2 bs)).length ≤ Ev) → + (contractionCertificate eval ev).time q ≤ + (2 ^ q.2.numEdges * (q.2.vertices.length + 1) + 1) * + (K * ((encInput encT q).length + Ev + 1) ^ K + 2 * E) ∧ + (contractionCertificate eval ev).space q ≤ + K * ((encInput encT q).length + Ev + 1) ^ K + ES := by + obtain ⟨K₀, hK₀⟩ := Scheme.certificate_le (sumScheme_polyTime (eval := eval) (encT := encT)) + (leafCertificate eval ev) + have hV : (vertexCertificate (encT := encT)).IsPolynomial := + NativeCertificate.ofPolyTime_isPolynomial _ + have hQ : (queryCertificate (encT := encT)).IsPolynomial := + NativeCertificate.ofPolyTime_isPolynomial _ + have hF := NativeCertificate.ofPolyTime_isPolynomial (fuel_polyTime (encT := encT)) + have hR := NativeCertificate.ofPolyTime_isPolynomial (sumRoot_polyTime (encT := encT)) + obtain ⟨cv, dv, hvt⟩ := hV.1.exists_le + obtain ⟨cv', dv', hvs⟩ := hV.2.exists_le + obtain ⟨cq, dq, hqt⟩ := hQ.1.exists_le + obtain ⟨cq', dq', hqs⟩ := hQ.2.exists_le + obtain ⟨cf, df, hft⟩ := hF.1.exists_le + obtain ⟨cf', df', hfs⟩ := hF.2.exists_le + obtain ⟨cr, dr, hrt⟩ := hR.1.exists_le + obtain ⟨cr', dr', hrs⟩ := hR.2.exists_le + obtain ⟨D, hD⟩ : ∃ D, D = K₀ + dv + dv' + dq + dq' + df + df' + dr + dr' + 2 := ⟨_, rfl⟩ + obtain ⟨G, hG⟩ : ∃ G, G = K₀ * 96 ^ K₀ + (2 * cv * 13 ^ dv + 24 + cq * 27 ^ dq) + 16 := + ⟨_, rfl⟩ + obtain ⟨G', hG'⟩ : ∃ G', G' = K₀ * 96 ^ K₀ + (2 * cv' * 13 ^ dv' + 24 + cq' * 27 ^ dq') := + ⟨_, rfl⟩ + obtain ⟨C, hC⟩ : ∃ C, C = cf + cr + cf' + cr' + 2 * G + G' + 4 := ⟨_, rfl⟩ + refine ⟨C + D, fun q E ES Ev hE => ?_⟩ + obtain ⟨P, d⟩ := q + obtain ⟨n, hn⟩ : ∃ n, n = (encInput encT (P, d)).length := ⟨_, rfl⟩ + obtain ⟨z, hz⟩ : ∃ z, z = n + Ev := ⟨_, rfl⟩ + obtain ⟨W, hW⟩ : ∃ W, W = (z + 1) ^ D := ⟨_, rfl⟩ + rw [← hn] + rw [show n + Ev + 1 = z + 1 by omega] + have hnP : 2 * P + 1 + (encDescribed encT d).length = n := by + rw [hn]; simp [encInput] + have hmk := length_numEdges_vertices_le (encT := encT) d + -- Every vertex is at most `n` long. + have hvlen (v : List ℕ × T) (hv : v ∈ d.vertices) : + (encPair (encList encNat) encT v).length ≤ n := by + have h1 := List.le_sum_of_mem (List.mem_map_of_mem (f := fun a => + (encPair (encList encNat) encT a).length) hv) + have h2 := sum_length_le_length_encList (encPair (encList encNat) encT) d.vertices + have h3 : (encList (encPair (encList encNat) encT) d.vertices).length ≤ + (encDescribed encT d).length := by + change _ ≤ (encPair encUnary (encList (encPair (encList encNat) encT)) + (d.numEdges, d.vertices)).length + simp only [length_encPair]; omega + omega + have hnode (x : Node T) (hx : (sumInvariant eval (P, d)).node x) : + (encNode encT x).length ≤ 5 * z + 7 := (length_encNode_le hx).trans (by omega) + have hleafT (x : Node T) (hx : (sumInvariant eval (P, d)).node x) : + (leafCertificate eval ev).time x ≤ + 2 * cv * (5 * z + 8) ^ dv + 2 * (5 * z + 7) + cq * (11 * z + 16) ^ dq + E := by + have h1 := hnode x hx + rw [leafCertificate_time] + have h2 : (vertexCertificate (encT := encT)).time x ≤ cv * (5 * z + 8) ^ dv := + (hvt x).trans (Nat.mul_le_mul_left _ (Nat.pow_le_pow_left (by omega) _)) + have h2' : 2 * (vertexCertificate (encT := encT)).time x ≤ 2 * cv * (5 * z + 8) ^ dv := by + rw [Nat.mul_assoc]; exact Nat.mul_le_mul_left 2 h2 + cases hvx : x.2.1.2.vertices[x.2.2.2]? with + | none => simp only [Option.elim]; omega + | some v => + simp only [Option.elim] + have hmem : v ∈ x.2.1.2.vertices := List.mem_of_getElem? hvx + obtain ⟨hq, -, -, -⟩ := hx + rw [hq] at hmem + have h3 := hvlen v hmem + have h4 : (queryCertificate (encT := encT)).time (x, v) ≤ cq * (11 * z + 16) ^ dq := by + refine (hqt (x, v)).trans (Nat.mul_le_mul_left _ (Nat.pow_le_pow_left ?_ _)) + have := length_encPair (enc₁ := encNode encT) (enc₂ := encPair (encList encNat) encT) + (x, v) + dsimp only at this + omega + have h5 := (hE v hmem (wordBits x.2.2.1 v.1) (by simp [wordBits])).1 + omega + have hleafS (x : Node T) (hx : (sumInvariant eval (P, d)).node x) : + (leafCertificate eval ev).space x ≤ + 2 * cv' * (5 * z + 8) ^ dv' + 2 * (5 * z + 7) + cq' * (11 * z + 16) ^ dq' + ES := by + have h1 := hnode x hx + rw [leafCertificate_space] + have h2 : (vertexCertificate (encT := encT)).space x ≤ cv' * (5 * z + 8) ^ dv' := + (hvs x).trans (Nat.mul_le_mul_left _ (Nat.pow_le_pow_left (by omega) _)) + have h2' : 2 * (vertexCertificate (encT := encT)).space x ≤ 2 * cv' * (5 * z + 8) ^ dv' := by + rw [Nat.mul_assoc]; exact Nat.mul_le_mul_left 2 h2 + cases hvx : x.2.1.2.vertices[x.2.2.2]? with + | none => simp only [Option.elim]; omega + | some v => + simp only [Option.elim] + have hmem : v ∈ x.2.1.2.vertices := List.mem_of_getElem? hvx + obtain ⟨hq, -, -, -⟩ := hx + rw [hq] at hmem + have h3 := hvlen v hmem + have h4 : (queryCertificate (encT := encT)).space (x, v) ≤ cq' * (11 * z + 16) ^ dq' := by + refine (hqs (x, v)).trans (Nat.mul_le_mul_left _ (Nat.pow_le_pow_left ?_ _)) + have := length_encPair (enc₁ := encNode encT) (enc₂ := encPair (encList encNat) encT) + (x, v) + dsimp only at this + omega + have h5 := (hE v hmem (wordBits x.2.2.1 v.1) (by simp [wordBits])).2.1 + omega + obtain ⟨h, hh, he⟩ := eval_sumRoot (eval := eval) P d + have hres := hK₀ (NativeCertificate.ofPolyTime fuel_polyTime) + (NativeCertificate.ofPolyTime sumRoot_polyTime) (sumInvariant eval (P, d)) (13 * z + 18) + (2 * cv * (5 * z + 8) ^ dv + 2 * (5 * z + 7) + cq * (11 * z + 16) ^ dq + E) + (2 * cv' * (5 * z + 8) ^ dv' + 2 * (5 * z + 7) + cq' * (11 * z + 16) ^ dq' + ES) + (fun x hx => ⟨(hnode x hx).trans (by omega), fun _ => ⟨hleafT x hx, hleafS x hx⟩⟩) + (fun x a ha => by + obtain ⟨hx, ha1, ha2⟩ := ha + have h1 := hnode x hx + have h2 : (encNat a.1).length ≤ d.numEdges + d.vertices.length + 1 := by + refine length_encNat_lt (lt_of_le_of_lt ha1 ?_) + calc 2 ^ d.numEdges + d.vertices.length + < 2 ^ d.numEdges + 2 ^ d.vertices.length := by + have := Nat.lt_two_pow_self (n := d.vertices.length); omega + _ ≤ 2 ^ (d.numEdges + d.vertices.length + 1) := by + rw [pow_succ, pow_add, Nat.mul_two] + exact Nat.add_le_add (Nat.le_mul_of_pos_right _ (Nat.two_pow_pos _)) + (Nat.le_mul_of_pos_left _ (Nat.two_pow_pos _)) + have h3 := length_encNat_lt ha2 + have h4 : (encPair (encNode encT) encAcc (x, a)).length = + 2 * (encNode encT x).length + 1 + (encAcc a).length := length_encPair (x, a) + have h5 : (encAcc a).length = 2 * (encNat a.1).length + 1 + (encNat a.2).length := + length_encPair a + omega) + (fun y hy => by + rcases hy with hy | ⟨v, hv, w, -, rfl⟩ + · have := length_encNat_lt hy; omega + · have := (hE v hv (wordBits w v.1) (by simp [wordBits])).2.2; omega) + (P, d) _ _ _ he + rw [← hn] at hres + -- The clock and the size of the fuel. + have hfuel : fuel (P, d) < 2 ^ (d.numEdges + d.vertices.length + 2) := by + have hk : d.vertices.length + 1 ≤ 2 ^ d.vertices.length := Nat.lt_two_pow_self + have h1 : 2 ^ d.numEdges * (d.vertices.length + 1) ≤ 2 ^ (d.numEdges + d.vertices.length) := + by rw [pow_add]; exact Nat.mul_le_mul_left _ hk + have h2 : 2 ^ (d.numEdges + d.vertices.length + 2) = 4 * 2 ^ (d.numEdges + d.vertices.length) := + by rw [pow_add]; ring + have h3 := Nat.two_pow_pos (d.numEdges + d.vertices.length) + simp only [fuel] + omega + have hfl := length_encNat_lt hfuel + obtain ⟨B, hB⟩ : ∃ B, B = 2 ^ d.numEdges * (d.vertices.length + 1) + 1 := ⟨_, rfl⟩ + have hfuel1 : fuel (P, d) + 1 = 2 * B := by rw [hB]; simp only [fuel]; ring + have hB1 : 1 ≤ B := by omega + rw [← hB] + -- The polynomial parts, each at most a constant times `W`. + have hW1 : z + 1 ≤ W := by + rw [hW] + calc z + 1 = (z + 1) ^ 1 := (pow_one _).symm + _ ≤ (z + 1) ^ D := Nat.pow_le_pow_right (Nat.succ_pos _) (by omega) + have hpoly {c e : ℕ} (he : e ≤ D) : c * (n + 1) ^ e ≤ c * W := by + rw [hW] + exact Nat.mul_le_mul_left _ ((Nat.pow_le_pow_left (by omega) _).trans + (Nat.pow_le_pow_right (Nat.succ_pos _) he)) + have hterm {c a b e : ℕ} (he : e ≤ D) : c * (a * z + b) ^ e ≤ c * (a + b) ^ e * W := by + rw [hW]; exact term_le he + have hT1 : K₀ * ((h + 1) * (13 * z + 18 + 1)) ^ K₀ ≤ K₀ * 96 ^ K₀ * W := by + have h3 : (h + 1) * (13 * z + 18 + 1) ≤ 39 * z + 57 := by + have := Nat.mul_le_mul_right (13 * z + 18 + 1) (show h + 1 ≤ 3 by omega); omega + exact (Nat.mul_le_mul_left _ (Nat.pow_le_pow_left h3 _)).trans (hterm (by omega)) + have hTL : 2 * cv * (5 * z + 8) ^ dv + 2 * (5 * z + 7) + cq * (11 * z + 16) ^ dq ≤ + (2 * cv * 13 ^ dv + 24 + cq * 27 ^ dq) * W := by + have a1 := hterm (c := 2 * cv) (a := 5) (b := 8) (show dv ≤ D by omega) + have a2 := hterm (c := cq) (a := 11) (b := 16) (show dq ≤ D by omega) + have e : (2 * cv * 13 ^ dv + 24 + cq * 27 ^ dq) * W = + 2 * cv * (5 + 8) ^ dv * W + 24 * W + cq * (11 + 16) ^ dq * W := by ring + omega + have hSL : 2 * cv' * (5 * z + 8) ^ dv' + 2 * (5 * z + 7) + cq' * (11 * z + 16) ^ dq' ≤ + (2 * cv' * 13 ^ dv' + 24 + cq' * 27 ^ dq') * W := by + have a1 := hterm (c := 2 * cv') (a := 5) (b := 8) (show dv' ≤ D by omega) + have a2 := hterm (c := cq') (a := 11) (b := 16) (show dq' ≤ D by omega) + have e : (2 * cv' * 13 ^ dv' + 24 + cq' * 27 ^ dq') * W = + 2 * cv' * (5 + 8) ^ dv' * W + 24 * W + cq' * (11 + 16) ^ dq' * W := by ring + omega + have hclock : ((encNat (fuel (P, d))).length + 1) ^ 2 ≤ 16 * W := by + have h3 : (encNat (fuel (P, d))).length + 1 ≤ 4 * (z + 1) := by omega + calc ((encNat (fuel (P, d))).length + 1) ^ 2 ≤ (4 * (z + 1)) ^ 2 := Nat.pow_le_pow_left h3 2 + _ = 16 * (z + 1) ^ 2 := by ring + _ ≤ 16 * W := by + rw [hW]; exact Nat.mul_le_mul_left _ (Nat.pow_le_pow_right (Nat.succ_pos _) (by omega)) + have hCD : C * W ≤ (C + D) * (z + 1) ^ (C + D) := by + rw [hW]; exact Nat.mul_le_mul (by omega) (Nat.pow_le_pow_right (Nat.succ_pos _) (by omega)) + obtain ⟨htime, hspace⟩ := hres + have hft' := hft (P, d) + have hrt' := hrt (P, d) + have hfs' := hfs (P, d) + have hrs' := hrs (P, d) + rw [← hn] at hft' hrt' hfs' hrs' + have hcf := hft'.trans (hpoly (c := cf) (show df ≤ D by omega)) + have hcr := hrt'.trans (hpoly (c := cr) (show dr ≤ D by omega)) + have hcf' := hfs'.trans (hpoly (c := cf') (show df' ≤ D by omega)) + have hcr' := hrs'.trans (hpoly (c := cr') (show dr' ≤ D by omega)) + constructor + · rw [hfuel1] at htime + obtain ⟨S, hS⟩ : ∃ S, S = K₀ * ((h + 1) * (13 * z + 18 + 1)) ^ K₀ + + (2 * cv * (5 * z + 8) ^ dv + 2 * (5 * z + 7) + cq * (11 * z + 16) ^ dq + E) + + ((encNat (fuel (P, d))).length + 1) ^ 2 := ⟨_, rfl⟩ + rw [← hS] at htime + have hstep : S ≤ G * W + E := by + have e : G * W = K₀ * 96 ^ K₀ * W + (2 * cv * 13 ^ dv + 24 + cq * 27 ^ dq) * W + 16 * W := by + rw [hG]; ring + omega + have hm := Nat.mul_le_mul_left (2 * B) hstep + calc (contractionCertificate eval ev).time (P, d) + ≤ cf * W + cr * W + 2 * W + 2 * B * (G * W + E) := by + rw [contractionCertificate_time]; unfold sumCertificate; omega + _ ≤ B * ((cf + cr + 2) * W) + B * (2 * G * W) + 2 * B * E := by + have e2 : 2 * B * (G * W + E) = B * (2 * G * W) + 2 * B * E := by ring + have e3 : cf * W + cr * W + 2 * W = (cf + cr + 2) * W := by ring + have e4 : (cf + cr + 2) * W ≤ B * ((cf + cr + 2) * W) := Nat.le_mul_of_pos_left _ hB1 + omega + _ = B * ((cf + cr + 2 + 2 * G) * W) + 2 * B * E := by ring + _ ≤ B * (C * W) + 2 * B * E := + Nat.add_le_add_right (Nat.mul_le_mul_left B (Nat.mul_le_mul_right W (by omega))) _ + _ = B * (C * W + 2 * E) := by ring + _ ≤ B * ((C + D) * (z + 1) ^ (C + D) + 2 * E) := + Nat.mul_le_mul_left _ (Nat.add_le_add_right hCD _) + · have h1 : (contractionCertificate eval ev).space (P, d) ≤ C * W + ES := by + rw [contractionCertificate_space] + unfold sumCertificate + have e : C * W = cf * W + cr * W + cf' * W + cr' * W + 2 * G * W + G' * W + 4 * W := by + rw [hC]; ring + have e' : G' * W = K₀ * 96 ^ K₀ * W + (2 * cv' * 13 ^ dv' + 24 + cq' * 27 ^ dq') * W := by + rw [hG']; ring + have hfl' : (encNat (fuel (P, d))).length ≤ W := by omega + omega + exact h1.trans (Nat.add_le_add_right hCD _) + +end Native + +end DescribedNetwork + +end Cslib.Circuits.Frontier diff --git a/ComplexityTests/DescribedContraction.lean b/ComplexityTests/DescribedContraction.lean new file mode 100644 index 0000000000..7581ad62a3 --- /dev/null +++ b/ComplexityTests/DescribedContraction.lean @@ -0,0 +1,79 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.Circuit.Frontier.TensorNetwork.DescribedContraction +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.BitString +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListBinaryLookup +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListPrimitives + +/-! # Direct summation over described networks + +Tables as descriptors, with table lookup as the evaluator: the stack machine on a one-edge +network, the agreement with explicit networks, and one native machine for the contraction with +its resource theorem. -/ + +namespace ComplexityTests.DescribedContraction + +open Cslib.Circuits.Frontier Cslib.Circuits.Frontier.DescribedNetwork Cslib.BitString + Turing.MultiTapeTM + +/-- One edge between two vertices with tables `[2, 3]` and `[5, 7]`: contraction +`2 * 5 + 3 * 7 = 31`. -/ +def oneEdge : DescribedNetwork (List ℕ) := ⟨1, [([0], [2, 3]), ([0], [5, 7])]⟩ + +example : oneEdge.contraction ExplicitTensorNetwork.entry = 31 := by decide + +-- The stack machine, with fuel `2 (2 ^ 1 * 3) + 1 = 13`, returns `31 % 16 = 15`. +example : (sumScheme ExplicitTensorNetwork.entry).run fuel sumRoot (4, oneEdge) = some 15 := by + decide + +-- With one step fewer, the sum still waits for the second assignment's weight +-- `3 * 7 = 21 ≡ 5`. +example : (sumScheme ExplicitTensorNetwork.entry).run (fun _ => 12) sumRoot (4, oneEdge) = + some 5 := by + decide + +-- On every input, the run is the contraction modulo `2 ^ P`, for any evaluator. +example {T : Type} (eval : T → List Bool → ℕ) (q : ℕ × DescribedNetwork T) : + (sumScheme eval).run fuel sumRoot q = some (q.2.contraction eval % 2 ^ q.1) := + run_sumScheme q + +-- Explicit networks are described networks with their tables. +example (d : ExplicitTensorNetwork ℕ) : + (ofExplicit d).contraction ExplicitTensorNetwork.entry = d.contraction := + contraction_ofExplicit d + +/-- Table lookup is polynomial-time. -/ +theorem entry_polyTime : ComputableInPolyTime (fun q : List ℕ × List Bool => + ExplicitTensorNetwork.entry q.1 q.2) (encPair (encList encNat) encBits) encNat := by + have hindex := ComputableInPolyTime.bitsBinaryValue.comp + (ComputableInPolyTime.bitsReverse.comp (ComputableInPolyTime.snd (encList encNat) encBits)) + refine (ComputableInPolyTime.listGetD_binary (encA := encNat) (encL := encList encNat) 0 + (ComputableInPolyTime.fst (encList encNat) encBits) hindex).of_enc_eq fun q => ?_ + simp only [Function.comp_def, ExplicitTensorNetwork.entry, lsbValue_eq_binaryValue_reverse] + +/-- **One native machine contracts explicit networks** given as described networks with their +tables, from the table-lookup certificate. -/ +noncomputable def tableCertificate : + Cslib.GenericRAM.NativeCertificate + (fun q : ℕ × DescribedNetwork (List ℕ) => + q.2.contraction ExplicitTensorNetwork.entry % 2 ^ q.1) + (encInput (encList encNat)) encNat := + contractionCertificate ExplicitTensorNetwork.entry + (Cslib.GenericRAM.NativeCertificate.ofPolyTime entry_polyTime) + +-- The resource theorem applies to it. +example : ∃ K : ℕ, ∀ (q : ℕ × DescribedNetwork (List ℕ)) (E ES Ev : ℕ), + (∀ v ∈ q.2.vertices, ∀ bs : List Bool, bs.length = v.1.length → + (Cslib.GenericRAM.NativeCertificate.ofPolyTime entry_polyTime).time (v.2, bs) ≤ E ∧ + (Cslib.GenericRAM.NativeCertificate.ofPolyTime entry_polyTime).space (v.2, bs) ≤ ES ∧ + (encNat (ExplicitTensorNetwork.entry v.2 bs)).length ≤ Ev) → + tableCertificate.time q ≤ (2 ^ q.2.numEdges * (q.2.vertices.length + 1) + 1) * + (K * ((encInput (encList encNat) q).length + Ev + 1) ^ K + 2 * E) ∧ + tableCertificate.space q ≤ K * ((encInput (encList encNat) q).length + Ev + 1) ^ K + ES := + contractionCertificate_le _ + +end ComplexityTests.DescribedContraction From c1407c7afcd472e9346d40afc65f261bf8976085 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 23:37:40 -0400 Subject: [PATCH 035/129] feat(PolyTime): Newton inversion inverts every unit of a Galois ring GaloisRingArith.inv's correctness assumed that the seed x^(2^e-2) inverts x modulo 2. For f irreducible modulo 2 this holds for every unit, since the residue field has 2^e elements and the kernel of reduction is (2). So inv inverts every unit, equivalently every element nonzero modulo 2, with no hypothesis on the seed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../PolyTime/GaloisRingUnits.lean | 127 ++++++++++++++++++ ComplexityTests/GaloisRingUnits.lean | 51 +++++++ 3 files changed, 179 insertions(+) create mode 100644 Complexity/Computability/PolyTime/GaloisRingUnits.lean create mode 100644 ComplexityTests/GaloisRingUnits.lean diff --git a/Complexity.lean b/Complexity.lean index de719dda8e..6175ed3d3b 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1997,6 +1997,7 @@ public import Complexity.Computability.PolyTime.CSP.ToCNF public import Complexity.Computability.PolyTime.CommonCountingAccum public import Complexity.Computability.PolyTime.FineGrained.ParameterCheck public import Complexity.Computability.PolyTime.GaloisRingArithmetic +public import Complexity.Computability.PolyTime.GaloisRingUnits public import Complexity.Computability.PolyTime.GreedyLabelSchedule public import Complexity.Computability.PolyTime.GreedySlotScore public import Complexity.Computability.PolyTime.Int.ExactDiv diff --git a/Complexity/Computability/PolyTime/GaloisRingUnits.lean b/Complexity/Computability/PolyTime/GaloisRingUnits.lean new file mode 100644 index 0000000000..fb25f4ca24 --- /dev/null +++ b/Complexity/Computability/PolyTime/GaloisRingUnits.lean @@ -0,0 +1,127 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.PolyTime.GaloisRingArithmetic +public import Complexity.Foundations.RingTheory.GaloisRing +import Mathlib.FieldTheory.Finite.Basic + +/-! +# Newton inversion inverts every unit of a Galois ring + +`Complexity/Computability/PolyTime/GaloisRingArithmetic.lean` computes in +`A_p = (ℤ / 2 ^ p)[U] / (f)` on coefficient lists, and inverts by `p` Newton steps from the seed +`x ^ (2 ^ e - 2)`, `e = deg f` (`GaloisRingArith.inv`). Its correctness +(`GaloisRingArith.val_mul_inv`) assumes that the seed inverts `x` modulo `2`. This file discharges +that assumption when `f` is irreducible modulo `2`, so that `A_p` is the Galois ring +`GaloisRing 2 p f`. + +Reduction modulo `2` maps `A_p` onto the field `𝔽_2[U] / (f)` with `2 ^ e` elements, where every +nonzero `r` has `r ^ (2 ^ e - 1) = 1`. A unit `y` reduces to a nonzero element, so +`y y ^ (2 ^ e - 2) - 1` lies in the kernel `(2)` of reduction (`GaloisRing.ker_residue`). The seed +is computed natively (`GaloisRingArith.seed`), so no inversion modulo `2` is needed separately. + +So `GaloisRingArith.inv` inverts every unit, that is every element that is nonzero modulo `2` +(`GaloisRing.isUnit_iff`), in polynomial time (`GaloisRingArith.computableInPolyTime_inv`). This +is the inversion of units by inversion modulo two and Newton lifting of [OpenAI2026ETH] +(Appendix D.4.1, p. 58), with `ℓ = 1`. + +## Main results + +* `Cslib.GaloisRingArith.natDegree_monicOf`: `monicOf fl` has degree `fl.length`. +* `Cslib.GaloisRingArith.exists_mul_pow_eq_one_add_two_mul`: the seed inverts a unit modulo `2`. +* `Cslib.GaloisRingArith.val_mul_inv_of_isUnit`, `Cslib.GaloisRingArith.val_inv_of_isUnit`: + Newton inversion inverts every unit. +* `Cslib.GaloisRingArith.val_mul_inv_of_residue_ne_zero`: it inverts every element that is nonzero + modulo `2`. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Appendix D.4.1 (pp. 57–58). +-/ + +public section + +namespace Cslib.GaloisRingArith + +open Polynomial Finset + +/-- `monicOf fl` has degree `fl.length`. -/ +theorem natDegree_monicOf (fl : List ℕ) : (monicOf fl).natDegree = fl.length := by + unfold monicOf + rw [natDegree_add_eq_left_of_degree_lt, natDegree_X_pow] + rw [degree_X_pow] + refine (degree_sum_le _ _).trans_lt ?_ + rw [Finset.sup_lt_iff (WithBot.bot_lt_coe _)] + intro i hi + exact (degree_C_mul_X_pow_le _ _).trans_lt (WithBot.coe_lt_coe.mpr (mem_range.mp hi)) + +variable {p : ℕ} {fl : List ℕ} + +/-- `A_p` is the Galois ring `GaloisRing 2 p (monicOf fl)`. -/ +theorem ring_eq_galoisRing : Ring p fl = GaloisRing 2 p (monicOf fl) := rfl + +/-- **The seed inverts a unit modulo `2`.** In a Galois ring of rank `e`, the residue field has +`2 ^ e` elements, so a unit `y` satisfies `y y ^ (2 ^ e - 2) = 1 + 2 w`. -/ +theorem exists_mul_pow_eq_one_add_two_mul [NeZero p] + (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2)))) {y : Ring p fl} + (hy : IsUnit y) : ∃ w : Ring p fl, y * y ^ (2 ^ fl.length - 2) = 1 + 2 * w := by + set F := AdjoinRoot ((monicOf fl).map (Int.castRingHom (ZMod 2))) + have : Fact (Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2)))) := ⟨hirr⟩ + have hcard : Nat.card F = 2 ^ fl.length := by + rw [GaloisRing.natCard_adjoinRoot (monic_monicOf fl), natDegree_monicOf] + have : Finite F := Nat.finite_of_card_ne_zero (by rw [hcard]; positivity) + let : Fintype F := Fintype.ofFinite F + have he : 1 ≤ fl.length := by + have := natDegree_pos_iff_degree_pos.mpr (degree_pos_of_irreducible hirr) + rwa [(monic_monicOf fl).natDegree_map, natDegree_monicOf] at this + have h2 : 2 ≤ 2 ^ fl.length := by + calc 2 = 2 ^ 1 := rfl + _ ≤ 2 ^ fl.length := Nat.pow_le_pow_right (by norm_num) he + -- The residue of `y` is nonzero, so its `(2 ^ e - 1)`-th power is `1`. + have hr : GaloisRing.residue 2 p (monicOf fl) y ≠ 0 := + (GaloisRing.isUnit_iff (monic_monicOf fl) hirr).mp hy + have hpow : GaloisRing.residue 2 p (monicOf fl) y ^ (2 ^ fl.length - 1) = 1 := by + have := FiniteField.pow_card_sub_one_eq_one _ hr + rwa [Fintype.card_eq_nat_card, hcard] at this + have hker : y * y ^ (2 ^ fl.length - 2) - 1 ∈ + RingHom.ker (GaloisRing.residue 2 p (monicOf fl)) := by + rw [RingHom.mem_ker, map_sub, map_mul, map_pow, map_one, ← pow_succ', + show 2 ^ fl.length - 2 + 1 = 2 ^ fl.length - 1 by omega, hpow, sub_self] + rw [GaloisRing.ker_residue (monic_monicOf fl), Ideal.mem_span_singleton] at hker + obtain ⟨w, hw⟩ := hker + exact ⟨w, by rw [← sub_eq_iff_eq_add', hw]; push_cast; rfl⟩ + +/-- **Newton inversion inverts every unit** of the Galois ring `A_p`, `f` irreducible modulo +`2`. -/ +theorem val_mul_inv_of_isUnit (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2)))) + {x : List ℕ} (hx : IsUnit (val p fl x)) : val p fl x * val p fl (inv p fl x) = 1 := by + rcases Nat.eq_zero_or_pos p with rfl | hp + · -- `A_0` is the zero ring. + have : Subsingleton (ZMod (2 ^ 0)) := by rw [pow_zero]; infer_instance + have := (AdjoinRoot.of ((monicOf fl).map (Int.castRingHom (ZMod (2 ^ 0))))).codomain_trivial + exact Subsingleton.elim _ _ + · have : NeZero p := ⟨hp.ne'⟩ + obtain ⟨w, hw⟩ := exists_mul_pow_eq_one_add_two_mul hirr hx + exact val_mul_inv x hw + +/-- **Newton inversion computes the inverse of a unit.** -/ +theorem val_inv_of_isUnit (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2)))) + {x : List ℕ} (hx : IsUnit (val p fl x)) : + val p fl (inv p fl x) = _root_.Ring.inverse (val p fl x) := by + rw [← _root_.Ring.inverse_mul_cancel_left _ (val p fl (inv p fl x)) hx, + val_mul_inv_of_isUnit hirr hx, mul_one] + +/-- **Newton inversion inverts every element that is nonzero modulo `2`.** -/ +theorem val_mul_inv_of_residue_ne_zero [NeZero p] + (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2)))) {x : List ℕ} + (hx : GaloisRing.residue 2 p (monicOf fl) (val p fl x) ≠ 0) : + val p fl x * val p fl (inv p fl x) = 1 := + val_mul_inv_of_isUnit hirr ((GaloisRing.isUnit_iff (monic_monicOf fl) hirr).mpr hx) + +end Cslib.GaloisRingArith diff --git a/ComplexityTests/GaloisRingUnits.lean b/ComplexityTests/GaloisRingUnits.lean new file mode 100644 index 0000000000..7325e3bcaf --- /dev/null +++ b/ComplexityTests/GaloisRingUnits.lean @@ -0,0 +1,51 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.PolyTime.GaloisRingUnits +import Mathlib.Algebra.Polynomial.SpecificDegree + +/-! # Newton inversion of units in a Galois ring + +For `f = U ^ 2 + U + 1`, irreducible modulo `2`, Newton inversion inverts every unit of +`(ℤ / 8)[U] / (f)` (`p = 3`, `fl = [1, 1]`) with no hypothesis on the seed. -/ + +namespace ComplexityTests.GaloisRingUnits + +open Cslib.GaloisRingArith Polynomial + +/-- `U ^ 2 + U + 1` is irreducible modulo `2`: it has degree two and no root in `𝔽_2`. -/ +theorem irreducible_monicOf_one_one : + Irreducible ((monicOf [1, 1]).map (Int.castRingHom (ZMod 2))) := by + have hmap : (monicOf [1, 1]).map (Int.castRingHom (ZMod 2)) = X ^ 2 + X + 1 := by + simp [monicOf, Finset.sum_range_succ] + ring + have hmon : ((monicOf [1, 1]).map (Int.castRingHom (ZMod 2))).Monic := + (monic_monicOf _).map _ + have hdeg : ((monicOf [1, 1]).map (Int.castRingHom (ZMod 2))).natDegree = 2 := by + rw [(monic_monicOf _).natDegree_map, natDegree_monicOf]; rfl + rw [hmon.irreducible_iff_roots_eq_zero_of_degree_le_three (by omega) (by omega)] + refine Multiset.eq_zero_of_forall_notMem fun x hx => ?_ + rw [mem_roots hmon.ne_zero, IsRoot, hmap] at hx + simp only [eval_add, eval_pow, eval_X, eval_one] at hx + fin_cases x <;> exact absurd hx (by decide) + +/-- **Every unit is inverted**, with no hypothesis on the seed. -/ +example (x : List ℕ) (hx : IsUnit (val 3 [1, 1] x)) : + val 3 [1, 1] x * val 3 [1, 1] (inv 3 [1, 1] x) = 1 := + val_mul_inv_of_isUnit irreducible_monicOf_one_one hx + +/-- Every element that is nonzero modulo `2` is inverted. -/ +example (x : List ℕ) (hx : GaloisRing.residue 2 3 (monicOf [1, 1]) (val 3 [1, 1] x) ≠ 0) : + val 3 [1, 1] x * val 3 [1, 1] (inv 3 [1, 1] x) = 1 := + val_mul_inv_of_residue_ne_zero irreducible_monicOf_one_one hx + +/-- The seed inverts every unit modulo `2`: here `e = 2`, so the seed is `x ^ 2`. -/ +example (y : Ring 3 [1, 1]) (hy : IsUnit y) : ∃ w, y * y ^ (2 ^ 2 - 2) = 1 + 2 * w := + exists_mul_pow_eq_one_add_two_mul irreducible_monicOf_one_one hy + +example : (monicOf [1, 1]).natDegree = 2 := natDegree_monicOf _ + +end ComplexityTests.GaloisRingUnits From bcc7b10f810423dfdc9763b338be3ae9fba3ab93 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 23:48:24 -0400 Subject: [PATCH 036/129] wip: native recovery definitions and constants Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/NativeRecovery.lean | 263 ++++++++++++++++++ .../TensorNetwork/TaperedRecovery.lean | 10 +- .../PolyTime/GaloisRingSeries.lean | 236 ++++++++++++++++ 3 files changed, 504 insertions(+), 5 deletions(-) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean create mode 100644 Complexity/Computability/PolyTime/GaloisRingSeries.lean diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean new file mode 100644 index 0000000000..11c89c7025 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean @@ -0,0 +1,263 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ActiveCall +public import Complexity.Computability.PolyTime.GaloisRingSeries + +/-! +# Native tapered recovery (Lemma D.5) on coefficient lists + +This file gives the native procedure behind the recovery step of the contraction algorithm of +[OpenAI2026ETH] (§D.4.4, Lemma D.5, pp. 60–61), and proves that it computes the mathematical +recovery `Cslib.Circuits.Frontier.recoverFromValues` of +`Complexity/Computability/Circuit/Frontier/TensorNetwork/ActiveCall.lean`. + +**Input.** A precision `p`, a shift `Δ`, the monic polynomial `fl` of +`Complexity/Computability/PolyTime/GaloisRingArithmetic.lean`, the parameters `a, u, h`, the +interpolation points `ζ` and, for each node index `k < 2 ^ (a + 1)`, `k ≠ 2 ^ a` (the node +`t = k - 2 ^ a`, `NativeRecovery.node`), the values at the points. All arithmetic is in +`A_N = (ℤ / 2 ^ N)[U] / (f)` with `N = p + Δ`. + +**Procedure** (`NativeRecovery.recover`). For each node `t` of multiplicity `J = q_t`: + +1. interpolate the values (`GaloisRingArith.interp`); +2. multiply by the reciprocal of `E_t(t + α z)` below `z ^ J` (`NativeRecovery.recip`). Each factor + of `E_t(t + α z)` is a power of `1 + c_q z` with `c_q = α / (t - q)`, a `2`-adic integer + (`NativeRecovery.slopeConst`), whose reciprocal is the geometric series of `-c_q`; +3. combine the `J` coefficients with the multipliers `2 ^ Δ E_t(0) (-t / α) ^ j` + (`NativeRecovery.coeffMult`). Since `E_t(0) = ∏_q (-q c_q / α) ^ (q_q)`, the multiplier is + `2 ^ (Δ - (a + 1)(M_t + j)) ∏_q (-q c_q) ^ (q_q) (-t) ^ j` with `M_t = ∑_q q_q`, a `2`-adic + integer once `Δ ≥ (a + 1)(M_t + j)`. + +The sum over the nodes is `2 ^ Δ` times the recovered value. Dividing its coordinates by +`2 ^ Δ` (`GaloisRingArith.shiftDown`) gives the recovered value modulo `2 ^ p`. + +**Precision.** The values at node `t` need only be known modulo `2 ^ p_t`: the multipliers have +`2`-adic valuation at least `Δ + p - p_t` when `p + 6 h D_t + q_t (a + 1) ≤ p_t` +((D.10) and the evaluation loss), so the error is killed in `A_N`. This is the precision estimate +of Lemma D.5 (`Cslib.Circuits.Frontier.exists_taperedRecovery_eq_add`), carried out natively. + +## Main definitions + +* `Cslib.Circuits.Frontier.NativeRecovery.slopeConst`: the constant `α / (t - q)`. +* `Cslib.Circuits.Frontier.NativeRecovery.recip`: the truncated reciprocal of `E_t(t + α z)`. +* `Cslib.Circuits.Frontier.NativeRecovery.coeffMult`: the multipliers. +* `Cslib.Circuits.Frontier.NativeRecovery.recover`: native tapered recovery. + +## Main results + +* `Cslib.Circuits.Frontier.NativeRecovery.val_slopeConst`: the constant is correct. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.4, Lemma D.5, + (D.9)–(D.10), pp. 60–61. +-/ + +@[expose] public section + +namespace Cslib.Circuits.Frontier.NativeRecovery + +open Cslib.GaloisRingArith Polynomial Finset + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +/-! ### Nodes -/ + +/-- The node `t = k - 2 ^ a` of index `k`. -/ +def node (a k : ℕ) : ℤ := (k : ℤ) - 2 ^ a + +/-- The multiplicity `q_t = h (u - v₂ t)` of the node of index `k`. -/ +def mult (a u h k : ℕ) : ℕ := h * (u - padicValNat 2 (k - 2 ^ a + (2 ^ a - k))) + +theorem mult_eq (a u h k : ℕ) : mult a u h k = taperMult u h (node a k) := by + have : ((k : ℤ) - 2 ^ a).natAbs = k - 2 ^ a + (2 ^ a - k) := by + rw [show (2 : ℤ) ^ a = ((2 ^ a : ℕ) : ℤ) by push_cast; rfl] + omega + rw [mult, taperMult, node, padicValInt, this] + +/-- The indices of the nodes other than the node of index `k`. -/ +def others (a k : ℕ) : List ℕ := + (List.range (2 ^ (a + 1))).filter fun k' => k' ≠ 2 ^ a ∧ k' ≠ k + +/-- The total multiplicity `M_t = ∑_{q ≠ t} q_q` of the other nodes. -/ +def multSum (a u h k : ℕ) : ℕ := ((others a k).map (mult a u h)).sum + +/-! ### Constants -/ + +/-- The integer `x - y` as a constant of `A_N`. -/ +def diffConst (N : ℕ) (fl : List ℕ) (x y : ℕ) : List ℕ := + if x < y then neg N fl [y - x] else [x - y] + +/-- The `2`-adic integer `2 ^ (a + 1) / (x - y)` as a constant of `A_N`, for +`0 < |x - y| ≤ 2 ^ (a + 1)`: with `|x - y| = 2 ^ v o`, `o` odd, it is +`± 2 ^ (a + 1 - v) o⁻¹`. -/ +def slopeConst (N : ℕ) (fl : List ℕ) (a x y : ℕ) : List ℕ := + let d := x - y + (y - x) + let v := padicValNat 2 d + let w := mul N fl [2 ^ (a + 1 - v)] (inv N fl [d / 2 ^ v]) + if x < y then neg N fl w else w + +/-! ### The reciprocal of `E_t(t + α z)` -/ + +/-- The geometric series `∑_{i < J} (-c) ^ i z ^ i`, the reciprocal of `1 + c z` below +`z ^ J`. -/ +def geom (N : ℕ) (fl : List ℕ) (J : ℕ) (c : List ℕ) : List (List ℕ) := + (List.range J).map fun i => pow N fl (neg N fl c) i + +/-- Multiply by the reciprocal of `(1 + c z) ^ m` below `z ^ J`. -/ +def mulGeomPow (N : ℕ) (fl : List ℕ) (J : ℕ) (c : List ℕ) (m : ℕ) (R : List (List ℕ)) : + List (List ℕ) := + (List.range m).foldl (fun R _ => tmul N fl J R (geom N fl J c)) R + +/-- **The reciprocal of `E_t(t + α z)` below `z ^ J`**, for the node of index `k`: the product of +the reciprocals of `(1 + c_q z) ^ (q_q)` over the other nodes `q`, `c_q = α / (t - q)`. -/ +def recip (N : ℕ) (fl : List ℕ) (a u h k J : ℕ) : List (List ℕ) := + (others a k).foldl (fun R k' => mulGeomPow N fl J (slopeConst N fl a k k') (mult a u h k') R) + [one fl] + +/-! ### The multipliers -/ + +/-- `∏_{q ≠ t} (-q c_q) ^ (q_q)` for the node of index `k`. -/ +def basisNum (N : ℕ) (fl : List ℕ) (a u h k : ℕ) : List ℕ := + (others a k).foldl (fun acc k' => mul N fl acc + (pow N fl (mul N fl (diffConst N fl (2 ^ a) k') (slopeConst N fl a k k')) (mult a u h k'))) + (one fl) + +/-- The multiplier `2 ^ Δ E_t(0) (-t / α) ^ j` of the coefficient of `z ^ j` at the node of +index `k`, as `2 ^ (Δ - (a + 1)(M_t + j)) ∏_{q ≠ t} (-q c_q) ^ (q_q) (-t) ^ j`. -/ +def coeffMult (N : ℕ) (fl : List ℕ) (a u h Δ k j : ℕ) : List ℕ := + mul N fl (mul N fl [2 ^ (Δ - (a + 1) * (multSum a u h k + j))] (basisNum N fl a u h k)) + (pow N fl (diffConst N fl (2 ^ a) k) j) + +/-! ### Recovery -/ + +/-- The contribution of the node of index `k`, from its values `w` at the points `ζ`: +interpolate, divide by `E_t(t + α z)` below `z ^ q_t`, and combine the coefficients with the +multipliers. -/ +def nodeTerm (N : ℕ) (fl : List ℕ) (a u h Δ : ℕ) (ζ : List (List ℕ)) (k : ℕ) + (w : List (List ℕ)) : List ℕ := + let J := mult a u h k + let Q := tmul N fl J (interp N fl ζ w) (recip N fl a u h k J) + (List.range J).foldl (fun S j => add N fl S (mul N fl (coeffMult N fl a u h Δ k j) (Q.getD j []))) + (zero fl) + +/-- **Native tapered recovery.** From the values `W.getD k []` at the points `ζ` for each node +index `k`, the value recovered modulo `2 ^ p`, computing in `A_(p + Δ)` and dividing by +`2 ^ Δ` at the end. -/ +def recover (p Δ : ℕ) (fl : List ℕ) (a u h : ℕ) (ζ : List (List ℕ)) (W : List (List (List ℕ))) : + List ℕ := + shiftDown Δ ((List.range (2 ^ (a + 1))).foldl (fun S k => + if k = 2 ^ a then S else add (p + Δ) fl S (nodeTerm (p + Δ) fl a u h Δ ζ k (W.getD k []))) + (zero fl)) + +/-! ### Correctness of the constants -/ + +variable {N : ℕ} {fl : List ℕ} + +theorem val_diffConst (x y : ℕ) : + val N fl (diffConst N fl x y) = (((x : ℤ) - y : ℤ) : Ring N fl) := by + unfold diffConst + split_ifs with h + · rw [val_neg, val_singleton, Nat.cast_sub h.le] + push_cast + ring + · rw [val_singleton, Nat.cast_sub (not_lt.mp h)] + push_cast + ring + +/-- The `2`-adic integer `2 ^ (a + 1) / (t - q)` for distinct grid points. -/ +noncomputable def slope (a : ℕ) {t q : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (hq : q ∈ cauchyGrid (2 ^ a)) (hne : q ≠ t) : ℤ₍₂₎ := + ⟨2 ^ (a + 1) / ((t : ℚ) - q), padicValuation_two_pow_div_le_one ht hq hne⟩ + +theorem node_mem_cauchyGrid {a k : ℕ} (hk : k < 2 ^ (a + 1)) : node a k ∈ cauchyGrid (2 ^ a) := by + rw [cauchyGrid_two_pow, mem_Ico, node] + have : (k : ℤ) < 2 ^ (a + 1) := by exact_mod_cast hk + rw [pow_succ] at this + constructor <;> omega + +theorem node_injective (a : ℕ) : Function.Injective (node a) := fun k k' h => by + unfold node at h + omega + +/-- **The slope constant is correct**: for every ring homomorphism `ρ` from `ℤ_(2)`, it is the +image of `2 ^ (a + 1) / (t - q)`. -/ +theorem val_slopeConst (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2)))) + (ρ : ℤ₍₂₎ →+* Ring N fl) {a k k' : ℕ} (hk : k < 2 ^ (a + 1)) (hk' : k' < 2 ^ (a + 1)) + (hne : k' ≠ k) : + val N fl (slopeConst N fl a k k') = + ρ (slope a (node_mem_cauchyGrid hk) (node_mem_cauchyGrid hk') + (fun h => hne (node_injective a h))) := by + set d := k - k' + (k' - k) + set v := padicValNat 2 d + set o := d / 2 ^ v + have hd0 : d ≠ 0 := by omega + have hdlt : d < 2 ^ (a + 1) := by omega + have hdv : d = 2 ^ v * o := (Nat.mul_div_cancel' pow_padicValNat_dvd).symm + have hodd : Odd o := by + rw [Nat.odd_iff, ← Nat.two_dvd_ne_zero] + intro h2 + have hnd : ¬ 2 ^ (v + 1) ∣ d := pow_succ_padicValNat_not_dvd hd0 + refine hnd ?_ + have h' : 2 ^ (v + 1) ∣ 2 ^ v * o := by + rw [pow_succ]; exact Nat.mul_dvd_mul_left _ h2 + rwa [← hdv] at h' + have hva : v ≤ a + 1 := by + have : 2 ^ v ≤ d := Nat.le_of_dvd (Nat.pos_of_ne_zero hd0) pow_padicValNat_dvd + have : v < a + 1 := (Nat.pow_lt_pow_iff_right (by norm_num)).mp (this.trans_lt hdlt) + omega + have hunit := isUnit_natCast_of_odd (p := N) (fl := fl) hodd + -- In `ℤ_(2)`, `slope * o = ± 2 ^ (a + 1 - v)`. + set c := slope a (node_mem_cauchyGrid hk) (node_mem_cauchyGrid hk') + (fun h => hne (node_injective a h)) + have hc : c * (o : ℤ₍₂₎) = if k < k' then -((2 ^ (a + 1 - v) : ℕ) : ℤ₍₂₎) + else ((2 ^ (a + 1 - v) : ℕ) : ℤ₍₂₎) := by + apply Subtype.val_injective + have hdiff : ((node a k : ℤ) : ℚ) - (node a k' : ℤ) = + if k < k' then -((2 : ℚ) ^ v * o) else (2 : ℚ) ^ v * o := by + have : ((d : ℕ) : ℚ) = (2 : ℚ) ^ v * o := by exact_mod_cast hdv + rw [← this] + simp only [node, d] + split_ifs with h + · rw [Nat.cast_add, Nat.cast_sub h.le, Nat.sub_eq_zero_of_le h.le]; push_cast; ring + · rw [Nat.sub_eq_zero_of_le (not_lt.mp h), Nat.cast_add, Nat.cast_sub (not_lt.mp h)] + push_cast; ring + have ho : (o : ℚ) ≠ 0 := by exact_mod_cast hodd.pos.ne' + have h2v : (2 : ℚ) ^ (a + 1) = 2 ^ (a + 1 - v) * 2 ^ v := by + rw [← pow_add, Nat.sub_add_cancel hva] + simp only [slope, c, Subring.coe_mul, Subring.coe_natCast, apply_ite Subtype.val, + Subring.coe_neg] + rw [hdiff] + split_ifs + · push_cast + field_simp + rw [h2v] + ring + · push_cast + field_simp + rw [h2v] + ring + have hρ := congrArg ρ hc + rw [map_mul, map_natCast] at hρ + have hρc : ρ c = ρ (if k < k' then -((2 ^ (a + 1 - v) : ℕ) : ℤ₍₂₎) + else ((2 ^ (a + 1 - v) : ℕ) : ℤ₍₂₎)) * + _root_.Ring.inverse (o : Ring N fl) := by + rw [← hρ, mul_assoc, _root_.Ring.mul_inverse_cancel _ hunit, mul_one] + rw [hρc] + have hw : val N fl (mul N fl [2 ^ (a + 1 - v)] (inv N fl [o])) = + ((2 ^ (a + 1 - v) : ℕ) : Ring N fl) * _root_.Ring.inverse (o : Ring N fl) := by + rw [val_mul, val_singleton, val_inv_of_isUnit hirr (by rwa [val_singleton]), val_singleton] + unfold slopeConst + split_ifs with h + · rw [val_neg, hw, map_neg, map_natCast, neg_mul] + · rw [hw, map_natCast] + +end Cslib.Circuits.Frontier.NativeRecovery diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/TaperedRecovery.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/TaperedRecovery.lean index 4d914e4644..5fc26599d7 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/TaperedRecovery.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/TaperedRecovery.lean @@ -147,7 +147,7 @@ private theorem coe_twoPowUnit_inv (n : ℕ) : simp [twoPowUnit, Units.val_inv_eq_inv_val] /-- Distinct integers are units of a `ℚ`-algebra apart. -/ -private theorem pairwise_isUnit_sub (s : Finset ℤ) : +theorem pairwise_isUnit_sub (s : Finset ℤ) : (s : Set ℤ).Pairwise fun i j => IsUnit ((i : K) - (j : K)) := by intro i _ j _ hij have h : ((i : ℚ) - j) ≠ 0 := sub_ne_zero.mpr (by exact_mod_cast hij) @@ -170,7 +170,7 @@ theorem eval_zero_eq_taperedRecovery {a u h : ℕ} {F : K[X]} /-! ### Integrality of the Hermite basis in local coordinates -/ /-- The rationals `α / (t - q)` with `t ≠ q` on the grid, `α = 2 ^ (a + 1)`, lie in `ℤ_(2)`. -/ -private theorem padicValuation_two_pow_div_le_one {a : ℕ} {t q : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) +theorem padicValuation_two_pow_div_le_one {a : ℕ} {t q : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) (hq : q ∈ cauchyGrid (2 ^ a)) (hne : q ≠ t) : Rat.padicValuation 2 ((2 : ℚ) ^ (a + 1) / ((t : ℚ) - q)) ≤ 1 := by have hle := padicValuation_two_pow_le_sub ht hq hne @@ -184,7 +184,7 @@ private theorem padicValuation_two_pow_div_le_one {a : ℕ} {t q : ℤ} (ht : t /-- The Hermite basis over `ℚ` in local coordinates is a product of powers of `1 + α / (t - q) z`. -/ -private theorem hermiteBasis_comp_rat (a u h : ℕ) (t : ℤ) : +theorem hermiteBasis_comp_rat (a u h : ℕ) (t : ℤ) : (hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : ℚ)) (taperMult u h) t).comp (C (t : ℚ) + C (2 ^ (a + 1)) * X) = ∏ q ∈ (recoveryNodes a).erase t, @@ -342,7 +342,7 @@ private theorem inTwoPow_coeff_hermiteQuotient {B : Subring K} · exact inTwoPow_zero B n /-- The value of `E_t(0)` in `K` is the image of the rational one. -/ -private theorem eval_zero_hermiteBasis_eq (a u h : ℕ) {t : ℤ} (ht : t ∈ recoveryNodes a) : +theorem eval_zero_hermiteBasis_eq (a u h : ℕ) {t : ℤ} (ht : t ∈ recoveryNodes a) : (hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : K)) (taperMult u h) t).eval 0 = algebraMap ℚ K ((hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : ℚ)) (taperMult u h) t).eval 0) := by @@ -352,7 +352,7 @@ private theorem eval_zero_hermiteBasis_eq (a u h : ℕ) {t : ℤ} (ht : t ∈ re ← map_zero (algebraMap ℚ K), eval₂_at_apply] /-- `E_t(0) ≠ 0`: its factors are `-q / (t - q)` with `q ≠ 0`. -/ -private theorem eval_zero_hermiteBasis_ne_zero (a u h : ℕ) {t : ℤ} : +theorem eval_zero_hermiteBasis_ne_zero (a u h : ℕ) {t : ℤ} : (hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : ℚ)) (taperMult u h) t).eval 0 ≠ 0 := by rw [eval_hermiteBasis, prod_ne_zero_iff] intro q hq diff --git a/Complexity/Computability/PolyTime/GaloisRingSeries.lean b/Complexity/Computability/PolyTime/GaloisRingSeries.lean new file mode 100644 index 0000000000..c42ccbdf98 --- /dev/null +++ b/Complexity/Computability/PolyTime/GaloisRingSeries.lean @@ -0,0 +1,236 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.PolyTime.GaloisRingInterpolation +public import Complexity.Computability.PolyTime.GaloisRingUnits +import Mathlib.Data.List.GetD + +/-! +# Coordinates, precision changes and truncated products over `(ℤ / 2 ^ p)[U] / (f)` + +Supporting operations on the coefficient lists of +`Complexity/Computability/PolyTime/GaloisRingArithmetic.lean`, for the native recovery step of +the contraction algorithm of [OpenAI2026ETH] (Appendix D.4.4, pp. 60–61). + +* **Coordinates.** The coordinates of an element of `A_p` in the basis `1, U, …, U ^ (e - 1)` are + determined modulo `2 ^ p` (`GaloisRingArith.mod_eq_of_val_eq`), and every element is the value + of a reduced coefficient list (`GaloisRingArith.exists_val_eq`). +* **Precision.** Reduction `A_N → A_n` for `n ≤ N` (`GaloisRingArith.reduce`) keeps the + coordinates (`GaloisRingArith.reduce_val`). Dividing the coordinates of `2 ^ Δ y` by `2 ^ Δ` + gives `y` modulo `2 ^ (N - Δ)` (`GaloisRingArith.val_shiftDown`): this is how a sum that is + known to be divisible by `2 ^ Δ` loses its common power of two. +* **Truncated products** (`GaloisRingArith.tmul`) of polynomials in `z` over `A_p`: the first + `J` coefficients of a product, by the Cauchy formula (`GaloisRingArith.val_tmul`). + +## Main definitions + +* `Cslib.GaloisRingArith.reduce`: reduction to a lower precision. +* `Cslib.GaloisRingArith.shiftDown`: division of the coordinates by `2 ^ Δ`. +* `Cslib.GaloisRingArith.tmul`: truncated products. + +## Main results + +* `Cslib.GaloisRingArith.mod_eq_of_val_eq`, `Cslib.GaloisRingArith.exists_val_eq`: coordinates. +* `Cslib.GaloisRingArith.val_shiftDown`: cancelling a common power of two. +* `Cslib.GaloisRingArith.val_tmul`: truncated products are correct. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Appendix D.4.1 and D.4.4 + (pp. 57–61). +-/ + +@[expose] public section + +namespace Cslib.GaloisRingArith + +open Polynomial Finset + +variable {p : ℕ} {fl : List ℕ} + +/-! ### Coordinates -/ + +/-- `A_0` is the zero ring. -/ +theorem subsingleton_ring_zero : Subsingleton (Ring 0 fl) := by + have : Subsingleton (ZMod (2 ^ 0)) := by rw [pow_zero]; infer_instance + exact (AdjoinRoot.of ((monicOf fl).map (Int.castRingHom (ZMod (2 ^ 0))))).codomain_trivial + +private theorem nontrivial_zmod (hp : 0 < p) : Nontrivial (ZMod (2 ^ p)) := + have : Fact (1 < 2 ^ p) := ⟨Nat.one_lt_two_pow hp.ne'⟩ + ZMod.nontrivial _ + +/-- A coefficient list is the class of the polynomial with those coefficients. -/ +theorem val_eq_mk (c : List ℕ) : val p fl c = AdjoinRoot.mk _ + (∑ i ∈ range fl.length, C ((c.getD i 0 : ℕ) : ZMod (2 ^ p)) * X ^ i) := by + simp only [val, map_sum, map_mul, map_pow, AdjoinRoot.mk_X, map_natCast] + +/-- The single coordinate list `[c]` has value `c`. -/ +@[simp] theorem val_singleton (c : ℕ) : val p fl [c] = (c : Ring p fl) := by + rcases Nat.eq_zero_or_pos fl.length with he | he + · -- The defining polynomial is `1`, so the ring is trivial. + have h1 : (monicOf fl).map (Int.castRingHom (ZMod (2 ^ p))) = 1 := by simp [monicOf, he] + have h : (1 : Ring p fl) = 0 := by + rw [← map_one (AdjoinRoot.mk ((monicOf fl).map (Int.castRingHom (ZMod (2 ^ p))))), + AdjoinRoot.mk_eq_zero, h1] + exact (subsingleton_of_zero_eq_one h.symm).elim _ _ + · rw [val, ← sum_range_add_sum_Ico _ he, sum_range_one, sum_eq_zero fun i hi => by + rw [List.getD_eq_default _ _ (by simp at hi ⊢; omega)]; simp] + simp + +/-- The coordinates of the value of a coefficient list, read off by division by the monic +polynomial. -/ +theorem coeff_modByMonicHom_val (c : List ℕ) (i : ℕ) : + (AdjoinRoot.modByMonicHom ((monic_monicOf fl).map (Int.castRingHom (ZMod (2 ^ p)))) + (val p fl c)).coeff i = if i < fl.length then ((c.getD i 0 : ℕ) : ZMod (2 ^ p)) else 0 := by + rcases Nat.eq_zero_or_pos p with rfl | hp + · have : Subsingleton (ZMod (2 ^ 0)) := by rw [pow_zero]; infer_instance + exact Subsingleton.elim _ _ + have := nontrivial_zmod hp + rw [val_eq_mk, AdjoinRoot.modByMonicHom_mk, (modByMonic_eq_self_iff ((monic_monicOf fl).map _)).mpr] + · simp only [finsetSum_coeff, coeff_C_mul, coeff_X_pow, mul_ite, mul_one, mul_zero, + sum_ite_eq, mem_range] + · rw [(monic_monicOf fl).degree_map, degree_eq_natDegree (monic_monicOf fl).ne_zero, + natDegree_monicOf] + refine (degree_sum_le _ _).trans_lt ((Finset.sup_lt_iff (WithBot.bot_lt_coe _)).mpr + fun i hi => ?_) + exact (degree_C_mul_X_pow_le _ _).trans_lt (by exact_mod_cast mem_range.mp hi) + +/-- **Coordinates are determined modulo `2 ^ p`.** -/ +theorem mod_eq_of_val_eq {c d : List ℕ} (h : val p fl c = val p fl d) {i : ℕ} + (hi : i < fl.length) : c.getD i 0 % 2 ^ p = d.getD i 0 % 2 ^ p := by + have := congrArg (fun y => (AdjoinRoot.modByMonicHom + ((monic_monicOf fl).map (Int.castRingHom (ZMod (2 ^ p)))) y).coeff i) h + simp only [coeff_modByMonicHom_val, hi, ↓reduceIte] at this + exact (ZMod.natCast_eq_natCast_iff' _ _ _).mp this + +/-- **Every element is the value of a reduced coefficient list.** -/ +theorem exists_val_eq (y : Ring p fl) : + ∃ r : List ℕ, r.length = fl.length ∧ (∀ x ∈ r, x < 2 ^ p) ∧ val p fl r = y := by + rcases Nat.eq_zero_or_pos p with rfl | hp + · have := subsingleton_ring_zero (fl := fl) + exact ⟨zero fl, by simp, by simp [zero], Subsingleton.elim _ _⟩ + have := nontrivial_zmod hp + have hg : ((monicOf fl).map (Int.castRingHom (ZMod (2 ^ p)))).Monic := (monic_monicOf fl).map _ + have he : ((monicOf fl).map (Int.castRingHom (ZMod (2 ^ p)))).natDegree = fl.length := by + rw [(monic_monicOf fl).natDegree_map, natDegree_monicOf] + set P := AdjoinRoot.modByMonicHom hg y + refine ⟨(List.range fl.length).map fun i => (P.coeff i).val, by simp, ?_, ?_⟩ + · intro x hx + obtain ⟨i, -, rfl⟩ := List.mem_map.mp hx + exact ZMod.val_lt _ + · have hsum := (AdjoinRoot.powerBasis' hg).basis.sum_repr y + have hrepr : ∀ i, (AdjoinRoot.powerBasis' hg).basis.repr y i = P.coeff i := fun _ => rfl + rw [PowerBasis.coe_basis] at hsum + simp only [AdjoinRoot.powerBasis'_gen, hrepr] at hsum + rw [Fin.sum_univ_eq_sum_range (fun i => P.coeff i • AdjoinRoot.root _ ^ i), + AdjoinRoot.powerBasis'_dim, he] at hsum + conv_rhs => rw [← hsum] + rw [val] + refine sum_congr rfl fun i hi => ?_ + rw [List.getD_eq_getElem _ _ (by simpa using hi), List.getElem_map, List.getElem_range, + Algebra.smul_def, ← map_natCast (algebraMap (ZMod (2 ^ p)) _), ZMod.natCast_zmod_val] + +/-- Mapping a function that fixes `0` commutes with reading a coordinate. -/ +theorem getD_map_of_map_zero {f : ℕ → ℕ} (hf : f 0 = 0) (c : List ℕ) (i : ℕ) : + (c.map f).getD i 0 = f (c.getD i 0) := by + rw [List.getD_eq_getElem?_getD, List.getD_eq_getElem?_getD, List.getElem?_map] + cases c[i]? <;> simp [hf] + +/-- Scaling every coordinate by `k` multiplies the value by `k`. -/ +theorem val_map_mul (k : ℕ) (c : List ℕ) : val p fl (c.map (k * ·)) = k * val p fl c := by + rw [val, val, mul_sum] + refine sum_congr rfl fun i _ => ?_ + rw [getD_map_of_map_zero (mul_zero k)] + push_cast + ring + +/-! ### Changing the precision -/ + +/-- Reduction `A_N → A_n` from a higher precision to a lower one. -/ +noncomputable def reduce {n N : ℕ} (h : n ≤ N) (fl : List ℕ) : Ring N fl →+* Ring n fl := + AdjoinRoot.map (ZMod.castHom (pow_dvd_pow 2 h) (ZMod (2 ^ n))) _ _ + (GaloisRing.map_castHom_map (monicOf fl) (pow_dvd_pow 2 h)).symm.dvd + +/-- **Reduction keeps the coordinates.** -/ +theorem reduce_val {n N : ℕ} (h : n ≤ N) (c : List ℕ) : + reduce h fl (val N fl c) = val n fl c := by + simp only [reduce, val, map_sum, map_mul, map_pow, map_natCast, AdjoinRoot.map_root] + +/-- Division of each coordinate by `2 ^ Δ`. -/ +def shiftDown (Δ : ℕ) (c : List ℕ) : List ℕ := c.map (· / 2 ^ Δ) + +/-- **Cancelling a common power of two.** If a coefficient list has value `2 ^ Δ y` at precision +`n + Δ`, dividing its coordinates by `2 ^ Δ` gives the reduction of `y` to precision `n`. -/ +theorem val_shiftDown {n Δ : ℕ} {s : List ℕ} {y : Ring (n + Δ) fl} + (h : val (n + Δ) fl s = 2 ^ Δ * y) : + val n fl (shiftDown Δ s) = reduce (Nat.le_add_right n Δ) fl y := by + obtain ⟨r, -, -, rfl⟩ := exists_val_eq y + rw [reduce_val, val, val] + refine sum_congr rfl fun i hi => ?_ + have hi' := mem_range.mp hi + have hmod := mod_eq_of_val_eq (h.trans (by + rw [show (2 : Ring (n + Δ) fl) ^ Δ = ((2 ^ Δ : ℕ) : Ring (n + Δ) fl) by push_cast; rfl, + ← val_map_mul])) hi' + rw [getD_map_of_map_zero (mul_zero _), pow_add, mul_comm (2 ^ n), + Nat.mul_mod_mul_left] at hmod + rw [shiftDown, getD_map_of_map_zero (f := (· / 2 ^ Δ)) (Nat.zero_div _)] + congr 1 + set x := s.getD i 0 + -- `x = 2 ^ Δ (r_i mod 2 ^ n) + 2 ^ Δ 2 ^ n k`. + have hx : x = 2 ^ Δ * (r.getD i 0 % 2 ^ n + 2 ^ n * (x / (2 ^ Δ * 2 ^ n))) := by + conv_lhs => rw [← Nat.mod_add_div x (2 ^ Δ * 2 ^ n), hmod] + ring + rw [hx, Nat.mul_div_cancel_left _ (Nat.two_pow_pos Δ), Nat.cast_add, natCast_mod_two_pow, + Nat.cast_mul, natCast_two_pow, zero_mul, add_zero] + +/-! ### Truncated products -/ + +/-- The coefficient of `z ^ j` in the product of two polynomials over `A_p`, by the Cauchy +formula `∑ i ≤ j, P_i R_(j - i)`. -/ +def cauchyCoeff (p : ℕ) (fl : List ℕ) (P R : List (List ℕ)) (j : ℕ) : List ℕ := + (List.range (j + 1)).foldl (fun acc i => add p fl acc (mul p fl (P.getD i []) (R.getD (j - i) []))) + (zero fl) + +/-- The **truncated product**: the first `J` coefficients of the product of two polynomials over +`A_p`. -/ +def tmul (p : ℕ) (fl : List ℕ) (J : ℕ) (P R : List (List ℕ)) : List (List ℕ) := + (List.range J).map (cauchyCoeff p fl P R) + +theorem val_cauchyCoeff (P R : List (List ℕ)) (j : ℕ) : + val p fl (cauchyCoeff p fl P R j) = + ∑ i ∈ range (j + 1), val p fl (P.getD i []) * val p fl (R.getD (j - i) []) := by + have h (m : ℕ) : val p fl ((List.range m).foldl + (fun acc i => add p fl acc (mul p fl (P.getD i []) (R.getD (j - i) []))) (zero fl)) = + ∑ i ∈ range m, val p fl (P.getD i []) * val p fl (R.getD (j - i) []) := by + induction m with + | zero => simp + | succ m ih => + rw [List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil, val_add, val_mul, + ih, sum_range_succ] + exact h _ + +@[simp] theorem length_tmul (J : ℕ) (P R : List (List ℕ)) : (tmul p fl J P R).length = J := by + simp [tmul] + +/-- **Truncated products are correct**: below `z ^ J` the coefficients are those of the product. -/ +theorem val_tmul {J : ℕ} (P R : List (List ℕ)) {j : ℕ} (hj : j < J) : + val p fl ((tmul p fl J P R).getD j []) = + ∑ i ∈ range (j + 1), val p fl (P.getD i []) * val p fl (R.getD (j - i) []) := by + rw [tmul, List.getD_eq_getElem _ _ (by simpa using hj), List.getElem_map, List.getElem_range, + val_cauchyCoeff] + +/-! ### Odd scalars are units -/ + +/-- An odd natural number is a unit of `A_p`. -/ +theorem isUnit_natCast_of_odd {o : ℕ} (ho : Odd o) : IsUnit (o : Ring p fl) := by + have hcop : Nat.Coprime o (2 ^ p) := (Nat.coprime_two_right.mpr ho).pow_right p + have := (ZMod.unitOfCoprime o hcop).isUnit.map (algebraMap (ZMod (2 ^ p)) (Ring p fl)) + simpa using this + +end Cslib.GaloisRingArith From 3685abaef55599db833f8bf8e8388dd96b6e5101 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 23:49:30 -0400 Subject: [PATCH 037/129] feat(circuit/frontier): explicit packet entries of the child networks (Theorem D.1, T2.9 item 1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The child networks of an active call keep their packet tensors implicit (§D.4.6 of OpenAI2026ETH). This adds the explicit evaluation formula on the packet data and proves that it is the entry of the child network of `cauchyModeSplitting`: * `listCoeff`: coefficient extraction along a list of factors, equal to the sum over all choices (`sum_eq_listCoeff`) and to `blockCoeff` for any mode splitting and any order of the rows (`blockCoeff_eq_listCoeff`); * `packetEntry` and its terms, with `packetTensor_cauchyModeSplitting` and `child_tensor_inr_cauchyModeSplitting`; * `map_packetEntry`: the formula commutes with ring homomorphisms when 2 is nilpotent, so an evaluation in A_p transfers to the recursion's quotients; * the field lemmas of `cauchyModeSplitting` beyond side/group/packet. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Frontier/TensorNetwork/PacketEntry.lean | 678 ++++++++++++++++++ .../Frontier/TensorNetworkPacketEntry.lean | 75 ++ 3 files changed, 754 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEntry.lean create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEntry.lean diff --git a/Complexity.lean b/Complexity.lean index 47ca344c79..61358cafb6 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -877,6 +877,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DirectCont public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEntry public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness public import Complexity.Computability.Circuit.Frontier.Unconditional public import Complexity.Computability.Circuit.Frontier.Witness diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEntry.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEntry.lean new file mode 100644 index 0000000000..bd8a7ffe7e --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEntry.lean @@ -0,0 +1,678 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CauchyModes +import all Complexity.Computability.Circuit.Frontier.TensorNetwork.CauchyModes +import Mathlib.Algebra.BigOperators.Fin +import Mathlib.Data.Fintype.Fin + +/-! +# Entries of the child networks + +The child networks of an active call of the contraction algorithm of [OpenAI2026ETH] are kept +implicit: a packet vertex has up to `2 L` ports, so its table would have `2 ^ (2 L)` entries. The +source evaluates an entry by expanding the product of the at most `L` affine row factors of the +packet, enumerating the choices of a local term or a mode in each factor, keeping those with the +required exponents, and multiplying by the powers of the substituted modes (§D.4.6, p. 63). This +file gives that evaluation as an explicit formula on the data of the packet and proves that it is +the entry of the child network of `TensorNetwork.cauchyModeSplitting`. + +**Coefficient extraction along a list** (`TensorNetwork.listCoeff`). For a list of factors, each +a function `T : Option M → R` (the local term at `none` and the coefficient of the mode `Z` at +`some Z`), and exponents `γ` on the modes of a finite set `F`, the coefficient of `∏ Z ^ γ Z` in +`∏ (T none + ∑ Z ∈ F, T (some Z) Z)` is computed recursively: the first factor contributes its +local term, or one of the modes still needed. This is the coefficient of a block +(`TensorNetwork.ModeSplitting.blockCoeff_eq_listCoeff`) for any mode splitting, with the rows of +the block in any order. + +**Packet entries** (`TensorNetwork.packetEntry`). For the Cauchy modes of an active node, the +entry of the packet vertex of the child of group `g` at the packet `q` depends only on: +* the parameters `κ`, `ζ`, `a`, `i` and `t` of the node; +* whether the two symbols `sym q` of the packet are `g` and whether they differ; +* the exponents of the modes `(q, y₀, b)` of the packet; +* the slots and bits of its row ports and of its column ports, as two lists. + +The local form of a row is a sum of kernel values over the column ports set to one +(`TensorNetwork.packetLocalForm`), a mode coefficient is the geometric expansion term (D.12) +(`TensorNetwork.packetModeCoeff`), and a mode value is a sum of powers `j ^ b` over the column ports +of its tile set to one (`TensorNetwork.packetModeValue`). The main result +(`TensorNetwork.packetTensor_cauchyModeSplitting`, and for the child itself +`TensorNetwork.child_tensor_inr_cauchyModeSplitting`) is that this formula is the packet tensor. +A retained vertex keeps its tensor (`TensorNetwork.ModeSplitting.child_tensor_inl`). + +The formula commutes with ring homomorphisms (`TensorNetwork.map_packetEntry`) when `2` is +nilpotent, so an evaluation in one coefficient ring, such as the coefficient lists of +`(ℤ / 2 ^ p)[U] / (f)`, gives the entry in every ring it maps to, such as the quotients of the +recursion. + +This file also supplies the field lemmas of `TensorNetwork.cauchyModeSplitting` beyond its sides, +groups and packets (`TensorNetwork.cauchyModeSplitting_localPart`, +`TensorNetwork.cauchyModeSplitting_coeff`, `TensorNetwork.cauchyModeSplitting_form` and the mode +packets, sources and targets), through `import all`, since the definition is not exposed. + +## Main definitions + +* `Cslib.Circuits.Frontier.TensorNetwork.listCoeff`: coefficient extraction along a list. +* `Cslib.Circuits.Frontier.TensorNetwork.packetLocalForm`, + `Cslib.Circuits.Frontier.TensorNetwork.packetModeCoeff`, + `Cslib.Circuits.Frontier.TensorNetwork.packetModeValue`, + `Cslib.Circuits.Frontier.TensorNetwork.packetRowTerm`: the terms of a packet entry. +* `Cslib.Circuits.Frontier.TensorNetwork.packetEntry`: the entry of a packet vertex. + +## Main results + +* `Cslib.Circuits.Frontier.TensorNetwork.sum_eq_listCoeff`: the recursion sums over all choices. +* `Cslib.Circuits.Frontier.TensorNetwork.ModeSplitting.blockCoeff_eq_listCoeff`: block + coefficients by the recursion. +* `Cslib.Circuits.Frontier.TensorNetwork.packetTensor_cauchyModeSplitting`, + `Cslib.Circuits.Frontier.TensorNetwork.child_tensor_inr_cauchyModeSplitting`: the packet entries + of the children. +* `Cslib.Circuits.Frontier.TensorNetwork.map_packetEntry`: packet entries commute with ring + homomorphisms. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.5–§D.4.6, + (D.12)–(D.16), pp. 62–63. +-/ + +public section + +namespace Cslib.Circuits.Frontier + +open Finset + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +namespace TensorNetwork + +/-! ### The remaining fields of the Cauchy mode splitting -/ + +section Fields + +variable {R V E G P : Type*} [CommRing R] [DecidableEq G] [DecidableEq P] + {N : TensorNetwork R Bool V E} {group : V → G} {packet : E → P} {slot : E → ℕ} + {sym : P → G × G} {i : ℕ} + (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) + (htgt : ∀ e, side (N.tgt e) = false) + (horient : ∀ e, (group (N.src e), group (N.tgt e)) = + if (slot e).testBit i then (sym (packet e)).swap else sym (packet e)) + {a : ℕ} (hslot : ∀ e, slot e < 2 ^ a) (hia : i < a) (κ : ℤ₍₂₎ →+* R) (ζ : R) {p : ℕ} + (hp : (2 : R) ^ p = 0) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) (hti : (2 : ℤ) ^ i ∣ t) + (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) + +/-- The local part of the Cauchy mode splitting: the Cauchy matrix between ports of the same +group. -/ +@[simp] theorem cauchyModeSplitting_localPart [Fintype P] : + (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').localPart = + fun e e' => if group (N.tgt e') = group (N.src e) then + cauchyValueMatrix κ ζ a t packet slot e e' else 0 := by + unfold cauchyModeSplitting; rfl + +/-- The packet of the mode `(q, y₀, b)` is `q`. -/ +@[simp] theorem cauchyModeSplitting_modePacket [Fintype P] : + (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').modePacket = + Prod.fst := by + unfold cauchyModeSplitting; rfl + +/-- The source group of the mode `(q, y₀, b)`: the group of the columns of its tile. -/ +@[simp] theorem cauchyModeSplitting_source [Fintype P] : + (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').source = + fun Z => if (Z.2.1 : ℕ).testBit i then (sym Z.1).1 else (sym Z.1).2 := by + unfold cauchyModeSplitting; rfl + +/-- The target group of the mode `(q, y₀, b)`: the other group of its packet. -/ +@[simp] theorem cauchyModeSplitting_target [Fintype P] : + (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').target = + fun Z => if (Z.2.1 : ℕ).testBit i then (sym Z.1).2 else (sym Z.1).1 := by + unfold cauchyModeSplitting; rfl + +/-- The mode coefficients are `cauchyModeCoeff`. -/ +@[simp] theorem cauchyModeSplitting_coeff [Fintype P] : + (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').coeff = + cauchyModeCoeff κ ζ a i t packet slot sym := by + unfold cauchyModeSplitting; rfl + +/-- The mode forms are `cauchyModeForm`. -/ +@[simp] theorem cauchyModeSplitting_form [Fintype P] : + (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').form = + cauchyModeForm R i packet slot sym := by + unfold cauchyModeSplitting; rfl + +end Fields + +/-! ### Coefficient extraction along a list -/ + +section ListCoeff + +variable {R M : Type*} [CommRing R] [DecidableEq M] + +/-- **Coefficient extraction along a list of factors.** Each factor is a function +`T : Option M → R`, standing for `T none + ∑ Z ∈ F, T (some Z) Z`. The value at the exponents `γ` +is the coefficient of `∏ Z ∈ F, Z ^ γ Z` in the product of the factors: the first factor +contributes its local term `T none`, or the coefficient of a mode `Z` with `γ Z ≠ 0`, lowering +`γ Z` by one, and the empty product has the coefficient `1` at zero exponents. -/ +@[expose] def listCoeff (F : Finset M) : List (Option M → R) → (M → ℕ) → R + | [], γ => if ∀ Z ∈ F, γ Z = 0 then 1 else 0 + | T :: ts, γ => T none * listCoeff F ts γ + + ∑ Z ∈ F, if γ Z = 0 then 0 else + T (some Z) * listCoeff F ts (Function.update γ Z (γ Z - 1)) + +@[simp] theorem listCoeff_nil (F : Finset M) (γ : M → ℕ) : + listCoeff (R := R) F [] γ = if ∀ Z ∈ F, γ Z = 0 then 1 else 0 := + rfl + +theorem listCoeff_cons (F : Finset M) (T : Option M → R) (ts : List (Option M → R)) + (γ : M → ℕ) : + listCoeff F (T :: ts) γ = T none * listCoeff F ts γ + + ∑ Z ∈ F, if γ Z = 0 then 0 else + T (some Z) * listCoeff F ts (Function.update γ Z (γ Z - 1)) := + rfl + +/-- The count condition after choosing the mode `Z ∈ F` in the first factor. -/ +private theorem count_cons_iff {F : Finset M} {Z : M} (hZ : Z ∈ F) (c γ : M → ℕ) : + (∀ Z' ∈ F, (if Z = Z' then 1 else 0) + c Z' = γ Z') ↔ + γ Z ≠ 0 ∧ ∀ Z' ∈ F, c Z' = Function.update γ Z (γ Z - 1) Z' := by + constructor + · intro h + have hz := h Z hZ + simp only [↓reduceIte] at hz + refine ⟨by omega, fun Z' hZ' => ?_⟩ + by_cases hZZ : Z' = Z + · subst hZZ + rw [Function.update_self] + omega + · have := h Z' hZ' + simp only [Ne.symm hZZ, ↓reduceIte, zero_add] at this + rw [Function.update_of_ne hZZ, this] + · rintro ⟨h0, h⟩ Z' hZ' + by_cases hZZ : Z' = Z + · subst hZZ + simp only [↓reduceIte, h Z' hZ', Function.update_self] + omega + · simp only [Ne.symm hZZ, ↓reduceIte, zero_add, h Z' hZ', Function.update_of_ne hZZ] + +/-- Picks of `Fin.cons o φ`: those of `o`, and those of `φ`. -/ +private theorem card_cons_eq {n : ℕ} (o : Option M) (φ : Fin n → Option M) (Z : M) : + #{j | (Fin.cons o φ : Fin (n + 1) → Option M) j = some Z} = + (if o = some Z then 1 else 0) + #{j | φ j = some Z} := by + rw [Fin.card_filter_univ_succ'] + simp only [Fin.cons_zero, Fin.cons_succ] + +omit [DecidableEq M] in +/-- A sum over the functions on `Fin (n + 1)` splits off the value at `0`. -/ +private theorem sum_pi_fin_succ [Fintype M] {n : ℕ} (f : (Fin (n + 1) → Option M) → R) : + ∑ φ, f φ = ∑ o : Option M, ∑ φ : Fin n → Option M, f (Fin.cons o φ) := by + rw [← Fintype.sum_prod_type'] + exact (Fintype.sum_equiv (Fin.consEquiv fun _ => Option M) _ _ fun x => rfl).symm + +/-- **The recursion sums over all choices.** For factors `T j` indexed by `Fin n`, the sum over +the ways `φ` to pick in each factor its local term or a mode of `F`, each mode `Z` picked `γ Z` +times, of the product of the picked terms is `listCoeff`. -/ +theorem sum_eq_listCoeff [Fintype M] (F : Finset M) : + ∀ (n : ℕ) (T : Fin n → Option M → R) (γ : M → ℕ), + (∑ φ : Fin n → Option M, + if (∀ j Z, φ j = some Z → Z ∈ F) ∧ ∀ Z ∈ F, #{j | φ j = some Z} = γ Z then + ∏ j, T j (φ j) else 0) = listCoeff F (List.ofFn T) γ + | 0, T, γ => by + rw [Fintype.sum_unique, List.ofFn_zero, listCoeff_nil] + simp only [IsEmpty.forall_iff, true_and, Finset.univ_eq_empty, filter_empty, card_empty, + Finset.prod_empty] + by_cases h : ∀ Z ∈ F, γ Z = 0 + · rw [ite_eq_left h, ite_eq_left fun Z hZ => (h Z hZ).symm] + · rw [ite_eq_right h, ite_eq_right fun h' => h fun Z hZ => (h' Z hZ).symm] + | n + 1, T, γ => by + rw [sum_pi_fin_succ, Fintype.sum_option, List.ofFn_succ, listCoeff_cons] + congr 1 + · -- The local term of the first factor. + rw [← sum_eq_listCoeff F n (fun j => T j.succ) γ, mul_sum] + refine sum_congr rfl fun φ _ => ?_ + simp only [Fin.prod_univ_succ, Fin.cons_zero, Fin.cons_succ, + Fin.forall_fin_succ, card_cons_eq, reduceCtorEq, false_implies, implies_true, true_and, + ↓reduceIte, zero_add, mul_ite, mul_zero] + · -- A mode in the first factor. + rw [← sum_subset (subset_univ F) fun Z _ hZ => ?_] + · refine sum_congr rfl fun Z hZ => ?_ + rw [← sum_eq_listCoeff F n (fun j => T j.succ), mul_sum] + simp only [Fin.prod_univ_succ, Fin.cons_zero, Fin.cons_succ, + Fin.forall_fin_succ, card_cons_eq, Option.some.injEq, forall_eq', hZ, true_and, + count_cons_iff hZ] + by_cases h0 : γ Z = 0 + · simp only [h0, ne_eq, not_true_eq_false, false_and, and_false, ↓reduceIte, + sum_const_zero] + · simp only [h0, ne_eq, not_false_eq_true, true_and, ↓reduceIte, mul_ite, mul_zero] + · refine sum_eq_zero fun φ _ => ite_eq_right ?_ + simp only [Fin.forall_fin_succ, Fin.cons_zero, Option.some.injEq, forall_eq', not_and] + exact fun h => absurd h.1 hZ + +/-- `listCoeff` along the image of an injective map of modes. -/ +theorem listCoeff_map {M' : Type*} [DecidableEq M'] (f : M' ↪ M) (F : Finset M') : + ∀ (ts : List (Option M → R)) (γ : M → ℕ), + listCoeff (F.map f) ts γ = listCoeff F (ts.map fun T => T ∘ Option.map f) (γ ∘ f) + | [], γ => by simp + | T :: ts, γ => by + rw [List.map_cons, listCoeff_cons, listCoeff_cons, sum_map, listCoeff_map f F ts γ] + refine congrArg _ (sum_congr rfl fun Z _ => ?_) + rw [listCoeff_map f F ts, Function.update_comp_eq_of_injective _ f.injective] + rfl + +end ListCoeff + +/-! ### Block coefficients by the recursion -/ + +namespace ModeSplitting + +variable {R V E G P M : Type*} [CommRing R] [Fintype M] {N : TensorNetwork R Bool V E} + {W : Matrix E E R} (S : ModeSplitting N W G P M) [Fintype E] [DecidableEq E] [DecidableEq P] + [DecidableEq G] [DecidableEq M] + +/-- **Block coefficients by the recursion.** For a list `l` of the row ports of the block `k`, +without repetition and in any order, the coefficient of a mode monomial in the product of the +affine factors of the block is `listCoeff` along the terms of the rows of `l`. -/ +theorem blockCoeff_eq_listCoeff (k : P × G) (β : M → ℕ) (Y X : E → Bool) {l : List E} + (hl : l.Nodup) (hmem : ∀ e, e ∈ l ↔ S.rowBlock e = k) : + S.blockCoeff k β Y X = + listCoeff {Z | S.targetBlock Z = k} (l.map fun e => S.choiceTerm Y X e) β := by + let σ : Fin l.length ≃ {e // S.rowBlock e = k} := + Equiv.ofBijective (fun j => ⟨l.get j, (hmem _).1 (List.get_mem _ _)⟩) + ⟨fun j j' h => (hl.get_inj_iff).1 (congrArg Subtype.val h), fun e => by + obtain ⟨j, hj⟩ := List.mem_iff_get.1 ((hmem e.1).2 e.2) + exact ⟨j, Subtype.ext hj⟩⟩ + have hl' : (l.map fun e => S.choiceTerm Y X e) = + List.ofFn fun j => S.choiceTerm Y X (σ j).1 := by + conv_lhs => rw [← List.ofFn_get l, List.map_ofFn] + rfl + have hcard (φ : {e // S.rowBlock e = k} → Option M) (Z : M) : + #{j | φ (σ j) = some Z} = #{e | φ e = some Z} := + Finset.card_equiv σ fun j => by simp only [mem_filter, mem_univ, true_and] + rw [hl', ← sum_eq_listCoeff] + refine Fintype.sum_equiv (σ.arrowCongr (Equiv.refl _)).symm _ _ fun φ => ?_ + simp only [Equiv.arrowCongr_symm, Equiv.refl_symm, Equiv.arrowCongr_apply, Equiv.coe_refl, + Function.comp_apply, id_eq, Equiv.symm_symm, mem_filter, mem_univ, true_and] + refine if_congr (and_congr ⟨fun h j Z => h (σ j) Z, fun h e Z => ?_⟩ + (forall_congr' fun Z => imp_congr_right fun _ => ?_)) ?_ rfl + · simpa using h (σ.symm e) Z + · rw [hcard] + · exact (Fintype.prod_equiv σ _ _ fun j => rfl).symm + +/-- A retained vertex of a child keeps its tensor, reading the row bits on its edges when it is a +row and the column bits when it is a column. -/ +theorem child_tensor_inl [DecidableEq V] (β : M → ℕ) (g : G) (v : {v // S.group v = g}) + (y : {e // S.group (N.src e) = g} ⊕ {e // S.group (N.tgt e) = g} → Bool) : + (S.child β g).tensor (.inl v) y = + if S.side v.1 then + N.tensor v.1 fun e => if h : N.src e = v.1 then y (.inl ⟨e, h ▸ v.2⟩) else false + else + N.tensor v.1 fun e => if h : N.tgt e = v.1 then y (.inr ⟨e, h ▸ v.2⟩) else false := + rfl + +/-- A packet vertex of a child has the packet tensor at the bits of its ports. -/ +theorem child_tensor_inr [DecidableEq V] (β : M → ℕ) (g : G) (q : P) + (y : {e // S.group (N.src e) = g} ⊕ {e // S.group (N.tgt e) = g} → Bool) : + (S.child β g).tensor (.inr q) y = + S.packetTensor β q g + (fun e => if h : S.group (N.src e) = g then y (.inl ⟨e, h⟩) else false) + (fun e => if h : S.group (N.tgt e) = g then y (.inr ⟨e, h⟩) else false) := + rfl + +end ModeSplitting + +/-! ### Packet entries -/ + +section Packet + +variable {R : Type*} [CommRing R] + +/-- The local form of a row port in slot `x` of a packet: the sum of the kernel values +`W_{y - x}(T)` over its column ports `(y, X_y)` set to one. -/ +@[expose] noncomputable def packetLocalForm (κ : ℤ₍₂₎ →+* R) (ζ : R) (a : ℕ) (t : ℤ) + (cols : List (ℕ × Bool)) (x : ℕ) : R := + (cols.map fun c => if c.2 then kernelValue κ ζ a t ((c.1 : ℤ) - x) else 0).sum + +/-- The coefficient of the mode `(y₀, b)` of a packet in the form of a row port in slot `x`: when +the two symbols of the packet differ and digit `i` of `x` is that of `y₀`, the term +`W_{y₀ - x}(T) ρ ^ b` of the geometric expansion (D.12); otherwise zero. -/ +@[expose] noncomputable def packetModeCoeff (κ : ℤ₍₂₎ →+* R) (ζ : R) (a i : ℕ) (t : ℤ) + (differ : Bool) {p : ℕ} (x : ℕ) (Z : Fin (2 ^ (i + 1)) × Fin p) : R := + if differ = true ∧ x.testBit i = (Z.1 : ℕ).testBit i then + kernelValue κ ζ a t ((Z.1 : ℤ) - x) * crossRatio a i t ((Z.1 : ℤ) - x) ζ ^ (Z.2 : ℕ) + else 0 + +variable (R) in +/-- The value `L_Z(X)` of the mode `Z = (y₀, b)` of a packet on its column ports: when the two +symbols of the packet differ, the sum of `j ^ b` over the column ports in slots +`y = y₀ + 2 ^ (i + 1) j` set to one. -/ +@[expose] def packetModeValue (i : ℕ) (differ : Bool) {p : ℕ} + (cols : List (ℕ × Bool)) (Z : Fin (2 ^ (i + 1)) × Fin p) : R := + (cols.map fun c => if c.2 = true ∧ differ = true ∧ c.1 % 2 ^ (i + 1) = (Z.1 : ℕ) then + ((c.1 / 2 ^ (i + 1) : ℕ) : R) ^ (Z.2 : ℕ) else 0).sum + +/-- The terms of the affine factor of a row port `(x, Y_x)` of a packet: its local term at `none`, +and the coefficient of the mode `Z` at `some Z`. -/ +@[expose] noncomputable def packetRowTerm (κ : ℤ₍₂₎ →+* R) (ζ : R) (a i : ℕ) (t : ℤ) + (differ : Bool) {p : ℕ} (cols : List (ℕ × Bool)) (r : ℕ × Bool) : + Option (Fin (2 ^ (i + 1)) × Fin p) → R + | none => affineFactor r.2 (packetLocalForm κ ζ a t cols r.1) + | some Z => affineSign r.2 * packetModeCoeff κ ζ a i t differ r.1 Z + +/-- Whether the modes of the tile `y₀` of a packet target the group `g`, given whether the two +symbols of the packet are `g`: the target is the second symbol when digit `i` of `y₀` is `1`. -/ +@[expose] def modeTargets (i : ℕ) (e₁ e₂ : Bool) (y₀ : ℕ) : Bool := + if y₀.testBit i then e₂ else e₁ + +/-- Whether the modes of the tile `y₀` of a packet have the source `g`: the source is the first +symbol when digit `i` of `y₀` is `1`. -/ +@[expose] def modeSources (i : ℕ) (e₁ e₂ : Bool) (y₀ : ℕ) : Bool := + if y₀.testBit i then e₁ else e₂ + +/-- **The entry of a packet vertex** of a child (§D.4.6, p. 63). The packet has row ports +`rows` and column ports `cols`, each a slot and a bit; `e₁` and `e₂` say whether the two symbols +of the packet are the group of the child, and `differ` whether they differ; `β` gives the exponents +of the modes `(y₀, b)` of the packet. The entry is the coefficient, at the exponents of the modes +targeting the group, of the product of the affine row factors, times the powers of the values of +the modes with the group as source. -/ +@[expose] noncomputable def packetEntry (κ : ℤ₍₂₎ →+* R) (ζ : R) (a i : ℕ) (t : ℤ) {p : ℕ} + (e₁ e₂ differ : Bool) (β : Fin (2 ^ (i + 1)) × Fin p → ℕ) (rows cols : List (ℕ × Bool)) : R := + listCoeff {Z | modeTargets i e₁ e₂ Z.1 = true} + (rows.map (packetRowTerm κ ζ a i t differ cols)) β * + ∏ Z with modeSources i e₁ e₂ Z.1 = true, packetModeValue R i differ cols Z ^ β Z + +end Packet + +/-! ### Packet entries under ring homomorphisms -/ + +section Map + +variable {R S : Type*} [CommRing R] [CommRing S] + +/-- Kernel values commute with ring homomorphisms. -/ +theorem map_kernelValue (ψ : R →+* S) (κ : ℤ₍₂₎ →+* R) (ζ : R) (a : ℕ) (t d : ℤ) : + ψ (kernelValue κ ζ a t d) = kernelValue (ψ.comp κ) (ψ ζ) a t d := + Polynomial.hom_eval₂ _ _ _ _ + +/-- A ring homomorphism maps the inverse of a unit to the inverse of its image. -/ +private theorem map_ringInverse (ψ : R →+* S) {u : R} (hu : IsUnit u) : + ψ (Ring.inverse u) = Ring.inverse (ψ u) := by + obtain ⟨w, rfl⟩ := hu + have h : ψ (w : R) = ((Units.map (ψ : R →* S) w : Sˣ) : S) := rfl + rw [Ring.inverse_unit, h, Ring.inverse_unit, Units.coe_map_inv] + rfl + +/-- **Cross ratios commute with ring homomorphisms** when `2` is nilpotent and +`2 ^ (i + 1) ∤ t - d₀` with `i ≤ a`, so that the inverted element is a unit. -/ +theorem map_crossRatio (ψ : R →+* S) {n : ℕ} (hn : (2 : R) ^ n = 0) {a i : ℕ} (hia : i ≤ a) + {t d₀ : ℤ} (hcross : ¬ (2 : ℤ) ^ (i + 1) ∣ t - d₀) (ζ : R) : + ψ (crossRatio a i t d₀ ζ) = crossRatio a i t d₀ (ψ ζ) := by + obtain ⟨hvi, ⟨c, hc⟩, -⟩ := two_adic_split hcross + have hU : IsUnit ((((t - d₀) / 2 ^ padicValInt 2 (t - d₀) : ℤ) : R) + + 2 ^ (a + 1 - padicValInt 2 (t - d₀)) * ζ) := by + rw [hc, show a + 1 - padicValInt 2 (t - d₀) = a - padicValInt 2 (t - d₀) + 1 by omega, + pow_succ] + convert isUnit_one_add_two_mul hn ((c : R) + 2 ^ (a - padicValInt 2 (t - d₀)) * ζ) using 1 + push_cast + ring + rw [crossRatio, crossRatio, map_mul, map_pow, map_ofNat, map_ringInverse ψ hU, map_add, + map_mul, map_pow, map_ofNat, map_intCast] + +/-- Mode coefficients commute with ring homomorphisms when `2` is nilpotent, at a grid point `t` +of valuation `i ≤ a`. -/ +theorem map_packetModeCoeff (ψ : R →+* S) {n : ℕ} (hn : (2 : R) ^ n = 0) (κ : ℤ₍₂₎ →+* R) + (ζ : R) {a i : ℕ} (hia : i ≤ a) {t : ℤ} (hti : (2 : ℤ) ^ i ∣ t) + (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) (differ : Bool) {p : ℕ} (x : ℕ) + (Z : Fin (2 ^ (i + 1)) × Fin p) : + ψ (packetModeCoeff κ ζ a i t differ x Z) = + packetModeCoeff (ψ.comp κ) (ψ ζ) a i t differ x Z := by + unfold packetModeCoeff + split_ifs with h + · rw [map_mul, map_pow, map_kernelValue, + map_crossRatio ψ hn hia (not_dvd_sub_sub_of_testBit_eq hti hti' Z.1.isLt h.2)] + · exact map_zero ψ + +/-- Local forms commute with ring homomorphisms. -/ +theorem map_packetLocalForm (ψ : R →+* S) (κ : ℤ₍₂₎ →+* R) (ζ : R) (a : ℕ) (t : ℤ) + (cols : List (ℕ × Bool)) (x : ℕ) : + ψ (packetLocalForm κ ζ a t cols x) = packetLocalForm (ψ.comp κ) (ψ ζ) a t cols x := by + rw [packetLocalForm, packetLocalForm, map_list_sum, List.map_map] + congr 1 + refine List.map_congr_left fun c _ => ?_ + simp only [Function.comp_apply, apply_ite ψ, map_zero, map_kernelValue] + +/-- Mode values commute with ring homomorphisms. -/ +theorem map_packetModeValue (ψ : R →+* S) (i : ℕ) (differ : Bool) {p : ℕ} + (cols : List (ℕ × Bool)) (Z : Fin (2 ^ (i + 1)) × Fin p) : + ψ (packetModeValue R i differ cols Z) = packetModeValue S i differ cols Z := by + rw [packetModeValue, packetModeValue, map_list_sum, List.map_map] + congr 1 + refine List.map_congr_left fun c _ => ?_ + simp only [Function.comp_apply, apply_ite ψ, map_zero, map_pow, map_natCast] + +/-- `listCoeff` commutes with ring homomorphisms. -/ +theorem map_listCoeff {M : Type*} [DecidableEq M] (ψ : R →+* S) (F : Finset M) : + ∀ (ts : List (Option M → R)) (γ : M → ℕ), + ψ (listCoeff F ts γ) = listCoeff F (ts.map fun T => ψ ∘ T) γ + | [], γ => by simp only [listCoeff_nil, List.map_nil, apply_ite ψ, map_one, map_zero] + | T :: ts, γ => by + rw [List.map_cons, listCoeff_cons, listCoeff_cons, map_add, map_mul, map_listCoeff ψ F ts, + map_sum] + refine congrArg _ (sum_congr rfl fun Z _ => ?_) + rw [apply_ite ψ, map_zero, map_mul, map_listCoeff ψ F ts] + rfl + +/-- **Packet entries commute with ring homomorphisms** when `2` is nilpotent, at a grid point `t` +of valuation `i ≤ a`. So a packet entry evaluated in one coefficient ring gives the entry in +every ring it maps to. -/ +theorem map_packetEntry (ψ : R →+* S) {n : ℕ} (hn : (2 : R) ^ n = 0) (κ : ℤ₍₂₎ →+* R) (ζ : R) + {a i : ℕ} (hia : i ≤ a) {t : ℤ} (hti : (2 : ℤ) ^ i ∣ t) (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) + {p : ℕ} (e₁ e₂ differ : Bool) (β : Fin (2 ^ (i + 1)) × Fin p → ℕ) + (rows cols : List (ℕ × Bool)) : + ψ (packetEntry κ ζ a i t e₁ e₂ differ β rows cols) = + packetEntry (ψ.comp κ) (ψ ζ) a i t e₁ e₂ differ β rows cols := by + rw [packetEntry, packetEntry, map_mul, map_listCoeff, map_prod, List.map_map] + congr 1 + · congr 1 + refine List.map_congr_left fun r _ => funext fun o => ?_ + cases o with + | none => + cases hr : r.2 <;> + simp only [Function.comp_apply, packetRowTerm, affineFactor, hr, Bool.false_eq_true, + ↓reduceIte, map_sub, map_one, map_packetLocalForm] + | some Z => + cases hr : r.2 <;> + simp only [Function.comp_apply, packetRowTerm, affineSign, hr, Bool.false_eq_true, + ↓reduceIte, map_mul, map_neg, map_one, map_packetModeCoeff ψ hn κ ζ hia hti hti'] + · refine prod_congr rfl fun Z _ => ?_ + rw [map_pow, map_packetModeValue] + +end Map + +/-! ### The packet entries of the Cauchy children -/ + +section Cauchy + +variable {R V E G P : Type*} [CommRing R] [Fintype E] [DecidableEq E] [Fintype P] [DecidableEq G] + [DecidableEq P] {N : TensorNetwork R Bool V E} {group : V → G} {packet : E → P} {slot : E → ℕ} + {sym : P → G × G} {i : ℕ} + (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) + (htgt : ∀ e, side (N.tgt e) = false) + (horient : ∀ e, (group (N.src e), group (N.tgt e)) = + if (slot e).testBit i then (sym (packet e)).swap else sym (packet e)) + {a : ℕ} (hslot : ∀ e, slot e < 2 ^ a) (hia : i < a) (κ : ℤ₍₂₎ →+* R) (ζ : R) {p : ℕ} + (hp : (2 : R) ^ p = 0) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) (hti : (2 : ℤ) ^ i ∣ t) + (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) + +/-- The modes of the packet `q`, as modes of the splitting. -/ +private def packetModes (q : P) : Fin (2 ^ (i + 1)) × Fin p ↪ P × Fin (2 ^ (i + 1)) × Fin p := + ⟨fun Z => (q, Z), fun _ _ h => (Prod.ext_iff.1 h).2⟩ + +omit [Fintype E] [DecidableEq E] [Fintype P] [DecidableEq G] [DecidableEq P] in +private theorem packetModes_apply (q : P) (Z : Fin (2 ^ (i + 1)) × Fin p) : + packetModes q Z = (q, Z) := + rfl + +omit [Fintype E] [DecidableEq E] in +/-- The modes targeting the block `(q, g)` are the modes of `q` whose tiles target `g`. -/ +private theorem targetBlock_eq_iff (q q' : P) (g : G) (Z : Fin (2 ^ (i + 1)) × Fin p) : + (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').targetBlock + (q', Z) = (q, g) ↔ + q' = q ∧ modeTargets i (decide ((sym q).1 = g)) (decide ((sym q).2 = g)) Z.1 = true := by + simp only [ModeSplitting.targetBlock, cauchyModeSplitting_modePacket, + cauchyModeSplitting_target, Prod.mk.injEq, modeTargets] + constructor + · rintro ⟨rfl, h⟩ + exact ⟨rfl, by split_ifs at h ⊢ <;> simp_all⟩ + · rintro ⟨rfl, h⟩ + exact ⟨rfl, by split_ifs at h ⊢ <;> simp_all⟩ + +omit [Fintype E] [DecidableEq E] in +/-- The modes with the source block `(q, g)` are the modes of `q` whose tiles have source `g`. -/ +private theorem sourceBlock_eq_iff (q q' : P) (g : G) (Z : Fin (2 ^ (i + 1)) × Fin p) : + (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').sourceBlock + (q', Z) = (q, g) ↔ + q' = q ∧ modeSources i (decide ((sym q).1 = g)) (decide ((sym q).2 = g)) Z.1 = true := by + simp only [ModeSplitting.sourceBlock, cauchyModeSplitting_modePacket, + cauchyModeSplitting_source, Prod.mk.injEq, modeSources] + constructor + · rintro ⟨rfl, h⟩ + exact ⟨rfl, by split_ifs at h ⊢ <;> simp_all⟩ + · rintro ⟨rfl, h⟩ + exact ⟨rfl, by split_ifs at h ⊢ <;> simp_all⟩ + +omit [DecidableEq E] in +/-- The local form of a row port of the block `(q, g)` is `packetLocalForm` on the column ports +of the packet `q` in the group `g`. -/ +private theorem localForm_cauchyModeSplitting (q : P) (g : G) (X : E → Bool) {cols : List E} + (hcols : cols.Nodup) (hcols' : ∀ e, e ∈ cols ↔ packet e = q ∧ group (N.tgt e) = g) {e : E} + (he : packet e = q) (heg : group (N.src e) = g) : + (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').localForm X e = + packetLocalForm κ ζ a t (cols.map fun e => (slot e, X e)) (slot e) := by + classical + rw [ModeSplitting.localForm, packetLocalForm, List.map_map, ← List.sum_toFinset _ hcols, + ← sum_subset (subset_univ _) fun e' _ he' => ?_] + · refine sum_congr rfl fun e' he' => ?_ + obtain ⟨hq, hg⟩ := (hcols' e').1 (List.mem_toFinset.1 he') + simp only [cauchyModeSplitting_localPart, cauchyValueMatrix, hg, heg, he, hq, ↓reduceIte, + Function.comp_apply, mul_ite, mul_one, mul_zero] + · have h' : ¬ (packet e' = q ∧ group (N.tgt e') = g) := fun h => + he' (List.mem_toFinset.2 ((hcols' e').2 h)) + simp only [cauchyModeSplitting_localPart, cauchyValueMatrix, heg, he] + split_ifs with h₁ h₂ <;> first | simp only [zero_mul] | exact absurd ⟨h₂.symm, h₁⟩ h' + +omit [DecidableEq E] in +/-- The value of a mode `(q, Z)` with source `g` is `packetModeValue` on the column ports of the +packet `q` in the group `g`. -/ +private theorem modeValue_cauchyModeSplitting (q : P) (g : G) (X : E → Bool) {cols : List E} + (hcols : cols.Nodup) (hcols' : ∀ e, e ∈ cols ↔ packet e = q ∧ group (N.tgt e) = g) + (Z : Fin (2 ^ (i + 1)) × Fin p) + (hZ : (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').source + (q, Z) = g) : + (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').modeValue X (q, Z) = + packetModeValue R i (decide ((sym q).1 ≠ (sym q).2)) (cols.map fun e => (slot e, X e)) Z := by + classical + set S := cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti' + rw [ModeSplitting.modeValue, packetModeValue, List.map_map, ← List.sum_toFinset _ hcols, + ← sum_subset (subset_univ _) fun e' _ he' => ?_] + · refine sum_congr rfl fun e' he' => ?_ + obtain ⟨hq, -⟩ := (hcols' e').1 (List.mem_toFinset.1 he') + simp only [S, cauchyModeSplitting_form, cauchyModeForm, hq, true_and, Function.comp_apply, + decide_eq_true_eq] + by_cases hX : X e' <;> simp [hX] + · -- A column port outside the block contributes nothing. + have h' : ¬ (packet e' = q ∧ group (N.tgt e') = g) := fun h => + he' (List.mem_toFinset.2 ((hcols' e').2 h)) + by_cases h0 : S.form (q, Z) e' = 0 + · rw [h0, zero_mul] + · obtain ⟨hq, hg⟩ := S.form_ne_zero (q, Z) e' h0 + exact absurd ⟨hq.symm, hg.symm.trans hZ⟩ h' + +/-- **The packet tensors of the Cauchy children.** For lists `rows` and `cols` of the row and +column ports of the packet `q` in the group `g`, without repetition and in any order, the packet +tensor of `cauchyModeSplitting` is `packetEntry` at their slots and bits. -/ +theorem packetTensor_cauchyModeSplitting (β : P × Fin (2 ^ (i + 1)) × Fin p → ℕ) (q : P) (g : G) + (Y X : E → Bool) {rows cols : List E} (hrows : rows.Nodup) + (hrows' : ∀ e, e ∈ rows ↔ packet e = q ∧ group (N.src e) = g) (hcols : cols.Nodup) + (hcols' : ∀ e, e ∈ cols ↔ packet e = q ∧ group (N.tgt e) = g) : + (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').packetTensor β q g + Y X = + packetEntry κ ζ a i t (decide ((sym q).1 = g)) (decide ((sym q).2 = g)) + (decide ((sym q).1 ≠ (sym q).2)) (fun Z => β (q, Z)) (rows.map fun e => (slot e, Y e)) + (cols.map fun e => (slot e, X e)) := by + set S := cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti' + have htarget : ({Z | S.targetBlock Z = (q, g)} : Finset (P × Fin (2 ^ (i + 1)) × Fin p)) = + ({Z | modeTargets i (decide ((sym q).1 = g)) (decide ((sym q).2 = g)) Z.1 = true} : + Finset (Fin (2 ^ (i + 1)) × Fin p)).map (packetModes q) := by + ext ⟨q', Z⟩ + simp only [S, mem_filter, mem_univ, true_and, mem_map, targetBlock_eq_iff, packetModes_apply, + Prod.mk.injEq] + constructor + · rintro ⟨rfl, h⟩ + exact ⟨Z, h, rfl, rfl⟩ + · rintro ⟨Z', h, rfl, rfl⟩ + exact ⟨rfl, h⟩ + have hsource : ({Z | S.sourceBlock Z = (q, g)} : Finset (P × Fin (2 ^ (i + 1)) × Fin p)) = + ({Z | modeSources i (decide ((sym q).1 = g)) (decide ((sym q).2 = g)) Z.1 = true} : + Finset (Fin (2 ^ (i + 1)) × Fin p)).map (packetModes q) := by + ext ⟨q', Z⟩ + simp only [S, mem_filter, mem_univ, true_and, mem_map, sourceBlock_eq_iff, packetModes_apply, + Prod.mk.injEq] + constructor + · rintro ⟨rfl, h⟩ + exact ⟨Z, h, rfl, rfl⟩ + · rintro ⟨Z', h, rfl, rfl⟩ + exact ⟨rfl, h⟩ + rw [ModeSplitting.packetTensor, packetEntry, S.blockCoeff_eq_listCoeff (q, g) β Y X hrows + (fun e => (hrows' e).trans (by rw [ModeSplitting.rowBlock, Prod.mk.injEq]; exact Iff.rfl)), + htarget, listCoeff_map, hsource, prod_map, + List.map_map, List.map_map] + congr 1 + · congr 1 + refine List.map_congr_left fun e he => funext fun o => ?_ + obtain ⟨hq, hg⟩ := (hrows' e).1 he + cases o with + | none => + simp only [Function.comp_apply, Option.map_none, ModeSplitting.choiceTerm, packetRowTerm] + rw [localForm_cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti' q g X + hcols hcols' hq hg] + | some Z => + simp only [Function.comp_apply, Option.map_some, ModeSplitting.choiceTerm, packetRowTerm, + S, cauchyModeSplitting_coeff, cauchyModeCoeff, packetModeCoeff, packetModes_apply, hq, + true_and, decide_eq_true_eq] + · refine prod_congr rfl fun Z hZ => ?_ + have hZ' : S.source (q, Z) = g := congrArg Prod.snd + ((sourceBlock_eq_iff side hsrc htgt horient hslot hia κ ζ hp ht hti hti' q q g Z).2 + ⟨rfl, (mem_filter.1 hZ).2⟩) + rw [packetModes_apply, modeValue_cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht + hti hti' q g X hcols hcols' Z hZ'] + +/-- **The packet entries of the Cauchy children** (§D.4.6, p. 63): the entry of the packet vertex +`q` of the child of group `g` at the bits `y` on its ports is `packetEntry` at the slots and bits +of its row ports and column ports, listed without repetition and in any order. -/ +theorem child_tensor_inr_cauchyModeSplitting [DecidableEq V] + (β : P × Fin (2 ^ (i + 1)) × Fin p → ℕ) (g : G) (q : P) + (y : {e // (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').group + (N.src e) = g} ⊕ + {e // (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').group + (N.tgt e) = g} → Bool) + {rows cols : List E} (hrows : rows.Nodup) + (hrows' : ∀ e, e ∈ rows ↔ packet e = q ∧ group (N.src e) = g) + (hcols : cols.Nodup) (hcols' : ∀ e, e ∈ cols ↔ packet e = q ∧ group (N.tgt e) = g) : + ((cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').child β g).tensor + (.inr q) y = + packetEntry κ ζ a i t (decide ((sym q).1 = g)) (decide ((sym q).2 = g)) + (decide ((sym q).1 ≠ (sym q).2)) (fun Z => β (q, Z)) + (rows.map fun e => (slot e, if h : _ = g then y (.inl ⟨e, h⟩) else false)) + (cols.map fun e => (slot e, if h : _ = g then y (.inr ⟨e, h⟩) else false)) := + packetTensor_cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti' β q g _ _ + hrows hrows' hcols hcols' + +end Cauchy + +end TensorNetwork + +end Cslib.Circuits.Frontier diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEntry.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEntry.lean new file mode 100644 index 0000000000..fe79b79655 --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEntry.lean @@ -0,0 +1,75 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEntry +public import ComplexityTests.Computability.Circuit.Frontier.TensorNetworkCauchyModes + +/-! +# Tests for the entries of the child networks + +The network of the Cauchy mode tests: one packet of two slots, oriented in coordinate `0`, with +groups `false` and `true`. Edge `0` runs from row `0` (group `false`) to column `2` (group +`true`), and edge `1` from row `1` (group `true`) to column `3` (group `false`). So the packet +vertex of the child of group `false` has one row port, edge `0` in slot `0`, and one column port, +edge `1` in slot `1`. +-/ + +@[expose] public section + +namespace ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.PacketEntry + +open Cslib.Circuits.Frontier TensorNetwork +open ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.CauchyModes + +/-- Coefficient extraction: the coefficient of `m` in `(2 + 3 m) (5 + 7 m)` is `2 · 7 + 3 · 5`. -/ +example : listCoeff (R := ℤ) ({()} : Finset Unit) + [fun o => o.elim 2 fun _ => 3, fun o => o.elim 5 fun _ => 7] (fun _ => 1) = 29 := by + simp [listCoeff] + +/-- The coefficient of `m ^ 2` is `3 · 7`, and there is no `m ^ 3` term. -/ +example : listCoeff (R := ℤ) ({()} : Finset Unit) + [fun o => o.elim 2 fun _ => 3, fun o => o.elim 5 fun _ => 7] (fun _ => 2) = 21 ∧ + listCoeff (R := ℤ) ({()} : Finset Unit) + [fun o => o.elim 2 fun _ => 3, fun o => o.elim 5 fun _ => 7] (fun _ => 3) = 0 := by + simp [listCoeff] + +/-- The Cauchy mode splitting of the test network at `T = 1 + 4 ζ`. -/ +noncomputable abbrev split (ζ : ZMod (2 ^ 2)) := + cauchyModeSplitting (sym := sym) (i := 0) side (by decide) (by decide) horient (a := 1) + (fun e => by fin_cases e <;> decide) (by decide) κ ζ (p := 2) (by decide) (t := 1) (by decide) + (by decide) (by decide) + +/-- **The packet entry of the child of group `false`**, at the bits `y`, is `packetEntry` on its row +port (edge `0`, slot `0`) and its column port (edge `1`, slot `1`). -/ +example (ζ : ZMod (2 ^ 2)) (β : Unit × Fin (2 ^ (0 + 1)) × Fin 2 → ℕ) + (y : {e // (split ζ).group (net.src e) = false} ⊕ {e // (split ζ).group (net.tgt e) = false} → + Bool) : + ((split ζ).child β false).tensor (.inr ()) y = + packetEntry κ ζ 1 0 1 true false true (fun Z => β ((), Z)) + ([0].map fun e => (slot e, if h : _ = false then y (.inl ⟨e, h⟩) else false)) + ([1].map fun e => (slot e, if h : _ = false then y (.inr ⟨e, h⟩) else false)) := + child_tensor_inr_cauchyModeSplitting side (by decide) (by decide) horient + (fun e => by fin_cases e <;> decide) (by decide) κ ζ (by decide) (by decide) (by decide) + (by decide) β false () y (rows := [0]) (cols := [1]) (by decide) + (fun e => by fin_cases e <;> decide) (by decide) (fun e => by fin_cases e <;> decide) + +/-- With no ports and zero exponents, a packet entry is `1`. -/ +example (ζ : ZMod (2 ^ 2)) : + packetEntry (p := 2) κ ζ 1 0 1 true false true (fun _ => 0) [] [] = 1 := by + simp [packetEntry] + +/-- Packet entries reduce along `ℤ / 4 → ℤ / 2`. -/ +example (ζ : ZMod (2 ^ 2)) (β : Fin (2 ^ (0 + 1)) × Fin 2 → ℕ) (rows cols : List (ℕ × Bool)) : + ZMod.castHom (by decide : 2 ∣ 2 ^ 2) (ZMod 2) + (packetEntry κ ζ 1 0 1 true false true β rows cols) = + packetEntry ((ZMod.castHom (by decide : 2 ∣ 2 ^ 2) (ZMod 2)).comp κ) + (ZMod.castHom (by decide : 2 ∣ 2 ^ 2) (ZMod 2) ζ) 1 0 1 true false true β rows cols := + map_packetEntry _ (n := 2) (by decide) κ ζ (by decide) (by decide) (by decide) _ _ _ β rows cols + +end ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.PacketEntry From 7999990a6fc5a2970a7b95b92122923bd0d355ae Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 23:52:05 -0400 Subject: [PATCH 038/129] wip: reciprocal and multiplier correctness Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/NativeRecovery.lean | 231 ++++++++++++++++++ 1 file changed, 231 insertions(+) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean index 11c89c7025..17729dea7e 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean @@ -9,6 +9,7 @@ module public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ActiveCall public import Complexity.Computability.PolyTime.GaloisRingSeries +import Mathlib.Data.List.GetD /-! # Native tapered recovery (Lemma D.5) on coefficient lists @@ -260,4 +261,234 @@ theorem val_slopeConst (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (Z · rw [val_neg, hw, map_neg, map_natCast, neg_mul] · rw [hw, map_natCast] +/-- The slope `2 ^ (a + 1) / (t - q)` for node indices `k, k'`, and `0` off the grid. -/ +noncomputable def slope' (a k k' : ℕ) : ℤ₍₂₎ := + if h : k < 2 ^ (a + 1) ∧ k' < 2 ^ (a + 1) ∧ k' ≠ k then + slope a (node_mem_cauchyGrid h.1) (node_mem_cauchyGrid h.2.1) + (fun e => h.2.2 (node_injective a e)) + else 0 + +theorem val_slopeConst' (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2)))) + (ρ : ℤ₍₂₎ →+* Ring N fl) {a k k' : ℕ} (hk : k < 2 ^ (a + 1)) (hk' : k' < 2 ^ (a + 1)) + (hne : k' ≠ k) : val N fl (slopeConst N fl a k k') = ρ (slope' a k k') := by + rw [val_slopeConst hirr ρ hk hk' hne, slope', dite_eq_left_of_eq_true (eq_true ⟨hk, hk', hne⟩)] + +/-! ### Correctness of the reciprocal -/ + +/-- The geometric series `∑ (-c) ^ i z ^ i`, the inverse of `1 + c z`. -/ +noncomputable def geomSeries {R : Type*} [CommRing R] (c : R) : PowerSeries R := + PowerSeries.mk fun i => (-c) ^ i + +theorem one_add_mul_geomSeries {R : Type*} [CommRing R] (c : R) : + (1 + PowerSeries.C c * PowerSeries.X) * geomSeries c = 1 := by + ext n + rw [add_mul, one_mul, map_add, PowerSeries.coeff_one, mul_assoc] + rcases n with _ | n + · simp [geomSeries] + · rw [PowerSeries.coeff_C_mul, PowerSeries.coeff_succ_X_mul] + simp only [geomSeries, PowerSeries.coeff_mk, pow_succ] + simp only [Nat.succ_ne_zero, ↓reduceIte, mul_neg] + ring + +/-- The reciprocal of `E_t(t + α z)` over `ℤ_(2)` for the node of index `k`, in the order of +`NativeRecovery.recip`. -/ +noncomputable def recipSeries (a u h k : ℕ) : PowerSeries ℤ₍₂₎ := + (others a k).foldl (fun ψ k' => ψ * geomSeries (slope' a k k') ^ mult a u h k') 1 + +theorem mem_others {a k k' : ℕ} : k' ∈ others a k ↔ k' < 2 ^ (a + 1) ∧ k' ≠ 2 ^ a ∧ k' ≠ k := by + simp [others] + +/-- A truncated product computes the coefficients of the product of power series. -/ +theorem val_tmul_eq {ρ : ℤ₍₂₎ →+* Ring N fl} {J : ℕ} {P G : List (List ℕ)} + {φ χ : PowerSeries ℤ₍₂₎} (hP : ∀ i < J, val N fl (P.getD i []) = ρ (PowerSeries.coeff i φ)) + (hG : ∀ i < J, val N fl (G.getD i []) = ρ (PowerSeries.coeff i χ)) {i : ℕ} (hi : i < J) : + val N fl ((tmul N fl J P G).getD i []) = ρ (PowerSeries.coeff i (φ * χ)) := by + rw [val_tmul _ _ hi, PowerSeries.coeff_mul, Finset.Nat.sum_antidiagonal_eq_sum_range_succ + (fun l m => PowerSeries.coeff l φ * PowerSeries.coeff m χ), map_sum] + refine sum_congr rfl fun l hl => ?_ + have hl' := mem_range.mp hl + rw [hP l (by omega), hG (i - l) (by omega), map_mul] + +theorem val_geom {ρ : ℤ₍₂₎ →+* Ring N fl} {J : ℕ} {c : List ℕ} {γ : ℤ₍₂₎} + (hc : val N fl c = ρ γ) {i : ℕ} (hi : i < J) : + val N fl ((geom N fl J c).getD i []) = ρ (PowerSeries.coeff i (geomSeries γ)) := by + rw [geom, List.getD_eq_getElem _ _ (by simpa using hi), List.getElem_map, List.getElem_range, + val_pow, val_neg, hc, geomSeries, PowerSeries.coeff_mk, map_pow, map_neg] + +/-- **The native reciprocal is correct**: its coefficients below `z ^ J` are the images of those +of `NativeRecovery.recipSeries`. -/ +theorem val_recip (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2)))) + (ρ : ℤ₍₂₎ →+* Ring N fl) {a u h k J : ℕ} (hk : k < 2 ^ (a + 1)) {i : ℕ} (hi : i < J) : + val N fl ((recip N fl a u h k J).getD i []) = ρ (PowerSeries.coeff i (recipSeries a u h k)) := by + -- The invariant of the fold over the other nodes. + suffices H : ∀ (l : List ℕ), (∀ k' ∈ l, k' < 2 ^ (a + 1) ∧ k' ≠ k) → + ∀ (R : List (List ℕ)) (ψ : PowerSeries ℤ₍₂₎), + (∀ i < J, val N fl (R.getD i []) = ρ (PowerSeries.coeff i ψ)) → + ∀ i < J, val N fl ((l.foldl (fun R k' => + mulGeomPow N fl J (slopeConst N fl a k k') (mult a u h k') R) R).getD i []) = + ρ (PowerSeries.coeff i (l.foldl (fun ψ k' => ψ * geomSeries (slope' a k k') ^ + mult a u h k') ψ)) by + refine H _ (fun k' hk' => ⟨(mem_others.mp hk').1, (mem_others.mp hk').2.2⟩) _ _ ?_ i hi + intro i _ + rcases i with _ | i + · simp [PowerSeries.coeff_one] + · rw [List.getD_cons_succ, List.getD_nil, val_nil, PowerSeries.coeff_one, if_neg (by omega), map_zero] + intro l + induction l with + | nil => intro _ R ψ hR; simpa using hR + | cons k' l ih => + intro hl R ψ hR + rw [List.foldl_cons, List.foldl_cons] + refine ih (fun k'' hk'' => hl k'' (List.mem_cons_of_mem _ hk'')) _ _ ?_ + obtain ⟨hk', hne⟩ := hl k' List.mem_cons_self + have hc := val_slopeConst' hirr ρ hk hk' hne + -- `m` multiplications by the geometric series. + have hm (m : ℕ) : ∀ i < J, val N fl ((mulGeomPow N fl J (slopeConst N fl a k k') m R).getD + i []) = ρ (PowerSeries.coeff i (ψ * geomSeries (slope' a k k') ^ m)) := by + induction m with + | zero => simpa [mulGeomPow] using hR + | succ m ihm => + intro i hi + rw [mulGeomPow, List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil, + ← mulGeomPow, pow_succ, ← mul_assoc] + exact val_tmul_eq ihm (fun i hi => val_geom hc hi) hi + exact hm _ + +/-! ### Products over the nodes -/ + +/-- The other nodes, as integers, are the nonzero grid points other than `t`. -/ +theorem prod_others {M : Type*} [CommMonoid M] (f : ℤ → M) {a k : ℕ} : + ((others a k).map fun k' => f (node a k')).prod = + ∏ q ∈ (recoveryNodes a).erase (node a k), f q := by + have hnd : ((others a k).map (node a)).Nodup := + (List.nodup_range.filter _).map (node_injective a) + have hset : ((others a k).map (node a)).toFinset = (recoveryNodes a).erase (node a k) := by + ext q + simp only [List.mem_toFinset, List.mem_map, mem_others, recoveryNodes_eq, mem_erase, mem_Ico] + obtain ⟨L, hL⟩ : ∃ L : ℕ, 2 ^ a = L := ⟨_, rfl⟩ + have hLz : (2 : ℤ) ^ a = (L : ℤ) := by rw [← hL]; push_cast; rfl + have hL2 : (2 : ℕ) ^ (a + 1) = 2 * L := by rw [← hL]; ring + simp only [node, hLz, hL, hL2] + constructor + · rintro ⟨k', ⟨hk'1, hk'2, hk'3⟩, rfl⟩ + omega + · rintro ⟨hqk, hq0, hq1, hq2⟩ + exact ⟨(q + L).toNat, ⟨by omega, by omega, by omega⟩, by omega⟩ + rw [← hset, List.prod_toFinset _ hnd, List.map_map] + rfl + +/-- The nodes are the images of the indices `k < 2 ^ (a + 1)`, `k ≠ 2 ^ a`. -/ +theorem recoveryNodes_eq_image (a : ℕ) : + recoveryNodes a = ((range (2 ^ (a + 1))).filter (· ≠ 2 ^ a)).image (node a) := by + ext q + simp only [recoveryNodes_eq, mem_erase, mem_Ico, mem_image, mem_filter, mem_range] + obtain ⟨L, hL⟩ : ∃ L : ℕ, 2 ^ a = L := ⟨_, rfl⟩ + have hLz : (2 : ℤ) ^ a = (L : ℤ) := by rw [← hL]; push_cast; rfl + have hL2 : (2 : ℕ) ^ (a + 1) = 2 * L := by rw [← hL]; ring + simp only [node, hLz, hL, hL2] + constructor + · rintro ⟨hq0, hq1, hq2⟩ + exact ⟨(q + L).toNat, ⟨by omega, by omega⟩, by omega⟩ + · rintro ⟨k', ⟨hk'1, hk'2⟩, rfl⟩ + omega + +/-- A sum over the nodes is a sum over the node indices. -/ +theorem sum_recoveryNodes {M : Type*} [AddCommMonoid M] (f : ℤ → M) (a : ℕ) : + ∑ t ∈ recoveryNodes a, f t = + ∑ k ∈ range (2 ^ (a + 1)), if k = 2 ^ a then 0 else f (node a k) := by + rw [recoveryNodes_eq_image, sum_image fun _ _ _ _ e => node_injective a e, sum_ite, + sum_const_zero, zero_add] + +/-! ### Correctness of the multipliers -/ + +/-- A fold of multiplications is a product. -/ +theorem val_foldl_mul {ι : Type*} (g : ι → List ℕ) (l : List ι) (c : List ℕ) : + val N fl (l.foldl (fun acc x => mul N fl acc (g x)) c) = + val N fl c * (l.map fun x => val N fl (g x)).prod := by + induction l generalizing c with + | nil => simp + | cons x l ih => rw [List.foldl_cons, ih, val_mul, List.map_cons, List.prod_cons, mul_assoc] + +/-- The multiplier `2 ^ Δ E_t(0) (-t / α) ^ j` as a `2`-adic integer: +`2 ^ (Δ - (a + 1)(M_t + j)) ∏_{q ≠ t} (-q c_q) ^ (q_q) (-t) ^ j`. -/ +noncomputable def gamma (a u h Δ k j : ℕ) : ℤ₍₂₎ := + ((2 ^ (Δ - (a + 1) * (multSum a u h k + j)) : ℕ) : ℤ₍₂₎) * + ((others a k).map fun k' : ℕ => + (((((2 ^ a : ℕ) : ℤ) - (k' : ℤ) : ℤ) : ℤ₍₂₎) * slope' a k k') ^ mult a u h k').prod * + ((((2 ^ a : ℕ) : ℤ) - k : ℤ) : ℤ₍₂₎) ^ j + +/-- **The native multiplier is correct.** -/ +theorem val_coeffMult (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2)))) + (ρ : ℤ₍₂₎ →+* Ring N fl) {a u h Δ k : ℕ} (hk : k < 2 ^ (a + 1)) (j : ℕ) : + val N fl (coeffMult N fl a u h Δ k j) = ρ (gamma a u h Δ k j) := by + rw [coeffMult, val_mul, val_mul, val_singleton, val_pow, val_diffConst, basisNum, + val_foldl_mul, val_one, one_mul, gamma, map_mul, map_mul, map_natCast, map_pow, map_intCast, + map_list_prod, List.map_map] + congr 2 + refine congrArg List.prod (List.map_congr_left fun k' hk' => ?_) + obtain ⟨hk'1, -, hk'3⟩ := mem_others.mp hk' + simp only [Function.comp_apply, val_pow, val_mul, val_diffConst, val_slopeConst' hirr ρ hk hk'1 + hk'3, map_pow, map_mul, map_intCast] + +/-- The value `E_t(0)` over `ℚ` as a product over the other node indices. -/ +theorem eval_zero_hermiteBasis_eq_prod (a u h k : ℕ) : + (hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : ℚ)) (taperMult u h) (node a k)).eval 0 = + ((others a k).map fun k' => + (_root_.Ring.inverse ((node a k : ℚ) - node a k') * (0 - node a k')) ^ + mult a u h k').prod := by + rw [eval_hermiteBasis, ← prod_others] + simp only [mult_eq] + +/-- **The multiplier is `2 ^ Δ E_t(0) (-t / α) ^ j`.** -/ +theorem coe_gamma {a u h Δ k j : ℕ} (hk : k < 2 ^ (a + 1)) + (hΔ : (a + 1) * (multSum a u h k + j) ≤ Δ) : + ((gamma a u h Δ k j : ℤ₍₂₎) : ℚ) = 2 ^ Δ * + (hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : ℚ)) (taperMult u h) (node a k)).eval 0 * + (-(node a k : ℚ) / 2 ^ (a + 1)) ^ j := by + rw [eval_zero_hermiteBasis_eq_prod] + -- Each factor is `(-q c_q / α) ^ (q_q)`. + set X : ℕ → ℚ := fun k' => ((((((2 ^ a : ℕ) : ℤ) - (k' : ℤ) : ℤ) : ℤ₍₂₎) * slope' a k k' : + ℤ₍₂₎) : ℚ) + have hfac : ∀ k' ∈ others a k, + (_root_.Ring.inverse ((node a k : ℚ) - node a k') * (0 - node a k')) ^ mult a u h k' = + X k' ^ mult a u h k' / (2 ^ (a + 1)) ^ mult a u h k' := by + intro k' hk' + obtain ⟨hk'1, -, hk'3⟩ := mem_others.mp hk' + have hne : (node a k : ℚ) - node a k' ≠ 0 := by + rw [sub_ne_zero]; exact_mod_cast fun e => hk'3 (node_injective a e).symm + have hs : ((slope' a k k' : ℤ₍₂₎) : ℚ) = 2 ^ (a + 1) / ((node a k : ℚ) - node a k') := by + rw [slope', dite_eq_left_of_eq_true (eq_true ⟨hk, hk'1, hk'3⟩)]; rfl + rw [← div_pow] + congr 1 + simp only [X, Subring.coe_mul, hs, Subring.coe_intCast, _root_.Ring.inverse_eq_inv'] + have hq : ((Int.cast (((2 ^ a : ℕ) : ℤ) - (k' : ℤ))) : ℚ) = -(node a k' : ℚ) := by + simp only [node]; push_cast; ring + rw [hq, zero_sub] + field_simp + have hprod : ∀ l : List ℕ, (l.map fun k' => X k' ^ mult a u h k' / + (2 ^ (a + 1) : ℚ) ^ mult a u h k').prod = + (l.map fun k' => X k' ^ mult a u h k').prod / (2 ^ (a + 1) : ℚ) ^ (l.map (mult a u h)).sum := by + intro l + induction l with + | nil => simp + | cons x l ih => rw [List.map_cons, List.prod_cons, ih, List.map_cons, List.prod_cons, + List.map_cons, List.sum_cons, div_mul_div_comm, ← pow_add] + rw [List.map_congr_left hfac, hprod, gamma, Subring.coe_mul, Subring.coe_mul, Subring.coe_pow, + Subring.coe_natCast, Subring.coe_intCast] + have hlist : ((((others a k).map fun k' : ℕ => + (((((2 ^ a : ℕ) : ℤ) - (k' : ℤ) : ℤ) : ℤ₍₂₎) * slope' a k k') ^ mult a u h k').prod : + ℤ₍₂₎) : ℚ) = ((others a k).map fun k' => X k' ^ mult a u h k').prod := by + refine (map_list_prod (ℤ₍₂₎).subtype _).trans ?_ + rw [List.map_map] + rfl + rw [hlist, ← multSum] + have h2 : (2 : ℚ) ^ Δ = 2 ^ (Δ - (a + 1) * (multSum a u h k + j)) * + ((2 : ℚ) ^ (a + 1)) ^ (multSum a u h k + j) := by + rw [← pow_mul, ← pow_add, Nat.sub_add_cancel hΔ] + rw [h2, show (-(node a k : ℚ)) = ((Int.cast (((2 ^ a : ℕ) : ℤ) - (k : ℤ))) : ℚ) by + simp only [node]; push_cast; ring, div_pow, pow_add] + field_simp + push_cast + ring + end Cslib.Circuits.Frontier.NativeRecovery From 3efcd6b73732f14af256fc443750d49cf6cad346 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 23:56:25 -0400 Subject: [PATCH 039/129] feat(circuit/frontier): native tapered recovery components (Lemma D.5) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Native recovery in A_(p+Δ) on coefficient lists: interpolation, the truncated reciprocal of E_t(t + αz) as a product of geometric series, the multipliers 2^Δ E_t(0)(-t/α)^j as 2-adic integers, and cancellation of 2^Δ. Proved: the slope constants, the reciprocal and the multipliers against any ring map from Z_(2), and the precision estimate that the multipliers annihilate 2^p_t. Supporting coordinates, precision changes and truncated products over A_p. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 2 + .../TensorNetwork/NativeRecovery.lean | 139 ++++++++++++++++-- .../PolyTime/GaloisRingSeries.lean | 7 +- .../Frontier/TensorNetworkNativeRecovery.lean | 52 +++++++ 4 files changed, 187 insertions(+), 13 deletions(-) create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean diff --git a/Complexity.lean b/Complexity.lean index b1df660e8b..d717434438 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -885,6 +885,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates public import Complexity.Computability.Circuit.Frontier.TensorNetwork.LocalDeformation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeRecovery public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Recursion public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Reindex public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Schedule @@ -2007,6 +2008,7 @@ public import Complexity.Computability.PolyTime.CommonCountingAccum public import Complexity.Computability.PolyTime.FineGrained.ParameterCheck public import Complexity.Computability.PolyTime.GaloisRingArithmetic public import Complexity.Computability.PolyTime.GaloisRingInterpolation +public import Complexity.Computability.PolyTime.GaloisRingSeries public import Complexity.Computability.PolyTime.GaloisRingUnits public import Complexity.Computability.PolyTime.GreedyLabelSchedule public import Complexity.Computability.PolyTime.GreedySlotScore diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean index 17729dea7e..ea11c070b1 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean @@ -44,16 +44,34 @@ The sum over the nodes is `2 ^ Δ` times the recovered value. Dividing its coord ((D.10) and the evaluation loss), so the error is killed in `A_N`. This is the precision estimate of Lemma D.5 (`Cslib.Circuits.Frontier.exists_taperedRecovery_eq_add`), carried out natively. +**Status.** This file proves the components: the constants, the reciprocal, the multipliers and +the precision estimate, each against an arbitrary ring homomorphism `ρ : ℤ_(2) → A_N`. The +assembly into `recover = recoverFromValues mod 2 ^ p` (through a reduction `B → A_N` and the +interpolation of `GaloisRingArith.interp`) and the polynomial-time certificate are not yet done. + ## Main definitions * `Cslib.Circuits.Frontier.NativeRecovery.slopeConst`: the constant `α / (t - q)`. * `Cslib.Circuits.Frontier.NativeRecovery.recip`: the truncated reciprocal of `E_t(t + α z)`. * `Cslib.Circuits.Frontier.NativeRecovery.coeffMult`: the multipliers. * `Cslib.Circuits.Frontier.NativeRecovery.recover`: native tapered recovery. +* `Cslib.Circuits.Frontier.NativeRecovery.recipSeries`, + `Cslib.Circuits.Frontier.NativeRecovery.gamma`: the reciprocal and the multipliers over + `ℤ_(2)`. ## Main results -* `Cslib.Circuits.Frontier.NativeRecovery.val_slopeConst`: the constant is correct. +* `Cslib.Circuits.Frontier.NativeRecovery.val_slopeConst`: the constant `α / (t - q)` is correct. +* `Cslib.Circuits.Frontier.NativeRecovery.val_recip`: the native reciprocal is correct below + `z ^ J`. +* `Cslib.Circuits.Frontier.NativeRecovery.val_coeffMult`, + `Cslib.Circuits.Frontier.NativeRecovery.coe_gamma`: the native multiplier is the image of + `2 ^ Δ E_t(0) (-t / α) ^ j`. +* `Cslib.Circuits.Frontier.NativeRecovery.gamma_mul_two_pow_eq_zero`: under the precision + hypothesis of Lemma D.5, the multipliers annihilate `2 ^ p_t` in `A_(p + Δ)`. +* `Cslib.Circuits.Frontier.NativeRecovery.sum_recoveryNodes`, + `Cslib.Circuits.Frontier.NativeRecovery.prod_others`: sums and products over the nodes as + iterations over node indices. ## References @@ -75,14 +93,36 @@ local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) /-- The node `t = k - 2 ^ a` of index `k`. -/ def node (a k : ℕ) : ℤ := (k : ℤ) - 2 ^ a +/-- The `2`-adic valuation of `d`, by at most `b` halvings: `v₂ d` when `d < 2 ^ b` +(`NativeRecovery.twoVal_eq`), and `0` for `d = 0`. -/ +def twoVal : ℕ → ℕ → ℕ + | 0, _ => 0 + | b + 1, d => if d ≠ 0 ∧ d % 2 = 0 then twoVal b (d / 2) + 1 else 0 + +theorem twoVal_eq : ∀ {b d : ℕ}, d < 2 ^ b → twoVal b d = padicValNat 2 d + | 0, d, hd => by rw [pow_zero] at hd; obtain rfl : d = 0 := by omega + simp [twoVal] + | b + 1, d, hd => by + rw [twoVal] + split_ifs with h + · obtain ⟨hd0, h2⟩ := h + have hd' : d = 2 * (d / 2) := by omega + rw [twoVal_eq (by rw [pow_succ] at hd; omega)] + conv_rhs => rw [hd'] + rw [padicValNat.mul (by norm_num) (by omega), padicValNat_self, add_comm] + · rcases Nat.eq_zero_or_pos d with rfl | hpos + · simp + · exact (padicValNat.eq_zero_of_not_dvd (by omega)).symm + /-- The multiplicity `q_t = h (u - v₂ t)` of the node of index `k`. -/ -def mult (a u h k : ℕ) : ℕ := h * (u - padicValNat 2 (k - 2 ^ a + (2 ^ a - k))) +def mult (a u h k : ℕ) : ℕ := h * (u - twoVal (a + 1) (k - 2 ^ a + (2 ^ a - k))) -theorem mult_eq (a u h k : ℕ) : mult a u h k = taperMult u h (node a k) := by +theorem mult_eq {a k : ℕ} (u h : ℕ) (hk : k < 2 ^ (a + 1)) : + mult a u h k = taperMult u h (node a k) := by have : ((k : ℤ) - 2 ^ a).natAbs = k - 2 ^ a + (2 ^ a - k) := by rw [show (2 : ℤ) ^ a = ((2 ^ a : ℕ) : ℤ) by push_cast; rfl] omega - rw [mult, taperMult, node, padicValInt, this] + rw [mult, taperMult, node, padicValInt, this, twoVal_eq (by rw [pow_succ] at hk ⊢; omega)] /-- The indices of the nodes other than the node of index `k`. -/ def others (a k : ℕ) : List ℕ := @@ -102,7 +142,7 @@ def diffConst (N : ℕ) (fl : List ℕ) (x y : ℕ) : List ℕ := `± 2 ^ (a + 1 - v) o⁻¹`. -/ def slopeConst (N : ℕ) (fl : List ℕ) (a x y : ℕ) : List ℕ := let d := x - y + (y - x) - let v := padicValNat 2 d + let v := twoVal (a + 1) d let w := mul N fl [2 ^ (a + 1 - v)] (inv N fl [d / 2 ^ v]) if x < y then neg N fl w else w @@ -256,7 +296,8 @@ theorem val_slopeConst (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (Z have hw : val N fl (mul N fl [2 ^ (a + 1 - v)] (inv N fl [o])) = ((2 ^ (a + 1 - v) : ℕ) : Ring N fl) * _root_.Ring.inverse (o : Ring N fl) := by rw [val_mul, val_singleton, val_inv_of_isUnit hirr (by rwa [val_singleton]), val_singleton] - unfold slopeConst + simp only [slopeConst] + rw [show twoVal (a + 1) (k - k' + (k' - k)) = v from twoVal_eq hdlt] split_ifs with h · rw [val_neg, hw, map_neg, map_natCast, neg_mul] · rw [hw, map_natCast] @@ -319,7 +360,8 @@ theorem val_geom {ρ : ℤ₍₂₎ →+* Ring N fl} {J : ℕ} {c : List ℕ} { of `NativeRecovery.recipSeries`. -/ theorem val_recip (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2)))) (ρ : ℤ₍₂₎ →+* Ring N fl) {a u h k J : ℕ} (hk : k < 2 ^ (a + 1)) {i : ℕ} (hi : i < J) : - val N fl ((recip N fl a u h k J).getD i []) = ρ (PowerSeries.coeff i (recipSeries a u h k)) := by + val N fl ((recip N fl a u h k J).getD i []) = + ρ (PowerSeries.coeff i (recipSeries a u h k)) := by -- The invariant of the fold over the other nodes. suffices H : ∀ (l : List ℕ), (∀ k' ∈ l, k' < 2 ^ (a + 1) ∧ k' ≠ k) → ∀ (R : List (List ℕ)) (ψ : PowerSeries ℤ₍₂₎), @@ -332,7 +374,8 @@ theorem val_recip (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2 intro i _ rcases i with _ | i · simp [PowerSeries.coeff_one] - · rw [List.getD_cons_succ, List.getD_nil, val_nil, PowerSeries.coeff_one, if_neg (by omega), map_zero] + · rw [List.getD_cons_succ, List.getD_nil, val_nil, PowerSeries.coeff_one, + ite_eq_right_of_eq_false _ _ (eq_false (Nat.succ_ne_zero i)), map_zero] intro l induction l with | nil => intro _ R ψ hR; simpa using hR @@ -437,7 +480,8 @@ theorem eval_zero_hermiteBasis_eq_prod (a u h k : ℕ) : (_root_.Ring.inverse ((node a k : ℚ) - node a k') * (0 - node a k')) ^ mult a u h k').prod := by rw [eval_hermiteBasis, ← prod_others] - simp only [mult_eq] + refine congrArg List.prod (List.map_congr_left fun k' hk' => ?_) + rw [mult_eq u h (mem_others.mp hk').1] /-- **The multiplier is `2 ^ Δ E_t(0) (-t / α) ^ j`.** -/ theorem coe_gamma {a u h Δ k j : ℕ} (hk : k < 2 ^ (a + 1)) @@ -467,7 +511,8 @@ theorem coe_gamma {a u h Δ k j : ℕ} (hk : k < 2 ^ (a + 1)) field_simp have hprod : ∀ l : List ℕ, (l.map fun k' => X k' ^ mult a u h k' / (2 ^ (a + 1) : ℚ) ^ mult a u h k').prod = - (l.map fun k' => X k' ^ mult a u h k').prod / (2 ^ (a + 1) : ℚ) ^ (l.map (mult a u h)).sum := by + (l.map fun k' => X k' ^ mult a u h k').prod / + (2 ^ (a + 1) : ℚ) ^ (l.map (mult a u h)).sum := by intro l induction l with | nil => simp @@ -491,4 +536,78 @@ theorem coe_gamma {a u h Δ k j : ℕ} (hk : k < 2 ^ (a + 1)) push_cast ring +/-! ### Precision -/ + +theorem node_mem_recoveryNodes {a k : ℕ} (hk : k < 2 ^ (a + 1)) (hkL : k ≠ 2 ^ a) : + node a k ∈ recoveryNodes a := by + rw [recoveryNodes_eq_image] + exact mem_image_of_mem _ (mem_filter.mpr ⟨mem_range.mpr hk, hkL⟩) + +/-- **The multipliers kill the input error.** If `p + 6 h D_t + q_t (a + 1) ≤ p_t`, the +multiplier of every coefficient below `z ^ q_t` is divisible by `2 ^ (N - p_t)` in `ℤ_(2)`, +`N = p + Δ`, so it annihilates `2 ^ p_t` in `A_N`. -/ +theorem gamma_mul_two_pow_eq_zero (ρ : ℤ₍₂₎ →+* Ring N fl) {a u h Δ p pt k j : ℕ} + (hk : k < 2 ^ (a + 1)) (hkL : k ≠ 2 ^ a) (hj : j < mult a u h k) + (hΔ : (a + 1) * (multSum a u h k + j) ≤ Δ) + (hpt : p + 6 * h * 2 ^ (a - padicValInt 2 (node a k)) + mult a u h k * (a + 1) ≤ pt) + (hN : N = p + Δ) : ρ (gamma a u h Δ k j) * 2 ^ pt = 0 := by + rcases le_or_gt N pt with hle | hlt + · rw [show (2 : Ring N fl) ^ pt = ((2 ^ pt : ℕ) : Ring N fl) by push_cast; rfl, + ← Nat.sub_add_cancel hle, pow_add, Nat.cast_mul, natCast_two_pow, mul_zero, mul_zero] + set e := N - pt + have ht := node_mem_recoveryNodes hk hkL + set E0 := (hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : ℚ)) (taperMult u h) + (node a k)).eval 0 + have hE0 : E0 ≠ 0 := eval_zero_hermiteBasis_ne_zero a u h + have ht0 : (node a k : ℚ) ≠ 0 := by exact_mod_cast ne_of_mem_erase ht + have hx0 : (-(node a k : ℚ) / 2 ^ (a + 1)) ≠ 0 := + div_ne_zero (neg_ne_zero.mpr ht0) (by positivity) + have hγ := coe_gamma (u := u) (h := h) hk hΔ + have hγ0 : ((gamma a u h Δ k j : ℤ₍₂₎) : ℚ) ≠ 0 := by + rw [hγ]; exact mul_ne_zero (mul_ne_zero (by positivity) hE0) (pow_ne_zero _ hx0) + -- The valuation of the multiplier is at least `e`. + have h2 : padicValRat 2 (2 : ℚ) = 1 := by exact_mod_cast padicValRat.self (p := 2) one_lt_two + have hvE : -(6 * (h : ℤ) * 2 ^ (a - padicValInt 2 (node a k))) ≤ padicValRat 2 E0 := by + have := neg_le_padicValRat_eval_zero_hermiteBasis_taper (a := a) u h + (mem_of_mem_erase (by rw [← recoveryNodes_eq]; exact ht)) (ne_of_mem_erase ht) + rw [← recoveryNodes_eq] at this + exact this + have hvx : -((a : ℤ) + 1) ≤ padicValRat 2 (-(node a k : ℚ) / 2 ^ (a + 1)) := by + rw [padicValRat.div (neg_ne_zero.mpr ht0) (by positivity), padicValRat.neg, + padicValRat.of_int, padicValRat.pow, h2] + push_cast + omega + have hv : (e : ℤ) ≤ padicValRat 2 ((gamma a u h Δ k j : ℤ₍₂₎) : ℚ) := by + rw [hγ, padicValRat.mul (mul_ne_zero (by positivity) hE0) (pow_ne_zero _ hx0), + padicValRat.mul (by positivity) hE0, padicValRat.pow, padicValRat.pow, h2] + have hjv : (j : ℤ) * padicValRat 2 (-(node a k : ℚ) / 2 ^ (a + 1)) ≥ + -((mult a u h k : ℤ) * ((a : ℤ) + 1)) := by + have : (j : ℤ) ≤ mult a u h k := by exact_mod_cast hj.le + nlinarith + have hpt' : (p : ℤ) + 6 * h * 2 ^ (a - padicValInt 2 (node a k)) + + (mult a u h k : ℤ) * (a + 1) ≤ pt := by exact_mod_cast hpt + have he : (e : ℤ) = N - pt := by omega + rw [he, hN] + push_cast + linarith + -- So `γ = 2 ^ e γ'` with `γ' ∈ ℤ_(2)`. + have hmem : ((gamma a u h Δ k j : ℤ₍₂₎) : ℚ) / 2 ^ e ∈ ℤ₍₂₎ := by + have hne : ((gamma a u h Δ k j : ℤ₍₂₎) : ℚ) / 2 ^ e ≠ 0 := div_ne_zero hγ0 (by positivity) + have hval : 0 ≤ padicValRat 2 (((gamma a u h Δ k j : ℤ₍₂₎) : ℚ) / 2 ^ e) := by + rw [padicValRat.div hγ0 (by positivity), padicValRat.pow, h2] + omega + change Rat.padicValuation 2 _ ≤ 1 + simp only [Rat.padicValuation, Valuation.coe_mk, MonoidWithZeroHom.coe_mk, ZeroHom.coe_mk, + hne, ↓reduceIte] + rw [← WithZero.exp_zero, WithZero.exp_le_exp] + omega + have hsplit : gamma a u h Δ k j = ((2 ^ e : ℕ) : ℤ₍₂₎) * ⟨_, hmem⟩ := by + apply Subtype.val_injective + simp only [Subring.coe_mul, Subring.coe_natCast] + push_cast + field_simp + rw [hsplit, map_mul, map_natCast, mul_comm, ← mul_assoc, + show (2 : Ring N fl) ^ pt = ((2 ^ pt : ℕ) : Ring N fl) by push_cast; rfl, ← Nat.cast_mul, + ← pow_add, show pt + e = N by omega, natCast_two_pow, zero_mul] + end Cslib.Circuits.Frontier.NativeRecovery diff --git a/Complexity/Computability/PolyTime/GaloisRingSeries.lean b/Complexity/Computability/PolyTime/GaloisRingSeries.lean index c42ccbdf98..7f3935612c 100644 --- a/Complexity/Computability/PolyTime/GaloisRingSeries.lean +++ b/Complexity/Computability/PolyTime/GaloisRingSeries.lean @@ -92,7 +92,8 @@ theorem coeff_modByMonicHom_val (c : List ℕ) (i : ℕ) : · have : Subsingleton (ZMod (2 ^ 0)) := by rw [pow_zero]; infer_instance exact Subsingleton.elim _ _ have := nontrivial_zmod hp - rw [val_eq_mk, AdjoinRoot.modByMonicHom_mk, (modByMonic_eq_self_iff ((monic_monicOf fl).map _)).mpr] + rw [val_eq_mk, AdjoinRoot.modByMonicHom_mk, + (modByMonic_eq_self_iff ((monic_monicOf fl).map _)).mpr] · simp only [finsetSum_coeff, coeff_C_mul, coeff_X_pow, mul_ite, mul_one, mul_zero, sum_ite_eq, mem_range] · rw [(monic_monicOf fl).degree_map, degree_eq_natDegree (monic_monicOf fl).ne_zero, @@ -194,8 +195,8 @@ theorem val_shiftDown {n Δ : ℕ} {s : List ℕ} {y : Ring (n + Δ) fl} /-- The coefficient of `z ^ j` in the product of two polynomials over `A_p`, by the Cauchy formula `∑ i ≤ j, P_i R_(j - i)`. -/ def cauchyCoeff (p : ℕ) (fl : List ℕ) (P R : List (List ℕ)) (j : ℕ) : List ℕ := - (List.range (j + 1)).foldl (fun acc i => add p fl acc (mul p fl (P.getD i []) (R.getD (j - i) []))) - (zero fl) + (List.range (j + 1)).foldl + (fun acc i => add p fl acc (mul p fl (P.getD i []) (R.getD (j - i) []))) (zero fl) /-- The **truncated product**: the first `J` coefficients of the product of two polynomials over `A_p`. -/ diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean new file mode 100644 index 0000000000..c368f3ddc8 --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean @@ -0,0 +1,52 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeRecovery + +/-! # Native tapered recovery on small instances + +Computations over `(ℤ / 2 ^ N)[U] / (U ^ 2 + U + 1)` (`fl = [1, 1]`) with `L = 2` slots +(`a = 1`), and the correctness statements of the components. -/ + +namespace ComplexityTests.TensorNetworkNativeRecovery + +open Cslib.GaloisRingArith Cslib.Circuits.Frontier Cslib.Circuits.Frontier.NativeRecovery + +-- `2 - 5 = -3 = 5` modulo `8`. +example : diffConst 3 [1, 1] 2 5 = [5, 0] := by decide + +-- `α / (t - q) = 4 / 2 = 2`, and `4 / (0 - 3) = -4 / 3 = 4` modulo `8` (since `3⁻¹ = 3`). +example : slopeConst 3 [1, 1] 1 3 1 = [2, 0] := by decide +example : slopeConst 3 [1, 1] 1 0 3 = [4, 0] := by decide + +-- `(1 + z) ^ 2 = 1 + 2 z + z ^ 2`, truncated below `z ^ 2`. +example : tmul 3 [1, 1] 2 [[1, 0], [1, 0]] [[1, 0], [1, 0]] = [[1, 0], [2, 0]] := by decide + +-- Cancelling `2`: `6 + 2 U = 2 (3 + U)`. +example : shiftDown 1 [6, 2] = [3, 1] := by decide + +-- With `u = h = 1`, the odd nodes `t = ±1` have multiplicity `1` and `t = -2` has `0`. +example : (mult 1 1 1 1, mult 1 1 1 3, mult 1 1 1 0) = (1, 1, 0) := by decide + +/-- **Recovery of `F(0)` for `F(T) = T + 5`.** Here `q_{±1} = 1`, so recovery reads the values +`F(-1) = 4` and `F(1) = 6` at the single point `ζ = 0`, and returns +`E_{-1}(0) F(-1) + E_1(0) F(1) = (4 + 6) / 2 = 5` modulo `8`, computing modulo `2 ^ (3 + 2)`. -/ +example : recover 3 2 [1, 1] 1 1 1 [[0, 0]] [[], [[4, 0]], [], [[6, 0]]] = [5, 0] := by decide + +/-- The reciprocal of `E_t(t + α z)` is correct, for every ring homomorphism from `ℤ_(2)`. -/ +example (hirr : Irreducible ((monicOf [1, 1]).map (Int.castRingHom (ZMod 2)))) + (ρ : Valuation.integer (Rat.padicValuation 2) →+* Ring 5 [1, 1]) {i : ℕ} (hi : i < 2) : + val 5 [1, 1] ((recip 5 [1, 1] 1 1 1 1 2).getD i []) = + ρ (PowerSeries.coeff i (recipSeries 1 1 1 1)) := + val_recip hirr ρ (by norm_num) hi + +/-- The multipliers are `2 ^ Δ E_t(0) (-t / α) ^ j`. -/ +example : ((gamma 1 1 1 2 1 0 : Valuation.integer (Rat.padicValuation 2)) : ℚ) = 2 ^ 2 * + (Polynomial.hermiteBasis (recoveryNodes 1) (fun q : ℤ => (q : ℚ)) (taperMult 1 1) + (node 1 1)).eval 0 * (-(node 1 1 : ℚ) / 2 ^ (1 + 1)) ^ 0 := + coe_gamma (by norm_num) (by decide) + +end ComplexityTests.TensorNetworkNativeRecovery From 4c076d5efa568f6cc7e6c15595ad61e214b373ef Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 23:57:33 -0400 Subject: [PATCH 040/129] feat(circuit/frontier): the local Cauchy kernel as a product of units (Lemma D.4 evaluation) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `kernelValue_eq_prod`: with t - q = 2^v_q u_q, u_q odd, W_d(t + αζ) is κ(kernelUnit a) times ∏_{q ≠ d, t} (u_q + 2^(a+1-v_q) ζ), times 2^(a+1-v_d) ζ when d ≠ t, where kernelUnit a is the inverse of the odd part of C'(0). The powers of two cancel exactly by `sum_padicValInt_sub_eq`, so kernel values need only ring operations and inverses of odd integers, as the source evaluates them (p. 60). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Frontier/TensorNetwork/KernelProduct.lean | 288 ++++++++++++++++++ .../Frontier/TensorNetworkPacketEntry.lean | 14 + 3 files changed, 303 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/KernelProduct.lean diff --git a/Complexity.lean b/Complexity.lean index 61358cafb6..88a37828a3 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -876,6 +876,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformatio public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DirectContraction public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.KernelProduct public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEntry public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/KernelProduct.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/KernelProduct.lean new file mode 100644 index 0000000000..8eca2827ff --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/KernelProduct.lean @@ -0,0 +1,288 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CauchyModes +import Complexity.Foundations.NumberTheory.Padics.RatValuation + +/-! +# The local Cauchy kernel as a product of units + +Lemma D.4 of [OpenAI2026ETH] (pp. 59–60) shows that the Cauchy kernel `W_d` on the grid `S` of +`L = 2 ^ a` slots has `2`-integral coefficients in the local coordinate `z`, `T = t + α z`, +`α = 2 ^ (a + 1)`. The source evaluates it by dividing each factor `t - q + α z`, `q ≠ t`, by the +known power `2 ^ v₂(t - q)` and inverting only odd integers (p. 60). This file proves the resulting +product formula, so that a kernel value is computed with ring operations and inverses of odd +integers. + +Write `t - q = 2 ^ v_q u_q` with `u_q` odd. Since `S` is a complete residue system modulo +`2 ^ (a + 1)`, the valuations `v_q`, `q ≠ t`, sum to those of the normalization +`C'(0) = ∏_{q ≠ 0} (0 - q)` (`sum_padicValInt_sub_eq`). So the powers of two cancel, and + +* `W_t(t + α z) = c ∏_{q ≠ t} (u_q + 2 ^ (a + 1 - v_q) z)`, +* `W_d(t + α z) = c 2 ^ (a + 1 - v_d) z ∏_{q ≠ d, t} (u_q + 2 ^ (a + 1 - v_q) z)` for `d ≠ t`, + +where `c = ∏_{q ≠ 0} u'_q⁻¹` is the inverse of the odd part of `C'(0)` (`kernelUnit`). Every +factor is integral, and `c` is a unit of `ℤ_(2)` (`localKernel_eq_prod`). Evaluating at `ζ` in a +ring receiving `ℤ_(2)` gives `kernelValue_eq_prod`. + +## Main definitions + +* `Cslib.Circuits.Frontier.kernelUnit`: the inverse of the odd part of `C'(0)`, in `ℤ_(2)`. + +## Main results + +* `Cslib.Circuits.Frontier.sum_padicValInt_sub_eq`: the valuations of `t - q` sum to those of + `C'(0)`. +* `Cslib.Circuits.Frontier.localKernel_eq_prod`: the local kernel as a product of units. +* `Cslib.Circuits.Frontier.kernelValue_eq_prod`: its values. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Lemma D.4 and its + evaluation, pp. 59–60. +-/ + +public section + +namespace Cslib.Circuits.Frontier + +open Polynomial Finset + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +/-- An integer is `2 ^ v₂(n)` times its odd part `n / 2 ^ v₂(n)`. -/ +private theorem two_pow_mul_ediv (n : ℤ) : 2 ^ padicValInt 2 n * (n / 2 ^ padicValInt 2 n) = n := + Int.mul_ediv_cancel' (by exact_mod_cast padicValInt_dvd (p := 2) n) + +/-- The odd part of a nonzero integer is nonzero. -/ +private theorem ediv_ne_zero {n : ℤ} (hn : n ≠ 0) : n / 2 ^ padicValInt 2 n ≠ 0 := by + intro h + exact hn (by rw [← two_pow_mul_ediv n, h, mul_zero]) + +/-- The odd part of a nonzero integer is not divisible by `2`. -/ +private theorem padicValInt_ediv_eq_zero {n : ℤ} (hn : n ≠ 0) : + padicValInt 2 (n / 2 ^ padicValInt 2 n) = 0 := by + refine padicValInt.eq_zero_of_not_dvd fun ⟨c, hc⟩ => ?_ + have hc' : n / 2 ^ padicValInt 2 n = 2 * c := by exact_mod_cast hc + have h2 : (2 : ℤ) ^ (padicValInt 2 n + 1) ∣ n := ⟨c, by + calc n = 2 ^ padicValInt 2 n * (n / 2 ^ padicValInt 2 n) := (two_pow_mul_ediv n).symm + _ = 2 ^ (padicValInt 2 n + 1) * c := by rw [hc', pow_succ]; ring⟩ + have h := (padicValInt_dvd_iff (p := 2) (padicValInt 2 n + 1) n).mp (by exact_mod_cast h2) + omega + +/-- The valuation of the odd part of a nonzero integer is one. -/ +private theorem padicValuation_ediv {n : ℤ} (hn : n ≠ 0) : + Rat.padicValuation 2 (((n / 2 ^ padicValInt 2 n : ℤ) : ℚ)) = 1 := by + rw [Rat.padicValuation_intCast_of_ne_zero 2 (ediv_ne_zero hn), padicValInt_ediv_eq_zero hn] + rfl + +/-- The inverse of an odd part is `2`-integral. -/ +private theorem inv_ediv_mem {n : ℤ} (hn : n ≠ 0) : + (((n / 2 ^ padicValInt 2 n : ℤ) : ℚ))⁻¹ ∈ ℤ₍₂₎ := by + change Rat.padicValuation 2 _ ≤ 1 + rw [map_inv₀, padicValuation_ediv hn, inv_one] + +/-- Distinct grid points of `2 ^ a` slots differ by at most `a` factors of two. -/ +private theorem padicValInt_sub_le {a : ℕ} {t q : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (hq : q ∈ cauchyGrid (2 ^ a)) (hne : q ≠ t) : padicValInt 2 (t - q) ≤ a := by + have hval := padicValuation_two_pow_le_sub ht hq hne + have hne' : t - q ≠ 0 := sub_ne_zero.mpr (Ne.symm hne) + have h2 : Rat.padicValuation 2 ((2 : ℚ) ^ a) = WithZero.exp (-(a : ℤ)) := by + exact_mod_cast Rat.padicValuation_pow_self 2 a + have h' : Rat.padicValuation 2 ((t : ℚ) - q) = Rat.padicValuation 2 ((t - q : ℤ) : ℚ) := by + push_cast; rfl + rw [h', Rat.padicValuation_intCast_of_ne_zero 2 hne', h2, WithZero.exp_le_exp] at hval + omega + +/-- A product of differences `s - q` over the other grid points is `2 ^ (∑ v₂(s - q))` times the +product of their odd parts. -/ +private theorem prod_sub_eq (s : ℤ) (F : Finset ℤ) : + ∏ q ∈ F, ((s : ℚ) - q) = 2 ^ (∑ q ∈ F, padicValInt 2 (s - q)) * + ∏ q ∈ F, (((s - q) / 2 ^ padicValInt 2 (s - q) : ℤ) : ℚ) := by + rw [← prod_pow_eq_pow_sum, ← prod_mul_distrib] + refine prod_congr rfl fun q _ => ?_ + exact_mod_cast (two_pow_mul_ediv (s - q)).symm + +/-- **The valuations of the differences sum to those of the normalization.** For `t` on the grid +of `2 ^ a` slots, `∑_{q ≠ t} v₂(t - q) = ∑_{q ≠ 0} v₂(0 - q)`. -/ +theorem sum_padicValInt_sub_eq {a : ℕ} {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) : + ∑ q ∈ (cauchyGrid (2 ^ a)).erase t, padicValInt 2 (t - q) = + ∑ q ∈ (cauchyGrid (2 ^ a)).erase 0, padicValInt 2 (0 - q) := by + have hv (s : ℤ) : Rat.padicValuation 2 (∏ q ∈ (cauchyGrid (2 ^ a)).erase s, ((s : ℚ) - q)) = + WithZero.exp (-((∑ q ∈ (cauchyGrid (2 ^ a)).erase s, padicValInt 2 (s - q) : ℕ) : ℤ)) := by + rw [prod_sub_eq s, map_mul, map_prod] + rw [prod_eq_one fun q hq => padicValuation_ediv (sub_ne_zero.mpr + (Ne.symm (ne_of_mem_erase hq))), mul_one] + exact_mod_cast Rat.padicValuation_pow_self 2 _ + have h := padicValuation_prod_sub_eq ht + have h0 := hv 0 + push_cast at h0 + rw [hv t, h0, WithZero.exp_inj, neg_inj] at h + exact_mod_cast h + +/-- The inverse `∏_{q ≠ 0} u'_q⁻¹` of the odd part of `C'(0) = ∏_{q ≠ 0} (0 - q)`, a unit of +`ℤ_(2)`, with `u'_q` the odd part of `0 - q`. -/ +noncomputable def kernelUnit (a : ℕ) : ℤ₍₂₎ := + ∏ q ∈ (cauchyGrid (2 ^ a)).erase 0, + if h : (0 : ℤ) - q = 0 then 1 else ⟨_, inv_ediv_mem h⟩ + +/-- The value of `kernelUnit`. -/ +theorem coe_kernelUnit (a : ℕ) : + ((kernelUnit a : ℤ₍₂₎) : ℚ) = (∏ q ∈ (cauchyGrid (2 ^ a)).erase 0, + (((0 - q) / 2 ^ padicValInt 2 (0 - q) : ℤ) : ℚ))⁻¹ := by + change (ℤ₍₂₎).subtype (kernelUnit a) = _ + rw [kernelUnit, map_prod, ← prod_inv_distrib] + refine prod_congr rfl fun q hq => ?_ + have hne : (0 : ℤ) - q ≠ 0 := sub_ne_zero.mpr (Ne.symm (ne_of_mem_erase hq)) + rw [dite_eq_right hne] + rfl + +/-- The `2`-integral factor `u_q + 2 ^ (a + 1 - v_q) z` of the local kernel, where +`t - q = 2 ^ v_q u_q`. -/ +private noncomputable def unitFactor (a : ℕ) (t q : ℤ) : ℤ₍₂₎[X] := + C (((t - q) / 2 ^ padicValInt 2 (t - q) : ℤ) : ℤ₍₂₎) + + C ((2 : ℤ₍₂₎) ^ (a + 1 - padicValInt 2 (t - q))) * X + +private theorem map_unitFactor (a : ℕ) (t q : ℤ) : + (unitFactor a t q).map (ℤ₍₂₎).subtype = + C ((((t - q) / 2 ^ padicValInt 2 (t - q) : ℤ) : ℚ)) + + C ((2 : ℚ) ^ (a + 1 - padicValInt 2 (t - q))) * X := by + simp only [unitFactor, Polynomial.map_add, Polynomial.map_mul, + Polynomial.map_X, map_intCast, map_pow, map_ofNat, Polynomial.map_intCast, + Polynomial.map_pow, Polynomial.map_ofNat] + +/-- A shifted factor `t + α z - q`, `q ≠ t` on the grid, is `2 ^ v_q` times the unit factor. -/ +private theorem shift_eq_unitFactor {a : ℕ} {t q : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (hq : q ∈ cauchyGrid (2 ^ a)) (hne : q ≠ t) : + C (t : ℚ) + C ((2 : ℚ) ^ (a + 1)) * X - C (q : ℚ) = + C ((2 : ℚ) ^ padicValInt 2 (t - q)) * (unitFactor a t q).map (ℤ₍₂₎).subtype := by + have hle := padicValInt_sub_le ht hq hne + have hsplit : ((t : ℚ) - q) = 2 ^ padicValInt 2 (t - q) * + (((t - q) / 2 ^ padicValInt 2 (t - q) : ℤ) : ℚ) := by + exact_mod_cast (two_pow_mul_ediv (t - q)).symm + have hpow : (2 : ℚ) ^ padicValInt 2 (t - q) * 2 ^ (a + 1 - padicValInt 2 (t - q)) = + 2 ^ (a + 1) := by + rw [← pow_add, Nat.add_sub_cancel' (by omega)] + rw [map_unitFactor, mul_add, ← mul_assoc, ← C_mul, ← C_mul, hpow, ← hsplit, C_sub] + ring + +/-- The shifted factors over grid points other than `t`. -/ +private theorem prod_shift_eq {a : ℕ} {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) (F : Finset ℤ) + (hF : F ⊆ (cauchyGrid (2 ^ a)).erase t) : + ∏ q ∈ F, (C (t : ℚ) + C ((2 : ℚ) ^ (a + 1)) * X - C (q : ℚ)) = + C ((2 : ℚ) ^ ∑ q ∈ F, padicValInt 2 (t - q)) * + ∏ q ∈ F, (unitFactor a t q).map (ℤ₍₂₎).subtype := by + rw [← prod_pow_eq_pow_sum, map_prod, ← prod_mul_distrib] + refine prod_congr rfl fun q hq => ?_ + obtain ⟨hqt, hqS⟩ := mem_erase.mp (hF hq) + exact shift_eq_unitFactor ht hqS hqt + +/-- **The local kernel as a product of units** (Lemma D.4 and its evaluation, p. 60). For grid +points `t` and `d` of `2 ^ a` slots, with `t - q = 2 ^ v_q u_q`, `u_q` odd, the local kernel is +`kernelUnit a` times `∏_{q ≠ d, t} (u_q + 2 ^ (a + 1 - v_q) z)`, times `2 ^ (a + 1 - v_d) z` when +`d ≠ t`. -/ +theorem localKernel_eq_prod {a : ℕ} {t d : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (hd : d ∈ cauchyGrid (2 ^ a)) : + localKernel a t d = C (kernelUnit a) * + (if d = t then 1 else C ((2 : ℤ₍₂₎) ^ (a + 1 - padicValInt 2 (t - d))) * X) * + ∏ q ∈ ((cauchyGrid (2 ^ a)).erase d).erase t, + (C (((t - q) / 2 ^ padicValInt 2 (t - q) : ℤ) : ℤ₍₂₎) + + C ((2 : ℤ₍₂₎) ^ (a + 1 - padicValInt 2 (t - q))) * X) := by + refine Polynomial.map_injective (ℤ₍₂₎).subtype Subtype.val_injective ?_ + rw [map_localKernel ht hd, cauchyKernel, Lagrange.eval_nodal, mul_comp, C_comp, Lagrange.nodal, + Polynomial.prod_comp] + simp only [sub_comp, X_comp, C_comp] + -- The normalization `C'(0) = 2 ^ S₀ w`. + set S₀ := ∑ q ∈ (cauchyGrid (2 ^ a)).erase 0, padicValInt 2 (0 - q) + set w := ∏ q ∈ (cauchyGrid (2 ^ a)).erase 0, (((0 - q) / 2 ^ padicValInt 2 (0 - q) : ℤ) : ℚ) + have hC0 : ∏ q ∈ (cauchyGrid (2 ^ a)).erase 0, ((0 : ℚ) - (q : ℚ)) = 2 ^ S₀ * w := by + have := prod_sub_eq 0 ((cauchyGrid (2 ^ a)).erase 0) + push_cast at this + exact this + have hsum := sum_padicValInt_sub_eq ht + have hRHS : (C (kernelUnit a) * + (if d = t then 1 else C ((2 : ℤ₍₂₎) ^ (a + 1 - padicValInt 2 (t - d))) * X) * + ∏ q ∈ ((cauchyGrid (2 ^ a)).erase d).erase t, + (C (((t - q) / 2 ^ padicValInt 2 (t - q) : ℤ) : ℤ₍₂₎) + + C ((2 : ℤ₍₂₎) ^ (a + 1 - padicValInt 2 (t - q))) * X)).map (ℤ₍₂₎).subtype = + C w⁻¹ * (if d = t then 1 else C ((2 : ℚ) ^ (a + 1 - padicValInt 2 (t - d))) * X) * + ∏ q ∈ ((cauchyGrid (2 ^ a)).erase d).erase t, (unitFactor a t q).map (ℤ₍₂₎).subtype := by + rw [Polynomial.map_mul, Polynomial.map_mul, Polynomial.map_C, Polynomial.map_prod] + congr 2 + · exact congrArg C (coe_kernelUnit a) + · split_ifs + · exact Polynomial.map_one _ + · simp only [Polynomial.map_mul, Polynomial.map_X, map_pow, map_ofNat, Polynomial.map_pow, + Polynomial.map_ofNat] + rw [hRHS, hC0] + rcases eq_or_ne d t with rfl | hdt + · rw [ite_eq_left rfl, mul_one, erase_idem, prod_shift_eq ht _ subset_rfl, hsum, ← mul_assoc, + ← C_mul] + congr 2 + rw [mul_inv] + change ((2 : ℚ) ^ S₀)⁻¹ * w⁻¹ * 2 ^ S₀ = w⁻¹ + rw [mul_comm, ← mul_assoc, mul_inv_cancel₀ (pow_ne_zero _ two_ne_zero), one_mul] + · rw [ite_eq_right hdt] + have htd : t ∈ (cauchyGrid (2 ^ a)).erase d := mem_erase.mpr ⟨hdt.symm, ht⟩ + have hdt' : d ∈ (cauchyGrid (2 ^ a)).erase t := mem_erase.mpr ⟨hdt, hd⟩ + have hsub : ((cauchyGrid (2 ^ a)).erase d).erase t ⊆ (cauchyGrid (2 ^ a)).erase t := + fun q hq => by + simp only [mem_erase] at hq ⊢ + exact ⟨hq.1, hq.2.2⟩ + set sv := ∑ q ∈ ((cauchyGrid (2 ^ a)).erase d).erase t, padicValInt 2 (t - q) + set U := ∏ q ∈ ((cauchyGrid (2 ^ a)).erase d).erase t, (unitFactor a t q).map (ℤ₍₂₎).subtype + -- `S₀ = v_d + ∑_{q ≠ d, t} v_q`. + have hsplit : S₀ = padicValInt 2 (t - d) + sv := + calc S₀ = ∑ q ∈ (cauchyGrid (2 ^ a)).erase t, padicValInt 2 (t - q) := hsum.symm + _ = padicValInt 2 (t - d) + + ∑ q ∈ ((cauchyGrid (2 ^ a)).erase t).erase d, padicValInt 2 (t - q) := + (add_sum_erase _ (fun q => padicValInt 2 (t - q)) hdt').symm + _ = padicValInt 2 (t - d) + sv := by rw [erase_right_comm] + have hvd := padicValInt_sub_le ht hd hdt + have hw : w ≠ 0 := prod_ne_zero_iff.mpr fun q hq => by + exact_mod_cast ediv_ne_zero (sub_ne_zero.mpr (Ne.symm (ne_of_mem_erase hq))) + rw [← mul_prod_erase _ _ htd, prod_shift_eq ht _ hsub, + show C (t : ℚ) + C ((2 : ℚ) ^ (a + 1)) * X - C (t : ℚ) = C ((2 : ℚ) ^ (a + 1)) * X by ring] + have hL : C (2 ^ S₀ * w)⁻¹ * (C ((2 : ℚ) ^ (a + 1)) * X * (C ((2 : ℚ) ^ sv) * U)) = + C ((2 ^ S₀ * w)⁻¹ * 2 ^ (a + 1) * 2 ^ sv) * (X * U) := by + rw [C_mul, C_mul]; ring + have hR : C w⁻¹ * (C ((2 : ℚ) ^ (a + 1 - padicValInt 2 (t - d))) * X) * U = + C (w⁻¹ * 2 ^ (a + 1 - padicValInt 2 (t - d))) * (X * U) := by + rw [C_mul]; ring + rw [hL, hR] + congr 2 + have h2 : (2 : ℚ) ^ (a + 1) = + 2 ^ (a + 1 - padicValInt 2 (t - d)) * 2 ^ padicValInt 2 (t - d) := by + rw [← pow_add, Nat.sub_add_cancel (by omega)] + rw [h2, hsplit, pow_add] + field_simp + +/-- **Kernel values as products of units.** In a ring `R` receiving `ℤ_(2)` through `κ`, the +kernel value `W_d(t + α ζ)` is `κ(kernelUnit a)` times `∏_{q ≠ d, t} (u_q + 2 ^ (a + 1 - v_q) ζ)`, +times `2 ^ (a + 1 - v_d) ζ` when `d ≠ t`. -/ +theorem kernelValue_eq_prod {R : Type*} [CommRing R] (κ : ℤ₍₂₎ →+* R) (ζ : R) {a : ℕ} + {t d : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) (hd : d ∈ cauchyGrid (2 ^ a)) : + kernelValue κ ζ a t d = κ (kernelUnit a) * + (if d = t then 1 else 2 ^ (a + 1 - padicValInt 2 (t - d)) * ζ) * + ∏ q ∈ ((cauchyGrid (2 ^ a)).erase d).erase t, + (((t - q) / 2 ^ padicValInt 2 (t - q) : ℤ) + 2 ^ (a + 1 - padicValInt 2 (t - q)) * ζ) := by + rw [kernelValue, localKernel_eq_prod ht hd] + change eval₂RingHom κ ζ _ = _ + rw [map_mul, map_mul, map_prod] + simp only [coe_eval₂RingHom, eval₂_C] + congr 1 + · congr 1 + split_ifs + · exact eval₂_one _ _ + · rw [eval₂_mul, eval₂_C, eval₂_X, map_pow, map_ofNat] + · refine prod_congr rfl fun q _ => ?_ + rw [eval₂_add, eval₂_mul, eval₂_C, eval₂_C, eval₂_X, map_intCast, map_pow, map_ofNat] + +end Cslib.Circuits.Frontier diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEntry.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEntry.lean index fe79b79655..cdda8d275b 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEntry.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEntry.lean @@ -7,6 +7,7 @@ Authors: Samuel Schlesinger module -- upstream: local-only (test driver) +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.KernelProduct public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEntry public import ComplexityTests.Computability.Circuit.Frontier.TensorNetworkCauchyModes @@ -72,4 +73,17 @@ example (ζ : ZMod (2 ^ 2)) (β : Fin (2 ^ (0 + 1)) × Fin 2 → ℕ) (rows cols (ZMod.castHom (by decide : 2 ∣ 2 ^ 2) (ZMod 2) ζ) 1 0 1 true false true β rows cols := map_packetEntry _ (n := 2) (by decide) κ ζ (by decide) (by decide) (by decide) _ _ _ β rows cols +/-- **Kernel values as products of units**, on the grid `{-2, -1, 0, 1}` of two slots: the value +`W_0(1 + 4 ζ)` is `κ(kernelUnit 1)` times `2 ^ (2 - v₂(1)) ζ` times the factors at `q = -2, -1`. -/ +example (ζ : ZMod (2 ^ 2)) : + kernelValue κ ζ 1 1 0 = κ (kernelUnit 1) * (2 ^ (1 + 1 - padicValInt 2 (1 - 0)) * ζ) * + ∏ q ∈ ((cauchyGrid (2 ^ 1)).erase 0).erase 1, + (((1 - q) / 2 ^ padicValInt 2 (1 - q) : ℤ) + 2 ^ (1 + 1 - padicValInt 2 (1 - q)) * ζ) := by + rw [kernelValue_eq_prod κ ζ (by decide) (by decide), ite_eq_right (by decide)] + +/-- The valuations of `1 - q`, `q ∈ {-2, -1, 0}`, sum to those of `0 - q`, `q ∈ {-2, -1, 1}`. -/ +example : ∑ q ∈ (cauchyGrid (2 ^ 1)).erase 1, padicValInt 2 (1 - q) = + ∑ q ∈ (cauchyGrid (2 ^ 1)).erase 0, padicValInt 2 (0 - q) := + sum_padicValInt_sub_eq (by decide) + end ComplexityTests.Computability.Circuit.Frontier.TensorNetwork.PacketEntry From 9dde334a6d8734195b34d228ec6868bf3aa021cc Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 00:09:53 -0400 Subject: [PATCH 041/129] feat(circuit/frontier): native recovery computes recoverFromValues mod 2^p MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bridge #1906's native interpolation to `Lagrange.ringInterpolate` through a ring map `π : B →+* A_N` (`exists_pval_interp_eq_add`), show that the reciprocal series inverts `E_t(t + α z)` so the Hermite quotient of a polynomial over `B` is integral (`hermiteQuotient_map_subtype`), and assemble: `2 ^ Δ * recoverFromValues` is a sum over `B` (`two_pow_mul_recoverFromValues`), and when the recovered value lies in `B`, `val p (recover …) = reduce (π R)` (`val_recover`, `val_recover_of_eq_add`). Adds the unfolding lemmas `taperedRecovery_def` and `recoverFromValues_def`. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/ActiveCall.lean | 6 + .../TensorNetwork/NativeRecovery.lean | 358 +++++++++++++++++- .../TensorNetwork/TaperedRecovery.lean | 7 + .../Frontier/TensorNetworkNativeRecovery.lean | 3 + 4 files changed, 369 insertions(+), 5 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ActiveCall.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ActiveCall.lean index afa5522d4c..5a747682d8 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ActiveCall.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ActiveCall.lean @@ -133,6 +133,12 @@ noncomputable def recoverFromValues (B : Subring K) (a u h : ℕ) {n : ℕ} (ζ (v : ℤ → Fin n → B) : K := taperedRecovery a u h fun t => (Lagrange.ringInterpolate univ ζ (v t)).map B.subtype +theorem recoverFromValues_def (B : Subring K) (a u h : ℕ) {n : ℕ} (ζ : Fin n → B) + (v : ℤ → Fin n → B) : + recoverFromValues B a u h ζ v = + taperedRecovery a u h fun t => (Lagrange.ringInterpolate univ ζ (v t)).map B.subtype := by + rw [recoverFromValues] + /-- A nonzero integer `t` of valuation `i` is divisible by `2 ^ i` but not by `2 ^ (i + 1)`. -/ theorem two_pow_padicValInt_dvd_and_not_dvd {t : ℤ} (ht : t ≠ 0) : (2 : ℤ) ^ padicValInt 2 t ∣ t ∧ ¬ (2 : ℤ) ^ (padicValInt 2 t + 1) ∣ t := by diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean index ea11c070b1..53f3c49408 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean @@ -17,7 +17,7 @@ import Mathlib.Data.List.GetD This file gives the native procedure behind the recovery step of the contraction algorithm of [OpenAI2026ETH] (§D.4.4, Lemma D.5, pp. 60–61), and proves that it computes the mathematical recovery `Cslib.Circuits.Frontier.recoverFromValues` of -`Complexity/Computability/Circuit/Frontier/TensorNetwork/ActiveCall.lean`. +`Complexity/Computability/Circuit/Frontier/TensorNetwork/ActiveCall.lean` modulo `2 ^ p`. **Input.** A precision `p`, a shift `Δ`, the monic polynomial `fl` of `Complexity/Computability/PolyTime/GaloisRingArithmetic.lean`, the parameters `a, u, h`, the @@ -44,10 +44,14 @@ The sum over the nodes is `2 ^ Δ` times the recovered value. Dividing its coord ((D.10) and the evaluation loss), so the error is killed in `A_N`. This is the precision estimate of Lemma D.5 (`Cslib.Circuits.Frontier.exists_taperedRecovery_eq_add`), carried out natively. -**Status.** This file proves the components: the constants, the reciprocal, the multipliers and -the precision estimate, each against an arbitrary ring homomorphism `ρ : ℤ_(2) → A_N`. The -assembly into `recover = recoverFromValues mod 2 ^ p` (through a reduction `B → A_N` and the -interpolation of `GaloisRingArith.interp`) and the polynomial-time certificate are not yet done. +**Correctness.** Let `π : B → A_N` be a ring homomorphism from a subring `B ⊇ ℤ_(2)` of a +`ℚ`-algebra `K` (in the manuscript, `B = O`). The native interpolant is the image of the +interpolant over `B` modulo `2 ^ p_t` (`NativeRecovery.exists_pval_interp_eq_add`), the Hermite +quotient over `K` of a polynomial over `B` is the image of its product with the reciprocal series +(`NativeRecovery.hermiteQuotient_map_subtype`), and `2 ^ Δ` times the recovered value is the sum +of the node contributions over `B` (`NativeRecovery.two_pow_mul_recoverFromValues`). So when the +recovered value `recoverFromValues B a u h ζ v` lies in `B`, native recovery computes its image +modulo `2 ^ p` (`NativeRecovery.val_recover`). ## Main definitions @@ -58,6 +62,7 @@ interpolation of `GaloisRingArith.interp`) and the polynomial-time certificate a * `Cslib.Circuits.Frontier.NativeRecovery.recipSeries`, `Cslib.Circuits.Frontier.NativeRecovery.gamma`: the reciprocal and the multipliers over `ℤ_(2)`. +* `Cslib.Circuits.Frontier.NativeRecovery.nodeSum`: the contribution of a node over `B`. ## Main results @@ -72,6 +77,15 @@ interpolation of `GaloisRingArith.interp`) and the polynomial-time certificate a * `Cslib.Circuits.Frontier.NativeRecovery.sum_recoveryNodes`, `Cslib.Circuits.Frontier.NativeRecovery.prod_others`: sums and products over the nodes as iterations over node indices. +* `Cslib.Circuits.Frontier.NativeRecovery.exists_pval_interp_eq_add`: the interpolation bridge. +* `Cslib.Circuits.Frontier.NativeRecovery.invOfUnit_hermiteBasis_comp`, + `Cslib.Circuits.Frontier.NativeRecovery.hermiteQuotient_map_subtype`: the reciprocal series + inverts `E_t(t + α z)`, so the Hermite quotient of a polynomial over `B` is integral. +* `Cslib.Circuits.Frontier.NativeRecovery.two_pow_mul_recoverFromValues`: `2 ^ Δ` times the + recovered value as a sum over `B`. +* `Cslib.Circuits.Frontier.NativeRecovery.val_recover`, + `Cslib.Circuits.Frontier.NativeRecovery.val_recover_of_eq_add`: native recovery computes the + mathematical recovery modulo `2 ^ p`. ## References @@ -400,6 +414,7 @@ theorem val_recip (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2 /-! ### Products over the nodes -/ /-- The other nodes, as integers, are the nonzero grid points other than `t`. -/ +@[to_additive /-- The other nodes, as integers, are the nonzero grid points other than `t`. -/] theorem prod_others {M : Type*} [CommMonoid M] (f : ℤ → M) {a k : ℕ} : ((others a k).map fun k' => f (node a k')).prod = ∏ q ∈ (recoveryNodes a).erase (node a k), f q := by @@ -610,4 +625,337 @@ theorem gamma_mul_two_pow_eq_zero (ρ : ℤ₍₂₎ →+* Ring N fl) {a u h Δ show (2 : Ring N fl) ^ pt = ((2 ^ pt : ℕ) : Ring N fl) by push_cast; rfl, ← Nat.cast_mul, ← pow_add, show pt + e = N by omega, natCast_two_pow, zero_mul] +/-! ### The interpolation bridge -/ + +/-- The coefficients of a polynomial over `A_N` given by a list of coefficient lists. -/ +theorem coeff_pval (P : List (List ℕ)) (l : ℕ) : + (pval N fl P).coeff l = val N fl (P.getD l []) := by + rw [pval, finsetSum_coeff] + simp only [coeff_C_mul, coeff_X_pow, mul_ite, mul_one, mul_zero, sum_ite_eq, mem_range] + split_ifs with h + · rfl + · rw [List.getD_eq_default _ _ (by omega), val_nil] + +/-- Interpolation at `n` nodes has degree below `n`. -/ +theorem degree_pval_interp_lt (ζl w : List (List ℕ)) : + (pval N fl (interp N fl ζl w)).degree < ζl.length := by + rw [← mem_degreeLT, pval_interp] + refine Submodule.sum_mem _ fun i hi => ?_ + rw [← smul_eq_C_mul] + refine Submodule.smul_mem _ _ (mem_degreeLT.mpr ((degree_le_natDegree).trans_lt ?_)) + have hle : (∏ j ∈ (range ζl.length).erase i, (X - C (val N fl (ζl.getD j [])))).natDegree ≤ + #((range ζl.length).erase i) := + (natDegree_prod_le _ _).trans ((sum_le_sum fun j _ => natDegree_X_sub_C_le _).trans_eq + (card_eq_sum_ones _).symm) + rw [card_erase_of_mem hi, card_range] at hle + have := mem_range.mp hi + exact_mod_cast (show _ < ζl.length by omega) + +/-- **The interpolation bridge.** Let `π : B → A_N` be a ring homomorphism, and let the native +nodes `ζl` represent the images of nodes `ζ i ∈ B` with unit differences. If the native values +`w` agree with the images of `r i ∈ B` modulo `2 ^ e`, then the native interpolant agrees with the +image of the interpolant of `r` over `B` modulo `2 ^ e`. -/ +theorem exists_pval_interp_eq_add (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2)))) + {B : Type*} [CommRing B] (π : B →+* Ring N fl) {n : ℕ} (ζ : Fin n → B) + (hζ : Pairwise fun k l => IsUnit (ζ k - ζ l)) (ζl : List (List ℕ)) (hlen : ζl.length = n) + (hζl : ∀ i : Fin n, val N fl (ζl.getD i []) = π (ζ i)) (r : Fin n → B) + (w : List (List ℕ)) (e : ℕ) + (hw : ∀ i : Fin n, ∃ c, val N fl (w.getD i []) = π (r i) + 2 ^ e * c) : + ∃ g : (Ring N fl)[X], + pval N fl (interp N fl ζl w) = (Lagrange.ringInterpolate univ ζ r).map π + C (2 ^ e) * g := by + subst hlen + have hvs : ((univ : Finset (Fin ζl.length)) : Set (Fin ζl.length)).Pairwise + fun i j => IsUnit (π (ζ i) - π (ζ j)) := fun i _ j _ hij => by + simp only + rw [← map_sub]; exact (hζ hij).map π + -- The native interpolant is the interpolant of the native values at the images of the nodes. + have hinv : ∀ i < ζl.length, val N fl (basisDen N fl ζl i) * + val N fl (inv N fl (basisDen N fl ζl i)) = 1 := fun i hi => by + refine val_mul_inv_of_isUnit hirr ?_ + rw [val_basisDen, IsUnit.prod_iff] + intro j hj + have hj' := mem_range.mp (mem_of_mem_erase hj) + rw [show i = ((⟨i, hi⟩ : Fin ζl.length) : ℕ) from rfl, hζl, + show j = ((⟨j, hj'⟩ : Fin ζl.length) : ℕ) from rfl, hζl] + exact hvs (mem_univ _) (mem_univ _) fun h => ne_of_mem_erase hj (congrArg Fin.val h).symm + have hnat : pval N fl (interp N fl ζl w) = + Lagrange.ringInterpolate univ (fun i => π (ζ i)) fun i => val N fl (w.getD i []) := by + conv_lhs => rw [Lagrange.eq_ringInterpolate (f := pval N fl (interp N fl ζl w)) hvs (by + rw [card_univ, Fintype.card_fin]; exact degree_pval_interp_lt ζl w)] + congr 1 + funext i + rw [← hζl, eval_pval_interp ζl w hinv i.2] + -- The image of the interpolant over `B` takes the values `π (r i)`. + have hdeg : ((Lagrange.ringInterpolate univ ζ r).map π).degree < + #(univ : Finset (Fin ζl.length)) := + (degree_map_le).trans_lt (Lagrange.degree_ringInterpolate_lt r) + obtain ⟨g, hg⟩ := Lagrange.exists_ringInterpolate_eq_add hvs hdeg (π := 2 ^ e) + (r := fun i => val N fl (w.getD i [])) fun i _ => by + obtain ⟨c, hc⟩ := hw i + refine ⟨c, ?_⟩ + rw [hc, eval_map, eval₂_at_apply, + Lagrange.eval_ringInterpolate_at_node (fun i _ j _ hij => hζ hij) r (mem_univ i)] + exact ⟨g, hnat.trans hg⟩ + +/-! ### The Hermite quotient over `B` -/ + +/-- A fold of multiplications is a list product. -/ +theorem foldl_mul_eq {M ι : Type*} [CommMonoid M] (g : ι → M) (l : List ι) (c : M) : + l.foldl (fun x y => x * g y) c = c * (l.map g).prod := by + induction l generalizing c with + | nil => simp + | cons x l ih => rw [List.foldl_cons, ih, List.map_cons, List.prod_cons, mul_assoc] + +theorem map_geomSeries {R S : Type*} [CommRing R] [CommRing S] (f : R →+* S) (c : R) : + PowerSeries.map f (geomSeries c) = geomSeries (f c) := by + ext n + simp [geomSeries, PowerSeries.coeff_map] + +theorem coe_slope' {a k k' : ℕ} (hk : k < 2 ^ (a + 1)) (hk' : k' < 2 ^ (a + 1)) (hne : k' ≠ k) : + ((slope' a k k' : ℤ₍₂₎) : ℚ) = 2 ^ (a + 1) / ((node a k : ℚ) - node a k') := by + rw [slope', dite_eq_left_of_eq_true (eq_true ⟨hk, hk', hne⟩)] + rfl + +/-- Over `ℚ`, `E_t(t + α z)` times the reciprocal series is one. -/ +theorem coe_hermiteBasis_comp_mul_recipSeries {a k : ℕ} (u h : ℕ) (hk : k < 2 ^ (a + 1)) : + ((hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : ℚ)) (taperMult u h) (node a k)).comp + (C (node a k : ℚ) + C (2 ^ (a + 1)) * X) : PowerSeries ℚ) * + PowerSeries.map (ℤ₍₂₎).subtype (recipSeries a u h k) = 1 := by + rw [hermiteBasis_comp_rat, ← prod_others (fun q : ℤ => + (1 + C ((2 : ℚ) ^ (a + 1) / ((node a k : ℚ) - q)) * X) ^ taperMult u h q), + recipSeries, foldl_mul_eq, one_mul, ← Polynomial.coeToPowerSeries.ringHom_apply, map_list_prod, + map_list_prod, List.map_map, List.map_map, ← List.prod_map_mul] + refine List.prod_eq_one fun x hx => ?_ + obtain ⟨k', hk', rfl⟩ := List.mem_map.mp hx + obtain ⟨hk'1, -, hk'3⟩ := mem_others.mp hk' + simp only [Function.comp_apply, map_pow, Polynomial.coeToPowerSeries.ringHom_apply, + Polynomial.coe_add, Polynomial.coe_one, Polynomial.coe_mul, + Polynomial.coe_C, Polynomial.coe_X, map_geomSeries, Subring.coe_subtype, + coe_slope' hk hk'1 hk'3, ← mult_eq u h hk'1] + rw [← mul_pow, one_add_mul_geomSeries, one_pow] + +section Hermite + +variable {K : Type*} [CommRing K] [Algebra ℚ K] + +/-- **The reciprocal series is the inverse of `E_t(t + α z)`** over every `ℚ`-algebra. -/ +theorem invOfUnit_hermiteBasis_comp {a k : ℕ} (u h : ℕ) (hk : k < 2 ^ (a + 1)) + (hkL : k ≠ 2 ^ a) : + PowerSeries.invOfUnit (((hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : K)) + (taperMult u h) (node a k)).comp (C (node a k : K) + C (2 ^ (a + 1)) * X) : K[X]) : + PowerSeries K) 1 = + PowerSeries.map ((algebraMap ℚ K).comp (ℤ₍₂₎).subtype) (recipSeries a u h k) := by + have ht := node_mem_recoveryNodes hk hkL + set φ : PowerSeries K := (((hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : K)) + (taperMult u h) (node a k)).comp (C (node a k : K) + C (2 ^ (a + 1)) * X) : K[X]) : + PowerSeries K) + have hv : (fun q : ℤ => algebraMap ℚ K (q : ℚ)) = fun q : ℤ => (q : K) := + funext fun q => map_intCast _ q + have hlin : (C (node a k : ℚ) + C ((2 : ℚ) ^ (a + 1)) * X).map (algebraMap ℚ K) = + C (node a k : K) + C (2 ^ (a + 1)) * X := by + rw [Polynomial.map_add, Polynomial.map_mul, Polynomial.map_C, Polynomial.map_C, + Polynomial.map_X, map_intCast, map_pow, map_ofNat] + have hφ : φ = PowerSeries.map (algebraMap ℚ K) + (((hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : ℚ)) (taperMult u h) (node a k)).comp + (C (node a k : ℚ) + C (2 ^ (a + 1)) * X) : ℚ[X]) : PowerSeries ℚ) := by + rw [← Polynomial.polynomial_map_coe, Polynomial.map_comp, + map_hermiteBasis _ (pairwise_isUnit_sub _) ht, hv, hlin] + have h1 : φ * PowerSeries.invOfUnit φ 1 = 1 := + PowerSeries.mul_invOfUnit _ _ (by + rw [Polynomial.constantCoeff_coe, coeff_zero_eq_eval_zero, eval_comp, eval_add, eval_C, + eval_mul, eval_C, eval_X, mul_zero, add_zero, + eval_hermiteBasis_self (pairwise_isUnit_sub _) ht, Units.val_one]) + have h2 : φ * PowerSeries.map ((algebraMap ℚ K).comp (ℤ₍₂₎).subtype) (recipSeries a u h k) = + 1 := by + rw [hφ, PowerSeries.map_comp, RingHom.comp_apply, ← map_mul, + coe_hermiteBasis_comp_mul_recipSeries u h hk, map_one] + calc PowerSeries.invOfUnit φ 1 + = PowerSeries.invOfUnit φ 1 * (φ * PowerSeries.map ((algebraMap ℚ K).comp (ℤ₍₂₎).subtype) + (recipSeries a u h k)) := by rw [h2, mul_one] + _ = _ := by rw [← mul_assoc, mul_comm _ φ, h1, one_mul] + +/-- **The Hermite quotient over `B`.** For a polynomial `P` over `B`, the Hermite quotient over +`K` at the node of index `k` is the image of the truncation below `z ^ q_t` of the product of `P` +with the reciprocal series. -/ +theorem hermiteQuotient_map_subtype (B : Subring K) (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) + {a k : ℕ} (u h : ℕ) (hk : k < 2 ^ (a + 1)) (hkL : k ≠ 2 ^ a) (P : B[X]) : + hermiteQuotient (recoveryNodes a) (fun q : ℤ => (q : K)) (taperMult u h) (2 ^ (a + 1)) + (node a k) ((P.map B.subtype : K[X]) : PowerSeries K) = + (PowerSeries.trunc (mult a u h k) ((P : PowerSeries B) * + PowerSeries.map (integerHom B hB) (recipSeries a u h k))).map B.subtype := by + rw [hermiteQuotient, invOfUnit_hermiteBasis_comp u h hk hkL, ← subtype_comp_integerHom B hB, + PowerSeries.map_comp, RingHom.comp_apply, Polynomial.polynomial_map_coe, ← map_mul, + PowerSeries.trunc_map, mult_eq u h hk] + +/-! ### Assembly -/ + +/-- The contribution of the node of index `k` to `2 ^ Δ` times the recovered value, over `B`: +`∑_{j < q_t} γ_{t,j} c_j`, where `c_j` is the coefficient of `z ^ j` in the product of `P` with +the reciprocal series and `γ_{t,j} = 2 ^ Δ E_t(0) (-t / α) ^ j` (`NativeRecovery.gamma`). -/ +noncomputable def nodeSum (B : Subring K) (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) + (a u h Δ k : ℕ) (P : B[X]) : B := + ∑ j ∈ range (mult a u h k), integerHom B hB (gamma a u h Δ k j) * + PowerSeries.coeff j ((P : PowerSeries B) * + PowerSeries.map (integerHom B hB) (recipSeries a u h k)) + +theorem multSum_add_mult {a k : ℕ} (u h : ℕ) (hk : k < 2 ^ (a + 1)) (hkL : k ≠ 2 ^ a) : + multSum a u h k + mult a u h k = ∑ t ∈ recoveryNodes a, taperMult u h t := by + rw [multSum, List.map_congr_left (g := fun k' => taperMult u h (node a k')) + fun k' hk' => mult_eq u h (mem_others.mp hk').1, + sum_others (fun q => taperMult u h q), mult_eq u h hk, + sum_erase_add _ _ (node_mem_recoveryNodes hk hkL)] + +theorem le_of_lt_mult {a u h Δ k j : ℕ} (hk : k < 2 ^ (a + 1)) (hkL : k ≠ 2 ^ a) + (hj : j < mult a u h k) (hΔ : (a + 1) * ∑ t ∈ recoveryNodes a, taperMult u h t ≤ Δ) : + (a + 1) * (multSum a u h k + j) ≤ Δ := + (Nat.mul_le_mul_left _ (by rw [← multSum_add_mult u h hk hkL]; omega)).trans hΔ + +/-- **`2 ^ Δ` times the recovered value lies in `B`**, as the sum of the node contributions +`NativeRecovery.nodeSum`, once `Δ ≥ (a + 1) ∑_t q_t`. -/ +theorem two_pow_mul_recoverFromValues (B : Subring K) (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) + {a u h Δ : ℕ} (hΔ : (a + 1) * ∑ t ∈ recoveryNodes a, taperMult u h t ≤ Δ) {n : ℕ} + (ζ : Fin n → B) (v : ℤ → Fin n → B) : + 2 ^ Δ * recoverFromValues B a u h ζ v = + ((∑ k ∈ range (2 ^ (a + 1)), if k = 2 ^ a then 0 else + nodeSum B hB a u h Δ k (Lagrange.ringInterpolate univ ζ (v (node a k))) : B) : K) := by + rw [recoverFromValues_def, taperedRecovery_def, sum_recoveryNodes, mul_sum, + AddSubmonoidClass.coe_finsetSum] + refine sum_congr rfl fun k hk => ?_ + have hk' := mem_range.mp hk + split_ifs with hkL + · simp + rw [hermiteQuotient_map_subtype B hB u h hk' hkL, eval_map, + PowerSeries.eval₂_trunc_eq_sum_range, eval_zero_hermiteBasis_eq a u h + (node_mem_recoveryNodes hk' hkL), nodeSum, AddSubmonoidClass.coe_finsetSum, mul_sum, + mul_sum] + refine sum_congr rfl fun j hj => ?_ + have hγ := coe_gamma (u := u) (h := h) hk' (le_of_lt_mult hk' hkL (mem_range.mp hj) hΔ) + have hint : ((integerHom B hB (gamma a u h Δ k j) : B) : K) = + algebraMap ℚ K ((gamma a u h Δ k j : ℤ₍₂₎) : ℚ) := rfl + rw [Subring.coe_mul, hint, hγ, Subring.coe_subtype] + simp only [map_mul, map_pow, map_ofNat] + ring + +/-- A fold that adds a term at each index of a range sums the terms. -/ +theorem val_foldl_range (F : List ℕ → ℕ → List ℕ) (g : ℕ → Ring N fl) + (hF : ∀ S k, val N fl (F S k) = val N fl S + g k) (c : List ℕ) (m : ℕ) : + val N fl ((List.range m).foldl F c) = val N fl c + ∑ k ∈ range m, g k := by + induction m with + | zero => simp + | succ m ih => + rw [List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil, hF, ih, + sum_range_succ, add_assoc] + +/-- **The native contribution of a node is correct**: if the native values agree with the +images of `r i` modulo `2 ^ p_t`, where `p + 6 h D_t + q_t (a + 1) ≤ p_t`, then the native +contribution of the node of index `k` is the image of `NativeRecovery.nodeSum`. -/ +theorem val_nodeTerm (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2)))) + (B : Subring K) (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) (π : B →+* Ring N fl) + {p a u h Δ pt k : ℕ} (hk : k < 2 ^ (a + 1)) (hkL : k ≠ 2 ^ a) + (hΔ : (a + 1) * ∑ t ∈ recoveryNodes a, taperMult u h t ≤ Δ) (hN : N = p + Δ) + (hpt : 0 < mult a u h k → + p + 6 * h * 2 ^ (a - padicValInt 2 (node a k)) + mult a u h k * (a + 1) ≤ pt) + {n : ℕ} (ζ : Fin n → B) (hζ : Pairwise fun k l => IsUnit (ζ k - ζ l)) + (ζl : List (List ℕ)) (hlen : ζl.length = n) + (hζl : ∀ i : Fin n, val N fl (ζl.getD i []) = π (ζ i)) (r : Fin n → B) (w : List (List ℕ)) + (hw : 0 < mult a u h k → ∀ i : Fin n, ∃ c, val N fl (w.getD i []) = π (r i) + 2 ^ pt * c) : + val N fl (nodeTerm N fl a u h Δ ζl k w) = + π (nodeSum B hB a u h Δ k (Lagrange.ringInterpolate univ ζ r)) := by + rcases Nat.eq_zero_or_pos (mult a u h k) with h0 | hpos + · simp [nodeTerm, nodeSum, h0] + obtain ⟨g, hg⟩ := exists_pval_interp_eq_add hirr π ζ hζ ζl hlen hζl r w pt (hw hpos) + set ρ := π.comp (integerHom B hB) + set P := Lagrange.ringInterpolate univ ζ r + rw [nodeTerm, val_foldl_range _ (fun j => val N fl (coeffMult N fl a u h Δ k j) * + val N fl ((tmul N fl (mult a u h k) (interp N fl ζl w) + (recip N fl a u h k (mult a u h k))).getD j [])) + (fun S j => by rw [val_add, val_mul]), val_zero, zero_add, nodeSum, map_sum] + refine sum_congr rfl fun j hj => ?_ + have hj' := mem_range.mp hj + have hcoef : ∀ l, val N fl ((interp N fl ζl w).getD l []) = π (P.coeff l) + 2 ^ pt * g.coeff l := + fun l => by rw [← coeff_pval, hg, coeff_add, coeff_map, coeff_C_mul] + have hconv : π (PowerSeries.coeff j ((P : PowerSeries B) * + PowerSeries.map (integerHom B hB) (recipSeries a u h k))) = + ∑ l ∈ range (j + 1), + π (P.coeff l) * ρ (PowerSeries.coeff (j - l) (recipSeries a u h k)) := by + rw [PowerSeries.coeff_mul, Finset.Nat.sum_antidiagonal_eq_sum_range_succ (fun l m => + PowerSeries.coeff l (P : PowerSeries B) * + PowerSeries.coeff m (PowerSeries.map (integerHom B hB) (recipSeries a u h k))), map_sum] + refine sum_congr rfl fun l _ => ?_ + rw [Polynomial.coeff_coe, PowerSeries.coeff_map, map_mul] + rfl + have hkill : ρ (gamma a u h Δ k j) * 2 ^ pt = 0 := + gamma_mul_two_pow_eq_zero ρ hk hkL hj' (le_of_lt_mult hk hkL hj' hΔ) (hpt hpos) hN + rw [val_coeffMult hirr ρ hk j, val_tmul _ _ hj', map_mul, hconv, + sum_congr rfl fun l hl => by + rw [hcoef l, val_recip hirr ρ hk (show j - l < mult a u h k by omega)]] + simp only [add_mul, sum_add_distrib, mul_add, mul_assoc] + rw [← mul_sum, ← mul_assoc, hkill, zero_mul, add_zero] + rfl + + +/-- **Native recovery computes the mathematical recovery modulo `2 ^ p`.** Let `π : B → A_N`, +`N = p + Δ`, be a ring homomorphism, with `Δ ≥ (a + 1) ∑_t q_t`. Let the native points `ζl` +represent the images of points `ζ i ∈ B` with unit differences, and at every node index `k` of +positive multiplicity let the native values `W.getD k []` agree with the images of the values +`v t i` at `t = node a k` modulo `2 ^ p_t`, where `p + 6 h D_t + q_t (a + 1) ≤ p_t`. If the +recovered value `recoverFromValues B a u h ζ v` is an element `R` of `B`, then native recovery +computes the reduction of `π R` modulo `2 ^ p`. -/ +theorem val_recover (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2)))) + (B : Subring K) (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) {p Δ : ℕ} + (π : B →+* Ring (p + Δ) fl) {a u h : ℕ} + (hΔ : (a + 1) * ∑ t ∈ recoveryNodes a, taperMult u h t ≤ Δ) (pt : ℤ → ℕ) + (hpt : ∀ t ∈ recoveryNodes a, 0 < taperMult u h t → + p + 6 * h * 2 ^ (a - padicValInt 2 t) + taperMult u h t * (a + 1) ≤ pt t) + {n : ℕ} (ζ : Fin n → B) (hζ : Pairwise fun k l => IsUnit (ζ k - ζ l)) + (ζl : List (List ℕ)) (hlen : ζl.length = n) + (hζl : ∀ i : Fin n, val (p + Δ) fl (ζl.getD i []) = π (ζ i)) (v : ℤ → Fin n → B) + (W : List (List (List ℕ))) + (hW : ∀ k < 2 ^ (a + 1), k ≠ 2 ^ a → 0 < mult a u h k → ∀ i : Fin n, ∃ c, + val (p + Δ) fl ((W.getD k []).getD i []) = π (v (node a k) i) + 2 ^ pt (node a k) * c) + {R : B} (hR : recoverFromValues B a u h ζ v = R) : + val p fl (recover p Δ fl a u h ζl W) = reduce (Nat.le_add_right p Δ) fl (π R) := by + refine val_shiftDown ?_ + have hsum := two_pow_mul_recoverFromValues B hB hΔ ζ v + rw [hR] at hsum + have hsum' : (2 : B) ^ Δ * R = _ := Subtype.val_injective (by push_cast; exact hsum) + rw [← map_ofNat π, ← map_pow, ← map_mul, hsum', + val_foldl_range _ (fun k => if k = 2 ^ a then 0 else + val (p + Δ) fl (nodeTerm (p + Δ) fl a u h Δ ζl k (W.getD k []))) (fun S k => by + split_ifs <;> simp [val_add]), val_zero, zero_add, map_sum] + refine sum_congr rfl fun k hk => ?_ + have hk' := mem_range.mp hk + split_ifs with hkL + · rw [map_zero] + have ht := node_mem_recoveryNodes hk' hkL + exact val_nodeTerm hirr B hB π hk' hkL hΔ rfl + (fun hq => by rw [mult_eq u h hk'] at hq ⊢; exact hpt _ ht hq) ζ hζ ζl hlen hζl _ _ + (hW k hk' hkL) + +/-- **Native recovery is correct modulo `2 ^ p`.** Under the hypotheses of +`NativeRecovery.val_recover`, if the recovered value agrees with an element `x` of `B` modulo +`2 ^ p` (as `TensorNetwork.exists_recoverFromValues_eq_add_of_children` gives for the +contraction), then native recovery computes the reduction of `π x` modulo `2 ^ p`. -/ +theorem val_recover_of_eq_add + (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2)))) + (B : Subring K) (hB : ∀ c ∈ ℤ₍₂₎, algebraMap ℚ K c ∈ B) {p Δ : ℕ} + (π : B →+* Ring (p + Δ) fl) {a u h : ℕ} + (hΔ : (a + 1) * ∑ t ∈ recoveryNodes a, taperMult u h t ≤ Δ) (pt : ℤ → ℕ) + (hpt : ∀ t ∈ recoveryNodes a, 0 < taperMult u h t → + p + 6 * h * 2 ^ (a - padicValInt 2 t) + taperMult u h t * (a + 1) ≤ pt t) + {n : ℕ} (ζ : Fin n → B) (hζ : Pairwise fun k l => IsUnit (ζ k - ζ l)) + (ζl : List (List ℕ)) (hlen : ζl.length = n) + (hζl : ∀ i : Fin n, val (p + Δ) fl (ζl.getD i []) = π (ζ i)) (v : ℤ → Fin n → B) + (W : List (List (List ℕ))) + (hW : ∀ k < 2 ^ (a + 1), k ≠ 2 ^ a → 0 < mult a u h k → ∀ i : Fin n, ∃ c, + val (p + Δ) fl ((W.getD k []).getD i []) = π (v (node a k) i) + 2 ^ pt (node a k) * c) + (x : B) {b : K} (hb : b ∈ B) (hx : recoverFromValues B a u h ζ v = (x : K) + 2 ^ p * b) : + val p fl (recover p Δ fl a u h ζl W) = reduce (Nat.le_add_right p Δ) fl (π x) := by + rw [val_recover hirr B hB π hΔ pt hpt ζ hζ ζl hlen hζl v W hW (R := x + 2 ^ p * ⟨b, hb⟩) + (by rw [hx]; rfl), map_add, map_add, map_mul, map_mul, map_pow, map_pow, map_ofNat, + map_ofNat, show (2 : Ring p fl) ^ p = ((2 ^ p : ℕ) : Ring p fl) by push_cast; rfl, + natCast_two_pow, zero_mul, add_zero] + +end Hermite + end Cslib.Circuits.Frontier.NativeRecovery diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/TaperedRecovery.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/TaperedRecovery.lean index 5fc26599d7..6331ce206a 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/TaperedRecovery.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/TaperedRecovery.lean @@ -135,6 +135,13 @@ noncomputable def taperedRecovery (a u h : ℕ) (G : ℤ → K[X]) : K := (hermiteQuotient (recoveryNodes a) (fun q : ℤ => (q : K)) (taperMult u h) (2 ^ (a + 1)) t (G t)).eval (algebraMap ℚ K (-(t : ℚ) / 2 ^ (a + 1))) +theorem taperedRecovery_def (a u h : ℕ) (G : ℤ → K[X]) : + taperedRecovery a u h G = ∑ t ∈ recoveryNodes a, + (hermiteBasis (recoveryNodes a) (fun q : ℤ => (q : K)) (taperMult u h) t).eval 0 * + (hermiteQuotient (recoveryNodes a) (fun q : ℤ => (q : K)) (taperMult u h) (2 ^ (a + 1)) t + (G t)).eval (algebraMap ℚ K (-(t : ℚ) / 2 ^ (a + 1))) := by + rw [taperedRecovery] + /-- `2 ^ n` as a unit of a `ℚ`-algebra. -/ private noncomputable def twoPowUnit (K : Type*) [CommRing K] [Algebra ℚ K] (n : ℕ) : Kˣ := Units.map (algebraMap ℚ K : ℚ →* K) (Units.mk0 ((2 : ℚ) ^ n) (by positivity)) diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean index c368f3ddc8..dcc0b2ff21 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean @@ -36,6 +36,9 @@ example : (mult 1 1 1 1, mult 1 1 1 3, mult 1 1 1 0) = (1, 1, 0) := by decide `E_{-1}(0) F(-1) + E_1(0) F(1) = (4 + 6) / 2 = 5` modulo `8`, computing modulo `2 ^ (3 + 2)`. -/ example : recover 3 2 [1, 1] 1 1 1 [[0, 0]] [[], [[4, 0]], [], [[6, 0]]] = [5, 0] := by decide +/-- The same recovery with `Δ = (a + 1) ∑_t q_t = 4`, the shift that `val_recover` assumes. -/ +example : recover 3 4 [1, 1] 1 1 1 [[0, 0]] [[], [[4, 0]], [], [[6, 0]]] = [5, 0] := by decide + /-- The reciprocal of `E_t(t + α z)` is correct, for every ring homomorphism from `ℤ_(2)`. -/ example (hirr : Irreducible ((monicOf [1, 1]).map (Int.castRingHom (ZMod 2)))) (ρ : Valuation.integer (Rat.padicValuation 2) →+* Ring 5 [1, 1]) {i : ℕ} (hi : i < 2) : From 955f94ab192ec98f3e1c9d8402a69ff6ab03f420 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 00:14:48 -0400 Subject: [PATCH 042/129] feat(circuit/frontier): direct summation over described networks with ring operations on values Generalize the leaf of #1914 from natural numbers modulo 2^P to any ring operations on values (SumOps), with their reduced values, homomorphisms and polynomial-time certificates. The scheme computes the fold of the operations (sumValue); a homomorphism takes it to the contraction of the mapped entries. Instances: natural numbers modulo 2^P (SumOps.mod, the previous behaviour, with sumValue_mod and modContractionCertificate) and coefficient lists of (Z/2^p)[U]/(f) with GaloisRingArith (SumOps.galois, val_sumValue_galois). The resource theorem takes a bound Rv on reduced values. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/DescribedContraction.lean | 902 ++++++++++++------ ComplexityTests/DescribedContraction.lean | 69 +- 2 files changed, 639 insertions(+), 332 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean index fb1320cf92..9a511b6bca 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean @@ -9,6 +9,7 @@ module public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.GenericRAM.Native.StackRecursion +public import Complexity.Computability.PolyTime.GaloisRingArithmetic public import Complexity.Foundations.Data.BitString.List.Binary public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Words public import Complexity.Computability.GenericRAM.Native.BooleanFold @@ -44,43 +45,60 @@ computed from a description instead (Appendix D.4.6, p. 63). A *described networ whose entry at the bits `bs` on the ports is `eval t bs` for an evaluator `eval`. Explicit networks are the case of tables (`DescribedNetwork.ofExplicit`). +**Values and their operations** (`SumOps`). The entries are values `Y` combined by ring +operations that depend on a precision `q : Q`: a zero, a one, a sum and a product, whose results +are *reduced* (of bounded size). The two instances are natural numbers modulo `2 ^ P` +(`SumOps.mod`) and the coefficient lists of `A_p = (ℤ / 2 ^ p)[U] / (f)` at the precision +`(p, fl)` (`SumOps.galois`), the coefficient rings of the recursion (Appendix D.4.1, p. 57). A map +of the values into a commutative semiring that takes the operations at `q` to the semiring +operations (`SumOps.IsHom`) takes the computed sum to the contraction of the mapped entries. + **Direct summation as a stack recursion** (`DescribedNetwork.sumScheme`). The sum over the -`2 ^ m` assignments is a call whose children are the assignments, in binary order; an assignment -is a call whose children are the entries of the vertices at its bits, multiplied modulo `2 ^ P`; -an entry is a leaf, evaluated by the evaluator. The derivation has `2 ^ m (k + 1) + 1` nodes for -`k` vertices and two open calls (`DescribedNetwork.eval_sumRoot`). So a native certificate of the -evaluator, with any bounds, gives one native machine for the contraction modulo `2 ^ P` on every -input (`DescribedNetwork.contractionCertificate`), through +`2 ^ m` assignments is a call whose children are the assignments, in binary order, added with the +operations; an assignment is a call whose children are the entries of the vertices at its bits, +multiplied with the operations; an entry is a leaf, evaluated by the evaluator. The derivation has +`2 ^ m (k + 1) + 1` nodes for `k` vertices and two open calls, and its value is the fold of the +operations (`DescribedNetwork.sumValue`, `DescribedNetwork.eval_sumRoot`). So a native certificate +of the evaluator, with any bounds, gives one native machine for the sum on every input +(`DescribedNetwork.contractionCertificate`), through `Cslib.GenericRAM.StackRecursion.Scheme.certificate`. If the evaluator takes time `E`, space `ES` -and outputs at most `Ev` bits on every vertex, the machine takes time `2 ^ m (k + 1) + 1` times -`K (n + Ev + 1) ^ K + 2 E` and space `K (n + Ev + 1) ^ K + ES` on inputs of length `n` -(`DescribedNetwork.contractionCertificate_le`). This is the leaf step of the contraction -algorithm, with implicit packet tensors as descriptors. +and outputs at most `Ev` bits on every vertex, and reduced values are at most `Rv` bits, the +machine takes time `2 ^ m (k + 1) + 1` times `K (n + Ev + Rv + 1) ^ K + 2 E` and space +`K (n + Ev + Rv + 1) ^ K + ES` on inputs of length `n` +(`DescribedNetwork.contractionCertificate_le`). Under a homomorphism the value is the contraction +(`DescribedNetwork.SumOps.IsHom.map_sumValue`): modulo `2 ^ P` for natural numbers +(`DescribedNetwork.sumValue_mod`, `DescribedNetwork.modContractionCertificate`), and in `A_p` for +coefficient lists (`DescribedNetwork.val_sumValue_galois`). This is the leaf step of the +contraction algorithm, with implicit packet tensors as descriptors. ## Main definitions * `Cslib.Circuits.Frontier.DescribedNetwork`: a network with described tensors. * `Cslib.Circuits.Frontier.DescribedNetwork.contraction`: its contraction. +* `Cslib.Circuits.Frontier.SumOps`: ring operations on values, with `SumOps.IsHom` and + `SumOps.PolyTime`; the instances `SumOps.mod` and `SumOps.galois`. * `Cslib.Circuits.Frontier.DescribedNetwork.sumScheme`: direct summation as a recursion scheme. +* `Cslib.Circuits.Frontier.DescribedNetwork.sumValue`: its value. * `Cslib.Circuits.Frontier.DescribedNetwork.encDescribed`: the encoding of a described network. * `Cslib.Circuits.Frontier.DescribedNetwork.contractionCertificate`: one native machine for the - contraction modulo `2 ^ P`. + sum. ## Main results * `Cslib.GenericRAM.StackRecursion.Scheme.Loop.counted`: a call whose children are indexed by a counter. -* `Cslib.Circuits.Frontier.DescribedNetwork.eval_sumRoot`: direct summation evaluates to the - contraction modulo `2 ^ P`, with `2 ^ m (k + 1) + 1` nodes and two open calls. +* `Cslib.Circuits.Frontier.DescribedNetwork.eval_sumRoot`: direct summation evaluates to + `sumValue`, with `2 ^ m (k + 1) + 1` nodes and two open calls. +* `Cslib.Circuits.Frontier.SumOps.IsHom.map_sumValue`: under a homomorphism, the value is the + contraction; `DescribedNetwork.sumValue_mod` and `DescribedNetwork.val_sumValue_galois`. * `Cslib.Circuits.Frontier.DescribedNetwork.contraction_ofExplicit`: explicit networks. -* `Cslib.Circuits.Frontier.DescribedNetwork.run_sumScheme`: the machine computes the contraction - modulo `2 ^ P`. +* `Cslib.Circuits.Frontier.DescribedNetwork.run_sumScheme`: the machine computes `sumValue`. * `Cslib.Circuits.Frontier.DescribedNetwork.contractionCertificate_le`: its time and space. ## References -* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Appendix D.4.6–D.4.7 - (pp. 63–64). +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Appendix D.4.1 (p. 57) + and Appendix D.4.6–D.4.7 (pp. 63–64). -/ @[expose] public section @@ -124,7 +142,80 @@ end Cslib.GenericRAM.StackRecursion.Scheme namespace Cslib.Circuits.Frontier -open Finset Cslib.BitString Cslib.GenericRAM Cslib.GenericRAM.StackRecursion +open Finset Cslib.BitString Cslib.GenericRAM Cslib.GenericRAM.StackRecursion Turing.MultiTapeTM + +/-! ### Values and their operations -/ + +/-- **Ring operations on values** for direct summation: at each precision `q : Q`, a zero, a +one, a sum and a product on the values `Y`, and the *reduced* values at `q`, among which are the +zero, the one and every sum and product. -/ +structure SumOps (Q Y : Type) where + /-- The zero at a precision. -/ + zero : Q → Y + /-- The one at a precision. -/ + one : Q → Y + /-- The sum at a precision. -/ + add : Q → Y → Y → Y + /-- The product at a precision. -/ + mul : Q → Y → Y → Y + /-- The reduced values at a precision. -/ + Reduced : Q → Y → Prop + reduced_zero (q : Q) : Reduced q (zero q) + reduced_one (q : Q) : Reduced q (one q) + reduced_add (q : Q) (a b : Y) : Reduced q (add q a b) + reduced_mul (q : Q) (a b : Y) : Reduced q (mul q a b) + +namespace SumOps + +variable {Q Y : Type} (ops : SumOps Q Y) + +/-- A *homomorphism* of the operations at `q`: a map of the values into a commutative semiring +taking the zero, the one, sums and products at `q` to those of the semiring. -/ +structure IsHom (q : Q) {R : Type*} [CommSemiring R] (φ : Y → R) : Prop where + map_zero : φ (ops.zero q) = 0 + map_one : φ (ops.one q) = 1 + map_add (a b : Y) : φ (ops.add q a b) = φ a + φ b + map_mul (a b : Y) : φ (ops.mul q a b) = φ a * φ b + +/-- **Polynomial-time operations**, for encodings of the precisions and the values. -/ +structure PolyTime (encQ : Q ↪ List Bool) (encY : Y ↪ List Bool) : Prop where + zero : ComputableInPolyTime ops.zero encQ encY + one : ComputableInPolyTime ops.one encQ encY + add : ComputableInPolyTime (fun z : Q × Y × Y => ops.add z.1 z.2.1 z.2.2) + (encPair encQ (encPair encY encY)) encY + mul : ComputableInPolyTime (fun z : Q × Y × Y => ops.mul z.1 z.2.1 z.2.2) + (encPair encQ (encPair encY encY)) encY + +/-- A left fold of the sum or the product from a reduced start is reduced. -/ +theorem reduced_foldl {α : Type} (q : Q) (op : Y → Y → Y) (hop : ∀ a b, ops.Reduced q (op a b)) + (f : α → Y) (l : List α) {a : Y} (ha : ops.Reduced q a) : + ops.Reduced q (l.foldl (fun acc v => op acc (f v)) a) := by + induction l generalizing a with + | nil => exact ha + | cons v l ih => exact ih (hop _ _) + +variable {ops} + +/-- A homomorphism takes a fold of products to the product of the images. -/ +theorem IsHom.map_foldl_mul {q : Q} {R : Type*} [CommSemiring R] {φ : Y → R} (h : ops.IsHom q φ) + {α : Type} (f : α → Y) (l : List α) (a : Y) : + φ (l.foldl (fun acc v => ops.mul q acc (f v)) a) = φ a * (l.map fun v => φ (f v)).prod := by + induction l generalizing a with + | nil => simp + | cons v l ih => rw [List.foldl_cons, ih, h.map_mul, List.map_cons, List.prod_cons, mul_assoc] + +/-- A homomorphism takes a fold of sums over a range to the sum of the images. -/ +theorem IsHom.map_foldl_add {q : Q} {R : Type*} [CommSemiring R] {φ : Y → R} (h : ops.IsHom q φ) + (g : ℕ → Y) (n : ℕ) (a : Y) : + φ ((List.range n).foldl (fun acc j => ops.add q acc (g j)) a) = + φ a + ∑ j ∈ range n, φ (g j) := by + induction n with + | zero => simp + | succ n ih => + rw [List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil, h.map_add, ih, + sum_range_succ, add_assoc] + +end SumOps /-- A Boolean tensor network with *described* tensors: the number of edges, numbered from zero, and for each vertex its ordered list of ports and a descriptor of its tensor. -/ @@ -136,7 +227,7 @@ structure DescribedNetwork (T : Type) where namespace DescribedNetwork -variable {T : Type} (eval : T → List Bool → ℕ) +variable {T : Type} /-- The bits that a word, read as an assignment of bits to the edges, puts on a list of ports; a port past the end of the word reads `false`. -/ @@ -144,8 +235,9 @@ def wordBits (word : List Bool) (ports : List ℕ) : List Bool := ports.map fun i => word.getD i false /-- The *contraction*: the sum over all assignments of bits to the edges of the product of the -entries of the vertices at the bits on their ports. -/ -def contraction (d : DescribedNetwork T) : ℕ := +entries of the vertices at the bits on their ports, for entries in a commutative semiring. -/ +def contraction {R : Type*} [CommSemiring R] (eval : T → List Bool → R) + (d : DescribedNetwork T) : R := ∑ x : Fin d.numEdges → Bool, (d.vertices.map fun v => eval v.2 (wordBits (List.ofFn x) v.1)).prod @@ -171,25 +263,36 @@ theorem contraction_ofExplicit (d : ExplicitTensorNetwork ℕ) : funext i exact getD_ofFn x i +/-- The contraction commutes with maps of semirings. -/ +theorem map_contraction {R S : Type*} [CommSemiring R] [CommSemiring S] (ψ : R →+* S) + (eval : T → List Bool → R) (d : DescribedNetwork T) : + ψ (d.contraction eval) = d.contraction fun t bs => ψ (eval t bs) := by + rw [contraction, contraction, map_sum] + refine sum_congr rfl fun x _ => ?_ + rw [map_list_prod, List.map_map] + rfl + /-! ### Direct summation as a recursion scheme -/ /-- A node of direct summation: its kind (`0` the sum, `1` an assignment, `2` an entry), the precision and the network, and the current word and vertex index. -/ -abbrev Node (T : Type) := ℕ × ((ℕ × DescribedNetwork T) × (List Bool × ℕ)) +abbrev Node (Q T : Type) := ℕ × ((Q × DescribedNetwork T) × (List Bool × ℕ)) + +variable {Q Y : Type} (ops : SumOps Q Y) (eval : T → List Bool → Y) /-- The entry of a node: the evaluator at the descriptor of its vertex and the bits of its word -on the vertex's ports, or zero for an index past the vertices. -/ -def entryOf (x : Node T) : ℕ := - (x.2.1.2.vertices[x.2.2.2]?).elim 0 fun v => eval v.2 (wordBits x.2.2.1 v.1) +on the vertex's ports, or the zero for an index past the vertices. -/ +def entryOf (x : Node Q T) : Y := + (x.2.1.2.vertices[x.2.2.2]?).elim (ops.zero x.2.1.1) fun v => eval v.2 (wordBits x.2.2.1 v.1) /-- **Direct summation as a recursion scheme.** The sum (kind `0`) has the `2 ^ m` assignments as -children, adding their values modulo `2 ^ P`; an assignment (kind `1`) has the entries of the -vertices as children, multiplying them modulo `2 ^ P`; an entry (kind `2`) is a leaf. The -accumulator is a counter and a residue. -/ -def sumScheme : Scheme (Node T) (ℕ × ℕ) ℕ where +children, adding their values; an assignment (kind `1`) has the entries of the vertices as +children, multiplying them; an entry (kind `2`) is a leaf. The operations are those at the +precision of the node, and the accumulator is a counter and a value. -/ +def sumScheme : Scheme (Node Q T) (ℕ × Y) Y where isLeaf x := x.1 == 2 - leaf := entryOf eval - start x := (0, (if x.1 = 0 then 0 else 1) % 2 ^ x.2.1.1) + leaf := entryOf ops eval + start x := (0, if x.1 = 0 then ops.zero x.2.1.1 else ops.one x.2.1.1) next x a := if x.1 = 0 then (if a.1 < 2 ^ x.2.1.2.numEdges then some (1, (x.2.1, (lowBits x.2.1.2.numEdges a.1, 0))) @@ -198,157 +301,242 @@ def sumScheme : Scheme (Node T) (ℕ × ℕ) ℕ where (if a.1 < x.2.1.2.vertices.length then some (2, (x.2.1, (x.2.2.1, min x.2.1.2.vertices.length a.1))) else none) else none - absorb x a y := (a.1 + 1, if x.1 = 0 then (a.2 + y) % 2 ^ x.2.1.1 else a.2 * y % 2 ^ x.2.1.1) + absorb x a y := + (a.1 + 1, if x.1 = 0 then ops.add x.2.1.1 a.2 y else ops.mul x.2.1.1 a.2 y) finish _ a := a.2 -/-- The root of direct summation on `(P, d)`. -/ -def sumRoot (q : ℕ × DescribedNetwork T) : Node T := (0, (q, ([], 0))) +/-- The root of direct summation on `(q, d)`. -/ +def sumRoot (q : Q × DescribedNetwork T) : Node Q T := (0, (q, ([], 0))) + +/-- The weight of a word: the product of the entries at its bits, folded with the operations at +`q`. -/ +def weight (q : Q) (d : DescribedNetwork T) (word : List Bool) : Y := + d.vertices.foldl (fun acc v => ops.mul q acc (eval v.2 (wordBits word v.1))) (ops.one q) -/-- The weight of a word modulo `2 ^ P`: the product of the entries at its bits, reduced after -each factor. -/ -def weightMod (P : ℕ) (d : DescribedNetwork T) (word : List Bool) : ℕ := - d.vertices.foldl (fun acc v => acc * eval v.2 (wordBits word v.1) % 2 ^ P) (1 % 2 ^ P) +/-- **The value of direct summation**: the weights of the `2 ^ m` words in binary order, folded +with the sum at `q`. -/ +def sumValue (q : Q) (d : DescribedNetwork T) : Y := + (List.range (2 ^ d.numEdges)).foldl + (fun acc j => ops.add q acc (weight ops eval q d (lowBits d.numEdges j))) (ops.zero q) /-- The shape of a node of direct summation on `q`: it keeps `q`, its kind is at most `2`, its word has at most `m` bits and its index is at most the number of vertices. -/ -def NodeShape (q : ℕ × DescribedNetwork T) (x : Node T) : Prop := +def NodeShape (q : Q × DescribedNetwork T) (x : Node Q T) : Prop := x.2.1 = q ∧ x.1 ≤ 2 ∧ x.2.2.1.length ≤ q.2.numEdges ∧ x.2.2.2 ≤ q.2.vertices.length -/-- The invariant of direct summation on `q = (P, d)`: nodes have the shape of `NodeShape`, frames -also have a counter at most `2 ^ m + k` and a residue below `2 ^ P`, and every value is a residue -below `2 ^ P` or an entry of a vertex at the bits of an `m`-bit word. -/ -def sumInvariant (q : ℕ × DescribedNetwork T) : Invariant (Node T) (ℕ × ℕ) ℕ := +/-- The invariant of direct summation on `q`: nodes have the shape of `NodeShape`, frames also +have a counter at most `2 ^ m + k` and a reduced value, and every value is reduced or an entry of +a vertex at the bits of an `m`-bit word. -/ +def sumInvariant (q : Q × DescribedNetwork T) : Invariant (Node Q T) (ℕ × Y) Y := ⟨NodeShape q, fun x a => NodeShape q x ∧ a.1 ≤ 2 ^ q.2.numEdges + q.2.vertices.length ∧ - a.2 < 2 ^ q.1, - fun y => y < 2 ^ q.1 ∨ ∃ v ∈ q.2.vertices, ∃ w : List Bool, w.length = q.2.numEdges ∧ + ops.Reduced q.1 a.2, + fun y => ops.Reduced q.1 y ∨ ∃ v ∈ q.2.vertices, ∃ w : List Bool, w.length = q.2.numEdges ∧ y = eval v.2 (wordBits w v.1)⟩ -variable {eval} +variable {ops eval} /-- An entry is a leaf. -/ -theorem eval_entry (q : ℕ × DescribedNetwork T) {word : List Bool} {v : ℕ} +theorem eval_entry (q : Q × DescribedNetwork T) {word : List Bool} {v : ℕ} (hw : word.length = q.2.numEdges) (hv : v < q.2.vertices.length) : - (sumScheme eval).Eval (sumInvariant eval q) (2, (q, (word, v))) - (entryOf eval (2, (q, (word, v)))) 1 0 := by - refine Scheme.Eval.leaf (R := sumScheme eval) ⟨rfl, by omega, hw.le, hv.le⟩ rfl + (sumScheme ops eval).Eval (sumInvariant ops eval q) (2, (q, (word, v))) + (entryOf ops eval (2, (q, (word, v)))) 1 0 := by + refine Scheme.Eval.leaf (R := sumScheme ops eval) ⟨rfl, by omega, hw.le, hv.le⟩ rfl (.inr ⟨q.2.vertices[v], List.getElem_mem hv, word, hw, ?_⟩) - change (q.2.vertices[v]?).elim 0 _ = _ + change (q.2.vertices[v]?).elim _ _ = _ rw [List.getElem?_eq_getElem hv] rfl -private theorem foldl_take_succ (d : DescribedNetwork T) (P : ℕ) (word : List Bool) (c : ℕ) +private theorem foldl_take_succ (q : Q) (d : DescribedNetwork T) (word : List Bool) (c : ℕ) (hc : c < d.vertices.length) : - (d.vertices.take (c + 1)).foldl (fun acc v => acc * eval v.2 (wordBits word v.1) % 2 ^ P) - (1 % 2 ^ P) = - (d.vertices.take c).foldl (fun acc v => acc * eval v.2 (wordBits word v.1) % 2 ^ P) - (1 % 2 ^ P) * entryOf eval (2, ((P, d), (word, c))) % 2 ^ P := by + (d.vertices.take (c + 1)).foldl (fun acc v => ops.mul q acc (eval v.2 (wordBits word v.1))) + (ops.one q) = + ops.mul q ((d.vertices.take c).foldl + (fun acc v => ops.mul q acc (eval v.2 (wordBits word v.1))) (ops.one q)) + (entryOf ops eval (2, ((q, d), (word, c)))) := by rw [List.take_add_one, List.getElem?_eq_getElem hc, Option.toList_some, List.foldl_append] simp [entryOf, List.getElem?_eq_getElem hc] -private theorem foldl_mod_lt {α : Type} (f : α → ℕ) (P : ℕ) (l : List α) : - l.foldl (fun acc v => acc * f v % 2 ^ P) (1 % 2 ^ P) < 2 ^ P := by - have h (a : ℕ) (ha : a < 2 ^ P) : l.foldl (fun acc v => acc * f v % 2 ^ P) a < 2 ^ P := by - induction l generalizing a with - | nil => exact ha - | cons v l ih => exact ih _ (Nat.mod_lt _ (Nat.two_pow_pos P)) - exact h _ (Nat.mod_lt _ (Nat.two_pow_pos P)) - /-- **An assignment evaluates to the weight of its word**, with one node per vertex plus one and one open call. -/ -theorem eval_assign (P : ℕ) (d : DescribedNetwork T) {word : List Bool} +theorem eval_assign (q : Q) (d : DescribedNetwork T) {word : List Bool} (hw : word.length = d.numEdges) : - ∃ h ≤ 1, (sumScheme eval).Eval (sumInvariant eval (P, d)) (1, ((P, d), (word, 0))) - (weightMod eval P d word) (d.vertices.length + 1) h := by - set acc : ℕ → ℕ × ℕ := fun c => (c, (d.vertices.take c).foldl - (fun acc v => acc * eval v.2 (wordBits word v.1) % 2 ^ P) (1 % 2 ^ P)) - have hshape : NodeShape (P, d) ((1, ((P, d), (word, 0))) : Node T) := + ∃ h ≤ 1, (sumScheme ops eval).Eval (sumInvariant ops eval (q, d)) (1, ((q, d), (word, 0))) + (weight ops eval q d word) (d.vertices.length + 1) h := by + set acc : ℕ → ℕ × Y := fun c => (c, (d.vertices.take c).foldl + (fun acc v => ops.mul q acc (eval v.2 (wordBits word v.1))) (ops.one q)) + have hshape : NodeShape (q, d) ((1, ((q, d), (word, 0))) : Node Q T) := ⟨rfl, by omega, hw.le, Nat.zero_le _⟩ - obtain ⟨h, hh, hloop⟩ := Scheme.Loop.counted (R := sumScheme eval) - (I := sumInvariant eval (P, d)) - (x := (1, ((P, d), (word, 0)))) (acc := acc) - (child := fun c => (2, ((P, d), (word, c)))) - (val := fun c => entryOf eval (2, ((P, d), (word, c)))) + have hred (c : ℕ) : ops.Reduced q (acc c).2 := + ops.reduced_foldl q _ (ops.reduced_mul q) _ _ (ops.reduced_one q) + obtain ⟨h, hh, hloop⟩ := Scheme.Loop.counted (R := sumScheme ops eval) + (I := sumInvariant ops eval (q, d)) + (x := (1, ((q, d), (word, 0)))) (acc := acc) + (child := fun c => (2, ((q, d), (word, c)))) + (val := fun c => entryOf ops eval (2, ((q, d), (word, c)))) (nodes := fun _ => 1) (height := fun _ => 0) d.vertices.length 0 (fun c hc => by simp [sumScheme, acc, hc, min_eq_right hc.le]) (by simp [sumScheme, acc]) (fun c hc => by simp only [sumScheme, acc, one_ne_zero, ite_false] - rw [foldl_take_succ d P word c hc]) - (fun c hc => eval_entry (P, d) hw hc) - (fun c hc => ⟨hshape, (Nat.le_of_lt hc).trans (Nat.le_add_left _ _), foldl_mod_lt _ _ _⟩) + rw [foldl_take_succ q d word c hc]) + (fun c hc => eval_entry (q, d) hw hc) + (fun c hc => ⟨hshape, (Nat.le_of_lt hc).trans (Nat.le_add_left _ _), hred c⟩) (fun _ _ => le_rfl) - have hstart : (sumScheme eval).start (1, ((P, d), (word, 0))) = acc 0 := by + have hstart : (sumScheme ops eval).start (1, ((q, d), (word, 0))) = acc 0 := by simp [sumScheme, acc] rw [← hstart] at hloop refine ⟨h + 1, by omega, ?_⟩ - have := Scheme.Eval.call (R := sumScheme eval) (I := sumInvariant eval (P, d)) - (x := (1, ((P, d), (word, 0)))) hshape rfl hloop (.inl (foldl_mod_lt _ _ _)) - simpa [sumScheme, acc, weightMod, List.take_length, Finset.sum_const, Finset.card_range] using + have := Scheme.Eval.call (R := sumScheme ops eval) (I := sumInvariant ops eval (q, d)) + (x := (1, ((q, d), (word, 0)))) hshape rfl hloop (.inl (hred d.vertices.length)) + simpa [sumScheme, acc, weight, List.take_length, Finset.sum_const, Finset.card_range] using this -private theorem sum_lowBits (n : ℕ) (g : List Bool → ℕ) : +private theorem sum_lowBits {M : Type*} [AddCommMonoid M] (n : ℕ) (g : List Bool → M) : ∑ j ∈ range (2 ^ n), g (lowBits n j) = ∑ x : Fin n → Bool, g (List.ofFn x) := by rw [← Fin.sum_univ_eq_sum_range] exact Fintype.sum_equiv (equivFin n).symm _ _ fun k => by rw [lowBits_eq_ofFn_equivFin_symm] -private theorem foldl_mul_mod {α : Type} (f : α → ℕ) (m : ℕ) (l : List α) (a : ℕ) : - l.foldl (fun acc v => acc * f v % m) (a % m) = a * (l.map f).prod % m := by - induction l generalizing a with - | nil => simp - | cons v l ih => - rw [List.foldl_cons, Nat.mod_mul_mod, ih, List.map_cons, List.prod_cons, Nat.mul_assoc] - -private theorem weightMod_eq (P : ℕ) (d : DescribedNetwork T) (word : List Bool) : - weightMod eval P d word = - (d.vertices.map fun v => eval v.2 (wordBits word v.1)).prod % 2 ^ P := by - rw [weightMod, foldl_mul_mod, Nat.one_mul] - -/-- **Direct summation computes the contraction modulo `2 ^ P`**, with `2 ^ m (k + 1) + 1` -nodes for `m` edges and `k` vertices, and at most two open calls. -/ -theorem eval_sumRoot (P : ℕ) (d : DescribedNetwork T) : - ∃ h ≤ 2, (sumScheme eval).Eval (sumInvariant eval (P, d)) (sumRoot (P, d)) - (d.contraction eval % 2 ^ P) (2 ^ d.numEdges * (d.vertices.length + 1) + 1) h := by - set acc : ℕ → ℕ × ℕ := fun c => (c, (∑ j ∈ range c, weightMod eval P d (lowBits d.numEdges j)) % - 2 ^ P) - have hassign (c : ℕ) := eval_assign (eval := eval) P d (length_lowBits d.numEdges c) +/-- **Direct summation computes `sumValue`**, with `2 ^ m (k + 1) + 1` nodes for `m` edges and +`k` vertices, and at most two open calls. -/ +theorem eval_sumRoot (q : Q) (d : DescribedNetwork T) : + ∃ h ≤ 2, (sumScheme ops eval).Eval (sumInvariant ops eval (q, d)) (sumRoot (q, d)) + (sumValue ops eval q d) (2 ^ d.numEdges * (d.vertices.length + 1) + 1) h := by + set acc : ℕ → ℕ × Y := fun c => (c, (List.range c).foldl + (fun acc j => ops.add q acc (weight ops eval q d (lowBits d.numEdges j))) (ops.zero q)) + have hassign (c : ℕ) := eval_assign (ops := ops) (eval := eval) q d + (length_lowBits d.numEdges c) choose hgt hhgt heval using hassign - have hshape : NodeShape (P, d) (sumRoot (P, d)) := ⟨rfl, by simp [sumRoot], by simp [sumRoot], + have hshape : NodeShape (q, d) (sumRoot (q, d)) := ⟨rfl, by simp [sumRoot], by simp [sumRoot], by simp [sumRoot]⟩ - obtain ⟨h, hh, hloop⟩ := Scheme.Loop.counted (R := sumScheme eval) - (I := sumInvariant eval (P, d)) - (x := sumRoot (P, d)) (acc := acc) - (child := fun c => (1, ((P, d), (lowBits d.numEdges c, 0)))) - (val := fun c => weightMod eval P d (lowBits d.numEdges c)) + have hred (c : ℕ) : ops.Reduced q (acc c).2 := + ops.reduced_foldl q _ (ops.reduced_add q) _ _ (ops.reduced_zero q) + obtain ⟨h, hh, hloop⟩ := Scheme.Loop.counted (R := sumScheme ops eval) + (I := sumInvariant ops eval (q, d)) + (x := sumRoot (q, d)) (acc := acc) + (child := fun c => (1, ((q, d), (lowBits d.numEdges c, 0)))) + (val := fun c => weight ops eval q d (lowBits d.numEdges c)) (nodes := fun _ => d.vertices.length + 1) (height := hgt) (2 ^ d.numEdges) 1 (fun c hc => by simp [sumScheme, sumRoot, acc, hc]) (by simp [sumScheme, sumRoot, acc]) - (fun c hc => by simp [sumScheme, sumRoot, acc, sum_range_succ, Nat.add_mod]) + (fun c hc => by + simp only [sumScheme, sumRoot, acc, ite_true, List.range_succ, List.foldl_append, + List.foldl_cons, List.foldl_nil]) (fun c _ => heval c) - (fun c hc => ⟨hshape, by simp only [acc]; omega, Nat.mod_lt _ (Nat.two_pow_pos P)⟩) + (fun c hc => ⟨hshape, by simp only [acc]; omega, hred c⟩) (fun c _ => hhgt c) - have hstart : (sumScheme eval).start (sumRoot (P, d)) = acc 0 := by + have hstart : (sumScheme ops eval).start (sumRoot (q, d)) = acc 0 := by simp [sumScheme, sumRoot, acc] rw [← hstart] at hloop refine ⟨h + 1, by omega, ?_⟩ - have := Scheme.Eval.call (R := sumScheme eval) (I := sumInvariant eval (P, d)) - (x := sumRoot (P, d)) hshape rfl hloop (.inl (Nat.mod_lt _ (Nat.two_pow_pos P))) - have hval : (sumScheme eval).finish (sumRoot (P, d)) (acc (2 ^ d.numEdges)) = - d.contraction eval % 2 ^ P := by - simp only [sumScheme, acc, contraction] - rw [sum_lowBits d.numEdges (weightMod eval P d)] - simp only [weightMod_eq] - exact (Finset.sum_nat_mod _ _ _).symm - rw [hval, sum_const, card_range, smul_eq_mul] at this + have := Scheme.Eval.call (R := sumScheme ops eval) (I := sumInvariant ops eval (q, d)) + (x := sumRoot (q, d)) hshape rfl hloop (.inl (hred (2 ^ d.numEdges))) + rw [sum_const, card_range, smul_eq_mul] at this exact this +/-- `sumValue` is reduced. -/ +theorem reduced_sumValue (q : Q) (d : DescribedNetwork T) : ops.Reduced q (sumValue ops eval q d) := + ops.reduced_foldl q _ (ops.reduced_add q) _ _ (ops.reduced_zero q) + +/-- **Under a homomorphism, the value is the contraction** of the mapped entries. -/ +theorem _root_.Cslib.Circuits.Frontier.SumOps.IsHom.map_sumValue {q : Q} {R : Type*} + [CommSemiring R] {φ : Y → R} (h : ops.IsHom q φ) (d : DescribedNetwork T) : + φ (sumValue ops eval q d) = d.contraction fun t bs => φ (eval t bs) := by + rw [sumValue, h.map_foldl_add, h.map_zero, zero_add, contraction, + sum_lowBits d.numEdges (fun w => φ (weight ops eval q d w))] + refine sum_congr rfl fun x _ => ?_ + rw [weight, h.map_foldl_mul, h.map_one, one_mul] + +/-! ### Natural numbers modulo a power of two -/ + +/-- **Natural numbers modulo `2 ^ P`**, with the precision `P`; the reduced values are those +below `2 ^ P`. -/ +def _root_.Cslib.Circuits.Frontier.SumOps.mod : SumOps ℕ ℕ where + zero _ := 0 + one P := 1 % 2 ^ P + add P a b := (a + b) % 2 ^ P + mul P a b := a * b % 2 ^ P + Reduced P y := y < 2 ^ P + reduced_zero P := Nat.two_pow_pos P + reduced_one P := Nat.mod_lt _ (Nat.two_pow_pos P) + reduced_add P _ _ := Nat.mod_lt _ (Nat.two_pow_pos P) + reduced_mul P _ _ := Nat.mod_lt _ (Nat.two_pow_pos P) + +/-- The cast into `ZMod (2 ^ P)` is a homomorphism of the operations at `P`. -/ +theorem _root_.Cslib.Circuits.Frontier.SumOps.mod_isHom (P : ℕ) : + SumOps.mod.IsHom P (Nat.cast : ℕ → ZMod (2 ^ P)) where + map_zero := Nat.cast_zero + map_one := by simp only [SumOps.mod, ZMod.natCast_mod, Nat.cast_one] + map_add a b := by simp only [SumOps.mod, ZMod.natCast_mod, Nat.cast_add] + map_mul a b := by simp only [SumOps.mod, ZMod.natCast_mod, Nat.cast_mul] + +/-- **Modulo `2 ^ P`, the value is the contraction modulo `2 ^ P`.** -/ +theorem sumValue_mod (eval : T → List Bool → ℕ) (P : ℕ) (d : DescribedNetwork T) : + sumValue SumOps.mod eval P d = d.contraction eval % 2 ^ P := by + have h := (SumOps.mod_isHom P).map_sumValue (eval := eval) d + have h2 : ((d.contraction eval : ℕ) : ZMod (2 ^ P)) = + d.contraction fun t bs => ((eval t bs : ℕ) : ZMod (2 ^ P)) := + map_contraction (Nat.castRingHom (ZMod (2 ^ P))) eval d + have h' := congrArg ZMod.val (h.trans h2.symm) + rwa [ZMod.val_cast_of_lt (reduced_sumValue (ops := SumOps.mod) P d), ZMod.val_natCast] at h' + +/-! ### Coefficient lists of `(ℤ / 2 ^ p)[U] / (f)` -/ + +/-- **Coefficient lists of `A_p = (ℤ / 2 ^ p)[U] / (f)`**, with the precision `(p, fl)` and the +operations of `GaloisRingArith`; the reduced values have `e = fl.length` coordinates, each at most +`2 ^ p`. -/ +def _root_.Cslib.Circuits.Frontier.SumOps.galois : SumOps (ℕ × List ℕ) (List ℕ) where + zero q := GaloisRingArith.zero q.2 + one q := GaloisRingArith.one q.2 + add q := GaloisRingArith.add q.1 q.2 + mul q := GaloisRingArith.mul q.1 q.2 + Reduced q c := c.length = q.2.length ∧ ∀ x ∈ c, x ≤ 2 ^ q.1 + reduced_zero q := ⟨GaloisRingArith.length_zero _, fun x hx => by + simp only [GaloisRingArith.zero, List.mem_map] at hx + obtain ⟨_, -, rfl⟩ := hx + exact Nat.zero_le _⟩ + reduced_one q := ⟨by simp [GaloisRingArith.one], fun x hx => by + simp only [GaloisRingArith.one, List.mem_map] at hx + obtain ⟨i, -, rfl⟩ := hx + split_ifs + · exact Nat.one_le_two_pow + · exact Nat.zero_le _⟩ + reduced_add q _ _ := ⟨GaloisRingArith.length_add _ _ _ _, + fun _ hx => (GaloisRingArith.lt_of_mem_add hx).le⟩ + reduced_mul q _ _ := ⟨GaloisRingArith.length_mul _ _ _ _, + fun _ hx => (GaloisRingArith.lt_of_mem_mul hx).le⟩ + +/-- The value in `A_p` is a homomorphism of the operations at `(p, fl)`. -/ +theorem _root_.Cslib.Circuits.Frontier.SumOps.galois_isHom (q : ℕ × List ℕ) : + SumOps.galois.IsHom q (GaloisRingArith.val q.1 q.2) where + map_zero := GaloisRingArith.val_zero + map_one := GaloisRingArith.val_one + map_add := GaloisRingArith.val_add + map_mul := GaloisRingArith.val_mul + +/-- **In `A_p`, the value is the contraction** of the values of the entries. -/ +theorem val_sumValue_galois (eval : T → List Bool → List ℕ) (q : ℕ × List ℕ) + (d : DescribedNetwork T) : + GaloisRingArith.val q.1 q.2 (sumValue SumOps.galois eval q d) = + d.contraction fun t bs => GaloisRingArith.val q.1 q.2 (eval t bs) := + (SumOps.galois_isHom q).map_sumValue d + +/-- A reduced coefficient list at `(p, fl)` is at most `e (2 p + 3)` long in binary, with +`e = fl.length`. -/ +theorem _root_.Cslib.Circuits.Frontier.SumOps.length_encList_le_of_galois_reduced + {q : ℕ × List ℕ} {c : List ℕ} (hc : SumOps.galois.Reduced q c) : + (encList encNat c).length ≤ q.2.length * (2 * q.1 + 3) := by + obtain ⟨hlen, hle⟩ := hc + have := GaloisRingArith.length_encList_le (p := q.1 + 1) hlen fun x hx => + (hle x hx).trans_lt (Nat.pow_lt_pow_right (by norm_num) (Nat.lt_succ_self _)) + calc (encList encNat c).length ≤ q.2.length * (2 * (q.1 + 1) + 1) := this + _ = q.2.length * (2 * q.1 + 3) := by ring + /-! ### The native machine -/ section Native -open Turing.MultiTapeTM - -variable (encT : T ↪ List Bool) +variable (encQ : Q ↪ List Bool) (encY : Y ↪ List Bool) (encT : T ↪ List Bool) /-- A described network is its number of edges and its list of vertices. -/ def equivProd : DescribedNetwork T ≃ ℕ × List (List ℕ × T) where @@ -362,18 +550,18 @@ each the pair of its list of ports in binary and its descriptor. -/ def encDescribed : DescribedNetwork T ↪ List Bool := encOfEquiv equivProd (encPair encUnary (encList (encPair (encList encNat) encT))) -/-- The encoding of an input `(P, d)`: the precision in unary and the network. -/ -abbrev encInput : ℕ × DescribedNetwork T ↪ List Bool := encPair encUnary (encDescribed encT) +/-- The encoding of an input `(q, d)`: the precision and the network. -/ +abbrev encInput : Q × DescribedNetwork T ↪ List Bool := encPair encQ (encDescribed encT) /-- The encoding of a node: its kind in unary, its input, its word and its vertex index in unary. -/ -abbrev encNode : Node T ↪ List Bool := - encPair encUnary (encPair (encInput encT) (encPair encBits encUnary)) +abbrev encNode : Node Q T ↪ List Bool := + encPair encUnary (encPair (encInput encQ encT) (encPair encBits encUnary)) -/-- The encoding of an accumulator: the counter and the residue, in binary. -/ -abbrev encAcc : ℕ × ℕ ↪ List Bool := encPair encNat encNat +/-- The encoding of an accumulator: the counter in binary and the value. -/ +abbrev encAcc : ℕ × Y ↪ List Bool := encPair encNat encY -variable {encT} +variable {encQ encY encT} /-- The number of edges of a described network, in unary. -/ theorem numEdges_polyTime : @@ -388,29 +576,29 @@ theorem vertices_polyTime : (ComputableInPolyTime.snd _ _).comp (ComputableInPolyTime.of_eq (f := equivProd) fun _ => rfl) /-- The kind of a node. -/ -theorem kind_polyTime : ComputableInPolyTime (fun x : Node T => x.1) (encNode encT) +theorem kind_polyTime : ComputableInPolyTime (fun x : Node Q T => x.1) (encNode encQ encT) encUnary := ComputableInPolyTime.fst _ _ -/-- The input `(P, d)` of a node. -/ -theorem input_polyTime : ComputableInPolyTime (fun x : Node T => x.2.1) (encNode encT) - (encInput encT) := (ComputableInPolyTime.fst _ _).comp (ComputableInPolyTime.snd _ _) +/-- The input `(q, d)` of a node. -/ +theorem input_polyTime : ComputableInPolyTime (fun x : Node Q T => x.2.1) (encNode encQ encT) + (encInput encQ encT) := (ComputableInPolyTime.fst _ _).comp (ComputableInPolyTime.snd _ _) /-- The precision of a node. -/ -theorem precision_polyTime : ComputableInPolyTime (fun x : Node T => x.2.1.1) - (encNode encT) encUnary := (ComputableInPolyTime.fst _ _).comp input_polyTime +theorem precision_polyTime : ComputableInPolyTime (fun x : Node Q T => x.2.1.1) + (encNode encQ encT) encQ := (ComputableInPolyTime.fst _ _).comp input_polyTime /-- The network of a node. -/ -theorem network_polyTime : ComputableInPolyTime (fun x : Node T => x.2.1.2) - (encNode encT) (encDescribed encT) := (ComputableInPolyTime.snd _ _).comp input_polyTime +theorem network_polyTime : ComputableInPolyTime (fun x : Node Q T => x.2.1.2) + (encNode encQ encT) (encDescribed encT) := (ComputableInPolyTime.snd _ _).comp input_polyTime /-- The word of a node. -/ -theorem word_polyTime : ComputableInPolyTime (fun x : Node T => x.2.2.1) - (encNode encT) encBits := +theorem word_polyTime : ComputableInPolyTime (fun x : Node Q T => x.2.2.1) + (encNode encQ encT) encBits := (ComputableInPolyTime.fst _ _).comp (ComputableInPolyTime.snd _ _).snd' /-- The vertex index of a node. -/ -theorem index_polyTime : ComputableInPolyTime (fun x : Node T => x.2.2.2) - (encNode encT) encUnary := +theorem index_polyTime : ComputableInPolyTime (fun x : Node Q T => x.2.2.2) + (encNode encQ encT) encUnary := (ComputableInPolyTime.snd _ _).comp (ComputableInPolyTime.snd _ _).snd' /-- `2 ^ P`, from `P` in unary. -/ @@ -419,49 +607,44 @@ private theorem twoPow {α : Type} {encIn : α ↪ List Bool} {P : α → ℕ} ComputableInPolyTime (fun a => 2 ^ P a) encIn encNat := ComputableInPolyTime.binary_two_pow.comp hP -/-- `x % 2 ^ P`, from `P` in unary. -/ -private theorem modTwoPow {α : Type} {encIn : α ↪ List Bool} {x P : α → ℕ} - (hx : ComputableInPolyTime x encIn encNat) (hP : ComputableInPolyTime P encIn encUnary) : - ComputableInPolyTime (fun a => x a % 2 ^ P a) encIn encNat := - ComputableInPolyTime.binary_mod.comp (hx.prod (twoPow hP)) - -/-- **The polynomial-time components of direct summation.** -/ -theorem sumScheme_polyTime : (sumScheme eval).PolyTime (encNode encT) encAcc encNat where +/-- **The polynomial-time components of direct summation**, from polynomial-time operations. -/ +theorem sumScheme_polyTime (hops : ops.PolyTime encQ encY) : + (sumScheme ops eval).PolyTime (encNode encQ encT) (encAcc encY) encY where isLeaf := ComputableInPolyTime.beq kind_polyTime (ComputableInPolyTime.const 2) start := by - refine (ComputableInPolyTime.const 0).prod (modTwoPow ?_ precision_polyTime) - refine (ComputableInPolyTime.ite (encMid := encNat) (ComputableInPolyTime.beq kind_polyTime - (ComputableInPolyTime.const 0)) (ComputableInPolyTime.const 0) - (ComputableInPolyTime.const 1)).of_enc_eq fun x => ?_ - by_cases h : x.1 = 0 <;> simp [h] + refine (ComputableInPolyTime.const 0).prod ?_ + exact (ComputableInPolyTime.ite (ComputableInPolyTime.beq kind_polyTime + (ComputableInPolyTime.const 0)) (hops.zero.comp precision_polyTime) + (hops.one.comp precision_polyTime)).of_enc_eq fun x => by + by_cases h : x.1 = 0 <;> simp [h] next := by - have hx := ComputableInPolyTime.fst (encNode encT) encAcc - have ha := ComputableInPolyTime.snd (encNode encT) encAcc + have hx := ComputableInPolyTime.fst (encNode encQ encT) (encAcc encY) + have ha := ComputableInPolyTime.snd (encNode encQ encT) (encAcc encY) have hk := kind_polyTime.comp hx - have hc := (ComputableInPolyTime.fst encNat encNat).comp ha + have hc := (ComputableInPolyTime.fst encNat encY).comp ha have hq := input_polyTime.comp hx have hd := network_polyTime.comp hx have hm := numEdges_polyTime.comp hd have hlen := (ComputableInPolyTime.length_list _).comp (vertices_polyTime.comp hd) - have hassign : ComputableInPolyTime (fun p : Node T × (ℕ × ℕ) => + have hassign : ComputableInPolyTime (fun p : Node Q T × (ℕ × Y) => (if p.2.1 < 2 ^ p.1.2.1.2.numEdges then - some ((1, (p.1.2.1, (lowBits p.1.2.1.2.numEdges p.2.1, 0))) : Node T) else none)) - (encPair (encNode encT) encAcc) (encOption (encNode encT)) := + some ((1, (p.1.2.1, (lowBits p.1.2.1.2.numEdges p.2.1, 0))) : Node Q T) else none)) + (encPair (encNode encQ encT) (encAcc encY)) (encOption (encNode encQ encT)) := (ComputableInPolyTime.ite (ComputableInPolyTime.binary_lt' hc (twoPow hm)) - ((ComputableInPolyTime.optionSome (encNode encT)).comp + ((ComputableInPolyTime.optionSome (encNode encQ encT)).comp ((ComputableInPolyTime.const (encOut := encUnary) 1).prod (hq.prod ((BooleanFold.decoder_polyTime.comp (hm.prod hc)).prod (ComputableInPolyTime.const (encOut := encUnary) 0))))) (ComputableInPolyTime.const none)).of_enc_eq fun p => by by_cases h : p.2.1 < 2 ^ p.1.2.1.2.numEdges <;> simp [h] - have hentry : ComputableInPolyTime (fun p : Node T × (ℕ × ℕ) => + have hentry : ComputableInPolyTime (fun p : Node Q T × (ℕ × Y) => (if p.2.1 < p.1.2.1.2.vertices.length then - some ((2, (p.1.2.1, (p.1.2.2.1, min p.1.2.1.2.vertices.length p.2.1))) : Node T) + some ((2, (p.1.2.1, (p.1.2.2.1, min p.1.2.1.2.vertices.length p.2.1))) : Node Q T) else none)) - (encPair (encNode encT) encAcc) (encOption (encNode encT)) := + (encPair (encNode encQ encT) (encAcc encY)) (encOption (encNode encQ encT)) := (ComputableInPolyTime.ite (ComputableInPolyTime.binary_lt' hc (ComputableInPolyTime.unary_to_binary.comp hlen)) - ((ComputableInPolyTime.optionSome (encNode encT)).comp + ((ComputableInPolyTime.optionSome (encNode encQ encT)).comp ((ComputableInPolyTime.const (encOut := encUnary) 2).prod (hq.prod ((word_polyTime.comp hx).prod (ComputableInPolyTime.binary_min_unary' hlen hc))))) @@ -475,32 +658,32 @@ theorem sumScheme_polyTime : (sumScheme eval).PolyTime (encNode encT) encAcc enc · simp [sumScheme, h0] · by_cases h1 : p.1.1 = 1 <;> simp [sumScheme, h0, h1] absorb := by - have hx := (ComputableInPolyTime.fst (encNode encT) encAcc).comp - (ComputableInPolyTime.fst (encPair (encNode encT) encAcc) encNat) - have ha := (ComputableInPolyTime.snd (encNode encT) encAcc).comp - (ComputableInPolyTime.fst (encPair (encNode encT) encAcc) encNat) - have hy := ComputableInPolyTime.snd (encPair (encNode encT) encAcc) encNat - have hc := (ComputableInPolyTime.fst encNat encNat).comp ha - have hr := (ComputableInPolyTime.snd encNat encNat).comp ha + have hx := (ComputableInPolyTime.fst (encNode encQ encT) (encAcc encY)).comp + (ComputableInPolyTime.fst (encPair (encNode encQ encT) (encAcc encY)) encY) + have ha := (ComputableInPolyTime.snd (encNode encQ encT) (encAcc encY)).comp + (ComputableInPolyTime.fst (encPair (encNode encQ encT) (encAcc encY)) encY) + have hy := ComputableInPolyTime.snd (encPair (encNode encQ encT) (encAcc encY)) encY + have hc := (ComputableInPolyTime.fst encNat encY).comp ha + have hr := (ComputableInPolyTime.snd encNat encY).comp ha have hP := precision_polyTime.comp hx refine (ComputableInPolyTime.binary_add.comp (hc.prod (ComputableInPolyTime.const 1))).prod ((ComputableInPolyTime.ite (ComputableInPolyTime.beq (kind_polyTime.comp hx) (ComputableInPolyTime.const 0)) - (modTwoPow (ComputableInPolyTime.binary_add.comp (hr.prod hy)) hP) - (modTwoPow (ComputableInPolyTime.binary_mul' hr hy) hP)).of_enc_eq fun q => ?_) + (hops.add.comp (hP.prod (hr.prod hy))) + (hops.mul.comp (hP.prod (hr.prod hy)))).of_enc_eq fun q => ?_) by_cases h : q.1.1.1 = 0 <;> simp [h] - finish := (ComputableInPolyTime.snd encNat encNat).comp (ComputableInPolyTime.snd _ _) + finish := (ComputableInPolyTime.snd encNat encY).comp (ComputableInPolyTime.snd _ _) /-- The descriptor of a vertex and the bits of the node's word on its ports. -/ theorem query_polyTime : - ComputableInPolyTime (fun p : Node T × (List ℕ × T) => (p.2.2, wordBits p.1.2.2.1 p.2.1)) - (encPair (encNode encT) (encPair (encList encNat) encT)) (encPair encT encBits) := by - have hv := ComputableInPolyTime.snd (encNode encT) (encPair (encList encNat) encT) + ComputableInPolyTime (fun p : Node Q T × (List ℕ × T) => (p.2.2, wordBits p.1.2.2.1 p.2.1)) + (encPair (encNode encQ encT) (encPair (encList encNat) encT)) (encPair encT encBits) := by + have hv := ComputableInPolyTime.snd (encNode encQ encT) (encPair (encList encNat) encT) have hports := (ComputableInPolyTime.fst (encList encNat) encT).comp hv - have hword := word_polyTime.comp (ComputableInPolyTime.fst (encNode encT) + have hword := word_polyTime.comp (ComputableInPolyTime.fst (encNode encQ encT) (encPair (encList encNat) encT)) - let E := encPair (encNode encT) (encPair (encList encNat) encT) - have hbit : ComputableInPolyTime (fun z : (Node T × (List ℕ × T)) × ℕ => + let E := encPair (encNode encQ encT) (encPair (encList encNat) encT) + have hbit : ComputableInPolyTime (fun z : (Node Q T × (List ℕ × T)) × ℕ => z.1.1.2.2.1.getD z.2 false) (encPair E encNat) encBool := ComputableInPolyTime.listGetD_binary (encA := encBool) (encL := encBits) false (hword.comp (ComputableInPolyTime.fst E encNat)) (ComputableInPolyTime.snd E encNat) @@ -510,55 +693,61 @@ theorem query_polyTime : /-- The vertex of a node, if its index is in range. -/ noncomputable def vertexCertificate : - NativeCertificate (fun x : Node T => x.2.1.2.vertices[x.2.2.2]?) (encNode encT) + NativeCertificate (fun x : Node Q T => x.2.1.2.vertices[x.2.2.2]?) (encNode encQ encT) (encOption (encPair (encList encNat) encT)) := NativeCertificate.ofPolyTime (ComputableInPolyTime.getElem? (vertices_polyTime.comp network_polyTime) index_polyTime) /-- The query to the evaluator: the descriptor and the bits on the ports. -/ noncomputable def queryCertificate : - NativeCertificate (fun p : Node T × (List ℕ × T) => (p.2.2, wordBits p.1.2.2.1 p.2.1)) - (encPair (encNode encT) (encPair (encList encNat) encT)) (encPair encT encBits) := + NativeCertificate (fun p : Node Q T × (List ℕ × T) => (p.2.2, wordBits p.1.2.2.1 p.2.1)) + (encPair (encNode encQ encT) (encPair (encList encNat) encT)) (encPair encT encBits) := NativeCertificate.ofPolyTime query_polyTime +/-- The zero at the precision of a node. -/ +noncomputable def zeroCertificate (hops : ops.PolyTime encQ encY) : + NativeCertificate (fun x : Node Q T => ops.zero x.2.1.1) (encNode encQ encT) encY := + NativeCertificate.ofPolyTime (hops.zero.comp precision_polyTime) + variable (eval) in /-- **The leaf step**: the evaluator at the descriptor of the node's vertex and its bits, from a native certificate of the evaluator with any bounds. -/ -noncomputable def leafCertificate - (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) - encNat) : - NativeCertificate (sumScheme eval).leaf (encNode encT) encNat := - NativeCertificate.option (value := fun x : Node T => x.2.1.2.vertices[x.2.2.2]?) - (fallback := fun _ => 0) (body := fun p => eval p.2.2 (wordBits p.1.2.2.1 p.2.1)) - vertexCertificate (NativeCertificate.const 0) (ev.comp queryCertificate) - -/-- The time of the leaf step: the vertex lookup, and the query and the evaluator at a vertex. -/ -theorem leafCertificate_time - (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) - encNat) (x : Node T) : - (leafCertificate eval ev).time x = 2 * (vertexCertificate (encT := encT)).time x + - (encNode encT x).length + - (x.2.1.2.vertices[x.2.2.2]?).elim (encNode encT x).length - fun v => (queryCertificate (encT := encT)).time (x, v) + +noncomputable def leafCertificate (hops : ops.PolyTime encQ encY) + (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) encY) : + NativeCertificate (sumScheme ops eval).leaf (encNode encQ encT) encY := + NativeCertificate.option (value := fun x : Node Q T => x.2.1.2.vertices[x.2.2.2]?) + (fallback := fun x => ops.zero x.2.1.1) (body := fun p => eval p.2.2 (wordBits p.1.2.2.1 p.2.1)) + vertexCertificate (zeroCertificate hops) (ev.comp queryCertificate) + +/-- The time of the leaf step: the vertex lookup, and the zero or the query and the evaluator at +a vertex. -/ +theorem leafCertificate_time (hops : ops.PolyTime encQ encY) + (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) encY) + (x : Node Q T) : + (leafCertificate eval hops ev).time x = + 2 * (vertexCertificate (encQ := encQ) (encT := encT)).time x + (encNode encQ encT x).length + + (x.2.1.2.vertices[x.2.2.2]?).elim ((zeroCertificate (encT := encT) hops).time x) + fun v => (queryCertificate (encQ := encQ) (encT := encT)).time (x, v) + ev.time (v.2, wordBits x.2.2.1 v.1) := rfl /-- The space of the leaf step. -/ -theorem leafCertificate_space - (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) - encNat) (x : Node T) : - (leafCertificate eval ev).space x = 2 * (vertexCertificate (encT := encT)).space x + - (encNode encT x).length + - (x.2.1.2.vertices[x.2.2.2]?).elim (encNode encT x).length - fun v => (queryCertificate (encT := encT)).space (x, v) + +theorem leafCertificate_space (hops : ops.PolyTime encQ encY) + (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) encY) + (x : Node Q T) : + (leafCertificate eval hops ev).space x = + 2 * (vertexCertificate (encQ := encQ) (encT := encT)).space x + (encNode encQ encT x).length + + (x.2.1.2.vertices[x.2.2.2]?).elim ((zeroCertificate (encT := encT) hops).space x) + fun v => (queryCertificate (encQ := encQ) (encT := encT)).space (x, v) + ev.space (v.2, wordBits x.2.2.1 v.1) := rfl /-- The number of steps of direct summation: `2 (2 ^ m (k + 1) + 1) - 1`. -/ -def fuel (q : ℕ × DescribedNetwork T) : ℕ := +def fuel (q : Q × DescribedNetwork T) : ℕ := 2 * (2 ^ q.2.numEdges * (q.2.vertices.length + 1)) + 1 /-- The fuel in polynomial time. -/ -theorem fuel_polyTime : ComputableInPolyTime (fuel (T := T)) (encInput encT) encNat := by - have hd := ComputableInPolyTime.snd encUnary (encDescribed encT) +theorem fuel_polyTime : ComputableInPolyTime (fuel (Q := Q) (T := T)) (encInput encQ encT) + encNat := by + have hd := ComputableInPolyTime.snd encQ (encDescribed encT) have hm := numEdges_polyTime.comp hd have hlen := (ComputableInPolyTime.length_list _).comp (vertices_polyTime.comp hd) exact ComputableInPolyTime.binary_add.comp ((ComputableInPolyTime.binary_mul' @@ -567,51 +756,49 @@ theorem fuel_polyTime : ComputableInPolyTime (fuel (T := T)) (encInput encT) enc (ComputableInPolyTime.const 1))))).prod (ComputableInPolyTime.const 1)) /-- The root in polynomial time. -/ -theorem sumRoot_polyTime : ComputableInPolyTime (sumRoot (T := T)) (encInput encT) - (encNode encT) := +theorem sumRoot_polyTime : ComputableInPolyTime (sumRoot (Q := Q) (T := T)) (encInput encQ encT) + (encNode encQ encT) := (ComputableInPolyTime.const 0).prod (ComputableInPolyTime.id'.prod (ComputableInPolyTime.const ([], 0))) variable (eval) in -/-- **One native machine for direct summation over described networks**, from a native -certificate of the evaluator. It returns `some (d.contraction eval % 2 ^ P)` +/-- **One native machine for direct summation over described networks**, from polynomial-time +operations and a native certificate of the evaluator. It returns `some (sumValue ops eval q d)` (`run_sumScheme`). -/ -noncomputable def sumCertificate - (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) - encNat) : - NativeCertificate ((sumScheme eval).run fuel sumRoot) (encInput encT) (encOption encNat) := - Scheme.certificate sumScheme_polyTime (leafCertificate eval ev) +noncomputable def sumCertificate (hops : ops.PolyTime encQ encY) + (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) encY) : + NativeCertificate ((sumScheme ops eval).run fuel sumRoot) (encInput encQ encT) + (encOption encY) := + Scheme.certificate (sumScheme_polyTime hops) (leafCertificate eval hops ev) (NativeCertificate.ofPolyTime fuel_polyTime) (NativeCertificate.ofPolyTime sumRoot_polyTime) -/-- **The machine computes the contraction modulo `2 ^ P`.** -/ -theorem run_sumScheme (q : ℕ × DescribedNetwork T) : - (sumScheme eval).run fuel sumRoot q = some (q.2.contraction eval % 2 ^ q.1) := by - obtain ⟨h, -, he⟩ := eval_sumRoot (eval := eval) q.1 q.2 +/-- **The machine computes `sumValue`.** -/ +theorem run_sumScheme (q : Q × DescribedNetwork T) : + (sumScheme ops eval).run fuel sumRoot q = some (sumValue ops eval q.1 q.2) := by + obtain ⟨h, -, he⟩ := eval_sumRoot (ops := ops) (eval := eval) q.1 q.2 exact Scheme.run_eq_some he (by simp [fuel]; omega) variable (eval) in -/-- **Direct summation over described networks, as a native certificate** for the contraction -modulo `2 ^ P` on every input `(P, d)`, from a native certificate of the evaluator. -/ -noncomputable def contractionCertificate - (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) - encNat) : - NativeCertificate (fun q : ℕ × DescribedNetwork T => q.2.contraction eval % 2 ^ q.1) - (encInput encT) encNat := - (NativeCertificate.post (sumCertificate eval ev) - (ComputableInDegree.getD (enc₁ := encNat) (enc := encOption encNat) 0).inO_one).congr +/-- **Direct summation over described networks, as a native certificate** for `sumValue` on +every input `(q, d)`, from polynomial-time operations and a native certificate of the +evaluator. -/ +noncomputable def contractionCertificate [Inhabited Y] (hops : ops.PolyTime encQ encY) + (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) encY) : + NativeCertificate (fun q : Q × DescribedNetwork T => sumValue ops eval q.1 q.2) + (encInput encQ encT) encY := + (NativeCertificate.post (sumCertificate eval hops ev) + (ComputableInDegree.getD (enc₁ := encY) (enc := encOption encY) default).inO_one).congr fun q => by simp [run_sumScheme] /-- The time is that of the stack machine. -/ -@[simp] theorem contractionCertificate_time - (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) - encNat) : - (contractionCertificate eval ev).time = (sumCertificate eval ev).time := rfl +@[simp] theorem contractionCertificate_time [Inhabited Y] (hops : ops.PolyTime encQ encY) + (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) encY) : + (contractionCertificate eval hops ev).time = (sumCertificate eval hops ev).time := rfl /-- The space is that of the stack machine. -/ -@[simp] theorem contractionCertificate_space - (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) - encNat) : - (contractionCertificate eval ev).space = (sumCertificate eval ev).space := rfl +@[simp] theorem contractionCertificate_space [Inhabited Y] (hops : ops.PolyTime encQ encY) + (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) encY) : + (contractionCertificate eval hops ev).space = (sumCertificate eval hops ev).space := rfl /-! ### Resources -/ @@ -625,8 +812,8 @@ theorem length_numEdges_vertices_le (d : DescribedNetwork T) : omega /-- A node of the right shape is at most `5 n + 7` long, for `n` the length of the input. -/ -theorem length_encNode_le {q : ℕ × DescribedNetwork T} {x : Node T} (hx : NodeShape q x) : - (encNode encT x).length ≤ 5 * (encInput encT q).length + 7 := by +theorem length_encNode_le {q : Q × DescribedNetwork T} {x : Node Q T} (hx : NodeShape q x) : + (encNode encQ encT x).length ≤ 5 * (encInput encQ encT q).length + 7 := by obtain ⟨hq, hk, hw, hv⟩ := hx obtain ⟨k, ⟨P, d⟩, w, v⟩ := x simp only at hq hk hw hv @@ -650,51 +837,59 @@ private theorem length_encNat_lt {y P : ℕ} (h : y < 2 ^ P) : (encNat y).length exact h' /-- **The resources of direct summation over described networks.** There is `K` such that, for -an input `(P, d)` of length `n` with `m` edges and `k` vertices, if the evaluator takes time at +an input `(q, d)` of length `n` with `m` edges and `k` vertices, if the evaluator takes time at most `E` and space at most `ES` and outputs at most `Ev` bits on every vertex and every list of -bits on its ports, then the time is at most `2 ^ m (k + 1) + 1` times -`K (n + Ev + 1) ^ K + 2 E`, and the space at most `K (n + Ev + 1) ^ K + ES`. -/ -theorem contractionCertificate_le - (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) - encNat) : - ∃ K : ℕ, ∀ (q : ℕ × DescribedNetwork T) (E ES Ev : ℕ), +bits on its ports, and the reduced values at `q` are at most `Rv` bits, then the time is at most +`2 ^ m (k + 1) + 1` times `K (n + Ev + Rv + 1) ^ K + 2 E`, and the space at most +`K (n + Ev + Rv + 1) ^ K + ES`. -/ +theorem contractionCertificate_le [Inhabited Y] (hops : ops.PolyTime encQ encY) + (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) encY) : + ∃ K : ℕ, ∀ (q : Q × DescribedNetwork T) (E ES Ev Rv : ℕ), (∀ v ∈ q.2.vertices, ∀ bs : List Bool, bs.length = v.1.length → - ev.time (v.2, bs) ≤ E ∧ ev.space (v.2, bs) ≤ ES ∧ (encNat (eval v.2 bs)).length ≤ Ev) → - (contractionCertificate eval ev).time q ≤ + ev.time (v.2, bs) ≤ E ∧ ev.space (v.2, bs) ≤ ES ∧ (encY (eval v.2 bs)).length ≤ Ev) → + (∀ y, ops.Reduced q.1 y → (encY y).length ≤ Rv) → + (contractionCertificate eval hops ev).time q ≤ (2 ^ q.2.numEdges * (q.2.vertices.length + 1) + 1) * - (K * ((encInput encT q).length + Ev + 1) ^ K + 2 * E) ∧ - (contractionCertificate eval ev).space q ≤ - K * ((encInput encT q).length + Ev + 1) ^ K + ES := by - obtain ⟨K₀, hK₀⟩ := Scheme.certificate_le (sumScheme_polyTime (eval := eval) (encT := encT)) - (leafCertificate eval ev) - have hV : (vertexCertificate (encT := encT)).IsPolynomial := + (K * ((encInput encQ encT q).length + Ev + Rv + 1) ^ K + 2 * E) ∧ + (contractionCertificate eval hops ev).space q ≤ + K * ((encInput encQ encT q).length + Ev + Rv + 1) ^ K + ES := by + obtain ⟨K₀, hK₀⟩ := Scheme.certificate_le + (sumScheme_polyTime (ops := ops) (eval := eval) (encT := encT) hops) + (leafCertificate eval hops ev) + have hV : (vertexCertificate (encQ := encQ) (encT := encT)).IsPolynomial := + NativeCertificate.ofPolyTime_isPolynomial _ + have hQ : (queryCertificate (encQ := encQ) (encT := encT)).IsPolynomial := NativeCertificate.ofPolyTime_isPolynomial _ - have hQ : (queryCertificate (encT := encT)).IsPolynomial := + have hZ : (zeroCertificate (encT := encT) hops).IsPolynomial := NativeCertificate.ofPolyTime_isPolynomial _ - have hF := NativeCertificate.ofPolyTime_isPolynomial (fuel_polyTime (encT := encT)) - have hR := NativeCertificate.ofPolyTime_isPolynomial (sumRoot_polyTime (encT := encT)) + have hF := NativeCertificate.ofPolyTime_isPolynomial (fuel_polyTime (encQ := encQ) (encT := encT)) + have hR := NativeCertificate.ofPolyTime_isPolynomial + (sumRoot_polyTime (encQ := encQ) (encT := encT)) obtain ⟨cv, dv, hvt⟩ := hV.1.exists_le obtain ⟨cv', dv', hvs⟩ := hV.2.exists_le obtain ⟨cq, dq, hqt⟩ := hQ.1.exists_le obtain ⟨cq', dq', hqs⟩ := hQ.2.exists_le + obtain ⟨cz, dz, hzt⟩ := hZ.1.exists_le + obtain ⟨cz', dz', hzs⟩ := hZ.2.exists_le obtain ⟨cf, df, hft⟩ := hF.1.exists_le obtain ⟨cf', df', hfs⟩ := hF.2.exists_le obtain ⟨cr, dr, hrt⟩ := hR.1.exists_le obtain ⟨cr', dr', hrs⟩ := hR.2.exists_le - obtain ⟨D, hD⟩ : ∃ D, D = K₀ + dv + dv' + dq + dq' + df + df' + dr + dr' + 2 := ⟨_, rfl⟩ - obtain ⟨G, hG⟩ : ∃ G, G = K₀ * 96 ^ K₀ + (2 * cv * 13 ^ dv + 24 + cq * 27 ^ dq) + 16 := - ⟨_, rfl⟩ - obtain ⟨G', hG'⟩ : ∃ G', G' = K₀ * 96 ^ K₀ + (2 * cv' * 13 ^ dv' + 24 + cq' * 27 ^ dq') := + obtain ⟨D, hD⟩ : ∃ D, D = K₀ + dv + dv' + dq + dq' + dz + dz' + df + df' + dr + dr' + 2 := ⟨_, rfl⟩ + obtain ⟨G, hG⟩ : ∃ G, G = K₀ * 96 ^ K₀ + + (2 * cv * 13 ^ dv + 12 + cz * 13 ^ dz + cq * 27 ^ dq) + 16 := ⟨_, rfl⟩ + obtain ⟨G', hG'⟩ : ∃ G', G' = K₀ * 96 ^ K₀ + + (2 * cv' * 13 ^ dv' + 12 + cz' * 13 ^ dz' + cq' * 27 ^ dq') := ⟨_, rfl⟩ obtain ⟨C, hC⟩ : ∃ C, C = cf + cr + cf' + cr' + 2 * G + G' + 4 := ⟨_, rfl⟩ - refine ⟨C + D, fun q E ES Ev hE => ?_⟩ + refine ⟨C + D, fun q E ES Ev Rv hE hRv => ?_⟩ obtain ⟨P, d⟩ := q - obtain ⟨n, hn⟩ : ∃ n, n = (encInput encT (P, d)).length := ⟨_, rfl⟩ - obtain ⟨z, hz⟩ : ∃ z, z = n + Ev := ⟨_, rfl⟩ + obtain ⟨n, hn⟩ : ∃ n, n = (encInput encQ encT (P, d)).length := ⟨_, rfl⟩ + obtain ⟨z, hz⟩ : ∃ z, z = n + Ev + Rv := ⟨_, rfl⟩ obtain ⟨W, hW⟩ : ∃ W, W = (z + 1) ^ D := ⟨_, rfl⟩ rw [← hn] - rw [show n + Ev + 1 = z + 1 by omega] - have hnP : 2 * P + 1 + (encDescribed encT d).length = n := by + rw [show n + Ev + Rv + 1 = z + 1 by omega] + have hnP : 2 * (encQ P).length + 1 + (encDescribed encT d).length = n := by rw [hn]; simp [encInput] have hmk := length_numEdges_vertices_le (encT := encT) d -- Every vertex is at most `n` long. @@ -709,63 +904,79 @@ theorem contractionCertificate_le (d.numEdges, d.vertices)).length simp only [length_encPair]; omega omega - have hnode (x : Node T) (hx : (sumInvariant eval (P, d)).node x) : - (encNode encT x).length ≤ 5 * z + 7 := (length_encNode_le hx).trans (by omega) - have hleafT (x : Node T) (hx : (sumInvariant eval (P, d)).node x) : - (leafCertificate eval ev).time x ≤ - 2 * cv * (5 * z + 8) ^ dv + 2 * (5 * z + 7) + cq * (11 * z + 16) ^ dq + E := by + have hnode (x : Node Q T) (hx : (sumInvariant ops eval (P, d)).node x) : + (encNode encQ encT x).length ≤ 5 * z + 7 := (length_encNode_le hx).trans (by omega) + have hleafT (x : Node Q T) (hx : (sumInvariant ops eval (P, d)).node x) : + (leafCertificate eval hops ev).time x ≤ 2 * cv * (5 * z + 8) ^ dv + (5 * z + 7) + + cz * (5 * z + 8) ^ dz + cq * (11 * z + 16) ^ dq + E := by have h1 := hnode x hx rw [leafCertificate_time] - have h2 : (vertexCertificate (encT := encT)).time x ≤ cv * (5 * z + 8) ^ dv := + have h2 : (vertexCertificate (encQ := encQ) (encT := encT)).time x ≤ + cv * (5 * z + 8) ^ dv := (hvt x).trans (Nat.mul_le_mul_left _ (Nat.pow_le_pow_left (by omega) _)) - have h2' : 2 * (vertexCertificate (encT := encT)).time x ≤ 2 * cv * (5 * z + 8) ^ dv := by + have h2' : 2 * (vertexCertificate (encQ := encQ) (encT := encT)).time x ≤ + 2 * cv * (5 * z + 8) ^ dv := by rw [Nat.mul_assoc]; exact Nat.mul_le_mul_left 2 h2 cases hvx : x.2.1.2.vertices[x.2.2.2]? with - | none => simp only [Option.elim]; omega + | none => + simp only [Option.elim] + have h3 : (zeroCertificate (encT := encT) hops).time x ≤ cz * (5 * z + 8) ^ dz := + (hzt x).trans (Nat.mul_le_mul_left _ (Nat.pow_le_pow_left (by omega) _)) + omega | some v => simp only [Option.elim] have hmem : v ∈ x.2.1.2.vertices := List.mem_of_getElem? hvx obtain ⟨hq, -, -, -⟩ := hx rw [hq] at hmem have h3 := hvlen v hmem - have h4 : (queryCertificate (encT := encT)).time (x, v) ≤ cq * (11 * z + 16) ^ dq := by + have h4 : (queryCertificate (encQ := encQ) (encT := encT)).time (x, v) ≤ + cq * (11 * z + 16) ^ dq := by refine (hqt (x, v)).trans (Nat.mul_le_mul_left _ (Nat.pow_le_pow_left ?_ _)) - have := length_encPair (enc₁ := encNode encT) (enc₂ := encPair (encList encNat) encT) - (x, v) + have := length_encPair (enc₁ := encNode encQ encT) + (enc₂ := encPair (encList encNat) encT) (x, v) dsimp only at this omega have h5 := (hE v hmem (wordBits x.2.2.1 v.1) (by simp [wordBits])).1 omega - have hleafS (x : Node T) (hx : (sumInvariant eval (P, d)).node x) : - (leafCertificate eval ev).space x ≤ - 2 * cv' * (5 * z + 8) ^ dv' + 2 * (5 * z + 7) + cq' * (11 * z + 16) ^ dq' + ES := by + have hleafS (x : Node Q T) (hx : (sumInvariant ops eval (P, d)).node x) : + (leafCertificate eval hops ev).space x ≤ 2 * cv' * (5 * z + 8) ^ dv' + (5 * z + 7) + + cz' * (5 * z + 8) ^ dz' + cq' * (11 * z + 16) ^ dq' + ES := by have h1 := hnode x hx rw [leafCertificate_space] - have h2 : (vertexCertificate (encT := encT)).space x ≤ cv' * (5 * z + 8) ^ dv' := + have h2 : (vertexCertificate (encQ := encQ) (encT := encT)).space x ≤ + cv' * (5 * z + 8) ^ dv' := (hvs x).trans (Nat.mul_le_mul_left _ (Nat.pow_le_pow_left (by omega) _)) - have h2' : 2 * (vertexCertificate (encT := encT)).space x ≤ 2 * cv' * (5 * z + 8) ^ dv' := by + have h2' : 2 * (vertexCertificate (encQ := encQ) (encT := encT)).space x ≤ + 2 * cv' * (5 * z + 8) ^ dv' := by rw [Nat.mul_assoc]; exact Nat.mul_le_mul_left 2 h2 cases hvx : x.2.1.2.vertices[x.2.2.2]? with - | none => simp only [Option.elim]; omega + | none => + simp only [Option.elim] + have h3 : (zeroCertificate (encT := encT) hops).space x ≤ cz' * (5 * z + 8) ^ dz' := + (hzs x).trans (Nat.mul_le_mul_left _ (Nat.pow_le_pow_left (by omega) _)) + omega | some v => simp only [Option.elim] have hmem : v ∈ x.2.1.2.vertices := List.mem_of_getElem? hvx obtain ⟨hq, -, -, -⟩ := hx rw [hq] at hmem have h3 := hvlen v hmem - have h4 : (queryCertificate (encT := encT)).space (x, v) ≤ cq' * (11 * z + 16) ^ dq' := by + have h4 : (queryCertificate (encQ := encQ) (encT := encT)).space (x, v) ≤ + cq' * (11 * z + 16) ^ dq' := by refine (hqs (x, v)).trans (Nat.mul_le_mul_left _ (Nat.pow_le_pow_left ?_ _)) - have := length_encPair (enc₁ := encNode encT) (enc₂ := encPair (encList encNat) encT) - (x, v) + have := length_encPair (enc₁ := encNode encQ encT) + (enc₂ := encPair (encList encNat) encT) (x, v) dsimp only at this omega have h5 := (hE v hmem (wordBits x.2.2.1 v.1) (by simp [wordBits])).2.1 omega - obtain ⟨h, hh, he⟩ := eval_sumRoot (eval := eval) P d + obtain ⟨h, hh, he⟩ := eval_sumRoot (ops := ops) (eval := eval) P d have hres := hK₀ (NativeCertificate.ofPolyTime fuel_polyTime) - (NativeCertificate.ofPolyTime sumRoot_polyTime) (sumInvariant eval (P, d)) (13 * z + 18) - (2 * cv * (5 * z + 8) ^ dv + 2 * (5 * z + 7) + cq * (11 * z + 16) ^ dq + E) - (2 * cv' * (5 * z + 8) ^ dv' + 2 * (5 * z + 7) + cq' * (11 * z + 16) ^ dq' + ES) + (NativeCertificate.ofPolyTime sumRoot_polyTime) (sumInvariant ops eval (P, d)) (13 * z + 18) + (2 * cv * (5 * z + 8) ^ dv + (5 * z + 7) + cz * (5 * z + 8) ^ dz + + cq * (11 * z + 16) ^ dq + E) + (2 * cv' * (5 * z + 8) ^ dv' + (5 * z + 7) + cz' * (5 * z + 8) ^ dz' + + cq' * (11 * z + 16) ^ dq' + ES) (fun x hx => ⟨(hnode x hx).trans (by omega), fun _ => ⟨hleafT x hx, hleafS x hx⟩⟩) (fun x a ha => by obtain ⟨hx, ha1, ha2⟩ := ha @@ -779,15 +990,15 @@ theorem contractionCertificate_le rw [pow_succ, pow_add, Nat.mul_two] exact Nat.add_le_add (Nat.le_mul_of_pos_right _ (Nat.two_pow_pos _)) (Nat.le_mul_of_pos_left _ (Nat.two_pow_pos _)) - have h3 := length_encNat_lt ha2 - have h4 : (encPair (encNode encT) encAcc (x, a)).length = - 2 * (encNode encT x).length + 1 + (encAcc a).length := length_encPair (x, a) - have h5 : (encAcc a).length = 2 * (encNat a.1).length + 1 + (encNat a.2).length := + have h3 := hRv a.2 ha2 + have h4 : (encPair (encNode encQ encT) (encAcc encY) (x, a)).length = + 2 * (encNode encQ encT x).length + 1 + (encAcc encY a).length := length_encPair (x, a) + have h5 : (encAcc encY a).length = 2 * (encNat a.1).length + 1 + (encY a.2).length := length_encPair a omega) (fun y hy => by rcases hy with hy | ⟨v, hv, w, -, rfl⟩ - · have := length_encNat_lt hy; omega + · have := hRv y hy; omega · have := (hE v hv (wordBits w v.1) (by simp [wordBits])).2.2; omega) (P, d) _ _ _ he rw [← hn] at hres @@ -821,19 +1032,25 @@ theorem contractionCertificate_le have h3 : (h + 1) * (13 * z + 18 + 1) ≤ 39 * z + 57 := by have := Nat.mul_le_mul_right (13 * z + 18 + 1) (show h + 1 ≤ 3 by omega); omega exact (Nat.mul_le_mul_left _ (Nat.pow_le_pow_left h3 _)).trans (hterm (by omega)) - have hTL : 2 * cv * (5 * z + 8) ^ dv + 2 * (5 * z + 7) + cq * (11 * z + 16) ^ dq ≤ - (2 * cv * 13 ^ dv + 24 + cq * 27 ^ dq) * W := by + have hTL : 2 * cv * (5 * z + 8) ^ dv + (5 * z + 7) + cz * (5 * z + 8) ^ dz + + cq * (11 * z + 16) ^ dq ≤ (2 * cv * 13 ^ dv + 12 + cz * 13 ^ dz + cq * 27 ^ dq) * W := by have a1 := hterm (c := 2 * cv) (a := 5) (b := 8) (show dv ≤ D by omega) - have a2 := hterm (c := cq) (a := 11) (b := 16) (show dq ≤ D by omega) - have e : (2 * cv * 13 ^ dv + 24 + cq * 27 ^ dq) * W = - 2 * cv * (5 + 8) ^ dv * W + 24 * W + cq * (11 + 16) ^ dq * W := by ring + have a2 := hterm (c := cz) (a := 5) (b := 8) (show dz ≤ D by omega) + have a3 := hterm (c := cq) (a := 11) (b := 16) (show dq ≤ D by omega) + have e : (2 * cv * 13 ^ dv + 12 + cz * 13 ^ dz + cq * 27 ^ dq) * W = + 2 * cv * (5 + 8) ^ dv * W + 12 * W + cz * (5 + 8) ^ dz * W + cq * (11 + 16) ^ dq * W := by + ring omega - have hSL : 2 * cv' * (5 * z + 8) ^ dv' + 2 * (5 * z + 7) + cq' * (11 * z + 16) ^ dq' ≤ - (2 * cv' * 13 ^ dv' + 24 + cq' * 27 ^ dq') * W := by + have hSL : 2 * cv' * (5 * z + 8) ^ dv' + (5 * z + 7) + cz' * (5 * z + 8) ^ dz' + + cq' * (11 * z + 16) ^ dq' ≤ + (2 * cv' * 13 ^ dv' + 12 + cz' * 13 ^ dz' + cq' * 27 ^ dq') * W := by have a1 := hterm (c := 2 * cv') (a := 5) (b := 8) (show dv' ≤ D by omega) - have a2 := hterm (c := cq') (a := 11) (b := 16) (show dq' ≤ D by omega) - have e : (2 * cv' * 13 ^ dv' + 24 + cq' * 27 ^ dq') * W = - 2 * cv' * (5 + 8) ^ dv' * W + 24 * W + cq' * (11 + 16) ^ dq' * W := by ring + have a2 := hterm (c := cz') (a := 5) (b := 8) (show dz' ≤ D by omega) + have a3 := hterm (c := cq') (a := 11) (b := 16) (show dq' ≤ D by omega) + have e : (2 * cv' * 13 ^ dv' + 12 + cz' * 13 ^ dz' + cq' * 27 ^ dq') * W = + 2 * cv' * (5 + 8) ^ dv' * W + 12 * W + cz' * (5 + 8) ^ dz' * W + + cq' * (11 + 16) ^ dq' * W := by + ring omega have hclock : ((encNat (fuel (P, d))).length + 1) ^ 2 ≤ 16 * W := by have h3 : (encNat (fuel (P, d))).length + 1 ≤ 4 * (z + 1) := by omega @@ -856,15 +1073,17 @@ theorem contractionCertificate_le constructor · rw [hfuel1] at htime obtain ⟨S, hS⟩ : ∃ S, S = K₀ * ((h + 1) * (13 * z + 18 + 1)) ^ K₀ + - (2 * cv * (5 * z + 8) ^ dv + 2 * (5 * z + 7) + cq * (11 * z + 16) ^ dq + E) + + (2 * cv * (5 * z + 8) ^ dv + (5 * z + 7) + cz * (5 * z + 8) ^ dz + + cq * (11 * z + 16) ^ dq + E) + ((encNat (fuel (P, d))).length + 1) ^ 2 := ⟨_, rfl⟩ rw [← hS] at htime have hstep : S ≤ G * W + E := by - have e : G * W = K₀ * 96 ^ K₀ * W + (2 * cv * 13 ^ dv + 24 + cq * 27 ^ dq) * W + 16 * W := by + have e : G * W = K₀ * 96 ^ K₀ * W + + (2 * cv * 13 ^ dv + 12 + cz * 13 ^ dz + cq * 27 ^ dq) * W + 16 * W := by rw [hG]; ring omega have hm := Nat.mul_le_mul_left (2 * B) hstep - calc (contractionCertificate eval ev).time (P, d) + calc (contractionCertificate eval hops ev).time (P, d) ≤ cf * W + cr * W + 2 * W + 2 * B * (G * W + E) := by rw [contractionCertificate_time]; unfold sumCertificate; omega _ ≤ B * ((cf + cr + 2) * W) + B * (2 * G * W) + 2 * B * E := by @@ -878,17 +1097,70 @@ theorem contractionCertificate_le _ = B * (C * W + 2 * E) := by ring _ ≤ B * ((C + D) * (z + 1) ^ (C + D) + 2 * E) := Nat.mul_le_mul_left _ (Nat.add_le_add_right hCD _) - · have h1 : (contractionCertificate eval ev).space (P, d) ≤ C * W + ES := by + · have h1 : (contractionCertificate eval hops ev).space (P, d) ≤ C * W + ES := by rw [contractionCertificate_space] unfold sumCertificate have e : C * W = cf * W + cr * W + cf' * W + cr' * W + 2 * G * W + G' * W + 4 * W := by rw [hC]; ring - have e' : G' * W = K₀ * 96 ^ K₀ * W + (2 * cv' * 13 ^ dv' + 24 + cq' * 27 ^ dq') * W := by + have e' : G' * W = K₀ * 96 ^ K₀ * W + + (2 * cv' * 13 ^ dv' + 12 + cz' * 13 ^ dz' + cq' * 27 ^ dq') * W := by rw [hG']; ring have hfl' : (encNat (fuel (P, d))).length ≤ W := by omega omega exact h1.trans (Nat.add_le_add_right hCD _) +/-! ### The two instances -/ + +/-- Natural numbers modulo `2 ^ P` are polynomial-time, with `P` in unary. -/ +theorem _root_.Cslib.Circuits.Frontier.SumOps.mod_polyTime : + SumOps.mod.PolyTime encUnary encNat where + zero := ComputableInPolyTime.const 0 + one := GaloisRingArith.computableInPolyTime_mod_two_pow (ComputableInPolyTime.const 1) + ComputableInPolyTime.id' + add := GaloisRingArith.computableInPolyTime_mod_two_pow + (ComputableInPolyTime.binary_add.comp ((ComputableInPolyTime.fst encNat encNat).comp + (ComputableInPolyTime.snd _ _) |>.prod ((ComputableInPolyTime.snd encNat encNat).comp + (ComputableInPolyTime.snd _ _)))) (ComputableInPolyTime.fst _ _) + mul := GaloisRingArith.computableInPolyTime_mod_two_pow + (ComputableInPolyTime.binary_mul' ((ComputableInPolyTime.fst encNat encNat).comp + (ComputableInPolyTime.snd _ _)) ((ComputableInPolyTime.snd encNat encNat).comp + (ComputableInPolyTime.snd _ _))) (ComputableInPolyTime.fst _ _) + +/-- Coefficient lists are polynomial-time, with `p` in unary and the coefficients of `f` in +binary. -/ +theorem _root_.Cslib.Circuits.Frontier.SumOps.galois_polyTime : + SumOps.galois.PolyTime (encPair encUnary (encList encNat)) (encList encNat) where + zero := GaloisRingArith.computableInPolyTime_tabulate (g := fun _ _ => 0) + ((ComputableInPolyTime.length_list encNat).comp (ComputableInPolyTime.snd _ _)) + (ComputableInPolyTime.const 0) + one := GaloisRingArith.computableInPolyTime_one (ComputableInPolyTime.snd _ _) + add := by + have hq := ComputableInPolyTime.fst (encPair encUnary (encList encNat)) + (encPair (encList encNat) (encList encNat)) + have hcd := ComputableInPolyTime.snd (encPair encUnary (encList encNat)) + (encPair (encList encNat) (encList encNat)) + exact GaloisRingArith.computableInPolyTime_add ((ComputableInPolyTime.fst _ _).comp hq) + ((ComputableInPolyTime.snd _ _).comp hq) ((ComputableInPolyTime.fst _ _).comp hcd) + ((ComputableInPolyTime.snd _ _).comp hcd) + mul := by + have hq := ComputableInPolyTime.fst (encPair encUnary (encList encNat)) + (encPair (encList encNat) (encList encNat)) + have hcd := ComputableInPolyTime.snd (encPair encUnary (encList encNat)) + (encPair (encList encNat) (encList encNat)) + exact GaloisRingArith.computableInPolyTime_mul ((ComputableInPolyTime.fst _ _).comp hq) + ((ComputableInPolyTime.snd _ _).comp hq) ((ComputableInPolyTime.fst _ _).comp hcd) + ((ComputableInPolyTime.snd _ _).comp hcd) + +/-- **Direct summation modulo `2 ^ P`**: one native machine for `d.contraction eval % 2 ^ P` on +every input `(P, d)`, with `P` in unary, from a native certificate of a natural-number +evaluator. -/ +noncomputable def modContractionCertificate (eval : T → List Bool → ℕ) + (ev : NativeCertificate (fun q : T × List Bool => eval q.1 q.2) (encPair encT encBits) + encNat) : + NativeCertificate (fun q : ℕ × DescribedNetwork T => q.2.contraction eval % 2 ^ q.1) + (encInput encUnary encT) encNat := + (contractionCertificate eval SumOps.mod_polyTime ev).congr fun q => sumValue_mod eval q.1 q.2 + end Native end DescribedNetwork diff --git a/ComplexityTests/DescribedContraction.lean b/ComplexityTests/DescribedContraction.lean index 7581ad62a3..ac5647045b 100644 --- a/ComplexityTests/DescribedContraction.lean +++ b/ComplexityTests/DescribedContraction.lean @@ -12,8 +12,8 @@ import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListP /-! # Direct summation over described networks Tables as descriptors, with table lookup as the evaluator: the stack machine on a one-edge -network, the agreement with explicit networks, and one native machine for the contraction with -its resource theorem. -/ +network modulo `2 ^ P` and over coefficient lists of `(ℤ / 4)[U] / (U ^ 2 + U + 1)`, the agreement +with explicit networks, and one native machine for the contraction with its resource theorem. -/ namespace ComplexityTests.DescribedContraction @@ -26,20 +26,44 @@ def oneEdge : DescribedNetwork (List ℕ) := ⟨1, [([0], [2, 3]), ([0], [5, 7]) example : oneEdge.contraction ExplicitTensorNetwork.entry = 31 := by decide --- The stack machine, with fuel `2 (2 ^ 1 * 3) + 1 = 13`, returns `31 % 16 = 15`. -example : (sumScheme ExplicitTensorNetwork.entry).run fuel sumRoot (4, oneEdge) = some 15 := by +-- The stack machine modulo `2 ^ 4`, with fuel `2 (2 ^ 1 * 3) + 1 = 13`, returns `31 % 16 = 15`. +example : (sumScheme SumOps.mod ExplicitTensorNetwork.entry).run fuel sumRoot (4, oneEdge) = + some 15 := by decide -- With one step fewer, the sum still waits for the second assignment's weight -- `3 * 7 = 21 ≡ 5`. -example : (sumScheme ExplicitTensorNetwork.entry).run (fun _ => 12) sumRoot (4, oneEdge) = - some 5 := by +example : (sumScheme SumOps.mod ExplicitTensorNetwork.entry).run (fun _ => 12) sumRoot + (4, oneEdge) = some 5 := by decide --- On every input, the run is the contraction modulo `2 ^ P`, for any evaluator. +-- On every input, the run modulo `2 ^ P` is the contraction modulo `2 ^ P`, for any evaluator. example {T : Type} (eval : T → List Bool → ℕ) (q : ℕ × DescribedNetwork T) : - (sumScheme eval).run fuel sumRoot q = some (q.2.contraction eval % 2 ^ q.1) := - run_sumScheme q + (sumScheme SumOps.mod eval).run fuel sumRoot q = some (q.2.contraction eval % 2 ^ q.1) := by + rw [run_sumScheme, sumValue_mod] + +/-- One edge between two vertices whose entries are coefficient lists of +`(ℤ / 4)[U] / (U ^ 2 + U + 1)`: `U, 1` and `U, 3`. The contraction is +`U ^ 2 + 3 = 2 - U`, the list `[2, 3]`. -/ +def galoisEdge : DescribedNetwork (List (List ℕ)) := + ⟨1, [([0], [[0, 1], [1, 0]]), ([0], [[0, 1], [3, 0]])]⟩ + +/-- Table lookup on coefficient lists. -/ +def galoisEntry (t : List (List ℕ)) (bs : List Bool) : List ℕ := + t.getD (lsbValue bs) [] + +-- The stack machine over coefficient lists at the precision `(2, [1, 1])`. +example : (sumScheme SumOps.galois galoisEntry).run fuel sumRoot ((2, [1, 1]), galoisEdge) = + some [2, 3] := by + decide + +-- On every input, the value of the run in `A_p` is the contraction of the values of the entries. +example {T : Type} (eval : T → List Bool → List ℕ) (q : (ℕ × List ℕ) × DescribedNetwork T) : + (sumScheme SumOps.galois eval).run fuel sumRoot q = + some (sumValue SumOps.galois eval q.1 q.2) ∧ + Cslib.GaloisRingArith.val q.1.1 q.1.2 (sumValue SumOps.galois eval q.1 q.2) = + q.2.contraction fun t bs => Cslib.GaloisRingArith.val q.1.1 q.1.2 (eval t bs) := + ⟨run_sumScheme q, val_sumValue_galois eval q.1 q.2⟩ -- Explicit networks are described networks with their tables. example (d : ExplicitTensorNetwork ℕ) : @@ -55,25 +79,36 @@ theorem entry_polyTime : ComputableInPolyTime (fun q : List ℕ × List Bool => (ComputableInPolyTime.fst (encList encNat) encBits) hindex).of_enc_eq fun q => ?_ simp only [Function.comp_def, ExplicitTensorNetwork.entry, lsbValue_eq_binaryValue_reverse] -/-- **One native machine contracts explicit networks** given as described networks with their -tables, from the table-lookup certificate. -/ +/-- **One native machine contracts explicit networks** modulo `2 ^ P`, given as described +networks with their tables, from the table-lookup certificate. -/ noncomputable def tableCertificate : Cslib.GenericRAM.NativeCertificate (fun q : ℕ × DescribedNetwork (List ℕ) => q.2.contraction ExplicitTensorNetwork.entry % 2 ^ q.1) - (encInput (encList encNat)) encNat := - contractionCertificate ExplicitTensorNetwork.entry + (encInput encUnary (encList encNat)) encNat := + modContractionCertificate ExplicitTensorNetwork.entry (Cslib.GenericRAM.NativeCertificate.ofPolyTime entry_polyTime) --- The resource theorem applies to it. +-- The resource theorem applies to it, with residues of at most `P` bits. example : ∃ K : ℕ, ∀ (q : ℕ × DescribedNetwork (List ℕ)) (E ES Ev : ℕ), (∀ v ∈ q.2.vertices, ∀ bs : List Bool, bs.length = v.1.length → (Cslib.GenericRAM.NativeCertificate.ofPolyTime entry_polyTime).time (v.2, bs) ≤ E ∧ (Cslib.GenericRAM.NativeCertificate.ofPolyTime entry_polyTime).space (v.2, bs) ≤ ES ∧ (encNat (ExplicitTensorNetwork.entry v.2 bs)).length ≤ Ev) → tableCertificate.time q ≤ (2 ^ q.2.numEdges * (q.2.vertices.length + 1) + 1) * - (K * ((encInput (encList encNat) q).length + Ev + 1) ^ K + 2 * E) ∧ - tableCertificate.space q ≤ K * ((encInput (encList encNat) q).length + Ev + 1) ^ K + ES := - contractionCertificate_le _ + (K * ((encInput encUnary (encList encNat) q).length + Ev + q.1 + 1) ^ K + 2 * E) ∧ + tableCertificate.space q ≤ + K * ((encInput encUnary (encList encNat) q).length + Ev + q.1 + 1) ^ K + ES := by + obtain ⟨K, hK⟩ := contractionCertificate_le (ops := SumOps.mod) + (encT := encList encNat) SumOps.mod_polyTime + (Cslib.GenericRAM.NativeCertificate.ofPolyTime entry_polyTime) + refine ⟨K, fun q E ES Ev hE => hK q E ES Ev q.1 hE fun y hy => ?_⟩ + have h := Nat.size_le.mpr (show y < 2 ^ q.1 from hy) + rw [← Nat.size_eq_bits_len] at h + exact h + +-- Coefficient lists are polynomial-time operations, with `p` in unary. +example : SumOps.galois.PolyTime (encPair encUnary (encList encNat)) (encList encNat) := + SumOps.galois_polyTime end ComplexityTests.DescribedContraction From 5280f8c663ae5ac1c5d088fa6eeba7e020aed0d5 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 00:19:08 -0400 Subject: [PATCH 043/129] feat(circuit/frontier): native recovery is polynomial-time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Certify `NativeRecovery.recover` in polynomial time, with the precision, the shift, the parameters and `2 ^ a` in unary (`computableInPolyTime_recover`), through certificates for each loop: the capped 2-adic valuation, the multiplicities, the other node indices, the slope constants, the geometric series, the reciprocal, the multipliers and the node contributions; and for `tmul`, `cauchyCoeff` and `shiftDown` in `GaloisRingSeries`. `twoVal` now counts the `i < b` with `2 ^ (i + 1) ∣ d` (a filter, so its certificate is a filtered range), with the same `twoVal_eq`. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/NativeRecovery.lean | 463 +++++++++++++++++- .../PolyTime/GaloisRingSeries.lean | 130 +++++ .../Frontier/TensorNetworkNativeRecovery.lean | 9 + 3 files changed, 582 insertions(+), 20 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean index 53f3c49408..34d46b1938 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean @@ -9,6 +9,17 @@ module public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ActiveCall public import Complexity.Computability.PolyTime.GaloisRingSeries +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Binary +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Length +import Complexity.Computability.Machines.Turing.MultiTape.Internal.EqualityPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryDivision +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListLookup +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListMap +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeLoops +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeNatFrontends import Mathlib.Data.List.GetD /-! @@ -53,6 +64,10 @@ of the node contributions over `B` (`NativeRecovery.two_pow_mul_recoverFromValue recovered value `recoverFromValues B a u h ζ v` lies in `B`, native recovery computes its image modulo `2 ^ p` (`NativeRecovery.val_recover`). +**Polynomial time.** Every loop is a fold over a range or over the other node indices, so native +recovery is polynomial-time with the precision, the shift, the parameters and `2 ^ a` in unary +(`NativeRecovery.computableInPolyTime_recover`). + ## Main definitions * `Cslib.Circuits.Frontier.NativeRecovery.slopeConst`: the constant `α / (t - q)`. @@ -86,6 +101,8 @@ modulo `2 ^ p` (`NativeRecovery.val_recover`). * `Cslib.Circuits.Frontier.NativeRecovery.val_recover`, `Cslib.Circuits.Frontier.NativeRecovery.val_recover_of_eq_add`: native recovery computes the mathematical recovery modulo `2 ^ p`. +* `Cslib.Circuits.Frontier.NativeRecovery.computableInPolyTime_recover`: native recovery is + polynomial-time. ## References @@ -107,26 +124,33 @@ local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) /-- The node `t = k - 2 ^ a` of index `k`. -/ def node (a k : ℕ) : ℤ := (k : ℤ) - 2 ^ a -/-- The `2`-adic valuation of `d`, by at most `b` halvings: `v₂ d` when `d < 2 ^ b` -(`NativeRecovery.twoVal_eq`), and `0` for `d = 0`. -/ -def twoVal : ℕ → ℕ → ℕ - | 0, _ => 0 - | b + 1, d => if d ≠ 0 ∧ d % 2 = 0 then twoVal b (d / 2) + 1 else 0 - -theorem twoVal_eq : ∀ {b d : ℕ}, d < 2 ^ b → twoVal b d = padicValNat 2 d - | 0, d, hd => by rw [pow_zero] at hd; obtain rfl : d = 0 := by omega - simp [twoVal] - | b + 1, d, hd => by - rw [twoVal] - split_ifs with h - · obtain ⟨hd0, h2⟩ := h - have hd' : d = 2 * (d / 2) := by omega - rw [twoVal_eq (by rw [pow_succ] at hd; omega)] - conv_rhs => rw [hd'] - rw [padicValNat.mul (by norm_num) (by omega), padicValNat_self, add_comm] - · rcases Nat.eq_zero_or_pos d with rfl | hpos - · simp - · exact (padicValNat.eq_zero_of_not_dvd (by omega)).symm +/-- The `2`-adic valuation of `d`, capped at `b`: the number of `i < b` with `2 ^ (i + 1) ∣ d`, +and `0` for `d = 0`. It is `v₂ d` when `d < 2 ^ b` (`NativeRecovery.twoVal_eq`). -/ +def twoVal (b d : ℕ) : ℕ := + if d = 0 then 0 else ((List.range b).filter fun i => d % 2 ^ (i + 1) = 0).length + +theorem twoVal_eq {b d : ℕ} (hd : d < 2 ^ b) : twoVal b d = padicValNat 2 d := by + rw [twoVal] + split_ifs with h0 + · simp [h0] + set v := padicValNat 2 d + have hvb : v < b := by + have : 2 ^ v ≤ d := Nat.le_of_dvd (Nat.pos_of_ne_zero h0) pow_padicValNat_dvd + exact (Nat.pow_lt_pow_iff_right (by norm_num)).mp (this.trans_lt hd) + have key (m : ℕ) : ((List.range m).filter fun i => d % 2 ^ (i + 1) = 0).length = min m v := by + induction m with + | zero => simp + | succ m ih => + rw [List.range_succ, List.filter_append, List.length_append, ih] + have hiff : d % 2 ^ (m + 1) = 0 ↔ m + 1 ≤ v := by + rw [← Nat.dvd_iff_mod_eq_zero] + exact padicValNat_dvd_iff_le h0 + by_cases hm : m + 1 ≤ v + · simp [hiff.mpr hm] + omega + · simp [mt hiff.mp hm] + omega + rw [key, min_eq_right hvb.le] /-- The multiplicity `q_t = h (u - v₂ t)` of the node of index `k`. -/ def mult (a u h k : ℕ) : ℕ := h * (u - twoVal (a + 1) (k - 2 ^ a + (2 ^ a - k))) @@ -700,6 +724,7 @@ theorem exists_pval_interp_eq_add (hirr : Irreducible ((monicOf fl).map (Int.cas /-! ### The Hermite quotient over `B` -/ /-- A fold of multiplications is a list product. -/ +@[to_additive /-- A fold of additions is a list sum. -/] theorem foldl_mul_eq {M ι : Type*} [CommMonoid M] (g : ι → M) (l : List ι) (c : M) : l.foldl (fun x y => x * g y) c = c * (l.map g).prod := by induction l generalizing c with @@ -958,4 +983,402 @@ theorem val_recover_of_eq_add end Hermite +/-! ### Polynomial time -/ + +section PolyTime + +open Turing.MultiTapeTM + +variable {α : Type} {encIn : α ↪ List Bool} + +private theorem lengthPolyTime {β : Type} {encB : β ↪ List Bool} {l : α → List β} + (hl : ComputableInPolyTime l encIn (encList encB)) : + ComputableInPolyTime (fun x => (l x).length) encIn encUnary := + (ComputableInPolyTime.length_list encB).comp hl + +private theorem getDPolyTime {P : α → List (List ℕ)} {k : α → ℕ} + (hP : ComputableInPolyTime P encIn (encList (encList encNat))) + (hk : ComputableInPolyTime k encIn encUnary) : + ComputableInPolyTime (fun x => (P x).getD (k x) []) encIn (encList encNat) := + (ComputableInPolyTime.listGetD (encA := encList encNat) []).comp (hP.prod hk) + +/-- A unary natural as a one-coordinate list. -/ +private theorem singletonPolyTime {n : α → ℕ} (hn : ComputableInPolyTime n encIn encNat) : + ComputableInPolyTime (fun x => [n x]) encIn (encList encNat) := + ComputableInPolyTime.cons' hn (ComputableInPolyTime.const []) + +private theorem polyBounded_unary {n : α → ℕ} (hn : ComputableInPolyTime n encIn encUnary) : + PolyBounded (fun x => (encIn x).length) n := by + simpa using ComputableInPolyTime.polyBounded_length hn + +/-- The length of a polynomial with `J` small coefficients is polynomially bounded. -/ +private theorem polyBounded_polyLength {p J : α → ℕ} {fl : α → List ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hJ : ComputableInPolyTime J encIn encUnary) : + PolyBounded (fun x => (encIn x).length) + (fun x => J x * (2 * ((fl x).length * (2 * (p x + 1) + 1)) + 1)) := by + have hpl := polyBounded_unary hp + have hfll : PolyBounded (fun x => (encIn x).length) fun x => (fl x).length := + (lengthPolyTime hfl).polyBounded_length.mono fun x => by simp + exact (polyBounded_unary hJ).mul (((PolyBounded.const 2).mul (hfll.mul (((PolyBounded.const 2).mul + (hpl.add (PolyBounded.const 1))).add (PolyBounded.const 1)))).add (PolyBounded.const 1)) + +private theorem length_encList_mul_le (N : ℕ) (fl c d : List ℕ) : + (encList encNat (mul N fl c d)).length ≤ fl.length * (2 * N + 1) := + length_encList_le (length_mul N fl c d) fun _ h => lt_of_mem_mul h + +private theorem length_encList_add_le (N : ℕ) (fl c d : List ℕ) : + (encList encNat (add N fl c d)).length ≤ fl.length * (2 * N + 1) := + length_encList_le (length_add N fl c d) fun _ h => lt_of_mem_add h + +/-- **The capped `2`-adic valuation is polynomial-time**, in unary. -/ +@[fun_prop] +theorem computableInPolyTime_twoVal {b d : α → ℕ} (hb : ComputableInPolyTime b encIn encUnary) + (hd : ComputableInPolyTime d encIn encUnary) : + ComputableInPolyTime (fun x => twoVal (b x) (d x)) encIn encUnary := by + have h1 := ComputableInPolyTime.fst encIn encUnary + have h2 := ComputableInPolyTime.snd encIn encUnary + have htest : ComputableInPolyTime (fun q : α × ℕ => d q.1 % 2 ^ (q.2 + 1) == 0) + (encPair encIn encUnary) encBool := + ComputableInPolyTime.beq (ComputableInPolyTime.binary_mod.comp + ((ComputableInPolyTime.unary_to_binary.comp (hd.comp h1)).prod + (ComputableInPolyTime.binary_two_pow.comp + (ComputableInPolyTime.unary_add' h2 (ComputableInPolyTime.const 1))))) + (ComputableInPolyTime.const 0) + refine (ComputableInPolyTime.ite (ComputableInPolyTime.beq hd (ComputableInPolyTime.const 0)) + (ComputableInPolyTime.const 0) (lengthPolyTime (ComputableInPolyTime.filterWithInput + (test := fun x i => d x % 2 ^ (i + 1) == 0) (ComputableInPolyTime.range.comp hb) + htest))).of_enc_eq fun x => ?_ + by_cases h : d x = 0 <;> simp [twoVal, h, beq_eq_decide] + +/-- `2 ^ (a + 1)` in unary, from `2 ^ a` in unary. -/ +private theorem twoPowSuccPolyTime {a : α → ℕ} + (hL : ComputableInPolyTime (fun x => 2 ^ a x) encIn encUnary) : + ComputableInPolyTime (fun x => 2 ^ (a x + 1)) encIn encUnary := + (ComputableInPolyTime.unary_add' hL hL).of_enc_eq fun x => by rw [pow_succ, mul_two] + +/-- **The multiplicities are polynomial-time**, in unary, given `2 ^ a` in unary. -/ +@[fun_prop] +theorem computableInPolyTime_mult {a u h k : α → ℕ} + (ha : ComputableInPolyTime a encIn encUnary) + (hL : ComputableInPolyTime (fun x => 2 ^ a x) encIn encUnary) + (hu : ComputableInPolyTime u encIn encUnary) (hh : ComputableInPolyTime h encIn encUnary) + (hk : ComputableInPolyTime k encIn encUnary) : + ComputableInPolyTime (fun x => mult (a x) (u x) (h x) (k x)) encIn encUnary := + ComputableInPolyTime.unary_mul' hh (ComputableInPolyTime.unary_sub' hu + (computableInPolyTime_twoVal (ComputableInPolyTime.unary_add' ha (ComputableInPolyTime.const 1)) + (ComputableInPolyTime.unary_add' (ComputableInPolyTime.unary_sub' hk hL) + (ComputableInPolyTime.unary_sub' hL hk)))) + +/-- **The other node indices are polynomial-time**, given `2 ^ a` in unary. -/ +@[fun_prop] +theorem computableInPolyTime_others {a k : α → ℕ} + (hL : ComputableInPolyTime (fun x => 2 ^ a x) encIn encUnary) + (hk : ComputableInPolyTime k encIn encUnary) : + ComputableInPolyTime (fun x => others (a x) (k x)) encIn (encList encUnary) := by + have h1 := ComputableInPolyTime.fst encIn encUnary + have h2 := ComputableInPolyTime.snd encIn encUnary + refine (ComputableInPolyTime.filterWithInput + (test := fun x k' => !(k' == 2 ^ a x) && !(k' == k x)) + (ComputableInPolyTime.range.comp (twoPowSuccPolyTime hL)) + (ComputableInPolyTime.bool_and' + (ComputableInPolyTime.bool_not.comp (ComputableInPolyTime.beq h2 (hL.comp h1))) + (ComputableInPolyTime.bool_not.comp (ComputableInPolyTime.beq h2 (hk.comp h1))))).of_enc_eq + fun x => ?_ + congr 1 + +/-- **The total multiplicity of the other nodes is polynomial-time**, in unary. -/ +@[fun_prop] +theorem computableInPolyTime_multSum {a u h k : α → ℕ} + (ha : ComputableInPolyTime a encIn encUnary) + (hL : ComputableInPolyTime (fun x => 2 ^ a x) encIn encUnary) + (hu : ComputableInPolyTime u encIn encUnary) (hh : ComputableInPolyTime h encIn encUnary) + (hk : ComputableInPolyTime k encIn encUnary) : + ComputableInPolyTime (fun x => multSum (a x) (u x) (h x) (k x)) encIn encUnary := by + have hq := ComputableInPolyTime.fst (encPair encIn encUnary) encUnary + have hx := (ComputableInPolyTime.fst encIn encUnary).comp hq + have hs := (ComputableInPolyTime.snd encIn encUnary).comp hq + have hk' := ComputableInPolyTime.snd (encPair encIn encUnary) encUnary + have hfold := ComputableInPolyTime.foldlWithInput_of_bounded_growth + (encA := encUnary) (encL := encList encUnary) + (step := fun x s k' => s + mult (a x) (u x) (h x) k') (ComputableInPolyTime.const 0) + ⟨computableInPolyTime_others hL hk, ComputableInPolyTime.unary_add' hs + (computableInPolyTime_mult (ha.comp hx) (hL.comp hx) (hu.comp hx) (hh.comp hx) hk'), + inferInstance, inferInstance⟩ + ⟨(polyBounded_unary hh).mul (polyBounded_unary hu), fun x s k' _ => by + have : mult (a x) (u x) (h x) k' ≤ h x * u x := + Nat.mul_le_mul_left _ (Nat.sub_le _ _) + simp only [encUnary_apply, List.length_replicate] + omega⟩ + refine hfold.of_enc_eq fun x => ?_ + rw [multSum, foldl_add_eq, zero_add] + +/-- **Integer differences as constants are polynomial-time**, with `x, y` in unary. -/ +@[fun_prop] +theorem computableInPolyTime_diffConst {N x y : α → ℕ} {fl : α → List ℕ} + (hN : ComputableInPolyTime N encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hx : ComputableInPolyTime x encIn encUnary) (hy : ComputableInPolyTime y encIn encUnary) : + ComputableInPolyTime (fun z => diffConst (N z) (fl z) (x z) (y z)) encIn (encList encNat) := + ComputableInPolyTime.ite (ComputableInPolyTime.unary_lt' hx hy) + (computableInPolyTime_neg hN hfl (singletonPolyTime + (ComputableInPolyTime.unary_to_binary.comp (ComputableInPolyTime.unary_sub' hy hx)))) + (singletonPolyTime (ComputableInPolyTime.unary_to_binary.comp + (ComputableInPolyTime.unary_sub' hx hy))) |>.of_enc_eq fun z => by simp [diffConst] + +/-- **The slope constants are polynomial-time**, with `a, x, y` in unary. -/ +@[fun_prop] +theorem computableInPolyTime_slopeConst {N a x y : α → ℕ} {fl : α → List ℕ} + (hN : ComputableInPolyTime N encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (ha : ComputableInPolyTime a encIn encUnary) + (hx : ComputableInPolyTime x encIn encUnary) (hy : ComputableInPolyTime y encIn encUnary) : + ComputableInPolyTime (fun z => slopeConst (N z) (fl z) (a z) (x z) (y z)) encIn + (encList encNat) := by + have hd := ComputableInPolyTime.unary_add' (ComputableInPolyTime.unary_sub' hx hy) + (ComputableInPolyTime.unary_sub' hy hx) + have ha1 := ComputableInPolyTime.unary_add' ha (ComputableInPolyTime.const 1) + have hv := computableInPolyTime_twoVal ha1 hd + have hw := computableInPolyTime_mul hN hfl + (singletonPolyTime (ComputableInPolyTime.binary_two_pow.comp + (ComputableInPolyTime.unary_sub' ha1 hv))) + (computableInPolyTime_inv hN hfl (singletonPolyTime (ComputableInPolyTime.binary_div.comp + ((ComputableInPolyTime.unary_to_binary.comp hd).prod + (ComputableInPolyTime.binary_two_pow.comp hv))))) + exact (ComputableInPolyTime.ite (ComputableInPolyTime.unary_lt' hx hy) + (computableInPolyTime_neg hN hfl hw) hw).of_enc_eq fun z => by simp [slopeConst] + +/-- **Truncated geometric series are polynomial-time**, with `J` in unary. -/ +@[fun_prop] +theorem computableInPolyTime_geom {N J : α → ℕ} {fl c : α → List ℕ} + (hN : ComputableInPolyTime N encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hJ : ComputableInPolyTime J encIn encUnary) + (hc : ComputableInPolyTime c encIn (encList encNat)) : + ComputableInPolyTime (fun x => geom (N x) (fl x) (J x) (c x)) encIn + (encList (encList encNat)) := by + have h1 := ComputableInPolyTime.fst encIn encUnary + have h2 := ComputableInPolyTime.snd encIn encUnary + exact ComputableInPolyTime.mapWithInput (ComputableInPolyTime.range.comp hJ) + (computableInPolyTime_pow (hN.comp h1) (hfl.comp h1) + (computableInPolyTime_neg (hN.comp h1) (hfl.comp h1) (hc.comp h1)) h2) + +theorem mulGeomPow_succ (N : ℕ) (fl : List ℕ) (J : ℕ) (c : List ℕ) (m : ℕ) + (R : List (List ℕ)) : + mulGeomPow N fl J c (m + 1) R = tmul N fl J (mulGeomPow N fl J c m R) (geom N fl J c) := by + rw [mulGeomPow, List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil, + ← mulGeomPow] + +/-- A multiplication by a power of a geometric series is the input or a truncated product. -/ +theorem mulGeomPow_eq_or (N : ℕ) (fl : List ℕ) (J : ℕ) (c : List ℕ) (m : ℕ) + (R : List (List ℕ)) : mulGeomPow N fl J c m R = R ∨ + ∃ R', mulGeomPow N fl J c m R = tmul N fl J R' (geom N fl J c) := by + rcases m with _ | m + · exact Or.inl (by simp [mulGeomPow]) + · exact Or.inr ⟨_, mulGeomPow_succ N fl J c m R⟩ + +/-- The encoded length of a truncated product is bounded. -/ +private theorem length_encPoly_tmul_le (N : ℕ) (fl : List ℕ) (J : ℕ) (P R : List (List ℕ)) : + (encList (encList encNat) (tmul N fl J P R)).length ≤ + J * (2 * (fl.length * (2 * (N + 1) + 1)) + 1) := + length_encPoly_le (length_tmul J P R).le fun _ hc => small_of_mem_tmul hc + +/-- **Multiplication by a power of a geometric series is polynomial-time**, with `J, m` in +unary. -/ +@[fun_prop] +theorem computableInPolyTime_mulGeomPow {N J m : α → ℕ} {fl c : α → List ℕ} + {R : α → List (List ℕ)} + (hN : ComputableInPolyTime N encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hJ : ComputableInPolyTime J encIn encUnary) + (hc : ComputableInPolyTime c encIn (encList encNat)) + (hm : ComputableInPolyTime m encIn encUnary) + (hR : ComputableInPolyTime R encIn (encList (encList encNat))) : + ComputableInPolyTime (fun x => mulGeomPow (N x) (fl x) (J x) (c x) (m x) (R x)) encIn + (encList (encList encNat)) := by + let encS := encList (encList encNat) + have hq := ComputableInPolyTime.fst (encPair encIn encS) encUnary + have hx := (ComputableInPolyTime.fst encIn encS).comp hq + have hP := (ComputableInPolyTime.snd encIn encS).comp hq + exact ComputableInPolyTime.foldlWithInput_of_bounded_growth + (encA := encUnary) (encL := encList encUnary) + (step := fun x R _ => tmul (N x) (fl x) (J x) R (geom (N x) (fl x) (J x) (c x))) hR + ⟨ComputableInPolyTime.range.comp hm, computableInPolyTime_tmul (hN.comp hx) (hfl.comp hx) + (hJ.comp hx) hP (computableInPolyTime_geom (hN.comp hx) (hfl.comp hx) (hJ.comp hx) + (hc.comp hx)), inferInstance, inferInstance⟩ + ⟨polyBounded_polyLength hN hfl hJ, fun x R _ _ => by + have := length_encPoly_tmul_le (N x) (fl x) (J x) R (geom (N x) (fl x) (J x) (c x)) + omega⟩ + +/-- **The truncated reciprocal of `E_t(t + α z)` is polynomial-time**, given `2 ^ a` in +unary. -/ +@[fun_prop] +theorem computableInPolyTime_recip {N a u h k J : α → ℕ} {fl : α → List ℕ} + (hN : ComputableInPolyTime N encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (ha : ComputableInPolyTime a encIn encUnary) + (hL : ComputableInPolyTime (fun x => 2 ^ a x) encIn encUnary) + (hu : ComputableInPolyTime u encIn encUnary) (hh : ComputableInPolyTime h encIn encUnary) + (hk : ComputableInPolyTime k encIn encUnary) (hJ : ComputableInPolyTime J encIn encUnary) : + ComputableInPolyTime (fun x => recip (N x) (fl x) (a x) (u x) (h x) (k x) (J x)) encIn + (encList (encList encNat)) := by + let encS := encList (encList encNat) + have hq := ComputableInPolyTime.fst (encPair encIn encS) encUnary + have hx := (ComputableInPolyTime.fst encIn encS).comp hq + have hP := (ComputableInPolyTime.snd encIn encS).comp hq + have hk' := ComputableInPolyTime.snd (encPair encIn encS) encUnary + exact ComputableInPolyTime.foldlWithInput_of_bounded_growth + (encA := encUnary) (encL := encList encUnary) + (step := fun x R k' => mulGeomPow (N x) (fl x) (J x) (slopeConst (N x) (fl x) (a x) (k x) k') + (mult (a x) (u x) (h x) k') R) + (ComputableInPolyTime.cons' (computableInPolyTime_one hfl) (ComputableInPolyTime.const [])) + ⟨computableInPolyTime_others hL hk, computableInPolyTime_mulGeomPow (hN.comp hx) + (hfl.comp hx) (hJ.comp hx) (computableInPolyTime_slopeConst (hN.comp hx) (hfl.comp hx) + (ha.comp hx) (hk.comp hx) hk') + (computableInPolyTime_mult (ha.comp hx) (hL.comp hx) (hu.comp hx) (hh.comp hx) hk') hP, + inferInstance, inferInstance⟩ + ⟨polyBounded_polyLength hN hfl hJ, fun x R k' _ => by + rcases mulGeomPow_eq_or (N x) (fl x) (J x) (slopeConst (N x) (fl x) (a x) (k x) k') + (mult (a x) (u x) (h x) k') R with h0 | ⟨R', h1⟩ + · rw [h0]; omega + · have := length_encPoly_tmul_le (N x) (fl x) (J x) R' + (geom (N x) (fl x) (J x) (slopeConst (N x) (fl x) (a x) (k x) k')) + rw [h1]; omega⟩ + +/-- **The products `∏_q (-q c_q) ^ (q_q)` are polynomial-time**, given `2 ^ a` in unary. -/ +@[fun_prop] +theorem computableInPolyTime_basisNum {N a u h k : α → ℕ} {fl : α → List ℕ} + (hN : ComputableInPolyTime N encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (ha : ComputableInPolyTime a encIn encUnary) + (hL : ComputableInPolyTime (fun x => 2 ^ a x) encIn encUnary) + (hu : ComputableInPolyTime u encIn encUnary) (hh : ComputableInPolyTime h encIn encUnary) + (hk : ComputableInPolyTime k encIn encUnary) : + ComputableInPolyTime (fun x => basisNum (N x) (fl x) (a x) (u x) (h x) (k x)) encIn + (encList encNat) := by + have hq := ComputableInPolyTime.fst (encPair encIn (encList encNat)) encUnary + have hx := (ComputableInPolyTime.fst encIn (encList encNat)).comp hq + have hacc := (ComputableInPolyTime.snd encIn (encList encNat)).comp hq + have hk' := ComputableInPolyTime.snd (encPair encIn (encList encNat)) encUnary + have hN' := hN.comp hx + have hfl' := hfl.comp hx + exact ComputableInPolyTime.foldlWithInput_of_bounded_growth + (encA := encUnary) (encL := encList encUnary) + (step := fun x acc k' => mul (N x) (fl x) acc (pow (N x) (fl x) + (mul (N x) (fl x) (diffConst (N x) (fl x) (2 ^ a x) k') + (slopeConst (N x) (fl x) (a x) (k x) k')) (mult (a x) (u x) (h x) k'))) + (computableInPolyTime_one hfl) + ⟨computableInPolyTime_others hL hk, computableInPolyTime_mul hN' hfl' hacc + (computableInPolyTime_pow hN' hfl' (computableInPolyTime_mul hN' hfl' + (computableInPolyTime_diffConst hN' hfl' (hL.comp hx) hk') + (computableInPolyTime_slopeConst hN' hfl' (ha.comp hx) (hk.comp hx) hk')) + (computableInPolyTime_mult (ha.comp hx) (hL.comp hx) (hu.comp hx) (hh.comp hx) hk')), + inferInstance, inferInstance⟩ + ⟨polyBounded_reducedLength hN hfl, fun x acc k' _ => + (length_encList_mul_le _ _ _ _).trans (Nat.le_add_left _ _)⟩ + +/-- **The multipliers are polynomial-time**, given `2 ^ a` in unary. -/ +@[fun_prop] +theorem computableInPolyTime_coeffMult {N a u h Δ k j : α → ℕ} {fl : α → List ℕ} + (hN : ComputableInPolyTime N encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (ha : ComputableInPolyTime a encIn encUnary) + (hL : ComputableInPolyTime (fun x => 2 ^ a x) encIn encUnary) + (hu : ComputableInPolyTime u encIn encUnary) (hh : ComputableInPolyTime h encIn encUnary) + (hΔ : ComputableInPolyTime Δ encIn encUnary) (hk : ComputableInPolyTime k encIn encUnary) + (hj : ComputableInPolyTime j encIn encUnary) : + ComputableInPolyTime (fun x => coeffMult (N x) (fl x) (a x) (u x) (h x) (Δ x) (k x) (j x)) + encIn (encList encNat) := + computableInPolyTime_mul hN hfl (computableInPolyTime_mul hN hfl + (singletonPolyTime (ComputableInPolyTime.binary_two_pow.comp + (ComputableInPolyTime.unary_sub' hΔ (ComputableInPolyTime.unary_mul' + (ComputableInPolyTime.unary_add' ha (ComputableInPolyTime.const 1)) + (ComputableInPolyTime.unary_add' (computableInPolyTime_multSum ha hL hu hh hk) hj))))) + (computableInPolyTime_basisNum hN hfl ha hL hu hh hk)) + (computableInPolyTime_pow hN hfl (computableInPolyTime_diffConst hN hfl hL hk) hj) + +/-- **The contribution of a node is polynomial-time**, given `2 ^ a` in unary. -/ +@[fun_prop] +theorem computableInPolyTime_nodeTerm {N a u h Δ k : α → ℕ} {fl : α → List ℕ} + {ζ w : α → List (List ℕ)} + (hN : ComputableInPolyTime N encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (ha : ComputableInPolyTime a encIn encUnary) + (hL : ComputableInPolyTime (fun x => 2 ^ a x) encIn encUnary) + (hu : ComputableInPolyTime u encIn encUnary) (hh : ComputableInPolyTime h encIn encUnary) + (hΔ : ComputableInPolyTime Δ encIn encUnary) + (hζ : ComputableInPolyTime ζ encIn (encList (encList encNat))) + (hk : ComputableInPolyTime k encIn encUnary) + (hw : ComputableInPolyTime w encIn (encList (encList encNat))) : + ComputableInPolyTime (fun x => nodeTerm (N x) (fl x) (a x) (u x) (h x) (Δ x) (ζ x) (k x) (w x)) + encIn (encList encNat) := by + have hJ := computableInPolyTime_mult ha hL hu hh hk + have hQ := computableInPolyTime_tmul hN hfl hJ (computableInPolyTime_interp hN hfl hζ hw) + (computableInPolyTime_recip hN hfl ha hL hu hh hk hJ) + have hq := ComputableInPolyTime.fst (encPair encIn (encList encNat)) encUnary + have hx := (ComputableInPolyTime.fst encIn (encList encNat)).comp hq + have hS := (ComputableInPolyTime.snd encIn (encList encNat)).comp hq + have hj := ComputableInPolyTime.snd (encPair encIn (encList encNat)) encUnary + exact ComputableInPolyTime.foldlWithInput_of_bounded_growth + (encA := encUnary) (encL := encList encUnary) + (step := fun x S j => add (N x) (fl x) S (mul (N x) (fl x) + (coeffMult (N x) (fl x) (a x) (u x) (h x) (Δ x) (k x) j) + ((tmul (N x) (fl x) (mult (a x) (u x) (h x) (k x)) (interp (N x) (fl x) (ζ x) (w x)) + (recip (N x) (fl x) (a x) (u x) (h x) (k x) (mult (a x) (u x) (h x) (k x)))).getD j []))) + (computableInPolyTime_zero hfl) + ⟨ComputableInPolyTime.range.comp hJ, computableInPolyTime_add (hN.comp hx) (hfl.comp hx) hS + (computableInPolyTime_mul (hN.comp hx) (hfl.comp hx) + (computableInPolyTime_coeffMult (hN.comp hx) (hfl.comp hx) (ha.comp hx) (hL.comp hx) + (hu.comp hx) (hh.comp hx) (hΔ.comp hx) (hk.comp hx) hj) + (getDPolyTime (hQ.comp hx) hj)), inferInstance, inferInstance⟩ + ⟨polyBounded_reducedLength hN hfl, fun x S j _ => + (length_encList_add_le _ _ _ _).trans (Nat.le_add_left _ _)⟩ + +/-- **Native tapered recovery is polynomial-time**, with the precision `p`, the shift `Δ`, the +parameters `a, u, h` and `2 ^ a` in unary, and the points and values as coefficient lists. -/ +@[fun_prop] +theorem computableInPolyTime_recover {p Δ a u h : α → ℕ} {fl : α → List ℕ} + {ζ : α → List (List ℕ)} {W : α → List (List (List ℕ))} + (hp : ComputableInPolyTime p encIn encUnary) (hΔ : ComputableInPolyTime Δ encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (ha : ComputableInPolyTime a encIn encUnary) + (hL : ComputableInPolyTime (fun x => 2 ^ a x) encIn encUnary) + (hu : ComputableInPolyTime u encIn encUnary) (hh : ComputableInPolyTime h encIn encUnary) + (hζ : ComputableInPolyTime ζ encIn (encList (encList encNat))) + (hW : ComputableInPolyTime W encIn (encList (encList (encList encNat)))) : + ComputableInPolyTime (fun x => recover (p x) (Δ x) (fl x) (a x) (u x) (h x) (ζ x) (W x)) + encIn (encList encNat) := by + have hN := ComputableInPolyTime.unary_add' hp hΔ + have hq := ComputableInPolyTime.fst (encPair encIn (encList encNat)) encUnary + have hx := (ComputableInPolyTime.fst encIn (encList encNat)).comp hq + have hS := (ComputableInPolyTime.snd encIn (encList encNat)).comp hq + have hk := ComputableInPolyTime.snd (encPair encIn (encList encNat)) encUnary + have hstep : ComputableInPolyTime (fun q : (α × List ℕ) × ℕ => + if (q.2 == 2 ^ a q.1.1) = true then q.1.2 else add (p q.1.1 + Δ q.1.1) (fl q.1.1) q.1.2 + (nodeTerm (p q.1.1 + Δ q.1.1) (fl q.1.1) (a q.1.1) (u q.1.1) (h q.1.1) (Δ q.1.1) + (ζ q.1.1) q.2 ((W q.1.1).getD q.2 []))) + (encPair (encPair encIn (encList encNat)) encUnary) (encList encNat) := + ComputableInPolyTime.ite (ComputableInPolyTime.beq hk (hL.comp hx)) hS + (computableInPolyTime_add (hN.comp hx) (hfl.comp hx) hS + (computableInPolyTime_nodeTerm (hN.comp hx) (hfl.comp hx) (ha.comp hx) (hL.comp hx) + (hu.comp hx) (hh.comp hx) (hΔ.comp hx) (hζ.comp hx) hk + ((ComputableInPolyTime.listGetD (encA := encList (encList encNat)) []).comp + ((hW.comp hx).prod hk)))) + have hfold := ComputableInPolyTime.foldlWithInput_of_bounded_growth + (encA := encUnary) (encL := encList encUnary) + (step := fun x S k => if (k == 2 ^ a x) = true then S else add (p x + Δ x) (fl x) S + (nodeTerm (p x + Δ x) (fl x) (a x) (u x) (h x) (Δ x) (ζ x) k ((W x).getD k []))) + (computableInPolyTime_zero hfl) + ⟨ComputableInPolyTime.range.comp (twoPowSuccPolyTime hL), hstep, inferInstance, + inferInstance⟩ + ⟨polyBounded_reducedLength hN hfl, fun x S k _ => by + split + · omega + · exact (length_encList_add_le _ _ _ _).trans (Nat.le_add_left _ _)⟩ + refine (computableInPolyTime_shiftDown hΔ hfold).of_enc_eq fun x => ?_ + simp only [recover, beq_iff_eq, Function.comp_apply] + +end PolyTime + end Cslib.Circuits.Frontier.NativeRecovery diff --git a/Complexity/Computability/PolyTime/GaloisRingSeries.lean b/Complexity/Computability/PolyTime/GaloisRingSeries.lean index 7f3935612c..455838f145 100644 --- a/Complexity/Computability/PolyTime/GaloisRingSeries.lean +++ b/Complexity/Computability/PolyTime/GaloisRingSeries.lean @@ -9,6 +9,15 @@ module public import Complexity.Computability.PolyTime.GaloisRingInterpolation public import Complexity.Computability.PolyTime.GaloisRingUnits +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Binary +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Length +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryDivision +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListLookup +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListMap +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeLoops import Mathlib.Data.List.GetD /-! @@ -28,6 +37,10 @@ the contraction algorithm of [OpenAI2026ETH] (Appendix D.4.4, pp. 60–61). * **Truncated products** (`GaloisRingArith.tmul`) of polynomials in `z` over `A_p`: the first `J` coefficients of a product, by the Cauchy formula (`GaloisRingArith.val_tmul`). +Truncated products and `GaloisRingArith.shiftDown` are polynomial-time, with the precision, the +length and the shift in unary (`GaloisRingArith.computableInPolyTime_tmul`, +`GaloisRingArith.computableInPolyTime_shiftDown`). + ## Main definitions * `Cslib.GaloisRingArith.reduce`: reduction to a lower precision. @@ -39,6 +52,8 @@ the contraction algorithm of [OpenAI2026ETH] (Appendix D.4.4, pp. 60–61). * `Cslib.GaloisRingArith.mod_eq_of_val_eq`, `Cslib.GaloisRingArith.exists_val_eq`: coordinates. * `Cslib.GaloisRingArith.val_shiftDown`: cancelling a common power of two. * `Cslib.GaloisRingArith.val_tmul`: truncated products are correct. +* `Cslib.GaloisRingArith.computableInPolyTime_tmul`, + `Cslib.GaloisRingArith.computableInPolyTime_shiftDown`: polynomial time. ## References @@ -234,4 +249,119 @@ theorem isUnit_natCast_of_odd {o : ℕ} (ho : Odd o) : IsUnit (o : Ring p fl) := have := (ZMod.unitOfCoprime o hcop).isUnit.map (algebraMap (ZMod (2 ^ p)) (Ring p fl)) simpa using this +/-! ### Polynomial time -/ + +section PolyTime + +open Turing.MultiTapeTM + +variable {α : Type} {encIn : α ↪ List Bool} + +private theorem lengthPolyTime {β : Type} {encB : β ↪ List Bool} {l : α → List β} + (hl : ComputableInPolyTime l encIn (encList encB)) : + ComputableInPolyTime (fun x => (l x).length) encIn encUnary := + (ComputableInPolyTime.length_list encB).comp hl + +private theorem getDPolyTime {P : α → List (List ℕ)} {k : α → ℕ} + (hP : ComputableInPolyTime P encIn (encList (encList encNat))) + (hk : ComputableInPolyTime k encIn encUnary) : + ComputableInPolyTime (fun x => (P x).getD (k x) []) encIn (encList encNat) := + (ComputableInPolyTime.listGetD (encA := encList encNat) []).comp (hP.prod hk) + +/-- The zero element is polynomial-time. -/ +theorem computableInPolyTime_zero {fl : α → List ℕ} + (hfl : ComputableInPolyTime fl encIn (encList encNat)) : + ComputableInPolyTime (fun x => zero (fl x)) encIn (encList encNat) := + computableInPolyTime_tabulate (g := fun _ _ => 0) (lengthPolyTime hfl) + (ComputableInPolyTime.const 0) + +/-- The length `e (2 p + 1)` of a reduced coefficient list is polynomially bounded. -/ +theorem polyBounded_reducedLength {p : α → ℕ} {fl : α → List ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) : + PolyBounded (fun x => (encIn x).length) (fun x => (fl x).length * (2 * p x + 1)) := by + have hpl : PolyBounded (fun x => (encIn x).length) p := by + simpa using (ComputableInPolyTime.polyBounded_length hp) + have hfll : PolyBounded (fun x => (encIn x).length) fun x => (fl x).length := + (lengthPolyTime hfl).polyBounded_length.mono fun x => by simp + exact hfll.mul (((PolyBounded.const 2).mul hpl).add (PolyBounded.const 1)) + +/-- **Division of the coordinates by `2 ^ Δ` is polynomial-time**, with `Δ` in unary. -/ +@[fun_prop] +theorem computableInPolyTime_shiftDown {Δ : α → ℕ} {c : α → List ℕ} + (hΔ : ComputableInPolyTime Δ encIn encUnary) + (hc : ComputableInPolyTime c encIn (encList encNat)) : + ComputableInPolyTime (fun x => shiftDown (Δ x) (c x)) encIn (encList encNat) := + ComputableInPolyTime.mapWithInput hc (ComputableInPolyTime.binary_div.comp + ((ComputableInPolyTime.snd encIn encNat).prod (ComputableInPolyTime.binary_two_pow.comp + (hΔ.comp (ComputableInPolyTime.fst encIn encNat))))) + +theorem lt_of_mem_cauchyCoeff {P R : List (List ℕ)} {j x : ℕ} (hx : x ∈ cauchyCoeff p fl P R j) : + x < 2 ^ p := by + rw [cauchyCoeff, List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil] at hx + exact lt_of_mem_add hx + +theorem length_cauchyCoeff (P R : List (List ℕ)) (j : ℕ) : + (cauchyCoeff p fl P R j).length = fl.length := by + rw [cauchyCoeff, List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil, + length_add] + +/-- **Cauchy coefficients are polynomial-time**, with the precision and the index in unary. -/ +@[fun_prop] +theorem computableInPolyTime_cauchyCoeff {p j : α → ℕ} {fl : α → List ℕ} + {P R : α → List (List ℕ)} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hP : ComputableInPolyTime P encIn (encList (encList encNat))) + (hR : ComputableInPolyTime R encIn (encList (encList encNat))) + (hj : ComputableInPolyTime j encIn encUnary) : + ComputableInPolyTime (fun x => cauchyCoeff (p x) (fl x) (P x) (R x) (j x)) encIn + (encList encNat) := by + have hstep : ComputableInPolyTime (fun q : (α × List ℕ) × ℕ => + add (p q.1.1) (fl q.1.1) q.1.2 (mul (p q.1.1) (fl q.1.1) ((P q.1.1).getD q.2 []) + ((R q.1.1).getD (j q.1.1 - q.2) []))) + (encPair (encPair encIn (encList encNat)) encUnary) (encList encNat) := by + have hq := ComputableInPolyTime.fst (encPair encIn (encList encNat)) encUnary + have hx := (ComputableInPolyTime.fst encIn (encList encNat)).comp hq + have hacc := (ComputableInPolyTime.snd encIn (encList encNat)).comp hq + have hi := ComputableInPolyTime.snd (encPair encIn (encList encNat)) encUnary + exact computableInPolyTime_add (hp.comp hx) (hfl.comp hx) hacc + (computableInPolyTime_mul (hp.comp hx) (hfl.comp hx) (getDPolyTime (hP.comp hx) hi) + (getDPolyTime (hR.comp hx) (ComputableInPolyTime.unary_sub' (hj.comp hx) hi))) + exact ComputableInPolyTime.foldlWithInput_of_bounded_growth + (encA := encUnary) (encL := encList encUnary) + (step := fun x acc i => add (p x) (fl x) acc (mul (p x) (fl x) ((P x).getD i []) + ((R x).getD (j x - i) []))) + (computableInPolyTime_zero hfl) + ⟨ComputableInPolyTime.range.comp + (ComputableInPolyTime.unary_add' hj (ComputableInPolyTime.const 1)), + hstep, inferInstance, inferInstance⟩ + ⟨polyBounded_reducedLength hp hfl, fun x acc i _ => by + have := length_encList_le (length_add (p x) (fl x) acc (mul (p x) (fl x) ((P x).getD i []) + ((R x).getD (j x - i) []))) fun _ h => lt_of_mem_add h + omega⟩ + +/-- **Truncated products are polynomial-time**, with the precision and the length in unary. -/ +@[fun_prop] +theorem computableInPolyTime_tmul {p J : α → ℕ} {fl : α → List ℕ} {P R : α → List (List ℕ)} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hJ : ComputableInPolyTime J encIn encUnary) + (hP : ComputableInPolyTime P encIn (encList (encList encNat))) + (hR : ComputableInPolyTime R encIn (encList (encList encNat))) : + ComputableInPolyTime (fun x => tmul (p x) (fl x) (J x) (P x) (R x)) encIn + (encList (encList encNat)) := by + have h1 := ComputableInPolyTime.fst encIn encUnary + have h2 := ComputableInPolyTime.snd encIn encUnary + exact ComputableInPolyTime.mapWithInput (ComputableInPolyTime.range.comp hJ) + (computableInPolyTime_cauchyCoeff (hp.comp h1) (hfl.comp h1) (hP.comp h1) (hR.comp h1) h2) + +/-- Every coefficient of a truncated product is small. -/ +theorem small_of_mem_tmul {J : ℕ} {P R : List (List ℕ)} {c : List ℕ} + (hc : c ∈ tmul p fl J P R) : Small p fl c := by + obtain ⟨j, -, rfl⟩ := List.mem_map.mp hc + exact small_of_lt (length_cauchyCoeff P R j) fun _ h => lt_of_mem_cauchyCoeff h + +end PolyTime + end Cslib.GaloisRingArith diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean index dcc0b2ff21..35cced1e54 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean @@ -28,6 +28,9 @@ example : tmul 3 [1, 1] 2 [[1, 0], [1, 0]] [[1, 0], [1, 0]] = [[1, 0], [2, 0]] : -- Cancelling `2`: `6 + 2 U = 2 (3 + U)`. example : shiftDown 1 [6, 2] = [3, 1] := by decide +-- The capped `2`-adic valuation: `v₂ 12 = 2`, `v₂ 5 = 0`, and `0` at `0`. +example : (twoVal 4 12, twoVal 4 5, twoVal 4 0) = (2, 0, 0) := by decide + -- With `u = h = 1`, the odd nodes `t = ±1` have multiplicity `1` and `t = -2` has `0`. example : (mult 1 1 1 1, mult 1 1 1 3, mult 1 1 1 0) = (1, 1, 0) := by decide @@ -52,4 +55,10 @@ example : ((gamma 1 1 1 2 1 0 : Valuation.integer (Rat.padicValuation 2)) : ℚ) (node 1 1)).eval 0 * (-(node 1 1 : ℚ) / 2 ^ (1 + 1)) ^ 0 := coe_gamma (by norm_num) (by decide) +/-- The capped `2`-adic valuation is polynomial-time in unary. -/ +example : Turing.MultiTapeTM.ComputableInPolyTime (fun n : ℕ => twoVal n n) + Turing.MultiTapeTM.encUnary Turing.MultiTapeTM.encUnary := + computableInPolyTime_twoVal Turing.MultiTapeTM.ComputableInPolyTime.id' + Turing.MultiTapeTM.ComputableInPolyTime.id' + end ComplexityTests.TensorNetworkNativeRecovery From 62dc6f5b477b45ee214b469849e0c8181c6ef6cc Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 00:27:53 -0400 Subject: [PATCH 044/129] feat(circuit/frontier): evaluate packet entries on coefficient lists of A_p Native evaluation of the packet entries of the child networks (Theorem D.1, section D.4.6) on GaloisRingArith coefficient lists: kernel values by the product formula of Lemma D.4's evaluation, cross ratios by Newton inversion of an element that is one modulo two, local forms, mode coefficients and values, row terms, and coefficient extraction along a list of modes. val_packetEntryList proves the value in A_p is packetEntry at the reduction of Z_(2); map_val_packetEntryList transports it along any ring map, using that ring maps from Z_(2) are unique when 2 is nilpotent (padicInteger_ringHom_ext). The 2-adic valuation is computed by halving (twoVal), so the functions reduce; tests check tiny packets by decide. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../TensorNetwork/PacketEvaluator.lean | 639 ++++++++++++++++++ .../TensorNetworkPacketEvaluator.lean | 80 +++ 3 files changed, 720 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEvaluator.lean diff --git a/Complexity.lean b/Complexity.lean index faf65580db..652a35a911 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -884,6 +884,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCo public import Complexity.Computability.Circuit.Frontier.TensorNetwork.KernelProduct public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEntry +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEvaluator public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness public import Complexity.Computability.Circuit.Frontier.Unconditional public import Complexity.Computability.Circuit.Frontier.Witness diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean new file mode 100644 index 0000000000..93db7912a8 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean @@ -0,0 +1,639 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.KernelProduct +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEntry +public import Complexity.Computability.PolyTime.GaloisRingArithmetic +public import Complexity.Foundations.NumberTheory.Padics.RatValuation +import Mathlib.Algebra.BigOperators.Group.List.Basic +import Mathlib.Data.List.GetD + +/-! +# Evaluating packet entries on coefficient lists + +The packet tensors of the child networks of the contraction algorithm of [OpenAI2026ETH] are +implicit: an entry is computed from the data of its packet (§D.4.6, p. 63). This file gives that +computation on the coefficient lists of `A_p = (ℤ / 2 ^ p)[U] / (f)`, with the arithmetic of +`Cslib.GaloisRingArith`, and proves that it is `TensorNetwork.packetEntry` in `A_p`, so in every +ring that `A_p` maps to. + +* **Scalars.** `ℤ_(2)` reduces to `A_p` (`NativePacket.scalarHom`), and any two ring maps from + `ℤ_(2)` to a ring in which `2` is nilpotent agree (`padicInteger_ringHom_ext`). An integer is a + constant list (`NativePacket.intConst`). +* **Kernel values** (`NativePacket.kernelValueList`) follow Lemma D.4's evaluation + (`kernelValue_eq_prod`, p. 60): the inverse of the odd part of `C'(0)`, times + `2 ^ (a + 1 - v_d) ζ` when `d ≠ t`, times the `2 ^ (a + 1) - 2` linear factors + `u_q + 2 ^ (a + 1 - v_q) ζ`. The odd part of `C'(0)` is a product of odd integers, so it is `1` + modulo `2` and `GaloisRingArith.inv` inverts it. +* **Cross ratios** (`NativePacket.crossRatioList`) invert `u₀ + 2 ^ (a + 1 - v) ζ`, which is `1` + modulo `2` since `u₀` is odd and `v ≤ i ≤ a`. +* **Local forms, mode coefficients, mode values and row terms** follow + `TensorNetwork.packetLocalForm`, `TensorNetwork.packetModeCoeff`, + `TensorNetwork.packetModeValue` and `TensorNetwork.packetRowTerm`, with the modes `(y₀, b)` as + pairs of natural numbers. +* **Coefficient extraction** (`NativePacket.listCoeffList`) is the recursion + `TensorNetwork.listCoeff` on coefficient lists, along a duplicate-free list of modes. + +The main result (`NativePacket.val_packetEntryList`) is that the value of +`NativePacket.packetEntryList` in `A_p` is `TensorNetwork.packetEntry` at the reduction of +`ℤ_(2)` and the value of `ζ`. Transported along a ring map `ψ : A_p → S` with `2` nilpotent in +`S` (`NativePacket.map_val_packetEntryList`), it is the packet entry over `S` at any ring map +`ℤ_(2) → S`; with `TensorNetwork.child_tensor_inr_cauchyModeSplitting`, it is the entry of a +packet vertex of a child network, for instance over `B ⧸ (2 ^ p)`. + +These are functions on lists of natural numbers with explicit recursion; their certificates of +time `exp(O(L log s))` and polynomial space are separate. + +## Main definitions + +* `Cslib.Circuits.Frontier.NativePacket.scalarHom`: the reduction `ℤ_(2) → A_p`. +* `Cslib.Circuits.Frontier.NativePacket.kernelValueList`, + `Cslib.Circuits.Frontier.NativePacket.crossRatioList`: kernel values and cross ratios. +* `Cslib.Circuits.Frontier.NativePacket.listCoeffList`: coefficient extraction. +* `Cslib.Circuits.Frontier.NativePacket.packetEntryList`: the entry of a packet vertex. + +## Main results + +* `Cslib.Circuits.Frontier.padicInteger_ringHom_ext`: ring maps from `ℤ_(2)` are unique when `2` + is nilpotent. +* `Cslib.Circuits.Frontier.NativePacket.val_kernelValueList`, + `Cslib.Circuits.Frontier.NativePacket.val_crossRatioList`, + `Cslib.Circuits.Frontier.NativePacket.val_listCoeffList`: correctness of the parts. +* `Cslib.Circuits.Frontier.NativePacket.val_packetEntryList`, + `Cslib.Circuits.Frontier.NativePacket.map_val_packetEntryList`: packet entries. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Lemma D.4 and its + evaluation (pp. 59–60), §D.4.1 (p. 57) and §D.4.6 (p. 63). +-/ + +@[expose] public section + +namespace Cslib.Circuits.Frontier + +open Finset + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +/-- An odd natural number is a unit of a ring in which `2` is nilpotent. -/ +private theorem isUnit_natCast_of_odd {S : Type*} [CommRing S] {n : ℕ} (hn : (2 : S) ^ n = 0) + {m : ℕ} (hm : Odd m) : IsUnit (m : S) := by + obtain ⟨k, rfl⟩ := hm + rw [Nat.cast_add, Nat.cast_mul, Nat.cast_one, Nat.cast_ofNat, add_comm] + exact IsNilpotent.isUnit_one_add ⟨n, by rw [mul_pow, hn, zero_mul]⟩ + +/-- **Ring maps from `ℤ_(2)` are unique** into a ring in which `2` is nilpotent: an element +times its odd denominator is its numerator. -/ +theorem padicInteger_ringHom_ext {S : Type*} [CommRing S] {n : ℕ} (hn : (2 : S) ^ n = 0) + (φ ψ : ℤ₍₂₎ →+* S) : φ = ψ := by + ext x + have hden : ¬ 2 ∣ (x : ℚ).den := Rat.padicValuation_le_one_iff.mp x.2 + have h : x * ((x : ℚ).den : ℤ₍₂₎) = ((x : ℚ).num : ℤ₍₂₎) := + Subtype.ext (by push_cast; exact Rat.mul_den_eq_num _) + have h1 := congrArg φ h + have h2 := congrArg ψ h + rw [map_mul, map_natCast, map_intCast] at h1 h2 + exact (isUnit_natCast_of_odd hn (Nat.odd_iff.mpr (Nat.two_dvd_ne_zero.mp hden))).mul_right_cancel + (h1.trans h2.symm) + +namespace NativePacket + +open GaloisRingArith TensorNetwork + +variable {p : ℕ} {fl : List ℕ} + +/-! ### Scalars -/ + +/-- The reduction `ℤ_(2) → A_p`, through `ℤ / 2 ^ p`. -/ +noncomputable def scalarHom (p : ℕ) (fl : List ℕ) : ℤ₍₂₎ →+* Ring p fl := + (algebraMap (ZMod (2 ^ p)) (Ring p fl)).comp (Rat.padicIntegerToZModPow 2 p) + +/-- `2 ^ p = 0` in `A_p`. -/ +theorem two_pow_eq_zero : (2 : Ring p fl) ^ p = 0 := by + have h := natCast_two_pow (p := p) (fl := fl) + push_cast at h + exact h + +/-- With no coordinates, `A_p` is the zero ring. -/ +theorem subsingleton_of_length_eq_zero (h : fl.length = 0) : Subsingleton (Ring p fl) := by + set f := (monicOf fl).map (Int.castRingHom (ZMod (2 ^ p))) + have hf : f = 1 := by + simp only [f, List.length_eq_zero_iff.mp h, monicOf, List.length_nil, pow_zero, range_zero, + sum_empty, add_zero, Polynomial.map_one] + have h1 : AdjoinRoot.mk f f = AdjoinRoot.mk f 1 := congrArg _ hf + rw [AdjoinRoot.mk_self, map_one] at h1 + exact subsingleton_of_zero_eq_one h1 + +/-- The value of a tabulated list. -/ +theorem val_tabulate (g : ℕ → ℕ) : + val p fl ((List.range fl.length).map g) = + ∑ i ∈ range fl.length, (g i : Ring p fl) * AdjoinRoot.root _ ^ i := by + refine sum_congr rfl fun i hi => ?_ + rw [List.getD_eq_getElem _ _ (by simpa using hi), List.getElem_map, List.getElem_range] + +/-- An integer as a constant coefficient list: its residue modulo `2 ^ p` in the first of the `e` +coordinates. -/ +def intConst (p : ℕ) (fl : List ℕ) (n : ℤ) : List ℕ := + (List.range fl.length).map fun j => if j = 0 then (n % 2 ^ p).toNat else 0 + +/-- The value of a constant list is the integer. -/ +@[simp] theorem val_intConst (n : ℤ) : val p fl (intConst p fl n) = n := by + rcases Nat.eq_zero_or_pos fl.length with h | h + · have := subsingleton_of_length_eq_zero (p := p) h + exact Subsingleton.elim _ _ + rw [intConst, val_tabulate, sum_eq_single 0 (fun j _ hj => by simp [hj]) + (fun h0 => absurd (mem_range.mpr h) h0)] + have hnn : 0 ≤ n % 2 ^ p := Int.emod_nonneg _ (pow_ne_zero _ two_ne_zero) + have h2 : ((2 ^ p : ℤ) : Ring p fl) = 0 := by + rw [Int.cast_pow, Int.cast_ofNat]; exact two_pow_eq_zero + simp only [↓reduceIte, pow_zero, mul_one] + rw [show (((n % 2 ^ p).toNat : ℕ) : Ring p fl) = ((n % 2 ^ p : ℤ) : Ring p fl) by + rw [← Int.cast_natCast, Int.toNat_of_nonneg hnn]] + rw [Int.emod_def, Int.cast_sub, Int.cast_mul, h2, zero_mul, sub_zero] + +/-- The product of a list of coefficient lists. -/ +def prodList (p : ℕ) (fl : List ℕ) (cs : List (List ℕ)) : List ℕ := + cs.foldl (mul p fl) (one fl) + +/-- The sum of a list of coefficient lists. -/ +def sumList (p : ℕ) (fl : List ℕ) (cs : List (List ℕ)) : List ℕ := + cs.foldl (add p fl) (zero fl) + +private theorem val_foldl_mul (cs : List (List ℕ)) (c : List ℕ) : + val p fl (cs.foldl (mul p fl) c) = val p fl c * (cs.map (val p fl)).prod := by + induction cs generalizing c with + | nil => simp + | cons d cs ih => rw [List.foldl_cons, ih, val_mul, List.map_cons, List.prod_cons, mul_assoc] + +private theorem val_foldl_add (cs : List (List ℕ)) (c : List ℕ) : + val p fl (cs.foldl (add p fl) c) = val p fl c + (cs.map (val p fl)).sum := by + induction cs generalizing c with + | nil => simp + | cons d cs ih => rw [List.foldl_cons, ih, val_add, List.map_cons, List.sum_cons, add_assoc] + +@[simp] theorem val_prodList (cs : List (List ℕ)) : + val p fl (prodList p fl cs) = (cs.map (val p fl)).prod := by + rw [prodList, val_foldl_mul, val_one, one_mul] + +@[simp] theorem val_sumList (cs : List (List ℕ)) : + val p fl (sumList p fl cs) = (cs.map (val p fl)).sum := by + rw [sumList, val_foldl_add, val_zero, zero_add] + +/-! ### Inverting elements that are one modulo two -/ + +/-- Products of elements `1 + 2 x` have this form. -/ +private theorem exists_prod_eq_one_add_two_mul {R : Type*} [CommRing R] (l : List R) + (hl : ∀ y ∈ l, ∃ w, y = 1 + 2 * w) : ∃ w, l.prod = 1 + 2 * w := by + induction l with + | nil => exact ⟨0, by simp⟩ + | cons y l ih => + obtain ⟨u, hu⟩ := hl y List.mem_cons_self + obtain ⟨w, hw⟩ := ih fun z hz => hl z (List.mem_cons_of_mem _ hz) + exact ⟨u + w + 2 * u * w, by rw [List.prod_cons, hu, hw]; ring⟩ + +/-- **Inversion of an element that is one modulo two**: Newton's inversion needs no further +hypothesis. -/ +theorem val_mul_inv_of_eq (c : List ℕ) {w : Ring p fl} (h : val p fl c = 1 + 2 * w) : + val p fl c * val p fl (inv p fl c) = 1 := by + obtain ⟨y, hy⟩ := exists_prod_eq_one_add_two_mul + (List.replicate (2 ^ fl.length - 2) (val p fl c)) fun z hz => ⟨w, by + rw [List.eq_of_mem_replicate hz, h]⟩ + rw [List.prod_replicate] at hy + refine val_mul_inv c (w := w + y + 2 * w * y) ?_ + rw [hy, h] + ring + +/-- An inverse is unique. -/ +private theorem eq_of_mul_eq_one {R : Type*} [CommRing R] {a b c : R} (hb : a * b = 1) + (hc : c * a = 1) : b = c := by + calc b = (c * a) * b := by rw [hc, one_mul] + _ = c * (a * b) := mul_assoc _ _ _ + _ = c := by rw [hb, mul_one] + +/-- An odd integer is `1 + 2 w`. -/ +private theorem intCast_odd {R : Type*} [CommRing R] {u : ℤ} (hu : Odd u) : + ∃ w : R, (u : R) = 1 + 2 * w := by + obtain ⟨m, rfl⟩ := hu + exact ⟨m, by push_cast; ring⟩ + +/-! ### Odd parts -/ + +/-- An integer is `2 ^ v₂(n)` times its odd part. -/ +private theorem two_pow_mul_ediv (n : ℤ) : 2 ^ padicValInt 2 n * (n / 2 ^ padicValInt 2 n) = n := + Int.mul_ediv_cancel' (by exact_mod_cast padicValInt_dvd (p := 2) n) + +/-- The odd part of a nonzero integer is odd. -/ +theorem odd_ediv_two_pow {n : ℤ} (hn : n ≠ 0) : Odd (n / 2 ^ padicValInt 2 n) := by + rw [← Int.not_even_iff_odd, even_iff_two_dvd] + rintro ⟨c, hc⟩ + have h2 : (2 : ℤ) ^ (padicValInt 2 n + 1) ∣ n := ⟨c, by + calc n = 2 ^ padicValInt 2 n * (n / 2 ^ padicValInt 2 n) := (two_pow_mul_ediv n).symm + _ = 2 ^ (padicValInt 2 n + 1) * c := by rw [hc, pow_succ]; ring⟩ + have h := (padicValInt_dvd_iff (p := 2) (padicValInt 2 n + 1) n).mp (by exact_mod_cast h2) + omega + +/-- The odd part of a nonzero integer is nonzero. -/ +private theorem ediv_two_pow_ne_zero {n : ℤ} (hn : n ≠ 0) : n / 2 ^ padicValInt 2 n ≠ 0 := by + intro h + exact hn (by rw [← two_pow_mul_ediv n, h, mul_zero]) + +/-- An integer not divisible by `2 ^ (i + 1)` has valuation at most `i`. -/ +theorem padicValInt_le_of_not_dvd {n : ℤ} {i : ℕ} (hn : ¬ (2 : ℤ) ^ (i + 1) ∣ n) : + padicValInt 2 n ≤ i := by + by_contra h + exact hn ((pow_dvd_pow (2 : ℤ) (show i + 1 ≤ padicValInt 2 n by omega)).trans + (by exact_mod_cast padicValInt_dvd (p := 2) n)) + +/-! ### The `2`-adic valuation, computably -/ + +/-- The `2`-adic valuation of `m`, halving at most `k` times. -/ +def twoValAux : ℕ → ℕ → ℕ + | 0, _ => 0 + | k + 1, m => if m ≠ 0 ∧ m % 2 = 0 then twoValAux k (m / 2) + 1 else 0 + +/-- The `2`-adic valuation of an integer, by repeated halving: `padicValInt 2` (`twoVal_eq`). -/ +def twoVal (n : ℤ) : ℕ := twoValAux n.natAbs n.natAbs + +theorem twoValAux_eq : ∀ (k m : ℕ), m ≤ k → twoValAux k m = padicValNat 2 m + | 0, m, h => by rw [Nat.le_zero.mp h, twoValAux]; simp + | k + 1, m, h => by + rw [twoValAux] + split_ifs with hm + · obtain ⟨hm0, hm2⟩ := hm + have hm' : m = 2 * (m / 2) := by omega + rw [twoValAux_eq k (m / 2) (by omega)] + conv_rhs => rw [hm', padicValNat.mul two_ne_zero (by omega), padicValNat_self, add_comm] + · by_cases hm0 : m = 0 + · rw [hm0]; simp + · exact (padicValNat.eq_zero_of_not_dvd fun h2 => hm ⟨hm0, by omega⟩).symm + +/-- **The valuation by halving is `padicValInt 2`.** -/ +@[simp] theorem twoVal_eq (n : ℤ) : twoVal n = padicValInt 2 n := + twoValAux_eq _ _ le_rfl + +/-! ### Kernel values -/ + +/-- The linear factor `u + 2 ^ k ζ`. -/ +def linFactor (p : ℕ) (fl : List ℕ) (u : ℤ) (k : ℕ) (ζ : List ℕ) : List ℕ := + add p fl (intConst p fl u) (mul p fl (intConst p fl (2 ^ k)) ζ) + +@[simp] theorem val_linFactor (u : ℤ) (k : ℕ) (ζ : List ℕ) : + val p fl (linFactor p fl u k ζ) = (u : Ring p fl) + 2 ^ k * val p fl ζ := by + simp only [linFactor, val_add, val_mul, val_intConst] + push_cast + ring + +/-- A linear factor with an odd constant and `k ≥ 1` is one modulo two. -/ +theorem exists_val_linFactor_eq {u : ℤ} (hu : Odd u) {k : ℕ} (hk : 1 ≤ k) (ζ : List ℕ) : + ∃ w : Ring p fl, val p fl (linFactor p fl u k ζ) = 1 + 2 * w := by + obtain ⟨w, hw⟩ := intCast_odd (R := Ring p fl) hu + obtain ⟨k', rfl⟩ := Nat.exists_eq_add_of_le hk + exact ⟨w + 2 ^ k' * val p fl ζ, by rw [val_linFactor, hw, pow_add]; ring⟩ + +/-- The grid `[-2 ^ a, 2 ^ a)` of `2 ^ a` slots, in increasing order. -/ +def gridList (a : ℕ) : List ℤ := + (List.range (2 ^ (a + 1))).map fun j : ℕ => (j : ℤ) - ((2 ^ a : ℕ) : ℤ) + +theorem nodup_gridList (a : ℕ) : (gridList a).Nodup := + List.Nodup.map (fun x y h => by simpa using h) List.nodup_range + +theorem mem_gridList {a : ℕ} {q : ℤ} : q ∈ gridList a ↔ q ∈ cauchyGrid (2 ^ a) := by + rw [gridList, List.mem_map, mem_cauchyGrid, pow_succ] + generalize 2 ^ a = L + constructor + · rintro ⟨j, hj, rfl⟩ + rw [List.mem_range] at hj + omega + · rintro ⟨h1, h2⟩ + refine ⟨(q + L).toNat, List.mem_range.mpr ?_, ?_⟩ <;> omega + +/-- The odd part of `C'(0) = ∏_{q ≠ 0} (0 - q)`, as a coefficient list. -/ +def kernelUnitDen (p : ℕ) (fl : List ℕ) (a : ℕ) : List ℕ := + prodList p fl (((gridList a).filter (· ≠ 0)).map fun q => + intConst p fl ((0 - q) / 2 ^ twoVal (0 - q))) + +/-- `κ (kernelUnit a)`, the inverse of the odd part of `C'(0)`. -/ +def kernelUnitList (p : ℕ) (fl : List ℕ) (a : ℕ) : List ℕ := + inv p fl (kernelUnitDen p fl a) + +/-- The value of `kernelUnitDen`. -/ +private theorem val_kernelUnitDen (a : ℕ) : + val p fl (kernelUnitDen p fl a) = ∏ q ∈ (cauchyGrid (2 ^ a)).erase 0, + (((0 - q) / 2 ^ padicValInt 2 (0 - q) : ℤ) : Ring p fl) := by + simp only [kernelUnitDen, twoVal_eq] + rw [val_prodList, List.map_map, ← List.prod_toFinset _ + ((nodup_gridList a).filter _)] + refine prod_congr ?_ fun q _ => val_intConst _ + ext q + simp only [List.mem_toFinset, List.mem_filter, mem_gridList, mem_erase, decide_eq_true_eq] + tauto + +/-- **The kernel unit**: `kernelUnitList` is the reduction of `kernelUnit a`. -/ +theorem val_kernelUnitList (a : ℕ) : + val p fl (kernelUnitList p fl a) = scalarHom p fl (kernelUnit a) := by + have hne (q : ℤ) (hq : q ∈ (cauchyGrid (2 ^ a)).erase 0) : (0 : ℤ) - q ≠ 0 := + sub_ne_zero.mpr (Ne.symm (ne_of_mem_erase hq)) + -- The odd part of `C'(0)` is one modulo two. + obtain ⟨w, hw⟩ := exists_prod_eq_one_add_two_mul + (((cauchyGrid (2 ^ a)).erase 0).toList.map fun q => + (((0 - q) / 2 ^ padicValInt 2 (0 - q) : ℤ) : Ring p fl)) fun y hy => by + obtain ⟨q, hq, rfl⟩ := List.mem_map.mp hy + exact intCast_odd (odd_ediv_two_pow (hne q (Finset.mem_toList.mp hq))) + rw [Finset.prod_map_toList] at hw + have hinv := val_mul_inv_of_eq (kernelUnitDen p fl a) (w := w) (by rw [val_kernelUnitDen, hw]) + refine eq_of_mul_eq_one hinv ?_ + -- `kernelUnit a` inverts the odd part of `C'(0)` in `ℤ_(2)`. + have hunit : kernelUnit a * ∏ q ∈ (cauchyGrid (2 ^ a)).erase 0, + (((0 - q) / 2 ^ padicValInt 2 (0 - q) : ℤ) : ℤ₍₂₎) = 1 := by + refine Subtype.val_injective ?_ + change (ℤ₍₂₎).subtype (kernelUnit a * _) = (ℤ₍₂₎).subtype 1 + rw [map_mul, map_prod, map_one] + change ((kernelUnit a : ℤ₍₂₎) : ℚ) * _ = 1 + rw [coe_kernelUnit] + simp only [map_intCast] + refine inv_mul_cancel₀ (prod_ne_zero_iff.mpr fun q hq => ?_) + exact_mod_cast ediv_two_pow_ne_zero (hne q hq) + rw [val_kernelUnitDen] + have := congrArg (scalarHom p fl) hunit + rw [map_mul, map_prod, map_one] at this + simpa only [map_intCast] using this + +/-- **Kernel values on coefficient lists** (Lemma D.4's evaluation, p. 60): the kernel unit, +times `2 ^ (a + 1 - v_d) ζ` when `d ≠ t`, times the linear factors `u_q + 2 ^ (a + 1 - v_q) ζ` +over the grid points `q ≠ d, t`. -/ +def kernelValueList (p : ℕ) (fl : List ℕ) (a : ℕ) (t d : ℤ) (ζ : List ℕ) : List ℕ := + mul p fl (mul p fl (kernelUnitList p fl a) + (if d = t then one fl else + mul p fl (intConst p fl (2 ^ (a + 1 - twoVal (t - d)))) ζ)) + (prodList p fl (((gridList a).filter fun q => q ≠ d ∧ q ≠ t).map fun q => + linFactor p fl ((t - q) / 2 ^ twoVal (t - q)) (a + 1 - twoVal (t - q)) ζ)) + +/-- **Kernel values are correct**: for grid points `t` and `d`, the value is +`kernelValue` at the reduction of `ℤ_(2)` and the value of `ζ`. -/ +theorem val_kernelValueList {a : ℕ} {t d : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (hd : d ∈ cauchyGrid (2 ^ a)) (ζ : List ℕ) : + val p fl (kernelValueList p fl a t d ζ) = kernelValue (scalarHom p fl) (val p fl ζ) a t d := by + simp only [kernelValueList, twoVal_eq] + rw [kernelValue_eq_prod _ _ ht hd, val_mul, val_mul, val_kernelUnitList, + val_prodList, List.map_map, ← List.prod_toFinset _ ((nodup_gridList a).filter _)] + congr 1 + · congr 1 + split_ifs + · exact val_one + · rw [val_mul, val_intConst]; push_cast; rfl + · refine prod_congr ?_ fun q _ => val_linFactor _ _ _ + ext q + simp only [List.mem_toFinset, List.mem_filter, mem_gridList, mem_erase, decide_eq_true_eq] + tauto + +/-! ### Cross ratios -/ + +/-- **Cross ratios on coefficient lists**: `2 ^ (i + 1 - v)` times the inverse of +`u₀ + 2 ^ (a + 1 - v) ζ`, where `t - d₀ = 2 ^ v u₀`. -/ +def crossRatioList (p : ℕ) (fl : List ℕ) (a i : ℕ) (t d₀ : ℤ) (ζ : List ℕ) : List ℕ := + mul p fl (intConst p fl (2 ^ (i + 1 - twoVal (t - d₀)))) + (inv p fl (linFactor p fl ((t - d₀) / 2 ^ twoVal (t - d₀)) (a + 1 - twoVal (t - d₀)) ζ)) + +/-- **Cross ratios are correct** when `2 ^ (i + 1) ∤ t - d₀` and `i ≤ a`: the inverted factor is +one modulo two. -/ +theorem val_crossRatioList {a i : ℕ} (hia : i ≤ a) {t d₀ : ℤ} + (hcross : ¬ (2 : ℤ) ^ (i + 1) ∣ t - d₀) (ζ : List ℕ) : + val p fl (crossRatioList p fl a i t d₀ ζ) = crossRatio a i t d₀ (val p fl ζ) := by + have hv := padicValInt_le_of_not_dvd hcross + have hn : t - d₀ ≠ 0 := by rintro h; exact hcross (h ▸ dvd_zero _) + obtain ⟨w, hw⟩ := exists_val_linFactor_eq (p := p) (fl := fl) (odd_ediv_two_pow hn) + (show 1 ≤ a + 1 - padicValInt 2 (t - d₀) by omega) ζ + have hinv := val_mul_inv_of_eq _ hw + have hU := IsUnit.of_mul_eq_one _ hinv + simp only [crossRatioList, twoVal_eq] + rw [crossRatio, val_mul, val_intConst, ← val_linFactor] + congr 1 + · push_cast; rfl + · calc val p fl (inv p fl _) = (Ring.inverse (val p fl _) * val p fl _) * val p fl (inv p fl _) := + by rw [Ring.inverse_mul_cancel _ hU, one_mul] + _ = Ring.inverse (val p fl _) := by rw [mul_assoc, hinv, mul_one] + +/-! ### The terms of a packet entry -/ + +/-- The local form of a row port in slot `x`: the sum of the kernel values at the column ports +set to one. -/ +def localFormList (p : ℕ) (fl : List ℕ) (a : ℕ) (t : ℤ) (cols : List (ℕ × Bool)) (x : ℕ) + (ζ : List ℕ) : List ℕ := + sumList p fl (cols.map fun c => if c.2 then kernelValueList p fl a t ((c.1 : ℤ) - x) ζ + else zero fl) + +theorem val_localFormList {a : ℕ} {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + {cols : List (ℕ × Bool)} (hcols : ∀ c ∈ cols, c.1 < 2 ^ a) {x : ℕ} (hx : x < 2 ^ a) + (ζ : List ℕ) : + val p fl (localFormList p fl a t cols x ζ) = + packetLocalForm (scalarHom p fl) (val p fl ζ) a t cols x := by + rw [localFormList, val_sumList, List.map_map, packetLocalForm] + congr 1 + refine List.map_congr_left fun c hc => ?_ + simp only [Function.comp_apply] + split_ifs + · exact val_kernelValueList ht (sub_mem_cauchyGrid hx (hcols c hc)) ζ + · exact val_zero + +/-- The coefficient of the mode `(y₀, b)` in the form of a row port in slot `x`. -/ +def modeCoeffList (p : ℕ) (fl : List ℕ) (a i : ℕ) (t : ℤ) (differ : Bool) (x y₀ b : ℕ) + (ζ : List ℕ) : List ℕ := + if differ = true ∧ x.testBit i = y₀.testBit i then + mul p fl (kernelValueList p fl a t ((y₀ : ℤ) - x) ζ) + (pow p fl (crossRatioList p fl a i t ((y₀ : ℤ) - x) ζ) b) + else zero fl + +theorem val_modeCoeffList {a i : ℕ} (hia : i < a) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (hti : (2 : ℤ) ^ i ∣ t) (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) (differ : Bool) {x : ℕ} + (hx : x < 2 ^ a) {nb : ℕ} (Z : Fin (2 ^ (i + 1)) × Fin nb) (ζ : List ℕ) : + val p fl (modeCoeffList p fl a i t differ x Z.1 Z.2 ζ) = + packetModeCoeff (scalarHom p fl) (val p fl ζ) a i t differ x Z := by + have hy : (Z.1 : ℕ) < 2 ^ a := + Z.1.isLt.trans_le (Nat.pow_le_pow_right (by norm_num) (by omega)) + rw [modeCoeffList, packetModeCoeff] + split_ifs with h + · rw [val_mul, val_pow, val_kernelValueList ht (sub_mem_cauchyGrid hx hy), + val_crossRatioList hia.le (not_dvd_sub_sub_of_testBit_eq hti hti' Z.1.isLt h.2)] + · exact val_zero + +/-- The value of the mode `(y₀, b)` on the column ports: the sum of `j ^ b` over the column ports +in slots `y₀ + 2 ^ (i + 1) j` set to one, when the symbols of the packet differ. -/ +def modeValueList (p : ℕ) (fl : List ℕ) (i : ℕ) (differ : Bool) (cols : List (ℕ × Bool)) + (y₀ b : ℕ) : List ℕ := + sumList p fl (cols.map fun c => + if c.2 = true ∧ differ = true ∧ c.1 % 2 ^ (i + 1) = y₀ then + pow p fl (intConst p fl ((c.1 / 2 ^ (i + 1) : ℕ) : ℤ)) b + else zero fl) + +theorem val_modeValueList (i : ℕ) (differ : Bool) (cols : List (ℕ × Bool)) {nb : ℕ} + (Z : Fin (2 ^ (i + 1)) × Fin nb) : + val p fl (modeValueList p fl i differ cols Z.1 Z.2) = + packetModeValue (Ring p fl) i differ cols Z := by + rw [modeValueList, val_sumList, List.map_map, packetModeValue] + congr 1 + refine List.map_congr_left fun c _ => ?_ + simp only [Function.comp_apply] + split_ifs + · rw [val_pow, val_intConst, Int.cast_natCast] + · exact val_zero + +/-- The terms of the affine factor of a row port `r = (x, Y_x)`: its local term at `none`, and +the coefficient of the mode `(y₀, b)` at `some (y₀, b)`. -/ +def rowTermList (p : ℕ) (fl : List ℕ) (a i : ℕ) (t : ℤ) (differ : Bool) + (cols : List (ℕ × Bool)) (ζ : List ℕ) (r : ℕ × Bool) : Option (ℕ × ℕ) → List ℕ + | none => if r.2 then localFormList p fl a t cols r.1 ζ + else sub p fl (one fl) (localFormList p fl a t cols r.1 ζ) + | some Z => if r.2 then modeCoeffList p fl a i t differ r.1 Z.1 Z.2 ζ + else neg p fl (modeCoeffList p fl a i t differ r.1 Z.1 Z.2 ζ) + +/-- The modes `(y₀, b)` with `y₀ < 2 ^ (i + 1)` and `b < nb`, as pairs. -/ +def modeEmbedding (i nb : ℕ) : Fin (2 ^ (i + 1)) × Fin nb ↪ ℕ × ℕ := + ⟨fun Z => (Z.1, Z.2), fun Z Z' h => by + simp only [Prod.mk.injEq] at h + exact Prod.ext (Fin.ext h.1) (Fin.ext h.2)⟩ + +@[simp] theorem modeEmbedding_apply (i nb : ℕ) (Z : Fin (2 ^ (i + 1)) × Fin nb) : + modeEmbedding i nb Z = ((Z.1 : ℕ), (Z.2 : ℕ)) := + rfl + +/-- Row terms are correct, at the modes of the packet. -/ +theorem val_rowTermList {a i : ℕ} (hia : i < a) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (hti : (2 : ℤ) ^ i ∣ t) (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) (differ : Bool) + {cols : List (ℕ × Bool)} (hcols : ∀ c ∈ cols, c.1 < 2 ^ a) {r : ℕ × Bool} (hr : r.1 < 2 ^ a) + (nb : ℕ) (ζ : List ℕ) : + (fun o => val p fl (rowTermList p fl a i t differ cols ζ r o)) ∘ + Option.map (modeEmbedding i nb) = + packetRowTerm (p := nb) (scalarHom p fl) (val p fl ζ) a i t differ cols r := by + funext o + cases o with + | none => + simp only [Function.comp_apply, Option.map_none, rowTermList, packetRowTerm, affineFactor] + split_ifs + · exact val_localFormList ht hcols hr ζ + · rw [val_sub, val_one, val_localFormList ht hcols hr ζ] + | some Z => + simp only [Function.comp_apply, Option.map_some, rowTermList, packetRowTerm, affineSign, + modeEmbedding_apply] + have h := val_modeCoeffList (p := p) (fl := fl) hia ht hti hti' differ hr Z ζ + split_ifs + · rw [one_mul]; exact h + · rw [val_neg, h, neg_one_mul] + +/-! ### Coefficient extraction -/ + +/-- **Coefficient extraction on coefficient lists**, `TensorNetwork.listCoeff` along a list of +modes: the first factor contributes its local term, or one of the modes still needed. -/ +def listCoeffList (p : ℕ) (fl : List ℕ) {M : Type} [DecidableEq M] (modes : List M) : + List (Option M → List ℕ) → (M → ℕ) → List ℕ + | [], γ => if ∀ Z ∈ modes, γ Z = 0 then one fl else zero fl + | T :: ts, γ => add p fl (mul p fl (T none) (listCoeffList p fl modes ts γ)) + (sumList p fl (modes.map fun Z => if γ Z = 0 then zero fl else + mul p fl (T (some Z)) (listCoeffList p fl modes ts (Function.update γ Z (γ Z - 1))))) + +/-- **Coefficient extraction is correct**, along a duplicate-free list of modes. -/ +theorem val_listCoeffList {M : Type} [DecidableEq M] {modes : List M} (hm : modes.Nodup) : + ∀ (ts : List (Option M → List ℕ)) (γ : M → ℕ), + val p fl (listCoeffList p fl modes ts γ) = + TensorNetwork.listCoeff modes.toFinset (ts.map fun T o => val p fl (T o)) γ + | [], γ => by + rw [listCoeffList, List.map_nil, TensorNetwork.listCoeff_nil] + simp only [List.mem_toFinset] + split_ifs + · exact val_one + · exact val_zero + | T :: ts, γ => by + rw [listCoeffList, List.map_cons, TensorNetwork.listCoeff_cons, val_add, val_mul, + val_listCoeffList hm ts γ, val_sumList, List.map_map, ← List.sum_toFinset _ hm] + congr 1 + refine sum_congr rfl fun Z _ => ?_ + simp only [Function.comp_apply] + split_ifs + · exact val_zero + · rw [val_mul, val_listCoeffList hm ts] + +/-! ### Packet entries -/ + +/-- The modes `(y₀, b)` with `y₀ < 2 ^ (i + 1)` and `b < nb`, in lexicographic order. -/ +def modeList (i nb : ℕ) : List (ℕ × ℕ) := + List.range (2 ^ (i + 1)) ×ˢ List.range nb + +theorem nodup_modeList (i nb : ℕ) : (modeList i nb).Nodup := + List.nodup_range.product List.nodup_range + +theorem mem_modeList {i nb : ℕ} {Z : ℕ × ℕ} : Z ∈ modeList i nb ↔ Z.1 < 2 ^ (i + 1) ∧ Z.2 < nb := by + rw [modeList, List.mem_product, List.mem_range, List.mem_range] + +/-- The modes of `modeList` with a property of the tile, as the image of those of the packet. -/ +private theorem toFinset_filter_modeList (i nb : ℕ) (P : ℕ → Bool) : + ((modeList i nb).filter fun Z => P Z.1).toFinset = + ({Z | P Z.1 = true} : Finset (Fin (2 ^ (i + 1)) × Fin nb)).map (modeEmbedding i nb) := by + ext ⟨y, b⟩ + simp only [List.mem_toFinset, List.mem_filter, mem_modeList, mem_map, mem_filter, mem_univ, + true_and, modeEmbedding_apply] + constructor + · rintro ⟨⟨hy, hb⟩, h⟩ + exact ⟨(⟨y, hy⟩, ⟨b, hb⟩), h, rfl⟩ + · rintro ⟨Z, h, hZ⟩ + simp only [Prod.mk.injEq] at hZ + obtain ⟨rfl, rfl⟩ := hZ + exact ⟨⟨Z.1.isLt, Z.2.isLt⟩, h⟩ + +/-- **The entry of a packet vertex on coefficient lists** (§D.4.6, p. 63): coefficient extraction +along the row terms, at the exponents `β` of the modes targeting the group, times the powers of +the values of the modes with the group as source. The modes are the pairs `(y₀, b)`. -/ +def packetEntryList (p : ℕ) (fl : List ℕ) (a i : ℕ) (t : ℤ) (nb : ℕ) (e₁ e₂ differ : Bool) + (β : ℕ × ℕ → ℕ) (rows cols : List (ℕ × Bool)) (ζ : List ℕ) : List ℕ := + mul p fl + (listCoeffList p fl ((modeList i nb).filter fun Z => modeTargets i e₁ e₂ Z.1) + (rows.map (rowTermList p fl a i t differ cols ζ)) β) + (prodList p fl (((modeList i nb).filter fun Z => modeSources i e₁ e₂ Z.1).map fun Z => + pow p fl (modeValueList p fl i differ cols Z.1 Z.2) (β Z))) + +/-- **Packet entries are correct.** For a grid point `t` of valuation `i < a` and ports in slots +below `2 ^ a`, the value of `packetEntryList` in `A_p` is `packetEntry` at the reduction of +`ℤ_(2)`, the value of `ζ` and the exponents `β` on the modes of the packet. -/ +theorem val_packetEntryList {a i : ℕ} (hia : i < a) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) + (hti : (2 : ℤ) ^ i ∣ t) (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) (nb : ℕ) (e₁ e₂ differ : Bool) + (β : ℕ × ℕ → ℕ) {rows cols : List (ℕ × Bool)} (hrows : ∀ r ∈ rows, r.1 < 2 ^ a) + (hcols : ∀ c ∈ cols, c.1 < 2 ^ a) (ζ : List ℕ) : + val p fl (packetEntryList p fl a i t nb e₁ e₂ differ β rows cols ζ) = + packetEntry (p := nb) (scalarHom p fl) (val p fl ζ) a i t e₁ e₂ differ + (fun Z => β (Z.1, Z.2)) rows cols := by + rw [packetEntryList, packetEntry, val_mul] + congr 1 + · rw [val_listCoeffList ((nodup_modeList i nb).filter _), toFinset_filter_modeList, + TensorNetwork.listCoeff_map, List.map_map, List.map_map] + refine congrArg₂ _ (List.map_congr_left fun r hr => ?_) rfl + exact val_rowTermList hia ht hti hti' differ hcols (hrows r hr) nb ζ + · rw [val_prodList, List.map_map, ← List.prod_toFinset _ ((nodup_modeList i nb).filter _), + toFinset_filter_modeList, prod_map] + refine prod_congr rfl fun Z _ => ?_ + simp only [Function.comp_apply, modeEmbedding_apply] + rw [val_pow, val_modeValueList] + +/-- **Packet entries in every ring receiving `A_p`.** Along a ring map `ψ : A_p → S` with `2` +nilpotent in `S`, the value of `packetEntryList` is `packetEntry` over `S` at any ring map +`κ : ℤ_(2) → S` and at the image of `ζ`. -/ +theorem map_val_packetEntryList {S : Type*} [CommRing S] (ψ : Ring p fl →+* S) {n : ℕ} + (hn : (2 : S) ^ n = 0) (κ : ℤ₍₂₎ →+* S) {a i : ℕ} (hia : i < a) {t : ℤ} + (ht : t ∈ cauchyGrid (2 ^ a)) (hti : (2 : ℤ) ^ i ∣ t) (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) + (nb : ℕ) (e₁ e₂ differ : Bool) (β : ℕ × ℕ → ℕ) {rows cols : List (ℕ × Bool)} + (hrows : ∀ r ∈ rows, r.1 < 2 ^ a) (hcols : ∀ c ∈ cols, c.1 < 2 ^ a) (ζ : List ℕ) : + ψ (val p fl (packetEntryList p fl a i t nb e₁ e₂ differ β rows cols ζ)) = + packetEntry (p := nb) κ (ψ (val p fl ζ)) a i t e₁ e₂ differ (fun Z => β (Z.1, Z.2)) + rows cols := by + rw [val_packetEntryList hia ht hti hti' nb e₁ e₂ differ β hrows hcols ζ, + map_packetEntry ψ two_pow_eq_zero _ _ hia.le hti hti', + padicInteger_ringHom_ext hn (ψ.comp (scalarHom p fl)) κ] + +end NativePacket + +end Cslib.Circuits.Frontier diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEvaluator.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEvaluator.lean new file mode 100644 index 0000000000..2094374c50 --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEvaluator.lean @@ -0,0 +1,80 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEvaluator + +/-! +# Tests for packet entries on coefficient lists + +Tiny packets of `2 ^ 1` slots over `A_3 = (ℤ / 8)[U] / (U ^ 2 + U + 1)`, at the grid point `t = 1` +of valuation `i = 0`. The kernel unit and one kernel value are checked by hand; the packet entry +with one row port and zero exponents is the local form, a single kernel value; and the +correctness theorem applies to these packets. +-/ + +@[expose] public section + +namespace ComplexityTests.TensorNetworkPacketEvaluator + +open Cslib Cslib.Circuits.Frontier Cslib.Circuits.Frontier.NativePacket + Cslib.Circuits.Frontier.TensorNetwork + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +-- The valuation by halving. +example : twoVal 12 = 2 ∧ twoVal (-8) = 3 ∧ twoVal 0 = 0 := by decide + +-- The grid of `2 ^ 1` slots is `-2, -1, 0, 1`; the odd parts of `0 - q` for `q ≠ 0` are +-- `1, 1, -1`, so `C'(0)` has odd part `-1`, and its inverse is `-1 ≡ 7`. +example : kernelUnitList 3 [1, 1] 1 = [7, 0] := by decide + +-- `W_1(1 + 4 U)`: the factors for `q = -2, -1, 0` are `3 + 4 U`, `1 + 2 U` and `1 + 4 U`, whose +-- product is `3 + 6 U` modulo `8`; times `-1` it is `5 + 2 U`. +example : kernelValueList 3 [1, 1] 1 1 1 [0, 1] = [5, 2] := by decide + +-- For `d ≠ t` the kernel value has the factor `2 ^ (a + 1 - v_d) ζ`, which vanishes at `ζ = 0`. +example : kernelValueList 3 [1, 1] 1 1 0 [0, 0] = [0, 0] := by decide + +-- The cross ratio at `d₀ = 0` and `ζ = 1`: `2` times the inverse of `1 + 4 = 5`, which is `5` +-- modulo `8`, so `10 ≡ 2`. +example : crossRatioList 3 [1, 1] 1 0 1 0 [1, 0] = [2, 0] := by decide + +-- One row port in slot `0` set to one, one column port in slot `1` set to one, and zero +-- exponents: the entry is the local form `W_{1 - 0}(T)`. +example : packetEntryList 3 [1, 1] 1 0 1 2 true false true (fun _ => 0) [(0, true)] [(1, true)] + [0, 1] = kernelValueList 3 [1, 1] 1 1 1 [0, 1] := by + decide + +-- With the row port set to zero, the entry is `1 - W_1(T)`. +example : packetEntryList 3 [1, 1] 1 0 1 2 true false true (fun _ => 0) [(0, false)] [(1, true)] + [0, 1] = GaloisRingArith.sub 3 [1, 1] (GaloisRingArith.one [1, 1]) + (kernelValueList 3 [1, 1] 1 1 1 [0, 1]) := by + decide + +/-- The hypotheses of the correctness theorem hold for these packets. -/ +theorem val_packetEntryList_tiny (β : ℕ × ℕ → ℕ) (ζ : List ℕ) : + GaloisRingArith.val 3 [1, 1] (packetEntryList 3 [1, 1] 1 0 1 2 true false true β + [(0, true), (1, false)] [(1, true), (0, true)] ζ) = + packetEntry (p := 2) (scalarHom 3 [1, 1]) (GaloisRingArith.val 3 [1, 1] ζ) 1 0 1 true false + true (fun Z => β (Z.1, Z.2)) [(0, true), (1, false)] [(1, true), (0, true)] := + val_packetEntryList (by norm_num) (by simp [mem_cauchyGrid]) (by norm_num) (by norm_num) 2 true + false true β (by simp) (by simp) ζ + +-- Transported to any ring with `2` nilpotent, at any ring map from `ℤ_(2)`. +example {S : Type} [CommRing S] (ψ : GaloisRingArith.Ring 3 [1, 1] →+* S) {n : ℕ} + (hn : (2 : S) ^ n = 0) (κ : ℤ₍₂₎ →+* S) (β : ℕ × ℕ → ℕ) (ζ : List ℕ) : + ψ (GaloisRingArith.val 3 [1, 1] (packetEntryList 3 [1, 1] 1 0 1 2 true false true β + [(0, true)] [(1, true)] ζ)) = + packetEntry (p := 2) κ (ψ (GaloisRingArith.val 3 [1, 1] ζ)) 1 0 1 true false true + (fun Z => β (Z.1, Z.2)) [(0, true)] [(1, true)] := + map_val_packetEntryList ψ hn κ (by norm_num) (by simp [mem_cauchyGrid]) (by norm_num) + (by norm_num) 2 true false true β (by simp) (by simp) ζ + +end ComplexityTests.TensorNetworkPacketEvaluator From 6f54c2ddb83a021f4f67304dc31259d580b0311f Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 00:30:46 -0400 Subject: [PATCH 045/129] fix(circuit/frontier): terminal rewrites in twoVal_eq Replace the two non-terminal `simp` calls flagged by source hygiene with explicit rewrites. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Circuit/Frontier/TensorNetwork/NativeRecovery.lean | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean index 34d46b1938..daa823e54c 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean @@ -145,10 +145,11 @@ theorem twoVal_eq {b d : ℕ} (hd : d < 2 ^ b) : twoVal b d = padicValNat 2 d := have hiff : d % 2 ^ (m + 1) = 0 ↔ m + 1 ≤ v := by rw [← Nat.dvd_iff_mod_eq_zero] exact padicValNat_dvd_iff_le h0 + rw [List.filter_singleton] by_cases hm : m + 1 ≤ v - · simp [hiff.mpr hm] + · rw [decide_eq_true (hiff.mpr hm), Bool.cond_true, List.length_singleton] omega - · simp [mt hiff.mp hm] + · rw [decide_eq_false (mt hiff.mp hm), Bool.cond_false, List.length_nil] omega rw [key, min_eq_right hvb.le] From 04fe069c8e48cbd5351a146c239d22540cce8a07 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 00:30:50 -0400 Subject: [PATCH 046/129] refactor(circuit/frontier): packet modes by dense index y0 * nb + b Read the exponents of the packet modes at dense indices, matching a dense beta-slice in the packet descriptor, so coefficient extraction runs over a list of indices with a dense exponent list. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/PacketEvaluator.lean | 134 ++++++++++-------- .../TensorNetworkPacketEvaluator.lean | 15 +- 2 files changed, 83 insertions(+), 66 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean index 93db7912a8..e9e8449c56 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean @@ -35,8 +35,8 @@ ring that `A_p` maps to. modulo `2` since `u₀` is odd and `v ≤ i ≤ a`. * **Local forms, mode coefficients, mode values and row terms** follow `TensorNetwork.packetLocalForm`, `TensorNetwork.packetModeCoeff`, - `TensorNetwork.packetModeValue` and `TensorNetwork.packetRowTerm`, with the modes `(y₀, b)` as - pairs of natural numbers. + `TensorNetwork.packetModeValue` and `TensorNetwork.packetRowTerm`, with a mode `(y₀, b)` given + by its dense index `y₀ nb + b` (`NativePacket.modeIndex`). * **Coefficient extraction** (`NativePacket.listCoeffList`) is the recursion `TensorNetwork.listCoeff` on coefficient lists, along a duplicate-free list of modes. @@ -485,32 +485,45 @@ theorem val_modeValueList (i : ℕ) (differ : Bool) (cols : List (ℕ × Bool)) · rw [val_pow, val_intConst, Int.cast_natCast] · exact val_zero +/-- The tile of a dense mode index `y₀ nb + b`. -/ +theorem modeIndex_div {nb y b : ℕ} (hb : b < nb) : (y * nb + b) / nb = y := by + rw [Nat.add_comm, Nat.add_mul_div_right _ _ (by omega), Nat.div_eq_of_lt hb, Nat.zero_add] + +/-- The exponent of a dense mode index `y₀ nb + b`. -/ +theorem modeIndex_mod {nb y b : ℕ} (hb : b < nb) : (y * nb + b) % nb = b := by + rw [Nat.add_comm, Nat.add_mul_mod_self_right, Nat.mod_eq_of_lt hb] + +/-- The dense index `y₀ nb + b` of the mode `(y₀, b)` of a packet with `nb` exponents per +tile. -/ +def modeIndex (i nb : ℕ) : Fin (2 ^ (i + 1)) × Fin nb ↪ ℕ := + ⟨fun Z => Z.1 * nb + Z.2, fun Z Z' h => by + have h1 := modeIndex_div (y := Z.1) Z.2.isLt + have h2 := modeIndex_mod (y := Z.1) Z.2.isLt + simp only at h + rw [h, modeIndex_div Z'.2.isLt] at h1 + rw [h, modeIndex_mod Z'.2.isLt] at h2 + exact Prod.ext (Fin.ext h1.symm) (Fin.ext h2.symm)⟩ + +@[simp] theorem modeIndex_apply (i nb : ℕ) (Z : Fin (2 ^ (i + 1)) × Fin nb) : + modeIndex i nb Z = (Z.1 : ℕ) * nb + Z.2 := + rfl + /-- The terms of the affine factor of a row port `r = (x, Y_x)`: its local term at `none`, and -the coefficient of the mode `(y₀, b)` at `some (y₀, b)`. -/ -def rowTermList (p : ℕ) (fl : List ℕ) (a i : ℕ) (t : ℤ) (differ : Bool) - (cols : List (ℕ × Bool)) (ζ : List ℕ) (r : ℕ × Bool) : Option (ℕ × ℕ) → List ℕ +the coefficient of the mode `(k / nb, k % nb)` at `some k`. -/ +def rowTermList (p : ℕ) (fl : List ℕ) (a i : ℕ) (t : ℤ) (nb : ℕ) (differ : Bool) + (cols : List (ℕ × Bool)) (ζ : List ℕ) (r : ℕ × Bool) : Option ℕ → List ℕ | none => if r.2 then localFormList p fl a t cols r.1 ζ else sub p fl (one fl) (localFormList p fl a t cols r.1 ζ) - | some Z => if r.2 then modeCoeffList p fl a i t differ r.1 Z.1 Z.2 ζ - else neg p fl (modeCoeffList p fl a i t differ r.1 Z.1 Z.2 ζ) - -/-- The modes `(y₀, b)` with `y₀ < 2 ^ (i + 1)` and `b < nb`, as pairs. -/ -def modeEmbedding (i nb : ℕ) : Fin (2 ^ (i + 1)) × Fin nb ↪ ℕ × ℕ := - ⟨fun Z => (Z.1, Z.2), fun Z Z' h => by - simp only [Prod.mk.injEq] at h - exact Prod.ext (Fin.ext h.1) (Fin.ext h.2)⟩ - -@[simp] theorem modeEmbedding_apply (i nb : ℕ) (Z : Fin (2 ^ (i + 1)) × Fin nb) : - modeEmbedding i nb Z = ((Z.1 : ℕ), (Z.2 : ℕ)) := - rfl + | some k => if r.2 then modeCoeffList p fl a i t differ r.1 (k / nb) (k % nb) ζ + else neg p fl (modeCoeffList p fl a i t differ r.1 (k / nb) (k % nb) ζ) -/-- Row terms are correct, at the modes of the packet. -/ +/-- Row terms are correct, at the dense indices of the modes of the packet. -/ theorem val_rowTermList {a i : ℕ} (hia : i < a) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) - (hti : (2 : ℤ) ^ i ∣ t) (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) (differ : Bool) + (hti : (2 : ℤ) ^ i ∣ t) (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) (nb : ℕ) (differ : Bool) {cols : List (ℕ × Bool)} (hcols : ∀ c ∈ cols, c.1 < 2 ^ a) {r : ℕ × Bool} (hr : r.1 < 2 ^ a) - (nb : ℕ) (ζ : List ℕ) : - (fun o => val p fl (rowTermList p fl a i t differ cols ζ r o)) ∘ - Option.map (modeEmbedding i nb) = + (ζ : List ℕ) : + (fun o => val p fl (rowTermList p fl a i t nb differ cols ζ r o)) ∘ + Option.map (modeIndex i nb) = packetRowTerm (p := nb) (scalarHom p fl) (val p fl ζ) a i t differ cols r := by funext o cases o with @@ -521,7 +534,7 @@ theorem val_rowTermList {a i : ℕ} (hia : i < a) {t : ℤ} (ht : t ∈ cauchyGr · rw [val_sub, val_one, val_localFormList ht hcols hr ζ] | some Z => simp only [Function.comp_apply, Option.map_some, rowTermList, packetRowTerm, affineSign, - modeEmbedding_apply] + modeIndex_apply, modeIndex_div Z.2.isLt, modeIndex_mod Z.2.isLt] have h := val_modeCoeffList (p := p) (fl := fl) hia ht hti hti' differ hr Z ζ split_ifs · rw [one_mul]; exact h @@ -561,62 +574,59 @@ theorem val_listCoeffList {M : Type} [DecidableEq M] {modes : List M} (hm : mode /-! ### Packet entries -/ -/-- The modes `(y₀, b)` with `y₀ < 2 ^ (i + 1)` and `b < nb`, in lexicographic order. -/ -def modeList (i nb : ℕ) : List (ℕ × ℕ) := - List.range (2 ^ (i + 1)) ×ˢ List.range nb - -theorem nodup_modeList (i nb : ℕ) : (modeList i nb).Nodup := - List.nodup_range.product List.nodup_range - -theorem mem_modeList {i nb : ℕ} {Z : ℕ × ℕ} : Z ∈ modeList i nb ↔ Z.1 < 2 ^ (i + 1) ∧ Z.2 < nb := by - rw [modeList, List.mem_product, List.mem_range, List.mem_range] - -/-- The modes of `modeList` with a property of the tile, as the image of those of the packet. -/ -private theorem toFinset_filter_modeList (i nb : ℕ) (P : ℕ → Bool) : - ((modeList i nb).filter fun Z => P Z.1).toFinset = - ({Z | P Z.1 = true} : Finset (Fin (2 ^ (i + 1)) × Fin nb)).map (modeEmbedding i nb) := by - ext ⟨y, b⟩ - simp only [List.mem_toFinset, List.mem_filter, mem_modeList, mem_map, mem_filter, mem_univ, - true_and, modeEmbedding_apply] +/-- The dense indices below `2 ^ (i + 1) nb` whose tile has a property, as the image of the +modes of the packet. -/ +private theorem toFinset_filter_range (i nb : ℕ) (P : ℕ → Bool) : + ((List.range (2 ^ (i + 1) * nb)).filter fun k => P (k / nb)).toFinset = + ({Z | P Z.1 = true} : Finset (Fin (2 ^ (i + 1)) × Fin nb)).map (modeIndex i nb) := by + ext k + simp only [List.mem_toFinset, List.mem_filter, List.mem_range, mem_map, mem_filter, mem_univ, + true_and, modeIndex_apply] constructor - · rintro ⟨⟨hy, hb⟩, h⟩ - exact ⟨(⟨y, hy⟩, ⟨b, hb⟩), h, rfl⟩ - · rintro ⟨Z, h, hZ⟩ - simp only [Prod.mk.injEq] at hZ - obtain ⟨rfl, rfl⟩ := hZ - exact ⟨⟨Z.1.isLt, Z.2.isLt⟩, h⟩ + · rintro ⟨hk, h⟩ + have hnb : 0 < nb := Nat.pos_of_ne_zero fun h0 => by simp [h0] at hk + exact ⟨(⟨k / nb, (Nat.div_lt_iff_lt_mul hnb).mpr hk⟩, ⟨k % nb, Nat.mod_lt _ hnb⟩), h, + Nat.div_add_mod' k nb⟩ + · rintro ⟨Z, h, rfl⟩ + refine ⟨?_, by rw [modeIndex_div Z.2.isLt]; exact h⟩ + calc (Z.1 : ℕ) * nb + Z.2 < (Z.1 + 1) * nb := by rw [Nat.add_mul, Nat.one_mul]; omega + _ ≤ 2 ^ (i + 1) * nb := Nat.mul_le_mul_right _ Z.1.isLt /-- **The entry of a packet vertex on coefficient lists** (§D.4.6, p. 63): coefficient extraction along the row terms, at the exponents `β` of the modes targeting the group, times the powers of -the values of the modes with the group as source. The modes are the pairs `(y₀, b)`. -/ +the values of the modes with the group as source. A mode `(y₀, b)` is its dense index +`y₀ nb + b`, so `β` is read at dense indices, as from a dense list of exponents. -/ def packetEntryList (p : ℕ) (fl : List ℕ) (a i : ℕ) (t : ℤ) (nb : ℕ) (e₁ e₂ differ : Bool) - (β : ℕ × ℕ → ℕ) (rows cols : List (ℕ × Bool)) (ζ : List ℕ) : List ℕ := + (β : ℕ → ℕ) (rows cols : List (ℕ × Bool)) (ζ : List ℕ) : List ℕ := mul p fl - (listCoeffList p fl ((modeList i nb).filter fun Z => modeTargets i e₁ e₂ Z.1) - (rows.map (rowTermList p fl a i t differ cols ζ)) β) - (prodList p fl (((modeList i nb).filter fun Z => modeSources i e₁ e₂ Z.1).map fun Z => - pow p fl (modeValueList p fl i differ cols Z.1 Z.2) (β Z))) + (listCoeffList p fl ((List.range (2 ^ (i + 1) * nb)).filter fun k => + modeTargets i e₁ e₂ (k / nb)) + (rows.map (rowTermList p fl a i t nb differ cols ζ)) β) + (prodList p fl (((List.range (2 ^ (i + 1) * nb)).filter fun k => + modeSources i e₁ e₂ (k / nb)).map fun k => + pow p fl (modeValueList p fl i differ cols (k / nb) (k % nb)) (β k))) /-- **Packet entries are correct.** For a grid point `t` of valuation `i < a` and ports in slots below `2 ^ a`, the value of `packetEntryList` in `A_p` is `packetEntry` at the reduction of -`ℤ_(2)`, the value of `ζ` and the exponents `β` on the modes of the packet. -/ +`ℤ_(2)`, the value of `ζ` and the exponents `β` at the dense indices of the modes. -/ theorem val_packetEntryList {a i : ℕ} (hia : i < a) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) (hti : (2 : ℤ) ^ i ∣ t) (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) (nb : ℕ) (e₁ e₂ differ : Bool) - (β : ℕ × ℕ → ℕ) {rows cols : List (ℕ × Bool)} (hrows : ∀ r ∈ rows, r.1 < 2 ^ a) + (β : ℕ → ℕ) {rows cols : List (ℕ × Bool)} (hrows : ∀ r ∈ rows, r.1 < 2 ^ a) (hcols : ∀ c ∈ cols, c.1 < 2 ^ a) (ζ : List ℕ) : val p fl (packetEntryList p fl a i t nb e₁ e₂ differ β rows cols ζ) = packetEntry (p := nb) (scalarHom p fl) (val p fl ζ) a i t e₁ e₂ differ - (fun Z => β (Z.1, Z.2)) rows cols := by + (fun Z => β (Z.1 * nb + Z.2)) rows cols := by rw [packetEntryList, packetEntry, val_mul] congr 1 - · rw [val_listCoeffList ((nodup_modeList i nb).filter _), toFinset_filter_modeList, + · rw [val_listCoeffList (List.nodup_range.filter _), toFinset_filter_range, TensorNetwork.listCoeff_map, List.map_map, List.map_map] refine congrArg₂ _ (List.map_congr_left fun r hr => ?_) rfl - exact val_rowTermList hia ht hti hti' differ hcols (hrows r hr) nb ζ - · rw [val_prodList, List.map_map, ← List.prod_toFinset _ ((nodup_modeList i nb).filter _), - toFinset_filter_modeList, prod_map] + exact val_rowTermList hia ht hti hti' nb differ hcols (hrows r hr) ζ + · rw [val_prodList, List.map_map, ← List.prod_toFinset _ (List.nodup_range.filter _), + toFinset_filter_range, prod_map] refine prod_congr rfl fun Z _ => ?_ - simp only [Function.comp_apply, modeEmbedding_apply] + simp only [Function.comp_apply, modeIndex_apply, modeIndex_div Z.2.isLt, + modeIndex_mod Z.2.isLt] rw [val_pow, val_modeValueList] /-- **Packet entries in every ring receiving `A_p`.** Along a ring map `ψ : A_p → S` with `2` @@ -625,10 +635,10 @@ nilpotent in `S`, the value of `packetEntryList` is `packetEntry` over `S` at an theorem map_val_packetEntryList {S : Type*} [CommRing S] (ψ : Ring p fl →+* S) {n : ℕ} (hn : (2 : S) ^ n = 0) (κ : ℤ₍₂₎ →+* S) {a i : ℕ} (hia : i < a) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) (hti : (2 : ℤ) ^ i ∣ t) (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) - (nb : ℕ) (e₁ e₂ differ : Bool) (β : ℕ × ℕ → ℕ) {rows cols : List (ℕ × Bool)} + (nb : ℕ) (e₁ e₂ differ : Bool) (β : ℕ → ℕ) {rows cols : List (ℕ × Bool)} (hrows : ∀ r ∈ rows, r.1 < 2 ^ a) (hcols : ∀ c ∈ cols, c.1 < 2 ^ a) (ζ : List ℕ) : ψ (val p fl (packetEntryList p fl a i t nb e₁ e₂ differ β rows cols ζ)) = - packetEntry (p := nb) κ (ψ (val p fl ζ)) a i t e₁ e₂ differ (fun Z => β (Z.1, Z.2)) + packetEntry (p := nb) κ (ψ (val p fl ζ)) a i t e₁ e₂ differ (fun Z => β (Z.1 * nb + Z.2)) rows cols := by rw [val_packetEntryList hia ht hti hti' nb e₁ e₂ differ β hrows hcols ζ, map_packetEntry ψ two_pow_eq_zero _ _ hia.le hti hti', diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEvaluator.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEvaluator.lean index 2094374c50..435359142c 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEvaluator.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEvaluator.lean @@ -58,22 +58,29 @@ example : packetEntryList 3 [1, 1] 1 0 1 2 true false true (fun _ => 0) [(0, fal (kernelValueList 3 [1, 1] 1 1 1 [0, 1]) := by decide +-- Exponent `1` at the mode `(0, 1)`, of dense index `0 * 2 + 1 = 1`, which targets the group +-- (digit `0` of `y₀ = 0` selects the first symbol): the single row factor contributes the +-- coefficient of that mode, and no source mode has a positive exponent. +example : packetEntryList 3 [1, 1] 1 0 1 2 true false true (fun k => if k = 1 then 1 else 0) + [(0, true)] [(1, true)] [0, 1] = modeCoeffList 3 [1, 1] 1 0 1 true 0 0 1 [0, 1] := by + decide + /-- The hypotheses of the correctness theorem hold for these packets. -/ -theorem val_packetEntryList_tiny (β : ℕ × ℕ → ℕ) (ζ : List ℕ) : +theorem val_packetEntryList_tiny (β : ℕ → ℕ) (ζ : List ℕ) : GaloisRingArith.val 3 [1, 1] (packetEntryList 3 [1, 1] 1 0 1 2 true false true β [(0, true), (1, false)] [(1, true), (0, true)] ζ) = packetEntry (p := 2) (scalarHom 3 [1, 1]) (GaloisRingArith.val 3 [1, 1] ζ) 1 0 1 true false - true (fun Z => β (Z.1, Z.2)) [(0, true), (1, false)] [(1, true), (0, true)] := + true (fun Z => β (Z.1 * 2 + Z.2)) [(0, true), (1, false)] [(1, true), (0, true)] := val_packetEntryList (by norm_num) (by simp [mem_cauchyGrid]) (by norm_num) (by norm_num) 2 true false true β (by simp) (by simp) ζ -- Transported to any ring with `2` nilpotent, at any ring map from `ℤ_(2)`. example {S : Type} [CommRing S] (ψ : GaloisRingArith.Ring 3 [1, 1] →+* S) {n : ℕ} - (hn : (2 : S) ^ n = 0) (κ : ℤ₍₂₎ →+* S) (β : ℕ × ℕ → ℕ) (ζ : List ℕ) : + (hn : (2 : S) ^ n = 0) (κ : ℤ₍₂₎ →+* S) (β : ℕ → ℕ) (ζ : List ℕ) : ψ (GaloisRingArith.val 3 [1, 1] (packetEntryList 3 [1, 1] 1 0 1 2 true false true β [(0, true)] [(1, true)] ζ)) = packetEntry (p := 2) κ (ψ (GaloisRingArith.val 3 [1, 1] ζ)) 1 0 1 true false true - (fun Z => β (Z.1, Z.2)) [(0, true)] [(1, true)] := + (fun Z => β (Z.1 * 2 + Z.2)) [(0, true)] [(1, true)] := map_val_packetEntryList ψ hn κ (by norm_num) (by simp [mem_cauchyGrid]) (by norm_num) (by norm_num) 2 true false true β (by simp) (by simp) ζ From b1ee3e5bda2ca3923b4ff2349a326436ae5f48a1 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 00:33:41 -0400 Subject: [PATCH 047/129] feat(circuit/frontier): the value table of native recovery `NativeRecovery.storeValue` appends a value to the row of a node index in the table that `recover` reads: the accumulator update of a recursive call once the value at an interpolation point is complete. Storing a sequence of values gives each node index its values in order (`getD_foldl_storeValue`), in polynomial time (`computableInPolyTime_storeValue`). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/NativeRecovery.lean | 70 +++++++++++++++++++ .../Frontier/TensorNetworkNativeRecovery.lean | 5 ++ 2 files changed, 75 insertions(+) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean index daa823e54c..48f1349891 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeRecovery.lean @@ -74,6 +74,8 @@ recovery is polynomial-time with the precision, the shift, the parameters and `2 * `Cslib.Circuits.Frontier.NativeRecovery.recip`: the truncated reciprocal of `E_t(t + α z)`. * `Cslib.Circuits.Frontier.NativeRecovery.coeffMult`: the multipliers. * `Cslib.Circuits.Frontier.NativeRecovery.recover`: native tapered recovery. +* `Cslib.Circuits.Frontier.NativeRecovery.storeValue`: storing a value in the table that `recover` + reads, the accumulator update of a recursive call. * `Cslib.Circuits.Frontier.NativeRecovery.recipSeries`, `Cslib.Circuits.Frontier.NativeRecovery.gamma`: the reciprocal and the multipliers over `ℤ_(2)`. @@ -103,6 +105,9 @@ recovery is polynomial-time with the precision, the shift, the parameters and `2 mathematical recovery modulo `2 ^ p`. * `Cslib.Circuits.Frontier.NativeRecovery.computableInPolyTime_recover`: native recovery is polynomial-time. +* `Cslib.Circuits.Frontier.NativeRecovery.getD_foldl_storeValue`, + `Cslib.Circuits.Frontier.NativeRecovery.computableInPolyTime_storeValue`: storing a sequence of + values gives each node index its values in order, in polynomial time. ## References @@ -238,6 +243,39 @@ def recover (p Δ : ℕ) (fl : List ℕ) (a u h : ℕ) (ζ : List (List ℕ)) (W if k = 2 ^ a then S else add (p + Δ) fl S (nodeTerm (p + Δ) fl a u h Δ ζ k (W.getD k []))) (zero fl)) +/-! ### Storing the values -/ + +/-- Store the value `y` at node index `k`, after the values already stored there. This is the +accumulator update of a recursive call once the value at an interpolation point is complete; +`NativeRecovery.recover` reads the table. -/ +def storeValue (W : List (List (List ℕ))) (k : ℕ) (y : List ℕ) : List (List (List ℕ)) := + (List.range (max W.length (k + 1))).map fun k' => + if k' = k then W.getD k' [] ++ [y] else W.getD k' [] + +theorem getD_storeValue (W : List (List (List ℕ))) (k : ℕ) (y : List ℕ) (k' : ℕ) : + (storeValue W k y).getD k' [] = if k' = k then W.getD k [] ++ [y] else W.getD k' [] := by + unfold storeValue + by_cases hk' : k' < max W.length (k + 1) + · rw [List.getD_eq_getElem _ _ (by simpa using hk'), List.getElem_map, List.getElem_range] + split_ifs with h + · rw [h] + · rfl + · have hne : k' ≠ k := by omega + rw [List.getD_eq_default _ _ (by rw [List.length_map, List.length_range]; omega), + ite_eq_right_of_eq_false _ _ (eq_false hne), List.getD_eq_default _ _ (by omega)] + +/-- **Storing a sequence of values**: each node index receives its values in order. -/ +theorem getD_foldl_storeValue (W : List (List (List ℕ))) (l : List (ℕ × List ℕ)) (k : ℕ) : + (l.foldl (fun W e => storeValue W e.1 e.2) W).getD k [] = + W.getD k [] ++ (l.filter fun e => e.1 = k).map Prod.snd := by + induction l generalizing W with + | nil => simp + | cons e l ih => + rw [List.foldl_cons, ih, getD_storeValue, List.filter_cons] + by_cases h : e.1 = k + · simp [h] + · simp [h, Ne.symm h] + /-! ### Correctness of the constants -/ variable {N : ℕ} {fl : List ℕ} @@ -1008,6 +1046,12 @@ private theorem singletonPolyTime {n : α → ℕ} (hn : ComputableInPolyTime n ComputableInPolyTime (fun x => [n x]) encIn (encList encNat) := ComputableInPolyTime.cons' hn (ComputableInPolyTime.const []) +/-- A coefficient list as a one-element list. -/ +private theorem singletonPolyTime' {c : α → List ℕ} + (hc : ComputableInPolyTime c encIn (encList encNat)) : + ComputableInPolyTime (fun x => [c x]) encIn (encList (encList encNat)) := + ComputableInPolyTime.cons' hc (ComputableInPolyTime.const []) + private theorem polyBounded_unary {n : α → ℕ} (hn : ComputableInPolyTime n encIn encUnary) : PolyBounded (fun x => (encIn x).length) n := by simpa using ComputableInPolyTime.polyBounded_length hn @@ -1380,6 +1424,32 @@ theorem computableInPolyTime_recover {p Δ a u h : α → ℕ} {fl : α → List refine (computableInPolyTime_shiftDown hΔ hfold).of_enc_eq fun x => ?_ simp only [recover, beq_iff_eq, Function.comp_apply] +/-- **Storing a value is polynomial-time**, with the node index in unary. -/ +@[fun_prop] +theorem computableInPolyTime_storeValue {W : α → List (List (List ℕ))} {k : α → ℕ} + {y : α → List ℕ} + (hW : ComputableInPolyTime W encIn (encList (encList (encList encNat)))) + (hk : ComputableInPolyTime k encIn encUnary) + (hy : ComputableInPolyTime y encIn (encList encNat)) : + ComputableInPolyTime (fun x => storeValue (W x) (k x) (y x)) encIn + (encList (encList (encList encNat))) := by + have h1 := ComputableInPolyTime.fst encIn encUnary + have h2 := ComputableInPolyTime.snd encIn encUnary + have hrow := (ComputableInPolyTime.listGetD (encA := encList (encList encNat)) []).comp + ((hW.comp h1).prod h2) + have hlen := lengthPolyTime hW + have hmax : ComputableInPolyTime (fun x => max (W x).length (k x + 1)) encIn encUnary := + (ComputableInPolyTime.unary_add' hlen (ComputableInPolyTime.unary_sub' + (ComputableInPolyTime.unary_add' hk (ComputableInPolyTime.const 1)) hlen)).of_enc_eq + fun x => by congr 1; omega + refine (ComputableInPolyTime.mapWithInput + (f := fun x k' => if (k' == k x) = true then (W x).getD k' [] ++ [y x] else (W x).getD k' []) + (ComputableInPolyTime.range.comp hmax) + (ComputableInPolyTime.ite (ComputableInPolyTime.beq h2 (hk.comp h1)) + (ComputableInPolyTime.listAppend' hrow (singletonPolyTime' (hy.comp h1))) hrow)).of_enc_eq + fun x => ?_ + simp only [storeValue, beq_iff_eq, Function.comp_apply] + end PolyTime end Cslib.Circuits.Frontier.NativeRecovery diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean index 35cced1e54..d3c422fa82 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean @@ -39,6 +39,11 @@ example : (mult 1 1 1 1, mult 1 1 1 3, mult 1 1 1 0) = (1, 1, 0) := by decide `E_{-1}(0) F(-1) + E_1(0) F(1) = (4 + 6) / 2 = 5` modulo `8`, computing modulo `2 ^ (3 + 2)`. -/ example : recover 3 2 [1, 1] 1 1 1 [[0, 0]] [[], [[4, 0]], [], [[6, 0]]] = [5, 0] := by decide +/-- The value table of that recovery, built by storing `F(-1)` at node index `1` and `F(1)` at +node index `3`. -/ +example : storeValue (storeValue [] 1 [4, 0]) 3 [6, 0] = [[], [[4, 0]], [], [[6, 0]]] := by + decide + /-- The same recovery with `Δ = (a + 1) ∑_t q_t = 4`, the shift that `val_recover` assumes. -/ example : recover 3 4 [1, 1] 1 1 1 [[0, 0]] [[], [[4, 0]], [], [[6, 0]]] = [5, 0] := by decide From 2bfdbb3987491b80db6822102f3848b1f9f2099a Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 00:36:30 -0400 Subject: [PATCH 048/129] =?UTF-8?q?feat(PolyTime):=20the=20kernel=20of=20r?= =?UTF-8?q?eduction=20A=5FN=20=E2=86=92=20A=5Fn=20is=202^n=20A=5FN?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `exists_eq_add_of_reduce_eq`: elements of `(ℤ/2^N)[U]/(f)` with equal reductions to precision `n` differ by `2 ^ n` times an element; so a native value computed at precision `p_t` lifts to `A_(p+Δ)` in the form that `NativeRecovery.val_recover` assumes (`exists_val_eq_add_of_val_eq`). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../PolyTime/GaloisRingSeries.lean | 26 ++++++++++++++++++- .../Frontier/TensorNetworkNativeRecovery.lean | 5 ++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/Complexity/Computability/PolyTime/GaloisRingSeries.lean b/Complexity/Computability/PolyTime/GaloisRingSeries.lean index 455838f145..082432783f 100644 --- a/Complexity/Computability/PolyTime/GaloisRingSeries.lean +++ b/Complexity/Computability/PolyTime/GaloisRingSeries.lean @@ -31,7 +31,8 @@ the contraction algorithm of [OpenAI2026ETH] (Appendix D.4.4, pp. 60–61). determined modulo `2 ^ p` (`GaloisRingArith.mod_eq_of_val_eq`), and every element is the value of a reduced coefficient list (`GaloisRingArith.exists_val_eq`). * **Precision.** Reduction `A_N → A_n` for `n ≤ N` (`GaloisRingArith.reduce`) keeps the - coordinates (`GaloisRingArith.reduce_val`). Dividing the coordinates of `2 ^ Δ y` by `2 ^ Δ` + coordinates (`GaloisRingArith.reduce_val`), and its kernel is `2 ^ n A_N` + (`GaloisRingArith.exists_eq_add_of_reduce_eq`). Dividing the coordinates of `2 ^ Δ y` by `2 ^ Δ` gives `y` modulo `2 ^ (N - Δ)` (`GaloisRingArith.val_shiftDown`): this is how a sum that is known to be divisible by `2 ^ Δ` loses its common power of two. * **Truncated products** (`GaloisRingArith.tmul`) of polynomials in `z` over `A_p`: the first @@ -51,6 +52,8 @@ length and the shift in unary (`GaloisRingArith.computableInPolyTime_tmul`, * `Cslib.GaloisRingArith.mod_eq_of_val_eq`, `Cslib.GaloisRingArith.exists_val_eq`: coordinates. * `Cslib.GaloisRingArith.val_shiftDown`: cancelling a common power of two. +* `Cslib.GaloisRingArith.exists_eq_add_of_reduce_eq`, + `Cslib.GaloisRingArith.exists_val_eq_add_of_val_eq`: the kernel of reduction is `2 ^ n A_N`. * `Cslib.GaloisRingArith.val_tmul`: truncated products are correct. * `Cslib.GaloisRingArith.computableInPolyTime_tmul`, `Cslib.GaloisRingArith.computableInPolyTime_shiftDown`: polynomial time. @@ -178,6 +181,27 @@ theorem reduce_val {n N : ℕ} (h : n ≤ N) (c : List ℕ) : reduce h fl (val N fl c) = val n fl c := by simp only [reduce, val, map_sum, map_mul, map_pow, map_natCast, AdjoinRoot.map_root] +/-- **Equal reductions differ by a multiple of `2 ^ n`**: the kernel of reduction +`A_N → A_n` is `2 ^ n A_N`. -/ +theorem exists_eq_add_of_reduce_eq {n N : ℕ} (h : n ≤ N) {x y : Ring N fl} + (hxy : reduce h fl x = reduce h fl y) : ∃ c, x = y + 2 ^ n * c := by + obtain ⟨r, -, -, rfl⟩ := exists_val_eq x + obtain ⟨s, -, -, rfl⟩ := exists_val_eq y + rw [reduce_val, reduce_val] at hxy + choose! q hq using fun i (hi : i < fl.length) => Nat.modEq_iff_dvd.mp (mod_eq_of_val_eq hxy hi) + refine ⟨-∑ i ∈ range fl.length, ((q i : ℤ) : Ring N fl) * AdjoinRoot.root _ ^ i, ?_⟩ + rw [val, val, mul_neg, mul_sum, ← sub_eq_add_neg, ← sum_sub_distrib] + refine sum_congr rfl fun i hi => ?_ + have hi' := congrArg (Int.cast : ℤ → Ring N fl) (hq i (mem_range.mp hi)) + push_cast at hi' + linear_combination (-(AdjoinRoot.root _ ^ i : Ring N fl)) * hi' + +/-- **A value at a lower precision lifts with an error divisible by `2 ^ n`**: a coefficient list +whose value in `A_n` is the reduction of `y ∈ A_N` has value `y + 2 ^ n c` in `A_N`. -/ +theorem exists_val_eq_add_of_val_eq {n N : ℕ} (h : n ≤ N) {w : List ℕ} {y : Ring N fl} + (hw : val n fl w = reduce h fl y) : ∃ c, val N fl w = y + 2 ^ n * c := + exists_eq_add_of_reduce_eq h (by rw [reduce_val, hw]) + /-- Division of each coordinate by `2 ^ Δ`. -/ def shiftDown (Δ : ℕ) (c : List ℕ) : List ℕ := c.map (· / 2 ^ Δ) diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean index d3c422fa82..f0afe5bb95 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeRecovery.lean @@ -66,4 +66,9 @@ example : Turing.MultiTapeTM.ComputableInPolyTime (fun n : ℕ => twoVal n n) computableInPolyTime_twoVal Turing.MultiTapeTM.ComputableInPolyTime.id' Turing.MultiTapeTM.ComputableInPolyTime.id' +/-- A value computed at precision `3` lifts to precision `5` with an error divisible by `2 ^ 3`. -/ +example (y : Ring 5 [1, 1]) (hw : val 3 [1, 1] [5, 0] = reduce (by norm_num) [1, 1] y) : + ∃ c, val 5 [1, 1] [5, 0] = y + 2 ^ 3 * c := + exists_val_eq_add_of_val_eq _ hw + end ComplexityTests.TensorNetworkNativeRecovery From 5317b590e65543f68faca9b3904e73810d7db10f Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 00:43:28 -0400 Subject: [PATCH 049/129] feat(circuit/frontier): coefficient extraction as a stack recursion Packet entries expand at most L affine row factors and keep the coefficient of a mode monomial (section D.4.6). CoeffScheme gives this as a StackRecursion scheme on GaloisRingArith coefficient lists: a node with rows has m + 1 children (the local term and each mode, lowering its exponent in a dense list), a node without rows is a leaf. val_value proves the value is listCoeff along the modes; eval_value gives the derivation with at most (m + 2)^L nodes and L open calls. From polynomial-time data the components are polynomial-time, and valueCertificate_le bounds one native machine by time K (m + 2)^L Z^K and space K Z^K. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Frontier/TensorNetwork/CoeffScheme.lean | 750 ++++++++++++++++++ .../Frontier/TensorNetworkCoeffScheme.lean | 62 ++ 3 files changed, 813 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/CoeffScheme.lean create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkCoeffScheme.lean diff --git a/Complexity.lean b/Complexity.lean index 649788fcf0..c980ba15b1 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -876,6 +876,7 @@ public import Complexity.Computability.Circuit.Frontier.Sweep public import Complexity.Computability.Circuit.Frontier.TensorNetwork public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Binarize public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CauchyModes +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CoeffScheme public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DescribedContraction diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/CoeffScheme.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/CoeffScheme.lean new file mode 100644 index 0000000000..16d7dbbce5 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/CoeffScheme.lean @@ -0,0 +1,750 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DescribedContraction +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEvaluator +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Length +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Binary +import Complexity.Computability.Machines.Turing.MultiTape.Internal.EqualityPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryDivision +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListBinaryLookup +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListLookup +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListMap +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeNatFrontends +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeOptionPrimitives +import Mathlib.Algebra.BigOperators.Intervals +import Mathlib.Data.List.GetD + +/-! +# Coefficient extraction as a stack recursion + +An entry of a packet tensor of the contraction algorithm of [OpenAI2026ETH] expands a product of +at most `L` affine row factors and keeps the coefficient of a mode monomial. The source evaluates +it in time `exp(O(L log s))` and polynomial space (§D.4.6, p. 63). This file gives that +evaluation as an instance of `Cslib.GenericRAM.StackRecursion.Scheme`, on the coefficient lists +of `A_p = (ℤ / 2 ^ p)[U] / (f)`. + +The data (`CoeffScheme.Terms`) are, at a context `x : C`, the precision `(p, fl)`, the list of +the (dense indices of the) modes, and the terms of the row factors: `term x r none` is the local +term of the row `r`, and `term x r (some k)` the coefficient of the mode `k`. A node of the +recursion is a context, the rows still to expand and the exponents still needed, as a dense list. +A node with rows `r :: rs` has `m + 1` children for `m` modes: the local term (`c = 0`), and the +mode `modes[c - 1]` with its exponent lowered (`c ≥ 1`); it adds the products of their values +with the terms of `r`, skipping the modes with exponent zero. A node without rows is a leaf: one +when every exponent is zero, else zero. + +* `CoeffScheme.Terms.value` is the value the recursion computes, and + `CoeffScheme.Terms.val_value` proves that its value in `A_p` is `TensorNetwork.listCoeff` along + the modes, at the values of the terms. +* `CoeffScheme.Terms.eval_value`: the derivation has `CoeffScheme.count m L ≤ (m + 2) ^ L` nodes + for `L` rows (`CoeffScheme.count_le`) and at most `L` open calls, with nodes whose rows are a + suffix of the root's and whose exponents are below the root's, and reduced values. + +So the run has `exp(O(L log m))` steps on a stack of at most `L + 1` frames of polynomial size: +the time and space of §D.4.6. With polynomial-time data (`CoeffScheme.Terms.PolyTime`: the +precision with `p` in unary, the modes and the terms), every component of the scheme is +polynomial-time (`CoeffScheme.Terms.scheme_polyTime`), and one native machine computes `value` +on every node (`CoeffScheme.Terms.valueCertificate`), within time `K (m + 2) ^ L Z ^ K` and space +`K Z ^ K` for `Z = n + m + Rv + 2`, where `n` is the length of the node and `Rv` bounds the +reduced coefficient lists (`CoeffScheme.Terms.valueCertificate_le`). + +## Main definitions + +* `Cslib.Circuits.Frontier.CoeffScheme.Terms`: the data of coefficient extraction. +* `Cslib.Circuits.Frontier.CoeffScheme.Terms.value`: its value. +* `Cslib.Circuits.Frontier.CoeffScheme.Terms.scheme`: the recursion scheme. +* `Cslib.Circuits.Frontier.CoeffScheme.Terms.valueCertificate`: one native machine for `value`. + +## Main results + +* `Cslib.Circuits.Frontier.CoeffScheme.Terms.val_value`: the value is `listCoeff`. +* `Cslib.Circuits.Frontier.CoeffScheme.Terms.eval_value`: the derivation, its nodes and height. +* `Cslib.Circuits.Frontier.CoeffScheme.count_le`: the node count. +* `Cslib.Circuits.Frontier.CoeffScheme.Terms.scheme_polyTime`: polynomial-time components. +* `Cslib.Circuits.Frontier.CoeffScheme.Terms.run_scheme`: the machine computes `value`. +* `Cslib.Circuits.Frontier.CoeffScheme.Terms.valueCertificate_le`: its time and space. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.6 (p. 63). +-/ + +@[expose] public section + +namespace Cslib.Circuits.Frontier + +namespace CoeffScheme + +open Finset GaloisRingArith NativePacket Cslib.GenericRAM Cslib.GenericRAM.StackRecursion + +/-- **The data of coefficient extraction**: at a context `x : C`, the precision `(p, fl)` of the +coefficient lists, the list of the modes, and the terms of the row factors (`term x r none` the +local term of the row `r`, `term x r (some k)` the coefficient of the mode `k`). -/ +structure Terms (C Rw : Type) where + /-- The precision `(p, fl)`. -/ + prec : C → ℕ × List ℕ + /-- The modes, as dense indices. -/ + modes : C → List ℕ + /-- The terms of the row factors. -/ + term : C → Rw → Option ℕ → List ℕ + +/-- A node: a context, the rows still to expand, and the exponents still needed, as a dense +list. -/ +abbrev Node (C Rw : Type) := C × (List Rw × List ℕ) + +/-- The number of nodes of the derivation for `m` modes and `L` rows. -/ +def count (m : ℕ) : ℕ → ℕ + | 0 => 1 + | L + 1 => (m + 1) * count m L + 1 + +/-- **At most `(m + 2) ^ L` nodes.** -/ +theorem count_le (m : ℕ) : ∀ L, count m L ≤ (m + 2) ^ L + | 0 => le_rfl + | L + 1 => by + have h := count_le m L + have h1 := Nat.one_le_two_pow.trans (Nat.pow_le_pow_left (show 2 ≤ m + 2 by omega) L) + rw [count, pow_succ] + nlinarith + +namespace Terms + +variable {C Rw : Type} (S : Terms C Rw) + +/-- The mode of child `c ≥ 1`: `modes[c - 1]`. -/ +def modeOf (x : C) (c : ℕ) : ℕ := (S.modes x).getD (c - 1) 0 + +/-- The exponents of child `c`: unchanged for the local term `c = 0`, and with the exponent of +the mode `modeOf c` lowered by one for `c ≥ 1`. -/ +def childExps (x : C) (γ : List ℕ) (c : ℕ) : List ℕ := + (List.range γ.length).map fun j => + if c ≠ 0 ∧ j = S.modeOf x c then γ.getD j 0 - 1 else γ.getD j 0 + +/-- The contribution of child `c` with value `y` at the row `r`: the local term times `y` for +`c = 0`; for `c ≥ 1`, zero when the mode has exponent zero, else its coefficient times `y`. -/ +def contrib (x : C) (r : Rw) (γ : List ℕ) (c : ℕ) (y : List ℕ) : List ℕ := + if c = 0 then mul (S.prec x).1 (S.prec x).2 (S.term x r none) y + else if γ.getD (S.modeOf x c) 0 = 0 then zero (S.prec x).2 + else mul (S.prec x).1 (S.prec x).2 (S.term x r (some (S.modeOf x c))) y + +/-- The leaf: one when every mode has exponent zero, else zero. -/ +def leafValue (x : C) (γ : List ℕ) : List ℕ := + if (S.modes x).all (fun k => γ.getD k 0 == 0) then one (S.prec x).2 else zero (S.prec x).2 + +/-- **The value of coefficient extraction**, as the recursion computes it: the sum over the +children of their contributions. -/ +def value (x : C) : List Rw → List ℕ → List ℕ + | [], γ => S.leafValue x γ + | r :: rs, γ => (List.range ((S.modes x).length + 1)).foldl + (fun s c => add (S.prec x).1 (S.prec x).2 s + (S.contrib x r γ c (value x rs (S.childExps x γ c)))) (zero (S.prec x).2) + +/-! ### Correctness -/ + +theorem length_childExps (x : C) (γ : List ℕ) (c : ℕ) : (S.childExps x γ c).length = γ.length := by + simp [childExps] + +theorem getD_childExps (x : C) (γ : List ℕ) (c j : ℕ) : + (S.childExps x γ c).getD j 0 = + if c ≠ 0 ∧ j = S.modeOf x c then γ.getD j 0 - 1 else γ.getD j 0 := by + by_cases hj : j < γ.length + · rw [childExps, List.getD_eq_getElem _ _ (by simpa using hj), List.getElem_map, + List.getElem_range] + · rw [List.getD_eq_default _ _ (by simp [childExps]; omega), + List.getD_eq_default _ _ (by omega)] + split_ifs <;> simp + +/-- The exponents of child `c ≥ 1` are those of the node with the exponent of its mode lowered. -/ +theorem getD_childExps_succ (x : C) (γ : List ℕ) (c : ℕ) : + (fun j => (S.childExps x γ (c + 1)).getD j 0) = + Function.update (fun j => γ.getD j 0) (S.modeOf x (c + 1)) + (γ.getD (S.modeOf x (c + 1)) 0 - 1) := by + funext j + rw [getD_childExps] + by_cases hj : j = S.modeOf x (c + 1) + · subst hj; simp + · simp [hj] + +private theorem val_foldl_range_add {p : ℕ} {fl : List ℕ} (g : ℕ → List ℕ) (n : ℕ) + (a : List ℕ) : + val p fl ((List.range n).foldl (fun s c => add p fl s (g c)) a) = + val p fl a + ∑ c ∈ range n, val p fl (g c) := by + induction n with + | zero => simp + | succ n ih => + rw [List.range_succ, List.foldl_append, List.foldl_cons, List.foldl_nil, val_add, ih, + sum_range_succ, add_assoc] + +private theorem sum_range_getD {M : Type*} [AddCommMonoid M] (f : ℕ → M) : + ∀ l : List ℕ, ∑ c ∈ range l.length, f (l.getD c 0) = (l.map f).sum + | [] => by simp + | Z :: l => by + rw [List.length_cons, sum_range_succ', List.map_cons, List.sum_cons, add_comm] + simp only [List.getD_cons_succ, List.getD_cons_zero, sum_range_getD f l] + +/-- **The value is `listCoeff`**: along duplicate-free modes, the value in `A_p` of `value` is +`TensorNetwork.listCoeff` at the values of the terms and the exponents of the dense list. -/ +theorem val_value (x : C) (hm : (S.modes x).Nodup) : + ∀ (rows : List Rw) (γ : List ℕ), + val (S.prec x).1 (S.prec x).2 (S.value x rows γ) = + TensorNetwork.listCoeff (S.modes x).toFinset + (rows.map fun r o => val (S.prec x).1 (S.prec x).2 (S.term x r o)) + (fun k => γ.getD k 0) + | [], γ => by + rw [value, leafValue, List.map_nil, TensorNetwork.listCoeff_nil] + simp only [List.all_eq_true, beq_iff_eq, List.mem_toFinset] + split_ifs + · exact val_one + · exact val_zero + | r :: rs, γ => by + rw [value, val_foldl_range_add, val_zero, zero_add, sum_range_succ', List.map_cons, + TensorNetwork.listCoeff_cons, add_comm] + congr 1 + · simp only [contrib, ↓reduceIte, val_mul, childExps, Ne, not_true_eq_false, false_and] + rw [val_value x hm rs] + congr 2 + funext j + by_cases hj : j < γ.length + · rw [List.getD_eq_getElem _ _ (by simpa using hj), List.getElem_map, List.getElem_range] + · rw [List.getD_eq_default _ _ (by simp; omega), List.getD_eq_default _ _ (by omega)] + · rw [List.sum_toFinset _ hm, ← sum_range_getD] + refine sum_congr rfl fun c hc => ?_ + simp only [contrib, Nat.add_one_ne_zero, ↓reduceIte, modeOf, Nat.add_sub_cancel] + split_ifs with h0 + · exact val_zero + · rw [val_mul, val_value x hm rs, getD_childExps_succ S x γ c] + rfl + +/-! ### The recursion scheme -/ + +/-- **Coefficient extraction as a recursion scheme.** A node with rows has `m + 1` children, +counted by the accumulator, and adds their contributions at its first row; a node without rows +is a leaf. -/ +def scheme : Scheme (Node C Rw) (ℕ × List ℕ) (List ℕ) where + isLeaf x := x.2.1.isEmpty + leaf x := S.leafValue x.1 x.2.2 + start x := (0, zero (S.prec x.1).2) + next x a := + if x.2.1 ≠ [] ∧ a.1 ≤ (S.modes x.1).length then + some (x.1, (x.2.1.tail, S.childExps x.1 x.2.2 a.1)) + else none + absorb x a y := (a.1 + 1, (x.2.1.head?).elim a.2 fun r => + add (S.prec x.1).1 (S.prec x.1).2 a.2 (S.contrib x.1 r x.2.2 a.1 y)) + finish _ a := a.2 + +/-- The invariant at the root `(x₀, (rows₀, γ₀))`: nodes keep the context, have a suffix of the +rows and exponents of the same length below those of the root; frames also have a counter at +most `m + 1` and a reduced value; values are reduced. -/ +def invariant (x₀ : C) (rows₀ : List Rw) (γ₀ : List ℕ) : + Invariant (Node C Rw) (ℕ × List ℕ) (List ℕ) := + ⟨fun x => x.1 = x₀ ∧ x.2.1 <:+ rows₀ ∧ x.2.2.length = γ₀.length ∧ + ∀ j, x.2.2.getD j 0 ≤ γ₀.getD j 0, + fun x a => (x.1 = x₀ ∧ x.2.1 <:+ rows₀ ∧ x.2.2.length = γ₀.length ∧ + ∀ j, x.2.2.getD j 0 ≤ γ₀.getD j 0) ∧ a.1 ≤ (S.modes x₀).length + 1 ∧ + SumOps.galois.Reduced (S.prec x₀) a.2, + fun y => SumOps.galois.Reduced (S.prec x₀) y⟩ + +/-- The leaf value is reduced. -/ +theorem reduced_leafValue (x : C) (γ : List ℕ) : + SumOps.galois.Reduced (S.prec x) (S.leafValue x γ) := by + unfold leafValue + split_ifs + · exact SumOps.galois.reduced_one (S.prec x) + · exact SumOps.galois.reduced_zero (S.prec x) + +/-- **The derivation of coefficient extraction.** From a node whose rows are a suffix of the +root's and whose exponents are below the root's, the recursion computes `value`, with +`count m L` nodes for `L` rows and at most `L` open calls. -/ +theorem eval_value (x₀ : C) (rows₀ : List Rw) (γ₀ : List ℕ) : + ∀ (rows : List Rw) (γ : List ℕ), rows <:+ rows₀ → γ.length = γ₀.length → + (∀ j, γ.getD j 0 ≤ γ₀.getD j 0) → + ∃ h ≤ rows.length, S.scheme.Eval (S.invariant x₀ rows₀ γ₀) (x₀, (rows, γ)) + (S.value x₀ rows γ) (count (S.modes x₀).length rows.length) h + | [], γ, hs, hl, hγ => + ⟨0, le_rfl, Scheme.Eval.leaf (R := S.scheme) ⟨rfl, hs, hl, hγ⟩ rfl + (S.reduced_leafValue x₀ γ)⟩ + | r :: rs, γ, hs, hl, hγ => by + set m := (S.modes x₀).length + set acc : ℕ → ℕ × List ℕ := fun c => (c, (List.range c).foldl + (fun s c => add (S.prec x₀).1 (S.prec x₀).2 s + (S.contrib x₀ r γ c (S.value x₀ rs (S.childExps x₀ γ c)))) (zero (S.prec x₀).2)) + have hred (c : ℕ) : SumOps.galois.Reduced (S.prec x₀) (acc c).2 := + SumOps.galois.reduced_foldl (S.prec x₀) _ (SumOps.galois.reduced_add (S.prec x₀)) _ _ + (SumOps.galois.reduced_zero (S.prec x₀)) + have hchild (c : ℕ) := eval_value x₀ rows₀ γ₀ rs (S.childExps x₀ γ c) + ((List.suffix_cons r rs).trans hs) (by rw [length_childExps, hl]) + (fun j => by have := hγ j; rw [getD_childExps]; split_ifs <;> omega) + choose hgt hhgt heval using hchild + have hnode : (S.invariant x₀ rows₀ γ₀).node (x₀, (r :: rs, γ)) := ⟨rfl, hs, hl, hγ⟩ + obtain ⟨h, hh, hloop⟩ := Scheme.Loop.counted (R := S.scheme) + (I := S.invariant x₀ rows₀ γ₀) (x := (x₀, (r :: rs, γ))) (acc := acc) + (child := fun c => (x₀, (rs, S.childExps x₀ γ c))) + (val := fun c => S.value x₀ rs (S.childExps x₀ γ c)) + (nodes := fun _ => count m rs.length) (height := hgt) (m + 1) rs.length + (fun c hc => by + simp only [scheme, acc, ne_eq, reduceCtorEq, not_false_eq_true, true_and, + List.tail_cons, ite_eq_left_iff, not_le] + omega) + (by simp [scheme, acc, m]) + (fun c hc => by + simp only [scheme, acc, List.head?_cons, Option.elim_some, List.range_succ, + List.foldl_append, List.foldl_cons, List.foldl_nil]) + (fun c _ => heval c) + (fun c hc => ⟨hnode, by simp only [acc]; omega, hred c⟩) + (fun c _ => hhgt c) + have hstart : S.scheme.start (x₀, (r :: rs, γ)) = acc 0 := by simp [scheme, acc] + rw [← hstart] at hloop + refine ⟨h + 1, by simp only [List.length_cons]; omega, ?_⟩ + have := Scheme.Eval.call (R := S.scheme) (I := S.invariant x₀ rows₀ γ₀) + (x := (x₀, (r :: rs, γ))) hnode rfl hloop (hred (m + 1)) + rw [sum_const, card_range, smul_eq_mul] at this + exact this + +/-- **The root**: from `(x, (rows, γ))`, the recursion computes `value x rows γ` with at most +`(m + 2) ^ L` nodes for `m` modes and `L` rows. -/ +theorem eval_root (x : C) (rows : List Rw) (γ : List ℕ) : + ∃ h ≤ rows.length, S.scheme.Eval (S.invariant x rows γ) (x, (rows, γ)) (S.value x rows γ) + (count (S.modes x).length rows.length) h := + S.eval_value x rows γ rows γ (List.suffix_refl _) rfl fun _ => le_rfl + + +/-! ### Polynomial-time components -/ + +section PolyTime + +open Turing.MultiTapeTM + +variable {encC : C ↪ List Bool} {encRw : Rw ↪ List Bool} + +/-- The encoding of a node: the context, the rows and the exponents in binary. -/ +abbrev encNode (encC : C ↪ List Bool) (encRw : Rw ↪ List Bool) : Node C Rw ↪ List Bool := + encPair encC (encPair (encList encRw) (encList encNat)) + +/-- The encoding of an accumulator: the counter and the sum, in binary. -/ +abbrev encAcc : ℕ × List ℕ ↪ List Bool := encPair encNat (encList encNat) + +/-- **Polynomial-time data**: the precision with `p` in unary, the modes, and the terms. -/ +structure PolyTime (encC : C ↪ List Bool) (encRw : Rw ↪ List Bool) : Prop where + prec : ComputableInPolyTime S.prec encC (encPair encUnary (encList encNat)) + modes : ComputableInPolyTime S.modes encC (encList encNat) + term : ComputableInPolyTime (fun z : C × Rw × Option ℕ => S.term z.1 z.2.1 z.2.2) + (encPair encC (encPair encRw (encOption encNat))) (encList encNat) + +variable {S} (hS : S.PolyTime encC encRw) +include hS + +section Lift + +variable {α : Type} {encIn : α ↪ List Bool} {x : α → C} + (hx : ComputableInPolyTime x encIn encC) +include hx + +theorem precision_polyTime : ComputableInPolyTime (fun a => (S.prec (x a)).1) encIn encUnary := + (ComputableInPolyTime.fst _ _).comp (hS.prec.comp hx) + +theorem coeffs_polyTime : ComputableInPolyTime (fun a => (S.prec (x a)).2) encIn + (encList encNat) := + (ComputableInPolyTime.snd _ _).comp (hS.prec.comp hx) + +theorem zero_polyTime : ComputableInPolyTime (fun a => zero (S.prec (x a)).2) encIn + (encList encNat) := + computableInPolyTime_tabulate (g := fun _ _ => 0) + ((ComputableInPolyTime.length_list encNat).comp (coeffs_polyTime hS hx)) + (ComputableInPolyTime.const 0) + +theorem modeOf_polyTime {c : α → ℕ} (hc : ComputableInPolyTime c encIn encNat) : + ComputableInPolyTime (fun a => S.modeOf (x a) (c a)) encIn encNat := + ComputableInPolyTime.listGetD_binary (encA := encNat) 0 (hS.modes.comp hx) + (ComputableInPolyTime.binary_sub' hc (ComputableInPolyTime.const 1)) + +theorem childExps_polyTime {γ : α → List ℕ} {c : α → ℕ} + (hγ : ComputableInPolyTime γ encIn (encList encNat)) + (hc : ComputableInPolyTime c encIn encNat) : + ComputableInPolyTime (fun a => S.childExps (x a) (γ a) (c a)) encIn (encList encNat) := by + have h1 := ComputableInPolyTime.fst encIn encUnary + have h2 := ComputableInPolyTime.snd encIn encUnary + have hget := computableInPolyTime_getD (hγ.comp h1) h2 + have hf : ComputableInPolyTime (fun q : α × ℕ => + if c q.1 = 0 then (γ q.1).getD q.2 0 else + if q.2 = S.modeOf (x q.1) (c q.1) then (γ q.1).getD q.2 0 - 1 else (γ q.1).getD q.2 0) + (encPair encIn encUnary) encNat := + (ComputableInPolyTime.ite (ComputableInPolyTime.beq (hc.comp h1) + (ComputableInPolyTime.const 0)) hget + (ComputableInPolyTime.ite (ComputableInPolyTime.beq + (ComputableInPolyTime.unary_to_binary.comp h2) (modeOf_polyTime hS (hx.comp h1) + (hc.comp h1))) + (ComputableInPolyTime.binary_sub' hget (ComputableInPolyTime.const 1)) hget)).of_enc_eq + fun q => by + by_cases h0 : c q.1 = 0 + · simp [h0] + · by_cases hj : q.2 = S.modeOf (x q.1) (c q.1) <;> simp [h0, hj] + refine (ComputableInPolyTime.mapWithInput (f := fun a j => + if c a = 0 then (γ a).getD j 0 else + if j = S.modeOf (x a) (c a) then (γ a).getD j 0 - 1 else (γ a).getD j 0) + (ComputableInPolyTime.range.comp ((ComputableInPolyTime.length_list encNat).comp hγ)) + hf).of_enc_eq fun a => ?_ + refine congrArg _ (List.map_congr_left fun j _ => ?_) + by_cases h0 : c a = 0 + · simp [h0] + · by_cases hj : j = S.modeOf (x a) (c a) <;> simp [h0, hj] + +theorem contrib_polyTime {r : α → Rw} {γ : α → List ℕ} {c : α → ℕ} {y : α → List ℕ} + (hr : ComputableInPolyTime r encIn encRw) + (hγ : ComputableInPolyTime γ encIn (encList encNat)) + (hc : ComputableInPolyTime c encIn encNat) + (hy : ComputableInPolyTime y encIn (encList encNat)) : + ComputableInPolyTime (fun a => S.contrib (x a) (r a) (γ a) (c a) (y a)) encIn + (encList encNat) := by + have hp := precision_polyTime hS hx + have hfl := coeffs_polyTime hS hx + have hk := modeOf_polyTime hS hx hc + have hterm (o : α → Option ℕ) (ho : ComputableInPolyTime o encIn (encOption encNat)) : + ComputableInPolyTime (fun a => S.term (x a) (r a) (o a)) encIn (encList encNat) := + hS.term.comp (hx.prod (hr.prod ho)) + exact (ComputableInPolyTime.ite (ComputableInPolyTime.beq hc (ComputableInPolyTime.const 0)) + (computableInPolyTime_mul hp hfl (hterm _ (ComputableInPolyTime.const none)) hy) + (ComputableInPolyTime.ite (ComputableInPolyTime.beq + (ComputableInPolyTime.listGetD_binary (encA := encNat) 0 hγ hk) + (ComputableInPolyTime.const 0)) + (zero_polyTime hS hx) + (computableInPolyTime_mul hp hfl + (hterm _ ((ComputableInPolyTime.optionSome encNat).comp hk)) hy))).of_enc_eq + fun a => by + by_cases h0 : c a = 0 + · simp [contrib, h0] + · simp [contrib, h0] + +end Lift + +/-- The leaf is polynomial-time. -/ +theorem leafValue_polyTime : + ComputableInPolyTime (fun x : Node C Rw => S.leafValue x.1 x.2.2) (encNode encC encRw) + (encList encNat) := by + have hx := ComputableInPolyTime.fst encC (encPair (encList encRw) (encList encNat)) + have hγ := (ComputableInPolyTime.snd (encList encRw) (encList encNat)).comp + (ComputableInPolyTime.snd encC (encPair (encList encRw) (encList encNat))) + have htest : ComputableInPolyTime (fun z : Node C Rw × ℕ => z.1.2.2.getD z.2 0 == 0) + (encPair (encNode encC encRw) encNat) encBool := + ComputableInPolyTime.beq (ComputableInPolyTime.listGetD_binary (encA := encNat) 0 + (hγ.comp (ComputableInPolyTime.fst _ _)) (ComputableInPolyTime.snd _ _)) + (ComputableInPolyTime.const 0) + exact ComputableInPolyTime.ite (ComputableInPolyTime.allWithInput (hS.modes.comp hx) htest) + (computableInPolyTime_one (coeffs_polyTime hS hx)) (zero_polyTime hS hx) + +/-- **The components of coefficient extraction are polynomial-time.** -/ +theorem scheme_polyTime : S.scheme.PolyTime (encNode encC encRw) encAcc (encList encNat) where + isLeaf := (ComputableInPolyTime.listIsEmpty (encA := encRw)).comp + ((ComputableInPolyTime.fst _ _).comp (ComputableInPolyTime.snd _ _)) + start := (ComputableInPolyTime.const 0).prod (zero_polyTime hS (ComputableInPolyTime.fst _ _)) + next := by + have hn := ComputableInPolyTime.fst (encNode encC encRw) encAcc + have hx := (ComputableInPolyTime.fst _ _).comp hn + have hrows := (ComputableInPolyTime.fst (encList encRw) (encList encNat)).comp + ((ComputableInPolyTime.snd _ _).comp hn) + have hγ := (ComputableInPolyTime.snd (encList encRw) (encList encNat)).comp + ((ComputableInPolyTime.snd _ _).comp hn) + have hc := (ComputableInPolyTime.fst encNat (encList encNat)).comp + (ComputableInPolyTime.snd (encNode encC encRw) encAcc) + have hm := ComputableInPolyTime.unary_to_binary.comp + ((ComputableInPolyTime.length_list encNat).comp (hS.modes.comp hx)) + have hsome : ComputableInPolyTime (fun z : Node C Rw × (ℕ × List ℕ) => + some (z.1.1, (z.1.2.1.tail, S.childExps z.1.1 z.1.2.2 z.2.1))) + (encPair (encNode encC encRw) encAcc) (encOption (encNode encC encRw)) := + (ComputableInPolyTime.optionSome (encNode encC encRw)).comp + (hx.prod (((ComputableInPolyTime.tail (encA := encRw)).comp hrows).prod + (childExps_polyTime hS hx hγ hc))) + refine (ComputableInPolyTime.ite ((ComputableInPolyTime.listIsEmpty (encA := encRw)).comp hrows) + (ComputableInPolyTime.const none) + (ComputableInPolyTime.ite (ComputableInPolyTime.binary_le' hc hm) hsome + (ComputableInPolyTime.const none))).of_enc_eq fun z => ?_ + by_cases he : z.1.2.1 = [] + · simp [scheme, he] + · by_cases hle : z.2.1 ≤ (S.modes z.1.1).length <;> simp [scheme, he, hle] + absorb := by + have hq := ComputableInPolyTime.fst (encPair (encNode encC encRw) encAcc) (encList encNat) + have hn := (ComputableInPolyTime.fst (encNode encC encRw) encAcc).comp hq + have ha := (ComputableInPolyTime.snd (encNode encC encRw) encAcc).comp hq + have hy := ComputableInPolyTime.snd (encPair (encNode encC encRw) encAcc) (encList encNat) + have hx := (ComputableInPolyTime.fst _ _).comp hn + have hrows := (ComputableInPolyTime.fst (encList encRw) (encList encNat)).comp + ((ComputableInPolyTime.snd _ _).comp hn) + have hγ := (ComputableInPolyTime.snd (encList encRw) (encList encNat)).comp + ((ComputableInPolyTime.snd _ _).comp hn) + have hc := (ComputableInPolyTime.fst encNat (encList encNat)).comp ha + have hs := (ComputableInPolyTime.snd encNat (encList encNat)).comp ha + let E := encPair (encPair (encNode encC encRw) encAcc) (encList encNat) + have hbody : ComputableInPolyTime + (fun w : ((Node C Rw × (ℕ × List ℕ)) × List ℕ) × Rw => + add (S.prec w.1.1.1.1).1 (S.prec w.1.1.1.1).2 w.1.1.2.2 + (S.contrib w.1.1.1.1 w.2 w.1.1.1.2.2 w.1.1.2.1 w.1.2)) (encPair E encRw) + (encList encNat) := by + have h1 := ComputableInPolyTime.fst E encRw + exact computableInPolyTime_add (precision_polyTime hS (hx.comp h1)) + (coeffs_polyTime hS (hx.comp h1)) (hs.comp h1) + (contrib_polyTime hS (hx.comp h1) (ComputableInPolyTime.snd E encRw) (hγ.comp h1) + (hc.comp h1) (hy.comp h1)) + exact (ComputableInPolyTime.binary_add.comp (hc.prod (ComputableInPolyTime.const 1))).prod + (ComputableInPolyTime.optionElimWithInput ((ComputableInPolyTime.head? encRw).comp hrows) + hs hbody) + finish := (ComputableInPolyTime.snd encNat (encList encNat)).comp (ComputableInPolyTime.snd _ _) + + +/-! ### The native machine and its resources -/ + +/-- The number of steps: `2 (m + 2) ^ L` for `m` modes and `L` rows, at least the `2 N - 1` +steps of a derivation with `N ≤ (m + 2) ^ L` nodes. -/ +def fuel (x : Node C Rw) : ℕ := 2 * ((S.modes x.1).length + 2) ^ x.2.1.length + +theorem fuel_polyTime : ComputableInPolyTime S.fuel (encNode encC encRw) encNat := by + have hx := ComputableInPolyTime.fst encC (encPair (encList encRw) (encList encNat)) + have hrows := (ComputableInPolyTime.fst (encList encRw) (encList encNat)).comp + (ComputableInPolyTime.snd encC (encPair (encList encRw) (encList encNat))) + exact ComputableInPolyTime.binary_mul' (ComputableInPolyTime.const 2) + (ComputableInPolyTime.binary_pow' (ComputableInPolyTime.binary_add.comp + ((ComputableInPolyTime.unary_to_binary.comp ((ComputableInPolyTime.length_list encNat).comp + (hS.modes.comp hx))).prod (ComputableInPolyTime.const 2))) + ((ComputableInPolyTime.length_list encRw).comp hrows)) + +/-- The leaf certificate. -/ +noncomputable def leafCertificate : + NativeCertificate S.scheme.leaf (encNode encC encRw) (encList encNat) := + NativeCertificate.ofPolyTime (leafValue_polyTime hS) + +/-- **One native machine for coefficient extraction**, from polynomial-time data: the clocked +run of the scheme from the node itself. -/ +noncomputable def runCertificate : + NativeCertificate (S.scheme.run S.fuel id) (encNode encC encRw) + (encOption (encList encNat)) := + Scheme.certificate (scheme_polyTime hS) (leafCertificate hS) + (NativeCertificate.ofPolyTime (fuel_polyTime hS)) + (NativeCertificate.ofPolyTime ComputableInPolyTime.id') + +omit hS in +/-- **The machine computes `value`.** -/ +theorem run_scheme (x : Node C Rw) : S.scheme.run S.fuel id x = some (S.value x.1 x.2.1 x.2.2) := by + obtain ⟨h, -, he⟩ := S.eval_root x.1 x.2.1 x.2.2 + refine Scheme.run_eq_some he ?_ + have := count_le (S.modes x.1).length x.2.1.length + simp only [fuel] + omega + +/-- **Coefficient extraction, as a native certificate** for `value` on every node. -/ +noncomputable def valueCertificate : + NativeCertificate (fun x : Node C Rw => S.value x.1 x.2.1 x.2.2) (encNode encC encRw) + (encList encNat) := + (NativeCertificate.post (runCertificate hS) + (ComputableInDegree.getD (enc₁ := encList encNat) (enc := encOption (encList encNat)) + []).inO_one).congr fun x => by simp [run_scheme] + +@[simp] theorem valueCertificate_time : + (valueCertificate hS).time = (runCertificate hS).time := rfl + +@[simp] theorem valueCertificate_space : + (valueCertificate hS).space = (runCertificate hS).space := rfl + +omit hS in +private theorem sum_map_le_of_getD_le {g : ℕ → ℕ} (hg : Monotone g) : + ∀ (γ γ₀ : List ℕ), γ.length = γ₀.length → (∀ j, γ.getD j 0 ≤ γ₀.getD j 0) → + (γ.map g).sum ≤ (γ₀.map g).sum + | [], [], _, _ => le_rfl + | a :: γ, b :: γ₀, hl, h => by + simp only [List.map_cons, List.sum_cons] + exact Nat.add_le_add (hg (by simpa using h 0)) + (sum_map_le_of_getD_le hg γ γ₀ (by simpa using hl) fun j => by simpa using h (j + 1)) + +omit hS in +/-- A node of the derivation from a root is at most as long as the root. -/ +theorem length_encNode_le {x₀ : C} {rows₀ : List Rw} {γ₀ : List ℕ} {x : Node C Rw} + (hx : (S.invariant x₀ rows₀ γ₀).node x) : + (encNode encC encRw x).length ≤ (encNode encC encRw (x₀, (rows₀, γ₀))).length := by + obtain ⟨hx₀, ⟨pre, hpre⟩, hl, hγ⟩ := hx + obtain ⟨x', rs, γ⟩ := x + simp only at hx₀ hpre hl hγ + subst hx₀ hpre + have h1 : (encList encRw rs).length ≤ (encList encRw (pre ++ rs)).length := by + simp only [length_encList, List.map_append, List.sum_append] + omega + have h2 : (encList encNat γ).length ≤ (encList encNat γ₀).length := by + simp only [length_encList] + exact sum_map_le_of_getD_le (fun a b hab => by + change 2 * a.bits.length + 1 ≤ 2 * b.bits.length + 1 + rw [Nat.size_eq_bits_len, Nat.size_eq_bits_len] + exact Nat.add_le_add_right (Nat.mul_le_mul_left 2 (Nat.size_le_size hab)) 1) γ γ₀ hl hγ + simp only [length_encPair] + omega + +omit hS in +private theorem length_encNat_lt {y P : ℕ} (h : y < 2 ^ P) : (encNat y).length ≤ P := by + have h' := Nat.size_le.mpr h + rw [← Nat.size_eq_bits_len] at h' + exact h' + +/-- **The resources of coefficient extraction.** There is `K` such that on a node of length `n` +with `m` modes and `L` rows, if the reduced values of its precision are at most `Rv` bits long, +then with `Z = n + m + Rv + 2` the time is at most `K (m + 2) ^ L Z ^ K` and the space at most +`K Z ^ K`: time `exp(O(L log m))` times a polynomial, in polynomial space. -/ +theorem valueCertificate_le : + ∃ K : ℕ, ∀ (x : Node C Rw) (Rv : ℕ), + (∀ y, SumOps.galois.Reduced (S.prec x.1) y → (encList encNat y).length ≤ Rv) → + (valueCertificate hS).time x ≤ K * ((S.modes x.1).length + 2) ^ x.2.1.length * + ((encNode encC encRw x).length + (S.modes x.1).length + Rv + 2) ^ K ∧ + (valueCertificate hS).space x ≤ + K * ((encNode encC encRw x).length + (S.modes x.1).length + Rv + 2) ^ K := by + obtain ⟨K₀, hK₀⟩ := Scheme.certificate_le (scheme_polyTime hS) (leafCertificate hS) + have hL := NativeCertificate.ofPolyTime_isPolynomial (leafValue_polyTime hS) + have hF := NativeCertificate.ofPolyTime_isPolynomial (fuel_polyTime hS) + have hR := NativeCertificate.ofPolyTime_isPolynomial + (ComputableInPolyTime.id' (encIn := encNode encC encRw)) + obtain ⟨cl, dl, hlt⟩ := hL.1.exists_le + obtain ⟨cl', dl', hls⟩ := hL.2.exists_le + obtain ⟨cf, df, hft⟩ := hF.1.exists_le + obtain ⟨cf', df', hfs⟩ := hF.2.exists_le + obtain ⟨cr, dr, hrt⟩ := hR.1.exists_le + obtain ⟨cr', dr', hrs⟩ := hR.2.exists_le + obtain ⟨D, hD⟩ : ∃ D, D = 2 * K₀ + dl + dl' + df + df' + dr + dr' + 4 := ⟨_, rfl⟩ + obtain ⟨G, hG⟩ : ∃ G, G = cf + cr + 2 + 3 * (K₀ * 8 ^ K₀ + cl + 9) := ⟨_, rfl⟩ + obtain ⟨G', hG'⟩ : ∃ G', G' = cf' + cr' + 2 + K₀ * 8 ^ K₀ + cl' + 2 := ⟨_, rfl⟩ + refine ⟨G + G' + D, fun x Rv hRv => ?_⟩ + obtain ⟨x₀, rows₀, γ₀⟩ := x + simp only + obtain ⟨n, hn⟩ : ∃ n, n = (encNode encC encRw (x₀, (rows₀, γ₀))).length := ⟨_, rfl⟩ + obtain ⟨m, hm⟩ : ∃ m, m = (S.modes x₀).length := ⟨_, rfl⟩ + obtain ⟨L, hLen⟩ : ∃ L, L = rows₀.length := ⟨_, rfl⟩ + obtain ⟨Z, hZ⟩ : ∃ Z, Z = n + m + Rv + 2 := ⟨_, rfl⟩ + obtain ⟨W, hW⟩ : ∃ W, W = Z ^ D := ⟨_, rfl⟩ + rw [← hn, ← hm, ← hLen, ← hZ] + have hZ2 : 2 ≤ Z := by omega + have hLn : L ≤ n := by + have := length_le_length_encList encRw rows₀ + rw [hn, hLen] + simp only [length_encPair] + omega + have hpowZ {a : ℕ} (ha : a ≤ D) : Z ^ a ≤ W := by + rw [hW]; exact Nat.pow_le_pow_right (by omega) ha + have hZW : Z ≤ W := by + calc Z = Z ^ 1 := (pow_one Z).symm + _ ≤ W := hpowZ (by omega) + have hn1 {c e : ℕ} (he : e ≤ D) : c * (n + 1) ^ e ≤ c * W := + Nat.mul_le_mul_left _ ((Nat.pow_le_pow_left (by omega) _).trans (hpowZ he)) + have hP : 1 ≤ (m + 2) ^ L := Nat.one_le_pow _ _ (by omega) + -- The derivation and the bounds on its nodes, frames and values. + obtain ⟨h, hh, he⟩ := S.eval_root x₀ rows₀ γ₀ + have hnode (x : Node C Rw) (hx : (S.invariant x₀ rows₀ γ₀).node x) : + (encNode encC encRw x).length ≤ n := hn ▸ length_encNode_le (S := S) hx + have hres := hK₀ (NativeCertificate.ofPolyTime (fuel_polyTime hS)) + (NativeCertificate.ofPolyTime ComputableInPolyTime.id') (S.invariant x₀ rows₀ γ₀) (3 * Z) + (cl * Z ^ dl) (cl' * Z ^ dl') + (fun x hx => ⟨(hnode x hx).trans (by omega), fun _ => + ⟨(hlt x).trans (Nat.mul_le_mul_left _ ((Nat.pow_le_pow_left (by + have := hnode x hx; omega) _))), + (hls x).trans (Nat.mul_le_mul_left _ ((Nat.pow_le_pow_left (by + have := hnode x hx; omega) _)))⟩⟩) + (fun x a ha => by + obtain ⟨hx, ha1, ha2⟩ := ha + have h1 := hnode x hx + have h2 : (encNat a.1).length ≤ m + 2 := + length_encNat_lt (lt_of_le_of_lt (hm ▸ ha1) (by + have := Nat.lt_two_pow_self (n := m + 2); omega)) + have h3 := hRv a.2 ha2 + have h4 : (encPair (encNode encC encRw) encAcc (x, a)).length = + 2 * (encNode encC encRw x).length + 1 + (encAcc a).length := length_encPair (x, a) + have h5 : (encAcc a).length = 2 * (encNat a.1).length + 1 + (encList encNat a.2).length := + length_encPair a + omega) + (fun y hy => by have := hRv y hy; omega) + (x₀, (rows₀, γ₀)) _ _ _ he + obtain ⟨htime, hspace⟩ := hres + -- The fuel and its clock. + have hfuel : S.fuel (x₀, (rows₀, γ₀)) = 2 * (m + 2) ^ L := by simp only [fuel, hm, hLen] + have hfuelLen : (encNat (S.fuel (x₀, (rows₀, γ₀)))).length ≤ 2 * Z ^ 2 := by + have hpow : (m + 2) ^ L < 2 ^ ((m + 2) * L + 1) := by + calc (m + 2) ^ L ≤ (2 ^ (m + 2)) ^ L := + Nat.pow_le_pow_left (Nat.lt_two_pow_self).le _ + _ = 2 ^ ((m + 2) * L) := (pow_mul _ _ _).symm + _ < 2 ^ ((m + 2) * L + 1) := Nat.pow_lt_pow_right (by norm_num) (by omega) + have h1 : S.fuel (x₀, (rows₀, γ₀)) < 2 ^ ((m + 2) * L + 2) := by + rw [hfuel, pow_succ]; omega + have h2 := length_encNat_lt h1 + have h3 : (m + 2) * L ≤ Z * Z := Nat.mul_le_mul (by omega) (by omega) + have h4 : 4 ≤ Z * Z := Nat.mul_le_mul hZ2 hZ2 + rw [sq] + omega + have hclock : ((encNat (S.fuel (x₀, (rows₀, γ₀)))).length + 1) ^ 2 ≤ 9 * W := by + have hZsq : 1 ≤ Z ^ 2 := Nat.one_le_pow _ _ (by omega) + calc ((encNat (S.fuel (x₀, (rows₀, γ₀)))).length + 1) ^ 2 ≤ (3 * Z ^ 2) ^ 2 := + Nat.pow_le_pow_left (by omega) 2 + _ = 9 * Z ^ 4 := by ring + _ ≤ 9 * W := Nat.mul_le_mul_left _ (hpowZ (by omega)) + have hstack : K₀ * ((h + 1) * (3 * Z + 1)) ^ K₀ ≤ K₀ * 8 ^ K₀ * W := by + have h1 : (h + 1) * (3 * Z + 1) ≤ 8 * Z ^ 2 := by + have hh2 : h + 1 ≤ 2 * Z := by omega + have hZZ : Z ≤ Z ^ 2 := Nat.le_self_pow (by norm_num) Z + calc (h + 1) * (3 * Z + 1) ≤ (2 * Z) * (3 * Z + 1) := Nat.mul_le_mul_right _ hh2 + _ = 6 * Z ^ 2 + 2 * Z := by ring + _ ≤ 8 * Z ^ 2 := by omega + calc K₀ * ((h + 1) * (3 * Z + 1)) ^ K₀ ≤ K₀ * (8 * Z ^ 2) ^ K₀ := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_left h1 _) + _ = K₀ * 8 ^ K₀ * Z ^ (2 * K₀) := by rw [Nat.mul_pow, ← pow_mul]; ring + _ ≤ K₀ * 8 ^ K₀ * W := Nat.mul_le_mul_left _ (hpowZ (by omega)) + have hTL : cl * Z ^ dl ≤ cl * W := Nat.mul_le_mul_left _ (hpowZ (by omega)) + have hSL : cl' * Z ^ dl' ≤ cl' * W := Nat.mul_le_mul_left _ (hpowZ (by omega)) + have hcf := (hft (x₀, (rows₀, γ₀))).trans (hn ▸ hn1 (c := cf) (show df ≤ D by omega)) + have hcr := (hrt (x₀, (rows₀, γ₀))).trans (hn ▸ hn1 (c := cr) (show dr ≤ D by omega)) + have hcf' := (hfs (x₀, (rows₀, γ₀))).trans (hn ▸ hn1 (c := cf') (show df' ≤ D by omega)) + have hcr' := (hrs (x₀, (rows₀, γ₀))).trans (hn ▸ hn1 (c := cr') (show dr' ≤ D by omega)) + rw [← hn] at htime hspace + have hWK : W ≤ Z ^ (G + G' + D) := hW ▸ Nat.pow_le_pow_right (by omega) (by omega) + have hnW : n ≤ W := by omega + constructor + · rw [valueCertificate_time] + unfold runCertificate + obtain ⟨P, hPdef⟩ : ∃ P, P = (m + 2) ^ L := ⟨_, rfl⟩ + rw [← hPdef] at hP ⊢ + obtain ⟨Y, hY⟩ : ∃ Y, Y = K₀ * ((h + 1) * (3 * Z + 1)) ^ K₀ + cl * Z ^ dl + + ((encNat (S.fuel (x₀, (rows₀, γ₀)))).length + 1) ^ 2 := ⟨_, rfl⟩ + rw [← hY] at htime + have hfP : (S.fuel (x₀, (rows₀, γ₀)) + 1) * Y = (2 * P + 1) * Y := by rw [hfuel, hPdef] + have hstep : Y ≤ (K₀ * 8 ^ K₀ + cl + 9) * W := by + have e : (K₀ * 8 ^ K₀ + cl + 9) * W = K₀ * 8 ^ K₀ * W + cl * W + 9 * W := by ring + omega + have h1 := Nat.mul_le_mul_left (2 * P + 1) hstep + have h2 : (2 * P + 1) * ((K₀ * 8 ^ K₀ + cl + 9) * W) ≤ + P * (3 * (K₀ * 8 ^ K₀ + cl + 9) * W) := by + have : 2 * P + 1 ≤ 3 * P := by omega + calc (2 * P + 1) * ((K₀ * 8 ^ K₀ + cl + 9) * W) + ≤ (3 * P) * ((K₀ * 8 ^ K₀ + cl + 9) * W) := Nat.mul_le_mul_right _ this + _ = P * (3 * (K₀ * 8 ^ K₀ + cl + 9) * W) := by ring + have h3 : cf * W + cr * W + 2 * W ≤ P * ((cf + cr + 2) * W) := by + have e : cf * W + cr * W + 2 * W = (cf + cr + 2) * W := by ring + rw [e]; exact Nat.le_mul_of_pos_left _ hP + have h4 : P * ((cf + cr + 2) * W) + P * (3 * (K₀ * 8 ^ K₀ + cl + 9) * W) = P * (G * W) := by + rw [hG]; ring + calc _ ≤ P * (G * W) := by omega + _ ≤ (G + G' + D) * P * Z ^ (G + G' + D) := by + rw [show (G + G' + D) * P * Z ^ (G + G' + D) = P * ((G + G' + D) * Z ^ (G + G' + D)) by + ring] + exact Nat.mul_le_mul_left _ (Nat.mul_le_mul (by omega) hWK) + · rw [valueCertificate_space] + unfold runCertificate + have h1 : (encNat (S.fuel (x₀, (rows₀, γ₀)))).length ≤ 2 * W := by + have := hpowZ (show 2 ≤ D by omega); omega + calc _ ≤ cf' * W + cr' * W + 2 * W + K₀ * 8 ^ K₀ * W + cl' * W + 2 * W := by omega + _ = G' * W := by rw [hG']; ring + _ ≤ (G + G' + D) * Z ^ (G + G' + D) := Nat.mul_le_mul (by omega) hWK + +end PolyTime + +end Terms + +end CoeffScheme + +end Cslib.Circuits.Frontier diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkCoeffScheme.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkCoeffScheme.lean new file mode 100644 index 0000000000..4c20e833d8 --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkCoeffScheme.lean @@ -0,0 +1,62 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CoeffScheme + +/-! +# Tests for coefficient extraction as a stack recursion + +Two rows `1` and `2` over `A_3 = (ℤ / 8)[U] / (U ^ 2 + U + 1)`, with the modes of dense indices +`0` and `2`. The row `r` has the local term `r` and the coefficient `k + 1 + U` at the mode `k`. +The coefficient of the mode `0` in the product of the two row factors is +`1 (1 + U) + (1 + U) 2 = 3 + 3 U`, and the machine returns it. +-/ + +@[expose] public section + +namespace ComplexityTests.TensorNetworkCoeffScheme + +open Cslib Cslib.Circuits.Frontier Cslib.Circuits.Frontier.CoeffScheme + +/-- The data of the test. -/ +def terms : Terms Unit ℕ where + prec _ := (3, [1, 1]) + modes _ := [0, 2] + term _ r o := match o with + | none => [r, 0] + | some k => [k + 1, 1] + +-- The value, at exponent `1` on the mode `0`. +example : terms.value () [1, 2] [1, 0, 0] = [3, 3] := by decide + +-- The stack machine, with fuel `2 (2 + 2) ^ 2 = 32`, returns it. +example : terms.scheme.run terms.fuel id ((), ([1, 2], [1, 0, 0])) = some [3, 3] := by decide + +-- With both exponents zero, the coefficient of the constant term is `1 * 2 = 2`. +example : terms.value () [1, 2] [0, 0, 0] = [2, 0] := by decide + +-- A mode needed twice from one row factor has coefficient zero. +example : terms.value () [1] [2, 0, 0] = [0, 0] := by decide + +-- On every node, the run is the value, and the value in `A_p` is `listCoeff`. +example (x : Node Unit ℕ) : + terms.scheme.run terms.fuel id x = some (terms.value x.1 x.2.1 x.2.2) := + terms.run_scheme x + +example (rows : List ℕ) (γ : List ℕ) : + GaloisRingArith.val 3 [1, 1] (terms.value () rows γ) = + TensorNetwork.listCoeff ({0, 2} : Finset ℕ) + (rows.map fun r o => GaloisRingArith.val 3 [1, 1] (terms.term () r o)) + (fun k => γ.getD k 0) := + terms.val_value () (by decide) rows γ + +-- At most `(m + 2) ^ L` nodes. +example : count 2 2 = 13 ∧ count 2 2 ≤ (2 + 2) ^ 2 := by decide + +end ComplexityTests.TensorNetworkCoeffScheme From 3130c30f154ebf91cc7fde8f90727f18e62a6fd6 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 00:47:04 -0400 Subject: [PATCH 050/129] refactor(circuit/frontier): take the number of slots L as a parameter of kernel values Kernel values iterate over the 2^(a+1) grid points, so a native certificate needs the number of slots L = 2^a in unary. The native functions now take L, and the correctness theorems assume L = 2^a. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/PacketEvaluator.lean | 114 ++++++++++-------- .../TensorNetworkPacketEvaluator.lean | 30 ++--- 2 files changed, 76 insertions(+), 68 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean index e9e8449c56..0d0f3f82e7 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean @@ -27,7 +27,8 @@ ring that `A_p` maps to. `ℤ_(2)` to a ring in which `2` is nilpotent agree (`padicInteger_ringHom_ext`). An integer is a constant list (`NativePacket.intConst`). * **Kernel values** (`NativePacket.kernelValueList`) follow Lemma D.4's evaluation - (`kernelValue_eq_prod`, p. 60): the inverse of the odd part of `C'(0)`, times + (`kernelValue_eq_prod`, p. 60), over the grid of `L = 2 ^ a` slots, with `L` a parameter so that + it can be read in unary: the inverse of the odd part of `C'(0)`, times `2 ^ (a + 1 - v_d) ζ` when `d ≠ t`, times the `2 ^ (a + 1) - 2` linear factors `u_q + 2 ^ (a + 1 - v_q) ζ`. The odd part of `C'(0)` is a product of odd integers, so it is `1` modulo `2` and `GaloisRingArith.inv` inverts it. @@ -298,16 +299,16 @@ theorem exists_val_linFactor_eq {u : ℤ} (hu : Odd u) {k : ℕ} (hk : 1 ≤ k) obtain ⟨k', rfl⟩ := Nat.exists_eq_add_of_le hk exact ⟨w + 2 ^ k' * val p fl ζ, by rw [val_linFactor, hw, pow_add]; ring⟩ -/-- The grid `[-2 ^ a, 2 ^ a)` of `2 ^ a` slots, in increasing order. -/ -def gridList (a : ℕ) : List ℤ := - (List.range (2 ^ (a + 1))).map fun j : ℕ => (j : ℤ) - ((2 ^ a : ℕ) : ℤ) +/-- The grid `[-L, L)` of `L` slots, in increasing order. The number of slots is a parameter, in +place of `2 ^ a`, so that it can be read in unary. -/ +def gridList (L : ℕ) : List ℤ := + (List.range (2 * L)).map fun j : ℕ => (j : ℤ) - (L : ℤ) -theorem nodup_gridList (a : ℕ) : (gridList a).Nodup := +theorem nodup_gridList (L : ℕ) : (gridList L).Nodup := List.Nodup.map (fun x y h => by simpa using h) List.nodup_range -theorem mem_gridList {a : ℕ} {q : ℤ} : q ∈ gridList a ↔ q ∈ cauchyGrid (2 ^ a) := by - rw [gridList, List.mem_map, mem_cauchyGrid, pow_succ] - generalize 2 ^ a = L +theorem mem_gridList {L : ℕ} {q : ℤ} : q ∈ gridList L ↔ q ∈ cauchyGrid L := by + rw [gridList, List.mem_map, mem_cauchyGrid] constructor · rintro ⟨j, hj, rfl⟩ rw [List.mem_range] at hj @@ -316,29 +317,30 @@ theorem mem_gridList {a : ℕ} {q : ℤ} : q ∈ gridList a ↔ q ∈ cauchyGrid refine ⟨(q + L).toNat, List.mem_range.mpr ?_, ?_⟩ <;> omega /-- The odd part of `C'(0) = ∏_{q ≠ 0} (0 - q)`, as a coefficient list. -/ -def kernelUnitDen (p : ℕ) (fl : List ℕ) (a : ℕ) : List ℕ := - prodList p fl (((gridList a).filter (· ≠ 0)).map fun q => +def kernelUnitDen (p : ℕ) (fl : List ℕ) (L : ℕ) : List ℕ := + prodList p fl (((gridList L).filter (· ≠ 0)).map fun q => intConst p fl ((0 - q) / 2 ^ twoVal (0 - q))) /-- `κ (kernelUnit a)`, the inverse of the odd part of `C'(0)`. -/ -def kernelUnitList (p : ℕ) (fl : List ℕ) (a : ℕ) : List ℕ := - inv p fl (kernelUnitDen p fl a) +def kernelUnitList (p : ℕ) (fl : List ℕ) (L : ℕ) : List ℕ := + inv p fl (kernelUnitDen p fl L) /-- The value of `kernelUnitDen`. -/ -private theorem val_kernelUnitDen (a : ℕ) : - val p fl (kernelUnitDen p fl a) = ∏ q ∈ (cauchyGrid (2 ^ a)).erase 0, +private theorem val_kernelUnitDen (L : ℕ) : + val p fl (kernelUnitDen p fl L) = ∏ q ∈ (cauchyGrid L).erase 0, (((0 - q) / 2 ^ padicValInt 2 (0 - q) : ℤ) : Ring p fl) := by simp only [kernelUnitDen, twoVal_eq] rw [val_prodList, List.map_map, ← List.prod_toFinset _ - ((nodup_gridList a).filter _)] + ((nodup_gridList L).filter _)] refine prod_congr ?_ fun q _ => val_intConst _ ext q simp only [List.mem_toFinset, List.mem_filter, mem_gridList, mem_erase, decide_eq_true_eq] tauto /-- **The kernel unit**: `kernelUnitList` is the reduction of `kernelUnit a`. -/ -theorem val_kernelUnitList (a : ℕ) : - val p fl (kernelUnitList p fl a) = scalarHom p fl (kernelUnit a) := by +theorem val_kernelUnitList {a L : ℕ} (hL : L = 2 ^ a) : + val p fl (kernelUnitList p fl L) = scalarHom p fl (kernelUnit a) := by + subst hL have hne (q : ℤ) (hq : q ∈ (cauchyGrid (2 ^ a)).erase 0) : (0 : ℤ) - q ≠ 0 := sub_ne_zero.mpr (Ne.symm (ne_of_mem_erase hq)) -- The odd part of `C'(0)` is one modulo two. @@ -348,7 +350,8 @@ theorem val_kernelUnitList (a : ℕ) : obtain ⟨q, hq, rfl⟩ := List.mem_map.mp hy exact intCast_odd (odd_ediv_two_pow (hne q (Finset.mem_toList.mp hq))) rw [Finset.prod_map_toList] at hw - have hinv := val_mul_inv_of_eq (kernelUnitDen p fl a) (w := w) (by rw [val_kernelUnitDen, hw]) + have hinv := val_mul_inv_of_eq (kernelUnitDen p fl (2 ^ a)) (w := w) + (by rw [val_kernelUnitDen, hw]) refine eq_of_mul_eq_one hinv ?_ -- `kernelUnit a` inverts the odd part of `C'(0)` in `ℤ_(2)`. have hunit : kernelUnit a * ∏ q ∈ (cauchyGrid (2 ^ a)).erase 0, @@ -369,21 +372,23 @@ theorem val_kernelUnitList (a : ℕ) : /-- **Kernel values on coefficient lists** (Lemma D.4's evaluation, p. 60): the kernel unit, times `2 ^ (a + 1 - v_d) ζ` when `d ≠ t`, times the linear factors `u_q + 2 ^ (a + 1 - v_q) ζ` over the grid points `q ≠ d, t`. -/ -def kernelValueList (p : ℕ) (fl : List ℕ) (a : ℕ) (t d : ℤ) (ζ : List ℕ) : List ℕ := - mul p fl (mul p fl (kernelUnitList p fl a) +def kernelValueList (p : ℕ) (fl : List ℕ) (a L : ℕ) (t d : ℤ) (ζ : List ℕ) : List ℕ := + mul p fl (mul p fl (kernelUnitList p fl L) (if d = t then one fl else mul p fl (intConst p fl (2 ^ (a + 1 - twoVal (t - d)))) ζ)) - (prodList p fl (((gridList a).filter fun q => q ≠ d ∧ q ≠ t).map fun q => + (prodList p fl (((gridList L).filter fun q => q ≠ d ∧ q ≠ t).map fun q => linFactor p fl ((t - q) / 2 ^ twoVal (t - q)) (a + 1 - twoVal (t - q)) ζ)) /-- **Kernel values are correct**: for grid points `t` and `d`, the value is `kernelValue` at the reduction of `ℤ_(2)` and the value of `ζ`. -/ -theorem val_kernelValueList {a : ℕ} {t d : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) +theorem val_kernelValueList {a L : ℕ} (hL : L = 2 ^ a) {t d : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) (hd : d ∈ cauchyGrid (2 ^ a)) (ζ : List ℕ) : - val p fl (kernelValueList p fl a t d ζ) = kernelValue (scalarHom p fl) (val p fl ζ) a t d := by + val p fl (kernelValueList p fl a L t d ζ) = + kernelValue (scalarHom p fl) (val p fl ζ) a t d := by + subst hL simp only [kernelValueList, twoVal_eq] - rw [kernelValue_eq_prod _ _ ht hd, val_mul, val_mul, val_kernelUnitList, - val_prodList, List.map_map, ← List.prod_toFinset _ ((nodup_gridList a).filter _)] + rw [kernelValue_eq_prod _ _ ht hd, val_mul, val_mul, val_kernelUnitList rfl, + val_prodList, List.map_map, ← List.prod_toFinset _ ((nodup_gridList _).filter _)] congr 1 · congr 1 split_ifs @@ -425,42 +430,43 @@ theorem val_crossRatioList {a i : ℕ} (hia : i ≤ a) {t d₀ : ℤ} /-- The local form of a row port in slot `x`: the sum of the kernel values at the column ports set to one. -/ -def localFormList (p : ℕ) (fl : List ℕ) (a : ℕ) (t : ℤ) (cols : List (ℕ × Bool)) (x : ℕ) +def localFormList (p : ℕ) (fl : List ℕ) (a L : ℕ) (t : ℤ) (cols : List (ℕ × Bool)) (x : ℕ) (ζ : List ℕ) : List ℕ := - sumList p fl (cols.map fun c => if c.2 then kernelValueList p fl a t ((c.1 : ℤ) - x) ζ + sumList p fl (cols.map fun c => if c.2 then kernelValueList p fl a L t ((c.1 : ℤ) - x) ζ else zero fl) -theorem val_localFormList {a : ℕ} {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) +theorem val_localFormList {a L : ℕ} (hL : L = 2 ^ a) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) {cols : List (ℕ × Bool)} (hcols : ∀ c ∈ cols, c.1 < 2 ^ a) {x : ℕ} (hx : x < 2 ^ a) (ζ : List ℕ) : - val p fl (localFormList p fl a t cols x ζ) = + val p fl (localFormList p fl a L t cols x ζ) = packetLocalForm (scalarHom p fl) (val p fl ζ) a t cols x := by rw [localFormList, val_sumList, List.map_map, packetLocalForm] congr 1 refine List.map_congr_left fun c hc => ?_ simp only [Function.comp_apply] split_ifs - · exact val_kernelValueList ht (sub_mem_cauchyGrid hx (hcols c hc)) ζ + · exact val_kernelValueList hL ht (sub_mem_cauchyGrid hx (hcols c hc)) ζ · exact val_zero /-- The coefficient of the mode `(y₀, b)` in the form of a row port in slot `x`. -/ -def modeCoeffList (p : ℕ) (fl : List ℕ) (a i : ℕ) (t : ℤ) (differ : Bool) (x y₀ b : ℕ) +def modeCoeffList (p : ℕ) (fl : List ℕ) (a L i : ℕ) (t : ℤ) (differ : Bool) (x y₀ b : ℕ) (ζ : List ℕ) : List ℕ := if differ = true ∧ x.testBit i = y₀.testBit i then - mul p fl (kernelValueList p fl a t ((y₀ : ℤ) - x) ζ) + mul p fl (kernelValueList p fl a L t ((y₀ : ℤ) - x) ζ) (pow p fl (crossRatioList p fl a i t ((y₀ : ℤ) - x) ζ) b) else zero fl -theorem val_modeCoeffList {a i : ℕ} (hia : i < a) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) +theorem val_modeCoeffList {a L i : ℕ} (hL : L = 2 ^ a) (hia : i < a) {t : ℤ} + (ht : t ∈ cauchyGrid (2 ^ a)) (hti : (2 : ℤ) ^ i ∣ t) (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) (differ : Bool) {x : ℕ} (hx : x < 2 ^ a) {nb : ℕ} (Z : Fin (2 ^ (i + 1)) × Fin nb) (ζ : List ℕ) : - val p fl (modeCoeffList p fl a i t differ x Z.1 Z.2 ζ) = + val p fl (modeCoeffList p fl a L i t differ x Z.1 Z.2 ζ) = packetModeCoeff (scalarHom p fl) (val p fl ζ) a i t differ x Z := by have hy : (Z.1 : ℕ) < 2 ^ a := Z.1.isLt.trans_le (Nat.pow_le_pow_right (by norm_num) (by omega)) rw [modeCoeffList, packetModeCoeff] split_ifs with h - · rw [val_mul, val_pow, val_kernelValueList ht (sub_mem_cauchyGrid hx hy), + · rw [val_mul, val_pow, val_kernelValueList hL ht (sub_mem_cauchyGrid hx hy), val_crossRatioList hia.le (not_dvd_sub_sub_of_testBit_eq hti hti' Z.1.isLt h.2)] · exact val_zero @@ -510,19 +516,20 @@ def modeIndex (i nb : ℕ) : Fin (2 ^ (i + 1)) × Fin nb ↪ ℕ := /-- The terms of the affine factor of a row port `r = (x, Y_x)`: its local term at `none`, and the coefficient of the mode `(k / nb, k % nb)` at `some k`. -/ -def rowTermList (p : ℕ) (fl : List ℕ) (a i : ℕ) (t : ℤ) (nb : ℕ) (differ : Bool) +def rowTermList (p : ℕ) (fl : List ℕ) (a L i : ℕ) (t : ℤ) (nb : ℕ) (differ : Bool) (cols : List (ℕ × Bool)) (ζ : List ℕ) (r : ℕ × Bool) : Option ℕ → List ℕ - | none => if r.2 then localFormList p fl a t cols r.1 ζ - else sub p fl (one fl) (localFormList p fl a t cols r.1 ζ) - | some k => if r.2 then modeCoeffList p fl a i t differ r.1 (k / nb) (k % nb) ζ - else neg p fl (modeCoeffList p fl a i t differ r.1 (k / nb) (k % nb) ζ) + | none => if r.2 then localFormList p fl a L t cols r.1 ζ + else sub p fl (one fl) (localFormList p fl a L t cols r.1 ζ) + | some k => if r.2 then modeCoeffList p fl a L i t differ r.1 (k / nb) (k % nb) ζ + else neg p fl (modeCoeffList p fl a L i t differ r.1 (k / nb) (k % nb) ζ) /-- Row terms are correct, at the dense indices of the modes of the packet. -/ -theorem val_rowTermList {a i : ℕ} (hia : i < a) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) +theorem val_rowTermList {a L i : ℕ} (hL : L = 2 ^ a) (hia : i < a) {t : ℤ} + (ht : t ∈ cauchyGrid (2 ^ a)) (hti : (2 : ℤ) ^ i ∣ t) (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) (nb : ℕ) (differ : Bool) {cols : List (ℕ × Bool)} (hcols : ∀ c ∈ cols, c.1 < 2 ^ a) {r : ℕ × Bool} (hr : r.1 < 2 ^ a) (ζ : List ℕ) : - (fun o => val p fl (rowTermList p fl a i t nb differ cols ζ r o)) ∘ + (fun o => val p fl (rowTermList p fl a L i t nb differ cols ζ r o)) ∘ Option.map (modeIndex i nb) = packetRowTerm (p := nb) (scalarHom p fl) (val p fl ζ) a i t differ cols r := by funext o @@ -530,12 +537,12 @@ theorem val_rowTermList {a i : ℕ} (hia : i < a) {t : ℤ} (ht : t ∈ cauchyGr | none => simp only [Function.comp_apply, Option.map_none, rowTermList, packetRowTerm, affineFactor] split_ifs - · exact val_localFormList ht hcols hr ζ - · rw [val_sub, val_one, val_localFormList ht hcols hr ζ] + · exact val_localFormList hL ht hcols hr ζ + · rw [val_sub, val_one, val_localFormList hL ht hcols hr ζ] | some Z => simp only [Function.comp_apply, Option.map_some, rowTermList, packetRowTerm, affineSign, modeIndex_apply, modeIndex_div Z.2.isLt, modeIndex_mod Z.2.isLt] - have h := val_modeCoeffList (p := p) (fl := fl) hia ht hti hti' differ hr Z ζ + have h := val_modeCoeffList (p := p) (fl := fl) hL hia ht hti hti' differ hr Z ζ split_ifs · rw [one_mul]; exact h · rw [val_neg, h, neg_one_mul] @@ -596,12 +603,12 @@ private theorem toFinset_filter_range (i nb : ℕ) (P : ℕ → Bool) : along the row terms, at the exponents `β` of the modes targeting the group, times the powers of the values of the modes with the group as source. A mode `(y₀, b)` is its dense index `y₀ nb + b`, so `β` is read at dense indices, as from a dense list of exponents. -/ -def packetEntryList (p : ℕ) (fl : List ℕ) (a i : ℕ) (t : ℤ) (nb : ℕ) (e₁ e₂ differ : Bool) +def packetEntryList (p : ℕ) (fl : List ℕ) (a L i : ℕ) (t : ℤ) (nb : ℕ) (e₁ e₂ differ : Bool) (β : ℕ → ℕ) (rows cols : List (ℕ × Bool)) (ζ : List ℕ) : List ℕ := mul p fl (listCoeffList p fl ((List.range (2 ^ (i + 1) * nb)).filter fun k => modeTargets i e₁ e₂ (k / nb)) - (rows.map (rowTermList p fl a i t nb differ cols ζ)) β) + (rows.map (rowTermList p fl a L i t nb differ cols ζ)) β) (prodList p fl (((List.range (2 ^ (i + 1) * nb)).filter fun k => modeSources i e₁ e₂ (k / nb)).map fun k => pow p fl (modeValueList p fl i differ cols (k / nb) (k % nb)) (β k))) @@ -609,11 +616,12 @@ def packetEntryList (p : ℕ) (fl : List ℕ) (a i : ℕ) (t : ℤ) (nb : ℕ) ( /-- **Packet entries are correct.** For a grid point `t` of valuation `i < a` and ports in slots below `2 ^ a`, the value of `packetEntryList` in `A_p` is `packetEntry` at the reduction of `ℤ_(2)`, the value of `ζ` and the exponents `β` at the dense indices of the modes. -/ -theorem val_packetEntryList {a i : ℕ} (hia : i < a) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) +theorem val_packetEntryList {a L i : ℕ} (hL : L = 2 ^ a) (hia : i < a) {t : ℤ} + (ht : t ∈ cauchyGrid (2 ^ a)) (hti : (2 : ℤ) ^ i ∣ t) (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) (nb : ℕ) (e₁ e₂ differ : Bool) (β : ℕ → ℕ) {rows cols : List (ℕ × Bool)} (hrows : ∀ r ∈ rows, r.1 < 2 ^ a) (hcols : ∀ c ∈ cols, c.1 < 2 ^ a) (ζ : List ℕ) : - val p fl (packetEntryList p fl a i t nb e₁ e₂ differ β rows cols ζ) = + val p fl (packetEntryList p fl a L i t nb e₁ e₂ differ β rows cols ζ) = packetEntry (p := nb) (scalarHom p fl) (val p fl ζ) a i t e₁ e₂ differ (fun Z => β (Z.1 * nb + Z.2)) rows cols := by rw [packetEntryList, packetEntry, val_mul] @@ -621,7 +629,7 @@ theorem val_packetEntryList {a i : ℕ} (hia : i < a) {t : ℤ} (ht : t ∈ cauc · rw [val_listCoeffList (List.nodup_range.filter _), toFinset_filter_range, TensorNetwork.listCoeff_map, List.map_map, List.map_map] refine congrArg₂ _ (List.map_congr_left fun r hr => ?_) rfl - exact val_rowTermList hia ht hti hti' nb differ hcols (hrows r hr) ζ + exact val_rowTermList hL hia ht hti hti' nb differ hcols (hrows r hr) ζ · rw [val_prodList, List.map_map, ← List.prod_toFinset _ (List.nodup_range.filter _), toFinset_filter_range, prod_map] refine prod_congr rfl fun Z _ => ?_ @@ -633,14 +641,14 @@ theorem val_packetEntryList {a i : ℕ} (hia : i < a) {t : ℤ} (ht : t ∈ cauc nilpotent in `S`, the value of `packetEntryList` is `packetEntry` over `S` at any ring map `κ : ℤ_(2) → S` and at the image of `ζ`. -/ theorem map_val_packetEntryList {S : Type*} [CommRing S] (ψ : Ring p fl →+* S) {n : ℕ} - (hn : (2 : S) ^ n = 0) (κ : ℤ₍₂₎ →+* S) {a i : ℕ} (hia : i < a) {t : ℤ} + (hn : (2 : S) ^ n = 0) (κ : ℤ₍₂₎ →+* S) {a L i : ℕ} (hL : L = 2 ^ a) (hia : i < a) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) (hti : (2 : ℤ) ^ i ∣ t) (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) (nb : ℕ) (e₁ e₂ differ : Bool) (β : ℕ → ℕ) {rows cols : List (ℕ × Bool)} (hrows : ∀ r ∈ rows, r.1 < 2 ^ a) (hcols : ∀ c ∈ cols, c.1 < 2 ^ a) (ζ : List ℕ) : - ψ (val p fl (packetEntryList p fl a i t nb e₁ e₂ differ β rows cols ζ)) = + ψ (val p fl (packetEntryList p fl a L i t nb e₁ e₂ differ β rows cols ζ)) = packetEntry (p := nb) κ (ψ (val p fl ζ)) a i t e₁ e₂ differ (fun Z => β (Z.1 * nb + Z.2)) rows cols := by - rw [val_packetEntryList hia ht hti hti' nb e₁ e₂ differ β hrows hcols ζ, + rw [val_packetEntryList hL hia ht hti hti' nb e₁ e₂ differ β hrows hcols ζ, map_packetEntry ψ two_pow_eq_zero _ _ hia.le hti hti', padicInteger_ringHom_ext hn (ψ.comp (scalarHom p fl)) κ] diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEvaluator.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEvaluator.lean index 435359142c..5115a5d231 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEvaluator.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEvaluator.lean @@ -33,14 +33,14 @@ example : twoVal 12 = 2 ∧ twoVal (-8) = 3 ∧ twoVal 0 = 0 := by decide -- The grid of `2 ^ 1` slots is `-2, -1, 0, 1`; the odd parts of `0 - q` for `q ≠ 0` are -- `1, 1, -1`, so `C'(0)` has odd part `-1`, and its inverse is `-1 ≡ 7`. -example : kernelUnitList 3 [1, 1] 1 = [7, 0] := by decide +example : kernelUnitList 3 [1, 1] 2 = [7, 0] := by decide -- `W_1(1 + 4 U)`: the factors for `q = -2, -1, 0` are `3 + 4 U`, `1 + 2 U` and `1 + 4 U`, whose -- product is `3 + 6 U` modulo `8`; times `-1` it is `5 + 2 U`. -example : kernelValueList 3 [1, 1] 1 1 1 [0, 1] = [5, 2] := by decide +example : kernelValueList 3 [1, 1] 1 2 1 1 [0, 1] = [5, 2] := by decide -- For `d ≠ t` the kernel value has the factor `2 ^ (a + 1 - v_d) ζ`, which vanishes at `ζ = 0`. -example : kernelValueList 3 [1, 1] 1 1 0 [0, 0] = [0, 0] := by decide +example : kernelValueList 3 [1, 1] 1 2 1 0 [0, 0] = [0, 0] := by decide -- The cross ratio at `d₀ = 0` and `ζ = 1`: `2` times the inverse of `1 + 4 = 5`, which is `5` -- modulo `8`, so `10 ≡ 2`. @@ -48,40 +48,40 @@ example : crossRatioList 3 [1, 1] 1 0 1 0 [1, 0] = [2, 0] := by decide -- One row port in slot `0` set to one, one column port in slot `1` set to one, and zero -- exponents: the entry is the local form `W_{1 - 0}(T)`. -example : packetEntryList 3 [1, 1] 1 0 1 2 true false true (fun _ => 0) [(0, true)] [(1, true)] - [0, 1] = kernelValueList 3 [1, 1] 1 1 1 [0, 1] := by +example : packetEntryList 3 [1, 1] 1 2 0 1 2 true false true (fun _ => 0) [(0, true)] [(1, true)] + [0, 1] = kernelValueList 3 [1, 1] 1 2 1 1 [0, 1] := by decide -- With the row port set to zero, the entry is `1 - W_1(T)`. -example : packetEntryList 3 [1, 1] 1 0 1 2 true false true (fun _ => 0) [(0, false)] [(1, true)] +example : packetEntryList 3 [1, 1] 1 2 0 1 2 true false true (fun _ => 0) [(0, false)] [(1, true)] [0, 1] = GaloisRingArith.sub 3 [1, 1] (GaloisRingArith.one [1, 1]) - (kernelValueList 3 [1, 1] 1 1 1 [0, 1]) := by + (kernelValueList 3 [1, 1] 1 2 1 1 [0, 1]) := by decide -- Exponent `1` at the mode `(0, 1)`, of dense index `0 * 2 + 1 = 1`, which targets the group -- (digit `0` of `y₀ = 0` selects the first symbol): the single row factor contributes the -- coefficient of that mode, and no source mode has a positive exponent. -example : packetEntryList 3 [1, 1] 1 0 1 2 true false true (fun k => if k = 1 then 1 else 0) - [(0, true)] [(1, true)] [0, 1] = modeCoeffList 3 [1, 1] 1 0 1 true 0 0 1 [0, 1] := by +example : packetEntryList 3 [1, 1] 1 2 0 1 2 true false true (fun k => if k = 1 then 1 else 0) + [(0, true)] [(1, true)] [0, 1] = modeCoeffList 3 [1, 1] 1 2 0 1 true 0 0 1 [0, 1] := by decide /-- The hypotheses of the correctness theorem hold for these packets. -/ theorem val_packetEntryList_tiny (β : ℕ → ℕ) (ζ : List ℕ) : - GaloisRingArith.val 3 [1, 1] (packetEntryList 3 [1, 1] 1 0 1 2 true false true β + GaloisRingArith.val 3 [1, 1] (packetEntryList 3 [1, 1] 1 2 0 1 2 true false true β [(0, true), (1, false)] [(1, true), (0, true)] ζ) = packetEntry (p := 2) (scalarHom 3 [1, 1]) (GaloisRingArith.val 3 [1, 1] ζ) 1 0 1 true false true (fun Z => β (Z.1 * 2 + Z.2)) [(0, true), (1, false)] [(1, true), (0, true)] := - val_packetEntryList (by norm_num) (by simp [mem_cauchyGrid]) (by norm_num) (by norm_num) 2 true - false true β (by simp) (by simp) ζ + val_packetEntryList (by norm_num) (by norm_num) (by simp [mem_cauchyGrid]) (by norm_num) + (by norm_num) 2 true false true β (by simp) (by simp) ζ -- Transported to any ring with `2` nilpotent, at any ring map from `ℤ_(2)`. example {S : Type} [CommRing S] (ψ : GaloisRingArith.Ring 3 [1, 1] →+* S) {n : ℕ} (hn : (2 : S) ^ n = 0) (κ : ℤ₍₂₎ →+* S) (β : ℕ → ℕ) (ζ : List ℕ) : - ψ (GaloisRingArith.val 3 [1, 1] (packetEntryList 3 [1, 1] 1 0 1 2 true false true β + ψ (GaloisRingArith.val 3 [1, 1] (packetEntryList 3 [1, 1] 1 2 0 1 2 true false true β [(0, true)] [(1, true)] ζ)) = packetEntry (p := 2) κ (ψ (GaloisRingArith.val 3 [1, 1] ζ)) 1 0 1 true false true (fun Z => β (Z.1 * 2 + Z.2)) [(0, true)] [(1, true)] := - map_val_packetEntryList ψ hn κ (by norm_num) (by simp [mem_cauchyGrid]) (by norm_num) - (by norm_num) 2 true false true β (by simp) (by simp) ζ + map_val_packetEntryList ψ hn κ (by norm_num) (by norm_num) (by simp [mem_cauchyGrid]) + (by norm_num) (by norm_num) 2 true false true β (by simp) (by simp) ζ end ComplexityTests.TensorNetworkPacketEvaluator From 145491542cd6c3efcac34d7ef9645b5f000c44ed Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 00:54:53 -0400 Subject: [PATCH 051/129] fix(circuit/frontier): mode terms vanish when a packet has no exponents With nb = 0 the exponent k % nb is k itself; guard it so that the exponent of a mode term is always below nb. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Circuit/Frontier/TensorNetwork/PacketEvaluator.lean | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean index 0d0f3f82e7..8bf1751e32 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean @@ -515,12 +515,14 @@ def modeIndex (i nb : ℕ) : Fin (2 ^ (i + 1)) × Fin nb ↪ ℕ := rfl /-- The terms of the affine factor of a row port `r = (x, Y_x)`: its local term at `none`, and -the coefficient of the mode `(k / nb, k % nb)` at `some k`. -/ +the coefficient of the mode `(k / nb, k % nb)` at `some k` (zero when there are no exponents, +`nb = 0`, so that the exponent `k % nb` stays below `nb`). -/ def rowTermList (p : ℕ) (fl : List ℕ) (a L i : ℕ) (t : ℤ) (nb : ℕ) (differ : Bool) (cols : List (ℕ × Bool)) (ζ : List ℕ) (r : ℕ × Bool) : Option ℕ → List ℕ | none => if r.2 then localFormList p fl a L t cols r.1 ζ else sub p fl (one fl) (localFormList p fl a L t cols r.1 ζ) - | some k => if r.2 then modeCoeffList p fl a L i t differ r.1 (k / nb) (k % nb) ζ + | some k => if nb = 0 then zero fl else + if r.2 then modeCoeffList p fl a L i t differ r.1 (k / nb) (k % nb) ζ else neg p fl (modeCoeffList p fl a L i t differ r.1 (k / nb) (k % nb) ζ) /-- Row terms are correct, at the dense indices of the modes of the packet. -/ @@ -541,7 +543,8 @@ theorem val_rowTermList {a L i : ℕ} (hL : L = 2 ^ a) (hia : i < a) {t : ℤ} · rw [val_sub, val_one, val_localFormList hL ht hcols hr ζ] | some Z => simp only [Function.comp_apply, Option.map_some, rowTermList, packetRowTerm, affineSign, - modeIndex_apply, modeIndex_div Z.2.isLt, modeIndex_mod Z.2.isLt] + modeIndex_apply, modeIndex_div Z.2.isLt, modeIndex_mod Z.2.isLt, + Nat.pos_iff_ne_zero.mp (Nat.zero_lt_of_lt Z.2.isLt), ↓reduceIte] have h := val_modeCoeffList (p := p) (fl := fl) hL hia ht hti hti' differ hr Z ζ split_ifs · rw [one_mul]; exact h From f3be9f4f73c7c61ceb7ef1d460bad0d6610a8274 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 00:55:50 -0400 Subject: [PATCH 052/129] feat(combinatorics): enumerate the tuples of bounded weight with a successor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `budgetTuples ws p` lists the tuples of weight below `p` in lexicographic order and `budgetNext` is the successor that walks it from the zero tuple: the polynomial-delay enumeration of the mode exponents in §D.4.5 (p. 63) of the Boolean tensor contraction proof. `sum_filter_weight_lt_eq` turns a sum over the mode budget into a sum along the list. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Combinatorics/BudgetTuples.lean | 320 ++++++++++++++++++ .../Combinatorics/BudgetTuples.lean | 40 +++ 3 files changed, 361 insertions(+) create mode 100644 Complexity/Foundations/Combinatorics/BudgetTuples.lean create mode 100644 ComplexityTests/Foundations/Combinatorics/BudgetTuples.lean diff --git a/Complexity.lean b/Complexity.lean index a501a62396..e4ec57b7ee 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2547,6 +2547,7 @@ public import Complexity.Foundations.Combinatorics.AffineDisperser.IndependentSo public import Complexity.Foundations.Combinatorics.AffineDisperser.Threshold public import Complexity.Foundations.Combinatorics.BalancedSchedules public import Complexity.Foundations.Combinatorics.BinomialTail +public import Complexity.Foundations.Combinatorics.BudgetTuples public import Complexity.Foundations.Combinatorics.CentralBinomial public import Complexity.Foundations.Combinatorics.DepthReduction public import Complexity.Foundations.Combinatorics.Expander.AffineFamily diff --git a/Complexity/Foundations/Combinatorics/BudgetTuples.lean b/Complexity/Foundations/Combinatorics/BudgetTuples.lean new file mode 100644 index 0000000000..d520039bd0 --- /dev/null +++ b/Complexity/Foundations/Combinatorics/BudgetTuples.lean @@ -0,0 +1,320 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Algebra.BigOperators.Group.Finset.Basic +public import Mathlib.Data.Fintype.Pi +public import Mathlib.Data.List.Chain +import Mathlib.Algebra.BigOperators.Group.List.Basic +import Mathlib.Algebra.Order.BigOperators.Group.Finset +import Mathlib.Data.List.GetD +import Mathlib.Data.List.Nodup + +/-! +# Enumerating the tuples of bounded weight + +Give the coordinates of a tuple of natural numbers positive weights `ws`. The tuples of total +weight `∑ j, ws[j] * β[j] < p` (`weight`) are the exponent tuples of the modes in the proof of the +Boolean tensor contraction theorem of [OpenAI2026ETH] (§D.4.5, p. 63), where `p` is the +precision. The source enumerates them "with polynomial delay and space by ordering the variables +and successively choosing each exponent subject to the remaining budget". This file makes that +enumeration explicit: + +* `budgetTuples ws p` lists the tuples in lexicographic order, the first coordinate most + significant, by choosing each coordinate subject to the remaining budget; +* `budgetNext ws p β` is the successor of `β`: it advances the tail if it can, and otherwise + increments the coordinate and resets the tail to zero, which is feasible since every feasible + partial choice has the all-zero continuation. + +The list holds exactly the tuples of weight below `p` (`mem_budgetTuples`), each once +(`nodup_budgetTuples`). It starts at the zero tuple (`head?_budgetTuples`), each entry is followed +by its successor (`isChain_budgetTuples`), and the last one has none (`budgetNext_getLast?`). So +iterating `budgetNext` from zero visits every tuple once, with one successor computation, linear +in the number of coordinates, between consecutive tuples. + +## Main definitions + +* `Cslib.BudgetTuples.weight`: the weight of a tuple. +* `Cslib.BudgetTuples.headCount`: the number of feasible values of a coordinate. +* `Cslib.BudgetTuples.budgetTuples`: the tuples of weight below a budget, in lexicographic order. +* `Cslib.BudgetTuples.budgetNext`: the successor of a tuple. + +## Main results + +* `Cslib.BudgetTuples.mem_budgetTuples`, `Cslib.BudgetTuples.nodup_budgetTuples`: the list holds + each tuple of weight below the budget once. +* `Cslib.BudgetTuples.head?_budgetTuples`, `Cslib.BudgetTuples.isChain_budgetTuples`, + `Cslib.BudgetTuples.budgetNext_getLast?`: the successor walks the list from the zero tuple. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.5, the enumeration + after (D.15), p. 63. +-/ + +public section + +namespace Cslib.BudgetTuples + +/-- The weight `∑ j, ws[j] * β[j]` of a tuple `β` for the weights `ws`. -/ +@[expose] def weight : List ℕ → List ℕ → ℕ + | w :: ws, b :: β => w * b + weight ws β + | _, _ => 0 + +/-- The number of values `b` of a coordinate of weight `w` with `w * b < p`, that is +`⌈p / w⌉` for `w > 0`. -/ +@[expose] def headCount (w p : ℕ) : ℕ := (p + w - 1) / w + +/-- A value fits the budget exactly when it is below `headCount`. -/ +theorem lt_headCount_iff {w p b : ℕ} (hw : 0 < w) : b < headCount w p ↔ w * b < p := by + rw [headCount, Nat.lt_iff_add_one_le, Nat.le_div_iff_mul_le hw, Nat.add_mul, Nat.one_mul, + Nat.mul_comm b w] + omega + +/-- **The tuples of weight below `p`**, in lexicographic order: the first coordinate takes each +value `b` with `w * b < p` in increasing order, and the rest is a tuple of weight below +`p - w * b`. -/ +@[expose] def budgetTuples : List ℕ → ℕ → List (List ℕ) + | [], p => if 0 < p then [[]] else [] + | w :: ws, p => (List.range (headCount w p)).flatMap fun b => + (budgetTuples ws (p - w * b)).map (b :: ·) + +/-- **The successor of a tuple**: advance the tail within its remaining budget if possible, and +otherwise increment the first coordinate and reset the tail to zero. -/ +@[expose] def budgetNext : List ℕ → ℕ → List ℕ → Option (List ℕ) + | w :: ws, p, b :: β => + match budgetNext ws (p - w * b) β with + | some β' => some (b :: β') + | none => if w * (b + 1) < p then some ((b + 1) :: List.replicate ws.length 0) else none + | _, _, _ => none + +/-! ### The tuples in the list -/ + +@[simp] theorem budgetTuples_nil (p : ℕ) : budgetTuples [] p = if 0 < p then [[]] else [] := rfl + +theorem budgetTuples_cons (w : ℕ) (ws : List ℕ) (p : ℕ) : + budgetTuples (w :: ws) p = (List.range (headCount w p)).flatMap fun b => + (budgetTuples ws (p - w * b)).map (b :: ·) := rfl + +@[simp] theorem weight_nil (β : List ℕ) : weight [] β = 0 := rfl + +@[simp] theorem weight_cons_cons (w : ℕ) (ws : List ℕ) (b : ℕ) (β : List ℕ) : + weight (w :: ws) (b :: β) = w * b + weight ws β := rfl + +/-- **The list holds exactly the tuples of weight below `p`**, for positive weights. -/ +theorem mem_budgetTuples {ws : List ℕ} (hw : ∀ w ∈ ws, 0 < w) {p : ℕ} {β : List ℕ} : + β ∈ budgetTuples ws p ↔ β.length = ws.length ∧ weight ws β < p := by + induction ws generalizing p β with + | nil => + simp only [budgetTuples_nil, weight_nil, List.length_nil, List.length_eq_zero_iff] + split_ifs with hp <;> simp [hp] + | cons w ws ih => + have hw0 : 0 < w := hw w List.mem_cons_self + have ih' := @ih (fun w' hw' => hw w' (List.mem_cons_of_mem _ hw')) + rw [budgetTuples_cons] + simp only [List.mem_flatMap, List.mem_range, lt_headCount_iff hw0, List.mem_map] + constructor + · rintro ⟨b, hb, γ, hγ, rfl⟩ + rw [ih'] at hγ + simp only [List.length_cons, weight_cons_cons, hγ.1, true_and] + omega + · intro ⟨hlen, hwt⟩ + rcases β with _ | ⟨b, γ⟩ + · simp at hlen + · simp only [List.length_cons, Nat.add_right_cancel_iff, weight_cons_cons] at hlen hwt + exact ⟨b, by omega, γ, ih'.mpr ⟨hlen, by omega⟩, rfl⟩ + +/-- **Each tuple appears once.** -/ +theorem nodup_budgetTuples (ws : List ℕ) (p : ℕ) : (budgetTuples ws p).Nodup := by + induction ws generalizing p with + | nil => + simp only [budgetTuples_nil] + split_ifs <;> simp + | cons w ws ih => + rw [budgetTuples_cons, List.nodup_flatMap] + refine ⟨fun b _ => (ih _).map (List.cons_injective), ?_⟩ + refine (List.nodup_range).imp fun {b b'} hbb' => ?_ + intro x hx hy + simp only [List.mem_map] at hx hy + obtain ⟨γ, -, rfl⟩ := hx + obtain ⟨γ', -, h⟩ := hy + exact hbb' (List.cons.inj h).1.symm + +/-! ### The successor walks the list -/ + +private theorem getLast?_flatMap_range_succ {α : Type*} (f : ℕ → List α) (k : ℕ) + (hk : f k ≠ []) : ((List.range (k + 1)).flatMap f).getLast? = (f k).getLast? := by + rw [List.range_succ, List.flatMap_append, List.flatMap_singleton, List.getLast?_append] + cases h : (f k).getLast? with + | none => exact absurd (List.getLast?_eq_none_iff.mp h) hk + | some x => rfl + +private theorem isChain_flatMap_range {α : Type*} {R : α → α → Prop} (f : ℕ → List α) (n : ℕ) + (hne : ∀ b < n, f b ≠ []) (hblock : ∀ b < n, List.IsChain R (f b)) + (hjoin : ∀ b, b + 1 < n → ∀ x ∈ (f b).getLast?, ∀ y ∈ (f (b + 1)).head?, R x y) : + List.IsChain R ((List.range n).flatMap f) := by + induction n with + | zero => simp + | succ k ih => + rw [List.range_succ, List.flatMap_append, List.flatMap_singleton, List.isChain_append] + refine ⟨ih (fun b hb => hne b (by omega)) (fun b hb => hblock b (by omega)) + (fun b hb => hjoin b (by omega)), hblock k (by omega), ?_⟩ + intro x hx y hy + rcases k with _ | k + · simp at hx + · rw [getLast?_flatMap_range_succ f k (hne k (by omega))] at hx + exact hjoin k (by omega) x hx y hy + +/-- **The list starts at the zero tuple**, for positive weights and a positive budget. -/ +theorem head?_budgetTuples {ws : List ℕ} (hw : ∀ w ∈ ws, 0 < w) {p : ℕ} (hp : 0 < p) : + (budgetTuples ws p).head? = some (List.replicate ws.length 0) := by + induction ws generalizing p with + | nil => simp [hp] + | cons w ws ih => + have hw0 : 0 < w := hw w List.mem_cons_self + have ih' := @ih (fun w' hw' => hw w' (List.mem_cons_of_mem _ hw')) + obtain ⟨k, hk⟩ : ∃ k, headCount w p = k + 1 := + Nat.exists_eq_succ_of_ne_zero (Nat.pos_iff_ne_zero.mp ((lt_headCount_iff hw0).mpr + (by simpa using hp))) + rw [budgetTuples_cons, hk, List.range_succ_eq_map, List.flatMap_cons, List.head?_append, + List.head?_map, Nat.mul_zero, Nat.sub_zero, ih' hp] + rfl + +/-- **The last tuple has no successor.** -/ +theorem budgetNext_getLast? {ws : List ℕ} (hw : ∀ w ∈ ws, 0 < w) {p : ℕ} : + ∀ x ∈ (budgetTuples ws p).getLast?, budgetNext ws p x = none := by + induction ws generalizing p with + | nil => intro x _; rfl + | cons w ws ih => + have hw0 : 0 < w := hw w List.mem_cons_self + have ih' := @ih (fun w' hw' => hw w' (List.mem_cons_of_mem _ hw')) + intro x hx + rw [budgetTuples_cons] at hx + rcases hn : headCount w p with _ | k + · rw [hn] at hx; simp at hx + · have hk : w * k < p := (lt_headCount_iff hw0).mp (by omega) + have hne : (budgetTuples ws (p - w * k)).map (k :: ·) ≠ [] := by + rw [Ne, List.map_eq_nil_iff, ← List.head?_eq_none_iff, + head?_budgetTuples (fun w' hw' => hw w' (List.mem_cons_of_mem _ hw')) (by omega)] + simp + rw [hn, getLast?_flatMap_range_succ _ k hne, List.getLast?_map] at hx + obtain ⟨x', hx', rfl⟩ := Option.map_eq_some_iff.mp hx + have hnot : ¬ w * (k + 1) < p := fun h => by + have := (lt_headCount_iff hw0).mpr h; omega + simp only [budgetNext, ih' x' hx', hnot, ↓reduceIte] + +/-- **Each tuple is followed by its successor.** -/ +theorem isChain_budgetTuples {ws : List ℕ} (hw : ∀ w ∈ ws, 0 < w) (p : ℕ) : + List.IsChain (fun x y => budgetNext ws p x = some y) (budgetTuples ws p) := by + induction ws generalizing p with + | nil => + simp only [budgetTuples_nil] + split_ifs <;> simp + | cons w ws ih => + have hw0 : 0 < w := hw w List.mem_cons_self + have hws : ∀ w' ∈ ws, 0 < w' := fun w' hw' => hw w' (List.mem_cons_of_mem _ hw') + rw [budgetTuples_cons] + refine isChain_flatMap_range _ _ (fun b hb => ?_) (fun b _ => ?_) (fun b hb x hx y hy => ?_) + · have hb' := (lt_headCount_iff hw0).mp hb + rw [Ne, List.map_eq_nil_iff, ← List.head?_eq_none_iff, head?_budgetTuples hws (by omega)] + simp + · rw [List.isChain_map] + refine (ih hws (p - w * b)).imp fun x y hxy => ?_ + simp only [budgetNext, hxy] + · have hb1 := (lt_headCount_iff hw0).mp hb + rw [List.getLast?_map] at hx + obtain ⟨x', hx', rfl⟩ := Option.map_eq_some_iff.mp hx + rw [List.head?_map, head?_budgetTuples hws (by omega)] at hy + simp only [Option.map_some, Option.mem_def, Option.some.injEq] at hy + subst hy + simp only [budgetNext, budgetNext_getLast? hws x' hx', hb1, ↓reduceIte] + +/-! ### Tuples indexed by a finite type -/ + +section Indexed + +variable {M : Type*} [DecidableEq M] (ms : List M) + +/-- The tuple listed by `l` as a function of the coordinates listed by `ms`: coordinate `Z` reads +the entry of `l` at the position of `Z` in `ms`. -/ +@[expose] def ofList (l : List ℕ) (Z : M) : ℕ := l.getD (ms.idxOf Z) 0 + +omit [DecidableEq M] in +/-- The weight of the tuple of the values of `β` at the coordinates `ms`. -/ +private theorem weight_map (w β : M → ℕ) : + weight (ms.map w) (ms.map β) = (ms.map fun Z => w Z * β Z).sum := by + induction ms with + | nil => rfl + | cons Z ms ih => simp only [List.map_cons, weight_cons_cons, ih, List.sum_cons] + +variable {ms} + +/-- Reading back the values of a function at the coordinates `ms`. -/ +private theorem ofList_map (hcover : ∀ Z, Z ∈ ms) (β : M → ℕ) : + ofList ms (ms.map β) = β := by + funext Z + have hi : ms.idxOf Z < ms.length := List.idxOf_lt_length_iff.mpr (hcover Z) + rw [ofList, List.getD_eq_getElem _ _ (by simpa using hi), List.getElem_map, + List.getElem_idxOf hi] + +/-- Listing the values of `ofList ms l` at the coordinates `ms` recovers `l`. -/ +private theorem map_ofList (hms : ms.Nodup) {l : List ℕ} (hl : l.length = ms.length) : + ms.map (ofList ms l) = l := by + apply List.ext_getElem (by simp [hl]) + intro i h₁ h₂ + simp only [List.getElem_map, ofList] + rw [List.Nodup.idxOf_getElem hms i (by simpa using h₁), List.getD_eq_getElem] + +/-- **The sum over the tuples of weight below `p`** is the sum along `budgetTuples`, for positive +weights and any duplicate-free list `ms` of all coordinates. The finite set is the mode budget +`Cslib.Circuits.Frontier.modeBudget w p` of the contraction recursion. -/ +theorem sum_filter_weight_lt_eq [Fintype M] (hms : ms.Nodup) (hcover : ∀ Z, Z ∈ ms) + {w : M → ℕ} (hw : ∀ Z, 0 < w Z) (p : ℕ) {S : Type*} [AddCommMonoid S] (f : (M → ℕ) → S) : + ∑ β ∈ ({β ∈ Fintype.piFinset fun _ => Finset.range p | ∑ Z, w Z * β Z < p} : + Finset (M → ℕ)), f β = + ((budgetTuples (ms.map w) p).map fun l => f (ofList ms l)).sum := by + have hws : ∀ x ∈ ms.map w, 0 < x := by + intro x hx + obtain ⟨Z, -, rfl⟩ := List.mem_map.mp hx + exact hw Z + have hsum (g : M → ℕ) : ∑ Z, g Z = (ms.map g).sum := by + rw [← List.sum_toFinset g hms] + congr 1 + ext Z + simp [hcover Z] + set L := budgetTuples (ms.map w) p + have hlen : ∀ l ∈ L, l.length = ms.length := fun l hl => by + simpa using ((mem_budgetTuples hws).mp hl).1 + have hnodup : (L.map (ofList ms)).Nodup := by + refine (nodup_budgetTuples _ _).map_on fun l hl l' hl' h => ?_ + rw [← map_ofList hms (hlen l hl), ← map_ofList hms (hlen l' hl'), h] + have hset : (L.map (ofList ms)).toFinset = + ({β ∈ Fintype.piFinset fun _ => Finset.range p | ∑ Z, w Z * β Z < p} : + Finset (M → ℕ)) := by + ext β + simp only [List.mem_toFinset, List.mem_map, Finset.mem_filter, Fintype.mem_piFinset, + Finset.mem_range] + constructor + · rintro ⟨l, hl, rfl⟩ + have hwt := ((mem_budgetTuples hws).mp hl).2 + rw [← map_ofList hms (hlen l hl), weight_map, ← hsum] at hwt + refine ⟨fun Z => ?_, hwt⟩ + calc ofList ms l Z ≤ w Z * ofList ms l Z := Nat.le_mul_of_pos_left _ (hw Z) + _ ≤ ∑ Z', w Z' * ofList ms l Z' := + Finset.single_le_sum (f := fun Z' => w Z' * ofList ms l Z') (fun _ _ => Nat.zero_le _) + (Finset.mem_univ Z) + _ < p := hwt + · rintro ⟨-, hβ⟩ + refine ⟨ms.map β, (mem_budgetTuples hws).mpr ⟨by simp, ?_⟩, ofList_map hcover β⟩ + rwa [weight_map, ← hsum] + rw [← hset, List.sum_toFinset _ hnodup, List.map_map] + rfl + +end Indexed + +end Cslib.BudgetTuples diff --git a/ComplexityTests/Foundations/Combinatorics/BudgetTuples.lean b/ComplexityTests/Foundations/Combinatorics/BudgetTuples.lean new file mode 100644 index 0000000000..4754db4b58 --- /dev/null +++ b/ComplexityTests/Foundations/Combinatorics/BudgetTuples.lean @@ -0,0 +1,40 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ +module + +public import Complexity.Foundations.Combinatorics.BudgetTuples +import Mathlib.Data.Fin.VecNotation +import Mathlib.Data.List.FinRange + +public section + +open Cslib.BudgetTuples + +namespace Cslib.Tests.BudgetTuples + +/-! With weights `(1, 2)` and budget `4`, the tuples `(b₀, b₁)` with `b₀ + 2 b₁ < 4`, in +lexicographic order. -/ + +example : budgetTuples [1, 2] 4 = [[0, 0], [0, 1], [1, 0], [1, 1], [2, 0], [3, 0]] := by + decide +kernel + +/-- The successor walks the list from the zero tuple and stops after the last one. -/ +example : budgetNext [1, 2] 4 [0, 0] = some [0, 1] ∧ budgetNext [1, 2] 4 [0, 1] = some [1, 0] ∧ + budgetNext [1, 2] 4 [1, 1] = some [2, 0] ∧ budgetNext [1, 2] 4 [2, 0] = some [3, 0] ∧ + budgetNext [1, 2] 4 [3, 0] = none := by + decide +kernel + +/-- An empty budget has no tuples, and a positive one has the empty tuple for no coordinates. -/ +example : budgetTuples [3] 0 = [] ∧ budgetTuples [] 1 = [[]] := by decide +kernel + +/-- The six tuples of weight below `4` for the weights `![1, 2]` on `Fin 2`, counted along the +list. -/ +example : ∑ _β ∈ ({β ∈ Fintype.piFinset fun _ => Finset.range 4 | + ∑ Z, ![1, 2] Z * β Z < 4} : Finset (Fin 2 → ℕ)), 1 = 6 := by + rw [sum_filter_weight_lt_eq (List.nodup_finRange 2) (List.mem_finRange) (by decide) 4] + decide +kernel + +end Cslib.Tests.BudgetTuples From bd3e539aafd09e726ce1edcf6782a2fc986ae1d7 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 00:59:54 -0400 Subject: [PATCH 053/129] feat(PolyTime): the successor of a tuple of bounded weight in polynomial time `budgetNextScan` computes `budgetNext` by one left scan over the coordinates (`budgetNextScan_eq`), and `computableInPolyTime_budgetNext` certifies it, so the enumeration of the mode exponents has polynomial delay. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Computability/PolyTime/BudgetTuples.lean | 200 ++++++++++++++++++ .../Combinatorics/BudgetTuples.lean | 57 ++++- .../Combinatorics/BudgetTuples.lean | 4 + 4 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 Complexity/Computability/PolyTime/BudgetTuples.lean diff --git a/Complexity.lean b/Complexity.lean index e4ec57b7ee..6f8dd26fcc 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2009,6 +2009,7 @@ public import Complexity.Computability.PolyTime.BooleanAnalysis.BoundedIncidence public import Complexity.Computability.PolyTime.BooleanAnalysis.BoundedIncidenceSemantics public import Complexity.Computability.PolyTime.BooleanAnalysis.BoundedIncidenceStepCert public import Complexity.Computability.PolyTime.BooleanAnalysis.SparsePolyCounter +public import Complexity.Computability.PolyTime.BudgetTuples public import Complexity.Computability.PolyTime.CNF.BranchedSparseCNFCounter public import Complexity.Computability.PolyTime.CNF.GreedyParityContext public import Complexity.Computability.PolyTime.CNF.GreedyParitySearch diff --git a/Complexity/Computability/PolyTime/BudgetTuples.lean b/Complexity/Computability/PolyTime/BudgetTuples.lean new file mode 100644 index 0000000000..312db6ff99 --- /dev/null +++ b/Complexity/Computability/PolyTime/BudgetTuples.lean @@ -0,0 +1,200 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ +module +-- upstream: candidate + +public import Complexity.Foundations.Combinatorics.BudgetTuples +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Basic +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.List +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Option +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Binary +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Option +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.LinearSpaceArith +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Length +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeLoops + +/-! +# The successor of a tuple of bounded weight in polynomial time + +`Cslib.BudgetTuples.budgetNext` walks the tuples of weight below a budget, the mode exponents of +the Boolean tensor contraction proof of [OpenAI2026ETH] (§D.4.5, p. 63). This file certifies that +one step is polynomial-time, with the weights, the budget and the tuple in binary +(`computableInPolyTime_budgetNext`), through its left scan `budgetNextScan`. So the enumeration +has polynomial delay. + +## Main definitions + +* `Cslib.BudgetTuples.encScanState`, `Cslib.BudgetTuples.encBudgetInput`: encodings of the scan + state and of the inputs. + +## Main results + +* `Cslib.BudgetTuples.computableInPolyTime_scanStep`: one step of the scan. +* `Cslib.BudgetTuples.computableInPolyTime_budgetNext`: the successor. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.5, the enumeration + after (D.15), p. 63. +-/ + +public section + +namespace Cslib.BudgetTuples + +open Turing.MultiTapeTM + +/-- The state of the scan: the remaining budget in binary, the number of remaining weights in +unary, the coordinates read and the successor found so far in binary. -/ +abbrev ScanState : Type := ℕ × ℕ × List ℕ × Option (List ℕ) + +/-- The encoding of `ScanState`. -/ +@[expose] def encScanState : ScanState ↪ List Bool := + encPair encNat (encPair encUnary (encPair (encList encNat) (encOption (encList encNat)))) + +/-- The inputs `(ws, p, β)` of `budgetNext`, in binary. -/ +@[expose] def encBudgetInput : List ℕ × ℕ × List ℕ ↪ List Bool := + encPair (encList encNat) (encPair encNat (encList encNat)) + +/-- **One step of the scan is polynomial-time.** -/ +theorem computableInPolyTime_scanStep : + ComputableInPolyTime (fun q : ScanState × (ℕ × ℕ) => scanStep q.1 q.2) + (encPair encScanState (encPair encNat encNat)) encScanState := by + let enc := encPair encScanState (encPair encNat encNat) + have hr : ComputableInPolyTime (fun q : ScanState × (ℕ × ℕ) => q.1.1) enc encNat := by + unfold enc encScanState; fun_prop + have hrest : ComputableInPolyTime (fun q : ScanState × (ℕ × ℕ) => q.1.2.1) enc encUnary := by + unfold enc encScanState; fun_prop + have hpre : ComputableInPolyTime (fun q : ScanState × (ℕ × ℕ) => q.1.2.2.1) enc + (encList encNat) := by + unfold enc encScanState; fun_prop + have hbest : ComputableInPolyTime (fun q : ScanState × (ℕ × ℕ) => q.1.2.2.2) enc + (encOption (encList encNat)) := by + unfold enc encScanState; fun_prop + have hw : ComputableInPolyTime (fun q : ScanState × (ℕ × ℕ) => q.2.1) enc encNat := by + unfold enc; fun_prop + have hb : ComputableInPolyTime (fun q : ScanState × (ℕ × ℕ) => q.2.2) enc encNat := by + unfold enc; fun_prop + have hb1 : ComputableInPolyTime (fun q : ScanState × (ℕ × ℕ) => q.2.2 + 1) enc encNat := + ComputableInPolyTime.binary_add.comp (hb.prod (.const 1)) + have hrest1 := ComputableInPolyTime.unary_sub' hrest (.const 1) + have hnew : ComputableInPolyTime (fun q : ScanState × (ℕ × ℕ) => + some (q.1.2.2.1 ++ (q.2.2 + 1) :: List.replicate (q.1.2.1 - 1) 0)) enc + (encOption (encList encNat)) := + (ComputableInPolyTime.optionSome (encList encNat)).comp (ComputableInPolyTime.listAppend' hpre + (ComputableInPolyTime.cons' hb1 (ComputableInPolyTime.replicate hrest1 (.const 0)))) + have hcond := ComputableInPolyTime.binary_lt' (ComputableInPolyTime.binary_mul' hw hb1) hr + have h := ((ComputableInPolyTime.binary_sub' hr (ComputableInPolyTime.binary_mul' hw hb)).prod + (hrest1.prod (((ComputableInPolyTime.snoc encNat).comp (hpre.prod hb)).prod + (hcond.ite' hnew hbest)))) + unfold encScanState + refine h.of_enc_eq fun q => ?_ + simp only [scanStep, decide_eq_true_eq, Function.comp_apply] + +/-- The coordinates of the zipped pairs form a prefix of the tuple. -/ +private theorem map_snd_zip_prefix (ws β : List ℕ) : (ws.zip β).map Prod.snd <+: β := by + induction ws generalizing β with + | nil => exact List.nil_prefix + | cons w ws ih => + cases β with + | nil => exact List.nil_prefix + | cons b β => simpa [List.prefix_cons_inj] using ih β + +/-- A prefix has a shorter encoding. -/ +private theorem length_encList_le_of_prefix {l₁ l₂ : List ℕ} (h : l₁ <+: l₂) : + (encList encNat l₁).length ≤ (encList encNat l₂).length := by + rw [length_encList, length_encList] + exact (h.sublist.map _).sum_le_sum fun _ _ => Nat.zero_le _ + +/-- A successor has at most one more bit. -/ +private theorem length_encNat_succ_le (b : ℕ) : + (encNat (b + 1)).length ≤ (encNat b).length + 1 := by + rw [length_encNat_eq_size, length_encNat_eq_size] + refine Nat.size_le.mpr ?_ + have := Nat.lt_size_self b + rw [pow_succ] + omega + +/-- **The successor is polynomial-time**, with the weights, the budget and the tuple in binary: +the enumeration of the tuples of bounded weight has polynomial delay. -/ +theorem computableInPolyTime_budgetNext : + ComputableInPolyTime (fun x : List ℕ × ℕ × List ℕ => budgetNext x.1 x.2.1 x.2.2) + encBudgetInput (encOption (encList encNat)) := by + have hws : ComputableInPolyTime (fun x : List ℕ × ℕ × List ℕ => x.1) encBudgetInput + (encList encNat) := by unfold encBudgetInput; fun_prop + have hp : ComputableInPolyTime (fun x : List ℕ × ℕ × List ℕ => x.2.1) encBudgetInput encNat := by + unfold encBudgetInput; fun_prop + have hβ : ComputableInPolyTime (fun x : List ℕ × ℕ × List ℕ => x.2.2) encBudgetInput + (encList encNat) := by unfold encBudgetInput; fun_prop + have hlist := (ComputableInPolyTime.zip encNat encNat).comp (hws.prod hβ) + have hinit : ComputableInPolyTime (fun x : List ℕ × ℕ × List ℕ => + ((x.2.1, x.1.length, [], none) : ScanState)) encBudgetInput encScanState := by + unfold encScanState + exact hp.prod (((ComputableInPolyTime.length_list _).comp hws).prod + ((ComputableInPolyTime.const (encOut := encList encNat) []).prod + (ComputableInPolyTime.const (encOut := encOption (encList encNat)) none))) + let size (x : List ℕ × ℕ × List ℕ) : ℕ := 4 * (encBudgetInput x).length + 8 + have hsize : PolyBounded (fun x => (encBudgetInput x).length) size := + ((PolyBounded.const 4).mul .self).add (.const 8) + have hproj : ComputableInPolyTime (fun s : ScanState => s.2.2.2) encScanState + (encOption (encList encNat)) := by unfold encScanState; fun_prop + refine (hproj.comp (ComputableInPolyTime.foldl_spec (step := scanStep) hlist hinit + computableInPolyTime_scanStep + (fun x consumed s => s.1 ≤ x.2.1 ∧ s.2.1 ≤ x.1.length ∧ s.2.2.1 = consumed.map Prod.snd ∧ + ∀ l, s.2.2.2 = some l → + (encList encNat l).length ≤ (encList encNat x.2.2).length + x.1.length + 2) + (fun x => ⟨le_rfl, le_rfl, rfl, fun _ h => by cases h⟩) ?_ hsize ?_).1).of_enc_eq + fun x => ?_ + · -- The invariant is preserved. + rintro ⟨ws, p, β⟩ consumed s ⟨w, b⟩ hpre ⟨hr, hrest, hcons, hbest⟩ + have hprefix : s.2.2.1 ++ [b] <+: β := by + have h1 : (consumed ++ [(w, b)]).map Prod.snd <+: (ws.zip β).map Prod.snd := + hpre.map _ + rw [List.map_append, ← hcons] at h1 + exact h1.trans (map_snd_zip_prefix ws β) + refine ⟨(Nat.sub_le _ _).trans hr, (Nat.sub_le _ _).trans hrest, by + simp [scanStep, hcons], fun l hl => ?_⟩ + simp only [scanStep] at hl + split_ifs at hl with hcond + · cases hl + have hle := length_encList_le_of_prefix hprefix + have hsucc := length_encNat_succ_le b + simp only [length_encList, List.map_append, List.sum_append, List.map_cons, List.sum_cons, + List.map_replicate, List.sum_replicate, List.map_nil, List.sum_nil, smul_eq_mul] at hle ⊢ + have h0 : (encNat 0).length = 0 := rfl + rw [h0] at ⊢ + have : s.2.1 - 1 ≤ ws.length := (Nat.sub_le _ _).trans hrest + omega + · exact hbest l hl + · -- The states are polynomially bounded. + rintro ⟨ws, p, β⟩ consumed s hpre ⟨hr, hrest, hcons, hbest⟩ + obtain ⟨r, rest, pre, best⟩ := s + simp only at hr hrest hcons hbest + have hpreβ : pre <+: β := hcons ▸ (hpre.map _).trans (map_snd_zip_prefix ws β) + have hpl := length_encList_le_of_prefix hpreβ + have hrl := length_encNat_mono hr + have hbl : (encOption (encList encNat) best).length ≤ + (encList encNat β).length + ws.length + 3 := by + cases best with + | none => simp [encOption_none] + | some l => simp only [encOption_some, List.length_cons]; have := hbest l rfl; omega + have hlen : ws.length ≤ (encList encNat ws).length := by + clear hpre hrest hbest hbl + rw [length_encList] + induction ws with + | nil => simp + | cons _ _ ih => simp only [List.length_cons, List.map_cons, List.sum_cons]; omega + simp only [encScanState, encBudgetInput, length_encPair, encUnary_apply, + List.length_replicate, size] + omega + · rw [← budgetNextScan_eq] + rfl + +end Cslib.BudgetTuples diff --git a/Complexity/Foundations/Combinatorics/BudgetTuples.lean b/Complexity/Foundations/Combinatorics/BudgetTuples.lean index d520039bd0..ebc78e7228 100644 --- a/Complexity/Foundations/Combinatorics/BudgetTuples.lean +++ b/Complexity/Foundations/Combinatorics/BudgetTuples.lean @@ -34,8 +34,13 @@ enumeration explicit: The list holds exactly the tuples of weight below `p` (`mem_budgetTuples`), each once (`nodup_budgetTuples`). It starts at the zero tuple (`head?_budgetTuples`), each entry is followed by its successor (`isChain_budgetTuples`), and the last one has none (`budgetNext_getLast?`). So -iterating `budgetNext` from zero visits every tuple once, with one successor computation, linear -in the number of coordinates, between consecutive tuples. +iterating `budgetNext` from zero visits every tuple once. The successor is also a left scan +(`budgetNextScan_eq`), one pass over the coordinates; its polynomial-time certificate is in +`Complexity/Computability/PolyTime/BudgetTuples.lean`. + +For tuples indexed by a finite type `M`, listed by a duplicate-free list of all coordinates, a sum +over the tuples of weight below `p`, the mode budget of the contraction recursion, is the sum +along `budgetTuples` (`sum_filter_weight_lt_eq`). ## Main definitions @@ -43,6 +48,8 @@ in the number of coordinates, between consecutive tuples. * `Cslib.BudgetTuples.headCount`: the number of feasible values of a coordinate. * `Cslib.BudgetTuples.budgetTuples`: the tuples of weight below a budget, in lexicographic order. * `Cslib.BudgetTuples.budgetNext`: the successor of a tuple. +* `Cslib.BudgetTuples.scanStep`, `Cslib.BudgetTuples.budgetNextScan`: the successor as a left scan. +* `Cslib.BudgetTuples.ofList`: a listed tuple as a function of its coordinates. ## Main results @@ -50,6 +57,8 @@ in the number of coordinates, between consecutive tuples. each tuple of weight below the budget once. * `Cslib.BudgetTuples.head?_budgetTuples`, `Cslib.BudgetTuples.isChain_budgetTuples`, `Cslib.BudgetTuples.budgetNext_getLast?`: the successor walks the list from the zero tuple. +* `Cslib.BudgetTuples.budgetNextScan_eq`: the scan computes the successor. +* `Cslib.BudgetTuples.sum_filter_weight_lt_eq`: a sum over the budget is a sum along the list. ## References @@ -317,4 +326,48 @@ theorem sum_filter_weight_lt_eq [Fintype M] (hms : ms.Nodup) (hcover : ∀ Z, Z end Indexed +/-! ### The successor as a left scan -/ + +/-- One step of the left scan behind `budgetNext`, reading a weight and a coordinate `(w, b)`. The +state holds the remaining budget, the number of remaining weights, the coordinates read so far, +and the successor obtained by incrementing the deepest position read so far that has room. -/ +@[expose] def scanStep (s : ℕ × ℕ × List ℕ × Option (List ℕ)) (q : ℕ × ℕ) : + ℕ × ℕ × List ℕ × Option (List ℕ) := + (s.1 - q.1 * q.2, s.2.1 - 1, s.2.2.1 ++ [q.2], + if q.1 * (q.2 + 1) < s.1 then some (s.2.2.1 ++ (q.2 + 1) :: List.replicate (s.2.1 - 1) 0) + else s.2.2.2) + +/-- **The successor by a left scan**: the deepest position with room is incremented and the +positions after it reset to zero. -/ +@[expose] def budgetNextScan (ws : List ℕ) (p : ℕ) (β : List ℕ) : Option (List ℕ) := + ((ws.zip β).foldl scanStep (p, ws.length, [], none)).2.2.2 + +private theorem foldl_scanStep (ws β : List ℕ) (r : ℕ) (pre : List ℕ) (best : Option (List ℕ)) : + ((ws.zip β).foldl scanStep (r, ws.length, pre, best)).2.2.2 = + match budgetNext ws r β with + | some t => some (pre ++ t) + | none => best := by + induction ws generalizing β r pre best with + | nil => rfl + | cons w ws ih => + rcases β with _ | ⟨b, β⟩ + · rfl + · rw [List.zip_cons_cons, List.foldl_cons] + have hstep : scanStep (r, (w :: ws).length, pre, best) (w, b) = + (r - w * b, ws.length, pre ++ [b], + if w * (b + 1) < r then some (pre ++ (b + 1) :: List.replicate ws.length 0) + else best) := by + simp [scanStep] + rw [hstep, ih] + simp only [budgetNext] + cases budgetNext ws (r - w * b) β with + | some t => simp + | none => split_ifs <;> rfl + +/-- The left scan computes the successor. -/ +theorem budgetNextScan_eq (ws : List ℕ) (p : ℕ) (β : List ℕ) : + budgetNextScan ws p β = budgetNext ws p β := by + rw [budgetNextScan, foldl_scanStep] + cases budgetNext ws p β <;> rfl + end Cslib.BudgetTuples diff --git a/ComplexityTests/Foundations/Combinatorics/BudgetTuples.lean b/ComplexityTests/Foundations/Combinatorics/BudgetTuples.lean index 4754db4b58..f14bcd5db4 100644 --- a/ComplexityTests/Foundations/Combinatorics/BudgetTuples.lean +++ b/ComplexityTests/Foundations/Combinatorics/BudgetTuples.lean @@ -27,6 +27,10 @@ example : budgetNext [1, 2] 4 [0, 0] = some [0, 1] ∧ budgetNext [1, 2] 4 [0, 1 budgetNext [1, 2] 4 [3, 0] = none := by decide +kernel +/-- The left scan agrees. -/ +example : budgetNextScan [1, 2] 4 [1, 1] = some [2, 0] ∧ budgetNextScan [1, 2] 4 [3, 0] = none := by + decide +kernel + /-- An empty budget has no tuples, and a positive one has the empty tuple for no coordinates. -/ example : budgetTuples [3] 0 = [] ∧ budgetTuples [] 1 = [[]] := by decide +kernel From fb6f83dfc4a8d276da1ae465a830edc39f4a5fbc Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 01:05:56 -0400 Subject: [PATCH 054/129] feat(circuit/frontier): the native packet evaluator in time exp(O(L log s)) and polynomial space Row terms of packet entries are polynomial-time: kernel values, cross ratios, local forms, mode coefficients and values, with the counts in unary and the integers in binary sign-magnitude form; the 2-adic valuation needs at most size |n| halvings. A packet context gives coefficient extraction for a packet as polynomial-time CoeffScheme data. packetEntryNative multiplies the stack recursion's value by the source mode powers; val_packetEntryNative_eq proves it is packetEntry in A_p, and entryCertificate_le bounds one native machine by time K (m + 2)^L Z^K and space K Z^K. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Frontier/TensorNetwork/PacketTerms.lean | 1183 +++++++++++++++++ .../Frontier/TensorNetworkPacketTerms.lean | 62 + 3 files changed, 1246 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketTerms.lean create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketTerms.lean diff --git a/Complexity.lean b/Complexity.lean index c980ba15b1..8ee6b74377 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -887,6 +887,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.KernelProd public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEntry public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEvaluator +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketTerms public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness public import Complexity.Computability.Circuit.Frontier.Unconditional public import Complexity.Computability.Circuit.Frontier.Witness diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketTerms.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketTerms.lean new file mode 100644 index 0000000000..f7da6829e8 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketTerms.lean @@ -0,0 +1,1183 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CoeffScheme +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.IntBinary +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Int.Binary +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Int.BinaryDiv +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Binary +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Length +import Complexity.Computability.Machines.Turing.MultiTape.Internal.EqualityPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryDivision +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListBinaryLookup +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListLookup +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListMap +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeLoops +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeNatFrontends +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeOptionPrimitives +import Mathlib.Data.List.GetD + +/-! +# The native packet evaluator + +The native packet evaluator (`Cslib.Circuits.Frontier.NativePacket`) computes the entries of the +packet vertices of the child networks of the contraction algorithm of [OpenAI2026ETH] on +coefficient lists (§D.4.6, p. 63). This file makes it one native machine with the source's +bounds: time `exp(O(L log s))` and polynomial space. + +* **Row terms in polynomial time.** Kernel values, cross ratios, local forms, mode coefficients + and values, and the row terms are polynomial-time + (`NativePacket.kernelValueList_polyTime`, …, `NativePacket.rowTermList_polyTime`), with the + precision `p`, the bit counts `a` and `i`, the number of slots `L` and the number of exponents + `nb` in unary, and the integers in binary sign-magnitude form (`Turing.MultiTapeTM.encIntBin`). + The `2`-adic valuation needs at most `size |n|` halvings (`NativePacket.twoVal_polyTime`). +* **A packet context** (`NativePacket.PacketCtx`) holds what the row terms and the modes need, + with the number of tiles `2 ^ (i + 1)` in unary. Coefficient extraction for a packet + (`NativePacket.packetTerms`) is an instance of `CoeffScheme.Terms` with polynomial-time data + (`NativePacket.packetTerms_polyTime`). +* **The native packet entry** (`NativePacket.packetEntryNative`) multiplies the value of the stack + recursion by the powers of the source mode values. Its value in `A_p` is `packetEntry` + (`NativePacket.val_packetEntryNative_eq`), and one native machine computes it + (`NativePacket.entryCertificate`) in time `K (m + 2) ^ L Z ^ K` and space `K Z ^ K`, for `m` + target modes, `L` row ports and `Z` polynomial in the input and the reduced coefficient lists + (`NativePacket.entryCertificate_le`). The exponents are read in unary, since the source's are + below the precision. + +## Main definitions + +* `Cslib.Circuits.Frontier.NativePacket.PacketCtx`: the context of a packet entry. +* `Cslib.Circuits.Frontier.NativePacket.packetTerms`: coefficient extraction for a packet. +* `Cslib.Circuits.Frontier.NativePacket.packetEntryNative`: the native packet entry. +* `Cslib.Circuits.Frontier.NativePacket.entryCertificate`: its native machine. + +## Main results + +* `Cslib.Circuits.Frontier.NativePacket.twoVal_polyTime`: the `2`-adic valuation. +* `Cslib.Circuits.Frontier.NativePacket.kernelValueList_polyTime`, + `Cslib.Circuits.Frontier.NativePacket.crossRatioList_polyTime`: kernel values and cross ratios. +* `Cslib.Circuits.Frontier.NativePacket.rowTermList_polyTime`: the row terms. +* `Cslib.Circuits.Frontier.NativePacket.val_packetEntryNative_eq`, + `Cslib.Circuits.Frontier.NativePacket.map_val_packetEntryNative`: the native entry is the + packet entry, in `A_p` and in every ring receiving it. +* `Cslib.Circuits.Frontier.NativePacket.entryCertificate_le`: its time and space. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.6 (p. 63). +-/ + +@[expose] public section + +namespace Cslib.Circuits.Frontier + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +namespace NativePacket + +open GaloisRingArith TensorNetwork Turing.MultiTapeTM Cslib.GenericRAM + +/-! ### The `2`-adic valuation by halving -/ + +/-- The valuation by halving is `padicValNat 2` whenever the fuel exceeds the valuation. -/ +theorem twoValAux_eq_of_lt : ∀ (k m : ℕ), (m = 0 ∨ padicValNat 2 m < k) → + twoValAux k m = padicValNat 2 m + | 0, m, h => by + rcases h with rfl | h + · rw [twoValAux]; simp + · omega + | k + 1, m, h => by + rw [twoValAux] + split_ifs with hm + · obtain ⟨hm0, hm2⟩ := hm + have hm' : m = 2 * (m / 2) := by omega + have hv : padicValNat 2 m = padicValNat 2 (m / 2) + 1 := by + conv_lhs => rw [hm', padicValNat.mul two_ne_zero (by omega), padicValNat_self, add_comm] + rw [hv, twoValAux_eq_of_lt k (m / 2) (by omega)] + · by_cases hm0 : m = 0 + · rw [hm0]; simp + · exact (padicValNat.eq_zero_of_not_dvd fun h2 => hm ⟨hm0, by omega⟩).symm + +/-- The valuation of a nonzero natural is below its bit length. -/ +theorem padicValNat_two_lt_size {m : ℕ} (hm : m ≠ 0) : padicValNat 2 m < m.size := by + have h1 : 2 ^ padicValNat 2 m ≤ m := Nat.le_of_dvd (Nat.pos_of_ne_zero hm) pow_padicValNat_dvd + have h2 := Nat.lt_size_self m + exact (Nat.pow_lt_pow_iff_right (by norm_num)).mp (h1.trans_lt h2) + +/-- **The valuation needs only `size |n|` halvings.** -/ +theorem twoVal_eq_twoValAux_size (n : ℤ) : twoVal n = twoValAux n.natAbs.size n.natAbs := by + rw [twoVal_eq, twoValAux_eq_of_lt] + · rfl + · by_cases h : n.natAbs = 0 + · exact .inl h + · exact .inr (padicValNat_two_lt_size h) + +/-- One step of the valuation by halving, on the value, the count and whether it has stopped. -/ +def twoValStep (s : ℕ × ℕ × Bool) : ℕ × ℕ × Bool := + if s.2.2 = false ∧ s.1 ≠ 0 ∧ s.1 % 2 = 0 then (s.1 / 2, s.2.1 + 1, false) else (s.1, s.2.1, true) + +private theorem twoValStep_iterate : ∀ (k m v : ℕ), + (twoValStep^[k] (m, v, false)).2.1 = v + twoValAux k m ∧ + ∀ m' v', twoValStep^[k] (m', v', true) = (m', v', true) + | 0, m, v => ⟨by simp [twoValAux], fun _ _ => rfl⟩ + | k + 1, m, v => by + refine ⟨?_, fun m' v' => ?_⟩ + · rw [Function.iterate_succ_apply] + by_cases hm : m ≠ 0 ∧ m % 2 = 0 + · have h1 : twoValStep (m, v, false) = (m / 2, v + 1, false) := by + simp [twoValStep, hm.1, hm.2] + have h2 : twoValAux (k + 1) m = twoValAux k (m / 2) + 1 := by + simp [twoValAux, hm.1, hm.2] + rw [h1, h2, (twoValStep_iterate k (m / 2) (v + 1)).1] + ring + · have h1 : twoValStep (m, v, false) = (m, v, true) := by + simp only [twoValStep] + split_ifs with h + · exact absurd ⟨h.2.1, h.2.2⟩ hm + · rfl + have h2 : twoValAux (k + 1) m = 0 := by + simp only [twoValAux, hm, ↓reduceIte] + rw [h1, h2, (twoValStep_iterate k m v).2] + simp + · have h1 : twoValStep (m', v', true) = (m', v', true) := by simp [twoValStep] + rw [Function.iterate_succ_apply, h1, (twoValStep_iterate k m' v').2] + +private theorem foldl_range_eq_iterate {β : Type} (f : β → β) : ∀ (k : ℕ) (s : β), + (List.range k).foldl (fun s _ => f s) s = f^[k] s + | 0, s => rfl + | k + 1, s => by + rw [List.range_succ, List.foldl_append, foldl_range_eq_iterate f k s, List.foldl_cons, + List.foldl_nil, Function.iterate_succ_apply'] + +/-- The valuation by halving as a fold over `size |n|` steps. -/ +theorem twoVal_eq_foldl (n : ℤ) : + twoVal n = ((List.range n.natAbs.size).foldl (fun s _ => twoValStep s) + (n.natAbs, 0, false)).2.1 := by + rw [foldl_range_eq_iterate, (twoValStep_iterate _ _ 0).1, zero_add, twoVal_eq_twoValAux_size] + +section PolyTime + +variable {α : Type} {encIn : α ↪ List Bool} + +private theorem intBin_natAbs' : + ComputableInPolyTime Int.natAbs encIntBin encNat := + .of_tidy ComputableTidilyInPolyTime.intBin_natAbs + +private theorem intBin_isNeg' : + ComputableInPolyTime (fun z : ℤ => decide (z < 0)) encIntBin encBool := + .of_tidy ComputableTidilyInPolyTime.intBin_isNeg + +/-- **The `2`-adic valuation is polynomial-time**, on binary integers, in unary. -/ +theorem twoVal_polyTime : ComputableInPolyTime twoVal encIntBin encUnary := by + let encS := encPair encNat (encPair encUnary encBool) + have hstep : ComputableInPolyTime (fun q : (ℤ × (ℕ × ℕ × Bool)) × ℕ => twoValStep q.1.2) + (encPair (encPair encIntBin encS) encUnary) encS := by + have hs := (ComputableInPolyTime.snd encIntBin encS).comp + (ComputableInPolyTime.fst (encPair encIntBin encS) encUnary) + have hm := (ComputableInPolyTime.fst encNat (encPair encUnary encBool)).comp hs + have hv := (ComputableInPolyTime.fst encUnary encBool).comp + ((ComputableInPolyTime.snd encNat (encPair encUnary encBool)).comp hs) + have hb := (ComputableInPolyTime.snd encUnary encBool).comp + ((ComputableInPolyTime.snd encNat (encPair encUnary encBool)).comp hs) + have hgo := (ComputableInPolyTime.binary_div.comp + (hm.prod (ComputableInPolyTime.const 2))).prod + ((ComputableInPolyTime.unary_succ.comp hv).prod + (ComputableInPolyTime.const (encOut := encBool) false)) + have hstay := hm.prod (hv.prod (ComputableInPolyTime.const (encOut := encBool) true)) + refine (ComputableInPolyTime.ite hb hstay (ComputableInPolyTime.ite + (ComputableInPolyTime.beq hm (ComputableInPolyTime.const 0)) hstay + (ComputableInPolyTime.ite (ComputableInPolyTime.beq + (ComputableInPolyTime.binary_mod.comp (hm.prod (ComputableInPolyTime.const 2))) + (ComputableInPolyTime.const 0)) hgo hstay))).of_enc_eq fun q => congrArg encS ?_ + obtain ⟨⟨z, m, v, b⟩, j⟩ := q + simp only [twoValStep] + cases b <;> by_cases h0 : m = 0 <;> by_cases h2 : m % 2 = 0 <;> simp [h0, h2] + have hfold := ComputableInPolyTime.foldlWithInput_of_bounded_growth + (encA := encUnary) (encL := encList encUnary) + (step := fun _ s _ => twoValStep s) + (intBin_natAbs'.prod ((ComputableInPolyTime.const 0).prod (ComputableInPolyTime.const false))) + ⟨ComputableInPolyTime.range.comp (ComputableInPolyTime.binary_size.comp intBin_natAbs'), + hstep, inferInstance, inferInstance⟩ + (growth := fun _ => 2) ⟨PolyBounded.const 2, fun z s _ _ => ?_⟩ + · refine ((ComputableInPolyTime.fst encUnary encBool).comp + ((ComputableInPolyTime.snd encNat (encPair encUnary encBool)).comp hfold)).of_enc_eq + fun z => ?_ + simp only [Function.comp_apply, twoVal_eq_foldl] + · obtain ⟨m, v, b⟩ := s + have hdiv : (encNat (m / 2)).length ≤ (encNat m).length := by + change (m / 2).bits.length ≤ m.bits.length + rw [Nat.size_eq_bits_len, Nat.size_eq_bits_len] + exact Nat.size_le_size (Nat.div_le_self _ _) + simp only [twoValStep] + split_ifs <;> cases b <;> + simp only [length_encPair, encUnary_apply, List.length_replicate, encBool_apply, + List.length_cons, List.length_nil] <;> omega + +end PolyTime + + +/-! ### Residues of integers -/ + +/-- The odd part of an integer in sign-magnitude form. -/ +theorem ediv_two_pow_twoVal (n : ℤ) : + n / 2 ^ twoVal n = if n < 0 then -((n.natAbs / 2 ^ twoVal n : ℕ) : ℤ) + else ((n.natAbs / 2 ^ twoVal n : ℕ) : ℤ) := by + have hdvd : 2 ^ twoVal n ∣ n.natAbs := by + rw [twoVal_eq]; exact pow_padicValNat_dvd + have hdvd' : (2 : ℤ) ^ twoVal n ∣ (n.natAbs : ℤ) := by exact_mod_cast hdvd + generalize twoVal n = v at * + split_ifs with h + · have hn : n = -(n.natAbs : ℤ) := by rw [Int.ofNat_natAbs_of_nonpos h.le, neg_neg] + conv_lhs => rw [hn] + rw [Int.neg_ediv_of_dvd hdvd'] + push_cast + rfl + · have hn : n = (n.natAbs : ℤ) := (Int.natAbs_of_nonneg (not_lt.mp h)).symm + conv_lhs => rw [hn] + push_cast + rfl + +/-- The residue of an integer power of two. -/ +theorem toNat_two_pow_emod (k p : ℕ) : ((2 : ℤ) ^ k % 2 ^ p).toNat = 2 ^ k % 2 ^ p := by + rw [show (2 : ℤ) ^ k % 2 ^ p = ((2 ^ k % 2 ^ p : ℕ) : ℤ) by push_cast; rfl, Int.toNat_natCast] + +/-- The residue of a natural number. -/ +theorem toNat_natCast_emod (m p : ℕ) : ((m : ℤ) % 2 ^ p).toNat = m % 2 ^ p := by + rw [show (m : ℤ) % 2 ^ p = ((m % 2 ^ p : ℕ) : ℤ) by push_cast; rfl, Int.toNat_natCast] + +section PolyTime + +variable {α : Type} {encIn : α ↪ List Bool} + +private theorem intBin_natAbs'' : + ComputableInPolyTime Int.natAbs encIntBin encNat := + .of_tidy ComputableTidilyInPolyTime.intBin_natAbs + +private theorem intBin_isNeg'' : + ComputableInPolyTime (fun z : ℤ => decide (z < 0)) encIntBin encBool := + .of_tidy ComputableTidilyInPolyTime.intBin_isNeg + +private theorem intBin_signMagnitude' : + ComputableInPolyTime (fun p : Bool × ℕ => if p.1 then -(p.2 : ℤ) else p.2) + (encPair encBool encNat) encIntBin := + .of_tidy ComputableTidilyInPolyTime.intBin_signMagnitude + +private theorem two_pow' {k : α → ℕ} (hk : ComputableInPolyTime k encIn encUnary) : + ComputableInPolyTime (fun x => 2 ^ k x) encIn encNat := + (ComputableInPolyTime.of_tidy ComputableTidilyInPolyTime.binary_two_pow).comp hk + +/-- The odd part of a binary integer. -/ +theorem oddPart_polyTime {z : α → ℤ} (hz : ComputableInPolyTime z encIn encIntBin) : + ComputableInPolyTime (fun x => z x / 2 ^ twoVal (z x)) encIn encIntBin := by + have habs := intBin_natAbs''.comp hz + have hv := twoVal_polyTime.comp hz + refine (intBin_signMagnitude'.comp ((intBin_isNeg''.comp hz).prod + (ComputableInPolyTime.binary_div.comp (habs.prod (two_pow' hv))))).of_enc_eq fun x => ?_ + simp only [Function.comp_apply, decide_eq_true_eq, ediv_two_pow_twoVal (z x)] + +/-- The residue modulo `2 ^ p` of a binary integer, with `p` in unary. -/ +theorem residue_polyTime {n : α → ℤ} {p : α → ℕ} + (hn : ComputableInPolyTime n encIn encIntBin) (hp : ComputableInPolyTime p encIn encUnary) : + ComputableInPolyTime (fun x => (n x % 2 ^ p x).toNat) encIn encNat := + (ComputableInPolyTime.intBin_emodNat' hn (two_pow' hp)).of_enc_eq fun x => by + simp only [Nat.cast_pow, Nat.cast_ofNat] + +/-- An integer as a constant coefficient list, from its residue. -/ +theorem intConst_polyTime {p : α → ℕ} {fl : α → List ℕ} {n : α → ℤ} + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hres : ComputableInPolyTime (fun x => (n x % 2 ^ p x).toNat) encIn encNat) : + ComputableInPolyTime (fun x => intConst (p x) (fl x) (n x)) encIn (encList encNat) := by + have h2 := ComputableInPolyTime.snd encIn encUnary + refine computableInPolyTime_tabulate ((ComputableInPolyTime.length_list encNat).comp hfl) + ((ComputableInPolyTime.ite (ComputableInPolyTime.beq h2 (ComputableInPolyTime.const 0)) + (hres.comp (ComputableInPolyTime.fst encIn encUnary)) + (ComputableInPolyTime.const 0)).of_enc_eq fun q => ?_) + by_cases h : q.2 = 0 <;> simp [h] + +/-- The constant list of a power of two `2 ^ k`, with `k` in unary. -/ +theorem intConst_two_pow_polyTime {p : α → ℕ} {fl : α → List ℕ} {k : α → ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hk : ComputableInPolyTime k encIn encUnary) : + ComputableInPolyTime (fun x => intConst (p x) (fl x) ((2 : ℤ) ^ k x)) encIn + (encList encNat) := + intConst_polyTime hfl ((ComputableInPolyTime.binary_mod.comp + ((two_pow' hk).prod (two_pow' hp))).of_enc_eq fun x => by + simp only [Function.comp_apply, toNat_two_pow_emod]) + +/-- The constant list of a binary natural number. -/ +theorem intConst_natCast_polyTime {p : α → ℕ} {fl : α → List ℕ} {m : α → ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hm : ComputableInPolyTime m encIn encNat) : + ComputableInPolyTime (fun x => intConst (p x) (fl x) (m x : ℤ)) encIn (encList encNat) := + intConst_polyTime hfl ((ComputableInPolyTime.binary_mod.comp + (hm.prod (two_pow' hp))).of_enc_eq fun x => by + simp only [Function.comp_apply, toNat_natCast_emod]) + +/-- The constant list of the odd part of a binary integer. -/ +theorem intConst_oddPart_polyTime {p : α → ℕ} {fl : α → List ℕ} {z : α → ℤ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hz : ComputableInPolyTime z encIn encIntBin) : + ComputableInPolyTime (fun x => intConst (p x) (fl x) (z x / 2 ^ twoVal (z x))) encIn + (encList encNat) := + intConst_polyTime hfl (residue_polyTime (oddPart_polyTime hz) hp) + +/-! ### Sums and products of lists -/ + +private theorem polyBounded_reduced' {p : α → ℕ} {fl : α → List ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) : + PolyBounded (fun x => (encIn x).length) (fun x => (fl x).length * (2 * p x + 1)) := by + have hpl := ComputableInPolyTime.polyBounded_length hp + have hfll := ComputableInPolyTime.polyBounded_length hfl + refine (hfll.mul (((PolyBounded.const 2).mul hpl).add (PolyBounded.const 1))).mono fun x => ?_ + have h1 : p x ≤ (encUnary (p x)).length := by simp + have h2 : (fl x).length ≤ (encList encNat (fl x)).length := by + rw [length_encList] + calc (fl x).length = ((fl x).map fun _ => 1).sum := by simp + _ ≤ ((fl x).map fun a => 2 * (encNat a).length + 1).sum := + List.sum_le_sum fun a _ => by omega + exact Nat.mul_le_mul h2 (Nat.add_le_add_right (Nat.mul_le_mul_left 2 h1) 1) + +/-- **Products of computed lists of coefficient lists are polynomial-time.** -/ +theorem prodList_polyTime {p : α → ℕ} {fl : α → List ℕ} {cs : α → List (List ℕ)} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hcs : ComputableInPolyTime cs encIn (encList (encList encNat))) : + ComputableInPolyTime (fun x => prodList (p x) (fl x) (cs x)) encIn (encList encNat) := by + have hstep : ComputableInPolyTime (fun q : (α × List ℕ) × List ℕ => mul (p q.1.1) (fl q.1.1) + q.1.2 q.2) (encPair (encPair encIn (encList encNat)) (encList encNat)) (encList encNat) := by + have hx := (ComputableInPolyTime.fst encIn (encList encNat)).comp + (ComputableInPolyTime.fst (encPair encIn (encList encNat)) (encList encNat)) + exact computableInPolyTime_mul (hp.comp hx) (hfl.comp hx) + ((ComputableInPolyTime.snd encIn (encList encNat)).comp (ComputableInPolyTime.fst _ _)) + (ComputableInPolyTime.snd _ _) + exact ComputableInPolyTime.foldlWithInput_of_bounded_growth + (encA := encList encNat) (encL := encList (encList encNat)) + (step := fun x acc c => mul (p x) (fl x) acc c) (computableInPolyTime_one hfl) + ⟨hcs, hstep, inferInstance, inferInstance⟩ ⟨polyBounded_reduced' hp hfl, fun x acc c _ => by + have := length_encList_le (length_mul (p x) (fl x) acc c) fun _ h => lt_of_mem_mul h + omega⟩ + +/-- **Sums of computed lists of coefficient lists are polynomial-time.** -/ +theorem sumList_polyTime {p : α → ℕ} {fl : α → List ℕ} {cs : α → List (List ℕ)} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hcs : ComputableInPolyTime cs encIn (encList (encList encNat))) : + ComputableInPolyTime (fun x => sumList (p x) (fl x) (cs x)) encIn (encList encNat) := by + have hstep : ComputableInPolyTime (fun q : (α × List ℕ) × List ℕ => add (p q.1.1) (fl q.1.1) + q.1.2 q.2) (encPair (encPair encIn (encList encNat)) (encList encNat)) (encList encNat) := by + have hx := (ComputableInPolyTime.fst encIn (encList encNat)).comp + (ComputableInPolyTime.fst (encPair encIn (encList encNat)) (encList encNat)) + exact computableInPolyTime_add (hp.comp hx) (hfl.comp hx) + ((ComputableInPolyTime.snd encIn (encList encNat)).comp (ComputableInPolyTime.fst _ _)) + (ComputableInPolyTime.snd _ _) + exact ComputableInPolyTime.foldlWithInput_of_bounded_growth + (encA := encList encNat) (encL := encList (encList encNat)) + (step := fun x acc c => add (p x) (fl x) acc c) + (computableInPolyTime_tabulate (g := fun _ _ => 0) + ((ComputableInPolyTime.length_list encNat).comp hfl) (ComputableInPolyTime.const 0)) + ⟨hcs, hstep, inferInstance, inferInstance⟩ ⟨polyBounded_reduced' hp hfl, fun x acc c _ => by + have := length_encList_le (length_add (p x) (fl x) acc c) fun _ h => lt_of_mem_add h + omega⟩ + +/-! ### Kernel values and cross ratios -/ + +/-- The linear factor `u + 2 ^ k ζ` for the odd part `u` of a binary integer. -/ +theorem linFactor_polyTime {p : α → ℕ} {fl : α → List ℕ} {z : α → ℤ} {k : α → ℕ} + {ζ : α → List ℕ} (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hz : ComputableInPolyTime z encIn encIntBin) (hk : ComputableInPolyTime k encIn encUnary) + (hζ : ComputableInPolyTime ζ encIn (encList encNat)) : + ComputableInPolyTime (fun x => linFactor (p x) (fl x) (z x / 2 ^ twoVal (z x)) (k x) (ζ x)) + encIn (encList encNat) := + computableInPolyTime_add hp hfl (intConst_oddPart_polyTime hp hfl hz) + (computableInPolyTime_mul hp hfl (intConst_two_pow_polyTime hp hfl hk) hζ) + +/-- The grid of `L` slots, with `L` in unary, as binary integers. -/ +theorem gridList_polyTime : ComputableInPolyTime gridList encUnary (encList encIntBin) := by + have hj := ComputableInPolyTime.intBin_natCast.comp (ComputableInPolyTime.unary_to_binary.comp + (ComputableInPolyTime.snd encUnary encUnary)) + have hL := ComputableInPolyTime.intBin_natCast.comp (ComputableInPolyTime.unary_to_binary.comp + (ComputableInPolyTime.fst encUnary encUnary)) + exact ComputableInPolyTime.mapWithInput (f := fun (L j : ℕ) => (j : ℤ) - (L : ℤ)) + (ComputableInPolyTime.range.comp (ComputableInPolyTime.unary_mul' + (ComputableInPolyTime.const 2) ComputableInPolyTime.id')) + (ComputableInPolyTime.intBin_sub.comp (hj.prod hL)) + +end PolyTime + + +section PolyTime + +variable {α : Type} {encIn : α ↪ List Bool} + +/-- Whether two binary integers differ. -/ +private theorem decide_ne_polyTime {u v : α → ℤ} (hu : ComputableInPolyTime u encIn encIntBin) + (hv : ComputableInPolyTime v encIn encIntBin) : + ComputableInPolyTime (fun x => decide (u x ≠ v x)) encIn encBool := + (ComputableInPolyTime.ite (ComputableInPolyTime.beq hu hv) + (ComputableInPolyTime.const (encOut := encBool) false) + (ComputableInPolyTime.const (encOut := encBool) true)).of_enc_eq fun x => by + by_cases h : u x = v x <;> simp [h] + +/-- **The odd part of `C'(0)` is polynomial-time**, with `L` in unary. -/ +theorem kernelUnitDen_polyTime {p : α → ℕ} {fl : α → List ℕ} {L : α → ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hL : ComputableInPolyTime L encIn encUnary) : + ComputableInPolyTime (fun x => kernelUnitDen (p x) (fl x) (L x)) encIn (encList encNat) := by + have h1 := ComputableInPolyTime.fst encIn encIntBin + have h2 := ComputableInPolyTime.snd encIn encIntBin + have hfilt := ComputableInPolyTime.filterWithInput (test := fun _ q => decide (q ≠ 0)) + (gridList_polyTime.comp hL) (decide_ne_polyTime h2 (ComputableInPolyTime.const 0)) + exact prodList_polyTime hp hfl (ComputableInPolyTime.mapWithInput + (f := fun x q => intConst (p x) (fl x) ((0 - q) / 2 ^ twoVal (0 - q))) hfilt + (intConst_oddPart_polyTime (hp.comp h1) (hfl.comp h1) + (ComputableInPolyTime.intBin_sub.comp ((ComputableInPolyTime.const 0).prod h2)))) + +/-- **The kernel unit is polynomial-time.** -/ +theorem kernelUnitList_polyTime {p : α → ℕ} {fl : α → List ℕ} {L : α → ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hL : ComputableInPolyTime L encIn encUnary) : + ComputableInPolyTime (fun x => kernelUnitList (p x) (fl x) (L x)) encIn (encList encNat) := + computableInPolyTime_inv hp hfl (kernelUnitDen_polyTime hp hfl hL) + +/-- **Kernel values are polynomial-time**, with `a` and `L` in unary and `t`, `d` binary. -/ +theorem kernelValueList_polyTime {p : α → ℕ} {fl : α → List ℕ} {a L : α → ℕ} {t d : α → ℤ} + {ζ : α → List ℕ} (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (ha : ComputableInPolyTime a encIn encUnary) (hL : ComputableInPolyTime L encIn encUnary) + (ht : ComputableInPolyTime t encIn encIntBin) (hd : ComputableInPolyTime d encIn encIntBin) + (hζ : ComputableInPolyTime ζ encIn (encList encNat)) : + ComputableInPolyTime (fun x => kernelValueList (p x) (fl x) (a x) (L x) (t x) (d x) (ζ x)) + encIn (encList encNat) := by + have hexp {z : α → ℤ} (hz : ComputableInPolyTime z encIn encIntBin) : + ComputableInPolyTime (fun x => a x + 1 - twoVal (z x)) encIn encUnary := + ComputableInPolyTime.unary_sub' (ComputableInPolyTime.unary_add' ha + (ComputableInPolyTime.const 1)) (twoVal_polyTime.comp hz) + have htd := ComputableInPolyTime.intBin_sub.comp (ht.prod hd) + have hmid : ComputableInPolyTime (fun x => if d x = t x then one (fl x) else + mul (p x) (fl x) (intConst (p x) (fl x) (2 ^ (a x + 1 - twoVal (t x - d x)))) (ζ x)) + encIn (encList encNat) := + (ComputableInPolyTime.ite (ComputableInPolyTime.beq hd ht) (computableInPolyTime_one hfl) + (computableInPolyTime_mul hp hfl (intConst_two_pow_polyTime hp hfl (hexp htd)) hζ)).of_enc_eq + fun x => by by_cases h : d x = t x <;> simp [h] + have h1 := ComputableInPolyTime.fst encIn encIntBin + have h2 := ComputableInPolyTime.snd encIn encIntBin + have htest : ComputableInPolyTime (fun z : α × ℤ => decide (z.2 ≠ d z.1 ∧ z.2 ≠ t z.1)) + (encPair encIn encIntBin) encBool := + (ComputableInPolyTime.ite (ComputableInPolyTime.beq h2 (hd.comp h1)) + (ComputableInPolyTime.const (encOut := encBool) false) (ComputableInPolyTime.ite + (ComputableInPolyTime.beq h2 (ht.comp h1)) + (ComputableInPolyTime.const (encOut := encBool) false) + (ComputableInPolyTime.const (encOut := encBool) true))).of_enc_eq fun z => by + by_cases h : z.2 = d z.1 <;> by_cases h' : z.2 = t z.1 <;> simp [h, h'] + have hfilt := ComputableInPolyTime.filterWithInput + (test := fun x q => decide (q ≠ d x ∧ q ≠ t x)) (gridList_polyTime.comp hL) htest + have htq := ComputableInPolyTime.intBin_sub.comp ((ht.comp h1).prod h2) + have hfac := ComputableInPolyTime.mapWithInput + (f := fun x q => linFactor (p x) (fl x) ((t x - q) / 2 ^ twoVal (t x - q)) + (a x + 1 - twoVal (t x - q)) (ζ x)) hfilt + (linFactor_polyTime (hp.comp h1) (hfl.comp h1) htq + (ComputableInPolyTime.unary_sub' (ComputableInPolyTime.unary_add' (ha.comp h1) + (ComputableInPolyTime.const 1)) (twoVal_polyTime.comp htq)) (hζ.comp h1)) + exact computableInPolyTime_mul hp hfl + (computableInPolyTime_mul hp hfl (kernelUnitList_polyTime hp hfl hL) hmid) + (prodList_polyTime hp hfl hfac) + +/-- **Cross ratios are polynomial-time.** -/ +theorem crossRatioList_polyTime {p : α → ℕ} {fl : α → List ℕ} {a i : α → ℕ} {t d : α → ℤ} + {ζ : α → List ℕ} (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (ha : ComputableInPolyTime a encIn encUnary) (hi : ComputableInPolyTime i encIn encUnary) + (ht : ComputableInPolyTime t encIn encIntBin) (hd : ComputableInPolyTime d encIn encIntBin) + (hζ : ComputableInPolyTime ζ encIn (encList encNat)) : + ComputableInPolyTime (fun x => crossRatioList (p x) (fl x) (a x) (i x) (t x) (d x) (ζ x)) + encIn (encList encNat) := by + have htd := ComputableInPolyTime.intBin_sub.comp (ht.prod hd) + have hv := twoVal_polyTime.comp htd + exact computableInPolyTime_mul hp hfl + (intConst_two_pow_polyTime hp hfl (ComputableInPolyTime.unary_sub' + (ComputableInPolyTime.unary_add' hi (ComputableInPolyTime.const 1)) hv)) + (computableInPolyTime_inv hp hfl (linFactor_polyTime hp hfl htd + (ComputableInPolyTime.unary_sub' (ComputableInPolyTime.unary_add' ha + (ComputableInPolyTime.const 1)) hv) hζ)) + +end PolyTime + + +section PolyTime + +variable {α : Type} {encIn : α ↪ List Bool} + +/-- **Local forms are polynomial-time**, with the column ports in binary. -/ +theorem localFormList_polyTime {p : α → ℕ} {fl : α → List ℕ} {a L : α → ℕ} {t : α → ℤ} + {cols : α → List (ℕ × Bool)} {x : α → ℕ} {ζ : α → List ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (ha : ComputableInPolyTime a encIn encUnary) (hL : ComputableInPolyTime L encIn encUnary) + (ht : ComputableInPolyTime t encIn encIntBin) + (hcols : ComputableInPolyTime cols encIn (encList (encPair encNat encBool))) + (hx : ComputableInPolyTime x encIn encNat) + (hζ : ComputableInPolyTime ζ encIn (encList encNat)) : + ComputableInPolyTime (fun z => localFormList (p z) (fl z) (a z) (L z) (t z) (cols z) (x z) + (ζ z)) encIn (encList encNat) := by + have h1 := ComputableInPolyTime.fst encIn (encPair encNat encBool) + have h2 := ComputableInPolyTime.snd encIn (encPair encNat encBool) + have hd := ComputableInPolyTime.intBin_sub.comp + ((ComputableInPolyTime.intBin_natCast.comp ((ComputableInPolyTime.fst _ _).comp h2)).prod + (ComputableInPolyTime.intBin_natCast.comp (hx.comp h1))) + have hf : ComputableInPolyTime (fun q : α × (ℕ × Bool) => + if q.2.2 then kernelValueList (p q.1) (fl q.1) (a q.1) (L q.1) (t q.1) ((q.2.1 : ℤ) - x q.1) + (ζ q.1) else zero (fl q.1)) (encPair encIn (encPair encNat encBool)) (encList encNat) := + ComputableInPolyTime.ite ((ComputableInPolyTime.snd _ _).comp h2) + (kernelValueList_polyTime (hp.comp h1) (hfl.comp h1) (ha.comp h1) (hL.comp h1) (ht.comp h1) + hd (hζ.comp h1)) + (computableInPolyTime_tabulate (g := fun _ _ => 0) + ((ComputableInPolyTime.length_list encNat).comp (hfl.comp h1)) + (ComputableInPolyTime.const 0)) + exact sumList_polyTime hp hfl (ComputableInPolyTime.mapWithInput + (f := fun z c => if c.2 then kernelValueList (p z) (fl z) (a z) (L z) (t z) ((c.1 : ℤ) - x z) + (ζ z) else zero (fl z)) hcols hf) + +/-- **Mode coefficients are polynomial-time**, with the slots in binary and the exponent `b` in +unary. -/ +theorem modeCoeffList_polyTime {p : α → ℕ} {fl : α → List ℕ} {a L i : α → ℕ} {t : α → ℤ} + {differ : α → Bool} {x y₀ b : α → ℕ} {ζ : α → List ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (ha : ComputableInPolyTime a encIn encUnary) (hL : ComputableInPolyTime L encIn encUnary) + (hi : ComputableInPolyTime i encIn encUnary) (ht : ComputableInPolyTime t encIn encIntBin) + (hdiffer : ComputableInPolyTime differ encIn encBool) + (hx : ComputableInPolyTime x encIn encNat) (hy₀ : ComputableInPolyTime y₀ encIn encNat) + (hb : ComputableInPolyTime b encIn encUnary) + (hζ : ComputableInPolyTime ζ encIn (encList encNat)) : + ComputableInPolyTime (fun z => modeCoeffList (p z) (fl z) (a z) (L z) (i z) (t z) (differ z) + (x z) (y₀ z) (b z) (ζ z)) encIn (encList encNat) := by + have hd := ComputableInPolyTime.intBin_sub.comp + ((ComputableInPolyTime.intBin_natCast.comp hy₀).prod + (ComputableInPolyTime.intBin_natCast.comp hx)) + have hzero := computableInPolyTime_tabulate (g := fun _ _ => 0) + ((ComputableInPolyTime.length_list encNat).comp hfl) (ComputableInPolyTime.const 0) + refine (ComputableInPolyTime.ite hdiffer (ComputableInPolyTime.ite (ComputableInPolyTime.beq + (ComputableInPolyTime.binary_testBit hx hi) (ComputableInPolyTime.binary_testBit hy₀ hi)) + (computableInPolyTime_mul hp hfl (kernelValueList_polyTime hp hfl ha hL ht hd hζ) + (computableInPolyTime_pow hp hfl (crossRatioList_polyTime hp hfl ha hi ht hd hζ) hb)) + hzero) hzero).of_enc_eq fun z => ?_ + simp only [modeCoeffList] + cases hdz : differ z <;> by_cases hbit : (x z).testBit (i z) = (y₀ z).testBit (i z) <;> + simp [hbit, GaloisRingArith.zero] + +/-- **Mode values are polynomial-time**, with the exponent `b` in unary. -/ +theorem modeValueList_polyTime {p : α → ℕ} {fl : α → List ℕ} {i : α → ℕ} {differ : α → Bool} + {cols : α → List (ℕ × Bool)} {y₀ b : α → ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (hi : ComputableInPolyTime i encIn encUnary) + (hdiffer : ComputableInPolyTime differ encIn encBool) + (hcols : ComputableInPolyTime cols encIn (encList (encPair encNat encBool))) + (hy₀ : ComputableInPolyTime y₀ encIn encNat) (hb : ComputableInPolyTime b encIn encUnary) : + ComputableInPolyTime (fun z => modeValueList (p z) (fl z) (i z) (differ z) (cols z) (y₀ z) + (b z)) encIn (encList encNat) := by + have h1 := ComputableInPolyTime.fst encIn (encPair encNat encBool) + have h2 := ComputableInPolyTime.snd encIn (encPair encNat encBool) + have hc := (ComputableInPolyTime.fst encNat encBool).comp h2 + have hbit := (ComputableInPolyTime.snd encNat encBool).comp h2 + have hT := two_pow' (ComputableInPolyTime.unary_add' (hi.comp h1) (ComputableInPolyTime.const 1)) + have hzero := computableInPolyTime_tabulate (g := fun _ _ => 0) + ((ComputableInPolyTime.length_list encNat).comp (hfl.comp h1)) (ComputableInPolyTime.const 0) + have hpow := computableInPolyTime_pow (hp.comp h1) (hfl.comp h1) + (intConst_natCast_polyTime (hp.comp h1) (hfl.comp h1) + (ComputableInPolyTime.binary_div.comp (hc.prod hT))) (hb.comp h1) + have hf : ComputableInPolyTime (fun q : α × (ℕ × Bool) => + if q.2.2 = true ∧ differ q.1 = true ∧ q.2.1 % 2 ^ (i q.1 + 1) = y₀ q.1 then + pow (p q.1) (fl q.1) (intConst (p q.1) (fl q.1) ((q.2.1 / 2 ^ (i q.1 + 1) : ℕ) : ℤ)) (b q.1) + else zero (fl q.1)) (encPair encIn (encPair encNat encBool)) (encList encNat) := by + refine (ComputableInPolyTime.ite hbit (ComputableInPolyTime.ite (hdiffer.comp h1) + (ComputableInPolyTime.ite (ComputableInPolyTime.beq + (ComputableInPolyTime.binary_mod.comp (hc.prod hT)) (hy₀.comp h1)) hpow hzero) hzero) + hzero).of_enc_eq fun q => ?_ + cases hq : q.2.2 <;> cases hd : differ q.1 <;> + by_cases hm : q.2.1 % 2 ^ (i q.1 + 1) = y₀ q.1 <;> simp [hq, hd, hm, GaloisRingArith.zero] + exact sumList_polyTime hp hfl (ComputableInPolyTime.mapWithInput + (f := fun z c => if c.2 = true ∧ differ z = true ∧ c.1 % 2 ^ (i z + 1) = y₀ z then + pow (p z) (fl z) (intConst (p z) (fl z) ((c.1 / 2 ^ (i z + 1) : ℕ) : ℤ)) (b z) + else zero (fl z)) hcols hf) + +/-- **Row terms are polynomial-time**, with `nb` in unary and the mode index in binary. -/ +theorem rowTermList_polyTime {p : α → ℕ} {fl : α → List ℕ} {a L i nb : α → ℕ} {t : α → ℤ} + {differ : α → Bool} {cols : α → List (ℕ × Bool)} {ζ : α → List ℕ} {r : α → ℕ × Bool} + {o : α → Option ℕ} + (hp : ComputableInPolyTime p encIn encUnary) + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (ha : ComputableInPolyTime a encIn encUnary) (hL : ComputableInPolyTime L encIn encUnary) + (hi : ComputableInPolyTime i encIn encUnary) (ht : ComputableInPolyTime t encIn encIntBin) + (hnb : ComputableInPolyTime nb encIn encUnary) + (hdiffer : ComputableInPolyTime differ encIn encBool) + (hcols : ComputableInPolyTime cols encIn (encList (encPair encNat encBool))) + (hζ : ComputableInPolyTime ζ encIn (encList encNat)) + (hr : ComputableInPolyTime r encIn (encPair encNat encBool)) + (ho : ComputableInPolyTime o encIn (encOption encNat)) : + ComputableInPolyTime (fun z => rowTermList (p z) (fl z) (a z) (L z) (i z) (t z) (nb z) + (differ z) (cols z) (ζ z) (r z) (o z)) encIn (encList encNat) := by + have hx := (ComputableInPolyTime.fst encNat encBool).comp hr + have hbit := (ComputableInPolyTime.snd encNat encBool).comp hr + have hloc := localFormList_polyTime hp hfl ha hL ht hcols hx hζ + have hnone : ComputableInPolyTime (fun z => if (r z).2 then + localFormList (p z) (fl z) (a z) (L z) (t z) (cols z) (r z).1 (ζ z) else + sub (p z) (fl z) (one (fl z)) (localFormList (p z) (fl z) (a z) (L z) (t z) (cols z) + (r z).1 (ζ z))) encIn (encList encNat) := + ComputableInPolyTime.ite hbit hloc + (computableInPolyTime_sub hp hfl (computableInPolyTime_one hfl) hloc) + have h1 := ComputableInPolyTime.fst encIn encNat + have hk := ComputableInPolyTime.snd encIn encNat + have hnb' := ComputableInPolyTime.unary_to_binary.comp (hnb.comp h1) + have hy := ComputableInPolyTime.binary_div.comp (hk.prod hnb') + have hexp := ComputableInPolyTime.binary_min_unary' (hnb.comp h1) + (ComputableInPolyTime.binary_mod.comp (hk.prod hnb')) + have hcoef := modeCoeffList_polyTime (hp.comp h1) (hfl.comp h1) (ha.comp h1) (hL.comp h1) + (hi.comp h1) (ht.comp h1) (hdiffer.comp h1) (hx.comp h1) hy hexp (hζ.comp h1) + have hzero := computableInPolyTime_tabulate (g := fun _ _ => 0) + ((ComputableInPolyTime.length_list encNat).comp (hfl.comp h1)) (ComputableInPolyTime.const 0) + have hsome : ComputableInPolyTime (fun q : α × ℕ => if nb q.1 = 0 then zero (fl q.1) else + if (r q.1).2 then modeCoeffList (p q.1) (fl q.1) (a q.1) (L q.1) (i q.1) (t q.1) + (differ q.1) (r q.1).1 (q.2 / nb q.1) (q.2 % nb q.1) (ζ q.1) + else neg (p q.1) (fl q.1) (modeCoeffList (p q.1) (fl q.1) (a q.1) (L q.1) (i q.1) (t q.1) + (differ q.1) (r q.1).1 (q.2 / nb q.1) (q.2 % nb q.1) (ζ q.1))) + (encPair encIn encNat) (encList encNat) := by + refine (ComputableInPolyTime.ite (ComputableInPolyTime.beq (hnb.comp h1) + (ComputableInPolyTime.const 0)) hzero (ComputableInPolyTime.ite (hbit.comp h1) hcoef + (computableInPolyTime_neg (hp.comp h1) (hfl.comp h1) hcoef))).of_enc_eq fun q => ?_ + by_cases h0 : nb q.1 = 0 + · simp [h0, GaloisRingArith.zero] + · have hmin : min (nb q.1) (q.2 % nb q.1) = q.2 % nb q.1 := + min_eq_right (Nat.mod_lt _ (Nat.pos_of_ne_zero h0)).le + simp only [h0, beq_iff_eq, ↓reduceIte, Function.comp_apply, hmin] + refine (ComputableInPolyTime.optionElimWithInput (body := fun z k => if nb z = 0 then zero (fl z) + else if (r z).2 then modeCoeffList (p z) (fl z) (a z) (L z) (i z) (t z) (differ z) (r z).1 + (k / nb z) (k % nb z) (ζ z) + else neg (p z) (fl z) (modeCoeffList (p z) (fl z) (a z) (L z) (i z) (t z) (differ z) (r z).1 + (k / nb z) (k % nb z) (ζ z))) ho hnone hsome).of_enc_eq fun z => ?_ + cases o z <;> rfl + +end PolyTime + + +/-! ### Coefficient extraction for a packet -/ + +/-- **The context of a packet entry**: the precision `p` and the coefficients `fl` of `f`, the +bit counts `a` and `i`, the number of slots `L = 2 ^ a` and of tiles `tiles = 2 ^ (i + 1)`, the +grid point `t`, the number `nb` of exponents per tile, whether the two symbols of the packet are +the group (`e₁`, `e₂`) and whether they differ, the column ports, and `ζ`. -/ +structure PacketCtx where + /-- The precision. -/ + p : ℕ + /-- The coefficients of `f` below the leading one. -/ + fl : List ℕ + /-- The number of slot bits. -/ + a : ℕ + /-- The number of slots, `2 ^ a`. -/ + L : ℕ + /-- The digit of the tiles. -/ + i : ℕ + /-- The number of tiles, `2 ^ (i + 1)`. -/ + tiles : ℕ + /-- The grid point. -/ + t : ℤ + /-- The number of exponents per tile. -/ + nb : ℕ + /-- Whether the first symbol is the group. -/ + e₁ : Bool + /-- Whether the second symbol is the group. -/ + e₂ : Bool + /-- Whether the two symbols differ. -/ + differ : Bool + /-- The column ports: slots and bits. -/ + cols : List (ℕ × Bool) + /-- The interpolation value. -/ + ζ : List ℕ + +namespace PacketCtx + +/-- A packet context as nested pairs. -/ +def equivProd : PacketCtx ≃ (ℕ × List ℕ) × ((ℕ × ℕ) × ((ℕ × ℕ) × (ℤ × (ℕ × + ((Bool × Bool × Bool) × (List (ℕ × Bool) × List ℕ)))))) where + toFun c := ((c.p, c.fl), ((c.a, c.L), ((c.i, c.tiles), (c.t, (c.nb, + ((c.e₁, c.e₂, c.differ), (c.cols, c.ζ))))))) + invFun q := ⟨q.1.1, q.1.2, q.2.1.1, q.2.1.2, q.2.2.1.1, q.2.2.1.2, q.2.2.2.1, q.2.2.2.2.1, + q.2.2.2.2.2.1.1, q.2.2.2.2.2.1.2.1, q.2.2.2.2.2.1.2.2, q.2.2.2.2.2.2.1, q.2.2.2.2.2.2.2⟩ + left_inv _ := rfl + right_inv _ := rfl + +/-- The encoding of the nested pairs of a packet context. -/ +abbrev encProd : (ℕ × List ℕ) × ((ℕ × ℕ) × ((ℕ × ℕ) × (ℤ × (ℕ × + ((Bool × Bool × Bool) × (List (ℕ × Bool) × List ℕ)))))) ↪ List Bool := + encPair (encPair encUnary (encList encNat)) + (encPair (encPair encUnary encUnary) (encPair (encPair encUnary encUnary) + (encPair encIntBin (encPair encUnary (encPair (encPair encBool (encPair encBool encBool)) + (encPair (encList (encPair encNat encBool)) (encList encNat))))))) + +/-- **The encoding of a packet context**: the counts in unary, `t` in binary sign-magnitude +form, and the lists in binary. -/ +def enc : PacketCtx ↪ List Bool := encOfEquiv equivProd encProd + +private theorem toProd_polyTime : ComputableInPolyTime equivProd enc encProd := + ComputableInPolyTime.of_eq fun _ => rfl + +private theorem rest_polyTime : + ComputableInPolyTime (fun c : PacketCtx => (equivProd c).2.2.2.2.2) enc + (encPair (encPair encBool (encPair encBool encBool)) + (encPair (encList (encPair encNat encBool)) (encList encNat))) := + (ComputableInPolyTime.snd _ _).comp ((ComputableInPolyTime.snd _ _).comp + ((ComputableInPolyTime.snd _ _).comp ((ComputableInPolyTime.snd _ _).comp + ((ComputableInPolyTime.snd _ _).comp toProd_polyTime)))) + +/-- The precision, in polynomial time. -/ +theorem p_polyTime : ComputableInPolyTime PacketCtx.p enc encUnary := + (ComputableInPolyTime.fst _ _).comp ((ComputableInPolyTime.fst _ _).comp toProd_polyTime) +/-- The coefficients of `f`, in polynomial time. -/ +theorem fl_polyTime : ComputableInPolyTime PacketCtx.fl enc (encList encNat) := + (ComputableInPolyTime.snd _ _).comp ((ComputableInPolyTime.fst _ _).comp toProd_polyTime) +/-- The number of slot bits, in polynomial time. -/ +theorem a_polyTime : ComputableInPolyTime PacketCtx.a enc encUnary := + (ComputableInPolyTime.fst _ _).comp ((ComputableInPolyTime.fst _ _).comp + ((ComputableInPolyTime.snd _ _).comp toProd_polyTime)) +/-- The number of slots, in polynomial time. -/ +theorem L_polyTime : ComputableInPolyTime PacketCtx.L enc encUnary := + (ComputableInPolyTime.snd _ _).comp ((ComputableInPolyTime.fst _ _).comp + ((ComputableInPolyTime.snd _ _).comp toProd_polyTime)) +/-- The digit of the tiles, in polynomial time. -/ +theorem i_polyTime : ComputableInPolyTime PacketCtx.i enc encUnary := + (ComputableInPolyTime.fst _ _).comp ((ComputableInPolyTime.fst _ _).comp + ((ComputableInPolyTime.snd _ _).comp ((ComputableInPolyTime.snd _ _).comp toProd_polyTime))) +/-- The number of tiles, in polynomial time. -/ +theorem tiles_polyTime : ComputableInPolyTime PacketCtx.tiles enc encUnary := + (ComputableInPolyTime.snd _ _).comp ((ComputableInPolyTime.fst _ _).comp + ((ComputableInPolyTime.snd _ _).comp ((ComputableInPolyTime.snd _ _).comp toProd_polyTime))) +/-- The grid point, in polynomial time. -/ +theorem t_polyTime : ComputableInPolyTime PacketCtx.t enc encIntBin := + (ComputableInPolyTime.fst _ _).comp ((ComputableInPolyTime.snd _ _).comp + ((ComputableInPolyTime.snd _ _).comp ((ComputableInPolyTime.snd _ _).comp toProd_polyTime))) +/-- The number of exponents per tile, in polynomial time. -/ +theorem nb_polyTime : ComputableInPolyTime PacketCtx.nb enc encUnary := + (ComputableInPolyTime.fst _ _).comp ((ComputableInPolyTime.snd _ _).comp + ((ComputableInPolyTime.snd _ _).comp ((ComputableInPolyTime.snd _ _).comp + ((ComputableInPolyTime.snd _ _).comp toProd_polyTime)))) +/-- Whether the first symbol is the group, in polynomial time. -/ +theorem e₁_polyTime : ComputableInPolyTime PacketCtx.e₁ enc encBool := + (ComputableInPolyTime.fst _ _).comp ((ComputableInPolyTime.fst _ _).comp rest_polyTime) +/-- Whether the second symbol is the group, in polynomial time. -/ +theorem e₂_polyTime : ComputableInPolyTime PacketCtx.e₂ enc encBool := + (ComputableInPolyTime.fst _ _).comp ((ComputableInPolyTime.snd _ _).comp + ((ComputableInPolyTime.fst _ _).comp rest_polyTime)) +/-- Whether the symbols differ, in polynomial time. -/ +theorem differ_polyTime : ComputableInPolyTime PacketCtx.differ enc encBool := + (ComputableInPolyTime.snd _ _).comp ((ComputableInPolyTime.snd _ _).comp + ((ComputableInPolyTime.fst _ _).comp rest_polyTime)) +/-- The column ports, in polynomial time. -/ +theorem cols_polyTime : ComputableInPolyTime PacketCtx.cols enc + (encList (encPair encNat encBool)) := + (ComputableInPolyTime.fst _ _).comp ((ComputableInPolyTime.snd _ _).comp rest_polyTime) +/-- The interpolation value, in polynomial time. -/ +theorem ζ_polyTime : ComputableInPolyTime PacketCtx.ζ enc (encList encNat) := + (ComputableInPolyTime.snd _ _).comp ((ComputableInPolyTime.snd _ _).comp rest_polyTime) + +end PacketCtx + +/-- The dense indices of the modes of a packet whose tiles target the group. -/ +def targetModes (c : PacketCtx) : List ℕ := + (List.range (c.tiles * c.nb)).filter fun k => modeTargets c.i c.e₁ c.e₂ (k / c.nb) + +/-- **Coefficient extraction for a packet**: the precision, the target modes and the row +terms. -/ +def packetTerms : CoeffScheme.Terms PacketCtx (ℕ × Bool) where + prec c := (c.p, c.fl) + modes := targetModes + term c r o := rowTermList c.p c.fl c.a c.L c.i c.t c.nb c.differ c.cols c.ζ r o + +/-- **The data of coefficient extraction for a packet are polynomial-time.** -/ +theorem packetTerms_polyTime : packetTerms.PolyTime PacketCtx.enc (encPair encNat encBool) where + prec := PacketCtx.p_polyTime.prod PacketCtx.fl_polyTime + modes := by + have hrange : ComputableInPolyTime (fun c : PacketCtx => List.range (c.tiles * c.nb)) + PacketCtx.enc (encList encNat) := + (ComputableInPolyTime.mapWithInput (f := fun _ (k : ℕ) => k) + (ComputableInPolyTime.range.comp (ComputableInPolyTime.unary_mul' + PacketCtx.tiles_polyTime PacketCtx.nb_polyTime)) + (ComputableInPolyTime.unary_to_binary.comp + (ComputableInPolyTime.snd PacketCtx.enc encUnary))).of_enc_eq fun c => by + simp + have h1 := ComputableInPolyTime.fst PacketCtx.enc encNat + have h2 := ComputableInPolyTime.snd PacketCtx.enc encNat + have hy := ComputableInPolyTime.binary_div.comp + (h2.prod (ComputableInPolyTime.unary_to_binary.comp (PacketCtx.nb_polyTime.comp h1))) + have htest : ComputableInPolyTime (fun q : PacketCtx × ℕ => + modeTargets q.1.i q.1.e₁ q.1.e₂ (q.2 / q.1.nb)) (encPair PacketCtx.enc encNat) + encBool := + ComputableInPolyTime.ite (ComputableInPolyTime.binary_testBit hy + (PacketCtx.i_polyTime.comp h1)) (PacketCtx.e₂_polyTime.comp h1) + (PacketCtx.e₁_polyTime.comp h1) + exact ComputableInPolyTime.filterWithInput hrange htest + term := by + let E := encPair PacketCtx.enc (encPair (encPair encNat encBool) (encOption encNat)) + have hc := ComputableInPolyTime.fst PacketCtx.enc + (encPair (encPair encNat encBool) (encOption encNat)) + have hr := (ComputableInPolyTime.fst (encPair encNat encBool) (encOption encNat)).comp + (ComputableInPolyTime.snd PacketCtx.enc (encPair (encPair encNat encBool) + (encOption encNat))) + have ho := (ComputableInPolyTime.snd (encPair encNat encBool) (encOption encNat)).comp + (ComputableInPolyTime.snd PacketCtx.enc (encPair (encPair encNat encBool) + (encOption encNat))) + exact rowTermList_polyTime (PacketCtx.p_polyTime.comp hc) (PacketCtx.fl_polyTime.comp hc) + (PacketCtx.a_polyTime.comp hc) (PacketCtx.L_polyTime.comp hc) + (PacketCtx.i_polyTime.comp hc) (PacketCtx.t_polyTime.comp hc) + (PacketCtx.nb_polyTime.comp hc) (PacketCtx.differ_polyTime.comp hc) + (PacketCtx.cols_polyTime.comp hc) (PacketCtx.ζ_polyTime.comp hc) hr ho + +/-- **Coefficient extraction for a packet is correct**: with `tiles = 2 ^ (i + 1)`, the value of +the recursion in `A_p` is that of the coefficient extraction of `packetEntryList`. -/ +theorem val_packetTerms_value (c : PacketCtx) (htiles : c.tiles = 2 ^ (c.i + 1)) + (rows : List (ℕ × Bool)) (γ : List ℕ) : + val c.p c.fl (packetTerms.value c rows γ) = + val c.p c.fl (listCoeffList c.p c.fl ((List.range (2 ^ (c.i + 1) * c.nb)).filter fun k => + modeTargets c.i c.e₁ c.e₂ (k / c.nb)) + (rows.map (rowTermList c.p c.fl c.a c.L c.i c.t c.nb c.differ c.cols c.ζ)) + (fun k => γ.getD k 0)) := by + rw [val_listCoeffList (List.nodup_range.filter _), List.map_map] + have h := packetTerms.val_value c ((List.nodup_range).filter _) rows γ + simp only [packetTerms, targetModes, htiles] at h + exact h + + +/-! ### Packet entries natively -/ + +/-- The dense indices of the modes of a packet whose tiles have the group as source. -/ +def sourceModes (c : PacketCtx) : List ℕ := + (List.range (c.tiles * c.nb)).filter fun k => modeSources c.i c.e₁ c.e₂ (k / c.nb) + +/-- The product of the powers of the values of the source modes, at the exponents of a dense +list. -/ +def sourceProduct (c : PacketCtx) (β : List ℕ) : List ℕ := + prodList c.p c.fl ((sourceModes c).map fun k => + pow c.p c.fl (modeValueList c.p c.fl c.i c.differ c.cols (k / c.nb) (k % c.nb)) (β.getD k 0)) + +/-- **A packet entry, natively**: coefficient extraction by the stack recursion, at the +exponents of the dense list `β`, times the product of the powers of the source mode values. -/ +def packetEntryNative (c : PacketCtx) (rows : List (ℕ × Bool)) (β : List ℕ) : List ℕ := + mul c.p c.fl (packetTerms.value c rows β) (sourceProduct c β) + +/-- **The native packet entry is `packetEntryList`** in `A_p`, when `tiles = 2 ^ (i + 1)`. -/ +theorem val_packetEntryNative (c : PacketCtx) (htiles : c.tiles = 2 ^ (c.i + 1)) + (rows : List (ℕ × Bool)) (β : List ℕ) : + val c.p c.fl (packetEntryNative c rows β) = + val c.p c.fl (packetEntryList c.p c.fl c.a c.L c.i c.t c.nb c.e₁ c.e₂ c.differ + (fun k => β.getD k 0) rows c.cols c.ζ) := by + rw [packetEntryNative, packetEntryList, val_mul, val_mul, val_packetTerms_value c htiles, + sourceProduct, sourceModes, htiles] + +/-- **The native packet entry is the packet entry** (§D.4.6, p. 63): for a grid point `t` of +valuation `i < a`, `L = 2 ^ a`, `tiles = 2 ^ (i + 1)` and ports in slots below `L`, its value in +`A_p` is `packetEntry` at the reduction of `ℤ_(2)`, the value of `ζ`, and the exponents of `β` +at the dense indices of the modes. -/ +theorem val_packetEntryNative_eq (c : PacketCtx) (hL : c.L = 2 ^ c.a) (hia : c.i < c.a) + (htiles : c.tiles = 2 ^ (c.i + 1)) (ht : c.t ∈ cauchyGrid (2 ^ c.a)) + (hti : (2 : ℤ) ^ c.i ∣ c.t) (hti' : ¬ (2 : ℤ) ^ (c.i + 1) ∣ c.t) + {rows : List (ℕ × Bool)} (hrows : ∀ r ∈ rows, r.1 < 2 ^ c.a) + (hcols : ∀ r ∈ c.cols, r.1 < 2 ^ c.a) (β : List ℕ) : + val c.p c.fl (packetEntryNative c rows β) = + packetEntry (p := c.nb) (scalarHom c.p c.fl) (val c.p c.fl c.ζ) c.a c.i c.t c.e₁ c.e₂ + c.differ (fun Z => β.getD (Z.1 * c.nb + Z.2) 0) rows c.cols := by + rw [val_packetEntryNative c htiles, val_packetEntryList hL hia ht hti hti' c.nb c.e₁ c.e₂ + c.differ _ hrows hcols] + +/-- **The native packet entry in every ring receiving `A_p`.** Along a ring map `ψ : A_p → S` +with `2` nilpotent in `S`, the value of the native entry is `packetEntry` over `S` at any ring +map `κ : ℤ_(2) → S` and at the image of `ζ`; with `child_tensor_inr_cauchyModeSplitting`, it is +the entry of a packet vertex of a child network over `S`. -/ +theorem map_val_packetEntryNative {S : Type*} [CommRing S] (c : PacketCtx) + (ψ : Ring c.p c.fl →+* S) {n : ℕ} (hn : (2 : S) ^ n = 0) (κ : ℤ₍₂₎ →+* S) + (hL : c.L = 2 ^ c.a) (hia : c.i < c.a) (htiles : c.tiles = 2 ^ (c.i + 1)) + (ht : c.t ∈ cauchyGrid (2 ^ c.a)) (hti : (2 : ℤ) ^ c.i ∣ c.t) + (hti' : ¬ (2 : ℤ) ^ (c.i + 1) ∣ c.t) {rows : List (ℕ × Bool)} + (hrows : ∀ r ∈ rows, r.1 < 2 ^ c.a) (hcols : ∀ r ∈ c.cols, r.1 < 2 ^ c.a) (β : List ℕ) : + ψ (val c.p c.fl (packetEntryNative c rows β)) = + packetEntry (p := c.nb) κ (ψ (val c.p c.fl c.ζ)) c.a c.i c.t c.e₁ c.e₂ c.differ + (fun Z => β.getD (Z.1 * c.nb + Z.2) 0) rows c.cols := by + rw [val_packetEntryNative c htiles, map_val_packetEntryList ψ hn κ hL hia ht hti hti' c.nb + c.e₁ c.e₂ c.differ _ hrows hcols] + +section Certificate + +/-- The input of the native packet evaluator: a context, the row ports and the dense exponents, +the exponents in unary. -/ +abbrev encEntryInput : PacketCtx × (List (ℕ × Bool) × List ℕ) ↪ List Bool := + encPair PacketCtx.enc (encPair (encList (encPair encNat encBool)) (encList encUnary)) + +/-- The root of coefficient extraction, with the exponents re-encoded in binary. -/ +theorem node_polyTime : + ComputableInPolyTime (fun q : PacketCtx × (List (ℕ × Bool) × List ℕ) => q) + encEntryInput (CoeffScheme.Terms.encNode PacketCtx.enc (encPair encNat encBool)) := by + have hβ := (ComputableInPolyTime.snd (encList (encPair encNat encBool)) (encList encUnary)).comp + (ComputableInPolyTime.snd PacketCtx.enc + (encPair (encList (encPair encNat encBool)) (encList encUnary))) + have hβ' : ComputableInPolyTime (fun q : PacketCtx × (List (ℕ × Bool) × List ℕ) => q.2.2) + encEntryInput (encList encNat) := + (ComputableInPolyTime.mapWithInput (f := fun _ (k : ℕ) => k) hβ + (ComputableInPolyTime.unary_to_binary.comp (ComputableInPolyTime.snd _ _))).of_enc_eq + fun q => by simp + exact (ComputableInPolyTime.fst _ _).prod + (((ComputableInPolyTime.fst (encList (encPair encNat encBool)) (encList encUnary)).comp + (ComputableInPolyTime.snd _ _)).prod hβ') + +/-- The source product is polynomial-time, with the exponents in unary. -/ +theorem sourceProduct_polyTime : + ComputableInPolyTime (fun q : PacketCtx × (List (ℕ × Bool) × List ℕ) => + sourceProduct q.1 q.2.2) encEntryInput (encList encNat) := by + have hc := ComputableInPolyTime.fst PacketCtx.enc + (encPair (encList (encPair encNat encBool)) (encList encUnary)) + have hβ := (ComputableInPolyTime.snd (encList (encPair encNat encBool)) (encList encUnary)).comp + (ComputableInPolyTime.snd PacketCtx.enc + (encPair (encList (encPair encNat encBool)) (encList encUnary))) + have hp := PacketCtx.p_polyTime.comp hc + have hfl := PacketCtx.fl_polyTime.comp hc + have hrange : ComputableInPolyTime (fun q : PacketCtx × (List (ℕ × Bool) × List ℕ) => + List.range (q.1.tiles * q.1.nb)) encEntryInput (encList encNat) := + (ComputableInPolyTime.mapWithInput (f := fun _ (k : ℕ) => k) + (ComputableInPolyTime.range.comp (ComputableInPolyTime.unary_mul' + (PacketCtx.tiles_polyTime.comp hc) (PacketCtx.nb_polyTime.comp hc))) + (ComputableInPolyTime.unary_to_binary.comp (ComputableInPolyTime.snd _ _))).of_enc_eq + fun q => by simp + have h1 := ComputableInPolyTime.fst encEntryInput encNat + have h2 := ComputableInPolyTime.snd encEntryInput encNat + have hnb := ComputableInPolyTime.unary_to_binary.comp (PacketCtx.nb_polyTime.comp (hc.comp h1)) + have hy := ComputableInPolyTime.binary_div.comp (h2.prod hnb) + have htest : ComputableInPolyTime (fun z : (PacketCtx × (List (ℕ × Bool) × List ℕ)) × ℕ => + modeSources z.1.1.i z.1.1.e₁ z.1.1.e₂ (z.2 / z.1.1.nb)) (encPair encEntryInput encNat) + encBool := + ComputableInPolyTime.ite (ComputableInPolyTime.binary_testBit hy + (PacketCtx.i_polyTime.comp (hc.comp h1))) (PacketCtx.e₁_polyTime.comp (hc.comp h1)) + (PacketCtx.e₂_polyTime.comp (hc.comp h1)) + have hmodes := ComputableInPolyTime.filterWithInput + (test := fun q k => modeSources q.1.i q.1.e₁ q.1.e₂ (k / q.1.nb)) hrange htest + have hexp := ComputableInPolyTime.binary_min_unary' + (ComputableInPolyTime.unary_add' (PacketCtx.nb_polyTime.comp (hc.comp h1)) + (ComputableInPolyTime.const 1)) + (ComputableInPolyTime.binary_mod.comp (h2.prod hnb)) + have hval := modeValueList_polyTime (hp.comp h1) (hfl.comp h1) + (PacketCtx.i_polyTime.comp (hc.comp h1)) (PacketCtx.differ_polyTime.comp (hc.comp h1)) + (PacketCtx.cols_polyTime.comp (hc.comp h1)) hy hexp + have hβk : ComputableInPolyTime (fun z : (PacketCtx × (List (ℕ × Bool) × List ℕ)) × ℕ => + z.1.2.2.getD z.2 0) (encPair encEntryInput encNat) encUnary := + ComputableInPolyTime.listGetD_binary (encA := encUnary) 0 (hβ.comp h1) h2 + have hlist := ComputableInPolyTime.mapWithInput + (f := fun q k => pow q.1.p q.1.fl (modeValueList q.1.p q.1.fl q.1.i q.1.differ q.1.cols + (k / q.1.nb) (min (q.1.nb + 1) (k % q.1.nb))) (q.2.2.getD k 0)) hmodes + (computableInPolyTime_pow (hp.comp h1) (hfl.comp h1) hval hβk) + refine (prodList_polyTime hp hfl hlist).of_enc_eq fun q => ?_ + simp only [sourceProduct, sourceModes] + congr 2 + refine List.map_congr_left fun k hk => ?_ + have hk' := List.mem_range.mp (List.mem_filter.mp hk).1 + have hnb0 : 0 < q.1.nb := Nat.pos_of_ne_zero fun h0 => by simp [h0] at hk' + rw [min_eq_right ((Nat.mod_lt _ hnb0).le.trans (Nat.le_succ _))] + +/-- The final product of the native packet evaluator is polynomial-time. -/ +theorem mulFinal_polyTime : + ComputableInPolyTime (fun z : (PacketCtx × (List (ℕ × Bool) × List ℕ)) × (List ℕ × List ℕ) => + mul z.1.1.p z.1.1.fl z.2.1 z.2.2) + (encPair encEntryInput (encPair (encList encNat) (encList encNat))) (encList encNat) := by + have hc := (ComputableInPolyTime.fst PacketCtx.enc + (encPair (encList (encPair encNat encBool)) (encList encUnary))).comp + (ComputableInPolyTime.fst encEntryInput (encPair (encList encNat) (encList encNat))) + have hy := ComputableInPolyTime.snd encEntryInput (encPair (encList encNat) (encList encNat)) + exact computableInPolyTime_mul (PacketCtx.p_polyTime.comp hc) (PacketCtx.fl_polyTime.comp hc) + ((ComputableInPolyTime.fst _ _).comp hy) ((ComputableInPolyTime.snd _ _).comp hy) + +/-- **The native packet evaluator**: one native machine for `packetEntryNative` on every +context, row ports and dense exponents (in unary). Its time is that of the stack recursion of +coefficient extraction (`CoeffScheme.Terms.valueCertificate_le`, `exp(O(L log m))` times a +polynomial), plus polynomial time. -/ +noncomputable def entryCertificate : + NativeCertificate (fun q : PacketCtx × (List (ℕ × Bool) × List ℕ) => + packetEntryNative q.1 q.2.1 q.2.2) encEntryInput (encList encNat) := + ((NativeCertificate.ofPolyTime mulFinal_polyTime).comp + ((NativeCertificate.ofPolyTime ComputableInPolyTime.id').prod + (((CoeffScheme.Terms.valueCertificate packetTerms_polyTime).comp + (NativeCertificate.ofPolyTime node_polyTime)).prod + (NativeCertificate.ofPolyTime sourceProduct_polyTime)))).congr fun _ => rfl + + +/-- The value of coefficient extraction is reduced. -/ +theorem reduced_value {C Rw : Type} (S : CoeffScheme.Terms C Rw) (x : C) (rows : List Rw) + (γ : List ℕ) : SumOps.galois.Reduced (S.prec x) (S.value x rows γ) := by + cases rows with + | nil => exact S.reduced_leafValue x γ + | cons r rs => + exact SumOps.galois.reduced_foldl (S.prec x) _ (SumOps.galois.reduced_add (S.prec x)) _ _ + (SumOps.galois.reduced_zero (S.prec x)) + +/-- The source product is reduced. -/ +theorem reduced_sourceProduct (c : PacketCtx) (β : List ℕ) : + SumOps.galois.Reduced (c.p, c.fl) (sourceProduct c β) := + SumOps.galois.reduced_foldl (c.p, c.fl) _ (SumOps.galois.reduced_mul (c.p, c.fl)) id _ + (SumOps.galois.reduced_one (c.p, c.fl)) + +/-- **The resources of the native packet evaluator.** There is `K` such that on an input of +length `n` with `m` target modes and `L` row ports, if the reduced coefficient lists of its +precision are at most `Rv` bits long, then with `Z = n + m + Rv + 2` the time is at most +`K (m + 2) ^ L Z ^ K` and the space at most `K Z ^ K`: the `exp(O(L log s))` time and polynomial +space of §D.4.6 (p. 63). -/ +theorem entryCertificate_le : + ∃ K : ℕ, ∀ (q : PacketCtx × (List (ℕ × Bool) × List ℕ)) (Rv : ℕ), + (∀ y, SumOps.galois.Reduced (q.1.p, q.1.fl) y → (encList encNat y).length ≤ Rv) → + entryCertificate.time q ≤ K * ((targetModes q.1).length + 2) ^ q.2.1.length * + ((encEntryInput q).length + (targetModes q.1).length + Rv + 2) ^ K ∧ + entryCertificate.space q ≤ + K * ((encEntryInput q).length + (targetModes q.1).length + Rv + 2) ^ K := by + obtain ⟨K₁, hK₁⟩ := CoeffScheme.Terms.valueCertificate_le packetTerms_polyTime + have hM := NativeCertificate.ofPolyTime_isPolynomial mulFinal_polyTime + have hI := NativeCertificate.ofPolyTime_isPolynomial + (ComputableInPolyTime.id' (encIn := encEntryInput)) + have hN := NativeCertificate.ofPolyTime_isPolynomial node_polyTime + have hP := NativeCertificate.ofPolyTime_isPolynomial sourceProduct_polyTime + obtain ⟨cm, dm, hmt⟩ := hM.1.exists_le + obtain ⟨cm', dm', hms⟩ := hM.2.exists_le + obtain ⟨ci, di, hit⟩ := hI.1.exists_le + obtain ⟨ci', di', his⟩ := hI.2.exists_le + obtain ⟨cn, dn, hnt⟩ := hN.1.exists_le + obtain ⟨cn', dn', hns⟩ := hN.2.exists_le + obtain ⟨cp, dp, hpt⟩ := hP.1.exists_le + obtain ⟨cp', dp', hps⟩ := hP.2.exists_le + obtain ⟨D, hD⟩ : ∃ D, D = K₁ + dm + dm' + di + di' + dn + dn' + dp + dp' := ⟨_, rfl⟩ + obtain ⟨G, hG⟩ : ∃ G, G = K₁ + cm * 8 ^ dm + cm' * 8 ^ dm' + ci + ci' + cn + cn' + cp + cp' := + ⟨_, rfl⟩ + refine ⟨G + D, fun q Rv hRv => ?_⟩ + obtain ⟨n, hn⟩ : ∃ n, n = (encEntryInput q).length := ⟨_, rfl⟩ + obtain ⟨m, hm⟩ : ∃ m, m = (targetModes q.1).length := ⟨_, rfl⟩ + obtain ⟨Z, hZ⟩ : ∃ Z, Z = n + m + Rv + 2 := ⟨_, rfl⟩ + rw [← hn, ← hm, ← hZ] + have hZ1 : 1 ≤ Z := by omega + have hpowZ {a b : ℕ} (hab : a ≤ b) : Z ^ a ≤ Z ^ b := Nat.pow_le_pow_right hZ1 hab + have hP1 : 1 ≤ (m + 2) ^ q.2.1.length := Nat.one_le_pow _ _ (by omega) + -- The node is no longer than the input: binary exponents are shorter than unary ones. + have hnode : (CoeffScheme.Terms.encNode PacketCtx.enc (encPair encNat encBool) q).length ≤ n := by + rw [hn] + obtain ⟨c, rows, β⟩ := q + have hβ : (encList encNat β).length ≤ (encList encUnary β).length := by + simp only [length_encList] + refine List.sum_le_sum fun k _ => ?_ + have : (encNat k).length ≤ k := by + change k.bits.length ≤ k + rw [Nat.size_eq_bits_len] + exact (Nat.size_le.mpr (Nat.lt_two_pow_self)).trans le_rfl + simp only [encUnary_apply, List.length_replicate] + omega + simp only [length_encPair] + omega + have hval := hK₁ q (Rv) (by simpa [packetTerms] using hRv) + simp only [packetTerms] at hval + rw [← hm] at hval + obtain ⟨hvt, hvs⟩ := hval + -- The polynomial parts. + have hpoly {c d e : ℕ} (hle : e ≤ n + 1) (hd : d ≤ D) : c * e ^ d ≤ c * Z ^ D := + Nat.mul_le_mul_left _ ((Nat.pow_le_pow_left (by omega) _).trans (hpowZ hd)) + have hZn : (CoeffScheme.Terms.encNode PacketCtx.enc (encPair encNat encBool) q).length + + m + Rv + 2 ≤ Z := by omega + have hvalT : (CoeffScheme.Terms.valueCertificate packetTerms_polyTime).time q ≤ + K₁ * (m + 2) ^ q.2.1.length * Z ^ D := + hvt.trans (Nat.mul_le_mul_left _ ((Nat.pow_le_pow_left hZn _).trans (hpowZ (by omega)))) + have hvalS : (CoeffScheme.Terms.valueCertificate packetTerms_polyTime).space q ≤ K₁ * Z ^ D := + hvs.trans (Nat.mul_le_mul_left _ ((Nat.pow_le_pow_left hZn _).trans (hpowZ (by omega)))) + -- The final multiplication reads the input and two reduced lists. + have hred1 := hRv _ (reduced_value packetTerms q.1 q.2.1 q.2.2) + have hred2 := hRv _ (reduced_sourceProduct q.1 q.2.2) + have hmulIn : (encPair encEntryInput (encPair (encList encNat) (encList encNat)) + (q, (packetTerms.value q.1 q.2.1 q.2.2, sourceProduct q.1 q.2.2))).length + 1 ≤ 8 * Z := by + have e1 := length_encPair (enc₁ := encEntryInput) + (enc₂ := encPair (encList encNat) (encList encNat)) + (q, (packetTerms.value q.1 q.2.1 q.2.2, sourceProduct q.1 q.2.2)) + have e2 := length_encPair (enc₁ := encList encNat) (enc₂ := encList encNat) + (packetTerms.value q.1 q.2.1 q.2.2, sourceProduct q.1 q.2.2) + simp only [packetTerms] at hred1 e1 e2 ⊢ + rw [e1, e2, ← hn] + omega + have hmulT : (NativeCertificate.ofPolyTime mulFinal_polyTime).time + (q, (packetTerms.value q.1 q.2.1 q.2.2, sourceProduct q.1 q.2.2)) ≤ cm * 8 ^ dm * Z ^ D := + (hmt _).trans (by + calc cm * (_ + 1) ^ dm ≤ cm * (8 * Z) ^ dm := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_left hmulIn _) + _ = cm * 8 ^ dm * Z ^ dm := by rw [Nat.mul_pow]; ring + _ ≤ cm * 8 ^ dm * Z ^ D := Nat.mul_le_mul_left _ (hpowZ (by omega))) + have hmulS : (NativeCertificate.ofPolyTime mulFinal_polyTime).space + (q, (packetTerms.value q.1 q.2.1 q.2.2, sourceProduct q.1 q.2.2)) ≤ cm' * 8 ^ dm' * Z ^ D := + (hms _).trans (by + calc cm' * (_ + 1) ^ dm' ≤ cm' * (8 * Z) ^ dm' := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_left hmulIn _) + _ = cm' * 8 ^ dm' * Z ^ dm' := by rw [Nat.mul_pow]; ring + _ ≤ cm' * 8 ^ dm' * Z ^ D := Nat.mul_le_mul_left _ (hpowZ (by omega))) + have hit' := (hit q).trans (hpoly (c := ci) (by omega) (show di ≤ D by omega)) + have his' := (his q).trans (hpoly (c := ci') (by omega) (show di' ≤ D by omega)) + have hnt' := (hnt q).trans (hpoly (c := cn) (by omega) (show dn ≤ D by omega)) + have hns' := (hns q).trans (hpoly (c := cn') (by omega) (show dn' ≤ D by omega)) + have hpt' := (hpt q).trans (hpoly (c := cp) (by omega) (show dp ≤ D by omega)) + have hps' := (hps q).trans (hpoly (c := cp') (by omega) (show dp' ≤ D by omega)) + have hWK : Z ^ D ≤ Z ^ (G + D) := hpowZ (by omega) + obtain ⟨P, hPdef⟩ : ∃ P, P = (m + 2) ^ q.2.1.length := ⟨_, rfl⟩ + rw [← hPdef] at hP1 hvalT ⊢ + have htime : entryCertificate.time q = + (NativeCertificate.ofPolyTime ComputableInPolyTime.id').time q + + ((NativeCertificate.ofPolyTime node_polyTime).time q + + (CoeffScheme.Terms.valueCertificate packetTerms_polyTime).time q + + (NativeCertificate.ofPolyTime sourceProduct_polyTime).time q) + + (NativeCertificate.ofPolyTime mulFinal_polyTime).time + (q, (packetTerms.value q.1 q.2.1 q.2.2, sourceProduct q.1 q.2.2)) := rfl + have hspace : entryCertificate.space q = + (NativeCertificate.ofPolyTime ComputableInPolyTime.id').space q + + ((NativeCertificate.ofPolyTime node_polyTime).space q + + (CoeffScheme.Terms.valueCertificate packetTerms_polyTime).space q + + (NativeCertificate.ofPolyTime sourceProduct_polyTime).space q) + + (NativeCertificate.ofPolyTime mulFinal_polyTime).space + (q, (packetTerms.value q.1 q.2.1 q.2.2, sourceProduct q.1 q.2.2)) := rfl + constructor + · rw [htime] + have e : G * (P * Z ^ D) = P * (K₁ * Z ^ D) + P * ((G - K₁) * Z ^ D) := by + rw [← Nat.mul_add, ← Nat.add_mul, Nat.add_sub_cancel' (by omega)]; ring + have h1 : cm * 8 ^ dm * Z ^ D + ci * Z ^ D + cn * Z ^ D + cp * Z ^ D ≤ + P * ((G - K₁) * Z ^ D) := by + have : cm * 8 ^ dm * Z ^ D + ci * Z ^ D + cn * Z ^ D + cp * Z ^ D = + (cm * 8 ^ dm + ci + cn + cp) * Z ^ D := by ring + rw [this] + exact (Nat.mul_le_mul_right _ (by omega)).trans (Nat.le_mul_of_pos_left _ hP1) + calc _ ≤ K₁ * P * Z ^ D + (cm * 8 ^ dm * Z ^ D + ci * Z ^ D + cn * Z ^ D + cp * Z ^ D) := by + omega + _ ≤ G * (P * Z ^ D) := by + have : K₁ * P * Z ^ D = P * (K₁ * Z ^ D) := by ring + omega + _ ≤ (G + D) * P * Z ^ (G + D) := by + rw [show (G + D) * P * Z ^ (G + D) = (G + D) * (P * Z ^ (G + D)) by ring] + exact Nat.mul_le_mul (by omega) (Nat.mul_le_mul_left _ hWK) + · rw [hspace] + calc _ ≤ K₁ * Z ^ D + cm' * 8 ^ dm' * Z ^ D + ci' * Z ^ D + cn' * Z ^ D + cp' * Z ^ D := by + omega + _ = (K₁ + cm' * 8 ^ dm' + ci' + cn' + cp') * Z ^ D := by ring + _ ≤ (G + D) * Z ^ (G + D) := Nat.mul_le_mul (by omega) hWK + +end Certificate + +end NativePacket + +end Cslib.Circuits.Frontier diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketTerms.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketTerms.lean new file mode 100644 index 0000000000..e0d388e5fb --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketTerms.lean @@ -0,0 +1,62 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketTerms + +/-! +# Tests for the native packet evaluator + +The tiny packet of the packet-evaluator tests: `2 ^ 1` slots over +`A_3 = (ℤ / 8)[U] / (U ^ 2 + U + 1)`, the grid point `t = 1` of valuation `i = 0`, two exponents +per tile, one row port in slot `0` and one column port in slot `1` set to one, `ζ = U`. The +native entry, through the stack recursion, agrees with `packetEntryList`, and the correctness +theorem and the certificate apply. +-/ + +@[expose] public section + +namespace ComplexityTests.TensorNetworkPacketTerms + +open Cslib Cslib.Circuits.Frontier Cslib.Circuits.Frontier.NativePacket + Cslib.Circuits.Frontier.TensorNetwork + +/-- The context of the tiny packet. -/ +def ctx : PacketCtx := + ⟨3, [1, 1], 1, 2, 0, 2, 1, 2, true, false, true, [(1, true)], [0, 1]⟩ + +-- Zero exponents: the entry is the local form, the kernel value `W_1(T) = 5 + 2 U`. +example : packetEntryNative ctx [(0, true)] [0, 0, 0, 0] = [5, 2] := by decide + +-- It agrees with `packetEntryList`, here also as lists. +example : packetEntryNative ctx [(0, true)] [0, 0, 0, 0] = + packetEntryList 3 [1, 1] 1 2 0 1 2 true false true (fun _ => 0) [(0, true)] [(1, true)] + [0, 1] := by + decide + +-- Exponent `1` at the target mode of dense index `1`: the mode coefficient. +example : packetEntryNative ctx [(0, true)] [0, 1, 0, 0] = + modeCoeffList 3 [1, 1] 1 2 0 1 true 0 0 1 [0, 1] := by + decide + +/-- The correctness theorem applies to the tiny packet. -/ +theorem val_ctx (β : List ℕ) : + GaloisRingArith.val 3 [1, 1] (packetEntryNative ctx [(0, true)] β) = + packetEntry (p := 2) (scalarHom 3 [1, 1]) (GaloisRingArith.val 3 [1, 1] [0, 1]) 1 0 1 true + false true (fun Z => β.getD (Z.1 * 2 + Z.2) 0) [(0, true)] [(1, true)] := + val_packetEntryNative_eq ctx (by decide) (by decide) (by decide) + (by simp [ctx, mem_cauchyGrid]) (by norm_num [ctx]) (by norm_num [ctx]) (by simp) + (by simp [ctx]) β + +-- One native machine computes the native entry. +noncomputable example : Cslib.GenericRAM.NativeCertificate + (fun q : PacketCtx × (List (ℕ × Bool) × List ℕ) => packetEntryNative q.1 q.2.1 q.2.2) + encEntryInput (Turing.MultiTapeTM.encList Turing.MultiTapeTM.encNat) := + entryCertificate + +end ComplexityTests.TensorNetworkPacketTerms From e03f0ce8c06c2bc51ec0ec1a69623d3111aac882 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 01:12:17 -0400 Subject: [PATCH 055/129] feat(circuit/frontier): descriptors of packet vertices and their native evaluator PacketDesc is a packet context, the ports (isRow, slot) in port order and the dense exponents. packetVertexEntry reads the row and column ports from the bits; vertexCertificate is its native machine, the evaluator of the described-network direct summation at coefficient lists, with val_packetVertexEntry and the bounds vertexCertificate_le. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/PacketTerms.lean | 209 +++++++++++++++++- .../Frontier/TensorNetworkPacketTerms.lean | 20 ++ 2 files changed, 228 insertions(+), 1 deletion(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketTerms.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketTerms.lean index f7da6829e8..0905c77e84 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketTerms.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketTerms.lean @@ -52,6 +52,12 @@ bounds: time `exp(O(L log s))` and polynomial space. target modes, `L` row ports and `Z` polynomial in the input and the reduced coefficient lists (`NativePacket.entryCertificate_le`). The exponents are read in unary, since the source's are below the precision. +* **Descriptors** (`NativePacket.PacketDesc`): a packet context, the ports `(isRow, slot)` of the + vertex in its port order and the dense exponents, "its packet data, the tuple `β`, the + interpolation value, and the creation precision" (p. 63). `NativePacket.vertexCertificate` + computes the entry at the bits on the ports, the evaluator of + `Cslib.Circuits.Frontier.DescribedNetwork` direct summation at coefficient lists, with the same + bounds (`NativePacket.vertexCertificate_le`). ## Main definitions @@ -59,6 +65,10 @@ bounds: time `exp(O(L log s))` and polynomial space. * `Cslib.Circuits.Frontier.NativePacket.packetTerms`: coefficient extraction for a packet. * `Cslib.Circuits.Frontier.NativePacket.packetEntryNative`: the native packet entry. * `Cslib.Circuits.Frontier.NativePacket.entryCertificate`: its native machine. +* `Cslib.Circuits.Frontier.NativePacket.PacketDesc`, + `Cslib.Circuits.Frontier.NativePacket.packetVertexEntry`, + `Cslib.Circuits.Frontier.NativePacket.vertexCertificate`: descriptors of packet vertices, their + entries at the bits on their ports, and the native machine for them. ## Main results @@ -69,7 +79,10 @@ bounds: time `exp(O(L log s))` and polynomial space. * `Cslib.Circuits.Frontier.NativePacket.val_packetEntryNative_eq`, `Cslib.Circuits.Frontier.NativePacket.map_val_packetEntryNative`: the native entry is the packet entry, in `A_p` and in every ring receiving it. -* `Cslib.Circuits.Frontier.NativePacket.entryCertificate_le`: its time and space. +* `Cslib.Circuits.Frontier.NativePacket.entryCertificate_le`, + `Cslib.Circuits.Frontier.NativePacket.vertexCertificate_le`: their time and space. +* `Cslib.Circuits.Frontier.NativePacket.val_packetVertexEntry`: the entry of a packet vertex is + the packet entry at the ports read from its bits. ## References @@ -1178,6 +1191,200 @@ theorem entryCertificate_le : end Certificate +/-! ### Descriptors of packet vertices -/ + +/-- **The descriptor of a packet vertex**: a packet context, whose column ports are read from +the bits instead, the ports of the vertex in its port order, each `(isRow, slot)`, and the dense +exponents of the modes of the packet. -/ +abbrev PacketDesc := PacketCtx × (List (Bool × ℕ) × List ℕ) + +/-- The ports of one side, with the bits on them: the slots of the ports with `isRow = side`, +paired with their bits, in port order. -/ +def sidePorts (ports : List (Bool × ℕ)) (bs : List Bool) (side : Bool) : List (ℕ × Bool) := + ((List.range ports.length).filter fun j => (ports.getD j (false, 0)).1 == side).map fun j => + ((ports.getD j (false, 0)).2, bs.getD j false) + +/-- **The entry of a packet vertex at the bits on its ports**: the native packet entry, with the +row ports and the column ports read from the bits. -/ +def packetVertexEntry (d : PacketDesc) (bs : List Bool) : List ℕ := + packetEntryNative { d.1 with cols := sidePorts d.2.1 bs false } (sidePorts d.2.1 bs true) d.2.2 + +/-- The encoding of a packet descriptor: the context, the ports in binary, and the exponents in +unary. -/ +abbrev encPacketDesc : PacketDesc ↪ List Bool := + encPair PacketCtx.enc (encPair (encList (encPair encBool encNat)) (encList encUnary)) + +/-- The ports of one side are polynomial-time. -/ +theorem sidePorts_polyTime {α : Type} {encIn : α ↪ List Bool} {ports : α → List (Bool × ℕ)} + {bs : α → List Bool} {side : α → Bool} + (hports : ComputableInPolyTime ports encIn (encList (encPair encBool encNat))) + (hbs : ComputableInPolyTime bs encIn encBits) + (hside : ComputableInPolyTime side encIn encBool) : + ComputableInPolyTime (fun x => sidePorts (ports x) (bs x) (side x)) encIn + (encList (encPair encNat encBool)) := by + have h1 := ComputableInPolyTime.fst encIn encUnary + have h2 := ComputableInPolyTime.snd encIn encUnary + have hport := (ComputableInPolyTime.listGetD (encA := encPair encBool encNat) + (encL := encList (encPair encBool encNat)) (false, 0)).comp ((hports.comp h1).prod h2) + have htest := ComputableInPolyTime.beq ((ComputableInPolyTime.fst _ _).comp hport) + (hside.comp h1) + have hfilt := ComputableInPolyTime.filterWithInput + (test := fun x j => ((ports x).getD j (false, 0)).1 == side x) + (ComputableInPolyTime.range.comp ((ComputableInPolyTime.length_list _).comp hports)) htest + have hbit := (ComputableInPolyTime.listGetD (encA := encBool) (encL := encBits) false).comp + ((hbs.comp h1).prod h2) + exact ComputableInPolyTime.mapWithInput + (f := fun x j => (((ports x).getD j (false, 0)).2, (bs x).getD j false)) hfilt + (((ComputableInPolyTime.snd _ _).comp hport).prod hbit) + +/-- The input of the native packet evaluator from a descriptor and the bits on its ports. -/ +theorem entryInput_polyTime : + ComputableInPolyTime (fun q : PacketDesc × List Bool => + (({ q.1.1 with cols := sidePorts q.1.2.1 q.2 false } : PacketCtx), + (sidePorts q.1.2.1 q.2 true, q.1.2.2))) + (encPair encPacketDesc encBits) encEntryInput := by + have hd := ComputableInPolyTime.fst encPacketDesc encBits + have hbs := ComputableInPolyTime.snd encPacketDesc encBits + have hc := (ComputableInPolyTime.fst _ _).comp hd + have hports := (ComputableInPolyTime.fst (encList (encPair encBool encNat)) + (encList encUnary)).comp ((ComputableInPolyTime.snd _ _).comp hd) + have hβ := (ComputableInPolyTime.snd (encList (encPair encBool encNat)) + (encList encUnary)).comp ((ComputableInPolyTime.snd _ _).comp hd) + have hcols := sidePorts_polyTime hports hbs (ComputableInPolyTime.const false) + have hrows := sidePorts_polyTime hports hbs (ComputableInPolyTime.const true) + have h01 := (PacketCtx.p_polyTime.comp hc).prod (PacketCtx.fl_polyTime.comp hc) + have h23 := (PacketCtx.a_polyTime.comp hc).prod (PacketCtx.L_polyTime.comp hc) + have h45 := (PacketCtx.i_polyTime.comp hc).prod (PacketCtx.tiles_polyTime.comp hc) + have hsyms := (PacketCtx.e₁_polyTime.comp hc).prod ((PacketCtx.e₂_polyTime.comp hc).prod + (PacketCtx.differ_polyTime.comp hc)) + have hlast := hcols.prod (PacketCtx.ζ_polyTime.comp hc) + have htuple := h01.prod (h23.prod (h45.prod ((PacketCtx.t_polyTime.comp hc).prod + ((PacketCtx.nb_polyTime.comp hc).prod (hsyms.prod hlast))))) + have hctx : ComputableInPolyTime (fun q : PacketDesc × List Bool => + ({ q.1.1 with cols := sidePorts q.1.2.1 q.2 false } : PacketCtx)) + (encPair encPacketDesc encBits) PacketCtx.enc := + htuple.of_enc_eq fun _ => rfl + exact hctx.prod (hrows.prod hβ) + +/-- **The native packet evaluator on descriptors**: one native machine for the entry of a packet +vertex at the bits on its ports, the `ev` of the direct summation of +`Cslib.Circuits.Frontier.DescribedNetwork` at coefficient lists (`SumOps.galois`). -/ +noncomputable def vertexCertificate : + NativeCertificate (fun q : PacketDesc × List Bool => packetVertexEntry q.1 q.2) + (encPair encPacketDesc encBits) (encList encNat) := + (entryCertificate.comp (NativeCertificate.ofPolyTime entryInput_polyTime)).congr fun _ => rfl + +/-- **The resources of the native packet evaluator on descriptors.** There is `K` such that on a +descriptor and bits of total length `n`, with `m` target modes and `L` row ports, if the reduced +coefficient lists of its precision are at most `Rv` bits long, then with `Z = n + m + Rv + 2` the +time is at most `K (m + 2) ^ L Z ^ K` and the space at most `K Z ^ K`. -/ +theorem vertexCertificate_le : + ∃ K : ℕ, ∀ (q : PacketDesc × List Bool) (Rv : ℕ), + (∀ y, SumOps.galois.Reduced (q.1.1.p, q.1.1.fl) y → (encList encNat y).length ≤ Rv) → + vertexCertificate.time q ≤ K * ((targetModes q.1.1).length + 2) ^ + (sidePorts q.1.2.1 q.2 true).length * + ((encPair encPacketDesc encBits q).length + (targetModes q.1.1).length + Rv + 2) ^ K ∧ + vertexCertificate.space q ≤ K * ((encPair encPacketDesc encBits q).length + + (targetModes q.1.1).length + Rv + 2) ^ K := by + obtain ⟨K₀, hK₀⟩ := entryCertificate_le + have hI := NativeCertificate.ofPolyTime_isPolynomial entryInput_polyTime + obtain ⟨ci, di, hit⟩ := hI.1.exists_le + obtain ⟨ci', di', his⟩ := hI.2.exists_le + obtain ⟨cl, dl, hlen⟩ := (ComputableInPolyTime.polyBounded_length entryInput_polyTime).exists_le + obtain ⟨D, hD⟩ : ∃ D, D = (dl + 1) * K₀ + di + di' := ⟨_, rfl⟩ + obtain ⟨G, hG⟩ : ∃ G, G = K₀ * (cl + 1) ^ K₀ + ci + ci' := ⟨_, rfl⟩ + refine ⟨G + D, fun q Rv hRv => ?_⟩ + obtain ⟨n, hn⟩ : ∃ n, n = (encPair encPacketDesc encBits q).length := ⟨_, rfl⟩ + obtain ⟨m, hm⟩ : ∃ m, m = (targetModes q.1.1).length := ⟨_, rfl⟩ + obtain ⟨Z, hZ⟩ : ∃ Z, Z = n + m + Rv + 2 := ⟨_, rfl⟩ + rw [← hn, ← hm, ← hZ] + have hZ1 : 1 ≤ Z := by omega + have hpowZ {a b : ℕ} (hab : a ≤ b) : Z ^ a ≤ Z ^ b := Nat.pow_le_pow_right hZ1 hab + obtain ⟨P, hPdef⟩ : ∃ P, P = (m + 2) ^ (sidePorts q.1.2.1 q.2 true).length := ⟨_, rfl⟩ + rw [← hPdef] + have hP1 : 1 ≤ P := hPdef ▸ Nat.one_le_pow _ _ (by omega) + have hentry := hK₀ ((({ q.1.1 with cols := sidePorts q.1.2.1 q.2 false } : PacketCtx), + (sidePorts q.1.2.1 q.2 true, q.1.2.2))) Rv hRv + -- The input of the entry is polynomially long. + have hin := hlen q + rw [← hn] at hin + obtain ⟨n', hn'⟩ : ∃ n', n' = (encEntryInput ((({ q.1.1 with + cols := sidePorts q.1.2.1 q.2 false } : PacketCtx), (sidePorts q.1.2.1 q.2 true, + q.1.2.2)))).length := ⟨_, rfl⟩ + rw [← hn'] at hin hentry + have hZ' : n' + m + Rv + 2 ≤ (cl + 1) * Z ^ (dl + 1) := by + have h1 : cl * (n + 1) ^ dl ≤ cl * Z ^ (dl + 1) := + Nat.mul_le_mul_left _ ((Nat.pow_le_pow_left (by omega) _).trans (hpowZ (by omega))) + have h2 : Z ≤ Z ^ (dl + 1) := by + calc Z = Z ^ 1 := (pow_one Z).symm + _ ≤ Z ^ (dl + 1) := hpowZ (by omega) + have e : (cl + 1) * Z ^ (dl + 1) = cl * Z ^ (dl + 1) + Z ^ (dl + 1) := by ring + omega + have hZK : (n' + m + Rv + 2) ^ K₀ ≤ (cl + 1) ^ K₀ * Z ^ D := by + calc (n' + m + Rv + 2) ^ K₀ ≤ ((cl + 1) * Z ^ (dl + 1)) ^ K₀ := Nat.pow_le_pow_left hZ' _ + _ = (cl + 1) ^ K₀ * Z ^ ((dl + 1) * K₀) := by rw [Nat.mul_pow, ← pow_mul] + _ ≤ (cl + 1) ^ K₀ * Z ^ D := Nat.mul_le_mul_left _ (hpowZ (by omega)) + have het : entryCertificate.time ((({ q.1.1 with cols := sidePorts q.1.2.1 q.2 false } : + PacketCtx), (sidePorts q.1.2.1 q.2 true, q.1.2.2))) ≤ + K₀ * P * (n' + m + Rv + 2) ^ K₀ := by + rw [hPdef, hm]; exact hentry.1 + have hes : entryCertificate.space ((({ q.1.1 with cols := sidePorts q.1.2.1 q.2 false } : + PacketCtx), (sidePorts q.1.2.1 q.2 true, q.1.2.2))) ≤ K₀ * (n' + m + Rv + 2) ^ K₀ := by + rw [hm]; exact hentry.2 + have hit' : (NativeCertificate.ofPolyTime entryInput_polyTime).time q ≤ ci * Z ^ D := + (hit q).trans (Nat.mul_le_mul_left _ ((Nat.pow_le_pow_left (by omega) _).trans + (hpowZ (by omega)))) + have his' : (NativeCertificate.ofPolyTime entryInput_polyTime).space q ≤ ci' * Z ^ D := + (his q).trans (Nat.mul_le_mul_left _ ((Nat.pow_le_pow_left (by omega) _).trans + (hpowZ (by omega)))) + have hWK : Z ^ D ≤ Z ^ (G + D) := hpowZ (by omega) + have htime : vertexCertificate.time q = + (NativeCertificate.ofPolyTime entryInput_polyTime).time q + + entryCertificate.time ((({ q.1.1 with cols := sidePorts q.1.2.1 q.2 false } : PacketCtx), + (sidePorts q.1.2.1 q.2 true, q.1.2.2))) := rfl + have hspace : vertexCertificate.space q = + (NativeCertificate.ofPolyTime entryInput_polyTime).space q + + entryCertificate.space ((({ q.1.1 with cols := sidePorts q.1.2.1 q.2 false } : PacketCtx), + (sidePorts q.1.2.1 q.2 true, q.1.2.2))) := rfl + constructor + · rw [htime] + have h1 : K₀ * P * (n' + m + Rv + 2) ^ K₀ ≤ P * (K₀ * (cl + 1) ^ K₀ * Z ^ D) := by + calc K₀ * P * (n' + m + Rv + 2) ^ K₀ ≤ K₀ * P * ((cl + 1) ^ K₀ * Z ^ D) := + Nat.mul_le_mul_left _ hZK + _ = P * (K₀ * (cl + 1) ^ K₀ * Z ^ D) := by ring + have h2 : ci * Z ^ D ≤ P * (ci * Z ^ D) := Nat.le_mul_of_pos_left _ hP1 + have e : P * (K₀ * (cl + 1) ^ K₀ * Z ^ D) + P * (ci * Z ^ D) ≤ P * (G * Z ^ D) := by + rw [← Nat.mul_add, ← Nat.add_mul] + exact Nat.mul_le_mul_left _ (Nat.mul_le_mul_right _ (by omega)) + calc _ ≤ P * (G * Z ^ D) := by omega + _ ≤ (G + D) * P * Z ^ (G + D) := by + rw [show (G + D) * P * Z ^ (G + D) = P * ((G + D) * Z ^ (G + D)) by ring] + exact Nat.mul_le_mul_left _ (Nat.mul_le_mul (by omega) hWK) + · rw [hspace] + have h1 : K₀ * (n' + m + Rv + 2) ^ K₀ ≤ K₀ * (cl + 1) ^ K₀ * Z ^ D := by + rw [Nat.mul_assoc]; exact Nat.mul_le_mul_left _ hZK + calc _ ≤ (K₀ * (cl + 1) ^ K₀ + ci') * Z ^ D := by rw [Nat.add_mul]; omega + _ ≤ (G + D) * Z ^ (G + D) := Nat.mul_le_mul (by omega) hWK + +/-- **The entry of a packet vertex is the packet entry** at the row and column ports read from +its bits. -/ +theorem val_packetVertexEntry (d : PacketDesc) (bs : List Bool) (hL : d.1.L = 2 ^ d.1.a) + (hia : d.1.i < d.1.a) (htiles : d.1.tiles = 2 ^ (d.1.i + 1)) + (ht : d.1.t ∈ cauchyGrid (2 ^ d.1.a)) (hti : (2 : ℤ) ^ d.1.i ∣ d.1.t) + (hti' : ¬ (2 : ℤ) ^ (d.1.i + 1) ∣ d.1.t) (hports : ∀ x ∈ d.2.1, x.2 < 2 ^ d.1.a) : + val d.1.p d.1.fl (packetVertexEntry d bs) = + packetEntry (p := d.1.nb) (scalarHom d.1.p d.1.fl) (val d.1.p d.1.fl d.1.ζ) d.1.a d.1.i + d.1.t d.1.e₁ d.1.e₂ d.1.differ (fun Z => d.2.2.getD (Z.1 * d.1.nb + Z.2) 0) + (sidePorts d.2.1 bs true) (sidePorts d.2.1 bs false) := by + have hside (side : Bool) : ∀ r ∈ sidePorts d.2.1 bs side, r.1 < 2 ^ d.1.a := by + intro r hr + obtain ⟨j, hj, rfl⟩ := List.mem_map.mp hr + have hj' := List.mem_range.mp (List.mem_filter.mp hj).1 + exact hports _ (by rw [List.getD_eq_getElem _ _ hj']; exact List.getElem_mem _) + exact val_packetEntryNative_eq { d.1 with cols := sidePorts d.2.1 bs false } hL hia htiles ht + hti hti' (hside true) (hside false) d.2.2 + + end NativePacket end Cslib.Circuits.Frontier diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketTerms.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketTerms.lean index e0d388e5fb..4d43bf56eb 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketTerms.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketTerms.lean @@ -59,4 +59,24 @@ noncomputable example : Cslib.GenericRAM.NativeCertificate encEntryInput (Turing.MultiTapeTM.encList Turing.MultiTapeTM.encNat) := entryCertificate +/-- The descriptor of the tiny packet vertex: a row port in slot `0`, then a column port in slot +`1`, and zero exponents. Its column ports are read from the bits. -/ +def desc : PacketDesc := (ctx, ([(true, 0), (false, 1)], [0, 0, 0, 0])) + +-- At the bits `1, 1` on its ports, the entry is the kernel value `5 + 2 U`. +example : packetVertexEntry desc [true, true] = [5, 2] := by decide + +-- With the column bit `0`, the local form vanishes, and the row factor at bit `1` is `0`. +example : packetVertexEntry desc [true, false] = [0, 0] := by decide + +/-- The correctness theorem applies to the descriptor. -/ +theorem val_desc (bs : List Bool) : + GaloisRingArith.val 3 [1, 1] (packetVertexEntry desc bs) = + packetEntry (p := 2) (scalarHom 3 [1, 1]) (GaloisRingArith.val 3 [1, 1] [0, 1]) 1 0 1 true + false true (fun Z => [0, 0, 0, 0].getD (Z.1 * 2 + Z.2) 0) (sidePorts desc.2.1 bs true) + (sidePorts desc.2.1 bs false) := + val_packetVertexEntry desc bs (by decide) (by decide) (by decide) + (by simp [desc, ctx, mem_cauchyGrid]) (by norm_num [desc, ctx]) (by norm_num [desc, ctx]) + (by decide) + end ComplexityTests.TensorNetworkPacketTerms From 5736072f826686a53f5cc6ff93c24f2ce71bea2b Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 01:13:48 -0400 Subject: [PATCH 056/129] chore: regenerate Complexity.lean after merging main Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Complexity.lean b/Complexity.lean index 162a20efed..8f445fbc17 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -886,12 +886,10 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DirectCont public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates public import Complexity.Computability.Circuit.Frontier.TensorNetwork.KernelProduct -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEntry -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEvaluator public import Complexity.Computability.Circuit.Frontier.TensorNetwork.LocalDeformation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEntry +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEvaluator public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Schedule public import Complexity.Computability.Circuit.Frontier.TensorNetwork.TaperedRecovery public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness From 74a464ed1aeb8bf722b4ad4da3b449d7dbc79179 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 01:14:36 -0400 Subject: [PATCH 057/129] chore: regenerate Complexity.lean after merging the base Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Complexity.lean b/Complexity.lean index 87fbb6cc89..3c43ca3ff0 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -879,8 +879,8 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ActiveCall public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Binarize public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CauchyModes -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CoeffScheme public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildSize +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CoeffScheme public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DescribedContraction From 4f2952814b6adc84a7f4719b968e5145537ebcb0 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 01:17:45 -0400 Subject: [PATCH 058/129] feat(circuit/frontier): the cursor over the children of an active call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `cursorNext` walks the indices `(t, k, β, g)` of the children of an active call: the next group, else the next exponent tuple (`budgetNext`), else the next point, else the next node. `cursorList` lists them, and the walk from `cursorStart` follows it (`cursorList_walk`). One step is polynomial-time (`computableInPolyTime_cursorStep`), so the children have polynomial delay. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Frontier/TensorNetwork/ChildCursor.lean | 412 ++++++++++++++++++ .../Frontier/TensorNetworkChildCursor.lean | 46 ++ 3 files changed, 459 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildCursor.lean create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkChildCursor.lean diff --git a/Complexity.lean b/Complexity.lean index 6f8dd26fcc..1f7f032585 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -879,6 +879,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ActiveCall public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Binarize public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CauchyModes +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildCursor public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildSize public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildCursor.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildCursor.lean new file mode 100644 index 0000000000..3a210ee817 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildCursor.lean @@ -0,0 +1,412 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ +module +-- upstream: candidate + +public import Complexity.Foundations.Combinatorics.BudgetTuples +public import Complexity.Computability.PolyTime.BudgetTuples +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Plain +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeNatFrontends +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeOptionPrimitives + +/-! +# The cursor over the children of an active call + +An active call of the Boolean tensor contraction recursion of [OpenAI2026ETH] (§D.4.5–§D.4.7, +pp. 62–64) evaluates, for each node `t` of positive multiplicity, each interpolation point `k`, +each exponent tuple `β` of weight below the node's precision and each group `g < r`, one child +network. This file walks these indices with a cursor: the state is `(t, k, β, g)`, and +`cursorNext` advances the group, then the exponents (`Cslib.BudgetTuples.budgetNext`), then the +point, then the node, resetting the inner indices to their first values. The nodes are numbered +`0, …, T - 1`; node `t` has the weights `ws t` and the budget `ps t`. + +`cursorList` lists the indices in this order, nested from the node outwards. Under positive counts, +weights and budgets, the walk from `cursorStart` follows it: the list starts at `cursorStart` +(`head?_cursorList`), each entry is followed by its successor (`isChain_cursorList`) and the last +one has none (`cursorNext_getLast?`). `cursorList_walk` states this by index, in the form a loop +over counted children consumes. One step reads only the current node's weights and budget and the +next node's number of modes (`cursorNext_eq_cursorStep`), and it is polynomial-time +(`computableInPolyTime_cursorStep`), so the children are enumerated with polynomial delay. + +## Main definitions + +* `Cslib.Circuits.Frontier.ChildCursor.cursorList`: the indices `(t, k, β, g)` in order. +* `Cslib.Circuits.Frontier.ChildCursor.cursorStart`, + `Cslib.Circuits.Frontier.ChildCursor.cursorNext`: the cursor. +* `Cslib.Circuits.Frontier.ChildCursor.cursorStep`, + `Cslib.Circuits.Frontier.ChildCursor.encStepInput`: one step from the data of the nodes, and its + input encoding. + +## Main results + +* `Cslib.Circuits.Frontier.ChildCursor.head?_cursorList`, + `Cslib.Circuits.Frontier.ChildCursor.isChain_cursorList`, + `Cslib.Circuits.Frontier.ChildCursor.cursorNext_getLast?`, + `Cslib.Circuits.Frontier.ChildCursor.cursorList_walk`: the cursor walks the list. +* `Cslib.Circuits.Frontier.ChildCursor.isChain_flatMap`: a chain of nonempty chained blocks. +* `Cslib.Circuits.Frontier.ChildCursor.computableInPolyTime_cursorStep`: polynomial delay. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.5–§D.4.7, pp. 62–64. +-/ + +public section + +namespace Cslib.Circuits.Frontier.ChildCursor + +open Cslib.BudgetTuples + +/-! ### Chains of blocks -/ + +/-- **A chain of nonempty blocks.** The concatenation of nonempty chains is a chain when the last +entry of each block is related to the first entry of the next. -/ +theorem isChain_flatMap {α β : Type*} {R : β → β → Prop} (f : α → List β) (l : List α) + (hne : ∀ a ∈ l, f a ≠ []) (hblock : ∀ a ∈ l, List.IsChain R (f a)) + (hjoin : List.IsChain (fun a a' => ∀ x ∈ (f a).getLast?, ∀ y ∈ (f a').head?, R x y) l) : + List.IsChain R (l.flatMap f) := by + induction l with + | nil => simp + | cons a l ih => + rw [List.flatMap_cons, List.isChain_append] + refine ⟨hblock a List.mem_cons_self, ih (fun b hb => hne b (List.mem_cons_of_mem _ hb)) + (fun b hb => hblock b (List.mem_cons_of_mem _ hb)) hjoin.tail, ?_⟩ + intro x hx y hy + rcases l with _ | ⟨b, l⟩ + · simp at hy + · rw [List.flatMap_cons, List.head?_append] at hy + obtain ⟨y', hy'⟩ := Option.ne_none_iff_exists'.mp + (fun h => hne b (List.mem_cons_of_mem _ List.mem_cons_self) (List.head?_eq_none_iff.mp h)) + rw [hy', Option.some_or] at hy + exact (List.isChain_cons_cons.mp hjoin).1 x hx y (hy' ▸ hy) + +/-- The last entry of a concatenation of blocks whose last block is nonempty. -/ +theorem getLast?_flatMap_concat {α β : Type*} (f : α → List β) (l : List α) (a : α) + (ha : f a ≠ []) : ((l ++ [a]).flatMap f).getLast? = (f a).getLast? := by + rw [List.flatMap_append, List.flatMap_singleton, List.getLast?_append] + cases h : (f a).getLast? with + | none => exact absurd (List.getLast?_eq_none_iff.mp h) ha + | some x => rfl + +private theorem isChain_range_of {R : ℕ → ℕ → Prop} {n : ℕ} (h : ∀ m, m + 1 < n → R m (m + 1)) : + List.IsChain R (List.range n) := by + rcases n with _ | n + · simp + · exact (List.isChain_range_succ R n).mpr fun m hm => h m (by omega) + +private theorem head?_flatMap_range {β : Type*} (f : ℕ → List β) {n : ℕ} (hn : 0 < n) {x : β} + (hx : (f 0).head? = some x) : ((List.range n).flatMap f).head? = some x := by + obtain ⟨n, rfl⟩ := Nat.exists_eq_succ_of_ne_zero hn.ne' + rw [List.range_succ_eq_map, List.flatMap_cons, List.head?_append, hx, Option.some_or] + +private theorem getLast?_flatMap_range {β : Type*} (f : ℕ → List β) {n : ℕ} (hn : 0 < n) + (hne : f (n - 1) ≠ []) : ((List.range n).flatMap f).getLast? = (f (n - 1)).getLast? := by + obtain ⟨n, rfl⟩ := Nat.exists_eq_succ_of_ne_zero hn.ne' + rw [List.range_succ, getLast?_flatMap_concat f _ _ (by simpa using hne)] + rfl + +/-! ### The cursor -/ + +/-- A state of the cursor: the node, the point, the exponents and the group. -/ +abbrev State : Type := ℕ × ℕ × List ℕ × ℕ + +variable (ws : ℕ → List ℕ) (ps : ℕ → ℕ) (T n r : ℕ) + +/-- The children of one exponent tuple: one per group. -/ +@[expose] def groupBlock (t k : ℕ) (β : List ℕ) : List State := + (List.range r).map fun g => (t, k, β, g) + +/-- The children of one point: the exponent tuples of weight below `ps t`, in lexicographic +order. -/ +@[expose] def pointBlock (t k : ℕ) : List State := + (budgetTuples (ws t) (ps t)).flatMap (groupBlock r t k) + +/-- The children of one node: its `n` points. -/ +@[expose] def nodeBlock (t : ℕ) : List State := + (List.range n).flatMap (pointBlock ws ps r t) + +/-- **The children of an active call**, as indices `(t, k, β, g)`: nested from the node outwards, +for the nodes `t < T`, the points `k < n`, the exponent tuples of weight below `ps t` and the +groups `g < r`. -/ +@[expose] def cursorList : List State := + (List.range T).flatMap (nodeBlock ws ps n r) + +/-- The first index. -/ +@[expose] def cursorStart : State := (0, 0, List.replicate (ws 0).length 0, 0) + +/-- **The successor of an index**: the next group, else the next exponent tuple, else the next +point, else the next node, with the inner indices reset to their first values. -/ +@[expose] def cursorNext (s : State) : Option State := + if s.2.2.2 + 1 < r then some (s.1, s.2.1, s.2.2.1, s.2.2.2 + 1) + else + match budgetNext (ws s.1) (ps s.1) s.2.2.1 with + | some β => some (s.1, s.2.1, β, 0) + | none => + if s.2.1 + 1 < n then some (s.1, s.2.1 + 1, List.replicate (ws s.1).length 0, 0) + else if s.1 + 1 < T then some (s.1 + 1, 0, List.replicate (ws (s.1 + 1)).length 0, 0) + else none + +variable {ws ps T n r} + +section Walk + +variable (hr : 0 < r) (hn : 0 < n) (hps : ∀ t, 0 < ps t) (hws : ∀ t, ∀ w ∈ ws t, 0 < w) +include hr + +theorem head?_groupBlock (t k : ℕ) (β : List ℕ) : + (groupBlock r t k β).head? = some (t, k, β, 0) := by + obtain ⟨r, rfl⟩ := Nat.exists_eq_succ_of_ne_zero hr.ne' + simp [groupBlock, List.range_succ_eq_map] + +theorem getLast?_groupBlock (t k : ℕ) (β : List ℕ) : + (groupBlock r t k β).getLast? = some (t, k, β, r - 1) := by + obtain ⟨r, rfl⟩ := Nat.exists_eq_succ_of_ne_zero hr.ne' + simp [groupBlock, List.range_succ, List.getLast?_map] + +theorem groupBlock_ne_nil (t k : ℕ) (β : List ℕ) : groupBlock r t k β ≠ [] := by + rw [Ne, ← List.head?_eq_none_iff, head?_groupBlock hr] + simp + +include hps hws + +theorem head?_pointBlock (t k : ℕ) : + (pointBlock ws ps r t k).head? = some (t, k, List.replicate (ws t).length 0, 0) := by + rw [pointBlock, List.head?_flatMap] + rw [show budgetTuples (ws t) (ps t) = + List.replicate (ws t).length 0 :: (budgetTuples (ws t) (ps t)).tail from + (List.eq_cons_of_mem_head? (head?_budgetTuples (hws t) (hps t))), List.findSome?_cons, + head?_groupBlock hr] + +theorem getLast?_pointBlock (t k : ℕ) : ∃ β, (pointBlock ws ps r t k).getLast? = + some (t, k, β, r - 1) ∧ budgetNext (ws t) (ps t) β = none := by + obtain ⟨β, hβ⟩ : ∃ β, (budgetTuples (ws t) (ps t)).getLast? = some β := by + refine Option.ne_none_iff_exists'.mp fun h => ?_ + have := head?_budgetTuples (hws t) (hps t) + rw [List.getLast?_eq_none_iff] at h + simp [h] at this + refine ⟨β, ?_, budgetNext_getLast? (hws t) β hβ⟩ + obtain ⟨ys, hys⟩ := List.getLast?_eq_some_iff.mp hβ + rw [pointBlock, hys, getLast?_flatMap_concat _ _ _ (groupBlock_ne_nil hr t k β), + getLast?_groupBlock hr] + +theorem pointBlock_ne_nil (t k : ℕ) : pointBlock ws ps r t k ≠ [] := by + rw [Ne, ← List.head?_eq_none_iff, head?_pointBlock hr hps hws] + simp + +omit hps in +/-- Within one point, the cursor walks the exponent tuples and the groups. -/ +theorem isChain_pointBlock (t k : ℕ) : + List.IsChain (fun x y => cursorNext ws ps T n r x = some y) (pointBlock ws ps r t k) := by + refine isChain_flatMap _ _ (fun β _ => groupBlock_ne_nil hr t k β) (fun β _ => ?_) ?_ + · rw [groupBlock, List.isChain_map] + refine isChain_range_of fun g hg => ?_ + simp [cursorNext, hg] + · refine (isChain_budgetTuples (hws t) (ps t)).imp fun β β' hββ' => ?_ + intro x hx y hy + rw [getLast?_groupBlock hr] at hx + rw [head?_groupBlock hr] at hy + simp only [Option.mem_def, Option.some.injEq] at hx hy + subst hx hy + have : ¬ r - 1 + 1 < r := by omega + simp [cursorNext, this, hββ'] + +include hn + +theorem head?_nodeBlock (t : ℕ) : + (nodeBlock ws ps n r t).head? = some (t, 0, List.replicate (ws t).length 0, 0) := + head?_flatMap_range _ hn (head?_pointBlock hr hps hws t 0) + +theorem nodeBlock_ne_nil (t : ℕ) : nodeBlock ws ps n r t ≠ [] := by + rw [Ne, ← List.head?_eq_none_iff, head?_nodeBlock hr hn hps hws] + simp + +theorem getLast?_nodeBlock (t : ℕ) : ∃ β, (nodeBlock ws ps n r t).getLast? = + some (t, n - 1, β, r - 1) ∧ budgetNext (ws t) (ps t) β = none := by + obtain ⟨β, hβ, hnone⟩ := getLast?_pointBlock hr hps hws t (n - 1) + exact ⟨β, by rw [nodeBlock, getLast?_flatMap_range _ hn (pointBlock_ne_nil hr hps hws t _), hβ], + hnone⟩ + +omit hn in +/-- Within one node, the cursor walks the points, the exponent tuples and the groups. -/ +theorem isChain_nodeBlock (t : ℕ) : + List.IsChain (fun x y => cursorNext ws ps T n r x = some y) (nodeBlock ws ps n r t) := by + refine isChain_flatMap _ _ (fun k _ => pointBlock_ne_nil hr hps hws t k) + (fun k _ => isChain_pointBlock hr hws t k) (isChain_range_of fun k hk => ?_) + intro x hx y hy + obtain ⟨β, hβ, hnone⟩ := getLast?_pointBlock hr hps hws t k + rw [hβ] at hx + rw [head?_pointBlock hr hps hws] at hy + simp only [Option.mem_def, Option.some.injEq] at hx hy + subst hx hy + have : ¬ r - 1 + 1 < r := by omega + simp [cursorNext, this, hnone, hk] + +/-- **The walk starts at `cursorStart`.** -/ +theorem head?_cursorList (hT : 0 < T) : (cursorList ws ps T n r).head? = some (cursorStart ws) := + head?_flatMap_range _ hT (head?_nodeBlock hr hn hps hws 0) + +/-- **Each index is followed by its successor.** -/ +theorem isChain_cursorList : + List.IsChain (fun x y => cursorNext ws ps T n r x = some y) (cursorList ws ps T n r) := by + refine isChain_flatMap _ _ (fun t _ => nodeBlock_ne_nil hr hn hps hws t) + (fun t _ => isChain_nodeBlock hr hps hws t) (isChain_range_of fun t ht => ?_) + intro x hx y hy + obtain ⟨β, hβ, hnone⟩ := getLast?_nodeBlock hr hn hps hws t + rw [hβ] at hx + rw [head?_nodeBlock hr hn hps hws] at hy + simp only [Option.mem_def, Option.some.injEq] at hx hy + subst hx hy + have h1 : ¬ r - 1 + 1 < r := by omega + have h2 : ¬ n - 1 + 1 < n := by omega + simp [cursorNext, h1, h2, hnone, ht] + +/-- **The last index has no successor.** -/ +theorem cursorNext_getLast? : + ∀ x ∈ (cursorList ws ps T n r).getLast?, cursorNext ws ps T n r x = none := by + intro x hx + rcases Nat.eq_zero_or_pos T with rfl | hT + · simp [cursorList] at hx + obtain ⟨β, hβ, hnone⟩ := getLast?_nodeBlock hr hn hps hws (T - 1) + rw [cursorList, getLast?_flatMap_range _ hT (nodeBlock_ne_nil hr hn hps hws _), hβ] at hx + simp only [Option.mem_def, Option.some.injEq] at hx + subst hx + have h1 : ¬ r - 1 + 1 < r := by omega + have h2 : ¬ n - 1 + 1 < n := by omega + have h3 : ¬ T - 1 + 1 < T := by omega + simp [cursorNext, h1, h2, h3, hnone] + +/-- **The indexed walk**, in the form a counted loop consumes: the first child is `cursorStart`, +child `c + 1` is the successor of child `c`, and the last child has no successor. -/ +theorem cursorList_walk (hT : 0 < T) : + ∃ h : 0 < (cursorList ws ps T n r).length, + (cursorList ws ps T n r)[0] = cursorStart ws ∧ + (∀ c (hc : c + 1 < (cursorList ws ps T n r).length), + cursorNext ws ps T n r (cursorList ws ps T n r)[c] = + some (cursorList ws ps T n r)[c + 1]) ∧ + cursorNext ws ps T n r ((cursorList ws ps T n r)[(cursorList ws ps T n r).length - 1]) = + none := by + have hhead := head?_cursorList hr hn hps hws (T := T) hT + have hpos : 0 < (cursorList ws ps T n r).length := by + rcases h : cursorList ws ps T n r with _ | ⟨x, l⟩ + · rw [h] at hhead; simp at hhead + · simp + refine ⟨hpos, ?_, fun c hc => (isChain_cursorList hr hn hps hws).getElem c hc, ?_⟩ + · rw [List.head?_eq_getElem?, List.getElem?_eq_getElem hpos] at hhead + exact Option.some.inj hhead + · refine cursorNext_getLast? hr hn hps hws _ ?_ + rw [List.getLast?_eq_getElem?, List.getElem?_eq_getElem (by omega)] + rfl + +end Walk + +/-! ### Polynomial time -/ + +/-- One step of the cursor, from the counts `(T, n, r)`, the current node's weights and budget, and +the next node's number of modes: `cursorNext` with the data of the nodes read off. -/ +@[expose] def cursorStep (T n r : ℕ) (wt : List ℕ) (pt nextLen : ℕ) (s : State) : Option State := + if s.2.2.2 + 1 < r then some (s.1, s.2.1, s.2.2.1, s.2.2.2 + 1) + else + (budgetNext wt pt s.2.2.1).elim + (if s.2.1 + 1 < n then some (s.1, s.2.1 + 1, List.replicate wt.length 0, 0) + else if s.1 + 1 < T then some (s.1 + 1, 0, List.replicate nextLen 0, 0) else none) + fun β => some (s.1, s.2.1, β, 0) + +/-- The cursor is its step at the current node. -/ +theorem cursorNext_eq_cursorStep (ws : ℕ → List ℕ) (ps : ℕ → ℕ) (T n r : ℕ) (s : State) : + cursorNext ws ps T n r s = cursorStep T n r (ws s.1) (ps s.1) (ws (s.1 + 1)).length s := by + unfold cursorNext cursorStep + split_ifs <;> cases budgetNext (ws s.1) (ps s.1) s.2.2.1 <;> rfl + +open Turing.MultiTapeTM + +/-- The inputs of `cursorStep`: the counts in unary, the weights and the budget in binary, the next +number of modes in unary, and the state with its exponents in binary. -/ +abbrev StepInput : Type := ((ℕ × ℕ × ℕ) × (List ℕ × ℕ × ℕ)) × State + +/-- The encoding of a state. -/ +@[expose] def encState : State ↪ List Bool := + encPair encUnary (encPair encUnary (encPair (encList encNat) encUnary)) + +/-- The encoding of `StepInput`. -/ +@[expose] def encStepInput : StepInput ↪ List Bool := + encPair (encPair (encPair encUnary (encPair encUnary encUnary)) + (encPair (encList encNat) (encPair encNat encUnary))) encState + +/-- **The cursor step is polynomial-time**: the children are enumerated with polynomial delay. -/ +theorem computableInPolyTime_cursorStep : + ComputableInPolyTime (fun x : StepInput => + cursorStep x.1.1.1 x.1.1.2.1 x.1.1.2.2 x.1.2.1 x.1.2.2.1 x.1.2.2.2 x.2) encStepInput + (encOption encState) := by + let e := encStepInput + have hT : ComputableInPolyTime (fun x : StepInput => x.1.1.1) e encUnary := by + unfold e encStepInput; fun_prop + have hn : ComputableInPolyTime (fun x : StepInput => x.1.1.2.1) e encUnary := by + unfold e encStepInput; fun_prop + have hr : ComputableInPolyTime (fun x : StepInput => x.1.1.2.2) e encUnary := by + unfold e encStepInput; fun_prop + have hwt : ComputableInPolyTime (fun x : StepInput => x.1.2.1) e (encList encNat) := by + unfold e encStepInput; fun_prop + have hpt : ComputableInPolyTime (fun x : StepInput => x.1.2.2.1) e encNat := by + unfold e encStepInput; fun_prop + have hnext : ComputableInPolyTime (fun x : StepInput => x.1.2.2.2) e encUnary := by + unfold e encStepInput; fun_prop + have ht : ComputableInPolyTime (fun x : StepInput => x.2.1) e encUnary := by + unfold e encStepInput encState; fun_prop + have hk : ComputableInPolyTime (fun x : StepInput => x.2.2.1) e encUnary := by + unfold e encStepInput encState; fun_prop + have hβ : ComputableInPolyTime (fun x : StepInput => x.2.2.2.1) e (encList encNat) := by + unfold e encStepInput encState; fun_prop + have hg : ComputableInPolyTime (fun x : StepInput => x.2.2.2.2) e encUnary := by + unfold e encStepInput encState; fun_prop + have hsome {f : StepInput → State} (hf : ComputableInPolyTime f e encState) : + ComputableInPolyTime (fun x => some (f x)) e (encOption encState) := + (ComputableInPolyTime.optionSome encState).comp hf + have hmk {t k g : StepInput → ℕ} {β : StepInput → List ℕ} + (h1 : ComputableInPolyTime t e encUnary) (h2 : ComputableInPolyTime k e encUnary) + (h3 : ComputableInPolyTime β e (encList encNat)) (h4 : ComputableInPolyTime g e encUnary) : + ComputableInPolyTime (fun x => ((t x, k x, β x, g x) : State)) e encState := by + unfold encState; exact h1.prod (h2.prod (h3.prod h4)) + have hsucc {f : StepInput → ℕ} (hf : ComputableInPolyTime f e encUnary) : + ComputableInPolyTime (fun x => f x + 1) e encUnary := + ComputableInPolyTime.unary_add' hf (.const 1) + have hbudget := computableInPolyTime_budgetNext.comp + (show ComputableInPolyTime (fun x : StepInput => (x.1.2.1, x.1.2.2.1, x.2.2.2.1)) e + encBudgetInput by unfold encBudgetInput; exact hwt.prod (hpt.prod hβ)) + have hzeros {f : StepInput → ℕ} (hf : ComputableInPolyTime f e encUnary) : + ComputableInPolyTime (fun x => List.replicate (f x) 0) e (encList encNat) := + ComputableInPolyTime.replicate hf (.const 0) + have hfallback : ComputableInPolyTime (fun x : StepInput => + if x.2.2.1 + 1 < x.1.1.2.1 then + some ((x.2.1, x.2.2.1 + 1, List.replicate x.1.2.1.length 0, 0) : State) + else if x.2.1 + 1 < x.1.1.1 then + some ((x.2.1 + 1, 0, List.replicate x.1.2.2.2 0, 0) : State) + else none) e (encOption encState) := by + refine (ComputableInPolyTime.ite' (ComputableInPolyTime.unary_lt' (hsucc hk) hn) + (hsome (hmk ht (hsucc hk) (hzeros ((ComputableInPolyTime.length_list _).comp hwt)) + (.const 0))) + (ComputableInPolyTime.ite' (ComputableInPolyTime.unary_lt' (hsucc ht) hT) + (hsome (hmk (hsucc ht) (.const 0) (hzeros hnext) (.const 0))) (.const none))).of_enc_eq + fun x => ?_ + simp only [decide_eq_true_eq, Function.comp_apply] + have hbody : ComputableInPolyTime (fun p : StepInput × List ℕ => + some ((p.1.2.1, p.1.2.2.1, p.2, 0) : State)) (encPair e (encList encNat)) + (encOption encState) := by + refine (ComputableInPolyTime.optionSome encState).comp ?_ + unfold encState + exact (ht.comp (ComputableInPolyTime.fst _ _)).prod + ((hk.comp (ComputableInPolyTime.fst _ _)).prod ((ComputableInPolyTime.snd _ _).prod + (.const 0))) + have helim := ComputableInPolyTime.optionElimWithInput + (body := fun x β => some ((x.2.1, x.2.2.1, β, 0) : State)) hbudget hfallback hbody + have h := ComputableInPolyTime.ite' (ComputableInPolyTime.unary_lt' (hsucc hg) hr) + (hsome (hmk ht hk hβ (hsucc hg))) helim + refine h.of_enc_eq fun x => ?_ + simp only [cursorStep, decide_eq_true_eq, Function.comp_apply] + +end Cslib.Circuits.Frontier.ChildCursor diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkChildCursor.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkChildCursor.lean new file mode 100644 index 0000000000..e226003f58 --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkChildCursor.lean @@ -0,0 +1,46 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ +module +-- upstream: local-only (test driver) + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildCursor + +public section + +open Cslib.Circuits.Frontier.ChildCursor + +namespace Cslib.Tests.TensorNetworkChildCursor + +/-! Two nodes: node `0` has one mode of weight `1` and budget `2`, so the exponents `0, 1`; node +`1` has two modes of weights `1, 2` and budget `2`, so the exponents `00, 10`. With one point and +two groups the cursor visits eight children. -/ + +def ws (t : ℕ) : List ℕ := if t = 0 then [1] else [1, 2] +def ps (_ : ℕ) : ℕ := 2 + +example : cursorList ws ps 2 1 2 = + [(0, 0, [0], 0), (0, 0, [0], 1), (0, 0, [1], 0), (0, 0, [1], 1), + (1, 0, [0, 0], 0), (1, 0, [0, 0], 1), (1, 0, [1, 0], 0), (1, 0, [1, 0], 1)] := by + decide +kernel + +/-- The cursor walks the list and stops after the last child. -/ +example : cursorStart ws = (0, 0, [0], 0) ∧ + cursorNext ws ps 2 1 2 (0, 0, [0], 1) = some (0, 0, [1], 0) ∧ + cursorNext ws ps 2 1 2 (0, 0, [1], 1) = some (1, 0, [0, 0], 0) ∧ + cursorNext ws ps 2 1 2 (1, 0, [1, 0], 1) = none := by + decide +kernel + +/-- The walk, by index, from the general theorem. -/ +example : ∃ h : 0 < (cursorList ws ps 2 1 2).length, + (cursorList ws ps 2 1 2)[0] = cursorStart ws ∧ + (∀ c (hc : c + 1 < (cursorList ws ps 2 1 2).length), + cursorNext ws ps 2 1 2 (cursorList ws ps 2 1 2)[c] = some (cursorList ws ps 2 1 2)[c + 1]) ∧ + cursorNext ws ps 2 1 2 ((cursorList ws ps 2 1 2)[(cursorList ws ps 2 1 2).length - 1]) = + none := + cursorList_walk (by norm_num) (by norm_num) (fun _ => by simp [ps]) + (fun t w hw => by unfold ws at hw; split_ifs at hw <;> simp at hw <;> omega) (by norm_num) + +end Cslib.Tests.TensorNetworkChildCursor From af142d19bc9e0d360af8b5f53b34218742134ec1 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 01:20:03 -0400 Subject: [PATCH 059/129] refactor(combinatorics): count the feasible coordinate values with Mathlib's ceiling division MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review of #1942: replace the parallel `headCount` with `p ⌈/⌉ w`, which is definitionally `(p + w - 1) / w`; `lt_ceilDiv_iff` now follows from `ceilDiv_le_iff_le_mul`. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Combinatorics/BudgetTuples.lean | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/Complexity/Foundations/Combinatorics/BudgetTuples.lean b/Complexity/Foundations/Combinatorics/BudgetTuples.lean index ebc78e7228..3ed809c732 100644 --- a/Complexity/Foundations/Combinatorics/BudgetTuples.lean +++ b/Complexity/Foundations/Combinatorics/BudgetTuples.lean @@ -8,6 +8,7 @@ module public import Complexity.Init public import Mathlib.Algebra.BigOperators.Group.Finset.Basic +public import Mathlib.Algebra.Order.Floor.Div public import Mathlib.Data.Fintype.Pi public import Mathlib.Data.List.Chain import Mathlib.Algebra.BigOperators.Group.List.Basic @@ -45,7 +46,6 @@ along `budgetTuples` (`sum_filter_weight_lt_eq`). ## Main definitions * `Cslib.BudgetTuples.weight`: the weight of a tuple. -* `Cslib.BudgetTuples.headCount`: the number of feasible values of a coordinate. * `Cslib.BudgetTuples.budgetTuples`: the tuples of weight below a budget, in lexicographic order. * `Cslib.BudgetTuples.budgetNext`: the successor of a tuple. * `Cslib.BudgetTuples.scanStep`, `Cslib.BudgetTuples.budgetNextScan`: the successor as a left scan. @@ -75,22 +75,17 @@ namespace Cslib.BudgetTuples | w :: ws, b :: β => w * b + weight ws β | _, _ => 0 -/-- The number of values `b` of a coordinate of weight `w` with `w * b < p`, that is -`⌈p / w⌉` for `w > 0`. -/ -@[expose] def headCount (w p : ℕ) : ℕ := (p + w - 1) / w - -/-- A value fits the budget exactly when it is below `headCount`. -/ -theorem lt_headCount_iff {w p b : ℕ} (hw : 0 < w) : b < headCount w p ↔ w * b < p := by - rw [headCount, Nat.lt_iff_add_one_le, Nat.le_div_iff_mul_le hw, Nat.add_mul, Nat.one_mul, - Nat.mul_comm b w] - omega +/-- A value of a coordinate of weight `w` fits the budget `p` exactly when it is below the ceiling +division `p ⌈/⌉ w`. -/ +theorem lt_ceilDiv_iff {w p b : ℕ} (hw : 0 < w) : b < p ⌈/⌉ w ↔ w * b < p := by + rw [← not_le, ceilDiv_le_iff_le_mul hw, not_le] /-- **The tuples of weight below `p`**, in lexicographic order: the first coordinate takes each -value `b` with `w * b < p` in increasing order, and the rest is a tuple of weight below -`p - w * b`. -/ +value `b` with `w * b < p` (`lt_ceilDiv_iff`) in increasing order, and the rest is a tuple of +weight below `p - w * b`. -/ @[expose] def budgetTuples : List ℕ → ℕ → List (List ℕ) | [], p => if 0 < p then [[]] else [] - | w :: ws, p => (List.range (headCount w p)).flatMap fun b => + | w :: ws, p => (List.range (p ⌈/⌉ w)).flatMap fun b => (budgetTuples ws (p - w * b)).map (b :: ·) /-- **The successor of a tuple**: advance the tail within its remaining budget if possible, and @@ -107,7 +102,7 @@ otherwise increment the first coordinate and reset the tail to zero. -/ @[simp] theorem budgetTuples_nil (p : ℕ) : budgetTuples [] p = if 0 < p then [[]] else [] := rfl theorem budgetTuples_cons (w : ℕ) (ws : List ℕ) (p : ℕ) : - budgetTuples (w :: ws) p = (List.range (headCount w p)).flatMap fun b => + budgetTuples (w :: ws) p = (List.range (p ⌈/⌉ w)).flatMap fun b => (budgetTuples ws (p - w * b)).map (b :: ·) := rfl @[simp] theorem weight_nil (β : List ℕ) : weight [] β = 0 := rfl @@ -126,7 +121,7 @@ theorem mem_budgetTuples {ws : List ℕ} (hw : ∀ w ∈ ws, 0 < w) {p : ℕ} { have hw0 : 0 < w := hw w List.mem_cons_self have ih' := @ih (fun w' hw' => hw w' (List.mem_cons_of_mem _ hw')) rw [budgetTuples_cons] - simp only [List.mem_flatMap, List.mem_range, lt_headCount_iff hw0, List.mem_map] + simp only [List.mem_flatMap, List.mem_range, lt_ceilDiv_iff hw0, List.mem_map] constructor · rintro ⟨b, hb, γ, hγ, rfl⟩ rw [ih'] at hγ @@ -187,8 +182,8 @@ theorem head?_budgetTuples {ws : List ℕ} (hw : ∀ w ∈ ws, 0 < w) {p : ℕ} | cons w ws ih => have hw0 : 0 < w := hw w List.mem_cons_self have ih' := @ih (fun w' hw' => hw w' (List.mem_cons_of_mem _ hw')) - obtain ⟨k, hk⟩ : ∃ k, headCount w p = k + 1 := - Nat.exists_eq_succ_of_ne_zero (Nat.pos_iff_ne_zero.mp ((lt_headCount_iff hw0).mpr + obtain ⟨k, hk⟩ : ∃ k, p ⌈/⌉ w = k + 1 := + Nat.exists_eq_succ_of_ne_zero (Nat.pos_iff_ne_zero.mp ((lt_ceilDiv_iff hw0).mpr (by simpa using hp))) rw [budgetTuples_cons, hk, List.range_succ_eq_map, List.flatMap_cons, List.head?_append, List.head?_map, Nat.mul_zero, Nat.sub_zero, ih' hp] @@ -204,9 +199,9 @@ theorem budgetNext_getLast? {ws : List ℕ} (hw : ∀ w ∈ ws, 0 < w) {p : ℕ} have ih' := @ih (fun w' hw' => hw w' (List.mem_cons_of_mem _ hw')) intro x hx rw [budgetTuples_cons] at hx - rcases hn : headCount w p with _ | k + rcases hn : p ⌈/⌉ w with _ | k · rw [hn] at hx; simp at hx - · have hk : w * k < p := (lt_headCount_iff hw0).mp (by omega) + · have hk : w * k < p := (lt_ceilDiv_iff hw0).mp (by omega) have hne : (budgetTuples ws (p - w * k)).map (k :: ·) ≠ [] := by rw [Ne, List.map_eq_nil_iff, ← List.head?_eq_none_iff, head?_budgetTuples (fun w' hw' => hw w' (List.mem_cons_of_mem _ hw')) (by omega)] @@ -214,7 +209,7 @@ theorem budgetNext_getLast? {ws : List ℕ} (hw : ∀ w ∈ ws, 0 < w) {p : ℕ} rw [hn, getLast?_flatMap_range_succ _ k hne, List.getLast?_map] at hx obtain ⟨x', hx', rfl⟩ := Option.map_eq_some_iff.mp hx have hnot : ¬ w * (k + 1) < p := fun h => by - have := (lt_headCount_iff hw0).mpr h; omega + have := (lt_ceilDiv_iff hw0).mpr h; omega simp only [budgetNext, ih' x' hx', hnot, ↓reduceIte] /-- **Each tuple is followed by its successor.** -/ @@ -229,13 +224,13 @@ theorem isChain_budgetTuples {ws : List ℕ} (hw : ∀ w ∈ ws, 0 < w) (p : ℕ have hws : ∀ w' ∈ ws, 0 < w' := fun w' hw' => hw w' (List.mem_cons_of_mem _ hw') rw [budgetTuples_cons] refine isChain_flatMap_range _ _ (fun b hb => ?_) (fun b _ => ?_) (fun b hb x hx y hy => ?_) - · have hb' := (lt_headCount_iff hw0).mp hb + · have hb' := (lt_ceilDiv_iff hw0).mp hb rw [Ne, List.map_eq_nil_iff, ← List.head?_eq_none_iff, head?_budgetTuples hws (by omega)] simp · rw [List.isChain_map] refine (ih hws (p - w * b)).imp fun x y hxy => ?_ simp only [budgetNext, hxy] - · have hb1 := (lt_headCount_iff hw0).mp hb + · have hb1 := (lt_ceilDiv_iff hw0).mp hb rw [List.getLast?_map] at hx obtain ⟨x', hx', rfl⟩ := Option.map_eq_some_iff.mp hx rw [List.head?_map, head?_budgetTuples hws (by omega)] at hy From 8daeef7a000815f86545a8b1e38f8846d251daf3 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 01:23:56 -0400 Subject: [PATCH 060/129] feat(circuit/frontier): the values a call stores along the cursor `absorbAt` multiplies a child's value into the product over the groups, adds the product to the sum over the exponent tuples after the last group, and stores the sum after the last tuple. `runAcc_cursorList`: along `cursorList` it stores, for each node and point, the sum over the tuples of the products over the groups, seen through any map respecting the operations. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/ChildCursor.lean | 181 ++++++++++++++++++ 1 file changed, 181 insertions(+) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildCursor.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildCursor.lean index 3a210ee817..60e643d903 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildCursor.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildCursor.lean @@ -409,4 +409,185 @@ theorem computableInPolyTime_cursorStep : refine h.of_enc_eq fun x => ?_ simp only [cursorStep, decide_eq_true_eq, Function.comp_apply] +/-! ### Accumulating the values of the children -/ + +section Accumulate + +variable (ws ps r) +variable {S R : Type*} [CommSemiring R] (mul add : S → S → S) (one zero : S) (φ : S → R) + (hmul : ∀ x y, φ (mul x y) = φ x * φ y) (hadd : ∀ x y, φ (add x y) = φ x + φ y) + (hone : φ one = 1) + +/-- The accumulator of a call: the product over the groups so far, the sum over the exponent tuples +so far, and the stored values, one per node and point. -/ +abbrev Acc (S : Type*) : Type _ := S × S × List S + +/-- **Absorb the value `y` of the child at `s`**: multiply it into the product over the groups; +after the last group, add the product to the sum over the exponent tuples; after the last tuple, +store the sum. -/ +@[expose] def absorbAt (s : State) (acc : Acc S) (y : S) : Acc S := + if s.2.2.2 + 1 < r then (mul acc.1 y, acc.2.1, acc.2.2) + else if (budgetNext (ws s.1) (ps s.1) s.2.2.1).isNone then + (one, zero, acc.2.2 ++ [add acc.2.1 (mul acc.1 y)]) + else (one, add acc.2.1 (mul acc.1 y), acc.2.2) + +/-- The accumulator after the children `l` with the values `val`. -/ +@[expose] def runAcc (val : State → S) (l : List State) (acc : Acc S) : Acc S := + l.foldl (fun acc s => absorbAt ws ps r mul add one zero s acc (val s)) acc + +omit [CommSemiring R] in +/-- Absorbing two lists of children absorbs one, then the other. -/ +theorem runAcc_append (val : State → S) (l₁ l₂ : List State) (acc : Acc S) : + runAcc ws ps r mul add one zero val (l₁ ++ l₂) acc = + runAcc ws ps r mul add one zero val l₂ (runAcc ws ps r mul add one zero val l₁ acc) := by + simp only [runAcc, List.foldl_append] + +include hmul in +/-- Before the last group, the product over the groups accumulates. -/ +private theorem runAcc_groups (val : State → S) (t k : ℕ) (β : List ℕ) (j : ℕ) (hj : j + 1 ≤ r) + (acc : Acc S) : + (runAcc ws ps r mul add one zero val ((List.range j).map fun g => (t, k, β, g)) acc).2 = + acc.2 ∧ + φ (runAcc ws ps r mul add one zero val ((List.range j).map fun g => (t, k, β, g)) acc).1 = + φ acc.1 * ((List.range j).map fun g => φ (val (t, k, β, g))).prod := by + induction j with + | zero => simp [runAcc] + | succ j ih => + obtain ⟨h1, h2⟩ := ih (by omega) + have hsplit : runAcc ws ps r mul add one zero val + ((List.range (j + 1)).map fun g => (t, k, β, g)) acc = + absorbAt ws ps r mul add one zero (t, k, β, j) (runAcc ws ps r mul add one zero val + ((List.range j).map fun g => (t, k, β, g)) acc) (val (t, k, β, j)) := by + simp only [runAcc, List.range_succ, List.map_append, List.map_singleton, List.foldl_append, + List.foldl_cons, List.foldl_nil] + rw [hsplit] + simp only [absorbAt, show j + 1 < r by omega, ↓reduceIte] + refine ⟨h1, ?_⟩ + rw [hmul, h2, List.range_succ, List.map_append, List.prod_append, List.map_singleton, + List.prod_singleton, mul_assoc] + +include hmul hadd in +/-- After the last group of a tuple, the product joins the sum, and after the last tuple the sum is +stored. -/ +private theorem runAcc_groupBlock (hr : 0 < r) (val : State → S) (t k : ℕ) (β : List ℕ) + (acc : Acc S) : + ∃ q, φ q = φ acc.2.1 + φ acc.1 * ((List.range r).map fun g => φ (val (t, k, β, g))).prod ∧ + runAcc ws ps r mul add one zero val (groupBlock r t k β) acc = + if (budgetNext (ws t) (ps t) β).isNone then (one, zero, acc.2.2 ++ [q]) + else (one, q, acc.2.2) := by + obtain ⟨r, rfl⟩ := Nat.exists_eq_succ_of_ne_zero hr.ne' + obtain ⟨h1, h2⟩ := runAcc_groups ws ps (r + 1) mul add one zero φ hmul val t k β r le_rfl acc + set mid := runAcc ws ps (r + 1) mul add one zero val + ((List.range r).map fun g => (t, k, β, g)) acc + refine ⟨add acc.2.1 (mul mid.1 (val (t, k, β, r))), ?_, ?_⟩ + · rw [hadd, hmul, h2, List.range_succ, List.map_append, List.prod_append, List.map_singleton, + List.prod_singleton, mul_assoc] + · have hsplit : runAcc ws ps (r + 1) mul add one zero val (groupBlock (r + 1) t k β) acc = + absorbAt ws ps (r + 1) mul add one zero (t, k, β, r) mid (val (t, k, β, r)) := by + simp only [groupBlock, runAcc, mid, List.range_succ, List.map_append, List.map_singleton, + List.foldl_append, List.foldl_cons, List.foldl_nil] + rw [hsplit] + simp only [absorbAt, lt_irrefl, ↓reduceIte, h1] + +include hmul hadd hone in +/-- Over a list of tuples whose last one alone has no successor, the sum over the tuples is +stored. -/ +private theorem runAcc_tuples (hr : 0 < r) (val : State → S) (t k : ℕ) (B : List (List ℕ)) + (hB : B ≠ []) (hsome : ∀ β ∈ B.dropLast, (budgetNext (ws t) (ps t) β).isSome) + (hlast : (budgetNext (ws t) (ps t) (B.getLast hB)).isNone) (sm : S) (V : List S) : + ∃ q, φ q = φ sm + (B.map fun β => ((List.range r).map fun g => φ (val (t, k, β, g))).prod).sum ∧ + runAcc ws ps r mul add one zero val (B.flatMap (groupBlock r t k)) (one, sm, V) = + (one, zero, V ++ [q]) := by + induction B generalizing sm with + | nil => exact absurd rfl hB + | cons β B ih => + obtain ⟨q₁, hq₁, hrun₁⟩ := runAcc_groupBlock ws ps r mul add one zero φ hmul hadd hr val t k β + (one, sm, V) + rcases B with _ | ⟨β', B⟩ + · simp only [List.getLast_singleton] at hlast + refine ⟨q₁, ?_, ?_⟩ + · simpa [hone] using hq₁ + · simpa [runAcc, hlast] using hrun₁ + · have hβ : (budgetNext (ws t) (ps t) β).isSome := hsome β (by simp) + have hnone : (budgetNext (ws t) (ps t) β).isNone = false := by + simpa [Option.isNone_iff_eq_none, Option.isSome_iff_ne_none] using hβ + obtain ⟨q, hq, hrun⟩ := ih (List.cons_ne_nil _ _) + (fun γ hγ => hsome γ (by + simp only [List.dropLast_cons_cons]; exact List.mem_cons_of_mem _ hγ)) + (by simpa using hlast) q₁ + refine ⟨q, ?_, ?_⟩ + · simp only [hq, hq₁, hone, one_mul, List.map_cons, List.sum_cons, add_assoc] + · rw [List.flatMap_cons, runAcc_append, hrun₁, hnone] + simpa using hrun + +/-- The values stored for a node and point: the sum over the exponent tuples of the products over +the groups. -/ +@[expose] def sumAt (val : State → S) (t k : ℕ) : R := + ((budgetTuples (ws t) (ps t)).map fun β => + ((List.range r).map fun g => φ (val (t, k, β, g))).prod).sum + +include hmul hadd hone + +/-- Over the points of a node, one sum is stored per point. -/ +private theorem runAcc_points (hzero : φ zero = 0) (hr : 0 < r) (hps : ∀ t, 0 < ps t) + (hws : ∀ t, ∀ w ∈ ws t, 0 < w) (val : State → S) (t j : ℕ) (V : List S) : + ∃ qs : List S, qs.map φ = (List.range j).map (sumAt ws ps r φ val t) ∧ + runAcc ws ps r mul add one zero val ((List.range j).flatMap (pointBlock ws ps r t)) + (one, zero, V) = (one, zero, V ++ qs) := by + induction j generalizing V with + | zero => exact ⟨[], rfl, by simp [runAcc]⟩ + | succ j ih => + obtain ⟨qs, hqs, hrun⟩ := ih V + set B := budgetTuples (ws t) (ps t) + have hB : B ≠ [] := by + rw [Ne, ← List.head?_eq_none_iff, head?_budgetTuples (hws t) (hps t)] + simp + have hsome : ∀ β ∈ B.dropLast, (budgetNext (ws t) (ps t) β).isSome := by + intro β hβ + obtain ⟨i, hi, rfl⟩ := List.mem_iff_getElem.mp hβ + rw [List.length_dropLast] at hi + have hlen : B.length = (budgetTuples (ws t) (ps t)).length := rfl + rw [List.getElem_dropLast, (isChain_budgetTuples (hws t) (ps t)).getElem i (by omega)] + rfl + have hlast : (budgetNext (ws t) (ps t) (B.getLast hB)).isNone := by + rw [budgetNext_getLast? (hws t) _ (List.getLast?_eq_some_getLast hB ▸ rfl)] + rfl + obtain ⟨q, hq, hrunq⟩ := runAcc_tuples ws ps r mul add one zero φ hmul hadd hone hr val t j B + hB hsome hlast zero (V ++ qs) + refine ⟨qs ++ [q], ?_, ?_⟩ + · rw [List.map_append, hqs, List.range_succ, List.map_append, List.map_singleton, + List.map_singleton, hq, hzero, zero_add] + rfl + · rw [List.range_succ, List.flatMap_append, runAcc_append, hrun, List.flatMap_singleton, + pointBlock, hrunq, List.append_assoc] + +/-- **The values stored by a call**: absorbing the values of the children along `cursorList` +stores, for each node and point in order, the sum over the exponent tuples of the products over +the groups (`sumAt`), as seen through `φ`. -/ +theorem runAcc_cursorList (hzero : φ zero = 0) (hr : 0 < r) (hps : ∀ t, 0 < ps t) + (hws : ∀ t, ∀ w ∈ ws t, 0 < w) (val : State → S) : + ∃ qs : List S, qs.map φ = + (List.range T).flatMap (fun t => (List.range n).map (sumAt ws ps r φ val t)) ∧ + runAcc ws ps r mul add one zero val (cursorList ws ps T n r) (one, zero, []) = + (one, zero, qs) := by + have key : ∀ j, ∃ qs : List S, qs.map φ = + (List.range j).flatMap (fun t => (List.range n).map (sumAt ws ps r φ val t)) ∧ + runAcc ws ps r mul add one zero val ((List.range j).flatMap (nodeBlock ws ps n r)) + (one, zero, []) = (one, zero, qs) := by + intro j + induction j with + | zero => exact ⟨[], rfl, by simp [runAcc]⟩ + | succ j ih => + obtain ⟨qs, hqs, hrun⟩ := ih + obtain ⟨qs', hqs', hrun'⟩ := runAcc_points ws ps r mul add one zero φ hmul hadd hone hzero hr + hps hws val j n qs + refine ⟨qs ++ qs', ?_, ?_⟩ + · rw [List.map_append, hqs, hqs', List.range_succ, List.flatMap_append, + List.flatMap_singleton] + · rw [List.range_succ, List.flatMap_append, runAcc_append, hrun, List.flatMap_singleton, + nodeBlock, hrun'] + exact key T + +end Accumulate + end Cslib.Circuits.Frontier.ChildCursor From 207d29dcb87a1a8dded602ffcf32defdbcc8b401 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 01:25:37 -0400 Subject: [PATCH 061/129] feat(circuit/frontier): the stored values are the sums over the mode budget `sumAt_eq_sum_filter` turns the value a call stores for a node and point into the sum over the mode budget, through `sum_filter_weight_lt_eq`. Adds a test of the accumulator on two nodes. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/ChildCursor.lean | 31 +++++++++++++++++++ .../Frontier/TensorNetworkChildCursor.lean | 8 +++++ 2 files changed, 39 insertions(+) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildCursor.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildCursor.lean index 60e643d903..27c5911930 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildCursor.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildCursor.lean @@ -34,6 +34,14 @@ over counted children consumes. One step reads only the current node's weights a next node's number of modes (`cursorNext_eq_cursorStep`), and it is polynomial-time (`computableInPolyTime_cursorStep`), so the children are enumerated with polynomial delay. +**Accumulating.** `absorbAt` folds a child's value into the accumulator of the call: it multiplies +it into the product over the groups, adds the product to the sum over the exponent tuples after the +last group, and stores the sum after the last tuple. Along `cursorList` this stores, for each node +and point in order, the sum over the tuples of the products over the groups (`runAcc_cursorList`), +seen through any map to a commutative semiring that respects the operations, such as the value of +a coefficient list. With the weights listed along the modes, that sum is the sum over the mode +budget of the right side of (D.16) (`sumAt_eq_sum_filter`). + ## Main definitions * `Cslib.Circuits.Frontier.ChildCursor.cursorList`: the indices `(t, k, β, g)` in order. @@ -42,6 +50,8 @@ next node's number of modes (`cursorNext_eq_cursorStep`), and it is polynomial-t * `Cslib.Circuits.Frontier.ChildCursor.cursorStep`, `Cslib.Circuits.Frontier.ChildCursor.encStepInput`: one step from the data of the nodes, and its input encoding. +* `Cslib.Circuits.Frontier.ChildCursor.absorbAt`, `Cslib.Circuits.Frontier.ChildCursor.runAcc`, + `Cslib.Circuits.Frontier.ChildCursor.sumAt`: the accumulator of a call and the stored values. ## Main results @@ -51,6 +61,9 @@ next node's number of modes (`cursorNext_eq_cursorStep`), and it is polynomial-t `Cslib.Circuits.Frontier.ChildCursor.cursorList_walk`: the cursor walks the list. * `Cslib.Circuits.Frontier.ChildCursor.isChain_flatMap`: a chain of nonempty chained blocks. * `Cslib.Circuits.Frontier.ChildCursor.computableInPolyTime_cursorStep`: polynomial delay. +* `Cslib.Circuits.Frontier.ChildCursor.runAcc_cursorList`, + `Cslib.Circuits.Frontier.ChildCursor.sumAt_eq_sum_filter`: the stored values are the sums over + the mode budget. ## References @@ -588,6 +601,24 @@ theorem runAcc_cursorList (hzero : φ zero = 0) (hr : 0 < r) (hps : ∀ t, 0 < p nodeBlock, hrun'] exact key T +omit hmul hadd hone in +/-- **The stored sum is the sum over the mode budget.** If the weights of node `t` list the +weights `w` of the modes along a duplicate-free list `ms` of all of them, and the product over the +groups of the children of each tuple `l` is `F (ofList ms l)`, then the value stored for `t` and `k` +is the sum of `F` over the tuples of weight below `ps t`, which is +`Cslib.Circuits.Frontier.modeBudget w (ps t)`. -/ +theorem sumAt_eq_sum_filter {M : Type*} [Fintype M] [DecidableEq M] {ms : List M} + (hms : ms.Nodup) (hcover : ∀ Z, Z ∈ ms) {w : M → ℕ} (hw : ∀ Z, 0 < w Z) (val : State → S) + (t k : ℕ) (hwt : ws t = ms.map w) (F : (M → ℕ) → R) + (hF : ∀ l ∈ budgetTuples (ws t) (ps t), + ((List.range r).map fun g => φ (val (t, k, l, g))).prod = F (ofList ms l)) : + sumAt ws ps r φ val t k = ∑ β ∈ ({β ∈ Fintype.piFinset fun _ => Finset.range (ps t) | + ∑ Z, w Z * β Z < ps t} : Finset (M → ℕ)), F β := by + rw [sum_filter_weight_lt_eq hms hcover hw (ps t) F, sumAt, ← hwt] + congr 1 + refine List.map_congr_left fun l hl => ?_ + exact hF l hl + end Accumulate end Cslib.Circuits.Frontier.ChildCursor diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkChildCursor.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkChildCursor.lean index e226003f58..092e7b54e8 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkChildCursor.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkChildCursor.lean @@ -43,4 +43,12 @@ example : ∃ h : 0 < (cursorList ws ps 2 1 2).length, cursorList_walk (by norm_num) (by norm_num) (fun _ => by simp [ps]) (fun t w hw => by unfold ws at hw; split_ifs at hw <;> simp at hw <;> omega) (by norm_num) +/-- With the value `t + Σβ + g + 1` at each child, the call stores `1·2 + 2·3 = 8` for node `0` and +`2·3 + 3·4 = 18` for node `1`. -/ +def val (s : State) : ℕ := s.1 + s.2.2.1.sum + s.2.2.2 + 1 + +example : (runAcc ws ps 2 (· * ·) (· + ·) 1 0 val (cursorList ws ps 2 1 2) (1, 0, [])) = + (1, 0, [8, 18]) := by + decide +kernel + end Cslib.Tests.TensorNetworkChildCursor From 956e30a0a3aa13ba766ab23bcdfeac890ce9e924 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 01:49:10 -0400 Subject: [PATCH 062/129] feat(circuit/frontier): vertex descriptors and the entries of native child networks (Theorem D.1, T2.9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VertexDesc (a table or a packet descriptor) with one native evaluator and its resource bound; a described network with port lists has the contraction of the network it describes; retained vertices keep their tensors; the packet vertices of a Cauchy child have the child's entries for ports in any order with tags computed from the same port list; reductionLift maps the Galois ring back into O / 2^n with reductionLift ∘ reduction = mk. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 3 + .../Frontier/TensorNetwork/ChildEntries.lean | 308 ++++++++++++++++ .../Frontier/TensorNetwork/ReductionLift.lean | 121 ++++++ .../Frontier/TensorNetwork/VertexDesc.lean | 344 ++++++++++++++++++ .../Frontier/TensorNetworkVertexDesc.lean | 95 +++++ 5 files changed, 871 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildEntries.lean create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/ReductionLift.lean create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/VertexDesc.lean create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkVertexDesc.lean diff --git a/Complexity.lean b/Complexity.lean index 3054842fdb..c17dc3af9f 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -879,6 +879,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ActiveCall public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Binarize public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CauchyModes +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildEntries public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildSize public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CoeffScheme public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint @@ -893,8 +894,10 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpans public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEntry public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEvaluator public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketTerms +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ReductionLift public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Schedule public import Complexity.Computability.Circuit.Frontier.TensorNetwork.TaperedRecovery +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.VertexDesc public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness public import Complexity.Computability.Circuit.Frontier.Unconditional public import Complexity.Computability.Circuit.Frontier.Witness diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildEntries.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildEntries.lean new file mode 100644 index 0000000000..2eb8415168 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildEntries.lean @@ -0,0 +1,308 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.VertexDesc +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DescribedContraction +import Mathlib.Data.List.GetD + +/-! +# The entries of the child networks from their descriptors + +A child network of the contraction recursion of [OpenAI2026ETH] (`ModeSplitting.child`, §D.4.6, +p. 63) is carried natively as a network of vertex descriptors (`VertexDesc`) with port lists. This +file proves that such a network has the contraction of the child, once each descriptor reads its +ports in the right order. + +* **Port lists** (`DescribedNetwork.contraction_eq_of_ports`). Number the vertices and the edges of + a Boolean tensor network `N`, and give each vertex any list of edges as its ports and a + descriptor. If the evaluator at the descriptor of `v`, read at the bits of an assignment on its + ports, is the tensor of `v` at that assignment, the described network has the contraction of + `N`. +* **Retained vertices** (`ModeSplitting.child_tensor_inl_eq_tensor`). A vertex of group `g` keeps + its tensor in the child of group `g`: a row reads its row ports and a column its column ports. + So a retained vertex keeps its descriptor, and its ports are its parent ports, in the same + order, renumbered as child edges. +* **Packet vertices** (`TensorNetwork.map_val_entry_child_inr`). Let the ports of the packet + vertex `q` be the child edges of packet `q` in any order without repetition, and let its + descriptor tag each port with whether it is a row port and with its slot, in the same order. Then + along any ring map from `A_p` to the coefficient ring of the child, the descriptor's entry at the + bits on its ports is the child's entry (`child_tensor_inr_cauchyModeSplitting`), for the + packet's context matching the node `(t, ζ)`, the group `g`, the symbols of `q` and the exponents + `β`. The tags are computed from the port list itself, so tags and bits align by construction + (`NativePacket.sidePorts_map`). + +## Main results + +* `Cslib.Circuits.Frontier.DescribedNetwork.contraction_eq_of_ports`: a described network with + port lists has the contraction of the network it describes. +* `Cslib.Circuits.Frontier.NativePacket.sidePorts_map`: the ports of one side of a tagged list. +* `Cslib.Circuits.Frontier.TensorNetwork.ModeSplitting.child_tensor_inl_eq_tensor`: the entries + of the retained vertices of a child. +* `Cslib.Circuits.Frontier.TensorNetwork.map_val_entry_child_inr`: the entries of the packet + vertices of a Cauchy child, from their descriptors. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.6, p. 63. +-/ + +public section + +namespace Cslib.Circuits.Frontier + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +open Finset GaloisRingArith NativePacket + +/-! ### Port lists -/ + +namespace DescribedNetwork + +variable {T : Type} {R : Type*} [CommSemiring R] {V E : Type*} [Fintype V] [Fintype E] + [DecidableEq E] + +/-- **A described network with port lists has the contraction of the network it describes.** +Number the vertices of `N` by `ev` and its edges by `ee`, and give each vertex `v` a list of edges +`ports v` and a descriptor `desc v`. If the evaluator at `desc v`, read at the bits of every +assignment on `ports v`, is the tensor of `v` at that assignment, the described network with the +numbered ports and the descriptors has the contraction of `N`. -/ +theorem contraction_eq_of_ports {k l : ℕ} (N : TensorNetwork R Bool V E) (ev : V ≃ Fin k) + (ee : E ≃ Fin l) (ports : V → List E) (desc : V → T) (eval : T → List Bool → R) + (h : ∀ v (x : E → Bool), eval (desc v) ((ports v).map x) = N.tensor v x) : + (⟨l, List.ofFn fun i => ((ports (ev.symm i)).map fun e => (ee e : ℕ), desc (ev.symm i))⟩ : + DescribedNetwork T).contraction eval = N.contraction := by + simp only [contraction, TensorNetwork.contraction, List.map_ofFn, List.prod_ofFn] + rw [← (ee.arrowCongr (Equiv.refl Bool)).symm.sum_comp] + refine sum_congr rfl fun x _ => ?_ + rw [← ev.symm.prod_comp] + refine prod_congr rfl fun v _ => ?_ + simp only [Function.comp_apply] + rw [← h] + congr 1 + simp only [wordBits, List.map_map] + refine List.map_congr_left fun e _ => ?_ + simp only [Function.comp_apply, Equiv.arrowCongr_symm, Equiv.arrowCongr_apply, + Equiv.symm_symm, Equiv.refl_symm, Equiv.coe_refl, id_eq] + rw [List.getD_eq_getElem _ _ (by simp), List.getElem_ofFn] + +end DescribedNetwork + +/-! ### The ports of one side of a tagged list -/ + +namespace NativePacket + +/-- **The ports of one side of a tagged list.** If the ports of a vertex are tagged, in their +order, by whether they are row ports and by their slots, and the bits are read in the same order, +the ports of one side are the ports with that tag, with their slots and bits, in order. -/ +theorem sidePorts_map {α : Type*} (cs : List α) (tag : α → Bool) (sl : α → ℕ) (y : α → Bool) + (side : Bool) : + sidePorts (cs.map fun c => (tag c, sl c)) (cs.map y) side = + (cs.filter fun c => tag c == side).map fun c => (sl c, y c) := by + induction cs with + | nil => rfl + | cons c cs ih => + simp only [sidePorts, List.length_map] at ih + simp only [sidePorts, List.map_cons, List.length_cons, List.length_map, + List.range_succ_eq_map, List.filter_cons, List.filter_map, List.getD_cons_zero, + Function.comp_def, List.getD_cons_succ] + by_cases hc : tag c == side + · rw [ite_eq_left hc, ite_eq_left hc] + simp only [List.map_cons, List.map_map] + rw [← ih] + rfl + · rw [ite_eq_right hc, ite_eq_right hc] + simp only [List.map_map] + rw [← ih] + rfl + +end NativePacket + +namespace TensorNetwork + +/-! ### Retained vertices -/ + +namespace ModeSplitting + +variable {R V E G P M : Type*} [CommRing R] [Fintype M] [DecidableEq V] [Fintype E] + [DecidableEq E] [DecidableEq P] [DecidableEq G] [DecidableEq M] {N : TensorNetwork R Bool V E} + {W : Matrix E E R} (S : ModeSplitting N W G P M) + +/-- **The retained vertices of a child**: a vertex of group `g` keeps its tensor in the child of +group `g`, reading at each of its edges the child edge of that edge: a row its row ports and a +column its column ports. -/ +theorem child_tensor_inl_eq_tensor (β : M → ℕ) (g : G) (v : {v // S.group v = g}) + (y : {e // S.group (N.src e) = g} ⊕ {e // S.group (N.tgt e) = g} → Bool) : + (S.child β g).tensor (.inl v) y = N.tensor v.1 fun e => + if S.side v.1 then (if h : S.group (N.src e) = g then y (.inl ⟨e, h⟩) else false) + else (if h : S.group (N.tgt e) = g then y (.inr ⟨e, h⟩) else false) := by + simp only [child] + by_cases hv : S.side v.1 + · rw [ite_eq_left hv] + refine N.tensor_congr _ _ _ fun e he => ?_ + simp only [ite_eq_left hv] + by_cases hs : N.src e = v.1 + · rw [dite_eq_left hs, dite_eq_left (hs ▸ v.2)] + · have ht : N.tgt e = v.1 := he.resolve_left hs + have := S.side_tgt e + rw [ht, hv] at this + exact absurd this (by decide) + · rw [ite_eq_right hv] + refine N.tensor_congr _ _ _ fun e he => ?_ + simp only [ite_eq_right hv] + by_cases ht : N.tgt e = v.1 + · rw [dite_eq_left ht, dite_eq_left (ht ▸ v.2)] + · have hs : N.src e = v.1 := he.resolve_right ht + have := S.side_src e + rw [hs] at this + exact absurd this hv + +end ModeSplitting + +/-! ### Packet vertices -/ + +section Ports + +variable {R V E G : Type*} [DecidableEq G] {N : TensorNetwork R Bool V E} {group : V → G} + {slot : E → ℕ} + +/-- The row ports of a list of child edges, with their slots and bits. -/ +private theorem filter_isLeft_map {g : G} + (cs : List ({e // group (N.src e) = g} ⊕ {e // group (N.tgt e) = g})) + (y : {e // group (N.src e) = g} ⊕ {e // group (N.tgt e) = g} → Bool) : + (cs.filter fun c => c.isLeft == true).map + (fun c => (slot (Sum.elim Subtype.val Subtype.val c), y c)) = + ((cs.filterMap Sum.getLeft?).map Subtype.val).map fun e => + (slot e, if h : group (N.src e) = g then y (.inl ⟨e, h⟩) else false) := by + induction cs with + | nil => rfl + | cons c cs ih => + rcases c with c | c + · rw [List.filter_cons_of_pos rfl, List.filterMap_cons_some (f := Sum.getLeft?) rfl, + List.map_cons, List.map_cons, List.map_cons, ih, dite_eq_left c.2] + rfl + · rw [List.filter_cons_of_neg (by simp), List.filterMap_cons_none (f := Sum.getLeft?) rfl, ih] + +/-- The column ports of a list of child edges, with their slots and bits. -/ +private theorem filter_isLeft_false_map {g : G} + (cs : List ({e // group (N.src e) = g} ⊕ {e // group (N.tgt e) = g})) + (y : {e // group (N.src e) = g} ⊕ {e // group (N.tgt e) = g} → Bool) : + (cs.filter fun c => c.isLeft == false).map + (fun c => (slot (Sum.elim Subtype.val Subtype.val c), y c)) = + ((cs.filterMap Sum.getRight?).map Subtype.val).map fun e => + (slot e, if h : group (N.tgt e) = g then y (.inr ⟨e, h⟩) else false) := by + induction cs with + | nil => rfl + | cons c cs ih => + rcases c with c | c + · rw [List.filter_cons_of_neg (by simp), List.filterMap_cons_none (f := Sum.getRight?) rfl, + ih] + · rw [List.filter_cons_of_pos rfl, List.filterMap_cons_some (f := Sum.getRight?) rfl, + List.map_cons, List.map_cons, List.map_cons, ih, dite_eq_left c.2] + rfl + +end Ports + +section Cauchy + +variable {R V E G P : Type*} [CommRing R] [Fintype E] [DecidableEq E] [Fintype P] [DecidableEq G] + [DecidableEq P] {N : TensorNetwork R Bool V E} {group : V → G} {packet : E → P} {slot : E → ℕ} + {sym : P → G × G} {i : ℕ} + (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) + (htgt : ∀ e, side (N.tgt e) = false) + (horient : ∀ e, (group (N.src e), group (N.tgt e)) = + if (slot e).testBit i then (sym (packet e)).swap else sym (packet e)) + {a : ℕ} (hslot : ∀ e, slot e < 2 ^ a) (hia : i < a) (κ : ℤ₍₂₎ →+* R) (ζ : R) {p : ℕ} + (hp : (2 : R) ^ p = 0) {t : ℤ} (ht : t ∈ cauchyGrid (2 ^ a)) (hti : (2 : ℤ) ^ i ∣ t) + (hti' : ¬ (2 : ℤ) ^ (i + 1) ∣ t) + +/-- **The packet vertices of a Cauchy child, from their descriptors.** Let the ports of the +packet vertex `q` in the child of group `g` be the child edges of packet `q`, listed without +repetition in any order, and let the descriptor's ports tag each of them, in the same order, with +whether it is a row port and with its slot. Let the packet's context have the node `t` of +valuation `i < a`, `L = 2 ^ a` slots, `2 ^ (i + 1)` tiles and `p` exponents per tile, the symbols +of `q` against `g`, an interpolation value mapping to `ζ`, and the dense exponents of `β` at `q`. +Then along a ring map `ψ` from `A_p`, at the packet's precision, to `R`, the descriptor's entry at +the bits on its ports is the child's entry. -/ +theorem map_val_entry_child_inr [DecidableEq V] (β : P × Fin (2 ^ (i + 1)) × Fin p → ℕ) (g : G) + (q : P) + (cs : List ({e // (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti + hti').group (N.src e) = g} ⊕ + {e // (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').group + (N.tgt e) = g})) + (hcs : cs.Nodup) (hcs' : ∀ c, c ∈ cs ↔ packet (Sum.elim Subtype.val Subtype.val c) = q) + (c : PacketCtx) (βd : List ℕ) (ψ : Ring c.p c.fl →+* R) (hζ : ψ (val c.p c.fl c.ζ) = ζ) + (ha : c.a = a) (hL : c.L = 2 ^ a) (hi : c.i = i) (htiles : c.tiles = 2 ^ (i + 1)) + (ht' : c.t = t) (hnb : c.nb = p) (he₁ : c.e₁ = decide ((sym q).1 = g)) + (he₂ : c.e₂ = decide ((sym q).2 = g)) (hdiffer : c.differ = decide ((sym q).1 ≠ (sym q).2)) + (hβ : ∀ Z : Fin (2 ^ (i + 1)) × Fin p, βd.getD (Z.1 * p + Z.2) 0 = β (q, Z)) + (y : {e // (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').group + (N.src e) = g} ⊕ + {e // (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').group + (N.tgt e) = g} → Bool) : + ψ (val c.p c.fl (VertexDesc.entry + (.inr (c, cs.map fun e => (e.isLeft, slot (Sum.elim Subtype.val Subtype.val e)), βd)) + (cs.map y))) = + ((cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').child β g).tensor + (.inr q) y := by + subst ha hi ht' hnb + have hports : ∀ x ∈ cs.map (fun e => (e.isLeft, slot (Sum.elim Subtype.val Subtype.val e))), + x.2 < 2 ^ c.a := by + intro x hx + obtain ⟨e, -, rfl⟩ := List.mem_map.mp hx + exact hslot _ + have key := VertexDesc.map_val_entry_inr (c, cs.map fun e => (e.isLeft, + slot (Sum.elim Subtype.val Subtype.val e)), βd) (cs.map y) ψ hp κ hL hia htiles ht hti hti' + hports + have hgoal : ψ (val c.p c.fl (VertexDesc.entry (.inr (c, cs.map fun e => (e.isLeft, + slot (Sum.elim Subtype.val Subtype.val e)), βd)) (cs.map y))) = _ := key + rw [hgoal] + dsimp only + have hrows : ((cs.filterMap Sum.getLeft?).map Subtype.val).Nodup := + ((hcs.filterMap fun _ _ _ h₁ h₂ => by + rw [Option.mem_def, Sum.getLeft?_eq_some_iff] at h₁ h₂; rw [h₁, h₂])).map + Subtype.val_injective + have hcols : ((cs.filterMap Sum.getRight?).map Subtype.val).Nodup := + ((hcs.filterMap fun _ _ _ h₁ h₂ => by + rw [Option.mem_def, Sum.getRight?_eq_some_iff] at h₁ h₂; rw [h₁, h₂])).map + Subtype.val_injective + have hrows' : ∀ e, e ∈ (cs.filterMap Sum.getLeft?).map Subtype.val ↔ + packet e = q ∧ group (N.src e) = g := by + intro e + simp only [List.mem_map, List.mem_filterMap, Sum.getLeft?_eq_some_iff] + constructor + · rintro ⟨e', ⟨c, hc, rfl⟩, rfl⟩ + exact ⟨(hcs' _).1 hc, by simpa using e'.2⟩ + · rintro ⟨hq, hg⟩ + have hg' : (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').group + (N.src e) = g := by simpa using hg + exact ⟨⟨e, hg'⟩, ⟨_, (hcs' (.inl ⟨e, hg'⟩)).2 hq, rfl⟩, rfl⟩ + have hcols' : ∀ e, e ∈ (cs.filterMap Sum.getRight?).map Subtype.val ↔ + packet e = q ∧ group (N.tgt e) = g := by + intro e + simp only [List.mem_map, List.mem_filterMap, Sum.getRight?_eq_some_iff] + constructor + · rintro ⟨e', ⟨c, hc, rfl⟩, rfl⟩ + exact ⟨(hcs' _).1 hc, by simpa using e'.2⟩ + · rintro ⟨hq, hg⟩ + have hg' : (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').group + (N.tgt e) = g := by simpa using hg + exact ⟨⟨e, hg'⟩, ⟨_, (hcs' (.inr ⟨e, hg'⟩)).2 hq, rfl⟩, rfl⟩ + rw [child_tensor_inr_cauchyModeSplitting side hsrc htgt horient hslot hia κ _ hp ht hti hti' β + g q y hrows hrows' hcols hcols', sidePorts_map, sidePorts_map, filter_isLeft_map, + filter_isLeft_false_map, hζ, he₁, he₂, hdiffer] + congr 1 + funext Z + exact hβ Z + +end Cauchy + +end TensorNetwork + +end Cslib.Circuits.Frontier diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ReductionLift.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ReductionLift.lean new file mode 100644 index 0000000000..423643c610 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ReductionLift.lean @@ -0,0 +1,121 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEvaluator +public import Complexity.Foundations.NumberTheory.Padics.AdjoinRootReduction +import Mathlib.Data.ZMod.QuotientRing + +/-! +# The Galois ring maps into `O / 2 ^ n` + +The contraction recursion of [OpenAI2026ETH] reasons over `O = ℤ_(2)[U] / (f)` and its quotients +`O / (2 ^ n)` (the children of a call live over `O / (2 ^ {p_t})`, `TensorNetwork.callSplitting`), +while the native algorithm computes in `A_n = (ℤ / 2 ^ n)[U] / (f)`, the Galois ring +`GaloisRing 2 n f`. Reduction `O → A_n` (`AdjoinRoot.reduction`) factors through `O / (2 ^ n)`; +this file gives the map back, `reductionLift : A_n → O / (2 ^ n)`, with +`reductionLift ∘ reduction = mk` (`reductionLift_comp_reduction`). It sends `U` to the +class of `U` and is the unique ring map from `ℤ / 2 ^ n` on scalars. So a value computed natively +in `A_n` and mapped along `reductionLift` is the class of any element of `O` reducing to it: the +canonical lift of a coefficient list (`reductionLift_reduction`). + +## Main definitions + +* `Cslib.Circuits.Frontier.reductionLift`: the map `A_n → O / (2 ^ n)`. + +## Main results + +* `Cslib.Circuits.Frontier.reductionLift_comp_reduction`: `reductionLift ∘ reduction = mk`. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.1, p. 57. +-/ + +public section + +open Polynomial AdjoinRoot + +namespace Cslib.Circuits.Frontier + +variable {f : ℤ[X]} (hf : f.Monic) (hirr : Irreducible (f.map (Int.castRingHom (ZMod 2)))) + (n : ℕ) + +/-- `2 ^ n = 0` in `O / (2 ^ n)`. -/ +private theorem two_pow_eq_zero_quotient : (2 : ((padicValuation 2 hf hirr).integer ⧸ + Ideal.span {(2 : (padicValuation 2 hf hirr).integer) ^ n})) ^ n = 0 := by + have : Ideal.Quotient.mk (Ideal.span {(2 : (padicValuation 2 hf hirr).integer) ^ n}) + ((2 : (padicValuation 2 hf hirr).integer) ^ n) = 0 := + Ideal.Quotient.eq_zero_iff_mem.mpr (Ideal.mem_span_singleton_self _) + rwa [map_pow, map_ofNat] at this + +/-- The scalars `ℤ / 2 ^ n → O / (2 ^ n)`. -/ +private noncomputable def scalarLift : ZMod (2 ^ n) →+* ((padicValuation 2 hf hirr).integer ⧸ + Ideal.span {(2 : (padicValuation 2 hf hirr).integer) ^ n}) := + (Ideal.Quotient.lift _ (Int.castRingHom _) fun a ha => by + obtain ⟨c, rfl⟩ := Ideal.mem_span_singleton.mp ha + rw [map_mul, eq_intCast, Int.cast_natCast, Nat.cast_pow, Nat.cast_ofNat, + two_pow_eq_zero_quotient hf hirr n, zero_mul]).comp + (Int.quotientSpanNatEquivZMod (2 ^ n)).symm.toRingHom + +/-- The class of `U` in `O / (2 ^ n)`. -/ +private noncomputable def rootClass : ((padicValuation 2 hf hirr).integer ⧸ + Ideal.span {(2 : (padicValuation 2 hf hirr).integer) ^ n}) := + Ideal.Quotient.mk _ (integerEquiv hf hirr (root _)) + +private theorem eval₂_rootClass : + (f.map (Int.castRingHom (ZMod (2 ^ n)))).eval₂ (scalarLift hf hirr n) (rootClass hf hirr n) = + 0 := by + rw [eval₂_map] + have hcomp : (scalarLift hf hirr n).comp (Int.castRingHom (ZMod (2 ^ n))) = + ((Ideal.Quotient.mk _).comp (integerEquiv hf hirr).toRingHom).comp + ((of _).comp (Int.castRingHom (Rat.padicValuation 2).integer)) := + RingHom.ext_int _ _ + rw [hcomp, rootClass, show Ideal.Quotient.mk _ (integerEquiv hf hirr (root _)) = + ((Ideal.Quotient.mk _).comp (integerEquiv hf hirr).toRingHom) (root _) from rfl, + ← Polynomial.hom_eval₂, ← eval₂_map, eval₂_root, map_zero] + +/-- **The Galois ring maps into `O / (2 ^ n)`**: `ℤ / 2 ^ n` by its unique ring map, and `U` to +its class. -/ +noncomputable def reductionLift : GaloisRing 2 n f →+* ((padicValuation 2 hf hirr).integer ⧸ + Ideal.span {(2 : (padicValuation 2 hf hirr).integer) ^ n}) := + lift (scalarLift hf hirr n) (rootClass hf hirr n) (eval₂_rootClass hf hirr n) + +/-- **`reductionLift` undoes reduction modulo `2 ^ n`**: reducing an element of `O` to `A_n` and +mapping back gives its class in `O / (2 ^ n)`. -/ +theorem reductionLift_comp_reduction : + (reductionLift hf hirr n).comp (reduction hf hirr n) = Ideal.Quotient.mk _ := by + -- `U` of `O` is the class of `U` of `ℤ[X] / (f)`. + have hroot : integerEquiv hf hirr (root _) = ⟨map (Int.castRingHom ℚ) f _ dvd_rfl (root f), + (Valuation.mem_integer_iff _ _).mpr (padicValuation_map_le_one hf hirr _)⟩ := by + apply Subtype.ext + change (integerEquiv hf hirr (root _) : AdjoinRoot (f.map (Int.castRingHom ℚ))) = + map (Int.castRingHom ℚ) f _ dvd_rfl (root f) + rw [map_root, coe_integerEquiv] + conv_lhs => rw [← mk_X] + rw [integerMap_mk, Polynomial.map_X, mk_X] + have key : (reductionLift hf hirr n).comp (integerReduction 2 f n) = + (Ideal.Quotient.mk _).comp (integerEquiv hf hirr).toRingHom := by + refine ringHom_ext ?_ ?_ + · exact padicInteger_ringHom_ext + (two_pow_eq_zero_quotient hf hirr n) _ _ + · rw [RingHom.comp_apply, ← reduction_integerEquiv hf hirr, hroot, reduction_toRat hf hirr, + map_root, + reductionLift, lift_root, rootClass] + rfl + ext x + obtain ⟨y, rfl⟩ := (integerEquiv hf hirr).surjective x + rw [RingHom.comp_apply, reduction_integerEquiv, ← RingHom.comp_apply, key] + rfl + +/-- `reductionLift (reduction x)` is the class of `x`. -/ +theorem reductionLift_reduction (x : (padicValuation 2 hf hirr).integer) : + reductionLift hf hirr n (reduction hf hirr n x) = Ideal.Quotient.mk _ x := + RingHom.congr_fun (reductionLift_comp_reduction hf hirr n) x + +end Cslib.Circuits.Frontier diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/VertexDesc.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/VertexDesc.lean new file mode 100644 index 0000000000..cfb4546d15 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/VertexDesc.lean @@ -0,0 +1,344 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketTerms +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Sum +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Sum +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Degree.Bounds +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.BitString +import Complexity.Computability.Machines.Turing.MultiTape.Internal.DegreeOptionPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListBinaryLookup +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListPrimitives +import Complexity.Foundations.Data.BitString.Binary +import Mathlib.Data.List.GetD + +/-! +# Descriptors of the vertices of the recursion's networks + +The networks of the contraction recursion of [OpenAI2026ETH] (§D.4.6–§D.4.7, pp. 63–64) have two +kinds of vertices. The vertices of the input network keep their tables, and they survive into +every child of their group with the same ports. The packet vertices created at a call are +described by "its packet data, the tuple `β`, the interpolation value, and the creation +precision" (p. 63), and from then on they also survive into the children of their group. A +vertex descriptor (`VertexDesc`) is therefore a table or a packet descriptor +(`NativePacket.PacketDesc`), and a retained vertex keeps its descriptor. + +`VertexDesc.entry` evaluates a descriptor at the bits on its ports: the table entry, as a constant +coefficient list, or the native packet entry at the packet's creation precision. A network of the +recursion reads the entries at its own precision `(p, fl)` (`GaloisRingArith.val p fl`), so a +retained packet vertex created at a lower precision is read at the higher one: its coefficients +are those of the same element of `O = ℤ_(2)[U] / (f)`. One native machine evaluates every +descriptor (`VertexDesc.entryCertificate`), with the packet evaluator's bounds: time +`K (m + 2) ^ L Z ^ K` and space `K Z ^ K`, for `m` target modes and `L` row ports of a packet (none +for a table) and `Z` polynomial in the input and the reduced coefficient lists +(`VertexDesc.entryCertificate_le`). It is the evaluator of direct summation over networks of +descriptors at coefficient lists (`DescribedNetwork.contractionCertificate` at `SumOps.galois`). + +## Main definitions + +* `Cslib.Circuits.Frontier.VertexDesc`: a table or a packet descriptor. +* `Cslib.Circuits.Frontier.VertexDesc.entry`: its entry at the bits on its ports. +* `Cslib.Circuits.Frontier.VertexDesc.enc`: its encoding. +* `Cslib.Circuits.Frontier.VertexDesc.entryCertificate`: the native evaluator. + +## Main results + +* `Cslib.Circuits.Frontier.VertexDesc.val_entry_inl`: a table entry is read as a constant. +* `Cslib.Circuits.Frontier.VertexDesc.map_val_entry_inr`: a packet entry is the packet entry in + every ring receiving `A_p` with `2` nilpotent. +* `Cslib.Circuits.Frontier.VertexDesc.entryCertificate_le`: the time and space of the evaluator. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.6–§D.4.7, pp. 63–64. +-/ + +public section + +namespace Cslib.Circuits.Frontier + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +open GaloisRingArith NativePacket TensorNetwork Turing.MultiTapeTM Cslib.GenericRAM + +/-- **The descriptor of a vertex** of a network of the contraction recursion: the table of a +vertex of the input network (`inl`), or the descriptor of a packet vertex (`inr`). -/ +abbrev VertexDesc : Type := List ℕ ⊕ PacketDesc + +namespace VertexDesc + +/-- **The entry of a vertex at the bits on its ports**: the table entry, as a constant coefficient +list, or the native packet entry at the packet's creation precision. -/ +@[expose] def entry : VertexDesc → List Bool → List ℕ + | .inl table, bs => [ExplicitTensorNetwork.entry table bs] + | .inr d, bs => packetVertexEntry d bs + +/-- The encoding of a descriptor: a tag, then the table in binary or the packet descriptor. -/ +abbrev enc : VertexDesc ↪ List Bool := encSum (encList encNat) encPacketDesc + +/-- The number of target modes of a descriptor: those of its packet, and none for a table. -/ +@[expose] def modes : VertexDesc → ℕ + | .inl _ => 0 + | .inr d => (targetModes d.1).length + +/-- The number of row ports of a descriptor at the bits on its ports: those of its packet, and +none for a table. -/ +@[expose] def rowPorts : VertexDesc → List Bool → ℕ + | .inl _, _ => 0 + | .inr d, bs => (sidePorts d.2.1 bs true).length + +/-- The reduced coefficient lists of the creation precision of a descriptor are at most `Rv` bits +long; no condition for a table. -/ +@[expose] def ReducedLE (Rv : ℕ) : VertexDesc → Prop + | .inl _ => True + | .inr d => ∀ y, SumOps.galois.Reduced (d.1.p, d.1.fl) y → (encList encNat y).length ≤ Rv + +/-! ### Values -/ + +@[simp] theorem entry_inl (table : List ℕ) (bs : List Bool) : + entry (.inl table) bs = [ExplicitTensorNetwork.entry table bs] := rfl + +@[simp] theorem entry_inr (d : PacketDesc) (bs : List Bool) : + entry (.inr d) bs = packetVertexEntry d bs := rfl + +/-- **A table entry is read as a constant** at every precision. -/ +theorem val_entry_inl (p : ℕ) (fl table : List ℕ) (bs : List Bool) : + val p fl (entry (.inl table) bs) = + ((ExplicitTensorNetwork.entry table bs : ℕ) : Ring p fl) := by + obtain h | ⟨e, he⟩ : fl.length = 0 ∨ ∃ e, fl.length = e + 1 := + (Nat.eq_zero_or_pos _).imp id fun h => ⟨_, (Nat.succ_pred_eq_of_pos h).symm⟩ + · have := subsingleton_of_length_eq_zero (p := p) h + exact Subsingleton.elim _ _ + · rw [entry_inl, val, he, Finset.sum_range_succ', + Finset.sum_eq_zero fun i _ => by rw [List.getD_cons_succ, List.getD_nil]; simp] + simp + +/-- **A packet entry in every ring receiving `A_p`.** Along a ring map `ψ` from `A_p`, at the +precision `p` of the packet, to a ring in which `2` is nilpotent, the entry of a packet descriptor +at the bits on its ports is `packetEntry` at any ring map `κ : ℤ_(2) → S`, the image of the +interpolation value, the dense exponents, and the row and column ports read from the bits. -/ +theorem map_val_entry_inr {S : Type*} [CommRing S] (d : PacketDesc) (bs : List Bool) + (ψ : Ring d.1.p d.1.fl →+* S) {n : ℕ} (hn : (2 : S) ^ n = 0) (κ : ℤ₍₂₎ →+* S) + (hL : d.1.L = 2 ^ d.1.a) (hia : d.1.i < d.1.a) (htiles : d.1.tiles = 2 ^ (d.1.i + 1)) + (ht : d.1.t ∈ cauchyGrid (2 ^ d.1.a)) (hti : (2 : ℤ) ^ d.1.i ∣ d.1.t) + (hti' : ¬ (2 : ℤ) ^ (d.1.i + 1) ∣ d.1.t) (hports : ∀ x ∈ d.2.1, x.2 < 2 ^ d.1.a) : + ψ (val d.1.p d.1.fl (entry (.inr d) bs)) = + packetEntry (p := d.1.nb) κ (ψ (val d.1.p d.1.fl d.1.ζ)) d.1.a d.1.i d.1.t d.1.e₁ d.1.e₂ + d.1.differ (fun Z => d.2.2.getD (Z.1 * d.1.nb + Z.2) 0) (sidePorts d.2.1 bs true) + (sidePorts d.2.1 bs false) := by + have hside (side : Bool) : ∀ r ∈ sidePorts d.2.1 bs side, r.1 < 2 ^ d.1.a := by + intro r hr + obtain ⟨j, hj, rfl⟩ := List.mem_map.mp hr + have hj' := List.mem_range.mp (List.mem_filter.mp hj).1 + exact hports _ (by rw [List.getD_eq_getElem _ _ hj']; exact List.getElem_mem _) + exact map_val_packetEntryNative { d.1 with cols := sidePorts d.2.1 bs false } ψ hn κ hL hia + htiles ht hti hti' (hside true) (hside false) d.2.2 + +/-- A packet entry is a reduced coefficient list at the packet's precision. -/ +theorem reduced_entry_inr (d : PacketDesc) (bs : List Bool) : + SumOps.galois.Reduced (d.1.p, d.1.fl) (entry (.inr d) bs) := + SumOps.galois.reduced_mul (d.1.p, d.1.fl) _ _ + +/-! ### The native evaluator -/ + +/-- The right summand of a descriptor and its bits. -/ +theorem getRight_polyTime : + ComputableInPolyTime (fun q : VertexDesc × List Bool => q.1.getRight?) (encPair enc encBits) + (encOption encPacketDesc) := + (ComputableInO.getRight?_deg _ _).computableInPolyTime.comp (ComputableInPolyTime.fst _ _) + +/-- The table entry of a descriptor at its bits, the empty table standing for a packet, as a +singleton list. -/ +theorem tableEntry_polyTime : + ComputableInPolyTime (fun q : VertexDesc × List Bool => + [ExplicitTensorNetwork.entry (q.1.getLeft?.getD []) q.2]) (encPair enc encBits) + (encList encNat) := by + have hleft : ComputableInPolyTime (fun q : VertexDesc × List Bool => q.1.getLeft?) + (encPair enc encBits) (encOption (encList encNat)) := + (ComputableInO.getLeft?_deg _ _).computableInPolyTime.comp (ComputableInPolyTime.fst _ _) + have htable : ComputableInPolyTime (fun q : VertexDesc × List Bool => q.1.getLeft?.getD []) + (encPair enc encBits) (encList encNat) := + (ComputableInDegree.getD (enc₁ := encList encNat) (enc := encOption (encList encNat)) + []).computableInPolyTime.comp hleft + have hindex := ComputableInPolyTime.bitsBinaryValue.comp + (ComputableInPolyTime.bitsReverse.comp (ComputableInPolyTime.snd enc encBits)) + have hentry := ComputableInPolyTime.listGetD_binary (encA := encNat) (encL := encList encNat) 0 + htable hindex + exact (ComputableInPolyTime.cons' hentry (ComputableInPolyTime.const [])).of_enc_eq fun q => by + simp only [ExplicitTensorNetwork.entry, BitString.lsbValue_eq_binaryValue_reverse, + Function.comp_apply] + +/-- The input of the packet evaluator from a descriptor's bits and its packet. -/ +theorem packetInput_polyTime : + ComputableInPolyTime (fun p : (VertexDesc × List Bool) × PacketDesc => (p.2, p.1.2)) + (encPair (encPair enc encBits) encPacketDesc) (encPair encPacketDesc encBits) := + (ComputableInPolyTime.snd _ _).prod + ((ComputableInPolyTime.snd enc encBits).comp (ComputableInPolyTime.fst _ _)) + +/-- **One native machine for the entries of all descriptors**: the table lookup for a table, and +the native packet evaluator (`NativePacket.vertexCertificate`) for a packet. -/ +noncomputable def entryCertificate : + NativeCertificate (fun q : VertexDesc × List Bool => entry q.1 q.2) (encPair enc encBits) + (encList encNat) := + (NativeCertificate.option (value := fun q : VertexDesc × List Bool => q.1.getRight?) + (fallback := fun q => [ExplicitTensorNetwork.entry (q.1.getLeft?.getD []) q.2]) + (body := fun p => packetVertexEntry p.2 p.1.2) + (NativeCertificate.ofPolyTime getRight_polyTime) + (NativeCertificate.ofPolyTime tableEntry_polyTime) + ((vertexCertificate.comp (NativeCertificate.ofPolyTime packetInput_polyTime)).congr + fun _ => rfl)).congr fun q => by + rcases q with ⟨d | d, bs⟩ <;> rfl + +/-- `c (y + 1) ^ d ≤ c Z ^ D` when `y + 1 ≤ Z` and `d ≤ D`. -/ +private theorem mul_pow_le_mul_pow {c y d Z D : ℕ} (hy : y + 1 ≤ Z) (hd : d ≤ D) : + c * (y + 1) ^ d ≤ c * Z ^ D := + Nat.mul_le_mul_left _ ((Nat.pow_le_pow_left hy _).trans + (Nat.pow_le_pow_right (by omega) hd)) + +/-- **The resources of the native evaluator of descriptors.** There is `K` such that on a +descriptor and bits of total length `n`, with `m` target modes and `L` row ports (`0` and `0` for a +table), if the reduced coefficient lists of a packet's precision are at most `Rv` bits long, then +with `Z = n + m + Rv + 2` the time is at most `K (m + 2) ^ L Z ^ K` and the space at most +`K Z ^ K`. -/ +theorem entryCertificate_le : + ∃ K : ℕ, ∀ (q : VertexDesc × List Bool) (Rv : ℕ), q.1.ReducedLE Rv → + entryCertificate.time q ≤ K * (q.1.modes + 2) ^ q.1.rowPorts q.2 * + ((encPair enc encBits q).length + q.1.modes + Rv + 2) ^ K ∧ + entryCertificate.space q ≤ + K * ((encPair enc encBits q).length + q.1.modes + Rv + 2) ^ K := by + obtain ⟨K₀, hK₀⟩ := vertexCertificate_le + have hR := NativeCertificate.ofPolyTime_isPolynomial getRight_polyTime + have hT := NativeCertificate.ofPolyTime_isPolynomial tableEntry_polyTime + have hI := NativeCertificate.ofPolyTime_isPolynomial packetInput_polyTime + obtain ⟨cr, dr, hrt⟩ := hR.1.exists_le + obtain ⟨cr', dr', hrs⟩ := hR.2.exists_le + obtain ⟨ct, dt, htt⟩ := hT.1.exists_le + obtain ⟨ct', dt', hts⟩ := hT.2.exists_le + obtain ⟨ci, di, hit⟩ := hI.1.exists_le + obtain ⟨ci', di', his⟩ := hI.2.exists_le + obtain ⟨D, hD⟩ : ∃ D, D = K₀ + dr + dr' + dt + dt' + di + di' + 1 := ⟨_, rfl⟩ + obtain ⟨G, hG⟩ : ∃ G, G = K₀ + 2 * cr + 2 * cr' + ct + ct' + ci * 3 ^ di + ci' * 3 ^ di' + 1 := + ⟨_, rfl⟩ + refine ⟨G + D, fun q Rv hRv => ?_⟩ + obtain ⟨n, hn⟩ : ∃ n, n = (encPair enc encBits q).length := ⟨_, rfl⟩ + obtain ⟨Z, hZ⟩ : ∃ Z, Z = n + q.1.modes + Rv + 2 := ⟨_, rfl⟩ + obtain ⟨P, hP⟩ : ∃ P, P = (q.1.modes + 2) ^ q.1.rowPorts q.2 := ⟨_, rfl⟩ + rw [← hn, ← hZ, ← hP] + have hP1 : 1 ≤ P := hP ▸ Nat.one_le_pow _ _ (by omega) + have hnZ : n + 1 ≤ Z := by omega + have hZD : Z ^ D ≤ Z ^ (G + D) := Nat.pow_le_pow_right (by omega) (by omega) + have hnD : n ≤ Z ^ D := by + calc n ≤ Z := by omega + _ = Z ^ 1 := (pow_one Z).symm + _ ≤ Z ^ D := Nat.pow_le_pow_right (by omega) (by omega) + have hrt' : (NativeCertificate.ofPolyTime getRight_polyTime).time q ≤ cr * Z ^ D := + (hrt q).trans (hn ▸ mul_pow_le_mul_pow hnZ (by omega)) + have hrs' : (NativeCertificate.ofPolyTime getRight_polyTime).space q ≤ cr' * Z ^ D := + (hrs q).trans (hn ▸ mul_pow_le_mul_pow hnZ (by omega)) + rcases q with ⟨d | d, bs⟩ + · -- A table: the lookup alone. + have htt' : (NativeCertificate.ofPolyTime tableEntry_polyTime).time (.inl d, bs) ≤ + ct * Z ^ D := (htt _).trans (hn ▸ mul_pow_le_mul_pow hnZ (by omega)) + have hts' : (NativeCertificate.ofPolyTime tableEntry_polyTime).space (.inl d, bs) ≤ + ct' * Z ^ D := (hts _).trans (hn ▸ mul_pow_le_mul_pow hnZ (by omega)) + have htime : entryCertificate.time (.inl d, bs) = + 2 * (NativeCertificate.ofPolyTime getRight_polyTime).time (.inl d, bs) + n + + (NativeCertificate.ofPolyTime tableEntry_polyTime).time (.inl d, bs) := by + rw [hn]; rfl + have hspace : entryCertificate.space (.inl d, bs) = + 2 * (NativeCertificate.ofPolyTime getRight_polyTime).space (.inl d, bs) + n + + (NativeCertificate.ofPolyTime tableEntry_polyTime).space (.inl d, bs) := by + rw [hn]; rfl + constructor + · rw [htime] + calc _ ≤ G * Z ^ D := by + have : (2 * cr + 1 + ct) * Z ^ D ≤ G * Z ^ D := Nat.mul_le_mul_right _ (by omega) + have e : (2 * cr + 1 + ct) * Z ^ D = 2 * (cr * Z ^ D) + Z ^ D + ct * Z ^ D := by ring + omega + _ ≤ (G + D) * P * Z ^ (G + D) := + Nat.mul_le_mul ((Nat.le_add_right G D).trans (Nat.le_mul_of_pos_right _ hP1)) hZD + · rw [hspace] + calc _ ≤ G * Z ^ D := by + have : (2 * cr' + 1 + ct') * Z ^ D ≤ G * Z ^ D := Nat.mul_le_mul_right _ (by omega) + have e : (2 * cr' + 1 + ct') * Z ^ D = 2 * (cr' * Z ^ D) + Z ^ D + ct' * Z ^ D := by + ring + omega + _ ≤ (G + D) * Z ^ (G + D) := Nat.mul_le_mul (by omega) hZD + · -- A packet: the input of the packet evaluator, then the evaluator. + have hn' : n = 2 * ((encPacketDesc d).length + 1) + 1 + (encBits bs).length := by + rw [hn]; exact length_encPair _ + have hlen : (encPair encPacketDesc encBits (d, bs)).length ≤ n := by + have h3 : (encPair encPacketDesc encBits (d, bs)).length = + 2 * (encPacketDesc d).length + 1 + (encBits bs).length := length_encPair _ + omega + have hlenI : (encPair (encPair enc encBits) encPacketDesc ((.inr d, bs), d)).length + 1 ≤ + 3 * Z := by + have h1 : (encPair (encPair enc encBits) encPacketDesc ((.inr d, bs), d)).length = + 2 * (encPair enc encBits (.inr d, bs)).length + 1 + (encPacketDesc d).length := + length_encPair _ + omega + have hpow3 {c e : ℕ} (he : e ≤ D) : c * (3 * Z) ^ e ≤ c * 3 ^ e * Z ^ D := by + rw [Nat.mul_pow, ← Nat.mul_assoc] + exact Nat.mul_le_mul_left _ (Nat.pow_le_pow_right (by omega) he) + have hit' : (NativeCertificate.ofPolyTime packetInput_polyTime).time ((.inr d, bs), d) ≤ + ci * 3 ^ di * Z ^ D := + (hit _).trans ((Nat.mul_le_mul_left _ (Nat.pow_le_pow_left hlenI _)).trans + (hpow3 (by omega))) + have his' : (NativeCertificate.ofPolyTime packetInput_polyTime).space ((.inr d, bs), d) ≤ + ci' * 3 ^ di' * Z ^ D := + (his _).trans ((Nat.mul_le_mul_left _ (Nat.pow_le_pow_left hlenI _)).trans + (hpow3 (by omega))) + obtain ⟨hvt, hvs⟩ := hK₀ (d, bs) Rv hRv + have hZ' : (encPair encPacketDesc encBits (d, bs)).length + (targetModes d.1).length + Rv + 2 + ≤ Z := by rw [hZ]; simp only [modes]; omega + have hpowK : ((encPair encPacketDesc encBits (d, bs)).length + (targetModes d.1).length + Rv + + 2) ^ K₀ ≤ Z ^ D := + (Nat.pow_le_pow_left hZ' _).trans (Nat.pow_le_pow_right (by omega) (by omega)) + have hvt' : vertexCertificate.time (d, bs) ≤ K₀ * P * Z ^ D := by + refine hvt.trans ?_ + rw [hP] + exact Nat.mul_le_mul_left _ hpowK + have hvs' : vertexCertificate.space (d, bs) ≤ K₀ * Z ^ D := + hvs.trans (Nat.mul_le_mul_left _ hpowK) + have htime : entryCertificate.time (.inr d, bs) = + 2 * (NativeCertificate.ofPolyTime getRight_polyTime).time (.inr d, bs) + n + + ((NativeCertificate.ofPolyTime packetInput_polyTime).time ((.inr d, bs), d) + + vertexCertificate.time (d, bs)) := by + rw [hn]; rfl + have hspace : entryCertificate.space (.inr d, bs) = + 2 * (NativeCertificate.ofPolyTime getRight_polyTime).space (.inr d, bs) + n + + ((NativeCertificate.ofPolyTime packetInput_polyTime).space ((.inr d, bs), d) + + vertexCertificate.space (d, bs)) := by + rw [hn]; rfl + constructor + · rw [htime] + have h1 : 2 * (cr * Z ^ D) + Z ^ D + ci * 3 ^ di * Z ^ D ≤ + P * ((2 * cr + 1 + ci * 3 ^ di) * Z ^ D) := by + calc _ = (2 * cr + 1 + ci * 3 ^ di) * Z ^ D := by ring + _ ≤ _ := Nat.le_mul_of_pos_left _ hP1 + have h2 : P * ((2 * cr + 1 + ci * 3 ^ di) * Z ^ D) + K₀ * P * Z ^ D ≤ P * (G * Z ^ D) := by + rw [show K₀ * P * Z ^ D = P * (K₀ * Z ^ D) by ring, ← Nat.mul_add, ← Nat.add_mul] + exact Nat.mul_le_mul_left _ (Nat.mul_le_mul_right _ (by omega)) + calc _ ≤ P * (G * Z ^ D) := by omega + _ ≤ (G + D) * P * Z ^ (G + D) := by + rw [show (G + D) * P * Z ^ (G + D) = P * ((G + D) * Z ^ (G + D)) by ring] + exact Nat.mul_le_mul_left _ (Nat.mul_le_mul (by omega) hZD) + · rw [hspace] + calc _ ≤ G * Z ^ D := by + have : (2 * cr' + 1 + ci' * 3 ^ di' + K₀) * Z ^ D ≤ G * Z ^ D := + Nat.mul_le_mul_right _ (by omega) + have e : (2 * cr' + 1 + ci' * 3 ^ di' + K₀) * Z ^ D = + 2 * (cr' * Z ^ D) + Z ^ D + ci' * 3 ^ di' * Z ^ D + K₀ * Z ^ D := by ring + omega + _ ≤ (G + D) * Z ^ (G + D) := Nat.mul_le_mul (by omega) hZD + +end VertexDesc + +end Cslib.Circuits.Frontier diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkVertexDesc.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkVertexDesc.lean new file mode 100644 index 0000000000..e3be365541 --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkVertexDesc.lean @@ -0,0 +1,95 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: local-only (test driver) + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildEntries +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ReductionLift + +/-! +# Tests for vertex descriptors and the entries of child networks + +A table descriptor reads its table little-endian and is a constant in `A_p`. A packet descriptor +over the tiny packet of the packet-evaluator tests (`2 ^ 1` slots over +`A_3 = (ℤ / 8)[U] / (U ^ 2 + U + 1)`, `t = 1`, `ζ = U`) with one row port in slot `0` and one +column port in slot `1` evaluates to the kernel value `5 + 2 U` at zero exponents, whatever the +column ports of its context, since they are read from the tags. The ports of one side of a tagged +list come out in order. Reducing an element of `O` to `A_n` and mapping back along +`reductionLift` gives its class modulo `2 ^ n`. +-/ + +@[expose] public section + +namespace ComplexityTests.TensorNetworkVertexDesc + +open Cslib Cslib.Circuits.Frontier Cslib.Circuits.Frontier.NativePacket + +/-! ### Tables -/ + +-- The bits `[true, false]` index the table at `1`. +example : VertexDesc.entry (.inl [3, 5, 7, 9]) [true, false] = [5] := by decide + +example : VertexDesc.entry (.inl [3, 5, 7, 9]) [false, true] = [7] := by decide + +-- A table has no modes and no row ports. +example : VertexDesc.modes (.inl [3, 5]) = 0 := rfl + +example : VertexDesc.rowPorts (.inl [3, 5]) [true] = 0 := rfl + +-- A table entry is a constant in `A_p`. +example : GaloisRingArith.val 3 [1, 1] (VertexDesc.entry (.inl [3, 5]) [true]) = + ((5 : ℕ) : GaloisRingArith.Ring 3 [1, 1]) := + VertexDesc.val_entry_inl 3 [1, 1] [3, 5] [true] + +/-! ### Packets -/ + +/-- The context of the tiny packet, with no column ports of its own. -/ +def ctx : PacketCtx := + ⟨3, [1, 1], 1, 2, 0, 2, 1, 2, true, false, true, [], [0, 1]⟩ + +/-- The descriptor: a row port in slot `0` and a column port in slot `1`, zero exponents. -/ +def desc : VertexDesc := .inr (ctx, [(true, 0), (false, 1)], [0, 0, 0, 0]) + +-- The column ports are read from the tags and the bits: the entry is `W_1(T) = 5 + 2 U`. +example : VertexDesc.entry desc [true, true] = [5, 2] := by decide + +example : VertexDesc.rowPorts desc [true, true] = 1 := by decide + +/-! ### Tagged ports -/ + +example : sidePorts [(true, 3), (false, 1), (true, 2)] [true, false, false] true = + [(3, true), (2, false)] := by decide + +example : sidePorts [(true, 3), (false, 1), (true, 2)] [true, false, false] false = + [(1, false)] := by decide + +-- The same, through `sidePorts_map` on a list tagged by parity. +example : sidePorts ([4, 1, 2].map fun c => (decide (c % 2 = 0), c)) ([4, 1, 2].map fun c => c < 3) + true = [(4, false), (2, true)] := by + rw [sidePorts_map] + decide + +/-! ### Resources -/ + +example : ∃ K : ℕ, ∀ (q : VertexDesc × List Bool) (Rv : ℕ), q.1.ReducedLE Rv → + VertexDesc.entryCertificate.time q ≤ K * (q.1.modes + 2) ^ q.1.rowPorts q.2 * + ((Turing.MultiTapeTM.encPair VertexDesc.enc Turing.MultiTapeTM.encBits q).length + + q.1.modes + Rv + 2) ^ K ∧ + VertexDesc.entryCertificate.space q ≤ + K * ((Turing.MultiTapeTM.encPair VertexDesc.enc Turing.MultiTapeTM.encBits q).length + + q.1.modes + Rv + 2) ^ K := + VertexDesc.entryCertificate_le + +/-! ### Back from `A_n` to `O / (2 ^ n)` -/ + +example {f : Polynomial ℤ} (hf : f.Monic) + (hirr : Irreducible (f.map (Int.castRingHom (ZMod 2)))) (n : ℕ) + (x : (AdjoinRoot.padicValuation 2 hf hirr).integer) : + reductionLift hf hirr n (AdjoinRoot.reduction hf hirr n x) = Ideal.Quotient.mk _ x := + reductionLift_reduction hf hirr n x + +end ComplexityTests.TensorNetworkVertexDesc From 0da975a467ca8f6cb486d725f473a37697a1ed41 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 01:54:27 -0400 Subject: [PATCH 063/129] chore: regenerate Complexity.lean after merges Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Complexity.lean b/Complexity.lean index cee98a3e56..649c3ae75a 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -883,12 +883,11 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ActiveCall public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Binarize public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CauchyModes +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildCursor public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildEntries public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildNumbering -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildCursor public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildSize public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CoeffScheme -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildSize public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DescribedContraction @@ -902,17 +901,14 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeReco public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEntry public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEvaluator public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketTerms -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ReductionLift -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.LocalDeformation -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeRecovery public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Recursion +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ReductionLift public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Reindex public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Schedule +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ScheduleCount public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ScheduledRecursion public import Complexity.Computability.Circuit.Frontier.TensorNetwork.TaperedRecovery public import Complexity.Computability.Circuit.Frontier.TensorNetwork.VertexDesc -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ScheduleCount public import Complexity.Computability.Circuit.Frontier.TwoSidedWitness public import Complexity.Computability.Circuit.Frontier.Unconditional public import Complexity.Computability.Circuit.Frontier.Witness From ef0982eab5f5b4afe84ca2cc4a5cc4522b68df7d Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 01:59:27 -0400 Subject: [PATCH 064/129] refactor(circuit/frontier): share the port/descriptor record between explicit and described networks `DescribedNetwork T` moves to `TensorNetwork/Explicit.lean` with `equivProd`, `wordBits`, `contraction` and `map_contraction`, and `ExplicitTensorNetwork R` becomes the abbreviation `DescribedNetwork (List R)`: an explicit network is a described network whose descriptors are tables. `DescribedNetwork.ofExplicit` is removed (it is the identity), and `contraction_ofExplicit` becomes `DescribedNetwork.contraction_entry`, for any commutative semiring. `DirectContraction` uses the shared `DescribedNetwork.equivProd` in place of its own copy. Addresses reviewer-codex-edemaine's request on #1928. Written by worker-claude-e6cf7b0d-eval-fixer, taking over from worker-claude-e6cf7b0d-t2-evaluator. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/DescribedContraction.lean | 63 +---------- .../TensorNetwork/DirectContraction.lean | 14 +-- .../Frontier/TensorNetwork/Explicit.lean | 106 +++++++++++++++--- ComplexityTests/DescribedContraction.lean | 4 +- 4 files changed, 96 insertions(+), 91 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean index 9a511b6bca..cbf09baa78 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean @@ -43,7 +43,7 @@ packet vertex has up to `2 L` ports, so its table would be superpolynomial, and computed from a description instead (Appendix D.4.6, p. 63). A *described network* (`DescribedNetwork T`) has, for each vertex, its ports and a descriptor `t : T` of its tensor, whose entry at the bits `bs` on the ports is `eval t bs` for an evaluator `eval`. Explicit -networks are the case of tables (`DescribedNetwork.ofExplicit`). +networks are the case of tables (`ExplicitTensorNetwork`, `DescribedNetwork.contraction_entry`). **Values and their operations** (`SumOps`). The entries are values `Y` combined by ring operations that depend on a precision `q : Q`: a zero, a one, a sum and a product, whose results @@ -73,8 +73,6 @@ contraction algorithm, with implicit packet tensors as descriptors. ## Main definitions -* `Cslib.Circuits.Frontier.DescribedNetwork`: a network with described tensors. -* `Cslib.Circuits.Frontier.DescribedNetwork.contraction`: its contraction. * `Cslib.Circuits.Frontier.SumOps`: ring operations on values, with `SumOps.IsHom` and `SumOps.PolyTime`; the instances `SumOps.mod` and `SumOps.galois`. * `Cslib.Circuits.Frontier.DescribedNetwork.sumScheme`: direct summation as a recursion scheme. @@ -91,7 +89,6 @@ contraction algorithm, with implicit packet tensors as descriptors. `sumValue`, with `2 ^ m (k + 1) + 1` nodes and two open calls. * `Cslib.Circuits.Frontier.SumOps.IsHom.map_sumValue`: under a homomorphism, the value is the contraction; `DescribedNetwork.sumValue_mod` and `DescribedNetwork.val_sumValue_galois`. -* `Cslib.Circuits.Frontier.DescribedNetwork.contraction_ofExplicit`: explicit networks. * `Cslib.Circuits.Frontier.DescribedNetwork.run_sumScheme`: the machine computes `sumValue`. * `Cslib.Circuits.Frontier.DescribedNetwork.contractionCertificate_le`: its time and space. @@ -217,61 +214,10 @@ theorem IsHom.map_foldl_add {q : Q} {R : Type*} [CommSemiring R] {φ : Y → R} end SumOps -/-- A Boolean tensor network with *described* tensors: the number of edges, numbered from zero, -and for each vertex its ordered list of ports and a descriptor of its tensor. -/ -structure DescribedNetwork (T : Type) where - /-- The number of edges; the edges are numbered `0, …, numEdges - 1`. -/ - numEdges : ℕ - /-- The vertices, each given by its ports and the descriptor of its tensor. -/ - vertices : List (List ℕ × T) - namespace DescribedNetwork variable {T : Type} -/-- The bits that a word, read as an assignment of bits to the edges, puts on a list of ports; -a port past the end of the word reads `false`. -/ -def wordBits (word : List Bool) (ports : List ℕ) : List Bool := - ports.map fun i => word.getD i false - -/-- The *contraction*: the sum over all assignments of bits to the edges of the product of the -entries of the vertices at the bits on their ports, for entries in a commutative semiring. -/ -def contraction {R : Type*} [CommSemiring R] (eval : T → List Bool → R) - (d : DescribedNetwork T) : R := - ∑ x : Fin d.numEdges → Bool, - (d.vertices.map fun v => eval v.2 (wordBits (List.ofFn x) v.1)).prod - -/-- An explicit network, with its tables as descriptors. -/ -def ofExplicit (d : ExplicitTensorNetwork ℕ) : DescribedNetwork (List ℕ) := - ⟨d.numEdges, d.vertices⟩ - -private theorem getD_ofFn {n : ℕ} (x : Fin n → Bool) (i : ℕ) : - (List.ofFn x).getD i false = if h : i < n then x ⟨i, h⟩ else false := by - by_cases h : i < n - · rw [dite_eq_left h, List.getD_eq_getElem _ _ (by simpa using h), List.getElem_ofFn] - · rw [dite_eq_right h, List.getD_eq_default _ _ (by simp only [List.length_ofFn]; omega)] - -/-- **Explicit networks**: with table lookup as the evaluator, the contraction is that of the -explicit network. -/ -theorem contraction_ofExplicit (d : ExplicitTensorNetwork ℕ) : - (ofExplicit d).contraction ExplicitTensorNetwork.entry = d.contraction := by - refine sum_congr rfl fun x _ => ?_ - simp only [wordBits, ExplicitTensorNetwork.portBits] - congr 1 - refine List.map_congr_left fun v _ => ?_ - congr 2 - funext i - exact getD_ofFn x i - -/-- The contraction commutes with maps of semirings. -/ -theorem map_contraction {R S : Type*} [CommSemiring R] [CommSemiring S] (ψ : R →+* S) - (eval : T → List Bool → R) (d : DescribedNetwork T) : - ψ (d.contraction eval) = d.contraction fun t bs => ψ (eval t bs) := by - rw [contraction, contraction, map_sum] - refine sum_congr rfl fun x _ => ?_ - rw [map_list_prod, List.map_map] - rfl - /-! ### Direct summation as a recursion scheme -/ /-- A node of direct summation: its kind (`0` the sum, `1` an assignment, `2` an entry), the @@ -538,13 +484,6 @@ section Native variable (encQ : Q ↪ List Bool) (encY : Y ↪ List Bool) (encT : T ↪ List Bool) -/-- A described network is its number of edges and its list of vertices. -/ -def equivProd : DescribedNetwork T ≃ ℕ × List (List ℕ × T) where - toFun d := (d.numEdges, d.vertices) - invFun p := ⟨p.1, p.2⟩ - left_inv _ := rfl - right_inv _ := rfl - /-- **The encoding of a described network**: its number of edges in unary, then its vertices, each the pair of its list of ports in binary and its descriptor. -/ def encDescribed : DescribedNetwork T ↪ List Bool := diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DirectContraction.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DirectContraction.lean index 954c85cb00..0ae9f6e8fa 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DirectContraction.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DirectContraction.lean @@ -136,17 +136,11 @@ def directContraction (x : ℕ × ℕ × ExplicitTensorNetwork ℕ) : ℕ := /-! ### Input encoding -/ -/-- An explicit network over `ℕ` is its number of edges and its list of vertices. -/ -def equivProd : ExplicitTensorNetwork ℕ ≃ ℕ × List (List ℕ × List ℕ) where - toFun d := (d.numEdges, d.vertices) - invFun p := ⟨p.1, p.2⟩ - left_inv _ := rfl - right_inv _ := rfl - /-- **The encoding of an explicit network**: its number of edges in unary, then its list of vertices, each the pair of its list of ports and its table, as lists of binary numbers. -/ def encExplicitNetwork : ExplicitTensorNetwork ℕ ↪ List Bool := - encOfEquiv equivProd (encPair encUnary (encList (encPair (encList encNat) (encList encNat)))) + encOfEquiv DescribedNetwork.equivProd + (encPair encUnary (encList (encPair (encList encNat) (encList encNat)))) /-- **The encoding of a contraction input `(P, r, d)`**: the precision `P` in unary, then the number `r` of row vertices in binary and the network (`encExplicitNetwork`). -/ @@ -216,7 +210,7 @@ private theorem vertices_polyTime : ComputableInPolyTime (fun d : ExplicitTensorNetwork ℕ => d.vertices) encExplicitNetwork encVertices := (ComputableInPolyTime.snd encUnary encVertices).comp - (ComputableInPolyTime.of_eq (f := equivProd) fun _ => rfl) + (ComputableInPolyTime.of_eq (f := DescribedNetwork.equivProd) fun _ => rfl) /-- `2 ^ P` in binary from `P` in unary. -/ private theorem twoPow_polyTime : @@ -331,7 +325,7 @@ theorem numEdges_polyTime : ComputableInPolyTime (fun x : ℕ × ℕ × ExplicitTensorNetwork ℕ => x.2.2.numEdges) encContractionInput encUnary := (ComputableInPolyTime.fst encUnary encVertices).comp - ((ComputableInPolyTime.of_eq (f := equivProd) fun _ => rfl).comp + ((ComputableInPolyTime.of_eq (f := DescribedNetwork.equivProd) fun _ => rfl).comp ((ComputableInPolyTime.snd encNat encExplicitNetwork).comp (ComputableInPolyTime.snd encUnary (encPair encNat encExplicitNetwork)))) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Explicit.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Explicit.lean index 42914f7f8f..313760ebfc 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Explicit.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Explicit.lean @@ -16,18 +16,24 @@ import Mathlib.Data.Set.Card /-! # Explicit Boolean tensor networks -An *explicit* Boolean tensor network (`ExplicitTensorNetwork R`) is the data an algorithm reads: -the number of edges, numbered `0, 1, …`, and a list of vertices, each given by - -* its ordered list of *ports*, the numbers of the edges at the vertex, and -* its *table*: the value of its tensor when port `i` carries the bit `bs[i]` is the entry at - position `BitString.lsbValue bs`, the number whose binary digit `i` is `bs[i]` - (`BitString.testBit_lsbValue`). +A Boolean tensor network as an algorithm reads it (`DescribedNetwork T`) is the number of edges, +numbered `0, 1, …`, and a list of vertices, each given by its ordered list of *ports*, the numbers +of the edges at the vertex, and a *descriptor* `t : T` of its tensor. For an evaluator +`eval : T → List Bool → R`, the entry of the vertex when port `i` carries the bit `bs[i]` is +`eval t bs`, and the contraction (`DescribedNetwork.contraction`) sums, over all assignments of +bits to the edges, the product of the entries at the bits on the ports. + +An *explicit* network (`ExplicitTensorNetwork R`) has tables as descriptors: the value of the +tensor of a vertex when port `i` carries the bit `bs[i]` is the entry of its table at position +`BitString.lsbValue bs`, the number whose binary digit `i` is `bs[i]` +(`BitString.testBit_lsbValue`). So a vertex with `k` ports has a table of `2 ^ k` entries. The contraction of an explicit network (`ExplicitTensorNetwork.contraction`) sums, over all assignments of bits to the edges, the product -of the table entries at the bits on the ports. It needs no well-formedness: a port naming no edge -reads `false`, and a position past the end of a table has entry zero. +of the table entries at the bits on the ports, which is the contraction of the described network +with table lookup as the evaluator (`DescribedNetwork.contraction_entry`). It needs no +well-formedness: a port naming no edge reads `false`, and a position past the end of a table has +entry zero. Its size measures are the number of edges, the number of vertices, the maximum number of ports of a vertex (`ExplicitTensorNetwork.maxDegree`) and the total length of the tables @@ -45,7 +51,12 @@ one end of every edge below `r` and the other at or above it gives a rows-first ## Main definitions -* `Cslib.Circuits.Frontier.ExplicitTensorNetwork`: an explicit Boolean tensor network. +* `Cslib.Circuits.Frontier.DescribedNetwork`: a Boolean tensor network given by the ports and + the descriptors of its vertices. +* `Cslib.Circuits.Frontier.DescribedNetwork.contraction`: its contraction, for an evaluator of + the descriptors. +* `Cslib.Circuits.Frontier.ExplicitTensorNetwork`: an explicit Boolean tensor network, with + tables as descriptors. * `Cslib.Circuits.Frontier.ExplicitTensorNetwork.entry`, `Cslib.Circuits.Frontier.ExplicitTensorNetwork.portBits`: table lookup and the bits on a list of ports. @@ -60,6 +71,10 @@ one end of every edge below `r` and the other at or above it gives a rows-first ## Main results +* `Cslib.Circuits.Frontier.DescribedNetwork.map_contraction`: the contraction commutes with maps + of semirings. +* `Cslib.Circuits.Frontier.DescribedNetwork.contraction_entry`: explicit networks are described + networks with table lookup as the evaluator. * `Cslib.Circuits.Frontier.ExplicitTensorNetwork.maxDegree_le_iff`: the degree bound. * `Cslib.Circuits.Frontier.TensorNetwork.entry_explicitTable`: a written-out table evaluates the tensor. @@ -81,15 +96,53 @@ one end of every edge below `r` and the other at or above it gives a rows-first namespace Cslib.Circuits.Frontier -/-- An *explicit Boolean tensor network* over `R`: the number of edges, numbered from zero, and a -list of vertices. A vertex is given by its ordered list of *ports*, the numbers of its edges, and -its *table*, whose entry at position `BitString.lsbValue bs` is the value of its tensor when -port `i` carries the bit `bs[i]`. -/ -structure ExplicitTensorNetwork (R : Type*) where +/-- A Boolean tensor network with *described* tensors: the number of edges, numbered from zero, +and a list of vertices. A vertex is given by its ordered list of *ports*, the numbers of its +edges, and a descriptor `t : T` of its tensor. -/ +structure DescribedNetwork (T : Type*) where /-- The number of edges; the edges are numbered `0, …, numEdges - 1`. -/ numEdges : ℕ - /-- The vertices, each given by its ports and its table. -/ - vertices : List (List ℕ × List R) + /-- The vertices, each given by its ports and the descriptor of its tensor. -/ + vertices : List (List ℕ × T) + +namespace DescribedNetwork + +variable {T : Type*} + +/-- A described network is its number of edges and its list of vertices. -/ +def equivProd : DescribedNetwork T ≃ ℕ × List (List ℕ × T) where + toFun d := (d.numEdges, d.vertices) + invFun p := ⟨p.1, p.2⟩ + left_inv _ := rfl + right_inv _ := rfl + +/-- The bits that a word, read as an assignment of bits to the edges, puts on a list of ports; +a port past the end of the word reads `false`. -/ +def wordBits (word : List Bool) (ports : List ℕ) : List Bool := + ports.map fun i => word.getD i false + +/-- The *contraction*: the sum over all assignments of bits to the edges of the product of the +entries of the vertices at the bits on their ports, for entries in a commutative semiring. -/ +def contraction {R : Type*} [CommSemiring R] (eval : T → List Bool → R) + (d : DescribedNetwork T) : R := + ∑ x : Fin d.numEdges → Bool, + (d.vertices.map fun v => eval v.2 (wordBits (List.ofFn x) v.1)).prod + +/-- The contraction commutes with maps of semirings. -/ +theorem map_contraction {R S : Type*} [CommSemiring R] [CommSemiring S] (ψ : R →+* S) + (eval : T → List Bool → R) (d : DescribedNetwork T) : + ψ (d.contraction eval) = d.contraction fun t bs => ψ (eval t bs) := by + rw [contraction, contraction, map_sum] + refine Finset.sum_congr rfl fun x _ => ?_ + rw [map_list_prod, List.map_map] + rfl + +end DescribedNetwork + +/-- An *explicit Boolean tensor network* over `R`: a network whose descriptors are *tables*. The +entry of the table of a vertex at position `BitString.lsbValue bs` is the value of its tensor +when port `i` carries the bit `bs[i]`. -/ +abbrev ExplicitTensorNetwork (R : Type*) := DescribedNetwork (List R) namespace ExplicitTensorNetwork @@ -151,6 +204,25 @@ instance (r : ℕ) : Decidable (d.IsRowsFirstBipartite r) := by end ExplicitTensorNetwork +private theorem getD_ofFn {n : ℕ} (x : Fin n → Bool) (i : ℕ) : + (List.ofFn x).getD i false = if h : i < n then x ⟨i, h⟩ else false := by + by_cases h : i < n + · rw [dite_eq_left h, List.getD_eq_getElem _ _ (by simpa using h), List.getElem_ofFn] + · rw [dite_eq_right h, List.getD_eq_default _ _ (by simp only [List.length_ofFn]; omega)] + +/-- **Explicit networks**: with table lookup as the evaluator, the contraction of a described +network is that of the explicit network. -/ +theorem DescribedNetwork.contraction_entry {R : Type*} [CommSemiring R] + (d : ExplicitTensorNetwork R) : + DescribedNetwork.contraction ExplicitTensorNetwork.entry d = d.contraction := by + refine Finset.sum_congr rfl fun x _ => ?_ + simp only [wordBits, ExplicitTensorNetwork.portBits] + congr 1 + refine List.map_congr_left fun v _ => ?_ + congr 2 + funext i + exact getD_ofFn x i + /-! ### Writing a tensor network out -/ namespace TensorNetwork diff --git a/ComplexityTests/DescribedContraction.lean b/ComplexityTests/DescribedContraction.lean index ac5647045b..060648f89d 100644 --- a/ComplexityTests/DescribedContraction.lean +++ b/ComplexityTests/DescribedContraction.lean @@ -67,8 +67,8 @@ example {T : Type} (eval : T → List Bool → List ℕ) (q : (ℕ × List ℕ) -- Explicit networks are described networks with their tables. example (d : ExplicitTensorNetwork ℕ) : - (ofExplicit d).contraction ExplicitTensorNetwork.entry = d.contraction := - contraction_ofExplicit d + DescribedNetwork.contraction ExplicitTensorNetwork.entry d = d.contraction := + contraction_entry d /-- Table lookup is polynomial-time. -/ theorem entry_polyTime : ComputableInPolyTime (fun q : List ℕ × List Bool => From d18d15fb22791661fd919cdcd4f6f0f41659225c Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:02:00 -0400 Subject: [PATCH 065/129] test(circuit/frontier): name IsRowsFirstBipartite explicitly on an anonymous explicit network MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An anonymous constructor ascribed ExplicitTensorNetwork ℕ elaborates at the shared record DescribedNetwork (List ℕ), so field notation no longer finds the explicit-network namespace. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Computability/Circuit/Frontier/TensorNetwork.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetwork.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetwork.lean index f030828558..9206e0547a 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetwork.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetwork.lean @@ -103,8 +103,8 @@ example : ¬ parallel.IsRowsFirstBipartite 2 := by decide example : ¬ (cycle eqTable).IsRowsFirstBipartite 2 := by decide /-- A port naming no edge is excluded. -/ -example : ¬ (⟨1, [([0], [1, 1]), ([0, 5], [1, 1, 1, 1])]⟩ : - ExplicitTensorNetwork ℕ).IsRowsFirstBipartite 1 := by decide +example : ¬ ExplicitTensorNetwork.IsRowsFirstBipartite + (⟨1, [([0], [1, 1]), ([0, 5], [1, 1, 1, 1])]⟩ : ExplicitTensorNetwork ℕ) 1 := by decide /-- Written out with the old vertices first, the subdivision of `parallelNetwork` is rows-first bipartite with degree `2`, both by the general lemmas and by evaluation. -/ From 88ddd692e8e09af6b8d8acbc1601b58f47f5ffb3 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:02:42 -0400 Subject: [PATCH 066/129] refactor(circuit/frontier): number packet modes with Mathlib's finProdFinEquiv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace `NativePacket.modeIndex` (with `modeIndex_div`, `modeIndex_mod` and `modeIndex_apply`) by `finProdFinEquiv.toEmbedding.trans Fin.valEmbedding`, which numbers the mode `(y₀, b)` as `b + nb y₀`, the same number. `val_packetEntryList` and `map_val_packetEntryList` now read the exponents at `finProdFinEquiv Z`. Addresses reviewer-codex-edemaine's request on #1932. Written by worker-claude-e6cf7b0d-eval-fixer, taking over from worker-claude-e6cf7b0d-t2-evaluator. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/PacketEvaluator.lean | 62 +++++++------------ .../TensorNetworkPacketEvaluator.lean | 6 +- 2 files changed, 27 insertions(+), 41 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean index 8bf1751e32..69da2d1d5f 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketEvaluator.lean @@ -37,7 +37,7 @@ ring that `A_p` maps to. * **Local forms, mode coefficients, mode values and row terms** follow `TensorNetwork.packetLocalForm`, `TensorNetwork.packetModeCoeff`, `TensorNetwork.packetModeValue` and `TensorNetwork.packetRowTerm`, with a mode `(y₀, b)` given - by its dense index `y₀ nb + b` (`NativePacket.modeIndex`). + by its dense index `b + nb y₀`, its number under `finProdFinEquiv`. * **Coefficient extraction** (`NativePacket.listCoeffList`) is the recursion `TensorNetwork.listCoeff` on coefficient lists, along a duplicate-free list of modes. @@ -491,28 +491,15 @@ theorem val_modeValueList (i : ℕ) (differ : Bool) (cols : List (ℕ × Bool)) · rw [val_pow, val_intConst, Int.cast_natCast] · exact val_zero -/-- The tile of a dense mode index `y₀ nb + b`. -/ -theorem modeIndex_div {nb y b : ℕ} (hb : b < nb) : (y * nb + b) / nb = y := by - rw [Nat.add_comm, Nat.add_mul_div_right _ _ (by omega), Nat.div_eq_of_lt hb, Nat.zero_add] - -/-- The exponent of a dense mode index `y₀ nb + b`. -/ -theorem modeIndex_mod {nb y b : ℕ} (hb : b < nb) : (y * nb + b) % nb = b := by - rw [Nat.add_comm, Nat.add_mul_mod_self_right, Nat.mod_eq_of_lt hb] - -/-- The dense index `y₀ nb + b` of the mode `(y₀, b)` of a packet with `nb` exponents per -tile. -/ -def modeIndex (i nb : ℕ) : Fin (2 ^ (i + 1)) × Fin nb ↪ ℕ := - ⟨fun Z => Z.1 * nb + Z.2, fun Z Z' h => by - have h1 := modeIndex_div (y := Z.1) Z.2.isLt - have h2 := modeIndex_mod (y := Z.1) Z.2.isLt - simp only at h - rw [h, modeIndex_div Z'.2.isLt] at h1 - rw [h, modeIndex_mod Z'.2.isLt] at h2 - exact Prod.ext (Fin.ext h1.symm) (Fin.ext h2.symm)⟩ - -@[simp] theorem modeIndex_apply (i nb : ℕ) (Z : Fin (2 ^ (i + 1)) × Fin nb) : - modeIndex i nb Z = (Z.1 : ℕ) * nb + Z.2 := - rfl +/-- The tile of the dense index `b + nb y₀` of a mode `(y₀, b)`. -/ +private theorem finProdFinEquiv_div {m n : ℕ} (Z : Fin m × Fin n) : + (finProdFinEquiv Z : ℕ) / n = Z.1 := + congrArg (fun W => (W.1 : ℕ)) (finProdFinEquiv.symm_apply_apply Z) + +/-- The exponent of the dense index `b + nb y₀` of a mode `(y₀, b)`. -/ +private theorem finProdFinEquiv_mod {m n : ℕ} (Z : Fin m × Fin n) : + (finProdFinEquiv Z : ℕ) % n = Z.2 := + congrArg (fun W => (W.2 : ℕ)) (finProdFinEquiv.symm_apply_apply Z) /-- The terms of the affine factor of a row port `r = (x, Y_x)`: its local term at `none`, and the coefficient of the mode `(k / nb, k % nb)` at `some k` (zero when there are no exponents, @@ -532,7 +519,7 @@ theorem val_rowTermList {a L i : ℕ} (hL : L = 2 ^ a) (hia : i < a) {t : ℤ} {cols : List (ℕ × Bool)} (hcols : ∀ c ∈ cols, c.1 < 2 ^ a) {r : ℕ × Bool} (hr : r.1 < 2 ^ a) (ζ : List ℕ) : (fun o => val p fl (rowTermList p fl a L i t nb differ cols ζ r o)) ∘ - Option.map (modeIndex i nb) = + Option.map (finProdFinEquiv.toEmbedding.trans Fin.valEmbedding) = packetRowTerm (p := nb) (scalarHom p fl) (val p fl ζ) a i t differ cols r := by funext o cases o with @@ -543,7 +530,8 @@ theorem val_rowTermList {a L i : ℕ} (hL : L = 2 ^ a) (hia : i < a) {t : ℤ} · rw [val_sub, val_one, val_localFormList hL ht hcols hr ζ] | some Z => simp only [Function.comp_apply, Option.map_some, rowTermList, packetRowTerm, affineSign, - modeIndex_apply, modeIndex_div Z.2.isLt, modeIndex_mod Z.2.isLt, + Function.Embedding.trans_apply, Equiv.coe_toEmbedding, Fin.valEmbedding_apply, + finProdFinEquiv_div, finProdFinEquiv_mod, Nat.pos_iff_ne_zero.mp (Nat.zero_lt_of_lt Z.2.isLt), ↓reduceIte] have h := val_modeCoeffList (p := p) (fl := fl) hL hia ht hti hti' differ hr Z ζ split_ifs @@ -588,24 +576,22 @@ theorem val_listCoeffList {M : Type} [DecidableEq M] {modes : List M} (hm : mode modes of the packet. -/ private theorem toFinset_filter_range (i nb : ℕ) (P : ℕ → Bool) : ((List.range (2 ^ (i + 1) * nb)).filter fun k => P (k / nb)).toFinset = - ({Z | P Z.1 = true} : Finset (Fin (2 ^ (i + 1)) × Fin nb)).map (modeIndex i nb) := by + ({Z | P Z.1 = true} : Finset (Fin (2 ^ (i + 1)) × Fin nb)).map + (finProdFinEquiv.toEmbedding.trans Fin.valEmbedding) := by ext k simp only [List.mem_toFinset, List.mem_filter, List.mem_range, mem_map, mem_filter, mem_univ, - true_and, modeIndex_apply] + true_and, Function.Embedding.trans_apply, Equiv.coe_toEmbedding, Fin.valEmbedding_apply] constructor · rintro ⟨hk, h⟩ - have hnb : 0 < nb := Nat.pos_of_ne_zero fun h0 => by simp [h0] at hk - exact ⟨(⟨k / nb, (Nat.div_lt_iff_lt_mul hnb).mpr hk⟩, ⟨k % nb, Nat.mod_lt _ hnb⟩), h, - Nat.div_add_mod' k nb⟩ + exact ⟨finProdFinEquiv.symm ⟨k, hk⟩, h, congrArg Fin.val (finProdFinEquiv.apply_symm_apply _)⟩ · rintro ⟨Z, h, rfl⟩ - refine ⟨?_, by rw [modeIndex_div Z.2.isLt]; exact h⟩ - calc (Z.1 : ℕ) * nb + Z.2 < (Z.1 + 1) * nb := by rw [Nat.add_mul, Nat.one_mul]; omega - _ ≤ 2 ^ (i + 1) * nb := Nat.mul_le_mul_right _ Z.1.isLt + exact ⟨(finProdFinEquiv Z).isLt, by rw [finProdFinEquiv_div]; exact h⟩ /-- **The entry of a packet vertex on coefficient lists** (§D.4.6, p. 63): coefficient extraction along the row terms, at the exponents `β` of the modes targeting the group, times the powers of the values of the modes with the group as source. A mode `(y₀, b)` is its dense index -`y₀ nb + b`, so `β` is read at dense indices, as from a dense list of exponents. -/ +`b + nb y₀` (`finProdFinEquiv`), so `β` is read at dense indices, as from a dense list of +exponents. -/ def packetEntryList (p : ℕ) (fl : List ℕ) (a L i : ℕ) (t : ℤ) (nb : ℕ) (e₁ e₂ differ : Bool) (β : ℕ → ℕ) (rows cols : List (ℕ × Bool)) (ζ : List ℕ) : List ℕ := mul p fl @@ -626,7 +612,7 @@ theorem val_packetEntryList {a L i : ℕ} (hL : L = 2 ^ a) (hia : i < a) {t : (hcols : ∀ c ∈ cols, c.1 < 2 ^ a) (ζ : List ℕ) : val p fl (packetEntryList p fl a L i t nb e₁ e₂ differ β rows cols ζ) = packetEntry (p := nb) (scalarHom p fl) (val p fl ζ) a i t e₁ e₂ differ - (fun Z => β (Z.1 * nb + Z.2)) rows cols := by + (fun Z => β (finProdFinEquiv Z)) rows cols := by rw [packetEntryList, packetEntry, val_mul] congr 1 · rw [val_listCoeffList (List.nodup_range.filter _), toFinset_filter_range, @@ -636,8 +622,8 @@ theorem val_packetEntryList {a L i : ℕ} (hL : L = 2 ^ a) (hia : i < a) {t : · rw [val_prodList, List.map_map, ← List.prod_toFinset _ (List.nodup_range.filter _), toFinset_filter_range, prod_map] refine prod_congr rfl fun Z _ => ?_ - simp only [Function.comp_apply, modeIndex_apply, modeIndex_div Z.2.isLt, - modeIndex_mod Z.2.isLt] + simp only [Function.comp_apply, Function.Embedding.trans_apply, Equiv.coe_toEmbedding, + Fin.valEmbedding_apply, finProdFinEquiv_div, finProdFinEquiv_mod] rw [val_pow, val_modeValueList] /-- **Packet entries in every ring receiving `A_p`.** Along a ring map `ψ : A_p → S` with `2` @@ -649,7 +635,7 @@ theorem map_val_packetEntryList {S : Type*} [CommRing S] (ψ : Ring p fl →+* S (nb : ℕ) (e₁ e₂ differ : Bool) (β : ℕ → ℕ) {rows cols : List (ℕ × Bool)} (hrows : ∀ r ∈ rows, r.1 < 2 ^ a) (hcols : ∀ c ∈ cols, c.1 < 2 ^ a) (ζ : List ℕ) : ψ (val p fl (packetEntryList p fl a L i t nb e₁ e₂ differ β rows cols ζ)) = - packetEntry (p := nb) κ (ψ (val p fl ζ)) a i t e₁ e₂ differ (fun Z => β (Z.1 * nb + Z.2)) + packetEntry (p := nb) κ (ψ (val p fl ζ)) a i t e₁ e₂ differ (fun Z => β (finProdFinEquiv Z)) rows cols := by rw [val_packetEntryList hL hia ht hti hti' nb e₁ e₂ differ β hrows hcols ζ, map_packetEntry ψ two_pow_eq_zero _ _ hia.le hti hti', diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEvaluator.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEvaluator.lean index 5115a5d231..6294be96ea 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEvaluator.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketEvaluator.lean @@ -58,7 +58,7 @@ example : packetEntryList 3 [1, 1] 1 2 0 1 2 true false true (fun _ => 0) [(0, f (kernelValueList 3 [1, 1] 1 2 1 1 [0, 1]) := by decide --- Exponent `1` at the mode `(0, 1)`, of dense index `0 * 2 + 1 = 1`, which targets the group +-- Exponent `1` at the mode `(0, 1)`, of dense index `1 + 2 * 0 = 1`, which targets the group -- (digit `0` of `y₀ = 0` selects the first symbol): the single row factor contributes the -- coefficient of that mode, and no source mode has a positive exponent. example : packetEntryList 3 [1, 1] 1 2 0 1 2 true false true (fun k => if k = 1 then 1 else 0) @@ -70,7 +70,7 @@ theorem val_packetEntryList_tiny (β : ℕ → ℕ) (ζ : List ℕ) : GaloisRingArith.val 3 [1, 1] (packetEntryList 3 [1, 1] 1 2 0 1 2 true false true β [(0, true), (1, false)] [(1, true), (0, true)] ζ) = packetEntry (p := 2) (scalarHom 3 [1, 1]) (GaloisRingArith.val 3 [1, 1] ζ) 1 0 1 true false - true (fun Z => β (Z.1 * 2 + Z.2)) [(0, true), (1, false)] [(1, true), (0, true)] := + true (fun Z => β (finProdFinEquiv Z)) [(0, true), (1, false)] [(1, true), (0, true)] := val_packetEntryList (by norm_num) (by norm_num) (by simp [mem_cauchyGrid]) (by norm_num) (by norm_num) 2 true false true β (by simp) (by simp) ζ @@ -80,7 +80,7 @@ example {S : Type} [CommRing S] (ψ : GaloisRingArith.Ring 3 [1, 1] →+* S) {n ψ (GaloisRingArith.val 3 [1, 1] (packetEntryList 3 [1, 1] 1 2 0 1 2 true false true β [(0, true)] [(1, true)] ζ)) = packetEntry (p := 2) κ (ψ (GaloisRingArith.val 3 [1, 1] ζ)) 1 0 1 true false true - (fun Z => β (Z.1 * 2 + Z.2)) [(0, true)] [(1, true)] := + (fun Z => β (finProdFinEquiv Z)) [(0, true)] [(1, true)] := map_val_packetEntryList ψ hn κ (by norm_num) (by norm_num) (by simp [mem_cauchyGrid]) (by norm_num) (by norm_num) 2 true false true β (by simp) (by simp) ζ From bd6a6a6fd4339f271e353ae10b40e09ecfc233f5 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:05:46 -0400 Subject: [PATCH 067/129] feat(GenericRAM/Native): derivations of stack recursions from recursion trees Loop.counted (counted children), exists_eval_of_counted (an Eval derivation from a value function on the recursion tree, with the node count Nat.recursionBound and the stack depth), and value_of_run_eq_some (a value returned by the clocked run satisfies the root derivation's invariant, for any fuel). For the T2.9 instance of #1538. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../GenericRAM/Native/StackRecursionTree.lean | 162 ++++++++++++++++++ ComplexityTests/StackRecursionTree.lean | 79 +++++++++ 3 files changed, 242 insertions(+) create mode 100644 Complexity/Computability/GenericRAM/Native/StackRecursionTree.lean create mode 100644 ComplexityTests/StackRecursionTree.lean diff --git a/Complexity.lean b/Complexity.lean index 6dcff0450f..d773d84e22 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1513,6 +1513,7 @@ public import Complexity.Computability.GenericRAM.Native.IndexFold public import Complexity.Computability.GenericRAM.Native.MeasurementFold public import Complexity.Computability.GenericRAM.Native.Nat public import Complexity.Computability.GenericRAM.Native.StackRecursion +public import Complexity.Computability.GenericRAM.Native.StackRecursionTree public import Complexity.Computability.GenericRAM.Native.SubsetPairFold public import Complexity.Computability.GenericRAM.Signed public import Complexity.Computability.GenericRAM.Signed.Fits diff --git a/Complexity/Computability/GenericRAM/Native/StackRecursionTree.lean b/Complexity/Computability/GenericRAM/Native/StackRecursionTree.lean new file mode 100644 index 0000000000..d1e011cbde --- /dev/null +++ b/Complexity/Computability/GenericRAM/Native/StackRecursionTree.lean @@ -0,0 +1,162 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.GenericRAM.Native.StackRecursion +public import Complexity.Foundations.Data.Nat.RecursionTree + +/-! +# Derivations of stack recursions from their recursion trees + +A derivation `Cslib.GenericRAM.StackRecursion.Scheme.Eval` of a recursion scheme records the +value of a node with its number of nodes and its stack depth. This file builds derivations from +a description of the recursion tree, and reads off what a clocked run returns. + +**Counted children** (`Scheme.Loop.counted`). A call whose accumulators `acc c` ask for the +children `child c` for `c < bound`, and then stop, runs its loop through the children's +derivations. + +**The recursion tree** (`Scheme.exists_eval_of_counted`). Let `value` assign values to the nodes +satisfying `I.node`, such that a leaf has its leaf value, and a call at depth `d < D` has at most +`b d` counted children, all at depth `d + 1` and smaller in a measure, with its value the +`finish` of the accumulated children's values. Then every node at depth `d ≤ D` has a derivation +of its value, with at most `Nat.recursionBound b (D - d) d` nodes and at most `D - d` stack +levels. This is the induction "from the leaves" of a recursive algorithm, with its node count. + +**What a run returns** (`Scheme.value_of_run_eq_some`). Every value the clocked run returns, for +any fuel, satisfies the invariant of a derivation of the root. If the values carry a tag that +only the root's value has, a returned tagged value is the root's value, even when the fuel is too +small to vouch for the run. + +## Main results + +* `Cslib.GenericRAM.StackRecursion.Scheme.Loop.counted`: the loop over counted children. +* `Cslib.GenericRAM.StackRecursion.Scheme.exists_eval_of_counted`: derivations with the node count + of the recursion tree. +* `Cslib.GenericRAM.StackRecursion.Scheme.value_of_run_eq_some`: a returned value satisfies the + invariant. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Appendix D.4.7 (p. 64). +-/ + +public section + +namespace Cslib.GenericRAM.StackRecursion.Scheme + +open Finset + +variable {X A Y : Type} {R : Scheme X A Y} {I : Invariant X A Y} + +/-- **A call with counted children.** If, from the accumulators `acc c`, the call `x` asks for the +children `child c` for `c < bound` and then stops, and the children evaluate to `val c`, then its +loop from `acc 0` ends at `acc bound`, with the children's nodes and at most `H` levels. -/ +theorem Loop.counted {x : X} {acc : ℕ → A} {child : ℕ → X} {val : ℕ → Y} {nodes height : ℕ → ℕ} + (bound H : ℕ) (hnext : ∀ c < bound, R.next x (acc c) = some (child c)) + (hdone : R.next x (acc bound) = none) + (habsorb : ∀ c < bound, R.absorb x (acc c) (val c) = acc (c + 1)) + (heval : ∀ c < bound, R.Eval I (child c) (val c) (nodes c) (height c)) + (hframe : ∀ c < bound, I.frame x (acc c)) (hH : ∀ c < bound, height c ≤ H) : + ∃ h ≤ H, R.Loop I x (acc 0) (acc bound) (∑ c ∈ range bound, nodes c) h := by + -- From any start `s ≤ bound`. + have key : ∀ k s, s + k = bound → + ∃ h ≤ H, R.Loop I x (acc s) (acc bound) (∑ c ∈ Ico s bound, nodes c) h := by + intro k + induction k with + | zero => + intro s hs + subst hs + exact ⟨0, Nat.zero_le _, by simpa using Loop.done hdone⟩ + | succ k ih => + intro s hs + obtain ⟨h, hh, hl⟩ := ih (s + 1) (by omega) + have hs' : s < bound := by omega + have hl' := Loop.child (hframe s hs') (hnext s hs') (heval s hs') + (by rw [habsorb s hs']; exact hl) + refine ⟨max (height s) h, max_le (hH s hs') hh, ?_⟩ + rwa [sum_eq_sum_Ico_succ_bot hs'] + simpa using key bound 0 (by omega) + +/-- A recursion tree has at least its root. -/ +private theorem one_le_recursionBound (b : ℕ → ℕ) (k d : ℕ) : 1 ≤ Nat.recursionBound b k d := by + cases k with + | zero => simp + | succ k => rw [Nat.recursionBound_succ]; omega + +/-- **Derivations from the recursion tree.** Let `value` assign values to the nodes satisfying +`I.node`, all satisfying `I.value`, such that + +* a leaf has its leaf value; +* a call at depth `d` has `d < D` and at most `b d` children, counted: from the accumulators + `acc c`, starting at `start`, it asks for `child c` for `c < cnt` and then stops, absorbing the + children's values; the frames satisfy `I.frame`, and the children satisfy `I.node`, are smaller + in the measure `μ` and lie at depth `d + 1`; and its value is the `finish` of the last + accumulator. + +Then every node at depth `d ≤ D` has a derivation of its value with at most +`Nat.recursionBound b (D - d) d` nodes and at most `D - d` stack levels. -/ +theorem exists_eval_of_counted (μ depth : X → ℕ) (value : X → Y) (b : ℕ → ℕ) (D : ℕ) + (hleaf : ∀ x, I.node x → R.isLeaf x = true → value x = R.leaf x) + (hcall : ∀ x, I.node x → R.isLeaf x = false → depth x < D ∧ + ∃ (cnt : ℕ) (acc : ℕ → A) (child : ℕ → X), cnt ≤ b (depth x) ∧ acc 0 = R.start x ∧ + (∀ c < cnt, R.next x (acc c) = some (child c)) ∧ R.next x (acc cnt) = none ∧ + (∀ c < cnt, R.absorb x (acc c) (value (child c)) = acc (c + 1)) ∧ + (∀ c < cnt, I.frame x (acc c) ∧ I.node (child c) ∧ μ (child c) < μ x ∧ + depth (child c) = depth x + 1) ∧ + value x = R.finish x (acc cnt)) + (hvalue : ∀ x, I.node x → I.value (value x)) (x : X) (hx : I.node x) (hxD : depth x ≤ D) : + ∃ n h, R.Eval I x (value x) n h ∧ n ≤ Nat.recursionBound b (D - depth x) (depth x) ∧ + h ≤ D - depth x := by + induction hμ : μ x using Nat.strong_induction_on generalizing x with + | _ k ih => + cases hl : R.isLeaf x + · obtain ⟨hd, cnt, acc, child, hcnt, h0, hnext, hdone, habs, hkids, hfin⟩ := hcall x hx hl + have hev : ∀ c < cnt, ∃ n h, R.Eval I (child c) (value (child c)) n h ∧ + n ≤ Nat.recursionBound b (D - (depth x + 1)) (depth x + 1) ∧ + h ≤ D - (depth x + 1) := fun c hc => by + obtain ⟨-, hn, hμc, hdc⟩ := hkids c hc + obtain ⟨n, h, he, hnb, hhb⟩ := ih (μ (child c)) (hμ ▸ hμc) (child c) hn (by omega) rfl + rw [hdc] at hnb hhb + exact ⟨n, h, he, hnb, hhb⟩ + choose! nodes height hev using hev + obtain ⟨h, hh, hloop⟩ := Loop.counted (I := I) (val := fun c => value (child c)) cnt + (D - (depth x + 1)) hnext hdone habs (fun c hc => (hev c hc).1) + (fun c hc => (hkids c hc).1) (fun c hc => (hev c hc).2.2) + rw [h0] at hloop + have hv : I.value (R.finish x (acc cnt)) := hfin ▸ hvalue x hx + refine ⟨(∑ c ∈ range cnt, nodes c) + 1, h + 1, hfin ▸ Eval.call hx hl hloop hv, ?_, by omega⟩ + obtain ⟨k', hk'⟩ : ∃ k', D - depth x = k' + 1 := ⟨D - depth x - 1, by omega⟩ + have hk'' : D - (depth x + 1) = k' := by omega + rw [hk', Nat.recursionBound_succ] + have hsum : ∑ c ∈ range cnt, nodes c ≤ cnt * Nat.recursionBound b k' (depth x + 1) := by + calc ∑ c ∈ range cnt, nodes c ≤ ∑ _c ∈ range cnt, Nat.recursionBound b k' (depth x + 1) := + sum_le_sum fun c hc => hk'' ▸ (hev c (mem_range.mp hc)).2.1 + _ = cnt * Nat.recursionBound b k' (depth x + 1) := by rw [sum_const, card_range, + smul_eq_mul] + have := Nat.mul_le_mul_right (Nat.recursionBound b k' (depth x + 1)) hcnt + omega + · refine ⟨1, 0, ?_, one_le_recursionBound _ _ _, Nat.zero_le _⟩ + rw [hleaf x hx hl] + exact Eval.leaf hx hl (hleaf x hx hl ▸ hvalue x hx) + +/-- **What a clocked run returns.** For any fuel, a value returned by the run satisfies the +invariant of a derivation of the root. -/ +theorem value_of_run_eq_some {J : Type} {fuel : J → ℕ} {root : J → X} {a : J} {y y' : Y} + {N H : ℕ} (he : R.Eval I (root a) y N H) (hrun : R.run fuel root a = some y') : + I.value y' := by + obtain ⟨-, -, ⟨x, hr, -⟩ | ⟨z, hr, hz⟩⟩ := he.good_iterate (fuel a) + · simp only [run] at hrun + rw [hr] at hrun + simp at hrun + · simp only [run] at hrun + rw [hr] at hrun + cases hrun + exact hz + +end Cslib.GenericRAM.StackRecursion.Scheme diff --git a/ComplexityTests/StackRecursionTree.lean b/ComplexityTests/StackRecursionTree.lean new file mode 100644 index 0000000000..a1b309e6a4 --- /dev/null +++ b/ComplexityTests/StackRecursionTree.lean @@ -0,0 +1,79 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.GenericRAM.Native.StackRecursionTree + +/-! # Derivations from recursion trees on a toy scheme + +The full binary tree of height `k`: a node with `x > 0` levels below it has two children with +`x - 1` levels, a leaf has value `1`, and a call adds its children's values. The value of a node +with `x` levels is `2 ^ x`. `exists_eval_of_counted` derives its value with at most +`Nat.recursionBound (fun _ => 2) k 0` nodes. The values are tagged with the depth, and +`value_of_run_eq_some` shows that a value tagged `0` returned by the run, with any fuel, is the +root's. -/ + +namespace ComplexityTests.StackRecursionTree + +open Cslib.GenericRAM Cslib.GenericRAM.StackRecursion + +/-- The binary tree scheme on `(depth, levels below)`, with values tagged by the depth. The +accumulator counts the children done and sums their values. -/ +def tree : Scheme (ℕ × ℕ) (ℕ × ℕ) (ℕ × ℕ) where + isLeaf x := x.2 == 0 + leaf x := (x.1, 1) + start _ := (0, 0) + next x a := if a.1 < 2 then some (x.1 + 1, x.2 - 1) else none + absorb _ a y := (a.1 + 1, a.2 + y.2) + finish x a := (x.1, a.2) + +/-- The nodes of the tree of height `k`, and the root's value `2 ^ k` for the depth-`0` tag. -/ +def inv (k : ℕ) : Invariant (ℕ × ℕ) (ℕ × ℕ) (ℕ × ℕ) := + ⟨fun x => x.1 + x.2 = k, fun _ _ => True, fun y => y.1 = 0 → y.2 = 2 ^ k⟩ + +/-- **The derivation of the root**, with the node count of the recursion tree. -/ +theorem exists_eval_tree (k : ℕ) : + ∃ n h, tree.Eval (inv k) (0, k) (0, 2 ^ k) n h ∧ + n ≤ Nat.recursionBound (fun _ => 2) k 0 ∧ h ≤ k := by + have := Scheme.exists_eval_of_counted (R := tree) (I := inv k) (fun x => x.2) (fun x => x.1) + (fun x => (x.1, 2 ^ x.2)) (fun _ => 2) k + (fun x _ hl => by + simp only [tree, beq_iff_eq] at hl ⊢ + rw [hl, pow_zero]) + (fun x hx hl => by + simp only [tree, beq_eq_false_iff_ne, ne_eq] at hl + simp only [inv] at hx + refine ⟨by omega, 2, fun c => (c, c * 2 ^ (x.2 - 1)), fun _ => (x.1 + 1, x.2 - 1), le_rfl, + by simp [tree], fun c hc => by simp [tree]; omega, by simp [tree], fun c hc => ?_, + fun c hc => ⟨trivial, by simp only [inv]; omega, show x.2 - 1 < x.2 by omega, rfl⟩, ?_⟩ + · simp only [tree, Prod.mk.injEq, true_and] + ring + · simp only [tree, Prod.mk.injEq, true_and] + rw [show x.2 = x.2 - 1 + 1 by omega, pow_succ] + simp only [Nat.add_sub_cancel] + ring) + (fun x hx h0 => by + simp only [inv] at hx + simp only at h0 ⊢ + rw [show x.2 = k by omega]) (0, k) (by simp [inv]) (by simp) + simpa using this + +/-- **A tagged value returned by the run is the root's**, for every fuel. -/ +theorem run_tree {k f y : ℕ} + (hrun : tree.run (fun _ : Unit => f) (fun _ => (0, k)) () = some (0, y)) : y = 2 ^ k := by + obtain ⟨n, h, he, -, -⟩ := exists_eval_tree k + exact Scheme.value_of_run_eq_some (fuel := fun _ : Unit => f) (root := fun _ => (0, k)) + (a := ()) he hrun rfl + +-- The tree of height `3` has `15` nodes, within the bound. +example : Nat.recursionBound (fun _ => 2) 3 0 = 15 := by decide + +-- With `2 * 15 - 1` steps the run returns the root's value. +example : tree.run (fun _ : Unit => 29) (fun _ => (0, 3)) () = some (0, 8) := by decide + +-- With too little fuel it may return a child's value, tagged with a positive depth. +example : tree.run (fun _ : Unit => 28) (fun _ => (0, 3)) () = some (1, 4) := by decide + +end ComplexityTests.StackRecursionTree From 30bb409365be3d96e260ab173b5a85e3e5cfd1df Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:06:28 -0400 Subject: [PATCH 068/129] refactor(circuit/frontier): read packet exponents at finProdFinEquiv in the native evaluator Follows #1932's switch from `modeIndex` to Mathlib's `finProdFinEquiv`: the statements of `val_packetEntryNative_eq`, `map_val_packetEntryNative` and `val_packetVertexEntry` read the dense exponents at `finProdFinEquiv Z` (the same number as `Z.1 * nb + Z.2`). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Circuit/Frontier/TensorNetwork/PacketTerms.lean | 6 +++--- .../Circuit/Frontier/TensorNetworkPacketTerms.lean | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketTerms.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketTerms.lean index 0905c77e84..f614aa598e 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketTerms.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/PacketTerms.lean @@ -913,7 +913,7 @@ theorem val_packetEntryNative_eq (c : PacketCtx) (hL : c.L = 2 ^ c.a) (hia : c.i (hcols : ∀ r ∈ c.cols, r.1 < 2 ^ c.a) (β : List ℕ) : val c.p c.fl (packetEntryNative c rows β) = packetEntry (p := c.nb) (scalarHom c.p c.fl) (val c.p c.fl c.ζ) c.a c.i c.t c.e₁ c.e₂ - c.differ (fun Z => β.getD (Z.1 * c.nb + Z.2) 0) rows c.cols := by + c.differ (fun Z => β.getD (finProdFinEquiv Z) 0) rows c.cols := by rw [val_packetEntryNative c htiles, val_packetEntryList hL hia ht hti hti' c.nb c.e₁ c.e₂ c.differ _ hrows hcols] @@ -929,7 +929,7 @@ theorem map_val_packetEntryNative {S : Type*} [CommRing S] (c : PacketCtx) (hrows : ∀ r ∈ rows, r.1 < 2 ^ c.a) (hcols : ∀ r ∈ c.cols, r.1 < 2 ^ c.a) (β : List ℕ) : ψ (val c.p c.fl (packetEntryNative c rows β)) = packetEntry (p := c.nb) κ (ψ (val c.p c.fl c.ζ)) c.a c.i c.t c.e₁ c.e₂ c.differ - (fun Z => β.getD (Z.1 * c.nb + Z.2) 0) rows c.cols := by + (fun Z => β.getD (finProdFinEquiv Z) 0) rows c.cols := by rw [val_packetEntryNative c htiles, map_val_packetEntryList ψ hn κ hL hia ht hti hti' c.nb c.e₁ c.e₂ c.differ _ hrows hcols] @@ -1374,7 +1374,7 @@ theorem val_packetVertexEntry (d : PacketDesc) (bs : List Bool) (hL : d.1.L = 2 (hti' : ¬ (2 : ℤ) ^ (d.1.i + 1) ∣ d.1.t) (hports : ∀ x ∈ d.2.1, x.2 < 2 ^ d.1.a) : val d.1.p d.1.fl (packetVertexEntry d bs) = packetEntry (p := d.1.nb) (scalarHom d.1.p d.1.fl) (val d.1.p d.1.fl d.1.ζ) d.1.a d.1.i - d.1.t d.1.e₁ d.1.e₂ d.1.differ (fun Z => d.2.2.getD (Z.1 * d.1.nb + Z.2) 0) + d.1.t d.1.e₁ d.1.e₂ d.1.differ (fun Z => d.2.2.getD (finProdFinEquiv Z) 0) (sidePorts d.2.1 bs true) (sidePorts d.2.1 bs false) := by have hside (side : Bool) : ∀ r ∈ sidePorts d.2.1 bs side, r.1 < 2 ^ d.1.a := by intro r hr diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketTerms.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketTerms.lean index 4d43bf56eb..e8bd157553 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketTerms.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkPacketTerms.lean @@ -48,7 +48,7 @@ example : packetEntryNative ctx [(0, true)] [0, 1, 0, 0] = theorem val_ctx (β : List ℕ) : GaloisRingArith.val 3 [1, 1] (packetEntryNative ctx [(0, true)] β) = packetEntry (p := 2) (scalarHom 3 [1, 1]) (GaloisRingArith.val 3 [1, 1] [0, 1]) 1 0 1 true - false true (fun Z => β.getD (Z.1 * 2 + Z.2) 0) [(0, true)] [(1, true)] := + false true (fun Z => β.getD (finProdFinEquiv Z) 0) [(0, true)] [(1, true)] := val_packetEntryNative_eq ctx (by decide) (by decide) (by decide) (by simp [ctx, mem_cauchyGrid]) (by norm_num [ctx]) (by norm_num [ctx]) (by simp) (by simp [ctx]) β @@ -73,7 +73,7 @@ example : packetVertexEntry desc [true, false] = [0, 0] := by decide theorem val_desc (bs : List Bool) : GaloisRingArith.val 3 [1, 1] (packetVertexEntry desc bs) = packetEntry (p := 2) (scalarHom 3 [1, 1]) (GaloisRingArith.val 3 [1, 1] [0, 1]) 1 0 1 true - false true (fun Z => [0, 0, 0, 0].getD (Z.1 * 2 + Z.2) 0) (sidePorts desc.2.1 bs true) + false true (fun Z => [0, 0, 0, 0].getD (finProdFinEquiv Z) 0) (sidePorts desc.2.1 bs true) (sidePorts desc.2.1 bs false) := val_packetVertexEntry desc bs (by decide) (by decide) (by decide) (by simp [desc, ctx, mem_cauchyGrid]) (by norm_num [desc, ctx]) (by norm_num [desc, ctx]) From a74e6d026c3ea4e29a79b9c39a58694561f72f93 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:08:20 -0400 Subject: [PATCH 069/129] refactor(circuit/frontier): read packet exponents at finProdFinEquiv for vertex descriptors Follows #1932's switch from `modeIndex` to Mathlib's `finProdFinEquiv`: `map_val_entry_inr` and the dense-exponent hypothesis of `map_val_entry_child_inr` read the exponents at `finProdFinEquiv Z` (the same number as `Z.1 * nb + Z.2`). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Circuit/Frontier/TensorNetwork/ChildEntries.lean | 2 +- .../Circuit/Frontier/TensorNetwork/VertexDesc.lean | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildEntries.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildEntries.lean index 2eb8415168..bb3b4b2d16 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildEntries.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildEntries.lean @@ -241,7 +241,7 @@ theorem map_val_entry_child_inr [DecidableEq V] (β : P × Fin (2 ^ (i + 1)) × (ha : c.a = a) (hL : c.L = 2 ^ a) (hi : c.i = i) (htiles : c.tiles = 2 ^ (i + 1)) (ht' : c.t = t) (hnb : c.nb = p) (he₁ : c.e₁ = decide ((sym q).1 = g)) (he₂ : c.e₂ = decide ((sym q).2 = g)) (hdiffer : c.differ = decide ((sym q).1 ≠ (sym q).2)) - (hβ : ∀ Z : Fin (2 ^ (i + 1)) × Fin p, βd.getD (Z.1 * p + Z.2) 0 = β (q, Z)) + (hβ : ∀ Z : Fin (2 ^ (i + 1)) × Fin p, βd.getD (finProdFinEquiv Z) 0 = β (q, Z)) (y : {e // (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').group (N.src e) = g} ⊕ {e // (cauchyModeSplitting side hsrc htgt horient hslot hia κ ζ hp ht hti hti').group diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/VertexDesc.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/VertexDesc.lean index cfb4546d15..3fff52005a 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/VertexDesc.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/VertexDesc.lean @@ -131,7 +131,7 @@ theorem map_val_entry_inr {S : Type*} [CommRing S] (d : PacketDesc) (bs : List B (hti' : ¬ (2 : ℤ) ^ (d.1.i + 1) ∣ d.1.t) (hports : ∀ x ∈ d.2.1, x.2 < 2 ^ d.1.a) : ψ (val d.1.p d.1.fl (entry (.inr d) bs)) = packetEntry (p := d.1.nb) κ (ψ (val d.1.p d.1.fl d.1.ζ)) d.1.a d.1.i d.1.t d.1.e₁ d.1.e₂ - d.1.differ (fun Z => d.2.2.getD (Z.1 * d.1.nb + Z.2) 0) (sidePorts d.2.1 bs true) + d.1.differ (fun Z => d.2.2.getD (finProdFinEquiv Z) 0) (sidePorts d.2.1 bs true) (sidePorts d.2.1 bs false) := by have hside (side : Bool) : ∀ r ∈ sidePorts d.2.1 bs side, r.1 < 2 ^ d.1.a := by intro r hr From 1d440fa2521b18668c01b56dee0ee285feb9df5a Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:08:24 -0400 Subject: [PATCH 070/129] feat(GenericRAM/Native): derivations by well-founded recursion; DescribedContraction uses the moved Loop.counted Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/DescribedContraction.lean | 41 +------------------ .../GenericRAM/Native/StackRecursionTree.lean | 36 +++++++++++++++- 2 files changed, 36 insertions(+), 41 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean index 9a511b6bca..6c74eb7f0e 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedContraction.lean @@ -8,7 +8,7 @@ module -- upstream: candidate public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit -public import Complexity.Computability.GenericRAM.Native.StackRecursion +public import Complexity.Computability.GenericRAM.Native.StackRecursionTree public import Complexity.Computability.PolyTime.GaloisRingArithmetic public import Complexity.Foundations.Data.BitString.List.Binary public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Words @@ -85,8 +85,6 @@ contraction algorithm, with implicit packet tensors as descriptors. ## Main results -* `Cslib.GenericRAM.StackRecursion.Scheme.Loop.counted`: a call whose children are indexed by - a counter. * `Cslib.Circuits.Frontier.DescribedNetwork.eval_sumRoot`: direct summation evaluates to `sumValue`, with `2 ^ m (k + 1) + 1` nodes and two open calls. * `Cslib.Circuits.Frontier.SumOps.IsHom.map_sumValue`: under a homomorphism, the value is the @@ -103,43 +101,6 @@ contraction algorithm, with implicit packet tensors as descriptors. @[expose] public section -namespace Cslib.GenericRAM.StackRecursion.Scheme - -open Finset - -variable {X A Y : Type} {R : Scheme X A Y} {I : Invariant X A Y} - -/-- **A call with counted children.** If, from the accumulators `acc c`, the call `x` asks for the -children `child c` for `c < bound` and then stops, and the children evaluate to `val c`, then its -loop from `acc 0` ends at `acc bound`, with the children's nodes and at most `H` levels. -/ -theorem Loop.counted {x : X} {acc : ℕ → A} {child : ℕ → X} {val : ℕ → Y} {nodes height : ℕ → ℕ} - (bound H : ℕ) (hnext : ∀ c < bound, R.next x (acc c) = some (child c)) - (hdone : R.next x (acc bound) = none) - (habsorb : ∀ c < bound, R.absorb x (acc c) (val c) = acc (c + 1)) - (heval : ∀ c < bound, R.Eval I (child c) (val c) (nodes c) (height c)) - (hframe : ∀ c < bound, I.frame x (acc c)) (hH : ∀ c < bound, height c ≤ H) : - ∃ h ≤ H, R.Loop I x (acc 0) (acc bound) (∑ c ∈ range bound, nodes c) h := by - -- From any start `s ≤ bound`. - have key : ∀ k s, s + k = bound → - ∃ h ≤ H, R.Loop I x (acc s) (acc bound) (∑ c ∈ Ico s bound, nodes c) h := by - intro k - induction k with - | zero => - intro s hs - subst hs - exact ⟨0, Nat.zero_le _, by simpa using Loop.done hdone⟩ - | succ k ih => - intro s hs - obtain ⟨h, hh, hl⟩ := ih (s + 1) (by omega) - have hs' : s < bound := by omega - have hl' := Loop.child (hframe s hs') (hnext s hs') (heval s hs') - (by rw [habsorb s hs']; exact hl) - refine ⟨max (height s) h, max_le (hH s hs') hh, ?_⟩ - rwa [sum_eq_sum_Ico_succ_bot hs'] - simpa using key bound 0 (by omega) - -end Cslib.GenericRAM.StackRecursion.Scheme - namespace Cslib.Circuits.Frontier open Finset Cslib.BitString Cslib.GenericRAM Cslib.GenericRAM.StackRecursion Turing.MultiTapeTM diff --git a/Complexity/Computability/GenericRAM/Native/StackRecursionTree.lean b/Complexity/Computability/GenericRAM/Native/StackRecursionTree.lean index d1e011cbde..0f72261147 100644 --- a/Complexity/Computability/GenericRAM/Native/StackRecursionTree.lean +++ b/Complexity/Computability/GenericRAM/Native/StackRecursionTree.lean @@ -26,7 +26,8 @@ satisfying `I.node`, such that a leaf has its leaf value, and a call at depth `d `b d` counted children, all at depth `d + 1` and smaller in a measure, with its value the `finish` of the accumulated children's values. Then every node at depth `d ≤ D` has a derivation of its value, with at most `Nat.recursionBound b (D - d) d` nodes and at most `D - d` stack -levels. This is the induction "from the leaves" of a recursive algorithm, with its node count. +levels. This is the induction "from the leaves" of a recursive algorithm, with its node count; +without the count, `Scheme.exists_eval_of_measure` is the induction alone. **What a run returns** (`Scheme.value_of_run_eq_some`). Every value the clocked run returns, for any fuel, satisfies the invariant of a derivation of the root. If the values carry a tag that @@ -36,6 +37,8 @@ small to vouch for the run. ## Main results * `Cslib.GenericRAM.StackRecursion.Scheme.Loop.counted`: the loop over counted children. +* `Cslib.GenericRAM.StackRecursion.Scheme.exists_eval_of_measure`: derivations by well-founded + recursion. * `Cslib.GenericRAM.StackRecursion.Scheme.exists_eval_of_counted`: derivations with the node count of the recursion tree. * `Cslib.GenericRAM.StackRecursion.Scheme.value_of_run_eq_some`: a returned value satisfies the @@ -83,6 +86,37 @@ theorem Loop.counted {x : X} {acc : ℕ → A} {child : ℕ → X} {val : ℕ rwa [sum_eq_sum_Ico_succ_bot hs'] simpa using key bound 0 (by omega) +/-- **Derivations by well-founded recursion.** Let `value` assign values to the nodes satisfying +`I.node`, all satisfying `I.value`, such that a leaf has its leaf value, and a call has counted +children: from the accumulators `acc c`, starting at `start`, it asks for `child c` for `c < cnt` +and then stops, absorbing the children's values; the frames satisfy `I.frame`, the children +satisfy `I.node` and are smaller in the measure `μ`; and its value is the `finish` of the last +accumulator. Then every node has a derivation of its value. `exists_eval_of_counted` adds the node +count. -/ +theorem exists_eval_of_measure (μ : X → ℕ) (value : X → Y) + (hleaf : ∀ x, I.node x → R.isLeaf x = true → value x = R.leaf x) + (hcall : ∀ x, I.node x → R.isLeaf x = false → + ∃ (cnt : ℕ) (acc : ℕ → A) (child : ℕ → X), acc 0 = R.start x ∧ + (∀ c < cnt, R.next x (acc c) = some (child c)) ∧ R.next x (acc cnt) = none ∧ + (∀ c < cnt, R.absorb x (acc c) (value (child c)) = acc (c + 1)) ∧ + (∀ c < cnt, I.frame x (acc c) ∧ I.node (child c) ∧ μ (child c) < μ x) ∧ + value x = R.finish x (acc cnt)) + (hvalue : ∀ x, I.node x → I.value (value x)) (x : X) (hx : I.node x) : + ∃ n h, R.Eval I x (value x) n h := by + induction hμ : μ x using Nat.strong_induction_on generalizing x with + | _ k ih => + cases hl : R.isLeaf x + · obtain ⟨cnt, acc, child, h0, hnext, hdone, habs, hkids, hfin⟩ := hcall x hx hl + have hev : ∀ c < cnt, ∃ n h, R.Eval I (child c) (value (child c)) n h := fun c hc => + ih (μ (child c)) (hμ ▸ (hkids c hc).2.2) (child c) (hkids c hc).2.1 rfl + choose! nodes height hev using hev + obtain ⟨h, -, hloop⟩ := Loop.counted (I := I) (val := fun c => value (child c)) cnt + (∑ c ∈ range cnt, height c) hnext hdone habs hev (fun c hc => (hkids c hc).1) + (fun c hc => single_le_sum (fun _ _ => Nat.zero_le _) (mem_range.mpr hc)) + rw [h0] at hloop + exact ⟨_, _, hfin ▸ Eval.call hx hl hloop (hfin ▸ hvalue x hx)⟩ + · exact ⟨1, 0, by rw [hleaf x hx hl]; exact Eval.leaf hx hl (hleaf x hx hl ▸ hvalue x hx)⟩ + /-- A recursion tree has at least its root. -/ private theorem one_le_recursionBound (b : ℕ → ℕ) (k d : ℕ) : 1 ≤ Nat.recursionBound b k d := by cases k with From 4bfc713e084d1944ee876290237e5b008a2dfc10 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:09:32 -0400 Subject: [PATCH 071/129] wip: described lift and native child (in progress) --- .../Frontier/TensorNetwork/DescribedLift.lean | 424 ++++++++++++++++++ .../Frontier/TensorNetwork/NativeChild.lean | 260 +++++++++++ 2 files changed, 684 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean new file mode 100644 index 0000000000..b0e05d0cf4 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean @@ -0,0 +1,424 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ReductionLift +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.VertexDesc +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Recursion +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Membership +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListLookup +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListMap +import Mathlib.Data.List.GetD +import Mathlib.Data.List.Sort + +/-! +# A described network over `O` + +The contraction recursion of [OpenAI2026ETH] (§D.4, pp. 57–64) is proved correct over the valuation +ring `O = ℤ_(2)[U] / (f)`, while the native algorithm carries networks of descriptors +(`DescribedNetwork VertexDesc`) and computes with coefficient lists in +`A_p = (ℤ / 2 ^ p)[U] / (f)`. This file gives the network over `O` that a described network stands +for. + +* **Lifts.** A coefficient list `c` lifts to `∑ i < e, c_i U ^ i` in `O` (`GaloisRingArith.liftO`), + and reducing the lift modulo `2 ^ p` gives the value of `c` in `A_p` + (`GaloisRingArith.reduction_liftO`). This is the canonical lifting of representatives of §D.4.1 + (p. 58): a retained vertex is read through the lift of its entry at every later precision. +* **Rows first.** A described network is *rows-first bipartite* at `r` + (`DescribedNetwork.IsRowsFirstBipartite`, the condition of + `ExplicitTensorNetwork.IsRowsFirstBipartite`) when every port names an edge and every edge is a + port of exactly one of the first `r` vertices, once, and of exactly one of the others, once. The + ends of the edges are then computed by listing the vertices at each edge + (`DescribedNetwork.incident`, `DescribedNetwork.edgeEnds`), in polynomial time + (`DescribedNetwork.computableInPolyTime_edgeEnds`). +* **The network** (`DescribedNetwork.toNetwork`). A rows-first described network and an evaluator + give a Boolean tensor network on `Fin k` vertices and `Fin m` edges with these ends, whose + tensor at a vertex is the evaluator at its descriptor and the bits on its ports. It has the + contraction of the described network (`DescribedNetwork.contraction_toNetwork`). Over `O`, with + the lifts of the entries, its contraction reduces modulo `2 ^ p` to the contraction of the + values in `A_p` (`DescribedNetwork.reduction_contraction_toNetwork`). + +## Main definitions + +* `Cslib.GaloisRingArith.liftO`: the lift of a coefficient list to `O`. +* `Cslib.Circuits.Frontier.DescribedNetwork.IsRowsFirstBipartite`: rows first. +* `Cslib.Circuits.Frontier.DescribedNetwork.incident`, + `Cslib.Circuits.Frontier.DescribedNetwork.edgeEnds`: the ends of the edges. +* `Cslib.Circuits.Frontier.DescribedNetwork.toNetwork`: the network of a described network. + +## Main results + +* `Cslib.GaloisRingArith.reduction_liftO`, `Cslib.GaloisRingArith.mk_liftO`: reducing a lift. +* `Cslib.Circuits.Frontier.DescribedNetwork.incident_eq`: the vertices at an edge. +* `Cslib.Circuits.Frontier.DescribedNetwork.contraction_toNetwork`, + `Cslib.Circuits.Frontier.DescribedNetwork.reduction_contraction_toNetwork`: the contraction. +* `Cslib.Circuits.Frontier.DescribedNetwork.computableInPolyTime_edgeEnds`: polynomial time. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.1 (pp. 57–58) and + §D.4.6 (pp. 63–64). +-/ + +public section + +open Polynomial AdjoinRoot Finset + +/-! ### Lifts of coefficient lists -/ + +namespace AdjoinRoot + +/-- `ℤ_(2)` lies in the valuation ring `O` of `ℚ[U] / (f)`. -/ +theorem algebraMap_mem_integer_padicValuation {f : ℤ[X]} (hf : f.Monic) + (hirr : Irreducible (f.map (Int.castRingHom (ZMod 2)))) : + ∀ c ∈ Valuation.integer (Rat.padicValuation 2), + algebraMap ℚ (AdjoinRoot (f.map (Int.castRingHom ℚ))) c ∈ + (padicValuation 2 hf hirr).integer := fun c hc => by + rw [Valuation.mem_integer_iff, padicValuation_algebraMap] + exact hc + +end AdjoinRoot + +namespace Cslib.GaloisRingArith + +variable {fl : List ℕ} (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2)))) + +/-- The class of `U` in the valuation ring `O = ℤ_(2)[U] / (f)`, `f = monicOf fl`. -/ +noncomputable def rootO : (padicValuation 2 (monic_monicOf fl) hirr).integer := + ⟨map (Int.castRingHom ℚ) (monicOf fl) _ dvd_rfl (root _), + (Valuation.mem_integer_iff _ _).mpr (padicValuation_map_le_one _ hirr _)⟩ + +/-- **The lift of a coefficient list to `O`**: `∑ i < e, c_i U ^ i`, with `e = fl.length`. -/ +noncomputable def liftO (c : List ℕ) : (padicValuation 2 (monic_monicOf fl) hirr).integer := + ∑ i ∈ range fl.length, ((c.getD i 0 : ℕ) : (padicValuation 2 (monic_monicOf fl) hirr).integer) * + rootO hirr ^ i + +/-- Reduction sends `U` to `U`. -/ +theorem reduction_rootO (p : ℕ) : + reduction (monic_monicOf fl) hirr p (rootO hirr) = root _ := by + rw [rootO, reduction_toRat, map_root] + +/-- **Reducing a lift modulo `2 ^ p` gives the value in `A_p`.** -/ +theorem reduction_liftO (p : ℕ) (c : List ℕ) : + reduction (monic_monicOf fl) hirr p (liftO hirr c) = val p fl c := by + simp only [liftO, val, map_sum, map_mul, map_natCast, map_pow, reduction_rootO] + +/-- **The class of a lift modulo `2 ^ p`** is the image of the value in `A_p`. -/ +theorem mk_liftO (p : ℕ) (c : List ℕ) : + Ideal.Quotient.mk _ (liftO hirr c) = + Cslib.Circuits.Frontier.reductionLift (monic_monicOf fl) hirr p (val p fl c) := by + rw [← reduction_liftO hirr p, Cslib.Circuits.Frontier.reductionLift_reduction] + +end Cslib.GaloisRingArith + +namespace Cslib.Circuits.Frontier + +/-! ### Lists of lists with one occurrence -/ + +/-- **One occurrence among the lists.** If an element occurs once in total among a list of +lists, exactly one of the lists contains it, once. -/ +theorem exists_unique_of_sum_count_eq_one {Ls : List (List ℕ)} {j : ℕ} + (h : (Ls.map fun l => l.count j).sum = 1) : + ∃ i, ∃ hi : i < Ls.length, Ls[i].count j = 1 ∧ + ∀ i' (hi' : i' < Ls.length), j ∈ Ls[i'] → i' = i := by + induction Ls with + | nil => simp at h + | cons l Ls ih => + rw [List.map_cons, List.sum_cons] at h + by_cases hj : j ∈ l + · have hl : l.count j = 1 := by + have := List.count_pos_iff.mpr hj + omega + have h0 : (Ls.map fun l => l.count j).sum = 0 := by omega + refine ⟨0, by simp, by simpa using hl, fun i' hi' hm => ?_⟩ + rcases i' with _ | i' + · rfl + · exfalso + have hi'' : i' < Ls.length := by simpa using hi' + simp only [List.getElem_cons_succ] at hm + have hpos := List.count_pos_iff.mpr hm + have hle : Ls[i'].count j ≤ (Ls.map fun l => l.count j).sum := + List.le_sum_of_mem (List.mem_map.mpr ⟨_, List.getElem_mem _, rfl⟩) + omega + · have hl : l.count j = 0 := List.count_eq_zero.mpr hj + obtain ⟨i, hi, hc, hu⟩ := ih (by omega) + refine ⟨i + 1, by simpa using hi, by simpa using hc, fun i' hi' hm => ?_⟩ + rcases i' with _ | i' + · exact absurd hm (by simpa using hj) + · simp only [List.getElem_cons_succ] at hm + rw [hu i' (by simpa using hi') hm] + +namespace DescribedNetwork + +variable {T : Type} (D : DescribedNetwork T) + +/-! ### Rows first -/ + +/-- **Rows-first bipartite**: the first `r` vertices are the *rows* and the others the *columns*, +every port names an edge, and every edge is a port of the rows exactly once and of the columns +exactly once (the condition of `ExplicitTensorNetwork.IsRowsFirstBipartite`). -/ +@[expose] def IsRowsFirstBipartite (r : ℕ) : Prop := + (∀ v ∈ D.vertices, ∀ i ∈ v.1, i < D.numEdges) ∧ + ∀ i < D.numEdges, ((D.vertices.take r).map fun v => v.1.count i).sum = 1 ∧ + ((D.vertices.drop r).map fun v => v.1.count i).sum = 1 + +instance (r : ℕ) : Decidable (D.IsRowsFirstBipartite r) := by + unfold IsRowsFirstBipartite + infer_instance + +/-- An explicit network is rows-first exactly when it is as a described network. -/ +theorem isRowsFirstBipartite_ofExplicit (d : ExplicitTensorNetwork ℕ) (r : ℕ) : + (ofExplicit d).IsRowsFirstBipartite r ↔ d.IsRowsFirstBipartite r := + Iff.rfl + +/-- The vertices at the edge `j`: those having `j` among their ports, in increasing order. -/ +@[expose] def incident (j : ℕ) : List ℕ := + (List.range D.vertices.length).filter fun v => j ∈ (D.vertices.map Prod.fst).getD v [] + +/-- **The ends of the edges**: for each edge, its first and its second vertex. -/ +@[expose] def edgeEnds : List (ℕ × ℕ) := + (List.range D.numEdges).map fun j => ((D.incident j).getD 0 0, (D.incident j).getD 1 0) + +variable {D} + +theorem getD_map_fst (v : ℕ) (hv : v < D.vertices.length) : + (D.vertices.map Prod.fst).getD v [] = D.vertices[v].1 := by + rw [List.getD_eq_getElem _ _ (by simpa using hv), List.getElem_map] + +/-- **The two ends of an edge** of a rows-first network: a row `s < r` and a column `t ≥ r`, which +are the only vertices with the edge among their ports, each once. -/ +theorem exists_ends {r : ℕ} (hD : D.IsRowsFirstBipartite r) {j : ℕ} (hj : j < D.numEdges) : + ∃ s t, ∃ hs : s < D.vertices.length, ∃ ht : t < D.vertices.length, s < r ∧ r ≤ t ∧ + D.vertices[s].1.count j = 1 ∧ D.vertices[t].1.count j = 1 ∧ + ∀ v (hv : v < D.vertices.length), j ∈ D.vertices[v].1 → v = s ∨ v = t := by + obtain ⟨hrow', hcol'⟩ := hD.2 j hj + have hrow : (((D.vertices.map Prod.fst).take r).map fun l => l.count j).sum = 1 := by + rw [← List.map_take, List.map_map] + exact hrow' + have hcol : (((D.vertices.map Prod.fst).drop r).map fun l => l.count j).sum = 1 := by + rw [← List.map_drop, List.map_map] + exact hcol' + obtain ⟨s, hs, hsc, hsu⟩ := exists_unique_of_sum_count_eq_one hrow + obtain ⟨t, ht, htc, htu⟩ := exists_unique_of_sum_count_eq_one hcol + simp only [List.length_take, List.length_map, List.length_drop] at hs ht + have hs' : s < D.vertices.length := by omega + have hsr : s < r := by omega + have ht' : r + t < D.vertices.length := by omega + refine ⟨s, r + t, hs', ht', hsr, by omega, ?_, ?_, fun v hv hm => ?_⟩ + · simpa using hsc + · simpa using htc + · by_cases hvr : v < r + · left + refine hsu v (by simp; omega) ?_ + simpa [List.getElem_take] using hm + · right + have := htu (v - r) (by simp; omega) (by + simp only [List.getElem_map, List.getElem_drop] + convert hm using 3 + omega) + omega + +/-- **The vertices at an edge** of a rows-first network are its row and its column. -/ +theorem incident_eq {r : ℕ} (hD : D.IsRowsFirstBipartite r) {j : ℕ} (hj : j < D.numEdges) : + ∃ s t, ∃ hs : s < D.vertices.length, ∃ ht : t < D.vertices.length, s < r ∧ r ≤ t ∧ + D.vertices[s].1.count j = 1 ∧ D.vertices[t].1.count j = 1 ∧ D.incident j = [s, t] := by + obtain ⟨s, t, hs, ht, hsr, hrt, hsc, htc, hu⟩ := exists_ends hD hj + refine ⟨s, t, hs, ht, hsr, hrt, hsc, htc, ?_⟩ + have hsorted : (D.incident j).Pairwise (· < ·) := + (List.pairwise_lt_range).filter _ + have hmem : ∀ v, v ∈ D.incident j ↔ v ∈ [s, t] := by + intro v + simp only [incident, List.mem_filter, List.mem_range, decide_eq_true_eq, List.mem_cons, + List.not_mem_nil, or_false] + constructor + · rintro ⟨hv, hm⟩ + rw [getD_map_fst v hv] at hm + exact hu v hv hm + · rintro (rfl | rfl) + · refine ⟨hs, ?_⟩ + rw [getD_map_fst _ hs] + exact List.count_pos_iff.mp (by omega) + · refine ⟨ht, ?_⟩ + rw [getD_map_fst _ ht] + exact List.count_pos_iff.mp (by omega) + have hst : [s, t].Pairwise (· < ·) := by simp; omega + exact hsorted.eq_of_mem_iff hst hmem + +/-- The first vertex at an edge of a rows-first network is a vertex. -/ +theorem getD_incident_zero_lt {r : ℕ} (hD : D.IsRowsFirstBipartite r) {j : ℕ} + (hj : j < D.numEdges) : (D.incident j).getD 0 0 < D.vertices.length := by + obtain ⟨s, t, hs, ht, -, -, -, -, h⟩ := incident_eq hD hj + rw [h] + exact hs + +/-- The second vertex at an edge of a rows-first network is a vertex. -/ +theorem getD_incident_one_lt {r : ℕ} (hD : D.IsRowsFirstBipartite r) {j : ℕ} + (hj : j < D.numEdges) : (D.incident j).getD 1 0 < D.vertices.length := by + obtain ⟨s, t, hs, ht, -, -, -, -, h⟩ := incident_eq hD hj + rw [h] + exact ht + +/-! ### The network of a described network -/ + +/-- The row of an edge of a rows-first network. -/ +@[expose] def srcFin {r : ℕ} (hD : D.IsRowsFirstBipartite r) (j : Fin D.numEdges) : + Fin D.vertices.length := + ⟨(D.incident j).getD 0 0, getD_incident_zero_lt hD j.2⟩ + +/-- The column of an edge of a rows-first network. -/ +@[expose] def tgtFin {r : ℕ} (hD : D.IsRowsFirstBipartite r) (j : Fin D.numEdges) : + Fin D.vertices.length := + ⟨(D.incident j).getD 1 0, getD_incident_one_lt hD j.2⟩ + +/-- **The network of a rows-first described network**, on `Fin k` vertices and `Fin m` edges: the +edge `j` runs from its row to its column, and the tensor of a vertex is the evaluator at its +descriptor and the bits on its ports. -/ +@[expose] noncomputable def toNetwork {r : ℕ} (hD : D.IsRowsFirstBipartite r) {R : Type*} + (eval : T → List Bool → R) : + TensorNetwork R Bool (Fin D.vertices.length) (Fin D.numEdges) where + src := D.srcFin hD + tgt := D.tgtFin hD + tensor v x := eval D.vertices[v].2 (wordBits (List.ofFn x) D.vertices[v].1) + tensor_congr v x y hxy := by + congr 1 + refine List.map_congr_left fun i hi => ?_ + have hi' : i < D.numEdges := hD.1 _ (List.getElem_mem _) i hi + obtain ⟨s, t, hs, ht, -, -, -, -, hinc⟩ := incident_eq hD hi' + have hv : (v : ℕ) ∈ D.incident i := by + simp only [incident, List.mem_filter, List.mem_range, decide_eq_true_eq] + exact ⟨v.2, by rw [getD_map_fst _ v.2]; exact hi⟩ + rw [List.getD_eq_getElem _ _ (by simpa using hi'), List.getD_eq_getElem _ _ (by simpa using hi'), + List.getElem_ofFn, List.getElem_ofFn] + refine hxy ⟨i, hi'⟩ ?_ + rw [hinc] at hv + simp only [List.mem_cons, List.not_mem_nil, or_false] at hv + rcases hv with hv | hv + · left + exact Fin.ext (by simp [srcFin, hinc, hv]) + · right + exact Fin.ext (by simp [tgtFin, hinc, hv]) + +variable {r : ℕ} (hD : D.IsRowsFirstBipartite r) + +@[simp] theorem toNetwork_src {R : Type*} (eval : T → List Bool → R) : + (D.toNetwork hD eval).src = D.srcFin hD := rfl + +@[simp] theorem toNetwork_tgt {R : Type*} (eval : T → List Bool → R) : + (D.toNetwork hD eval).tgt = D.tgtFin hD := rfl + +/-- The edges run from the rows. -/ +theorem srcFin_lt (j : Fin D.numEdges) : (D.srcFin hD j : ℕ) < r := by + obtain ⟨s, t, -, -, hsr, -, -, -, h⟩ := incident_eq hD j.2 + simp [srcFin, h, hsr] + +/-- The edges end at the columns. -/ +theorem le_tgtFin (j : Fin D.numEdges) : r ≤ (D.tgtFin hD j : ℕ) := by + obtain ⟨s, t, -, -, -, hrt, -, -, h⟩ := incident_eq hD j.2 + simp [tgtFin, h, hrt] + +/-- A vertex has an edge among its ports exactly when it is an end of the edge. -/ +theorem mem_ports_iff (v : Fin D.vertices.length) (j : Fin D.numEdges) : + (j : ℕ) ∈ D.vertices[v].1 ↔ D.srcFin hD j = v ∨ D.tgtFin hD j = v := by + obtain ⟨s, t, hs, ht, -, -, hsc, htc, hinc⟩ := incident_eq hD j.2 + have hmem : (v : ℕ) ∈ D.incident j ↔ (j : ℕ) ∈ D.vertices[v].1 := by + simp only [incident, List.mem_filter, List.mem_range, decide_eq_true_eq] + rw [getD_map_fst _ v.2] + exact ⟨fun h => h.2, fun h => ⟨v.2, h⟩⟩ + rw [← hmem, hinc] + simp only [List.mem_cons, List.not_mem_nil, or_false, Fin.ext_iff, srcFin, tgtFin, hinc, + List.getD_cons_zero, List.getD_cons_succ] + exact ⟨fun h => h.imp Eq.symm Eq.symm, fun h => h.imp Eq.symm Eq.symm⟩ + +/-- **The ends of the edges** are the native `edgeEnds`. -/ +theorem edgeEnds_eq : + D.edgeEnds = List.ofFn fun j => ((D.srcFin hD j : ℕ), (D.tgtFin hD j : ℕ)) := by + rw [edgeEnds, ← List.map_coe_finRange_eq_range, List.map_map, List.ofFn_eq_map] + rfl + +/-- **The network has the contraction of the described network.** -/ +theorem contraction_toNetwork {R : Type*} [CommSemiring R] (eval : T → List Bool → R) : + (D.toNetwork hD eval).contraction = D.contraction eval := by + simp only [TensorNetwork.contraction, contraction] + refine sum_congr rfl fun x _ => ?_ + rw [← List.ofFn_getElem_eq_map, List.prod_ofFn] + rfl + +/-- The network of a described network commutes with maps of the entries. -/ +theorem toNetwork_map {R S : Type*} [CommSemiring R] [CommSemiring S] (eval : T → List Bool → R) + (ψ : R →+* S) : (D.toNetwork hD eval).map ψ = D.toNetwork hD fun t bs => ψ (eval t bs) := + rfl + +/-- **Over `O`, the contraction reduces to the contraction in `A_p`.** For the network with the +lifts of the entries of the descriptors, reduction modulo `2 ^ p` of its contraction is the +contraction of the described network with the values of the entries in `A_p`. -/ +theorem reduction_contraction_toNetwork {fl : List ℕ} + (hirr : Irreducible ((GaloisRingArith.monicOf fl).map (Int.castRingHom (ZMod 2)))) + (eval : T → List Bool → List ℕ) (p : ℕ) : + reduction (GaloisRingArith.monic_monicOf fl) hirr p + (D.toNetwork hD fun t bs => GaloisRingArith.liftO hirr (eval t bs)).contraction = + D.contraction fun t bs => GaloisRingArith.val p fl (eval t bs) := by + rw [contraction_toNetwork, map_contraction] + simp only [GaloisRingArith.reduction_liftO] + +/-! ### Polynomial time -/ + +section PolyTime + +open Turing.MultiTapeTM + +variable {T : Type} (encT : T ↪ List Bool) + +/-- **The vertices at an edge are polynomial-time**, with the edge in unary. -/ +theorem computableInPolyTime_incident : + ComputableInPolyTime (fun q : DescribedNetwork T × ℕ => q.1.incident q.2) + (encPair (encDescribed encT) encUnary) (encList encUnary) := by + have hV : ComputableInPolyTime (fun q : DescribedNetwork T × ℕ => q.1.vertices) + (encPair (encDescribed encT) encUnary) (encList (encPair (encList encNat) encT)) := + vertices_polyTime.comp (ComputableInPolyTime.fst _ _) + have hP : ComputableInPolyTime (fun q : DescribedNetwork T × ℕ => q.1.vertices.map Prod.fst) + (encPair (encDescribed encT) encUnary) (encList (encList encNat)) := + ComputableInPolyTime.mapWithInput (f := fun _ v => v.1) hV + ((ComputableInPolyTime.fst _ _).comp (ComputableInPolyTime.snd _ _)) + have hrange : ComputableInPolyTime (fun q : DescribedNetwork T × ℕ => + List.range q.1.vertices.length) (encPair (encDescribed encT) encUnary) (encList encUnary) := + ComputableInPolyTime.range.comp ((ComputableInPolyTime.length_list _).comp hV) + have hports : ComputableInPolyTime (fun z : (DescribedNetwork T × ℕ) × ℕ => + (z.1.1.vertices.map Prod.fst).getD z.2 []) + (encPair (encPair (encDescribed encT) encUnary) encUnary) (encList encNat) := + (ComputableInPolyTime.listGetD (encA := encList encNat) []).comp + ((hP.comp (ComputableInPolyTime.fst _ _)).prod (ComputableInPolyTime.snd _ _)) + have hj : ComputableInPolyTime (fun z : (DescribedNetwork T × ℕ) × ℕ => z.1.2) + (encPair (encPair (encDescribed encT) encUnary) encUnary) encNat := + ComputableInPolyTime.unary_to_binary.comp + ((ComputableInPolyTime.snd _ _).comp (ComputableInPolyTime.fst _ _)) + exact ComputableInPolyTime.filterWithInput + (test := fun q v => decide (q.2 ∈ (q.1.vertices.map Prod.fst).getD v [])) hrange + (ComputableInPolyTime.decide_mem hports hj) + +/-- **The ends of the edges are polynomial-time.** -/ +theorem computableInPolyTime_edgeEnds : + ComputableInPolyTime (fun D : DescribedNetwork T => D.edgeEnds) (encDescribed encT) + (encList (encPair encNat encNat)) := by + have hinc : ComputableInPolyTime (fun q : DescribedNetwork T × ℕ => q.1.incident q.2) + (encPair (encDescribed encT) encUnary) (encList encUnary) := + computableInPolyTime_incident encT + have hget (i : ℕ) : ComputableInPolyTime (fun q : DescribedNetwork T × ℕ => + (q.1.incident q.2).getD i 0) (encPair (encDescribed encT) encUnary) encNat := + ComputableInPolyTime.unary_to_binary.comp + ((ComputableInPolyTime.listGetD (encA := encUnary) 0).comp + (hinc.prod (ComputableInPolyTime.const i))) + exact ComputableInPolyTime.mapWithInput + (f := fun D j => ((D.incident j).getD 0 0, (D.incident j).getD 1 0)) + (ComputableInPolyTime.range.comp numEdges_polyTime) ((hget 0).prod (hget 1)) + +end PolyTime + +end DescribedNetwork + +end Cslib.Circuits.Frontier diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean new file mode 100644 index 0000000000..cd5f8632b3 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean @@ -0,0 +1,260 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DescribedLift +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildEntries +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildNumbering +public import Complexity.Computability.PolyTime.PacketPacking +public import Complexity.Foundations.Combinatorics.BudgetTuples +import Mathlib.Data.List.GetD + +/-! +# The native child networks of an active call + +An active call of the contraction recursion of [OpenAI2026ETH] (§D.4.5–§D.4.7, pp. 62–64) on a +network of vertex descriptors evaluates, for each node `t`, each interpolation value `ζ`, each +exponent tuple `β` and each group `g`, the child network of group `g`. This file builds that child +natively as a network of descriptors (`NativeChild.nativeChild`), from the parent, the native +packing of its edges (`Cslib.PacketPacking.nativePacking`) and `(t, ζ, β, g)`, and proves that it +has the contraction of the child of the mode splitting at the node (`TensorNetwork.callSplitting`). + +**The child.** Its vertices are numbered as in `ChildNumbering.childVertexEquiv` and its edges as +in `ChildNumbering.childEdgeEquiv`: + +* the vertices of group `g`, in order, keep their descriptors, and their ports are their parent + ports, *in the parent's order*, each renumbered to its child edge: a row port by its position + among the row ports of group `g`, a column port by the number of row ports plus its position + among the column ports (`NativeChild.childEdgeOf`). A retained vertex reads its ports in its own + order, so its ports are not sorted; +* the packet `q` is a new vertex whose ports are its child edges in increasing order + (`ChildNumbering.childPorts`), each tagged with whether it is a row port and with the slot of its + parent edge, and whose descriptor carries the node, the value `ζ`, the symbols of `q` against + `g` and the slice of `β` at `q` (`NativeChild.packetCtx`, `NativeChild.betaSlice`), at the + child's precision `p_t`. + +The modes of the node are listed packet-major (`NativeChild.modeList`): the mode `(q, y, b)` is at +`q · 2 ^ (i + 1) · p_t + y · p_t + b`, so the dense exponents of packet `q` are a slice of `β`, and +`β` stands for the mode tuple `BudgetTuples.ofList (modeList …) β`. + +**Correctness** (`NativeChild.contraction_nativeChild`). Over `O = ℤ_(2)[U] / (f)` the parent is +the network `DescribedNetwork.toNetwork` with the lifts of its entries, and its packing is the +native packing, which is `PacketPacking.packing` of its edges (`PacketPacking.nativePacking_eq`), +moved to the ends of the parent (`NativeChild.parentPacking`). Along `reductionLift`, the entries of +the native child in `A_{p_t}` give the contraction of the child of `callSplitting` at the lift of +`ζ` and the mode tuple of `β`: the retained vertices by `ModeSplitting.child_tensor_inl_eq_tensor` +and the packets by `TensorNetwork.map_val_entry_child_inr`, assembled by +`DescribedNetwork.contraction_eq_of_ports`. The native child is again rows-first, with its retained +vertices as rows (`NativeChild.isRowsFirstBipartite_nativeChild`). + +## Main definitions + +* `Cslib.PacketPacking.Packing.relabel`: a packing moved to other ends of the edges. +* `Cslib.Circuits.Frontier.NativeChild.nativeChild`: the native child. +* `Cslib.Circuits.Frontier.NativeChild.childRows`: its number of rows. +* `Cslib.Circuits.Frontier.NativeChild.modeList`, `Cslib.Circuits.Frontier.NativeChild.modeWeights`: + the modes of a node, packet-major, and their weights. +* `Cslib.Circuits.Frontier.NativeChild.parentPacking`: the packing of the parent over `O`. + +## Main results + +* `Cslib.Circuits.Frontier.NativeChild.contraction_nativeChild`: the native child has the + contraction of the child. +* `Cslib.Circuits.Frontier.NativeChild.isRowsFirstBipartite_nativeChild`: it is rows-first. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.5–§D.4.7, pp. 62–64. +-/ + +public section + +/-! ### Moving a packing to other ends -/ + +namespace Cslib.PacketPacking + +variable {E R C R' C' : Type*} {rowOf : E → R} {colOf : E → C} {a r : ℕ} + +/-- **A packing moved to other ends of the edges**: the same packets, slots and symbols, with the +labels of the new ends read through `ρ` and `κ`, which agree with the old labels at the ends of +every edge. -/ +@[expose] def Packing.relabel (P : Packing rowOf colOf a r) (rowOf' : E → R') (colOf' : E → C') + (ρ : R' → Fin a → Fin r) (κ : C' → Fin a → Fin r) + (hρ : ∀ e, ρ (rowOf' e) = P.rowLabel (rowOf e)) (hκ : ∀ e, κ (colOf' e) = P.colLabel (colOf e)) : + Packing rowOf' colOf' a r where + rowLabel := ρ + colLabel := κ + numPackets := P.numPackets + packet := P.packet + slot := P.slot + symbols := P.symbols + injective := P.injective + labels_eq e i := by rw [hρ, hκ]; exact P.labels_eq e i + +end Cslib.PacketPacking + +namespace Cslib.Circuits.Frontier + +/-- The subring `ℤ_(2)` of rationals with odd denominator. -/ +local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) + +namespace NativeChild + +open ChildNumbering GaloisRingArith NativePacket + +/-! ### The native child -/ + +/-- **The groups of the vertices** in coordinate `i`: digit `i` of the row word of a row +(`v < r`), and of the column word of a column. -/ +@[expose] def groupsOf (r i k : ℕ) (words : List (List ℕ) × List (List ℕ)) : List ℕ := + (List.range k).map fun v => + if v < r then (words.1.getD v []).getD i 0 else (words.2.getD (v - r) []).getD i 0 + +/-- The parent edges whose end `sel` is in group `g`, in order: the row ports of group `g` for +`sel = Prod.fst`, its column ports for `sel = Prod.snd`. -/ +@[expose] def portEdges (groups : List ℕ) (g : ℕ) (ends : List (ℕ × ℕ)) (sel : ℕ × ℕ → ℕ) : + List ℕ := + (List.range ends.length).filter fun e => groups.getD (sel (ends.getD e (0, 0))) 0 = g + +/-- The position of the edge `e` among the parent edges whose end `sel` is in group `g`. -/ +@[expose] def portRank (groups : List ℕ) (g : ℕ) (ends : List (ℕ × ℕ)) (sel : ℕ × ℕ → ℕ) + (e : ℕ) : ℕ := + (List.range e).countP fun e' => groups.getD (sel (ends.getD e' (0, 0))) 0 = g + +/-- **The child edge of the parent port `e` of the vertex `v`**: for a row, its position among the +row ports of group `g`; for a column, the number of row ports plus its position among the column +ports. -/ +@[expose] def childEdgeOf (r : ℕ) (groups : List ℕ) (g : ℕ) (ends : List (ℕ × ℕ)) (v e : ℕ) : ℕ := + if v < r then portRank groups g ends Prod.fst e + else (portEdges groups g ends Prod.fst).length + portRank groups g ends Prod.snd e + +/-- **The parent edge of the child edge `j`**: the `j`-th row port of group `g`, or the +`(j - R)`-th column port for `R` row ports. -/ +@[expose] def parentEdge (groups : List ℕ) (g : ℕ) (ends : List (ℕ × ℕ)) (j : ℕ) : ℕ := + if j < (portEdges groups g ends Prod.fst).length then + (portEdges groups g ends Prod.fst).getD j 0 + else (portEdges groups g ends Prod.snd).getD (j - (portEdges groups g ends Prod.fst).length) 0 + +/-- **The context of the packet `q`** in the child of group `g` at the node `t` of valuation `i` +and the interpolation value `ζ`: the precision `p_t` and `p_t` exponents per tile, `2 ^ a` slots, +`2 ^ (i + 1)` tiles, and the symbols of `q` in coordinate `i` against `g`. -/ +@[expose] def packetCtx (fl : List ℕ) (a i pt : ℕ) (t : ℤ) (ζ : List ℕ) (g q : ℕ) : PacketCtx where + p := pt + fl := fl + a := a + L := 2 ^ a + i := i + tiles := 2 ^ (i + 1) + t := t + nb := pt + e₁ := decide ((PacketPacking.packetSymbols a 64 q i).1 = g) + e₂ := decide ((PacketPacking.packetSymbols a 64 q i).2 = g) + differ := decide ((PacketPacking.packetSymbols a 64 q i).1 ≠ + (PacketPacking.packetSymbols a 64 q i).2) + cols := [] + ζ := ζ + +/-- **The dense exponents of the packet `q`**: its slice of the packet-major list `β`. -/ +@[expose] def betaSlice (i pt : ℕ) (β : List ℕ) (q : ℕ) : List ℕ := + (β.drop (q * (2 ^ (i + 1) * pt))).take (2 ^ (i + 1) * pt) + +/-- The default vertex: no ports and the empty table. -/ +abbrev defaultVertex : List ℕ × VertexDesc := ([], .inl []) + +/-- **The native child** of group `g` at the node `t` of valuation `i`, the interpolation value +`ζ`, the precision `p_t` and the packet-major exponents `β`, of the rows-first parent `D` with `r` +rows and the native packing `pk` of its edges: the vertices of group `g` with their descriptors +and renumbered ports, then one packet vertex per packet. -/ +@[expose] def nativeChild (fl : List ℕ) (a i pt : ℕ) (t : ℤ) (ζ : List ℕ) (r : ℕ) + (D : DescribedNetwork VertexDesc) + (pk : (List (List ℕ) × List (List ℕ)) × ℕ × List (ℕ × ℕ)) (β : List ℕ) (g : ℕ) : + DescribedNetwork VertexDesc := + let groups := groupsOf r i D.vertices.length pk.1 + let cedges := childEdgeList groups g D.edgeEnds (pk.2.2.map Prod.fst) + ⟨cedges.length, + ((List.range D.vertices.length).filter fun v => groups.getD v 0 = g).map (fun v => + (((D.vertices.getD v defaultVertex).1.map (childEdgeOf r groups g D.edgeEnds v)), + (D.vertices.getD v defaultVertex).2)) ++ + (List.range pk.2.1).map fun q => + (childPorts cedges (groups.count g + q), + .inr (packetCtx fl a i pt t ζ g q, + (childPorts cedges (groups.count g + q)).map fun j => + (decide (j < (portEdges groups g D.edgeEnds Prod.fst).length), + (pk.2.2.getD (parentEdge groups g D.edgeEnds j) (0, 0)).2), + betaSlice i pt β q))⟩ + +/-- **The number of rows of the native child**: the number of vertices of group `g`. -/ +@[expose] def childRows (r i : ℕ) (D : DescribedNetwork VertexDesc) + (pk : (List (List ℕ) × List (List ℕ)) × ℕ × List (ℕ × ℕ)) (g : ℕ) : ℕ := + (groupsOf r i D.vertices.length pk.1).count g + +/-! ### The modes of a node -/ + +/-- The packet-major numbering of the modes `(q, y, b)`: `q · T p + y · p + b`. -/ +@[expose] def modeEquiv (P T p : ℕ) : Fin P × Fin T × Fin p ≃ Fin (P * (T * p)) := + (Equiv.prodCongr (Equiv.refl _) finProdFinEquiv).trans finProdFinEquiv + +/-- **The modes of a node, packet-major.** -/ +@[expose] def modeList (P T p : ℕ) : List (Fin P × Fin T × Fin p) := + List.ofFn (modeEquiv P T p).symm + +/-- **The weights of the modes, packet-major**: the mode `(q, y, b)` has weight `b + 1`. -/ +@[expose] def modeWeights (P T p : ℕ) : List ℕ := + (List.range (P * (T * p))).map fun j => j % p + 1 + +theorem modeEquiv_apply_val {P T p : ℕ} (Z : Fin P × Fin T × Fin p) : + (modeEquiv P T p Z : ℕ) = Z.1 * (T * p) + (Z.2.1 * p + Z.2.2) := by + simp only [modeEquiv, Equiv.trans_apply, Equiv.prodCongr_apply, Equiv.coe_refl, Prod.map, + id_eq, finProdFinEquiv_apply_val] + ring + +theorem nodup_modeList (P T p : ℕ) : (modeList P T p).Nodup := + List.nodup_ofFn.mpr (modeEquiv P T p).symm.injective + +theorem mem_modeList {P T p : ℕ} (Z : Fin P × Fin T × Fin p) : Z ∈ modeList P T p := + List.mem_ofFn.mpr ⟨modeEquiv P T p Z, by simp⟩ + +theorem idxOf_modeList {P T p : ℕ} (Z : Fin P × Fin T × Fin p) : + (modeList P T p).idxOf Z = modeEquiv P T p Z := by + have h : (modeList P T p)[(modeEquiv P T p Z : ℕ)]'(by simp [modeList]) = Z := by + simp [modeList] + rw [← List.Nodup.idxOf_getElem (nodup_modeList P T p) _ (by simp [modeList]), h] + +/-- The weights listed along the modes are `modeWeights`. -/ +theorem map_modeList_weight (P T p : ℕ) : + (modeList P T p).map (fun Z => (Z.2.2 : ℕ) + 1) = modeWeights P T p := by + apply List.ext_getElem (by simp [modeList, modeWeights]) + intro j h₁ h₂ + simp only [modeList, List.getElem_map, List.getElem_ofFn, modeWeights, List.getElem_range] + congr 1 + have := modeEquiv_apply_val (P := P) (T := T) (p := p) ((modeEquiv P T p).symm ⟨j, by simpa + [modeList] using h₁⟩) + rw [Equiv.apply_symm_apply] at this + have hb := ((modeEquiv P T p).symm ⟨j, by simpa [modeList] using h₁⟩).2.2.2 + simp only at this + rw [this, Nat.add_mod, Nat.add_mod (_ * p), Nat.mul_mod_left, show (T * p) = p * T by ring, + ← Nat.mul_assoc, Nat.mul_mod_left] + simp [Nat.mod_eq_of_lt hb] + +/-- **The slice of a packet** reads the mode tuple of `β`: entry `y · p + b` of the slice of `q` is +the exponent of the mode `(q, y, b)`. -/ +theorem getD_betaSlice {P i pt : ℕ} (β : List ℕ) (q : Fin P) (Z : Fin (2 ^ (i + 1)) × Fin pt) : + (betaSlice i pt β q).getD (Z.1 * pt + Z.2) 0 = + BudgetTuples.ofList (modeList P (2 ^ (i + 1)) pt) β (q, Z) := by + have hZ : (Z.1 : ℕ) * pt + Z.2 < 2 ^ (i + 1) * pt := by + have h1 := Z.1.2 + have h2 := Z.2.2 + calc (Z.1 : ℕ) * pt + Z.2 < Z.1 * pt + pt := by omega + _ = (Z.1 + 1) * pt := by ring + _ ≤ 2 ^ (i + 1) * pt := Nat.mul_le_mul_right _ h1 + rw [BudgetTuples.ofList, idxOf_modeList, modeEquiv_apply_val, betaSlice, List.getD_eq_getD_get?, + List.getElem?_take_of_lt hZ, List.getElem?_drop, ← List.getD_eq_getD_get?] + +end NativeChild + +end Cslib.Circuits.Frontier From 470d392c29448ce4c5c3453a084cd058fd8f7fbf Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:15:03 -0400 Subject: [PATCH 072/129] feat(circuit/frontier): the native contraction recursion as a stack recursion, its derivations and correctness from the step Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/FastContraction.lean | 419 ++++++++++++++++++ 1 file changed, 419 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean new file mode 100644 index 0000000000..e63da90ef1 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean @@ -0,0 +1,419 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildCursor +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeRecovery +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ScheduledRecursion +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.VertexDesc +public import Complexity.Computability.GenericRAM.Native.StackRecursionTree +public import Complexity.Computability.PolyTime.PacketPacking +import Mathlib.Data.List.GetD + +/-! +# The native contraction recursion as a stack recursion + +The recursion of Theorem D.1 of [OpenAI2026ETH] (Appendix D.4.7, p. 64), as a depth-first stack +recursion (`Cslib.GenericRAM.StackRecursion.Scheme`) over *calls*. A call +(`FastContraction.Call`) is a described network (`DescribedNetwork VertexDesc`) whose first `r` +vertices are its rows, with the schedule's scale `s` (the number of edges of the root, since the +machine sees no other size), its depth `d`, an edge bound, its precision `p` and the coefficients +`fl` of the modulus `f`. + +**Active calls and leaves.** A call with `M` edges is *active* (`FastContraction.active`) when the +schedule makes it an active call (`ContractionSchedule.callParams`, with `a = slotBits s d`, +`u = taper s d` and `h = ⌈3 M / u⌉`), its parameters satisfy the hypotheses of Lemma D.5, its +degrees are at most `degreeBound s`, the `M (2 L - 1) + 1` interpolation points are distinct +residues (`n ≤ 2 ^ e` for `e = deg f`), and `M` is below its edge bound. Every other call is a leaf, +evaluated by direct summation over the coefficient lists of `A_p` (`FastContraction.leafValue`). +A child receives its parent's number of edges as its bound, so the recursion terminates, as +`TensorNetwork.contractRec` does; a child that is not smaller is a leaf. + +**An active call** walks the children `(t, k, β, g)` with the cursor of +`Cslib.Circuits.Frontier.ChildCursor`, over the nodes `ts` of positive multiplicity +(`FastContraction.nodes`), the points, the mode tuples of weight below `p_t` and the `64` groups. +The child at a cursor state is given by a child builder `mk`, which computes the child's rows and +network (the native child of §D.4.6). The call absorbs the children's values with the operations of +`A_N` at the common precision `N = p + Δ'` (`FastContraction.wide`), where +`Δ' = (a + 1) 2 ^ (a + 1) h u + 8 h 2 ^ a + 8 M (a + 1)` bounds both the shift of Lemma D.5 and +every node precision `p_t - p`; it then stores the sums at the rows `ts[i]` of the table of native +recovery (`FastContraction.table`) and recovers its value with +`Cslib.Circuits.Frontier.NativeRecovery.recover`. + +**Values and derivations.** The values of the scheme are tagged with the depth. +`FastContraction.value` is the value of a call by well-founded recursion on the edge bound, and +`FastContraction.exists_eval` derives it in the scheme, so a run returning a value tagged `0` from a +root at depth `0` returns `value` of the root, for every fuel (`FastContraction.eq_value_of_run`). +**Correctness** (`FastContraction.val_value`): if the step of an active call is correct +(`FastContraction.StepCorrect`, the correctness of one call from the correctness of its children, +through Lemma D.5) and a well-formedness predicate passes to the children, then every well-formed +call computes the contraction of its network in `A_p`. + +## Main definitions + +* `Cslib.Circuits.Frontier.FastContraction.Call`: a call. +* `Cslib.Circuits.Frontier.FastContraction.active`: whether a call is active. +* `Cslib.Circuits.Frontier.FastContraction.scheme`: the stack recursion. +* `Cslib.Circuits.Frontier.FastContraction.value`: the value of a call. +* `Cslib.Circuits.Frontier.FastContraction.StepCorrect`: the correctness of one active call. + +## Main results + +* `Cslib.Circuits.Frontier.FastContraction.exists_eval`: derivations of the values. +* `Cslib.Circuits.Frontier.FastContraction.eq_value_of_run`: a tagged value returned by the run is + the root's value. +* `Cslib.Circuits.Frontier.FastContraction.val_value`: correctness from the step. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Appendix D.4.5–D.4.7 + (pp. 62–64). +-/ + +public section + +namespace Cslib.Circuits.Frontier.FastContraction + +open GaloisRingArith ChildCursor NativeRecovery ContractionSchedule Cslib.GenericRAM + Cslib.GenericRAM.StackRecursion + +/-- The parameters of a call: the scale `s`, the depth `d`, the edge bound, the precision `p`, the +coefficients `fl` of the modulus and the number `r` of rows. -/ +abbrev Params : Type := ℕ × ℕ × ℕ × ℕ × List ℕ × ℕ + +/-- **A call** of the recursion: its parameters and its network, whose first `r` vertices are its +rows. -/ +abbrev Call : Type := Params × DescribedNetwork VertexDesc + +namespace Call + +variable (x : Call) + +/-- The scale `s` of the schedule. -/ +abbrev scale : ℕ := x.1.1 +/-- The depth. -/ +abbrev depth : ℕ := x.1.2.1 +/-- The edge bound: the call is a leaf unless it has fewer edges. -/ +abbrev bound : ℕ := x.1.2.2.1 +/-- The precision `p`. -/ +abbrev prec : ℕ := x.1.2.2.2.1 +/-- The coefficients of the modulus `f = monicOf fl`. -/ +abbrev fl : List ℕ := x.1.2.2.2.2.1 +/-- The number of rows. -/ +abbrev rows : ℕ := x.1.2.2.2.2.2 +/-- The network. -/ +abbrev net : DescribedNetwork VertexDesc := x.2 +/-- The number `M` of edges. -/ +abbrev edges : ℕ := x.2.numEdges + +end Call + +/-! ### The data of a call -/ + +variable (x : Call) + +/-- The packets have `2 ^ a` slots, `a = slotBits s d`. -/ +@[expose] def slots : ℕ := slotBits x.scale x.depth + +/-- The number `u = taper s d` of levels of positive multiplicity. -/ +@[expose] def levels : ℕ := taper x.scale x.depth + +/-- The multiplicity step `h = ⌈3 M / u⌉`. -/ +@[expose] def step : ℕ := taperStep x.scale x.depth x.edges + +/-- The number `n = M (2 L - 1) + 1` of interpolation points. -/ +@[expose] def numPoints : ℕ := x.edges * (2 * 2 ^ slots x - 1) + 1 + +/-- **Whether a call is active**: the schedule makes it an active call, its parameters satisfy +Lemma D.5 (`1 ≤ u ≤ M`, `u ≤ a`), its degrees are at most `degreeBound s`, its interpolation +points are distinct residues (`n ≤ 2 ^ e`), and it has fewer edges than its bound. -/ +@[expose] def active : Bool := + decide (leafSize x.scale < x.edges ∧ 1 ≤ levels x ∧ levels x ≤ x.edges ∧ levels x ≤ slots x ∧ + (∀ v ∈ x.net.vertices, v.1.length ≤ degreeBound x.scale) ∧ numPoints x ≤ 2 ^ x.fl.length ∧ + x.edges < x.bound) + +/-- An active call has fewer edges than its bound. -/ +theorem edges_lt_bound_of_active {x : Call} (h : active x = true) : x.edges < x.bound := by + simp only [active, decide_eq_true_eq] at h + exact h.2.2.2.2.2.2 + +/-- The level `v₂ (t)` of the node of grid index `k`, `t = k - 2 ^ a`. -/ +@[expose] def level (k : ℕ) : ℕ := twoVal (slots x + 1) (k - 2 ^ slots x + (2 ^ slots x - k)) + +/-- **The nodes of positive multiplicity**, as increasing grid indices `k < 2 ^ (a + 1)`, +`k ≠ 2 ^ a`; the node of index `k` is `NativeRecovery.node a k = k - 2 ^ a`. -/ +@[expose] def nodes : List ℕ := + (List.range (2 ^ (slots x + 1))).filter fun k => + decide (k ≠ 2 ^ slots x ∧ 0 < mult (slots x) (levels x) (step x) k) + +/-- The precision `p_t = nodePrecision p h a M (v₂ t)` of the `i`-th node. -/ +@[expose] def nodePrec (i : ℕ) : ℕ := + nodePrecision x.prec (step x) (slots x) x.edges (level x ((nodes x).getD i 0)) + +/-- The ports of the vertices. -/ +@[expose] def ports : List (List ℕ) := x.net.vertices.map Prod.fst + +/-- **The edges as `(row, column)` pairs**, the column numbered among the columns: edge `j` joins +the first row and the first column whose ports contain it. -/ +@[expose] def edgeEnds : List (ℕ × ℕ) := + (List.range x.edges).map fun j => + (((List.range x.rows).find? fun v => ((ports x).getD v []).contains j).getD 0, + ((List.range (x.net.vertices.length - x.rows)).find? fun w => + ((ports x).getD (x.rows + w) []).contains j).getD 0) + +/-- **The packing of Lemma D.3**, natively (`PacketPacking.nativePacking`, `r = 64`). -/ +@[expose] def packing : (List (List ℕ) × List (List ℕ)) × ℕ × List (ℕ × ℕ) := + PacketPacking.nativePacking x.rows (x.net.vertices.length - x.rows) (slots x) 64 (edgeEnds x) + +/-- **The weights of the modes of the `i`-th node**, packet-major over `(q, y₀, b)` with +`y₀ < 2 ^ (v₂ t + 1)` and `b < p_t`: mode `(q, y₀, b)` has weight `b + 1`. -/ +@[expose] def weights (i : ℕ) : List ℕ := + (List.range ((packing x).2.1 * 2 ^ (level x ((nodes x).getD i 0) + 1) * nodePrec x i)).map + fun j => j % nodePrec x i + 1 + +/-- The shift `Δ' = (a + 1) 2 ^ (a + 1) h u + 8 h 2 ^ a + 8 M (a + 1)`, which bounds the shift +`(a + 1) ∑_t q_t` of Lemma D.5 and every `p_t - p`. -/ +@[expose] def shift : ℕ := + (slots x + 1) * 2 ^ (slots x + 1) * step x * levels x + 8 * step x * 2 ^ slots x + + 8 * x.edges * (slots x + 1) + +/-- The common precision `N = p + Δ'` of the accumulator. -/ +@[expose] def wide : ℕ := x.prec + shift x + +/-- The binary digits of `k`, as a coefficient list of length `e`: the lift of the `k`-th +residue. -/ +@[expose] def digits (e k : ℕ) : List ℕ := (List.range e).map fun i => if k.testBit i then 1 else 0 + +/-- **The interpolation points**: the lifts of the residues `0, …, n - 1`. -/ +@[expose] def points : List (List ℕ) := (List.range (numPoints x)).map (digits x.fl.length) + +/-- **The table of native recovery** from the stored values, node-major and point-minor: the +`c`-th value goes to row `ts[c / n]`. -/ +@[expose] def table (V : List (List ℕ)) : List (List (List ℕ)) := + (List.range V.length).foldl + (fun W c => storeValue W ((nodes x).getD (c / numPoints x) 0) (V.getD c [])) [] + +/-- The cursor's list of children. -/ +@[expose] def children : List State := + cursorList (weights x) (nodePrec x) (nodes x).length (numPoints x) 64 + +/-- The accumulator after absorbing the values `vals` of the children in order. -/ +@[expose] def runLoop (vals : State → List ℕ) : Acc (List ℕ) := + runAcc (weights x) (nodePrec x) 64 (mul (wide x) x.fl) (add (wide x) x.fl) (one x.fl) + (zero x.fl) vals (children x) (one x.fl, zero x.fl, []) + +/-- **The value of an active call** from its final accumulator: native recovery from the table of +the stored values. -/ +@[expose] def finishValue (acc : Acc (List ℕ)) : List ℕ := + recover x.prec (shift x) x.fl (slots x) (levels x) (step x) (points x) (table x acc.2.2) + +/-- **The value of a leaf**: direct summation in `A_p`. -/ +@[expose] def leafValue : List ℕ := + DescribedNetwork.sumValue SumOps.galois VertexDesc.entry (x.prec, x.fl) x.net + +/-! ### The scheme -/ + +variable (mk : Call → State → ℕ × DescribedNetwork VertexDesc) + +/-- **The child call** at a cursor state `c = (i, k, β, g)`: the rows and the network given by the +child builder `mk`, at depth `d + 1`, with the parent's number of edges as its bound and the +node's precision `p_i`. -/ +@[expose] def child (c : State) : Call := + ((x.scale, x.depth + 1, x.edges, nodePrec x c.1, x.fl, (mk x c).1), (mk x c).2) + +/-- The accumulator of a call: the cursor state of the next child, if any, and the accumulator of +the cursor. -/ +abbrev Frame : Type := Option State × Acc (List ℕ) + +/-- **The recursion as a stack recursion**, with values tagged by the depth. -/ +@[expose] def scheme : Scheme Call Frame (ℕ × List ℕ) where + isLeaf x := !active x + leaf x := (x.depth, leafValue x) + start x := (some (cursorStart (weights x)), (one x.fl, zero x.fl, [])) + next x a := a.1.map (child x mk) + absorb x a y := match a.1 with + | none => a + | some c => (cursorNext (weights x) (nodePrec x) (nodes x).length (numPoints x) 64 c, + absorbAt (weights x) (nodePrec x) 64 (mul (wide x) x.fl) (add (wide x) x.fl) (one x.fl) + (zero x.fl) c a.2 y.2) + finish x a := (x.depth, finishValue x a.2) + +/-- **The value of a call**, by well-founded recursion on the edge bound: the leaf value, or the +recovery from the values of the children. -/ +@[expose] def value (x : Call) : List ℕ := + if _h : active x = true then + finishValue x (runLoop x fun c => value (child x mk c)) + else leafValue x +termination_by x.bound +decreasing_by exact edges_lt_bound_of_active _h + +theorem value_of_active {x : Call} (h : active x = true) : + value mk x = finishValue x (runLoop x fun c => value mk (child x mk c)) := by + rw [value, dite_eq_left h] + +theorem value_of_not_active {x : Call} (h : active x = false) : value mk x = leafValue x := by + rw [value, dite_eq_right (by simp [h])] + +/-! ### Derivations -/ + +section Walk + +variable {x} + +/-- Every node precision is positive at a call with an edge. -/ +theorem nodePrec_pos (hM : 1 ≤ x.edges) (i : ℕ) : 0 < nodePrec x i := by + unfold nodePrec nodePrecision + have : 0 < 8 * x.edges * (slots x + 1) := by positivity + omega + +/-- Every weight is positive. -/ +theorem weights_pos (i : ℕ) : ∀ w ∈ weights x i, 0 < w := by + intro w hw + simp only [weights, List.mem_map] at hw + obtain ⟨j, -, rfl⟩ := hw + omega + +/-- An active call has an edge. -/ +theorem one_le_edges_of_active (h : active x = true) : 1 ≤ x.edges := by + simp only [active, decide_eq_true_eq] at h + omega + +/-- An active call has a node of positive multiplicity: the index `2 ^ a + 1`, of level `0`. -/ +theorem nodes_ne_nil_of_active (h : active x = true) : nodes x ≠ [] := by + have h' := h + simp only [active, decide_eq_true_eq] at h' + obtain ⟨-, hu, huM, hua, -, -, -⟩ := h' + have hL : 2 ≤ 2 ^ slots x := + (pow_one 2).symm.le.trans (Nat.pow_le_pow_right (by norm_num) (hu.trans hua)) + have hlt : 2 ^ slots x + 1 < 2 ^ (slots x + 1) := by rw [pow_succ]; linarith + have hk : 2 ^ slots x + 1 ∈ nodes x := by + simp only [nodes, List.mem_filter, List.mem_range, decide_eq_true_eq] + refine ⟨hlt, Nat.succ_ne_self _, ?_⟩ + rw [mult_eq _ _ hlt] + have h3 : 3 * x.edges ≤ step x * levels x := le_taperStep_mul hu + have hh : 0 < step x := by + rcases Nat.eq_zero_or_pos (step x) with h0 | h0 + · rw [h0] at h3; omega + · exact h0 + have hv : padicValInt 2 (node (slots x) (2 ^ slots x + 1)) = 0 := by + simp [node] + simp only [taperMult, hv, Nat.sub_zero] + positivity + intro hn + rw [hn] at hk + simp at hk + +end Walk + +/-- The frames of an active call along its children: the cursor at the `c`-th child, with the +values `vals` of the earlier children absorbed. -/ +@[expose] def frameAt (vals : State → List ℕ) (c : ℕ) : Frame := + ((children x)[c]?, runAcc (weights x) (nodePrec x) 64 (mul (wide x) x.fl) (add (wide x) x.fl) + (one x.fl) (zero x.fl) vals ((children x).take c) (one x.fl, zero x.fl, [])) + +/-- **The loop of an active call walks its children**: from `start`, the `c`-th frame asks for the +child at the `c`-th cursor state, absorbing it gives the next frame, and the last frame stops. -/ +theorem walk {x : Call} (h : active x = true) (vals : State → List ℕ) : + frameAt x vals 0 = (scheme mk).start x ∧ + (∀ c < (children x).length, (scheme mk).next x (frameAt x vals c) = + some (child x mk ((children x).getD c (0, 0, [], 0)))) ∧ + (scheme mk).next x (frameAt x vals (children x).length) = none ∧ + (∀ c < (children x).length, (scheme mk).absorb x (frameAt x vals c) + (x.depth + 1, vals ((children x).getD c (0, 0, [], 0))) = frameAt x vals (c + 1)) ∧ + runLoop x vals = (frameAt x vals (children x).length).2 := by + have hM := one_le_edges_of_active h + have hT : 0 < (nodes x).length := List.length_pos_iff.mpr (nodes_ne_nil_of_active h) + obtain ⟨hpos, h0, hstep, hlast⟩ := cursorList_walk (ws := weights x) (ps := nodePrec x) + (T := (nodes x).length) (n := numPoints x) (r := 64) (by norm_num) (by simp [numPoints]) + (nodePrec_pos hM) weights_pos hT + unfold frameAt runLoop children + refine ⟨?_, fun c hc => ?_, ?_, fun c hc => ?_, ?_⟩ + · simp only [scheme, List.take_zero, List.getElem?_eq_getElem hpos, h0, runAcc, List.foldl_nil] + · simp only [scheme, List.getElem?_eq_getElem hc, Option.map_some, List.getD_eq_getElem _ _ hc] + · simp only [scheme, List.getElem?_eq_none le_rfl, Option.map_none] + · simp only [scheme, List.getElem?_eq_getElem hc, List.getD_eq_getElem _ _ hc] + refine Prod.ext ?_ ?_ + · by_cases hc' : c + 1 < (cursorList (weights x) (nodePrec x) (nodes x).length + (numPoints x) 64).length + · simp only [List.getElem?_eq_getElem hc'] + exact hstep c hc' + · have hc'' : c = (cursorList (weights x) (nodePrec x) (nodes x).length (numPoints x) + 64).length - 1 := by omega + simp only [List.getElem?_eq_none (show (cursorList (weights x) (nodePrec x) + (nodes x).length (numPoints x) 64).length ≤ c + 1 by omega)] + subst hc'' + exact hlast + · rw [List.take_add_one, List.getElem?_eq_getElem hc] + simp only [runAcc, Option.toList_some, List.foldl_append, List.foldl_cons, List.foldl_nil] + · simp only [List.take_length] + +/-- The invariant of the derivation from a root `x₀` at depth `0`: the only node at depth `0` is +the root, and a value tagged `0` is the root's value. -/ +@[expose] def inv (x₀ : Call) : Invariant Call Frame (ℕ × List ℕ) := + ⟨fun x => x.depth = 0 → x = x₀, fun _ _ => True, fun y => y.1 = 0 → y.2 = value mk x₀⟩ + +/-- **Derivations of the values.** Every call satisfying the invariant of a root `x₀` has a +derivation of its tagged value. -/ +theorem exists_eval (x₀ : Call) (x : Call) (hx : (inv mk x₀).node x) : + ∃ n h, (scheme mk).Eval (inv mk x₀) x (x.depth, value mk x) n h := by + refine Scheme.exists_eval_of_measure (R := scheme mk) (I := inv mk x₀) Call.bound + (fun x => (x.depth, value mk x)) (fun x _ hl => ?_) (fun x _ hl => ?_) (fun x hx h0 => ?_) x hx + · have hl' : active x = false := by simpa [scheme] using hl + simp [scheme, value_of_not_active mk hl'] + · have hl' : active x = true := by simpa [scheme] using hl + obtain ⟨h0, hnext, hdone, habs, hfin⟩ := walk mk hl' fun c => value mk (child x mk c) + refine ⟨(children x).length, frameAt x fun c => value mk (child x mk c), + fun c => child x mk ((children x).getD c (0, 0, [], 0)), h0, hnext, hdone, fun c hc => habs c hc, + fun c _ => ⟨trivial, fun h => absurd h (by simp [child]), edges_lt_bound_of_active hl'⟩, ?_⟩ + simp only [scheme, Prod.mk.injEq, true_and] + rw [value_of_active mk hl', hfin] + · simp only at h0 + rw [hx h0] + +/-- **A tagged value returned by the run is the root's value**, for any fuel: if the root is at +depth `0` and the run returns `(0, y)`, then `y` is the value of the root. -/ +theorem eq_value_of_run {J : Type} {fuel : J → ℕ} {root : J → Call} {a : J} {y : List ℕ} + (hroot : (root a).depth = 0) (hrun : (scheme mk).run fuel root a = some (0, y)) : + y = value mk (root a) := by + obtain ⟨n, h, he⟩ := exists_eval mk (root a) (root a) fun _ => rfl + rw [hroot] at he + exact Scheme.value_of_run_eq_some he hrun rfl + +/-! ### Correctness -/ + +/-- **The correctness of one active call** for a well-formedness predicate `WF`: at a well-formed +active call, if the values `vals` of the children are their contractions in `A_{p_i}`, then the +recovered value is the contraction of the call's network in `A_p`. -/ +@[expose] def StepCorrect (WF : Call → Prop) : Prop := + ∀ x, WF x → active x = true → ∀ vals : State → List ℕ, + (∀ c ∈ children x, val (nodePrec x c.1) x.fl (vals c) = + (mk x c).2.contraction fun t bs => val (nodePrec x c.1) x.fl (VertexDesc.entry t bs)) → + val x.prec x.fl (finishValue x (runLoop x vals)) = + x.net.contraction fun t bs => val x.prec x.fl (VertexDesc.entry t bs) + +/-- **Well-formedness passes to the children** of an active call. -/ +@[expose] def WFChildren (WF : Call → Prop) : Prop := + ∀ x, WF x → active x = true → ∀ c ∈ children x, WF (child x mk c) + +/-- **Correctness of the recursion.** If the step of an active call is correct and +well-formedness passes to the children, every well-formed call computes the contraction of its +network in `A_p`. -/ +theorem val_value {WF : Call → Prop} (hstep : StepCorrect mk WF) (hwf : WFChildren mk WF) + (x : Call) (hx : WF x) : + val x.prec x.fl (value mk x) = + x.net.contraction fun t bs => val x.prec x.fl (VertexDesc.entry t bs) := by + induction hb : x.bound using Nat.strong_induction_on generalizing x with + | _ b ih => + cases ha : active x + · rw [value_of_not_active mk ha] + exact DescribedNetwork.val_sumValue_galois VertexDesc.entry (x.prec, x.fl) x.net + · rw [value_of_active mk ha] + refine hstep x hx ha _ fun c hc => ?_ + exact ih _ (hb ▸ edges_lt_bound_of_active ha) (child x mk c) (hwf x hx ha c hc) rfl + +end Cslib.Circuits.Frontier.FastContraction From babef77300239497e034102a8df0a59a9fdeb2f2 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:15:35 -0400 Subject: [PATCH 073/129] chore: regenerate Complexity.lean after merging the base Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Complexity.lean b/Complexity.lean index 88c5763cd3..49730c8bf0 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -903,8 +903,8 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeReco public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEntry public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEvaluator public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketTerms -public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ReductionLift public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Recursion +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ReductionLift public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Reindex public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Schedule public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ScheduledRecursion From e2804008f2cc59d2fae0d18ab4f0fcd5367540c5 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:22:26 -0400 Subject: [PATCH 074/129] feat(circuit/frontier): the native machine of the fast contraction algorithm, correct for every fuel Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/FastContraction.lean | 3 +- .../TensorNetwork/FastContractionMachine.lean | 372 ++++++++++++++++++ 2 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean index e63da90ef1..e1b1429a35 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean @@ -368,7 +368,8 @@ theorem exists_eval (x₀ : Call) (x : Call) (hx : (inv mk x₀).node x) : · have hl' : active x = true := by simpa [scheme] using hl obtain ⟨h0, hnext, hdone, habs, hfin⟩ := walk mk hl' fun c => value mk (child x mk c) refine ⟨(children x).length, frameAt x fun c => value mk (child x mk c), - fun c => child x mk ((children x).getD c (0, 0, [], 0)), h0, hnext, hdone, fun c hc => habs c hc, + fun c => child x mk ((children x).getD c (0, 0, [], 0)), h0, hnext, hdone, + fun c hc => habs c hc, fun c _ => ⟨trivial, fun h => absurd h (by simp [child]), edges_lt_bound_of_active hl'⟩, ?_⟩ simp only [scheme, Prod.mk.injEq, true_and] rw [value_of_active mk hl', hfin] diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean new file mode 100644 index 0000000000..1a20fd42eb --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean @@ -0,0 +1,372 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContraction +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DirectContraction +import Complexity.Computability.PolyTime.GaloisRingSeries +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Log +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.SqrtLog +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.BitString +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Sum +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryPower +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListMap +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeNatFrontends + +/-! +# The native machine of the fast contraction algorithm + +The stack recursion of `Cslib.Circuits.Frontier.FastContraction.scheme` as one native machine for +the contraction modulo `2 ^ P` of an explicit network `(r, G)`, with the precision `P` in binary. + +**The root.** The precision is first capped at the encoded length of the network +(`ExplicitTensorNetwork.capPrecision`), which does not change the residue. The root call +(`FastContraction.root`) has the scale `s = m`, the number of edges of `G`, depth `0`, edge bound +`m + 1`, the capped precision `p`, the tables of `G` as descriptors (`FastContraction.rootNet`) and +the modulus `f = X ^ (2 ⬝ 3 ^ t) + X ^ (3 ^ t) + 1` (`FastContraction.modulus`), irreducible modulo +`2` (`Cslib.irreducible_trinomial`), whose `2 ⬝ 3 ^ t > 4 ⌊log₂ m⌋ + 2` binary digits give the +interpolation points of every call of the schedule. + +**The fuel and the fallback.** The number of calls of the recursion depends, through the schedule's +constant, on the ratio of the precision to the number of edges, which the machine does not know in +advance. So the machine runs the recursion for a fixed clock `2 ^ (K₀ (m + p) / √⌊log₂ (m + p)⌋)` +(`FastContraction.fuel`), and returns the constant coordinate of the root's value when the run has +returned a value tagged with depth `0`, and direct summation otherwise +(`FastContraction.fastValue`). +A value tagged `0` is the root's value for every fuel (`FastContraction.eq_value_of_run`), so the +result is correct on every well-formed input, whatever the fuel +(`FastContraction.fastValue_eq`); the clock matters only for the running time. + +## Main definitions + +* `Cslib.Circuits.Frontier.FastContraction.encCall`, `encFrame`, `encValue`: the encodings. +* `Cslib.Circuits.Frontier.FastContraction.leafCertificate`: direct summation at the leaves. +* `Cslib.Circuits.Frontier.FastContraction.modulus`, `rootNet`, `root`, `fuel`: the root call and + the clock. +* `Cslib.Circuits.Frontier.FastContraction.fastValue`: the result of the machine. + +## Main results + +* `Cslib.Circuits.Frontier.FastContraction.val_rootNet`: the root computes the contraction of `G`. +* `Cslib.Circuits.Frontier.FastContraction.getD_mod_of_val_eq`: reading off the constant + coordinate. +* `Cslib.Circuits.Frontier.FastContraction.fastValue_eq`: the machine is correct on well-formed + inputs, for every fuel. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Appendix D.4.7 (p. 64). +-/ + +public section + +namespace Cslib.Circuits.Frontier.FastContraction + +open GaloisRingArith ChildCursor Cslib.GenericRAM Cslib.GenericRAM.StackRecursion + Turing.MultiTapeTM + +/-! ### Encodings -/ + +/-- The encoding of the parameters of a call: the scale, the depth, the edge bound and the +precision in unary, the coefficients of the modulus in binary and the number of rows in +binary. -/ +abbrev encParams : Params ↪ List Bool := + encPair encUnary (encPair encUnary (encPair encUnary (encPair encUnary + (encPair (encList encNat) encNat)))) + +/-- The encoding of a call: its parameters and its network. -/ +abbrev encCall : Call ↪ List Bool := + encPair encParams (DescribedNetwork.encDescribed VertexDesc.enc) + +/-- The encoding of a frame: the cursor state and the accumulator, in binary. -/ +abbrev encFrame : Frame ↪ List Bool := + encPair (encOption ChildCursor.encState) + (encPair (encList encNat) (encPair (encList encNat) (encList (encList encNat)))) + +/-- The encoding of a tagged value: the depth in unary and the coefficient list in binary. -/ +abbrev encValue : ℕ × List ℕ ↪ List Bool := encPair encUnary (encList encNat) + +/-! ### The leaves -/ + +/-- The depth of a call is polynomial-time. -/ +theorem depth_polyTime : ComputableInPolyTime Call.depth encCall encUnary := by + unfold Call.depth; fun_prop + +/-- The input of direct summation at a call, `((p, fl), D)`, is polynomial-time. -/ +theorem leafInput_polyTime : + ComputableInPolyTime (fun x : Call => ((x.prec, x.fl), x.net)) encCall + (DescribedNetwork.encInput (encPair encUnary (encList encNat)) VertexDesc.enc) := by + unfold Call.prec Call.fl Call.net; fun_prop + +variable (mk : Call → State → ℕ × DescribedNetwork VertexDesc) + +/-- **The leaf certificate**: direct summation over the coefficient lists of `A_p`, with the native +evaluator of the vertex descriptors, tagged with the depth. -/ +noncomputable def leafCertificate : NativeCertificate (scheme mk).leaf encCall encValue := + ((NativeCertificate.ofPolyTime depth_polyTime).prod + ((DescribedNetwork.contractionCertificate VertexDesc.entry SumOps.galois_polyTime + VertexDesc.entryCertificate).comp (NativeCertificate.ofPolyTime leafInput_polyTime))).congr + fun _ => rfl + +/-! ### The root -/ + +/-- The exponent `t` of the modulus, with `2 ^ (t + 1) > 4 ⌊log₂ m⌋ + 2`. -/ +@[expose] def modulusExp (m : ℕ) : ℕ := Nat.log 2 (4 * Nat.log 2 m + 2) + +/-- **The modulus** `X ^ (2 ⬝ 3 ^ t) + X ^ (3 ^ t) + 1`, by its coefficients below the leading +one. -/ +@[expose] def modulus (m : ℕ) : List ℕ := + (List.range (2 * 3 ^ modulusExp m)).map fun i => + if i = 0 ∨ i = 3 ^ modulusExp m then 1 else 0 + +/-- The modulus has `2 ⬝ 3 ^ t` coefficients. -/ +theorem length_modulus (m : ℕ) : (modulus m).length = 2 * 3 ^ modulusExp m := by + simp [modulus] + +/-- The modulus has a coefficient. -/ +theorem modulus_ne_nil (m : ℕ) : modulus m ≠ [] := by + rw [← List.length_pos_iff, length_modulus] + positivity + +/-- **The tables of an explicit network as vertex descriptors.** -/ +@[expose] def rootNet (G : ExplicitTensorNetwork ℕ) : DescribedNetwork VertexDesc := + ⟨G.numEdges, G.vertices.map fun v => (v.1, .inl v.2)⟩ + +/-- **The root call** of a capped input `(p, r, G)`: scale `m`, depth `0`, edge bound `m + 1`, +precision `p`, the modulus and the tables of `G`. -/ +@[expose] def root (q : ℕ × ℕ × ExplicitTensorNetwork ℕ) : Call := + ((q.2.2.numEdges, 0, q.2.2.numEdges + 1, q.1, modulus q.2.2.numEdges, q.2.1), rootNet q.2.2) + +/-- The constant of the clock. -/ +@[expose] def fuelConst : ℕ := 2 ^ 22 + +/-- **The clock** of a capped input `(p, r, G)`: `2 ^ (K₀ (m + p) / √⌊log₂ (m + p)⌋)`. -/ +@[expose] def fuel (q : ℕ × ℕ × ExplicitTensorNetwork ℕ) : ℕ := + 2 ^ (fuelConst * (q.2.2.numEdges + q.1) / Nat.sqrt (Nat.log 2 (q.2.2.numEdges + q.1))) + +/-- **The root computes the contraction of `G`**: in `A_p`, the contraction of the tables read as +constants is the image of the contraction of `G`. -/ +theorem val_rootNet (p : ℕ) (fl : List ℕ) (G : ExplicitTensorNetwork ℕ) : + (rootNet G).contraction (fun t bs => val p fl (VertexDesc.entry t bs)) = + (G.contraction : Ring p fl) := by + rw [← DescribedNetwork.contraction_ofExplicit] + have h := DescribedNetwork.map_contraction (Nat.castRingHom (Ring p fl)) + ExplicitTensorNetwork.entry (DescribedNetwork.ofExplicit G) + simp only [Nat.coe_castRingHom] at h + rw [h] + simp only [DescribedNetwork.contraction, rootNet, DescribedNetwork.ofExplicit, List.map_map] + refine Finset.sum_congr rfl fun x _ => ?_ + congr 1 + refine List.map_congr_left fun v _ => ?_ + simp only [Function.comp_apply, VertexDesc.val_entry_inl] + +/-- **Reading off the constant coordinate.** If a coefficient list has the value of a natural +number `k` in `A_p`, with `f` of positive degree, its constant coordinate is `k` modulo `2 ^ p`. -/ +theorem getD_mod_of_val_eq {p : ℕ} {fl y : List ℕ} {k : ℕ} (hfl : fl ≠ []) + (h : val p fl y = (k : Ring p fl)) : y.getD 0 0 % 2 ^ p = k % 2 ^ p := by + rw [← GaloisRingArith.val_singleton] at h + simpa using GaloisRingArith.mod_eq_of_val_eq h (List.length_pos_iff.mpr hfl) + +/-! ### The root and the clock in polynomial time -/ + +/-- The exponent of the modulus is polynomial-time, in unary from unary. -/ +theorem modulusExp_polyTime : ComputableInPolyTime modulusExp encUnary encUnary := by + have hb := ComputableInPolyTime.unary_to_binary + have hlog := ComputableInPolyTime.binary_log_two.comp hb + have harg := ComputableInPolyTime.unary_add' + (ComputableInPolyTime.unary_mul' (ComputableInPolyTime.const 4) hlog) + (ComputableInPolyTime.const 2) + exact ComputableInPolyTime.binary_log_two.comp (hb.comp harg) + +/-- `3 ^ t ≤ (4 ⌊log₂ m⌋ + 2) ^ 2` for the exponent `t` of the modulus. -/ +theorem three_pow_modulusExp_le (m : ℕ) : 3 ^ modulusExp m ≤ (4 * Nat.log 2 m + 2) ^ 2 := by + have h := Nat.pow_log_le_self 2 (show 4 * Nat.log 2 m + 2 ≠ 0 by omega) + calc 3 ^ modulusExp m ≤ 4 ^ modulusExp m := Nat.pow_le_pow_left (by norm_num) _ + _ = (2 ^ modulusExp m) ^ 2 := by rw [← pow_mul, mul_comm, pow_mul]; norm_num + _ ≤ (4 * Nat.log 2 m + 2) ^ 2 := Nat.pow_le_pow_left h _ + +/-- **The modulus is polynomial-time**, from the number of edges in unary. -/ +theorem modulus_polyTime : ComputableInPolyTime modulus encUnary (encList encNat) := by + have hb := ComputableInPolyTime.unary_to_binary + have hlog := ComputableInPolyTime.binary_log_two.comp hb + have hE : ComputableInPolyTime (fun m => 3 ^ modulusExp m) encUnary encUnary := by + refine ComputableInPolyTime.binary_to_unary_bounded + (ComputableInPolyTime.binary_pow.comp ((ComputableInPolyTime.const 3).prod + modulusExp_polyTime)) + ((ComputableInPolyTime.unary_pow 2).comp (ComputableInPolyTime.unary_add' + (ComputableInPolyTime.unary_mul' (ComputableInPolyTime.const 4) hlog) + (ComputableInPolyTime.const 2))) three_pow_modulusExp_le + have hrange := ComputableInPolyTime.range.comp + (ComputableInPolyTime.unary_mul' (ComputableInPolyTime.const 2) hE) + have hi := ComputableInPolyTime.snd encUnary encUnary + have htest : ComputableInPolyTime (fun q : ℕ × ℕ => decide (q.2 = 0 ∨ q.2 = 3 ^ modulusExp q.1)) + (encPair encUnary encUnary) encBool := + ComputableInPolyTime.decide_or + (ComputableInPolyTime.decide_eq' hi (ComputableInPolyTime.const 0)) + (ComputableInPolyTime.decide_eq' hi (hE.comp (ComputableInPolyTime.fst _ _))) + have hbody : ComputableInPolyTime (fun q : ℕ × ℕ => + if q.2 = 0 ∨ q.2 = 3 ^ modulusExp q.1 then 1 else 0) (encPair encUnary encUnary) encNat := + (ComputableInPolyTime.ite htest + (ComputableInPolyTime.const 1 : ComputableInPolyTime (fun _ : ℕ × ℕ => (1 : ℕ)) + (encPair encUnary encUnary) encNat) + (ComputableInPolyTime.const 0)).of_enc_eq fun q => by simp only [decide_eq_true_eq] + unfold modulus + exact ComputableInPolyTime.mapWithInput + (f := fun m i => if i = 0 ∨ i = 3 ^ modulusExp m then 1 else 0) hrange hbody + +/-- The number of edges of a capped input, in unary. -/ +private theorem edges_polyTime : + ComputableInPolyTime (fun q : ℕ × ℕ × ExplicitTensorNetwork ℕ => q.2.2.numEdges) + ExplicitTensorNetwork.encContractionInput encUnary := + ExplicitTensorNetwork.numEdges_polyTime + +/-- The network of a capped input. -/ +private theorem network_polyTime : + ComputableInPolyTime (fun q : ℕ × ℕ × ExplicitTensorNetwork ℕ => q.2.2) + ExplicitTensorNetwork.encContractionInput ExplicitTensorNetwork.encExplicitNetwork := + (ComputableInPolyTime.snd encNat ExplicitTensorNetwork.encExplicitNetwork).comp + (ComputableInPolyTime.snd encUnary (encPair encNat ExplicitTensorNetwork.encExplicitNetwork)) + +/-- **The tables as descriptors are polynomial-time.** -/ +theorem rootNet_polyTime : ComputableInPolyTime rootNet ExplicitTensorNetwork.encExplicitNetwork + (DescribedNetwork.encDescribed VertexDesc.enc) := by + have hpair : ComputableInPolyTime ExplicitTensorNetwork.equivProd + ExplicitTensorNetwork.encExplicitNetwork + (encPair encUnary (encList (encPair (encList encNat) (encList encNat)))) := + ComputableInPolyTime.of_eq fun _ => rfl + have hm := (ComputableInPolyTime.fst _ _).comp hpair + have hvs := (ComputableInPolyTime.snd _ _).comp hpair + have hv : ComputableInPolyTime (fun q : ExplicitTensorNetwork ℕ × (List ℕ × List ℕ) => + (q.2.1, (Sum.inl q.2.2 : VertexDesc))) + (encPair ExplicitTensorNetwork.encExplicitNetwork (encPair (encList encNat) (encList encNat))) + (encPair (encList encNat) VertexDesc.enc) := + ((ComputableInPolyTime.fst _ _).comp (ComputableInPolyTime.snd _ _)).prod + ((ComputableInPolyTime.sumInl _ _).comp + ((ComputableInPolyTime.snd _ _).comp (ComputableInPolyTime.snd _ _))) + have hmap := ComputableInPolyTime.mapWithInput (f := fun _ v => (v.1, (Sum.inl v.2 : VertexDesc))) + hvs hv + have hout : ComputableInPolyTime (DescribedNetwork.equivProd (T := VertexDesc)).symm + (encPair encUnary (encList (encPair (encList encNat) VertexDesc.enc))) + (DescribedNetwork.encDescribed VertexDesc.enc) := + ComputableInPolyTime.of_eq fun _ => rfl + exact (hout.comp (hm.prod hmap)).of_enc_eq fun _ => rfl + +/-- **The root call is polynomial-time**, from a capped input. -/ +theorem root_polyTime : ComputableInPolyTime root ExplicitTensorNetwork.encContractionInput + encCall := by + have hp := ComputableInPolyTime.fst encUnary (encPair encNat + ExplicitTensorNetwork.encExplicitNetwork) + have hr := (ComputableInPolyTime.fst encNat ExplicitTensorNetwork.encExplicitNetwork).comp + (ComputableInPolyTime.snd encUnary (encPair encNat ExplicitTensorNetwork.encExplicitNetwork)) + exact (edges_polyTime.prod ((ComputableInPolyTime.const 0).prod + ((ComputableInPolyTime.unary_add' edges_polyTime (ComputableInPolyTime.const 1)).prod + (hp.prod ((modulus_polyTime.comp edges_polyTime).prod hr))))).prod + (rootNet_polyTime.comp network_polyTime) + +/-- **The clock is polynomial-time**, from a capped input. -/ +theorem fuel_polyTime : ComputableInPolyTime fuel ExplicitTensorNetwork.encContractionInput + encNat := + (ComputableInPolyTime.unary_two_pow_div_sqrt_log_two fuelConst).comp + (ComputableInPolyTime.unary_add' edges_polyTime + (ComputableInPolyTime.fst encUnary (encPair encNat + ExplicitTensorNetwork.encExplicitNetwork))) + +/-! ### The result -/ + +/-- **The result of the machine** on an input `(P, r, G)`: with the capped precision `p`, the +constant coordinate of a returned root value modulo `2 ^ p` if the run returns a value tagged +`0`, and direct summation otherwise. -/ +@[expose] noncomputable def fastValue (q : ℕ × ℕ × ExplicitTensorNetwork ℕ) : ℕ := + ((scheme mk).run fuel root (ExplicitTensorNetwork.capPrecision q)).elim + (q.2.2.contraction % 2 ^ q.1) + fun v => if v.1 = 0 then v.2.getD 0 0 % 2 ^ (ExplicitTensorNetwork.capPrecision q).1 + else q.2.2.contraction % 2 ^ q.1 + +/-- **The machine is correct on well-formed inputs**, for every fuel. If the step of an active +call is correct and well-formedness passes to the children, then on every input whose root is +well-formed the result is the contraction modulo `2 ^ P`. -/ +theorem fastValue_eq {WF : Call → Prop} (hstep : StepCorrect mk WF) (hwf : WFChildren mk WF) + (q : ℕ × ℕ × ExplicitTensorNetwork ℕ) + (hroot : WF (root (ExplicitTensorNetwork.capPrecision q))) : + fastValue mk q = q.2.2.contraction % 2 ^ q.1 := by + unfold fastValue + rcases hrun : (scheme mk).run fuel root (ExplicitTensorNetwork.capPrecision q) with _ | ⟨t, y⟩ + · rfl + · simp only [Option.elim_some] + split_ifs with ht + · subst ht + have hy := eq_value_of_run mk (fuel := fuel) (root := root) rfl hrun + have hval : val (ExplicitTensorNetwork.capPrecision q).1 + (modulus (ExplicitTensorNetwork.capPrecision q).2.2.numEdges) y = + (rootNet (ExplicitTensorNetwork.capPrecision q).2.2).contraction fun t bs => + val (ExplicitTensorNetwork.capPrecision q).1 + (modulus (ExplicitTensorNetwork.capPrecision q).2.2.numEdges) + (VertexDesc.entry t bs) := hy ▸ val_value mk hstep hwf _ hroot + rw [val_rootNet] at hval + rw [getD_mod_of_val_eq (modulus_ne_nil _) hval] + exact ExplicitTensorNetwork.contraction_mod_two_pow_min q.1 q.2.2 + · rfl + +/-! ### The machine -/ + +/-- The encoding of an input `(P, r, G)`: the precision and the number of rows in binary, and the +network. -/ +abbrev encInput : ℕ × ℕ × ExplicitTensorNetwork ℕ ↪ List Bool := + encPair encNat (encPair encNat ExplicitTensorNetwork.encExplicitNetwork) + +variable {mk} + +/-- **The clocked run** from a capped input, as one native machine, given polynomial-time +components of the scheme. -/ +noncomputable def runCertificate (hpoly : (scheme mk).PolyTime encCall encFrame encValue) : + NativeCertificate ((scheme mk).run fuel root) ExplicitTensorNetwork.encContractionInput + (encOption encValue) := + Scheme.certificate hpoly (leafCertificate mk) (NativeCertificate.ofPolyTime fuel_polyTime) + (NativeCertificate.ofPolyTime root_polyTime) + +/-- The constant coordinate of a value modulo `2 ^ p`, for the capped precision `p`. -/ +theorem decode_polyTime : + ComputableInPolyTime (fun q : (ℕ × ℕ × ExplicitTensorNetwork ℕ) × (ℕ × List ℕ) => + q.2.2.getD 0 0 % 2 ^ (ExplicitTensorNetwork.capPrecision q.1).1) + (encPair encInput encValue) encNat := by + have hcap := (ComputableInPolyTime.fst _ _).comp + (ExplicitTensorNetwork.capPrecision_polyTime.comp (ComputableInPolyTime.fst encInput encValue)) + have hy := (ComputableInPolyTime.snd encUnary (encList encNat)).comp + (ComputableInPolyTime.snd encInput encValue) + exact GaloisRingArith.computableInPolyTime_mod_two_pow + ((ComputableInPolyTime.listGetD (encA := encNat) 0).comp (hy.prod (ComputableInPolyTime.const 0))) + hcap + +/-- Whether a value is tagged `0`. -/ +theorem tag_polyTime : + ComputableInPolyTime (fun q : (ℕ × ℕ × ExplicitTensorNetwork ℕ) × (ℕ × List ℕ) => + decide (q.2.1 = 0)) (encPair encInput encValue) encBool := + ComputableInPolyTime.decide_eq' ((ComputableInPolyTime.fst encUnary (encList encNat)).comp + (ComputableInPolyTime.snd encInput encValue)) (ComputableInPolyTime.const 0) + +/-- **The fast machine**: cap the precision and run the recursion; on a value tagged `0`, read +off its constant coordinate, and otherwise sum directly. It computes `fastValue`. -/ +noncomputable def fastCertificate (hpoly : (scheme mk).PolyTime encCall encFrame encValue) : + NativeCertificate (fastValue mk) encInput encNat := + (NativeCertificate.option + ((runCertificate hpoly).comp + (NativeCertificate.ofPolyTime ExplicitTensorNetwork.capPrecision_polyTime)) + ExplicitTensorNetwork.directContractionBinaryCertificate + (NativeCertificate.branch (NativeCertificate.ofPolyTime tag_polyTime) + (NativeCertificate.ofPolyTime decode_polyTime) + (ExplicitTensorNetwork.directContractionBinaryCertificate.comp + (NativeCertificate.ofPolyTime (ComputableInPolyTime.fst encInput encValue))))).congr + fun q => by + simp only [fastValue, Function.comp_apply] + cases (scheme mk).run fuel root (ExplicitTensorNetwork.capPrecision q) with + | none => rfl + | some v => simp only [Option.elim_some, decide_eq_true_eq] + +end Cslib.Circuits.Frontier.FastContraction From 3e3b6bf6130497c2079d1e41052350c4fcfe3f87 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:25:36 -0400 Subject: [PATCH 075/129] Merge #1952's review fixes; adapt to ExplicitTensorNetwork = DescribedNetwork (List R) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 2 ++ .../TensorNetwork/FastContractionMachine.lean | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Complexity.lean b/Complexity.lean index 3d634f7800..31f9d48e92 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -896,6 +896,8 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformatio public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DescribedContraction public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DirectContraction public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContraction +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionMachine public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates public import Complexity.Computability.Circuit.Frontier.TensorNetwork.KernelProduct public import Complexity.Computability.Circuit.Frontier.TensorNetwork.LocalDeformation diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean index 1a20fd42eb..3cfe7522c3 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean @@ -156,12 +156,12 @@ constants is the image of the contraction of `G`. -/ theorem val_rootNet (p : ℕ) (fl : List ℕ) (G : ExplicitTensorNetwork ℕ) : (rootNet G).contraction (fun t bs => val p fl (VertexDesc.entry t bs)) = (G.contraction : Ring p fl) := by - rw [← DescribedNetwork.contraction_ofExplicit] + rw [← DescribedNetwork.contraction_entry] have h := DescribedNetwork.map_contraction (Nat.castRingHom (Ring p fl)) - ExplicitTensorNetwork.entry (DescribedNetwork.ofExplicit G) + ExplicitTensorNetwork.entry G simp only [Nat.coe_castRingHom] at h rw [h] - simp only [DescribedNetwork.contraction, rootNet, DescribedNetwork.ofExplicit, List.map_map] + simp only [DescribedNetwork.contraction, rootNet, List.map_map] refine Finset.sum_congr rfl fun x _ => ?_ congr 1 refine List.map_congr_left fun v _ => ?_ @@ -237,7 +237,7 @@ private theorem network_polyTime : /-- **The tables as descriptors are polynomial-time.** -/ theorem rootNet_polyTime : ComputableInPolyTime rootNet ExplicitTensorNetwork.encExplicitNetwork (DescribedNetwork.encDescribed VertexDesc.enc) := by - have hpair : ComputableInPolyTime ExplicitTensorNetwork.equivProd + have hpair : ComputableInPolyTime (DescribedNetwork.equivProd (T := List ℕ)) ExplicitTensorNetwork.encExplicitNetwork (encPair encUnary (encList (encPair (encList encNat) (encList encNat)))) := ComputableInPolyTime.of_eq fun _ => rfl @@ -341,7 +341,8 @@ theorem decode_polyTime : have hy := (ComputableInPolyTime.snd encUnary (encList encNat)).comp (ComputableInPolyTime.snd encInput encValue) exact GaloisRingArith.computableInPolyTime_mod_two_pow - ((ComputableInPolyTime.listGetD (encA := encNat) 0).comp (hy.prod (ComputableInPolyTime.const 0))) + ((ComputableInPolyTime.listGetD (encA := encNat) 0).comp + (hy.prod (ComputableInPolyTime.const 0))) hcap /-- Whether a value is tagged `0`. -/ From 5e15a84ef07030c48c1fa20fd4f9dd121981bb3f Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:26:38 -0400 Subject: [PATCH 076/129] feat(circuit/frontier): native child networks and their contraction (wip) --- .../Frontier/TensorNetwork/NativeChild.lean | 621 +++++++++++++++++- .../Frontier/TensorNetwork/Recursion.lean | 4 +- 2 files changed, 610 insertions(+), 15 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean index cd5f8632b3..70b6759f88 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean @@ -12,6 +12,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildEntri public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ChildNumbering public import Complexity.Computability.PolyTime.PacketPacking public import Complexity.Foundations.Combinatorics.BudgetTuples +import Complexity.Foundations.Data.List.Lookup import Mathlib.Data.List.GetD /-! @@ -106,6 +107,7 @@ local notation "ℤ₍₂₎" => Valuation.integer (Rat.padicValuation 2) namespace NativeChild open ChildNumbering GaloisRingArith NativePacket +open ContractionSchedule (nodePrecision) /-! ### The native child -/ @@ -223,23 +225,26 @@ theorem idxOf_modeList {P T p : ℕ} (Z : Fin P × Fin T × Fin p) : (modeList P T p).idxOf Z = modeEquiv P T p Z := by have h : (modeList P T p)[(modeEquiv P T p Z : ℕ)]'(by simp [modeList]) = Z := by simp [modeList] - rw [← List.Nodup.idxOf_getElem (nodup_modeList P T p) _ (by simp [modeList]), h] + have := List.Nodup.idxOf_getElem (nodup_modeList P T p) (modeEquiv P T p Z : ℕ) + (by simp [modeList]) + rwa [h] at this + +/-- The last coordinate of a mode is its number modulo `p`. -/ +theorem val_modeEquiv_symm_snd_snd {P T p : ℕ} (j : Fin (P * (T * p))) : + (((modeEquiv P T p).symm j).2.2 : ℕ) = j % p := by + obtain ⟨Z, hZ⟩ : ∃ Z, (modeEquiv P T p).symm j = Z := ⟨_, rfl⟩ + have hj : (j : ℕ) = Z.1 * (T * p) + (Z.2.1 * p + Z.2.2) := by + rw [← modeEquiv_apply_val, ← hZ, Equiv.apply_symm_apply] + rw [hZ, hj, show (Z.1 : ℕ) * (T * p) + (Z.2.1 * p + Z.2.2) = Z.2.2 + p * (Z.1 * T + Z.2.1) by + ring, Nat.add_mul_mod_self_left, Nat.mod_eq_of_lt Z.2.2.2] /-- The weights listed along the modes are `modeWeights`. -/ theorem map_modeList_weight (P T p : ℕ) : (modeList P T p).map (fun Z => (Z.2.2 : ℕ) + 1) = modeWeights P T p := by apply List.ext_getElem (by simp [modeList, modeWeights]) intro j h₁ h₂ - simp only [modeList, List.getElem_map, List.getElem_ofFn, modeWeights, List.getElem_range] - congr 1 - have := modeEquiv_apply_val (P := P) (T := T) (p := p) ((modeEquiv P T p).symm ⟨j, by simpa - [modeList] using h₁⟩) - rw [Equiv.apply_symm_apply] at this - have hb := ((modeEquiv P T p).symm ⟨j, by simpa [modeList] using h₁⟩).2.2.2 - simp only at this - rw [this, Nat.add_mod, Nat.add_mod (_ * p), Nat.mul_mod_left, show (T * p) = p * T by ring, - ← Nat.mul_assoc, Nat.mul_mod_left] - simp [Nat.mod_eq_of_lt hb] + simp only [modeList, List.getElem_map, List.getElem_ofFn, modeWeights, List.getElem_range, + val_modeEquiv_symm_snd_snd] /-- **The slice of a packet** reads the mode tuple of `β`: entry `y · p + b` of the slice of `q` is the exponent of the mode `(q, y, b)`. -/ @@ -252,8 +257,598 @@ theorem getD_betaSlice {P i pt : ℕ} (β : List ℕ) (q : Fin P) (Z : Fin (2 ^ calc (Z.1 : ℕ) * pt + Z.2 < Z.1 * pt + pt := by omega _ = (Z.1 + 1) * pt := by ring _ ≤ 2 ^ (i + 1) * pt := Nat.mul_le_mul_right _ h1 - rw [BudgetTuples.ofList, idxOf_modeList, modeEquiv_apply_val, betaSlice, List.getD_eq_getD_get?, - List.getElem?_take_of_lt hZ, List.getElem?_drop, ← List.getD_eq_getD_get?] + rw [BudgetTuples.ofList, idxOf_modeList, modeEquiv_apply_val, betaSlice, + List.getD_eq_getElem?_getD, List.getElem?_take_of_lt hZ, List.getElem?_drop, + ← List.getD_eq_getElem?_getD] + +/-! ### The native lists in a numbered network -/ + +section Bridge + +variable {m k s : ℕ} (src tgt : Fin m → Fin k) (group : Fin k → Fin s) (g : Fin s) + +/-- Reading the group of an end of an edge from the lists. -/ +theorem getD_ofFn_group (sel : ℕ × ℕ → ℕ) (endF : Fin m → Fin k) + (hsel : ∀ e, sel ((src e : ℕ), (tgt e : ℕ)) = endF e) (e : Fin m) : + (List.ofFn fun v => (group v : ℕ)).getD + (sel ((List.ofFn fun e => ((src e : ℕ), (tgt e : ℕ))).getD e (0, 0))) 0 = + group (endF e) := by + rw [List.getD_ofFn, hsel, List.getD_ofFn] + +/-- **The native ports of group `g` at the end `sel`** are its members, in order. -/ +theorem portEdges_eq (sel : ℕ × ℕ → ℕ) (endF : Fin m → Fin k) + (hsel : ∀ e, sel ((src e : ℕ), (tgt e : ℕ)) = endF e) : + portEdges (List.ofFn fun v => (group v : ℕ)) g (List.ofFn fun e => ((src e : ℕ), (tgt e : ℕ))) + sel = (members fun e => group (endF e) = g).map Fin.val := by + rw [portEdges, List.length_ofFn, ← List.map_coe_finRange_eq_range, List.filter_map, members] + congr 1 + refine List.filter_congr fun e _ => ?_ + simp only [Function.comp_apply, getD_ofFn_group src tgt group sel endF hsel, Fin.val_inj] + +/-- **The native position of a port** is its position among the members. -/ +theorem portRank_eq (sel : ℕ × ℕ → ℕ) (endF : Fin m → Fin k) + (hsel : ∀ e, sel ((src e : ℕ), (tgt e : ℕ)) = endF e) (e : Fin m) : + portRank (List.ofFn fun v => (group v : ℕ)) g (List.ofFn fun e => ((src e : ℕ), (tgt e : ℕ))) + sel e = rank (fun e => group (endF e) = g) e := by + have hr : List.range e = ((List.finRange m).take e).map Fin.val := by + rw [List.map_take, List.map_coe_finRange_eq_range, List.take_range, min_eq_left e.2.le] + rw [portRank, rank, hr, List.countP_map] + refine List.countP_congr fun u _ => ?_ + simp only [Function.comp_apply, getD_ofFn_group src tgt group sel endF hsel, Fin.val_inj, + decide_eq_true_eq] + +/-- Counting a group among the groups of the vertices gives the number of its vertices. -/ +theorem count_ofFn_group : + (List.ofFn fun v => (group v : ℕ)).count (g : ℕ) = (members fun v => group v = g).length := by + rw [List.ofFn_eq_map, List.count, List.countP_map, members, ← List.countP_eq_length_filter] + refine List.countP_congr fun u _ => ?_ + simp [Fin.val_inj] + +/-- The native vertices of group `g` are its members, in order. -/ +theorem filter_range_group : + (List.range k).filter (fun v => (List.ofFn fun v => (group v : ℕ)).getD v 0 = (g : ℕ)) = + (members fun v => group v = g).map Fin.val := by + rw [← List.map_coe_finRange_eq_range, List.filter_map, members] + congr 1 + refine List.filter_congr fun v _ => ?_ + simp only [Function.comp_apply, List.getD_ofFn, Fin.val_inj] + +variable {r : ℕ} (hsrc : ∀ e, (src e : ℕ) < r) (htgt : ∀ e, r ≤ (tgt e : ℕ)) +include hsrc + +/-- **The child edge of a row port** is its number in `childEdgeEquiv`. -/ +theorem childEdgeOf_src (e : Fin m) (h : group (src e) = g) : + childEdgeOf r (List.ofFn fun v => (group v : ℕ)) g + (List.ofFn fun e => ((src e : ℕ), (tgt e : ℕ))) (src e) e = + (childEdgeEquiv src tgt group g (.inl ⟨e, h⟩) : ℕ) := by + rw [childEdgeOf, ite_eq_left (hsrc e), portRank_eq src tgt group g Prod.fst src (fun _ => rfl)] + simp [childEdgeEquiv] + +omit hsrc in +include htgt in +/-- **The child edge of a column port** is its number in `childEdgeEquiv`. -/ +theorem childEdgeOf_tgt (e : Fin m) (h : group (tgt e) = g) : + childEdgeOf r (List.ofFn fun v => (group v : ℕ)) g + (List.ofFn fun e => ((src e : ℕ), (tgt e : ℕ))) (tgt e) e = + (childEdgeEquiv src tgt group g (.inr ⟨e, h⟩) : ℕ) := by + rw [childEdgeOf, ite_eq_right (by have := htgt e; omega), + portRank_eq src tgt group g Prod.snd tgt (fun _ => rfl), + portEdges_eq src tgt group g Prod.fst src (fun _ => rfl), List.length_map] + simp [childEdgeEquiv] + +omit hsrc in +/-- **The parent edge of a numbered child edge.** -/ +theorem parentEdge_childEdgeEquiv (c : ChildEdge src tgt group g) : + parentEdge (List.ofFn fun v => (group v : ℕ)) g + (List.ofFn fun e => ((src e : ℕ), (tgt e : ℕ))) (childEdgeEquiv src tgt group g c) = + (Sum.elim (fun e => (e.1 : ℕ)) (fun e => (e.1 : ℕ)) c) := by + rw [parentEdge, portEdges_eq src tgt group g Prod.fst src (fun _ => rfl), + portEdges_eq src tgt group g Prod.snd tgt (fun _ => rfl), List.length_map] + rcases c with c | c + · have hlt := rank_lt (fun e => group (src e) = g) c.1 c.2 + simp only [childEdgeEquiv, Equiv.trans_apply, Equiv.sumCongr_apply, Sum.map_inl, + finSumFinEquiv_apply_left, Fin.val_castAdd, rankEquiv_apply_val, Sum.elim_inl] + rw [ite_eq_left hlt, List.getD_eq_getElem _ _ (by simpa using hlt), List.getElem_map, + getElem_rank (fun e => group (src e) = g) c.1 c.2] + · have hlt := rank_lt (fun e => group (tgt e) = g) c.1 c.2 + simp only [childEdgeEquiv, Equiv.trans_apply, Equiv.sumCongr_apply, Sum.map_inr, + finSumFinEquiv_apply_right, Fin.val_natAdd, rankEquiv_apply_val, Sum.elim_inr] + rw [ite_eq_right (by omega), Nat.add_sub_cancel_left, + List.getD_eq_getElem _ _ (by simpa using hlt), List.getElem_map, + getElem_rank (fun e => group (tgt e) = g) c.1 c.2] + +omit hsrc in +/-- A numbered child edge is a row port exactly when its number is below the number of row +ports. -/ +theorem lt_length_portEdges_iff (c : ChildEdge src tgt group g) : + (childEdgeEquiv src tgt group g c : ℕ) < + (portEdges (List.ofFn fun v => (group v : ℕ)) g + (List.ofFn fun e => ((src e : ℕ), (tgt e : ℕ))) Prod.fst).length ↔ c.isLeft := by + rw [portEdges_eq src tgt group g Prod.fst src (fun _ => rfl), List.length_map] + rcases c with c | c + · simp only [childEdgeEquiv, Equiv.trans_apply, Equiv.sumCongr_apply, Sum.map_inl, + finSumFinEquiv_apply_left, Fin.val_castAdd, rankEquiv_apply_val, Sum.isLeft_inl, iff_true] + exact rank_lt (fun e => group (src e) = g) c.1 c.2 + · simp only [childEdgeEquiv, Equiv.trans_apply, Equiv.sumCongr_apply, Sum.map_inr, + finSumFinEquiv_apply_right, Fin.val_natAdd, Sum.isLeft_inr, Bool.false_eq_true, iff_false] + omega + +end Bridge + +/-- A filter-map that keeps every entry is a map. -/ +theorem map_filterMap_of_isSome {α β γ : Type*} (f : α → Option β) (y : β → γ) (d : γ) + (l : List α) (h : ∀ x ∈ l, (f x).isSome) : + (l.filterMap f).map y = l.map fun x => ((f x).map y).getD d := by + induction l with + | nil => rfl + | cons x l ih => + obtain ⟨b, hb⟩ := Option.isSome_iff_exists.mp (h x List.mem_cons_self) + rw [List.filterMap_cons_some hb, List.map_cons, List.map_cons, hb, + ih fun x' hx' => h x' (List.mem_cons_of_mem _ hx')] + rfl + +/-! ### The parent over `O` -/ + +section Parent + +variable {fl : List ℕ} (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2)))) + {D : DescribedNetwork VertexDesc} {r : ℕ} (hD : D.IsRowsFirstBipartite r) + +/-- The valuation ring `O = ℤ_(2)[U] / (f)` of `f = monicOf fl`. -/ +noncomputable abbrev RingO : Subring (AdjoinRoot ((monicOf fl).map (Int.castRingHom ℚ))) := + (AdjoinRoot.padicValuation 2 (monic_monicOf fl) hirr).integer + +/-- **The parent over `O`**: the network of the descriptors with the lifts of their entries. -/ +@[expose] noncomputable def parentNetwork : + TensorNetwork (RingO hirr) Bool (Fin D.vertices.length) (Fin D.numEdges) := + D.toNetwork hD fun d bs => liftO hirr (d.entry bs) + +/-- The sides of the parent: the first `r` vertices are the rows. -/ +@[expose] def parentSide (r : ℕ) {k : ℕ} (v : Fin k) : Bool := decide ((v : ℕ) < r) + +theorem parentSide_src (j : Fin D.numEdges) : parentSide r (D.srcFin hD j) = true := + decide_eq_true (DescribedNetwork.srcFin_lt hD j) + +theorem parentSide_tgt (j : Fin D.numEdges) : parentSide r (D.tgtFin hD j) = false := + decide_eq_false (by have := DescribedNetwork.le_tgtFin hD j; omega) + +/-- The row of an edge, among the rows. -/ +@[expose] def rowOfP (j : Fin D.numEdges) : Fin r := + ⟨D.srcFin hD j, DescribedNetwork.srcFin_lt hD j⟩ + +/-- The column of an edge, among the columns. -/ +@[expose] def colOfP (j : Fin D.numEdges) : Fin (D.vertices.length - r) := + ⟨D.tgtFin hD j - r, by + have := DescribedNetwork.le_tgtFin hD j + have := (D.tgtFin hD j).2 + omega⟩ + +/-- **The native packing of the parent**: `nativePacking` of its `(row, column)` edges, with `r` +rows, `k - r` columns, packets of `2 ^ a` slots and `64` symbols. -/ +@[expose] def nativeParentPacking (r a : ℕ) (D : DescribedNetwork VertexDesc) : + (List (List ℕ) × List (List ℕ)) × ℕ × List (ℕ × ℕ) := + PacketPacking.nativePacking r (D.vertices.length - r) a 64 + (D.edgeEnds.map fun e => (e.1, e.2 - r)) + +/-- The `(row, column)` edges of the parent. -/ +theorem edgeEnds_shift : + D.edgeEnds.map (fun e => (e.1, e.2 - r)) = PacketPacking.edgeList (rowOfP hD) (colOfP hD) := by + rw [DescribedNetwork.edgeEnds_eq hD, PacketPacking.edgeList, List.map_ofFn] + rfl + +/-- **The packing of the parent over `O`**: `PacketPacking.packing` of its `(row, column)` edges, +moved to the ends of the parent. -/ +@[expose] noncomputable def parentPacking (a : ℕ) : + PacketPacking.Packing (D.srcFin hD) (D.tgtFin hD) a 64 := + (PacketPacking.packing (rowOfP hD) (colOfP hD) a 64).relabel (D.srcFin hD) (D.tgtFin hD) + (fun v => if h : (v : ℕ) < r then + (PacketPacking.packing (rowOfP hD) (colOfP hD) a 64).rowLabel ⟨v, h⟩ else fun _ => 0) + (fun v => if h : r ≤ (v : ℕ) then + (PacketPacking.packing (rowOfP hD) (colOfP hD) a 64).colLabel ⟨v - r, by omega⟩ + else fun _ => 0) + (fun e => by rw [dite_eq_left (DescribedNetwork.srcFin_lt hD e)]; rfl) + (fun e => by rw [dite_eq_left (DescribedNetwork.le_tgtFin hD e)]; rfl) + +/-- **The native packing of the parent is its packing over `O`** when `64 ^ a` is at most the +number of edges: the words of its labels, its number of packets, and the packet and slot of each +edge. -/ +theorem nativeParentPacking_eq {a : ℕ} (hK : 64 ^ a ≤ D.numEdges) : + nativeParentPacking r a D = + ((List.ofFn fun v => BalancedSchedules.scheduleToWord + ((PacketPacking.packing (rowOfP hD) (colOfP hD) a 64).rowLabel v), + List.ofFn fun w => BalancedSchedules.scheduleToWord + ((PacketPacking.packing (rowOfP hD) (colOfP hD) a 64).colLabel w)), + (parentPacking hD a).numPackets, + List.ofFn fun e => (((parentPacking hD a).packet e : ℕ), + ((parentPacking hD a).slot e : ℕ))) := by + rw [nativeParentPacking, edgeEnds_shift hD, PacketPacking.nativePacking_eq _ _ hK] + rfl + +/-- **The native groups of the parent** in coordinate `i < a` are the groups of the packing. -/ +theorem groupsOf_eq {a i : ℕ} (hK : 64 ^ a ≤ D.numEdges) (hia : i < a) : + groupsOf r i D.vertices.length (nativeParentPacking r a D).1 = + List.ofFn fun v => (packingGroup (parentSide r) (parentPacking hD a) ⟨i, hia⟩ v : ℕ) := by + rw [nativeParentPacking_eq hD hK] + apply List.ext_getElem (by simp [groupsOf]) + intro v h₁ h₂ + simp only [groupsOf, List.getElem_map, List.getElem_range, List.getElem_ofFn, packingGroup, + parentSide, parentPacking, PacketPacking.Packing.relabel] + have hvk : v < D.vertices.length := by simpa using h₂ + by_cases hv : v < r + · rw [ite_eq_left hv, decide_eq_true hv, ite_eq_left rfl, dite_eq_left hv, + List.getD_ofFn (fun v : Fin r => BalancedSchedules.scheduleToWord _) ⟨v, hv⟩, + BalancedSchedules.scheduleToWord, List.getD_ofFn (fun t : Fin a => _) ⟨i, hia⟩] + · have hvr : r ≤ v := by omega + have hlt : v - r < D.vertices.length - r := by omega + rw [ite_eq_right hv, decide_eq_false hv, ite_eq_right (by simp), dite_eq_left hvr, + List.getD_ofFn (fun w : Fin (D.vertices.length - r) => BalancedSchedules.scheduleToWord _) + ⟨v - r, hlt⟩, BalancedSchedules.scheduleToWord, List.getD_ofFn (fun t : Fin a => _) ⟨i, hia⟩] + +/-- **The mode splitting at a node of the parent** over `O`: `callSplitting` of the parent with +its packing, at the node `t`, the precision `p`, and the lift of the interpolation value `ζ`. -/ +@[expose] noncomputable def parentSplitting (a p u h : ℕ) (hua : u ≤ a) {t : ℤ} + (ht : t ∈ recoveryNodes a) (hq : 0 < taperMult u h t) (ζ : List ℕ) := + TensorNetwork.callSplitting (RingO hirr) + (AdjoinRoot.algebraMap_mem_integer_padicValuation _ hirr) (parentNetwork hirr hD) + (parentSide r) (parentSide_src hD) (parentSide_tgt hD) (parentPacking hD a) p u h hua ht hq + (liftO hirr ζ) + +section Fields + +variable {a p u h : ℕ} (hua : u ≤ a) {t : ℤ} (ht : t ∈ recoveryNodes a) + (hq : 0 < taperMult u h t) (ζ : List ℕ) + +/-- The rows of the splitting are the first `r` vertices. -/ +theorem parentSplitting_side : (parentSplitting hirr hD a p u h hua ht hq ζ).side = parentSide r := + TensorNetwork.callSplitting_side (RingO hirr) _ (parentNetwork hirr hD) (parentSide r) _ _ + (parentPacking hD a) p u h hua ht hq (liftO hirr ζ) + +/-- The groups of the splitting are the groups of the packing in coordinate `v₂(t)`. -/ +theorem parentSplitting_group : (parentSplitting hirr hD a p u h hua ht hq ζ).group = + packingGroup (parentSide r) (parentPacking hD a) + ⟨_, padicValInt_lt_of_taperMult_pos hq hua⟩ := + TensorNetwork.callSplitting_group (RingO hirr) _ (parentNetwork hirr hD) (parentSide r) _ _ + (parentPacking hD a) p u h hua ht hq (liftO hirr ζ) + +/-- The packets of the splitting are the packets of the packing. -/ +theorem parentSplitting_packet : + (parentSplitting hirr hD a p u h hua ht hq ζ).packet = (parentPacking hD a).packet := + TensorNetwork.callSplitting_packet (RingO hirr) _ (parentNetwork hirr hD) (parentSide r) _ _ + (parentPacking hD a) p u h hua ht hq (liftO hirr ζ) + +end Fields + +section Numbered + +variable {P : ℕ} (group : Fin D.vertices.length → Fin 64) (packet : Fin D.numEdges → Fin P) + (slot : Fin D.numEdges → ℕ) (g : Fin 64) + +/-- The ports of a retained vertex: its parent ports, each as its child edge. -/ +@[expose] def retainedPort (v : {v // group v = g}) (j : ℕ) : + Option (ChildEdge (D.srcFin hD) (D.tgtFin hD) group g) := + if hj : j < D.numEdges then + if (v.1 : ℕ) < r then + if hg : group (D.srcFin hD ⟨j, hj⟩) = g then some (.inl ⟨⟨j, hj⟩, hg⟩) else none + else if hg : group (D.tgtFin hD ⟨j, hj⟩) = g then some (.inr ⟨⟨j, hj⟩, hg⟩) else none + else none + +/-- **A parent port of a retained vertex is a child edge**, numbered by `childEdgeOf`. -/ +theorem retainedPort_spec (v : {v // group v = g}) {j : ℕ} (hj : j ∈ (D.vertices[(v.1 : ℕ)]'v.1.2).1) : + ∃ c, retainedPort hD group g v j = some c ∧ + (childEdgeEquiv (D.srcFin hD) (D.tgtFin hD) group g c : ℕ) = + childEdgeOf r (List.ofFn fun v => (group v : ℕ)) g + (List.ofFn fun e => ((D.srcFin hD e : ℕ), (D.tgtFin hD e : ℕ))) v j := by + have hjm : j < D.numEdges := hD.1 _ (List.getElem_mem _) j hj + have hends := (DescribedNetwork.mem_ports_iff hD v.1 ⟨j, hjm⟩).mp hj + have hsrcr := DescribedNetwork.srcFin_lt hD + have htgtr := DescribedNetwork.le_tgtFin hD + by_cases hvr : (v.1 : ℕ) < r + · have hs : D.srcFin hD ⟨j, hjm⟩ = v.1 := hends.resolve_right fun h => by + have := htgtr ⟨j, hjm⟩ + rw [h] at this + omega + have hg : group (D.srcFin hD ⟨j, hjm⟩) = g := hs ▸ v.2 + refine ⟨.inl ⟨⟨j, hjm⟩, hg⟩, ?_, ?_⟩ + · simp only [retainedPort, dite_eq_left hjm, ite_eq_left hvr, dite_eq_left hg] + · have := childEdgeOf_src (D.srcFin hD) (D.tgtFin hD) group g hsrcr ⟨j, hjm⟩ hg + rw [show ((v.1 : Fin _) : ℕ) = (D.srcFin hD ⟨j, hjm⟩ : ℕ) by rw [hs]] + exact this.symm + · have ht : D.tgtFin hD ⟨j, hjm⟩ = v.1 := hends.resolve_left fun h => by + have := hsrcr ⟨j, hjm⟩ + rw [h] at this + exact hvr this + have hg : group (D.tgtFin hD ⟨j, hjm⟩) = g := ht ▸ v.2 + refine ⟨.inr ⟨⟨j, hjm⟩, hg⟩, ?_, ?_⟩ + · simp only [retainedPort, dite_eq_left hjm, ite_eq_right hvr, dite_eq_left hg] + · have := childEdgeOf_tgt (D.srcFin hD) (D.tgtFin hD) group g htgtr ⟨j, hjm⟩ hg + rw [show ((v.1 : Fin _) : ℕ) = (D.tgtFin hD ⟨j, hjm⟩ : ℕ) by rw [ht]] + exact this.symm + +/-- **The ports of the packet vertex `q`**: the child edges of packet `q`, in increasing order of +their numbers. -/ +@[expose] def packetPorts (q : Fin P) : List (ChildEdge (D.srcFin hD) (D.tgtFin hD) group g) := + ((List.finRange _).filter fun j => + childTgt (D.srcFin hD) (D.tgtFin hD) group packet g + ((childEdgeEquiv (D.srcFin hD) (D.tgtFin hD) group g).symm j) = .inr q).map + (childEdgeEquiv (D.srcFin hD) (D.tgtFin hD) group g).symm + +/-- **The ports of the numbered child vertices**: the renumbered parent ports of a retained +vertex, and the child edges of a packet. -/ +@[expose] def numberedPorts : + ChildVertex group g P → List (ChildEdge (D.srcFin hD) (D.tgtFin hD) group g) := + Sum.elim (fun v => (D.vertices[(v.1 : ℕ)]'v.1.2).1.filterMap (retainedPort hD group g v)) + (packetPorts hD group packet g) + +/-- **The descriptors of the numbered child vertices**: the parent descriptor of a retained +vertex, and the packet descriptor of a packet. -/ +@[expose] def numberedDesc (fl : List ℕ) (a i pt : ℕ) (t : ℤ) (ζ β : List ℕ) : + ChildVertex group g P → VertexDesc := + Sum.elim (fun v => (D.vertices[(v.1 : ℕ)]'v.1.2).2) fun q => + .inr (packetCtx fl a i pt t ζ g q, + (packetPorts hD group packet g q).map fun c => + (c.isLeft, slot (Sum.elim Subtype.val Subtype.val c)), + betaSlice i pt β q) + +/-- The bit that a retained vertex reads at a parent port. -/ +theorem getD_map_retainedPort (v : {v // group v = g}) {j : ℕ} + (hj : j ∈ (D.vertices[(v.1 : ℕ)]'v.1.2).1) + (y : ChildEdge (D.srcFin hD) (D.tgtFin hD) group g → Bool) : + ((retainedPort hD group g v j).map y).getD false = + if parentSide r v.1 then + (if hg : group (D.srcFin hD ⟨j, hD.1 _ (List.getElem_mem _) j hj⟩) = g then + y (.inl ⟨_, hg⟩) else false) + else + (if hg : group (D.tgtFin hD ⟨j, hD.1 _ (List.getElem_mem _) j hj⟩) = g then + y (.inr ⟨_, hg⟩) else false) := by + have hjm : j < D.numEdges := hD.1 _ (List.getElem_mem _) j hj + by_cases hvr : (v.1 : ℕ) < r + · simp only [retainedPort, dite_eq_left hjm, ite_eq_left hvr, parentSide, decide_eq_true hvr, + ite_true] + split_ifs <;> rfl + · simp only [retainedPort, dite_eq_left hjm, ite_eq_right hvr, parentSide, + decide_eq_false hvr, Bool.false_eq_true, ite_false] + split_ifs <;> rfl + +/-- The ports of a packet are its child edges. -/ +theorem mem_packetPorts (q : Fin P) (c : ChildEdge (D.srcFin hD) (D.tgtFin hD) group g) : + c ∈ packetPorts hD group packet g q ↔ packet (Sum.elim Subtype.val Subtype.val c) = q := by + simp only [packetPorts, List.mem_map, List.mem_filter, List.mem_finRange, true_and, + decide_eq_true_eq] + constructor + · rintro ⟨j, hj, rfl⟩ + rcases hc : (childEdgeEquiv (D.srcFin hD) (D.tgtFin hD) group g).symm j with c | c <;> + rw [hc] at hj <;> simpa [childTgt] using hj + · intro hc + refine ⟨childEdgeEquiv (D.srcFin hD) (D.tgtFin hD) group g c, ?_, by simp⟩ + rw [Equiv.symm_apply_apply] + rcases c with c | c <;> simpa [childTgt] using hc + +/-- The ports of a packet are distinct. -/ +theorem nodup_packetPorts (q : Fin P) : (packetPorts hD group packet g q).Nodup := + ((List.nodup_finRange _).filter _).map (Equiv.injective _) + +/-- **The native ports of a packet** are its numbered ports. -/ +theorem childPorts_packet (q : Fin P) : + childPorts (List.ofFn fun j => + (((childVertexEquiv (P := P) group g) (childSrc (D.srcFin hD) (D.tgtFin hD) group g + ((childEdgeEquiv (D.srcFin hD) (D.tgtFin hD) group g).symm j)) : ℕ), + ((childVertexEquiv (P := P) group g) (childTgt (D.srcFin hD) (D.tgtFin hD) group packet g + ((childEdgeEquiv (D.srcFin hD) (D.tgtFin hD) group g).symm j)) : ℕ))) + ((List.ofFn fun v => (group v : ℕ)).count (g : ℕ) + q) = + (packetPorts hD group packet g q).map fun c => + (childEdgeEquiv (D.srcFin hD) (D.tgtFin hD) group g c : ℕ) := by + have h1 : (List.ofFn fun v => (group v : ℕ)).count (g : ℕ) + q = + ((childVertexEquiv (P := P) group g) (.inr q) : ℕ) := by + rw [count_ofFn_group] + simp [childVertexEquiv] + rw [h1, childPorts, List.length_ofFn, ← List.map_coe_finRange_eq_range, List.filter_map, + packetPorts, List.map_map] + congr 1 + · funext j + simp + · refine List.filter_congr fun j _ => ?_ + simp only [Function.comp_apply, List.getD_ofFn, Fin.val_inj, Equiv.apply_eq_iff_eq, + decide_eq_decide] + constructor + · rintro (h | h) + · rcases hc : (childEdgeEquiv (D.srcFin hD) (D.tgtFin hD) group g).symm j with c | c <;> + rw [hc] at h <;> simp [childSrc] at h + · exact h + · exact Or.inr + +/-- **The native child is the numbered child**: with the groups, packets and slots of the native +packing given by functions, the native child lists the numbered child vertices with their +numbered ports and descriptors. -/ +theorem nativeChild_eq (fl : List ℕ) (a i pt : ℕ) (t : ℤ) (ζ β : List ℕ) + (pk : (List (List ℕ) × List (List ℕ)) × ℕ × List (ℕ × ℕ)) + (hgroups : groupsOf r i D.vertices.length pk.1 = List.ofFn fun v => (group v : ℕ)) + (hpk1 : pk.2.1 = P) (hpk2 : pk.2.2 = List.ofFn fun e => ((packet e : ℕ), slot e)) : + nativeChild fl a i pt t ζ r D pk β g = + ⟨(members fun e => group (D.srcFin hD e) = g).length + + (members fun e => group (D.tgtFin hD e) = g).length, + List.ofFn fun c => + ((numberedPorts hD group packet g ((childVertexEquiv (P := P) group g).symm c)).map + fun e => (childEdgeEquiv (D.srcFin hD) (D.tgtFin hD) group g e : ℕ), + numberedDesc hD group packet slot g fl a i pt t ζ β + ((childVertexEquiv (P := P) group g).symm c))⟩ := by + have hpackets : pk.2.2.map Prod.fst = List.ofFn fun e => (packet e : ℕ) := by + rw [hpk2, List.map_ofFn] + rfl + have hcedges := childEdgeList_eq (P := P) (D.srcFin hD) (D.tgtFin hD) group packet g + have hsrcr := DescribedNetwork.srcFin_lt hD + have htgtr := DescribedNetwork.le_tgtFin hD + simp only [nativeChild] + rw [hgroups, DescribedNetwork.edgeEnds_eq hD, hpackets, hpk1, hpk2, hcedges] + simp only [DescribedNetwork.mk.injEq, List.length_ofFn, true_and] + refine Eq.trans ?_ (List.ofFn_add (n := (members fun v => group v = g).length) (m := P)).symm + congr 1 + · rw [filter_range_group, List.map_map] + apply List.ext_getElem (by simp) + intro n h₁ h₂ + have hn : n < (members fun v => group v = g).length := by simpa using h₁ + have hmem : group (members fun v => group v = g)[n] = g := + of_decide_eq_true (List.mem_filter.mp (List.getElem_mem hn)).2 + have hevl : (childVertexEquiv (P := P) group g).symm + (Fin.castLE (Nat.le_add_right _ _) ⟨n, hn⟩) = + .inl ⟨(members fun v => group v = g)[n], hmem⟩ := by + rw [childVertexEquiv, Equiv.symm_trans_apply, + show Fin.castLE (Nat.le_add_right _ _) ⟨n, hn⟩ = Fin.castAdd _ ⟨n, hn⟩ from rfl, + finSumFinEquiv_symm_apply_castAdd] + rfl + simp only [List.getElem_map, List.getElem_ofFn, Function.comp_apply] + rw [hevl] + refine Prod.ext ?_ ?_ + · simp only [numberedPorts, Sum.elim_inl] + rw [List.getD_eq_getElem _ _ ((members fun v => group v = g)[n]).2] + rw [map_filterMap_of_isSome _ _ 0 _ fun j hj => by + obtain ⟨c, hc, -⟩ := retainedPort_spec hD group g ⟨_, hmem⟩ hj + rw [hc] + rfl] + refine List.map_congr_left fun j hj => ?_ + obtain ⟨c, hc, hce⟩ := retainedPort_spec hD group g ⟨_, hmem⟩ hj + rw [hc] + exact hce.symm + · simp only [numberedDesc, Sum.elim_inl] + rw [List.getD_eq_getElem _ _ ((members fun v => group v = g)[n]).2] + · apply List.ext_getElem (by simp) + intro n h₁ h₂ + have hn : n < P := by simpa using h₁ + have hevr : (childVertexEquiv (P := P) group g).symm (Fin.natAdd _ ⟨n, hn⟩) = + .inr ⟨n, hn⟩ := by + rw [childVertexEquiv, Equiv.symm_trans_apply, finSumFinEquiv_symm_apply_natAdd] + rfl + simp only [List.getElem_map, List.getElem_range, List.getElem_ofFn] + rw [hevr] + have hcpq := childPorts_packet hD group packet g ⟨n, hn⟩ + simp only at hcpq + refine Prod.ext hcpq ?_ + simp only [numberedDesc, Sum.elim_inr] + congr 2 + refine Prod.ext ?_ rfl + rw [hcpq, List.map_map] + refine List.map_congr_left fun c _ => ?_ + refine Prod.ext ?_ ?_ + · simp only [Function.comp_apply] + rw [Bool.eq_iff_iff, decide_eq_true_iff] + exact lt_length_portEdges_iff (D.srcFin hD) (D.tgtFin hD) group g c + · simp only [Function.comp_apply] + rw [parentEdge_childEdgeEquiv] + rcases c with c | c <;> simp + +end Numbered + +section Child + +variable {a p u h : ℕ} (hK : 64 ^ a ≤ D.numEdges) (hua : u ≤ a) {t : ℤ} + (ht : t ∈ recoveryNodes a) (hq : 0 < taperMult u h t) (ζ β : List ℕ) (g : Fin 64) + +/-- The valuation of the node. -/ +local notation "iₜ" => padicValInt 2 t +/-- The precision of the node. -/ +local notation "ptₜ" => nodePrecision p h a (Fintype.card (Fin D.numEdges)) (padicValInt 2 t) +/-- The mode splitting at the node. -/ +local notation "S₀" => parentSplitting hirr hD a p u h hua ht hq ζ +/-- The mode tuple of `β`. -/ +local notation "β₀" => BudgetTuples.ofList (modeList (PacketPacking.Packing.numPackets (parentPacking hD a)) + (2 ^ (padicValInt 2 t + 1)) (nodePrecision p h a (Fintype.card (Fin D.numEdges)) + (padicValInt 2 t))) β +/-- The numbering of the child vertices. -/ +local notation "ev₀" => childVertexEquiv (P := PacketPacking.Packing.numPackets (parentPacking hD a)) + (TensorNetwork.ModeSplitting.group (parentSplitting hirr hD a p u h hua ht hq ζ)) g +/-- The numbering of the child edges. -/ +local notation "ee₀" => childEdgeEquiv (D.srcFin hD) (D.tgtFin hD) + (TensorNetwork.ModeSplitting.group (parentSplitting hirr hD a p u h hua ht hq ζ)) g + +/-- **The entries of the retained vertices** of the numbered child are the tensors of the child. -/ +theorem entry_numbered_inl (v : {v // (S₀).group v = g}) + (y : ChildEdge (D.srcFin hD) (D.tgtFin hD) (S₀).group g → Bool) : + reductionLift (monic_monicOf fl) hirr ptₜ (val ptₜ fl + ((numberedDesc hD (S₀).group (S₀).packet (fun e => ((parentPacking hD a).slot e : ℕ)) g + fl a iₜ ptₜ t ζ β (.inl v)).entry + ((numberedPorts hD (S₀).group (S₀).packet g (.inl v)).map y))) = + ((S₀).child β₀ g).tensor (.inl v) y := by + rw [TensorNetwork.ModeSplitting.child_tensor_inl_eq_tensor (S₀) β₀ g v y, TensorNetwork.map_tensor, + ← mk_liftO, parentSplitting_side hirr hD hua ht hq ζ] + simp only [numberedDesc, numberedPorts, Sum.elim_inl, parentNetwork, + DescribedNetwork.toNetwork, DescribedNetwork.wordBits] + congr 3 + rw [map_filterMap_of_isSome _ _ false _ fun j hj => by + obtain ⟨c, hc, -⟩ := retainedPort_spec hD (S₀).group g v hj + rw [hc] + rfl] + refine List.map_congr_left fun j hj => ?_ + rw [getD_map_retainedPort hD (S₀).group g v hj y, + List.getD_ofFn_of_lt _ (hD.1 _ (List.getElem_mem _) j hj)] + rfl + +/-- **The entries of the packet vertices** of the numbered child are the tensors of the child. -/ +theorem entry_numbered_inr (q : Fin (parentPacking hD a).numPackets) + (y : ChildEdge (D.srcFin hD) (D.tgtFin hD) (S₀).group g → Bool) : + reductionLift (monic_monicOf fl) hirr ptₜ (val ptₜ fl + ((numberedDesc hD (S₀).group (S₀).packet (fun e => ((parentPacking hD a).slot e : ℕ)) g + fl a iₜ ptₜ t ζ β (.inr q)).entry + ((numberedPorts hD (S₀).group (S₀).packet g (.inr q)).map y))) = + ((S₀).child β₀ g).tensor (.inr q) y := by + have hia : iₜ < a := padicValInt_lt_of_taperMult_pos hq hua + have hsym := PacketPacking.packetSymbols_eq (rowOfP hD) (colOfP hD) _ _ q ⟨iₜ, hia⟩ + simp only [numberedDesc, numberedPorts, Sum.elim_inr] + exact TensorNetwork.map_val_entry_child_inr (N := (parentNetwork hirr hD).map + (Ideal.Quotient.mk (nodeIdeal (RingO hirr) (Fintype.card (Fin D.numEdges)) a h p t))) + (parentSide r) (parentSide_src hD) (parentSide_tgt hD) + (packingGroup_src_tgt (parentSide r) (parentSide_src hD) (parentSide_tgt hD) + (parentPacking hD a) ⟨iₜ, hia⟩) + (packing_slot_lt (parentPacking hD a)) hia _ _ (two_pow_eq_zero_twoPowIdeal _ _) + (Finset.mem_of_mem_erase ht) (two_pow_padicValInt_dvd_and_not_dvd (Finset.ne_of_mem_erase ht)).1 + (two_pow_padicValInt_dvd_and_not_dvd (Finset.ne_of_mem_erase ht)).2 β₀ g q _ + (nodup_packetPorts hD (S₀).group (S₀).packet g q) + (fun c => (mem_packetPorts hD (S₀).group (S₀).packet g q c).trans (by + rw [parentSplitting_packet hirr hD hua ht hq ζ] + exact Iff.rfl)) + (packetCtx fl a iₜ ptₜ t ζ g q) (betaSlice iₜ ptₜ β q) + (reductionLift (monic_monicOf fl) hirr ptₜ) (mk_liftO hirr ptₜ ζ).symm rfl rfl rfl rfl rfl rfl + (by simp only [packetCtx, hsym, Fin.val_inj]; rfl) + (by simp only [packetCtx, hsym, Fin.val_inj]; rfl) + (by simp only [packetCtx, hsym, ne_eq, Fin.val_inj]; rfl) + (getD_betaSlice β q) y + +include hK in +/-- **The native child has the contraction of the child** ([OpenAI2026ETH], §D.4.6). For a +rows-first parent with `64 ^ a` at most its number of edges, a node `t` of positive multiplicity +with `u ≤ a`, an interpolation value `ζ`, packet-major exponents `β` and a group `g`, the native +child at the precision `p_t` of the node, with the entries of its descriptors in `A_{p_t}` mapped +along `reductionLift` to `O / (2 ^ {p_t})`, has the contraction of the child of group `g` of the +mode splitting at `t` (`parentSplitting`) at the mode tuple of `β`. -/ +theorem contraction_nativeChild : + (nativeChild fl a (padicValInt 2 t) + (nodePrecision p h a (Fintype.card (Fin D.numEdges)) (padicValInt 2 t)) t ζ r D + (nativeParentPacking r a D) β g).contraction + (fun d bs => reductionLift (monic_monicOf fl) hirr + (nodePrecision p h a (Fintype.card (Fin D.numEdges)) (padicValInt 2 t)) + (val (nodePrecision p h a (Fintype.card (Fin D.numEdges)) (padicValInt 2 t)) fl + (d.entry bs))) = + ((parentSplitting hirr hD a p u h hua ht hq ζ).child + (BudgetTuples.ofList (modeList (parentPacking hD a).numPackets (2 ^ (padicValInt 2 t + 1)) + (nodePrecision p h a (Fintype.card (Fin D.numEdges)) (padicValInt 2 t))) β) g).contraction := by + have hia : iₜ < a := padicValInt_lt_of_taperMult_pos hq hua + have hgroups : groupsOf r iₜ D.vertices.length (nativeParentPacking r a D).1 = + List.ofFn fun v => ((S₀).group v : ℕ) := by + rw [parentSplitting_group hirr hD hua ht hq ζ] + exact groupsOf_eq hD hK hia + have hpk1 : (nativeParentPacking r a D).2.1 = (parentPacking hD a).numPackets := by + rw [nativeParentPacking_eq hD hK] + have hpk2 : (nativeParentPacking r a D).2.2 = + List.ofFn fun e => (((S₀).packet e : ℕ), ((parentPacking hD a).slot e : ℕ)) := by + rw [nativeParentPacking_eq hD hK, parentSplitting_packet hirr hD hua ht hq ζ] + rw [nativeChild_eq hD (S₀).group (S₀).packet (fun e => ((parentPacking hD a).slot e : ℕ)) g + fl a iₜ ptₜ t ζ β _ hgroups hpk1 hpk2] + refine DescribedNetwork.contraction_eq_of_ports ((S₀).child β₀ g) _ _ _ _ _ ?_ + rintro (v | q) y + · exact entry_numbered_inl hirr hD hua ht hq ζ β g v y + · exact entry_numbered_inr hirr hD hua ht hq ζ β g q y + +end Child + +end Parent end NativeChild diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean index a6606b51b9..46ead171a8 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Recursion.lean @@ -182,7 +182,7 @@ variable {R V E : Type*} [CommRing R] [Fintype E] {a r : ℕ} a packing `Q` of its edges, and a nonzero grid point `t` of valuation `i < a`, the mode splitting of the Cauchy matrix at `T = t + α ζ` with the vertices grouped by their symbol in coordinate `i` of `Q` (`cauchyModeSplitting`, oriented by `packingGroup_src_tgt`). -/ -noncomputable def nodeSplitting (N : TensorNetwork R Bool V E) (side : V → Bool) +@[expose] noncomputable def nodeSplitting (N : TensorNetwork R Bool V E) (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) (htgt : ∀ e, side (N.tgt e) = false) (Q : PacketPacking.Packing N.src N.tgt a r) (κ : ℤ₍₂₎ →+* R) (ζ : R) {p : ℕ} (hp : (2 : R) ^ p = 0) {t : ℤ} (ht : t ∈ recoveryNodes a) (hia : padicValInt 2 t < a) : @@ -225,7 +225,7 @@ variable {K : Type*} [CommRing K] [Algebra ℚ K] (B : Subring K) /-- The mode splitting at the active node `t` and the point `z` of a call with input precision `p`: `nodeSplitting` for the network reduced modulo `2 ^ p_t`, at the reduction of `z`. -/ -noncomputable def callSplitting (N : TensorNetwork B Bool V E) (side : V → Bool) +@[expose] noncomputable def callSplitting (N : TensorNetwork B Bool V E) (side : V → Bool) (hsrc : ∀ e, side (N.src e) = true) (htgt : ∀ e, side (N.tgt e) = false) {a r : ℕ} (Q : PacketPacking.Packing N.src N.tgt a r) (p u h : ℕ) (hua : u ≤ a) {t : ℤ} (ht : t ∈ recoveryNodes a) (hq : 0 < taperMult u h t) (z : B) : From 215d85d102194b127cc67e558649b4a89e0a6d3b Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:26:49 -0400 Subject: [PATCH 077/129] feat(circuit/frontier): the call count with a constant explicit in C (as #PR-B) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/ScheduleCount.lean | 30 +++++++++++-------- .../Foundations/Data/Nat/RecursionTree.lean | 28 ++++++++--------- ComplexityTests/ScheduleCount.lean | 13 ++++---- 3 files changed, 40 insertions(+), 31 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ScheduleCount.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ScheduleCount.lean index 2a989ab9df..5a7c9da806 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ScheduleCount.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ScheduleCount.lean @@ -19,14 +19,16 @@ Under the parameter schedule of `Cslib.Circuits.Frontier.ContractionSchedule`, a `ContractionSchedule.ActiveBounds.card_modeBudget_le`), and there are at most `⌊log₂ s⌋` levels. The excess exponents sum to `O(s / √⌊log₂ s⌋)` (`ContractionSchedule.sum_branchExponent_le`), so the recursion has `2 ^ O(s / √⌊log₂ s⌋)` calls -(`ContractionSchedule.exists_recursionBound_le`): the count of Appendix D.4.7 (p. 64), in the +(`ContractionSchedule.recursionBound_le_two_pow_div_sqrt_log_two`, with the constant +`245760 C + 37`): the count of Appendix D.4.7 (p. 64), in the natural-number scale of the contraction interface. It bounds the nodes of the depth-first evaluation (`Nat.recursionBound`), hence the fuel of `Cslib.GenericRAM.StackRecursion.Scheme.certificate`. ## Main results -* `Cslib.Circuits.Frontier.ContractionSchedule.exists_recursionBound_le`: the call count. +* `Cslib.Circuits.Frontier.ContractionSchedule.recursionBound_le_two_pow_div_sqrt_log_two`: the + call count. ## References @@ -37,16 +39,20 @@ public section namespace Cslib.Circuits.Frontier.ContractionSchedule -/-- **The recursion has `2 ^ O(s / √⌊log₂ s⌋)` calls.** For every `C` there is `K` such that, -from `s = 2` on, a recursion of depth at most `D ≤ ⌊log₂ s⌋` whose calls at depth `d` have at -most `2 ^ (2 ⌊log₂ s⌋ + branchExponent C s d)` children has at most -`2 ^ (K s / √⌊log₂ s⌋)` nodes below its root. -/ -theorem exists_recursionBound_le (C : ℕ) : ∃ K, ∀ s D, 2 ≤ s → D ≤ Nat.log 2 s → +/-- **The recursion has `2 ^ O(s / √⌊log₂ s⌋)` calls**, with the constant `245760 C + 37`. From +`s = 2` on, a recursion of depth at most `D ≤ ⌊log₂ s⌋` whose calls at depth `d` have at most +`2 ^ (2 ⌊log₂ s⌋ + branchExponent C s d)` children has at most +`2 ^ ((245760 C + 37) s / √⌊log₂ s⌋)` nodes below its root. The constant is linear in `C`, so one +clock serves every admissibility constant at once, as a machine that does not know `C` needs. -/ +theorem recursionBound_le_two_pow_div_sqrt_log_two (C : ℕ) {s D : ℕ} (hs : 2 ≤ s) + (hD : D ≤ Nat.log 2 s) : Nat.recursionBound (fun d => 2 ^ (2 * Nat.log 2 s + branchExponent C s d)) D 0 ≤ - 2 ^ (K * s / Nat.sqrt (Nat.log 2 s)) := by - obtain ⟨K, hK⟩ := Nat.exists_recursionBound_le_two_pow_div_sqrt_log_two 2 (245760 * C + 4) - refine ⟨K, fun s D hs hD => (Nat.recursionBound_le _ D 0).trans ?_⟩ - simpa only [Nat.zero_add] using hK s D (fun d => 2 ^ (2 * Nat.log 2 s + branchExponent C s d)) - (branchExponent C s) hs hD (fun _ _ => le_rfl) (sum_branchExponent_le C hs hD) + 2 ^ ((245760 * C + 37) * s / Nat.sqrt (Nat.log 2 s)) := by + have h := Nat.mul_prod_le_two_pow_div_sqrt_log_two 2 (245760 * C + 4) hs hD + (b := fun d => 2 ^ (2 * Nat.log 2 s + branchExponent C s d)) (e := branchExponent C s) + (fun _ _ => le_rfl) (sum_branchExponent_le C hs hD) + rw [show 1 + 16 * 2 + (245760 * C + 4) = 245760 * C + 37 by ring] at h + refine (Nat.recursionBound_le _ D 0).trans ?_ + simpa only [Nat.zero_add] using h end Cslib.Circuits.Frontier.ContractionSchedule diff --git a/Complexity/Foundations/Data/Nat/RecursionTree.lean b/Complexity/Foundations/Data/Nat/RecursionTree.lean index e5f2982128..ff3e344693 100644 --- a/Complexity/Foundations/Data/Nat/RecursionTree.lean +++ b/Complexity/Foundations/Data/Nat/RecursionTree.lean @@ -25,7 +25,8 @@ most `k + 1` times the product of the branching factors (`Nat.recursionBound_le` **Recursion trees of size `2 ^ O(n / √(log n))`.** If a call at depth `j` has at most `2 ^ (c ⌊log₂ n⌋ + e j)` children, there are at most `⌊log₂ n⌋` levels, and the excess exponents sum to `O(n / √⌊log₂ n⌋)`, then the tree has `2 ^ O(n / √⌊log₂ n⌋)` nodes -(`Nat.exists_recursionBound_le_two_pow_div_sqrt_log_two`), in the natural-number scale of +(`Nat.mul_prod_le_two_pow_div_sqrt_log_two`, with the explicit constant `1 + 16 c + E`), in the +natural-number scale of `Nat.exists_three_pow_mul_le_two_pow_div_sqrt_log_two`. The polynomial factors `2 ^ (c ⌊log₂ n⌋)` contribute `2 ^ O(log² n)`, which the scale absorbs. Excess exponents halving with the depth sum to at most twice the first one (`Nat.sum_div_two_pow_le`). @@ -42,8 +43,8 @@ a call at depth `d` has `s ^ O(1) exp(O(s / √λ · (√2 / 8) ^ d))` children * `Nat.recursionBound_le`: at most `k + 1` times the product of the branching factors. * `Nat.sum_div_two_pow_le`: `∑ j < D, x / 2 ^ j ≤ 2 x`. -* `Nat.exists_recursionBound_le_two_pow_div_sqrt_log_two`: recursion trees of size - `2 ^ O(n / √⌊log₂ n⌋)`. +* `Nat.mul_prod_le_two_pow_div_sqrt_log_two`: recursion trees of size `2 ^ O(n / √⌊log₂ n⌋)`, + with an explicit constant. ## References @@ -132,17 +133,16 @@ theorem log_two_sq_le_div_sqrt_log_two {n : ℕ} (hn : 2 ≤ n) : have h2 := mul_div_le_mul_div_add 8 n (sqrt L) omega -/-- **Recursion trees of size `2 ^ O(n / √⌊log₂ n⌋)`.** For constants `c` and `E` there is `K` -such that, from `n = 2` on: if a call at depth `j < D` has at most `2 ^ (c ⌊log₂ n⌋ + e j)` -children, `D ≤ ⌊log₂ n⌋`, and `∑ j < D, e j ≤ E (n / √⌊log₂ n⌋)`, then -`(D + 1) ∏ j < D, max 1 (b j) ≤ 2 ^ (K n / √⌊log₂ n⌋)`, which bounds the nodes of the recursion -tree by `recursionBound_le`. -/ -theorem exists_recursionBound_le_two_pow_div_sqrt_log_two (c E : ℕ) : - ∃ K, ∀ (n D : ℕ) (b e : ℕ → ℕ), 2 ≤ n → D ≤ log 2 n → - (∀ j < D, b j ≤ 2 ^ (c * log 2 n + e j)) → - ∑ j ∈ range D, e j ≤ E * (n / sqrt (log 2 n)) → - (D + 1) * ∏ j ∈ range D, max 1 (b j) ≤ 2 ^ (K * n / sqrt (log 2 n)) := by - refine ⟨1 + 16 * c + E, fun n D b e hn hD hb he => ?_⟩ +/-- **Recursion trees of size `2 ^ O(n / √⌊log₂ n⌋)`**, with the constant `1 + 16 c + E`. From +`n = 2` on: if a call at depth `j < D` has at most `2 ^ (c ⌊log₂ n⌋ + e j)` children, +`D ≤ ⌊log₂ n⌋`, and `∑ j < D, e j ≤ E (n / √⌊log₂ n⌋)`, then +`(D + 1) ∏ j < D, max 1 (b j) ≤ 2 ^ ((1 + 16 c + E) n / √⌊log₂ n⌋)`, which bounds the nodes of the +recursion tree by `recursionBound_le`. The constant is linear in `E`, so a single clock serves a +family of excess budgets growing linearly in a parameter. -/ +theorem mul_prod_le_two_pow_div_sqrt_log_two (c E : ℕ) {n D : ℕ} {b e : ℕ → ℕ} (hn : 2 ≤ n) + (hD : D ≤ log 2 n) (hb : ∀ j < D, b j ≤ 2 ^ (c * log 2 n + e j)) + (he : ∑ j ∈ range D, e j ≤ E * (n / sqrt (log 2 n))) : + (D + 1) * ∏ j ∈ range D, max 1 (b j) ≤ 2 ^ ((1 + 16 * c + E) * n / sqrt (log 2 n)) := by set L := log 2 n set S := n / sqrt L have hL : L ≤ S := log_two_le_div_sqrt_log_two n diff --git a/ComplexityTests/ScheduleCount.lean b/ComplexityTests/ScheduleCount.lean index d9886e70c8..67b17d706b 100644 --- a/ComplexityTests/ScheduleCount.lean +++ b/ComplexityTests/ScheduleCount.lean @@ -15,11 +15,14 @@ namespace ComplexityTests.ScheduleCount open Cslib.Circuits.Frontier.ContractionSchedule --- At `s = 16` the depth is at most `⌊log₂ 16⌋ = 4`; one constant serves every depth and size. -example (C : ℕ) : ∃ K, ∀ D ≤ Nat.log 2 16, +-- At `s = 16` the depth is at most `⌊log₂ 16⌋ = 4`; the constant `245760 C + 37` serves every +-- depth and size. +example (C : ℕ) : ∀ D ≤ Nat.log 2 16, Nat.recursionBound (fun d => 2 ^ (2 * Nat.log 2 16 + branchExponent C 16 d)) D 0 ≤ - 2 ^ (K * 16 / Nat.sqrt (Nat.log 2 16)) := by - obtain ⟨K, hK⟩ := exists_recursionBound_le C - exact ⟨K, fun D hD => hK 16 D (by norm_num) hD⟩ + 2 ^ ((245760 * C + 37) * 16 / Nat.sqrt (Nat.log 2 16)) := + fun _ hD => recursionBound_le_two_pow_div_sqrt_log_two C (by norm_num) hD + +-- The constant is linear in `C`: at `C = 1` it is `245797`. +example : 245760 * 1 + 37 = 245797 := by norm_num end ComplexityTests.ScheduleCount From 6888efe4044303192111dd8da3985f099bd0d4f2 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:29:27 -0400 Subject: [PATCH 078/129] feat(Classes/CountingSimulation): the guard of the fast contraction algorithm and its small exceptions Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../CountingSimulation/FastAlgorithm.lean | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean new file mode 100644 index 0000000000..b6042451e3 --- /dev/null +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean @@ -0,0 +1,95 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Complexity.Classes.CountingSimulation.Guarded +public import Complexity.Computability.Complexity.Classes.CountingSimulation.Admissible +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionMachine +import Mathlib.Analysis.SpecificLimits.Normed + +/-! +# The fast contraction algorithm, assembled + +The guard of the fast contraction algorithm (`CountingSimulation.fastGuard`) tests what +`ExplicitAdmissible` already implies (rows first, full tables) and the degree bound +`degreeBound m` of the schedule (§D.4.2 of [OpenAI2026ETH], (D.6), p. 58). An admissible network of +degree at most `C ⌊log₂ s⌋` fails it only for `s` below a threshold depending on `C` +(`CountingSimulation.exists_le_of_fastGuard_eq_false`), since `degreeBound m ≥ m ^ (9 / 10)` and +`m ≥ s`. + +## Main definitions + +* `Cslib.Complexity.CountingSimulation.fastGuard`: the guard. + +## Main results + +* `Cslib.Complexity.CountingSimulation.exists_le_of_fastGuard_eq_false`: admissible networks + failing the guard are small. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Theorem D.1 (p. 53) and + Appendix D.4. +-/ + +public section + +namespace Cslib.Complexity.CountingSimulation + +open Cslib.Circuits.Frontier Cslib.Circuits.Frontier.ContractionSchedule Filter + +/-- **The guard of the fast algorithm**: the network is rows-first bipartite at its number of +rows, every table lists the values at all bit strings on its ports, and its maximum degree is at +most `degreeBound m`, for `m` its number of edges. -/ +@[expose] def fastGuard (G : ℕ × ExplicitTensorNetwork ℕ) : Bool := + decide (G.2.IsRowsFirstBipartite G.1 ∧ (∀ v ∈ G.2.vertices, v.2.length = 2 ^ v.1.length) ∧ + G.2.maxDegree ≤ degreeBound G.2.numEdges) + +/-- A power of the logarithm is eventually below a power: `(C L) ^ 10 ≤ 2 ^ (9 L)` from some +`L₀` on. -/ +private theorem exists_pow_ten_le (C : ℕ) : ∃ L₀, ∀ L, L₀ ≤ L → (C * L) ^ 10 ≤ 2 ^ (9 * L) := by + have hlim := tendsto_pow_const_div_const_pow_of_one_lt 10 (r := (2 : ℝ) ^ 9) (by norm_num) + have hC : (0 : ℝ) < 1 / ((C : ℝ) ^ 10 + 1) := by positivity + obtain ⟨L₀, hL₀⟩ := eventually_atTop.mp ((tendsto_order.1 hlim).2 _ hC) + refine ⟨L₀, fun L hL => ?_⟩ + have h := hL₀ L hL + have h2 : (0 : ℝ) < ((2 : ℝ) ^ 9) ^ L := by positivity + have h1 : (0 : ℝ) < (C : ℝ) ^ 10 + 1 := by positivity + rw [div_lt_div_iff₀ h2 h1, one_mul] at h + have hL10 : (0 : ℝ) ≤ (L : ℝ) ^ 10 := by positivity + have hle : ((C * L) ^ 10 : ℕ) ≤ ((2 ^ (9 * L) : ℕ) : ℝ) := by + push_cast + rw [mul_pow, pow_mul] + nlinarith + exact_mod_cast hle + +/-- **Admissible networks failing the guard are small.** For every `C` there is `s₀` such that a +network admissible at size `s` with constant `C` that fails `fastGuard` has `s ≤ s₀`. -/ +theorem exists_le_of_fastGuard_eq_false (C : ℕ) : + ∃ s₀, ∀ s G, ExplicitAdmissible C s G → fastGuard G = false → s ≤ s₀ := by + obtain ⟨L₀, hL₀⟩ := exists_pow_ten_le C + refine ⟨2 ^ L₀, fun s G hA hg => ?_⟩ + obtain ⟨hrows, -, hdeg, -, htab, hsm⟩ := hA + have hbig : degreeBound G.2.numEdges < G.2.maxDegree := by + simp only [fastGuard, decide_eq_false_iff_not, not_and, not_le] at hg + exact hg hrows htab + by_contra hs + rw [not_le] at hs + have hL : L₀ ≤ Nat.log 2 s := Nat.le_log_of_pow_le one_lt_two hs.le + have h1 : s ^ 9 ≤ G.2.numEdges ^ 9 := Nat.pow_le_pow_left hsm 9 + have h2 := pow_nine_le_degreeBound_pow_ten G.2.numEdges + have h3 : degreeBound G.2.numEdges ^ 10 < (C * Nat.log 2 s) ^ 10 := + Nat.pow_lt_pow_left (hbig.trans_le hdeg) (by norm_num) + have h4 := hL₀ _ hL + have h5 : 2 ^ (9 * Nat.log 2 s) ≤ s ^ 9 := by + rw [pow_mul'] + exact Nat.pow_le_pow_left + (Nat.pow_log_le_self 2 (by have := Nat.one_le_two_pow (n := L₀); omega)) 9 + omega + +end Cslib.Complexity.CountingSimulation From 4daaf4621a5bad0ca5a0f496176af977414c8416 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:29:46 -0400 Subject: [PATCH 079/129] feat(circuit/frontier): one native active call is correct when its stored values are (wip) --- .../Frontier/TensorNetwork/NativeCall.lean | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeCall.lean diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeCall.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeCall.lean new file mode 100644 index 0000000000..5c73846f92 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeCall.lean @@ -0,0 +1,168 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeChild +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeRecovery +import Mathlib.Data.List.GetD + +/-! +# Correctness of one native active call + +An active call of the native contraction recursion of [OpenAI2026ETH] (§D.4.4–§D.4.7, +pp. 60–64) on a rows-first network of descriptors stores, for each node index `k` of positive +multiplicity and each interpolation point `i`, a value in `A_{p_t}`, and recovers its own value +with native tapered recovery (`NativeRecovery.recover`). This file proves the call correct from +the correctness of its stored values (`NativeCall.val_recover_eq_contraction`): if each stored value +is, in `A_{p_t}`, the sum over the exponent tuples of the products over the groups of the +contractions of the native children (`NativeChild.nativeChild`), then the recovered value is the +contraction of the network in `A_p`. + +**The proof** is carried over `O = ℤ_(2)[U] / (f)` with the canonical lifts of the entries +(`DescribedNetwork.toNetwork`, `GaloisRingArith.liftO`), as in §D.4.1 (p. 58). The values at the +nodes are the lifts of the stored lists, so + +* modulo `2 ^ {p_t}` they are the right side of (D.16) for the children of `callSplitting`: the + native children have these contractions (`NativeChild.contraction_nativeChild`), and the sum over + the exponent lists is the sum over the mode budget (`BudgetTuples.sum_filter_weight_lt_eq`); +* so interpolation and Lemma D.5 recover the contraction modulo `2 ^ p` + (`TensorNetwork.exists_recoverFromValues_eq_add_of_children`); +* and the lifts reduce in `A_{p + Δ}` to exactly the stored lists, so native recovery computes the + reduction of the contraction (`NativeRecovery.val_recover_of_eq_add`), with no condition on + `p_t` beyond Lemma D.5's. + +The call's precision `p_t` of the node of valuation `i` is `nodePrecision p h a M i`, and the +shift `Δ` only needs `(a + 1) ∑_t q_t ≤ Δ`. + +## Main results + +* `Cslib.Circuits.Frontier.NativeCall.val_recover_eq_contraction`: one native active call is + correct when its stored values are. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.4–§D.4.7, pp. 60–64. +-/ + +public section + +namespace Cslib.Circuits.Frontier.NativeCall + +open Finset GaloisRingArith NativeChild NativeRecovery +open ContractionSchedule (nodePrecision) + +/-- The node index of a node: `t + 2 ^ a`. -/ +theorem toNat_node_add (a k : ℕ) : (node a k + 2 ^ a).toNat = k := by + rw [node, sub_add_cancel, Int.toNat_natCast] + +/-- A product over a range as a list is a product over `Fin`. -/ +theorem prod_map_range {M : Type*} [CommMonoid M] (F : ℕ → M) (n : ℕ) : + ((List.range n).map F).prod = ∏ g : Fin n, F g := by + rw [← List.map_coe_finRange_eq_range, List.map_map, ← List.ofFn_eq_map, List.prod_ofFn] + rfl + +variable {fl : List ℕ} (hirr : Irreducible ((monicOf fl).map (Int.castRingHom (ZMod 2)))) + {D : DescribedNetwork VertexDesc} {r : ℕ} (hD : D.IsRowsFirstBipartite r) + +include hD in +/-- **One native active call is correct when its stored values are** ([OpenAI2026ETH], +§D.4.4–§D.4.7). Let `D` be a rows-first network of descriptors with `M` edges, and let +`(a, u, h)` meet the hypotheses of Lemma D.5 (`1 ≤ u ≤ M`, `u ≤ a`, `3 M ≤ h u ≤ 3 M + u`), with +`64 ^ a ≤ M` and `(a + 1) ∑_t q_t ≤ Δ`. Let the interpolation values `ζl` have lifts with unit +differences, and more than `M (2 L - 1)` of them. Suppose that at every node index `k` of positive +multiplicity, with the node `t = k - 2 ^ a` of valuation `i` and precision `p_t`, each stored value +`W[k][i']` is, in `A_{p_t}`, the sum over the exponent lists of the packet-major modes of the +products over the `64` groups of the contractions in `A_{p_t}` of the native children. Then native +recovery returns the contraction of `D` in `A_p`. -/ +theorem val_recover_eq_contraction {a u h p Δ : ℕ} (hu : 1 ≤ u) (huM : u ≤ D.numEdges) + (hua : u ≤ a) (hh : 3 * D.numEdges ≤ h * u) (hh' : h * u ≤ 3 * D.numEdges + u) + (hK : 64 ^ a ≤ D.numEdges) + (hΔ : (a + 1) * ∑ t ∈ recoveryNodes a, taperMult u h t ≤ Δ) (ζl : List (List ℕ)) + (hζ : Pairwise fun i j : Fin ζl.length => + IsUnit (liftO hirr (ζl.getD i []) - liftO hirr (ζl.getD j []))) + (hn : D.numEdges * (2 * 2 ^ a - 1) < ζl.length) (W : List (List (List ℕ))) + (hW : ∀ k < 2 ^ (a + 1), k ≠ 2 ^ a → 0 < mult a u h k → ∀ i < ζl.length, + val (nodePrecision p h a D.numEdges (padicValInt 2 (node a k))) fl + ((W.getD k []).getD i []) = + ((BudgetTuples.budgetTuples + (modeWeights (nativeParentPacking r a D).2.1 (2 ^ (padicValInt 2 (node a k) + 1)) + (nodePrecision p h a D.numEdges (padicValInt 2 (node a k)))) + (nodePrecision p h a D.numEdges (padicValInt 2 (node a k)))).map fun β => + ((List.range 64).map fun g => + (nativeChild fl a (padicValInt 2 (node a k)) + (nodePrecision p h a D.numEdges (padicValInt 2 (node a k))) (node a k) + (ζl.getD i []) r D (nativeParentPacking r a D) β g).contraction + fun d bs => val (nodePrecision p h a D.numEdges (padicValInt 2 (node a k))) fl + (d.entry bs)).prod).sum) : + val p fl (recover p Δ fl a u h ζl W) = + D.contraction fun d bs => val p fl (d.entry bs) := by + have hB := AdjoinRoot.algebraMap_mem_integer_padicValuation (monic_monicOf fl) hirr + have hcard : Fintype.card (Fin D.numEdges) = D.numEdges := Fintype.card_fin _ + have hpk1 : (nativeParentPacking r a D).2.1 = (parentPacking hD a).numPackets := by + rw [nativeParentPacking_eq hD hK] + -- The points and the values over `O`: the lifts of the native lists. + let ζ : Fin ζl.length → RingO hirr := fun i => liftO hirr (ζl.getD i []) + let v : ℤ → Fin ζl.length → RingO hirr := fun t i => + liftO hirr ((W.getD (t + 2 ^ a).toNat []).getD i []) + -- Modulo `2 ^ {p_t}` the values are the right side of (D.16). + have hv : ∀ t (ht : t ∈ recoveryNodes a) (hq : 0 < taperMult u h t) (k : Fin ζl.length), + Ideal.Quotient.mk (nodeIdeal (RingO hirr) (Fintype.card (Fin D.numEdges)) a h p t) (v t k) = + ∑ β ∈ TensorNetwork.modeBudget (fun Z : Fin (parentPacking hD a).numPackets × + Fin (2 ^ (padicValInt 2 t + 1)) × + Fin (nodePrecision p h a (Fintype.card (Fin D.numEdges)) (padicValInt 2 t)) => + (Z.2.2 : ℕ) + 1) + (nodePrecision p h a (Fintype.card (Fin D.numEdges)) (padicValInt 2 t)), + ∏ g, ((parentSplitting hirr hD a p u h hua ht hq (ζl.getD k [])).child β g).contraction := by + intro t ht hq k + obtain ⟨kk, hkk, hkt⟩ : ∃ kk, (kk < 2 ^ (a + 1) ∧ kk ≠ 2 ^ a) ∧ node a kk = t := by + rw [recoveryNodes_eq_image, mem_image] at ht + obtain ⟨kk, hk, e⟩ := ht + exact ⟨kk, by simpa using hk, e⟩ + subst hkt + have hmult : 0 < mult a u h kk := by rw [mult_eq u h hkk.1]; exact hq + have hWk := hW kk hkk.1 hkk.2 hmult k k.2 + rw [← hcard] at hWk + simp only [v, toNat_node_add] + rw [mk_liftO hirr, hWk, map_list_sum, List.map_map, TensorNetwork.modeBudget, + BudgetTuples.sum_filter_weight_lt_eq (nodup_modeList _ _ _) mem_modeList + (fun Z => Nat.succ_pos _), map_modeList_weight, hpk1] + refine congrArg List.sum (List.map_congr_left fun β _ => ?_) + simp only [Function.comp_apply] + rw [map_list_prod, List.map_map, prod_map_range] + refine Fintype.prod_congr _ _ fun g => ?_ + simp only [Function.comp_apply] + rw [DescribedNetwork.map_contraction] + exact contraction_nativeChild hirr hD hK hua ht hq (ζl.getD k []) β g + obtain ⟨b, hb, hx⟩ := TensorNetwork.exists_recoverFromValues_eq_add_of_children (RingO hirr) hB + (parentNetwork hirr hD) (parentSide r) (parentSide_src hD) (parentSide_tgt hD) + (parentPacking hD a) (p := p) hu (by rwa [hcard]) hua (by rwa [hcard]) (by rwa [hcard]) ζ hζ + (by rwa [hcard]) v hv + -- Native recovery computes the reduction of the recovered value. + have hpt : ∀ t ∈ recoveryNodes a, 0 < taperMult u h t → + p + 6 * h * 2 ^ (a - padicValInt 2 t) + taperMult u h t * (a + 1) ≤ + nodePrecision p h a D.numEdges (padicValInt 2 t) := by + intro t _ _ + have h1 : h * (u - padicValInt 2 t) ≤ 8 * D.numEdges := + (Nat.mul_le_mul_left _ (Nat.sub_le _ _)).trans (by omega) + have e1 : 6 * h * 2 ^ (a - padicValInt 2 t) ≤ 8 * h * 2 ^ (a - padicValInt 2 t) := + Nat.mul_le_mul_right _ (by omega) + have e2 : h * (u - padicValInt 2 t) * (a + 1) ≤ 8 * D.numEdges * (a + 1) := + Nat.mul_le_mul_right _ h1 + simp only [nodePrecision, taperMult] + omega + have hrec := val_recover_of_eq_add hirr (RingO hirr) hB + (AdjoinRoot.reduction (monic_monicOf fl) hirr (p + Δ)) hΔ + (fun t => nodePrecision p h a D.numEdges (padicValInt 2 t)) hpt ζ hζ ζl rfl + (fun i => (reduction_liftO hirr _ _).symm) v W + (fun k _ _ _ i => ⟨0, by simp only [v, toNat_node_add, reduction_liftO, mul_zero, add_zero]⟩) + (parentNetwork hirr hD).contraction hb hx + rw [hrec, ← DescribedNetwork.reduction_contraction_toNetwork hD hirr (fun d bs => d.entry bs) p] + exact RingHom.congr_fun (AdjoinRoot.castHom_comp_reduction (monic_monicOf fl) hirr + (Nat.le_add_right p Δ)) _ + +end Cslib.Circuits.Frontier.NativeCall From 121d56d3699b80c5e669de1e2c39896aac22ab50 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:31:40 -0400 Subject: [PATCH 080/129] chore: adapt to the shared described-network record and finProdFinEquiv exponents --- .../Frontier/TensorNetwork/DescribedLift.lean | 11 +++++++++-- .../Frontier/TensorNetwork/NativeChild.lean | 15 ++++++--------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean index b0e05d0cf4..57e35857fa 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean @@ -173,10 +173,17 @@ instance (r : ℕ) : Decidable (D.IsRowsFirstBipartite r) := by infer_instance /-- An explicit network is rows-first exactly when it is as a described network. -/ -theorem isRowsFirstBipartite_ofExplicit (d : ExplicitTensorNetwork ℕ) (r : ℕ) : - (ofExplicit d).IsRowsFirstBipartite r ↔ d.IsRowsFirstBipartite r := +theorem isRowsFirstBipartite_explicit (d : ExplicitTensorNetwork ℕ) (r : ℕ) : + DescribedNetwork.IsRowsFirstBipartite d r ↔ ExplicitTensorNetwork.IsRowsFirstBipartite d r := Iff.rfl +/-- Being rows-first depends only on the ports, not on the descriptors. -/ +theorem isRowsFirstBipartite_map {T' : Type} (f : T → T') (r : ℕ) : + (⟨D.numEdges, D.vertices.map fun v => (v.1, f v.2)⟩ : DescribedNetwork T').IsRowsFirstBipartite r ↔ + D.IsRowsFirstBipartite r := by + simp only [IsRowsFirstBipartite, List.forall_mem_map, ← List.map_take, ← List.map_drop, + List.map_map, Function.comp_def] + /-- The vertices at the edge `j`: those having `j` among their ports, in increasing order. -/ @[expose] def incident (j : ℕ) : List ℕ := (List.range D.vertices.length).filter fun v => j ∈ (D.vertices.map Prod.fst).getD v [] diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean index 70b6759f88..4948963124 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean @@ -246,20 +246,17 @@ theorem map_modeList_weight (P T p : ℕ) : simp only [modeList, List.getElem_map, List.getElem_ofFn, modeWeights, List.getElem_range, val_modeEquiv_symm_snd_snd] -/-- **The slice of a packet** reads the mode tuple of `β`: entry `y · p + b` of the slice of `q` is +/-- **The slice of a packet** reads the mode tuple of `β`: entry `b + p y` of the slice of `q` is the exponent of the mode `(q, y, b)`. -/ theorem getD_betaSlice {P i pt : ℕ} (β : List ℕ) (q : Fin P) (Z : Fin (2 ^ (i + 1)) × Fin pt) : - (betaSlice i pt β q).getD (Z.1 * pt + Z.2) 0 = + (betaSlice i pt β q).getD (finProdFinEquiv Z) 0 = BudgetTuples.ofList (modeList P (2 ^ (i + 1)) pt) β (q, Z) := by - have hZ : (Z.1 : ℕ) * pt + Z.2 < 2 ^ (i + 1) * pt := by - have h1 := Z.1.2 - have h2 := Z.2.2 - calc (Z.1 : ℕ) * pt + Z.2 < Z.1 * pt + pt := by omega - _ = (Z.1 + 1) * pt := by ring - _ ≤ 2 ^ (i + 1) * pt := Nat.mul_le_mul_right _ h1 + have hZ : (finProdFinEquiv Z : ℕ) < 2 ^ (i + 1) * pt := (finProdFinEquiv Z).2 rw [BudgetTuples.ofList, idxOf_modeList, modeEquiv_apply_val, betaSlice, List.getD_eq_getElem?_getD, List.getElem?_take_of_lt hZ, List.getElem?_drop, - ← List.getD_eq_getElem?_getD] + ← List.getD_eq_getElem?_getD, finProdFinEquiv_apply_val] + congr 2 + ring /-! ### The native lists in a numbered network -/ From 57709b86802160608a9e86223c7d34d62a8a9f79 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:32:50 -0400 Subject: [PATCH 081/129] test(circuit/frontier): native child on a tiny network --- Complexity.lean | 3 + .../Frontier/TensorNetworkNativeChild.lean | 64 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeChild.lean diff --git a/Complexity.lean b/Complexity.lean index 49730c8bf0..66e3c77e00 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -893,12 +893,15 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.CoeffSchem public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Constraint public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Deformation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DescribedContraction +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DescribedLift public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DirectContraction public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates public import Complexity.Computability.Circuit.Frontier.TensorNetwork.KernelProduct public import Complexity.Computability.Circuit.Frontier.TensorNetwork.LocalDeformation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeCall +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeChild public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeRecovery public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEntry public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEvaluator diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeChild.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeChild.lean new file mode 100644 index 0000000000..1431e819e9 --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeChild.lean @@ -0,0 +1,64 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ +module +-- upstream: local-only (test driver) + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeCall + +public section + +open Cslib.Circuits.Frontier Cslib.Circuits.Frontier.NativeChild + +namespace Cslib.Tests.TensorNetworkNativeChild + +/-! ### A path with one row and two columns + +The row `0` joins the columns `1` and `2` by the edges `0` and `1`, and the vertices carry tables. +The network is rows-first at `1`, and the ends of its edges are computed from the ports. -/ + +/-- The tables of the vertices. -/ +def table (n : ℕ) : VertexDesc := .inl (List.replicate n 1) + +/-- The row `0` with ports `1, 0` (not in edge order), the columns `1` and `2`. -/ +def path : DescribedNetwork VertexDesc := + ⟨2, [([1, 0], table 4), ([0], table 2), ([1], table 2)]⟩ + +example : path.IsRowsFirstBipartite 1 := by decide + +example : ¬ path.IsRowsFirstBipartite 2 := by decide + +example : path.edgeEnds = [(0, 1), (0, 2)] := by decide + +/-- The native child of group `0` for a packing that puts the row and the column `1` in group `0`, +the column `2` in group `1`, and both edges in packet `0` with slots `0` and `1`. -/ +def child : DescribedNetwork VertexDesc := + nativeChild [1] 1 0 3 (-1) [0] 1 path (([[0]], [[0], [1]]), 1, [(0, 0), (0, 1)]) [] 0 + +/-- The child keeps the row and the column `1`, and adds the packet: three child edges, the two +row ports of group `0` and the column port of edge `0`. -/ +example : child.numEdges = 3 := by decide + +/-- The row keeps its parent's port order, renumbered (edge `1` is row port `1`, edge `0` row port +`0`); the column `1` reads the column port `2`; the packet reads all three child edges in order. -/ +example : child.vertices.map Prod.fst = [[1, 0], [2], [0, 1, 2]] := by decide + +/-- The packet tags its ports as row ports, row ports and a column port, with the slots of their +parent edges. -/ +example : (child.vertices.getD 2 ([], .inl [])).2.getRight?.map (fun d => d.2.1) = + some [(true, 0), (true, 1), (false, 0)] := by decide + +example : childRows 1 0 path (([[0]], [[0], [1]]), 1, [(0, 0), (0, 1)]) 0 = 2 := by decide + +example : child.IsRowsFirstBipartite 2 := by decide + +/-! ### The modes of a node -/ + +example : modeWeights 2 2 3 = [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3] := by decide + +example : betaSlice 0 3 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] 1 = [7, 8, 9, 10, 11, 12] := by + decide + +end Cslib.Tests.TensorNetworkNativeChild From af0bb0360a3adb4c7fa8a6b9ffe2324ea927622c Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:33:21 -0400 Subject: [PATCH 082/129] wip: fast machine resources scaffolding Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/FastContractionMachine.lean | 39 ++++++++++- .../CountingSimulation/FastAlgorithm.lean | 65 +++++++++++++++++++ 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean index 3cfe7522c3..3713b8431a 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean @@ -325,7 +325,8 @@ variable {mk} /-- **The clocked run** from a capped input, as one native machine, given polynomial-time components of the scheme. -/ -noncomputable def runCertificate (hpoly : (scheme mk).PolyTime encCall encFrame encValue) : +@[expose] noncomputable def runCertificate + (hpoly : (scheme mk).PolyTime encCall encFrame encValue) : NativeCertificate ((scheme mk).run fuel root) ExplicitTensorNetwork.encContractionInput (encOption encValue) := Scheme.certificate hpoly (leafCertificate mk) (NativeCertificate.ofPolyTime fuel_polyTime) @@ -354,7 +355,8 @@ theorem tag_polyTime : /-- **The fast machine**: cap the precision and run the recursion; on a value tagged `0`, read off its constant coordinate, and otherwise sum directly. It computes `fastValue`. -/ -noncomputable def fastCertificate (hpoly : (scheme mk).PolyTime encCall encFrame encValue) : +@[expose] noncomputable def fastCertificate + (hpoly : (scheme mk).PolyTime encCall encFrame encValue) : NativeCertificate (fastValue mk) encInput encNat := (NativeCertificate.option ((runCertificate hpoly).comp @@ -370,4 +372,37 @@ noncomputable def fastCertificate (hpoly : (scheme mk).PolyTime encCall encFrame | none => rfl | some v => simp only [Option.elim_some, decide_eq_true_eq] +/-- The time of the fast machine, unfolded: the capped run twice, the input, and the decoding or +direct summation. -/ +theorem fastCertificate_time (hpoly : (scheme mk).PolyTime encCall encFrame encValue) + (q : ℕ × ℕ × ExplicitTensorNetwork ℕ) : + (fastCertificate hpoly).time q = + 2 * ((NativeCertificate.ofPolyTime ExplicitTensorNetwork.capPrecision_polyTime).time q + + (runCertificate hpoly).time (ExplicitTensorNetwork.capPrecision q)) + + (encInput q).length + + ((scheme mk).run fuel root (ExplicitTensorNetwork.capPrecision q)).elim + (ExplicitTensorNetwork.directContractionBinaryCertificate.time q) fun v => + (NativeCertificate.ofPolyTime tag_polyTime).time (q, v) + + if decide (v.1 = 0) = true then + (NativeCertificate.ofPolyTime decode_polyTime).time (q, v) + else (NativeCertificate.ofPolyTime + (ComputableInPolyTime.fst encInput encValue)).time (q, v) + + ExplicitTensorNetwork.directContractionBinaryCertificate.time q := rfl + +/-- The space of the fast machine, unfolded. -/ +theorem fastCertificate_space (hpoly : (scheme mk).PolyTime encCall encFrame encValue) + (q : ℕ × ℕ × ExplicitTensorNetwork ℕ) : + (fastCertificate hpoly).space q = + 2 * ((NativeCertificate.ofPolyTime ExplicitTensorNetwork.capPrecision_polyTime).space q + + (runCertificate hpoly).space (ExplicitTensorNetwork.capPrecision q)) + + (encInput q).length + + ((scheme mk).run fuel root (ExplicitTensorNetwork.capPrecision q)).elim + (ExplicitTensorNetwork.directContractionBinaryCertificate.space q) fun v => + (NativeCertificate.ofPolyTime tag_polyTime).space (q, v) + + if decide (v.1 = 0) = true then + (NativeCertificate.ofPolyTime decode_polyTime).space (q, v) + else (NativeCertificate.ofPolyTime + (ComputableInPolyTime.fst encInput encValue)).space (q, v) + + ExplicitTensorNetwork.directContractionBinaryCertificate.space q := rfl + end Cslib.Circuits.Frontier.FastContraction diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean index b6042451e3..d1af3c697e 100644 --- a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean @@ -92,4 +92,69 @@ theorem exists_le_of_fastGuard_eq_false (C : ℕ) : (Nat.pow_log_le_self 2 (by have := Nat.one_le_two_pow (n := L₀); omega)) 9 omega +/-! ### The clock -/ + +open FastContraction in +/-- **The clock is subexponential on admissible inputs**: with `m ≤ C s` edges and precision +`P ≤ D s`, it is at most `2 ^ (K₀ (C + D) s / √⌊log₂ s⌋)`. -/ +theorem fuel_le {C D s P : ℕ} {G : ℕ × ExplicitTensorNetwork ℕ} (hA : ExplicitAdmissible C s G) + (hs : 2 ≤ s) (hP : P ≤ D * s) : + fuel (ExplicitTensorNetwork.capPrecision (P, G)) ≤ + 2 ^ (fuelConst * (C + D) * s / Nat.sqrt (Nat.log 2 s)) := by + obtain ⟨-, hm, -, -, -, hsm⟩ := hA + simp only [fuel, ExplicitTensorNetwork.capPrecision] + refine Nat.pow_le_pow_right (by norm_num) ?_ + set x := G.2.numEdges + min (ExplicitTensorNetwork.encExplicitNetwork G.2).length P + have hx : x ≤ (C + D) * s := by + have := min_le_right (ExplicitTensorNetwork.encExplicitNetwork G.2).length P + rw [add_mul]; omega + have hsx : s ≤ x := by omega + have hq : 1 ≤ Nat.sqrt (Nat.log 2 s) := by + rw [Nat.le_sqrt, Nat.one_mul]; exact Nat.le_log_of_pow_le one_lt_two (by simpa using hs) + have hq' : Nat.sqrt (Nat.log 2 s) ≤ Nat.sqrt (Nat.log 2 x) := + Nat.sqrt_le_sqrt (Nat.log_mono_right hsx) + calc fuelConst * x / Nat.sqrt (Nat.log 2 x) ≤ fuelConst * x / Nat.sqrt (Nat.log 2 s) := + Nat.div_le_div_left hq' hq + _ ≤ fuelConst * (C + D) * s / Nat.sqrt (Nat.log 2 s) := by + refine Nat.div_le_div_right ?_ + rw [Nat.mul_assoc] + exact Nat.mul_le_mul_left _ hx + +/-! ### The resources of the fast machine -/ + +section Resources + +open FastContraction Cslib.GenericRAM Cslib.GenericRAM.StackRecursion Turing.MultiTapeTM + +variable (mk : Call → ChildCursor.State → ℕ × DescribedNetwork VertexDesc) + +/-- **The resource analysis of the fast machine**, the remaining obligation of the running time. +For all constants `C` and `D` there are `K` and `s₀` such that on every input `(P, r, G)` with +`2 ≤ s`, `1 ≤ P ≤ D s`, `G` admissible at size `s` with constant `C` and passing the guard, the +root of the capped input has a derivation whose nodes, frames and values are at most `F` long, +whose leaves take time at most `TL` and space at most `SL`, with `H` open calls, such that +`H + F + SL ≤ K (n + 1) ^ K` and `TL ≤ K (n + 1) ^ K 2 ^ (K s / √⌊log₂ s⌋)` for the input length +`n`; and from `s₀` on its `N` nodes fit in the clock, `2 N - 1 ≤ fuel`. -/ +@[expose] def FastAnalysis : Prop := + ∀ C D : ℕ, ∃ K s₀ : ℕ, ∀ (s P : ℕ) (G : ℕ × ExplicitTensorNetwork ℕ), 2 ≤ s → 1 ≤ P → + P ≤ D * s → ExplicitAdmissible C s G → fastGuard G = true → + ∃ (I : Invariant Call Frame (ℕ × List ℕ)) (N H F TL SL : ℕ), + (scheme mk).Eval I (root (ExplicitTensorNetwork.capPrecision (P, G))) + (0, value mk (root (ExplicitTensorNetwork.capPrecision (P, G)))) N H ∧ + (s₀ ≤ s → 2 * N - 1 ≤ fuel (ExplicitTensorNetwork.capPrecision (P, G))) ∧ + (∀ x, I.node x → (encCall x).length ≤ F ∧ ((scheme mk).isLeaf x = true → + (leafCertificate mk).time x ≤ TL ∧ (leafCertificate mk).space x ≤ SL)) ∧ + (∀ x a, I.frame x a → (encPair encCall encFrame (x, a)).length ≤ F) ∧ + (∀ v, I.value v → (encValue v).length ≤ F) ∧ + H + F + SL ≤ K * ((FastContraction.encInput (P, G)).length + 1) ^ K ∧ + TL ≤ K * ((FastContraction.encInput (P, G)).length + 1) ^ K * + 2 ^ (K * s / Nat.sqrt (Nat.log 2 s)) + +/-- `c B ^ d ≤ c B ^ E X` for `1 ≤ B`, `d ≤ E` and `1 ≤ X`. -/ +private theorem lift_le {c B d E X : ℕ} (hB : 1 ≤ B) (hd : d ≤ E) (hX : 1 ≤ X) : + c * B ^ d ≤ c * B ^ E * X := + (Nat.mul_le_mul_left c (Nat.pow_le_pow_right hB hd)).trans (Nat.le_mul_of_pos_right _ hX) + +end Resources + end Cslib.Complexity.CountingSimulation From 9d3c390ec5f1d9a2c38331325589f581af244dfb Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:35:08 -0400 Subject: [PATCH 083/129] feat(circuit/frontier): the native child is rows-first --- .../Frontier/TensorNetwork/NativeChild.lean | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean index 4948963124..7594f828d9 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean @@ -732,8 +732,172 @@ theorem nativeChild_eq (fl : List ℕ) (a i pt : ℕ) (t : ℤ) (ζ β : List rw [parentEdge_childEdgeEquiv] rcases c with c | c <;> simp +/-- **The ports of a retained vertex are its child edges.** -/ +theorem mem_filterMap_retainedPort (x : {v // group v = g}) + (c : ChildEdge (D.srcFin hD) (D.tgtFin hD) group g) : + c ∈ (D.vertices[(x.1 : ℕ)]'x.1.2).1.filterMap (retainedPort hD group g x) ↔ + childSrc (P := P) (D.srcFin hD) (D.tgtFin hD) group g c = .inl x := by + have hsrcr := DescribedNetwork.srcFin_lt hD + have htgtr := DescribedNetwork.le_tgtFin hD + rw [List.mem_filterMap] + rcases c with ⟨e, he⟩ | ⟨e, he⟩ + · simp only [childSrc, Sum.elim_inl, Sum.inl.injEq, Subtype.ext_iff] + constructor + · rintro ⟨j, hj, hc⟩ + simp only [retainedPort] at hc + split_ifs at hc with hjm hxr hg hg <;> simp at hc + subst hc + rcases (DescribedNetwork.mem_ports_iff hD x.1 ⟨j, hjm⟩).mp hj with h | h + · exact h + · have := htgtr ⟨j, hjm⟩ + rw [h] at this + omega + · intro hs + refine ⟨e.val, (DescribedNetwork.mem_ports_iff hD x.1 e).mpr (Or.inl hs), ?_⟩ + have hxr : (x.1 : ℕ) < r := hs ▸ hsrcr e + simp only [retainedPort, dite_eq_left e.2, ite_eq_left hxr, Fin.eta] + rw [dite_eq_left he] + · simp only [childSrc, Sum.elim_inr, Sum.inl.injEq, Subtype.ext_iff] + constructor + · rintro ⟨j, hj, hc⟩ + simp only [retainedPort] at hc + split_ifs at hc with hjm hxr hg hg <;> simp at hc + subst hc + rcases (DescribedNetwork.mem_ports_iff hD x.1 ⟨j, hjm⟩).mp hj with h | h + · have := hsrcr ⟨j, hjm⟩ + rw [h] at this + exact absurd this hxr + · exact h + · intro ht + refine ⟨e.val, (DescribedNetwork.mem_ports_iff hD x.1 e).mpr (Or.inr ht), ?_⟩ + have hxr : ¬ (x.1 : ℕ) < r := by have := htgtr e; rw [ht] at this; omega + simp only [retainedPort, dite_eq_left e.2, ite_eq_right hxr, Fin.eta] + rw [dite_eq_left he] + +/-- A retained port names its parent edge. -/ +theorem val_of_retainedPort_eq_some {x : {v // group v = g}} {j : ℕ} + {c : ChildEdge (D.srcFin hD) (D.tgtFin hD) group g} (h : retainedPort hD group g x j = some c) : + (Sum.elim (fun e => (e.1 : ℕ)) (fun e => (e.1 : ℕ)) c) = j := by + simp only [retainedPort] at h + split_ifs at h <;> simp at h <;> subst h <;> rfl + +include hD in +/-- The ports of a vertex of a rows-first network are distinct. -/ + +theorem nodup_ports (v : Fin D.vertices.length) : (D.vertices[v]).1.Nodup := by + refine List.nodup_iff_count_le_one.mpr fun j => ?_ + by_cases hj : j ∈ (D.vertices[v]).1 + · have hjm : j < D.numEdges := hD.1 _ (List.getElem_mem _) j hj + obtain ⟨s, t, hs, ht, -, -, hsc, htc, hinc⟩ := DescribedNetwork.incident_eq hD hjm + rcases (DescribedNetwork.mem_ports_iff hD v ⟨j, hjm⟩).mp hj with h | h + · have hv : (v : ℕ) = s := by + rw [← h] + simp [DescribedNetwork.srcFin, hinc] + have : (D.vertices[v]).1 = (D.vertices[s]).1 := by simp only [Fin.getElem_fin, hv] + rw [this, hsc] + · have hv : (v : ℕ) = t := by + rw [← h] + simp [DescribedNetwork.tgtFin, hinc] + have : (D.vertices[v]).1 = (D.vertices[t]).1 := by simp only [Fin.getElem_fin, hv] + rw [this, htc] + · rw [List.count_eq_zero.mpr hj] + exact Nat.zero_le _ + +/-- **The numbered ports of a child vertex are its child edges**, without repetition. -/ +theorem mem_numberedPorts (v : ChildVertex group g P) + (c : ChildEdge (D.srcFin hD) (D.tgtFin hD) group g) : + c ∈ numberedPorts hD group packet g v ↔ + childSrc (D.srcFin hD) (D.tgtFin hD) group g c = v ∨ + childTgt (D.srcFin hD) (D.tgtFin hD) group packet g c = v := by + rcases v with x | q + · rw [numberedPorts, Sum.elim_inl, mem_filterMap_retainedPort] + refine ⟨Or.inl, fun h => h.resolve_right ?_⟩ + rcases c with c | c <;> simp [childTgt] + · rw [numberedPorts, Sum.elim_inr, mem_packetPorts] + rcases c with c | c <;> simp [childSrc, childTgt] + +theorem nodup_numberedPorts (v : ChildVertex group g P) : + (numberedPorts hD group packet g v).Nodup := by + rcases v with x | q + · exact (nodup_ports hD x.1).filterMap fun j j' c hc hc' => by + rw [← val_of_retainedPort_eq_some hD group g hc, ← val_of_retainedPort_eq_some hD group g hc'] + · exact nodup_packetPorts hD group packet g q + end Numbered +/-- **Being rows-first depends only on the multisets of ports.** -/ +theorem isRowsFirstBipartite_ofFn_iff {T T' : Type} {k l r : ℕ} (F : Fin k → List ℕ × T) + (F' : Fin k → List ℕ × T') (h : ∀ x, (F x).1.Perm (F' x).1) : + (⟨l, List.ofFn F⟩ : DescribedNetwork T).IsRowsFirstBipartite r ↔ + (⟨l, List.ofFn F'⟩ : DescribedNetwork T').IsRowsFirstBipartite r := by + have hc (i : ℕ) : (List.ofFn F).map (fun v => v.1.count i) = + (List.ofFn F').map (fun v => v.1.count i) := by + simp only [List.map_ofFn] + congr 1 + funext x + exact (h x).count_eq i + have hm : (∀ v ∈ List.ofFn F, ∀ i ∈ v.1, i < l) ↔ (∀ v ∈ List.ofFn F', ∀ i ∈ v.1, i < l) := by + simp only [List.mem_ofFn, forall_exists_index, forall_apply_eq_imp_iff] + exact forall_congr' fun x => forall_congr' fun i => imp_congr_left (h x).mem_iff + simp only [DescribedNetwork.IsRowsFirstBipartite, List.map_take, List.map_drop, hc, hm] + +section Rows + +variable {a i : ℕ} (hK : 64 ^ a ≤ D.numEdges) (hia : i < a) + +include hD hK hia in +/-- **The native child is rows-first**, with its retained vertices as its rows. -/ +theorem isRowsFirstBipartite_nativeChild (pt : ℕ) (t : ℤ) (ζ β : List ℕ) (g : Fin 64) : + (nativeChild fl a i pt t ζ r D (nativeParentPacking r a D) β g).IsRowsFirstBipartite + (childRows r i D (nativeParentPacking r a D) g) := by + have hgroups := groupsOf_eq hD hK hia + have hpk1 : (nativeParentPacking r a D).2.1 = (parentPacking hD a).numPackets := by + rw [nativeParentPacking_eq hD hK] + have hpk2 : (nativeParentPacking r a D).2.2 = List.ofFn fun e => + (((parentPacking hD a).packet e : ℕ), ((parentPacking hD a).slot e : ℕ)) := by + rw [nativeParentPacking_eq hD hK] + rw [childRows, hgroups, count_ofFn_group, + nativeChild_eq hD _ (parentPacking hD a).packet (fun e => ((parentPacking hD a).slot e : ℕ)) g + fl a i pt t ζ β _ hgroups hpk1 hpk2] + -- The incidence of the numbered child, with zero tensors. + let N : TensorNetwork ℕ Bool + (ChildVertex (packingGroup (parentSide r) (parentPacking hD a) ⟨i, hia⟩) g + (parentPacking hD a).numPackets) + (ChildEdge (D.srcFin hD) (D.tgtFin hD) + (packingGroup (parentSide r) (parentPacking hD a) ⟨i, hia⟩) g) := + { src := childSrc (D.srcFin hD) (D.tgtFin hD) _ g + tgt := childTgt (D.srcFin hD) (D.tgtFin hD) _ (parentPacking hD a).packet g + tensor := fun _ _ => 0 + tensor_congr := fun _ _ _ _ => rfl } + have hside : ∀ e, ((childVertexEquiv (P := (parentPacking hD a).numPackets) _ g (N.src e) : + Fin _) : ℕ) < (members fun v => + packingGroup (parentSide r) (parentPacking hD a) ⟨i, hia⟩ v = g).length ↔ + ¬ ((childVertexEquiv (P := (parentPacking hD a).numPackets) _ g (N.tgt e) : Fin _) : ℕ) < + (members fun v => packingGroup (parentSide r) (parentPacking hD a) ⟨i, hia⟩ v = g).length := by + rintro (e | e) <;> simp only [N, childSrc, childTgt, childVertexEquiv, Sum.elim_inl, + Sum.elim_inr, Equiv.trans_apply, Equiv.sumCongr_apply, Sum.map_inl, Equiv.refl_apply, + finSumFinEquiv_apply_left, finSumFinEquiv_apply_right, Fin.val_castAdd, Fin.val_natAdd, + rankEquiv_apply_val, Sum.map_inr] + · exact ⟨fun _ => by omega, fun _ => rank_lt _ _ e.2⟩ + · exact ⟨fun _ => by omega, fun _ => rank_lt _ _ e.2⟩ + have hexp := TensorNetwork.isRowsFirstBipartite_toExplicit N + (childVertexEquiv (P := (parentPacking hD a).numPackets) _ g) + (childEdgeEquiv (D.srcFin hD) (D.tgtFin hD) _ g) _ hside + refine (isRowsFirstBipartite_ofFn_iff _ _ fun c => ?_).mpr hexp + set v := (childVertexEquiv (P := (parentPacking hD a).numPackets) _ g).symm c + refine (List.perm_ext_iff_of_nodup ((nodup_numberedPorts hD _ _ g v).map + (Fin.val_injective.comp (Equiv.injective _))) + ((N.nodup_explicitPorts _ v).map Fin.val_injective)).mpr fun n => ?_ + simp only [List.mem_map, TensorNetwork.mem_explicitPorts, mem_numberedPorts] + constructor + · rintro ⟨e, he, rfl⟩ + exact ⟨_, by simpa [N] using he, rfl⟩ + · rintro ⟨j, hj, rfl⟩ + exact ⟨_, by simpa [N] using hj, by simp⟩ + +end Rows + + section Child variable {a p u h : ℕ} (hK : 64 ^ a ≤ D.numEdges) (hua : u ≤ a) {t : ℤ} From d1ca4a045210e96a0f62b0e1252ef1a210e8cb93 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:37:42 -0400 Subject: [PATCH 084/129] style(circuit/frontier): wrap long lines and pin flexible simps in the native child Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/DescribedLift.lean | 6 ++-- .../Frontier/TensorNetwork/NativeCall.lean | 3 +- .../Frontier/TensorNetwork/NativeChild.lean | 35 ++++++++++++------- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean index 57e35857fa..e0d0e1a0da 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean @@ -179,7 +179,8 @@ theorem isRowsFirstBipartite_explicit (d : ExplicitTensorNetwork ℕ) (r : ℕ) /-- Being rows-first depends only on the ports, not on the descriptors. -/ theorem isRowsFirstBipartite_map {T' : Type} (f : T → T') (r : ℕ) : - (⟨D.numEdges, D.vertices.map fun v => (v.1, f v.2)⟩ : DescribedNetwork T').IsRowsFirstBipartite r ↔ + (⟨D.numEdges, D.vertices.map fun v => (v.1, f v.2)⟩ : + DescribedNetwork T').IsRowsFirstBipartite r ↔ D.IsRowsFirstBipartite r := by simp only [IsRowsFirstBipartite, List.forall_mem_map, ← List.map_take, ← List.map_drop, List.map_map, Function.comp_def] @@ -300,7 +301,8 @@ descriptor and the bits on its ports. -/ have hv : (v : ℕ) ∈ D.incident i := by simp only [incident, List.mem_filter, List.mem_range, decide_eq_true_eq] exact ⟨v.2, by rw [getD_map_fst _ v.2]; exact hi⟩ - rw [List.getD_eq_getElem _ _ (by simpa using hi'), List.getD_eq_getElem _ _ (by simpa using hi'), + rw [List.getD_eq_getElem _ _ (by simpa using hi'), + List.getD_eq_getElem _ _ (by simpa using hi'), List.getElem_ofFn, List.getElem_ofFn] refine hxy ⟨i, hi'⟩ ?_ rw [hinc] at hv diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeCall.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeCall.lean index 5c73846f92..82af1b001c 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeCall.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeCall.lean @@ -117,7 +117,8 @@ theorem val_recover_eq_contraction {a u h p Δ : ℕ} (hu : 1 ≤ u) (huM : u Fin (nodePrecision p h a (Fintype.card (Fin D.numEdges)) (padicValInt 2 t)) => (Z.2.2 : ℕ) + 1) (nodePrecision p h a (Fintype.card (Fin D.numEdges)) (padicValInt 2 t)), - ∏ g, ((parentSplitting hirr hD a p u h hua ht hq (ζl.getD k [])).child β g).contraction := by + ∏ g, ((parentSplitting hirr hD a p u h hua ht hq (ζl.getD k [])).child β + g).contraction := by intro t ht hq k obtain ⟨kk, hkk, hkt⟩ : ∃ kk, (kk < 2 ^ (a + 1) ∧ kk ≠ 2 ^ a) ∧ node a kk = t := by rw [recoveryNodes_eq_image, mem_image] at ht diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean index 7594f828d9..e90fd66f59 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChild.lean @@ -86,7 +86,8 @@ labels of the new ends read through `ρ` and `κ`, which agree with the old labe every edge. -/ @[expose] def Packing.relabel (P : Packing rowOf colOf a r) (rowOf' : E → R') (colOf' : E → C') (ρ : R' → Fin a → Fin r) (κ : C' → Fin a → Fin r) - (hρ : ∀ e, ρ (rowOf' e) = P.rowLabel (rowOf e)) (hκ : ∀ e, κ (colOf' e) = P.colLabel (colOf e)) : + (hρ : ∀ e, ρ (rowOf' e) = P.rowLabel (rowOf e)) + (hκ : ∀ e, κ (colOf' e) = P.colLabel (colOf e)) : Packing rowOf' colOf' a r where rowLabel := ρ colLabel := κ @@ -479,7 +480,8 @@ theorem groupsOf_eq {a i : ℕ} (hK : 64 ^ a ≤ D.numEdges) (hia : i < a) : have hlt : v - r < D.vertices.length - r := by omega rw [ite_eq_right hv, decide_eq_false hv, ite_eq_right (by simp), dite_eq_left hvr, List.getD_ofFn (fun w : Fin (D.vertices.length - r) => BalancedSchedules.scheduleToWord _) - ⟨v - r, hlt⟩, BalancedSchedules.scheduleToWord, List.getD_ofFn (fun t : Fin a => _) ⟨i, hia⟩] + ⟨v - r, hlt⟩, BalancedSchedules.scheduleToWord, + List.getD_ofFn (fun t : Fin a => _) ⟨i, hia⟩] /-- **The mode splitting at a node of the parent** over `O`: `callSplitting` of the parent with its packing, at the node `t`, the precision `p`, and the lift of the interpolation value `ζ`. -/ @@ -530,7 +532,8 @@ variable {P : ℕ} (group : Fin D.vertices.length → Fin 64) (packet : Fin D.nu else none /-- **A parent port of a retained vertex is a child edge**, numbered by `childEdgeOf`. -/ -theorem retainedPort_spec (v : {v // group v = g}) {j : ℕ} (hj : j ∈ (D.vertices[(v.1 : ℕ)]'v.1.2).1) : +theorem retainedPort_spec (v : {v // group v = g}) {j : ℕ} + (hj : j ∈ (D.vertices[(v.1 : ℕ)]'v.1.2).1) : ∃ c, retainedPort hD group g v j = some c ∧ (childEdgeEquiv (D.srcFin hD) (D.tgtFin hD) group g c : ℕ) = childEdgeOf r (List.ofFn fun v => (group v : ℕ)) g @@ -745,7 +748,9 @@ theorem mem_filterMap_retainedPort (x : {v // group v = g}) constructor · rintro ⟨j, hj, hc⟩ simp only [retainedPort] at hc - split_ifs at hc with hjm hxr hg hg <;> simp at hc + split_ifs at hc with hjm hxr hg hg <;> + simp only [Option.some.injEq, Sum.inl.injEq, reduceCtorEq, + Subtype.mk.injEq] at hc subst hc rcases (DescribedNetwork.mem_ports_iff hD x.1 ⟨j, hjm⟩).mp hj with h | h · exact h @@ -761,7 +766,9 @@ theorem mem_filterMap_retainedPort (x : {v // group v = g}) constructor · rintro ⟨j, hj, hc⟩ simp only [retainedPort] at hc - split_ifs at hc with hjm hxr hg hg <;> simp at hc + split_ifs at hc with hjm hxr hg hg <;> + simp only [Option.some.injEq, Sum.inr.injEq, reduceCtorEq, + Subtype.mk.injEq] at hc subst hc rcases (DescribedNetwork.mem_ports_iff hD x.1 ⟨j, hjm⟩).mp hj with h | h · have := hsrcr ⟨j, hjm⟩ @@ -779,11 +786,10 @@ theorem val_of_retainedPort_eq_some {x : {v // group v = g}} {j : ℕ} {c : ChildEdge (D.srcFin hD) (D.tgtFin hD) group g} (h : retainedPort hD group g x j = some c) : (Sum.elim (fun e => (e.1 : ℕ)) (fun e => (e.1 : ℕ)) c) = j := by simp only [retainedPort] at h - split_ifs at h <;> simp at h <;> subst h <;> rfl + split_ifs at h <;> simp only [Option.some.injEq] at h <;> subst h <;> rfl include hD in /-- The ports of a vertex of a rows-first network are distinct. -/ - theorem nodup_ports (v : Fin D.vertices.length) : (D.vertices[v]).1.Nodup := by refine List.nodup_iff_count_le_one.mpr fun j => ?_ by_cases hj : j ∈ (D.vertices[v]).1 @@ -873,7 +879,8 @@ theorem isRowsFirstBipartite_nativeChild (pt : ℕ) (t : ℤ) (ζ β : List ℕ) Fin _) : ℕ) < (members fun v => packingGroup (parentSide r) (parentPacking hD a) ⟨i, hia⟩ v = g).length ↔ ¬ ((childVertexEquiv (P := (parentPacking hD a).numPackets) _ g (N.tgt e) : Fin _) : ℕ) < - (members fun v => packingGroup (parentSide r) (parentPacking hD a) ⟨i, hia⟩ v = g).length := by + (members fun v => + packingGroup (parentSide r) (parentPacking hD a) ⟨i, hia⟩ v = g).length := by rintro (e | e) <;> simp only [N, childSrc, childTgt, childVertexEquiv, Sum.elim_inl, Sum.elim_inr, Equiv.trans_apply, Equiv.sumCongr_apply, Sum.map_inl, Equiv.refl_apply, finSumFinEquiv_apply_left, finSumFinEquiv_apply_right, Fin.val_castAdd, Fin.val_natAdd, @@ -910,11 +917,13 @@ local notation "ptₜ" => nodePrecision p h a (Fintype.card (Fin D.numEdges)) (p /-- The mode splitting at the node. -/ local notation "S₀" => parentSplitting hirr hD a p u h hua ht hq ζ /-- The mode tuple of `β`. -/ -local notation "β₀" => BudgetTuples.ofList (modeList (PacketPacking.Packing.numPackets (parentPacking hD a)) +local notation "β₀" => BudgetTuples.ofList + (modeList (PacketPacking.Packing.numPackets (parentPacking hD a)) (2 ^ (padicValInt 2 t + 1)) (nodePrecision p h a (Fintype.card (Fin D.numEdges)) (padicValInt 2 t))) β /-- The numbering of the child vertices. -/ -local notation "ev₀" => childVertexEquiv (P := PacketPacking.Packing.numPackets (parentPacking hD a)) +local notation "ev₀" => + childVertexEquiv (P := PacketPacking.Packing.numPackets (parentPacking hD a)) (TensorNetwork.ModeSplitting.group (parentSplitting hirr hD a p u h hua ht hq ζ)) g /-- The numbering of the child edges. -/ local notation "ee₀" => childEdgeEquiv (D.srcFin hD) (D.tgtFin hD) @@ -928,7 +937,8 @@ theorem entry_numbered_inl (v : {v // (S₀).group v = g}) fl a iₜ ptₜ t ζ β (.inl v)).entry ((numberedPorts hD (S₀).group (S₀).packet g (.inl v)).map y))) = ((S₀).child β₀ g).tensor (.inl v) y := by - rw [TensorNetwork.ModeSplitting.child_tensor_inl_eq_tensor (S₀) β₀ g v y, TensorNetwork.map_tensor, + rw [TensorNetwork.ModeSplitting.child_tensor_inl_eq_tensor (S₀) β₀ g v y, + TensorNetwork.map_tensor, ← mk_liftO, parentSplitting_side hirr hD hua ht hq ζ] simp only [numberedDesc, numberedPorts, Sum.elim_inl, parentNetwork, DescribedNetwork.toNetwork, DescribedNetwork.wordBits] @@ -989,7 +999,8 @@ theorem contraction_nativeChild : (d.entry bs))) = ((parentSplitting hirr hD a p u h hua ht hq ζ).child (BudgetTuples.ofList (modeList (parentPacking hD a).numPackets (2 ^ (padicValInt 2 t + 1)) - (nodePrecision p h a (Fintype.card (Fin D.numEdges)) (padicValInt 2 t))) β) g).contraction := by + (nodePrecision p h a (Fintype.card (Fin D.numEdges)) (padicValInt 2 t))) β) + g).contraction := by have hia : iₜ < a := padicValInt_lt_of_taperMult_pos hq hua have hgroups : groupsOf r iₜ D.vertices.length (nativeParentPacking r a D).1 = List.ofFn fun v => ((S₀).group v : ℕ) := by From 211d01d4d7eaf3473648beafb001ef86e6583e04 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:39:18 -0400 Subject: [PATCH 085/129] feat(Classes/CountingSimulation): the fast machine within the interface's bounds, given the resource analysis Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../CountingSimulation/FastAlgorithm.lean | 275 ++++++++++++++++++ 1 file changed, 275 insertions(+) diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean index d1af3c697e..93797c1772 100644 --- a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean @@ -155,6 +155,281 @@ private theorem lift_le {c B d E X : ℕ} (hB : 1 ≤ B) (hd : d ≤ E) (hX : 1 c * B ^ d ≤ c * B ^ E * X := (Nat.mul_le_mul_left c (Nat.pow_le_pow_right hB hd)).trans (Nat.le_mul_of_pos_right _ hX) +private theorem mul_succ_le_sq (H F : ℕ) : (H + 1) * (F + 1) ≤ (H + F + 1) ^ 2 := by nlinarith + +private theorem mul_add_one_le {c x : ℕ} (hx : 1 ≤ x) : c * x + 1 ≤ (c + 1) * x := by nlinarith + +/-- The arithmetic of the time bound. -/ +private theorem time_assemble + {capT runT cnT crT capL fuel Z TL Wb n R₁ R₂ Q W c₁ k₁ c₃ ka c₂ k₂ : ℕ} + (hrun : runT ≤ cnT + crT + 2 * capL + (fuel + 1) * (Z + TL + Wb)) + (hq : capT + cnT + crT + capL ≤ Q) (hQ : Q ≤ c₁ * W) + (hP1 : (fuel + 1) * Z ≤ 2 * k₁ * W) (hP2 : (fuel + 1) * TL ≤ 2 * ka * W) + (hP3 : (fuel + 1) * Wb ≤ 2 * c₃ * W) (hn : n + 1 ≤ 1 * W) (hR₁ : R₁ ≤ c₂ * W) + (hR₂ : R₂ ≤ k₂ * W) : + 2 * (capT + runT) + n + (R₁ + R₂) ≤ (6 * c₁ + 1 + 4 * k₁ + 4 * c₃ + 4 * ka + c₂ + k₂) * W := by + have e : (fuel + 1) * (Z + TL + Wb) = (fuel + 1) * Z + (fuel + 1) * TL + (fuel + 1) * Wb := by + ring + rw [e] at hrun + nlinarith + +/-- The arithmetic of the space bound. -/ +private theorem space_assemble + {capS runS cnS crS capL fb Z SL n R₁ R₂ Q W c₁ k₁ c₃ ka c₂ k₂ : ℕ} + (hrun : runS ≤ cnS + crS + 2 * capL + Z + SL + fb) + (hq : capS + cnS + crS + capL + fb ≤ Q) (hQ : Q ≤ c₁ * W) + (hZ : Z ≤ k₁ * W) (hSL : SL ≤ ka * W) (hn : n + 1 ≤ 1 * W) (hR₁ : R₁ ≤ c₂ * W) + (hR₂ : R₂ ≤ k₂ * W) : + 2 * (capS + runS) + n + (R₁ + R₂) ≤ (6 * c₁ + 1 + 4 * k₁ + 4 * c₃ + 4 * ka + c₂ + k₂) * W := by + nlinarith + +variable {mk} + +/-- **The fast machine within the interface's bounds.** Given polynomial-time components of the +scheme and the resource analysis, for all constants `C` and `D` there is `K` such that on every +guarded input admissible at size `s ≥ 2` with constant `C` and with precision `1 ≤ P ≤ D s`, the +fast machine takes time at most `K (n + 1) ^ K 2 ^ (K s / √⌊log₂ s⌋)` and space at most +`K (n + 1) ^ K`. -/ +theorem fastCertificate_le (hpoly : (scheme mk).PolyTime encCall encFrame encValue) + (hana : FastAnalysis mk) (C D : ℕ) : + ∃ K : ℕ, ∀ (s P : ℕ) (G : ℕ × ExplicitTensorNetwork ℕ), 2 ≤ s → 1 ≤ P → P ≤ D * s → + ExplicitAdmissible C s G → fastGuard G = true → + (fastCertificate hpoly).time (P, G) ≤ + K * ((FastContraction.encInput (P, G)).length + 1) ^ K * + 2 ^ (K * s / Nat.sqrt (Nat.log 2 s)) ∧ + (fastCertificate hpoly).space (P, G) ≤ + K * ((FastContraction.encInput (P, G)).length + 1) ^ K := by + obtain ⟨Ka, s₀, hKa⟩ := hana C D + obtain ⟨K₁, hK₁⟩ := Scheme.certificate_le hpoly (leafCertificate mk) + obtain ⟨K₂, hK₂⟩ := ExplicitTensorNetwork.exists_directContractionBinaryCertificate_le + have hcapP := NativeCertificate.ofPolyTime_isPolynomial + ExplicitTensorNetwork.capPrecision_polyTime + have hcnP := NativeCertificate.ofPolyTime_isPolynomial fuel_polyTime + have hcrP := NativeCertificate.ofPolyTime_isPolynomial root_polyTime + have hcapL := ExplicitTensorNetwork.capPrecision_polyTime.polyBounded_length + have hfuelL := fuel_polyTime.polyBounded_length + obtain ⟨c₁, d₁, h₁⟩ := (((((((hcapP.1.add hcapP.2).add (hcnP.1.comp hcapL)).add + (hcnP.2.comp hcapL)).add (hcrP.1.comp hcapL)).add (hcrP.2.comp hcapL)).add hcapL).add + (hfuelL.comp hcapL)).exists_le + have htP := NativeCertificate.ofPolyTime_isPolynomial tag_polyTime + have hdP := NativeCertificate.ofPolyTime_isPolynomial decode_polyTime + have hfP := NativeCertificate.ofPolyTime_isPolynomial + (ComputableInPolyTime.fst FastContraction.encInput encValue) + obtain ⟨c₂, d₂, h₂⟩ := (((((htP.1.add htP.2).add hdP.1).add hdP.2).add hfP.1).add + hfP.2).exists_le + -- The constants. + set E := 2 * d₁ + 2 * Ka * K₁ + (Ka + 1) * d₂ + K₂ + Ka + 1 with hE + set Kc := 6 * c₁ + 1 + 4 * K₁ * (Ka + 1) ^ (2 * K₁) + 4 * (c₁ + 1) ^ 2 + 4 * Ka + + c₂ * (Ka + 2) ^ d₂ + K₂ * 2 ^ (C * s₀) with hKc + refine ⟨Kc + E + FastContraction.fuelConst * (C + D) + Ka, fun s P G hs hP hPD hA hg => ?_⟩ + obtain ⟨I, N, H, F, TL, SL, he, hN, hnode, hframe, hvalue, hHFS, hTL⟩ := + hKa s P G hs hP hPD hA hg + set q : ℕ × ℕ × ExplicitTensorNetwork ℕ := (P, G) with hq + set n := (FastContraction.encInput q).length with hn + set B := n + 1 with hB + have hB1 : 1 ≤ B := by omega + set S := Nat.sqrt (Nat.log 2 s) + set Xf := 2 ^ (FastContraction.fuelConst * (C + D) * s / S) + set Xa := 2 ^ (Ka * s / S) + set X := 2 ^ ((FastContraction.fuelConst * (C + D) + Ka) * s / S) + have hXf : 1 ≤ Xf := Nat.one_le_two_pow + have hXa : 1 ≤ Xa := Nat.one_le_two_pow + have hXfX : Xf ≤ X := Nat.pow_le_pow_right (by norm_num) + (Nat.div_le_div_right (Nat.mul_le_mul_right _ (by omega))) + have hXfXa : Xf * Xa ≤ X := by + rw [← pow_add] + refine Nat.pow_le_pow_right (by norm_num) (Nat.div_add_div_le_add_div.trans ?_) + rw [add_mul] + have hX1 : 1 ≤ X := Nat.one_le_two_pow + -- The run. + obtain ⟨hrT, hrS⟩ := hK₁ (NativeCertificate.ofPolyTime fuel_polyTime) + (NativeCertificate.ofPolyTime root_polyTime) I F TL SL hnode hframe hvalue + (ExplicitTensorNetwork.capPrecision q) _ N H he + have hfuel : FastContraction.fuel (ExplicitTensorNetwork.capPrecision q) ≤ Xf := + fuel_le hA hs hPD + have hq1 := h₁ q + have hrT' : (runCertificate hpoly).time (ExplicitTensorNetwork.capPrecision q) ≤ _ := hrT + have hrS' : (runCertificate hpoly).space (ExplicitTensorNetwork.capPrecision q) ≤ _ := hrS + -- Polynomial pieces. + set Q₁ := c₁ * B ^ d₁ with hQ₁ + have hBK (k : ℕ) : 1 ≤ B ^ k := Nat.one_le_pow _ _ (by omega) + have hHF : (H + 1) * (F + 1) ≤ ((Ka + 1) * B ^ Ka) ^ 2 := by + have h1 : H + F ≤ Ka * B ^ Ka := by have := hHFS; omega + have h2 := hBK Ka + calc (H + 1) * (F + 1) ≤ (H + F + 1) ^ 2 := mul_succ_le_sq H F + _ ≤ (Ka * B ^ Ka + B ^ Ka) ^ 2 := Nat.pow_le_pow_left (by omega) 2 + _ = ((Ka + 1) * B ^ Ka) ^ 2 := by ring + have hZ : K₁ * ((H + 1) * (F + 1)) ^ K₁ ≤ K₁ * (Ka + 1) ^ (2 * K₁) * B ^ (2 * Ka * K₁) := by + calc K₁ * ((H + 1) * (F + 1)) ^ K₁ ≤ K₁ * (((Ka + 1) * B ^ Ka) ^ 2) ^ K₁ := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_left hHF _) + _ = K₁ * (Ka + 1) ^ (2 * K₁) * B ^ (2 * Ka * K₁) := by + rw [mul_pow, ← pow_mul, mul_pow, ← pow_mul, ← pow_mul, + show Ka * 2 * K₁ = 2 * Ka * K₁ by ring] + ring + have hW : ((encNat (FastContraction.fuel (ExplicitTensorNetwork.capPrecision q))).length + 1) ^ 2 + ≤ (c₁ + 1) ^ 2 * B ^ (2 * d₁) := by + have h1 : (encNat (FastContraction.fuel (ExplicitTensorNetwork.capPrecision q))).length ≤ Q₁ := + by omega + have h2 := hBK d₁ + calc _ ≤ (Q₁ + 1) ^ 2 := Nat.pow_le_pow_left (by omega) 2 + _ ≤ ((c₁ + 1) * B ^ d₁) ^ 2 := Nat.pow_le_pow_left (mul_add_one_le h2) 2 + _ = (c₁ + 1) ^ 2 * B ^ (2 * d₁) := by ring + have hTL' : TL ≤ Ka * B ^ Ka * Xa := hTL + have hF : F ≤ Ka * B ^ Ka := by have := hHFS; omega + have hSL : SL ≤ Ka * B ^ Ka := by have := hHFS; omega + -- Decoding and direct summation. + set R₁ := c₂ * (Ka + 2) ^ d₂ * B ^ ((Ka + 1) * d₂) with hR₁ + set R₂ := K₂ * 2 ^ (C * s₀) * B ^ K₂ with hR₂ + have hdec : ∀ v : ℕ × List ℕ, I.value v → + (NativeCertificate.ofPolyTime tag_polyTime).time (q, v) + + (NativeCertificate.ofPolyTime tag_polyTime).space (q, v) + + (NativeCertificate.ofPolyTime decode_polyTime).time (q, v) + + (NativeCertificate.ofPolyTime decode_polyTime).space (q, v) + + (NativeCertificate.ofPolyTime + (ComputableInPolyTime.fst FastContraction.encInput encValue)).time (q, v) + + (NativeCertificate.ofPolyTime + (ComputableInPolyTime.fst FastContraction.encInput encValue)).space (q, v) ≤ R₁ := by + intro v hv + have h := h₂ (q, v) + have hv' := hvalue v hv + have hk1 : B ≤ B ^ (Ka + 1) := by + calc B = B ^ 1 := (pow_one B).symm + _ ≤ B ^ (Ka + 1) := Nat.pow_le_pow_right hB1 (by omega) + have hk2 : B ^ Ka ≤ B ^ (Ka + 1) := Nat.pow_le_pow_right hB1 (by omega) + have hlen : (encPair FastContraction.encInput encValue (q, v)).length + 1 ≤ + (Ka + 2) * B ^ (Ka + 1) := by + have hl := length_encPair (enc₁ := FastContraction.encInput) (enc₂ := encValue) (q, v) + simp only at hl + rw [hl] + have : Ka * B ^ Ka ≤ Ka * B ^ (Ka + 1) := Nat.mul_le_mul_left _ hk2 + have e : (Ka + 2) * B ^ (Ka + 1) = Ka * B ^ (Ka + 1) + 2 * B ^ (Ka + 1) := by ring + omega + calc _ ≤ c₂ * ((encPair FastContraction.encInput encValue (q, v)).length + 1) ^ d₂ := h + _ ≤ c₂ * ((Ka + 2) * B ^ (Ka + 1)) ^ d₂ := Nat.mul_le_mul_left _ (Nat.pow_le_pow_left hlen _) + _ = R₁ := by rw [hR₁, mul_pow, ← pow_mul, Nat.mul_assoc] + obtain ⟨hdT, hdS⟩ := hK₂ q + have hm : q.2.2.numEdges ≤ C * s := hA.2.1 + have hdir (hs₀ : s < s₀) : + ExplicitTensorNetwork.directContractionBinaryCertificate.time q ≤ R₂ := by + refine hdT.trans ?_ + have h2 : 2 ^ q.2.2.numEdges ≤ 2 ^ (C * s₀) := + Nat.pow_le_pow_right (by norm_num) (hm.trans (Nat.mul_le_mul_left _ hs₀.le)) + calc K₂ * ((encInput q).length + 1) ^ K₂ * 2 ^ q.2.2.numEdges + ≤ K₂ * B ^ K₂ * 2 ^ (C * s₀) := Nat.mul_le_mul_left _ h2 + _ = R₂ := by rw [hR₂]; ring + have hdirS : ExplicitTensorNetwork.directContractionBinaryCertificate.space q ≤ R₂ := + hdS.trans (by + rw [hR₂] + calc K₂ * ((encInput q).length + 1) ^ K₂ ≤ + K₂ * ((encInput q).length + 1) ^ K₂ * 2 ^ (C * s₀) := + Nat.le_mul_of_pos_right _ Nat.one_le_two_pow + _ = K₂ * 2 ^ (C * s₀) * B ^ K₂ := by ring) + have hbig : s₀ ≤ s → (scheme mk).run fuel root (ExplicitTensorNetwork.capPrecision q) = + some (0, value mk (root (ExplicitTensorNetwork.capPrecision q))) := + fun h => Scheme.run_eq_some he (hN h) + clear hKa hK₁ hK₂ h₁ h₂ + -- Everything against `W = B ^ E X`. + set W := B ^ E * X with hWdef + have lift {c d : ℕ} (hd : d ≤ E) : c * B ^ d ≤ c * W := by + rw [hWdef, ← Nat.mul_assoc]; exact lift_le hB1 hd hX1 + have lift' {c d : ℕ} (hd : d ≤ E) : c * B ^ d ≤ c * B ^ E := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_right hB1 hd) + have hQW : Q₁ ≤ c₁ * W := lift (by omega) + have hR₁W : R₁ ≤ c₂ * (Ka + 2) ^ d₂ * W := lift (by omega) + have hR₂W : R₂ ≤ K₂ * 2 ^ (C * s₀) * W := lift (by omega) + have hnW : n + 1 ≤ 1 * W := by + have := lift (c := 1) (d := 1) (by omega); rw [pow_one] at this; omega + have hfuel1 : FastContraction.fuel (ExplicitTensorNetwork.capPrecision q) + 1 ≤ 2 * Xf := by + omega + have hP1 : (FastContraction.fuel (ExplicitTensorNetwork.capPrecision q) + 1) * + (K₁ * ((H + 1) * (F + 1)) ^ K₁) ≤ 2 * (K₁ * (Ka + 1) ^ (2 * K₁)) * W := by + calc _ ≤ (2 * Xf) * (K₁ * (Ka + 1) ^ (2 * K₁) * B ^ (2 * Ka * K₁)) := + Nat.mul_le_mul hfuel1 hZ + _ = 2 * (K₁ * (Ka + 1) ^ (2 * K₁)) * (B ^ (2 * Ka * K₁) * Xf) := by ring + _ ≤ 2 * (K₁ * (Ka + 1) ^ (2 * K₁)) * W := Nat.mul_le_mul_left _ + (Nat.mul_le_mul (Nat.pow_le_pow_right hB1 (by omega)) hXfX) + have hP2 : (FastContraction.fuel (ExplicitTensorNetwork.capPrecision q) + 1) * TL ≤ + 2 * Ka * W := by + calc _ ≤ (2 * Xf) * (Ka * B ^ Ka * Xa) := Nat.mul_le_mul hfuel1 hTL' + _ = 2 * Ka * (B ^ Ka * (Xf * Xa)) := by ring + _ ≤ 2 * Ka * W := Nat.mul_le_mul_left _ + (Nat.mul_le_mul (Nat.pow_le_pow_right hB1 (by omega)) hXfXa) + have hP3 : (FastContraction.fuel (ExplicitTensorNetwork.capPrecision q) + 1) * + ((encNat (FastContraction.fuel (ExplicitTensorNetwork.capPrecision q))).length + 1) ^ 2 ≤ + 2 * (c₁ + 1) ^ 2 * W := by + calc _ ≤ (2 * Xf) * ((c₁ + 1) ^ 2 * B ^ (2 * d₁)) := Nat.mul_le_mul hfuel1 hW + _ = 2 * (c₁ + 1) ^ 2 * (B ^ (2 * d₁) * Xf) := by ring + _ ≤ 2 * (c₁ + 1) ^ 2 * W := Nat.mul_le_mul_left _ + (Nat.mul_le_mul (Nat.pow_le_pow_right hB1 (by omega)) hXfX) + have hZE : K₁ * ((H + 1) * (F + 1)) ^ K₁ ≤ K₁ * (Ka + 1) ^ (2 * K₁) * B ^ E := + hZ.trans (lift' (by omega)) + have hQE : Q₁ ≤ c₁ * B ^ E := lift' (by omega) + have hR₁E : R₁ ≤ c₂ * (Ka + 2) ^ d₂ * B ^ E := lift' (by omega) + have hR₂E : R₂ ≤ K₂ * 2 ^ (C * s₀) * B ^ E := lift' (by omega) + have hSLE : SL ≤ Ka * B ^ E := hSL.trans (lift' (by omega)) + have hnE : n + 1 ≤ 1 * B ^ E := by + have := lift' (c := 1) (d := 1) (by omega); rw [pow_one] at this; omega + have hKK : Kc ≤ Kc + E + FastContraction.fuelConst * (C + D) + Ka := by omega + have hBE : B ^ E ≤ B ^ (Kc + E + FastContraction.fuelConst * (C + D) + Ka) := + Nat.pow_le_pow_right hB1 (by omega) + have hXK : X ≤ 2 ^ ((Kc + E + FastContraction.fuelConst * (C + D) + Ka) * s / S) := + Nat.pow_le_pow_right (by norm_num) (Nat.div_le_div_right (Nat.mul_le_mul_right _ (by omega))) + refine ⟨?_, ?_⟩ + · rw [fastCertificate_time] + have hrest : ((scheme mk).run fuel root (ExplicitTensorNetwork.capPrecision q)).elim + (ExplicitTensorNetwork.directContractionBinaryCertificate.time q) (fun v => + (NativeCertificate.ofPolyTime tag_polyTime).time (q, v) + + if decide (v.1 = 0) = true then + (NativeCertificate.ofPolyTime decode_polyTime).time (q, v) + else (NativeCertificate.ofPolyTime + (ComputableInPolyTime.fst FastContraction.encInput encValue)).time (q, v) + + ExplicitTensorNetwork.directContractionBinaryCertificate.time q) ≤ R₁ + R₂ := by + rcases hr : (scheme mk).run fuel root (ExplicitTensorNetwork.capPrecision q) with _ | v + · by_cases hs₀ : s₀ ≤ s + · rw [hbig hs₀] at hr; cases hr + · simp only [Option.elim_none]; have := hdir (by omega); omega + · have hd := hdec v (Scheme.value_of_run_eq_some he hr) + simp only [Option.elim_some] + split_ifs with h0 + · omega + · by_cases hs₀ : s₀ ≤ s + · rw [hbig hs₀] at hr; cases hr; simp at h0 + · have := hdir (by omega); omega + have htot := time_assemble (capT := (NativeCertificate.ofPolyTime + ExplicitTensorNetwork.capPrecision_polyTime).time q) hrT' (by omega) hQW hP1 hP2 + hP3 hnW hR₁W hR₂W + have hKc' : Kc = 6 * c₁ + 1 + 4 * (K₁ * (Ka + 1) ^ (2 * K₁)) + 4 * (c₁ + 1) ^ 2 + 4 * Ka + + c₂ * (Ka + 2) ^ d₂ + K₂ * 2 ^ (C * s₀) := by rw [hKc]; ring + rw [← hKc'] at htot + calc _ ≤ _ := Nat.add_le_add_left hrest _ + _ ≤ Kc * W := htot + _ ≤ _ := by + rw [hWdef, ← Nat.mul_assoc] + exact Nat.mul_le_mul (Nat.mul_le_mul hKK hBE) hXK + · rw [fastCertificate_space] + have hrest : ((scheme mk).run fuel root (ExplicitTensorNetwork.capPrecision q)).elim + (ExplicitTensorNetwork.directContractionBinaryCertificate.space q) (fun v => + (NativeCertificate.ofPolyTime tag_polyTime).space (q, v) + + if decide (v.1 = 0) = true then + (NativeCertificate.ofPolyTime decode_polyTime).space (q, v) + else (NativeCertificate.ofPolyTime + (ComputableInPolyTime.fst FastContraction.encInput encValue)).space (q, v) + + ExplicitTensorNetwork.directContractionBinaryCertificate.space q) ≤ R₁ + R₂ := by + rcases hr : (scheme mk).run fuel root (ExplicitTensorNetwork.capPrecision q) with _ | v + · simp only [Option.elim_none]; omega + · have hd := hdec v (Scheme.value_of_run_eq_some he hr) + simp only [Option.elim_some] + split_ifs <;> omega + have htot := space_assemble (capS := (NativeCertificate.ofPolyTime + ExplicitTensorNetwork.capPrecision_polyTime).space q) (c₃ := (c₁ + 1) ^ 2) hrS' + (by omega) hQE hZE hSLE hnE hR₁E hR₂E + have hKc' : Kc = 6 * c₁ + 1 + 4 * (K₁ * (Ka + 1) ^ (2 * K₁)) + 4 * (c₁ + 1) ^ 2 + 4 * Ka + + c₂ * (Ka + 2) ^ d₂ + K₂ * 2 ^ (C * s₀) := by rw [hKc]; ring + rw [← hKc'] at htot + calc _ ≤ _ := Nat.add_le_add_left hrest _ + _ ≤ Kc * B ^ E := htot + _ ≤ _ := Nat.mul_le_mul hKK hBE + end Resources end Cslib.Complexity.CountingSimulation From 455906c815b7b702544c7c2bf47f2beeaae4ff46 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:40:13 -0400 Subject: [PATCH 086/129] feat(Classes/CountingSimulation): the contraction algorithm from the fast machine's remaining obligations Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../CountingSimulation/FastAlgorithm.lean | 59 +++++++++++++++++-- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/Complexity.lean b/Complexity.lean index 31f9d48e92..85079871ad 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1099,6 +1099,7 @@ public import Complexity.Computability.Complexity.Classes.CountingSimulation.Adm public import Complexity.Computability.Complexity.Classes.CountingSimulation.Assembly public import Complexity.Computability.Complexity.Classes.CountingSimulation.BlockNetworks public import Complexity.Computability.Complexity.Classes.CountingSimulation.Containments +public import Complexity.Computability.Complexity.Classes.CountingSimulation.FastAlgorithm public import Complexity.Computability.Complexity.Classes.CountingSimulation.Guarded public import Complexity.Computability.Complexity.Classes.CountingSimulation.Interfaces public import Complexity.Computability.Complexity.Classes.DP diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean index 93797c1772..1896c46835 100644 --- a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean @@ -15,21 +15,47 @@ import Mathlib.Analysis.SpecificLimits.Normed /-! # The fast contraction algorithm, assembled -The guard of the fast contraction algorithm (`CountingSimulation.fastGuard`) tests what -`ExplicitAdmissible` already implies (rows first, full tables) and the degree bound -`degreeBound m` of the schedule (§D.4.2 of [OpenAI2026ETH], (D.6), p. 58). An admissible network of -degree at most `C ⌊log₂ s⌋` fails it only for `s` below a threshold depending on `C` -(`CountingSimulation.exists_le_of_fastGuard_eq_false`), since `degreeBound m ≥ m ^ (9 / 10)` and -`m ≥ s`. +The contraction interface (`CountingSimulation.ContractionAlgorithm`) for admissible explicit +networks, from the native machine of the recursion of Theorem D.1 of [OpenAI2026ETH] +(`FastContraction.fastCertificate`), reduced to its remaining obligations +(`CountingSimulation.contractionAlgorithmOfFast`). + +**The guard** (`CountingSimulation.fastGuard`) tests what `ExplicitAdmissible` already implies +(rows first, full tables) and the degree bound `degreeBound m` of the schedule (§D.4.2, (D.6), +p. 58). An admissible network of degree at most `C ⌊log₂ s⌋` fails it only for `s` below a +threshold depending on `C` (`CountingSimulation.exists_le_of_fastGuard_eq_false`), since +`degreeBound m ≥ m ^ (9 / 10)` and `m ≥ s`. + +**The clock** is subexponential on admissible inputs: with `m ≤ C s` edges and precision +`P ≤ D s` it is at most `2 ^ (K₀ (C + D) s / √⌊log₂ s⌋)` (`CountingSimulation.fuel_le`). + +**The resources.** `CountingSimulation.FastAnalysis` is the resource analysis of the recursion on +admissible guarded inputs: a derivation of the root with polynomial depth, node sizes and leaf +space, leaf time `poly(n) 2 ^ O(s / √⌊log₂ s⌋)`, and, from some size on, a node count within the +clock. From it and polynomial-time components of the scheme, the fast machine meets the +interface's bounds (`CountingSimulation.fastCertificate_le`), through +`Cslib.GenericRAM.StackRecursion.Scheme.certificate_le`. Below the size threshold the run may not +finish, and the machine falls back to direct summation, at a constant factor. + +**Remaining obligations** (the hypotheses of `CountingSimulation.contractionAlgorithmOfFast`): the +guard and the components of the scheme in polynomial time, the correctness of one active call +(`FastContraction.StepCorrect`), the passage of well-formedness to the children +(`FastContraction.WFChildren`) and to the root, and the resource analysis. ## Main definitions * `Cslib.Complexity.CountingSimulation.fastGuard`: the guard. +* `Cslib.Complexity.CountingSimulation.FastAnalysis`: the resource analysis. +* `Cslib.Complexity.CountingSimulation.contractionAlgorithmOfFast`: the contraction algorithm from + the remaining obligations. ## Main results * `Cslib.Complexity.CountingSimulation.exists_le_of_fastGuard_eq_false`: admissible networks failing the guard are small. +* `Cslib.Complexity.CountingSimulation.fuel_le`: the clock on admissible inputs. +* `Cslib.Complexity.CountingSimulation.fastCertificate_le`: the fast machine within the + interface's bounds. ## References @@ -430,6 +456,27 @@ theorem fastCertificate_le (hpoly : (scheme mk).PolyTime encCall encFrame encVal _ ≤ Kc * B ^ E := htot _ ≤ _ := Nat.mul_le_mul hKK hBE +/-- **The contraction algorithm from the fast machine**, given its remaining obligations: a +polynomial-time guard, polynomial-time components of the scheme, the correctness of one active +call and the passage of well-formedness to the children (`FastContraction.StepCorrect`, +`FastContraction.WFChildren`), well-formed roots on guarded inputs, and the resource analysis +(`FastAnalysis`). It branches on the guard (`ContractionAlgorithm.ofGuarded`) to the fast machine +or to direct summation. -/ +noncomputable def contractionAlgorithmOfFast + (hguard : ComputableInPolyTime fastGuard + (encPair encNat ExplicitTensorNetwork.encExplicitNetwork) + encBool) + (hpoly : (scheme mk).PolyTime encCall encFrame encValue) {WF : Call → Prop} + (hstep : StepCorrect mk WF) (hwf : WFChildren mk WF) + (hroot : ∀ P G, fastGuard G = true → WF (root (ExplicitTensorNetwork.capPrecision (P, G)))) + (hana : FastAnalysis mk) : + ContractionAlgorithm (encPair encNat ExplicitTensorNetwork.encExplicitNetwork) + (fun G => G.2.contraction) ExplicitAdmissible := + ContractionAlgorithm.ofGuarded hguard (fastCertificate hpoly) + (fun P G hg => fastValue_eq mk hstep hwf (P, G) (hroot P G hg)) + (fun C D => fastCertificate_le hpoly hana C D) (fun _ _ _ hA => hA.2.1) + exists_le_of_fastGuard_eq_false + end Resources end Cslib.Complexity.CountingSimulation From 7b8cc9bf2e40d6fd2a8ea24b17535a90d2f985fd Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:42:18 -0400 Subject: [PATCH 087/129] feat(circuit/frontier): the modulus is irreducible modulo 2; tests of the fast machine Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/FastContractionMachine.lean | 37 +++++++++++++++ ComplexityTests/FastContraction.lean | 47 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 ComplexityTests/FastContraction.lean diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean index 3713b8431a..f93e9b6050 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean @@ -10,6 +10,8 @@ module public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContraction public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DirectContraction import Complexity.Computability.PolyTime.GaloisRingSeries +import Complexity.Foundations.FieldTheory.Finite.Trinomial +import Mathlib.Data.List.GetD import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Log import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.SqrtLog @@ -51,14 +53,21 @@ result is correct on every well-formed input, whatever the fuel * `Cslib.Circuits.Frontier.FastContraction.modulus`, `rootNet`, `root`, `fuel`: the root call and the clock. * `Cslib.Circuits.Frontier.FastContraction.fastValue`: the result of the machine. +* `Cslib.Circuits.Frontier.FastContraction.fastCertificate`: the machine, given polynomial-time + components of the scheme. ## Main results +* `Cslib.Circuits.Frontier.FastContraction.irreducible_modulus`: the modulus is irreducible modulo + `2`. * `Cslib.Circuits.Frontier.FastContraction.val_rootNet`: the root computes the contraction of `G`. * `Cslib.Circuits.Frontier.FastContraction.getD_mod_of_val_eq`: reading off the constant coordinate. * `Cslib.Circuits.Frontier.FastContraction.fastValue_eq`: the machine is correct on well-formed inputs, for every fuel. +* `Cslib.Circuits.Frontier.FastContraction.root_polyTime`, + `Cslib.Circuits.Frontier.FastContraction.fuel_polyTime`: the root and the clock in polynomial + time. ## References @@ -135,6 +144,34 @@ theorem modulus_ne_nil (m : ℕ) : modulus m ≠ [] := by rw [← List.length_pos_iff, length_modulus] positivity +/-- **The modulus is the trinomial** `X ^ (2 ⬝ 3 ^ t) + X ^ (3 ^ t) + 1` over `ℤ`. -/ +theorem monicOf_modulus (m : ℕ) : + monicOf (modulus m) = Polynomial.X ^ (2 * 3 ^ modulusExp m) + + Polynomial.X ^ (3 ^ modulusExp m) + 1 := by + set t := modulusExp m + have h3 : 0 < 3 ^ t := by positivity + have hsum : ∑ i ∈ Finset.range (2 * 3 ^ t), + Polynomial.C (((modulus m).getD i 0 : ℕ) : ℤ) * Polynomial.X ^ i = + Polynomial.X ^ (3 ^ t) + 1 := by + have hget (i : ℕ) (hi : i < 2 * 3 ^ t) : + (modulus m).getD i 0 = if i = 0 ∨ i = 3 ^ t then 1 else 0 := by + rw [List.getD_eq_getElem _ _ (by rw [length_modulus]; exact hi)] + simp [modulus, t] + rw [Finset.sum_congr rfl fun i hi => by rw [hget i (Finset.mem_range.mp hi)]] + rw [Finset.sum_eq_add (0 : ℕ) (3 ^ t) (by omega) (fun i _ hi => by + simp only [hi.1, hi.2, or_self, ↓reduceIte, Nat.cast_zero, map_zero, zero_mul]) + (fun h => absurd (Finset.mem_range.mpr (by omega)) h) + (fun h => absurd (Finset.mem_range.mpr (by omega)) h)] + simp [add_comm] + rw [monicOf, length_modulus, hsum, add_assoc] + +/-- **The modulus is irreducible modulo `2`**: its reduction is `Cslib.trinomial t`. -/ +theorem irreducible_modulus (m : ℕ) : + Irreducible ((monicOf (modulus m)).map (Int.castRingHom (ZMod 2))) := by + rw [monicOf_modulus] + simpa [Polynomial.map_add, Polynomial.map_pow, Polynomial.map_X, Polynomial.map_one, + Cslib.trinomial] using Cslib.irreducible_trinomial (modulusExp m) + /-- **The tables of an explicit network as vertex descriptors.** -/ @[expose] def rootNet (G : ExplicitTensorNetwork ℕ) : DescribedNetwork VertexDesc := ⟨G.numEdges, G.vertices.map fun v => (v.1, .inl v.2)⟩ diff --git a/ComplexityTests/FastContraction.lean b/ComplexityTests/FastContraction.lean new file mode 100644 index 0000000000..7a0f8cff74 --- /dev/null +++ b/ComplexityTests/FastContraction.lean @@ -0,0 +1,47 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.Complexity.Classes.CountingSimulation.FastAlgorithm + +/-! # The fast contraction machine: checked instances + +The modulus at `m = 4` edges, the root call of a small network, its guard, and the arithmetic of +the call data. -/ + +namespace ComplexityTests.FastContraction + +open Cslib.Circuits.Frontier Cslib.Circuits.Frontier.FastContraction + Cslib.Complexity.CountingSimulation + +-- At `m = 4`: `t = ⌊log₂ 10⌋ = 3`, so the modulus has `2 ⬝ 27 = 54` coefficients, `1` at `0` and +-- at `27`, which gives `2 ^ 54` interpolation residues. +example : modulusExp 4 = 3 := by decide +example : (modulus 4).length = 54 := by decide +example : (modulus 4).getD 0 0 = 1 ∧ (modulus 4).getD 27 0 = 1 ∧ (modulus 4).getD 1 0 = 0 := by + decide + +-- The modulus is irreducible modulo `2`, at every size. +example (m : ℕ) : Irreducible ((Cslib.GaloisRingArith.monicOf (modulus m)).map + (Int.castRingHom (ZMod 2))) := irreducible_modulus m + +/-- One row `0` and one column `1` joined by edge `0`, with full tables. -/ +def tiny : ℕ × ExplicitTensorNetwork ℕ := (1, ⟨1, [([0], [1, 2]), ([0], [3, 4])]⟩) + +-- The guard holds: rows first, full tables, degree `1 ≤ degreeBound 1 = 4`. +example : fastGuard tiny = true := by decide + +-- The root of `(P, r, G)` at `P = 3`: scale `1`, depth `0`, bound `2`, capped precision `3`. +example : (root (3, tiny)).1.1 = 1 ∧ (root (3, tiny)).1.2.1 = 0 ∧ + (root (3, tiny)).1.2.2.1 = 2 ∧ (root (3, tiny)).1.2.2.2.1 = 3 := by decide + +-- A call this small is a leaf: `leafSize 1 = 1`, so it has no more edges than the leaf size. +example : active (root (3, tiny)) = false := by decide + +-- The contraction of `tiny` is `1 ⬝ 3 + 2 ⬝ 4 = 11`, and the machine's result is `11 mod 2 ^ P` +-- whenever its root is well-formed (`fastValue_eq`). +example : tiny.2.contraction = 11 := by decide + +end ComplexityTests.FastContraction From 63c56cce03bef8ca32895769bdb47a6b21c8fcc2 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:47:48 -0400 Subject: [PATCH 088/129] feat(Classes/CountingSimulation): the guard of the fast algorithm is polynomial-time Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../CountingSimulation/FastAlgorithm.lean | 160 +++++++++++++++++- 1 file changed, 153 insertions(+), 7 deletions(-) diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean index 1896c46835..f76c342655 100644 --- a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean @@ -11,6 +11,17 @@ public import Complexity.Computability.Complexity.Classes.CountingSimulation.Gua public import Complexity.Computability.Complexity.Classes.CountingSimulation.Admissible public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionMachine import Mathlib.Analysis.SpecificLimits.Normed +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Binary +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Log +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.BitString +import Complexity.Computability.Machines.Turing.MultiTape.Internal.EqualityPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryDivision +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeNatFrontends +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimePrimitives /-! # The fast contraction algorithm, assembled @@ -37,8 +48,10 @@ interface's bounds (`CountingSimulation.fastCertificate_le`), through `Cslib.GenericRAM.StackRecursion.Scheme.certificate_le`. Below the size threshold the run may not finish, and the machine falls back to direct summation, at a constant factor. +The guard is polynomial-time (`CountingSimulation.fastGuard_polyTime`). + **Remaining obligations** (the hypotheses of `CountingSimulation.contractionAlgorithmOfFast`): the -guard and the components of the scheme in polynomial time, the correctness of one active call +components of the scheme in polynomial time, the correctness of one active call (`FastContraction.StepCorrect`), the passage of well-formedness to the children (`FastContraction.WFChildren`) and to the root, and the resource analysis. @@ -53,6 +66,7 @@ guard and the components of the scheme in polynomial time, the correctness of on * `Cslib.Complexity.CountingSimulation.exists_le_of_fastGuard_eq_false`: admissible networks failing the guard are small. +* `Cslib.Complexity.CountingSimulation.fastGuard_polyTime`: the guard is polynomial-time. * `Cslib.Complexity.CountingSimulation.fuel_le`: the clock on admissible inputs. * `Cslib.Complexity.CountingSimulation.fastCertificate_le`: the fast machine within the interface's bounds. @@ -118,6 +132,141 @@ theorem exists_le_of_fastGuard_eq_false (C : ℕ) : (Nat.pow_log_le_self 2 (by have := Nat.one_le_two_pow (n := L₀); omega)) 9 omega +/-! ### The guard in polynomial time -/ + +section GuardPolyTime + +open Turing.MultiTapeTM + +/-- The encoding of an explicit network with its number of rows. -/ +local notation "encG" => encPair encNat ExplicitTensorNetwork.encExplicitNetwork + +/-- The guard as a conjunction of list tests. -/ +private def guardTest (G : ℕ × ExplicitTensorNetwork ℕ) : Bool := + G.2.vertices.all (fun v => v.1.all fun i => decide (i < G.2.numEdges)) && + (List.range G.2.numEdges).all (fun i => + decide (((G.2.vertices.take G.1).flatMap Prod.fst).count i = 1) && + decide (((G.2.vertices.drop G.1).flatMap Prod.fst).count i = 1)) && + G.2.vertices.all (fun v => decide (v.2.length = 2 ^ v.1.length)) && + G.2.vertices.all (fun v => decide (v.1.length ≤ degreeBound G.2.numEdges)) + +private theorem fastGuard_eq_guardTest (G : ℕ × ExplicitTensorNetwork ℕ) : + fastGuard G = guardTest G := by + refine Bool.eq_iff_iff.mpr ?_ + simp only [fastGuard, guardTest, ExplicitTensorNetwork.IsRowsFirstBipartite, + ExplicitTensorNetwork.maxDegree_le_iff, decide_eq_true_eq, Bool.and_eq_true, + List.all_eq_true, List.mem_range, List.count_flatMap, Function.comp_def] + constructor + · rintro ⟨⟨h1, h2⟩, h3, h4⟩ + exact ⟨⟨⟨fun v hv i hi => h1 v hv i hi, fun i hi => h2 i hi⟩, h3⟩, h4⟩ + · rintro ⟨⟨⟨h1, h2⟩, h3⟩, h4⟩ + exact ⟨⟨fun v hv i hi => h1 v hv i hi, fun i hi => h2 i hi⟩, h3, h4⟩ + +/-- The degree bound `2 ^ (9 ⌊log₂ m⌋ / 10 + 2)`, in binary from `m` in unary. -/ +private theorem degreeBound_polyTime : ComputableInPolyTime degreeBound encUnary encNat := by + have hb := ComputableInPolyTime.unary_to_binary + have hlog := ComputableInPolyTime.binary_log_two.comp hb + have he : ComputableInPolyTime (fun m => 9 * Nat.log 2 m / 10 + 2) encUnary encUnary := + ComputableInPolyTime.binary_to_unary_bounded + (ComputableInPolyTime.binary_add.comp ((ComputableInPolyTime.binary_div.comp + ((ComputableInPolyTime.binary_mul' (ComputableInPolyTime.const 9) (hb.comp hlog)).prod + (ComputableInPolyTime.const 10))).prod (ComputableInPolyTime.const 2))) + (ComputableInPolyTime.unary_add' hlog (ComputableInPolyTime.const 2)) fun m => by + simp only [Function.comp_apply]; omega + exact ComputableInPolyTime.binary_two_pow.comp he + +/-- **The guard is polynomial-time.** -/ +theorem fastGuard_polyTime : ComputableInPolyTime fastGuard encG encBool := by + have hpair : ComputableInPolyTime (DescribedNetwork.equivProd (T := List ℕ)) + ExplicitTensorNetwork.encExplicitNetwork + (encPair encUnary (encList (encPair (encList encNat) (encList encNat)))) := + ComputableInPolyTime.of_eq fun _ => rfl + have hd := ComputableInPolyTime.snd encNat ExplicitTensorNetwork.encExplicitNetwork + have hr := ComputableInPolyTime.fst encNat ExplicitTensorNetwork.encExplicitNetwork + have hm : ComputableInPolyTime (fun G : ℕ × ExplicitTensorNetwork ℕ => G.2.numEdges) encG + encUnary := (ComputableInPolyTime.fst _ _).comp (hpair.comp hd) + have hvs : ComputableInPolyTime (fun G : ℕ × ExplicitTensorNetwork ℕ => G.2.vertices) encG + (encList (encPair (encList encNat) (encList encNat))) := + (ComputableInPolyTime.snd _ _).comp (hpair.comp hd) + -- Ports in range. + have h1 : ComputableInPolyTime (fun G : ℕ × ExplicitTensorNetwork ℕ => + G.2.vertices.all (fun v => v.1.all fun i => decide (i < G.2.numEdges))) encG encBool := by + refine ComputableInPolyTime.allWithInput hvs ?_ + refine ComputableInPolyTime.allWithInput + ((ComputableInPolyTime.fst _ _).comp (ComputableInPolyTime.snd _ _)) ?_ + exact ComputableInPolyTime.binary_lt.comp ((ComputableInPolyTime.snd _ _).prod + (ComputableInPolyTime.unary_to_binary.comp (hm.comp ((ComputableInPolyTime.fst _ _).comp + (ComputableInPolyTime.fst _ _))))) + -- Each edge has one row port and one column port. + have hlen := (ComputableInPolyTime.length_list _).comp hvs + have hcap := ComputableInPolyTime.binary_min_unary' hlen hr + have htake : ComputableInPolyTime (fun G : ℕ × ExplicitTensorNetwork ℕ => + G.2.vertices.take (min G.2.vertices.length G.1)) encG + (encList (encPair (encList encNat) (encList encNat))) := + .of_tidy (ComputableTidilyInPolyTime.listTakeWithInput hvs.tidy hcap.tidy) + have hdrop : ComputableInPolyTime (fun G : ℕ × ExplicitTensorNetwork ℕ => + G.2.vertices.drop (min G.2.vertices.length G.1)) encG + (encList (encPair (encList encNat) (encList encNat))) := + .of_tidy (ComputableTidilyInPolyTime.listDropWithInput hvs.tidy hcap.tidy) + have hflat {l : ℕ × ExplicitTensorNetwork ℕ → List (List ℕ × List ℕ)} + (hl : ComputableInPolyTime l encG (encList (encPair (encList encNat) (encList encNat)))) : + ComputableInPolyTime (fun G => (l G).flatMap Prod.fst) encG (encList encNat) := + ComputableInPolyTime.flatMapWithInput (f := fun _ v => v.1) hl + ((ComputableInPolyTime.fst _ _).comp (ComputableInPolyTime.snd _ _)) + have hcount {l : ℕ × ExplicitTensorNetwork ℕ → List ℕ} + (hl : ComputableInPolyTime l encG (encList encNat)) : + ComputableInPolyTime (fun p : (ℕ × ExplicitTensorNetwork ℕ) × ℕ => + decide ((l p.1).count p.2 = 1)) (encPair encG encUnary) encBool := by + have hc := ComputableInPolyTime.countPWithInput (test := fun p a => decide (a = p.2)) + (hl.comp (ComputableInPolyTime.fst encG encUnary)) + (ComputableInPolyTime.decide_eq' (ComputableInPolyTime.snd _ _) + (ComputableInPolyTime.unary_to_binary.comp ((ComputableInPolyTime.snd _ _).comp + (ComputableInPolyTime.fst _ _)))) + refine (ComputableInPolyTime.decide_eq' hc (ComputableInPolyTime.const 1)).of_enc_eq + fun p => ?_ + simp only [Function.comp_apply, List.count_eq_countP] + congr 3 + have h2 : ComputableInPolyTime (fun G : ℕ × ExplicitTensorNetwork ℕ => + (List.range G.2.numEdges).all (fun i => + decide (((G.2.vertices.take (min G.2.vertices.length G.1)).flatMap Prod.fst).count i = 1) && + decide (((G.2.vertices.drop (min G.2.vertices.length G.1)).flatMap Prod.fst).count i = + 1))) encG encBool := + ComputableInPolyTime.allWithInput (ComputableInPolyTime.range.comp hm) + (ComputableInPolyTime.bool_and.comp ((hcount (hflat htake)).prod (hcount (hflat hdrop)))) + -- Full tables. + have h3 : ComputableInPolyTime (fun G : ℕ × ExplicitTensorNetwork ℕ => + G.2.vertices.all (fun v => decide (v.2.length = 2 ^ v.1.length))) encG encBool := by + refine ComputableInPolyTime.allWithInput hvs ?_ + have hv := ComputableInPolyTime.snd encG (encPair (encList encNat) (encList encNat)) + exact ComputableInPolyTime.decide_eq' + (ComputableInPolyTime.unary_to_binary.comp ((ComputableInPolyTime.length_list _).comp + ((ComputableInPolyTime.snd _ _).comp hv))) + (ComputableInPolyTime.binary_two_pow.comp ((ComputableInPolyTime.length_list _).comp + ((ComputableInPolyTime.fst _ _).comp hv))) + -- The degree bound. + have h4 : ComputableInPolyTime (fun G : ℕ × ExplicitTensorNetwork ℕ => + G.2.vertices.all (fun v => decide (v.1.length ≤ degreeBound G.2.numEdges))) encG + encBool := by + refine ComputableInPolyTime.allWithInput hvs ?_ + exact ComputableInPolyTime.binary_le' + (ComputableInPolyTime.unary_to_binary.comp ((ComputableInPolyTime.length_list _).comp + ((ComputableInPolyTime.fst _ _).comp (ComputableInPolyTime.snd _ _)))) + (degreeBound_polyTime.comp (hm.comp (ComputableInPolyTime.fst _ _))) + refine (ComputableInPolyTime.bool_and.comp ((ComputableInPolyTime.bool_and.comp + ((ComputableInPolyTime.bool_and.comp (h1.prod h2)).prod h3)).prod h4)).of_enc_eq fun G => ?_ + have ht : G.2.vertices.take (min G.2.vertices.length G.1) = G.2.vertices.take G.1 := by + rcases le_total G.2.vertices.length G.1 with hle | hle + · rw [min_eq_left hle, List.take_length, List.take_of_length_le hle] + · rw [min_eq_right hle] + have hdr : G.2.vertices.drop (min G.2.vertices.length G.1) = G.2.vertices.drop G.1 := by + rcases le_total G.2.vertices.length G.1 with hle | hle + · rw [min_eq_left hle, List.drop_length, List.drop_of_length_le hle] + · rw [min_eq_right hle] + rw [fastGuard_eq_guardTest, guardTest] + simp only [Function.comp_apply, ht, hdr] + +end GuardPolyTime + /-! ### The clock -/ open FastContraction in @@ -456,23 +605,20 @@ theorem fastCertificate_le (hpoly : (scheme mk).PolyTime encCall encFrame encVal _ ≤ Kc * B ^ E := htot _ ≤ _ := Nat.mul_le_mul hKK hBE -/-- **The contraction algorithm from the fast machine**, given its remaining obligations: a -polynomial-time guard, polynomial-time components of the scheme, the correctness of one active +/-- **The contraction algorithm from the fast machine**, given its remaining obligations: +polynomial-time components of the scheme, the correctness of one active call and the passage of well-formedness to the children (`FastContraction.StepCorrect`, `FastContraction.WFChildren`), well-formed roots on guarded inputs, and the resource analysis (`FastAnalysis`). It branches on the guard (`ContractionAlgorithm.ofGuarded`) to the fast machine or to direct summation. -/ noncomputable def contractionAlgorithmOfFast - (hguard : ComputableInPolyTime fastGuard - (encPair encNat ExplicitTensorNetwork.encExplicitNetwork) - encBool) (hpoly : (scheme mk).PolyTime encCall encFrame encValue) {WF : Call → Prop} (hstep : StepCorrect mk WF) (hwf : WFChildren mk WF) (hroot : ∀ P G, fastGuard G = true → WF (root (ExplicitTensorNetwork.capPrecision (P, G)))) (hana : FastAnalysis mk) : ContractionAlgorithm (encPair encNat ExplicitTensorNetwork.encExplicitNetwork) (fun G => G.2.contraction) ExplicitAdmissible := - ContractionAlgorithm.ofGuarded hguard (fastCertificate hpoly) + ContractionAlgorithm.ofGuarded fastGuard_polyTime (fastCertificate hpoly) (fun P G hg => fastValue_eq mk hstep hwf (P, G) (hroot P G hg)) (fun C D => fastCertificate_le hpoly hana C D) (fun _ _ _ hA => hA.2.1) exists_le_of_fastGuard_eq_false From 21b7ab8e03f6121304699aa7540fabc6f5575973 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:50:49 -0400 Subject: [PATCH 089/129] feat(circuit/frontier): the values stored by an active call, at each node's precision Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/FastContraction.lean | 99 ++++++++++++++++++- 1 file changed, 94 insertions(+), 5 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean index e1b1429a35..bf9a730a9e 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean @@ -13,6 +13,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ScheduledR public import Complexity.Computability.Circuit.Frontier.TensorNetwork.VertexDesc public import Complexity.Computability.GenericRAM.Native.StackRecursionTree public import Complexity.Computability.PolyTime.PacketPacking +import Complexity.Computability.PolyTime.GaloisRingSeries import Mathlib.Data.List.GetD /-! @@ -41,7 +42,7 @@ The child at a cursor state is given by a child builder `mk`, which computes the network (the native child of §D.4.6). The call absorbs the children's values with the operations of `A_N` at the common precision `N = p + Δ'` (`FastContraction.wide`), where `Δ' = (a + 1) 2 ^ (a + 1) h u + 8 h 2 ^ a + 8 M (a + 1)` bounds both the shift of Lemma D.5 and -every node precision `p_t - p`; it then stores the sums at the rows `ts[i]` of the table of native +every node precision `p_t - p`; it then places the sums at the rows `ts[i]` of the table of native recovery (`FastContraction.table`) and recovers its value with `Cslib.Circuits.Frontier.NativeRecovery.recover`. @@ -67,6 +68,9 @@ call computes the contraction of its network in `A_p`. * `Cslib.Circuits.Frontier.FastContraction.exists_eval`: derivations of the values. * `Cslib.Circuits.Frontier.FastContraction.eq_value_of_run`: a tagged value returned by the run is the root's value. +* `Cslib.Circuits.Frontier.FastContraction.val_table_runLoop`: the table of an active call holds, + at each node's precision, the sums over the mode tuples of the products of the children's + values. * `Cslib.Circuits.Frontier.FastContraction.val_value`: correctness from the step. ## References @@ -192,11 +196,11 @@ residue. -/ /-- **The interpolation points**: the lifts of the residues `0, …, n - 1`. -/ @[expose] def points : List (List ℕ) := (List.range (numPoints x)).map (digits x.fl.length) -/-- **The table of native recovery** from the stored values, node-major and point-minor: the -`c`-th value goes to row `ts[c / n]`. -/ +/-- **The table of native recovery** from the stored values, node-major and point-minor: row +`ts[j]` holds the `j`-th block of `n` values, and the other rows are empty. -/ @[expose] def table (V : List (List ℕ)) : List (List (List ℕ)) := - (List.range V.length).foldl - (fun W c => storeValue W ((nodes x).getD (c / numPoints x) 0) (V.getD c [])) [] + (List.range (2 ^ (slots x + 1))).map fun k => + if k ∈ nodes x then (V.drop ((nodes x).idxOf k * numPoints x)).take (numPoints x) else [] /-- The cursor's list of children. -/ @[expose] def children : List State := @@ -385,6 +389,91 @@ theorem eq_value_of_run {J : Type} {fuel : J → ℕ} {root : J → Call} {a : J rw [hroot] at he exact Scheme.value_of_run_eq_some he hrun rfl +/-! ### The stored values -/ + +/-- An entry of a concatenation of blocks of `n` values. -/ +theorem getElem_flatMap_range {α : Type*} (f : ℕ → ℕ → α) {T n t k : ℕ} (ht : t < T) + (hk : k < n) : + ((List.range T).flatMap fun t => (List.range n).map (f t))[t * n + k]'(by + simp only [List.length_flatMap, List.length_map, List.length_range, List.map_const', + List.sum_replicate, smul_eq_mul] + nlinarith) = f t k := by + induction T with + | zero => omega + | succ T ih => + simp only [List.range_succ, List.flatMap_append, List.flatMap_singleton] + have hlen : ((List.range T).flatMap fun t => (List.range n).map (f t)).length = T * n := by + simp + rcases Nat.lt_or_ge t T with htT | htT + · rw [List.getElem_append_left (by rw [hlen]; nlinarith)] + exact ih htT + · have htT' : t = T := by omega + subst htT' + rw [List.getElem_append_right (by rw [hlen]; omega)] + simp [hlen] + +variable {x} + +/-- Every node precision is at most the common precision `N = p + Δ'`. -/ +theorem nodePrec_le_wide (i : ℕ) : nodePrec x i ≤ wide x := by + unfold nodePrec nodePrecision wide shift + have : 2 ^ (slots x - level x ((nodes x).getD i 0)) ≤ 2 ^ slots x := + Nat.pow_le_pow_right (by norm_num) (Nat.sub_le _ _) + have : 8 * step x * 2 ^ (slots x - level x ((nodes x).getD i 0)) ≤ 8 * step x * 2 ^ slots x := + Nat.mul_le_mul_left _ this + omega + +/-- **The values stored by an active call.** At the `j`-th node of positive multiplicity and the +`i`-th point, the table of native recovery holds, at the node's precision `p_j`, the sum over the +mode tuples of weight below `p_j` of the products over the `64` groups of the children's values: +the hypothesis of the one-call correctness of Lemma D.5 at that node. -/ +theorem val_table_runLoop (h : active x = true) (vals : State → List ℕ) {j : ℕ} + (hj : j < (nodes x).length) {i : ℕ} (hi : i < numPoints x) : + val (nodePrec x j) x.fl + (((table x (runLoop x vals).2.2).getD ((nodes x)[j]) []).getD i []) = + ((Cslib.BudgetTuples.budgetTuples (weights x j) (nodePrec x j)).map fun β => + ((List.range 64).map fun g => val (nodePrec x j) x.fl (vals (j, i, β, g))).prod).sum := by + have hM := one_le_edges_of_active h + obtain ⟨qs, hqs, hrun⟩ := runAcc_cursorList (ws := weights x) (ps := nodePrec x) + (T := (nodes x).length) (n := numPoints x) (r := 64) (mul (wide x) x.fl) (add (wide x) x.fl) + (one x.fl) (zero x.fl) (val (wide x) x.fl) (fun a b => val_mul a b) (fun a b => val_add a b) + val_one val_zero (by norm_num) (nodePrec_pos hM) weights_pos vals + have hloop : (runLoop x vals).2.2 = qs := by rw [runLoop, children, hrun] + rw [hloop] + -- The row of the `j`-th node is its block. + have hnodup : (nodes x).Nodup := (List.nodup_range).filter _ + have hmem : (nodes x)[j] ∈ nodes x := List.getElem_mem hj + have hlt : (nodes x)[j] < 2 ^ (slots x + 1) := by + have := hmem + simp only [nodes, List.mem_filter, List.mem_range] at this + exact this.1 + have hidx : (nodes x).idxOf (nodes x)[j] = j := hnodup.idxOf_getElem j hj + have hqlen : qs.length = (nodes x).length * numPoints x := by + have := congrArg List.length hqs + simpa [List.length_flatMap, List.sum_replicate] using this + have hpos : j * numPoints x + i < qs.length := by rw [hqlen]; nlinarith + have hrow : ((table x qs).getD ((nodes x)[j]) []).getD i [] = qs[j * numPoints x + i] := by + have htab : (table x qs).getD ((nodes x)[j]) [] = + (qs.drop (j * numPoints x)).take (numPoints x) := by + rw [table, List.getD_eq_getElem _ _ (by simp [hlt])] + simp [hmem, hidx] + rw [htab, List.getD_eq_getElem _ _ (by simp; omega), List.getElem_take, List.getElem_drop] + rw [hrow] + -- Its value at the common precision is the sum, which reduces to the node's precision. + have hφ : val (wide x) x.fl qs[j * numPoints x + i] = + sumAt (weights x) (nodePrec x) 64 (val (wide x) x.fl) vals j i := by + have h1 := congrArg (fun l => l[j * numPoints x + i]?) hqs + simp only [List.getElem?_map, List.getElem?_eq_getElem hpos, Option.map_some] at h1 + rw [List.getElem?_eq_getElem (by simpa [hqlen] using hpos), + getElem_flatMap_range (fun t k => sumAt (weights x) (nodePrec x) 64 (val (wide x) x.fl) + vals t k) hj hi] at h1 + exact Option.some.inj h1 + have hle := nodePrec_le_wide (x := x) j + rw [← reduce_val hle, hφ, sumAt, map_list_sum, List.map_map] + congr 1 + refine List.map_congr_left fun β _ => ?_ + simp only [Function.comp_def, map_list_prod, List.map_map, reduce_val] + /-! ### Correctness -/ /-- **The correctness of one active call** for a well-formedness predicate `WF`: at a well-formed From c6c8f6492733ca99f989ae82cddc4892b78a4bc0 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:53:13 -0400 Subject: [PATCH 090/129] feat(circuit/frontier): an active call has at least 64^a edges Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/FastContraction.lean | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean index bf9a730a9e..472874bfbb 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean @@ -65,6 +65,8 @@ call computes the contraction of its network in `A_p`. ## Main results +* `Cslib.Circuits.Frontier.FastContraction.pow_slots_le_edges_of_active`: an active call has at + least `64 ^ a` edges. * `Cslib.Circuits.Frontier.FastContraction.exists_eval`: derivations of the values. * `Cslib.Circuits.Frontier.FastContraction.eq_value_of_run`: a tagged value returned by the run is the root's value. @@ -287,6 +289,46 @@ theorem one_le_edges_of_active (h : active x = true) : 1 ≤ x.edges := by simp only [active, decide_eq_true_eq] at h omega +/-- **An active call has at least `64 ^ a` edges**, the hypothesis `r ^ a ≤ M` of the native packing +(`PacketPacking.nativePacking_eq`), which `active` implies: with `λ = ⌊log₂ s⌋`, `1 ≤ u` forces +`λ ≥ 1`, `a ≤ λ / 32768` (`ContractionSchedule.slotBits_le`), and +`M > leafSize s = ⌊s / ⌊√λ⌋⌋ ≥ 2 ^ (6 ⌊λ / 32768⌋)`, since `⌊√λ⌋ ≤ 2 ^ ((λ + 1) / 2)`. -/ +theorem pow_slots_le_edges_of_active (h : active x = true) : 64 ^ slots x ≤ x.edges := by + have h' := h + simp only [active, decide_eq_true_eq] at h' + obtain ⟨hleaf, hu, -, -, -, -, -⟩ := h' + set L := Nat.log 2 x.scale with hL + have hL1 : 1 ≤ L := by + have : levels x = L / 2 ^ (17 + x.depth) := rfl + rcases Nat.eq_zero_or_pos L with h0 | h0 + · rw [this, h0, Nat.zero_div] at hu; omega + · exact h0 + have hs0 : x.scale ≠ 0 := by + intro h0; rw [hL, h0, Nat.log_zero_right] at hL1; omega + have hsL : 2 ^ L ≤ x.scale := Nat.pow_log_le_self 2 hs0 + have ha : slots x ≤ L / 32768 := slotBits_le _ _ + -- `⌊√λ⌋ ≤ 2 ^ ((λ + 1) / 2)`. + have hsq : Nat.sqrt L ≤ 2 ^ ((L + 1) / 2) := by + have h1 : Nat.sqrt L ^ 2 ≤ (2 ^ ((L + 1) / 2)) ^ 2 := by + calc Nat.sqrt L ^ 2 ≤ L := Nat.sqrt_le' L + _ ≤ 2 ^ L := Nat.lt_two_pow_self.le + _ ≤ 2 ^ (2 * ((L + 1) / 2)) := Nat.pow_le_pow_right (by norm_num) (by omega) + _ = (2 ^ ((L + 1) / 2)) ^ 2 := by rw [mul_comm, pow_mul] + exact (Nat.pow_le_pow_iff_left (by norm_num)).mp h1 + have hsqpos : 0 < Nat.sqrt L := Nat.sqrt_pos.mpr hL1 + have h64 : 64 ^ slots x ≤ 2 ^ (6 * (L / 32768)) := by + calc 64 ^ slots x = 2 ^ (6 * slots x) := by rw [pow_mul]; norm_num + _ ≤ 2 ^ (6 * (L / 32768)) := Nat.pow_le_pow_right (by norm_num) (by omega) + have hprod : 64 ^ slots x * Nat.sqrt L ≤ x.scale := by + calc 64 ^ slots x * Nat.sqrt L ≤ 2 ^ (6 * (L / 32768)) * 2 ^ ((L + 1) / 2) := + Nat.mul_le_mul h64 hsq + _ = 2 ^ (6 * (L / 32768) + (L + 1) / 2) := (pow_add _ _ _).symm + _ ≤ 2 ^ L := Nat.pow_le_pow_right (by norm_num) (by omega) + _ ≤ x.scale := hsL + have hle : 64 ^ slots x ≤ leafSize x.scale := + (Nat.le_div_iff_mul_le hsqpos).mpr hprod + omega + /-- An active call has a node of positive multiplicity: the index `2 ^ a + 1`, of level `0`. -/ theorem nodes_ne_nil_of_active (h : active x = true) : nodes x ≠ [] := by have h' := h From 6668fe1c2e3f99902f30d2a3914176aba8385bbf Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:57:51 -0400 Subject: [PATCH 091/129] feat(circuit/frontier): the step of the native recursion is correct, given the points (bridge to #1967) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/FastContraction.lean | 23 +-- .../TensorNetwork/FastContractionStep.lean | 148 ++++++++++++++++++ 2 files changed, 154 insertions(+), 17 deletions(-) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionStep.lean diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean index 472874bfbb..915b801846 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean @@ -13,6 +13,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ScheduledR public import Complexity.Computability.Circuit.Frontier.TensorNetwork.VertexDesc public import Complexity.Computability.GenericRAM.Native.StackRecursionTree public import Complexity.Computability.PolyTime.PacketPacking +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeChild import Complexity.Computability.PolyTime.GaloisRingSeries import Mathlib.Data.List.GetD @@ -85,7 +86,7 @@ public section namespace Cslib.Circuits.Frontier.FastContraction -open GaloisRingArith ChildCursor NativeRecovery ContractionSchedule Cslib.GenericRAM +open GaloisRingArith ChildCursor NativeRecovery ContractionSchedule NativeChild Cslib.GenericRAM Cslib.GenericRAM.StackRecursion /-- The parameters of a call: the scale `s`, the depth `d`, the edge bound, the precision `p`, the @@ -161,26 +162,14 @@ theorem edges_lt_bound_of_active {x : Call} (h : active x = true) : x.edges < x. @[expose] def nodePrec (i : ℕ) : ℕ := nodePrecision x.prec (step x) (slots x) x.edges (level x ((nodes x).getD i 0)) -/-- The ports of the vertices. -/ -@[expose] def ports : List (List ℕ) := x.net.vertices.map Prod.fst - -/-- **The edges as `(row, column)` pairs**, the column numbered among the columns: edge `j` joins -the first row and the first column whose ports contain it. -/ -@[expose] def edgeEnds : List (ℕ × ℕ) := - (List.range x.edges).map fun j => - (((List.range x.rows).find? fun v => ((ports x).getD v []).contains j).getD 0, - ((List.range (x.net.vertices.length - x.rows)).find? fun w => - ((ports x).getD (x.rows + w) []).contains j).getD 0) - -/-- **The packing of Lemma D.3**, natively (`PacketPacking.nativePacking`, `r = 64`). -/ +/-- **The packing of Lemma D.3**, natively (`nativeParentPacking`, `r = 64`). -/ @[expose] def packing : (List (List ℕ) × List (List ℕ)) × ℕ × List (ℕ × ℕ) := - PacketPacking.nativePacking x.rows (x.net.vertices.length - x.rows) (slots x) 64 (edgeEnds x) + nativeParentPacking x.rows (slots x) x.net /-- **The weights of the modes of the `i`-th node**, packet-major over `(q, y₀, b)` with `y₀ < 2 ^ (v₂ t + 1)` and `b < p_t`: mode `(q, y₀, b)` has weight `b + 1`. -/ @[expose] def weights (i : ℕ) : List ℕ := - (List.range ((packing x).2.1 * 2 ^ (level x ((nodes x).getD i 0) + 1) * nodePrec x i)).map - fun j => j % nodePrec x i + 1 + modeWeights (packing x).2.1 (2 ^ (level x ((nodes x).getD i 0) + 1)) (nodePrec x i) /-- The shift `Δ' = (a + 1) 2 ^ (a + 1) h u + 8 h 2 ^ a + 8 M (a + 1)`, which bounds the shift `(a + 1) ∑_t q_t` of Lemma D.5 and every `p_t - p`. -/ @@ -280,7 +269,7 @@ theorem nodePrec_pos (hM : 1 ≤ x.edges) (i : ℕ) : 0 < nodePrec x i := by /-- Every weight is positive. -/ theorem weights_pos (i : ℕ) : ∀ w ∈ weights x i, 0 < w := by intro w hw - simp only [weights, List.mem_map] at hw + simp only [weights, modeWeights, List.mem_map] at hw obtain ⟨j, -, rfl⟩ := hw omega diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionStep.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionStep.lean new file mode 100644 index 0000000000..c3cb8d2ae5 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionStep.lean @@ -0,0 +1,148 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContraction +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeCall +import Mathlib.Data.List.GetD + +/-! +# The step of the native contraction recursion + +The child builder of the recursion of `Cslib.Circuits.Frontier.FastContraction.scheme` is the native +child (`FastContraction.nativeMk`: `NativeChild.childRows` and `NativeChild.nativeChild` at the +cursor state), and a call is well-formed (`FastContraction.WF`) when its modulus is irreducible +modulo `2` and its network is rows-first. The step of an active call is then correct +(`FastContraction.stepCorrect_nativeMk`) whenever its interpolation points have lifts with unit +differences: the values the call stores are the sums of (D.16) at each node +(`FastContraction.val_table_runLoop`), which is the hypothesis of the correctness of one native call +(`NativeCall.val_recover_eq_contraction`, [OpenAI2026ETH], §D.4.4–§D.4.7). + +## Main definitions + +* `Cslib.Circuits.Frontier.FastContraction.nativeMk`: the native child builder. +* `Cslib.Circuits.Frontier.FastContraction.WF`: well-formed calls. + +## Main results + +* `Cslib.Circuits.Frontier.FastContraction.stepCorrect_nativeMk`: the step is correct, given the + points. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.4–§D.4.7 (pp. 61–64). +-/ + +public section + +namespace Cslib.Circuits.Frontier.FastContraction + +open Finset GaloisRingArith ChildCursor NativeRecovery ContractionSchedule NativeChild + +/-- **The native child builder**: at the cursor state `(j, i, β, g)`, the rows and the native +child of group `g` at the `j`-th node, the `i`-th point and the exponents `β`. -/ +@[expose] def nativeMk (x : Call) (c : State) : ℕ × DescribedNetwork VertexDesc := + (childRows x.rows (level x ((nodes x).getD c.1 0)) x.net (packing x) c.2.2.2, + nativeChild x.fl (slots x) (level x ((nodes x).getD c.1 0)) (nodePrec x c.1) + (node (slots x) ((nodes x).getD c.1 0)) ((points x).getD c.2.1 []) x.rows x.net + (packing x) c.2.2.1 c.2.2.2) + +/-- **A well-formed call**: its modulus is irreducible modulo `2` and its network is rows-first. -/ +@[expose] def WF (x : Call) : Prop := + Irreducible ((monicOf x.fl).map (Int.castRingHom (ZMod 2))) ∧ + x.net.IsRowsFirstBipartite x.rows + +variable {x : Call} + +/-- The level of a node index is the `2`-adic valuation of its node. -/ +theorem level_eq {k : ℕ} (hk : k < 2 ^ (slots x + 1)) : + level x k = padicValInt 2 (node (slots x) k) := by + have : ((k : ℤ) - 2 ^ slots x).natAbs = k - 2 ^ slots x + (2 ^ slots x - k) := by + rw [show (2 : ℤ) ^ slots x = ((2 ^ slots x : ℕ) : ℤ) by push_cast; rfl] + omega + rw [level, node, padicValInt, this, twoVal_eq (by rw [pow_succ] at hk ⊢; omega)] + +/-- The tapered multiplicities sum to at most `2 ^ (a + 1) h u`. -/ +theorem sum_taperMult_le (a u h : ℕ) : + ∑ t ∈ recoveryNodes a, taperMult u h t ≤ 2 ^ (a + 1) * (h * u) := by + rw [sum_recoveryNodes] + calc ∑ k ∈ range (2 ^ (a + 1)), (if k = 2 ^ a then 0 else taperMult u h (node a k)) + ≤ ∑ _k ∈ range (2 ^ (a + 1)), h * u := sum_le_sum fun k _ => by + split_ifs + · exact Nat.zero_le _ + · exact Nat.mul_le_mul_left _ (Nat.sub_le _ _) + _ = 2 ^ (a + 1) * (h * u) := by rw [sum_const, card_range, smul_eq_mul] + +/-- The children of an active call include every state of a node, a point, a tuple of weight +below the node's precision and a group. -/ +theorem mem_children {j i : ℕ} {β : List ℕ} {g : ℕ} (hj : j < (nodes x).length) + (hi : i < numPoints x) (hβ : β ∈ BudgetTuples.budgetTuples (weights x j) (nodePrec x j)) + (hg : g < 64) : (j, i, β, g) ∈ children x := by + simp only [children, cursorList, nodeBlock, pointBlock, groupBlock, List.mem_flatMap, + List.mem_range, List.mem_map] + exact ⟨j, hj, i, hi, β, hβ, g, hg, rfl⟩ + +/-- **The step of an active call is correct**, for the native child builder and well-formed +calls, whenever the interpolation points of every well-formed active call have lifts with unit +differences. -/ +theorem stepCorrect_nativeMk + (hζ : ∀ x (hirr : Irreducible ((monicOf x.fl).map (Int.castRingHom (ZMod 2)))), + active x = true → Pairwise fun i j : Fin (points x).length => + IsUnit (liftO hirr ((points x).getD i []) - liftO hirr ((points x).getD j []))) : + StepCorrect nativeMk WF := by + intro x hx ha vals hvals + obtain ⟨hirr, hD⟩ := hx + have ha' := ha + simp only [active, decide_eq_true_eq] at ha' + obtain ⟨-, hu, huM, hua, -, -, -⟩ := ha' + have hlen : (points x).length = numPoints x := by simp [points] + refine NativeCall.val_recover_eq_contraction hirr hD hu huM hua (le_taperStep_mul hu) + (taperStep_mul_le _ _ _) (pow_slots_le_edges_of_active ha) ?_ (points x) (hζ x hirr ha) + (by rw [hlen]; exact Nat.lt_succ_self _) _ ?_ + · calc (slots x + 1) * ∑ t ∈ recoveryNodes (slots x), taperMult (levels x) (step x) t + ≤ (slots x + 1) * (2 ^ (slots x + 1) * (step x * levels x)) := + Nat.mul_le_mul_left _ (sum_taperMult_le _ _ _) + _ ≤ shift x := by + have e : (slots x + 1) * (2 ^ (slots x + 1) * (step x * levels x)) = + (slots x + 1) * 2 ^ (slots x + 1) * step x * levels x := by ring + unfold shift; omega + · intro k hk hkL hq i hi + rw [hlen] at hi + have hkmem : k ∈ nodes x := by + simp only [nodes, List.mem_filter, List.mem_range, decide_eq_true_eq] + exact ⟨hk, hkL, hq⟩ + obtain ⟨j, hj, hjk⟩ := List.getElem_of_mem hkmem + have hgetD : (nodes x).getD j 0 = k := by rw [List.getD_eq_getElem _ _ hj, hjk] + have hlev := level_eq (x := x) hk + have hprec : nodePrec x j = nodePrecision x.prec (step x) (slots x) x.edges + (padicValInt 2 (node (slots x) k)) := by + rw [nodePrec, hgetD, hlev] + have hw : weights x j = modeWeights (nativeParentPacking x.rows (slots x) x.net).2.1 + (2 ^ (padicValInt 2 (node (slots x) k) + 1)) + (nodePrecision x.prec (step x) (slots x) x.edges (padicValInt 2 (node (slots x) k))) := by + rw [weights, packing, hgetD, hlev, hprec] + have hstore := val_table_runLoop ha vals hj hi + rw [hjk, hw, hprec] at hstore + refine hstore.trans ?_ + refine congrArg List.sum (List.map_congr_left fun β hβ => ?_) + refine congrArg List.prod (List.map_congr_left fun g hg => ?_) + have hmem := mem_children hj (by omega) (by rw [hw, hprec]; exact hβ) + (List.mem_range.mp hg) + have key : ∀ v P, level x k = v → nodePrec x j = P → + val P x.fl (vals (j, i, β, g)) = + (nativeChild x.fl (slots x) v P (node (slots x) k) ((points x).getD i []) x.rows x.net + (nativeParentPacking x.rows (slots x) x.net) β g).contraction + fun d bs => val P x.fl (d.entry bs) := by + rintro v P rfl rfl + have h := hvals _ hmem + simp only [nativeMk, packing] at h + rw [hgetD] at h + exact h + exact key _ _ hlev hprec + +end Cslib.Circuits.Frontier.FastContraction From a180db51c40893cc4f752f282bf1f819cd3a6886 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 02:59:27 -0400 Subject: [PATCH 092/129] feat(Classes/CountingSimulation): the contraction algorithm from the native recursion, with the step, child well-formedness and roots proved Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../TensorNetwork/FastContractionStep.lean | 34 ++++++++++++++ .../CountingSimulation/FastAlgorithm.lean | 44 +++++++++++++++++++ 3 files changed, 79 insertions(+) diff --git a/Complexity.lean b/Complexity.lean index 1dcd33fdff..ce9efdf650 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -899,6 +899,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DirectCont public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContraction public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionMachine +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionStep public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates public import Complexity.Computability.Circuit.Frontier.TensorNetwork.KernelProduct public import Complexity.Computability.Circuit.Frontier.TensorNetwork.LocalDeformation diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionStep.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionStep.lean index c3cb8d2ae5..1ff9edf83f 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionStep.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionStep.lean @@ -32,6 +32,8 @@ differences: the values the call stores are the sums of (D.16) at each node * `Cslib.Circuits.Frontier.FastContraction.stepCorrect_nativeMk`: the step is correct, given the points. +* `Cslib.Circuits.Frontier.FastContraction.wfChildren_nativeMk`: well-formedness passes to the + children. ## References @@ -145,4 +147,36 @@ theorem stepCorrect_nativeMk exact h exact key _ _ hlev hprec +/-- The nodes of positive multiplicity, as grid indices. -/ +theorem mem_nodes {k : ℕ} : k ∈ nodes x ↔ + k < 2 ^ (slots x + 1) ∧ k ≠ 2 ^ slots x ∧ 0 < mult (slots x) (levels x) (step x) k := by + simp [nodes] + +/-- A child of the cursor lies at a node, a point and a group in range. -/ +theorem lt_of_mem_children {c : State} (hc : c ∈ children x) : + c.1 < (nodes x).length ∧ c.2.1 < numPoints x ∧ c.2.2.2 < 64 := by + simp only [children, cursorList, nodeBlock, pointBlock, groupBlock, List.mem_flatMap, + List.mem_range, List.mem_map] at hc + obtain ⟨j, hj, i, hi, β, -, g, hg, rfl⟩ := hc + exact ⟨hj, hi, hg⟩ + +/-- **Well-formedness passes to the native children**: they keep the modulus, and they are +rows-first (`NativeChild.isRowsFirstBipartite_nativeChild`). -/ +theorem wfChildren_nativeMk : WFChildren nativeMk WF := by + intro x hx ha c hc + obtain ⟨hirr, hD⟩ := hx + refine ⟨hirr, ?_⟩ + obtain ⟨hj, -, hg⟩ := lt_of_mem_children hc + have ha' := ha + simp only [active, decide_eq_true_eq] at ha' + obtain ⟨-, -, -, hua, -, -, -⟩ := ha' + have hkmem : (nodes x).getD c.1 0 ∈ nodes x := by + rw [List.getD_eq_getElem _ _ hj]; exact List.getElem_mem hj + obtain ⟨hk, -, hq⟩ := mem_nodes.mp hkmem + have hia : level x ((nodes x).getD c.1 0) < slots x := by + rw [level_eq hk] + exact padicValInt_lt_of_taperMult_pos (by rwa [← mult_eq _ _ hk]) hua + exact isRowsFirstBipartite_nativeChild (fl := x.fl) hD (pow_slots_le_edges_of_active ha) hia + (nodePrec x c.1) _ ((points x).getD c.2.1 []) c.2.2.1 ⟨c.2.2.2, hg⟩ + end Cslib.Circuits.Frontier.FastContraction diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean index f76c342655..9cb5d17ff7 100644 --- a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean @@ -10,6 +10,7 @@ module public import Complexity.Computability.Complexity.Classes.CountingSimulation.Guarded public import Complexity.Computability.Complexity.Classes.CountingSimulation.Admissible public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionMachine +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionStep import Mathlib.Analysis.SpecificLimits.Normed import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat @@ -50,6 +51,12 @@ finish, and the machine falls back to direct summation, at a constant factor. The guard is polynomial-time (`CountingSimulation.fastGuard_polyTime`). +For the native child builder (`FastContraction.nativeMk`), the step, the passage of +well-formedness to the children and the well-formed roots are proved +(`CountingSimulation.contractionAlgorithmOfNative`); there the remaining obligations are the +components of the scheme in polynomial time, the unit differences of the interpolation points and +the resource analysis. + **Remaining obligations** (the hypotheses of `CountingSimulation.contractionAlgorithmOfFast`): the components of the scheme in polynomial time, the correctness of one active call (`FastContraction.StepCorrect`), the passage of well-formedness to the children @@ -61,12 +68,15 @@ components of the scheme in polynomial time, the correctness of one active call * `Cslib.Complexity.CountingSimulation.FastAnalysis`: the resource analysis. * `Cslib.Complexity.CountingSimulation.contractionAlgorithmOfFast`: the contraction algorithm from the remaining obligations. +* `Cslib.Complexity.CountingSimulation.contractionAlgorithmOfNative`: the same for the native child + builder, with fewer obligations. ## Main results * `Cslib.Complexity.CountingSimulation.exists_le_of_fastGuard_eq_false`: admissible networks failing the guard are small. * `Cslib.Complexity.CountingSimulation.fastGuard_polyTime`: the guard is polynomial-time. +* `Cslib.Complexity.CountingSimulation.wf_root`: guarded inputs have well-formed roots. * `Cslib.Complexity.CountingSimulation.fuel_le`: the clock on admissible inputs. * `Cslib.Complexity.CountingSimulation.fastCertificate_le`: the fast machine within the interface's bounds. @@ -623,6 +633,40 @@ noncomputable def contractionAlgorithmOfFast (fun C D => fastCertificate_le hpoly hana C D) (fun _ _ _ hA => hA.2.1) exists_le_of_fastGuard_eq_false +/-- **Guarded inputs have well-formed roots**: the modulus is irreducible modulo `2`, and the +tables as descriptors keep the rows-first ports. -/ +theorem wf_root (P : ℕ) {G : ℕ × ExplicitTensorNetwork ℕ} (hg : fastGuard G = true) : + FastContraction.WF (root (ExplicitTensorNetwork.capPrecision (P, G))) := by + refine ⟨irreducible_modulus _, ?_⟩ + have hrows : G.2.IsRowsFirstBipartite G.1 := by + simp only [fastGuard, decide_eq_true_eq] at hg + exact hg.1 + obtain ⟨h1, h2⟩ := hrows + refine ⟨fun v hv i hi => ?_, fun i hi => ?_⟩ + · simp only [root, ExplicitTensorNetwork.capPrecision, rootNet, List.mem_map] at hv + obtain ⟨w, hw, rfl⟩ := hv + exact h1 w hw i hi + · simp only [root, ExplicitTensorNetwork.capPrecision, rootNet, ← List.map_take, + ← List.map_drop, List.map_map, Function.comp_def] at hi ⊢ + exact h2 i hi + +/-- **The contraction algorithm from the native recursion**, given its remaining obligations: the +components of the scheme in polynomial time, the unit differences of the interpolation points, and +the resource analysis. The step, the passage of well-formedness to the children and the +well-formed roots are proved (`FastContraction.stepCorrect_nativeMk`, +`FastContraction.wfChildren_nativeMk`, `CountingSimulation.wf_root`). -/ +noncomputable def contractionAlgorithmOfNative + (hpoly : (scheme nativeMk).PolyTime encCall encFrame encValue) + (hζ : ∀ x (hirr : Irreducible ((GaloisRingArith.monicOf x.fl).map + (Int.castRingHom (ZMod 2)))), active x = true → + Pairwise fun i j : Fin (points x).length => IsUnit (GaloisRingArith.liftO hirr + ((points x).getD i []) - GaloisRingArith.liftO hirr ((points x).getD j []))) + (hana : FastAnalysis nativeMk) : + ContractionAlgorithm (encPair encNat ExplicitTensorNetwork.encExplicitNetwork) + (fun G => G.2.contraction) ExplicitAdmissible := + contractionAlgorithmOfFast hpoly (stepCorrect_nativeMk hζ) wfChildren_nativeMk + (fun P _ hg => wf_root P hg) hana + end Resources end Cslib.Complexity.CountingSimulation From 8081ab947937de12f1d4a25b8cb4d10e478fe63a Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:04:55 -0400 Subject: [PATCH 093/129] feat(circuit/frontier): binary-digit points have lifts with unit differences MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit isUnit_liftO_sub: lifts of 0/1 lists that differ below fl.length differ by a unit, through AdjoinRoot.integerDigitLift. pairwise_isUnit_liftO_sub_digits: the binary digits of 0, ..., n - 1 (n <= 2 ^ fl.length) give the hζ hypothesis of NativeCall.val_recover_eq_contraction. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/DescribedLift.lean | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean index e0d0e1a0da..decff167be 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean @@ -14,6 +14,8 @@ import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Li import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Membership import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListLookup import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListMap +import Complexity.Computability.PolyTime.GaloisRingUnits +import Complexity.Foundations.NumberTheory.Padics.AdjoinRootDigitLift import Mathlib.Data.List.GetD import Mathlib.Data.List.Sort @@ -30,6 +32,9 @@ for. and reducing the lift modulo `2 ^ p` gives the value of `c` in `A_p` (`GaloisRingArith.reduction_liftO`). This is the canonical lifting of representatives of §D.4.1 (p. 58): a retained vertex is read through the lift of its entry at every later precision. + Lifts of distinct lists with binary coordinates differ by a unit + (`GaloisRingArith.isUnit_liftO_sub`); in particular the binary digits of `0, …, n - 1` are + interpolation points when `n ≤ 2 ^ e` (`GaloisRingArith.pairwise_isUnit_liftO_sub_digits`). * **Rows first.** A described network is *rows-first bipartite* at `r` (`DescribedNetwork.IsRowsFirstBipartite`, the condition of `ExplicitTensorNetwork.IsRowsFirstBipartite`) when every port names an edge and every edge is a @@ -55,6 +60,8 @@ for. ## Main results * `Cslib.GaloisRingArith.reduction_liftO`, `Cslib.GaloisRingArith.mk_liftO`: reducing a lift. +* `Cslib.GaloisRingArith.isUnit_liftO_sub`, + `Cslib.GaloisRingArith.pairwise_isUnit_liftO_sub_digits`: binary lists as interpolation points. * `Cslib.Circuits.Frontier.DescribedNetwork.incident_eq`: the vertices at an edge. * `Cslib.Circuits.Frontier.DescribedNetwork.contraction_toNetwork`, `Cslib.Circuits.Frontier.DescribedNetwork.reduction_contraction_toNetwork`: the contraction. @@ -115,6 +122,73 @@ theorem mk_liftO (p : ℕ) (c : List ℕ) : Cslib.Circuits.Frontier.reductionLift (monic_monicOf fl) hirr p (val p fl c) := by rw [← reduction_liftO hirr p, Cslib.Circuits.Frontier.reductionLift_reduction] +/-! ### Binary lists as interpolation points -/ + +/-- A lift of a list with binary coordinates is a digit lift. -/ +private theorem liftO_eq_integerDigitLift {c : List ℕ} (hc : ∀ i < fl.length, c.getD i 0 < 2) : + liftO hirr c = AdjoinRoot.integerDigitLift (monic_monicOf fl) hirr + fun i => ((c.getD i 0 : ℕ) : ZMod 2) := by + apply Subtype.ext + rw [AdjoinRoot.coe_integerDigitLift, Fin.sum_univ_eq_sum_range + (fun i => ((((c.getD i 0 : ℕ) : ZMod 2)).val : AdjoinRoot ((monicOf fl).map + (Int.castRingHom ℚ))) * AdjoinRoot.root _ ^ i), natDegree_monicOf, liftO] + push_cast + refine Finset.sum_congr rfl fun i hi => ?_ + rw [ZMod.val_natCast, Nat.mod_eq_of_lt (hc i (mem_range.mp hi))] + exact congrArg (_ * · ^ i) (AdjoinRoot.map_root _ _ _ _) + +/-- **Lifts of distinct binary lists differ by a unit.** If two lists have binary coordinates +below `e = fl.length` and differ at one of them, their lifts to `O` differ by a unit: they are +digit lifts (`AdjoinRoot.isUnit_integerDigitLift_sub`). -/ +theorem isUnit_liftO_sub {c c' : List ℕ} (hc : ∀ i < fl.length, c.getD i 0 < 2) + (hc' : ∀ i < fl.length, c'.getD i 0 < 2) + (hne : ∃ i < fl.length, c.getD i 0 ≠ c'.getD i 0) : + IsUnit (liftO hirr c - liftO hirr c') := by + rw [liftO_eq_integerDigitLift hirr hc, liftO_eq_integerDigitLift hirr hc'] + refine AdjoinRoot.isUnit_integerDigitLift_sub _ hirr fun h => ?_ + obtain ⟨i, hi, hne⟩ := hne + have h2 := congrArg ZMod.val (congrFun h ⟨i, by rwa [natDegree_monicOf]⟩) + rw [ZMod.val_natCast, ZMod.val_natCast, Nat.mod_eq_of_lt (hc i hi), + Nat.mod_eq_of_lt (hc' i hi)] at h2 + exact hne h2 + +/-- Coordinate `i < e` of the binary digits of `k` below `e`. -/ +theorem getD_map_testBit (e k i : ℕ) (hi : i < e) : + ((List.range e).map fun j => if k.testBit j then 1 else 0).getD i 0 = + if k.testBit i then 1 else 0 := by + rw [List.getD_eq_getElem _ _ (by simpa using hi), List.getElem_map, List.getElem_range] + +/-- **Binary-digit points are interpolation points.** The lists of the binary digits below +`e = fl.length` of `0, …, n - 1`, for `n ≤ 2 ^ e`, have lifts to `O` with unit differences. -/ +theorem pairwise_isUnit_liftO_sub_digits {n : ℕ} (hn : n ≤ 2 ^ fl.length) : + Pairwise fun i j : Fin ((List.range n).map fun k => + (List.range fl.length).map fun j => if k.testBit j then 1 else 0).length => + IsUnit (liftO hirr (((List.range n).map fun k => + (List.range fl.length).map fun j => if k.testBit j then 1 else 0).getD i []) - + liftO hirr (((List.range n).map fun k => + (List.range fl.length).map fun j => if k.testBit j then 1 else 0).getD j [])) := by + intro i j hij + have hi : (i : ℕ) < n := by simpa using i.2 + have hj : (j : ℕ) < n := by simpa using j.2 + rw [List.getD_eq_getElem _ _ i.2, List.getD_eq_getElem _ _ j.2, List.getElem_map, + List.getElem_map, List.getElem_range, List.getElem_range] + have hbin (k : ℕ) : ∀ b < fl.length, + ((List.range fl.length).map fun j => if k.testBit j then 1 else 0).getD b 0 < 2 := by + intro b hb + rw [getD_map_testBit _ _ _ hb] + split <;> omega + refine isUnit_liftO_sub hirr (hbin _) (hbin _) ?_ + by_contra hall + simp only [not_exists, not_and, not_not] at hall + refine hij (Fin.ext (Nat.eq_of_testBit_eq fun b => ?_)) + rcases lt_or_ge b fl.length with hb | hb + · have := hall b hb + rw [getD_map_testBit _ _ _ hb, getD_map_testBit _ _ _ hb] at this + revert this + cases (i : ℕ).testBit b <;> cases (j : ℕ).testBit b <;> simp + · have h2 : 2 ^ fl.length ≤ 2 ^ b := Nat.pow_le_pow_right (by norm_num) hb + rw [Nat.testBit_lt_two_pow (by omega), Nat.testBit_lt_two_pow (by omega)] + end Cslib.GaloisRingArith namespace Cslib.Circuits.Frontier From 5d0a92db6813e6d56a8c3089c30560553d6a7681 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:04:55 -0400 Subject: [PATCH 094/129] feat(circuit/frontier): the native child in polynomial time, and its shape computableInPolyTime_nativeChild and computableInPolyTime_childRows in composition form over the native encodings, with their specializations to the native parent packing (numPackets_nativeParentPacking_le bounds its packets by M (2 ^ a) ^ 12). The shape lemmas numEdges_nativeChild_le, length_vertices_nativeChild and mem_vertices_nativeChild support node-size bounds along the recursion. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../TensorNetwork/NativeChildPolyTime.lean | 733 ++++++++++++++++++ .../TensorNetworkNativeChildPolyTime.lean | 117 +++ 3 files changed, 851 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChildPolyTime.lean create mode 100644 ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeChildPolyTime.lean diff --git a/Complexity.lean b/Complexity.lean index 66e3c77e00..d8c1268a0e 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -902,6 +902,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.LocalDefor public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeCall public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeChild +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeChildPolyTime public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeRecovery public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEntry public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEvaluator diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChildPolyTime.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChildPolyTime.lean new file mode 100644 index 0000000000..d55a5603ee --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChildPolyTime.lean @@ -0,0 +1,733 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeChild +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.BitString +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Membership +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Binary +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Sum +import Complexity.Computability.Machines.Turing.MultiTape.Internal.EqualityPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListBinaryLookup +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListLookup +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListMap +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeNatFrontends +import Mathlib.Data.List.GetD + +/-! +# The native child networks in polynomial time + +The native child of group `g` of an active call of the contraction recursion of [OpenAI2026ETH] +(§D.4.6, p. 63), `NativeChild.nativeChild`, and its number of rows, `NativeChild.childRows`, are +computable in polynomial time over the encodings of the native recursion. The counts are unary, +the node `t` is in binary sign-magnitude form, and the lists are binary, except the exponents +`β`, which are unary as in a packet descriptor. The parent is encoded by +`DescribedNetwork.encDescribed VertexDesc.enc` and its packing by +`PacketPacking.encNativePacking`. + +The theorems are stated in composition form, for components computed from any input. Besides the +components themselves, they ask for `2 ^ a`, `2 ^ (i + 1)` and the number of packets in unary. +Every packet vertex stores `2 ^ a` and `2 ^ (i + 1)` in unary in its context, and the child has +one packet vertex per packet. For the native parent packing +(`NativeChild.nativeParentPacking`), the packing itself and its number of packets, at most +`M (2 ^ a) ^ 12` for `M` edges, are computed here, with the number of rows in unary. + +**The pieces.** +* The groups of the vertices are looked up in the label words, one vertex at a time. +* The child edges are `ChildNumbering.childEdgeList`, and the ports of a packet are + `ChildNumbering.childPorts`. +* A retained vertex renumbers its ports by counting the earlier edges with an end in its group. + `NativeChild.portRank_eq_countP` puts that count in a form without a unary loop over the port. +* A packet vertex tags each port with the slot of the parent edge, read off the ports of the + group. + +**The shape of the child**, for size bounds along the recursion: +* the child has at most twice as many edges as the parent; +* it has `childRows + P` vertices; +* each vertex is either a parent vertex, with its descriptor and its number of ports, or the + vertex of a packet `q`, with its context, its tags and its slice of `β`. + +## Main results + +* `Cslib.Circuits.Frontier.NativeChild.computableInPolyTime_nativeChild`, + `Cslib.Circuits.Frontier.NativeChild.computableInPolyTime_childRows`: the native child and its + number of rows are polynomial-time. +* `Cslib.Circuits.Frontier.NativeChild.computableInPolyTime_nativeChild_nativeParentPacking`, + `Cslib.Circuits.Frontier.NativeChild.computableInPolyTime_childRows_nativeParentPacking`: the + same for the native parent packing. +* `Cslib.Circuits.Frontier.NativeChild.numPackets_nativeParentPacking_le`: the number of packets. +* `Cslib.Circuits.Frontier.NativeChild.numEdges_nativeChild_le`, + `Cslib.Circuits.Frontier.NativeChild.length_vertices_nativeChild`, + `Cslib.Circuits.Frontier.NativeChild.mem_vertices_nativeChild`: the shape of the child. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.6, p. 63. +-/ + +public section + +namespace Cslib.Circuits.Frontier.NativeChild + +open Turing.MultiTapeTM ChildNumbering NativePacket + +/-- **The rank of a port without a loop over the port**: the edges before `e` with an end in group +`g`, the edges past the list counting as `(0, 0)`. -/ +theorem portRank_eq_countP (groups : List ℕ) (g : ℕ) (ends : List (ℕ × ℕ)) (sel : ℕ × ℕ → ℕ) + (e : ℕ) : + portRank groups g ends sel e = + (List.range (min ends.length e)).countP + (fun e' => decide (groups.getD (sel (ends.getD e' (0, 0))) 0 = g)) + + (e - ends.length) * (if groups.getD (sel (0, 0)) 0 = g then 1 else 0) := by + induction e with + | zero => simp [portRank] + | succ e ih => + have hstep : portRank groups g ends sel (e + 1) = portRank groups g ends sel e + + if groups.getD (sel (ends.getD e (0, 0))) 0 = g then 1 else 0 := by + simp only [portRank, List.range_succ, List.countP_append, List.countP_singleton, + decide_eq_true_eq] + rw [hstep, ih] + rcases lt_or_ge e ends.length with h | h + · rw [min_eq_right h.le, min_eq_right (show e + 1 ≤ ends.length by omega), List.range_succ, + List.countP_append, List.countP_singleton, Nat.sub_eq_zero_of_le h.le, + Nat.sub_eq_zero_of_le (show e + 1 ≤ ends.length by omega)] + simp only [zero_mul, add_zero, decide_eq_true_eq] + · rw [min_eq_left h, min_eq_left (show ends.length ≤ e + 1 by omega), + List.getD_eq_default _ _ h, show e + 1 - ends.length = e - ends.length + 1 by omega] + split_ifs <;> ring + +section PolyTime + +variable {δ : Type} {encIn : δ ↪ List Bool} {groups : δ → List ℕ} {g : δ → ℕ} + {ends : δ → List (ℕ × ℕ)} + +/-- Whether the end `sel` of the edge `e`, in unary, is in group `g`. -/ +private theorem test_polyTime (sel : ℕ × ℕ → ℕ) + (hsel : ComputableInPolyTime sel (encPair encNat encNat) encNat) + (hgroups : ComputableInPolyTime groups encIn (encList encNat)) + (hg : ComputableInPolyTime g encIn encNat) + (hends : ComputableInPolyTime ends encIn (encList (encPair encNat encNat))) : + ComputableInPolyTime (fun p : δ × ℕ => + decide ((groups p.1).getD (sel ((ends p.1).getD p.2 (0, 0))) 0 = g p.1)) + (encPair encIn encUnary) encBool := by + have hentry : ComputableInPolyTime (fun p : δ × ℕ => (ends p.1).getD p.2 (0, 0)) + (encPair encIn encUnary) (encPair encNat encNat) := + (ComputableInPolyTime.listGetD (0, 0)).comp + ((hends.comp (ComputableInPolyTime.fst _ _)).prod (ComputableInPolyTime.snd _ _)) + exact ComputableInPolyTime.decide_eq' + (ComputableInPolyTime.listGetD_binary 0 (hgroups.comp (ComputableInPolyTime.fst _ _)) + (hsel.comp hentry)) + (hg.comp (ComputableInPolyTime.fst _ _)) + +/-- **The ports of a group are polynomial-time.** -/ +theorem computableInPolyTime_portEdges (sel : ℕ × ℕ → ℕ) + (hsel : ComputableInPolyTime sel (encPair encNat encNat) encNat) + (hgroups : ComputableInPolyTime groups encIn (encList encNat)) + (hg : ComputableInPolyTime g encIn encNat) + (hends : ComputableInPolyTime ends encIn (encList (encPair encNat encNat))) : + ComputableInPolyTime (fun x => portEdges (groups x) (g x) (ends x) sel) encIn + (encList encUnary) := + ComputableInPolyTime.filterWithInput + (test := fun x e => decide ((groups x).getD (sel ((ends x).getD e (0, 0))) 0 = g x)) + (ComputableInPolyTime.range.comp ((ComputableInPolyTime.length_list _).comp hends)) + (test_polyTime sel hsel hgroups hg hends) + +/-- **The rank of a port is polynomial-time**, with the port in binary. -/ +theorem computableInPolyTime_portRank (sel : ℕ × ℕ → ℕ) + (hsel : ComputableInPolyTime sel (encPair encNat encNat) encNat) + (hgroups : ComputableInPolyTime groups encIn (encList encNat)) + (hg : ComputableInPolyTime g encIn encNat) + (hends : ComputableInPolyTime ends encIn (encList (encPair encNat encNat))) + {e : δ → ℕ} (he : ComputableInPolyTime e encIn encNat) : + ComputableInPolyTime (fun x => portRank (groups x) (g x) (ends x) sel (e x)) encIn encNat := by + have hlen : ComputableInPolyTime (fun x => (ends x).length) encIn encUnary := + (ComputableInPolyTime.length_list _).comp hends + have hcount := ComputableInPolyTime.countPWithInput + (test := fun x e' => decide ((groups x).getD (sel ((ends x).getD e' (0, 0))) 0 = g x)) + (ComputableInPolyTime.range.comp (ComputableInPolyTime.binary_min_unary' hlen he)) + (test_polyTime sel hsel hgroups hg hends) + have hdefault : ComputableInPolyTime (fun x => decide ((groups x).getD (sel (0, 0)) 0 = g x)) + encIn encBool := + ComputableInPolyTime.decide_eq' + (ComputableInPolyTime.listGetD_binary 0 hgroups (ComputableInPolyTime.const (sel (0, 0)))) + hg + have hextra : ComputableInPolyTime (fun x => + (e x - (ends x).length) * (if decide ((groups x).getD (sel (0, 0)) 0 = g x) then 1 else 0)) + encIn encNat := + ComputableInPolyTime.binary_mul' + (ComputableInPolyTime.binary_sub' he (ComputableInPolyTime.unary_to_binary.comp hlen)) + (ComputableInPolyTime.ite hdefault (ComputableInPolyTime.const 1) + (ComputableInPolyTime.const 0)) + refine (ComputableInPolyTime.binary_add.comp + ((ComputableInPolyTime.unary_to_binary.comp hcount).prod hextra)).of_enc_eq fun x => ?_ + rw [portRank_eq_countP] + simp only [decide_eq_true_eq, Function.comp_apply] + +/-- **The child edge of a port is polynomial-time**, with the vertex and the port in binary. -/ +theorem computableInPolyTime_childEdgeOf {r v e : δ → ℕ} + (hr : ComputableInPolyTime r encIn encNat) + (hgroups : ComputableInPolyTime groups encIn (encList encNat)) + (hg : ComputableInPolyTime g encIn encNat) + (hends : ComputableInPolyTime ends encIn (encList (encPair encNat encNat))) + (hv : ComputableInPolyTime v encIn encNat) (he : ComputableInPolyTime e encIn encNat) : + ComputableInPolyTime (fun x => childEdgeOf (r x) (groups x) (g x) (ends x) (v x) (e x)) + encIn encNat := by + have hR : ComputableInPolyTime (fun x => (portEdges (groups x) (g x) (ends x) Prod.fst).length) + encIn encNat := + ComputableInPolyTime.unary_to_binary.comp ((ComputableInPolyTime.length_list _).comp + (computableInPolyTime_portEdges Prod.fst (ComputableInPolyTime.fst _ _) hgroups hg hends)) + refine (ComputableInPolyTime.ite (ComputableInPolyTime.binary_lt' hv hr) + (computableInPolyTime_portRank Prod.fst (ComputableInPolyTime.fst _ _) hgroups hg hends he) + (ComputableInPolyTime.binary_add.comp (hR.prod (computableInPolyTime_portRank Prod.snd + (ComputableInPolyTime.snd _ _) hgroups hg hends he)))).of_enc_eq fun x => ?_ + simp only [childEdgeOf, decide_eq_true_eq, Function.comp_apply] + +/-- **The parent edge of a child edge is polynomial-time**, with the child edge in binary and the +parent edge in unary. -/ +theorem computableInPolyTime_parentEdge {j : δ → ℕ} + (hgroups : ComputableInPolyTime groups encIn (encList encNat)) + (hg : ComputableInPolyTime g encIn encNat) + (hends : ComputableInPolyTime ends encIn (encList (encPair encNat encNat))) + (hj : ComputableInPolyTime j encIn encNat) : + ComputableInPolyTime (fun x => parentEdge (groups x) (g x) (ends x) (j x)) encIn + encUnary := by + have hfst := computableInPolyTime_portEdges Prod.fst (ComputableInPolyTime.fst _ _) hgroups hg + hends + have hsnd := computableInPolyTime_portEdges Prod.snd (ComputableInPolyTime.snd _ _) hgroups hg + hends + have hR : ComputableInPolyTime (fun x => (portEdges (groups x) (g x) (ends x) Prod.fst).length) + encIn encNat := + ComputableInPolyTime.unary_to_binary.comp ((ComputableInPolyTime.length_list _).comp hfst) + refine (ComputableInPolyTime.ite (ComputableInPolyTime.binary_lt' hj hR) + (ComputableInPolyTime.listGetD_binary (encA := encUnary) 0 hfst hj) + (ComputableInPolyTime.listGetD_binary (encA := encUnary) 0 hsnd + (ComputableInPolyTime.binary_sub' hj hR))).of_enc_eq fun x => ?_ + simp only [parentEdge, decide_eq_true_eq] + +/-- **The groups of the vertices are polynomial-time.** -/ +theorem computableInPolyTime_groupsOf {r i k : δ → ℕ} + {words : δ → List (List ℕ) × List (List ℕ)} + (hr : ComputableInPolyTime r encIn encNat) (hi : ComputableInPolyTime i encIn encUnary) + (hk : ComputableInPolyTime k encIn encUnary) + (hwords : ComputableInPolyTime words encIn + (encPair (encList (encList encNat)) (encList (encList encNat)))) : + ComputableInPolyTime (fun x => groupsOf (r x) (i x) (k x) (words x)) encIn + (encList encNat) := by + let e := encPair encIn encUnary + have hx : ComputableInPolyTime (fun p : δ × ℕ => p.1) e encIn := ComputableInPolyTime.fst _ _ + have hv : ComputableInPolyTime (fun p : δ × ℕ => p.2) e encNat := + ComputableInPolyTime.unary_to_binary.comp (ComputableInPolyTime.snd _ _) + have hw₁ := (ComputableInPolyTime.fst _ _).comp (hwords.comp hx) + have hw₂ := (ComputableInPolyTime.snd _ _).comp (hwords.comp hx) + have hrow : ComputableInPolyTime (fun p : δ × ℕ => + (((words p.1).1.getD p.2 []).getD (i p.1) 0)) e encNat := + (ComputableInPolyTime.listGetD (encA := encNat) (encL := encList encNat) 0).comp + (((ComputableInPolyTime.listGetD (encA := encList encNat) (encL := encList (encList encNat)) + []).comp (hw₁.prod (ComputableInPolyTime.snd _ _))).prod (hi.comp hx)) + have hcol : ComputableInPolyTime (fun p : δ × ℕ => + (((words p.1).2.getD (p.2 - r p.1) []).getD (i p.1) 0)) e encNat := + (ComputableInPolyTime.listGetD (encA := encNat) (encL := encList encNat) 0).comp + ((ComputableInPolyTime.listGetD_binary (encA := encList encNat) [] hw₂ + (ComputableInPolyTime.binary_sub' hv (hr.comp hx))).prod (hi.comp hx)) + have h := ComputableInPolyTime.mapWithInput + (f := fun x v => if decide (v < r x) then ((words x).1.getD v []).getD (i x) 0 + else ((words x).2.getD (v - r x) []).getD (i x) 0) + (ComputableInPolyTime.range.comp hk) + (ComputableInPolyTime.ite (ComputableInPolyTime.binary_lt' hv (hr.comp hx)) hrow hcol) + refine h.of_enc_eq fun x => ?_ + simp only [groupsOf, decide_eq_true_eq, Function.comp_apply] + +/-- Taking a unary number of elements of a list. -/ +private theorem listTake {α : Type} {encA : α ↪ List Bool} {l : δ → List α} {n : δ → ℕ} + (hl : ComputableInPolyTime l encIn (encList encA)) + (hn : ComputableInPolyTime n encIn encUnary) : + ComputableInPolyTime (fun x => (l x).take (n x)) encIn (encList encA) := by + have hrev := ComputableInPolyTime.listReverseWithInput hl + have hdrop := (ComputableInPolyTime.dropUnary (encA := encA) (encL := encList encA)).comp + (hrev.prod (ComputableInPolyTime.unary_sub' + ((ComputableInPolyTime.length_list _).comp hl) hn)) + refine (ComputableInPolyTime.listReverseWithInput hdrop).of_enc_eq fun x => ?_ + change encList encA ((l x).take (n x)) = + encList encA (((l x).reverse.drop ((l x).length - n x)).reverse) + rw [List.drop_reverse, List.reverse_reverse] + rcases le_total (l x).length (n x) with h | h + · rw [Nat.sub_eq_zero_of_le h, Nat.sub_zero, List.take_length, List.take_of_length_le h] + · rw [Nat.sub_sub_self h] + +/-- Counting a group among the groups, in binary. -/ +private theorem count_polyTime (hgroups : ComputableInPolyTime groups encIn (encList encNat)) + (hg : ComputableInPolyTime g encIn encNat) : + ComputableInPolyTime (fun x => (groups x).count (g x)) encIn encNat := by + have h := ComputableInPolyTime.countPWithInput (test := fun x v => decide (v = g x)) hgroups + (ComputableInPolyTime.decide_eq' (ComputableInPolyTime.snd _ _) + (hg.comp (ComputableInPolyTime.fst _ _))) + refine (ComputableInPolyTime.unary_to_binary.comp h).of_enc_eq fun x => ?_ + rw [List.count] + congr 2 + +/-- **The retained vertices**: the vertices of group `g` with their renumbered ports. -/ +private theorem retained_polyTime {r : δ → ℕ} {V : δ → List (List ℕ × VertexDesc)} + (hr : ComputableInPolyTime r encIn encNat) + (hV : ComputableInPolyTime V encIn (encList (encPair (encList encNat) VertexDesc.enc))) + (hgroups : ComputableInPolyTime groups encIn (encList encNat)) + (hg : ComputableInPolyTime g encIn encNat) + (hends : ComputableInPolyTime ends encIn (encList (encPair encNat encNat))) : + ComputableInPolyTime (fun x => + ((List.range (V x).length).filter fun v => decide ((groups x).getD v 0 = g x)).map fun v => + (((V x).getD v defaultVertex).1.map (childEdgeOf (r x) (groups x) (g x) (ends x) v), + ((V x).getD v defaultVertex).2)) + encIn (encList (encPair (encList encNat) VertexDesc.enc)) := by + let e₁ := encPair encIn encUnary + have hx : ComputableInPolyTime (fun p : δ × ℕ => p.1) e₁ encIn := ComputableInPolyTime.fst _ _ + have hy : ComputableInPolyTime (fun p : δ × ℕ => p.2) e₁ encUnary := + ComputableInPolyTime.snd _ _ + have hfilter : ComputableInPolyTime (fun x => (List.range (V x).length).filter + fun v => decide ((groups x).getD v 0 = g x)) encIn (encList encUnary) := + ComputableInPolyTime.filterWithInput (test := fun x v => decide ((groups x).getD v 0 = g x)) + (ComputableInPolyTime.range.comp ((ComputableInPolyTime.length_list _).comp hV)) + (ComputableInPolyTime.decide_eq' + ((ComputableInPolyTime.listGetD (encA := encNat) (encL := encList encNat) 0).comp + ((hgroups.comp hx).prod hy)) (hg.comp hx)) + have hvtx : ComputableInPolyTime (fun p : δ × ℕ => (V p.1).getD p.2 defaultVertex) e₁ + (encPair (encList encNat) VertexDesc.enc) := + (ComputableInPolyTime.listGetD (encA := encPair (encList encNat) VertexDesc.enc) + (encL := encList (encPair (encList encNat) VertexDesc.enc)) defaultVertex).comp + ((hV.comp hx).prod hy) + have hxx : ComputableInPolyTime (fun q : (δ × ℕ) × ℕ => q.1.1) (encPair e₁ encNat) encIn := + hx.comp (ComputableInPolyTime.fst _ _) + have hr' : ComputableInPolyTime (fun q : (δ × ℕ) × ℕ => r q.1.1) (encPair e₁ encNat) encNat := + hr.comp hxx + have hgroups' : ComputableInPolyTime (fun q : (δ × ℕ) × ℕ => groups q.1.1) (encPair e₁ encNat) + (encList encNat) := hgroups.comp hxx + have hg' : ComputableInPolyTime (fun q : (δ × ℕ) × ℕ => g q.1.1) (encPair e₁ encNat) encNat := + hg.comp hxx + have hends' : ComputableInPolyTime (fun q : (δ × ℕ) × ℕ => ends q.1.1) (encPair e₁ encNat) + (encList (encPair encNat encNat)) := hends.comp hxx + have hv' : ComputableInPolyTime (fun q : (δ × ℕ) × ℕ => q.1.2) (encPair e₁ encNat) encNat := + ComputableInPolyTime.unary_to_binary.comp (hy.comp (ComputableInPolyTime.fst _ _)) + have he' : ComputableInPolyTime (fun q : (δ × ℕ) × ℕ => q.2) (encPair e₁ encNat) encNat := + ComputableInPolyTime.snd _ _ + have hce : ComputableInPolyTime (fun q : (δ × ℕ) × ℕ => + childEdgeOf (r q.1.1) (groups q.1.1) (g q.1.1) (ends q.1.1) q.1.2 q.2) + (encPair e₁ encNat) encNat := + computableInPolyTime_childEdgeOf hr' hgroups' hg' hends' hv' he' + have hports : ComputableInPolyTime (fun p : δ × ℕ => + ((V p.1).getD p.2 defaultVertex).1.map (childEdgeOf (r p.1) (groups p.1) (g p.1) + (ends p.1) p.2)) e₁ (encList encNat) := + ComputableInPolyTime.mapWithInput + (f := fun p e => childEdgeOf (r p.1) (groups p.1) (g p.1) (ends p.1) p.2 e) + ((ComputableInPolyTime.fst _ _).comp hvtx) hce + exact ComputableInPolyTime.mapWithInput + (f := fun x v => (((V x).getD v defaultVertex).1.map + (childEdgeOf (r x) (groups x) (g x) (ends x) v), ((V x).getD v defaultVertex).2)) + hfilter (hports.prod ((ComputableInPolyTime.snd _ _).comp hvtx)) + +variable {fl ζ β : δ → List ℕ} {a i pt r : δ → ℕ} {t : δ → ℤ} + {D : δ → DescribedNetwork VertexDesc} + {pk : δ → (List (List ℕ) × List (List ℕ)) × ℕ × List (ℕ × ℕ)} + +/-- **The context of a packet** is polynomial-time, with the packet in unary. -/ +private theorem packetCtx_polyTime (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (ha : ComputableInPolyTime a encIn encUnary) + (hL : ComputableInPolyTime (fun x => 2 ^ a x) encIn encUnary) + (hi : ComputableInPolyTime i encIn encUnary) + (hT : ComputableInPolyTime (fun x => 2 ^ (i x + 1)) encIn encUnary) + (hpt : ComputableInPolyTime pt encIn encUnary) (ht : ComputableInPolyTime t encIn encIntBin) + (hζ : ComputableInPolyTime ζ encIn (encList encNat)) + (hg : ComputableInPolyTime g encIn encNat) : + ComputableInPolyTime (fun p : δ × ℕ => + packetCtx (fl p.1) (a p.1) (i p.1) (pt p.1) (t p.1) (ζ p.1) (g p.1) p.2) + (encPair encIn encUnary) PacketCtx.enc := by + let e₁ := encPair encIn encUnary + have hx : ComputableInPolyTime (fun p : δ × ℕ => p.1) e₁ encIn := ComputableInPolyTime.fst _ _ + have hsym : ComputableInPolyTime (fun p : δ × ℕ => + PacketPacking.packetSymbols (a p.1) 64 p.2 (i p.1)) e₁ (encPair encNat encNat) := + PacketPacking.computableInPolyTime_packetSymbols (ha.comp hx) (ComputableInPolyTime.const 64) + (ComputableInPolyTime.unary_to_binary.comp (ComputableInPolyTime.snd _ _)) (hi.comp hx) + have hs₁ := (ComputableInPolyTime.fst _ _).comp hsym + have hs₂ := (ComputableInPolyTime.snd _ _).comp hsym + have htuple : ComputableInPolyTime (fun p : δ × ℕ => + PacketCtx.equivProd (packetCtx (fl p.1) (a p.1) (i p.1) (pt p.1) (t p.1) (ζ p.1) (g p.1) p.2)) + e₁ PacketCtx.encProd := + ((hpt.comp hx).prod (hfl.comp hx)).prod (((ha.comp hx).prod (hL.comp hx)).prod + (((hi.comp hx).prod (hT.comp hx)).prod ((ht.comp hx).prod ((hpt.comp hx).prod + (((ComputableInPolyTime.decide_eq' hs₁ (hg.comp hx)).prod + ((ComputableInPolyTime.decide_eq' hs₂ (hg.comp hx)).prod + (ComputableInPolyTime.decide_not (ComputableInPolyTime.decide_eq' hs₁ hs₂)))).prod + ((ComputableInPolyTime.const []).prod (hζ.comp hx))))))) + have hsymm : ComputableInPolyTime PacketCtx.equivProd.symm PacketCtx.encProd PacketCtx.enc := + ComputableInPolyTime.of_eq fun y => by + rw [PacketCtx.enc, encOfEquiv_apply, Equiv.apply_symm_apply] + exact (hsymm.comp htuple).of_enc_eq fun p => by + rw [Function.comp_apply, Equiv.symm_apply_apply] + +/-- **The packet vertices**: one per packet, with its ports, their tags, its context and its slice +of the exponents. -/ +private theorem packetVertices_polyTime {P : δ → ℕ} {cedges : δ → List (ℕ × ℕ)} + {place : δ → List (ℕ × ℕ)} + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (ha : ComputableInPolyTime a encIn encUnary) + (hL : ComputableInPolyTime (fun x => 2 ^ a x) encIn encUnary) + (hi : ComputableInPolyTime i encIn encUnary) + (hT : ComputableInPolyTime (fun x => 2 ^ (i x + 1)) encIn encUnary) + (hpt : ComputableInPolyTime pt encIn encUnary) (ht : ComputableInPolyTime t encIn encIntBin) + (hζ : ComputableInPolyTime ζ encIn (encList encNat)) + (hβ : ComputableInPolyTime β encIn (encList encUnary)) + (hg : ComputableInPolyTime g encIn encNat) + (hgroups : ComputableInPolyTime groups encIn (encList encNat)) + (hends : ComputableInPolyTime ends encIn (encList (encPair encNat encNat))) + (hP : ComputableInPolyTime P encIn encUnary) + (hcedges : ComputableInPolyTime cedges encIn (encList (encPair encNat encNat))) + (hplace : ComputableInPolyTime place encIn (encList (encPair encNat encNat))) : + ComputableInPolyTime (fun x => (List.range (P x)).map fun q => + (childPorts (cedges x) ((groups x).count (g x) + q), + (Sum.inr (packetCtx (fl x) (a x) (i x) (pt x) (t x) (ζ x) (g x) q, + (childPorts (cedges x) ((groups x).count (g x) + q)).map fun j => + (decide (j < (portEdges (groups x) (g x) (ends x) Prod.fst).length), + ((place x).getD (parentEdge (groups x) (g x) (ends x) j) (0, 0)).2), + betaSlice (i x) (pt x) (β x) q) : VertexDesc))) + encIn (encList (encPair (encList encNat) VertexDesc.enc)) := by + let e₁ := encPair encIn encUnary + have hx : ComputableInPolyTime (fun p : δ × ℕ => p.1) e₁ encIn := ComputableInPolyTime.fst _ _ + have hy : ComputableInPolyTime (fun p : δ × ℕ => p.2) e₁ encUnary := + ComputableInPolyTime.snd _ _ + -- The ports. + have hpq : ComputableInPolyTime (fun p : δ × ℕ => + childPorts (cedges p.1) ((groups p.1).count (g p.1) + p.2)) e₁ (encList encUnary) := + computableInPolyTime_childPorts.comp ((hcedges.comp hx).prod + (ComputableInPolyTime.binary_add.comp (((count_polyTime hgroups hg).comp hx).prod + (ComputableInPolyTime.unary_to_binary.comp hy)))) + have hpqb : ComputableInPolyTime (fun p : δ × ℕ => + childPorts (cedges p.1) ((groups p.1).count (g p.1) + p.2)) e₁ (encList encNat) := + (ComputableInPolyTime.mapWithInput (f := fun _ j => j) hpq + (ComputableInPolyTime.unary_to_binary.comp (ComputableInPolyTime.snd _ _))).of_enc_eq + fun p => by rw [List.map_id'] + -- The tags of the ports. + have hR : ComputableInPolyTime (fun x => (portEdges (groups x) (g x) (ends x) Prod.fst).length) + encIn encUnary := + (ComputableInPolyTime.length_list _).comp (computableInPolyTime_portEdges Prod.fst + (ComputableInPolyTime.fst _ _) hgroups hg hends) + have hxu : ComputableInPolyTime (fun q : (δ × ℕ) × ℕ => q.1.1) (encPair e₁ encUnary) encIn := + hx.comp (ComputableInPolyTime.fst _ _) + have hgroups' : ComputableInPolyTime (fun q : (δ × ℕ) × ℕ => groups q.1.1) (encPair e₁ encUnary) + (encList encNat) := hgroups.comp hxu + have hg' : ComputableInPolyTime (fun q : (δ × ℕ) × ℕ => g q.1.1) (encPair e₁ encUnary) encNat := + hg.comp hxu + have hends' : ComputableInPolyTime (fun q : (δ × ℕ) × ℕ => ends q.1.1) (encPair e₁ encUnary) + (encList (encPair encNat encNat)) := hends.comp hxu + have hj' : ComputableInPolyTime (fun q : (δ × ℕ) × ℕ => q.2) (encPair e₁ encUnary) encNat := + ComputableInPolyTime.unary_to_binary.comp (ComputableInPolyTime.snd _ _) + have hpe : ComputableInPolyTime (fun q : (δ × ℕ) × ℕ => + parentEdge (groups q.1.1) (g q.1.1) (ends q.1.1) q.2) (encPair e₁ encUnary) encUnary := + computableInPolyTime_parentEdge hgroups' hg' hends' hj' + have htag : ComputableInPolyTime (fun q : (δ × ℕ) × ℕ => + (decide (q.2 < (portEdges (groups q.1.1) (g q.1.1) (ends q.1.1) Prod.fst).length), + ((place q.1.1).getD (parentEdge (groups q.1.1) (g q.1.1) (ends q.1.1) q.2) (0, 0)).2)) + (encPair e₁ encUnary) (encPair encBool encNat) := + (ComputableInPolyTime.unary_lt' (ComputableInPolyTime.snd _ _) (hR.comp hxu)).prod + ((ComputableInPolyTime.snd _ _).comp + ((ComputableInPolyTime.listGetD (encA := encPair encNat encNat) + (encL := encList (encPair encNat encNat)) (0, 0)).comp ((hplace.comp hxu).prod hpe))) + have htags := ComputableInPolyTime.mapWithInput + (f := fun p j => (decide (j < (portEdges (groups p.1) (g p.1) (ends p.1) Prod.fst).length), + ((place p.1).getD (parentEdge (groups p.1) (g p.1) (ends p.1) j) (0, 0)).2)) hpq htag + -- The exponents. + have hTp := ComputableInPolyTime.unary_mul' hT hpt + have hdrop : ComputableInPolyTime (fun p : δ × ℕ => + (β p.1).drop (p.2 * (2 ^ (i p.1 + 1) * pt p.1))) e₁ (encList encUnary) := + (ComputableInPolyTime.dropUnary (encA := encUnary) (encL := encList encUnary)).comp + ((hβ.comp hx).prod (ComputableInPolyTime.unary_mul' hy (hTp.comp hx))) + have hslice : ComputableInPolyTime (fun p : δ × ℕ => betaSlice (i p.1) (pt p.1) (β p.1) p.2) + e₁ (encList encUnary) := listTake hdrop (hTp.comp hx) + have hpacket := hpqb.prod ((ComputableInPolyTime.sumInr (encList encNat) encPacketDesc).comp + ((packetCtx_polyTime hfl ha hL hi hT hpt ht hζ hg).prod (htags.prod hslice))) + exact ComputableInPolyTime.mapWithInput (ComputableInPolyTime.range.comp hP) hpacket + +/-- The parts of a native packing. -/ +private theorem packing_parts (hpk : ComputableInPolyTime pk encIn PacketPacking.encNativePacking) : + ComputableInPolyTime (fun x => (pk x).1) encIn + (encPair (encList (encList encNat)) (encList (encList encNat))) ∧ + ComputableInPolyTime (fun x => (pk x).2.2) encIn (encList (encPair encNat encNat)) := by + have hpk' : ComputableInPolyTime pk encIn + (encPair (encPair (encList (encList encNat)) (encList (encList encNat))) + (encPair encNat (encList (encPair encNat encNat)))) := hpk + exact ⟨(ComputableInPolyTime.fst _ _).comp hpk', + (ComputableInPolyTime.snd _ _).comp ((ComputableInPolyTime.snd _ _).comp hpk')⟩ + +/-- **The number of rows of the native child is polynomial-time.** -/ +theorem computableInPolyTime_childRows (hr : ComputableInPolyTime r encIn encNat) + (hi : ComputableInPolyTime i encIn encUnary) + (hD : ComputableInPolyTime D encIn (DescribedNetwork.encDescribed VertexDesc.enc)) + (hpk : ComputableInPolyTime pk encIn PacketPacking.encNativePacking) + (hg : ComputableInPolyTime g encIn encNat) : + ComputableInPolyTime (fun x => childRows (r x) (i x) (D x) (pk x) (g x)) encIn encNat := + count_polyTime (computableInPolyTime_groupsOf hr hi ((ComputableInPolyTime.length_list _).comp + (DescribedNetwork.vertices_polyTime.comp hD)) (packing_parts hpk).1) hg + +/-- **The native child is polynomial-time.** The counts `a`, `i` and `p_t` are unary, the node +`t` is in binary sign-magnitude form, the lists `fl` and `ζ`, the number of rows `r` and the group +`g` are in binary, and the exponents `β` are unary. The child stores `2 ^ a` and `2 ^ (i + 1)` in +unary in every packet vertex and has one packet vertex per packet, so these and the number of +packets are asked for in unary. -/ +theorem computableInPolyTime_nativeChild (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (ha : ComputableInPolyTime a encIn encUnary) + (hL : ComputableInPolyTime (fun x => 2 ^ a x) encIn encUnary) + (hi : ComputableInPolyTime i encIn encUnary) + (hT : ComputableInPolyTime (fun x => 2 ^ (i x + 1)) encIn encUnary) + (hpt : ComputableInPolyTime pt encIn encUnary) (ht : ComputableInPolyTime t encIn encIntBin) + (hζ : ComputableInPolyTime ζ encIn (encList encNat)) + (hr : ComputableInPolyTime r encIn encNat) + (hD : ComputableInPolyTime D encIn (DescribedNetwork.encDescribed VertexDesc.enc)) + (hpk : ComputableInPolyTime pk encIn PacketPacking.encNativePacking) + (hP : ComputableInPolyTime (fun x => (pk x).2.1) encIn encUnary) + (hβ : ComputableInPolyTime β encIn (encList encUnary)) + (hg : ComputableInPolyTime g encIn encNat) : + ComputableInPolyTime (fun x => + nativeChild (fl x) (a x) (i x) (pt x) (t x) (ζ x) (r x) (D x) (pk x) (β x) (g x)) + encIn (DescribedNetwork.encDescribed VertexDesc.enc) := by + obtain ⟨hwords, hplace⟩ := packing_parts hpk + have hV := DescribedNetwork.vertices_polyTime.comp hD + have hends := (DescribedNetwork.computableInPolyTime_edgeEnds VertexDesc.enc).comp hD + have hgroups := computableInPolyTime_groupsOf hr hi + ((ComputableInPolyTime.length_list _).comp hV) hwords + have hpackets : ComputableInPolyTime (fun x => (pk x).2.2.map Prod.fst) encIn + (encList encNat) := + ComputableInPolyTime.mapWithInput (f := fun _ q => q.1) hplace + ((ComputableInPolyTime.fst _ _).comp (ComputableInPolyTime.snd _ _)) + have hcedges : ComputableInPolyTime (fun x => + childEdgeList (groupsOf (r x) (i x) (D x).vertices.length (pk x).1) (g x) (D x).edgeEnds + ((pk x).2.2.map Prod.fst)) encIn (encList (encPair encNat encNat)) := + computableInPolyTime_childEdgeList.comp ((hgroups.prod hg).prod (hends.prod hpackets)) + have hpair := ((ComputableInPolyTime.length_list _).comp hcedges).prod + (ComputableInPolyTime.listAppend' (retained_polyTime hr hV hgroups hg hends) + (packetVertices_polyTime hfl ha hL hi hT hpt ht hζ hβ hg hgroups hends hP hcedges hplace)) + have hsymm : ComputableInPolyTime (DescribedNetwork.equivProd (T := VertexDesc)).symm + (encPair encUnary (encList (encPair (encList encNat) VertexDesc.enc))) + (DescribedNetwork.encDescribed VertexDesc.enc) := + ComputableInPolyTime.of_eq fun y => by + rw [DescribedNetwork.encDescribed, encOfEquiv_apply, Equiv.apply_symm_apply] + exact (hsymm.comp hpair).of_enc_eq fun x => by + rw [Function.comp_apply] + rfl + +end PolyTime + +/-! ### The child of the native parent packing -/ + +/-- A maximum of a list of numbers below `n` is at most `n`. -/ +private theorem foldl_max_le {l : List ℕ} {m n : ℕ} (hm : m ≤ n) (hl : ∀ x ∈ l, x ≤ n) : + l.foldl max m ≤ n := by + induction l generalizing m with + | nil => exact hm + | cons x l ih => + exact ih (max_le hm (hl x List.mem_cons_self)) fun y hy => hl y (List.mem_cons_of_mem _ hy) + +/-- **At most `M (2 ^ a) ^ 12` packets**: the native parent packing of a network with `M` edges +has at most one block of `2 ^ a` slots per edge, times the `4096 ^ a` words of pairs of +symbols. -/ +theorem numPackets_nativeParentPacking_le (r a : ℕ) (D : DescribedNetwork VertexDesc) : + (nativeParentPacking r a D).2.1 ≤ D.numEdges * (2 ^ a) ^ 12 := by + set pairs := PacketPacking.edgeWordPairs (PacketPacking.labelWords r (D.vertices.length - r) a 64 + (D.edgeEnds.map fun e => (e.1, e.2 - r))).1 (PacketPacking.labelWords r + (D.vertices.length - r) a 64 (D.edgeEnds.map fun e => (e.1, e.2 - r))).2 + (D.edgeEnds.map fun e => (e.1, e.2 - r)) with hpairs + have hlen : pairs.length = D.numEdges := by + simp [hpairs, PacketPacking.edgeWordPairs, DescribedNetwork.edgeEnds] + have hload : PacketPacking.maxLoadOf (PacketPacking.classIndices pairs) ≤ D.numEdges := by + refine foldl_max_le (Nat.zero_le _) fun x hx => ?_ + simp only [PacketPacking.classIndices, List.map_map, List.mem_map, List.mem_range, + Function.comp_apply] at hx + obtain ⟨j, hj, rfl⟩ := hx + have := List.countP_le_length (l := List.range j) + (p := fun j' => decide (pairs.getD j' ([], []) = pairs.getD j ([], []))) + rw [List.length_range] at this + omega + have hblocks : (PacketPacking.maxLoadOf (PacketPacking.classIndices pairs) + (2 ^ a - 1)) / + 2 ^ a ≤ D.numEdges := by + have hL : 1 ≤ 2 ^ a := Nat.one_le_two_pow + rcases Nat.eq_zero_or_pos D.numEdges with h0 | hpos + · rw [h0] at hload ⊢ + rw [Nat.le_zero.mp hload, zero_add] + exact (Nat.div_eq_of_lt (by omega)).le + · refine Nat.div_le_of_le_mul ?_ + have : 2 ^ a - 1 ≤ (2 ^ a - 1) * D.numEdges := Nat.le_mul_of_pos_right _ hpos + have e : 2 ^ a * D.numEdges = D.numEdges + (2 ^ a - 1) * D.numEdges := by + rw [Nat.sub_mul, one_mul] + have : D.numEdges ≤ 2 ^ a * D.numEdges := Nat.le_mul_of_pos_left _ (by omega) + omega + omega + have h64 : (64 * 64) ^ a = (2 ^ a) ^ 12 := by + rw [← pow_mul, mul_comm a 12, pow_mul] + norm_num + change (PacketPacking.maxLoadOf (PacketPacking.classIndices pairs) + (2 ^ a - 1)) / 2 ^ a * + (64 * 64) ^ a ≤ _ + rw [h64] + exact Nat.mul_le_mul_right _ hblocks + +section PolyTime + +variable {δ : Type} {encIn : δ ↪ List Bool} {fl ζ β : δ → List ℕ} {a i pt r g : δ → ℕ} + {t : δ → ℤ} {D : δ → DescribedNetwork VertexDesc} + +/-- **The native parent packing is polynomial-time**, with the number of rows and `a` in +unary. -/ +theorem computableInPolyTime_nativeParentPacking (hr : ComputableInPolyTime r encIn encUnary) + (ha : ComputableInPolyTime a encIn encUnary) + (hD : ComputableInPolyTime D encIn (DescribedNetwork.encDescribed VertexDesc.enc)) : + ComputableInPolyTime (fun x => nativeParentPacking (r x) (a x) (D x)) encIn + PacketPacking.encNativePacking := by + have hends := (DescribedNetwork.computableInPolyTime_edgeEnds VertexDesc.enc).comp hD + have hrb := ComputableInPolyTime.unary_to_binary.comp hr + have hshift : ComputableInPolyTime (fun x => (D x).edgeEnds.map fun e => (e.1, e.2 - r x)) + encIn (encList (encPair encNat encNat)) := + ComputableInPolyTime.mapWithInput (f := fun x e => (e.1, e.2 - r x)) hends + (((ComputableInPolyTime.fst _ _).comp (ComputableInPolyTime.snd _ _)).prod + (ComputableInPolyTime.binary_sub' ((ComputableInPolyTime.snd _ _).comp + (ComputableInPolyTime.snd _ _)) (hrb.comp (ComputableInPolyTime.fst _ _)))) + have hnC : ComputableInPolyTime (fun x => (D x).vertices.length - r x) encIn encUnary := + ComputableInPolyTime.unary_sub' ((ComputableInPolyTime.length_list _).comp + (DescribedNetwork.vertices_polyTime.comp hD)) hr + have hin : ComputableInPolyTime (fun x => (((r x, (D x).vertices.length - r x, a x), 64, + (D x).edgeEnds.map fun e => (e.1, e.2 - r x)) : PacketPacking.PackingInput)) encIn + (encPair (encPair encUnary (encPair encUnary encUnary)) + (encPair encNat (encList (encPair encNat encNat)))) := + (hr.prod (hnC.prod ha)).prod ((ComputableInPolyTime.const 64).prod hshift) + have h := PacketPacking.computableInPolyTime_nativePacking.comp hin + refine h.of_enc_eq fun x => ?_ + simp only [Function.comp_apply, nativeParentPacking] + +/-- **The number of packets of the native parent packing in unary** is polynomial-time, with `a` +and `2 ^ a` in unary. -/ +theorem computableInPolyTime_numPackets_nativeParentPacking + (hr : ComputableInPolyTime r encIn encUnary) (ha : ComputableInPolyTime a encIn encUnary) + (hL : ComputableInPolyTime (fun x => 2 ^ a x) encIn encUnary) + (hD : ComputableInPolyTime D encIn (DescribedNetwork.encDescribed VertexDesc.enc)) : + ComputableInPolyTime (fun x => (nativeParentPacking (r x) (a x) (D x)).2.1) encIn + encUnary := by + have hpk : ComputableInPolyTime (fun x => nativeParentPacking (r x) (a x) (D x)) encIn + (encPair (encPair (encList (encList encNat)) (encList (encList encNat))) + (encPair encNat (encList (encPair encNat encNat)))) := + computableInPolyTime_nativeParentPacking hr ha hD + exact ComputableInPolyTime.binary_to_unary_bounded + ((ComputableInPolyTime.fst _ _).comp ((ComputableInPolyTime.snd _ _).comp hpk)) + (ComputableInPolyTime.unary_mul' (DescribedNetwork.numEdges_polyTime.comp hD) + ((ComputableInPolyTime.unary_pow 12).comp hL)) + fun x => numPackets_nativeParentPacking_le (r x) (a x) (D x) + +/-- **The native child of the native parent packing is polynomial-time**, with the number of rows +in unary (`computableInPolyTime_nativeChild` for `nativeParentPacking`). -/ +theorem computableInPolyTime_nativeChild_nativeParentPacking + (hfl : ComputableInPolyTime fl encIn (encList encNat)) + (ha : ComputableInPolyTime a encIn encUnary) + (hL : ComputableInPolyTime (fun x => 2 ^ a x) encIn encUnary) + (hi : ComputableInPolyTime i encIn encUnary) + (hT : ComputableInPolyTime (fun x => 2 ^ (i x + 1)) encIn encUnary) + (hpt : ComputableInPolyTime pt encIn encUnary) (ht : ComputableInPolyTime t encIn encIntBin) + (hζ : ComputableInPolyTime ζ encIn (encList encNat)) + (hr : ComputableInPolyTime r encIn encUnary) + (hD : ComputableInPolyTime D encIn (DescribedNetwork.encDescribed VertexDesc.enc)) + (hβ : ComputableInPolyTime β encIn (encList encUnary)) + (hg : ComputableInPolyTime g encIn encNat) : + ComputableInPolyTime (fun x => nativeChild (fl x) (a x) (i x) (pt x) (t x) (ζ x) (r x) (D x) + (nativeParentPacking (r x) (a x) (D x)) (β x) (g x)) + encIn (DescribedNetwork.encDescribed VertexDesc.enc) := + computableInPolyTime_nativeChild hfl ha hL hi hT hpt ht hζ + (ComputableInPolyTime.unary_to_binary.comp hr) hD + (computableInPolyTime_nativeParentPacking hr ha hD) + (computableInPolyTime_numPackets_nativeParentPacking hr ha hL hD) hβ hg + +/-- **The number of rows of the native child of the native parent packing is polynomial-time.** -/ +theorem computableInPolyTime_childRows_nativeParentPacking + (hr : ComputableInPolyTime r encIn encUnary) (ha : ComputableInPolyTime a encIn encUnary) + (hi : ComputableInPolyTime i encIn encUnary) + (hD : ComputableInPolyTime D encIn (DescribedNetwork.encDescribed VertexDesc.enc)) + (hg : ComputableInPolyTime g encIn encNat) : + ComputableInPolyTime (fun x => childRows (r x) (i x) (D x) + (nativeParentPacking (r x) (a x) (D x)) (g x)) encIn encNat := + computableInPolyTime_childRows (ComputableInPolyTime.unary_to_binary.comp hr) hi hD + (computableInPolyTime_nativeParentPacking hr ha hD) hg + +end PolyTime + +/-! ### The shape of the native child -/ + +section Shape + +variable (fl : List ℕ) (a i pt : ℕ) (t : ℤ) (ζ : List ℕ) (r : ℕ) (D : DescribedNetwork VertexDesc) + (pk : (List (List ℕ) × List (List ℕ)) × ℕ × List (ℕ × ℕ)) (β : List ℕ) (g : ℕ) + +/-- **The child has at most twice as many edges as the parent**: one per end of a parent edge in +group `g`. -/ +theorem numEdges_nativeChild_le : + (nativeChild fl a i pt t ζ r D pk β g).numEdges ≤ 2 * D.numEdges := by + have hends : D.edgeEnds.length = D.numEdges := by simp [DescribedNetwork.edgeEnds] + have hzip : (D.edgeEnds.zip (pk.2.2.map Prod.fst)).length ≤ D.numEdges := by + rw [List.length_zip] + omega + simp only [nativeChild, childEdgeList, List.length_append, List.length_map] + have h₁ := List.length_filter_le (fun q : (ℕ × ℕ) × ℕ => + decide ((groupsOf r i D.vertices.length pk.1).getD q.1.1 0 = g)) + (D.edgeEnds.zip (pk.2.2.map Prod.fst)) + have h₂ := List.length_filter_le (fun q : (ℕ × ℕ) × ℕ => + decide ((groupsOf r i D.vertices.length pk.1).getD q.1.2 0 = g)) + (D.edgeEnds.zip (pk.2.2.map Prod.fst)) + omega + +/-- **The vertices of the child**: its rows, the parent vertices of group `g`, then one packet +vertex per packet. -/ +theorem length_vertices_nativeChild : + (nativeChild fl a i pt t ζ r D pk β g).vertices.length = childRows r i D pk g + pk.2.1 := by + simp only [nativeChild, childRows, List.length_append, List.length_map, List.length_range] + congr 1 + rw [List.count_eq_countP, List.countP_eq_length_filter, groupsOf, List.filter_map, + List.length_map] + congr 1 + refine List.filter_congr fun v hv => ?_ + rw [List.mem_range] at hv + simp only [Function.comp_apply] + rw [List.getD_eq_getElem _ _ (by simpa using hv), List.getElem_map, List.getElem_range, + beq_eq_decide] + +/-- **Every vertex of the child is a parent vertex or a packet vertex.** A vertex of the child is +either a parent vertex with renumbered ports, with the same descriptor and the same number of +ports, or the vertex of a packet `q`, whose descriptor is its context, tags of its ports with +slots from the packing (or `0`), and its slice of `β`. -/ +theorem mem_vertices_nativeChild {v : List ℕ × VertexDesc} + (hv : v ∈ (nativeChild fl a i pt t ζ r D pk β g).vertices) : + (∃ w ∈ D.vertices, v.2 = w.2 ∧ v.1.length = w.1.length) ∨ + ∃ q < pk.2.1, ∃ tags : List (Bool × ℕ), + v.2 = .inr (packetCtx fl a i pt t ζ g q, tags, betaSlice i pt β q) ∧ + tags.length = v.1.length ∧ ∀ b ∈ tags, b.2 = 0 ∨ ∃ y ∈ pk.2.2, b.2 = y.2 := by + simp only [nativeChild, List.mem_append, List.mem_map, List.mem_filter, List.mem_range] at hv + rcases hv with ⟨u, ⟨hu, -⟩, rfl⟩ | ⟨q, hq, rfl⟩ + · refine Or.inl ⟨D.vertices.getD u defaultVertex, ?_, rfl, List.length_map _⟩ + rw [List.getD_eq_getElem _ _ hu] + exact List.getElem_mem hu + · refine Or.inr ⟨q, hq, _, rfl, List.length_map _, fun b hb => ?_⟩ + obtain ⟨j, -, rfl⟩ := List.mem_map.mp hb + rcases lt_or_ge (parentEdge (groupsOf r i D.vertices.length pk.1) g D.edgeEnds j) + pk.2.2.length with h | h + · rw [List.getD_eq_getElem _ _ h] + exact Or.inr ⟨_, List.getElem_mem h, rfl⟩ + · rw [List.getD_eq_default _ _ h] + exact Or.inl rfl + +/-- The slice of `β` at a packet has at most `2 ^ (i + 1) p_t` exponents, all from `β`. -/ +theorem length_betaSlice_le (q : ℕ) : (betaSlice i pt β q).length ≤ 2 ^ (i + 1) * pt := + List.length_take_le _ _ + +theorem mem_of_mem_betaSlice {q b : ℕ} (hb : b ∈ betaSlice i pt β q) : b ∈ β := + List.mem_of_mem_drop (List.mem_of_mem_take hb) + +end Shape + +end Cslib.Circuits.Frontier.NativeChild diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeChildPolyTime.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeChildPolyTime.lean new file mode 100644 index 0000000000..5a4e66be69 --- /dev/null +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetworkNativeChildPolyTime.lean @@ -0,0 +1,117 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ +module +-- upstream: local-only (test driver) + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeChildPolyTime +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeNatFrontends + +public section + +open Cslib.Circuits.Frontier Cslib.Circuits.Frontier.NativeChild Cslib.GaloisRingArith + Turing.MultiTapeTM + +namespace Cslib.Tests.TensorNetworkNativeChildPolyTime + +/-! ### Ranks of ports past the edges + +A port past the edges reads the edge `(0, 0)`, so the rank of a large port counts it. -/ + +example : portRank [0, 1] 0 [(0, 1)] Prod.fst 3 = 3 := by decide + +example : portRank [0, 1] 0 [(0, 1)] Prod.fst 3 = + (List.range (min 1 3)).countP (fun e' => decide ([0, 1].getD (([(0, 1)].getD e' (0, 0)).1) 0 = + 0)) + (3 - 1) * (if [0, 1].getD ((0, 0) : ℕ × ℕ).1 0 = 0 then 1 else 0) := + portRank_eq_countP _ _ _ _ _ + +/-! ### The shape of a small child + +The path of `TensorNetworkNativeChild` with one row joined to two columns. The child of group `0` +keeps the row and the column `1` and adds one packet. -/ + +/-- The tables of the vertices. -/ +def table (n : ℕ) : VertexDesc := .inl (List.replicate n 1) + +/-- The row `0` with ports `1, 0`, the columns `1` and `2`. -/ +def path : DescribedNetwork VertexDesc := + ⟨2, [([1, 0], table 4), ([0], table 2), ([1], table 2)]⟩ + +/-- The packing: the row and the column `1` in group `0`, the column `2` in group `1`, both edges +in packet `0`. -/ +def packing : (List (List ℕ) × List (List ℕ)) × ℕ × List (ℕ × ℕ) := + (([[0]], [[0], [1]]), 1, [(0, 0), (0, 1)]) + +example : (nativeChild [1] 1 0 3 (-1) [0] 1 path packing [] 0).numEdges ≤ 2 * path.numEdges := + numEdges_nativeChild_le _ _ _ _ _ _ _ _ _ _ _ + +example : (nativeChild [1] 1 0 3 (-1) [0] 1 path packing [] 0).vertices.length = 3 := by + rw [length_vertices_nativeChild] + decide + +/-! ### Polynomial time on a concrete input + +The composition form applies to an input carrying a parent, its packing words and placements, +and its number of packets in unary, with the other parameters fixed. -/ + +/-- A parent, the packing words, the number of packets and the placements. -/ +abbrev Input : Type := + DescribedNetwork VertexDesc × (List (List ℕ) × List (List ℕ)) × ℕ × List (ℕ × ℕ) + +/-- The encoding of `Input`, with the number of packets in unary. -/ +abbrev encInput : Input ↪ List Bool := + encPair (DescribedNetwork.encDescribed VertexDesc.enc) + (encPair (encPair (encList (encList encNat)) (encList (encList encNat))) + (encPair encUnary (encList (encPair encNat encNat)))) + +example : ComputableInPolyTime (fun x : Input => + nativeChild [1] 1 0 3 (-1) [0] 1 x.1 x.2 [] 0) encInput + (DescribedNetwork.encDescribed VertexDesc.enc) := by + have hpk : ComputableInPolyTime (fun x : Input => x.2) encInput + PacketPacking.encNativePacking := + ((ComputableInPolyTime.fst _ _).comp (ComputableInPolyTime.snd _ _)).prod + ((ComputableInPolyTime.unary_to_binary.comp ((ComputableInPolyTime.fst _ _).comp + ((ComputableInPolyTime.snd _ _).comp (ComputableInPolyTime.snd _ _)))).prod + ((ComputableInPolyTime.snd _ _).comp + ((ComputableInPolyTime.snd _ _).comp (ComputableInPolyTime.snd _ _)))) + exact computableInPolyTime_nativeChild (ComputableInPolyTime.const _) + (ComputableInPolyTime.const _) (ComputableInPolyTime.const _) (ComputableInPolyTime.const _) + (ComputableInPolyTime.const _) (ComputableInPolyTime.const _) (ComputableInPolyTime.const _) + (ComputableInPolyTime.const _) (ComputableInPolyTime.const _) (ComputableInPolyTime.fst _ _) + hpk ((ComputableInPolyTime.fst _ _).comp + ((ComputableInPolyTime.snd _ _).comp (ComputableInPolyTime.snd _ _))) + (ComputableInPolyTime.const _) (ComputableInPolyTime.const _) + +/-- With the native parent packing, the number of rows and `a` are the only further inputs. -/ +example : ComputableInPolyTime (fun D : DescribedNetwork VertexDesc => + nativeChild [1] 1 0 3 (-1) [0] 1 D (nativeParentPacking 1 1 D) [] 0) + (DescribedNetwork.encDescribed VertexDesc.enc) + (DescribedNetwork.encDescribed VertexDesc.enc) := + computableInPolyTime_nativeChild_nativeParentPacking (ComputableInPolyTime.const _) + (ComputableInPolyTime.const _) (ComputableInPolyTime.const _) (ComputableInPolyTime.const _) + (ComputableInPolyTime.const _) (ComputableInPolyTime.const _) (ComputableInPolyTime.const _) + (ComputableInPolyTime.const _) (ComputableInPolyTime.const _) ComputableInPolyTime.id' + (ComputableInPolyTime.const _) (ComputableInPolyTime.const _) + +example : (nativeParentPacking 1 0 path).2.1 ≤ 2 := + (numPackets_nativeParentPacking_le 1 0 path).trans (by decide) + +/-! ### Binary-digit points + +The digits below `e = 2` of `0, 1, 2` are the lists of the interpolation points. -/ + +example : ((List.range 3).map fun k => (List.range 2).map fun j => if k.testBit j then 1 else 0) = + [[0, 0], [1, 0], [0, 1]] := by decide + +example (hirr : Irreducible ((monicOf [1, 1]).map (Int.castRingHom (ZMod 2)))) : + Pairwise fun i j : Fin ((List.range 3).map fun k => + (List.range [1, 1].length).map fun j => if k.testBit j then 1 else 0).length => + IsUnit (liftO hirr (((List.range 3).map fun k => + (List.range [1, 1].length).map fun j => if k.testBit j then 1 else 0).getD i []) - + liftO hirr (((List.range 3).map fun k => + (List.range [1, 1].length).map fun j => if k.testBit j then 1 else 0).getD j [])) := + pairwise_isUnit_liftO_sub_digits hirr (by decide) + +end Cslib.Tests.TensorNetworkNativeChildPolyTime From 0906c9ce7ebe4a54e8ca45ccc2751a43376998c5 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:08:22 -0400 Subject: [PATCH 095/129] refactor(circuit/frontier): one rows-first predicate on described networks ExplicitTensorNetwork R is DescribedNetwork (List R), so ExplicitTensorNetwork.IsRowsFirstBipartite and DescribedNetwork.IsRowsFirstBipartite were the same definition twice. Keep only DescribedNetwork.IsRowsFirstBipartite, defined beside DescribedNetwork in Explicit.lean; dot notation on explicit networks resolves to it. Remove isRowsFirstBipartite_explicit and update the docs and the test that named the explicit predicate. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/DescribedLift.lean | 27 +++------------ .../Frontier/TensorNetwork/Explicit.lean | 34 +++++++++---------- .../CountingSimulation/Admissible.lean | 2 +- .../Probabilistic/ExplicitBlockNetwork.lean | 2 +- .../Circuit/Frontier/TensorNetwork.lean | 2 +- 5 files changed, 24 insertions(+), 43 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean index e0d0e1a0da..365c6ca9ff 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean @@ -30,11 +30,10 @@ for. and reducing the lift modulo `2 ^ p` gives the value of `c` in `A_p` (`GaloisRingArith.reduction_liftO`). This is the canonical lifting of representatives of §D.4.1 (p. 58): a retained vertex is read through the lift of its entry at every later precision. -* **Rows first.** A described network is *rows-first bipartite* at `r` - (`DescribedNetwork.IsRowsFirstBipartite`, the condition of - `ExplicitTensorNetwork.IsRowsFirstBipartite`) when every port names an edge and every edge is a - port of exactly one of the first `r` vertices, once, and of exactly one of the others, once. The - ends of the edges are then computed by listing the vertices at each edge +* **Rows first.** When a described network is *rows-first bipartite* at `r` + (`DescribedNetwork.IsRowsFirstBipartite`), every port names an edge and every edge is a port of + exactly one of the first `r` vertices, once, and of exactly one of the others, once. The ends of + the edges are then computed by listing the vertices at each edge (`DescribedNetwork.incident`, `DescribedNetwork.edgeEnds`), in polynomial time (`DescribedNetwork.computableInPolyTime_edgeEnds`). * **The network** (`DescribedNetwork.toNetwork`). A rows-first described network and an evaluator @@ -47,7 +46,6 @@ for. ## Main definitions * `Cslib.GaloisRingArith.liftO`: the lift of a coefficient list to `O`. -* `Cslib.Circuits.Frontier.DescribedNetwork.IsRowsFirstBipartite`: rows first. * `Cslib.Circuits.Frontier.DescribedNetwork.incident`, `Cslib.Circuits.Frontier.DescribedNetwork.edgeEnds`: the ends of the edges. * `Cslib.Circuits.Frontier.DescribedNetwork.toNetwork`: the network of a described network. @@ -160,23 +158,6 @@ variable {T : Type} (D : DescribedNetwork T) /-! ### Rows first -/ -/-- **Rows-first bipartite**: the first `r` vertices are the *rows* and the others the *columns*, -every port names an edge, and every edge is a port of the rows exactly once and of the columns -exactly once (the condition of `ExplicitTensorNetwork.IsRowsFirstBipartite`). -/ -@[expose] def IsRowsFirstBipartite (r : ℕ) : Prop := - (∀ v ∈ D.vertices, ∀ i ∈ v.1, i < D.numEdges) ∧ - ∀ i < D.numEdges, ((D.vertices.take r).map fun v => v.1.count i).sum = 1 ∧ - ((D.vertices.drop r).map fun v => v.1.count i).sum = 1 - -instance (r : ℕ) : Decidable (D.IsRowsFirstBipartite r) := by - unfold IsRowsFirstBipartite - infer_instance - -/-- An explicit network is rows-first exactly when it is as a described network. -/ -theorem isRowsFirstBipartite_explicit (d : ExplicitTensorNetwork ℕ) (r : ℕ) : - DescribedNetwork.IsRowsFirstBipartite d r ↔ ExplicitTensorNetwork.IsRowsFirstBipartite d r := - Iff.rfl - /-- Being rows-first depends only on the ports, not on the descriptors. -/ theorem isRowsFirstBipartite_map {T' : Type} (f : T → T') (r : ℕ) : (⟨D.numEdges, D.vertices.map fun v => (v.1, f v.2)⟩ : diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Explicit.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Explicit.lean index 313760ebfc..a48aad9bc6 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/Explicit.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/Explicit.lean @@ -37,9 +37,9 @@ entry zero. Its size measures are the number of edges, the number of vertices, the maximum number of ports of a vertex (`ExplicitTensorNetwork.maxDegree`) and the total length of the tables -(`ExplicitTensorNetwork.tableLength`). A network is *rows-first bipartite* with `r` rows -(`ExplicitTensorNetwork.IsRowsFirstBipartite`) when every edge is a port of exactly one of its -first `r` vertices and of exactly one of the others. +(`ExplicitTensorNetwork.tableLength`). A described network is *rows-first bipartite* with `r` +rows (`DescribedNetwork.IsRowsFirstBipartite`) when every port names an edge and every edge is a +port of exactly one of its first `r` vertices and of exactly one of the others. **Writing a network out** (`TensorNetwork.toExplicit`). Given numberings of the vertices and the edges of a Boolean tensor network on finite types, list the edges at each vertex in increasing @@ -55,6 +55,8 @@ one end of every edge below `r` and the other at or above it gives a rows-first the descriptors of its vertices. * `Cslib.Circuits.Frontier.DescribedNetwork.contraction`: its contraction, for an evaluator of the descriptors. +* `Cslib.Circuits.Frontier.DescribedNetwork.IsRowsFirstBipartite`: the bipartition with the first + `r` vertices as rows. * `Cslib.Circuits.Frontier.ExplicitTensorNetwork`: an explicit Boolean tensor network, with tables as descriptors. * `Cslib.Circuits.Frontier.ExplicitTensorNetwork.entry`, @@ -63,8 +65,6 @@ one end of every edge below `r` and the other at or above it gives a rows-first * `Cslib.Circuits.Frontier.ExplicitTensorNetwork.contraction`: the contraction. * `Cslib.Circuits.Frontier.ExplicitTensorNetwork.maxDegree`, `Cslib.Circuits.Frontier.ExplicitTensorNetwork.tableLength`: size measures. -* `Cslib.Circuits.Frontier.ExplicitTensorNetwork.IsRowsFirstBipartite`: the bipartition with the - first `r` vertices as rows. * `Cslib.Circuits.Frontier.TensorNetwork.explicitPorts`, `Cslib.Circuits.Frontier.TensorNetwork.explicitTable`, `Cslib.Circuits.Frontier.TensorNetwork.toExplicit`: writing a Boolean tensor network out. @@ -137,6 +137,18 @@ theorem map_contraction {R S : Type*} [CommSemiring R] [CommSemiring S] (ψ : R rw [map_list_prod, List.map_map] rfl +/-- **Rows-first bipartite**: the first `r` vertices are the *rows* and the others the *columns*, +every port names an edge, and every edge is a port of the rows exactly once and of the columns +exactly once. So every edge joins a row to a column. -/ +def IsRowsFirstBipartite (d : DescribedNetwork T) (r : ℕ) : Prop := + (∀ v ∈ d.vertices, ∀ i ∈ v.1, i < d.numEdges) ∧ + ∀ i < d.numEdges, ((d.vertices.take r).map fun v => v.1.count i).sum = 1 ∧ + ((d.vertices.drop r).map fun v => v.1.count i).sum = 1 + +instance (d : DescribedNetwork T) (r : ℕ) : Decidable (d.IsRowsFirstBipartite r) := by + unfold IsRowsFirstBipartite + infer_instance + end DescribedNetwork /-- An *explicit Boolean tensor network* over `R`: a network whose descriptors are *tables*. The @@ -190,18 +202,6 @@ theorem length_le_maxDegree {v : List ℕ × List R} (hv : v ∈ d.vertices) : v.1.length ≤ d.maxDegree := d.maxDegree_le_iff.mp le_rfl v hv -/-- **Rows-first bipartite**: the first `r` vertices are the *rows* and the others the *columns*, -every port names an edge, and every edge is a port of the rows exactly once and of the columns -exactly once. So every edge joins a row to a column. -/ -def IsRowsFirstBipartite (r : ℕ) : Prop := - (∀ v ∈ d.vertices, ∀ i ∈ v.1, i < d.numEdges) ∧ - ∀ i < d.numEdges, ((d.vertices.take r).map fun v => v.1.count i).sum = 1 ∧ - ((d.vertices.drop r).map fun v => v.1.count i).sum = 1 - -instance (r : ℕ) : Decidable (d.IsRowsFirstBipartite r) := by - unfold IsRowsFirstBipartite - infer_instance - end ExplicitTensorNetwork private theorem getD_ofFn {n : ℕ} (x : Fin n → Bool) (i : ℕ) : diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/Admissible.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/Admissible.lean index d1afe23004..80fd8c882e 100644 --- a/Complexity/Computability/Complexity/Classes/CountingSimulation/Admissible.lean +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/Admissible.lean @@ -22,7 +22,7 @@ explicit Boolean tensor networks with a number of row vertices, networks are built (milestone H3) and the contraction theorem consumes them (milestone T2). A network `(r, G)` is admissible at size `s` with constant `C` when `G` is bipartite with its -first `r` vertices as rows (`Cslib.Circuits.Frontier.ExplicitTensorNetwork.IsRowsFirstBipartite`), +first `r` vertices as rows (`Cslib.Circuits.Frontier.DescribedNetwork.IsRowsFirstBipartite`), it has at most `C s` edges and at most `C s` vertices, its maximum degree is at most `C ⌊log₂ s⌋`, every table lists the values at all bit strings on its ports, and it has at least `s` edges. The last two conditions spare the contraction algorithm short tables and networks much smaller than diff --git a/Complexity/Computability/Machines/Turing/MultiTape/Probabilistic/ExplicitBlockNetwork.lean b/Complexity/Computability/Machines/Turing/MultiTape/Probabilistic/ExplicitBlockNetwork.lean index 56ed22be45..da05642876 100644 --- a/Complexity/Computability/Machines/Turing/MultiTape/Probabilistic/ExplicitBlockNetwork.lean +++ b/Complexity/Computability/Machines/Turing/MultiTape/Probabilistic/ExplicitBlockNetwork.lean @@ -43,7 +43,7 @@ accepting count (`Turing.MultiTapePTM.sum_contraction_explicitBlockNetwork`). `2 * (q * (3 * k + 1) * m)` edges and maximum degree at most `2 * (3 * k + 1) * m`, both `O(b)` per interval. Its first `q * (3 * k + 2)` vertices are the rows of a bipartition: every edge has exactly one port at a row and one at a column -(`Cslib.Circuits.Frontier.ExplicitTensorNetwork.IsRowsFirstBipartite`). Every table has length +(`Cslib.Circuits.Frontier.DescribedNetwork.IsRowsFirstBipartite`). Every table has length `2 ^ (number of ports)`, and its entries are at most `2 ^ b`. These are the inputs of the Boolean tensor contraction theorem [OpenAI2026ETH, Theorem D.1] diff --git a/ComplexityTests/Computability/Circuit/Frontier/TensorNetwork.lean b/ComplexityTests/Computability/Circuit/Frontier/TensorNetwork.lean index 9206e0547a..dcde7ed35a 100644 --- a/ComplexityTests/Computability/Circuit/Frontier/TensorNetwork.lean +++ b/ComplexityTests/Computability/Circuit/Frontier/TensorNetwork.lean @@ -103,7 +103,7 @@ example : ¬ parallel.IsRowsFirstBipartite 2 := by decide example : ¬ (cycle eqTable).IsRowsFirstBipartite 2 := by decide /-- A port naming no edge is excluded. -/ -example : ¬ ExplicitTensorNetwork.IsRowsFirstBipartite +example : ¬ DescribedNetwork.IsRowsFirstBipartite (⟨1, [([0], [1, 1]), ([0, 5], [1, 1, 1, 1])]⟩ : ExplicitTensorNetwork ℕ) 1 := by decide /-- Written out with the old vertices first, the subdivision of `parallelNetwork` is rows-first From 8bd4f74d69c4e863357dc68db5a1e20a6de48d67 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:09:03 -0400 Subject: [PATCH 096/129] doc(circuit/frontier): keep the rows-first bullet's first line Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Circuit/Frontier/TensorNetwork/DescribedLift.lean | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean index 365c6ca9ff..e3f6ad7c22 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/DescribedLift.lean @@ -30,10 +30,10 @@ for. and reducing the lift modulo `2 ^ p` gives the value of `c` in `A_p` (`GaloisRingArith.reduction_liftO`). This is the canonical lifting of representatives of §D.4.1 (p. 58): a retained vertex is read through the lift of its entry at every later precision. -* **Rows first.** When a described network is *rows-first bipartite* at `r` - (`DescribedNetwork.IsRowsFirstBipartite`), every port names an edge and every edge is a port of - exactly one of the first `r` vertices, once, and of exactly one of the others, once. The ends of - the edges are then computed by listing the vertices at each edge +* **Rows first.** A described network is *rows-first bipartite* at `r` + (`DescribedNetwork.IsRowsFirstBipartite`) when every port names an edge and every edge is a port + of exactly one of the first `r` vertices, once, and of exactly one of the others, once. The ends + of the edges are then computed by listing the vertices at each edge (`DescribedNetwork.incident`, `DescribedNetwork.edgeEnds`), in polynomial time (`DescribedNetwork.computableInPolyTime_edgeEnds`). * **The network** (`DescribedNetwork.toNetwork`). A rows-first described network and an evaluator From 408395fdf61f4579fbc342fe0c858f8d59fc44c0 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:09:57 -0400 Subject: [PATCH 097/129] doc(circuit/frontier): docstrings of the betaSlice lemmas Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Circuit/Frontier/TensorNetwork/NativeChildPolyTime.lean | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChildPolyTime.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChildPolyTime.lean index d55a5603ee..a58650892b 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChildPolyTime.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChildPolyTime.lean @@ -523,7 +523,7 @@ end PolyTime /-! ### The child of the native parent packing -/ -/-- A maximum of a list of numbers below `n` is at most `n`. -/ +/-- A left fold of `max` over numbers at most `n`, from at most `n`, is at most `n`. -/ private theorem foldl_max_le {l : List ℕ} {m n : ℕ} (hm : m ≤ n) (hl : ∀ x ∈ l, x ≤ n) : l.foldl max m ≤ n := by induction l generalizing m with @@ -721,10 +721,11 @@ theorem mem_vertices_nativeChild {v : List ℕ × VertexDesc} · rw [List.getD_eq_default _ _ h] exact Or.inl rfl -/-- The slice of `β` at a packet has at most `2 ^ (i + 1) p_t` exponents, all from `β`. -/ +/-- The slice of `β` at a packet has at most `2 ^ (i + 1) p_t` exponents. -/ theorem length_betaSlice_le (q : ℕ) : (betaSlice i pt β q).length ≤ 2 ^ (i + 1) * pt := List.length_take_le _ _ +/-- The exponents in the slice of `β` at a packet are exponents of `β`. -/ theorem mem_of_mem_betaSlice {q b : ℕ} (hb : b ∈ betaSlice i pt β q) : b ∈ β := List.mem_of_mem_drop (List.mem_of_mem_take hb) From eed1c7053f881fc162d6dc3d8ceba3d8c77115d2 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:16:20 -0400 Subject: [PATCH 098/129] feat(circuit/frontier): derivations over the reached calls, and the native child's edge and degree bounds The calls reached from a root of the native recursion, with derivations of the root's value over them: with their height (the bound plus the depth never increases) and, given a bound on the children of each call, their node count. The native child has at most M/16 edges under (D.6) and keeps the degree bound when 2L <= Delta: round 10's child facts in the native numbering. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 2 + .../FastContractionDerivation.lean | 198 +++++++++++++++ .../TensorNetwork/NativeChildBounds.lean | 238 ++++++++++++++++++ .../GenericRAM/Native/StackRecursionTree.lean | 39 +++ 4 files changed, 477 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionDerivation.lean create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChildBounds.lean diff --git a/Complexity.lean b/Complexity.lean index e333904ce2..3354e31410 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -898,6 +898,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DescribedL public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DirectContraction public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContraction +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionDerivation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionMachine public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionStep public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates @@ -906,6 +907,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.LocalDefor public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ModeExpansion public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeCall public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeChild +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeChildBounds public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeChildPolyTime public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeRecovery public import Complexity.Computability.Circuit.Frontier.TensorNetwork.PacketEntry diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionDerivation.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionDerivation.lean new file mode 100644 index 0000000000..79654aafce --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionDerivation.lean @@ -0,0 +1,198 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContraction +import Mathlib.Data.List.GetD + +/-! +# The derivations of the native contraction recursion + +The calls that the recursion of `Cslib.Circuits.Frontier.FastContraction.scheme` meets from a root +(`FastContraction.Reach`), and derivations of the root's value over them, with their height and, +given a bound on the children of each call, their number of nodes. This is the recursion tree of +Theorem D.1 of [OpenAI2026ETH] (Appendix D.4.7, p. 64), for any child builder. + +**The invariant** (`FastContraction.reachInv`). The nodes of a derivation from a root `x₀` are the +calls reached from `x₀`, its frames are the frames `FastContraction.frameAt` of a reached active call +along its children, and its values are the tagged values of reached calls. Every bound on reached +calls is a bound on the derivation. + +**The height.** The children of a call receive its number of edges as their bound, and an active +call has fewer edges than its bound, so the bound plus the depth never increases along the +recursion (`FastContraction.Reach.bound_add_depth_le`). From a root at depth `0` with edge bound +`B`, every active call has depth below `B - 1`, and the root has a derivation with at most `B` +stack levels (`FastContraction.exists_eval_reachInv`), whatever the schedule. + +**The node count.** If every reached active call has depth below `D` and at most `b d` children at +depth `d`, the derivation has at most `Nat.recursionBound b D 0` nodes and `D` stack levels +(`FastContraction.exists_eval_reachInv_count`). + +## Main definitions + +* `Cslib.Circuits.Frontier.FastContraction.Reach`: the calls reached from a root. +* `Cslib.Circuits.Frontier.FastContraction.reachInv`: the invariant of the derivations. + +## Main results + +* `Cslib.Circuits.Frontier.FastContraction.Reach.bound_add_depth_le`: the bound plus the depth + does not increase. +* `Cslib.Circuits.Frontier.FastContraction.exists_eval_reachInv`: a derivation of the root with its + height. +* `Cslib.Circuits.Frontier.FastContraction.exists_eval_reachInv_count`: a derivation of the root + with its node count. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Appendix D.4.7 (p. 64). +-/ + +public section + +namespace Cslib.Circuits.Frontier.FastContraction + +open ChildCursor Cslib.GenericRAM Cslib.GenericRAM.StackRecursion + +variable (mk : Call → State → ℕ × DescribedNetwork VertexDesc) + +/-- **The calls reached from a root** `x₀`: the root, and the children of reached active calls. -/ +inductive Reach (x₀ : Call) : Call → Prop + /-- The root is reached. -/ + | root : Reach x₀ x₀ + /-- The children of a reached active call are reached. -/ + | step {x : Call} (c : State) : Reach x₀ x → active x = true → c ∈ children x → + Reach x₀ (child x mk c) + +/-- **The invariant of the derivations from a root** `x₀`: the reached calls, the frames of reached +active calls along their children, and the tagged values of reached calls. -/ +@[expose] def reachInv (x₀ : Call) : Invariant Call Frame (ℕ × List ℕ) where + node := Reach mk x₀ + frame x a := Reach mk x₀ x ∧ active x = true ∧ + ∃ c ≤ (children x).length, a = frameAt x (fun c => value mk (child x mk c)) c + value y := ∃ x, Reach mk x₀ x ∧ y = (x.depth, value mk x) + +variable {mk} {x₀ : Call} + +namespace Reach + +/-- Reached calls keep the scale of the root. -/ +theorem scale_eq {x : Call} (h : Reach mk x₀ x) : x.scale = x₀.scale := by + induction h with + | root => rfl + | step c _ _ _ ih => exact ih + +/-- Reached calls keep the modulus of the root. -/ +theorem fl_eq {x : Call} (h : Reach mk x₀ x) : x.fl = x₀.fl := by + induction h with + | root => rfl + | step c _ _ _ ih => exact ih + +/-- **The bound plus the depth does not increase** along the recursion: a child's bound is its +parent's number of edges, which is below the parent's bound at an active call. -/ +theorem bound_add_depth_le {x : Call} (h : Reach mk x₀ x) : + x.bound + x.depth ≤ x₀.bound + x₀.depth := by + induction h with + | root => exact le_rfl + | step c _ ha _ ih => + rename_i x _ _ + have := edges_lt_bound_of_active ha + change x.edges + (x.depth + 1) ≤ _ + omega + +/-- **A reached active call lies above depth `B - 1`**, for the root's bound `B` plus its depth: +it has an edge and fewer edges than its bound. -/ +theorem depth_add_two_le_of_active {x : Call} (h : Reach mk x₀ x) (ha : active x = true) : + x.depth + 2 ≤ x₀.bound + x₀.depth := by + have h1 := h.bound_add_depth_le + have h2 := edges_lt_bound_of_active ha + have h3 := one_le_edges_of_active ha + omega + +/-- Reached calls lie at or below the root's depth. -/ +theorem depth_le {x : Call} (h : Reach mk x₀ x) : x₀.depth ≤ x.depth := by + induction h with + | root => exact le_rfl + | step c _ _ _ ih => change x₀.depth ≤ _ + 1; omega + +/-- A reached call at the root's depth is the root. -/ +theorem eq_root_of_depth_le {x : Call} (h : Reach mk x₀ x) (hd : x.depth ≤ x₀.depth) : + x = x₀ := by + cases h with + | root => rfl + | @step y c hx _ _ => + have := hx.depth_le + change y.depth + 1 ≤ x₀.depth at hd + omega + +end Reach + +/-- The derivation hypotheses of a reached active call: its frames along its children, which are +reached, smaller in the bound and one level deeper, and its value from the last frame. -/ +private theorem reach_call {x : Call} (hx : Reach mk x₀ x) (ha : active x = true) : + ∃ (acc : ℕ → Frame) (child' : ℕ → Call), acc 0 = (scheme mk).start x ∧ + (∀ c < (children x).length, (scheme mk).next x (acc c) = some (child' c)) ∧ + (scheme mk).next x (acc (children x).length) = none ∧ + (∀ c < (children x).length, (scheme mk).absorb x (acc c) + ((child' c).depth, value mk (child' c)) = acc (c + 1)) ∧ + (∀ c < (children x).length, (reachInv mk x₀).frame x (acc c) ∧ + Reach mk x₀ (child' c) ∧ (child' c).bound < x.bound ∧ (child' c).depth = x.depth + 1) ∧ + (x.depth, value mk x) = (scheme mk).finish x (acc (children x).length) := by + obtain ⟨h0, hnext, hdone, habs, hfin⟩ := walk mk ha fun c => value mk (child x mk c) + refine ⟨frameAt x fun c => value mk (child x mk c), + fun c => child x mk ((children x).getD c (0, 0, [], 0)), h0, hnext, hdone, + fun c hc => habs c hc, fun c hc => ⟨⟨hx, ha, c, hc.le, rfl⟩, ?_, ?_, rfl⟩, ?_⟩ + · exact Reach.step _ hx ha (List.getD_eq_getElem _ _ hc ▸ List.getElem_mem hc) + · exact edges_lt_bound_of_active ha + · simp only [scheme, Prod.mk.injEq, true_and] + rw [value_of_active mk ha, hfin] + +/-- The leaves and values of a derivation over the reached calls. -/ +private theorem reach_leaf {x : Call} (hl : (scheme mk).isLeaf x = true) : + (x.depth, value mk x) = (scheme mk).leaf x := by + have hl' : active x = false := by simpa [scheme] using hl + simp [scheme, value_of_not_active mk hl'] + +private theorem active_of_isLeaf_eq_false {x : Call} (hl : (scheme mk).isLeaf x = false) : + active x = true := by simpa [scheme] using hl + +/-- **A derivation of the root with its height.** A root at depth `0` with edge bound `B` has a +derivation of its tagged value over the reached calls with at most `B` stack levels, for every +child builder. -/ +theorem exists_eval_reachInv (hx₀ : x₀.depth = 0) : + ∃ N H, (scheme mk).Eval (reachInv mk x₀) x₀ (0, value mk x₀) N H ∧ H ≤ x₀.bound := by + obtain ⟨N, H, he, hH⟩ := Scheme.exists_eval_of_height (R := scheme mk) (I := reachInv mk x₀) + Call.bound Call.depth (fun x => (x.depth, value mk x)) x₀.bound (fun x _ hl => reach_leaf hl) + (fun x hx hl => by + have ha := active_of_isLeaf_eq_false hl + have hd := (show Reach mk x₀ x from hx).depth_add_two_le_of_active ha + obtain ⟨acc, child', h0, hnext, hdone, habs, hkids, hfin⟩ := reach_call hx ha + exact ⟨by omega, _, acc, child', h0, hnext, hdone, habs, hkids, hfin⟩) + (fun x hx => ⟨x, hx, rfl⟩) x₀ Reach.root (by omega) + rw [hx₀] at he hH + exact ⟨N, H, he, by omega⟩ + +/-- **A derivation of the root with its node count.** If every reached active call has depth below +`D` and at most `b d` children at depth `d`, a root at depth `0` has a derivation of its tagged value +over the reached calls with at most `Nat.recursionBound b D 0` nodes and `D` stack levels. -/ +theorem exists_eval_reachInv_count (hx₀ : x₀.depth = 0) (D : ℕ) (b : ℕ → ℕ) + (hcnt : ∀ x, Reach mk x₀ x → active x = true → + x.depth < D ∧ (children x).length ≤ b x.depth) : + ∃ N H, (scheme mk).Eval (reachInv mk x₀) x₀ (0, value mk x₀) N H ∧ + N ≤ Nat.recursionBound b D 0 ∧ H ≤ D := by + obtain ⟨N, H, he, hN, hH⟩ := Scheme.exists_eval_of_counted (R := scheme mk) (I := reachInv mk x₀) + Call.bound Call.depth (fun x => (x.depth, value mk x)) b D (fun x _ hl => reach_leaf hl) + (fun x hx hl => by + have ha := active_of_isLeaf_eq_false hl + obtain ⟨hd, hb⟩ := hcnt x hx ha + obtain ⟨acc, child', h0, hnext, hdone, habs, hkids, hfin⟩ := reach_call hx ha + exact ⟨hd, _, acc, child', hb, h0, hnext, hdone, habs, hkids, hfin⟩) + (fun x hx => ⟨x, hx, rfl⟩) x₀ Reach.root (by omega) + rw [hx₀] at he hN hH + exact ⟨N, H, he, hN, by omega⟩ + +end Cslib.Circuits.Frontier.FastContraction diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChildBounds.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChildBounds.lean new file mode 100644 index 0000000000..fe613a01c4 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChildBounds.lean @@ -0,0 +1,238 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeChild +import Mathlib.Data.List.GetD + +/-! +# The sizes of the native child networks + +The native child (`NativeChild.nativeChild`) of a rows-first parent with the native packing of its +edges keeps the two invariants of the recursion of [OpenAI2026ETH] (§D.4.6, p. 63) that its +running time needs: + +* **at most `M / 16` edges** (`NativeChild.sixteen_mul_numEdges_nativeChild_le`) under the size + hypothesis `2 Δ D² ≤ M` of (D.6), `D = 64 ^ (2 a)`, for a parent with `M` edges and maximum + degree `Δ`: the child has one edge per port of its group, and each group has at most `2 M / 64` + ports on each side (`PacketPacking.row_ports_packing_le`). This is + `TensorNetwork.mul_card_edges_child_callSplitting_le` for the native numbering; +* **the degree bound** (`NativeChild.length_le_of_mem_vertices_nativeChild`): a vertex of the + parent keeps its ports, and a packet vertex has at most `2 L` ports, `L = 2 ^ a`, so the child + again has maximum degree at most `Δ` when `2 L ≤ Δ`. This is + `TensorNetwork.ModeSplitting.rowsToColumnsLE_child` for the native numbering. + +Both need `64 ^ a ≤ M`, under which the native packing is the packing of Lemma D.3 +(`NativeChild.nativeParentPacking_eq`), and a node of valuation `i < a`. + +## Main results + +* `Cslib.Circuits.Frontier.NativeChild.numEdges_nativeChild_eq`: the edges of the child are the + ports of its group. +* `Cslib.Circuits.Frontier.NativeChild.sixteen_mul_numEdges_nativeChild_le`: at most `M / 16` + edges. +* `Cslib.Circuits.Frontier.NativeChild.length_le_of_mem_vertices_nativeChild`: the degree bound. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Lemma D.3 (pp. 58–59) and + §D.4.6 (p. 63). +-/ + +public section + +namespace Cslib.Circuits.Frontier.NativeChild + +open Finset ChildNumbering + +variable {r a i : ℕ} {D : DescribedNetwork VertexDesc} (hD : D.IsRowsFirstBipartite r) + +/-- The group of a vertex of the parent in coordinate `i < a` of its packing. -/ +noncomputable abbrev parentGroup (a : ℕ) {i : ℕ} (hia : i < a) : + Fin D.vertices.length → Fin 64 := + packingGroup (parentSide r) (parentPacking hD a) ⟨i, hia⟩ + +/-- **The native child is the numbered child** of the packing of the parent, when `64 ^ a ≤ M` and +`i < a`. -/ +theorem nativeChild_nativeParentPacking_eq (hK : 64 ^ a ≤ D.numEdges) (hia : i < a) + (fl : List ℕ) (pt : ℕ) (t : ℤ) (ζ β : List ℕ) (g : Fin 64) : + nativeChild fl a i pt t ζ r D (nativeParentPacking r a D) β g = + ⟨(members fun e => parentGroup hD a hia (D.srcFin hD e) = g).length + + (members fun e => parentGroup hD a hia (D.tgtFin hD e) = g).length, + List.ofFn fun c => + ((numberedPorts hD (parentGroup hD a hia) (parentPacking hD a).packet g + ((childVertexEquiv (P := (parentPacking hD a).numPackets) (parentGroup hD a hia) + g).symm c)).map + fun e => (childEdgeEquiv (D.srcFin hD) (D.tgtFin hD) (parentGroup hD a hia) g e : ℕ), + numberedDesc hD (parentGroup hD a hia) (parentPacking hD a).packet + (fun e => ((parentPacking hD a).slot e : ℕ)) g fl a i pt t ζ β + ((childVertexEquiv (P := (parentPacking hD a).numPackets) (parentGroup hD a hia) + g).symm c))⟩ := + nativeChild_eq hD _ _ _ g fl a i pt t ζ β _ (groupsOf_eq hD hK hia) + (by rw [nativeParentPacking_eq hD hK]) (by rw [nativeParentPacking_eq hD hK]) + +/-- The members of a predicate are as many as its elements. -/ +theorem length_members {n : ℕ} (p : Fin n → Prop) [DecidablePred p] : + (members p).length = #{v | p v} := by + rw [← Fintype.card_subtype, Fintype.card_congr (rankEquiv p), Fintype.card_fin] + +/-- **The edges of the native child are the ports of its group**: the row ports of group `g`, then +its column ports. -/ +theorem numEdges_nativeChild_eq (hK : 64 ^ a ≤ D.numEdges) (hia : i < a) (fl : List ℕ) (pt : ℕ) + (t : ℤ) (ζ β : List ℕ) (g : Fin 64) : + (nativeChild fl a i pt t ζ r D (nativeParentPacking r a D) β g).numEdges = + #{e | (parentPacking hD a).rowLabel (D.srcFin hD e) ⟨i, hia⟩ = g} + + #{e | (parentPacking hD a).colLabel (D.tgtFin hD e) ⟨i, hia⟩ = g} := by + rw [nativeChild_nativeParentPacking_eq hD hK hia] + simp only [length_members] + rw [card_packingGroup_src (parentSide r) (parentSide_src hD), + card_packingGroup_tgt (parentSide r) (parentSide_tgt hD)] + +/-- A row of a rows-first network is the row of at most as many edges as it has ports. -/ +theorem card_rowOfP_le (v : Fin r) : + #{e | rowOfP hD e = v} ≤ (D.vertices.getD v ([], .inl [])).1.length := by + by_cases hv : (v : ℕ) < D.vertices.length + · rw [List.getD_eq_getElem _ _ hv] + calc #{e | rowOfP hD e = v} + ≤ #((D.vertices[(v : ℕ)]'hv).1.toFinset) := by + refine card_le_card_of_injOn (fun e => (e : ℕ)) (fun e he => ?_) + (fun e _ e' _ h => Fin.ext h) + simp only [coe_filter, Set.mem_ofPred_eq, mem_univ, true_and] at he + rw [mem_coe, List.mem_toFinset] + exact (DescribedNetwork.mem_ports_iff hD ⟨v, hv⟩ e).mpr + (Or.inl (Fin.ext (show (D.srcFin hD e : ℕ) = v from congrArg Fin.val he))) + _ ≤ _ := List.toFinset_card_le _ + · have : #{e | rowOfP hD e = v} = 0 := by + refine card_eq_zero.mpr (filter_eq_empty_iff.mpr fun e _ he => hv ?_) + have := (D.srcFin hD e).2 + rw [show (D.srcFin hD e : ℕ) = v from congrArg Fin.val he] at this + exact this + omega + +/-- A column of a rows-first network is the column of at most as many edges as it has ports. -/ +theorem card_colOfP_le (w : Fin (D.vertices.length - r)) : + #{e | colOfP hD e = w} ≤ (D.vertices.getD (r + w) ([], .inl [])).1.length := by + have hv : r + (w : ℕ) < D.vertices.length := by have := w.2; omega + rw [List.getD_eq_getElem _ _ hv] + calc #{e | colOfP hD e = w} + ≤ #((D.vertices[r + (w : ℕ)]'hv).1.toFinset) := by + refine card_le_card_of_injOn (fun e => (e : ℕ)) (fun e he => ?_) + (fun e _ e' _ h => Fin.ext h) + simp only [coe_filter, Set.mem_ofPred_eq, mem_univ, true_and] at he + rw [mem_coe, List.mem_toFinset] + have hle := DescribedNetwork.le_tgtFin hD e + have hw : (D.tgtFin hD e : ℕ) - r = w := congrArg Fin.val he + exact (DescribedNetwork.mem_ports_iff hD ⟨r + w, hv⟩ e).mpr + (Or.inr (Fin.ext (by simp only; omega))) + _ ≤ _ := List.toFinset_card_le _ + +include hD in +/-- **At most `M / 16` edges in a native child**, under the size hypothesis `2 Δ D² ≤ M` of (D.6), +`D = 64 ^ (2 a)`, for a rows-first parent with `M ≥ 64 ^ a` edges and maximum degree `Δ`, at a node +of valuation `i < a`. -/ +theorem sixteen_mul_numEdges_nativeChild_le (hK : 64 ^ a ≤ D.numEdges) (hia : i < a) {Δ : ℕ} + (hdeg : ∀ v ∈ D.vertices, v.1.length ≤ Δ) (hM : 2 * Δ * (64 ^ (2 * a)) ^ 2 ≤ D.numEdges) + (fl : List ℕ) (pt : ℕ) (t : ℤ) (ζ β : List ℕ) (g : Fin 64) : + 16 * (nativeChild fl a i pt t ζ r D (nativeParentPacking r a D) β g).numEdges ≤ + D.numEdges := by + have hdeg' (v : ℕ) : (D.vertices.getD v ([], .inl [])).1.length ≤ Δ := by + by_cases hv : v < D.vertices.length + · rw [List.getD_eq_getElem _ _ hv] + exact hdeg _ (List.getElem_mem hv) + · rw [List.getD_eq_default _ _ (by omega)] + exact Nat.zero_le _ + have hrow : ∀ v, #{e | rowOfP hD e = v} ≤ Δ := fun v => (card_rowOfP_le hD v).trans (hdeg' _) + have hcol : ∀ w, #{e | colOfP hD e = w} ≤ Δ := fun w => (card_colOfP_le hD w).trans (hdeg' _) + have hM' : 2 * Δ * (64 ^ (2 * a)) ^ 2 ≤ Fintype.card (Fin D.numEdges) := by + rwa [Fintype.card_fin] + have h1 := PacketPacking.row_ports_packing_le (rowOfP hD) (colOfP hD) a 64 hrow hcol hM' + ⟨i, hia⟩ g + have h2 := PacketPacking.col_ports_packing_le (rowOfP hD) (colOfP hD) a 64 hrow hcol hM' + ⟨i, hia⟩ g + rw [Fintype.card_fin] at h1 h2 + rw [numEdges_nativeChild_eq hD hK hia] + have e1 : #{e | (parentPacking hD a).rowLabel (D.srcFin hD e) ⟨i, hia⟩ = g} = + #{e | (PacketPacking.packing (rowOfP hD) (colOfP hD) a 64).rowLabel (rowOfP hD e) + ⟨i, hia⟩ = g} := by + congr 1 + refine filter_congr fun e _ => ?_ + simp only [parentPacking, PacketPacking.Packing.relabel, + dite_eq_left (DescribedNetwork.srcFin_lt hD e)] + rfl + have e2 : #{e | (parentPacking hD a).colLabel (D.tgtFin hD e) ⟨i, hia⟩ = g} = + #{e | (PacketPacking.packing (rowOfP hD) (colOfP hD) a 64).colLabel (colOfP hD e) + ⟨i, hia⟩ = g} := by + congr 1 + refine filter_congr fun e _ => ?_ + simp only [parentPacking, PacketPacking.Packing.relabel, + dite_eq_left (DescribedNetwork.le_tgtFin hD e)] + rfl + rw [e1, e2] + omega + +/-- At most `2 ^ a` edges lie in a packet of a packing. -/ +theorem card_packet_le {E R C : Type*} [Fintype E] {rowOf : E → R} {colOf : E → C} {r : ℕ} + (Q : PacketPacking.Packing rowOf colOf a r) (q : Fin Q.numPackets) : + #{e | Q.packet e = q} ≤ 2 ^ a := by + calc #{e | Q.packet e = q} ≤ #(univ : Finset (Fin (2 ^ a))) := + card_le_card_of_injOn Q.slot (fun _ _ => mem_coe.mpr (mem_univ _)) fun e he e' he' h => by + simp only [coe_filter, Set.mem_ofPred_eq, mem_univ, true_and] at he he' + exact Q.injective (Prod.ext (he.trans he'.symm) h) + _ = 2 ^ a := by rw [card_univ, Fintype.card_fin] + +/-- Counting the elements of a sum type with a property. -/ +private theorem card_filter_sum {A B : Type*} [Fintype A] [Fintype B] (p : A ⊕ B → Prop) + [DecidablePred p] : + #{x | p x} = #{a | p (.inl a)} + #{b | p (.inr b)} := by + rw [← Fintype.card_subtype, Fintype.card_congr Equiv.subtypeSum, Fintype.card_sum, + Fintype.card_subtype, Fintype.card_subtype] + +include hD in +/-- **The degree bound passes to the native child**: if the parent has maximum degree at most `Δ` +and `2 · 2 ^ a ≤ Δ`, every vertex of the child has at most `Δ` ports. A parent vertex keeps its +ports, and a packet vertex has one port per end in group `g` of an edge of its packet, of which +there are at most `2 ^ a`. -/ +theorem length_le_of_mem_vertices_nativeChild (hK : 64 ^ a ≤ D.numEdges) (hia : i < a) {Δ : ℕ} + (hdeg : ∀ v ∈ D.vertices, v.1.length ≤ Δ) (hL : 2 * 2 ^ a ≤ Δ) (fl : List ℕ) (pt : ℕ) (t : ℤ) + (ζ β : List ℕ) (g : Fin 64) {v : List ℕ × VertexDesc} + (hv : v ∈ (nativeChild fl a i pt t ζ r D (nativeParentPacking r a D) β g).vertices) : + v.1.length ≤ Δ := by + rw [nativeChild_nativeParentPacking_eq hD hK hia] at hv + obtain ⟨c, rfl⟩ := List.mem_ofFn.mp hv + simp only [List.length_map] + rcases (childVertexEquiv (P := (parentPacking hD a).numPackets) (parentGroup hD a hia) g).symm c + with u | q + · simp only [numberedPorts, Sum.elim_inl] + exact (List.length_filterMap_le _ _).trans (hdeg _ (List.getElem_mem _)) + · simp only [numberedPorts, Sum.elim_inr, packetPorts, List.length_map] + set E := childEdgeEquiv (D.srcFin hD) (D.tgtFin hD) (parentGroup hD a hia) g + have hcount : ((List.finRange _).filter fun j => + decide (childTgt (D.srcFin hD) (D.tgtFin hD) (parentGroup hD a hia) + (parentPacking hD a).packet g (E.symm j) = .inr q)).length = + #{c | childTgt (D.srcFin hD) (D.tgtFin hD) (parentGroup hD a hia) + (parentPacking hD a).packet g c = .inr q} := by + rw [show ((List.finRange _).filter fun j => + decide (childTgt (D.srcFin hD) (D.tgtFin hD) (parentGroup hD a hia) + (parentPacking hD a).packet g (E.symm j) = .inr q)) = + members (fun j => childTgt (D.srcFin hD) (D.tgtFin hD) (parentGroup hD a hia) + (parentPacking hD a).packet g (E.symm j) = .inr q) from rfl, length_members, + ← Fintype.card_subtype, ← Fintype.card_subtype] + exact Fintype.card_congr (E.symm.subtypeEquiv fun _ => Iff.rfl) + rw [hcount, card_filter_sum] + simp only [childTgt, Sum.elim_inl, Sum.elim_inr, Sum.inr.injEq] + have h1 : ∀ (P : Fin D.numEdges → Prop) [DecidablePred P], + #{e : {e // P e} | (parentPacking hD a).packet e.1 = q} ≤ 2 ^ a := fun P _ => + (card_le_card_of_injOn Subtype.val (fun e he => by + simp only [coe_filter, Set.mem_ofPred_eq, mem_univ, true_and] at he ⊢ + exact he) + fun e _ e' _ h => Subtype.ext h).trans (card_packet_le _ q) + have := h1 (fun e => parentGroup hD a hia (D.srcFin hD e) = g) + have := h1 (fun e => parentGroup hD a hia (D.tgtFin hD e) = g) + omega + +end Cslib.Circuits.Frontier.NativeChild diff --git a/Complexity/Computability/GenericRAM/Native/StackRecursionTree.lean b/Complexity/Computability/GenericRAM/Native/StackRecursionTree.lean index 0f72261147..2eb5616ba5 100644 --- a/Complexity/Computability/GenericRAM/Native/StackRecursionTree.lean +++ b/Complexity/Computability/GenericRAM/Native/StackRecursionTree.lean @@ -41,6 +41,8 @@ small to vouch for the run. recursion. * `Cslib.GenericRAM.StackRecursion.Scheme.exists_eval_of_counted`: derivations with the node count of the recursion tree. +* `Cslib.GenericRAM.StackRecursion.Scheme.exists_eval_of_height`: derivations with their height + alone. * `Cslib.GenericRAM.StackRecursion.Scheme.value_of_run_eq_some`: a returned value satisfies the invariant. @@ -179,6 +181,43 @@ theorem exists_eval_of_counted (μ depth : X → ℕ) (value : X → Y) (b : ℕ rw [hleaf x hx hl] exact Eval.leaf hx hl (hleaf x hx hl ▸ hvalue x hx) +/-- **Derivations with their height.** Let `value` assign values to the nodes satisfying `I.node`, +all satisfying `I.value`, such that a leaf has its leaf value, and a call at depth `d` has +`d < D` and counted children, as in `exists_eval_of_counted`, at depth `d + 1`. Then every node at +depth `d ≤ D` has a derivation of its value with at most `D - d` stack levels. This is +`exists_eval_of_counted` without a bound on the number of children. -/ +theorem exists_eval_of_height (μ depth : X → ℕ) (value : X → Y) (D : ℕ) + (hleaf : ∀ x, I.node x → R.isLeaf x = true → value x = R.leaf x) + (hcall : ∀ x, I.node x → R.isLeaf x = false → depth x < D ∧ + ∃ (cnt : ℕ) (acc : ℕ → A) (child : ℕ → X), acc 0 = R.start x ∧ + (∀ c < cnt, R.next x (acc c) = some (child c)) ∧ R.next x (acc cnt) = none ∧ + (∀ c < cnt, R.absorb x (acc c) (value (child c)) = acc (c + 1)) ∧ + (∀ c < cnt, I.frame x (acc c) ∧ I.node (child c) ∧ μ (child c) < μ x ∧ + depth (child c) = depth x + 1) ∧ + value x = R.finish x (acc cnt)) + (hvalue : ∀ x, I.node x → I.value (value x)) (x : X) (hx : I.node x) (hxD : depth x ≤ D) : + ∃ n h, R.Eval I x (value x) n h ∧ h ≤ D - depth x := by + induction hμ : μ x using Nat.strong_induction_on generalizing x with + | _ k ih => + cases hl : R.isLeaf x + · obtain ⟨hd, cnt, acc, child, h0, hnext, hdone, habs, hkids, hfin⟩ := hcall x hx hl + have hev : ∀ c < cnt, ∃ n h, R.Eval I (child c) (value (child c)) n h ∧ + h ≤ D - (depth x + 1) := fun c hc => by + obtain ⟨-, hn, hμc, hdc⟩ := hkids c hc + obtain ⟨n, h, he, hhb⟩ := ih (μ (child c)) (hμ ▸ hμc) (child c) hn (by omega) rfl + rw [hdc] at hhb + exact ⟨n, h, he, hhb⟩ + choose! nodes height hev using hev + obtain ⟨h, hh, hloop⟩ := Loop.counted (I := I) (val := fun c => value (child c)) cnt + (D - (depth x + 1)) hnext hdone habs (fun c hc => (hev c hc).1) + (fun c hc => (hkids c hc).1) (fun c hc => (hev c hc).2) + rw [h0] at hloop + have hv : I.value (R.finish x (acc cnt)) := hfin ▸ hvalue x hx + exact ⟨(∑ c ∈ range cnt, nodes c) + 1, h + 1, hfin ▸ Eval.call hx hl hloop hv, by omega⟩ + · refine ⟨1, 0, ?_, Nat.zero_le _⟩ + rw [hleaf x hx hl] + exact Eval.leaf hx hl (hleaf x hx hl ▸ hvalue x hx) + /-- **What a clocked run returns.** For any fuel, a value returned by the run satisfies the invariant of a derivation of the root. -/ theorem value_of_run_eq_some {J : Type} {fuel : J → ℕ} {root : J → X} {a : J} {y y' : Y} From 7964b4ec2ea233bb56b5a35b7fe5fa7f978e121b Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:18:13 -0400 Subject: [PATCH 099/129] feat(circuit/frontier): the cost of a leaf of the fast contraction recursion Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/FastContractionLeaf.lean | 373 ++++++++++++++++++ .../TensorNetwork/FastContractionMachine.lean | 18 +- 2 files changed, 390 insertions(+), 1 deletion(-) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean new file mode 100644 index 0000000000..e93b600c1b --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean @@ -0,0 +1,373 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionMachine +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Lists +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Length + +/-! +# The leaves of the fast contraction algorithm + +The cost of the leaves of the native recursion of Theorem D.1 of [OpenAI2026ETH] +(`Cslib.Circuits.Frontier.FastContraction.leafCertificate`): direct summation over the `2 ^ M` +edge assignments of a call with `M` edges, whose packet entries cost `(m + 2) ^ L` for `m` target +modes and `L` row ports (§D.4.6–§D.4.7, pp. 63–64). + +## Main results + +* `Cslib.Circuits.Frontier.FastContraction.leafCertificate_le`: if every vertex of a call has at + most `R` target modes and at most `Λ` row ports, the leaf takes time at most + `K (n + R + 1) ^ K 2 ^ M (R + 2) ^ Λ` and space at most `K (n + R + 1) ^ K` on a call of encoded + length `n` with `M` edges. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.6–§D.4.7. +-/ + +public section + +namespace Cslib.Circuits.Frontier.FastContraction + +open GaloisRingArith NativePacket ChildCursor Cslib.GenericRAM Cslib.GenericRAM.StackRecursion + Turing.MultiTapeTM + +/-! ### Encoded lengths -/ + +/-- The precision and the coefficients of the modulus are at most the encoded length of a call. -/ +theorem prec_add_length_fl_le (x : Call) : x.prec + x.fl.length ≤ (encCall x).length := by + have h := length_le_length_encList encNat x.fl + obtain ⟨⟨s, d, b, p, fl, r⟩, D⟩ := x + simp only [Call.prec, Call.fl, encCall, encParams, length_encPair, encUnary_apply, + List.length_replicate] at h ⊢ + omega + +/-- The network of a call is at most its encoded length. -/ +theorem length_net_le (x : Call) : + (DescribedNetwork.encDescribed VertexDesc.enc x.net).length ≤ (encCall x).length := by + obtain ⟨p, D⟩ := x + simp only [Call.net, encCall, length_encPair] + omega + +/-- A vertex of a call is at most its encoded length. -/ +theorem length_vertex_le {x : Call} {v : List ℕ × VertexDesc} (hv : v ∈ x.net.vertices) : + (encPair (encList encNat) VertexDesc.enc v).length ≤ (encCall x).length := by + have h := length_le_encList_of_mem (encPair (encList encNat) VertexDesc.enc) hv + have h2 : (encList (encPair (encList encNat) VertexDesc.enc) x.net.vertices).length ≤ + (DescribedNetwork.encDescribed VertexDesc.enc x.net).length := by + change _ ≤ (encPair encUnary (encList (encPair (encList encNat) VertexDesc.enc)) + (x.net.numEdges, x.net.vertices)).length + rw [length_encPair] + dsimp only + omega + have h3 := length_net_le x + omega + +/-- The number of vertices of a call is at most its encoded length. -/ +theorem length_vertices_le (x : Call) : x.net.vertices.length ≤ (encCall x).length := by + have h := DescribedNetwork.length_numEdges_vertices_le (encT := VertexDesc.enc) x.net + have h3 := length_net_le x + omega + +/-! ### The cost of a leaf -/ + +/-- `c (y + 1) ^ d ≤ c W ^ d` when `y + 1 ≤ W`. -/ +private theorem mul_pow_le_of_le {c y d W : ℕ} (hy : y + 1 ≤ W) : c * (y + 1) ^ d ≤ c * W ^ d := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_left hy _) + +/-- Raising the exponent of `W ≥ 1`. -/ +private theorem mul_pow_le_mul_pow {c W d E : ℕ} (hW : 1 ≤ W) (hd : d ≤ E) : + c * W ^ d ≤ c * W ^ E := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_right hW hd) + +/-- The reduced coefficient lists against `W`. -/ +private theorem rv_le {n W cf df cp dp ct dt : ℕ} (hnW : n + 1 ≤ W) : + n * (2 * n + 3) + cf * (n + 1) ^ df * (2 * (cp * (n + 1) ^ dp) + 3) + + ct * (3 * n + 2) ^ dt ≤ + (5 + cf * (2 * cp + 3) + ct * 3 ^ dt) * W ^ (2 + df + dp + dt) := by + have hW1 : 1 ≤ W := by omega + have h1 : n * (2 * n + 3) ≤ 5 * W ^ 2 := + calc n * (2 * n + 3) ≤ W * (5 * W) := Nat.mul_le_mul (by omega) (by omega) + _ = 5 * W ^ 2 := by ring + have h2 : cf * (n + 1) ^ df * (2 * (cp * (n + 1) ^ dp) + 3) ≤ + cf * (2 * cp + 3) * W ^ (df + dp) := by + have hb1 : (n + 1) ^ df ≤ W ^ df := Nat.pow_le_pow_left hnW _ + have hb2 : cp * (n + 1) ^ dp ≤ cp * W ^ dp := mul_pow_le_of_le hnW + have hW1' : 1 ≤ W ^ dp := Nat.one_le_pow _ _ (by omega) + have : 2 * (cp * (n + 1) ^ dp) + 3 ≤ (2 * cp + 3) * W ^ dp := by + rw [add_mul, Nat.mul_assoc]; omega + calc cf * (n + 1) ^ df * (2 * (cp * (n + 1) ^ dp) + 3) + ≤ cf * W ^ df * ((2 * cp + 3) * W ^ dp) := + Nat.mul_le_mul (Nat.mul_le_mul_left _ hb1) this + _ = cf * (2 * cp + 3) * W ^ (df + dp) := by rw [pow_add]; ring + have h3 : ct * (3 * n + 2) ^ dt ≤ ct * 3 ^ dt * W ^ dt := + calc ct * (3 * n + 2) ^ dt ≤ ct * (3 * W) ^ dt := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_left (by omega) _) + _ = ct * 3 ^ dt * W ^ dt := by rw [mul_pow]; ring + have l1 : 5 * W ^ 2 ≤ 5 * W ^ (2 + df + dp + dt) := mul_pow_le_mul_pow hW1 (by omega) + have l2 : cf * (2 * cp + 3) * W ^ (df + dp) ≤ cf * (2 * cp + 3) * W ^ (2 + df + dp + dt) := + mul_pow_le_mul_pow hW1 (by omega) + have l3 : ct * 3 ^ dt * W ^ dt ≤ ct * 3 ^ dt * W ^ (2 + df + dp + dt) := + mul_pow_le_mul_pow hW1 (by omega) + rw [add_mul, add_mul] + omega + +/-- The input of direct summation against `W`. -/ +private theorem sum_le_of_le {W a b c₀ d₀ cR E₀ : ℕ} (hW : 1 ≤ W) (ha : a ≤ c₀ * W ^ d₀) + (hd : d₀ ≤ E₀) (hb : b ≤ cR * W ^ E₀) : a + b + b + 1 ≤ (c₀ + 2 * cR + 1) * W ^ E₀ := by + have h1 : c₀ * W ^ d₀ ≤ c₀ * W ^ E₀ := mul_pow_le_mul_pow hW hd + have h2 : 1 ≤ W ^ E₀ := Nat.one_le_pow _ _ (by omega) + rw [add_mul, add_mul, Nat.mul_assoc 2] + omega + +/-- The bound on the entries' arguments against `W`. -/ +private theorem zb_le {W n R Rv cR E₀ : ℕ} (hW : 1 ≤ W) (hE : 1 ≤ E₀) (hRv : Rv ≤ cR * W ^ E₀) + (hnR : 3 * n + R + 3 ≤ 3 * W) : 3 * n + R + Rv + 3 ≤ (3 + cR) * W ^ E₀ := by + have h1 : W ≤ W ^ E₀ := + calc W = W ^ 1 := (pow_one W).symm + _ ≤ W ^ E₀ := Nat.pow_le_pow_right hW hE + rw [add_mul] + omega + +/-- The arithmetic of the time of a leaf. -/ +private theorem time_assemble {Td Ti Ts cd dd ci di P X k W A Zb c₁ c₂ E₀ K₁ K₂ : ℕ} + (hW : 1 ≤ W) (hP : 1 ≤ P) (hX : 1 ≤ X) (hd : Td ≤ cd * W ^ dd) (hi : Ti ≤ ci * W ^ di) + (hs : Ts ≤ (P * (k + 1) + 1) * (K₁ * (A + 1) ^ K₁ + 2 * (K₂ * X * Zb ^ K₂))) + (hk : k + 1 ≤ W) (hA : A + 1 ≤ c₁ * W ^ E₀) (hZ : Zb ≤ c₂ * W ^ E₀) : + Td + (Ti + Ts) ≤ (2 * (K₁ * c₁ ^ K₁ + 2 * (K₂ * c₂ ^ K₂)) + cd + ci) * + W ^ (1 + E₀ * (K₁ + K₂) + dd + di) * P * X := by + have hWF1 : W ^ (E₀ * K₁) ≤ W ^ (E₀ * (K₁ + K₂)) := + Nat.pow_le_pow_right hW (by rw [Nat.mul_add]; omega) + have hWF2 : W ^ (E₀ * K₂) ≤ W ^ (E₀ * (K₁ + K₂)) := + Nat.pow_le_pow_right hW (by rw [Nat.mul_add]; omega) + set F := E₀ * (K₁ + K₂) + set S := W ^ (1 + F + dd + di) + have hA' : K₁ * (A + 1) ^ K₁ ≤ K₁ * c₁ ^ K₁ * W ^ F := + calc K₁ * (A + 1) ^ K₁ ≤ K₁ * (c₁ * W ^ E₀) ^ K₁ := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_left hA _) + _ = K₁ * c₁ ^ K₁ * W ^ (E₀ * K₁) := by rw [mul_pow, ← pow_mul, Nat.mul_assoc] + _ ≤ K₁ * c₁ ^ K₁ * W ^ F := Nat.mul_le_mul_left _ hWF1 + have hZ' : K₂ * X * Zb ^ K₂ ≤ K₂ * c₂ ^ K₂ * X * W ^ F := + calc K₂ * X * Zb ^ K₂ ≤ K₂ * X * (c₂ * W ^ E₀) ^ K₂ := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_left hZ _) + _ = K₂ * c₂ ^ K₂ * X * W ^ (E₀ * K₂) := by rw [mul_pow, ← pow_mul]; ring + _ ≤ K₂ * c₂ ^ K₂ * X * W ^ F := Nat.mul_le_mul_left _ hWF2 + have hin : K₁ * (A + 1) ^ K₁ + 2 * (K₂ * X * Zb ^ K₂) ≤ + (K₁ * c₁ ^ K₁ + 2 * (K₂ * c₂ ^ K₂)) * X * W ^ F := by + have : K₁ * c₁ ^ K₁ * W ^ F ≤ K₁ * c₁ ^ K₁ * W ^ F * X := Nat.le_mul_of_pos_right _ hX + have e : (K₁ * c₁ ^ K₁ + 2 * (K₂ * c₂ ^ K₂)) * X * W ^ F = + K₁ * c₁ ^ K₁ * W ^ F * X + 2 * (K₂ * c₂ ^ K₂ * X * W ^ F) := by ring + omega + have hPk : P * (k + 1) + 1 ≤ 2 * P * W := by + have : P * (k + 1) ≤ P * W := Nat.mul_le_mul_left _ hk + have : 1 ≤ P * W := Nat.one_le_iff_ne_zero.mpr (by positivity) + rw [Nat.mul_assoc]; omega + have hs' : Ts ≤ 2 * (K₁ * c₁ ^ K₁ + 2 * (K₂ * c₂ ^ K₂)) * S * P * X := + calc Ts ≤ (2 * P * W) * ((K₁ * c₁ ^ K₁ + 2 * (K₂ * c₂ ^ K₂)) * X * W ^ F) := + hs.trans (Nat.mul_le_mul hPk hin) + _ = 2 * (K₁ * c₁ ^ K₁ + 2 * (K₂ * c₂ ^ K₂)) * W ^ (1 + F) * P * X := by + rw [pow_add, pow_one]; ring + _ ≤ 2 * (K₁ * c₁ ^ K₁ + 2 * (K₂ * c₂ ^ K₂)) * S * P * X := + Nat.mul_le_mul_right _ (Nat.mul_le_mul_right _ + (Nat.mul_le_mul_left _ (Nat.pow_le_pow_right hW (by omega)))) + have hPX : 1 ≤ P * X := Nat.one_le_iff_ne_zero.mpr (by positivity) + have hd' : Td ≤ cd * S * P * X := + calc Td ≤ cd * S := hd.trans (mul_pow_le_mul_pow hW (by omega)) + _ ≤ cd * S * (P * X) := Nat.le_mul_of_pos_right _ hPX + _ = cd * S * P * X := by ring + have hi' : Ti ≤ ci * S * P * X := + calc Ti ≤ ci * S := hi.trans (mul_pow_le_mul_pow hW (by omega)) + _ ≤ ci * S * (P * X) := Nat.le_mul_of_pos_right _ hPX + _ = ci * S * P * X := by ring + have e : (2 * (K₁ * c₁ ^ K₁ + 2 * (K₂ * c₂ ^ K₂)) + cd + ci) * S * P * X = + 2 * (K₁ * c₁ ^ K₁ + 2 * (K₂ * c₂ ^ K₂)) * S * P * X + cd * S * P * X + ci * S * P * X := by + ring + omega + +/-- The arithmetic of the space of a leaf. -/ +private theorem space_assemble {Sd Si Ss cd dd ci di W A Zb c₁ c₂ E₀ K₁ K₂ ES : ℕ} + (hW : 1 ≤ W) (hd : Sd ≤ cd * W ^ dd) (hi : Si ≤ ci * W ^ di) + (hs : Ss ≤ K₁ * (A + 1) ^ K₁ + ES) (hA : A + 1 ≤ c₁ * W ^ E₀) (hES : ES = K₂ * Zb ^ K₂) + (hZ : Zb ≤ c₂ * W ^ E₀) : + Sd + (Si + Ss) ≤ (K₁ * c₁ ^ K₁ + K₂ * c₂ ^ K₂ + cd + ci) * + W ^ (E₀ * (K₁ + K₂) + dd + di) := by + have hWF1 : W ^ (E₀ * K₁) ≤ W ^ (E₀ * (K₁ + K₂) + dd + di) := + Nat.pow_le_pow_right hW (by rw [Nat.mul_add]; omega) + have hWF2 : W ^ (E₀ * K₂) ≤ W ^ (E₀ * (K₁ + K₂) + dd + di) := + Nat.pow_le_pow_right hW (by rw [Nat.mul_add]; omega) + set F := E₀ * (K₁ + K₂) + set S := W ^ (F + dd + di) + have hA' : K₁ * (A + 1) ^ K₁ ≤ K₁ * c₁ ^ K₁ * S := + calc K₁ * (A + 1) ^ K₁ ≤ K₁ * (c₁ * W ^ E₀) ^ K₁ := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_left hA _) + _ = K₁ * c₁ ^ K₁ * W ^ (E₀ * K₁) := by rw [mul_pow, ← pow_mul, Nat.mul_assoc] + _ ≤ K₁ * c₁ ^ K₁ * S := Nat.mul_le_mul_left _ hWF1 + have hZ' : ES ≤ K₂ * c₂ ^ K₂ * S := + calc ES = K₂ * Zb ^ K₂ := hES + _ ≤ K₂ * (c₂ * W ^ E₀) ^ K₂ := Nat.mul_le_mul_left _ (Nat.pow_le_pow_left hZ _) + _ = K₂ * c₂ ^ K₂ * W ^ (E₀ * K₂) := by rw [mul_pow, ← pow_mul, Nat.mul_assoc] + _ ≤ K₂ * c₂ ^ K₂ * S := Nat.mul_le_mul_left _ hWF2 + have hd' : Sd ≤ cd * S := hd.trans (mul_pow_le_mul_pow hW (by omega)) + have hi' : Si ≤ ci * S := hi.trans (mul_pow_le_mul_pow hW (by omega)) + have e : (K₁ * c₁ ^ K₁ + K₂ * c₂ ^ K₂ + cd + ci) * S = + K₁ * c₁ ^ K₁ * S + K₂ * c₂ ^ K₂ * S + cd * S + ci * S := by ring + omega + +/-- **The cost of a leaf.** There is `K` such that, on a call of encoded length `n` with `M` edges +whose vertices have at most `R` target modes and at most `Λ` row ports on every bit list on their +ports, direct summation takes time at most `K (n + R + 1) ^ K 2 ^ M (R + 2) ^ Λ` and space at most +`K (n + R + 1) ^ K`, whatever the child builder. -/ +theorem leafCertificate_le : ∃ K : ℕ, ∀ (mk : Call → State → ℕ × DescribedNetwork VertexDesc) + (x : Call) (R Λ : ℕ), + (∀ v ∈ x.net.vertices, v.2.modes ≤ R ∧ + ∀ bs : List Bool, bs.length = v.1.length → v.2.rowPorts bs ≤ Λ) → + (leafCertificate mk).time x ≤ + K * ((encCall x).length + R + 1) ^ K * 2 ^ x.edges * (R + 2) ^ Λ ∧ + (leafCertificate mk).space x ≤ K * ((encCall x).length + R + 1) ^ K := by + obtain ⟨K₁, hK₁⟩ := DescribedNetwork.contractionCertificate_le (ops := SumOps.galois) + (eval := VertexDesc.entry) (encQ := encPair encUnary (encList encNat)) + (encY := encList encNat) (encT := VertexDesc.enc) SumOps.galois_polyTime + VertexDesc.entryCertificate + obtain ⟨K₂, hK₂⟩ := VertexDesc.entryCertificate_le + have hD := NativeCertificate.ofPolyTime_isPolynomial depth_polyTime + have hI := NativeCertificate.ofPolyTime_isPolynomial leafInput_polyTime + obtain ⟨cd, dd, hdt⟩ := hD.1.exists_le + obtain ⟨cd', dd', hds⟩ := hD.2.exists_le + obtain ⟨ci, di, hit⟩ := hI.1.exists_le + obtain ⟨ci', di', his⟩ := hI.2.exists_le + obtain ⟨cl, dl, hl⟩ := leafInput_polyTime.polyBounded_length.exists_le + obtain ⟨cp, dp, hp⟩ := PacketCtx.p_polyTime.polyBounded_length.exists_le + obtain ⟨cf, df, hf⟩ := PacketCtx.fl_polyTime.polyBounded_length.exists_le + obtain ⟨ct, dt, ht⟩ := VertexDesc.tableEntry_polyTime.polyBounded_length.exists_le + -- The constants. + obtain ⟨e, he⟩ : ∃ e, e = 2 + df + dp + dt := ⟨_, rfl⟩ + obtain ⟨cR, hcR⟩ : ∃ cR, cR = 5 + cf * (2 * cp + 3) + ct * 3 ^ dt := ⟨_, rfl⟩ + obtain ⟨E₀, hE₀⟩ : ∃ E₀, E₀ = dl + e := ⟨_, rfl⟩ + obtain ⟨c₁, hc₁⟩ : ∃ c₁, c₁ = cl + 2 * cR + 1 := ⟨_, rfl⟩ + obtain ⟨c₂, hc₂⟩ : ∃ c₂, c₂ = 3 + cR := ⟨_, rfl⟩ + obtain ⟨G, hG⟩ : ∃ G, G = 2 * (K₁ * c₁ ^ K₁ + 2 * (K₂ * c₂ ^ K₂)) + cd + ci + + (K₁ * c₁ ^ K₁ + K₂ * c₂ ^ K₂) + cd' + ci' := ⟨_, rfl⟩ + obtain ⟨Dg, hDg⟩ : ∃ Dg, Dg = 1 + E₀ * (K₁ + K₂) + dd + di + dd' + di' := ⟨_, rfl⟩ + refine ⟨G + Dg, fun mk x R Λ hRΛ => ?_⟩ + obtain ⟨n, hn⟩ : ∃ n, n = (encCall x).length := ⟨_, rfl⟩ + obtain ⟨W, hW⟩ : ∃ W, W = n + R + 1 := ⟨_, rfl⟩ + rw [← hn, show n + R + 1 = W from hW.symm] + have hW1 : 1 ≤ W := by omega + have hnW : n + 1 ≤ W := by omega + -- The precision and the modulus of the call. + have hpfl := prec_add_length_fl_le x + rw [← hn] at hpfl + -- Every vertex and bit list on its ports is at most `3 n + 1` long. + have hq (v : List ℕ × VertexDesc) (hv : v ∈ x.net.vertices) (bs : List Bool) + (hbs : bs.length = v.1.length) : + (encPair VertexDesc.enc encBits (v.2, bs)).length ≤ 3 * n + 1 := by + have h1 := length_vertex_le hv + have h2 := length_le_length_encList encNat v.1 + rw [length_encPair] at h1 ⊢ + change 2 * (VertexDesc.enc v.2).length + 1 + bs.length ≤ 3 * n + 1 + omega + -- The reduced coefficient lists. + obtain ⟨Rv, hRv⟩ : ∃ Rv, Rv = n * (2 * n + 3) + cf * (n + 1) ^ df * (2 * (cp * (n + 1) ^ dp) + 3) + + ct * (3 * n + 2) ^ dt := ⟨_, rfl⟩ + have hred : ∀ y, SumOps.galois.Reduced (x.prec, x.fl) y → (encList encNat y).length ≤ Rv := by + intro y hy + have h := SumOps.length_encList_le_of_galois_reduced hy + simp only at h + have : x.fl.length * (2 * x.prec + 3) ≤ n * (2 * n + 3) := + Nat.mul_le_mul (by omega) (by omega) + omega + have hpacket (v : List ℕ × VertexDesc) (hv : v ∈ x.net.vertices) (d : PacketDesc) + (hd : v.2 = .inr d) : + ∀ y, SumOps.galois.Reduced (d.1.p, d.1.fl) y → (encList encNat y).length ≤ Rv := by + intro y hy + have h := SumOps.length_encList_le_of_galois_reduced hy + simp only at h + have h1 := length_vertex_le hv + rw [length_encPair, hd, length_encSum] at h1 + simp only [Sum.elim_inr] at h1 + have h2 : (PacketCtx.enc d.1).length ≤ n := by + have := length_encPair (enc₁ := PacketCtx.enc) + (enc₂ := encPair (encList (encPair encBool encNat)) (encList encUnary)) d + change (encPacketDesc d).length = _ at this + omega + have hpd : d.1.p ≤ cp * (n + 1) ^ dp := by + have := hp d.1 + rw [encUnary_apply, List.length_replicate] at this + exact this.trans (mul_pow_le_of_le (by omega)) + have hfd : d.1.fl.length ≤ cf * (n + 1) ^ df := + (length_le_length_encList encNat d.1.fl).trans ((hf d.1).trans (mul_pow_le_of_le (by omega))) + have : d.1.fl.length * (2 * d.1.p + 3) ≤ cf * (n + 1) ^ df * (2 * (cp * (n + 1) ^ dp) + 3) := + Nat.mul_le_mul hfd (by omega) + omega + -- The entries. + obtain ⟨Zb, hZb⟩ : ∃ Zb, Zb = 3 * n + R + Rv + 3 := ⟨_, rfl⟩ + have hentry : ∀ v ∈ ((x.prec, x.fl), x.net).2.vertices, ∀ bs : List Bool, + bs.length = v.1.length → + VertexDesc.entryCertificate.time (v.2, bs) ≤ K₂ * (R + 2) ^ Λ * Zb ^ K₂ ∧ + VertexDesc.entryCertificate.space (v.2, bs) ≤ K₂ * Zb ^ K₂ ∧ + (encList encNat (VertexDesc.entry v.2 bs)).length ≤ Rv := by + intro v hv bs hbs + have hlen := hq v hv bs hbs + obtain ⟨hmodes, hrows⟩ := hRΛ v hv + have hrows := hrows bs hbs + have hRLE : v.2.ReducedLE Rv := by + rcases hd : v.2 with t | d + · trivial + · exact hpacket v hv d hd + obtain ⟨hT, hS⟩ := hK₂ (v.2, bs) Rv hRLE + dsimp only at hT hS + have hZ : (encPair VertexDesc.enc encBits (v.2, bs)).length + v.2.modes + Rv + 2 ≤ Zb := by + omega + refine ⟨hT.trans ?_, hS.trans (Nat.mul_le_mul_left _ (Nat.pow_le_pow_left hZ _)), ?_⟩ + · refine Nat.mul_le_mul (Nat.mul_le_mul_left _ ?_) (Nat.pow_le_pow_left hZ _) + exact (Nat.pow_le_pow_left (by omega) _).trans (Nat.pow_le_pow_right (by omega) hrows) + · rcases hd : v.2 with t | d + · have := ht (v.2, bs) + rw [hd] at this + refine this.trans ?_ + have h3 : (encPair VertexDesc.enc encBits (Sum.inl t, bs)).length + 1 ≤ 3 * n + 2 := by + rw [← hd]; omega + calc ct * ((encPair VertexDesc.enc encBits (Sum.inl t, bs)).length + 1) ^ dt + ≤ ct * (3 * n + 2) ^ dt := Nat.mul_le_mul_left _ (Nat.pow_le_pow_left h3 _) + _ ≤ Rv := by omega + · exact hpacket v hv d hd _ (VertexDesc.reduced_entry_inr d bs) + obtain ⟨hsT, hsS⟩ := hK₁ ((x.prec, x.fl), x.net) (K₂ * (R + 2) ^ Λ * Zb ^ K₂) (K₂ * Zb ^ K₂) Rv + Rv hentry hred + -- Polynomial pieces against `W`. + have hk : x.net.vertices.length + 1 ≤ W := by have := length_vertices_le x; omega + have hRvW : Rv ≤ cR * W ^ e := by + rw [hRv, hcR, he]; exact rv_le hnW + have hWE : W ^ e ≤ W ^ E₀ := Nat.pow_le_pow_right hW1 (by omega) + have hA1 : (DescribedNetwork.encInput (encPair encUnary (encList encNat)) VertexDesc.enc + ((x.prec, x.fl), x.net)).length + Rv + Rv + 1 ≤ c₁ * W ^ E₀ := by + have h1 : (DescribedNetwork.encInput (encPair encUnary (encList encNat)) VertexDesc.enc + ((x.prec, x.fl), x.net)).length ≤ cl * W ^ dl := + (hl x).trans (by rw [← hn]; exact mul_pow_le_of_le hnW) + rw [hc₁] + exact sum_le_of_le hW1 h1 (by omega) (hRvW.trans (Nat.mul_le_mul_left _ hWE)) + have hZW : Zb ≤ c₂ * W ^ E₀ := by + rw [hc₂, hZb] + exact zb_le hW1 (by omega) (hRvW.trans (Nat.mul_le_mul_left _ hWE)) (by omega) + have hnW' : (encCall x).length + 1 ≤ W := by omega + have hdt' := (hdt x).trans (mul_pow_le_of_le (c := cd) (d := dd) hnW') + have hit' := (hit x).trans (mul_pow_le_of_le (c := ci) (d := di) hnW') + have hds' := (hds x).trans (mul_pow_le_of_le (c := cd') (d := dd') hnW') + have his' := (his x).trans (mul_pow_le_of_le (c := ci') (d := di') hnW') + have hP : 1 ≤ 2 ^ x.edges := Nat.one_le_two_pow + have hX : 1 ≤ (R + 2) ^ Λ := Nat.one_le_pow _ _ (by omega) + refine ⟨?_, ?_⟩ + · rw [leafCertificate_time] + refine (time_assemble hW1 hP hX hdt' hit' hsT hk hA1 hZW).trans ?_ + refine Nat.mul_le_mul_right _ (Nat.mul_le_mul_right _ (Nat.mul_le_mul (by omega) + (Nat.pow_le_pow_right hW1 (by omega)))) + · rw [leafCertificate_space] + refine (space_assemble hW1 hds' his' hsS hA1 rfl hZW).trans ?_ + exact Nat.mul_le_mul (by omega) (Nat.pow_le_pow_right hW1 (by omega)) + +end Cslib.Circuits.Frontier.FastContraction diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean index f93e9b6050..922fff07f2 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionMachine.lean @@ -118,12 +118,28 @@ variable (mk : Call → State → ℕ × DescribedNetwork VertexDesc) /-- **The leaf certificate**: direct summation over the coefficient lists of `A_p`, with the native evaluator of the vertex descriptors, tagged with the depth. -/ -noncomputable def leafCertificate : NativeCertificate (scheme mk).leaf encCall encValue := +@[expose] noncomputable def leafCertificate : NativeCertificate (scheme mk).leaf encCall encValue := ((NativeCertificate.ofPolyTime depth_polyTime).prod ((DescribedNetwork.contractionCertificate VertexDesc.entry SumOps.galois_polyTime VertexDesc.entryCertificate).comp (NativeCertificate.ofPolyTime leafInput_polyTime))).congr fun _ => rfl +/-- The time of a leaf: the depth, the input of direct summation, and direct summation. -/ +theorem leafCertificate_time (x : Call) : + (leafCertificate mk).time x = + (NativeCertificate.ofPolyTime depth_polyTime).time x + + ((NativeCertificate.ofPolyTime leafInput_polyTime).time x + + (DescribedNetwork.contractionCertificate VertexDesc.entry SumOps.galois_polyTime + VertexDesc.entryCertificate).time ((x.prec, x.fl), x.net)) := rfl + +/-- The space of a leaf. -/ +theorem leafCertificate_space (x : Call) : + (leafCertificate mk).space x = + (NativeCertificate.ofPolyTime depth_polyTime).space x + + ((NativeCertificate.ofPolyTime leafInput_polyTime).space x + + (DescribedNetwork.contractionCertificate VertexDesc.entry SumOps.galois_polyTime + VertexDesc.entryCertificate).space ((x.prec, x.fl), x.net)) := rfl + /-! ### The root -/ /-- The exponent `t` of the modulus, with `2 ^ (t + 1) > 4 ⌊log₂ m⌋ + 2`. -/ From 54de2851ab840b528ad18b4770e261ecc104d42d Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:19:13 -0400 Subject: [PATCH 100/129] feat(circuit/frontier): the native recursion follows the schedule Every call reached from a root on the schedule stays on it once the schedule's certificate holds: it keeps the scale, modulus, well-formedness and degree bound, and is small or an active call of the schedule, which then passes the machine's test. So the leaves have at most leafSize m edges and the active calls lie above depth log m - 2. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../FastContractionDerivation.lean | 8 +- .../FastContractionSchedule.lean | 213 ++++++++++++++++++ 3 files changed, 218 insertions(+), 4 deletions(-) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean diff --git a/Complexity.lean b/Complexity.lean index 3354e31410..bb48bbd41f 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -900,6 +900,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContraction public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionDerivation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionMachine +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionSchedule public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionStep public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates public import Complexity.Computability.Circuit.Frontier.TensorNetwork.KernelProduct diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionDerivation.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionDerivation.lean index 79654aafce..daf2bd07db 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionDerivation.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionDerivation.lean @@ -19,8 +19,8 @@ given a bound on the children of each call, their number of nodes. This is the r Theorem D.1 of [OpenAI2026ETH] (Appendix D.4.7, p. 64), for any child builder. **The invariant** (`FastContraction.reachInv`). The nodes of a derivation from a root `x₀` are the -calls reached from `x₀`, its frames are the frames `FastContraction.frameAt` of a reached active call -along its children, and its values are the tagged values of reached calls. Every bound on reached +calls reached from `x₀`, its frames are the frames `FastContraction.frameAt` of a reached active +call along its children, and its values are the tagged values of reached calls. Every bound on reached calls is a bound on the derivation. **The height.** The children of a call receive its number of edges as their bound, and an active @@ -177,8 +177,8 @@ theorem exists_eval_reachInv (hx₀ : x₀.depth = 0) : exact ⟨N, H, he, by omega⟩ /-- **A derivation of the root with its node count.** If every reached active call has depth below -`D` and at most `b d` children at depth `d`, a root at depth `0` has a derivation of its tagged value -over the reached calls with at most `Nat.recursionBound b D 0` nodes and `D` stack levels. -/ +`D` and at most `b d` children at depth `d`, a root at depth `0` has a derivation of its tagged +value over the reached calls with at most `Nat.recursionBound b D 0` nodes and `D` stack levels. -/ theorem exists_eval_reachInv_count (hx₀ : x₀.depth = 0) (D : ℕ) (b : ℕ → ℕ) (hcnt : ∀ x, Reach mk x₀ x → active x = true → x.depth < D ∧ (children x).length ≤ b x.depth) : diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean new file mode 100644 index 0000000000..f0ce875a44 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean @@ -0,0 +1,213 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionDerivation +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionMachine +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionStep +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeChildBounds +import Mathlib.Data.List.GetD + +/-! +# The native contraction recursion follows the schedule + +For a root with `m` edges, the calls of the native recursion +(`FastContraction.scheme FastContraction.nativeMk`) follow the parameter schedule of +`Cslib.Circuits.Frontier.ContractionSchedule` at scale `m` (§D.4.2 of [OpenAI2026ETH], p. 58): +once `m` is large enough for the schedule's certificate `ContractionSchedule.ActiveBounds`, every +reached call either has at most `leafSize m` edges or is an active call of the schedule +(`ContractionSchedule.Active`), and every reached call of the second kind passes the test +`FastContraction.active` of the machine. + +**The invariant** (`FastContraction.OnSchedule`): the call keeps the root's scale and modulus, is +well-formed, has maximum degree at most `degreeBound m`, has fewer edges than its bound, and is +small or active in the schedule. It passes from an active call to its native children +(`FastContraction.OnSchedule.child`) by round 10's child facts in the native numbering: at most +`M / 16` edges (`NativeChild.sixteen_mul_numEdges_nativeChild_le`), the degree bound +(`NativeChild.length_le_of_mem_vertices_nativeChild`), and the schedule's precisions +(`ContractionSchedule.ActiveBounds.nodePrecision_le`). + +**Consequences** for the reached calls: a call that the machine treats as a leaf has at most +`leafSize m` edges (`FastContraction.OnSchedule.edges_le_leafSize`), and an active one lies above +depth `⌊log₂ m⌋ - 2` (`FastContraction.OnSchedule.depth_add_two_le`). + +## Main definitions + +* `Cslib.Circuits.Frontier.FastContraction.OnSchedule`: the invariant. + +## Main results + +* `Cslib.Circuits.Frontier.FastContraction.OnSchedule.active_eq_true`: an active call of the + schedule passes the machine's test. +* `Cslib.Circuits.Frontier.FastContraction.OnSchedule.child`: the invariant passes to the native + children. +* `Cslib.Circuits.Frontier.FastContraction.Reach.onSchedule`: every reached call satisfies it. +* `Cslib.Circuits.Frontier.FastContraction.OnSchedule.edges_le_leafSize`: the leaves are small. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.2 (p. 58) and + §D.4.6–§D.4.7 (pp. 63–64). +-/ + +public section + +namespace Cslib.Circuits.Frontier.FastContraction + +open ContractionSchedule ChildCursor NativeRecovery NativeChild + +/-- **A call on the schedule** of a root with `m` edges, for the constant `C`: it keeps the root's +scale and modulus, is well-formed, has maximum degree at most `degreeBound m` and fewer edges than +its bound, and has at most `leafSize m` edges or is an active call of the schedule. -/ +structure OnSchedule (C m : ℕ) (x : Call) : Prop where + /-- The scale is the root's number of edges. -/ + scale_eq : x.scale = m + /-- The modulus is the root's. -/ + fl_eq : x.fl = modulus m + /-- The call is well-formed. -/ + wf : WF x + /-- The maximum degree is at most `degreeBound m`. -/ + degree_le : ∀ v ∈ x.net.vertices, v.1.length ≤ degreeBound m + /-- The call has fewer edges than its bound. -/ + edges_lt_bound : x.edges < x.bound + /-- The call is small, or an active call of the schedule. -/ + small_or_active : x.edges ≤ leafSize m ∨ Active C m x.depth x.edges x.prec + +/-- `4 ⌊log₂ m⌋ ≤ 2 ⬝ 3 ^ t` for the exponent `t` of the modulus. -/ +theorem four_mul_log_le_length_modulus (m : ℕ) : 4 * Nat.log 2 m ≤ (modulus m).length := by + rw [length_modulus] + set t := modulusExp m + have h1 : 4 * Nat.log 2 m + 2 < 2 ^ (t + 1) := Nat.lt_pow_succ_log_self one_lt_two _ + have h2 : 2 ^ t ≤ 3 ^ t := Nat.pow_le_pow_left (by norm_num) t + rw [pow_succ] at h1 + omega + +variable {C m : ℕ} {x : Call} + +namespace OnSchedule + +/-- **An active call of the schedule passes the machine's test** once the schedule's certificate +holds: the certificate gives `1 ≤ u ≤ M`, `u ≤ a` and the number of points, and the invariant gives +the degrees and the edge bound. -/ +theorem active_eq_true (hB : ∀ d M p, Active C m d M p → ActiveBounds C m d M p) + (h : OnSchedule C m x) (hA : Active C m x.depth x.edges x.prec) : active x = true := by + have hb := hB _ _ _ hA + have hpts : numPoints x ≤ 2 ^ x.fl.length := by + unfold numPoints slots + rw [h.scale_eq, h.fl_eq] + exact hb.points_le.trans (Nat.pow_le_pow_right (by norm_num) + (four_mul_log_le_length_modulus m)) + simp only [active, decide_eq_true_eq, levels, slots, h.scale_eq] + refine ⟨hA.leafSize_lt, hb.one_le_taper, hb.taper_le_edges, ?_, h.degree_le, ?_, + h.edges_lt_bound⟩ + · rw [slotBits_eq]; omega + · simpa only [numPoints, slots, h.scale_eq] using hpts + +/-- An active call on the schedule is active in the schedule: it has more than `leafSize m` +edges. -/ +theorem active_of_active (h : OnSchedule C m x) (ha : active x = true) : + Active C m x.depth x.edges x.prec := by + have ha' := ha + simp only [active, decide_eq_true_eq] at ha' + rcases h.small_or_active with hs | hA + · rw [h.scale_eq] at ha' + omega + · exact hA + +/-- **The leaves are small**: a call on the schedule that the machine treats as a leaf has at most +`leafSize m` edges, once the schedule's certificate holds. -/ +theorem edges_le_leafSize (hB : ∀ d M p, Active C m d M p → ActiveBounds C m d M p) + (h : OnSchedule C m x) (hl : active x = false) : x.edges ≤ leafSize m := by + rcases h.small_or_active with hs | hA + · exact hs + · rw [h.active_eq_true hB hA] at hl + cases hl + +/-- **An active call lies above depth `⌊log₂ m⌋ - 2`**, once the schedule's certificate holds. -/ +theorem depth_add_two_le (hB : ∀ d M p, Active C m d M p → ActiveBounds C m d M p) + (h : OnSchedule C m x) (ha : active x = true) : x.depth + 2 ≤ Nat.log 2 m := + (hB _ _ _ (h.active_of_active ha)).depth_add_two_le + +/-- The level of the node of a child of an active call is below `u`. -/ +theorem level_lt_levels {c : State} (hc : c ∈ children x) : + level x ((nodes x).getD c.1 0) < levels x := by + obtain ⟨hj, -, -⟩ := lt_of_mem_children hc + have hkmem : (nodes x).getD c.1 0 ∈ nodes x := by + rw [List.getD_eq_getElem _ _ hj]; exact List.getElem_mem hj + obtain ⟨hk, -, hq⟩ := mem_nodes.mp hkmem + rw [level_eq hk] + exact padicValInt_lt_of_taperMult_pos_left (by rwa [← mult_eq _ _ hk]) + +/-- **The invariant passes to the native children** of an active call, once the schedule's +certificate holds: a child has at most a sixteenth of the edges, keeps the degree bound, and its +precision is within the schedule. -/ +theorem child (hB : ∀ d M p, Active C m d M p → ActiveBounds C m d M p) (h : OnSchedule C m x) + (ha : active x = true) {c : State} (hc : c ∈ children x) : + OnSchedule C m (FastContraction.child x nativeMk c) := by + have hA := h.active_of_active ha + have hb := hB _ _ _ hA + obtain ⟨-, -, hg⟩ := lt_of_mem_children hc + have hK := pow_slots_le_edges_of_active ha + have hu := level_lt_levels hc + have ha' := ha + simp only [active, decide_eq_true_eq] at ha' + obtain ⟨-, -, -, hua, -, -, -⟩ := ha' + have hia : level x ((nodes x).getD c.1 0) < slots x := hu.trans_le hua + have hslots : slots x = slotBits m x.depth := by rw [slots, h.scale_eq] + have hlevels : levels x = taper m x.depth := by rw [levels, h.scale_eq] + -- The child's edges. + have h16 : 16 * (FastContraction.child x nativeMk c).edges ≤ x.edges := by + have := sixteen_mul_numEdges_nativeChild_le h.wf.2 hK hia h.degree_le + (Δ := degreeBound m) (by rw [hslots]; exact hb.degree_mul_pairs_sq_le) x.fl + (nodePrec x c.1) (node (slots x) ((nodes x).getD c.1 0)) ((points x).getD c.2.1 []) + c.2.2.1 ⟨c.2.2.2, hg⟩ + simpa only [FastContraction.child, nativeMk, packing, Call.edges] using this + have hM := one_le_edges_of_active ha + refine ⟨h.scale_eq, h.fl_eq, wfChildren_nativeMk x h.wf ha c hc, fun v hv => ?_, ?_, ?_⟩ + · refine length_le_of_mem_vertices_nativeChild h.wf.2 hK hia h.degree_le + (by rw [hslots]; exact hb.two_mul_slots_le) x.fl (nodePrec x c.1) + (node (slots x) ((nodes x).getD c.1 0)) ((points x).getD c.2.1 []) c.2.2.1 ⟨c.2.2.2, hg⟩ ?_ + simpa only [FastContraction.child, nativeMk, packing] using hv + · change _ < x.edges + omega + · by_cases hs : (FastContraction.child x nativeMk c).edges ≤ leafSize m + · exact Or.inl hs + · refine Or.inr (hA.child h16 (by omega) ?_) + change nodePrec x c.1 ≤ _ + unfold nodePrec + rw [hslots] + refine hb.nodePrecision_le _ _ (by rw [← hlevels]; exact hu) ?_ + have := taperStep_mul_le x.scale x.depth x.edges + rw [h.scale_eq] at this + simpa only [step, h.scale_eq] using this + +end OnSchedule + +/-- **Every reached call is on the schedule**, from a root on the schedule, once the schedule's +certificate holds. -/ +theorem Reach.onSchedule (hB : ∀ d M p, Active C m d M p → ActiveBounds C m d M p) {x₀ : Call} + (h₀ : OnSchedule C m x₀) (h : Reach nativeMk x₀ x) : OnSchedule C m x := by + induction h with + | root => exact h₀ + | step c _ ha hc ih => exact ih.child hB ha hc + +/-- **The root is on the schedule**: a well-formed root with `m` edges, at scale `m` with the +modulus of `m`, of maximum degree at most `degreeBound m`, with edge bound `m + 1` and precision at +most `C m` for `1 ≤ C`. -/ +theorem onSchedule_root {x₀ : Call} (hC : 1 ≤ C) (hs : x₀.scale = m) (he : x₀.edges = m) + (hfl : x₀.fl = modulus m) (hwf : WF x₀) + (hdeg : ∀ v ∈ x₀.net.vertices, v.1.length ≤ degreeBound m) + (hb : x₀.bound = m + 1) (hd : x₀.depth = 0) (hp : x₀.prec ≤ C * m) : OnSchedule C m x₀ := by + refine ⟨hs, hfl, hwf, hdeg, by omega, ?_⟩ + by_cases hl : x₀.edges ≤ leafSize m + · exact Or.inl hl + · refine Or.inr ?_ + rw [hd, he] + exact Active.root (Nat.le_mul_of_pos_left m hC) (by omega) hp + +end Cslib.Circuits.Frontier.FastContraction From 5f206b033ec6f83edfd76c0d69c048bccf848763 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:22:01 -0400 Subject: [PATCH 101/129] feat(circuit/frontier): the packet shape of the native recursion and the leaf cost under it Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/FastContractionLeaf.lean | 217 +++++++++++++++++- 1 file changed, 209 insertions(+), 8 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean index e93b600c1b..8a281f926f 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean @@ -8,7 +8,9 @@ module -- upstream: candidate public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionMachine +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionStep import Complexity.Computability.Machines.Turing.MultiTape.Complexity.Lists +import Mathlib.Data.List.GetD import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Length /-! @@ -33,18 +35,81 @@ modes and `L` row ports (§D.4.6–§D.4.7, pp. 63–64). public section -namespace Cslib.Circuits.Frontier.FastContraction +namespace Cslib.Circuits.Frontier -open GaloisRingArith NativePacket ChildCursor Cslib.GenericRAM Cslib.GenericRAM.StackRecursion - Turing.MultiTapeTM +/-! ### The descriptors of the native children -/ + +namespace NativeChild + +open ChildNumbering + +variable {D : DescribedNetwork VertexDesc} {r : ℕ} + +/-- **The descriptors of the native child.** A vertex of the native child of a rows-first parent +with at least `64 ^ a` edges keeps the descriptor of a parent vertex, or is the packet `q`, with +the packet context of `q`, the slice of `β` at `q`, and tags whose slots are below `2 ^ a` and +distinct among its row ports. -/ +theorem desc_of_mem_nativeChild (hD : D.IsRowsFirstBipartite r) {fl : List ℕ} {a i : ℕ} + (hK : 64 ^ a ≤ D.numEdges) (hia : i < a) (pt : ℕ) (t : ℤ) (ζ β : List ℕ) (g : Fin 64) + {v : List ℕ × VertexDesc} + (hv : v ∈ (nativeChild fl a i pt t ζ r D (nativeParentPacking r a D) β g).vertices) : + (∃ w ∈ D.vertices, v.2 = w.2) ∨ + ∃ q tags, v.2 = .inr (packetCtx fl a i pt t ζ g q, tags, betaSlice i pt β q) ∧ + (∀ y ∈ tags, y.2 < 2 ^ a) ∧ ((tags.filter (·.1)).map (·.2)).Nodup := by + have hgroups := groupsOf_eq hD hK hia + have hpk1 : (nativeParentPacking r a D).2.1 = (parentPacking hD a).numPackets := by + rw [nativeParentPacking_eq hD hK] + have hpk2 : (nativeParentPacking r a D).2.2 = List.ofFn fun e => + (((parentPacking hD a).packet e : ℕ), ((parentPacking hD a).slot e : ℕ)) := by + rw [nativeParentPacking_eq hD hK] + rw [nativeChild_eq hD _ (parentPacking hD a).packet (fun e => ((parentPacking hD a).slot e : ℕ)) + g fl a i pt t ζ β _ hgroups hpk1 hpk2] at hv + simp only [List.mem_ofFn] at hv + obtain ⟨c, rfl⟩ := hv + rcases hc : (childVertexEquiv (P := (parentPacking hD a).numPackets) + (packingGroup (parentSide r) (parentPacking hD a) ⟨i, hia⟩) g).symm c with w | q + · left + refine ⟨D.vertices[(w.1 : ℕ)], List.getElem_mem _, ?_⟩ + simp only [numberedDesc, Sum.elim_inl] + · right + simp only [numberedDesc, Sum.elim_inr] + refine ⟨q, _, rfl, ?_, ?_⟩ + · intro y hy + simp only [List.mem_map] at hy + obtain ⟨e, -, rfl⟩ := hy + exact Fin.isLt _ + · rw [List.filter_map, List.map_map] + refine List.Nodup.map_on ?_ ((nodup_packetPorts hD _ _ g q).filter _) + intro c₁ hc₁ c₂ hc₂ heq + simp only [List.mem_filter, Function.comp_apply] at hc₁ hc₂ + obtain ⟨hm₁, hl₁⟩ := hc₁ + obtain ⟨hm₂, hl₂⟩ := hc₂ + rw [mem_packetPorts] at hm₁ hm₂ + rcases c₁ with e₁ | e₁ + · rcases c₂ with e₂ | e₂ + · simp only [Function.comp_apply, Sum.elim_inl] at heq hm₁ hm₂ + have hinj := (parentPacking hD a).injective (a₁ := e₁.1) (a₂ := e₂.1) + (Prod.ext (hm₁.trans hm₂.symm) (Fin.ext heq)) + rw [Subtype.ext hinj] + · simp at hl₂ + · simp at hl₁ + +end NativeChild + +namespace FastContraction + +open GaloisRingArith NativePacket ChildCursor NativeRecovery ContractionSchedule NativeChild + Cslib.GenericRAM Cslib.GenericRAM.StackRecursion Turing.MultiTapeTM /-! ### Encoded lengths -/ -/-- The precision and the coefficients of the modulus are at most the encoded length of a call. -/ -theorem prec_add_length_fl_le (x : Call) : x.prec + x.fl.length ≤ (encCall x).length := by +/-- The scale, the precision and the coefficients of the modulus are at most the encoded length +of a call. -/ +theorem scale_add_prec_add_length_fl_le (x : Call) : + x.scale + x.prec + x.fl.length ≤ (encCall x).length := by have h := length_le_length_encList encNat x.fl obtain ⟨⟨s, d, b, p, fl, r⟩, D⟩ := x - simp only [Call.prec, Call.fl, encCall, encParams, length_encPair, encUnary_apply, + simp only [Call.scale, Call.prec, Call.fl, encCall, encParams, length_encPair, encUnary_apply, List.length_replicate] at h ⊢ omega @@ -261,7 +326,7 @@ theorem leafCertificate_le : ∃ K : ℕ, ∀ (mk : Call → State → ℕ × De have hW1 : 1 ≤ W := by omega have hnW : n + 1 ≤ W := by omega -- The precision and the modulus of the call. - have hpfl := prec_add_length_fl_le x + have hpfl := scale_add_prec_add_length_fl_le x rw [← hn] at hpfl -- Every vertex and bit list on its ports is at most `3 n + 1` long. have hq (v : List ℕ × VertexDesc) (hv : v ∈ x.net.vertices) (bs : List Bool) @@ -370,4 +435,140 @@ theorem leafCertificate_le : ∃ K : ℕ, ∀ (mk : Call → State → ℕ × De refine (space_assemble hW1 hds' his' hsS hA1 rfl hZW).trans ?_ exact Nat.mul_le_mul (by omega) (Nat.pow_le_pow_right hW1 (by omega)) -end Cslib.Circuits.Frontier.FastContraction +/-! ### The shape of the packets -/ + +/-- **The shape of the packets of a call**: every packet vertex has `i < a ≤ ⌊log₂ s⌋ / 32768`, +`2 ^ (i + 1)` tiles and at most `p` exponents per tile, and the slots of its ports are below +`2 ^ a` and distinct among its row ports. So it has at most `2 ^ (⌊log₂ s⌋ / 32768) p` target modes +and at most `2 ^ (⌊log₂ s⌋ / 32768)` row ports (`modes_le_of_packetShape`, +`rowPorts_le_of_packetShape`). -/ +def PacketShape (x : Call) : Prop := + ∀ v ∈ x.net.vertices, ∀ d : PacketDesc, v.2 = .inr d → + d.1.i < d.1.a ∧ d.1.a ≤ Nat.log 2 x.scale / 32768 ∧ d.1.tiles = 2 ^ (d.1.i + 1) ∧ + d.1.nb ≤ x.prec ∧ (∀ y ∈ d.2.1, y.2 < 2 ^ d.1.a) ∧ ((d.2.1.filter (·.1)).map (·.2)).Nodup + +/-- **The root has no packets.** -/ +theorem packetShape_root (q : ℕ × ℕ × ExplicitTensorNetwork ℕ) : PacketShape (root q) := by + intro v hv d hd + simp only [root, rootNet, Call.net, List.mem_map] at hv + obtain ⟨w, -, rfl⟩ := hv + cases hd + +/-- **The packet shape passes to the native children** of a well-formed active call: a retained +vertex keeps its descriptor, at a precision that only grows, and a new packet has `a` slot bits +(`slotBits_le`), `i < a` (the valuation of its node), `2 ^ (i + 1)` tiles, the child's precision +as its number of exponents per tile, and the slots of the packing, distinct within a packet. -/ +theorem packetShape_child {x : Call} (hx : WF x) (ha : active x = true) (hs : PacketShape x) + {c : State} (hc : c ∈ children x) : PacketShape (child x nativeMk c) := by + obtain ⟨-, hD⟩ := hx + obtain ⟨hj, -, hg⟩ := lt_of_mem_children hc + have ha' := ha + simp only [active, decide_eq_true_eq] at ha' + obtain ⟨-, -, -, hua, -, -, -⟩ := ha' + have hkmem : (nodes x).getD c.1 0 ∈ nodes x := by + rw [List.getD_eq_getElem _ _ hj]; exact List.getElem_mem hj + obtain ⟨hk, -, hq⟩ := mem_nodes.mp hkmem + have hia : level x ((nodes x).getD c.1 0) < slots x := by + rw [level_eq hk] + exact padicValInt_lt_of_taperMult_pos (by rwa [← mult_eq _ _ hk]) hua + have hp : x.prec ≤ nodePrec x c.1 := by unfold nodePrec nodePrecision; omega + intro v hv d hd + have hv' : v ∈ (nativeChild x.fl (slots x) (level x ((nodes x).getD c.1 0)) (nodePrec x c.1) + (node (slots x) ((nodes x).getD c.1 0)) ((points x).getD c.2.1 []) x.rows x.net + (nativeParentPacking x.rows (slots x) x.net) c.2.2.1 (⟨c.2.2.2, hg⟩ : Fin 64)).vertices := hv + rcases NativeChild.desc_of_mem_nativeChild hD (pow_slots_le_edges_of_active ha) hia _ _ _ _ _ + hv' with ⟨w, hw, hvw⟩ | ⟨q, tags, hvq, hslot, hnodup⟩ + · obtain ⟨h1, h2, h3, h4, h5, h6⟩ := hs w hw d (hvw ▸ hd) + exact ⟨h1, h2, h3, h4.trans hp, h5, h6⟩ + · rw [hvq] at hd + cases hd + exact ⟨hia, slotBits_le _ _, rfl, le_rfl, hslot, hnodup⟩ + +/-- The positions of a list satisfying a test are as many as its elements satisfying it. -/ +private theorem length_filter_range_getD {α : Type} (l : List α) (d : α) (p : α → Bool) : + ((List.range l.length).filter fun j => p (l.getD j d)).length = (l.filter p).length := by + have hl : (List.range l.length).map (fun j => l.getD j d) = l := by + refine List.ext_getElem (by simp) fun j h₁ h₂ => ?_ + simp only [List.getElem_map, List.getElem_range, List.getD_eq_getElem _ _ h₂] + conv_rhs => rw [← hl] + rw [List.filter_map, List.length_map] + rfl + +/-- `2 ^ (⌊log₂ s⌋ / 32768)` is at most `s + 1`. -/ +theorem two_pow_log_div_le (s : ℕ) : 2 ^ (Nat.log 2 s / 32768) ≤ s + 1 := by + rcases Nat.eq_zero_or_pos s with rfl | hs + · simp + · calc 2 ^ (Nat.log 2 s / 32768) ≤ 2 ^ Nat.log 2 s := + Nat.pow_le_pow_right (by norm_num) (Nat.div_le_self _ _) + _ ≤ s := Nat.pow_log_le_self 2 hs.ne' + _ ≤ s + 1 := Nat.le_succ s + +/-- **The target modes of a call with the packet shape**: at most `2 ^ (⌊log₂ s⌋ / 32768) p`. -/ +theorem modes_le_of_packetShape {x : Call} (hs : PacketShape x) {v : List ℕ × VertexDesc} + (hv : v ∈ x.net.vertices) : v.2.modes ≤ 2 ^ (Nat.log 2 x.scale / 32768) * x.prec := by + rcases hd : v.2 with t | d + · simp [VertexDesc.modes] + · obtain ⟨h1, h2, h3, h4, -, -⟩ := hs v hv d hd + simp only [VertexDesc.modes, targetModes] + calc ((List.range (d.1.tiles * d.1.nb)).filter _).length + ≤ (List.range (d.1.tiles * d.1.nb)).length := List.length_filter_le _ _ + _ = d.1.tiles * d.1.nb := List.length_range + _ ≤ 2 ^ (Nat.log 2 x.scale / 32768) * x.prec := by + rw [h3] + exact Nat.mul_le_mul (Nat.pow_le_pow_right (by norm_num) (by omega)) h4 + +/-- **The row ports of a call with the packet shape**: at most `2 ^ (⌊log₂ s⌋ / 32768)`, since +the slots of the row ports of a packet are distinct and below `2 ^ a`. -/ +theorem rowPorts_le_of_packetShape {x : Call} (hs : PacketShape x) {v : List ℕ × VertexDesc} + (hv : v ∈ x.net.vertices) (bs : List Bool) : + v.2.rowPorts bs ≤ 2 ^ (Nat.log 2 x.scale / 32768) := by + rcases hd : v.2 with t | d + · simp [VertexDesc.rowPorts] + · obtain ⟨-, h2, -, -, h5, h6⟩ := hs v hv d hd + simp only [VertexDesc.rowPorts, sidePorts, List.length_map] + have hlen := length_filter_range_getD d.2.1 (false, 0) (fun y => y.1 == true) + rw [hlen] + have hsub : ((d.2.1.filter (·.1)).map (·.2)).toFinset ⊆ Finset.range (2 ^ d.1.a) := by + intro y hy + simp only [List.mem_toFinset, List.mem_map, List.mem_filter] at hy + obtain ⟨z, ⟨hz, -⟩, rfl⟩ := hy + exact Finset.mem_range.mpr (h5 z hz) + have hcard := Finset.card_le_card hsub + rw [List.toFinset_card_of_nodup h6, Finset.card_range, List.length_map] at hcard + have hbeq : (d.2.1.filter fun y => y.1 == true) = d.2.1.filter (·.1) := by + congr 1 + rw [hbeq] + exact hcard.trans (Nat.pow_le_pow_right (by norm_num) h2) + +/-- **The cost of a leaf with the packet shape.** There is `K` such that, on a call of encoded +length `n` with `M` edges, scale `s` and precision `p` with the packet shape, writing +`A = 2 ^ (⌊log₂ s⌋ / 32768)`, direct summation takes time at most +`K (n + 1) ^ K 2 ^ M (A p + 2) ^ A` and space at most `K (n + 1) ^ K`. -/ +theorem leafCertificate_le_of_packetShape : ∃ K : ℕ, + ∀ (mk : Call → State → ℕ × DescribedNetwork VertexDesc) (x : Call), PacketShape x → + (leafCertificate mk).time x ≤ K * ((encCall x).length + 1) ^ K * 2 ^ x.edges * + (2 ^ (Nat.log 2 x.scale / 32768) * x.prec + 2) ^ 2 ^ (Nat.log 2 x.scale / 32768) ∧ + (leafCertificate mk).space x ≤ K * ((encCall x).length + 1) ^ K := by + obtain ⟨K, hK⟩ := leafCertificate_le + refine ⟨2 * K, fun mk x hs => ?_⟩ + obtain ⟨hT, hS⟩ := hK mk x (2 ^ (Nat.log 2 x.scale / 32768) * x.prec) + (2 ^ (Nat.log 2 x.scale / 32768)) fun v hv => + ⟨modes_le_of_packetShape hs hv, fun bs _ => rowPorts_le_of_packetShape hs hv bs⟩ + have hn := scale_add_prec_add_length_fl_le x + have hA := two_pow_log_div_le x.scale + have hR : (encCall x).length + 2 ^ (Nat.log 2 x.scale / 32768) * x.prec + 1 ≤ + ((encCall x).length + 1) ^ 2 := by + have : 2 ^ (Nat.log 2 x.scale / 32768) * x.prec ≤ ((encCall x).length + 1) * (encCall x).length := + Nat.mul_le_mul (by omega) (by omega) + nlinarith + have hpow : K * ((encCall x).length + 2 ^ (Nat.log 2 x.scale / 32768) * x.prec + 1) ^ K ≤ + 2 * K * ((encCall x).length + 1) ^ (2 * K) := + calc _ ≤ K * (((encCall x).length + 1) ^ 2) ^ K := Nat.mul_le_mul_left _ (Nat.pow_le_pow_left hR _) + _ = K * ((encCall x).length + 1) ^ (2 * K) := by rw [← pow_mul] + _ ≤ 2 * K * ((encCall x).length + 1) ^ (2 * K) := + Nat.mul_le_mul_right _ (by omega) + exact ⟨hT.trans (Nat.mul_le_mul_right _ (Nat.mul_le_mul_right _ hpow)), hS.trans hpow⟩ + +end FastContraction + +end Cslib.Circuits.Frontier From 419b9a2c90ec7d366d6a5c4e36585e277a637054 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:24:01 -0400 Subject: [PATCH 102/129] feat(circuit/frontier): the call count of the native recursion on the schedule An active call on the schedule has at most 2^(2 log m + e_d) children (the mode count (D.15) in the native numbering, with Lemma D.3 (1) for the native packing), so the root has a derivation with at most 2^((245760 C + 37) m / sqrt(log m)) nodes and log m stack levels. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../FastContractionSchedule.lean | 134 +++++++++++++++++- .../TensorNetwork/NativeChildBounds.lean | 24 ++++ 2 files changed, 151 insertions(+), 7 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean index f0ce875a44..19b2b44434 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean @@ -11,6 +11,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContra public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionMachine public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionStep public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeChildBounds +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.ScheduleCount import Mathlib.Data.List.GetD /-! @@ -33,8 +34,12 @@ small or active in the schedule. It passes from an active call to its native chi (`ContractionSchedule.ActiveBounds.nodePrecision_le`). **Consequences** for the reached calls: a call that the machine treats as a leaf has at most -`leafSize m` edges (`FastContraction.OnSchedule.edges_le_leafSize`), and an active one lies above -depth `⌊log₂ m⌋ - 2` (`FastContraction.OnSchedule.depth_add_two_le`). +`leafSize m` edges (`FastContraction.OnSchedule.edges_le_leafSize`), an active one lies above +depth `⌊log₂ m⌋ - 2` (`FastContraction.OnSchedule.depth_add_two_le`) and has at most +`2 ^ (2 ⌊log₂ m⌋ + e_d)` children (`FastContraction.OnSchedule.length_children_le`), by the mode +count (D.15). So the root has a derivation with at most `2 ^ ((245760 C + 37) m / √⌊log₂ m⌋)` nodes +(`FastContraction.exists_eval_onSchedule`, by +`ContractionSchedule.recursionBound_le_two_pow_div_sqrt_log_two`). ## Main definitions @@ -48,6 +53,9 @@ depth `⌊log₂ m⌋ - 2` (`FastContraction.OnSchedule.depth_add_two_le`). children. * `Cslib.Circuits.Frontier.FastContraction.Reach.onSchedule`: every reached call satisfies it. * `Cslib.Circuits.Frontier.FastContraction.OnSchedule.edges_le_leafSize`: the leaves are small. +* `Cslib.Circuits.Frontier.FastContraction.OnSchedule.length_children_le`: the number of children. +* `Cslib.Circuits.Frontier.FastContraction.exists_eval_onSchedule`: the derivation of the root with + its node count. ## References @@ -133,16 +141,20 @@ theorem depth_add_two_le (hB : ∀ d M p, Active C m d M p → ActiveBounds C m (h : OnSchedule C m x) (ha : active x = true) : x.depth + 2 ≤ Nat.log 2 m := (hB _ _ _ (h.active_of_active ha)).depth_add_two_le -/-- The level of the node of a child of an active call is below `u`. -/ -theorem level_lt_levels {c : State} (hc : c ∈ children x) : - level x ((nodes x).getD c.1 0) < levels x := by - obtain ⟨hj, -, -⟩ := lt_of_mem_children hc - have hkmem : (nodes x).getD c.1 0 ∈ nodes x := by +/-- The level of the `j`-th node of positive multiplicity is below `u`. -/ +theorem level_lt_levels_of_lt {j : ℕ} (hj : j < (nodes x).length) : + level x ((nodes x).getD j 0) < levels x := by + have hkmem : (nodes x).getD j 0 ∈ nodes x := by rw [List.getD_eq_getElem _ _ hj]; exact List.getElem_mem hj obtain ⟨hk, -, hq⟩ := mem_nodes.mp hkmem rw [level_eq hk] exact padicValInt_lt_of_taperMult_pos_left (by rwa [← mult_eq _ _ hk]) +/-- The level of the node of a child of an active call is below `u`. -/ +theorem level_lt_levels {c : State} (hc : c ∈ children x) : + level x ((nodes x).getD c.1 0) < levels x := + level_lt_levels_of_lt (lt_of_mem_children hc).1 + /-- **The invariant passes to the native children** of an active call, once the schedule's certificate holds: a child has at most a sixteenth of the edges, keeps the degree bound, and its precision is within the schedule. -/ @@ -210,4 +222,112 @@ theorem onSchedule_root {x₀ : Call} (hC : 1 ≤ C) (hs : x₀.scale = m) (he : rw [hd, he] exact Active.root (Nat.le_mul_of_pos_left m hC) (by omega) hp +/-! ### The number of children -/ + +/-- **The tuples of weight below `p` of the modes** are the mode budget of the weights `b + 1`. -/ +theorem length_budgetTuples_modeWeights (P T p : ℕ) : + (BudgetTuples.budgetTuples (modeWeights P T p) p).length = + Finset.card (TensorNetwork.modeBudget + (fun Z : Fin P × Fin T × Fin p => (Z.2.2 : ℕ) + 1) p) := by + have h := BudgetTuples.sum_filter_weight_lt_eq (nodup_modeList P T p) mem_modeList + (w := fun Z : Fin P × Fin T × Fin p => (Z.2.2 : ℕ) + 1) (fun _ => Nat.succ_pos _) p + (S := ℕ) (fun _ => 1) + rw [map_modeList_weight] at h + simp only [Finset.sum_const, smul_eq_mul, mul_one, List.map_const', List.sum_replicate] at h + rw [TensorNetwork.modeBudget, h] + +/-- **The length of the cursor's list**: at most `T n B r` for `T` nodes, `n` points, at most `B` +tuples at each node and `r` groups. -/ +theorem length_cursorList_le {ws : ℕ → List ℕ} {ps : ℕ → ℕ} {T n r B : ℕ} + (hB : ∀ t < T, (BudgetTuples.budgetTuples (ws t) (ps t)).length ≤ B) : + (cursorList ws ps T n r).length ≤ T * n * B * r := by + have hnode : ∀ t < T, (nodeBlock ws ps n r t).length ≤ n * B * r := fun t ht => by + simp only [nodeBlock, pointBlock, groupBlock, List.length_flatMap, List.length_map, + List.length_range, List.map_const', List.sum_replicate, smul_eq_mul] + have : ((List.range n).map fun _ => + (BudgetTuples.budgetTuples (ws t) (ps t)).length * r).sum ≤ n * (B * r) := by + rw [List.map_const', List.sum_replicate, smul_eq_mul, List.length_range] + exact Nat.mul_le_mul_left _ (Nat.mul_le_mul_right _ (hB t ht)) + simpa [Nat.mul_assoc] using this + simp only [cursorList, List.length_flatMap] + calc ((List.range T).map fun t => (nodeBlock ws ps n r t).length).sum + ≤ ((List.range T).map fun _ => n * B * r).sum := + List.sum_le_sum fun t ht => hnode t (List.mem_range.mp ht) + _ = T * n * B * r := by + rw [List.map_const', List.sum_replicate, smul_eq_mul, List.length_range]; ring + +/-- The nodes of positive multiplicity are at most `2 ^ (a + 1)`. -/ +theorem length_nodes_le (x : Call) : (nodes x).length ≤ 2 ^ (slots x + 1) := + (List.length_filter_le _ _).trans List.length_range.le + +namespace OnSchedule + +/-- **The number of children of an active call** on the schedule is at most +`2 ^ (2 ⌊log₂ m⌋ + e_d)`, `e_d = branchExponent C m d`, once the schedule's certificate holds: the +nodes, points and `64` groups contribute `2 ^ (2 ⌊log₂ m⌋)`, and each node has at most `2 ^ e_d` +tuples of exponents (the mode count (D.15)). -/ +theorem length_children_le (hB : ∀ d M p, Active C m d M p → ActiveBounds C m d M p) + (h : OnSchedule C m x) (ha : active x = true) : + (children x).length ≤ 2 ^ (2 * Nat.log 2 m + branchExponent C m x.depth) := by + have hA := h.active_of_active ha + have hb := hB _ _ _ hA + have hK := pow_slots_le_edges_of_active ha + have hslots : slots x = slotBits m x.depth := by rw [slots, h.scale_eq] + have hlevels : levels x = taper m x.depth := by rw [levels, h.scale_eq] + have hP : (packing x).2.1 * 2 ^ slots x ≤ 3 * x.edges := + numPackets_nativeParentPacking_mul_le h.wf.2 hK h.degree_le + (by rw [hslots]; exact hb.degree_mul_pairs_sq_le) (by rw [hslots]; exact hb.slots_mul_pairs_le) + have hT : ∀ j < (nodes x).length, (BudgetTuples.budgetTuples (weights x j) + (nodePrec x j)).length ≤ 2 ^ branchExponent C m x.depth := fun j hj => by + have hu := level_lt_levels_of_lt hj + set i := level x ((nodes x).getD j 0) + have e1 : nodePrec x j = nodePrecision x.prec (step x) (slotBits m x.depth) x.edges i := by + rw [nodePrec, hslots] + have e2 : weights x j = modeWeights (packing x).2.1 (2 ^ (i + 1)) + (nodePrecision x.prec (step x) (slotBits m x.depth) x.edges i) := by + rw [weights, ← e1] + have hh : step x * taper m x.depth ≤ 3 * x.edges + taper m x.depth := by + have := taperStep_mul_le x.scale x.depth x.edges + rw [h.scale_eq] at this + rw [step, h.scale_eq] + exact this + have hP' : Fintype.card (Fin (packing x).2.1) * 2 ^ slotBits m x.depth ≤ 3 * x.edges := by + rw [Fintype.card_fin, ← hslots]; exact hP + rw [e1, e2, length_budgetTuples_modeWeights] + exact hb.card_modeBudget_le hA (by rw [← hlevels]; exact hu) hh hP' + have hc := length_cursorList_le (n := numPoints x) (r := 64) hT + have hbr := hb.branching_le + have hN := length_nodes_le x + rw [hslots, pow_succ] at hN + rw [children] + calc (cursorList (weights x) (nodePrec x) (nodes x).length (numPoints x) 64).length + ≤ (nodes x).length * numPoints x * 2 ^ branchExponent C m x.depth * 64 := hc + _ ≤ 2 * 2 ^ slotBits m x.depth * numPoints x * 64 * 2 ^ branchExponent C m x.depth := by + have : (nodes x).length * numPoints x ≤ 2 * 2 ^ slotBits m x.depth * numPoints x := + Nat.mul_le_mul_right _ (by omega) + calc _ = (nodes x).length * numPoints x * 64 * 2 ^ branchExponent C m x.depth := by ring + _ ≤ _ := Nat.mul_le_mul_right _ (Nat.mul_le_mul_right _ this) + _ ≤ 2 ^ (2 * Nat.log 2 m) * 2 ^ branchExponent C m x.depth := by + refine Nat.mul_le_mul_right _ ?_ + simpa only [numPoints, slots, h.scale_eq] using hbr + _ = 2 ^ (2 * Nat.log 2 m + branchExponent C m x.depth) := (pow_add _ _ _).symm + +end OnSchedule + +/-! ### The derivation on the schedule -/ + +/-- **The derivation of a root on the schedule, with its node count** (§D.4.7, p. 64): once the +schedule's certificate holds at scale `m ≥ 2`, a root on the schedule at depth `0` has a derivation +of its tagged value over the reached calls with at most `2 ^ ((245760 C + 37) m / √⌊log₂ m⌋)` nodes +and `⌊log₂ m⌋` stack levels. -/ +theorem exists_eval_onSchedule (hB : ∀ d M p, Active C m d M p → ActiveBounds C m d M p) + {x₀ : Call} (h₀ : OnSchedule C m x₀) (hd : x₀.depth = 0) (hm : 2 ≤ m) : + ∃ N H, (scheme nativeMk).Eval (reachInv nativeMk x₀) x₀ (0, value nativeMk x₀) N H ∧ + N ≤ 2 ^ ((245760 * C + 37) * m / Nat.sqrt (Nat.log 2 m)) ∧ H ≤ Nat.log 2 m := by + obtain ⟨N, H, he, hN, hH⟩ := exists_eval_reachInv_count hd (Nat.log 2 m) + (fun d => 2 ^ (2 * Nat.log 2 m + branchExponent C m d)) fun x hx ha => by + have h := hx.onSchedule hB h₀ + exact ⟨by have := h.depth_add_two_le hB ha; omega, h.length_children_le hB ha⟩ + exact ⟨N, H, he, hN.trans (recursionBound_le_two_pow_div_sqrt_log_two C hm le_rfl), hH⟩ + end Cslib.Circuits.Frontier.FastContraction diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChildBounds.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChildBounds.lean index fe613a01c4..57b93e560b 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChildBounds.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/NativeChildBounds.lean @@ -37,6 +37,8 @@ Both need `64 ^ a ≤ M`, under which the native packing is the packing of Lemma * `Cslib.Circuits.Frontier.NativeChild.sixteen_mul_numEdges_nativeChild_le`: at most `M / 16` edges. * `Cslib.Circuits.Frontier.NativeChild.length_le_of_mem_vertices_nativeChild`: the degree bound. +* `Cslib.Circuits.Frontier.NativeChild.numPackets_nativeParentPacking_mul_le`: at most `3 M / L` + packets. ## References @@ -175,6 +177,28 @@ theorem sixteen_mul_numEdges_nativeChild_le (hK : 64 ^ a ≤ D.numEdges) (hia : rw [e1, e2] omega +include hD in +/-- **At most `3 M / L` packets** (Lemma D.3 (1)) in the native packing of a rows-first parent with +`M ≥ 64 ^ a` edges and maximum degree `Δ`, under (D.6): `2 Δ D² ≤ M` and `L D ≤ M`, +`D = 64 ^ (2 a)`, `L = 2 ^ a`. -/ +theorem numPackets_nativeParentPacking_mul_le (hK : 64 ^ a ≤ D.numEdges) {Δ : ℕ} + (hdeg : ∀ v ∈ D.vertices, v.1.length ≤ Δ) (hM : 2 * Δ * (64 ^ (2 * a)) ^ 2 ≤ D.numEdges) + (hL : 2 ^ a * 64 ^ (2 * a) ≤ D.numEdges) : + (nativeParentPacking r a D).2.1 * 2 ^ a ≤ 3 * D.numEdges := by + have hdeg' (v : ℕ) : (D.vertices.getD v ([], .inl [])).1.length ≤ Δ := by + by_cases hv : v < D.vertices.length + · rw [List.getD_eq_getElem _ _ hv] + exact hdeg _ (List.getElem_mem hv) + · rw [List.getD_eq_default _ _ (by omega)] + exact Nat.zero_le _ + have h := PacketPacking.numPackets_packing_mul_le (rowOfP hD) (colOfP hD) a 64 + (fun v => (card_rowOfP_le hD v).trans (hdeg' _)) + (fun w => (card_colOfP_le hD w).trans (hdeg' _)) + (by rwa [Fintype.card_fin]) (by rwa [Fintype.card_fin]) + rw [Fintype.card_fin] at h + rw [nativeParentPacking_eq hD hK] + exact h + /-- At most `2 ^ a` edges lie in a packet of a packing. -/ theorem card_packet_le {E R C : Type*} [Fintype E] {rowOf : E → R} {colOf : E → C} {r : ℕ} (Q : PacketPacking.Packing rowOf colOf a r) (q : Fin Q.numPackets) : From 985985529a6265297dc5404c6c5bbe77924d3fd3 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:26:48 -0400 Subject: [PATCH 103/129] feat(circuit/frontier): the leaf bound of the fast contraction analysis, with tests Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../TensorNetwork/FastContractionLeaf.lean | 206 ++++++++++++++++-- ComplexityTests/FastContractionLeaf.lean | 85 ++++++++ 3 files changed, 279 insertions(+), 13 deletions(-) create mode 100644 ComplexityTests/FastContractionLeaf.lean diff --git a/Complexity.lean b/Complexity.lean index ce9efdf650..54fe1f1565 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -898,6 +898,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DescribedL public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DirectContraction public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContraction +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionLeaf public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionMachine public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionStep public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean index 8a281f926f..8f75d42024 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean @@ -17,20 +17,54 @@ import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Length # The leaves of the fast contraction algorithm The cost of the leaves of the native recursion of Theorem D.1 of [OpenAI2026ETH] -(`Cslib.Circuits.Frontier.FastContraction.leafCertificate`): direct summation over the `2 ^ M` -edge assignments of a call with `M` edges, whose packet entries cost `(m + 2) ^ L` for `m` target -modes and `L` row ports (§D.4.6–§D.4.7, pp. 63–64). +(`Cslib.Circuits.Frontier.FastContraction.leafCertificate`), the leaf clause of the resource +analysis `Cslib.Complexity.CountingSimulation.FastAnalysis`. A leaf sums directly over the `2 ^ M` +edge assignments of a call with `M` edges (§D.4.7, p. 64), and the entry of a packet vertex costs +`(m + 2) ^ L` for `m` target modes and `L` row ports, the source's `exp(O(L log s))` (§D.4.6, +p. 63). + +**The cost of a leaf** (`FastContraction.leafCertificate_le`): with at most `R` target modes and +`Λ` row ports per vertex, time `K (n + R + 1) ^ K 2 ^ M (R + 2) ^ Λ` and space `K (n + R + 1) ^ K` +on a call of encoded length `n`, from the bounds of direct summation +(`DescribedNetwork.contractionCertificate_le`) and of the evaluator of descriptors +(`VertexDesc.entryCertificate_le`). + +**The packet shape** (`FastContraction.PacketShape`). Every packet vertex of a call of the +recursion has `i < a ≤ ⌊log₂ s⌋ / 32768`, `2 ^ (i + 1)` tiles, at most `p` exponents per tile, and +row ports in distinct slots below `2 ^ a`. So, with `A = 2 ^ (⌊log₂ s⌋ / 32768)`, it has at most +`A p` target modes and `A` row ports. The root has no packets, and the shape passes to the native +children of a well-formed active call (`FastContraction.packetShape_child`): a retained vertex +keeps its descriptor, and the row ports of a new packet are edges of one packet of the packing, +in distinct slots (`NativeChild.desc_of_mem_nativeChild`). + +**The leaf bound** (`FastContraction.exists_leafCertificate_le`). For `s ≤ m ≤ C s`, a leaf with +the packet shape and at most `2 m` edges takes time `K (n + 1) ^ K 2 ^ (K s / √⌊log₂ s⌋)` and space +`K (n + 1) ^ K`, provided that from some `s₀` on it has at most `leafSize m` edges and precision at +most `m ^ c`: then `2 ^ M ≤ 2 ^ (C s / √⌊log₂ s⌋)`, and `(A p + 2) ^ A` is +`2 ^ O(s ^ (1 / 32768) log s)` (`FastContraction.exists_packetFactor_le`). Below `s₀` everything +but a polynomial in `n` is a constant. + +## Main definitions + +* `Cslib.Circuits.Frontier.FastContraction.PacketShape`: the shape of the packets of a call. ## Main results -* `Cslib.Circuits.Frontier.FastContraction.leafCertificate_le`: if every vertex of a call has at - most `R` target modes and at most `Λ` row ports, the leaf takes time at most - `K (n + R + 1) ^ K 2 ^ M (R + 2) ^ Λ` and space at most `K (n + R + 1) ^ K` on a call of encoded - length `n` with `M` edges. +* `Cslib.Circuits.Frontier.FastContraction.leafCertificate_le`: the cost of a leaf. +* `Cslib.Circuits.Frontier.NativeChild.desc_of_mem_nativeChild`: the descriptors of a native + child. +* `Cslib.Circuits.Frontier.FastContraction.packetShape_root`, + `Cslib.Circuits.Frontier.FastContraction.packetShape_child`: the packet shape on the reached + calls. +* `Cslib.Circuits.Frontier.FastContraction.leafCertificate_le_of_packetShape`: the cost of a leaf + with the packet shape. +* `Cslib.Circuits.Frontier.FastContraction.exists_leafCertificate_le`: the leaf bound of the + resource analysis. ## References -* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.6–§D.4.7. +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.6–§D.4.7, + pp. 63–64. -/ public section @@ -338,8 +372,8 @@ theorem leafCertificate_le : ∃ K : ℕ, ∀ (mk : Call → State → ℕ × De change 2 * (VertexDesc.enc v.2).length + 1 + bs.length ≤ 3 * n + 1 omega -- The reduced coefficient lists. - obtain ⟨Rv, hRv⟩ : ∃ Rv, Rv = n * (2 * n + 3) + cf * (n + 1) ^ df * (2 * (cp * (n + 1) ^ dp) + 3) + - ct * (3 * n + 2) ^ dt := ⟨_, rfl⟩ + obtain ⟨Rv, hRv⟩ : ∃ Rv, Rv = n * (2 * n + 3) + + cf * (n + 1) ^ df * (2 * (cp * (n + 1) ^ dp) + 3) + ct * (3 * n + 2) ^ dt := ⟨_, rfl⟩ have hred : ∀ y, SumOps.galois.Reduced (x.prec, x.fl) y → (encList encNat y).length ≤ Rv := by intro y hy have h := SumOps.length_encList_le_of_galois_reduced hy @@ -558,17 +592,163 @@ theorem leafCertificate_le_of_packetShape : ∃ K : ℕ, have hA := two_pow_log_div_le x.scale have hR : (encCall x).length + 2 ^ (Nat.log 2 x.scale / 32768) * x.prec + 1 ≤ ((encCall x).length + 1) ^ 2 := by - have : 2 ^ (Nat.log 2 x.scale / 32768) * x.prec ≤ ((encCall x).length + 1) * (encCall x).length := - Nat.mul_le_mul (by omega) (by omega) + have : 2 ^ (Nat.log 2 x.scale / 32768) * x.prec ≤ + ((encCall x).length + 1) * (encCall x).length := Nat.mul_le_mul (by omega) (by omega) nlinarith have hpow : K * ((encCall x).length + 2 ^ (Nat.log 2 x.scale / 32768) * x.prec + 1) ^ K ≤ 2 * K * ((encCall x).length + 1) ^ (2 * K) := - calc _ ≤ K * (((encCall x).length + 1) ^ 2) ^ K := Nat.mul_le_mul_left _ (Nat.pow_le_pow_left hR _) + calc _ ≤ K * (((encCall x).length + 1) ^ 2) ^ K := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_left hR _) _ = K * ((encCall x).length + 1) ^ (2 * K) := by rw [← pow_mul] _ ≤ 2 * K * ((encCall x).length + 1) ^ (2 * K) := Nat.mul_le_mul_right _ (by omega) exact ⟨hT.trans (Nat.mul_le_mul_right _ (Nat.mul_le_mul_right _ hpow)), hS.trans hpow⟩ +/-! ### The leaf bound of the analysis -/ + +/-- `(μ + 1) ^ 2 ≤ 16 · 2 ^ (μ / 2)`. -/ +private theorem succ_sq_le_two_pow (μ : ℕ) : (μ + 1) ^ 2 ≤ 16 * 2 ^ (μ / 2) := by + have hk : μ / 4 + 1 ≤ 2 ^ (μ / 4) := Nat.lt_two_pow_self + have h1 : μ + 1 ≤ 4 * 2 ^ (μ / 4) := by omega + calc (μ + 1) ^ 2 ≤ (4 * 2 ^ (μ / 4)) ^ 2 := Nat.pow_le_pow_left h1 2 + _ = 16 * 2 ^ (2 * (μ / 4)) := by rw [mul_pow, ← pow_mul, mul_comm (μ / 4)]; norm_num + _ ≤ 16 * 2 ^ (μ / 2) := Nat.mul_le_mul_left _ (Nat.pow_le_pow_right (by norm_num) (by omega)) + +/-- **The packet factor is subexponential.** For `s ≤ m ≤ C s` and `p ≤ m ^ c`, with +`A = 2 ^ (⌊log₂ m⌋ / 32768)`, `(A p + 2) ^ A ≤ 2 ^ (K s / √⌊log₂ s⌋)` for a constant `K` depending +only on `C` and `c`: `A ≤ 2 ^ O(1) s ^ (1 / 32768)`, against `s / √⌊log₂ s⌋`. -/ +theorem exists_packetFactor_le (C c : ℕ) : ∃ K : ℕ, ∀ s m p : ℕ, 2 ≤ s → s ≤ m → m ≤ C * s → + p ≤ m ^ c → (2 ^ (Nat.log 2 m / 32768) * p + 2) ^ 2 ^ (Nat.log 2 m / 32768) ≤ + 2 ^ (K * s / Nat.sqrt (Nat.log 2 s)) := by + obtain ⟨c₀, hc₀⟩ : ∃ c₀, c₀ = Nat.log 2 C + 1 := ⟨_, rfl⟩ + refine ⟨16 * ((c₀ + 2) * (c + 1)) * 2 ^ c₀, fun s m p hs hsm hmC hp => ?_⟩ + set μ := Nat.log 2 s with hμ + set lam := Nat.log 2 m with hlam + set A := 2 ^ (lam / 32768) with hA + have hm2 : 2 ≤ m := hs.trans hsm + have hS1 : 1 ≤ Nat.sqrt μ := Nat.one_le_sqrt_log_two hs + -- `A ≤ m` and `m < 2 ^ (λ + 1)`. + have hAm : A ≤ m := (Nat.pow_le_pow_right (by norm_num) (Nat.div_le_self _ _)).trans + (Nat.pow_log_le_self 2 (by omega)) + have hmlam : m < 2 ^ (lam + 1) := Nat.lt_pow_succ_log_self (by norm_num) m + -- `λ ≤ c₀ + μ`. + have hlamμ : lam ≤ c₀ + μ := by + have hC : C < 2 ^ c₀ := hc₀ ▸ Nat.lt_pow_succ_log_self (by norm_num) C + have hs' : s < 2 ^ (μ + 1) := Nat.lt_pow_succ_log_self (by norm_num) s + have : m < 2 ^ (c₀ + μ + 1) := by + calc m ≤ C * s := hmC + _ < 2 ^ c₀ * 2 ^ (μ + 1) := Nat.mul_lt_mul'' hC hs' + _ = 2 ^ (c₀ + μ + 1) := by rw [← pow_add]; ring_nf + have := Nat.log_lt_of_lt_pow (by omega) this + omega + -- The base: `A p + 2 ≤ 2 ^ ((λ + 1) (c + 1) + 1)`. + have hbase : A * p + 2 ≤ 2 ^ ((lam + 1) * (c + 1) + 1) := by + have h1 : A * p ≤ m * m ^ c := Nat.mul_le_mul hAm hp + have h2 : 2 ≤ m * m ^ c := by + have : 1 ≤ m ^ c := Nat.one_le_pow _ _ (by omega) + nlinarith + have h3 : m * m ^ c = m ^ (c + 1) := by ring + have h4 : m ^ (c + 1) ≤ (2 ^ (lam + 1)) ^ (c + 1) := Nat.pow_le_pow_left hmlam.le _ + calc A * p + 2 ≤ 2 * m ^ (c + 1) := by omega + _ ≤ 2 * (2 ^ (lam + 1)) ^ (c + 1) := Nat.mul_le_mul_left _ h4 + _ = 2 ^ ((lam + 1) * (c + 1) + 1) := by rw [← pow_mul]; ring + -- The exponent. + have hA' : A ≤ 2 ^ c₀ * 2 ^ (μ / 32768) := by + rw [← pow_add]; exact Nat.pow_le_pow_right (by norm_num) (by omega) + have hE : ((lam + 1) * (c + 1) + 1) * A * Nat.sqrt μ ≤ + 16 * ((c₀ + 2) * (c + 1)) * 2 ^ c₀ * s := by + have h1 : (lam + 1) * (c + 1) + 1 ≤ (c₀ + 2) * (c + 1) * (μ + 1) := by + have : lam + 1 ≤ (c₀ + 1) * (μ + 1) := by nlinarith + nlinarith + have h2 : Nat.sqrt μ ≤ μ + 1 := (Nat.sqrt_le_self μ).trans (Nat.le_succ μ) + have h3 := succ_sq_le_two_pow μ + have h4 : 2 ^ (μ / 2) * 2 ^ (μ / 32768) ≤ 2 ^ μ := by + rw [← pow_add]; exact Nat.pow_le_pow_right (by norm_num) (by omega) + have h5 : 2 ^ μ ≤ s := Nat.pow_log_le_self 2 (by omega) + calc ((lam + 1) * (c + 1) + 1) * A * Nat.sqrt μ + ≤ ((c₀ + 2) * (c + 1) * (μ + 1)) * (2 ^ c₀ * 2 ^ (μ / 32768)) * (μ + 1) := + Nat.mul_le_mul (Nat.mul_le_mul h1 hA') h2 + _ = (c₀ + 2) * (c + 1) * 2 ^ c₀ * ((μ + 1) ^ 2 * 2 ^ (μ / 32768)) := by ring + _ ≤ (c₀ + 2) * (c + 1) * 2 ^ c₀ * (16 * 2 ^ (μ / 2) * 2 ^ (μ / 32768)) := + Nat.mul_le_mul_left _ (Nat.mul_le_mul_right _ h3) + _ = 16 * ((c₀ + 2) * (c + 1)) * 2 ^ c₀ * (2 ^ (μ / 2) * 2 ^ (μ / 32768)) := by ring + _ ≤ 16 * ((c₀ + 2) * (c + 1)) * 2 ^ c₀ * s := Nat.mul_le_mul_left _ (h4.trans h5) + have hE' : ((lam + 1) * (c + 1) + 1) * A ≤ 16 * ((c₀ + 2) * (c + 1)) * 2 ^ c₀ * s / Nat.sqrt μ := + (Nat.le_div_iff_mul_le (by omega)).mpr hE + calc (A * p + 2) ^ A ≤ (2 ^ ((lam + 1) * (c + 1) + 1)) ^ A := Nat.pow_le_pow_left hbase _ + _ = 2 ^ (((lam + 1) * (c + 1) + 1) * A) := by rw [← pow_mul] + _ ≤ 2 ^ (16 * ((c₀ + 2) * (c + 1)) * 2 ^ c₀ * s / Nat.sqrt μ) := + Nat.pow_le_pow_right (by norm_num) hE' + +/-- **The leaf bound of the resource analysis.** For all constants `C`, `c` and `s₀` there is `K` +such that a call with the packet shape, scale `m` with `s ≤ m ≤ C s` and at most `2 m` edges takes +time at most `K (n + 1) ^ K 2 ^ (K s / √⌊log₂ s⌋)` and space at most `K (n + 1) ^ K` at a leaf, +provided that from `s₀` on it has at most `leafSize m` edges and precision at most `m ^ c`. Below +`s₀`, the scale is bounded, so the packet factor is polynomial and `2 ^ M` is a constant; from +`s₀` on, `2 ^ M ≤ 2 ^ (C s / √⌊log₂ s⌋)` and the packet factor is subexponential +(`exists_packetFactor_le`). -/ +theorem exists_leafCertificate_le (C c s₀ : ℕ) : ∃ K : ℕ, + ∀ (mk : Call → State → ℕ × DescribedNetwork VertexDesc) (s : ℕ) (x : Call), 2 ≤ s → + s ≤ x.scale → x.scale ≤ C * s → PacketShape x → x.edges ≤ 2 * x.scale → + (s₀ ≤ s → x.edges ≤ leafSize x.scale ∧ x.prec ≤ x.scale ^ c) → + (leafCertificate mk).time x ≤ + K * ((encCall x).length + 1) ^ K * 2 ^ (K * s / Nat.sqrt (Nat.log 2 s)) ∧ + (leafCertificate mk).space x ≤ K * ((encCall x).length + 1) ^ K := by + obtain ⟨K₀, hK₀⟩ := leafCertificate_le_of_packetShape + obtain ⟨K₁, hK₁⟩ := exists_packetFactor_le C c + obtain ⟨Ks, hKs⟩ : ∃ Ks, Ks = K₀ * 2 ^ (2 * (C * s₀)) * (C * s₀ + 2) ^ (C * s₀) := ⟨_, rfl⟩ + refine ⟨Ks + K₀ + C * s₀ + C + K₁, fun mk s x hs hsm hmC hshape hM hbig => ?_⟩ + obtain ⟨hT, hS⟩ := hK₀ mk x hshape + set n := (encCall x).length with hn + set K := Ks + K₀ + C * s₀ + C + K₁ with hK + set S := Nat.sqrt (Nat.log 2 s) with hSdef + have hS1 : 1 ≤ S := Nat.one_le_sqrt_log_two hs + have hB1 : 1 ≤ n + 1 := by omega + have hX1 : 1 ≤ 2 ^ (K * s / S) := Nat.one_le_two_pow + have hnK : (n + 1) ^ K₀ ≤ (n + 1) ^ K := Nat.pow_le_pow_right hB1 (by omega) + refine ⟨?_, hS.trans (Nat.mul_le_mul (by omega) hnK)⟩ + set A := 2 ^ (Nat.log 2 x.scale / 32768) with hA + have hAm : A ≤ x.scale := (Nat.pow_le_pow_right (by norm_num) (Nat.div_le_self _ _)).trans + (Nat.pow_log_le_self 2 (by omega)) + rcases lt_or_ge s s₀ with hsmall | hlarge + · -- Below `s₀`: constants and a polynomial. + have hmC' : x.scale ≤ C * s₀ := hmC.trans (Nat.mul_le_mul_left _ hsmall.le) + have hp : x.prec ≤ n := by have := scale_add_prec_add_length_fl_le x; omega + have h2M : 2 ^ x.edges ≤ 2 ^ (2 * (C * s₀)) := + Nat.pow_le_pow_right (by norm_num) (by omega) + have hbase : A * x.prec + 2 ≤ (C * s₀ + 2) * (n + 1) := by + have : A * x.prec ≤ C * s₀ * n := Nat.mul_le_mul (hAm.trans hmC') hp + nlinarith + have hfac : (A * x.prec + 2) ^ A ≤ (C * s₀ + 2) ^ (C * s₀) * (n + 1) ^ (C * s₀) := by + calc (A * x.prec + 2) ^ A ≤ ((C * s₀ + 2) * (n + 1)) ^ A := Nat.pow_le_pow_left hbase _ + _ ≤ ((C * s₀ + 2) * (n + 1)) ^ (C * s₀) := + Nat.pow_le_pow_right (Nat.one_le_iff_ne_zero.mpr (by positivity)) (hAm.trans hmC') + _ = (C * s₀ + 2) ^ (C * s₀) * (n + 1) ^ (C * s₀) := mul_pow _ _ _ + calc (leafCertificate mk).time x ≤ K₀ * (n + 1) ^ K₀ * 2 ^ x.edges * (A * x.prec + 2) ^ A := hT + _ ≤ K₀ * (n + 1) ^ K₀ * 2 ^ (2 * (C * s₀)) * + ((C * s₀ + 2) ^ (C * s₀) * (n + 1) ^ (C * s₀)) := + Nat.mul_le_mul (Nat.mul_le_mul_left _ h2M) hfac + _ = Ks * (n + 1) ^ (K₀ + C * s₀) := by rw [hKs, pow_add]; ring + _ ≤ K * (n + 1) ^ K := Nat.mul_le_mul (by omega) (Nat.pow_le_pow_right hB1 (by omega)) + _ ≤ K * (n + 1) ^ K * 2 ^ (K * s / S) := Nat.le_mul_of_pos_right _ hX1 + · -- From `s₀` on: the leaf size and the packet factor. + obtain ⟨hleaf, hp⟩ := hbig hlarge + have hlog : S ≤ Nat.sqrt (Nat.log 2 x.scale) := Nat.sqrt_log_two_mono hsm + have hMs : x.edges ≤ C * s / S := + calc x.edges ≤ x.scale / Nat.sqrt (Nat.log 2 x.scale) := hleaf + _ ≤ x.scale / S := Nat.div_le_div_left hlog hS1 + _ ≤ C * s / S := Nat.div_le_div_right hmC + have hfac := hK₁ s x.scale x.prec hs hsm hmC hp + have hCK : (C + K₁) * s ≤ K * s := Nat.mul_le_mul_right _ (by omega) + have hexp : C * s / S + K₁ * s / S ≤ K * s / S := + Nat.div_add_div_le_add_div.trans (Nat.div_le_div_right (by rw [← add_mul]; exact hCK)) + calc (leafCertificate mk).time x ≤ K₀ * (n + 1) ^ K₀ * 2 ^ x.edges * (A * x.prec + 2) ^ A := hT + _ ≤ K₀ * (n + 1) ^ K₀ * 2 ^ (C * s / S) * 2 ^ (K₁ * s / S) := + Nat.mul_le_mul (Nat.mul_le_mul_left _ (Nat.pow_le_pow_right (by norm_num) hMs)) hfac + _ = K₀ * (n + 1) ^ K₀ * 2 ^ (C * s / S + K₁ * s / S) := by rw [pow_add]; ring + _ ≤ K * (n + 1) ^ K * 2 ^ (K * s / S) := + Nat.mul_le_mul (Nat.mul_le_mul (by omega) hnK) (Nat.pow_le_pow_right (by norm_num) hexp) + end FastContraction end Cslib.Circuits.Frontier diff --git a/ComplexityTests/FastContractionLeaf.lean b/ComplexityTests/FastContractionLeaf.lean new file mode 100644 index 0000000000..5a2370278f --- /dev/null +++ b/ComplexityTests/FastContractionLeaf.lean @@ -0,0 +1,85 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionLeaf + +/-! # The leaves of the fast contraction machine: checked instances + +The packet shape on a root and on a call with a packet vertex, the row ports and target modes it +bounds, and an input meeting every hypothesis of the leaf bound. -/ + +namespace ComplexityTests.FastContractionLeaf + +open Cslib.Circuits.Frontier Cslib.Circuits.Frontier.FastContraction + Cslib.Circuits.Frontier.NativePacket Cslib.Circuits.Frontier.ContractionSchedule + +/-- Two rows `0, 1` and two columns `2, 3`, joined by edges `0 : 0 → 2` and `1 : 1 → 3`. -/ +def pair : ℕ × ExplicitTensorNetwork ℕ := + (2, ⟨2, [([0], [1, 2]), ([1], [3, 4]), ([0], [1, 1]), ([1], [1, 0])]⟩) + +-- The root of `(1, pair)`: scale `2`, two edges, precision `1`. +example : (root (1, pair)).scale = 2 ∧ (root (1, pair)).edges = 2 ∧ + (root (1, pair)).prec = 1 := by decide + +-- The root has the packet shape: its vertices are tables. +example : PacketShape (root (1, pair)) := packetShape_root _ + +-- At `s = 2`, the root meets every hypothesis of `exists_leafCertificate_le` with `C = 1` and +-- `c = 2`, for every `s₀`: `leafSize 2 = 2` and `1 ≤ 2 ^ 2`. +example (s₀ : ℕ) : 2 ≤ 2 ∧ 2 ≤ (root (1, pair)).scale ∧ (root (1, pair)).scale ≤ 1 * 2 ∧ + PacketShape (root (1, pair)) ∧ (root (1, pair)).edges ≤ 2 * (root (1, pair)).scale ∧ + (s₀ ≤ 2 → (root (1, pair)).edges ≤ leafSize (root (1, pair)).scale ∧ + (root (1, pair)).prec ≤ (root (1, pair)).scale ^ 2) := + ⟨le_rfl, by decide, by decide, packetShape_root _, by decide, fun _ => by decide⟩ + +/-- A packet context with `a = 1` slot bit, `i = 0`, two tiles, precision `1` and one exponent per +tile. -/ +def ctx : PacketCtx where + p := 1 + fl := [1] + a := 1 + L := 2 + i := 0 + tiles := 2 + t := 1 + nb := 1 + e₁ := true + e₂ := false + differ := true + cols := [] + ζ := [0] + +/-- A packet vertex with a row port in slot `0` and a column port in slot `1`. -/ +def packetVertex : List ℕ × VertexDesc := ([0, 1], .inr (ctx, [(true, 0), (false, 1)], [0, 0])) + +/-- A call at scale `4` and precision `1` whose network is the packet vertex alone. -/ +def packetCall : Call := ((4, 1, 3, 1, [1], 0), ⟨2, [packetVertex]⟩) + +-- The packet vertex has one row port, and at most `2 ⬝ 1` target modes. +example : packetVertex.2.rowPorts [true, false] = 1 := by decide +example : packetVertex.2.modes ≤ 2 := by decide + +-- The packet shape is not vacuous on packets. Here `a = 1` exceeds `⌊log₂ 4⌋ / 32768 = 0`, so the +-- shape fails; a call one level down the schedule has `a ≤ ⌊log₂ s⌋ / 32768`. +example : ¬ PacketShape packetCall := by + intro h + have h1 : 1 ≤ Nat.log 2 4 / 32768 := (h packetVertex (by simp [packetCall]) _ rfl).2.1 + rw [show (4 : ℕ) = 2 ^ 2 by norm_num, Nat.log_pow (by norm_num)] at h1 + omega + +-- From scale `2 ^ 32768` on, the same vertex has the packet shape. +example (s : ℕ) (hs : 32768 ≤ Nat.log 2 s) : + PacketShape ((s, 1, 3, 1, [1], 0), ⟨2, [packetVertex]⟩) := by + intro v hv d hd + simp only [List.mem_singleton] at hv + subst hv + simp only [packetVertex, Sum.inr.injEq] at hd + subst hd + refine ⟨by decide, ?_, by decide, le_rfl, by decide, by decide⟩ + change 1 ≤ Nat.log 2 s / 32768 + omega + +end ComplexityTests.FastContractionLeaf From 8a661160b2ecf3afce47ab3fdb9d2e33aa770657 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:27:12 -0400 Subject: [PATCH 104/129] feat(Classes/CountingSimulation): the resource analysis of the native recursion from sizes and leaves FastAnalysis nativeMk follows from FastSizes (polynomial sizes of the reached calls, frames and values) and FastLeaves (the leaf bounds). The derivation of the root over the reached calls has at most m + 1 stack levels, and from some size on at most 2^((245760 C' + 37) m / sqrt(log m)) nodes for C' = p/m + 1, which fit in the clock. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../FastContractionDerivation.lean | 4 +- .../FastContractionSchedule.lean | 3 +- .../CountingSimulation/FastAnalysis.lean | 258 ++++++++++++++++++ 4 files changed, 263 insertions(+), 3 deletions(-) create mode 100644 Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean diff --git a/Complexity.lean b/Complexity.lean index bb48bbd41f..13058f0467 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1108,6 +1108,7 @@ public import Complexity.Computability.Complexity.Classes.CountingSimulation.Ass public import Complexity.Computability.Complexity.Classes.CountingSimulation.BlockNetworks public import Complexity.Computability.Complexity.Classes.CountingSimulation.Containments public import Complexity.Computability.Complexity.Classes.CountingSimulation.FastAlgorithm +public import Complexity.Computability.Complexity.Classes.CountingSimulation.FastAnalysis public import Complexity.Computability.Complexity.Classes.CountingSimulation.Guarded public import Complexity.Computability.Complexity.Classes.CountingSimulation.Interfaces public import Complexity.Computability.Complexity.Classes.DP diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionDerivation.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionDerivation.lean index daf2bd07db..7d1f9b0e8d 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionDerivation.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionDerivation.lean @@ -20,8 +20,8 @@ Theorem D.1 of [OpenAI2026ETH] (Appendix D.4.7, p. 64), for any child builder. **The invariant** (`FastContraction.reachInv`). The nodes of a derivation from a root `x₀` are the calls reached from `x₀`, its frames are the frames `FastContraction.frameAt` of a reached active -call along its children, and its values are the tagged values of reached calls. Every bound on reached -calls is a bound on the derivation. +call along its children, and its values are the tagged values of reached calls. Every bound on +reached calls is a bound on the derivation. **The height.** The children of a call receive its number of edges as their bound, and an active call has fewer edges than its bound, so the bound plus the depth never increases along the diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean index 19b2b44434..7431387434 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean @@ -276,7 +276,8 @@ theorem length_children_le (hB : ∀ d M p, Active C m d M p → ActiveBounds C have hlevels : levels x = taper m x.depth := by rw [levels, h.scale_eq] have hP : (packing x).2.1 * 2 ^ slots x ≤ 3 * x.edges := numPackets_nativeParentPacking_mul_le h.wf.2 hK h.degree_le - (by rw [hslots]; exact hb.degree_mul_pairs_sq_le) (by rw [hslots]; exact hb.slots_mul_pairs_le) + (by rw [hslots]; exact hb.degree_mul_pairs_sq_le) + (by rw [hslots]; exact hb.slots_mul_pairs_le) have hT : ∀ j < (nodes x).length, (BudgetTuples.budgetTuples (weights x j) (nodePrec x j)).length ≤ 2 ^ branchExponent C m x.depth := fun j hj => by have hu := level_lt_levels_of_lt hj diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean new file mode 100644 index 0000000000..ee65ad91e4 --- /dev/null +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean @@ -0,0 +1,258 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Complexity.Classes.CountingSimulation.FastAlgorithm +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionSchedule + +/-! +# The resource analysis of the native recursion: the recursion tree + +The resource analysis `CountingSimulation.FastAnalysis` of the native recursion +(`FastContraction.nativeMk`) of Theorem D.1 of [OpenAI2026ETH] (Appendix D.4.7, p. 64), reduced to +two bounds about single calls (`CountingSimulation.fastAnalysis_nativeMk_of`): + +* **sizes** (`CountingSimulation.FastSizes`): the calls reached from the root, their frames and + their values are polynomially long in the input; +* **leaves** (`CountingSimulation.FastLeaves`): direct summation at a reached leaf takes time + `poly(n) 2 ^ O(s / √⌊log₂ s⌋)` and polynomial space. + +The rest of the analysis is proved here: the derivation of the root over the reached calls +(`FastContraction.reachInv`), with at most `m + 1` stack levels for every input +(`FastContraction.exists_eval_reachInv`), and, from some size on, with at most +`2 ^ ((245760 C' + 37) m / √⌊log₂ m⌋)` nodes for `C' = ⌊p / m⌋ + 1` +(`FastContraction.exists_eval_onSchedule`), which fit in the clock +(`CountingSimulation.two_mul_sub_one_le_fuel`). The clock's fixed constant serves every precision +because the call count's exponent is linear in `C'`. + +## Main definitions + +* `Cslib.Complexity.CountingSimulation.FastSizes`: polynomial sizes along the recursion. +* `Cslib.Complexity.CountingSimulation.FastLeaves`: the leaf bounds. + +## Main results + +* `Cslib.Complexity.CountingSimulation.two_mul_sub_one_le_fuel`: the call count fits in the clock. +* `Cslib.Complexity.CountingSimulation.onSchedule_root_capPrecision`: guarded roots are on the + schedule. +* `Cslib.Complexity.CountingSimulation.fastAnalysis_nativeMk_of`: the resource analysis from the + sizes and the leaf bounds. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Theorem D.1 (p. 53) and + Appendix D.4.7 (p. 64). +-/ + +public section + +namespace Cslib.Complexity.CountingSimulation + +open Cslib.Circuits.Frontier Cslib.Circuits.Frontier.ContractionSchedule + Cslib.Circuits.Frontier.FastContraction Cslib.GenericRAM Cslib.GenericRAM.StackRecursion + Turing.MultiTapeTM + +/-! ### The clock -/ + +/-- **The call count fits in the clock.** For a root with `m ≥ D + 2` edges and precision +`p ≤ D m`, a derivation with at most `2 ^ ((245760 C' + 37) m / √⌊log₂ m⌋)` nodes, +`C' = ⌊p / m⌋ + 1`, has `2 N - 1` at most the clock `2 ^ (K₀ (m + p) / √⌊log₂ (m + p)⌋)`: the +exponent is at most `245797 (m + p) / √⌊log₂ m⌋`, and `⌊log₂ (m + p)⌋ ≤ 3 ⌊log₂ m⌋`. -/ +theorem two_mul_sub_one_le_fuel {m p N D : ℕ} (hm : D + 2 ≤ m) (hp : p ≤ D * m) + (hN : N ≤ 2 ^ ((245760 * (p / m + 1) + 37) * m / Nat.sqrt (Nat.log 2 m))) : + 2 * N - 1 ≤ 2 ^ (fuelConst * (m + p) / Nat.sqrt (Nat.log 2 (m + p))) := by + set L := Nat.log 2 m with hLdef + set σ := Nat.sqrt L with hσdef + set Y := m + p with hYdef + have hm2 : 2 ≤ m := by omega + have hσ : 1 ≤ σ := Nat.one_le_sqrt_log_two hm2 + -- The exponent of the count. + have hCm : (p / m + 1) * m ≤ Y := by + have := Nat.div_mul_le_self p m + rw [add_mul, one_mul] + omega + have hX : (245760 * (p / m + 1) + 37) * m ≤ 245797 * Y := by nlinarith + -- `⌊log₂ (m + p)⌋ ≤ 3 ⌊log₂ m⌋`. + have hYm : Y ≤ m * m := by nlinarith + have hL1 : 1 ≤ L := Nat.le_log_of_pow_le one_lt_two (by simpa using hm2) + have hL' : Nat.log 2 Y ≤ 3 * L := by + have h1 : m < 2 ^ (L + 1) := Nat.lt_pow_succ_log_self one_lt_two m + have h2 : Y < 2 ^ (2 * L + 2) := + calc Y ≤ m * m := hYm + _ < 2 ^ (L + 1) * 2 ^ (L + 1) := Nat.mul_self_lt_mul_self h1 + _ = 2 ^ (2 * L + 2) := by rw [← pow_add]; ring_nf + have := Nat.log_lt_of_lt_pow (by omega) h2 + omega + have hσ' : Nat.sqrt (Nat.log 2 Y) ≤ 3 * σ := by + have h1 : Nat.sqrt (Nat.log 2 Y) ≤ Nat.sqrt (3 * L) := Nat.sqrt_le_sqrt hL' + have h2 : Nat.sqrt (3 * L) < 2 * σ + 2 := by + rw [Nat.sqrt_lt'] + have := Nat.lt_succ_sqrt' L + nlinarith + omega + have hσ'0 : 0 < Nat.sqrt (Nat.log 2 Y) := + Nat.sqrt_pos.mpr ((Nat.log_mono_right (show m ≤ Y by omega)).trans_lt' hL1) + -- Comparing the exponents. + set k := Y / σ with hkdef + have hk : 1 ≤ k := by + refine (Nat.le_div_iff_mul_le hσ).mpr ?_ + have h1 : σ ≤ L := Nat.sqrt_le_self L + have h2 : L < m := Nat.log_lt_self 2 (by omega) + omega + have hE1 : (245760 * (p / m + 1) + 37) * m / σ ≤ 245797 * k + 245797 := + (Nat.div_le_div_right hX).trans (Nat.mul_div_le_mul_div_add _ _ _) + have hE2 : 1398101 * k ≤ fuelConst * Y / Nat.sqrt (Nat.log 2 Y) := by + have h1 : fuelConst * Y / (3 * σ) ≤ fuelConst * Y / Nat.sqrt (Nat.log 2 Y) := + Nat.div_le_div_left hσ' hσ'0 + have h2 : fuelConst * Y / (3 * σ) = fuelConst * Y / σ / 3 := by + rw [Nat.div_div_eq_div_mul, Nat.mul_comm σ 3] + have h3 : fuelConst * k ≤ fuelConst * Y / σ := by + rw [Nat.le_div_iff_mul_le hσ, Nat.mul_assoc] + exact Nat.mul_le_mul_left _ (Nat.div_mul_le_self Y σ) + have h4 : 1398101 * k ≤ fuelConst * k / 3 := by + rw [Nat.le_div_iff_mul_le (by norm_num)] + simp only [fuelConst] + nlinarith + omega + have hpow : 2 * N ≤ 2 ^ (fuelConst * Y / Nat.sqrt (Nat.log 2 Y)) := + calc 2 * N ≤ 2 * 2 ^ ((245760 * (p / m + 1) + 37) * m / σ) := Nat.mul_le_mul_left 2 hN + _ = 2 ^ ((245760 * (p / m + 1) + 37) * m / σ + 1) := by rw [pow_succ]; ring + _ ≤ 2 ^ (fuelConst * Y / Nat.sqrt (Nat.log 2 Y)) := + Nat.pow_le_pow_right (by norm_num) (by omega) + omega + +/-- The schedule's certificate for every constant up to `D + 1`, from some size on. -/ +theorem exists_activeBounds_le (D : ℕ) : + ∃ s₀, ∀ s, s₀ ≤ s → ∀ C ≤ D + 1, ∀ d M p, Active C s d M p → ActiveBounds C s d M p := by + choose f hf using exists_activeBounds + exact ⟨(Finset.range (D + 2)).sup f, fun s hs C hC d M p hA => + ((hf C) s ((Finset.le_sup (Finset.mem_range.mpr (by omega))).trans hs)).2 d M p hA⟩ + +/-! ### The root -/ + +/-- **Guarded roots are on the schedule**, for every constant `C ≥ 1` with precision at most +`C m`. -/ +theorem onSchedule_root_capPrecision {P C : ℕ} {G : ℕ × ExplicitTensorNetwork ℕ} (hg : fastGuard G = true) + (hC : 1 ≤ C) (hp : (ExplicitTensorNetwork.capPrecision (P, G)).1 ≤ C * G.2.numEdges) : + OnSchedule C G.2.numEdges (root (ExplicitTensorNetwork.capPrecision (P, G))) := by + have hdeg : G.2.maxDegree ≤ degreeBound G.2.numEdges := by + simp only [fastGuard, decide_eq_true_eq] at hg + exact hg.2.2 + rw [ExplicitTensorNetwork.maxDegree_le_iff] at hdeg + refine FastContraction.onSchedule_root hC rfl rfl rfl (wf_root P hg) (fun v hv => ?_) rfl rfl hp + simp only [root, ExplicitTensorNetwork.capPrecision, rootNet, List.mem_map] at hv + obtain ⟨w, hw, rfl⟩ := hv + exact hdeg w hw + +/-- The number of edges of an explicit network is below the length of an encoded input. -/ +theorem numEdges_lt_length_encInput (P : ℕ) (G : ℕ × ExplicitTensorNetwork ℕ) : + G.2.numEdges < (FastContraction.encInput (P, G)).length := by + simp only [FastContraction.encInput, length_encPair, ExplicitTensorNetwork.encExplicitNetwork, + encOfEquiv, Function.Embedding.trans_apply, Equiv.coe_toEmbedding] + simp only [DescribedNetwork.equivProd, Equiv.coe_fn_mk, encUnary_apply, List.length_replicate] + omega + +/-! ### The analysis from the sizes and the leaves -/ + +/-- **Polynomial sizes along the recursion**: for all constants `C` and `D` there is `K` such that +on every guarded input admissible at size `s ≥ 2` with constant `C` and with precision +`1 ≤ P ≤ D s`, every call reached from the root, its frames and its value are at most +`K (n + 1) ^ K` long, for the input length `n`. -/ +@[expose] def FastSizes : Prop := + ∀ C D : ℕ, ∃ K : ℕ, ∀ (s P : ℕ) (G : ℕ × ExplicitTensorNetwork ℕ), 2 ≤ s → 1 ≤ P → + P ≤ D * s → ExplicitAdmissible C s G → fastGuard G = true → + ∀ x, Reach nativeMk (root (ExplicitTensorNetwork.capPrecision (P, G))) x → + (encCall x).length ≤ K * ((FastContraction.encInput (P, G)).length + 1) ^ K ∧ + (∀ a, (reachInv nativeMk (root (ExplicitTensorNetwork.capPrecision (P, G)))).frame x a → + (encPair encCall encFrame (x, a)).length ≤ + K * ((FastContraction.encInput (P, G)).length + 1) ^ K) ∧ + (encValue (x.depth, value nativeMk x)).length ≤ + K * ((FastContraction.encInput (P, G)).length + 1) ^ K + +/-- **The leaf bounds**: for all constants `C` and `D` there is `K` such that on every guarded +input admissible at size `s ≥ 2` with constant `C` and with precision `1 ≤ P ≤ D s`, direct +summation at every reached call that the machine treats as a leaf takes time at most +`K (n + 1) ^ K 2 ^ (K s / √⌊log₂ s⌋)` and space at most `K (n + 1) ^ K`. -/ +@[expose] def FastLeaves : Prop := + ∀ C D : ℕ, ∃ K : ℕ, ∀ (s P : ℕ) (G : ℕ × ExplicitTensorNetwork ℕ), 2 ≤ s → 1 ≤ P → + P ≤ D * s → ExplicitAdmissible C s G → fastGuard G = true → + ∀ x, Reach nativeMk (root (ExplicitTensorNetwork.capPrecision (P, G))) x → active x = false → + (leafCertificate nativeMk).time x ≤ + K * ((FastContraction.encInput (P, G)).length + 1) ^ K * + 2 ^ (K * s / Nat.sqrt (Nat.log 2 s)) ∧ + (leafCertificate nativeMk).space x ≤ + K * ((FastContraction.encInput (P, G)).length + 1) ^ K + +/-- `a B ^ a ≤ a B ^ K` for `1 ≤ B` and `a ≤ K`. -/ +private theorem mul_pow_le_mul_pow {a B K : ℕ} (hB : 1 ≤ B) (ha : a ≤ K) : + a * B ^ a ≤ a * B ^ K := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_right hB ha) + +/-- **The resource analysis of the native recursion**, from polynomial sizes along the recursion +and the leaf bounds. The derivation of the root over the reached calls has at most `m + 1` stack +levels, and, from some size on, its nodes fit in the clock. -/ +theorem fastAnalysis_nativeMk_of (hS : FastSizes) (hL : FastLeaves) : FastAnalysis nativeMk := by + intro C D + obtain ⟨KS, hKS⟩ := hS C D + obtain ⟨KL, hKL⟩ := hL C D + obtain ⟨sA, hsA⟩ := exists_activeBounds_le D + refine ⟨KS + KL + 1, max sA (D + 2), fun s P G hs hP hPD hA hg => ?_⟩ + set q := ExplicitTensorNetwork.capPrecision (P, G) with hq + set x₀ := root q with hx₀ + set m := G.2.numEdges with hm + set n := (FastContraction.encInput (P, G)).length with hn + have hsm : s ≤ m := hA.2.2.2.2.2 + have hmn : m < n := numEdges_lt_length_encInput P G + have hp₀ : q.1 ≤ D * m := + (min_le_right _ _).trans (hPD.trans (Nat.mul_le_mul_left _ hsm)) + -- The derivation. + obtain ⟨N, H, he, hN, hH⟩ : ∃ N H, (scheme nativeMk).Eval (reachInv nativeMk x₀) x₀ + (0, value nativeMk x₀) N H ∧ (max sA (D + 2) ≤ s → 2 * N - 1 ≤ fuel q) ∧ H ≤ m + 1 := by + by_cases hs₀ : max sA (D + 2) ≤ s + · have hm2 : D + 2 ≤ m := by omega + have hC1 : q.1 / m + 1 ≤ D + 1 := by + have := Nat.div_le_div_right (c := m) hp₀ + rw [Nat.mul_div_cancel _ (by omega)] at this + omega + have hB := hsA m (by omega) (q.1 / m + 1) hC1 + have hpC : q.1 ≤ (q.1 / m + 1) * m := (Nat.lt_div_mul_add (a := q.1) (b := m) (by omega)).le.trans + (by rw [add_mul, one_mul]) + obtain ⟨N, H, he, hN, hH⟩ := exists_eval_onSchedule hB + (onSchedule_root_capPrecision hg (Nat.le_add_left 1 _) hpC) rfl (by omega) + refine ⟨N, H, he, fun _ => ?_, hH.trans ((Nat.log_lt_self 2 (by omega)).le.trans + (Nat.le_succ _))⟩ + exact two_mul_sub_one_le_fuel hm2 hp₀ hN + · obtain ⟨N, H, he, hH⟩ := exists_eval_reachInv (mk := nativeMk) (x₀ := x₀) rfl + exact ⟨N, H, he, fun h => absurd h hs₀, hH⟩ + -- The bounds. + obtain hKS' := hKS s P G hs hP hPD hA hg + obtain hKL' := hKL s P G hs hP hPD hA hg + set K := KS + KL + 1 + have hB1 : 1 ≤ n + 1 := by omega + refine ⟨reachInv nativeMk x₀, N, H, KS * (n + 1) ^ KS, KL * (n + 1) ^ KL * 2 ^ (KL * s / + Nat.sqrt (Nat.log 2 s)), KL * (n + 1) ^ KL, he, hN, fun x hx => ⟨(hKS' x hx).1, fun hl => ?_⟩, + fun x a ha => (hKS' x ha.1).2.1 a ha, fun v ⟨x, hx, hv⟩ => hv ▸ (hKS' x hx).2.2, ?_, ?_⟩ + · have hl' : active x = false := by simpa [scheme] using hl + exact hKL' x hx hl' + · have h1 : H ≤ 1 * (n + 1) ^ K := by + have : n + 1 ≤ (n + 1) ^ K := Nat.le_self_pow (by omega) _ + omega + have h2 := mul_pow_le_mul_pow (a := KS) hB1 (show KS ≤ K by omega) + have h3 := mul_pow_le_mul_pow (a := KL) hB1 (show KL ≤ K by omega) + calc H + KS * (n + 1) ^ KS + KL * (n + 1) ^ KL + ≤ 1 * (n + 1) ^ K + KS * (n + 1) ^ K + KL * (n + 1) ^ K := by omega + _ = K * (n + 1) ^ K := by ring + · refine Nat.mul_le_mul (mul_pow_le_mul_pow' hB1 (show KL ≤ K by omega)) + (Nat.pow_le_pow_right (by norm_num) (Nat.div_le_div_right (Nat.mul_le_mul_right _ + (by omega)))) +where + /-- `a B ^ a ≤ K B ^ K` for `1 ≤ B` and `a ≤ K`. -/ + mul_pow_le_mul_pow' {a B K : ℕ} (hB : 1 ≤ B) (ha : a ≤ K) : a * B ^ a ≤ K * B ^ K := + Nat.mul_le_mul ha (Nat.pow_le_pow_right hB ha) + +end Cslib.Complexity.CountingSimulation From 3afd33939deca3091ff1dd84970e5ab839fc78cd Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:27:41 -0400 Subject: [PATCH 105/129] style: wrap long lines Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Classes/CountingSimulation/FastAnalysis.lean | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean index ee65ad91e4..0ec7d020e9 100644 --- a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean @@ -137,8 +137,8 @@ theorem exists_activeBounds_le (D : ℕ) : /-- **Guarded roots are on the schedule**, for every constant `C ≥ 1` with precision at most `C m`. -/ -theorem onSchedule_root_capPrecision {P C : ℕ} {G : ℕ × ExplicitTensorNetwork ℕ} (hg : fastGuard G = true) - (hC : 1 ≤ C) (hp : (ExplicitTensorNetwork.capPrecision (P, G)).1 ≤ C * G.2.numEdges) : +theorem onSchedule_root_capPrecision {P C : ℕ} {G : ℕ × ExplicitTensorNetwork ℕ} + (hg : fastGuard G = true) (hC : 1 ≤ C) (hp : (ExplicitTensorNetwork.capPrecision (P, G)).1 ≤ C * G.2.numEdges) : OnSchedule C G.2.numEdges (root (ExplicitTensorNetwork.capPrecision (P, G))) := by have hdeg : G.2.maxDegree ≤ degreeBound G.2.numEdges := by simp only [fastGuard, decide_eq_true_eq] at hg @@ -220,8 +220,8 @@ theorem fastAnalysis_nativeMk_of (hS : FastSizes) (hL : FastLeaves) : FastAnalys rw [Nat.mul_div_cancel _ (by omega)] at this omega have hB := hsA m (by omega) (q.1 / m + 1) hC1 - have hpC : q.1 ≤ (q.1 / m + 1) * m := (Nat.lt_div_mul_add (a := q.1) (b := m) (by omega)).le.trans - (by rw [add_mul, one_mul]) + have hpC : q.1 ≤ (q.1 / m + 1) * m := + (Nat.lt_div_mul_add (a := q.1) (b := m) (by omega)).le.trans (by rw [add_mul, one_mul]) obtain ⟨N, H, he, hN, hH⟩ := exists_eval_onSchedule hB (onSchedule_root_capPrecision hg (Nat.le_add_left 1 _) hpC) rfl (by omega) refine ⟨N, H, he, fun _ => ?_, hH.trans ((Nat.log_lt_self 2 (by omega)).le.trans From ba1e006d9d82f9a54b6d065e07556e6b7035da0b Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:27:55 -0400 Subject: [PATCH 106/129] style: wrap a long line Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Complexity/Classes/CountingSimulation/FastAnalysis.lean | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean index 0ec7d020e9..1f7cefdec0 100644 --- a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean @@ -138,7 +138,8 @@ theorem exists_activeBounds_le (D : ℕ) : /-- **Guarded roots are on the schedule**, for every constant `C ≥ 1` with precision at most `C m`. -/ theorem onSchedule_root_capPrecision {P C : ℕ} {G : ℕ × ExplicitTensorNetwork ℕ} - (hg : fastGuard G = true) (hC : 1 ≤ C) (hp : (ExplicitTensorNetwork.capPrecision (P, G)).1 ≤ C * G.2.numEdges) : + (hg : fastGuard G = true) (hC : 1 ≤ C) + (hp : (ExplicitTensorNetwork.capPrecision (P, G)).1 ≤ C * G.2.numEdges) : OnSchedule C G.2.numEdges (root (ExplicitTensorNetwork.capPrecision (P, G))) := by have hdeg : G.2.maxDegree ≤ degreeBound G.2.numEdges := by simp only [fastGuard, decide_eq_true_eq] at hg From 342c523c6f574267f73a9de19e218fe59ab33f50 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:31:31 -0400 Subject: [PATCH 107/129] feat(Classes/CountingSimulation): the leaf bounds of the native recursion from the reached-call facts Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../CountingSimulation/FastLeaves.lean | 101 ++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 Complexity/Computability/Complexity/Classes/CountingSimulation/FastLeaves.lean diff --git a/Complexity.lean b/Complexity.lean index cfc8355a5f..abe4c527b9 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1110,6 +1110,7 @@ public import Complexity.Computability.Complexity.Classes.CountingSimulation.Blo public import Complexity.Computability.Complexity.Classes.CountingSimulation.Containments public import Complexity.Computability.Complexity.Classes.CountingSimulation.FastAlgorithm public import Complexity.Computability.Complexity.Classes.CountingSimulation.FastAnalysis +public import Complexity.Computability.Complexity.Classes.CountingSimulation.FastLeaves public import Complexity.Computability.Complexity.Classes.CountingSimulation.Guarded public import Complexity.Computability.Complexity.Classes.CountingSimulation.Interfaces public import Complexity.Computability.Complexity.Classes.DP diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastLeaves.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastLeaves.lean new file mode 100644 index 0000000000..c02a585e71 --- /dev/null +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastLeaves.lean @@ -0,0 +1,101 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Complexity.Classes.CountingSimulation.FastAnalysis +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionLeaf + +/-! +# The leaf bounds of the native recursion + +The leaf clause of the resource analysis of the native recursion of Theorem D.1 of +[OpenAI2026ETH] (`Cslib.Complexity.CountingSimulation.FastLeaves`), from the cost of a leaf with +the packet shape (`Cslib.Circuits.Frontier.FastContraction.exists_leafCertificate_le`), the sizes +of the reached calls (`Cslib.Complexity.CountingSimulation.FastSizes`) and three facts about the +reached calls: at most twice the scale in edges and, from some size on, at most the leaf size in +edges at a leaf and precision at most the square of the scale. + +Every reached call is well-formed and has the packet shape (`FastContraction.Reach.wf_packetShape`), +by `FastContraction.wfChildren_nativeMk` and `FastContraction.packetShape_child`. + +## Main results + +* `Cslib.Circuits.Frontier.FastContraction.Reach.wf_packetShape`: the reached calls are + well-formed and have the packet shape. +* `Cslib.Complexity.CountingSimulation.fastLeaves_of`: the leaf bounds. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.6–§D.4.7, + pp. 63–64. +-/ + +public section + +namespace Cslib.Circuits.Frontier.FastContraction + +/-- **The reached calls are well-formed and have the packet shape**, from a well-formed root with +the packet shape. -/ +theorem Reach.wf_packetShape {x₀ x : Call} (h : Reach nativeMk x₀ x) (hw : WF x₀) + (hs : PacketShape x₀) : WF x ∧ PacketShape x := by + induction h with + | root => exact ⟨hw, hs⟩ + | step c _ ha hc ih => + exact ⟨wfChildren_nativeMk _ ih.1 ha c hc, packetShape_child ih.1 ha ih.2 hc⟩ + +end Cslib.Circuits.Frontier.FastContraction + +namespace Cslib.Complexity.CountingSimulation + +open Cslib.Circuits.Frontier Cslib.Circuits.Frontier.ContractionSchedule + Cslib.Circuits.Frontier.FastContraction + +/-- **The leaf bounds of the native recursion**, from the sizes of the reached calls and three +facts about them on admissible guarded inputs: every reached call has at most twice its scale in +edges, and for each `C` and `D`, from some size on, every reached leaf has at most the leaf size in +edges and precision at most the square of its scale. -/ +theorem fastLeaves_of (hS : FastSizes) + (hE : ∀ (P : ℕ) (G : ℕ × ExplicitTensorNetwork ℕ) (x : Call), + Reach nativeMk (root (ExplicitTensorNetwork.capPrecision (P, G))) x → + x.edges ≤ 2 * x.scale) + (hbig : ∀ C D : ℕ, ∃ s₀ : ℕ, ∀ (s P : ℕ) (G : ℕ × ExplicitTensorNetwork ℕ), 2 ≤ s → 1 ≤ P → + P ≤ D * s → ExplicitAdmissible C s G → fastGuard G = true → s₀ ≤ s → + ∀ x, Reach nativeMk (root (ExplicitTensorNetwork.capPrecision (P, G))) x → + active x = false → x.edges ≤ leafSize x.scale ∧ x.prec ≤ x.scale ^ 2) : + FastLeaves := by + intro C D + obtain ⟨KS, hKS⟩ := hS C D + obtain ⟨s₀, hs₀⟩ := hbig C D + obtain ⟨KB, hKB⟩ := exists_leafCertificate_le C 2 s₀ + refine ⟨KB * (KS + 1) ^ KB + KS * KB + KB, fun s P G hs hP hPD hA hg x hx hl => ?_⟩ + set n := (FastContraction.encInput (P, G)).length with hn + have hscale : x.scale = G.2.numEdges := hx.scale_eq + have hsm : s ≤ x.scale := hscale ▸ hA.2.2.2.2.2 + have hmC : x.scale ≤ C * s := hscale ▸ hA.2.1 + obtain ⟨-, hshape⟩ := hx.wf_packetShape (wf_root P hg) (packetShape_root _) + obtain ⟨hT, hSp⟩ := hKB nativeMk s x hs hsm hmC hshape (hE P G x hx) + fun h => hs₀ s P G hs hP hPD hA hg h x hx hl + have hlen := (hKS s P G hs hP hPD hA hg x hx).1 + rw [← hn] at hlen + -- `n_x + 1 ≤ (KS + 1) (n + 1) ^ KS`. + have hB1 : 1 ≤ (n + 1) ^ KS := Nat.one_le_pow _ _ (by omega) + have hx1 : (encCall x).length + 1 ≤ (KS + 1) * (n + 1) ^ KS := by + rw [add_mul, one_mul]; omega + set K := KB * (KS + 1) ^ KB + KS * KB + KB + have hpoly : KB * ((encCall x).length + 1) ^ KB ≤ K * (n + 1) ^ K := + calc KB * ((encCall x).length + 1) ^ KB ≤ KB * ((KS + 1) * (n + 1) ^ KS) ^ KB := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_left hx1 _) + _ = KB * (KS + 1) ^ KB * (n + 1) ^ (KS * KB) := by rw [mul_pow, ← pow_mul]; ring + _ ≤ K * (n + 1) ^ K := + Nat.mul_le_mul (by omega) (Nat.pow_le_pow_right (by omega) (by omega)) + have hexp : KB * s / Nat.sqrt (Nat.log 2 s) ≤ K * s / Nat.sqrt (Nat.log 2 s) := + Nat.div_le_div_right (Nat.mul_le_mul_right _ (by omega)) + exact ⟨hT.trans (Nat.mul_le_mul hpoly (Nat.pow_le_pow_right (by norm_num) hexp)), + hSp.trans hpoly⟩ + +end Cslib.Complexity.CountingSimulation From ac56eab1c8aae3eefeb37761e60707c63b6ffaf4 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:32:23 -0400 Subject: [PATCH 108/129] test: the reached root of a guarded input has the packet shape Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- ComplexityTests/FastLeaves.lean | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 ComplexityTests/FastLeaves.lean diff --git a/ComplexityTests/FastLeaves.lean b/ComplexityTests/FastLeaves.lean new file mode 100644 index 0000000000..7cbfec8a8f --- /dev/null +++ b/ComplexityTests/FastLeaves.lean @@ -0,0 +1,29 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.Complexity.Classes.CountingSimulation.FastLeaves + +/-! # The leaf bounds of the native recursion: checked instances + +A guarded input whose root is reached, well-formed and has the packet shape. -/ + +namespace ComplexityTests.FastLeaves + +open Cslib.Circuits.Frontier Cslib.Circuits.Frontier.FastContraction + Cslib.Complexity.CountingSimulation + +/-- Two rows `0, 1` and two columns `2, 3`, joined by edges `0 : 0 → 2` and `1 : 1 → 3`. -/ +def pair : ℕ × ExplicitTensorNetwork ℕ := + (2, ⟨2, [([0], [1, 2]), ([1], [3, 4]), ([0], [1, 1]), ([1], [1, 0])]⟩) + +example : fastGuard pair = true := by decide + +-- The root of a guarded input is reached, well-formed, and has the packet shape. +example : WF (root (ExplicitTensorNetwork.capPrecision (1, pair))) ∧ + PacketShape (root (ExplicitTensorNetwork.capPrecision (1, pair))) := + (Reach.root (mk := nativeMk)).wf_packetShape (wf_root 1 (by decide)) (packetShape_root _) + +end ComplexityTests.FastLeaves From c972643583afee9a5c82ad5e614e2eb1c22efd1b Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:34:38 -0400 Subject: [PATCH 109/129] refactor(circuit/frontier): unfold nativeMk through one lemma; helpers for call shapes Every unfolding of nativeMk in the analysis goes through nativeMk_apply_of_active (well-formed, active, cursor state), so the caps on rows and exponents change one proof. Adds the arithmetic of the schedule at an active call and the bounds of the exponent tuples, points and node sizes used by the shape invariant. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../FastContractionSchedule.lean | 15 +- .../TensorNetwork/FastContractionShape.lean | 220 ++++++++++++++++++ 2 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean index 7431387434..6b89d270ca 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean @@ -69,6 +69,17 @@ namespace Cslib.Circuits.Frontier.FastContraction open ContractionSchedule ChildCursor NativeRecovery NativeChild +/-- **The native children of a well-formed active call**, unfolded: the rows and the native child +of the parent with its native packing, at the cursor state. Every unfolding of `nativeMk` in the +analysis goes through this lemma. -/ +theorem nativeMk_apply_of_active {x : Call} {c : State} (_hx : WF x) (_ha : active x = true) + (_hc : c ∈ children x) : + nativeMk x c = (childRows x.rows (level x ((nodes x).getD c.1 0)) x.net + (nativeParentPacking x.rows (slots x) x.net) c.2.2.2, + nativeChild x.fl (slots x) (level x ((nodes x).getD c.1 0)) (nodePrec x c.1) + (node (slots x) ((nodes x).getD c.1 0)) ((points x).getD c.2.1 []) x.rows x.net + (nativeParentPacking x.rows (slots x) x.net) c.2.2.1 c.2.2.2) := rfl + /-- **A call on the schedule** of a root with `m` edges, for the constant `C`: it keeps the root's scale and modulus, is well-formed, has maximum degree at most `degreeBound m` and fewer edges than its bound, and has at most `leafSize m` edges or is an active call of the schedule. -/ @@ -178,13 +189,13 @@ theorem child (hB : ∀ d M p, Active C m d M p → ActiveBounds C m d M p) (h : (Δ := degreeBound m) (by rw [hslots]; exact hb.degree_mul_pairs_sq_le) x.fl (nodePrec x c.1) (node (slots x) ((nodes x).getD c.1 0)) ((points x).getD c.2.1 []) c.2.2.1 ⟨c.2.2.2, hg⟩ - simpa only [FastContraction.child, nativeMk, packing, Call.edges] using this + simpa only [FastContraction.child, nativeMk_apply_of_active h.wf ha hc, Call.edges] using this have hM := one_le_edges_of_active ha refine ⟨h.scale_eq, h.fl_eq, wfChildren_nativeMk x h.wf ha c hc, fun v hv => ?_, ?_, ?_⟩ · refine length_le_of_mem_vertices_nativeChild h.wf.2 hK hia h.degree_le (by rw [hslots]; exact hb.two_mul_slots_le) x.fl (nodePrec x c.1) (node (slots x) ((nodes x).getD c.1 0)) ((points x).getD c.2.1 []) c.2.2.1 ⟨c.2.2.2, hg⟩ ?_ - simpa only [FastContraction.child, nativeMk, packing] using hv + simpa only [FastContraction.child, nativeMk_apply_of_active h.wf ha hc] using hv · change _ < x.edges omega · by_cases hs : (FastContraction.child x nativeMk c).edges ≤ leafSize m diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean new file mode 100644 index 0000000000..f97eb09a3e --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean @@ -0,0 +1,220 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionSchedule +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeChildPolyTime +import Mathlib.Data.List.GetD + +/-! +# The shapes of the calls of the native contraction recursion + +Structural bounds on every call reached from a root of the native recursion +(`FastContraction.scheme FastContraction.nativeMk`) with `m` edges, valid for every input, whatever +the schedule: they are what makes the calls, frames and values polynomially long. + +**The invariant** (`FastContraction.Shape`). A reached call at depth `d` keeps the root's scale and +modulus, is well-formed, has at most `2 m` edges, precision at most `p₀ + 40 m² d` for the root's +precision `p₀`, at most `V₀ + m² d` vertices for the root's `V₀` vertices, as many rows (unless it +is the root), and every vertex descriptor is a table of the root or a packet descriptor whose fields +are bounded (`FastContraction.PacketBound`). It passes to the native children +(`FastContraction.Shape.child`): a child has at most twice the edges of its parent +(`NativeChild.numEdges_nativeChild_le`), its precision `p_t = p + 8 h 2 ^ (a - i) + 8 M (a + 1)` is +at most `p + 40 m²` since `2 ^ a ≤ m`, and it adds at most `M (2 ^ a) ^ 12 ≤ m²` packet vertices +(`NativeChild.numPackets_nativeParentPacking_le`). + +## Main definitions + +* `Cslib.Circuits.Frontier.FastContraction.PacketBound`: the bounded packet descriptors. +* `Cslib.Circuits.Frontier.FastContraction.Shape`: the invariant. + +## Main results + +* `Cslib.Circuits.Frontier.FastContraction.Shape.child`: the invariant passes to the native + children. +* `Cslib.Circuits.Frontier.FastContraction.Reach.shape`: every reached call satisfies it. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §D.4.6–§D.4.7 (pp. 63–64). +-/ + +public section + +namespace Cslib.Circuits.Frontier.FastContraction + +open ContractionSchedule ChildCursor NativeRecovery NativeChild + +/-- **A bounded packet descriptor** for a root with modulus `fl` and `m` edges, at precision at most +`P`, on a vertex with `k` ports: its precision is at most `P` and is its number of exponents per +tile, its modulus is `fl`, it has `2 ^ a` slots with `a ≤ ⌊log₂ m⌋`, `2 ^ (i + 1)` tiles with `i ≤ a`, a grid point +of size at most `2 ^ a`, no column list, a 0/1 interpolation value with at most `e` coordinates, +`k` tags with slots below `2 ^ a`, and at most `2 ^ (i + 1) p` exponents, each at most `p`. -/ +@[expose] def PacketBound (fl : List ℕ) (m P k : ℕ) (d : NativePacket.PacketDesc) : Prop := + d.1.p ≤ P ∧ d.1.nb = d.1.p ∧ d.1.fl = fl ∧ d.1.a ≤ Nat.log 2 m ∧ d.1.L = 2 ^ d.1.a ∧ + d.1.i ≤ d.1.a ∧ d.1.tiles = 2 ^ (d.1.i + 1) ∧ d.1.t.natAbs ≤ 2 ^ d.1.a ∧ d.1.cols = [] ∧ + d.1.ζ.length ≤ fl.length ∧ (∀ z ∈ d.1.ζ, z ≤ 1) ∧ d.2.1.length = k ∧ + (∀ b ∈ d.2.1, b.2 < 2 ^ d.1.a) ∧ d.2.2.length ≤ d.1.tiles * d.1.p ∧ ∀ b ∈ d.2.2, b ≤ d.1.p + +theorem PacketBound.mono {fl : List ℕ} {m P P' k : ℕ} {d : NativePacket.PacketDesc} + (h : PacketBound fl m P k d) (hP : P ≤ P') : PacketBound fl m P' k d := + ⟨h.1.trans hP, h.2⟩ + +/-- **The shape of a call** reached from a root `x₀` with `m` edges, precision `p₀` and `V₀` +vertices, at depth `d`. -/ +structure Shape (x₀ x : Call) : Prop where + /-- The scale is the root's. -/ + scale_eq : x.scale = x₀.scale + /-- The modulus is the root's. -/ + fl_eq : x.fl = x₀.fl + /-- The call is well-formed. -/ + wf : WF x + /-- The bound plus the depth does not exceed the root's. -/ + bound_add_depth_le : x.bound + x.depth ≤ x₀.bound + x₀.depth + /-- At most twice the root's number of edges. -/ + edges_le : x.edges ≤ 2 * x₀.edges + /-- The precision grows by at most `40 m²` per level. -/ + prec_le : x.prec ≤ x₀.prec + x.depth * (40 * x₀.edges ^ 2) + /-- The vertices grow by at most `m²` per level. -/ + vertices_le : x.net.vertices.length ≤ x₀.net.vertices.length + x.depth * x₀.edges ^ 2 + /-- The rows are the root's or at most the vertices of a parent. -/ + rows_le : x = x₀ ∨ x.rows ≤ x₀.net.vertices.length + x.depth * x₀.edges ^ 2 + /-- Every descriptor is a descriptor of the root or a bounded packet descriptor. -/ + desc : ∀ v ∈ x.net.vertices, (∃ w ∈ x₀.net.vertices, v.2 = w.2) ∨ + ∃ d, v.2 = .inr d ∧ + PacketBound x₀.fl x₀.edges (x₀.prec + x.depth * (40 * x₀.edges ^ 2)) v.1.length d + +/-- The entries of a tuple of weight below `p` for positive weights are below `p`. -/ +theorem lt_of_mem_budgetTuples {ws β : List ℕ} {p : ℕ} (hw : ∀ w ∈ ws, 0 < w) + (hβ : β ∈ BudgetTuples.budgetTuples ws p) : ∀ b ∈ β, b < p := by + have key : ∀ (ws β : List ℕ), (∀ w ∈ ws, 0 < w) → β.length = ws.length → + ∀ b ∈ β, b ≤ BudgetTuples.weight ws β := by + intro ws + induction ws with + | nil => intro β _ hl b hb; simp_all + | cons w ws ih => + intro β hw hl b hb + rcases β with _ | ⟨b', β⟩ + · simp at hb + · simp only [BudgetTuples.weight_cons_cons] + simp only [List.length_cons, Nat.add_right_cancel_iff] at hl + rcases List.mem_cons.mp hb with rfl | hb + · have := hw w List.mem_cons_self + nlinarith + · have := ih β (fun w' hw' => hw w' (List.mem_cons_of_mem _ hw')) hl b hb + omega + obtain ⟨hl, hwt⟩ := (BudgetTuples.mem_budgetTuples hw).mp hβ + exact fun b hb => (key ws β hw hl b hb).trans_lt hwt + +/-- The exponent tuple of a child of an active call has entries below the node's precision. -/ +theorem lt_nodePrec_of_mem_children {x : Call} {c : State} (hc : c ∈ children x) : + ∀ b ∈ c.2.2.1, b < nodePrec x c.1 := by + simp only [children, cursorList, nodeBlock, pointBlock, groupBlock, List.mem_flatMap, + List.mem_range, List.mem_map] at hc + obtain ⟨j, -, i, -, β, hβ, g, -, rfl⟩ := hc + exact lt_of_mem_budgetTuples (weights_pos j) hβ + +/-- The ports of a vertex of a well-formed call are at most its edges. -/ +theorem length_le_edges_of_wf {x : Call} (hx : WF x) {v : List ℕ × VertexDesc} + (hv : v ∈ x.net.vertices) : v.1.length ≤ x.edges := by + obtain ⟨k, hk, rfl⟩ := List.getElem_of_mem hv + have hnd : (x.net.vertices[k]).1.Nodup := nodup_ports hx.2 ⟨k, hk⟩ + have hsub : (x.net.vertices[k]).1.toFinset ⊆ Finset.range x.edges := fun j hj => + Finset.mem_range.mpr (hx.2.1 _ (List.getElem_mem hk) j (List.mem_toFinset.mp hj)) + have := Finset.card_le_card hsub + rw [List.toFinset_card_of_nodup hnd, Finset.card_range] at this + simpa using this + +section Child + +variable {x₀ x : Call} + +/-- The schedule's numbers at an active call at scale `m`: `a ≤ ⌊log₂ m⌋`, `2 ^ (12 a) ≤ m`, +`u ≤ ⌊log₂ m⌋ < m`, and `h ≤ 3 M + u`. -/ +theorem slots_facts (ha : active x = true) : + slots x ≤ Nat.log 2 x.scale ∧ (2 ^ slots x) ^ 12 ≤ x.scale ∧ 2 ^ slots x ≤ x.scale ∧ + levels x ≤ Nat.log 2 x.scale ∧ Nat.log 2 x.scale < x.scale ∧ + step x ≤ 3 * x.edges + levels x := by + have ha' := ha + simp only [active, decide_eq_true_eq] at ha' + obtain ⟨-, hu, -, -, -, -, -⟩ := ha' + set L := Nat.log 2 x.scale + have hL1 : 1 ≤ L := by + have : levels x = L / 2 ^ (17 + x.depth) := rfl + rcases Nat.eq_zero_or_pos L with h0 | h0 + · rw [this, h0, Nat.zero_div] at hu; omega + · exact h0 + have hs0 : x.scale ≠ 0 := by + intro h0; simp only [L, h0, Nat.log_zero_right] at hL1; omega + have hsL : 2 ^ L ≤ x.scale := Nat.pow_log_le_self 2 hs0 + have ha32 : slots x ≤ L / 32768 := slotBits_le _ _ + have h12 : (2 ^ slots x) ^ 12 ≤ x.scale := by + rw [← pow_mul] + exact (Nat.pow_le_pow_right (by norm_num) (by omega)).trans hsL + refine ⟨by omega, h12, (Nat.pow_le_pow_right (by norm_num) (by omega)).trans hsL, + Nat.div_le_self _ _, Nat.log_lt_self 2 hs0, ?_⟩ + unfold step taperStep + rw [show taper x.scale x.depth = levels x from rfl] + rcases Nat.eq_zero_or_pos (levels x) with h0 | h0 + · rw [h0, Nat.div_zero]; exact Nat.zero_le _ + · exact (Nat.div_le_self _ _).trans (by omega) + +/-- **The node precisions grow by at most `40 m²`**, at an active call at scale `m` with at most +`m` edges. -/ +theorem nodePrec_le_add (ha : active x = true) (hM : x.edges ≤ x.scale) (j : ℕ) : + nodePrec x j ≤ x.prec + 40 * x.scale ^ 2 := by + obtain ⟨haL, -, hL, huL, hLs, hh⟩ := slots_facts ha + unfold nodePrec nodePrecision + set m := x.scale + have h1 : 2 ^ (slots x - level x ((nodes x).getD j 0)) ≤ m := + (Nat.pow_le_pow_right (by norm_num) (Nat.sub_le _ _)).trans hL + have h2 : step x ≤ 4 * m := by omega + have h3 : 8 * step x * 2 ^ (slots x - level x ((nodes x).getD j 0)) ≤ 32 * m ^ 2 := + calc 8 * step x * 2 ^ (slots x - level x ((nodes x).getD j 0)) ≤ 8 * (4 * m) * m := + Nat.mul_le_mul (Nat.mul_le_mul_left _ h2) h1 + _ = 32 * m ^ 2 := by ring + have h4 : 8 * x.edges * (slots x + 1) ≤ 8 * m ^ 2 := + calc 8 * x.edges * (slots x + 1) ≤ 8 * m * m := Nat.mul_le_mul (Nat.mul_le_mul_left _ hM) + (by omega) + _ = 8 * m ^ 2 := by ring + omega + +/-- The level of a node index is at most `a`, and its node has size at most `2 ^ a`. -/ +theorem level_le_slots_of_mem_children (ha : active x = true) {c : State} (hc : c ∈ children x) : + level x ((nodes x).getD c.1 0) < slots x ∧ + (node (slots x) ((nodes x).getD c.1 0)).natAbs ≤ 2 ^ slots x := by + have hu := OnSchedule.level_lt_levels hc + have ha' := ha + simp only [active, decide_eq_true_eq] at ha' + obtain ⟨-, -, -, hua, -, -, -⟩ := ha' + refine ⟨hu.trans_le hua, ?_⟩ + obtain ⟨hj, -, -⟩ := lt_of_mem_children hc + have hkmem : (nodes x).getD c.1 0 ∈ nodes x := by + rw [List.getD_eq_getElem _ _ hj]; exact List.getElem_mem hj + obtain ⟨hk, -, -⟩ := mem_nodes.mp hkmem + rw [pow_succ] at hk + unfold node + have e : (2 : ℤ) ^ slots x = ((2 ^ slots x : ℕ) : ℤ) := by push_cast; rfl + rw [e] + omega + +/-- The interpolation points are 0/1 lists with `e` coordinates. -/ +theorem getD_points_facts (x : Call) (i : ℕ) : + ((points x).getD i []).length ≤ x.fl.length ∧ ∀ z ∈ (points x).getD i [], z ≤ 1 := by + by_cases hi : i < (points x).length + · rw [List.getD_eq_getElem _ _ hi] + simp only [points, List.getElem_map, digits, List.length_map, List.length_range, le_refl, + List.mem_map, List.mem_range, true_and, forall_exists_index, and_imp] + rintro z j - rfl + split_ifs <;> omega + · rw [List.getD_eq_default _ _ (by omega)] + simp + +end Child + +end Cslib.Circuits.Frontier.FastContraction From 6ba1ca3ff44cfff55c289c04eab458a6860eac6f Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:34:42 -0400 Subject: [PATCH 110/129] feat(circuit/frontier): the components of the native contraction recursion in polynomial time `FastContraction.scheme_nativeMk_polyTime : (scheme nativeMk).PolyTime encCall encFrame encValue`, the `hpoly` hypothesis of `contractionAlgorithmOfNative` (#1538, #1970), from the native child in polynomial time (#1973). `contractionAlgorithmOfNative` now takes only the resource analysis: `stepCorrect_nativeMk` is unconditional, with the points' unit differences from #1973's `pairwise_isUnit_liftO_sub_digits`. - `FastContractionPolyTime.lean`: the schedule (`taper`, `slotBits`, `taperStep`, `leafSize`, `degreeBound`) and the data of a call (`active`, nodes, levels, `nodePrec`, the packing and its number of packets, the weights, points and table) in polynomial time on every call; `scheme_polyTime` for any polynomial-time builder; `nativeMk_polyTime`. - `nativeMk` caps the rows at the number of vertices (`rowCap`, also used by `packing`) and the exponents at the node's precision, since both are binary on arbitrary frames; `nativeMk_eq` shows the caps are inactive on well-formed active calls and cursor states. - `BudgetTuples.lt_of_mem_budgetTuples`: entries of tuples of weight below `p` are below `p`. - `FastAlgorithm.lean` reuses the public `degreeBound_polyTime`. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../TensorNetwork/FastContraction.lean | 21 +- .../FastContractionPolyTime.lean | 872 ++++++++++++++++++ .../TensorNetwork/FastContractionStep.lean | 108 ++- .../CountingSimulation/FastAlgorithm.lean | 43 +- .../Combinatorics/BudgetTuples.lean | 21 + ComplexityTests/FastContractionPolyTime.lean | 51 + 7 files changed, 1059 insertions(+), 58 deletions(-) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionPolyTime.lean create mode 100644 ComplexityTests/FastContractionPolyTime.lean diff --git a/Complexity.lean b/Complexity.lean index e333904ce2..f6e3b68073 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -899,6 +899,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.DirectCont public import Complexity.Computability.Circuit.Frontier.TensorNetwork.Explicit public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContraction public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionMachine +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionPolyTime public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionStep public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates public import Complexity.Computability.Circuit.Frontier.TensorNetwork.KernelProduct diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean index 915b801846..aab15a930b 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContraction.lean @@ -162,9 +162,26 @@ theorem edges_lt_bound_of_active {x : Call} (h : active x = true) : x.edges < x. @[expose] def nodePrec (i : ℕ) : ℕ := nodePrecision x.prec (step x) (slots x) x.edges (level x ((nodes x).getD i 0)) -/-- **The packing of Lemma D.3**, natively (`nativeParentPacking`, `r = 64`). -/ +/-- The number of rows capped at the number of vertices. The packing and the child builder use it +in place of the number of rows: it changes nothing for a rows-first network with an edge +(`FastContraction.rowCap_eq`), and it keeps them polynomial-time on every call, since the number +of rows is stored in binary and the packing lists one word per row. -/ +@[expose] def rowCap : ℕ := min x.rows x.net.vertices.length + +/-- The cap on the rows is inactive when there are at most as many rows as vertices. -/ +theorem rowCap_eq {x : Call} (h : x.rows ≤ x.net.vertices.length) : rowCap x = x.rows := + min_eq_left h + +/-- **The packing of Lemma D.3**, natively (`nativeParentPacking`, `r = 64`), with the rows +capped (`FastContraction.rowCap`). -/ @[expose] def packing : (List (List ℕ) × List (List ℕ)) × ℕ × List (ℕ × ℕ) := - nativeParentPacking x.rows (slots x) x.net + nativeParentPacking (rowCap x) (slots x) x.net + +/-- The packing is the native packing of the rows when there are at most as many rows as +vertices. -/ +theorem packing_eq {x : Call} (h : x.rows ≤ x.net.vertices.length) : + packing x = nativeParentPacking x.rows (slots x) x.net := by + rw [packing, rowCap_eq h] /-- **The weights of the modes of the `i`-th node**, packet-major over `(q, y₀, b)` with `y₀ < 2 ^ (v₂ t + 1)` and `b < p_t`: mode `(q, y₀, b)` has weight `b + 1`. -/ diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionPolyTime.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionPolyTime.lean new file mode 100644 index 0000000000..4d83637e21 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionPolyTime.lean @@ -0,0 +1,872 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionMachine +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.NativeChildPolyTime +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionStep +import Complexity.Computability.PolyTime.BudgetTuples +import Complexity.Computability.PolyTime.GaloisRingArithmetic +import Complexity.Computability.PolyTime.GaloisRingSeries +import Mathlib.Data.List.GetD +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Binary +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Div +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Log +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.SqrtLog +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Sqrt +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.BitString +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Membership +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Option +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryDivision +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeBinaryPower +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListLookup +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListMap +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeListPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeNatFrontends +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeOptionPrimitives +import Complexity.Computability.Machines.Turing.MultiTape.Internal.PolyTimeRadix + +/-! +# The components of the native contraction recursion in polynomial time + +The scheme `Cslib.Circuits.Frontier.FastContraction.scheme mk` of the contraction recursion of +[OpenAI2026ETH] has polynomial-time components (`Cslib.GenericRAM.StackRecursion.Scheme.PolyTime`) +for the encodings `FastContraction.encCall`, `encFrame` and `encValue` whenever the child builder +`mk` is polynomial-time (`FastContraction.scheme_polyTime`). The native child builder is +(`FastContraction.nativeMk_polyTime`), so the scheme of `FastContraction.nativeMk` has +polynomial-time components (`FastContraction.scheme_nativeMk_polyTime`). + +**Every call.** The components compute the data of a call on every call and every frame, not only +on the active calls and the cursor's states. The scale `s`, the depth, the edge bound, the +precision and the number `M` of edges are in unary, so the schedule's quantities are polynomial in +the input: +* `a = slotBits s d ≤ ⌊log₂ s⌋ / 32768`, so `2 ^ a ≤ s + 1` + (`ContractionSchedule.two_pow_slotBits_le`); +* the multiplicity step `h = ⌈3 M / u⌉` is at most `3 M + u`; +* every level is at most `a + 1` (`FastContraction.level_le`), so a node has at most `4 (s + 1)` + tiles; +* the native packing has at most `M (2 ^ a) ^ 12` packets + (`NativeChild.numPackets_nativeParentPacking_le`), which bounds the mode weights and the + cursor's exponent tuples. + +The number of rows and the cursor's exponents are stored in binary, so the packing and the child +builder cap them, at the number of vertices (`FastContraction.rowCap`) and at the node's precision. +The caps change nothing on well-formed active calls (`FastContraction.nativeMk_eq`). + +## Main results + +* `Cslib.Circuits.Frontier.ContractionSchedule.taper_polyTime`, + `Cslib.Circuits.Frontier.ContractionSchedule.slotBits_polyTime`, + `Cslib.Circuits.Frontier.ContractionSchedule.taperStep_polyTime`, + `Cslib.Circuits.Frontier.ContractionSchedule.leafSize_polyTime`, + `Cslib.Circuits.Frontier.ContractionSchedule.degreeBound_polyTime`: the schedule. +* `Cslib.Circuits.Frontier.FastContraction.active_polyTime`, `nodePrec_polyTime`, + `packets_polyTime`, `weights_polyTime`, `points_polyTime`, `table_polyTime`: the data of a call. +* `Cslib.Circuits.Frontier.FastContraction.scheme_polyTime`: the components of the scheme, for a + polynomial-time child builder. +* `Cslib.Circuits.Frontier.FastContraction.nativeMk_polyTime`, + `Cslib.Circuits.Frontier.FastContraction.scheme_nativeMk_polyTime`: the native child builder and + the components of its scheme. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH]. +-/ + +public section + +open Turing.MultiTapeTM + +namespace Cslib.Circuits.Frontier.ContractionSchedule + +/-! ### The schedule -/ + +/-- `⌊log₂ s⌋` in unary from `s` in unary. -/ +private theorem log_polyTime : ComputableInPolyTime (Nat.log 2) encUnary encUnary := + ComputableInPolyTime.binary_log_two.comp ComputableInPolyTime.unary_to_binary + +/-- **The taper is polynomial-time**, in unary from the scale and the depth in unary. -/ +theorem taper_polyTime : ComputableInPolyTime (fun q : ℕ × ℕ => taper q.1 q.2) + (encPair encUnary encUnary) encUnary := by + have hlog := log_polyTime.comp (ComputableInPolyTime.fst encUnary encUnary) + exact ComputableInPolyTime.binary_to_unary_bounded + (ComputableInPolyTime.binary_div.comp ((ComputableInPolyTime.unary_to_binary.comp hlog).prod + (ComputableInPolyTime.binary_two_pow.comp (ComputableInPolyTime.unary_add' + (ComputableInPolyTime.const 17) (ComputableInPolyTime.snd encUnary encUnary))))) + hlog fun q => Nat.div_le_self _ _ + +/-- A sum over `Finset.range` as the sum of a list. -/ +private theorem sum_range_eq_list (f : ℕ → ℕ) (n : ℕ) : + ∑ j ∈ Finset.range n, f j = ((List.range n).map f).sum := by + induction n with + | zero => simp + | succ n ih => + rw [Finset.sum_range_succ, List.range_succ, List.map_append, List.sum_append, ih] + simp + +/-- **The number of label coordinates is polynomial-time**, in unary from the scale and the depth +in unary. -/ +theorem slotBits_polyTime : ComputableInPolyTime (fun q : ℕ × ℕ => slotBits q.1 q.2) + (encPair encUnary encUnary) encUnary := by + have hd := ComputableInPolyTime.snd encUnary encUnary + have hlog := log_polyTime.comp (ComputableInPolyTime.fst encUnary encUnary) + have hterm : ComputableInPolyTime (fun p : (ℕ × ℕ) × ℕ => taper p.1.1 p.2) + (encPair (encPair encUnary encUnary) encUnary) encNat := + ComputableInPolyTime.unary_to_binary.comp (taper_polyTime.comp + (((ComputableInPolyTime.fst encUnary encUnary).comp (ComputableInPolyTime.fst _ _)).prod + (ComputableInPolyTime.snd _ _))) + have hterms : ComputableInPolyTime (fun q : ℕ × ℕ => (List.range (q.2 + 1)).map (taper q.1)) + (encPair encUnary encUnary) (encList encNat) := + ComputableInPolyTime.mapWithInput (f := fun q j => taper q.1 j) + (ComputableInPolyTime.range.comp + (ComputableInPolyTime.unary_add' hd (ComputableInPolyTime.const 1))) hterm + have hsum := (ComputableInPolyTime.of_tidy ComputableTidilyInPolyTime.binary_sum).comp hterms + have hval (q : ℕ × ℕ) : slotBits q.1 q.2 = 2 * ((List.range (q.2 + 1)).map (taper q.1)).sum := by + simp only [slotBits, prefixBits, sum_range_eq_list] + refine (ComputableInPolyTime.binary_to_unary_bounded + (ComputableInPolyTime.binary_mul' (ComputableInPolyTime.const 2) hsum) hlog + fun q => ?_).of_enc_eq fun q => ?_ + · simp only [Function.comp_apply, ← hval] + exact (slotBits_le q.1 q.2).trans (Nat.div_le_self _ _) + · simp only [Function.comp_apply, hval] + +/-- `2 ^ a ≤ s + 1` for the number `a = slotBits s d` of label coordinates. -/ +theorem two_pow_slotBits_le (s d : ℕ) : 2 ^ slotBits s d ≤ s + 1 := by + have ha := slotBits_le s d + rcases Nat.eq_zero_or_pos s with rfl | hs + · simp only [Nat.log_zero_right, Nat.zero_div, Nat.le_zero] at ha + simp [ha] + · calc 2 ^ slotBits s d ≤ 2 ^ Nat.log 2 s := + Nat.pow_le_pow_right (by norm_num) (ha.trans (Nat.div_le_self _ _)) + _ ≤ s := Nat.pow_log_le_self 2 hs.ne' + _ ≤ s + 1 := Nat.le_succ s + +/-- **The multiplicity step is polynomial-time**, in unary from the scale, the depth and the number +of edges in unary. -/ +theorem taperStep_polyTime : + ComputableInPolyTime (fun q : ℕ × ℕ × ℕ => taperStep q.1 q.2.1 q.2.2) + (encPair encUnary (encPair encUnary encUnary)) encUnary := by + have hs := ComputableInPolyTime.fst encUnary (encPair encUnary encUnary) + have hr := ComputableInPolyTime.snd encUnary (encPair encUnary encUnary) + have hd := (ComputableInPolyTime.fst encUnary encUnary).comp hr + have hM := (ComputableInPolyTime.snd encUnary encUnary).comp hr + have hu := taper_polyTime.comp (hs.prod hd) + exact ComputableInPolyTime.unary_div.comp ((ComputableInPolyTime.unary_sub' + (ComputableInPolyTime.unary_add' (ComputableInPolyTime.unary_mul' + (ComputableInPolyTime.const 3) hM) hu) (ComputableInPolyTime.const 1)).prod hu) + +/-- **The leaf size is polynomial-time**, in unary from the scale in unary. -/ +theorem leafSize_polyTime : ComputableInPolyTime leafSize encUnary encUnary := + ComputableInPolyTime.binary_to_unary_bounded + (ComputableInPolyTime.binary_div.comp (ComputableInPolyTime.unary_to_binary.prod + (ComputableInPolyTime.binary_sqrt' (ComputableInPolyTime.unary_to_binary.comp log_polyTime)))) + (ComputableInPolyTime.id' (encIn := encUnary)) fun _ => Nat.div_le_self _ _ + +/-- **The degree bound is polynomial-time**: `2 ^ (9 ⌊log₂ s⌋ / 10 + 2)` in binary from `s` in +unary. -/ +theorem degreeBound_polyTime : ComputableInPolyTime degreeBound encUnary encNat := by + have hb := ComputableInPolyTime.unary_to_binary + have hlog := ComputableInPolyTime.binary_log_two.comp hb + have he : ComputableInPolyTime (fun m => 9 * Nat.log 2 m / 10 + 2) encUnary encUnary := + ComputableInPolyTime.binary_to_unary_bounded + (ComputableInPolyTime.binary_add.comp ((ComputableInPolyTime.binary_div.comp + ((ComputableInPolyTime.binary_mul' (ComputableInPolyTime.const 9) (hb.comp hlog)).prod + (ComputableInPolyTime.const 10))).prod (ComputableInPolyTime.const 2))) + (ComputableInPolyTime.unary_add' hlog (ComputableInPolyTime.const 2)) fun m => by + simp only [Function.comp_apply]; omega + exact ComputableInPolyTime.binary_two_pow.comp he + +end Cslib.Circuits.Frontier.ContractionSchedule + +namespace Cslib.Circuits.Frontier.FastContraction + +open GaloisRingArith ChildCursor NativeRecovery ContractionSchedule NativeChild + Cslib.GenericRAM Cslib.GenericRAM.StackRecursion + +/-! ### The parameters of a call -/ + +/-- The scale is polynomial-time. -/ +theorem scale_polyTime : ComputableInPolyTime Call.scale encCall encUnary := by + unfold Call.scale; fun_prop + +/-- The edge bound is polynomial-time. -/ +theorem bound_polyTime : ComputableInPolyTime Call.bound encCall encUnary := by + unfold Call.bound; fun_prop + +/-- The precision is polynomial-time. -/ +theorem prec_polyTime : ComputableInPolyTime Call.prec encCall encUnary := by + unfold Call.prec; fun_prop + +/-- The coefficients of the modulus are polynomial-time. -/ +theorem fl_polyTime : ComputableInPolyTime Call.fl encCall (encList encNat) := by + unfold Call.fl; fun_prop + +/-- The number of rows is polynomial-time. -/ +theorem rows_polyTime : ComputableInPolyTime Call.rows encCall encNat := by + unfold Call.rows; fun_prop + +/-- The network is polynomial-time. -/ +theorem net_polyTime : + ComputableInPolyTime Call.net encCall (DescribedNetwork.encDescribed VertexDesc.enc) := by + unfold Call.net; fun_prop + +/-- The number of edges is polynomial-time, in unary. -/ +theorem edges_polyTime : ComputableInPolyTime Call.edges encCall encUnary := + DescribedNetwork.numEdges_polyTime.comp net_polyTime + +/-- The vertices are polynomial-time. -/ +theorem vertices_polyTime : ComputableInPolyTime (fun x : Call => x.net.vertices) encCall + (encList (encPair (encList encNat) VertexDesc.enc)) := + DescribedNetwork.vertices_polyTime.comp net_polyTime + +/-! ### The schedule of a call -/ + +/-- The number of label coordinates is polynomial-time. -/ +theorem slots_polyTime : ComputableInPolyTime slots encCall encUnary := + slotBits_polyTime.comp (scale_polyTime.prod depth_polyTime) + +/-- `2 ^ a ≤ s + 1` at every call. -/ +theorem two_pow_slots_le (x : Call) : 2 ^ slots x ≤ x.scale + 1 := two_pow_slotBits_le _ _ + +/-- The number `2 ^ a` of slots is polynomial-time, in unary. -/ +theorem twoPowSlots_polyTime : ComputableInPolyTime (fun x => 2 ^ slots x) encCall encUnary := + ComputableInPolyTime.binary_to_unary_bounded + (ComputableInPolyTime.binary_two_pow.comp slots_polyTime) + (ComputableInPolyTime.unary_add' scale_polyTime (ComputableInPolyTime.const 1)) + two_pow_slots_le + +/-- The number `2 ^ (a + 1)` of grid indices is polynomial-time, in unary. -/ +theorem twoPowSlotsSucc_polyTime : + ComputableInPolyTime (fun x => 2 ^ (slots x + 1)) encCall encUnary := + (ComputableInPolyTime.unary_add' twoPowSlots_polyTime twoPowSlots_polyTime).of_enc_eq fun x => by + rw [pow_succ, mul_two] + +/-- The number of levels is polynomial-time. -/ +theorem levels_polyTime : ComputableInPolyTime levels encCall encUnary := + taper_polyTime.comp (scale_polyTime.prod depth_polyTime) + +/-- The multiplicity step is polynomial-time. -/ +theorem step_polyTime : ComputableInPolyTime step encCall encUnary := + taperStep_polyTime.comp (scale_polyTime.prod (depth_polyTime.prod edges_polyTime)) + +/-- The number of interpolation points is polynomial-time, in unary. -/ +theorem numPoints_polyTime : ComputableInPolyTime numPoints encCall encUnary := + ComputableInPolyTime.unary_add' (ComputableInPolyTime.unary_mul' edges_polyTime + (ComputableInPolyTime.unary_sub' (ComputableInPolyTime.unary_mul' + (ComputableInPolyTime.const 2) twoPowSlots_polyTime) (ComputableInPolyTime.const 1))) + (ComputableInPolyTime.const 1) + +/-- The shift `Δ'` is polynomial-time, in unary. -/ +theorem shift_polyTime : ComputableInPolyTime shift encCall encUnary := + ComputableInPolyTime.unary_add' (ComputableInPolyTime.unary_add' + (ComputableInPolyTime.unary_mul' (ComputableInPolyTime.unary_mul' + (ComputableInPolyTime.unary_mul' + (ComputableInPolyTime.unary_add' slots_polyTime (ComputableInPolyTime.const 1)) + twoPowSlotsSucc_polyTime) step_polyTime) levels_polyTime) + (ComputableInPolyTime.unary_mul' (ComputableInPolyTime.unary_mul' + (ComputableInPolyTime.const 8) step_polyTime) twoPowSlots_polyTime)) + (ComputableInPolyTime.unary_mul' (ComputableInPolyTime.unary_mul' + (ComputableInPolyTime.const 8) edges_polyTime) + (ComputableInPolyTime.unary_add' slots_polyTime (ComputableInPolyTime.const 1))) + +/-- The common precision `N = p + Δ'` is polynomial-time, in unary. -/ +theorem wide_polyTime : ComputableInPolyTime wide encCall encUnary := + ComputableInPolyTime.unary_add' prec_polyTime shift_polyTime + +/-! ### Nodes, levels and precisions -/ + +/-- The capped valuation `twoVal b d` is at most `b`. -/ +private theorem twoVal_le (b d : ℕ) : twoVal b d ≤ b := by + unfold twoVal + split_ifs + · exact Nat.zero_le _ + · exact (List.length_filter_le _ _).trans (List.length_range (n := b)).le + +/-- Every level is at most `a + 1`. -/ +theorem level_le (x : Call) (k : ℕ) : level x k ≤ slots x + 1 := twoVal_le _ _ + +/-- The level of a grid index is polynomial-time, in unary. -/ +theorem level_polyTime : + ComputableInPolyTime (fun q : Call × ℕ => level q.1 q.2) (encPair encCall encUnary) + encUnary := by + have hx := ComputableInPolyTime.fst encCall encUnary + have hk := ComputableInPolyTime.snd encCall encUnary + have hL := twoPowSlots_polyTime.comp hx + have h := computableInPolyTime_twoVal + (ComputableInPolyTime.unary_add' (slots_polyTime.comp hx) (ComputableInPolyTime.const 1)) + (ComputableInPolyTime.unary_add' (ComputableInPolyTime.unary_sub' hk hL) + (ComputableInPolyTime.unary_sub' hL hk)) + exact h + +/-- The nodes of positive multiplicity are polynomial-time, as unary grid indices. -/ +theorem nodes_polyTime : ComputableInPolyTime nodes encCall (encList encUnary) := by + have hx := ComputableInPolyTime.fst encCall encUnary + have hk := ComputableInPolyTime.snd encCall encUnary + have hL := twoPowSlots_polyTime.comp hx + have hmult := computableInPolyTime_mult (slots_polyTime.comp hx) hL (levels_polyTime.comp hx) + (step_polyTime.comp hx) hk + have htest : ComputableInPolyTime (fun q : Call × ℕ => + decide (q.2 ≠ 2 ^ slots q.1 ∧ 0 < mult (slots q.1) (levels q.1) (step q.1) q.2)) + (encPair encCall encUnary) encBool := + (ComputableInPolyTime.bool_and' (ComputableInPolyTime.bool_not.comp + (ComputableInPolyTime.decide_eq' hk hL)) + (ComputableInPolyTime.unary_lt' (ComputableInPolyTime.const 0) hmult)).of_enc_eq fun q => by + simp only [Function.comp_apply, Bool.decide_and, decide_not] + have h := ComputableInPolyTime.filterWithInput + (test := fun (x : Call) k => + decide (k ≠ 2 ^ slots x ∧ 0 < mult (slots x) (levels x) (step x) k)) + (ComputableInPolyTime.range.comp twoPowSlotsSucc_polyTime) htest + exact h + +/-- The grid index of the `i`-th node is polynomial-time, in unary. -/ +theorem node_polyTime : + ComputableInPolyTime (fun q : Call × ℕ => (nodes q.1).getD q.2 0) (encPair encCall encUnary) + encUnary := by + have h := ComputableInPolyTime.listGetDWithInput + (nodes_polyTime.comp (ComputableInPolyTime.fst encCall encUnary)) + (ComputableInPolyTime.snd encCall encUnary) 0 + exact h + +/-- The level of the `i`-th node is polynomial-time, in unary. -/ +theorem nodeLevel_polyTime : + ComputableInPolyTime (fun q : Call × ℕ => level q.1 ((nodes q.1).getD q.2 0)) + (encPair encCall encUnary) encUnary := by + have h := level_polyTime.comp ((ComputableInPolyTime.fst encCall encUnary).prod node_polyTime) + exact h + +/-- The number `2 ^ (v₂ t + 1)` of tiles of the `i`-th node is polynomial-time, in unary. -/ +theorem tiles_polyTime : + ComputableInPolyTime (fun q : Call × ℕ => 2 ^ (level q.1 ((nodes q.1).getD q.2 0) + 1)) + (encPair encCall encUnary) encUnary := by + have hv := ComputableInPolyTime.binary_two_pow.comp + (ComputableInPolyTime.unary_add' nodeLevel_polyTime (ComputableInPolyTime.const 1)) + have hb := ComputableInPolyTime.unary_mul' (ComputableInPolyTime.const 4) + (twoPowSlots_polyTime.comp (ComputableInPolyTime.fst encCall encUnary)) + have h := ComputableInPolyTime.binary_to_unary_bounded hv hb fun q => by + simp only [Function.comp_apply] + calc 2 ^ (level q.1 ((nodes q.1).getD q.2 0) + 1) ≤ 2 ^ (slots q.1 + 2) := + Nat.pow_le_pow_right (by norm_num) + (by have := level_le q.1 ((nodes q.1).getD q.2 0); omega) + _ = 4 * 2 ^ slots q.1 := by rw [pow_add]; ring + exact h + +/-- **The precision of the `i`-th node is polynomial-time**, in unary. -/ +theorem nodePrec_polyTime : + ComputableInPolyTime (fun q : Call × ℕ => nodePrec q.1 q.2) (encPair encCall encUnary) + encUnary := by + have hx := ComputableInPolyTime.fst encCall encUnary + have hpow : ComputableInPolyTime + (fun q : Call × ℕ => 2 ^ (slots q.1 - level q.1 ((nodes q.1).getD q.2 0))) + (encPair encCall encUnary) encUnary := by + have h := ComputableInPolyTime.binary_to_unary_bounded (ComputableInPolyTime.binary_two_pow.comp + (ComputableInPolyTime.unary_sub' (slots_polyTime.comp hx) nodeLevel_polyTime)) + (twoPowSlots_polyTime.comp hx) + fun q => Nat.pow_le_pow_right (by norm_num) (Nat.sub_le _ _) + exact h + have h := ComputableInPolyTime.unary_add' (ComputableInPolyTime.unary_add' (prec_polyTime.comp hx) + (ComputableInPolyTime.unary_mul' (ComputableInPolyTime.unary_mul' + (ComputableInPolyTime.const 8) (step_polyTime.comp hx)) hpow)) + (ComputableInPolyTime.unary_mul' (ComputableInPolyTime.unary_mul' + (ComputableInPolyTime.const 8) (edges_polyTime.comp hx)) + (ComputableInPolyTime.unary_add' (slots_polyTime.comp hx) (ComputableInPolyTime.const 1))) + exact h + +/-! ### The packing and the weights -/ + +/-- The capped number of rows is polynomial-time, in unary. -/ +theorem rowCap_polyTime : ComputableInPolyTime rowCap encCall encUnary := + (ComputableInPolyTime.binary_min_unary.comp + (((ComputableInPolyTime.length_list _).comp vertices_polyTime).prod rows_polyTime)).of_enc_eq + fun x => by simp only [Function.comp_apply, rowCap, min_comm] + +/-- **The packing is polynomial-time** on every call. -/ +theorem packing_polyTime : ComputableInPolyTime packing encCall PacketPacking.encNativePacking := + computableInPolyTime_nativeParentPacking rowCap_polyTime slots_polyTime net_polyTime + +/-- **The number of packets is polynomial-time**, in unary, on every call: there are at most +`M (2 ^ a) ^ 12 ≤ M (s + 1) ^ 12` of them (`NativeChild.numPackets_nativeParentPacking_le`). -/ +theorem packets_polyTime : + ComputableInPolyTime (fun x : Call => (packing x).2.1) encCall encUnary := + computableInPolyTime_numPackets_nativeParentPacking rowCap_polyTime slots_polyTime + twoPowSlots_polyTime net_polyTime + +/-- **The weights of the modes of the `i`-th node are polynomial-time.** -/ +theorem weights_polyTime : + ComputableInPolyTime (fun q : Call × ℕ => weights q.1 q.2) (encPair encCall encUnary) + (encList encNat) := by + have hx := ComputableInPolyTime.fst encCall encUnary + have hp := nodePrec_polyTime + have hlen := ComputableInPolyTime.unary_mul' (packets_polyTime.comp hx) + (ComputableInPolyTime.unary_mul' tiles_polyTime hp) + have hj := ComputableInPolyTime.snd (encPair encCall encUnary) encUnary + have hpj := hp.comp (ComputableInPolyTime.fst (encPair encCall encUnary) encUnary) + have hf : ComputableInPolyTime (fun r : (Call × ℕ) × ℕ => r.2 % nodePrec r.1.1 r.1.2 + 1) + (encPair (encPair encCall encUnary) encUnary) encNat := by + have h := ComputableInPolyTime.unary_to_binary.comp (ComputableInPolyTime.unary_add' + (ComputableInPolyTime.unary_mod.comp (hj.prod hpj)) (ComputableInPolyTime.const 1)) + exact h + have h := ComputableInPolyTime.mapWithInput + (f := fun (q : Call × ℕ) j => j % nodePrec q.1 q.2 + 1) + (ComputableInPolyTime.range.comp hlen) hf + exact h + +/-- The number of modes of the `i`-th node is polynomial-time, in unary. -/ +theorem weightsLength_polyTime : + ComputableInPolyTime (fun q : Call × ℕ => (weights q.1 q.2).length) (encPair encCall encUnary) + encUnary := by + have h := (ComputableInPolyTime.length_list _).comp weights_polyTime + exact h + +/-! ### Points and the table -/ + +/-- The binary digits are polynomial-time, from the length and the number in unary. -/ +theorem digits_polyTime : + ComputableInPolyTime (fun q : ℕ × ℕ => digits q.1 q.2) (encPair encUnary encUnary) + (encList encNat) := by + have hk := (ComputableInPolyTime.snd encUnary encUnary).comp + (ComputableInPolyTime.fst (encPair encUnary encUnary) encUnary) + have hf : ComputableInPolyTime (fun r : (ℕ × ℕ) × ℕ => if r.1.2.testBit r.2 then 1 else 0) + (encPair (encPair encUnary encUnary) encUnary) encNat := by + have h := ComputableInPolyTime.ite (encMid := encNat) (ComputableInPolyTime.binary_testBit + (ComputableInPolyTime.unary_to_binary.comp hk) (ComputableInPolyTime.snd _ _)) + (ComputableInPolyTime.const 1) (ComputableInPolyTime.const 0) + exact h + have h := ComputableInPolyTime.mapWithInput + (f := fun (q : ℕ × ℕ) i => if q.2.testBit i then 1 else 0) + (ComputableInPolyTime.range.comp (ComputableInPolyTime.fst encUnary encUnary)) hf + exact h + +/-- **The interpolation points are polynomial-time.** -/ +theorem points_polyTime : ComputableInPolyTime points encCall (encList (encList encNat)) := by + have hf : ComputableInPolyTime (fun r : Call × ℕ => digits r.1.fl.length r.2) + (encPair encCall encUnary) (encList encNat) := by + have h := digits_polyTime.comp ((((ComputableInPolyTime.length_list _).comp fl_polyTime).comp + (ComputableInPolyTime.fst encCall encUnary)).prod (ComputableInPolyTime.snd _ _)) + exact h + have h := ComputableInPolyTime.mapWithInput (f := fun (x : Call) k => digits x.fl.length k) + (ComputableInPolyTime.range.comp numPoints_polyTime) hf + exact h + +/-- **The table of native recovery is polynomial-time**, from a call and the stored values. -/ +theorem table_polyTime : + ComputableInPolyTime (fun q : Call × List (List ℕ) => table q.1 q.2) + (encPair encCall (encList (encList encNat))) (encList (encList (encList encNat))) := by + let e := encPair encCall (encList (encList encNat)) + have hx := ComputableInPolyTime.fst encCall (encList (encList encNat)) + have hx' := hx.comp (ComputableInPolyTime.fst e encUnary) + have hV' := (ComputableInPolyTime.snd encCall (encList (encList encNat))).comp + (ComputableInPolyTime.fst e encUnary) + have hk := ComputableInPolyTime.snd e encUnary + have hn := numPoints_polyTime.comp hx' + have hmem := ComputableInPolyTime.decide_mem (nodes_polyTime.comp hx') hk + have hidx := ComputableInPolyTime.idxOfWithInput (nodes_polyTime.comp hx') hk + have hdrop : ComputableInPolyTime (fun r : (Call × List (List ℕ)) × ℕ => + r.1.2.drop ((nodes r.1.1).idxOf r.2 * numPoints r.1.1)) (encPair e encUnary) + (encList (encList encNat)) := + .of_tidy (ComputableTidilyInPolyTime.listDropWithInput hV'.tidy + (ComputableInPolyTime.unary_mul' hidx hn).tidy) + have htake : ComputableInPolyTime (fun r : (Call × List (List ℕ)) × ℕ => + (r.1.2.drop ((nodes r.1.1).idxOf r.2 * numPoints r.1.1)).take (numPoints r.1.1)) + (encPair e encUnary) (encList (encList encNat)) := + .of_tidy (ComputableTidilyInPolyTime.listTakeWithInput hdrop.tidy hn.tidy) + have hf : ComputableInPolyTime (fun r : (Call × List (List ℕ)) × ℕ => + if r.2 ∈ nodes r.1.1 then + (r.1.2.drop ((nodes r.1.1).idxOf r.2 * numPoints r.1.1)).take (numPoints r.1.1) + else []) (encPair e encUnary) (encList (encList encNat)) := by + have h := ComputableInPolyTime.ite hmem htake (ComputableInPolyTime.const []) + refine h.of_enc_eq fun r => ?_ + simp only [Function.comp_apply, decide_eq_true_eq] + have h := ComputableInPolyTime.mapWithInput (f := fun (q : Call × List (List ℕ)) k => + if k ∈ nodes q.1 then + (q.2.drop ((nodes q.1).idxOf k * numPoints q.1)).take (numPoints q.1) else []) + (ComputableInPolyTime.range.comp (twoPowSlotsSucc_polyTime.comp hx)) hf + exact h + +/-! ### Active calls -/ + +/-- Whether a call is active, as a conjunction of tests. -/ +theorem active_eq (x : Call) : active x = + (decide (leafSize x.scale < x.edges) && decide (1 ≤ levels x) && decide (levels x ≤ x.edges) && + decide (levels x ≤ slots x) && + x.net.vertices.all (fun v => decide (v.1.length ≤ degreeBound x.scale)) && + decide (numPoints x ≤ 2 ^ x.fl.length) && decide (x.edges < x.bound)) := by + simp only [active, Bool.decide_and, List.all_eq, decide_eq_true_eq, Bool.and_assoc] + +/-- **Whether a call is active is polynomial-time.** -/ +theorem active_polyTime : ComputableInPolyTime active encCall encBool := by + have hdeg : ComputableInPolyTime (fun x : Call => + x.net.vertices.all (fun v => decide (v.1.length ≤ degreeBound x.scale))) encCall encBool := + ComputableInPolyTime.allWithInput vertices_polyTime + (ComputableInPolyTime.binary_le' (ComputableInPolyTime.unary_to_binary.comp + ((ComputableInPolyTime.length_list _).comp ((ComputableInPolyTime.fst _ _).comp + (ComputableInPolyTime.snd _ _)))) + (degreeBound_polyTime.comp (scale_polyTime.comp (ComputableInPolyTime.fst _ _)))) + have h := ComputableInPolyTime.bool_and' (ComputableInPolyTime.bool_and' + (ComputableInPolyTime.bool_and' (ComputableInPolyTime.bool_and' + (ComputableInPolyTime.bool_and' (ComputableInPolyTime.bool_and' + (ComputableInPolyTime.unary_lt' (leafSize_polyTime.comp scale_polyTime) edges_polyTime) + (ComputableInPolyTime.unary_le' (ComputableInPolyTime.const 1) levels_polyTime)) + (ComputableInPolyTime.unary_le' levels_polyTime edges_polyTime)) + (ComputableInPolyTime.unary_le' levels_polyTime slots_polyTime)) hdeg) + (ComputableInPolyTime.binary_le' + (ComputableInPolyTime.unary_to_binary.comp numPoints_polyTime) + (ComputableInPolyTime.binary_two_pow.comp + ((ComputableInPolyTime.length_list _).comp fl_polyTime)))) + (ComputableInPolyTime.unary_lt' edges_polyTime bound_polyTime) + refine h.of_enc_eq fun x => ?_ + rw [active_eq] + rfl + +/-! ### The components of the scheme -/ + +section Scheme + +variable (mk : Call → State → ℕ × DescribedNetwork VertexDesc) + +/-- **The leaf test is polynomial-time.** -/ +theorem isLeaf_polyTime : ComputableInPolyTime (scheme mk).isLeaf encCall encBool := by + have h := ComputableInPolyTime.bool_not.comp active_polyTime + exact h + +/-- **The initial accumulator is polynomial-time.** -/ +theorem start_polyTime : ComputableInPolyTime (scheme mk).start encCall encFrame := by + have hlen : ComputableInPolyTime (fun x : Call => (weights x 0).length) encCall encUnary := by + have h := weightsLength_polyTime.comp + ((ComputableInPolyTime.id' (encIn := encCall)).prod (ComputableInPolyTime.const 0)) + exact h + have hst : ComputableInPolyTime (fun x : Call => cursorStart (weights x)) encCall + (encPair encUnary (encPair encUnary (encPair (encList encNat) encUnary))) := + (ComputableInPolyTime.const 0).prod ((ComputableInPolyTime.const 0).prod + ((ComputableInPolyTime.replicate hlen (ComputableInPolyTime.const 0)).prod + (ComputableInPolyTime.const 0))) + have hst' : ComputableInPolyTime (fun x : Call => cursorStart (weights x)) encCall + ChildCursor.encState := hst + have h : ComputableInPolyTime (scheme mk).start encCall + (encPair (encOption ChildCursor.encState) + (encPair (encList encNat) (encPair (encList encNat) (encList (encList encNat))))) := + ((ComputableInPolyTime.optionSome _).comp hst').prod + ((computableInPolyTime_one fl_polyTime).prod ((computableInPolyTime_zero fl_polyTime).prod + (ComputableInPolyTime.const []))) + exact h + +/-- The node index of a cursor state is polynomial-time. -/ +private theorem stateNode_polyTime : + ComputableInPolyTime (fun c : State => c.1) ChildCursor.encState encUnary := by + unfold ChildCursor.encState; fun_prop + +/-- The point index of a cursor state is polynomial-time. -/ +private theorem statePoint_polyTime : + ComputableInPolyTime (fun c : State => c.2.1) ChildCursor.encState encUnary := by + unfold ChildCursor.encState; fun_prop + +/-- The exponents of a cursor state are polynomial-time, in binary. -/ +private theorem stateExps_polyTime : + ComputableInPolyTime (fun c : State => c.2.2.1) ChildCursor.encState (encList encNat) := by + unfold ChildCursor.encState; fun_prop + +/-- The group of a cursor state is polynomial-time. -/ +private theorem stateGroup_polyTime : + ComputableInPolyTime (fun c : State => c.2.2.2) ChildCursor.encState encUnary := by + unfold ChildCursor.encState; fun_prop + +variable {mk} + +/-- **The child call at a cursor state is polynomial-time**, given a polynomial-time builder. -/ +theorem child_polyTime + (hmk : ComputableInPolyTime (fun q : Call × State => mk q.1 q.2) + (encPair encCall ChildCursor.encState) + (encPair encNat (DescribedNetwork.encDescribed VertexDesc.enc))) : + ComputableInPolyTime (fun q : Call × State => child q.1 mk q.2) + (encPair encCall ChildCursor.encState) encCall := by + have hx := ComputableInPolyTime.fst encCall ChildCursor.encState + have ht := stateNode_polyTime.comp (ComputableInPolyTime.snd encCall ChildCursor.encState) + have hprec : ComputableInPolyTime (fun q : Call × State => nodePrec q.1 q.2.1) + (encPair encCall ChildCursor.encState) encUnary := by + have h := nodePrec_polyTime.comp (hx.prod ht) + exact h + have h : ComputableInPolyTime (fun q : Call × State => child q.1 mk q.2) + (encPair encCall ChildCursor.encState) + (encPair (encPair encUnary (encPair encUnary (encPair encUnary (encPair encUnary + (encPair (encList encNat) encNat))))) (DescribedNetwork.encDescribed VertexDesc.enc)) := + ((scale_polyTime.comp hx).prod ((ComputableInPolyTime.unary_add' (depth_polyTime.comp hx) + (ComputableInPolyTime.const 1)).prod ((edges_polyTime.comp hx).prod (hprec.prod + ((fl_polyTime.comp hx).prod ((ComputableInPolyTime.fst _ _).comp hmk)))))).prod + ((ComputableInPolyTime.snd _ _).comp hmk) + exact h + +/-- **Child generation is polynomial-time**, given a polynomial-time builder. -/ +theorem next_polyTime + (hmk : ComputableInPolyTime (fun q : Call × State => mk q.1 q.2) + (encPair encCall ChildCursor.encState) + (encPair encNat (DescribedNetwork.encDescribed VertexDesc.enc))) : + ComputableInPolyTime (fun p : Call × Frame => (scheme mk).next p.1 p.2) + (encPair encCall encFrame) (encOption encCall) := by + have hc : ComputableInPolyTime (fun p : Call × Frame => p.2.1) (encPair encCall encFrame) + (encOption ChildCursor.encState) := + (ComputableInPolyTime.fst _ _).comp (ComputableInPolyTime.snd encCall encFrame) + have hbody : ComputableInPolyTime (fun r : (Call × Frame) × State => some (child r.1.1 mk r.2)) + (encPair (encPair encCall encFrame) ChildCursor.encState) (encOption encCall) := by + have h := (ComputableInPolyTime.optionSome encCall).comp ((child_polyTime hmk).comp + (((ComputableInPolyTime.fst encCall encFrame).comp (ComputableInPolyTime.fst _ _)).prod + (ComputableInPolyTime.snd _ _))) + exact h + have h := ComputableInPolyTime.optionElimWithInput + (body := fun (p : Call × Frame) (c : State) => some (child p.1 mk c)) hc + (ComputableInPolyTime.const (encOut := encOption encCall) none) hbody + refine h.of_enc_eq fun p => ?_ + rcases p with ⟨x, _ | c, acc⟩ <;> rfl + +variable (mk) + +/-- The inputs of `absorb` at a cursor state: the call, its frame, the child's value and the +state. -/ +private abbrev AbsorbIn : Type := ((Call × Frame) × (ℕ × List ℕ)) × State + +/-- The encoding of `AbsorbIn`. -/ +private abbrev encAbsorbIn : AbsorbIn ↪ List Bool := + encPair (encPair (encPair encCall encFrame) encValue) ChildCursor.encState + +/-- The call of an input of `absorb`. -/ +private theorem absorbCall_polyTime : + ComputableInPolyTime (fun r : AbsorbIn => r.1.1.1) encAbsorbIn encCall := + (ComputableInPolyTime.fst encCall encFrame).comp ((ComputableInPolyTime.fst _ encValue).comp + (ComputableInPolyTime.fst _ ChildCursor.encState)) + +/-- The state of an input of `absorb`. -/ +private theorem absorbState_polyTime : + ComputableInPolyTime (fun r : AbsorbIn => r.2) encAbsorbIn ChildCursor.encState := + ComputableInPolyTime.snd _ _ + +/-- **The cursor step of `absorb` is polynomial-time.** -/ +private theorem absorbCursor_polyTime : + ComputableInPolyTime (fun r : AbsorbIn => cursorNext (weights r.1.1.1) (nodePrec r.1.1.1) + (nodes r.1.1.1).length (numPoints r.1.1.1) 64 r.2) encAbsorbIn (encOption encState) := by + have hx := absorbCall_polyTime + have hc := absorbState_polyTime + have ht := stateNode_polyTime.comp hc + have hT := (ComputableInPolyTime.length_list _).comp (nodes_polyTime.comp hx) + have hn := numPoints_polyTime.comp hx + have hwt : ComputableInPolyTime (fun r : AbsorbIn => weights r.1.1.1 r.2.1) encAbsorbIn + (encList encNat) := by + have h := weights_polyTime.comp (hx.prod ht) + exact h + have hpt : ComputableInPolyTime (fun r : AbsorbIn => nodePrec r.1.1.1 r.2.1) encAbsorbIn + encNat := by + have h := ComputableInPolyTime.unary_to_binary.comp (nodePrec_polyTime.comp (hx.prod ht)) + exact h + have hnext : ComputableInPolyTime (fun r : AbsorbIn => (weights r.1.1.1 (r.2.1 + 1)).length) + encAbsorbIn encUnary := by + have h := weightsLength_polyTime.comp + (hx.prod (ComputableInPolyTime.unary_add' ht (ComputableInPolyTime.const 1))) + exact h + have hin : ComputableInPolyTime (fun r : AbsorbIn => + (((((nodes r.1.1.1).length, numPoints r.1.1.1, (64 : ℕ)), (weights r.1.1.1 r.2.1, + nodePrec r.1.1.1 r.2.1, (weights r.1.1.1 (r.2.1 + 1)).length)), r.2) : StepInput)) + encAbsorbIn (encPair (encPair (encPair encUnary (encPair encUnary encUnary)) + (encPair (encList encNat) (encPair encNat encUnary))) ChildCursor.encState) := + ((hT.prod (hn.prod (ComputableInPolyTime.const 64))).prod (hwt.prod (hpt.prod hnext))).prod hc + have hin' : ComputableInPolyTime (fun r : AbsorbIn => + (((((nodes r.1.1.1).length, numPoints r.1.1.1, (64 : ℕ)), (weights r.1.1.1 r.2.1, + nodePrec r.1.1.1 r.2.1, (weights r.1.1.1 (r.2.1 + 1)).length)), r.2) : StepInput)) + encAbsorbIn encStepInput := hin + refine (computableInPolyTime_cursorStep.comp hin').of_enc_eq fun r => ?_ + rw [cursorNext_eq_cursorStep] + rfl + +/-- **The accumulator step of `absorb` is polynomial-time.** -/ +private theorem absorbAcc_polyTime : + ComputableInPolyTime (fun r : AbsorbIn => absorbAt (weights r.1.1.1) (nodePrec r.1.1.1) 64 + (mul (wide r.1.1.1) r.1.1.1.fl) (add (wide r.1.1.1) r.1.1.1.fl) (one r.1.1.1.fl) + (zero r.1.1.1.fl) r.2 r.1.1.2.2 r.1.2.2) encAbsorbIn + (encPair (encList encNat) (encPair (encList encNat) (encList (encList encNat)))) := by + have hx := absorbCall_polyTime + have hc := absorbState_polyTime + have ht := stateNode_polyTime.comp hc + have hN := wide_polyTime.comp hx + have hfl := fl_polyTime.comp hx + have hacc : ComputableInPolyTime (fun r : AbsorbIn => r.1.1.2.2) encAbsorbIn + (encPair (encList encNat) (encPair (encList encNat) (encList (encList encNat)))) := + (ComputableInPolyTime.snd _ _).comp ((ComputableInPolyTime.snd encCall encFrame).comp + ((ComputableInPolyTime.fst _ encValue).comp + (ComputableInPolyTime.fst _ ChildCursor.encState))) + have hprod := (ComputableInPolyTime.fst _ _).comp hacc + have hsum := (ComputableInPolyTime.fst _ _).comp ((ComputableInPolyTime.snd _ _).comp hacc) + have hstored := (ComputableInPolyTime.snd _ _).comp ((ComputableInPolyTime.snd _ _).comp hacc) + have hy : ComputableInPolyTime (fun r : AbsorbIn => r.1.2.2) encAbsorbIn (encList encNat) := + (ComputableInPolyTime.snd encUnary (encList encNat)).comp ((ComputableInPolyTime.snd _ _).comp + (ComputableInPolyTime.fst _ ChildCursor.encState)) + have hpy := computableInPolyTime_mul hN hfl hprod hy + have hnew := computableInPolyTime_add hN hfl hsum hpy + have hone := computableInPolyTime_one hfl + have hzero := computableInPolyTime_zero hfl + have hlast : ComputableInPolyTime (fun r : AbsorbIn => + (BudgetTuples.budgetNext (weights r.1.1.1 r.2.1) (nodePrec r.1.1.1 r.2.1) r.2.2.2.1).isNone) + encAbsorbIn encBool := by + have hwt : ComputableInPolyTime (fun r : AbsorbIn => weights r.1.1.1 r.2.1) encAbsorbIn + (encList encNat) := by + have h := weights_polyTime.comp (hx.prod ht) + exact h + have hpt : ComputableInPolyTime (fun r : AbsorbIn => nodePrec r.1.1.1 r.2.1) encAbsorbIn + encNat := by + have h := ComputableInPolyTime.unary_to_binary.comp (nodePrec_polyTime.comp (hx.prod ht)) + exact h + have hin : ComputableInPolyTime (fun r : AbsorbIn => + (weights r.1.1.1 r.2.1, nodePrec r.1.1.1 r.2.1, r.2.2.2.1)) encAbsorbIn + (encPair (encList encNat) (encPair encNat (encList encNat))) := + hwt.prod (hpt.prod (stateExps_polyTime.comp hc)) + have hin' : ComputableInPolyTime (fun r : AbsorbIn => + (weights r.1.1.1 r.2.1, nodePrec r.1.1.1 r.2.1, r.2.2.2.1)) encAbsorbIn + BudgetTuples.encBudgetInput := hin + have h := ComputableInPolyTime.bool_not.comp (ComputableInPolyTime.optionIsSome + (BudgetTuples.computableInPolyTime_budgetNext.comp hin')) + refine h.of_enc_eq fun r => ?_ + simp only [Function.comp_apply] + cases BudgetTuples.budgetNext (weights r.1.1.1 r.2.1) (nodePrec r.1.1.1 r.2.1) r.2.2.2.1 <;> rfl + have hg : ComputableInPolyTime (fun r : AbsorbIn => decide (r.2.2.2.2 + 1 < 64)) encAbsorbIn + encBool := + ComputableInPolyTime.unary_lt' (ComputableInPolyTime.unary_add' (stateGroup_polyTime.comp hc) + (ComputableInPolyTime.const 1)) (ComputableInPolyTime.const 64) + have h := ComputableInPolyTime.ite hg (hpy.prod (hsum.prod hstored)) + (ComputableInPolyTime.ite hlast (hone.prod (hzero.prod (ComputableInPolyTime.listAppend' hstored + (ComputableInPolyTime.cons' hnew (ComputableInPolyTime.const []))))) + (hone.prod (hnew.prod hstored))) + refine h.of_enc_eq fun r => ?_ + simp only [absorbAt, decide_eq_true_eq, Function.comp_apply] + +/-- **Absorbing a child's value is polynomial-time.** -/ +theorem absorb_polyTime : + ComputableInPolyTime + (fun p : (Call × Frame) × (ℕ × List ℕ) => (scheme mk).absorb p.1.1 p.1.2 p.2) + (encPair (encPair encCall encFrame) encValue) encFrame := by + have hc : ComputableInPolyTime (fun p : (Call × Frame) × (ℕ × List ℕ) => p.1.2.1) + (encPair (encPair encCall encFrame) encValue) (encOption ChildCursor.encState) := + (ComputableInPolyTime.fst _ _).comp ((ComputableInPolyTime.snd encCall encFrame).comp + (ComputableInPolyTime.fst _ encValue)) + have hfallback : ComputableInPolyTime (fun p : (Call × Frame) × (ℕ × List ℕ) => p.1.2) + (encPair (encPair encCall encFrame) encValue) encFrame := + (ComputableInPolyTime.snd encCall encFrame).comp (ComputableInPolyTime.fst _ encValue) + have hbody : ComputableInPolyTime (fun r : AbsorbIn => + ((cursorNext (weights r.1.1.1) (nodePrec r.1.1.1) (nodes r.1.1.1).length (numPoints r.1.1.1) + 64 r.2, absorbAt (weights r.1.1.1) (nodePrec r.1.1.1) 64 + (mul (wide r.1.1.1) r.1.1.1.fl) (add (wide r.1.1.1) r.1.1.1.fl) (one r.1.1.1.fl) + (zero r.1.1.1.fl) r.2 r.1.1.2.2 r.1.2.2) : Frame)) encAbsorbIn encFrame := + absorbCursor_polyTime.prod (absorbAcc_polyTime) + have h := ComputableInPolyTime.optionElimWithInput + (body := fun (p : (Call × Frame) × (ℕ × List ℕ)) (c : State) => + ((cursorNext (weights p.1.1) (nodePrec p.1.1) (nodes p.1.1).length (numPoints p.1.1) 64 c, + absorbAt (weights p.1.1) (nodePrec p.1.1) 64 (mul (wide p.1.1) p.1.1.fl) + (add (wide p.1.1) p.1.1.fl) (one p.1.1.fl) (zero p.1.1.fl) c p.1.2.2 p.2.2) : Frame)) + hc hfallback hbody + refine h.of_enc_eq fun p => ?_ + rcases p with ⟨⟨x, _ | c, acc⟩, y⟩ <;> rfl + +/-- **The value of a call is polynomial-time.** -/ +theorem finish_polyTime : + ComputableInPolyTime (fun p : Call × Frame => (scheme mk).finish p.1 p.2) + (encPair encCall encFrame) encValue := by + have hx := ComputableInPolyTime.fst encCall encFrame + have hV : ComputableInPolyTime (fun p : Call × Frame => p.2.2.2.2) (encPair encCall encFrame) + (encList (encList encNat)) := + (ComputableInPolyTime.snd _ _).comp ((ComputableInPolyTime.snd _ _).comp + ((ComputableInPolyTime.snd _ _).comp (ComputableInPolyTime.snd encCall encFrame))) + have htab : ComputableInPolyTime (fun p : Call × Frame => table p.1 p.2.2.2.2) + (encPair encCall encFrame) (encList (encList (encList encNat))) := by + have h := table_polyTime.comp (hx.prod hV) + exact h + have hrec := computableInPolyTime_recover (prec_polyTime.comp hx) (shift_polyTime.comp hx) + (fl_polyTime.comp hx) (slots_polyTime.comp hx) (twoPowSlots_polyTime.comp hx) + (levels_polyTime.comp hx) (step_polyTime.comp hx) (points_polyTime.comp hx) htab + have h := (depth_polyTime.comp hx).prod hrec + exact h + +variable {mk} + +/-- **The components of the scheme are polynomial-time**, for a polynomial-time child builder. -/ +theorem scheme_polyTime + (hmk : ComputableInPolyTime (fun q : Call × State => mk q.1 q.2) + (encPair encCall ChildCursor.encState) + (encPair encNat (DescribedNetwork.encDescribed VertexDesc.enc))) : + (scheme mk).PolyTime encCall encFrame encValue := + ⟨isLeaf_polyTime mk, start_polyTime mk, next_polyTime hmk, absorb_polyTime mk, + finish_polyTime mk⟩ + +end Scheme + +/-! ### The native child builder -/ + +/-- **The native child builder is polynomial-time** on every call and cursor state +(`NativeChild.computableInPolyTime_nativeChild_nativeParentPacking`). The rows are capped at the +number of vertices and the exponents at the node's precision, so both are polynomial in unary; the +number `2 ^ a` of slots is at most `s + 1` and the number `2 ^ (v₂ t + 1)` of tiles at most +`4 (s + 1)`. -/ +theorem nativeMk_polyTime : + ComputableInPolyTime (fun q : Call × State => nativeMk q.1 q.2) + (encPair encCall ChildCursor.encState) + (encPair encNat (DescribedNetwork.encDescribed VertexDesc.enc)) := by + have hx := ComputableInPolyTime.fst encCall ChildCursor.encState + have hc := ComputableInPolyTime.snd encCall ChildCursor.encState + have ht := stateNode_polyTime.comp hc + have hj := statePoint_polyTime.comp hc + have hβ := stateExps_polyTime.comp hc + have hg := ComputableInPolyTime.unary_to_binary.comp (stateGroup_polyTime.comp hc) + have hr := rowCap_polyTime.comp hx + have ha := slots_polyTime.comp hx + have hL := twoPowSlots_polyTime.comp hx + have hfl := fl_polyTime.comp hx + have hD := net_polyTime.comp hx + have hi : ComputableInPolyTime (fun q : Call × State => level q.1 ((nodes q.1).getD q.2.1 0)) + (encPair encCall ChildCursor.encState) encUnary := by + have h := nodeLevel_polyTime.comp (hx.prod ht) + exact h + have hT : ComputableInPolyTime + (fun q : Call × State => 2 ^ (level q.1 ((nodes q.1).getD q.2.1 0) + 1)) + (encPair encCall ChildCursor.encState) encUnary := by + have h := tiles_polyTime.comp (hx.prod ht) + exact h + have hpt : ComputableInPolyTime (fun q : Call × State => nodePrec q.1 q.2.1) + (encPair encCall ChildCursor.encState) encUnary := by + have h := nodePrec_polyTime.comp (hx.prod ht) + exact h + have hnode : ComputableInPolyTime + (fun q : Call × State => node (slots q.1) ((nodes q.1).getD q.2.1 0)) + (encPair encCall ChildCursor.encState) encIntBin := by + have h := ComputableInPolyTime.intBin_subNatNat.comp + ((ComputableInPolyTime.unary_to_binary.comp (node_polyTime.comp (hx.prod ht))).prod + (ComputableInPolyTime.unary_to_binary.comp hL)) + refine h.of_enc_eq fun q => ?_ + simp only [Function.comp_apply, node, Int.subNatNat_eq_coe, Nat.cast_pow, Nat.cast_ofNat] + have hζ : ComputableInPolyTime (fun q : Call × State => (points q.1).getD q.2.2.1 []) + (encPair encCall ChildCursor.encState) (encList encNat) := by + have h := ComputableInPolyTime.listGetDWithInput (points_polyTime.comp hx) hj [] + exact h + have hcap : ComputableInPolyTime + (fun q : Call × State => q.2.2.2.1.map (min · (nodePrec q.1 q.2.1))) + (encPair encCall ChildCursor.encState) (encList encUnary) := by + have hf : ComputableInPolyTime (fun r : (Call × State) × ℕ => min r.2 (nodePrec r.1.1 r.1.2.1)) + (encPair (encPair encCall ChildCursor.encState) encNat) encUnary := by + have h := ComputableInPolyTime.binary_min_unary.comp + ((hpt.comp (ComputableInPolyTime.fst _ _)).prod (ComputableInPolyTime.snd _ _)) + refine h.of_enc_eq fun r => ?_ + simp only [Function.comp_apply, min_comm] + have h := ComputableInPolyTime.mapWithInput + (f := fun (q : Call × State) (b : ℕ) => min b (nodePrec q.1 q.2.1)) hβ hf + exact h + have hrows := computableInPolyTime_childRows_nativeParentPacking hr ha hi hD hg + have hchild := computableInPolyTime_nativeChild_nativeParentPacking hfl ha hL hi hT hpt hnode hζ + hr hD hcap hg + have h := hrows.prod hchild + exact h + +/-- **The components of the native contraction recursion are polynomial-time**: the hypothesis +`hpoly` of `CountingSimulation.contractionAlgorithmOfNative`. -/ +theorem scheme_nativeMk_polyTime : (scheme nativeMk).PolyTime encCall encFrame encValue := + scheme_polyTime nativeMk_polyTime + +end Cslib.Circuits.Frontier.FastContraction diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionStep.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionStep.lean index 1ff9edf83f..ea5822fb2d 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionStep.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionStep.lean @@ -16,11 +16,13 @@ import Mathlib.Data.List.GetD The child builder of the recursion of `Cslib.Circuits.Frontier.FastContraction.scheme` is the native child (`FastContraction.nativeMk`: `NativeChild.childRows` and `NativeChild.nativeChild` at the -cursor state), and a call is well-formed (`FastContraction.WF`) when its modulus is irreducible -modulo `2` and its network is rows-first. The step of an active call is then correct -(`FastContraction.stepCorrect_nativeMk`) whenever its interpolation points have lifts with unit -differences: the values the call stores are the sums of (D.16) at each node -(`FastContraction.val_table_runLoop`), which is the hypothesis of the correctness of one native call +cursor state, with the rows and the exponents capped so that it is polynomial-time on every state), +and a call is well-formed (`FastContraction.WF`) when its modulus is irreducible modulo `2` and its +network is rows-first. The step of an active call is then correct +(`FastContraction.stepCorrect_nativeMk`): its interpolation points, the binary digits of +`0, …, n - 1`, have lifts with unit differences (`FastContraction.pairwise_isUnit_points`), and the +values the call stores are the sums of (D.16) at each node (`FastContraction.val_table_runLoop`), +which is the hypothesis of the correctness of one native call (`NativeCall.val_recover_eq_contraction`, [OpenAI2026ETH], §D.4.4–§D.4.7). ## Main definitions @@ -30,8 +32,11 @@ differences: the values the call stores are the sums of (D.16) at each node ## Main results -* `Cslib.Circuits.Frontier.FastContraction.stepCorrect_nativeMk`: the step is correct, given the - points. +* `Cslib.Circuits.Frontier.FastContraction.pairwise_isUnit_points`: the points have lifts with unit + differences. +* `Cslib.Circuits.Frontier.FastContraction.nativeMk_eq`: the caps of the builder are inactive on + well-formed active calls. +* `Cslib.Circuits.Frontier.FastContraction.stepCorrect_nativeMk`: the step is correct. * `Cslib.Circuits.Frontier.FastContraction.wfChildren_nativeMk`: well-formedness passes to the children. @@ -47,12 +52,17 @@ namespace Cslib.Circuits.Frontier.FastContraction open Finset GaloisRingArith ChildCursor NativeRecovery ContractionSchedule NativeChild /-- **The native child builder**: at the cursor state `(j, i, β, g)`, the rows and the native -child of group `g` at the `j`-th node, the `i`-th point and the exponents `β`. -/ +child of group `g` at the `j`-th node, the `i`-th point and the exponents `β`. The rows are capped +at the number of vertices (`FastContraction.rowCap`) and the exponents at the node's precision +`p_j`. On a well-formed active call and the cursor's states neither cap changes anything +(`FastContraction.nativeMk_eq`); on every call and state they keep the builder polynomial-time, +since the number of rows and the exponents are stored in binary but the child lists one word per +row and stores the exponents in unary. -/ @[expose] def nativeMk (x : Call) (c : State) : ℕ × DescribedNetwork VertexDesc := - (childRows x.rows (level x ((nodes x).getD c.1 0)) x.net (packing x) c.2.2.2, + (childRows (rowCap x) (level x ((nodes x).getD c.1 0)) x.net (packing x) c.2.2.2, nativeChild x.fl (slots x) (level x ((nodes x).getD c.1 0)) (nodePrec x c.1) - (node (slots x) ((nodes x).getD c.1 0)) ((points x).getD c.2.1 []) x.rows x.net - (packing x) c.2.2.1 c.2.2.2) + (node (slots x) ((nodes x).getD c.1 0)) ((points x).getD c.2.1 []) (rowCap x) x.net + (packing x) (c.2.2.1.map (min · (nodePrec x c.1))) c.2.2.2) /-- **A well-formed call**: its modulus is irreducible modulo `2` and its network is rows-first. -/ @[expose] def WF (x : Call) : Prop := @@ -61,6 +71,40 @@ child of group `g` at the `j`-th node, the `i`-th point and the exponents `β`. variable {x : Call} +/-- A rows-first network with an edge has at most as many rows as vertices. -/ +theorem rows_le_of_isRowsFirstBipartite {D : DescribedNetwork VertexDesc} {r : ℕ} + (hD : D.IsRowsFirstBipartite r) (hM : 1 ≤ D.numEdges) : r ≤ D.vertices.length := by + by_contra h + have h1 := (hD.2 0 hM).2 + rw [List.drop_of_length_le (by omega)] at h1 + simp at h1 + +/-- The cap of `nativeMk` leaves a tuple of weight below `p` unchanged, for positive weights. -/ +theorem map_min_of_mem_budgetTuples {ws : List ℕ} (hw : ∀ w ∈ ws, 0 < w) {p : ℕ} {β : List ℕ} + (hβ : β ∈ BudgetTuples.budgetTuples ws p) : β.map (min · p) = β := by + calc β.map (min · p) = β.map id := List.map_congr_left fun b hb => by + have := BudgetTuples.lt_of_mem_budgetTuples hw hβ hb + simp only [id] + omega + _ = β := List.map_id β + +/-- **The caps of `nativeMk` are inactive** at a well-formed active call and a state of its cursor: +the builder is the native child of the rows and the exponents of the state. -/ +theorem nativeMk_eq (hx : WF x) (ha : active x = true) {c : State} (hc : c ∈ children x) : + nativeMk x c = + (childRows x.rows (level x ((nodes x).getD c.1 0)) x.net + (nativeParentPacking x.rows (slots x) x.net) c.2.2.2, + nativeChild x.fl (slots x) (level x ((nodes x).getD c.1 0)) (nodePrec x c.1) + (node (slots x) ((nodes x).getD c.1 0)) ((points x).getD c.2.1 []) x.rows x.net + (nativeParentPacking x.rows (slots x) x.net) c.2.2.1 c.2.2.2) := by + have hr := rows_le_of_isRowsFirstBipartite hx.2 (one_le_edges_of_active ha) + have hβ : c.2.2.1 ∈ BudgetTuples.budgetTuples (weights x c.1) (nodePrec x c.1) := by + simp only [children, cursorList, nodeBlock, pointBlock, groupBlock, List.mem_flatMap, + List.mem_range, List.mem_map] at hc + obtain ⟨j, -, i, -, β, hβ, g, -, rfl⟩ := hc + exact hβ + rw [nativeMk, packing_eq hr, rowCap_eq hr, map_min_of_mem_budgetTuples (weights_pos _) hβ] + /-- The level of a node index is the `2`-adic valuation of its node. -/ theorem level_eq {k : ℕ} (hk : k < 2 ^ (slots x + 1)) : level x k = padicValInt 2 (node (slots x) k) := by @@ -89,22 +133,31 @@ theorem mem_children {j i : ℕ} {β : List ℕ} {g : ℕ} (hj : j < (nodes x).l List.mem_range, List.mem_map] exact ⟨j, hj, i, hi, β, hβ, g, hg, rfl⟩ +/-- **The interpolation points of an active call have lifts with unit differences**: they are the +binary digits of `0, …, n - 1` with `n ≤ 2 ^ e` +(`GaloisRingArith.pairwise_isUnit_liftO_sub_digits`). -/ +theorem pairwise_isUnit_points (hirr : Irreducible ((monicOf x.fl).map (Int.castRingHom (ZMod 2)))) + (ha : active x = true) : + Pairwise fun i j : Fin (points x).length => + IsUnit (liftO hirr ((points x).getD i []) - liftO hirr ((points x).getD j [])) := by + have hn : numPoints x ≤ 2 ^ x.fl.length := by + simp only [active, decide_eq_true_eq] at ha + exact ha.2.2.2.2.2.1 + exact pairwise_isUnit_liftO_sub_digits hirr hn + /-- **The step of an active call is correct**, for the native child builder and well-formed -calls, whenever the interpolation points of every well-formed active call have lifts with unit -differences. -/ -theorem stepCorrect_nativeMk - (hζ : ∀ x (hirr : Irreducible ((monicOf x.fl).map (Int.castRingHom (ZMod 2)))), - active x = true → Pairwise fun i j : Fin (points x).length => - IsUnit (liftO hirr ((points x).getD i []) - liftO hirr ((points x).getD j []))) : - StepCorrect nativeMk WF := by +calls. -/ +theorem stepCorrect_nativeMk : StepCorrect nativeMk WF := by intro x hx ha vals hvals obtain ⟨hirr, hD⟩ := hx have ha' := ha simp only [active, decide_eq_true_eq] at ha' obtain ⟨-, hu, huM, hua, -, -, -⟩ := ha' have hlen : (points x).length = numPoints x := by simp [points] + have hr := rows_le_of_isRowsFirstBipartite hD (one_le_edges_of_active ha) refine NativeCall.val_recover_eq_contraction hirr hD hu huM hua (le_taperStep_mul hu) - (taperStep_mul_le _ _ _) (pow_slots_le_edges_of_active ha) ?_ (points x) (hζ x hirr ha) + (taperStep_mul_le _ _ _) (pow_slots_le_edges_of_active ha) ?_ (points x) + (pairwise_isUnit_points hirr ha) (by rw [hlen]; exact Nat.lt_succ_self _) _ ?_ · calc (slots x + 1) * ∑ t ∈ recoveryNodes (slots x), taperMult (levels x) (step x) t ≤ (slots x + 1) * (2 ^ (slots x + 1) * (step x * levels x)) := @@ -127,14 +180,15 @@ theorem stepCorrect_nativeMk have hw : weights x j = modeWeights (nativeParentPacking x.rows (slots x) x.net).2.1 (2 ^ (padicValInt 2 (node (slots x) k) + 1)) (nodePrecision x.prec (step x) (slots x) x.edges (padicValInt 2 (node (slots x) k))) := by - rw [weights, packing, hgetD, hlev, hprec] + rw [weights, packing_eq hr, hgetD, hlev, hprec] have hstore := val_table_runLoop ha vals hj hi rw [hjk, hw, hprec] at hstore refine hstore.trans ?_ refine congrArg List.sum (List.map_congr_left fun β hβ => ?_) refine congrArg List.prod (List.map_congr_left fun g hg => ?_) - have hmem := mem_children hj (by omega) (by rw [hw, hprec]; exact hβ) - (List.mem_range.mp hg) + have hβ' : β ∈ BudgetTuples.budgetTuples (weights x j) (nodePrec x j) := by + rw [hw, hprec]; exact hβ + have hmem := mem_children hj (by omega) hβ' (List.mem_range.mp hg) have key : ∀ v P, level x k = v → nodePrec x j = P → val P x.fl (vals (j, i, β, g)) = (nativeChild x.fl (slots x) v P (node (slots x) k) ((points x).getD i []) x.rows x.net @@ -142,7 +196,8 @@ theorem stepCorrect_nativeMk fun d bs => val P x.fl (d.entry bs) := by rintro v P rfl rfl have h := hvals _ hmem - simp only [nativeMk, packing] at h + rw [nativeMk_eq ⟨hirr, hD⟩ ha hmem] at h + simp only at h rw [hgetD] at h exact h exact key _ _ hlev hprec @@ -176,7 +231,10 @@ theorem wfChildren_nativeMk : WFChildren nativeMk WF := by have hia : level x ((nodes x).getD c.1 0) < slots x := by rw [level_eq hk] exact padicValInt_lt_of_taperMult_pos (by rwa [← mult_eq _ _ hk]) hua - exact isRowsFirstBipartite_nativeChild (fl := x.fl) hD (pow_slots_le_edges_of_active ha) hia - (nodePrec x c.1) _ ((points x).getD c.2.1 []) c.2.2.1 ⟨c.2.2.2, hg⟩ + have h := isRowsFirstBipartite_nativeChild (fl := x.fl) hD (pow_slots_le_edges_of_active ha) hia + (nodePrec x c.1) (node (slots x) ((nodes x).getD c.1 0)) ((points x).getD c.2.1 []) + c.2.2.1 ⟨c.2.2.2, hg⟩ + simp only [child, nativeMk_eq ⟨hirr, hD⟩ ha hc] + exact h end Cslib.Circuits.Frontier.FastContraction diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean index 9cb5d17ff7..a498a0ba11 100644 --- a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean @@ -11,6 +11,7 @@ public import Complexity.Computability.Complexity.Classes.CountingSimulation.Gua public import Complexity.Computability.Complexity.Classes.CountingSimulation.Admissible public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionMachine public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionStep +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionPolyTime import Mathlib.Analysis.SpecificLimits.Normed import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat @@ -51,11 +52,10 @@ finish, and the machine falls back to direct summation, at a constant factor. The guard is polynomial-time (`CountingSimulation.fastGuard_polyTime`). -For the native child builder (`FastContraction.nativeMk`), the step, the passage of -well-formedness to the children and the well-formed roots are proved -(`CountingSimulation.contractionAlgorithmOfNative`); there the remaining obligations are the -components of the scheme in polynomial time, the unit differences of the interpolation points and -the resource analysis. +For the native child builder (`FastContraction.nativeMk`), the components of the scheme are +polynomial-time (`FastContraction.scheme_nativeMk_polyTime`), and the step, the passage of +well-formedness to the children and the well-formed roots are proved; there the one remaining +obligation is the resource analysis (`CountingSimulation.contractionAlgorithmOfNative`). **Remaining obligations** (the hypotheses of `CountingSimulation.contractionAlgorithmOfFast`): the components of the scheme in polynomial time, the correctness of one active call @@ -69,7 +69,7 @@ components of the scheme in polynomial time, the correctness of one active call * `Cslib.Complexity.CountingSimulation.contractionAlgorithmOfFast`: the contraction algorithm from the remaining obligations. * `Cslib.Complexity.CountingSimulation.contractionAlgorithmOfNative`: the same for the native child - builder, with fewer obligations. + builder, from the resource analysis alone. ## Main results @@ -172,19 +172,6 @@ private theorem fastGuard_eq_guardTest (G : ℕ × ExplicitTensorNetwork ℕ) : · rintro ⟨⟨⟨h1, h2⟩, h3⟩, h4⟩ exact ⟨⟨fun v hv i hi => h1 v hv i hi, fun i hi => h2 i hi⟩, h3, h4⟩ -/-- The degree bound `2 ^ (9 ⌊log₂ m⌋ / 10 + 2)`, in binary from `m` in unary. -/ -private theorem degreeBound_polyTime : ComputableInPolyTime degreeBound encUnary encNat := by - have hb := ComputableInPolyTime.unary_to_binary - have hlog := ComputableInPolyTime.binary_log_two.comp hb - have he : ComputableInPolyTime (fun m => 9 * Nat.log 2 m / 10 + 2) encUnary encUnary := - ComputableInPolyTime.binary_to_unary_bounded - (ComputableInPolyTime.binary_add.comp ((ComputableInPolyTime.binary_div.comp - ((ComputableInPolyTime.binary_mul' (ComputableInPolyTime.const 9) (hb.comp hlog)).prod - (ComputableInPolyTime.const 10))).prod (ComputableInPolyTime.const 2))) - (ComputableInPolyTime.unary_add' hlog (ComputableInPolyTime.const 2)) fun m => by - simp only [Function.comp_apply]; omega - exact ComputableInPolyTime.binary_two_pow.comp he - /-- **The guard is polynomial-time.** -/ theorem fastGuard_polyTime : ComputableInPolyTime fastGuard encG encBool := by have hpair : ComputableInPolyTime (DescribedNetwork.equivProd (T := List ℕ)) @@ -650,21 +637,15 @@ theorem wf_root (P : ℕ) {G : ℕ × ExplicitTensorNetwork ℕ} (hg : fastGuard ← List.map_drop, List.map_map, Function.comp_def] at hi ⊢ exact h2 i hi -/-- **The contraction algorithm from the native recursion**, given its remaining obligations: the -components of the scheme in polynomial time, the unit differences of the interpolation points, and -the resource analysis. The step, the passage of well-formedness to the children and the -well-formed roots are proved (`FastContraction.stepCorrect_nativeMk`, +/-- **The contraction algorithm from the native recursion**, given its one remaining obligation, +the resource analysis. The components of the scheme are polynomial-time +(`FastContraction.scheme_nativeMk_polyTime`), and the step, the passage of well-formedness to the +children and the well-formed roots are proved (`FastContraction.stepCorrect_nativeMk`, `FastContraction.wfChildren_nativeMk`, `CountingSimulation.wf_root`). -/ -noncomputable def contractionAlgorithmOfNative - (hpoly : (scheme nativeMk).PolyTime encCall encFrame encValue) - (hζ : ∀ x (hirr : Irreducible ((GaloisRingArith.monicOf x.fl).map - (Int.castRingHom (ZMod 2)))), active x = true → - Pairwise fun i j : Fin (points x).length => IsUnit (GaloisRingArith.liftO hirr - ((points x).getD i []) - GaloisRingArith.liftO hirr ((points x).getD j []))) - (hana : FastAnalysis nativeMk) : +noncomputable def contractionAlgorithmOfNative (hana : FastAnalysis nativeMk) : ContractionAlgorithm (encPair encNat ExplicitTensorNetwork.encExplicitNetwork) (fun G => G.2.contraction) ExplicitAdmissible := - contractionAlgorithmOfFast hpoly (stepCorrect_nativeMk hζ) wfChildren_nativeMk + contractionAlgorithmOfFast scheme_nativeMk_polyTime stepCorrect_nativeMk wfChildren_nativeMk (fun P _ hg => wf_root P hg) hana end Resources diff --git a/Complexity/Foundations/Combinatorics/BudgetTuples.lean b/Complexity/Foundations/Combinatorics/BudgetTuples.lean index 3ed809c732..20d48a9bcd 100644 --- a/Complexity/Foundations/Combinatorics/BudgetTuples.lean +++ b/Complexity/Foundations/Combinatorics/BudgetTuples.lean @@ -55,6 +55,7 @@ along `budgetTuples` (`sum_filter_weight_lt_eq`). * `Cslib.BudgetTuples.mem_budgetTuples`, `Cslib.BudgetTuples.nodup_budgetTuples`: the list holds each tuple of weight below the budget once. +* `Cslib.BudgetTuples.lt_of_mem_budgetTuples`: the entries of these tuples are below the budget. * `Cslib.BudgetTuples.head?_budgetTuples`, `Cslib.BudgetTuples.isChain_budgetTuples`, `Cslib.BudgetTuples.budgetNext_getLast?`: the successor walks the list from the zero tuple. * `Cslib.BudgetTuples.budgetNextScan_eq`: the scan computes the successor. @@ -133,6 +134,26 @@ theorem mem_budgetTuples {ws : List ℕ} (hw : ∀ w ∈ ws, 0 < w) {p : ℕ} { · simp only [List.length_cons, Nat.add_right_cancel_iff, weight_cons_cons] at hlen hwt exact ⟨b, by omega, γ, ih'.mpr ⟨hlen, by omega⟩, rfl⟩ +/-- Each entry of a tuple is at most its weight, for positive weights. -/ +theorem le_weight_of_mem : ∀ {ws β : List ℕ}, (∀ w ∈ ws, 0 < w) → β.length = ws.length → + ∀ b ∈ β, b ≤ weight ws β + | [], [], _, _, b, hb => by simp at hb + | [], _ :: _, _, hl, _, _ => by simp at hl + | _ :: _, [], _, hl, _, _ => by simp at hl + | w :: ws, b' :: β, hw, hl, b, hb => by + rw [weight_cons_cons] + rcases List.mem_cons.mp hb with rfl | hb + · exact (Nat.le_mul_of_pos_left b (hw w List.mem_cons_self)).trans (Nat.le_add_right _ _) + · have := le_weight_of_mem (fun w' hw' => hw w' (List.mem_cons_of_mem _ hw')) + (by simpa using hl) b hb + omega + +/-- **Every entry of a tuple of weight below `p` is below `p`**, for positive weights. -/ +theorem lt_of_mem_budgetTuples {ws : List ℕ} (hw : ∀ w ∈ ws, 0 < w) {p : ℕ} {β : List ℕ} + (hβ : β ∈ budgetTuples ws p) {b : ℕ} (hb : b ∈ β) : b < p := by + obtain ⟨hl, hwt⟩ := (mem_budgetTuples hw).mp hβ + exact (le_weight_of_mem hw hl b hb).trans_lt hwt + /-- **Each tuple appears once.** -/ theorem nodup_budgetTuples (ws : List ℕ) (p : ℕ) : (budgetTuples ws p).Nodup := by induction ws generalizing p with diff --git a/ComplexityTests/FastContractionPolyTime.lean b/ComplexityTests/FastContractionPolyTime.lean new file mode 100644 index 0000000000..2b8c3ca973 --- /dev/null +++ b/ComplexityTests/FastContractionPolyTime.lean @@ -0,0 +1,51 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.Complexity.Classes.CountingSimulation.FastAlgorithm + +/-! # The components of the native contraction recursion in polynomial time: checked instances + +The caps on the rows and the exponents, the schedule's quantities on a call that is not active, +and the instance of the contraction interface from the resource analysis alone. -/ + +namespace ComplexityTests.FastContractionPolyTime + +open Turing.MultiTapeTM Cslib.Circuits.Frontier Cslib.Circuits.Frontier.FastContraction + Cslib.Circuits.Frontier.ContractionSchedule Cslib.Complexity.CountingSimulation + +/-- One row `0` and one column `1` joined by edge `0`, with full tables. -/ +def tiny : ExplicitTensorNetwork ℕ := ⟨1, [([0], [1, 2]), ([0], [3, 4])]⟩ + +-- The number of rows is stored in binary, so a call may claim `2 ^ 100` rows; the cap keeps the +-- two vertices, and on the rows-first root it is inactive. +example : rowCap (root (3, 2 ^ 100, tiny)) = 2 := by decide +example : rowCap (root (3, 1, tiny)) = 1 := by decide + +-- The packing of the root: one packet of `2 ^ 0` slots, within the bound `M (2 ^ a) ^ 12`. +example : slots (root (3, 1, tiny)) = 0 := by decide +example : (packing (root (3, 1, tiny))).2.1 = 1 := by decide +example : (packing (root (3, 1, tiny))).2.1 ≤ + (root (3, 1, tiny)).edges * (2 ^ slots (root (3, 1, tiny))) ^ 12 := + NativeChild.numPackets_nativeParentPacking_le _ _ _ + +-- On every call, `2 ^ a ≤ s + 1`, and the levels are at most `a + 1`. +example (x : Call) : 2 ^ slots x ≤ x.scale + 1 := two_pow_slots_le x +example (x : Call) (k : ℕ) : level x k ≤ slots x + 1 := level_le x k + +-- The cap on the exponents leaves the tuples of weight below the budget unchanged. +example : ([1, 0, 1] : List ℕ).map (min · 3) = [1, 0, 1] := + map_min_of_mem_budgetTuples (ws := [1, 1, 1]) (by decide) (by decide) + +-- The components of the scheme of the native child builder are polynomial-time. +example : (scheme nativeMk).PolyTime encCall encFrame encValue := scheme_nativeMk_polyTime + +-- The contraction interface for admissible explicit networks, from the resource analysis alone. +noncomputable example (hana : FastAnalysis nativeMk) : + ContractionAlgorithm (encPair encNat ExplicitTensorNetwork.encExplicitNetwork) + (fun G => G.2.contraction) ExplicitAdmissible := + contractionAlgorithmOfNative hana + +end ComplexityTests.FastContractionPolyTime From 0598212f3fee96359375191bda18e1ded9f51c8f Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:37:26 -0400 Subject: [PATCH 111/129] feat(circuit/frontier): structural shapes of the calls of the native recursion Every call reached from a well-formed root with m edges keeps the scale, modulus and well-formedness, has at most 2m edges, precision at most p0 + 40 m^2 d and at most V0 + m^2 d vertices at depth d, and every descriptor is a root table or a bounded packet descriptor. These hold for every input, whatever the schedule. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../TensorNetwork/FastContractionShape.lean | 117 ++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/Complexity.lean b/Complexity.lean index 13058f0467..8aa944bc7f 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -901,6 +901,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContra public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionDerivation public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionMachine public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionSchedule +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionShape public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionStep public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates public import Complexity.Computability.Circuit.Frontier.TensorNetwork.KernelProduct diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean index f97eb09a3e..eff3adae25 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean @@ -215,6 +215,123 @@ theorem getD_points_facts (x : Call) (i : ℕ) : · rw [List.getD_eq_default _ _ (by omega)] simp +/-- The rows of a native child are at most the parent's vertices. -/ +theorem childRows_le (r i : ℕ) (D : DescribedNetwork VertexDesc) + (pk : (List (List ℕ) × List (List ℕ)) × ℕ × List (ℕ × ℕ)) (g : ℕ) : + childRows r i D pk g ≤ D.vertices.length := by + unfold childRows + refine (List.count_le_length).trans ?_ + simp [groupsOf] + +/-- A packet descriptor built from a packet context with bounded fields is bounded. -/ +theorem packetBound_packetCtx {fl ζ β : List ℕ} {m P k a i pt g q : ℕ} {t : ℤ} + {tags : List (Bool × ℕ)} (hpt : pt ≤ P) (ha : a ≤ Nat.log 2 m) (hi : i ≤ a) + (ht : t.natAbs ≤ 2 ^ a) (hζ : ζ.length ≤ fl.length) (hζ1 : ∀ z ∈ ζ, z ≤ 1) + (htags : tags.length = k) (hslots : ∀ b ∈ tags, b.2 < 2 ^ a) + (hβ : β.length ≤ 2 ^ (i + 1) * pt) (hβ' : ∀ b ∈ β, b ≤ pt) : + PacketBound fl m P k (packetCtx fl a i pt t ζ g q, tags, β) := + ⟨hpt, rfl, rfl, ha, rfl, hi, rfl, ht, rfl, hζ, hζ1, htags, hslots, hβ, hβ'⟩ + +/-- **The shape passes to the native children** of an active call, from a root `x₀` at scale `m` +equal to its number of edges, with bound plus depth `m + 1`. -/ +theorem Shape.child (h₀s : x₀.scale = x₀.edges) (h₀b : x₀.bound + x₀.depth = x₀.edges + 1) + (h : Shape x₀ x) (ha : active x = true) {c : State} (hc : c ∈ children x) : + Shape x₀ (FastContraction.child x nativeMk c) := by + set m := x₀.edges + have hsc : x.scale = m := h.scale_eq.trans h₀s + have hM : x.edges ≤ m := by + have := edges_lt_bound_of_active ha + have := h.bound_add_depth_le + omega + obtain ⟨haL, h12, hL, -, -, -⟩ := slots_facts ha + rw [hsc] at haL h12 hL + have hK := pow_slots_le_edges_of_active ha + obtain ⟨hlev, hnode⟩ := level_le_slots_of_mem_children ha hc + have hprec : nodePrec x c.1 ≤ x.prec + 40 * m ^ 2 := by + have := nodePrec_le_add ha (hsc ▸ hM) c.1 + rwa [hsc] at this + have hmk := nativeMk_apply_of_active h.wf ha hc + set pk := nativeParentPacking x.rows (slots x) x.net + have hpk : pk.2.1 ≤ m ^ 2 := by + have := numPackets_nativeParentPacking_le x.rows (slots x) x.net + calc pk.2.1 ≤ x.edges * (2 ^ slots x) ^ 12 := this + _ ≤ m * m := Nat.mul_le_mul hM h12 + _ = m ^ 2 := by ring + have hslot : ∀ y ∈ pk.2.2, y.2 < 2 ^ slots x := by + intro y hy + rw [show pk = nativeParentPacking x.rows (slots x) x.net from rfl, + nativeParentPacking_eq h.wf.2 hK] at hy + obtain ⟨e, rfl⟩ := List.mem_ofFn.mp hy + exact ((parentPacking h.wf.2 (slots x)).slot e).2 + have hvert : (FastContraction.child x nativeMk c).net.vertices.length ≤ + x.net.vertices.length + m ^ 2 := by + change (nativeMk x c).2.vertices.length ≤ _ + rw [hmk, length_vertices_nativeChild] + have := childRows_le x.rows (level x ((nodes x).getD c.1 0)) x.net pk c.2.2.2 + omega + refine ⟨h.scale_eq, h.fl_eq, wfChildren_nativeMk x h.wf ha c hc, ?_, ?_, ?_, ?_, ?_, ?_⟩ + · have := edges_lt_bound_of_active ha + have := h.bound_add_depth_le + change x.edges + (x.depth + 1) ≤ _ + omega + · change (nativeMk x c).2.numEdges ≤ 2 * m + rw [hmk] + exact (numEdges_nativeChild_le _ _ _ _ _ _ _ _ _ _ _).trans (Nat.mul_le_mul_left 2 hM) + · change nodePrec x c.1 ≤ x₀.prec + (x.depth + 1) * (40 * m ^ 2) + have : x.prec ≤ x₀.prec + x.depth * (40 * m ^ 2) := h.prec_le + rw [add_one_mul] + omega + · change _ ≤ x₀.net.vertices.length + (x.depth + 1) * m ^ 2 + have : x.net.vertices.length ≤ x₀.net.vertices.length + x.depth * m ^ 2 := + h.vertices_le + rw [add_one_mul] + omega + · refine Or.inr ?_ + change (nativeMk x c).1 ≤ x₀.net.vertices.length + (x.depth + 1) * m ^ 2 + rw [hmk] + have := childRows_le x.rows (level x ((nodes x).getD c.1 0)) x.net pk c.2.2.2 + have : x.net.vertices.length ≤ x₀.net.vertices.length + x.depth * m ^ 2 := + h.vertices_le + rw [add_one_mul] + omega + · intro v hv + change v ∈ (nativeMk x c).2.vertices at hv + rw [hmk] at hv + rcases mem_vertices_nativeChild _ _ _ _ _ _ _ _ _ _ _ hv with ⟨w, hw, hd, hl⟩ | + ⟨q, -, tags, hd, htl, hts⟩ + · rcases h.desc w hw with ⟨u, hu, hu'⟩ | ⟨d, hwd, hb⟩ + · exact Or.inl ⟨u, hu, hd.trans hu'⟩ + · refine Or.inr ⟨d, hd.trans hwd, ?_⟩ + rw [hl] + refine hb.mono ?_ + change x₀.prec + x.depth * (40 * m ^ 2) ≤ x₀.prec + (x.depth + 1) * (40 * m ^ 2) + rw [add_one_mul] + omega + · refine Or.inr ⟨_, hd, ?_⟩ + have hζ := getD_points_facts x c.2.1 + have hβ := lt_nodePrec_of_mem_children hc + have hp : nodePrec x c.1 ≤ x₀.prec + (x.depth + 1) * (40 * m ^ 2) := by + have : x.prec ≤ x₀.prec + x.depth * (40 * m ^ 2) := h.prec_le + rw [add_one_mul] + omega + have hfl : x.fl.length = x₀.fl.length := by rw [h.fl_eq] + rw [h.fl_eq] at hζ ⊢ + refine packetBound_packetCtx hp haL hlev.le hnode (hfl ▸ hζ.1) hζ.2 htl (fun b hb => ?_) + (length_betaSlice_le _ _ _ _) fun b hb => (hβ b (mem_of_mem_betaSlice _ _ _ hb)).le + rcases hts b hb with h0 | ⟨y, hy, hy'⟩ + · rw [h0]; exact Nat.two_pow_pos _ + · rw [hy']; exact hslot y hy + +/-- **Every reached call has the shape** of a root `x₀` that is well-formed, at scale its number of +edges, with bound plus depth `m + 1`. -/ +theorem Reach.shape (h₀s : x₀.scale = x₀.edges) (h₀b : x₀.bound + x₀.depth = x₀.edges + 1) + (h₀ : WF x₀) (h : Reach nativeMk x₀ x) : Shape x₀ x := by + induction h with + | root => + exact ⟨rfl, rfl, h₀, le_rfl, by omega, Nat.le_add_right _ _, Nat.le_add_right _ _, + Or.inl rfl, fun v hv => Or.inl ⟨v, hv, rfl⟩⟩ + | step c _ ha hc ih => exact ih.child h₀s h₀b ha hc + end Child end Cslib.Circuits.Frontier.FastContraction From 0051ec650486e23b339486818baaa8ebc454efac Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:37:41 -0400 Subject: [PATCH 112/129] style: wrap a docstring Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/FastContractionShape.lean | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean index eff3adae25..ceeef247fe 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean @@ -52,9 +52,10 @@ open ContractionSchedule ChildCursor NativeRecovery NativeChild /-- **A bounded packet descriptor** for a root with modulus `fl` and `m` edges, at precision at most `P`, on a vertex with `k` ports: its precision is at most `P` and is its number of exponents per -tile, its modulus is `fl`, it has `2 ^ a` slots with `a ≤ ⌊log₂ m⌋`, `2 ^ (i + 1)` tiles with `i ≤ a`, a grid point -of size at most `2 ^ a`, no column list, a 0/1 interpolation value with at most `e` coordinates, -`k` tags with slots below `2 ^ a`, and at most `2 ^ (i + 1) p` exponents, each at most `p`. -/ +tile, its modulus is `fl`, it has `2 ^ a` slots with `a ≤ ⌊log₂ m⌋`, `2 ^ (i + 1)` tiles with +`i ≤ a`, a grid point of size at most `2 ^ a`, no column list, a 0/1 interpolation value with at +most `e` coordinates, `k` tags with slots below `2 ^ a`, and at most `2 ^ (i + 1) p` exponents, each +at most `p`. -/ @[expose] def PacketBound (fl : List ℕ) (m P k : ℕ) (d : NativePacket.PacketDesc) : Prop := d.1.p ≤ P ∧ d.1.nb = d.1.p ∧ d.1.fl = fl ∧ d.1.a ≤ Nat.log 2 m ∧ d.1.L = 2 ^ d.1.a ∧ d.1.i ≤ d.1.a ∧ d.1.tiles = 2 ^ (d.1.i + 1) ∧ d.1.t.natAbs ≤ 2 ^ d.1.a ∧ d.1.cols = [] ∧ From 4ef2e743927eea0e671cfa091f7d28e049f38a03 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:38:28 -0400 Subject: [PATCH 113/129] refactor(circuit/frontier): unfold nativeMk once in packetShape_child Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/FastContractionLeaf.lean | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean index 8f75d42024..b903a525a1 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean @@ -494,6 +494,10 @@ vertex keeps its descriptor, at a precision that only grows, and a new packet ha as its number of exponents per tile, and the slots of the packing, distinct within a packet. -/ theorem packetShape_child {x : Call} (hx : WF x) (ha : active x = true) (hs : PacketShape x) {c : State} (hc : c ∈ children x) : PacketShape (child x nativeMk c) := by + -- The builder at a cursor state of a well-formed active call: the uncapped native child. + have hmk : (nativeMk x c).2 = nativeChild x.fl (slots x) (level x ((nodes x).getD c.1 0)) + (nodePrec x c.1) (node (slots x) ((nodes x).getD c.1 0)) ((points x).getD c.2.1 []) x.rows + x.net (nativeParentPacking x.rows (slots x) x.net) c.2.2.1 c.2.2.2 := rfl obtain ⟨-, hD⟩ := hx obtain ⟨hj, -, hg⟩ := lt_of_mem_children hc have ha' := ha @@ -509,7 +513,9 @@ theorem packetShape_child {x : Call} (hx : WF x) (ha : active x = true) (hs : Pa intro v hv d hd have hv' : v ∈ (nativeChild x.fl (slots x) (level x ((nodes x).getD c.1 0)) (nodePrec x c.1) (node (slots x) ((nodes x).getD c.1 0)) ((points x).getD c.2.1 []) x.rows x.net - (nativeParentPacking x.rows (slots x) x.net) c.2.2.1 (⟨c.2.2.2, hg⟩ : Fin 64)).vertices := hv + (nativeParentPacking x.rows (slots x) x.net) c.2.2.1 (⟨c.2.2.2, hg⟩ : Fin 64)).vertices := by + change v ∈ (nativeMk x c).2.vertices at hv + rwa [hmk] at hv rcases NativeChild.desc_of_mem_nativeChild hD (pow_slots_le_edges_of_active ha) hia _ _ _ _ _ hv' with ⟨w, hw, hvw⟩ | ⟨q, tags, hvq, hslot, hnodup⟩ · obtain ⟨h1, h2, h3, h4, h5, h6⟩ := hs w hw d (hvw ▸ hd) From fac3d9ad4b54e14ea512523c0ca159192b4937dd Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:40:13 -0400 Subject: [PATCH 114/129] feat(circuit/frontier): encoded lengths of calls and packet descriptors The encoded length of a call is at most 64 (B+1)^3 + 2 B R when its numbers, lists and ports are at most B and its descriptors at most R long; a bounded packet descriptor is at most 200 (B+1)^2 long. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../TensorNetwork/FastContractionSize.lean | 150 ++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean diff --git a/Complexity.lean b/Complexity.lean index 8aa944bc7f..7abc06972e 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -902,6 +902,7 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContra public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionMachine public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionSchedule public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionShape +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionSize public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionStep public import Complexity.Computability.Circuit.Frontier.TensorNetwork.IntegralCoordinates public import Complexity.Computability.Circuit.Frontier.TensorNetwork.KernelProduct diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean new file mode 100644 index 0000000000..27609c69f2 --- /dev/null +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean @@ -0,0 +1,150 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionShape +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Length +import Mathlib.Data.List.GetD + +/-! +# The encoded sizes of the calls of the native contraction recursion + +Bounds on the encoded lengths of the calls (`FastContraction.encCall`) of the native recursion +from bounds on their numbers: the length of an encoded call is at most `64 (B + 1) ^ 3 + 2 B R` when +its numbers, lists and ports are at most `B` and its descriptors at most `R` long +(`FastContraction.length_encCall_le`), and a bounded packet descriptor is at most +`200 (B + 1) ^ 2` long (`FastContraction.length_enc_inr_le`). + +## Main results + +* `Cslib.Circuits.Frontier.FastContraction.length_encList_le`: lists of bounded elements. +* `Cslib.Circuits.Frontier.FastContraction.length_enc_packetCtx_le`: packet contexts. +* `Cslib.Circuits.Frontier.FastContraction.length_enc_inr_le`: bounded packet descriptors. +* `Cslib.Circuits.Frontier.FastContraction.length_encCall_le`: calls. +-/ + +public section + +namespace Cslib.Circuits.Frontier.FastContraction + +open Turing.MultiTapeTM NativePacket + +/-- A number at most `b` has at most `b` binary digits. -/ +theorem length_encNat_le {a b : ℕ} (h : a ≤ b) : (encNat a).length ≤ b := by + rw [encNat_apply, Nat.size_eq_bits_len] + exact Nat.size_le.mpr (h.trans_lt Nat.lt_two_pow_self) + +/-- A number below `2 ^ p` has at most `p` binary digits. -/ +theorem length_encNat_le_of_lt_two_pow {a p : ℕ} (h : a < 2 ^ p) : (encNat a).length ≤ p := by + rw [encNat_apply, Nat.size_eq_bits_len] + exact Nat.size_le.mpr h + +/-- **A list of elements of encoded length at most `B`** has encoded length at most +`k (2 B + 1)` for `k` elements. -/ +theorem length_encList_le {α : Type*} (enc : α ↪ List Bool) {l : List α} {B : ℕ} + (h : ∀ a ∈ l, (enc a).length ≤ B) : (encList enc l).length ≤ l.length * (2 * B + 1) := by + rw [length_encList] + calc (l.map fun a => 2 * (enc a).length + 1).sum ≤ (l.map fun _ => 2 * B + 1).sum := + List.sum_le_sum fun a ha => by have := h a ha; omega + _ = l.length * (2 * B + 1) := by simp + +/-- A list of numbers at most `1` has encoded length at most `3 k` for `k` numbers. -/ +theorem length_encList_le_of_le_one {l : List ℕ} (h : ∀ z ∈ l, z ≤ 1) : + (encList encNat l).length ≤ 3 * l.length := by + have := length_encList_le encNat (B := 1) fun z hz => length_encNat_le (h z hz) + omega + +/-- **A packet context with fields at most `B`** and no column list has encoded length at most +`64 (B + 1)`. -/ +theorem length_enc_packetCtx_le (c : PacketCtx) {B : ℕ} (hp : c.p ≤ B) (hfl : c.fl.length ≤ B) + (hfl1 : ∀ z ∈ c.fl, z ≤ 1) (ha : c.a ≤ B) (hL : c.L ≤ B) (hi : c.i ≤ B) (htl : c.tiles ≤ B) + (ht : c.t.natAbs ≤ B) (hnb : c.nb ≤ B) (hcols : c.cols = []) (hζ : c.ζ.length ≤ B) + (hζ1 : ∀ z ∈ c.ζ, z ≤ 1) : (PacketCtx.enc c).length ≤ 64 * (B + 1) := by + have h1 := length_encList_le_of_le_one hfl1 + have h2 := length_encList_le_of_le_one hζ1 + have h3 : c.t.natAbs.size ≤ B := (Nat.size_le.mpr (Nat.lt_two_pow_self)).trans ht + simp only [PacketCtx.enc, encOfEquiv, Function.Embedding.trans_apply, Equiv.coe_toEmbedding, + PacketCtx.equivProd, Equiv.coe_fn_mk, length_encPair, encUnary_apply, List.length_replicate, + length_encIntBin, encBool_apply, List.length_singleton, hcols, length_encList, List.map_nil, + List.sum_nil] at h1 h2 ⊢ + omega + +/-- `2 ^ a ≤ m` for `a ≤ ⌊log₂ m⌋` and `m ≠ 0`. -/ +theorem two_pow_le_of_le_log {a m : ℕ} (ha : a ≤ Nat.log 2 m) (hm : m ≠ 0) : 2 ^ a ≤ m := + (Nat.pow_le_pow_right (by norm_num) ha).trans (Nat.pow_log_le_self 2 hm) + +/-- **A bounded packet descriptor has encoded length at most `200 (B + 1) ^ 2`**, when +`2 (m + 1) (P + 1)`, the modulus's length and the number of ports are at most `B` and the +modulus has 0/1 coefficients. -/ +theorem length_enc_inr_le {fl : List ℕ} {m P k : ℕ} {d : PacketDesc} (h : PacketBound fl m P k d) + {B : ℕ} (hB : 2 * (m + 1) * (P + 1) ≤ B) (hfl : fl.length ≤ B) (hfl1 : ∀ z ∈ fl, z ≤ 1) + (hk : k ≤ B) : (VertexDesc.enc (.inr d)).length ≤ 200 * (B + 1) ^ 2 := by + obtain ⟨hp, hnb, hfl', ha, hL, hi, htl, ht, hcols, hζ, hζ1, htags, hslots, hβ, hβ'⟩ := h + have h2m : 2 * (m + 1) ≤ B := le_trans (Nat.le_mul_of_pos_right _ (by omega)) hB + have hP1 : P + 1 ≤ B := le_trans (Nat.le_mul_of_pos_left _ (by omega)) hB + have hLm : 2 ^ d.1.a ≤ m + 1 := by + rcases Nat.eq_zero_or_pos m with h0 | h0 + · rw [h0, Nat.log_zero_right, Nat.le_zero] at ha; rw [ha, h0]; simp + · exact (two_pow_le_of_le_log ha (by omega)).trans (Nat.le_succ _) + have htiles : d.1.tiles ≤ 2 * (m + 1) := by + rw [htl, pow_succ] + have := Nat.pow_le_pow_right (n := 2) (by norm_num) hi + omega + have haL := Nat.lt_two_pow_self (n := d.1.a) + have hiL := Nat.lt_two_pow_self (n := d.1.i) + have hiL' : 2 ^ d.1.i ≤ 2 ^ d.1.a := Nat.pow_le_pow_right (by norm_num) hi + have hflB : d.1.fl.length ≤ B := by rw [hfl']; exact hfl + have hfl1' : ∀ z ∈ d.1.fl, z ≤ 1 := by rw [hfl']; exact hfl1 + have hctx := length_enc_packetCtx_le d.1 (B := B) (by omega) hflB hfl1' (by omega) (by omega) + (by omega) (by omega) (by omega) (by omega) hcols (hζ.trans hfl) hζ1 + have htg : (encList (encPair encBool encNat) d.2.1).length ≤ B * (2 * (B + 3) + 1) := by + refine (length_encList_le _ (B := B + 3) fun b hb => ?_).trans + (Nat.mul_le_mul_right _ (htags ▸ hk)) + rw [length_encPair, encBool_apply, List.length_singleton] + have := length_encNat_le_of_lt_two_pow (hslots b hb) + have := Nat.lt_two_pow_self (n := d.1.a) + omega + have hbeta : (encList encUnary d.2.2).length ≤ B * (2 * B + 1) := by + refine (length_encList_le _ (B := B) fun b hb => ?_).trans (Nat.mul_le_mul_right _ ?_) + · rw [encUnary_apply, List.length_replicate] + exact (hβ' b hb).trans (hp.trans (by omega)) + · calc d.2.2.length ≤ d.1.tiles * d.1.p := hβ + _ ≤ 2 * (m + 1) * (P + 1) := Nat.mul_le_mul htiles (hp.trans (Nat.le_succ _)) + _ ≤ B := hB + rw [length_encSum] + simp only [Sum.elim_inr, encPacketDesc, length_encPair] + nlinarith + +/-- **The encoded length of a call** is at most `64 (B + 1) ^ 3 + 2 B R` when its scale, depth, +bound, precision, number of edges, number of vertices, modulus length, the binary length of its +rows and its ports are at most `B`, the modulus has 0/1 coefficients, and its descriptors are at +most `R` long. -/ +theorem length_encCall_le {x : Call} {B R : ℕ} (hs : x.scale ≤ B) (hd : x.depth ≤ B) + (hb : x.bound ≤ B) (hp : x.prec ≤ B) (hfl : x.fl.length ≤ B) (hfl1 : ∀ z ∈ x.fl, z ≤ 1) + (hr : (encNat x.rows).length ≤ B) (hE : x.edges ≤ B) (hV : x.net.vertices.length ≤ B) + (hports : ∀ v ∈ x.net.vertices, v.1.length ≤ B ∧ ∀ i ∈ v.1, i ≤ B) + (hdesc : ∀ v ∈ x.net.vertices, (VertexDesc.enc v.2).length ≤ R) : + (encCall x).length ≤ 64 * (B + 1) ^ 3 + 2 * B * R := by + have h1 := length_encList_le_of_le_one hfl1 + have hv : ∀ v ∈ x.net.vertices, (encPair (encList encNat) VertexDesc.enc v).length ≤ + 2 * (B * (2 * B + 1)) + 1 + R := fun v hv => by + rw [length_encPair] + have := (length_encList_le encNat (B := B) fun i hi => length_encNat_le + ((hports v hv).2 i hi)).trans (Nat.mul_le_mul_right _ (hports v hv).1) + have := hdesc v hv + omega + have hvs := (length_encList_le _ hv).trans (Nat.mul_le_mul_right _ hV) + simp only [encCall, encParams, length_encPair, encUnary_apply, List.length_replicate, + DescribedNetwork.encDescribed, encOfEquiv, Function.Embedding.trans_apply, + Equiv.coe_toEmbedding, DescribedNetwork.equivProd, Equiv.coe_fn_mk] at hvs ⊢ + have h2 : B * (2 * (2 * (B * (2 * B + 1)) + 1 + R) + 1) = + 8 * B ^ 3 + 4 * B ^ 2 + 3 * B + 2 * B * R := by ring + rw [h2] at hvs + nlinarith + +end Cslib.Circuits.Frontier.FastContraction From c8655bc32e776b6e78559232b45062c1d1fbf01e Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:43:00 -0400 Subject: [PATCH 115/129] feat(circuit/frontier): encoded lengths of values and frames The values of the calls are reduced at their precision, and frames whose accumulator is reduced have encoded length polynomial in their numbers; the accumulator stays reduced along the children and its stored values only grow. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/FastContractionSize.lean | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean index 27609c69f2..7c11f8508e 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean @@ -147,4 +147,134 @@ theorem length_encCall_le {x : Call} {B R : ℕ} (hs : x.scale ≤ B) (hd : x.de rw [h2] at hvs nlinarith +/-! ### Values -/ + +/-- A reduced coefficient list: `e` coordinates, each at most `2 ^ p`. -/ +@[expose] def ReducedAt (p : ℕ) (fl c : List ℕ) : Prop := c.length = fl.length ∧ ∀ z ∈ c, z ≤ 2 ^ p + +theorem reducedAt_add (p : ℕ) (fl c d : List ℕ) : ReducedAt p fl (GaloisRingArith.add p fl c d) := + ⟨GaloisRingArith.length_add _ _ _ _, fun _ hz => (GaloisRingArith.lt_of_mem_add hz).le⟩ + +theorem reducedAt_mul (p : ℕ) (fl c d : List ℕ) : ReducedAt p fl (GaloisRingArith.mul p fl c d) := + ⟨GaloisRingArith.length_mul _ _ _ _, fun _ hz => (GaloisRingArith.lt_of_mem_mul hz).le⟩ + +theorem reducedAt_zero (p : ℕ) (fl : List ℕ) : ReducedAt p fl (GaloisRingArith.zero fl) := + (SumOps.galois.reduced_zero (p, fl)) + +theorem reducedAt_one (p : ℕ) (fl : List ℕ) : ReducedAt p fl (GaloisRingArith.one fl) := + (SumOps.galois.reduced_one (p, fl)) + +/-- A reduced list at precision `p` has encoded length at most `e (2 (p + 1) + 1)`. -/ +theorem length_encList_le_of_reducedAt {p : ℕ} {fl c : List ℕ} (h : ReducedAt p fl c) : + (encList encNat c).length ≤ fl.length * (2 * (p + 1) + 1) := by + rw [← h.1] + exact length_encList_le _ fun z hz => length_encNat_le_of_lt_two_pow + ((h.2 z hz).trans_lt (Nat.pow_lt_pow_right (by norm_num) (Nat.lt_succ_self p))) + +/-- **The values of the calls are reduced** at their precision, for every child builder. -/ +theorem reducedAt_value (mk : Call → ChildCursor.State → ℕ × DescribedNetwork VertexDesc) (x : Call) : + ReducedAt x.prec x.fl (value mk x) := by + cases ha : active x + · rw [value_of_not_active mk ha] + exact DescribedNetwork.reduced_sumValue (ops := SumOps.galois) (eval := VertexDesc.entry) + (x.prec, x.fl) x.net + · rw [value_of_active mk ha, finishValue, NativeRecovery.recover] + set W := table x (runLoop x fun c => value mk (child x mk c)).2.2 + have hfold : ∀ (l : List ℕ) (S : List ℕ), ReducedAt (x.prec + shift x) x.fl S → + ReducedAt (x.prec + shift x) x.fl (l.foldl (fun S k => if k = 2 ^ slots x then S else + GaloisRingArith.add (x.prec + shift x) x.fl S (NativeRecovery.nodeTerm (x.prec + shift x) x.fl + (slots x) (levels x) (step x) (shift x) (points x) k (W.getD k []))) S) := by + intro l + induction l with + | nil => exact fun S hS => hS + | cons k l ih => + intro S hS + rw [List.foldl_cons] + refine ih _ ?_ + split_ifs + · exact hS + · exact reducedAt_add _ _ _ _ + obtain ⟨hl, hz⟩ := hfold (List.range (2 ^ (slots x + 1))) _ (reducedAt_zero _ _) + refine ⟨by rw [GaloisRingArith.shiftDown, List.length_map, hl], fun z hzm => ?_⟩ + simp only [GaloisRingArith.shiftDown, List.mem_map] at hzm + obtain ⟨w, hw, rfl⟩ := hzm + have := hz w hw + rw [pow_add] at this + exact Nat.le_of_lt_succ (Nat.lt_succ_of_le (Nat.div_le_of_le_mul (by linarith))) + +/-- **The encoded length of a tagged value** of a call. -/ +theorem length_encValue_le (mk : Call → ChildCursor.State → ℕ × DescribedNetwork VertexDesc) (x : Call) : + (encValue (x.depth, value mk x)).length ≤ + 2 * x.depth + 1 + x.fl.length * (2 * (x.prec + 1) + 1) := by + rw [length_encPair, encUnary_apply, List.length_replicate] + have := length_encList_le_of_reducedAt (reducedAt_value mk x) + simp only at this ⊢ + omega + +/-! ### Frames -/ + +section Frames + +variable {ws : ℕ → List ℕ} {ps : ℕ → ℕ} {N : ℕ} {fl : List ℕ} + +/-- An accumulator whose product, sum and stored values are reduced at precision `N`. -/ +@[expose] def AccReduced (N : ℕ) (fl : List ℕ) (acc : ChildCursor.Acc (List ℕ)) : Prop := + ReducedAt N fl acc.1 ∧ ReducedAt N fl acc.2.1 ∧ ∀ y ∈ acc.2.2, ReducedAt N fl y + +/-- **The accumulator stays reduced** along the children, and its stored values only grow. -/ +theorem accReduced_runAcc (vals : ChildCursor.State → List ℕ) (l : List ChildCursor.State) + {acc : ChildCursor.Acc (List ℕ)} (h : AccReduced N fl acc) : + AccReduced N fl (ChildCursor.runAcc ws ps 64 (GaloisRingArith.mul N fl) + (GaloisRingArith.add N fl) (GaloisRingArith.one fl) (GaloisRingArith.zero fl) vals l acc) ∧ + acc.2.2.length ≤ (ChildCursor.runAcc ws ps 64 (GaloisRingArith.mul N fl) + (GaloisRingArith.add N fl) (GaloisRingArith.one fl) (GaloisRingArith.zero fl) vals l + acc).2.2.length := by + induction l generalizing acc with + | nil => exact ⟨h, le_rfl⟩ + | cons s l ih => + simp only [ChildCursor.runAcc, List.foldl_cons] at ih ⊢ + have hstep : AccReduced N fl (ChildCursor.absorbAt ws ps 64 (GaloisRingArith.mul N fl) + (GaloisRingArith.add N fl) (GaloisRingArith.one fl) (GaloisRingArith.zero fl) s acc + (vals s)) ∧ acc.2.2.length ≤ (ChildCursor.absorbAt ws ps 64 (GaloisRingArith.mul N fl) + (GaloisRingArith.add N fl) (GaloisRingArith.one fl) (GaloisRingArith.zero fl) s acc + (vals s)).2.2.length := by + unfold ChildCursor.absorbAt + split_ifs + · exact ⟨⟨reducedAt_mul _ _ _ _, h.2.1, h.2.2⟩, le_rfl⟩ + · refine ⟨⟨reducedAt_one _ _, reducedAt_zero _ _, fun y hy => ?_⟩, by simp⟩ + rcases List.mem_append.mp hy with hy | hy + · exact h.2.2 y hy + · rw [List.mem_singleton.mp hy]; exact reducedAt_add _ _ _ _ + · exact ⟨⟨reducedAt_one _ _, reducedAt_add _ _ _ _, h.2.2⟩, le_rfl⟩ + obtain ⟨h1, h2⟩ := ih hstep.1 + exact ⟨h1, hstep.2.trans h2⟩ + +/-- **The encoded length of a frame** is at most `64 (B + 1) ^ 3` when its cursor state's numbers +and exponents, the number of stored values, `N + 1` and `e` are at most `B`, and its accumulator is +reduced at precision `N`. -/ +theorem length_encFrame_le {o : Option ChildCursor.State} {acc : ChildCursor.Acc (List ℕ)} + {B : ℕ} (hst : ∀ s, o = some s → s.1 ≤ B ∧ s.2.1 ≤ B ∧ s.2.2.1.length ≤ B ∧ + (∀ b ∈ s.2.2.1, b ≤ B) ∧ s.2.2.2 ≤ B) + (hacc : AccReduced N fl acc) (hst' : acc.2.2.length ≤ B) (hN : N + 1 ≤ B) + (he : fl.length ≤ B) : (encFrame (o, acc)).length ≤ 64 * (B + 1) ^ 3 := by + have hred : ∀ c, ReducedAt N fl c → (encList encNat c).length ≤ B * (2 * B + 1) := fun c hc => + (length_encList_le_of_reducedAt hc).trans (Nat.mul_le_mul he (by omega)) + have hopt : (encOption ChildCursor.encState o).length ≤ 4 * B ^ 2 + 7 * B + 4 := by + rcases o with _ | s + · simp [encOption] + · obtain ⟨h1, h2, h3, h4, h5⟩ := hst s rfl + have hβ := (length_encList_le encNat (B := B) fun b hb => length_encNat_le (h4 b hb)).trans + (Nat.mul_le_mul_right _ h3) + simp only [encOption, Function.Embedding.coeFn_mk, Option.elim_some, List.length_cons, + ChildCursor.encState, length_encPair, encUnary_apply, List.length_replicate] + nlinarith + have hstored := (length_encList_le (encList encNat) (B := B * (2 * B + 1)) fun y hy => + hred y (hacc.2.2 y hy)).trans (Nat.mul_le_mul_right _ hst') + have h1 := hred _ hacc.1 + have h2 := hred _ hacc.2.1 + simp only [encFrame, length_encPair] + nlinarith + +end Frames + end Cslib.Circuits.Frontier.FastContraction From 7cda8e1fd5e74b5bd0f0a939f55f82ed73607954 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:46:24 -0400 Subject: [PATCH 116/129] feat(circuit/frontier): the frames of an active call are polynomially long Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/FastContractionSize.lean | 149 +++++++++++++++++- 1 file changed, 148 insertions(+), 1 deletion(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean index 7c11f8508e..1aae89f159 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean @@ -18,7 +18,11 @@ Bounds on the encoded lengths of the calls (`FastContraction.encCall`) of the na from bounds on their numbers: the length of an encoded call is at most `64 (B + 1) ^ 3 + 2 B R` when its numbers, lists and ports are at most `B` and its descriptors at most `R` long (`FastContraction.length_encCall_le`), and a bounded packet descriptor is at most -`200 (B + 1) ^ 2` long (`FastContraction.length_enc_inr_le`). +`200 (B + 1) ^ 2` long (`FastContraction.length_enc_inr_le`). The values are reduced at their +precision (`FastContraction.reducedAt_value`), and the frames of an active call with at most `m` +edges at scale `m` and precision `p` are at most `64 (B + 1) ^ 3` long for +`B = 256 (m + 1) ^ 5 (p + 1) + e` (`FastContraction.length_encFrame_frameAt_le`): the accumulator +stays reduced at the common precision and stores at most one value per node and point. ## Main results @@ -26,6 +30,10 @@ its numbers, lists and ports are at most `B` and its descriptors at most `R` lon * `Cslib.Circuits.Frontier.FastContraction.length_enc_packetCtx_le`: packet contexts. * `Cslib.Circuits.Frontier.FastContraction.length_enc_inr_le`: bounded packet descriptors. * `Cslib.Circuits.Frontier.FastContraction.length_encCall_le`: calls. +* `Cslib.Circuits.Frontier.FastContraction.reducedAt_value`, + `Cslib.Circuits.Frontier.FastContraction.length_encValue_le`: values. +* `Cslib.Circuits.Frontier.FastContraction.length_encFrame_frameAt_le`: the frames of an active + call. -/ public section @@ -277,4 +285,143 @@ theorem length_encFrame_le {o : Option ChildCursor.State} {acc : ChildCursor.Acc end Frames +/-! ### The frames of an active call -/ + +section ActiveFrames + +variable {x : Call} + +/-- The bound `256 (m + 1) ^ 5 (p + 1) + e` on the numbers of the frames of an active call at scale +`m` with precision `p`. -/ +@[expose] def frameBound (x : Call) : ℕ := 256 * (x.scale + 1) ^ 5 * (x.prec + 1) + x.fl.length + +/-- The numbers of an active call with at most `m` edges at scale `m`. -/ +theorem active_numbers (ha : active x = true) (hM : x.edges ≤ x.scale) : + (nodes x).length ≤ 2 * x.scale ∧ numPoints x ≤ 2 * x.scale ^ 2 + 1 ∧ + (packing x).2.1 ≤ x.scale ^ 2 ∧ shift x ≤ 48 * (x.scale + 1) ^ 4 ∧ + ∀ j, nodePrec x j ≤ x.prec + 40 * x.scale ^ 2 ∧ + 2 ^ (level x ((nodes x).getD j 0) + 1) ≤ 4 * x.scale := by + obtain ⟨haL, h12, hL, huL, hLs, hh⟩ := slots_facts ha + have ha1 : slots x + 1 ≤ x.scale := by omega + have hu : levels x ≤ x.scale := by omega + have hstep : step x ≤ 4 * x.scale := by omega + have h2a : 2 ^ (slots x + 1) ≤ 2 * x.scale := by rw [pow_succ]; omega + refine ⟨(length_nodes_le x).trans h2a, ?_, ?_, ?_, fun j => ⟨nodePrec_le_add ha hM j, ?_⟩⟩ + · unfold numPoints + have : x.edges * (2 * 2 ^ slots x - 1) ≤ x.scale * (2 * x.scale) := + Nat.mul_le_mul hM (by omega) + nlinarith + · calc (packing x).2.1 ≤ x.edges * (2 ^ slots x) ^ 12 := + NativeChild.numPackets_nativeParentPacking_le _ _ _ + _ ≤ x.scale * x.scale := Nat.mul_le_mul hM h12 + _ = x.scale ^ 2 := by ring + · unfold shift + have e1 : (slots x + 1) * 2 ^ (slots x + 1) * step x * levels x ≤ + x.scale * (2 * x.scale) * (4 * x.scale) * x.scale := + Nat.mul_le_mul (Nat.mul_le_mul (Nat.mul_le_mul ha1 h2a) hstep) hu + have e2 : 8 * step x * 2 ^ slots x ≤ 8 * (4 * x.scale) * x.scale := + Nat.mul_le_mul (Nat.mul_le_mul_left _ hstep) hL + have e3 : 8 * x.edges * (slots x + 1) ≤ 8 * x.scale * x.scale := + Nat.mul_le_mul (Nat.mul_le_mul_left _ hM) ha1 + have h4 : x.scale ^ 4 ≤ (x.scale + 1) ^ 4 := Nat.pow_le_pow_left (Nat.le_succ _) 4 + have h2 : x.scale ^ 2 ≤ (x.scale + 1) ^ 4 := + (Nat.pow_le_pow_left (Nat.le_succ _) 2).trans (Nat.pow_le_pow_right (by omega) (by omega)) + have e4 : x.scale * (2 * x.scale) * (4 * x.scale) * x.scale + 8 * (4 * x.scale) * x.scale + + 8 * x.scale * x.scale = 8 * x.scale ^ 4 + 40 * x.scale ^ 2 := by ring + omega + · have : level x ((nodes x).getD j 0) ≤ slots x + 1 := by + unfold level NativeRecovery.twoVal + split_ifs + · omega + · exact (List.length_filter_le _ _).trans List.length_range.le + calc 2 ^ (level x ((nodes x).getD j 0) + 1) ≤ 2 ^ (slots x + 1 + 1) := + Nat.pow_le_pow_right (by norm_num) (by omega) + _ ≤ 4 * x.scale := by rw [pow_succ]; omega + +/-- The exponent tuple of a child of an active call is a tuple of weight below its node's +precision. -/ +theorem mem_budgetTuples_of_mem_children {c : ChildCursor.State} (hc : c ∈ children x) : + c.2.2.1 ∈ BudgetTuples.budgetTuples (weights x c.1) (nodePrec x c.1) := by + simp only [children, ChildCursor.cursorList, ChildCursor.nodeBlock, ChildCursor.pointBlock, + ChildCursor.groupBlock, List.mem_flatMap, List.mem_range, List.mem_map] at hc + obtain ⟨j, -, i, -, β, hβ, g, -, rfl⟩ := hc + exact hβ + +/-- **The frames of an active call** with at most `m` edges at scale `m` have encoded length at +most `64 (B + 1) ^ 3` for `B = frameBound x`. -/ +theorem length_encFrame_frameAt_le (ha : active x = true) (hM : x.edges ≤ x.scale) + (vals : ChildCursor.State → List ℕ) (c : ℕ) : + (encFrame (frameAt x vals c)).length ≤ 64 * (frameBound x + 1) ^ 3 := by + obtain ⟨hT, hn, hP, hsh, hj⟩ := active_numbers ha hM + have hM1 := one_le_edges_of_active ha + set m := x.scale with hm + set p := x.prec with hp + have hBf : 256 * ((m + 1) ^ 5 * (p + 1)) ≤ frameBound x := by + rw [← Nat.mul_assoc]; exact Nat.le_add_right _ _ + have hm5 : 1 ≤ (m + 1) ^ 5 := Nat.one_le_pow _ _ (by omega) + have hm1 : m + 1 ≤ (m + 1) ^ 5 := Nat.le_self_pow (by norm_num) _ + have hm2 : (m + 1) ^ 2 ≤ (m + 1) ^ 5 := Nat.pow_le_pow_right (by omega) (by norm_num) + have hm3 : (m + 1) ^ 3 ≤ (m + 1) ^ 5 := Nat.pow_le_pow_right (by omega) (by norm_num) + have hm4 : (m + 1) ^ 4 ≤ (m + 1) ^ 5 := Nat.pow_le_pow_right (by omega) (by norm_num) + have hsq : m ^ 2 ≤ (m + 1) ^ 2 := Nat.pow_le_pow_left (Nat.le_succ _) 2 + have hcu : m ^ 3 ≤ (m + 1) ^ 3 := Nat.pow_le_pow_left (Nat.le_succ _) 3 + have hP5 : (m + 1) ^ 5 ≤ (m + 1) ^ 5 * (p + 1) := Nat.le_mul_of_pos_right _ (by omega) + have hpq : p + 1 ≤ (m + 1) ^ 5 * (p + 1) := Nat.le_mul_of_pos_left _ (by omega) + -- The stored values. + obtain ⟨qs, hqs, hrun⟩ := ChildCursor.runAcc_cursorList (ws := weights x) (ps := nodePrec x) + (T := (nodes x).length) (n := numPoints x) (r := 64) (GaloisRingArith.mul (wide x) x.fl) + (GaloisRingArith.add (wide x) x.fl) (GaloisRingArith.one x.fl) (GaloisRingArith.zero x.fl) + (GaloisRingArith.val (wide x) x.fl) (fun a b => GaloisRingArith.val_mul a b) + (fun a b => GaloisRingArith.val_add a b) GaloisRingArith.val_one GaloisRingArith.val_zero + (by norm_num) (nodePrec_pos hM1) weights_pos vals + have hqlen : qs.length = (nodes x).length * numPoints x := by + have := congrArg List.length hqs + simpa [List.length_flatMap, List.sum_replicate] using this + have hinit : AccReduced (wide x) x.fl (GaloisRingArith.one x.fl, GaloisRingArith.zero x.fl, []) := + ⟨reducedAt_one _ _, reducedAt_zero _ _, by simp⟩ + have hpre := accReduced_runAcc (ws := weights x) (ps := nodePrec x) vals + ((children x).take c) hinit + have hsuf := accReduced_runAcc (ws := weights x) (ps := nodePrec x) vals + ((children x).drop c) hpre.1 + have hfull : ChildCursor.runAcc (weights x) (nodePrec x) 64 + (GaloisRingArith.mul (wide x) x.fl) (GaloisRingArith.add (wide x) x.fl) + (GaloisRingArith.one x.fl) (GaloisRingArith.zero x.fl) vals (children x) + (GaloisRingArith.one x.fl, GaloisRingArith.zero x.fl, []) = + (GaloisRingArith.one x.fl, GaloisRingArith.zero x.fl, qs) := by + rw [children]; exact hrun + rw [← ChildCursor.runAcc_append, List.take_append_drop, hfull] at hsuf + have hstored : (ChildCursor.runAcc (weights x) (nodePrec x) 64 + (GaloisRingArith.mul (wide x) x.fl) (GaloisRingArith.add (wide x) x.fl) + (GaloisRingArith.one x.fl) (GaloisRingArith.zero x.fl) vals ((children x).take c) + (GaloisRingArith.one x.fl, GaloisRingArith.zero x.fl, [])).2.2.length ≤ frameBound x := by + refine hsuf.2.trans ?_ + rw [hqlen] + have : (nodes x).length * numPoints x ≤ 2 * m * (2 * m ^ 2 + 1) := Nat.mul_le_mul hT hn + have : 2 * m * (2 * m ^ 2 + 1) = 4 * m ^ 3 + 2 * m := by ring + omega + refine length_encFrame_le (N := wide x) (fl := x.fl) (fun s hs => ?_) hpre.1 hstored ?_ + (Nat.le_add_left _ _) + · have hsm : s ∈ children x := List.mem_of_getElem? hs + obtain ⟨hj1, hi1, hg1⟩ := lt_of_mem_children hsm + have hβ := mem_budgetTuples_of_mem_children hsm + have hlen := ((BudgetTuples.mem_budgetTuples (weights_pos _)).mp hβ).1 + obtain ⟨hpj, hlj⟩ := hj s.1 + refine ⟨by omega, by omega, ?_, fun b hb => ?_, by omega⟩ + · rw [hlen, weights, NativeChild.modeWeights, List.length_map, List.length_range] + have h1 : (packing x).2.1 * (2 ^ (level x ((nodes x).getD s.1 0) + 1) * nodePrec x s.1) ≤ + m ^ 2 * (4 * m * (p + 40 * m ^ 2)) := + Nat.mul_le_mul hP (Nat.mul_le_mul hlj hpj) + have h2 : m ^ 2 * (4 * m * (p + 40 * m ^ 2)) = 4 * (m ^ 3 * p) + 160 * m ^ 5 := by ring + have h3 : m ^ 5 ≤ (m + 1) ^ 5 := Nat.pow_le_pow_left (Nat.le_succ _) 5 + have h4 : m ^ 3 * p ≤ (m + 1) ^ 5 * (p + 1) := + Nat.mul_le_mul (hcu.trans hm3) (Nat.le_succ _) + omega + · have := lt_of_mem_budgetTuples (weights_pos _) hβ b hb + have : m ^ 2 ≤ (m + 1) ^ 5 := hsq.trans hm2 + omega + · unfold wide + omega + +end ActiveFrames + end Cslib.Circuits.Frontier.FastContraction From f1ce1dc8545b1e94778490c6e0c5f4e1eaacba7c Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:46:33 -0400 Subject: [PATCH 117/129] refactor(circuit/frontier): packetShape_child through nativeMk_eq, for the capped builder of #1982 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean index b903a525a1..785b26d056 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionLeaf.lean @@ -497,7 +497,8 @@ theorem packetShape_child {x : Call} (hx : WF x) (ha : active x = true) (hs : Pa -- The builder at a cursor state of a well-formed active call: the uncapped native child. have hmk : (nativeMk x c).2 = nativeChild x.fl (slots x) (level x ((nodes x).getD c.1 0)) (nodePrec x c.1) (node (slots x) ((nodes x).getD c.1 0)) ((points x).getD c.2.1 []) x.rows - x.net (nativeParentPacking x.rows (slots x) x.net) c.2.2.1 c.2.2.2 := rfl + x.net (nativeParentPacking x.rows (slots x) x.net) c.2.2.1 c.2.2.2 := by + rw [nativeMk_eq hx ha hc] obtain ⟨-, hD⟩ := hx obtain ⟨hj, -, hg⟩ := lt_of_mem_children hc have ha' := ha From 40d120d90b6740633aa3df563d606f41d2ebf5a9 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:46:46 -0400 Subject: [PATCH 118/129] style: wrap long lines Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Frontier/TensorNetwork/FastContractionSize.lean | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean index 1aae89f159..f15f2cf689 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean @@ -180,7 +180,8 @@ theorem length_encList_le_of_reducedAt {p : ℕ} {fl c : List ℕ} (h : ReducedA ((h.2 z hz).trans_lt (Nat.pow_lt_pow_right (by norm_num) (Nat.lt_succ_self p))) /-- **The values of the calls are reduced** at their precision, for every child builder. -/ -theorem reducedAt_value (mk : Call → ChildCursor.State → ℕ × DescribedNetwork VertexDesc) (x : Call) : +theorem reducedAt_value (mk : Call → ChildCursor.State → ℕ × DescribedNetwork VertexDesc) + (x : Call) : ReducedAt x.prec x.fl (value mk x) := by cases ha : active x · rw [value_of_not_active mk ha] @@ -190,7 +191,8 @@ theorem reducedAt_value (mk : Call → ChildCursor.State → ℕ × DescribedNet set W := table x (runLoop x fun c => value mk (child x mk c)).2.2 have hfold : ∀ (l : List ℕ) (S : List ℕ), ReducedAt (x.prec + shift x) x.fl S → ReducedAt (x.prec + shift x) x.fl (l.foldl (fun S k => if k = 2 ^ slots x then S else - GaloisRingArith.add (x.prec + shift x) x.fl S (NativeRecovery.nodeTerm (x.prec + shift x) x.fl + GaloisRingArith.add (x.prec + shift x) x.fl S + (NativeRecovery.nodeTerm (x.prec + shift x) x.fl (slots x) (levels x) (step x) (shift x) (points x) k (W.getD k []))) S) := by intro l induction l with @@ -211,7 +213,8 @@ theorem reducedAt_value (mk : Call → ChildCursor.State → ℕ × DescribedNet exact Nat.le_of_lt_succ (Nat.lt_succ_of_le (Nat.div_le_of_le_mul (by linarith))) /-- **The encoded length of a tagged value** of a call. -/ -theorem length_encValue_le (mk : Call → ChildCursor.State → ℕ × DescribedNetwork VertexDesc) (x : Call) : +theorem length_encValue_le (mk : Call → ChildCursor.State → ℕ × DescribedNetwork VertexDesc) + (x : Call) : (encValue (x.depth, value mk x)).length ≤ 2 * x.depth + 1 + x.fl.length * (2 * (x.prec + 1) + 1) := by rw [length_encPair, encUnary_apply, List.length_replicate] From 9aabdcd63c7f445943c341c6429d2b162e00e5d9 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:50:55 -0400 Subject: [PATCH 119/129] feat(circuit/frontier): the sizes of the calls, values and frames along the recursion Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../TensorNetwork/FastContractionSize.lean | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean index f15f2cf689..c85bda0940 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean @@ -427,4 +427,103 @@ theorem length_encFrame_frameAt_le (ha : active x = true) (hM : x.edges ≤ x.sc end ActiveFrames +/-! ### The sizes along the recursion -/ + +section Sizes + +variable {x₀ x : Call} + +/-- **The sizes of a call of a given shape**: from a root `x₀` at depth `0`, at scale its number +`m` of edges, with bound `m + 1`, a 0/1 modulus and descriptors at most `200 (Z + 1) ^ 2` long, a +call of its shape, its tagged value and, if it is active, its frames are at most +`1000 (Z + 1) ^ 3` long, once `Z` dominates `256 (m + 1) ^ 5 (P + 1) + e` for the precision bound +`P = p₀ + 40 m² (m + 1)` and the vertices, rows and their growth. -/ +theorem Shape.length_le (h₀s : x₀.scale = x₀.edges) (h₀b : x₀.bound = x₀.edges + 1) + (h₀d : x₀.depth = 0) (h : Shape x₀ x) {Z : ℕ} + (hZ1 : 256 * (x₀.edges + 1) ^ 5 * (x₀.prec + (x₀.edges + 1) * (40 * x₀.edges ^ 2) + 1) + + x₀.fl.length ≤ Z) + (hZ2 : x₀.net.vertices.length + (x₀.edges + 1) * x₀.edges ^ 2 + (encNat x₀.rows).length ≤ Z) + (hfl1 : ∀ z ∈ x₀.fl, z ≤ 1) + (hR₀ : ∀ w ∈ x₀.net.vertices, (VertexDesc.enc w.2).length ≤ 200 * (Z + 1) ^ 2) : + (encCall x).length ≤ 1000 * (Z + 1) ^ 3 ∧ + (encValue (x.depth, value nativeMk x)).length ≤ 1000 * (Z + 1) ^ 3 ∧ + (active x = true → ∀ vals c, + (encPair encCall encFrame (x, frameAt x vals c)).length ≤ 1000 * (Z + 1) ^ 3) := by + set m := x₀.edges with hm + set P := x₀.prec + (m + 1) * (40 * m ^ 2) with hP + -- The numbers of `x`. + have hbd := h.bound_add_depth_le + rw [h₀b, h₀d] at hbd + have hd : x.depth ≤ m + 1 := by omega + have hprec : x.prec ≤ P := h.prec_le.trans (Nat.add_le_add_left + (Nat.mul_le_mul_right _ hd) _) + have hV : x.net.vertices.length ≤ x₀.net.vertices.length + (m + 1) * m ^ 2 := + h.vertices_le.trans (Nat.add_le_add_left (Nat.mul_le_mul_right _ hd) _) + have hm5 : m + 1 ≤ (m + 1) ^ 5 := Nat.le_self_pow (by norm_num) _ + have hP1 : P + 1 ≤ (m + 1) ^ 5 * (P + 1) := Nat.le_mul_of_pos_left _ (by positivity) + have hmP : 2 * (m + 1) * (P + 1) ≤ 256 * (m + 1) ^ 5 * (P + 1) := by + have : (m + 1) * (P + 1) ≤ (m + 1) ^ 5 * (P + 1) := Nat.mul_le_mul_right _ hm5 + nlinarith + have hmZ : 2 * (m + 1) ≤ Z := by + have : 2 * (m + 1) ≤ 2 * (m + 1) * (P + 1) := Nat.le_mul_of_pos_right _ (by omega) + omega + have hPZ : P + 1 ≤ Z := by + have : P + 1 ≤ 2 * (m + 1) * (P + 1) := Nat.le_mul_of_pos_left _ (by omega) + omega + have hflZ : x₀.fl.length ≤ Z := by omega + have hsc : x.scale = m := h.scale_eq.trans h₀s + have hE : x.edges ≤ 2 * m := h.edges_le + have hports : ∀ v ∈ x.net.vertices, v.1.length ≤ Z ∧ ∀ i ∈ v.1, i ≤ Z := fun v hv => + ⟨(length_le_edges_of_wf h.wf hv).trans (hE.trans (by omega)), + fun i hi => (h.wf.2.1 v hv i hi).le.trans (hE.trans (by omega))⟩ + have hR : ∀ v ∈ x.net.vertices, (VertexDesc.enc v.2).length ≤ 200 * (Z + 1) ^ 2 := by + intro v hv + rcases h.desc v hv with ⟨w, hw, hvw⟩ | ⟨d, hvd, hb⟩ + · rw [hvw]; exact hR₀ w hw + · rw [hvd] + refine length_enc_inr_le (hb.mono (Nat.add_le_add_left (Nat.mul_le_mul_right _ hd) _)) + (show 2 * (m + 1) * (P + 1) ≤ Z by omega) hflZ hfl1 (hports v hv).1 + have hrows : (encNat x.rows).length ≤ Z := by + rcases h.rows_le with rfl | hr + · omega + · exact (length_encNat_le (hr.trans (Nat.add_le_add_left + (Nat.mul_le_mul_right _ hd) _))).trans + (show x₀.net.vertices.length + (m + 1) * m ^ 2 ≤ Z by omega) + have hcall := length_encCall_le (B := Z) (R := 200 * (Z + 1) ^ 2) (x := x) + (by rw [hsc]; omega) (hd.trans (by omega)) (by omega) (hprec.trans (by omega)) + (h.fl_eq ▸ hflZ) (h.fl_eq ▸ hfl1) hrows (hE.trans (by omega)) (hV.trans (by omega)) hports hR + have hcall' : (encCall x).length ≤ 464 * (Z + 1) ^ 3 := by + have : 2 * Z * (200 * (Z + 1) ^ 2) ≤ 400 * (Z + 1) ^ 3 := by + have : Z ≤ Z + 1 := Nat.le_succ _ + calc 2 * Z * (200 * (Z + 1) ^ 2) = 400 * (Z * (Z + 1) ^ 2) := by ring + _ ≤ 400 * ((Z + 1) * (Z + 1) ^ 2) := Nat.mul_le_mul_left _ (Nat.mul_le_mul_right _ this) + _ = 400 * (Z + 1) ^ 3 := by ring + omega + have hZ3 : (Z + 1) ^ 2 ≤ (Z + 1) ^ 3 := Nat.pow_le_pow_right (by omega) (by norm_num) + have hZ1' : Z + 1 ≤ (Z + 1) ^ 3 := Nat.le_self_pow (by norm_num) _ + refine ⟨hcall'.trans (by omega), ?_, fun ha vals c => ?_⟩ + · have hv := length_encValue_le nativeMk x + rw [h.fl_eq] at hv + have : x₀.fl.length * (2 * (x.prec + 1) + 1) ≤ Z * (2 * Z + 1) := + Nat.mul_le_mul hflZ (by omega) + have : Z * (2 * Z + 1) ≤ 3 * (Z + 1) ^ 2 := by nlinarith + omega + · have hM : x.edges ≤ x.scale := by + have := edges_lt_bound_of_active ha + omega + have hf := length_encFrame_frameAt_le ha hM vals c + have hfb : frameBound x ≤ Z := by + unfold frameBound + rw [hsc, h.fl_eq] + have : 256 * (m + 1) ^ 5 * (x.prec + 1) ≤ 256 * (m + 1) ^ 5 * (P + 1) := + Nat.mul_le_mul_left _ (by omega) + omega + have hfZ : (frameBound x + 1) ^ 3 ≤ (Z + 1) ^ 3 := Nat.pow_le_pow_left (by omega) 3 + have hl := length_encPair (enc₁ := encCall) (enc₂ := encFrame) (x, frameAt x vals c) + simp only at hl + rw [hl] + omega + +end Sizes + end Cslib.Circuits.Frontier.FastContraction From fb4271c32fbd877dbbf2cb5d5d6111c72188aee9 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:53:56 -0400 Subject: [PATCH 120/129] feat(Classes/CountingSimulation): polynomial sizes along the native recursion FastSizes is proved: every call reached from a guarded root, its frames and its tagged value are at most 2^55 (n+1)^24 long. So FastAnalysis nativeMk now needs only the leaf bounds (fastAnalysis_nativeMk). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../CountingSimulation/FastAnalysis.lean | 166 ++++++++++++++++-- 1 file changed, 151 insertions(+), 15 deletions(-) diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean index 1f7cefdec0..3e5fee1e48 100644 --- a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean @@ -9,6 +9,8 @@ module public import Complexity.Computability.Complexity.Classes.CountingSimulation.FastAlgorithm public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionSchedule +public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContractionSize +import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Length /-! # The resource analysis of the native recursion: the recursion tree @@ -160,20 +162,20 @@ theorem numEdges_lt_length_encInput (P : ℕ) (G : ℕ × ExplicitTensorNetwork /-! ### The analysis from the sizes and the leaves -/ -/-- **Polynomial sizes along the recursion**: for all constants `C` and `D` there is `K` such that -on every guarded input admissible at size `s ≥ 2` with constant `C` and with precision +/-- **Polynomial sizes along the recursion**: for all constants `C` and `D` there are `c` and `d` +such that on every guarded input admissible at size `s ≥ 2` with constant `C` and with precision `1 ≤ P ≤ D s`, every call reached from the root, its frames and its value are at most -`K (n + 1) ^ K` long, for the input length `n`. -/ +`c (n + 1) ^ d` long, for the input length `n`. -/ @[expose] def FastSizes : Prop := - ∀ C D : ℕ, ∃ K : ℕ, ∀ (s P : ℕ) (G : ℕ × ExplicitTensorNetwork ℕ), 2 ≤ s → 1 ≤ P → + ∀ C D : ℕ, ∃ c d : ℕ, ∀ (s P : ℕ) (G : ℕ × ExplicitTensorNetwork ℕ), 2 ≤ s → 1 ≤ P → P ≤ D * s → ExplicitAdmissible C s G → fastGuard G = true → ∀ x, Reach nativeMk (root (ExplicitTensorNetwork.capPrecision (P, G))) x → - (encCall x).length ≤ K * ((FastContraction.encInput (P, G)).length + 1) ^ K ∧ + (encCall x).length ≤ c * ((FastContraction.encInput (P, G)).length + 1) ^ d ∧ (∀ a, (reachInv nativeMk (root (ExplicitTensorNetwork.capPrecision (P, G)))).frame x a → (encPair encCall encFrame (x, a)).length ≤ - K * ((FastContraction.encInput (P, G)).length + 1) ^ K) ∧ + c * ((FastContraction.encInput (P, G)).length + 1) ^ d) ∧ (encValue (x.depth, value nativeMk x)).length ≤ - K * ((FastContraction.encInput (P, G)).length + 1) ^ K + c * ((FastContraction.encInput (P, G)).length + 1) ^ d /-- **The leaf bounds**: for all constants `C` and `D` there is `K` such that on every guarded input admissible at size `s ≥ 2` with constant `C` and with precision `1 ≤ P ≤ D s`, direct @@ -199,10 +201,10 @@ and the leaf bounds. The derivation of the root over the reached calls has at mo levels, and, from some size on, its nodes fit in the clock. -/ theorem fastAnalysis_nativeMk_of (hS : FastSizes) (hL : FastLeaves) : FastAnalysis nativeMk := by intro C D - obtain ⟨KS, hKS⟩ := hS C D + obtain ⟨cS, dS, hKS⟩ := hS C D obtain ⟨KL, hKL⟩ := hL C D obtain ⟨sA, hsA⟩ := exists_activeBounds_le D - refine ⟨KS + KL + 1, max sA (D + 2), fun s P G hs hP hPD hA hg => ?_⟩ + refine ⟨cS + dS + KL + 1, max sA (D + 2), fun s P G hs hP hPD hA hg => ?_⟩ set q := ExplicitTensorNetwork.capPrecision (P, G) with hq set x₀ := root q with hx₀ set m := G.2.numEdges with hm @@ -233,9 +235,9 @@ theorem fastAnalysis_nativeMk_of (hS : FastSizes) (hL : FastLeaves) : FastAnalys -- The bounds. obtain hKS' := hKS s P G hs hP hPD hA hg obtain hKL' := hKL s P G hs hP hPD hA hg - set K := KS + KL + 1 + set K := cS + dS + KL + 1 have hB1 : 1 ≤ n + 1 := by omega - refine ⟨reachInv nativeMk x₀, N, H, KS * (n + 1) ^ KS, KL * (n + 1) ^ KL * 2 ^ (KL * s / + refine ⟨reachInv nativeMk x₀, N, H, cS * (n + 1) ^ dS, KL * (n + 1) ^ KL * 2 ^ (KL * s / Nat.sqrt (Nat.log 2 s)), KL * (n + 1) ^ KL, he, hN, fun x hx => ⟨(hKS' x hx).1, fun hl => ?_⟩, fun x a ha => (hKS' x ha.1).2.1 a ha, fun v ⟨x, hx, hv⟩ => hv ▸ (hKS' x hx).2.2, ?_, ?_⟩ · have hl' : active x = false := by simpa [scheme] using hl @@ -243,11 +245,14 @@ theorem fastAnalysis_nativeMk_of (hS : FastSizes) (hL : FastLeaves) : FastAnalys · have h1 : H ≤ 1 * (n + 1) ^ K := by have : n + 1 ≤ (n + 1) ^ K := Nat.le_self_pow (by omega) _ omega - have h2 := mul_pow_le_mul_pow (a := KS) hB1 (show KS ≤ K by omega) + have h2 : cS * (n + 1) ^ dS ≤ cS * (n + 1) ^ K := + Nat.mul_le_mul_left _ (Nat.pow_le_pow_right hB1 (by omega)) have h3 := mul_pow_le_mul_pow (a := KL) hB1 (show KL ≤ K by omega) - calc H + KS * (n + 1) ^ KS + KL * (n + 1) ^ KL - ≤ 1 * (n + 1) ^ K + KS * (n + 1) ^ K + KL * (n + 1) ^ K := by omega - _ = K * (n + 1) ^ K := by ring + calc H + cS * (n + 1) ^ dS + KL * (n + 1) ^ KL + ≤ 1 * (n + 1) ^ K + cS * (n + 1) ^ K + KL * (n + 1) ^ K := by omega + _ ≤ K * (n + 1) ^ K := by + rw [← add_mul, ← add_mul] + exact Nat.mul_le_mul_right _ (by omega) · refine Nat.mul_le_mul (mul_pow_le_mul_pow' hB1 (show KL ≤ K by omega)) (Nat.pow_le_pow_right (by norm_num) (Nat.div_le_div_right (Nat.mul_le_mul_right _ (by omega)))) @@ -256,4 +261,135 @@ where mul_pow_le_mul_pow' {a B K : ℕ} (hB : 1 ≤ B) (ha : a ≤ K) : a * B ^ a ≤ K * B ^ K := Nat.mul_le_mul ha (Nat.pow_le_pow_right hB ha) +/-! ### The sizes -/ + +/-- The modulus has at most `32 (m + 1) ^ 2` coefficients. -/ +theorem length_modulus_le (m : ℕ) : (modulus m).length ≤ 32 * (m + 1) ^ 2 := by + rw [length_modulus] + set t := modulusExp m + have h1 : 2 ^ t ≤ 4 * Nat.log 2 m + 2 := Nat.pow_log_le_self 2 (by omega) + have h2 : Nat.log 2 m ≤ m := by + rcases Nat.eq_zero_or_pos m with h | h + · simp [h] + · exact (Nat.log_lt_self 2 h.ne').le + have h3 : 3 ^ t ≤ (2 ^ t) ^ 2 := by + rw [← pow_mul, mul_comm, pow_mul] + exact Nat.pow_le_pow_left (by norm_num) t + have h4 : (2 ^ t) ^ 2 ≤ (4 * (m + 1)) ^ 2 := Nat.pow_le_pow_left (by omega) 2 + nlinarith + +/-- The coefficients of the modulus are `0` or `1`. -/ +theorem le_one_of_mem_modulus {m z : ℕ} (hz : z ∈ modulus m) : z ≤ 1 := by + simp only [modulus, List.mem_map, List.mem_range] at hz + obtain ⟨i, -, rfl⟩ := hz + split_ifs <;> omega + +/-- The encoded network is at most the encoded input long. -/ +theorem length_encExplicitNetwork_le (P : ℕ) (G : ℕ × ExplicitTensorNetwork ℕ) : + (ExplicitTensorNetwork.encExplicitNetwork G.2).length ≤ + (FastContraction.encInput (P, G)).length := by + simp only [FastContraction.encInput, length_encPair] + omega + +/-- The vertices of an explicit network, encoded, are at most the encoded network long. -/ +theorem length_vertices_le (G : ExplicitTensorNetwork ℕ) : + G.vertices.length ≤ (ExplicitTensorNetwork.encExplicitNetwork G).length ∧ + ∀ v ∈ G.vertices, (encList encNat v.2).length + 1 ≤ + (ExplicitTensorNetwork.encExplicitNetwork G).length := by + have hl : (ExplicitTensorNetwork.encExplicitNetwork G).length = 2 * G.numEdges + 1 + + (G.vertices.map fun v => 2 * (encPair (encList encNat) (encList encNat) v).length + 1).sum := by + simp only [ExplicitTensorNetwork.encExplicitNetwork, encOfEquiv, Function.Embedding.trans_apply, + Equiv.coe_toEmbedding, DescribedNetwork.equivProd, Equiv.coe_fn_mk, length_encPair, + encUnary_apply, List.length_replicate, length_encList] + refine ⟨?_, fun v hv => ?_⟩ + · have := List.length_le_sum_of_one_le (G.vertices.map fun v => + 2 * (encPair (encList encNat) (encList encNat) v).length + 1) (fun i hi => by + obtain ⟨v, -, rfl⟩ := List.mem_map.mp hi + omega) + rw [List.length_map] at this + omega + · have := List.le_sum_of_mem (List.mem_map_of_mem (f := fun v => + 2 * (encPair (encList encNat) (encList encNat) v).length + 1) hv) + have h2 := length_encPair (enc₁ := encList encNat) (enc₂ := encList encNat) v + omega + +/-- **Polynomial sizes along the recursion**: every call reached from the root of a guarded input, +its frames and its tagged value are at most `2 ^ 55 (n + 1) ^ 24` long. -/ +theorem fastSizes : FastSizes := by + intro C D + refine ⟨2 ^ 55, 24, fun s P G _ _ _ _ hg x hx => ?_⟩ + set q := ExplicitTensorNetwork.capPrecision (P, G) with hq + set x₀ := root q with hx₀ + set m := G.2.numEdges with hm + set n := (FastContraction.encInput (P, G)).length with hn + set Y := n + 1 with hY + have hmn : m < n := numEdges_lt_length_encInput P G + have hnet := length_encExplicitNetwork_le P G + obtain ⟨hV, hdesc⟩ := length_vertices_le G.2 + have hp₀ : x₀.prec ≤ n := (min_le_left _ _).trans hnet + have hrows : (encNat x₀.rows).length ≤ n := by + change (encNat G.1).length ≤ n + simp only [hn, FastContraction.encInput, length_encPair] + omega + have hV₀ : x₀.net.vertices.length ≤ n := by + change (List.map _ G.2.vertices).length ≤ n + rw [List.length_map]; omega + have hsh := Reach.shape (x₀ := x₀) rfl rfl (wf_root P hg) hx + set Z := 2 ^ 14 * Y ^ 8 with hZ + have hY1 : 1 ≤ Y := by omega + have hmY : m + 1 ≤ Y := by omega + have hY3 : Y ≤ Y ^ 3 := Nat.le_self_pow (by norm_num) _ + have hY8 : Y ^ 3 ≤ Y ^ 8 := Nat.pow_le_pow_right hY1 (by norm_num) + have hY2 : Y ^ 2 ≤ Y ^ 3 := Nat.pow_le_pow_right hY1 (by norm_num) + have hm2 : m ^ 2 ≤ Y ^ 2 := Nat.pow_le_pow_left (by omega) 2 + have hm5 : (m + 1) ^ 5 ≤ Y ^ 5 := Nat.pow_le_pow_left hmY 5 + have hPm : x₀.prec + (x₀.edges + 1) * (40 * x₀.edges ^ 2) + 1 ≤ 41 * Y ^ 3 := by + change x₀.prec + (m + 1) * (40 * m ^ 2) + 1 ≤ 41 * Y ^ 3 + have : (m + 1) * (40 * m ^ 2) ≤ Y * (40 * Y ^ 2) := Nat.mul_le_mul hmY (by omega) + have : Y * (40 * Y ^ 2) = 40 * Y ^ 3 := by ring + omega + have hZ1 : 256 * (x₀.edges + 1) ^ 5 * (x₀.prec + (x₀.edges + 1) * (40 * x₀.edges ^ 2) + 1) + + x₀.fl.length ≤ Z := by + have h1 : 256 * (x₀.edges + 1) ^ 5 * (x₀.prec + (x₀.edges + 1) * (40 * x₀.edges ^ 2) + 1) ≤ + 256 * Y ^ 5 * (41 * Y ^ 3) := Nat.mul_le_mul (Nat.mul_le_mul_left _ hm5) hPm + have h2 : 256 * Y ^ 5 * (41 * Y ^ 3) = 10496 * Y ^ 8 := by ring + have h3 : x₀.fl.length ≤ 32 * Y ^ 8 := (length_modulus_le m).trans + (by have := Nat.pow_le_pow_left hmY 2; omega) + omega + have hZ2 : x₀.net.vertices.length + (x₀.edges + 1) * x₀.edges ^ 2 + (encNat x₀.rows).length ≤ + Z := by + have : (x₀.edges + 1) * x₀.edges ^ 2 ≤ Y * Y ^ 2 := Nat.mul_le_mul hmY hm2 + have : Y * Y ^ 2 = Y ^ 3 := by ring + omega + have hR₀ : ∀ w ∈ x₀.net.vertices, (VertexDesc.enc w.2).length ≤ 200 * (Z + 1) ^ 2 := by + intro w hw + change w ∈ List.map _ G.2.vertices at hw + obtain ⟨v, hv, rfl⟩ := List.mem_map.mp hw + rw [length_encSum] + simp only [Sum.elim_inl] + have := hdesc v hv + have : Z ≤ (Z + 1) ^ 2 := (Nat.le_succ Z).trans (Nat.le_self_pow (by norm_num) _) + omega + obtain ⟨hc, hv, hf⟩ := hsh.length_le rfl rfl rfl hZ1 hZ2 + (fun z hz => le_one_of_mem_modulus hz) hR₀ + -- `1000 (Z + 1) ^ 3 ≤ 2 ^ 55 (n + 1) ^ 24`. + have hfin : 1000 * (Z + 1) ^ 3 ≤ 2 ^ 55 * Y ^ 24 := by + have h1 : Z + 1 ≤ 2 ^ 15 * Y ^ 8 := by + have : 1 ≤ Y ^ 8 := Nat.one_le_pow _ _ hY1 + omega + have h2 : (Z + 1) ^ 3 ≤ (2 ^ 15 * Y ^ 8) ^ 3 := Nat.pow_le_pow_left h1 3 + have h3 : (2 ^ 15 * Y ^ 8) ^ 3 = 2 ^ 45 * Y ^ 24 := by ring + rw [h3] at h2 + have h4 : 1000 * (2 ^ 45 * Y ^ 24) ≤ 2 ^ 55 * Y ^ 24 := by + rw [← Nat.mul_assoc] + exact Nat.mul_le_mul_right _ (by norm_num) + exact (Nat.mul_le_mul_left 1000 h2).trans h4 + refine ⟨hc.trans hfin, fun a ⟨_, ha, c, _, hac⟩ => ?_, hv.trans hfin⟩ + rw [hac] + exact (hf ha _ c).trans hfin + +/-- **The resource analysis of the native recursion**, given the leaf bounds. -/ +theorem fastAnalysis_nativeMk (hL : FastLeaves) : FastAnalysis nativeMk := + fastAnalysis_nativeMk_of fastSizes hL + end Cslib.Complexity.CountingSimulation From 08ac4e7e7f7bbd7a336a28e16156c97a28b028ee Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:54:57 -0400 Subject: [PATCH 121/129] feat(Classes/CountingSimulation): the facts at the reached calls for the leaf bounds Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../CountingSimulation/FastAnalysis.lean | 95 ++++++++++++++++--- 1 file changed, 80 insertions(+), 15 deletions(-) diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean index 3e5fee1e48..9e69bf87bf 100644 --- a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAnalysis.lean @@ -13,29 +13,33 @@ public import Complexity.Computability.Circuit.Frontier.TensorNetwork.FastContra import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Length /-! -# The resource analysis of the native recursion: the recursion tree +# The resource analysis of the native recursion: the recursion tree and the sizes The resource analysis `CountingSimulation.FastAnalysis` of the native recursion (`FastContraction.nativeMk`) of Theorem D.1 of [OpenAI2026ETH] (Appendix D.4.7, p. 64), reduced to -two bounds about single calls (`CountingSimulation.fastAnalysis_nativeMk_of`): +the leaf bounds (`CountingSimulation.fastAnalysis_nativeMk`): direct summation at a reached leaf +takes time `poly(n) 2 ^ O(s / √⌊log₂ s⌋)` and polynomial space (`CountingSimulation.FastLeaves`). -* **sizes** (`CountingSimulation.FastSizes`): the calls reached from the root, their frames and - their values are polynomially long in the input; -* **leaves** (`CountingSimulation.FastLeaves`): direct summation at a reached leaf takes time - `poly(n) 2 ^ O(s / √⌊log₂ s⌋)` and polynomial space. +The rest of the analysis is proved here: -The rest of the analysis is proved here: the derivation of the root over the reached calls -(`FastContraction.reachInv`), with at most `m + 1` stack levels for every input -(`FastContraction.exists_eval_reachInv`), and, from some size on, with at most -`2 ^ ((245760 C' + 37) m / √⌊log₂ m⌋)` nodes for `C' = ⌊p / m⌋ + 1` -(`FastContraction.exists_eval_onSchedule`), which fit in the clock -(`CountingSimulation.two_mul_sub_one_le_fuel`). The clock's fixed constant serves every precision -because the call count's exponent is linear in `C'`. +* **the derivation** of the root over the reached calls (`FastContraction.reachInv`), with at most + `m + 1` stack levels for every input (`FastContraction.exists_eval_reachInv`), and, from some size + on, with at most `2 ^ ((245760 C' + 37) m / √⌊log₂ m⌋)` nodes for `C' = ⌊p / m⌋ + 1` + (`FastContraction.exists_eval_onSchedule`), which fit in the clock + (`CountingSimulation.two_mul_sub_one_le_fuel`). The clock's fixed constant serves every precision + because the call count's exponent is linear in `C'`; +* **the sizes** (`CountingSimulation.fastSizes`): the reached calls, their frames and their values + are at most `2 ^ 55 (n + 1) ^ 24` long, from the structural shapes of the calls + (`FastContraction.Reach.shape`) and the encoded lengths of `FastContraction.Shape.length_le`. + +`CountingSimulation.reach_facts` collects what the leaf bounds may use about a reached call: its +scale `m` with `s ≤ m ≤ C s`, at most `2 m` edges, and, from some size on, precision at most `m ^ 4` +and at most `leafSize m` edges at a leaf. ## Main definitions * `Cslib.Complexity.CountingSimulation.FastSizes`: polynomial sizes along the recursion. -* `Cslib.Complexity.CountingSimulation.FastLeaves`: the leaf bounds. +* `Cslib.Complexity.CountingSimulation.FastLeaves`: the leaf bounds, the remaining obligation. ## Main results @@ -44,6 +48,11 @@ because the call count's exponent is linear in `C'`. schedule. * `Cslib.Complexity.CountingSimulation.fastAnalysis_nativeMk_of`: the resource analysis from the sizes and the leaf bounds. +* `Cslib.Complexity.CountingSimulation.fastSizes`: the sizes. +* `Cslib.Complexity.CountingSimulation.fastAnalysis_nativeMk`: the resource analysis from the leaf + bounds. +* `Cslib.Complexity.CountingSimulation.reach_facts`: the facts at the reached calls that the leaf + bounds use. ## References @@ -297,7 +306,8 @@ theorem length_vertices_le (G : ExplicitTensorNetwork ℕ) : ∀ v ∈ G.vertices, (encList encNat v.2).length + 1 ≤ (ExplicitTensorNetwork.encExplicitNetwork G).length := by have hl : (ExplicitTensorNetwork.encExplicitNetwork G).length = 2 * G.numEdges + 1 + - (G.vertices.map fun v => 2 * (encPair (encList encNat) (encList encNat) v).length + 1).sum := by + (G.vertices.map fun v => + 2 * (encPair (encList encNat) (encList encNat) v).length + 1).sum := by simp only [ExplicitTensorNetwork.encExplicitNetwork, encOfEquiv, Function.Embedding.trans_apply, Equiv.coe_toEmbedding, DescribedNetwork.equivProd, Equiv.coe_fn_mk, length_encPair, encUnary_apply, List.length_replicate, length_encList] @@ -392,4 +402,59 @@ theorem fastSizes : FastSizes := by theorem fastAnalysis_nativeMk (hL : FastLeaves) : FastAnalysis nativeMk := fastAnalysis_nativeMk_of fastSizes hL +/-! ### Facts at the leaves -/ + +/-- `D m + 40 m ^ 2 (m + 1) ≤ m ^ 4` from `m = D + 81` on. -/ +private theorem prec_arith {m D : ℕ} (hm : D + 81 ≤ m) : + D * m + (m + 1) * (40 * m ^ 2) ≤ m ^ 4 := by + have h1 : D * m ≤ m * m := Nat.mul_le_mul_right _ (by omega) + have h2 : 81 * m ^ 3 ≤ m ^ 4 := by + rw [pow_succ m 3, mul_comm (m ^ 3) m] + exact Nat.mul_le_mul_right _ (by omega) + have h3 : m * m ≤ m ^ 3 := by nlinarith + have h4 : m ^ 2 ≤ m ^ 3 := Nat.pow_le_pow_right (by omega) (by norm_num) + have e : (m + 1) * (40 * m ^ 2) = 40 * m ^ 3 + 40 * m ^ 2 := by ring + have h5 : m * m = m ^ 2 := by ring + omega + +/-- **The facts at the reached calls that the leaf bounds use.** For all constants `C` and `D` there +is `s₀` such that on every guarded input admissible at size `s ≥ 2` with constant `C` and with +precision `1 ≤ P ≤ D s`, every call reached from the root has scale `m` with `s ≤ m ≤ C s`, at most +`2 m` edges, and, from `s₀` on, precision at most `m ^ 4`, and at most `leafSize m` edges if the +machine treats it as a leaf. -/ +theorem reach_facts (C D : ℕ) : ∃ s₀, ∀ (s P : ℕ) (G : ℕ × ExplicitTensorNetwork ℕ), 2 ≤ s → + 1 ≤ P → P ≤ D * s → ExplicitAdmissible C s G → fastGuard G = true → + ∀ x, Reach nativeMk (root (ExplicitTensorNetwork.capPrecision (P, G))) x → + s ≤ x.scale ∧ x.scale ≤ C * s ∧ x.edges ≤ 2 * x.scale ∧ + (s₀ ≤ s → x.prec ≤ x.scale ^ 4 ∧ (active x = false → x.edges ≤ leafSize x.scale)) := by + obtain ⟨sA, hsA⟩ := exists_activeBounds_le D + refine ⟨max sA (D + 81), fun s P G hs hP hPD hA hg x hx => ?_⟩ + set q := ExplicitTensorNetwork.capPrecision (P, G) with hq + set m := G.2.numEdges with hm + have hsm : s ≤ m := hA.2.2.2.2.2 + have hmC : m ≤ C * s := hA.2.1 + have hp₀ : q.1 ≤ D * m := + (min_le_right _ _).trans (hPD.trans (Nat.mul_le_mul_left _ hsm)) + have hsh := Reach.shape (x₀ := root q) rfl rfl (wf_root P hg) hx + have hsc : x.scale = m := hsh.scale_eq + refine ⟨hsc ▸ hsm, hsc ▸ hmC, hsc ▸ hsh.edges_le, fun hs₀ => ⟨?_, fun hl => ?_⟩⟩ + · have hbd := hsh.bound_add_depth_le + change x.bound + x.depth ≤ (m + 1) + 0 at hbd + have hprec : x.prec ≤ q.1 + x.depth * (40 * m ^ 2) := hsh.prec_le + have : x.depth * (40 * m ^ 2) ≤ (m + 1) * (40 * m ^ 2) := Nat.mul_le_mul_right _ (by omega) + have := prec_arith (m := m) (D := D) (by omega) + rw [hsc] + omega + · have hm2 : D + 2 ≤ m := by omega + have hC1 : q.1 / m + 1 ≤ D + 1 := by + have := Nat.div_le_div_right (c := m) hp₀ + rw [Nat.mul_div_cancel _ (by omega)] at this + omega + have hB := hsA m (by omega) (q.1 / m + 1) hC1 + have hpC : q.1 ≤ (q.1 / m + 1) * m := + (Nat.lt_div_mul_add (a := q.1) (b := m) (by omega)).le.trans (by rw [add_mul, one_mul]) + have h := hx.onSchedule hB (onSchedule_root_capPrecision hg (Nat.le_add_left 1 _) hpC) + rw [hsc] + exact h.edges_le_leafSize hB hl + end Cslib.Complexity.CountingSimulation From a06db75204a5c19e398820609ce9b62d4844ac24 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:55:52 -0400 Subject: [PATCH 122/129] test(circuit/frontier): checked instances of the resource analysis Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- ComplexityTests/FastContractionAnalysis.lean | 70 ++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 ComplexityTests/FastContractionAnalysis.lean diff --git a/ComplexityTests/FastContractionAnalysis.lean b/ComplexityTests/FastContractionAnalysis.lean new file mode 100644 index 0000000000..29eb99127e --- /dev/null +++ b/ComplexityTests/FastContractionAnalysis.lean @@ -0,0 +1,70 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.Complexity.Classes.CountingSimulation.FastAnalysis + +/-! # The resource analysis of the native recursion: checked instances + +The derivation invariant and the shape invariant at a small root, the reduced values, the clock +arithmetic at a concrete size, and the statement of the remaining obligation. -/ + +namespace ComplexityTests.FastContractionAnalysis + +open Cslib.Circuits.Frontier Cslib.Circuits.Frontier.FastContraction + Cslib.Complexity.CountingSimulation Cslib.Circuits.Frontier.ContractionSchedule + +/-- One row `0` and one column `1` joined by edge `0`, with full tables. -/ +def tiny : ℕ × ExplicitTensorNetwork ℕ := (1, ⟨1, [([0], [1, 2]), ([0], [3, 4])]⟩) + +example : fastGuard tiny = true := by decide + +/-- The root of `tiny` at precision `3`. -/ +abbrev tinyRoot : Call := root (ExplicitTensorNetwork.capPrecision (3, tiny)) + +-- The root is reached, and it is the only reached call at depth `0`. +example : Reach nativeMk tinyRoot tinyRoot := .root +example (x : Call) (hx : Reach nativeMk tinyRoot x) (h0 : x.depth = 0) : x = tinyRoot := + hx.eq_root_of_depth_le (by rw [h0]; rfl) + +-- The root is a leaf, so its derivation has one node and no open call. +example : active tinyRoot = false := by decide + +-- The shape invariant holds at every reached call; at the root it is the root itself. +example : Shape tinyRoot tinyRoot := + Reach.shape rfl rfl (wf_root 3 (by decide)) .root + +-- The root is on the schedule for `C = 3`: its precision `3` is at most `3 ⬝ 1`. +example : OnSchedule 3 tiny.2.numEdges tinyRoot := + onSchedule_root_capPrecision (by decide) (by norm_num) (by decide) + +-- Values are reduced at their precision: at most `e` coordinates below `2 ^ p`. +example : ReducedAt tinyRoot.prec tinyRoot.fl (value nativeMk tinyRoot) := + reducedAt_value nativeMk tinyRoot + +-- The modulus at `m = 1` has `2 ⬝ 3 ^ 1 = 6` coefficients, within `32 (m + 1) ^ 2`. +example : (modulus 1).length = 6 := by decide +example : (modulus 1).length ≤ 32 * (1 + 1) ^ 2 := length_modulus_le 1 + +-- The clock arithmetic: with `m = 100` edges, precision `p = 50 ≤ 1 ⬝ m` and `D = 1`, a derivation +-- within the call count of `C' = ⌊50 / 100⌋ + 1 = 1` fits in the clock. +example (N : ℕ) (hN : N ≤ 2 ^ ((245760 * (50 / 100 + 1) + 37) * 100 / Nat.sqrt (Nat.log 2 100))) : + 2 * N - 1 ≤ 2 ^ (fuelConst * (100 + 50) / Nat.sqrt (Nat.log 2 (100 + 50))) := + two_mul_sub_one_le_fuel (D := 1) (by norm_num) (by norm_num) hN + +-- A child of a parent with `M` edges under (D.6) has at most `M / 16` edges. +example {r a i Δ : ℕ} {D : DescribedNetwork VertexDesc} (hD : D.IsRowsFirstBipartite r) + (hK : 64 ^ a ≤ D.numEdges) (hia : i < a) (hdeg : ∀ v ∈ D.vertices, v.1.length ≤ Δ) + (hM : 2 * Δ * (64 ^ (2 * a)) ^ 2 ≤ D.numEdges) (fl : List ℕ) (pt : ℕ) (t : ℤ) + (ζ β : List ℕ) (g : Fin 64) : + 16 * (NativeChild.nativeChild fl a i pt t ζ r D (NativeChild.nativeParentPacking r a D) β + g).numEdges ≤ D.numEdges := + NativeChild.sixteen_mul_numEdges_nativeChild_le hD hK hia hdeg hM fl pt t ζ β g + +-- The sizes are proved; the resource analysis needs only the leaf bounds. +example : FastSizes := fastSizes +example (hL : FastLeaves) : FastAnalysis nativeMk := fastAnalysis_nativeMk hL + +end ComplexityTests.FastContractionAnalysis From 24746977701e09a6bfd0c45e02dc33cd87a3e2fc Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 03:58:46 -0400 Subject: [PATCH 123/129] feat(Classes/CountingSimulation): the resource analysis of the native recursion holds FastAnalysis nativeMk is proved (fastAnalysis_nativeMk_holds): the leaf bounds of #1979 from the reached-call facts (at most twice the scale in edges; from some size on, precision at most the fourth power of the scale and at most leafSize edges at a leaf) and the sizes. The glue's hypotheses now use the (c, d) form of FastSizes and precision exponent 4. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../CountingSimulation/FastLeaves.lean | 61 ++++++++++++++++--- ComplexityTests/FastLeaves.lean | 12 +++- 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastLeaves.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastLeaves.lean index c02a585e71..f5b6d411c6 100644 --- a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastLeaves.lean +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastLeaves.lean @@ -18,7 +18,10 @@ The leaf clause of the resource analysis of the native recursion of Theorem D.1 the packet shape (`Cslib.Circuits.Frontier.FastContraction.exists_leafCertificate_le`), the sizes of the reached calls (`Cslib.Complexity.CountingSimulation.FastSizes`) and three facts about the reached calls: at most twice the scale in edges and, from some size on, at most the leaf size in -edges at a leaf and precision at most the square of the scale. +edges at a leaf and precision at most the fourth power of the scale. Those facts and the sizes are +proved (`FastContraction.Reach.edges_le_two_mul_scale`, `CountingSimulation.reach_facts`, +`CountingSimulation.fastSizes`), so the resource analysis of the native recursion holds +(`CountingSimulation.fastAnalysis_nativeMk_holds`). Every reached call is well-formed and has the packet shape (`FastContraction.Reach.wf_packetShape`), by `FastContraction.wfChildren_nativeMk` and `FastContraction.packetShape_child`. @@ -27,7 +30,12 @@ by `FastContraction.wfChildren_nativeMk` and `FastContraction.packetShape_child` * `Cslib.Circuits.Frontier.FastContraction.Reach.wf_packetShape`: the reached calls are well-formed and have the packet shape. -* `Cslib.Complexity.CountingSimulation.fastLeaves_of`: the leaf bounds. +* `Cslib.Complexity.CountingSimulation.fastLeaves_of`: the leaf bounds from the reached-call facts. +* `Cslib.Circuits.Frontier.FastContraction.Reach.edges_le_two_mul_scale`: at most twice the scale + in edges. +* `Cslib.Complexity.CountingSimulation.fastLeaves`: the leaf bounds. +* `Cslib.Complexity.CountingSimulation.fastAnalysis_nativeMk_holds`: the resource analysis of the + native recursion. ## References @@ -48,6 +56,26 @@ theorem Reach.wf_packetShape {x₀ x : Call} (h : Reach nativeMk x₀ x) (hw : W | step c _ ha hc ih => exact ⟨wfChildren_nativeMk _ ih.1 ha c hc, packetShape_child ih.1 ha ih.2 hc⟩ +/-- **Reached calls have at most twice the scale in edges**, from a root at scale its number of +edges with bound plus depth one more, for every input: a child has at most twice its parent's +edges (`NativeChild.numEdges_nativeChild_le`), and an active parent has at most the root's. -/ +theorem Reach.edges_le_two_mul_scale {x₀ x : Call} (h₀s : x₀.scale = x₀.edges) + (h₀b : x₀.bound + x₀.depth = x₀.edges + 1) (h : Reach nativeMk x₀ x) : + x.edges ≤ 2 * x.scale := by + have key : x.edges ≤ 2 * x₀.edges := by + induction h with + | root => omega + | @step y c hy ha _ _ => + have h1 : (nativeMk y c).2.numEdges ≤ 2 * y.edges := by + unfold nativeMk + exact NativeChild.numEdges_nativeChild_le _ _ _ _ _ _ _ _ _ _ _ + have h2 := hy.bound_add_depth_le + have h3 := edges_lt_bound_of_active ha + change (nativeMk y c).2.numEdges ≤ 2 * x₀.edges + omega + rw [h.scale_eq, h₀s] + exact key + end Cslib.Circuits.Frontier.FastContraction namespace Cslib.Complexity.CountingSimulation @@ -58,7 +86,7 @@ open Cslib.Circuits.Frontier Cslib.Circuits.Frontier.ContractionSchedule /-- **The leaf bounds of the native recursion**, from the sizes of the reached calls and three facts about them on admissible guarded inputs: every reached call has at most twice its scale in edges, and for each `C` and `D`, from some size on, every reached leaf has at most the leaf size in -edges and precision at most the square of its scale. -/ +edges and precision at most the fourth power of its scale. -/ theorem fastLeaves_of (hS : FastSizes) (hE : ∀ (P : ℕ) (G : ℕ × ExplicitTensorNetwork ℕ) (x : Call), Reach nativeMk (root (ExplicitTensorNetwork.capPrecision (P, G))) x → @@ -66,12 +94,13 @@ theorem fastLeaves_of (hS : FastSizes) (hbig : ∀ C D : ℕ, ∃ s₀ : ℕ, ∀ (s P : ℕ) (G : ℕ × ExplicitTensorNetwork ℕ), 2 ≤ s → 1 ≤ P → P ≤ D * s → ExplicitAdmissible C s G → fastGuard G = true → s₀ ≤ s → ∀ x, Reach nativeMk (root (ExplicitTensorNetwork.capPrecision (P, G))) x → - active x = false → x.edges ≤ leafSize x.scale ∧ x.prec ≤ x.scale ^ 2) : + active x = false → x.edges ≤ leafSize x.scale ∧ x.prec ≤ x.scale ^ 4) : FastLeaves := by intro C D - obtain ⟨KS, hKS⟩ := hS C D + obtain ⟨cS, dS, hKS⟩ := hS C D + set KS := cS + dS obtain ⟨s₀, hs₀⟩ := hbig C D - obtain ⟨KB, hKB⟩ := exists_leafCertificate_le C 2 s₀ + obtain ⟨KB, hKB⟩ := exists_leafCertificate_le C 4 s₀ refine ⟨KB * (KS + 1) ^ KB + KS * KB + KB, fun s P G hs hP hPD hA hg x hx hl => ?_⟩ set n := (FastContraction.encInput (P, G)).length with hn have hscale : x.scale = G.2.numEdges := hx.scale_eq @@ -80,8 +109,10 @@ theorem fastLeaves_of (hS : FastSizes) obtain ⟨-, hshape⟩ := hx.wf_packetShape (wf_root P hg) (packetShape_root _) obtain ⟨hT, hSp⟩ := hKB nativeMk s x hs hsm hmC hshape (hE P G x hx) fun h => hs₀ s P G hs hP hPD hA hg h x hx hl - have hlen := (hKS s P G hs hP hPD hA hg x hx).1 - rw [← hn] at hlen + have hlen' := (hKS s P G hs hP hPD hA hg x hx).1 + rw [← hn] at hlen' + have hlen : (encCall x).length ≤ KS * (n + 1) ^ KS := + hlen'.trans (Nat.mul_le_mul (by omega) (Nat.pow_le_pow_right (by omega) (by omega))) -- `n_x + 1 ≤ (KS + 1) (n + 1) ^ KS`. have hB1 : 1 ≤ (n + 1) ^ KS := Nat.one_le_pow _ _ (by omega) have hx1 : (encCall x).length + 1 ≤ (KS + 1) * (n + 1) ^ KS := by @@ -98,4 +129,18 @@ theorem fastLeaves_of (hS : FastSizes) exact ⟨hT.trans (Nat.mul_le_mul hpoly (Nat.pow_le_pow_right (by norm_num) hexp)), hSp.trans hpoly⟩ +/-- **The leaf bounds of the native recursion.** -/ +theorem fastLeaves : FastLeaves := + fastLeaves_of fastSizes (fun _ _ _ hx => hx.edges_le_two_mul_scale rfl rfl) fun C D => by + obtain ⟨s₀, hs₀⟩ := reach_facts C D + refine ⟨s₀, fun s P G hs hP hPD hA hg h x hx hl => ?_⟩ + obtain ⟨hp, hlf⟩ := (hs₀ s P G hs hP hPD hA hg x hx).2.2.2 h + exact ⟨hlf hl, hp⟩ + +/-- **The resource analysis of the native recursion holds**: the derivation of the root over the +reached calls has polynomial height, sizes and leaf space, leaf time `poly(n) 2 ^ O(s / √⌊log₂ s⌋)`, +and, from some size on, its nodes fit in the clock. -/ +theorem fastAnalysis_nativeMk_holds : FastAnalysis nativeMk := + fastAnalysis_nativeMk fastLeaves + end Cslib.Complexity.CountingSimulation diff --git a/ComplexityTests/FastLeaves.lean b/ComplexityTests/FastLeaves.lean index 7cbfec8a8f..03914e3872 100644 --- a/ComplexityTests/FastLeaves.lean +++ b/ComplexityTests/FastLeaves.lean @@ -8,7 +8,8 @@ import Complexity.Computability.Complexity.Classes.CountingSimulation.FastLeaves /-! # The leaf bounds of the native recursion: checked instances -A guarded input whose root is reached, well-formed and has the packet shape. -/ +A guarded input whose root is reached, well-formed and has the packet shape; the edge bound on +reached calls; and the resource analysis. -/ namespace ComplexityTests.FastLeaves @@ -26,4 +27,13 @@ example : WF (root (ExplicitTensorNetwork.capPrecision (1, pair))) ∧ PacketShape (root (ExplicitTensorNetwork.capPrecision (1, pair))) := (Reach.root (mk := nativeMk)).wf_packetShape (wf_root 1 (by decide)) (packetShape_root _) +-- Reached calls have at most twice the scale in edges, on every input. +example (P : ℕ) (G : ℕ × ExplicitTensorNetwork ℕ) (x : Call) + (hx : Reach nativeMk (root (ExplicitTensorNetwork.capPrecision (P, G))) x) : + x.edges ≤ 2 * x.scale := + hx.edges_le_two_mul_scale rfl rfl + +-- The resource analysis of the native recursion holds, with no remaining hypothesis. +example : FastAnalysis nativeMk := fastAnalysis_nativeMk_holds + end ComplexityTests.FastLeaves From ad3df99303a89ba1e55330c70cba3891381cc5ec Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 04:16:02 -0400 Subject: [PATCH 124/129] fix(Classes/CountingSimulation): unfold the unified rows-first predicate in the guard ExplicitTensorNetwork.IsRowsFirstBipartite is now DescribedNetwork.IsRowsFirstBipartite (#1967). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Complexity/Classes/CountingSimulation/FastAlgorithm.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean index a498a0ba11..e652204be5 100644 --- a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastAlgorithm.lean @@ -163,7 +163,7 @@ private def guardTest (G : ℕ × ExplicitTensorNetwork ℕ) : Bool := private theorem fastGuard_eq_guardTest (G : ℕ × ExplicitTensorNetwork ℕ) : fastGuard G = guardTest G := by refine Bool.eq_iff_iff.mpr ?_ - simp only [fastGuard, guardTest, ExplicitTensorNetwork.IsRowsFirstBipartite, + simp only [fastGuard, guardTest, DescribedNetwork.IsRowsFirstBipartite, ExplicitTensorNetwork.maxDegree_le_iff, decide_eq_true_eq, Bool.and_eq_true, List.all_eq_true, List.mem_range, List.count_flatMap, Function.comp_def] constructor From 1fa779161ddf39e4a16053ef023431e9380c0d75 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 04:16:02 -0400 Subject: [PATCH 125/129] doc(circuit/frontier): restore the exact quotation of the child edge bound MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The manuscript (§D.4.6, p. 63) reads "at most 6M/r = 3M/32 < M/8 edges". This restores the parent's text, so ChildSize.lean is unchanged by this PR. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Circuit/Frontier/TensorNetwork/ChildSize.lean | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildSize.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildSize.lean index 390e7d8943..5b5b429d66 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildSize.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/ChildSize.lean @@ -23,7 +23,8 @@ The child networks of a mode splitting (`TensorNetwork.ModeSplitting.child`, §D "a new packet vertex has at most `2L ⩽ Δ` ports"; * the child has one edge per port of its group (`ModeSplitting.card_edges_child`), so with at most `c M / r` ports per group on each side (Lemma D.3 (3)) it has at most `2 c M / r` edges - (`ModeSplitting.mul_card_edges_child_le`): the manuscript's "at most `6M / r < M / 8` edges". + (`ModeSplitting.mul_card_edges_child_le`): the manuscript's "at most `6M/r = 3M/32 < M/8` + edges" (§D.4.6, p. 63). ## Main results From e17051b469218795d25b8fadef3a3f2d72fe7bcd Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 04:16:48 -0400 Subject: [PATCH 126/129] refactor(circuit/frontier): the analysis through nativeMk_eq, for the capped builder of #1982 With #1982's capped child builder, nativeMk is no longer unfolded by rfl. The analysis unfolds it through nativeMk_eq (replacing nativeMk_apply_of_active), reads the packing through packing_eq, and uses #1982's BudgetTuples.lt_of_mem_budgetTuples instead of a local copy. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../FastContractionSchedule.lean | 18 +++---------- .../TensorNetwork/FastContractionShape.lean | 26 ++----------------- .../TensorNetwork/FastContractionSize.lean | 2 +- 3 files changed, 7 insertions(+), 39 deletions(-) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean index 6b89d270ca..e367f44adf 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSchedule.lean @@ -69,17 +69,6 @@ namespace Cslib.Circuits.Frontier.FastContraction open ContractionSchedule ChildCursor NativeRecovery NativeChild -/-- **The native children of a well-formed active call**, unfolded: the rows and the native child -of the parent with its native packing, at the cursor state. Every unfolding of `nativeMk` in the -analysis goes through this lemma. -/ -theorem nativeMk_apply_of_active {x : Call} {c : State} (_hx : WF x) (_ha : active x = true) - (_hc : c ∈ children x) : - nativeMk x c = (childRows x.rows (level x ((nodes x).getD c.1 0)) x.net - (nativeParentPacking x.rows (slots x) x.net) c.2.2.2, - nativeChild x.fl (slots x) (level x ((nodes x).getD c.1 0)) (nodePrec x c.1) - (node (slots x) ((nodes x).getD c.1 0)) ((points x).getD c.2.1 []) x.rows x.net - (nativeParentPacking x.rows (slots x) x.net) c.2.2.1 c.2.2.2) := rfl - /-- **A call on the schedule** of a root with `m` edges, for the constant `C`: it keeps the root's scale and modulus, is well-formed, has maximum degree at most `degreeBound m` and fewer edges than its bound, and has at most `leafSize m` edges or is an active call of the schedule. -/ @@ -189,13 +178,13 @@ theorem child (hB : ∀ d M p, Active C m d M p → ActiveBounds C m d M p) (h : (Δ := degreeBound m) (by rw [hslots]; exact hb.degree_mul_pairs_sq_le) x.fl (nodePrec x c.1) (node (slots x) ((nodes x).getD c.1 0)) ((points x).getD c.2.1 []) c.2.2.1 ⟨c.2.2.2, hg⟩ - simpa only [FastContraction.child, nativeMk_apply_of_active h.wf ha hc, Call.edges] using this + simpa only [FastContraction.child, nativeMk_eq h.wf ha hc, Call.edges] using this have hM := one_le_edges_of_active ha refine ⟨h.scale_eq, h.fl_eq, wfChildren_nativeMk x h.wf ha c hc, fun v hv => ?_, ?_, ?_⟩ · refine length_le_of_mem_vertices_nativeChild h.wf.2 hK hia h.degree_le (by rw [hslots]; exact hb.two_mul_slots_le) x.fl (nodePrec x c.1) (node (slots x) ((nodes x).getD c.1 0)) ((points x).getD c.2.1 []) c.2.2.1 ⟨c.2.2.2, hg⟩ ?_ - simpa only [FastContraction.child, nativeMk_apply_of_active h.wf ha hc] using hv + simpa only [FastContraction.child, nativeMk_eq h.wf ha hc] using hv · change _ < x.edges omega · by_cases hs : (FastContraction.child x nativeMk c).edges ≤ leafSize m @@ -285,8 +274,9 @@ theorem length_children_le (hB : ∀ d M p, Active C m d M p → ActiveBounds C have hK := pow_slots_le_edges_of_active ha have hslots : slots x = slotBits m x.depth := by rw [slots, h.scale_eq] have hlevels : levels x = taper m x.depth := by rw [levels, h.scale_eq] + have hr := rows_le_of_isRowsFirstBipartite h.wf.2 (one_le_edges_of_active ha) have hP : (packing x).2.1 * 2 ^ slots x ≤ 3 * x.edges := - numPackets_nativeParentPacking_mul_le h.wf.2 hK h.degree_le + packing_eq hr ▸ numPackets_nativeParentPacking_mul_le h.wf.2 hK h.degree_le (by rw [hslots]; exact hb.degree_mul_pairs_sq_le) (by rw [hslots]; exact hb.slots_mul_pairs_le) have hT : ∀ j < (nodes x).length, (BudgetTuples.budgetTuples (weights x j) diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean index ceeef247fe..76f982e9f8 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionShape.lean @@ -90,35 +90,13 @@ structure Shape (x₀ x : Call) : Prop where ∃ d, v.2 = .inr d ∧ PacketBound x₀.fl x₀.edges (x₀.prec + x.depth * (40 * x₀.edges ^ 2)) v.1.length d -/-- The entries of a tuple of weight below `p` for positive weights are below `p`. -/ -theorem lt_of_mem_budgetTuples {ws β : List ℕ} {p : ℕ} (hw : ∀ w ∈ ws, 0 < w) - (hβ : β ∈ BudgetTuples.budgetTuples ws p) : ∀ b ∈ β, b < p := by - have key : ∀ (ws β : List ℕ), (∀ w ∈ ws, 0 < w) → β.length = ws.length → - ∀ b ∈ β, b ≤ BudgetTuples.weight ws β := by - intro ws - induction ws with - | nil => intro β _ hl b hb; simp_all - | cons w ws ih => - intro β hw hl b hb - rcases β with _ | ⟨b', β⟩ - · simp at hb - · simp only [BudgetTuples.weight_cons_cons] - simp only [List.length_cons, Nat.add_right_cancel_iff] at hl - rcases List.mem_cons.mp hb with rfl | hb - · have := hw w List.mem_cons_self - nlinarith - · have := ih β (fun w' hw' => hw w' (List.mem_cons_of_mem _ hw')) hl b hb - omega - obtain ⟨hl, hwt⟩ := (BudgetTuples.mem_budgetTuples hw).mp hβ - exact fun b hb => (key ws β hw hl b hb).trans_lt hwt - /-- The exponent tuple of a child of an active call has entries below the node's precision. -/ theorem lt_nodePrec_of_mem_children {x : Call} {c : State} (hc : c ∈ children x) : ∀ b ∈ c.2.2.1, b < nodePrec x c.1 := by simp only [children, cursorList, nodeBlock, pointBlock, groupBlock, List.mem_flatMap, List.mem_range, List.mem_map] at hc obtain ⟨j, -, i, -, β, hβ, g, -, rfl⟩ := hc - exact lt_of_mem_budgetTuples (weights_pos j) hβ + exact fun b hb => BudgetTuples.lt_of_mem_budgetTuples (weights_pos j) hβ hb /-- The ports of a vertex of a well-formed call are at most its edges. -/ theorem length_le_edges_of_wf {x : Call} (hx : WF x) {v : List ℕ × VertexDesc} @@ -251,7 +229,7 @@ theorem Shape.child (h₀s : x₀.scale = x₀.edges) (h₀b : x₀.bound + x₀ have hprec : nodePrec x c.1 ≤ x.prec + 40 * m ^ 2 := by have := nodePrec_le_add ha (hsc ▸ hM) c.1 rwa [hsc] at this - have hmk := nativeMk_apply_of_active h.wf ha hc + have hmk := nativeMk_eq h.wf ha hc set pk := nativeParentPacking x.rows (slots x) x.net have hpk : pk.2.1 ≤ m ^ 2 := by have := numPackets_nativeParentPacking_le x.rows (slots x) x.net diff --git a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean index c85bda0940..0fd1f563a8 100644 --- a/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean +++ b/Complexity/Computability/Circuit/Frontier/TensorNetwork/FastContractionSize.lean @@ -419,7 +419,7 @@ theorem length_encFrame_frameAt_le (ha : active x = true) (hM : x.edges ≤ x.sc have h4 : m ^ 3 * p ≤ (m + 1) ^ 5 * (p + 1) := Nat.mul_le_mul (hcu.trans hm3) (Nat.le_succ _) omega - · have := lt_of_mem_budgetTuples (weights_pos _) hβ b hb + · have := BudgetTuples.lt_of_mem_budgetTuples (weights_pos _) hβ hb have : m ^ 2 ≤ (m + 1) ^ 5 := hsq.trans hm2 omega · unfold wide From 466873b0544a5597a6e122a7487a688d2493011d Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 04:17:54 -0400 Subject: [PATCH 127/129] feat(Classes/CountingSimulation): the contraction algorithm of Theorem D.1, unconditionally With #1982's capped child builder, hpoly and the points' unit differences are proved, and FastAnalysis nativeMk holds (#1988), so contractionAlgorithmOfNative fastAnalysis_nativeMk_holds is the contraction algorithm for admissible explicit networks, with no remaining hypothesis. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Classes/CountingSimulation/FastLeaves.lean | 11 +++++++++++ ComplexityTests/FastLeaves.lean | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastLeaves.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastLeaves.lean index f5b6d411c6..805a16171d 100644 --- a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastLeaves.lean +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastLeaves.lean @@ -36,6 +36,8 @@ by `FastContraction.wfChildren_nativeMk` and `FastContraction.packetShape_child` * `Cslib.Complexity.CountingSimulation.fastLeaves`: the leaf bounds. * `Cslib.Complexity.CountingSimulation.fastAnalysis_nativeMk_holds`: the resource analysis of the native recursion. +* `Cslib.Complexity.CountingSimulation.fastContractionAlgorithm`: the contraction algorithm of + Theorem D.1 for admissible explicit networks. ## References @@ -143,4 +145,13 @@ and, from some size on, its nodes fit in the clock. -/ theorem fastAnalysis_nativeMk_holds : FastAnalysis nativeMk := fastAnalysis_nativeMk fastLeaves +/-- **The contraction algorithm of Theorem D.1** for admissible explicit networks: the native +recursion behind a guard, with direct summation for the networks the guard rejects, in time +`poly(n) 2 ^ O(s / √⌊log₂ s⌋)` and polynomial space. -/ +noncomputable def fastContractionAlgorithm : + ContractionAlgorithm (Turing.MultiTapeTM.encPair Turing.MultiTapeTM.encNat + ExplicitTensorNetwork.encExplicitNetwork) + (fun G => G.2.contraction) ExplicitAdmissible := + contractionAlgorithmOfNative fastAnalysis_nativeMk_holds + end Cslib.Complexity.CountingSimulation diff --git a/ComplexityTests/FastLeaves.lean b/ComplexityTests/FastLeaves.lean index 03914e3872..2e205e997d 100644 --- a/ComplexityTests/FastLeaves.lean +++ b/ComplexityTests/FastLeaves.lean @@ -36,4 +36,9 @@ example (P : ℕ) (G : ℕ × ExplicitTensorNetwork ℕ) (x : Call) -- The resource analysis of the native recursion holds, with no remaining hypothesis. example : FastAnalysis nativeMk := fastAnalysis_nativeMk_holds +-- The contraction algorithm of Theorem D.1, with no remaining hypothesis. +noncomputable example : ContractionAlgorithm (Turing.MultiTapeTM.encPair Turing.MultiTapeTM.encNat + ExplicitTensorNetwork.encExplicitNetwork) (fun G => G.2.contraction) ExplicitAdmissible := + fastContractionAlgorithm + end ComplexityTests.FastLeaves From 1a45e962e74e108db88a1161d593cab0d115d30a Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 04:05:40 -0400 Subject: [PATCH 128/129] feat(Classes/CountingSimulation): NTIME, BPTIME and PPTIME in time 2^O(T/sqrt(log T)) and polynomial space The containments of BlockNetworks with fastContractionAlgorithm in place of their hypothesis: for time-constructible T with max n 2 <= T n, every problem in NTIME[T], BPTIME[T] or PPTIME[T] is decided by one deterministic machine in time 2^(C T n / sqrt(log2 (T n))) and workspace T n ^ d. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../CountingSimulation/FastSimulation.lean | 93 +++++++++++++++++++ ComplexityTests/FastSimulation.lean | 24 +++++ 3 files changed, 118 insertions(+) create mode 100644 Complexity/Computability/Complexity/Classes/CountingSimulation/FastSimulation.lean create mode 100644 ComplexityTests/FastSimulation.lean diff --git a/Complexity.lean b/Complexity.lean index bbeb929252..bc23f1b7c6 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1138,6 +1138,7 @@ public import Complexity.Computability.Complexity.Classes.CountingSimulation.Con public import Complexity.Computability.Complexity.Classes.CountingSimulation.FastAlgorithm public import Complexity.Computability.Complexity.Classes.CountingSimulation.FastAnalysis public import Complexity.Computability.Complexity.Classes.CountingSimulation.FastLeaves +public import Complexity.Computability.Complexity.Classes.CountingSimulation.FastSimulation public import Complexity.Computability.Complexity.Classes.CountingSimulation.Guarded public import Complexity.Computability.Complexity.Classes.CountingSimulation.Interfaces public import Complexity.Computability.Complexity.Classes.DP diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastSimulation.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastSimulation.lean new file mode 100644 index 0000000000..6813d087d7 --- /dev/null +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastSimulation.lean @@ -0,0 +1,93 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Complexity.Classes.CountingSimulation.BlockNetworks +public import Complexity.Computability.Complexity.Classes.CountingSimulation.FastLeaves + +/-! +# Nondeterministic and probabilistic time in time `2 ^ O(T / √log T)` and polynomial space + +The class containments of `Cslib.Complexity.CountingSimulation.BlockNetworks`, with the contraction +algorithm of Theorem D.1 of [OpenAI2026ETH] (`CountingSimulation.fastContractionAlgorithm`) in place +of their hypothesis. These are the containments of #1833: for time-constructible `T` with +`max n 2 ≤ T n`, every problem in `NTIME[T]`, `BPTIME[T]` or `PPTIME[T]` is decided by one +deterministic machine in time `2 ^ (C T n / √⌊log₂ (T n)⌋)` and workspace `T n ^ d`. + +## Main results + +* `Cslib.Complexity.CountingSimulation.NTIME_subset_iUnion_DTIME_two_pow_div_sqrt_log_two`, + `BPTIME_subset_iUnion_DTIME_two_pow_div_sqrt_log_two`, + `PPTIME_subset_iUnion_DTIME_two_pow_div_sqrt_log_two`: the class containments. +* `exists_decidableInTimeAndSpace_two_pow_div_sqrt_log_two_of_mem_NTIME`, + `exists_decidableInTimeAndSpace_two_pow_div_sqrt_log_two_of_mem_BPTIME`, + `exists_decidableInTimeAndSpace_two_pow_div_sqrt_log_two_of_mem_PPTIME`: joint time and space. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Theorem D.1 (p. 53). +-/ + +public section + +namespace Cslib.Complexity.CountingSimulation + +open Turing.MultiTapeTM + +variable {α : Type} {enc : α ↪ List Bool} {T : ℕ → ℕ} + +/-- **Nondeterministic time `T` in time `2 ^ O(T / √log T)` and space `T ^ O(1)`.** -/ +theorem exists_decidableInTimeAndSpace_two_pow_div_sqrt_log_two_of_mem_NTIME + (hT : TimeConstructible T) (hTn : ∀ n, max n 2 ≤ T n) {L : Set α} (hL : L ∈ NTIME enc T) : + ∃ C d : ℕ, DecidableInTimeAndSpace L enc + (fun a => 2 ^ (C * T (enc a).length / Nat.sqrt (Nat.log 2 (T (enc a).length)))) + (fun a => T (enc a).length ^ d) := + exists_decidableInTimeAndSpace_of_mem_NTIME_of_contractionAlgorithm fastContractionAlgorithm hT + hTn hL + +/-- **Bounded-error probabilistic time `T` in time `2 ^ O(T / √log T)` and space `T ^ O(1)`.** -/ +theorem exists_decidableInTimeAndSpace_two_pow_div_sqrt_log_two_of_mem_BPTIME + (hT : TimeConstructible T) (hTn : ∀ n, max n 2 ≤ T n) {L : Set α} (hL : L ∈ BPTIME enc T) : + ∃ C d : ℕ, DecidableInTimeAndSpace L enc + (fun a => 2 ^ (C * T (enc a).length / Nat.sqrt (Nat.log 2 (T (enc a).length)))) + (fun a => T (enc a).length ^ d) := + exists_decidableInTimeAndSpace_of_mem_BPTIME_of_contractionAlgorithm fastContractionAlgorithm hT + hTn hL + +/-- **Strict-majority probabilistic time `T` in time `2 ^ O(T / √log T)` and space +`T ^ O(1)`.** -/ +theorem exists_decidableInTimeAndSpace_two_pow_div_sqrt_log_two_of_mem_PPTIME + (hT : TimeConstructible T) (hTn : ∀ n, max n 2 ≤ T n) {L : Set α} (hL : L ∈ PPTIME enc T) : + ∃ C d : ℕ, DecidableInTimeAndSpace L enc + (fun a => 2 ^ (C * T (enc a).length / Nat.sqrt (Nat.log 2 (T (enc a).length)))) + (fun a => T (enc a).length ^ d) := + exists_decidableInTimeAndSpace_of_mem_PPTIME_of_contractionAlgorithm fastContractionAlgorithm hT + hTn hL + +/-- **`NTIME[T] ⊆ ⋃ C, DTIME[2 ^ (C T / √⌊log₂ T⌋)]`** for time-constructible `T` with +`max n 2 ≤ T n`. -/ +theorem NTIME_subset_iUnion_DTIME_two_pow_div_sqrt_log_two (hT : TimeConstructible T) + (hTn : ∀ n, max n 2 ≤ T n) : + NTIME enc T ⊆ ⋃ C, DTIME enc fun n => 2 ^ (C * T n / Nat.sqrt (Nat.log 2 (T n))) := + NTIME_subset_iUnion_DTIME_of_contractionAlgorithm fastContractionAlgorithm hT hTn + +/-- **`BPTIME[T] ⊆ ⋃ C, DTIME[2 ^ (C T / √⌊log₂ T⌋)]`** for time-constructible `T` with +`max n 2 ≤ T n`. -/ +theorem BPTIME_subset_iUnion_DTIME_two_pow_div_sqrt_log_two (hT : TimeConstructible T) + (hTn : ∀ n, max n 2 ≤ T n) : + BPTIME enc T ⊆ ⋃ C, DTIME enc fun n => 2 ^ (C * T n / Nat.sqrt (Nat.log 2 (T n))) := + BPTIME_subset_iUnion_DTIME_of_contractionAlgorithm fastContractionAlgorithm hT hTn + +/-- **`PPTIME[T] ⊆ ⋃ C, DTIME[2 ^ (C T / √⌊log₂ T⌋)]`** for time-constructible `T` with +`max n 2 ≤ T n`. -/ +theorem PPTIME_subset_iUnion_DTIME_two_pow_div_sqrt_log_two (hT : TimeConstructible T) + (hTn : ∀ n, max n 2 ≤ T n) : + PPTIME enc T ⊆ ⋃ C, DTIME enc fun n => 2 ^ (C * T n / Nat.sqrt (Nat.log 2 (T n))) := + PPTIME_subset_iUnion_DTIME_of_contractionAlgorithm fastContractionAlgorithm hT hTn + +end Cslib.Complexity.CountingSimulation diff --git a/ComplexityTests/FastSimulation.lean b/ComplexityTests/FastSimulation.lean new file mode 100644 index 0000000000..5ff99730da --- /dev/null +++ b/ComplexityTests/FastSimulation.lean @@ -0,0 +1,24 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.Complexity.Classes.CountingSimulation.FastSimulation + +/-! # Nondeterministic and probabilistic time in time `2 ^ O(T / √log T)`: checked instances + +The class containments at `T n = n + 2`, which is at least `max n 2`. -/ + +namespace ComplexityTests.FastSimulation + +open Cslib.Complexity Cslib.Complexity.CountingSimulation Turing.MultiTapeTM + +example : ∀ n, max n 2 ≤ n + 2 := fun n => by omega + +example {α : Type} {enc : α ↪ List Bool} (hT : TimeConstructible fun n => n + 2) : + NTIME enc (fun n => n + 2) ⊆ + ⋃ C, DTIME enc fun n => 2 ^ (C * (n + 2) / Nat.sqrt (Nat.log 2 (n + 2))) := + NTIME_subset_iUnion_DTIME_two_pow_div_sqrt_log_two hT fun n => by omega + +end ComplexityTests.FastSimulation From 7efa13948885bfa5279bfcaec3aef34cad81c8bb Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 04:35:25 -0400 Subject: [PATCH 129/129] test(Classes/CountingSimulation): the containments at a proved polynomial clock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test now instantiates the containments at T n = (n + 1)^2 + 1, which is proved time-constructible, instead of assuming a clock. FastSimulation.lean cites Hopcroft–Paul–Valiant, and the conditional modules point to the unconditional theorems (audit round 20). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../CountingSimulation/BlockNetworks.lean | 5 ++- .../CountingSimulation/Containments.lean | 3 ++ .../CountingSimulation/FastSimulation.lean | 1 + ComplexityTests/FastSimulation.lean | 42 +++++++++++++++---- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/BlockNetworks.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/BlockNetworks.lean index 719f8f5f84..c1ee6df5da 100644 --- a/Complexity/Computability/Complexity/Classes/CountingSimulation/BlockNetworks.lean +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/BlockNetworks.lean @@ -29,8 +29,9 @@ of the counting simulation (milestone H3 of #1833), so the conditional class con `Complexity.Computability.Complexity.Classes.CountingSimulation.Containments` hold assuming only a contraction algorithm. The joint time-and-space forms keep a single deciding machine within time `2 ^ (C * T n / √⌊log₂ (T n)⌋)` and workspace `T n ^ d`, for a time-constructible -`T` with `max n 2 ≤ T n`. The contraction algorithm remains an explicit hypothesis; this -module does not prove the unconditional simulation. +`T` with `max n 2 ≤ T n`. The contraction algorithm remains an explicit hypothesis here; +`Cslib.Complexity.CountingSimulation.NTIME_subset_iUnion_DTIME_two_pow_div_sqrt_log_two` and its +siblings discharge it with the contraction algorithm of Theorem D.1. **The family.** At the input `x`, the clock `t` and the schedule number `σ`, the network (`Cslib.Complexity.CountingSimulation.blockNetworkOf`) is the explicit block-counting network diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/Containments.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/Containments.lean index 81ed2bcab1..02af1ea073 100644 --- a/Complexity/Computability/Complexity/Classes/CountingSimulation/Containments.lean +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/Containments.lean @@ -34,6 +34,9 @@ The network hypothesis is discharged for explicit networks by `Cslib.Complexity.CountingSimulation.networkFamilies_explicitAdmissible`, so the containments hold assuming only a contraction algorithm (`Cslib.Complexity.CountingSimulation.NTIME_subset_iUnion_DTIME_of_contractionAlgorithm` and its +siblings). The contraction algorithm of Theorem D.1 discharges that hypothesis too, which gives the +containments unconditionally +(`Cslib.Complexity.CountingSimulation.NTIME_subset_iUnion_DTIME_two_pow_div_sqrt_log_two` and its siblings). ## Main definitions diff --git a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastSimulation.lean b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastSimulation.lean index 6813d087d7..9e5d30b254 100644 --- a/Complexity/Computability/Complexity/Classes/CountingSimulation/FastSimulation.lean +++ b/Complexity/Computability/Complexity/Classes/CountingSimulation/FastSimulation.lean @@ -30,6 +30,7 @@ deterministic machine in time `2 ^ (C T n / √⌊log₂ (T n)⌋)` and workspac ## References +* [J. Hopcroft, W. Paul, L. Valiant, *On Time Versus Space*][HopcroftPaulValiant1977] * [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Theorem D.1 (p. 53). -/ diff --git a/ComplexityTests/FastSimulation.lean b/ComplexityTests/FastSimulation.lean index 5ff99730da..5f090f9645 100644 --- a/ComplexityTests/FastSimulation.lean +++ b/ComplexityTests/FastSimulation.lean @@ -5,20 +5,48 @@ Authors: Samuel Schlesinger -/ import Complexity.Computability.Complexity.Classes.CountingSimulation.FastSimulation +import Complexity.Computability.Complexity.Classes.Constructible.Log /-! # Nondeterministic and probabilistic time in time `2 ^ O(T / √log T)`: checked instances -The class containments at `T n = n + 2`, which is at least `max n 2`. -/ +The class containments at the clock `T n = (n + 1) ^ 2 + 1`, which is time-constructible and at +least `max n 2`. -/ namespace ComplexityTests.FastSimulation open Cslib.Complexity Cslib.Complexity.CountingSimulation Turing.MultiTapeTM -example : ∀ n, max n 2 ≤ n + 2 := fun n => by omega - -example {α : Type} {enc : α ↪ List Bool} (hT : TimeConstructible fun n => n + 2) : - NTIME enc (fun n => n + 2) ⊆ - ⋃ C, DTIME enc fun n => 2 ^ (C * (n + 2) / Nat.sqrt (Nat.log 2 (n + 2))) := - NTIME_subset_iUnion_DTIME_two_pow_div_sqrt_log_two hT fun n => by omega +/-- The clock `(n + 1) ^ 2 + 1` is time-constructible. -/ +theorem timeConstructible_succ_sq_add_one : TimeConstructible fun n => (n + 1) ^ 2 + 1 := + timeConstructible_succ_sq.comp_of_computableInPolyTime (g := fun m => m + 1) + (ComputableInPolyTime.binary_add.comp + (ComputableInPolyTime.id'.prod (ComputableInPolyTime.const 1))) + fun _ => Nat.le_succ _ + +/-- The clock `(n + 1) ^ 2 + 1` is at least `max n 2`. -/ +theorem max_le_succ_sq_add_one (n : ℕ) : max n 2 ≤ (n + 1) ^ 2 + 1 := + max_le (by nlinarith) (by have := Nat.one_le_pow 2 (n + 1) (Nat.succ_pos n); omega) + +example {α : Type} {enc : α ↪ List Bool} : + NTIME enc (fun n => (n + 1) ^ 2 + 1) ⊆ ⋃ C, DTIME enc fun n => + 2 ^ (C * ((n + 1) ^ 2 + 1) / Nat.sqrt (Nat.log 2 ((n + 1) ^ 2 + 1))) := + NTIME_subset_iUnion_DTIME_two_pow_div_sqrt_log_two timeConstructible_succ_sq_add_one + max_le_succ_sq_add_one + +example {α : Type} {enc : α ↪ List Bool} : + BPTIME enc (fun n => (n + 1) ^ 2 + 1) ⊆ ⋃ C, DTIME enc fun n => + 2 ^ (C * ((n + 1) ^ 2 + 1) / Nat.sqrt (Nat.log 2 ((n + 1) ^ 2 + 1))) := + BPTIME_subset_iUnion_DTIME_two_pow_div_sqrt_log_two timeConstructible_succ_sq_add_one + max_le_succ_sq_add_one + +-- One machine within both bounds, for strict-majority time. +example {α : Type} {enc : α ↪ List Bool} {L : Set α} + (hL : L ∈ PPTIME enc fun n => (n + 1) ^ 2 + 1) : + ∃ C d : ℕ, DecidableInTimeAndSpace L enc + (fun a => 2 ^ (C * (((enc a).length + 1) ^ 2 + 1) / + Nat.sqrt (Nat.log 2 (((enc a).length + 1) ^ 2 + 1)))) + (fun a => (((enc a).length + 1) ^ 2 + 1) ^ d) := + exists_decidableInTimeAndSpace_two_pow_div_sqrt_log_two_of_mem_PPTIME + timeConstructible_succ_sq_add_one max_le_succ_sq_add_one hL end ComplexityTests.FastSimulation