From c33195005eae3e6da0e33ad648b13d3cefaf9b42 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 17:41:28 -0400 Subject: [PATCH 1/9] 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 3942e2e28..3e53d5172 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 000000000..cf45df7f8 --- /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 000000000..08c80a664 --- /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 000000000..28f5dd3f5 --- /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 000000000..5be31a11e --- /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 2/9] 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 3e53d5172..8ea2c2b14 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 000000000..c5aa9c025 --- /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 000000000..1f1322b39 --- /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 3/9] 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 28f5dd3f5..051a95b90 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 d13e8137f..40319f05b 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 5be31a11e..27d867afd 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 31ff8a6ae3d937e6263f8f29045eb1005a0cf90d Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 18:40:37 -0400 Subject: [PATCH 4/9] 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 8ea2c2b14..ffc52e5b2 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 000000000..d44097932 --- /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 000000000..044fad57b --- /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 000000000..998c69fca --- /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 5/9] 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 ffc52e5b2..09b1a7b72 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 d44097932..be7ad962d 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 044fad57b..000000000 --- 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 000000000..3c249ac98 --- /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 998c69fca..65ad05b7a 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 6/9] 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 8ea2c2b14..72282f4bf 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 000000000..422460a8e --- /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 000000000..227e628e6 --- /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 7/9] 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 b11988bdb..db420cdf2 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 000000000..77494386c --- /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 000000000..eb5731330 --- /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 3685abaef55599db833f8bf8e8388dd96b6e5101 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 23:49:30 -0400 Subject: [PATCH 8/9] 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 47ca344c7..61358cafb 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 000000000..bd8a7ffe7 --- /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 000000000..fe79b7965 --- /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 4c076d5efa568f6cc7e6c15595ad61e214b373ef Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Fri, 30 Oct 2026 23:57:33 -0400 Subject: [PATCH 9/9] 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 61358cafb..88a37828a 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 000000000..8eca2827f --- /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 fe79b7965..cdda8d275 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