From 3b23959e532898264c396ea83d148d3291fea381 Mon Sep 17 00:00:00 2001 From: Samuel Schlesinger Date: Tue, 27 Oct 2026 05:48:09 +0000 Subject: [PATCH 1/7] feat(FineGrained,Sparsification): list-level counting sparsification, dense relabeling, and ETH endpoints (#1255) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(FineGrained,Sparsification): list-level counting sparsification, dense relabeling, and ETH endpoints Implements the list-level counting-sparsification step, dense CNF variable relabeling, and fine-grained subexponential-time endpoints for #1226, following the unrefereed manuscript "The Exponential Time Hypothesis is False" (OpenAI, revision 7ef5c0cc, [OpenAI2026ETH]), §2 (Proposition 2.1) and §6.3 (Theorem 1.1 and Proposition 6.4): - `Complexity/Logics/Propositional/CNF/ModelCount/DenseRelabel.lean`: defines `Cslib.Logic.PL.CNF.denseRelabel φ := φ.relabel φ.distinctVars.idxOf` (contrasted with `CNF.compact`, which uses `φ.vars.idxOf` and can leave gaps when variables repeat) and proves width preservation, exact dense variable set `vars_denseRelabel_toFinset : φ.denseRelabel.vars.toFinset = Finset.range φ.numVars`, `modelCountOn_denseRelabel : φ.denseRelabel.modelCountOn (Finset.range φ.numVars) = φ.modelCount`, and polynomial-time computability (`SAT.computableInPolyTime_denseRelabel`). - `Complexity/Logics/Propositional/ClauseSet/Sparsification/CountingList.lean`: defines `Clause.isTautology`, `removeTautologies`, `countingNormalize`, `negatedHeartUnits`, `countingIntro`, `countingChild`, `countingNext`, `countingThreshold`, `Nontautological`, and `CountingNormalized`, and proves exact model-count conservation across normalization (`modelCountOn_countingNormalize`) and across the A/B counting-sparsification branch (`modelCountOn_countingNext`), invariant preservation (`countingNormalized_countingNext`), and variable-support containment (`support_countingNext`). - `Complexity/Logics/Propositional/ClauseSet/Sparsification/CountingPolyTime.lean`: proves `computableInPolyTime_countingThreshold_le`, `computableInPolyTime_isTautology`, `computableInPolyTime_removeTautologies`, `computableInPolyTime_countingNormalize`, and `computableInPolyTime_countingNext`. - `Complexity/Computability/Complexity/FineGrained/CountingScale.lean` and `CountingEndpoint.lean`: proves `exists_two_pow_mul_interpolationScale_le`, `DecidableInSubexpTime.of_interpolationScale`, `DecidableInSubexpTime.of_computableInO_interpolationScale`, `DecidableInSubexpTime.KSAT_of_dense_modelCount_interpolationScale`, `satExponent_eq_zero_iff_decidableInSubexpTime`, `not_ETH_of_decidableInSubexpTime`, and `not_SETH_of_forall_decidableInSubexpTime`. - `ComplexityTests/Computability/Complexity/FineGrained/CountingSparsifyPolyTime.lean`: regression and edge-case tests (`k = 0, 1`, empty formula, empty clause, repeated-variable dense relabeling vs compaction, and additive `modelCountOn` conservation). Refs #1226 AI assistance: written by worker-jetski-58251ede (Jetski agent). * docs: refine Appendix A.1, Proposition 6.4, and DenseRelabel source records (#1255) * chore: regenerate Complexity.lean after merging main --- Complexity.lean | 4 + .../FineGrained/CountingEndpoint.lean | 300 +++++++++++++ .../Complexity/FineGrained/CountingScale.lean | 29 ++ .../CNF/ModelCount/DenseRelabel.lean | 205 +++++++++ .../Sparsification/CountingList.lean | 399 ++++++++++++++++++ .../Sparsification/CountingPolyTime.lean | 100 +++++ .../ClauseSet/Sparsification/List.lean | 17 +- .../ClauseSet/Sparsification/PolyTime.lean | 63 +-- .../FineGrained/CountingSparsifyPolyTime.lean | 116 +++++ 9 files changed, 1203 insertions(+), 30 deletions(-) create mode 100644 Complexity/Computability/Complexity/FineGrained/CountingEndpoint.lean create mode 100644 Complexity/Logics/Propositional/CNF/ModelCount/DenseRelabel.lean create mode 100644 Complexity/Logics/Propositional/ClauseSet/Sparsification/CountingList.lean create mode 100644 Complexity/Logics/Propositional/ClauseSet/Sparsification/CountingPolyTime.lean create mode 100644 ComplexityTests/Computability/Complexity/FineGrained/CountingSparsifyPolyTime.lean diff --git a/Complexity.lean b/Complexity.lean index d43d4cf127..64ee60b849 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -762,6 +762,7 @@ public import Complexity.Computability.Complexity.Fagin.Verifier public import Complexity.Computability.Complexity.FineGrained public import Complexity.Computability.Complexity.FineGrained.Assignment public import Complexity.Computability.Complexity.FineGrained.BruteForce +public import Complexity.Computability.Complexity.FineGrained.CountingEndpoint public import Complexity.Computability.Complexity.FineGrained.CountingScale public import Complexity.Computability.Complexity.FineGrained.ExhaustiveSearch public import Complexity.Computability.Complexity.FineGrained.Exponents @@ -2191,6 +2192,7 @@ public import Complexity.Logics.Propositional.CNF.ImplicationChain public import Complexity.Logics.Propositional.CNF.ImplicationCycle public import Complexity.Logics.Propositional.CNF.ModelCount public import Complexity.Logics.Propositional.CNF.ModelCount.Declared +public import Complexity.Logics.Propositional.CNF.ModelCount.DenseRelabel public import Complexity.Logics.Propositional.CNF.ModelCount.Relabel public import Complexity.Logics.Propositional.CNF.Occurrence public import Complexity.Logics.Propositional.CNF.Parity @@ -2217,8 +2219,10 @@ public import Complexity.Logics.Propositional.ClauseSet.Sparsification.Bounds public import Complexity.Logics.Propositional.ClauseSet.Sparsification.Counting public import Complexity.Logics.Propositional.ClauseSet.Sparsification.CountingDeletion public import Complexity.Logics.Propositional.ClauseSet.Sparsification.CountingInsertions +public import Complexity.Logics.Propositional.ClauseSet.Sparsification.CountingList public import Complexity.Logics.Propositional.ClauseSet.Sparsification.CountingOccurrences public import Complexity.Logics.Propositional.ClauseSet.Sparsification.CountingParameters +public import Complexity.Logics.Propositional.ClauseSet.Sparsification.CountingPolyTime public import Complexity.Logics.Propositional.ClauseSet.Sparsification.CountingSurvival public import Complexity.Logics.Propositional.ClauseSet.Sparsification.CountingTree public import Complexity.Logics.Propositional.ClauseSet.Sparsification.CountingUnits diff --git a/Complexity/Computability/Complexity/FineGrained/CountingEndpoint.lean b/Complexity/Computability/Complexity/FineGrained/CountingEndpoint.lean new file mode 100644 index 0000000000..9fa0c30738 --- /dev/null +++ b/Complexity/Computability/Complexity/FineGrained/CountingEndpoint.lean @@ -0,0 +1,300 @@ +/- +Copyright (c) 2026 OpenAI, Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: OpenAI, Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Complexity.FineGrained.CountingScale +public import Complexity.Computability.Complexity.FineGrained.Reduction +public import Complexity.Logics.Propositional.CNF.ModelCount.DenseRelabel +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.BigO.Plain +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Basic +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Binary + +/-! +# From sublinear interpolation-scale counting to `satExponent k = 0`, `¬ETH`, and `¬SETH` + +Bridges the sublinear exponent `Cslib.Complexity.SubexponentialSAT.interpolationScale` of +Section 6.4, Proposition 6.4 (pp. 43–44) [OpenAI2026ETH] and dense variable relabeling +(`Cslib.Logic.PL.CNF.denseRelabel`) to the fine-grained satisfiability endpoints +(`DecidableInSubexpTime (KSAT k) encCNF CNF.numVars`, `satExponent k = 0`, `¬ETH`, and `¬SETH`). + +### Source relation and library adaptations + +* **Proposition 6.4 (§6.4, pp. 43–44) vs. Theorem 1.1:** Proposition 6.4 states that for every fixed + integer $k \ge 2$, there is a deterministic algorithm that, given a $k$-CNF formula $F$ on + $n \ge 20$ variables with $m$ clauses, outputs the exact number of satisfying assignments of $F$ + in $\{0, 1\}^n$ in time $(n + m)^{O_k(1)} \cdot 2^{O_k(\rho(n))}$, where + $\rho(n) = n (\log\log n)^2 / \sqrt{\log n}$ (formalized for all $n \in \mathbb{N}$ with the + $+16$ domain shift as `interpolationScale n`), whereas Theorem 1.1 states the sharper + single-$\log\log$ headline bound. This module uses Proposition 6.4's `interpolationScale`, which + is already $o(n)$ (`isLittleO_interpolationScale`). +* **Library adaptations:** First-occurrence dense variable numbering + (`CNF.denseRelabel φ = φ.relabel φ.distinctVars.idxOf`, mapping occurring variables onto + `Finset.range φ.numVars` while preserving `φ.modelCount` as + `φ.denseRelabel.modelCountOn (Finset.range φ.numVars)`) and the conditional complexity-class + reduction wrappers below are library adaptations connecting a dense-variable model counter to + `Cslib.Complexity`'s existing `KSAT`, `DecidableInSubexpTime`, `satExponent`, `ETH`, and `SETH` + definitions. This module assumes such a dense counter as a hypothesis and does not construct it. + +Specifically: +1. Any problem decidable in time `O(2 ^ (A₀ * interpolationScale (μ a)) * (|enc a| + 1) ^ d)` is + `DecidableInSubexpTime` with respect to `μ` (`DecidableInSubexpTime.of_interpolationScale`, + `DecidableInSubexpTime.of_computableInO_interpolationScale`). +2. Any model-counting routine `count : CNF ℕ → ℕ` that computes + `ψ.modelCountOn (Finset.range ψ.numVars)` on dense width-`≤ k` formulas (`ψ.vars.toFinset = + Finset.range ψ.numVars`) within time `O(2 ^ (A₀ * interpolationScale ψ.numVars) * (|ψ| + 1) ^ e)` + yields `DecidableInSubexpTime (KSAT k) encCNF CNF.numVars` via polynomial-time dense relabeling + and clause-width checking (`DecidableInSubexpTime.KSAT_of_dense_modelCount_interpolationScale`). +3. Subexponential-time decidability of `KSAT k` is equivalent to `satExponent k = 0` + (`satExponent_eq_zero_iff_decidableInSubexpTime`), refutes `ETH` at `k = 3` + (`not_ETH_of_decidableInSubexpTime`), and refutes `SETH` when established for all `k` + (`not_SETH_of_forall_decidableInSubexpTime`). + +## Main results + +* `Cslib.Logic.PL.CNF.denseRelabel_mem_KSAT_iff`: dense relabeling preserves `KSAT k`. +* `Cslib.Complexity.DecidableInSubexpTime.of_interpolationScale`, + `Cslib.Complexity.DecidableInSubexpTime.of_computableInO_interpolationScale`: subexponential + time from `interpolationScale` runtime bounds. +* `Cslib.Complexity.DecidableInSubexpTime.KSAT_of_dense_modelCount_interpolationScale`: + `DecidableInSubexpTime (KSAT k)` from a dense-variable model counter at `interpolationScale`. +* `Cslib.Complexity.satExponent_eq_zero_of_decidableInSubexpTime`, + `Cslib.Complexity.satExponent_eq_zero_iff_decidableInSubexpTime`: `satExponent k = 0`. +* `Cslib.Complexity.not_ETH_of_decidableInSubexpTime`, + `Cslib.Complexity.not_SETH_of_forall_decidableInSubexpTime`: refutation of `ETH` and `SETH`. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], Section 6.4, + Proposition 6.4, pp. 43–44. +* [R. Impagliazzo, R. Paturi, *On the complexity of k-SAT*][ImpagliazzoPaturi2001] +-/ + +@[expose] public section + +namespace Cslib.Logic.PL.CNF + +open Cslib.Complexity + +/-- Dense variable relabeling preserves membership in `KSAT k` for every `k` (including `k = 0, 1`, +empty formulas, and formulas with repeated literals or gaps in variable numbering). -/ +@[simp] +theorem denseRelabel_mem_KSAT_iff {k : ℕ} {φ : CNF ℕ} : + φ.denseRelabel ∈ KSAT k ↔ φ ∈ KSAT k := by + simp only [KSAT, Set.mem_ofPred_eq, all_length_le_denseRelabel, satisfiable_denseRelabel] + +end Cslib.Logic.PL.CNF + +namespace Cslib.Complexity + +open Cslib.Logic.PL Turing.MultiTapeTM SubexponentialSAT + +/-! ### From `interpolationScale` bounds to `DecidableInSubexpTime` -/ + +/-- Any problem decidable in time `O(2 ^ (A₀ * interpolationScale (μ a)) * (|enc a| + 1) ^ d)` is +decidable in subexponential time in `μ`. -/ +theorem DecidableInSubexpTime.of_interpolationScale {α : Type} {enc : α ↪ List Bool} + {A : Set α} {μ : α → ℕ} {A₀ : ℝ} {d : ℕ} + (h : DecidableInTimeO A enc (fun a => + (2 : ℝ) ^ (A₀ * interpolationScale (μ a)) * (((enc a).length : ℝ) + 1) ^ d)) : + DecidableInSubexpTime A enc μ := by + intro δ hδ + obtain ⟨K, _, hK⟩ := exists_two_pow_mul_interpolationScale_le A₀ hδ + refine ⟨d, h.mono ⌈K⌉₊ (fun a => ?_) fun a => by positivity⟩ + have hceil : K ≤ ⌈K⌉₊ := Nat.le_ceil K + have h1 : (2 : ℝ) ^ (A₀ * interpolationScale (μ a)) ≤ ⌈K⌉₊ * (2 : ℝ) ^ (δ * μ a) := + (hK (μ a)).trans (mul_le_mul_of_nonneg_right hceil (by positivity)) + have h2 := mul_le_mul_of_nonneg_right h1 + (by positivity : (0 : ℝ) ≤ (((enc a).length : ℝ) + 1) ^ d) + linarith + +/-- If `f : α → Bool` decides `A` in `ComputableInO` time bounded by +`c * (2 ^ (A₀ * interpolationScale (μ a)) * (|enc a| + 1) ^ e) + c`, then `A` is decidable in +subexponential time in `μ`. -/ +theorem DecidableInSubexpTime.of_computableInO_interpolationScale {α : Type} {enc : α ↪ List Bool} + {A : Set α} {μ : α → ℕ} {f : α → Bool} {t : α → ℕ} + (hf : ComputableInO f enc encBool t t) (hfA : ∀ a, a ∈ A ↔ f a = true) + {A₀ : ℝ} {c e : ℕ} + (ht : ∀ a, (t a : ℝ) ≤ + c * ((2 : ℝ) ^ (A₀ * interpolationScale (μ a)) * (((enc a).length : ℝ) + 1) ^ e) + c) : + DecidableInSubexpTime A enc μ := by + intro δ hδ + obtain ⟨K, _, hK⟩ := exists_two_pow_mul_interpolationScale_le A₀ hδ + refine DecidableInExpTime.of_computableInO hf hfA (c * ⌈K⌉₊ + c) e fun a => ?_ + refine (ht a).trans ?_ + have hceil : K ≤ ⌈K⌉₊ := Nat.le_ceil K + have hX1 : (1 : ℝ) ≤ (2 : ℝ) ^ (δ * μ a) := Real.one_le_rpow (by norm_num) (by positivity) + have hP1 : (1 : ℝ) ≤ (((enc a).length : ℝ) + 1) ^ e := + one_le_pow₀ (by linarith [(Nat.cast_nonneg (enc a).length : (0 : ℝ) ≤ _)]) + have hXP1 : (1 : ℝ) ≤ (2 : ℝ) ^ (δ * μ a) * (((enc a).length : ℝ) + 1) ^ e := + one_le_mul_of_one_le_of_one_le hX1 hP1 + have hcXP : (c : ℝ) ≤ (c : ℝ) * ((2 : ℝ) ^ (δ * μ a) * (((enc a).length : ℝ) + 1) ^ e) := + le_mul_of_one_le_right (Nat.cast_nonneg c) hXP1 + have h1 : (2 : ℝ) ^ (A₀ * interpolationScale (μ a)) ≤ ⌈K⌉₊ * (2 : ℝ) ^ (δ * μ a) := + (hK (μ a)).trans (mul_le_mul_of_nonneg_right hceil (by positivity)) + have h2 := mul_le_mul_of_nonneg_right h1 + (by positivity : (0 : ℝ) ≤ (((enc a).length : ℝ) + 1) ^ e) + rw [mul_assoc] at h2 + have h3 := mul_le_mul_of_nonneg_left h2 (Nat.cast_nonneg c) + push_cast + linarith + +/-! ### From dense-variable model counting to `DecidableInSubexpTime (KSAT k)` -/ + +open ComputableTidilyInPolyTime in +private theorem computableInPolyTime_pos_nat : + ComputableInPolyTime (fun n : ℕ => decide (0 < n)) encNat encBool := by + have heq : (fun n : ℕ => decide (0 < n)) = fun n => !(n == 0) := by + funext n + cases n <;> rfl + rw [heq] + refine .of_tidy ?_ + fun_prop + +/-- **From dense-variable model counting at `interpolationScale` to subexponential `k`-SAT.** +Suppose `count : CNF ℕ → ℕ` runs in time +`O(2 ^ (A₀ * interpolationScale ψ.numVars) * (|encCNF ψ| + 1) ^ e)` and computes the +declared-variable model count `ψ.modelCountOn (Finset.range ψ.numVars)` on every width-`≤ k` +formula `ψ` whose occurring variables are `Finset.range ψ.numVars`. Then `KSAT k` is decidable in +subexponential time in the number of occurring variables (`CNF.numVars`). -/ +theorem DecidableInSubexpTime.KSAT_of_dense_modelCount_interpolationScale {k : ℕ} + {count : CNF ℕ → ℕ} {t : CNF ℕ → ℕ} + (hcount : ComputableInO count encCNF encNat t t) + (hspec : ∀ ψ : CNF ℕ, (∀ c ∈ ψ, c.length ≤ k) → + ψ.vars.toFinset = Finset.range ψ.numVars → + count ψ = ψ.modelCountOn (Finset.range ψ.numVars)) + {A₀ : ℝ} {c e : ℕ} + (ht : ∀ ψ : CNF ℕ, (t ψ : ℝ) ≤ + c * ((2 : ℝ) ^ (A₀ * interpolationScale ψ.numVars) * (((encCNF ψ).length : ℝ) + 1) ^ e) + c) : + DecidableInSubexpTime (KSAT k) encCNF CNF.numVars := by + have hcount_tidy := hcount.tidy + obtain ⟨dpos, _, hpos⟩ := computableInPolyTime_pos_nat.tidy.exists_degree + have hposO := hpos.inO + obtain ⟨l, hl⟩ := hcount_tidy.length_le_time + have hcomp : ComputableInO (fun ψ : CNF ℕ => decide (0 < count ψ)) encCNF encBool _ _ := + .of_tidy (hposO.comp hcount_tidy) + let B : Set (CNF ℕ) := {ψ | decide (0 < count ψ) = true} + have hB_subexp : DecidableInSubexpTime B encCNF CNF.numVars := by + set A₁ : ℝ := max A₀ 0 * (dpos + 1) + set E : ℕ := e * (dpos + 1) + set C₀ : ℕ := (2 * c + 1) + (2 * l + 1) ^ dpos * (2 * c + 2) ^ dpos + refine DecidableInSubexpTime.of_computableInO_interpolationScale hcomp (fun _ => Iff.rfl) + (A₀ := A₁) (c := C₀) (e := E) fun ψ => ?_ + set L : ℝ := ((encCNF ψ).length : ℝ) + set X₀ : ℝ := (2 : ℝ) ^ (max A₀ 0 * interpolationScale ψ.numVars) + set P₀ : ℝ := (L + 1) ^ e + have hX₀1 : 1 ≤ X₀ := Real.one_le_rpow (by norm_num) + (mul_nonneg (le_max_right _ _) (interpolationScale_nonneg _)) + have hP₀1 : 1 ≤ P₀ := + one_le_pow₀ (by linarith [(Nat.cast_nonneg (encCNF ψ).length : (0 : ℝ) ≤ L)]) + have hXP1 : 1 ≤ X₀ * P₀ := one_le_mul_of_one_le_of_one_le hX₀1 hP₀1 + have hX_mono : (2 : ℝ) ^ (A₀ * interpolationScale ψ.numVars) ≤ X₀ := + Real.rpow_le_rpow_of_exponent_le (by norm_num) + (mul_le_mul_of_nonneg_right (le_max_left _ _) (interpolationScale_nonneg _)) + have ht_le : (t ψ : ℝ) ≤ (2 * c + 1) * (X₀ * P₀) := by + have h1 := ht ψ + have h2 : (2 : ℝ) ^ (A₀ * interpolationScale ψ.numVars) * P₀ ≤ X₀ * P₀ := + mul_le_mul_of_nonneg_right hX_mono (by positivity) + have h3 := mul_le_mul_of_nonneg_left h2 (Nat.cast_nonneg c) + have h4 : (c : ℝ) ≤ c * (X₀ * P₀) := le_mul_of_one_le_right (Nat.cast_nonneg c) hXP1 + linarith + have hlen_le : ((encNat (count ψ)).length : ℝ) + 1 ≤ + (2 * l + 1) * (2 * c + 2) * (X₀ * P₀) := by + have h1 : ((encNat (count ψ)).length : ℝ) ≤ l * ((t ψ : ℝ) + 1) := by + exact_mod_cast hl ψ + have h2 : (t ψ : ℝ) + 1 ≤ (2 * c + 2) * (X₀ * P₀) := by linarith + have h3 := mul_le_mul_of_nonneg_left h2 (Nat.cast_nonneg l) + nlinarith + have hpow_le : (((encNat (count ψ)).length : ℝ) + 1) ^ dpos ≤ + ((2 * l + 1) ^ dpos * (2 * c + 2) ^ dpos) * (X₀ * P₀) ^ dpos := by + calc (((encNat (count ψ)).length : ℝ) + 1) ^ dpos + ≤ ((2 * l + 1) * (2 * c + 2) * (X₀ * P₀)) ^ dpos := + pow_le_pow_left₀ (by positivity) hlen_le dpos + _ = ((2 * l + 1) ^ dpos * (2 * c + 2) ^ dpos) * (X₀ * P₀) ^ dpos := by + rw [mul_pow, mul_pow] + have hXP_dpos : (X₀ * P₀) ^ dpos ≤ (X₀ * P₀) ^ (dpos + 1) := + pow_le_pow_right₀ hXP1 (Nat.le_succ dpos) + have hXP_one : X₀ * P₀ ≤ (X₀ * P₀) ^ (dpos + 1) := + le_self_pow₀ hXP1 (Nat.succ_ne_zero dpos) + have hXP_Rewrite : (X₀ * P₀) ^ (dpos + 1) = + (2 : ℝ) ^ (A₁ * interpolationScale ψ.numVars) * (L + 1) ^ E := by + rw [mul_pow] + congr 1 + · simp only [X₀, A₁, ← Real.rpow_natCast] + rw [← Real.rpow_mul (by norm_num)] + congr 1 + push_cast + ring + · simp only [P₀, E, ← pow_mul] + have htot : (t ψ : ℝ) + (((encNat (count ψ)).length : ℝ) + 1) ^ dpos ≤ + C₀ * ((X₀ * P₀) ^ (dpos + 1)) := by + have ht_le' : (t ψ : ℝ) ≤ (c + 1 + c) * (X₀ * P₀) ^ (dpos + 1) := by + have := mul_le_mul_of_nonneg_left hXP_one (by positivity : (0 : ℝ) ≤ 2 * c + 1) + linarith + have hpow_le' : (((encNat (count ψ)).length : ℝ) + 1) ^ dpos ≤ + ((2 * l + 1) ^ dpos * (2 * c + 2) ^ dpos) * (X₀ * P₀) ^ (dpos + 1) := by + refine hpow_le.trans (mul_le_mul_of_nonneg_left hXP_dpos (by positivity)) + simp only [C₀] + push_cast + linarith + push_cast at htot ⊢ + rw [← hXP_Rewrite] + have hC₀ : (0 : ℝ) ≤ C₀ := Nat.cast_nonneg C₀ + linarith + have hB'_subexp : DecidableInSubexpTime {φ : CNF ℕ | φ.denseRelabel ∈ B} encCNF CNF.numVars := + hB_subexp.of_reduction SAT.computableInPolyTime_denseRelabel (fun _ => Iff.rfl) (c := 1) + fun φ => by rw [CNF.numVars_denseRelabel]; omega + intro δ hδ + have hKSAT_eq : KSAT k = {φ : CNF ℕ | (φ.all fun c => decide (c.length ≤ k)) = true} ∩ + {φ : CNF ℕ | φ.denseRelabel ∈ B} := by + ext φ + simp only [KSAT, B, Set.mem_inter_iff, Set.mem_ofPred_eq, List.all_eq_true, decide_eq_true_eq] + refine and_congr_right fun hφ => ?_ + have hvars : φ.denseRelabel.vars.toFinset = Finset.range φ.denseRelabel.numVars := by + rw [CNF.numVars_denseRelabel, CNF.vars_denseRelabel_toFinset] + rw [hspec φ.denseRelabel ((CNF.all_length_le_denseRelabel φ).mpr hφ) hvars, + CNF.numVars_denseRelabel, CNF.modelCountOn_denseRelabel, CNF.modelCount_pos_iff] + rw [hKSAT_eq] + exact (hB'_subexp δ hδ).inter (computableInPolyTime_all_length_le k) hδ.le + +/-! ### Exponent and ETH/SETH consequences -/ + +/-- If `KSAT k` is decidable in subexponential time in the number of variables, then +`satExponent k = 0`. -/ +theorem satExponent_eq_zero_of_decidableInSubexpTime {k : ℕ} + (h : DecidableInSubexpTime (KSAT k) encCNF CNF.numVars) : + satExponent k = 0 := by + refine le_antisymm (le_of_not_gt fun hpos => ?_) (satExponent_nonneg k) + exact not_decidableInExpTime_of_lt_satExponent (half_pos hpos).le (half_lt_self hpos) + (h (satExponent k / 2) (half_pos hpos)) + +/-- `satExponent k = 0` if and only if `KSAT k` is decidable in subexponential time in the number +of variables. -/ +theorem satExponent_eq_zero_iff_decidableInSubexpTime {k : ℕ} : + satExponent k = 0 ↔ DecidableInSubexpTime (KSAT k) encCNF CNF.numVars := by + refine ⟨fun h δ hδ => ?_, satExponent_eq_zero_of_decidableInSubexpTime⟩ + by_contra hndec + have hle := le_satExponent_of_not_decidableInExpTime hndec + linarith + +/-- Subexponential-time decidability of 3-SAT refutes the Exponential Time Hypothesis (`¬ETH`). -/ +theorem not_ETH_of_decidableInSubexpTime + (h : DecidableInSubexpTime (KSAT 3) encCNF CNF.numVars) : ¬ETH := by + rw [ETH_iff_satExponent_pos, satExponent_eq_zero_of_decidableInSubexpTime h] + exact lt_irrefl 0 + +/-- Subexponential-time decidability of `k`-SAT for all `k` refutes the Strong Exponential Time +Hypothesis (`¬SETH`). -/ +theorem not_SETH_of_forall_decidableInSubexpTime + (h : ∀ k, DecidableInSubexpTime (KSAT k) encCNF CNF.numVars) : ¬SETH := by + rintro hSETH + obtain ⟨k, hk⟩ := hSETH (1 / 2) (by norm_num) + have h12 : (1 : ℝ) - 1 / 2 = 1 / 2 := by norm_num + rw [h12] at hk + exact hk (h k (1 / 2) (by norm_num)) + +end Cslib.Complexity diff --git a/Complexity/Computability/Complexity/FineGrained/CountingScale.lean b/Complexity/Computability/Complexity/FineGrained/CountingScale.lean index fd4d1d6fae..606596edb3 100644 --- a/Complexity/Computability/Complexity/FineGrained/CountingScale.lean +++ b/Complexity/Computability/Complexity/FineGrained/CountingScale.lean @@ -79,4 +79,33 @@ theorem eventually_mul_interpolationScale_le (A : ℝ) {ε : ℝ} (hε : 0 < ε) have h := mul_le_mul_of_nonneg_right hn.le (Nat.cast_nonneg n : (0 : ℝ) ≤ n) simpa only [interpolationScale, mul_div_assoc, mul_assoc, mul_left_comm, mul_comm] using h +/-- The interpolation scale is nonnegative at every natural input. -/ +theorem interpolationScale_nonneg (n : ℕ) : 0 ≤ interpolationScale n := by + unfold interpolationScale + positivity + +/-- For any multiplier `A` and any `ε > 0`, `2 ^ (A * interpolationScale n)` is globally bounded +by a constant multiple of `2 ^ (ε * n)`. -/ +theorem exists_two_pow_mul_interpolationScale_le (A : ℝ) {ε : ℝ} (hε : 0 < ε) : + ∃ K : ℝ, 1 ≤ K ∧ ∀ n : ℕ, (2 : ℝ) ^ (A * interpolationScale n) ≤ K * (2 : ℝ) ^ (ε * n) := by + obtain ⟨N₀, hN₀⟩ := eventually_atTop.mp (eventually_mul_interpolationScale_le A hε) + let K : ℝ := 1 + ∑ m ∈ Finset.range N₀, (2 : ℝ) ^ (A * interpolationScale m) + have hsum_nonneg : 0 ≤ ∑ m ∈ Finset.range N₀, (2 : ℝ) ^ (A * interpolationScale m) := + Finset.sum_nonneg fun m _ => by positivity + have hK1 : 1 ≤ K := by dsimp [K]; linarith + refine ⟨K, hK1, fun n => ?_⟩ + have h2ε : (1 : ℝ) ≤ (2 : ℝ) ^ (ε * n) := Real.one_le_rpow (by norm_num) (by positivity) + by_cases hn : n < N₀ + · have hmem : n ∈ Finset.range N₀ := Finset.mem_range.mpr hn + have hle_sum : (2 : ℝ) ^ (A * interpolationScale n) ≤ + ∑ m ∈ Finset.range N₀, (2 : ℝ) ^ (A * interpolationScale m) := + Finset.single_le_sum (f := fun m => (2 : ℝ) ^ (A * interpolationScale m)) + (fun m _ => by positivity) hmem + have hle_K : (2 : ℝ) ^ (A * interpolationScale n) ≤ K := by dsimp [K]; linarith + exact hle_K.trans (le_mul_of_one_le_right (by positivity) h2ε) + · have hle : A * interpolationScale n ≤ ε * n := hN₀ n (not_lt.mp hn) + have hrpow : (2 : ℝ) ^ (A * interpolationScale n) ≤ (2 : ℝ) ^ (ε * n) := + Real.rpow_le_rpow_of_exponent_le (by norm_num) hle + exact hrpow.trans (le_mul_of_one_le_left (by positivity) hK1) + end Cslib.Complexity.SubexponentialSAT diff --git a/Complexity/Logics/Propositional/CNF/ModelCount/DenseRelabel.lean b/Complexity/Logics/Propositional/CNF/ModelCount/DenseRelabel.lean new file mode 100644 index 0000000000..b16783da0f --- /dev/null +++ b/Complexity/Logics/Propositional/CNF/ModelCount/DenseRelabel.lean @@ -0,0 +1,205 @@ +/- +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.Logics.Propositional.CNF.ModelCount.Declared +public import Complexity.Logics.Propositional.CNF.Compact.Relabel +public import Complexity.Computability.Complexity.Problems.SAT.PolyTime +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Binary +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Plain + +/-! +# Dense variable numbering for CNF formulas + +`CNF.denseRelabel φ` renames each variable of a CNF formula `φ` by its index in `φ.distinctVars`. +Unlike `CNF.compact` (which uses first positions in `φ.vars` and can leave gaps when variables +repeat), `CNF.denseRelabel φ` maps the occurring variables bijectively onto `0, …, φ.numVars - 1` +in order of first occurrence (a library adaptation to the declared-variable model of +[OpenAI2026ETH]). + +The renaming preserves clause lengths, clause count, `numVars`, `Satisfiable`, `modelCount`, and +gives `φ.denseRelabel.modelCountOn (Finset.range φ.numVars) = φ.modelCount`. It is computable in +polynomial time for any variable encoding, both into unary variable indices and into binary +natural-number indices (`encCNF`). + +## Main definitions + +* `Cslib.Logic.PL.CNF.denseRelabel`: rename variables by their positions in `distinctVars`. + +## Main results + +* `Cslib.Logic.PL.CNF.vars_denseRelabel_toFinset`, + `Cslib.Logic.PL.CNF.numVars_denseRelabel`, + `Cslib.Logic.PL.CNF.distinctVars_denseRelabel`: the occurring variables of `φ.denseRelabel` are + `0, …, φ.numVars - 1` in order. +* `Cslib.Logic.PL.CNF.satisfiable_denseRelabel`, + `Cslib.Logic.PL.CNF.modelCount_denseRelabel`, + `Cslib.Logic.PL.CNF.modelCountOn_denseRelabel`: satisfiability and model counts are preserved. +* `Cslib.Logic.PL.CNF.computableInPolyTime_denseRelabel_unary`, + `Cslib.Logic.PL.CNF.computableInPolyTime_denseRelabel`, + `Cslib.Complexity.SAT.computableInPolyTime_denseRelabel`: dense relabeling is computable in + polynomial time. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH] +-/ + +@[expose] public section + +namespace Cslib.Logic.PL.CNF + +open Turing.MultiTapeTM ComputableTidilyInPolyTime + +variable {V : Type*} [DecidableEq V] + +/-- Rename each variable of `φ` by its index in `φ.distinctVars`, producing dense variable indices +`0, …, φ.numVars - 1`. -/ +def denseRelabel (φ : CNF V) : CNF ℕ := + φ.relabel φ.distinctVars.idxOf + +/-- Dense relabeling preserves all clause lengths in order. -/ +@[simp] +theorem map_length_denseRelabel (φ : CNF V) : + φ.denseRelabel.map List.length = φ.map List.length := by + simp [denseRelabel, CNF.relabel, Clause.relabel, List.map_map, Function.comp_def] + +/-- Dense relabeling preserves the number of clauses. -/ +@[simp] +theorem length_denseRelabel (φ : CNF V) : + φ.denseRelabel.length = φ.length := by + simp [denseRelabel, CNF.relabel] + +/-- Dense relabeling preserves any uniform upper bound on clause lengths. -/ +theorem all_length_le_denseRelabel {k : ℕ} (φ : CNF V) : + (∀ c ∈ φ.denseRelabel, c.length ≤ k) ↔ ∀ c ∈ φ, c.length ≤ k := by + simp [denseRelabel, CNF.relabel] + +/-- The occurring variables of `φ.denseRelabel` are `Finset.range φ.numVars`. -/ +@[simp] +theorem vars_denseRelabel_toFinset (φ : CNF V) : + φ.denseRelabel.vars.toFinset = Finset.range φ.numVars := + vars_dense_relabel φ + +/-- Every variable occurring in `φ.denseRelabel` is strictly below `φ.numVars`. -/ +theorem mem_vars_denseRelabel_lt (φ : CNF V) {v : ℕ} (hv : v ∈ φ.denseRelabel.vars) : + v < φ.numVars := by + have hv' : v ∈ φ.denseRelabel.vars.toFinset := List.mem_toFinset.mpr hv + rwa [vars_denseRelabel_toFinset, Finset.mem_range] at hv' + +/-- Dense relabeling preserves the number of distinct occurring variables. -/ +@[simp] +theorem numVars_denseRelabel (φ : CNF V) : + φ.denseRelabel.numVars = φ.numVars := by + rw [numVars, vars_denseRelabel_toFinset, Finset.card_range] + +private theorem map_idxOf_of_nodup {α : Type*} [DecidableEq α] (l : List α) (hl : l.Nodup) : + l.map l.idxOf = List.range l.length := by + refine List.ext_getElem (by simp) fun i h₁ h₂ => ?_ + simp only [List.getElem_map, List.getElem_range] + exact List.Nodup.idxOf_getElem hl i _ + +/-- The distinct variables of `φ.denseRelabel` in order of first occurrence are +`0, …, φ.numVars - 1`. -/ +@[simp] +theorem distinctVars_denseRelabel (φ : CNF V) : + φ.denseRelabel.distinctVars = List.range φ.numVars := by + have hinj : Set.InjOn φ.distinctVars.idxOf {v | v ∈ φ.vars} := + fun _ ha _ _ h => (List.idxOf_inj (mem_distinctVars.mpr ha)).mp h + rw [denseRelabel, distinctVars_relabel_of_injOn φ φ.distinctVars.idxOf hinj, + map_idxOf_of_nodup φ.distinctVars φ.nodup_distinctVars, length_distinctVars] + +/-- Reading original variable values at their `distinctVars` indices recovers the evaluation of +`φ`. -/ +theorem eval_denseRelabel (φ : CNF V) (σ : V → Bool) : + φ.denseRelabel.eval (fun i => φ.distinctVars[i]?.elim false σ) = φ.eval σ := by + rw [denseRelabel, CNF.eval_relabel] + apply CNF.eval_congr + intro v hv + have hv' : v ∈ φ.distinctVars := mem_distinctVars.mpr hv + simp [List.getElem?_idxOf hv'] + +/-- Dense relabeling preserves satisfiability. -/ +@[simp] +theorem satisfiable_denseRelabel (φ : CNF V) : + φ.denseRelabel.Satisfiable ↔ φ.Satisfiable := by + constructor + · rintro ⟨σ, hσ⟩ + exact ⟨σ ∘ φ.distinctVars.idxOf, by simpa only [denseRelabel, CNF.eval_relabel] using hσ⟩ + · rintro ⟨σ, hσ⟩ + exact ⟨fun i => φ.distinctVars[i]?.elim false σ, (eval_denseRelabel φ σ).trans hσ⟩ + +/-- Dense relabeling preserves the occurring-variable model count. -/ +@[simp] +theorem modelCount_denseRelabel (φ : CNF V) : + φ.denseRelabel.modelCount = φ.modelCount := + modelCount_dense_relabel φ + +/-- Dense relabeling preserves the declared-variable model count on `Finset.range φ.numVars`. -/ +theorem modelCountOn_denseRelabel (φ : CNF V) : + φ.denseRelabel.modelCountOn (Finset.range φ.numVars) = φ.modelCount := + modelCountOn_dense_relabel φ + +section PolyTime + +variable {W : Type} [DecidableEq W] + +attribute [local fun_prop] ComputableInPolyTime.tidy in +/-- Dense relabeling is polynomial-time into unary variable indices for any variable encoding. -/ +@[fun_prop] +theorem computableInPolyTime_denseRelabel_unary (enc : W ↪ List Bool) : + ComputableInPolyTime (CNF.denseRelabel (V := W)) + (encList (encList (encPair enc encBool))) + (encList (encList (encPair encUnary encBool))) := by + have hd : ComputableTidilyInPolyTime (CNF.distinctVars (V := W)) + (encList (encList (encPair enc encBool))) (encList enc) := + (CNF.computableInPolyTime_distinctVars enc).tidy + refine .of_tidy ?_ + unfold CNF.denseRelabel CNF.relabel Clause.relabel Prod.map + fun_prop + +attribute [local fun_prop] ComputableInPolyTime.tidy in +/-- Dense relabeling is polynomial-time into binary natural-number variable indices (`encCNF`) for +any variable encoding. -/ +@[fun_prop] +theorem computableInPolyTime_denseRelabel (enc : W ↪ List Bool) : + ComputableInPolyTime (CNF.denseRelabel (V := W)) + (encList (encList (encPair enc encBool))) + Cslib.Complexity.encCNF := by + have h1 : ComputableTidilyInPolyTime (CNF.denseRelabel (V := W)) + (encList (encList (encPair enc encBool))) + (encList (encList (encPair encUnary encBool))) := + (computableInPolyTime_denseRelabel_unary enc).tidy + have h2 : ComputableTidilyInPolyTime (CNF.relabel (id : ℕ → ℕ)) + (encList (encList (encPair encUnary encBool))) + (encList (encList (encPair encNat encBool))) := + (CNF.computableInPolyTime_relabel (encV := encUnary) (encW := encNat) + (.of_tidy unary_to_binary)).tidy + have heq : (CNF.denseRelabel (V := W)) = fun φ => (CNF.denseRelabel φ).relabel id := by + funext φ + exact (CNF.relabel_id _).symm + rw [heq] + exact .of_tidy (h2.comp h1) + +end PolyTime + +end Cslib.Logic.PL.CNF + +namespace Cslib.Complexity.SAT + +open Cslib.Logic.PL Turing.MultiTapeTM + +/-- Dense variable relabeling on `CNF ℕ` is computable in polynomial time. -/ +@[fun_prop] +theorem computableInPolyTime_denseRelabel : + ComputableInPolyTime (CNF.denseRelabel (V := ℕ)) encCNF encCNF := + CNF.computableInPolyTime_denseRelabel encNat + +end Cslib.Complexity.SAT diff --git a/Complexity/Logics/Propositional/ClauseSet/Sparsification/CountingList.lean b/Complexity/Logics/Propositional/ClauseSet/Sparsification/CountingList.lean new file mode 100644 index 0000000000..208131c33d --- /dev/null +++ b/Complexity/Logics/Propositional/ClauseSet/Sparsification/CountingList.lean @@ -0,0 +1,399 @@ +/- +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.Logics.Propositional.ClauseSet.Sparsification.Counting +public import Complexity.Logics.Propositional.ClauseSet.Sparsification.List + +/-! +# Counting sparsification on CNF formulas + +List-level operations for the counting sparsification refinement of Appendix A.1 [OpenAI2026ETH]: +tautology removal (`removeTautologies`), nontautological normalization (`countingNormalize`), +singleton negated-heart unit clauses (`negatedHeartUnits`), the disjoint counting children +(`countingChild`), and one deterministic counting-sparsification step (`countingNext`). + +Initial preprocessing (`countingNormalize φ = normalize (removeTautologies φ)`) removes tautologies, +deduplicates literals and clauses, and reduces to a subset-minimal antichain. At each branching step +on a heavy heart `H` satisfying `deg_c(H) ≥ θ_{c - |H|}` (equation (A.1), p. 44, with the +unnumbered threshold schedule `θ j = M ^ j`), the `false` (A) branch adds `H` while the `true` (B) +branch adds both the petals `petals ψ c H` and the singleton negated-heart units +`negatedHeartUnits H` [OpenAI2026ETH, Appendix A.1, equation (A.2), pp. 44–45]. Applying +`dedupClauses` before `reduce` restores the duplicate-free antichain (`CountingNormalized`) across +steps without re-running tautology removal, while computing the exact clause-set operation +`ClauseSet.Sparsification.countingChild` (`toClauseSet_countingChild`) and conserving the +declared-variable model count (`modelCountOn_countingChild`). + +## Main definitions + +* `Cslib.Logic.PL.CNF.Sparsification.Clause.isTautology`, + `Cslib.Logic.PL.CNF.Sparsification.removeTautologies`, + `Cslib.Logic.PL.CNF.Sparsification.countingNormalize`: tautology removal and normalization. +* `Cslib.Logic.PL.CNF.Sparsification.negatedHeartUnits`, + `Cslib.Logic.PL.CNF.Sparsification.countingIntro`, + `Cslib.Logic.PL.CNF.Sparsification.countingChild`, + `Cslib.Logic.PL.CNF.Sparsification.countingNext`: the list-level counting step. +* `Cslib.Logic.PL.CNF.Sparsification.countingThreshold`: the counting thresholds `θ j = M ^ j`. +* `Cslib.Logic.PL.CNF.Sparsification.Nontautological`, + `Cslib.Logic.PL.CNF.Sparsification.CountingNormalized`: the nontautological normal form. + +## Main results + +* `Cslib.Logic.PL.CNF.Sparsification.eval_countingNormalize`, + `Cslib.Logic.PL.CNF.Sparsification.modelCountOn_countingNormalize`, + `Cslib.Logic.PL.CNF.Sparsification.countingNormalized_countingNormalize`: nontautological + normalization preserves evaluation and declared-variable model counts. +* `Cslib.Logic.PL.CNF.Sparsification.toClauseSet_countingChild`, + `Cslib.Logic.PL.CNF.Sparsification.toClauseSet_countingNext`: the list-level counting children + compute the clause-set counting children. +* `Cslib.Logic.PL.CNF.Sparsification.modelCountOn_countingChild`, + `Cslib.Logic.PL.CNF.Sparsification.modelCountOn_countingNext`: additive conservation of + `CNF.modelCountOn` across both branches. +* `Cslib.Logic.PL.CNF.Sparsification.countingNormalized_countingChild`, + `Cslib.Logic.PL.CNF.Sparsification.countingNormalized_countingNext`: the counting step preserves + the nontautological normal form. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*, Appendix A.1][OpenAI2026ETH] +-/ + +@[expose] public section + +namespace Cslib.Logic.PL.CNF.Sparsification + +open Finset ClauseSet.Sparsification +open scoped List + +variable {V : Type*} [DecidableEq V] + +/-! ### Tautology removal and counting normalization -/ + +/-- Whether a clause contains both a literal and its negation. -/ +def Clause.isTautology (C : Clause V) : Bool := + C.any fun l => C.any (· == Std.Sat.Literal.negate l) + +/-- Remove tautological clauses from a CNF formula. -/ +def removeTautologies (φ : CNF V) : CNF V := + φ.filter fun C => !Clause.isTautology C + +/-- Bring a formula into nontautological normal form: remove tautological clauses, deduplicate +literals and clauses, and reduce by strict subset containment. -/ +def countingNormalize (φ : CNF V) : CNF V := + normalize (removeTautologies φ) + +/-- Singleton clauses `[-l]` for each literal `l` of a heart `H`. -/ +def negatedHeartUnits (H : Clause V) : CNF V := + H.map fun l => [Std.Sat.Literal.negate l] + +/-- The clauses introduced by a counting-sparsification branch: the heart (`false`) or the petals +together with the singleton negated-heart units (`true`). -/ +def countingIntro (ψ : CNF V) (c : ℕ) (H : Clause V) (b : Bool) : CNF V := + if b then petals ψ c H ++ negatedHeartUnits H else [H] + +/-- A counting child of a branching node: append the introduced clauses, deduplicate clauses with +identical literal sets, and reduce by strict subset containment. -/ +def countingChild (ψ : CNF V) (c : ℕ) (H : Clause V) (b : Bool) : CNF V := + reduce (dedupClauses (ψ ++ countingIntro ψ c H b)) + +/-- One deterministic counting-sparsification step using the computed choice `choice θ k ψ`. -/ +def countingNext (θ : ℕ → ℕ) (k : ℕ) (ψ : CNF V) (b : Bool) : CNF V := + countingChild ψ (choice θ k ψ).1 (choice θ k ψ).2 b + +/-- The counting-sparsification threshold schedule `θ j = M ^ j` (defined on p. 44 of +[OpenAI2026ETH, Appendix A.1] for `0 ≤ j < k`, extended here to a total function on `ℕ`, and used +in the heavy-heart branching inequality `deg_c(H) ≥ θ_{c - |H|}` of equation (A.1)). -/ +def countingThreshold (M j : ℕ) : ℕ := M ^ j + +/-- For `1 ≤ M`, every counting threshold is positive. -/ +theorem one_le_countingThreshold {M : ℕ} (hM : 1 ≤ M) (j : ℕ) : + 1 ≤ countingThreshold M j := + Nat.one_le_pow j M hM + +/-! ### Nontautological normal form -/ + +/-- A CNF formula is *nontautological* if no clause contains both a literal and its negation. -/ +def Nontautological (ψ : CNF V) : Prop := + ∀ C ∈ ψ, ∀ l ∈ C, Std.Sat.Literal.negate l ∉ C + +/-- The normal form for list-level counting sparsification at width `k`: `Normalized k ψ` together +with `Nontautological ψ`. -/ +structure CountingNormalized (k : ℕ) (ψ : CNF V) : Prop extends Normalized k ψ where + nontautological : Nontautological ψ + +theorem Clause.isTautology_eq_true_iff {C : Clause V} : + Clause.isTautology C = true ↔ ∃ l ∈ C, Std.Sat.Literal.negate l ∈ C := by + simp [Clause.isTautology] + +theorem Clause.isTautology_eq_false_iff {C : Clause V} : + Clause.isTautology C = false ↔ ∀ l ∈ C, Std.Sat.Literal.negate l ∉ C := by + rw [← Bool.not_eq_true, Clause.isTautology_eq_true_iff] + push Not + rfl + +/-- Any tautological clause evaluates to `true` under every assignment. -/ +theorem eval_eq_true_of_isTautology {C : Clause V} (h : Clause.isTautology C = true) + (σ : V → Bool) : C.eval σ = true := by + obtain ⟨⟨v, b⟩, hl, hneg⟩ := Clause.isTautology_eq_true_iff.mp h + rw [Clause.eval_eq_true_iff] + by_cases hσ : σ v = b + · exact ⟨(v, b), hl, hσ⟩ + · refine ⟨Std.Sat.Literal.negate (v, b), hneg, ?_⟩ + cases b <;> cases hσ' : σ v <;> simp_all [Std.Sat.Literal.negate] + +/-- `removeTautologies φ` is a sublist of `φ`. -/ +theorem removeTautologies_sublist (φ : CNF V) : removeTautologies φ <+ φ := + List.filter_sublist + +/-- Removing tautological clauses preserves evaluation under every assignment. -/ +@[simp] +theorem eval_removeTautologies (φ : CNF V) (σ : V → Bool) : + (removeTautologies φ).eval σ = φ.eval σ := by + rw [Bool.eq_iff_iff, CNF.eval_eq_true_iff, CNF.eval_eq_true_iff] + constructor + · intro h C hC + rcases Bool.eq_false_or_eq_true (Clause.isTautology C) with htrue | hfalse + · exact eval_eq_true_of_isTautology htrue σ + · exact h C (List.mem_filter.mpr ⟨hC, by simp [hfalse]⟩) + · intro h C hC + exact h C (List.mem_filter.mp hC).1 + +/-- Removing tautological clauses preserves the declared-variable model count. -/ +@[simp] +theorem modelCountOn_removeTautologies (φ : CNF V) (declared : Finset V) : + (removeTautologies φ).modelCountOn declared = φ.modelCountOn declared := by + simp only [CNF.modelCountOn, eval_removeTautologies] + +/-- `removeTautologies φ` is nontautological. -/ +theorem nontautological_removeTautologies (φ : CNF V) : + Nontautological (removeTautologies φ) := by + intro C hC + have h := (List.mem_filter.mp hC).2 + rw [Bool.not_eq_true'] at h + exact Clause.isTautology_eq_false_iff.mp h + +/-- The clause set of `countingNormalize φ` has the same satisfying assignments as `φ`. -/ +theorem sat_toClauseSet_countingNormalize_iff (φ : CNF V) (σ : V → Bool) : + (countingNormalize φ).toClauseSet.Sat σ ↔ φ.eval σ = true := by + rw [countingNormalize, sat_toClauseSet_normalize_iff, eval_removeTautologies] + +/-- `countingNormalize φ` evaluates identically to `φ` under every assignment. -/ +@[simp] +theorem eval_countingNormalize (φ : CNF V) (σ : V → Bool) : + (countingNormalize φ).eval σ = φ.eval σ := by + rw [Bool.eq_iff_iff, ← CNF.sat_toClauseSet_iff, sat_toClauseSet_countingNormalize_iff] + +/-- `countingNormalize φ` preserves the declared-variable model count. -/ +@[simp] +theorem modelCountOn_countingNormalize (φ : CNF V) (declared : Finset V) : + (countingNormalize φ).modelCountOn declared = φ.modelCountOn declared := by + simp only [CNF.modelCountOn, eval_countingNormalize] + +/-- Every clause of `countingNormalize φ` has the literals of a nontautological clause of `φ`. -/ +theorem exists_of_mem_countingNormalize {φ : CNF V} {C : Clause V} + (hC : C ∈ countingNormalize φ) : + ∃ D ∈ removeTautologies φ, C.toFinset = D.toFinset := by + have hsub : countingNormalize φ <+ (removeTautologies φ).map dedup := + (reduce_sublist _).trans (dedupClauses_sublist _) + obtain ⟨D, hD, rfl⟩ := List.mem_map.mp (hsub.subset hC) + exact ⟨D, hD, toFinset_dedup D⟩ + +/-- The number of clauses of `countingNormalize φ` is at most that of `φ`. -/ +theorem length_countingNormalize_le (φ : CNF V) : + (countingNormalize φ).length ≤ φ.length := + ((reduce_sublist _).trans (dedupClauses_sublist _)).length_le.trans + ((List.length_map _).le.trans (removeTautologies_sublist φ).length_le) + +/-- Every variable occurring in `countingNormalize φ` occurs in `φ`. -/ +theorem mem_vars_of_mem_countingNormalize {φ : CNF V} : + ∀ C ∈ (countingNormalize φ).toClauseSet, ∀ l ∈ C, l.1 ∈ φ.vars.toFinset := by + intro C hC l hl + obtain ⟨C', hC', rfl⟩ := mem_toClauseSet.mp hC + obtain ⟨D, hD, hDeq⟩ := exists_of_mem_countingNormalize hC' + rw [hDeq, List.mem_toFinset] at hl + have hDφ := (removeTautologies_sublist φ).subset hD + exact List.mem_toFinset.mpr (List.mem_map.mpr ⟨l, List.mem_flatten.mpr ⟨D, hDφ, hl⟩, rfl⟩) + +/-- Every formula of width `k` is brought into nontautological normal form by +`countingNormalize`. -/ +theorem countingNormalized_countingNormalize {k : ℕ} {φ : CNF V} + (hφ : ∀ C ∈ φ, C.length ≤ k) : CountingNormalized k (countingNormalize φ) := by + refine ⟨normalized_normalize fun C hC => hφ C ((removeTautologies_sublist φ).subset hC), + fun C hC l hl hneg => ?_⟩ + obtain ⟨D, hD, hCD⟩ := exists_of_mem_countingNormalize hC + have hlD : l ∈ D := List.mem_toFinset.mp (hCD ▸ List.mem_toFinset.mpr hl) + have hnegD : Std.Sat.Literal.negate l ∈ D := + List.mem_toFinset.mp (hCD ▸ List.mem_toFinset.mpr hneg) + exact nontautological_removeTautologies φ D hD l hlD hnegD + +/-! ### Agreement with clause-set counting operations -/ + +/-- The clause set of `negatedHeartUnits H` is `ClauseSet.Sparsification.negatedHeartUnits`. -/ +theorem toClauseSet_negatedHeartUnits (H : Clause V) : + (negatedHeartUnits H).toClauseSet = + ClauseSet.Sparsification.negatedHeartUnits H.toFinset := by + ext C + simp [negatedHeartUnits, ClauseSet.Sparsification.negatedHeartUnits, CNF.toClauseSet] + +/-- The list-level counting children compute the clause-set counting children. -/ +theorem toClauseSet_countingChild {ψ : CNF V} (hnd : ∀ C ∈ ψ, C.Nodup) (c : ℕ) (H : Clause V) + (b : Bool) : + (countingChild ψ c H b).toClauseSet = + ClauseSet.Sparsification.countingChild ψ.toClauseSet c H.toFinset b := by + rw [countingChild, toClauseSet_reduce, toClauseSet_dedupClauses, toClauseSet_append] + cases b + · simp [countingIntro, ClauseSet.Sparsification.countingChild, + ClauseSet.Sparsification.child, ClauseSet.Sparsification.intro, CNF.toClauseSet] + · simp only [countingIntro, ite_true, toClauseSet_append, toClauseSet_petals hnd, + toClauseSet_negatedHeartUnits, ClauseSet.Sparsification.countingChild, Finset.union_assoc] + +/-- The clause set of `countingNext θ k ψ b` equals the clause-set counting child for the computed +choice. -/ +theorem toClauseSet_countingNext {θ : ℕ → ℕ} {k : ℕ} {ψ : CNF V} (h : Normalized k ψ) (b : Bool) : + (countingNext θ k ψ b).toClauseSet = ClauseSet.Sparsification.countingChild ψ.toClauseSet + (choice θ k ψ).1 (choice θ k ψ).2.toFinset b := + toClauseSet_countingChild h.nodup _ _ b + +/-- A formula and the canonical CNF of its clause set have the same declared-variable model +count. -/ +theorem modelCountOn_toCNF_toClauseSet (ψ : CNF V) (declared : Finset V) : + ψ.toClauseSet.toCNF.modelCountOn declared = ψ.modelCountOn declared := by + simp only [CNF.modelCountOn] + congr 1 + ext σ + simp only [Finset.mem_filter, Finset.mem_univ, true_and, ClauseSet.eval_toCNF_eq_true_iff, + CNF.sat_toClauseSet_iff] + +/-- **Additive model-count conservation for `countingChild`.** On any formula `ψ` without repeated +literals in its clauses, the declared-variable model count equals the sum of the counts of its two +counting children. -/ +theorem modelCountOn_countingChild {ψ : CNF V} (hnd : ∀ C ∈ ψ, C.Nodup) (c : ℕ) (H : Clause V) + (declared : Finset V) : + ψ.modelCountOn declared = + (countingChild ψ c H false).modelCountOn declared + + (countingChild ψ c H true).modelCountOn declared := by + rw [← modelCountOn_toCNF_toClauseSet ψ, + ← modelCountOn_toCNF_toClauseSet (countingChild ψ c H false), + ← modelCountOn_toCNF_toClauseSet (countingChild ψ c H true), + toClauseSet_countingChild hnd c H false, + toClauseSet_countingChild hnd c H true] + exact ClauseSet.Sparsification.modelCountOn_countingChild ψ.toClauseSet c H.toFinset declared + +/-- **Additive model-count conservation for `countingNext`.** On any `k`-normalized formula `ψ`, +one deterministic counting-sparsification step splits the declared-variable model count additively +across its `false` and `true` branches. -/ +theorem modelCountOn_countingNext {θ : ℕ → ℕ} {k : ℕ} {ψ : CNF V} (h : Normalized k ψ) + (declared : Finset V) : + ψ.modelCountOn declared = + (countingNext θ k ψ false).modelCountOn declared + + (countingNext θ k ψ true).modelCountOn declared := + modelCountOn_countingChild h.nodup _ _ declared + +/-! ### Preservation of normal form under the counting step -/ + +omit [DecidableEq V] in +private theorem literal_negate_ne_self (l : Literal V) : Std.Sat.Literal.negate l ≠ l := by + rcases l with ⟨v, b⟩ + cases b <;> simp [Std.Sat.Literal.negate] + +/-- The counting children preserve `CountingNormalized k` whenever `1 ≤ k` and the heart `H` is a +duplicate-free nontautological clause of width at most `k`. -/ +theorem countingNormalized_countingChild {k : ℕ} {ψ : CNF V} (hk : 1 ≤ k) + (h : CountingNormalized k ψ) {c : ℕ} {H : Clause V} (hH_nodup : H.Nodup) + (hH_len : H.length ≤ k) (hH_nontaut : ∀ l ∈ H, Std.Sat.Literal.negate l ∉ H) (b : Bool) : + CountingNormalized k (countingChild ψ c H b) := by + have hsub : countingChild ψ c H b <+ ψ ++ countingIntro ψ c H b := + (reduce_sublist _).trans (dedupClauses_sublist _) + have hintro : ∀ C ∈ countingIntro ψ c H b, + C.Nodup ∧ C.length ≤ k ∧ ∀ l ∈ C, Std.Sat.Literal.negate l ∉ C := by + intro C hC + cases b + · simp only [countingIntro, Bool.false_eq_true, ite_false, List.mem_singleton] at hC + subst hC + exact ⟨hH_nodup, hH_len, hH_nontaut⟩ + · simp only [countingIntro, ite_true, List.mem_append, petals, negatedHeartUnits, + List.mem_map] at hC + rcases hC with ⟨D, hD, rfl⟩ | ⟨l₀, _, rfl⟩ + · have hDψ : D ∈ ψ := List.filter_sublist.subset hD + refine ⟨(h.nodup D hDψ).filter _, ?_, fun l hl hneg => ?_⟩ + · exact (List.length_filter_le _ D).trans (h.length_le D hDψ) + · exact h.nontautological D hDψ l (List.mem_filter.mp hl).1 (List.mem_filter.mp hneg).1 + · refine ⟨List.nodup_singleton _, by simpa using hk, fun l hl hneg => ?_⟩ + rw [List.mem_singleton] at hl hneg + subst hl + exact literal_negate_ne_self _ hneg + refine ⟨⟨fun C hC => ?_, fun C hC => ?_, ?_, ?_⟩, fun C hC => ?_⟩ + · rcases List.mem_append.mp (hsub.subset hC) with hC | hC + · exact h.nodup C hC + · exact (hintro C hC).1 + · rcases List.mem_append.mp (hsub.subset hC) with hC | hC + · exact h.length_le C hC + · exact (hintro C hC).2.1 + · exact ((reduce_sublist _).map _).nodup (nodup_toFinset_dedupClauses _) + · rw [countingChild, toClauseSet_reduce] + exact reduced_reduce _ + · rcases List.mem_append.mp (hsub.subset hC) with hC | hC + · exact h.nontautological C hC + · exact (hintro C hC).2.2 + +private theorem choice_snd_sublist_or_nil (θ : ℕ → ℕ) (k : ℕ) (ψ : CNF V) : + (choice θ k ψ).2 = [] ∨ ∃ C ∈ ψ, (choice θ k ψ).2 <+ C := by + unfold choice + rcases hhead : ((List.range ((k + 1) * (k + 1) + 1)).flatMap fun r => + ((candidates k ψ).filter (heavy θ ψ)).filter fun x => rank k x == r).head? with _ | x + · exact Or.inl rfl + · refine Or.inr ?_ + have hx := List.mem_of_mem_head? hhead + obtain ⟨r, -, hxr⟩ := List.mem_flatMap.mp hx + have hxcand := (List.mem_filter.mp (List.mem_filter.mp hxr).1).1 + obtain ⟨C, hC, hxH⟩ := List.mem_flatMap.mp hxcand + obtain ⟨H, hH, rfl⟩ := List.mem_map.mp hxH + exact ⟨C, hC, mem_hearts hH⟩ + +/-- **One counting-sparsification step preserves `CountingNormalized k`.** -/ +theorem countingNormalized_countingNext (θ : ℕ → ℕ) {k : ℕ} (hk : 1 ≤ k) {ψ : CNF V} + (h : CountingNormalized k ψ) (b : Bool) : + CountingNormalized k (countingNext θ k ψ b) := by + rcases choice_snd_sublist_or_nil θ k ψ with hnil | ⟨C, hC, hsub⟩ + · refine countingNormalized_countingChild hk h ?_ ?_ ?_ b <;> simp [hnil] + · refine countingNormalized_countingChild hk h (hsub.nodup (h.nodup C hC)) + (hsub.length_le.trans (h.length_le C hC)) + (fun l hl hneg => h.nontautological C hC l (hsub.subset hl) (hsub.subset hneg)) b + +/-- Every literal occurring in `countingChild ψ c H b` occurs in `ψ` or has its variable in `H`. -/ +theorem support_countingChild {ψ : CNF V} {c : ℕ} {H : Clause V} {declared : Finset V} + (hψ : ∀ C ∈ ψ, ∀ l ∈ C, l.1 ∈ declared) (hH : ∀ l ∈ H, l.1 ∈ declared) (b : Bool) : + ∀ C ∈ countingChild ψ c H b, ∀ l ∈ C, l.1 ∈ declared := by + have hsub : countingChild ψ c H b <+ ψ ++ countingIntro ψ c H b := + (reduce_sublist _).trans (dedupClauses_sublist _) + intro C hC l hl + rcases List.mem_append.mp (hsub.subset hC) with hC | hC + · exact hψ C hC l hl + · cases b + · simp only [countingIntro, Bool.false_eq_true, ite_false, List.mem_singleton] at hC + subst hC + exact hH l hl + · simp only [countingIntro, ite_true, List.mem_append, petals, negatedHeartUnits, + List.mem_map] at hC + rcases hC with ⟨D, hD, rfl⟩ | ⟨l₀, hl₀, rfl⟩ + · exact hψ D (List.filter_sublist.subset hD) l (List.mem_filter.mp hl).1 + · rw [List.mem_singleton] at hl + subst hl + exact hH l₀ hl₀ + +/-- Every literal occurring in `countingNext θ k ψ b` has its variable in `declared` whenever `ψ` +does. -/ +theorem support_countingNext (θ : ℕ → ℕ) (k : ℕ) {ψ : CNF V} {declared : Finset V} + (hψ : ∀ C ∈ ψ, ∀ l ∈ C, l.1 ∈ declared) (b : Bool) : + ∀ C ∈ countingNext θ k ψ b, ∀ l ∈ C, l.1 ∈ declared := by + refine support_countingChild hψ (fun l hl => ?_) b + rcases choice_snd_sublist_or_nil θ k ψ with hnil | ⟨C, hC, hsub⟩ + · simp [hnil] at hl + · exact hψ C hC l (hsub.subset hl) + +end Cslib.Logic.PL.CNF.Sparsification diff --git a/Complexity/Logics/Propositional/ClauseSet/Sparsification/CountingPolyTime.lean b/Complexity/Logics/Propositional/ClauseSet/Sparsification/CountingPolyTime.lean new file mode 100644 index 0000000000..b4041e3106 --- /dev/null +++ b/Complexity/Logics/Propositional/ClauseSet/Sparsification/CountingPolyTime.lean @@ -0,0 +1,100 @@ +/- +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.Logics.Propositional.ClauseSet.Sparsification.CountingList +public import Complexity.Logics.Propositional.ClauseSet.Sparsification.PolyTime +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.Binary +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Plain + +/-! +# Counting sparsification -- the step is polynomial-time + +The list-level counting-sparsification step (`Cslib.Logic.PL.CNF.Sparsification.countingNext`), +tautology removal (`Cslib.Logic.PL.CNF.Sparsification.removeTautologies`), nontautological +normalization (`Cslib.Logic.PL.CNF.Sparsification.countingNormalize`), and comparison with the +counting thresholds `countingThreshold M j = M ^ j` +(`Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_countingThreshold_le`) are polynomial-time +on the framed encoding of formulas, for any encoding of the variables [OpenAI2026ETH, Appendix A.1]. + +## Main results + +* `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_countingThreshold_le`: comparisons with + `countingThreshold M`. +* `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_removeTautologies`, + `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_countingNormalize`: tautology removal and + nontautological normalization. +* `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_countingNext`: the list-level counting + step. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*, Appendix A.1][OpenAI2026ETH] +-/ + +@[expose] public section + +open Turing.MultiTapeTM ComputableTidilyInPolyTime + +namespace Cslib.Logic.PL.CNF.Sparsification + +/-- Comparing the counting thresholds `countingThreshold M j = M ^ j` with a number is +polynomial-time in unary. -/ +theorem computableInPolyTime_countingThreshold_le (M : ℕ) : + ComputableInPolyTime (fun p : ℕ × ℕ => decide (countingThreshold M p.1 ≤ p.2)) + (encPair encUnary encUnary) encBool := by + unfold countingThreshold + fun_prop + +variable {V : Type} [DecidableEq V] (encV : V ↪ List Bool) + +/-- The framed encoding of clauses. -/ +local notation "encC" => encList (encPair encV encBool) + +/-- The framed encoding of formulas. -/ +local notation "encF" => encList (encList (encPair encV encBool)) + +/-- Tautology testing on a clause is polynomial-time. -/ +theorem computableInPolyTime_isTautology : + ComputableInPolyTime (Clause.isTautology (V := V)) encC encBool := by + refine .of_tidy ?_ + unfold Clause.isTautology Std.Sat.Literal.negate + fun_prop + +/-- Removing tautological clauses from a formula is polynomial-time. -/ +theorem computableInPolyTime_removeTautologies : + ComputableInPolyTime (removeTautologies (V := V)) encF encF := by + refine .of_tidy ?_ + have htaut := (computableInPolyTime_isTautology encV).tidy + unfold removeTautologies + fun_prop + +/-- Nontautological normalization (`countingNormalize`) is polynomial-time. -/ +theorem computableInPolyTime_countingNormalize : + ComputableInPolyTime (countingNormalize (V := V)) encF encF := by + have hrem := (computableInPolyTime_removeTautologies encV).tidy + have hnorm := (computableInPolyTime_normalize encV).tidy + exact .of_tidy (hnorm.comp hrem) + +/-- **The list-level counting-sparsification step is polynomial-time.** -/ +theorem computableInPolyTime_countingNext {θ : ℕ → ℕ} + (hθ : ComputableInPolyTime (fun p : ℕ × ℕ => decide (θ p.1 ≤ p.2)) + (encPair encUnary encUnary) encBool) (k : ℕ) : + ComputableInPolyTime (fun p : CNF V × Bool => countingNext θ k p.1 p.2) + (encPair encF encBool) encF := by + refine .of_tidy ?_ + have := (computableInPolyTime_choice encV hθ k).tidy + have := (computableInPolyTime_reduce encV).tidy + have := (computableInPolyTime_dedupClauses encV).tidy + have := (computableInPolyTime_flower encV).tidy + unfold countingNext countingChild countingIntro petals negatedHeartUnits Std.Sat.Literal.negate + fun_prop + +end Cslib.Logic.PL.CNF.Sparsification diff --git a/Complexity/Logics/Propositional/ClauseSet/Sparsification/List.lean b/Complexity/Logics/Propositional/ClauseSet/Sparsification/List.lean index c48bccdcc8..cd7b9e3e24 100644 --- a/Complexity/Logics/Propositional/ClauseSet/Sparsification/List.lean +++ b/Complexity/Logics/Propositional/ClauseSet/Sparsification/List.lean @@ -178,7 +178,7 @@ private theorem not_and_not_subclause_iff {D C : Clause V} : cases subclause D C <;> cases subclause C D <;> simp /-- The list-level reduction computes the reduction of clause sets. -/ -private theorem toClauseSet_reduce (ψ : CNF V) : +theorem toClauseSet_reduce (ψ : CNF V) : (reduce ψ).toClauseSet = ClauseSet.Sparsification.reduce ψ.toClauseSet := by have hkeep (C : Clause V) : (ψ.all fun D => !(subclause D C && !subclause C D)) = true ↔ ∀ D' ∈ ψ.toClauseSet, ¬ D' ⊂ C.toFinset := by @@ -199,7 +199,8 @@ private theorem toClauseSet_reduce (ψ : CNF V) : obtain ⟨C, hC, rfl⟩ := mem_toClauseSet.mp hC' exact ⟨C, List.mem_filter.mpr ⟨hC, (hkeep C).mpr hk⟩, rfl⟩ -private theorem toClauseSet_flower {ψ : CNF V} (hnd : ∀ C ∈ ψ, C.Nodup) (c : ℕ) (H : Clause V) : +/-- The list-level flower computes the clause-set flower when clauses have no repeated literals. -/ +theorem toClauseSet_flower {ψ : CNF V} (hnd : ∀ C ∈ ψ, C.Nodup) (c : ℕ) (H : Clause V) : (flower ψ c H).toClauseSet = ClauseSet.Sparsification.flower ψ.toClauseSet c H.toFinset := by ext C' rw [mem_toClauseSet, ClauseSet.Sparsification.mem_flower, mem_toClauseSet] @@ -221,7 +222,8 @@ theorem toFinset_filter_not_mem (C H : Clause V) : rw [Bool.not_eq_true', List.any_eq_false] exact ⟨fun h hl => h l hl (beq_self_eq_true l), fun h l' hl' heq => h (beq_iff_eq.mp heq ▸ hl')⟩ -private theorem toClauseSet_petals {ψ : CNF V} (hnd : ∀ C ∈ ψ, C.Nodup) (c : ℕ) (H : Clause V) : +/-- The list-level petals compute the clause-set petals when clauses have no repeated literals. -/ +theorem toClauseSet_petals {ψ : CNF V} (hnd : ∀ C ∈ ψ, C.Nodup) (c : ℕ) (H : Clause V) : (petals ψ c H).toClauseSet = ClauseSet.Sparsification.petals ψ.toClauseSet c H.toFinset := by ext C' rw [mem_toClauseSet, ClauseSet.Sparsification.mem_petals, ← toClauseSet_flower hnd] @@ -289,7 +291,8 @@ private theorem heavy_eq_true_iff {θ : ℕ → ℕ} {k : ℕ} {ψ : CNF V} (h : /-! ### Candidates -/ omit [DecidableEq V] in -private theorem mem_hearts {k : ℕ} {C H : Clause V} (hH : H ∈ hearts k C) : H <+ C := by +/-- Every candidate heart of `C` is a sublist of `C`. -/ +theorem mem_hearts {k : ℕ} {C H : Clause V} (hH : H ∈ hearts k C) : H <+ C := by obtain ⟨S, -, rfl⟩ := List.mem_map.mp hH conv_rhs => rw [← List.zipIdx_map_fst 0 C] exact List.filter_sublist.map _ @@ -606,7 +609,8 @@ private theorem pairwise_zipIdx (ψ : CNF V) : simp only [List.getElem_zipIdx, Nat.zero_add] exact hij -private theorem nodup_toFinset_dedupClauses (ψ : CNF V) : +/-- Deduplicating clauses produces clauses with pairwise distinct literal sets. -/ +theorem nodup_toFinset_dedupClauses (ψ : CNF V) : ((dedupClauses ψ).map List.toFinset).Nodup := by rw [dedupClauses, List.map_map, List.nodup_iff_pairwise_ne, List.pairwise_map] refine ((pairwise_zipIdx ψ).filter _).imp_of_mem fun {p q} _ hq hpq heq => ?_ @@ -618,7 +622,8 @@ private theorem nodup_toFinset_dedupClauses (ψ : CNF V) : have h₂ : subclause q.1 p.1 = true := subclause_eq_true_iff.mpr heq.ge simp [hpq, h₁, h₂] at this -private theorem toClauseSet_dedupClauses (ψ : CNF V) : +/-- Deduplicating clauses preserves the clause set. -/ +theorem toClauseSet_dedupClauses (ψ : CNF V) : (dedupClauses ψ).toClauseSet = ψ.toClauseSet := by refine Finset.Subset.antisymm (toClauseSet_mono (dedupClauses_sublist ψ).subset) ?_ intro C' hC' diff --git a/Complexity/Logics/Propositional/ClauseSet/Sparsification/PolyTime.lean b/Complexity/Logics/Propositional/ClauseSet/Sparsification/PolyTime.lean index 74bdac9678..9e1a3cd24e 100644 --- a/Complexity/Logics/Propositional/ClauseSet/Sparsification/PolyTime.lean +++ b/Complexity/Logics/Propositional/ClauseSet/Sparsification/PolyTime.lean @@ -74,23 +74,36 @@ local notation "encC" => encList (encPair encV encBool) local notation "encF" => encList (encList (encPair encV encBool)) /-- The list-level reduction is polynomial-time. -/ -private theorem computableTidilyInPolyTime_reduce : - ComputableTidilyInPolyTime (reduce (V := V)) encF encF := by +theorem computableInPolyTime_reduce : + ComputableInPolyTime (reduce (V := V)) encF encF := by + refine .of_tidy ?_ unfold reduce subclause fun_prop -private theorem computableTidilyInPolyTime_flower : - ComputableTidilyInPolyTime (fun p : CNF V × (ℕ × Clause V) => flower p.1 p.2.1 p.2.2) +/-- Removing duplicate clauses with identical literal sets is polynomial-time. -/ +theorem computableInPolyTime_dedupClauses : + ComputableInPolyTime (dedupClauses (V := V)) encF encF := by + refine .of_tidy ?_ + unfold dedupClauses subclause + fun_prop + +/-- Computing the flower of a clause size and heart is polynomial-time. -/ +theorem computableInPolyTime_flower : + ComputableInPolyTime (fun p : CNF V × (ℕ × Clause V) => flower p.1 p.2.1 p.2.2) (encPair encF (encPair encUnary encC)) encF := by + refine .of_tidy ?_ unfold flower subclause fun_prop -private theorem computableTidilyInPolyTime_heavy {θ : ℕ → ℕ} - (hθ : ComputableTidilyInPolyTime (fun p : ℕ × ℕ => decide (θ p.1 ≤ p.2)) +/-- Testing whether a candidate `(c, H)` is heavy is polynomial-time. -/ +theorem computableInPolyTime_heavy {θ : ℕ → ℕ} + (hθ : ComputableInPolyTime (fun p : ℕ × ℕ => decide (θ p.1 ≤ p.2)) (encPair encUnary encUnary) encBool) : - ComputableTidilyInPolyTime (fun p : CNF V × (ℕ × Clause V) => heavy θ p.1 p.2) + ComputableInPolyTime (fun p : CNF V × (ℕ × Clause V) => heavy θ p.1 p.2) (encPair encF (encPair encUnary encC)) encBool := by - have := computableTidilyInPolyTime_flower encV + have hθ := hθ.tidy + refine .of_tidy ?_ + have := (computableInPolyTime_flower encV).tidy have h₁ : ComputableTidilyInPolyTime (fun p : CNF V × (ℕ × Clause V) => decide (1 ≤ p.2.2.length) && decide (p.2.2.length < p.2.1)) (encPair encF (encPair encUnary encC)) encBool := by @@ -102,8 +115,10 @@ private theorem computableTidilyInPolyTime_heavy {θ : ℕ → ℕ} exact bool_and.comp (h₁.prod (hθ.comp h₂)) omit [DecidableEq V] in -private theorem computableTidilyInPolyTime_candidates (k : ℕ) : - ComputableTidilyInPolyTime (candidates (V := V) k) encF (encList (encPair encUnary encC)) := by +/-- Listing the candidate pairs `(c, H)` of a formula is polynomial-time. -/ +theorem computableInPolyTime_candidates (k : ℕ) : + ComputableInPolyTime (candidates (V := V) k) encF (encList (encPair encUnary encC)) := by + refine .of_tidy ?_ have : ComputableTidilyInPolyTime (hearts (V := V) k) encC (encList encC) := by unfold hearts fun_prop @@ -111,14 +126,15 @@ private theorem computableTidilyInPolyTime_candidates (k : ℕ) : fun_prop /-- The computed choice is polynomial-time. -/ -private theorem computableTidilyInPolyTime_choice {θ : ℕ → ℕ} - (hθ : ComputableTidilyInPolyTime (fun p : ℕ × ℕ => decide (θ p.1 ≤ p.2)) +theorem computableInPolyTime_choice {θ : ℕ → ℕ} + (hθ : ComputableInPolyTime (fun p : ℕ × ℕ => decide (θ p.1 ≤ p.2)) (encPair encUnary encUnary) encBool) (k : ℕ) : - ComputableTidilyInPolyTime (choice (V := V) θ k) encF (encPair encUnary encC) := by + ComputableInPolyTime (choice (V := V) θ k) encF (encPair encUnary encC) := by + refine .of_tidy ?_ have hc : ComputableTidilyInPolyTime (fun ψ : CNF V => (candidates k ψ).filter (heavy θ ψ)) encF (encList (encPair encUnary encC)) := - filterWithInput (computableTidilyInPolyTime_candidates encV k) - (computableTidilyInPolyTime_heavy encV hθ) + filterWithInput (computableInPolyTime_candidates encV k).tidy + (computableInPolyTime_heavy encV hθ).tidy have hr : ComputableTidilyInPolyTime (fun x : ℕ × Clause V => rank k x) (encPair encUnary encC) encUnary := by unfold rank @@ -136,11 +152,10 @@ theorem computableInPolyTime_isSparse {θ : ℕ → ℕ} (hθ : ComputableInPolyTime (fun p : ℕ × ℕ => decide (θ p.1 ≤ p.2)) (encPair encUnary encUnary) encBool) (k : ℕ) : ComputableInPolyTime (isSparse (V := V) θ k) encF encBool := by - have hθ := hθ.tidy refine .of_tidy ?_ unfold isSparse - exact bool_not.comp (anyWithInput (computableTidilyInPolyTime_candidates encV k) - (computableTidilyInPolyTime_heavy encV hθ)) + exact bool_not.comp (anyWithInput (computableInPolyTime_candidates encV k).tidy + (computableInPolyTime_heavy encV hθ).tidy) /-- **The list-level step is polynomial-time.** -/ theorem computableInPolyTime_next {θ : ℕ → ℕ} @@ -148,11 +163,10 @@ theorem computableInPolyTime_next {θ : ℕ → ℕ} (encPair encUnary encUnary) encBool) (k : ℕ) : ComputableInPolyTime (fun p : CNF V × Bool => next θ k p.1 p.2) (encPair encF encBool) encF := by - have hθ := hθ.tidy refine .of_tidy ?_ - have := computableTidilyInPolyTime_choice encV hθ k - have := computableTidilyInPolyTime_reduce encV - have := computableTidilyInPolyTime_flower encV + have := (computableInPolyTime_choice encV hθ k).tidy + have := (computableInPolyTime_reduce encV).tidy + have := (computableInPolyTime_flower encV).tidy unfold next child intro petals fun_prop @@ -160,11 +174,12 @@ theorem computableInPolyTime_next {θ : ℕ → ℕ} theorem computableInPolyTime_normalize : ComputableInPolyTime (normalize (V := V)) encF encF := by refine .of_tidy ?_ - have := computableTidilyInPolyTime_reduce encV + have := (computableInPolyTime_reduce encV).tidy + have := (computableInPolyTime_dedupClauses encV).tidy have hdedup : ComputableTidilyInPolyTime (dedup (V := V)) encC encC := by unfold dedup fun_prop - unfold normalize dedupClauses subclause + unfold normalize fun_prop end Cslib.Logic.PL.CNF.Sparsification diff --git a/ComplexityTests/Computability/Complexity/FineGrained/CountingSparsifyPolyTime.lean b/ComplexityTests/Computability/Complexity/FineGrained/CountingSparsifyPolyTime.lean new file mode 100644 index 0000000000..9e8ca74af8 --- /dev/null +++ b/ComplexityTests/Computability/Complexity/FineGrained/CountingSparsifyPolyTime.lean @@ -0,0 +1,116 @@ +/- +Copyright (c) 2026 OpenAI, Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: OpenAI, Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Complexity.FineGrained.CountingEndpoint +public import Complexity.Logics.Propositional.ClauseSet.Sparsification.CountingPolyTime + +/-! +# Tests for dense CNF relabeling, list-level counting sparsification, and fine-grained endpoints + +Concrete regression tests and edge-case checks for: +* `Cslib.Logic.PL.CNF.denseRelabel` vs `CNF.compact` on gapped/repeated-literal formulas, empty + formulas, and width-`0`/`1` `KSAT` instances; +* `Cslib.Logic.PL.CNF.Sparsification.removeTautologies`, `countingNormalize`, `countingChild`, and + `countingNext`, including additive `modelCountOn` conservation and `CountingNormalized` + preservation; +* Polynomial-time certificates and fine-grained endpoint reductions in + `Complexity.Computability.Complexity.FineGrained.CountingEndpoint`. +-/ + +@[expose] public section + +namespace Cslib.Complexity.Tests + +open Cslib.Logic.PL Cslib.Logic.PL.CNF.Sparsification Turing.MultiTapeTM + +/-! ### 1. Concrete dense-relabeling vs compaction checks -/ + +/-- A concrete formula whose variable occurrences `[10, 10, 20, 5]` have a duplicate before `20` and +non-monotone variable numbers. -/ +def gappedFormula : CNF ℕ := + [[(10, true), (10, true)], [(20, false), (5, true)]] + +/-- `CNF.compact` uses first occurrence positions in `vars`, leaving a gap at `1` (`[0, 2, 3]`). -/ +example : gappedFormula.compact = [[(0, true), (0, true)], [(2, false), (3, true)]] := by + decide + +/-- `CNF.denseRelabel` uses indices in `distinctVars`, producing dense indices `[0, 1, 2]`. -/ +example : gappedFormula.denseRelabel = [[(0, true), (0, true)], [(1, false), (2, true)]] := by + decide + +example : gappedFormula.denseRelabel.distinctVars = List.range gappedFormula.numVars := + CNF.distinctVars_denseRelabel gappedFormula + +example : gappedFormula.denseRelabel.modelCountOn (Finset.range gappedFormula.numVars) = + gappedFormula.modelCount := + CNF.modelCountOn_denseRelabel gappedFormula + +/-- Edge cases: empty formula and empty-clause formula at `k = 0` and `k = 1`. -/ +example : CNF.denseRelabel ([] : CNF ℕ) = [] := rfl +example : CNF.denseRelabel ([[]] : CNF ℕ) = [[]] := rfl +example : CNF.denseRelabel ([] : CNF ℕ) ∈ KSAT 0 ↔ ([] : CNF ℕ) ∈ KSAT 0 := + CNF.denseRelabel_mem_KSAT_iff +example : CNF.denseRelabel ([[]] : CNF ℕ) ∈ KSAT 1 ↔ ([[]] : CNF ℕ) ∈ KSAT 1 := + CNF.denseRelabel_mem_KSAT_iff + +/-! ### 2. Concrete list-level counting sparsification checks -/ + +/-- A 2-CNF formula with a tautological clause `[(0, true), (0, false)]` and a size-2 flower of +2-clauses sharing the heart `[(0, true)]`. -/ +def flowerWithTautology : CNF ℕ := + [[(0, true), (0, false)], [(0, true), (1, true)], [(0, true), (2, false)]] + +/-- `removeTautologies` drops the tautological clause and keeps the two nontautological clauses. -/ +example : removeTautologies flowerWithTautology = + [[(0, true), (1, true)], [(0, true), (2, false)]] := by + decide + +/-- `countingNormalize` produces a `CountingNormalized 2` formula with the same 3-variable model +count. -/ +example : CountingNormalized 2 (countingNormalize flowerWithTautology) := + countingNormalized_countingNormalize (by decide) + +example : (countingNormalize flowerWithTautology).modelCountOn (Finset.range 3) = + flowerWithTautology.modelCountOn (Finset.range 3) := + modelCountOn_countingNormalize flowerWithTautology (Finset.range 3) + +/-- At threshold `M = 2` (`θ 1 = 2`), `choice (countingThreshold 2) 2` selects the heavy heart +`[(0, true)]` at clause size `2`. -/ +example : choice (countingThreshold 2) 2 (countingNormalize flowerWithTautology) = + (2, [(0, true)]) := by + decide + +/-- The `false` (A) branch reduces to the unit heart `[[(0, true)]]`, while the `true` (B) branch +introduces the petals `[[(1, true)], [(2, false)]]` and the negated-heart unit `[[(0, false)]]`. -/ +example : countingNext (countingThreshold 2) 2 (countingNormalize flowerWithTautology) false = + [[(0, true)]] := by + decide + +example : countingNext (countingThreshold 2) 2 (countingNormalize flowerWithTautology) true = + [[(1, true)], [(2, false)], [(0, false)]] := by + decide + +/-- Both counting children preserve `CountingNormalized 2` and partition the 5 satisfying +assignments on `{0, 1, 2}` into `4 + 1 = 5`. -/ +example : CountingNormalized 2 + (countingNext (countingThreshold 2) 2 (countingNormalize flowerWithTautology) true) := + countingNormalized_countingNext (countingThreshold 2) (by decide) + (countingNormalized_countingNormalize (by decide)) true + +example : (countingNormalize flowerWithTautology).modelCountOn (Finset.range 3) = + CNF.modelCountOn + (countingNext (countingThreshold 2) 2 (countingNormalize flowerWithTautology) false) + (Finset.range 3) + + CNF.modelCountOn + (countingNext (countingThreshold 2) 2 (countingNormalize flowerWithTautology) true) + (Finset.range 3) := + modelCountOn_countingNext + (countingNormalized_countingNormalize (k := 2) (by decide)).toNormalized (Finset.range 3) + +end Cslib.Complexity.Tests From ee83f216af5627f13311ed1cd01c05d1f70797e9 Mon Sep 17 00:00:00 2001 From: Samuel Schlesinger Date: Tue, 27 Oct 2026 05:48:16 +0000 Subject: [PATCH 2/7] feat(CNF): retain encoded context in native greedy parity search (#1295) --- Complexity.lean | 1 + .../PolyTime/CNF/GreedyParityContext.lean | 682 ++++++++++++++++++ ComplexityTests/CNFGreedyParityContext.lean | 157 ++++ ROADMAP.md | 6 + 4 files changed, 846 insertions(+) create mode 100644 Complexity/Computability/PolyTime/CNF/GreedyParityContext.lean create mode 100644 ComplexityTests/CNFGreedyParityContext.lean diff --git a/Complexity.lean b/Complexity.lean index 64ee60b849..78bf4adda0 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -1365,6 +1365,7 @@ public import Complexity.Computability.PCP.RegularConstraintGraph public import Complexity.Computability.PCP.SelectedComposition public import Complexity.Computability.PCP.ThreeSAT public import Complexity.Computability.PCP.TwoProverGame +public import Complexity.Computability.PolyTime.CNF.GreedyParityContext public import Complexity.Computability.PolyTime.CNF.GreedyParitySearch public import Complexity.Computability.PolyTime.CNF.SparsePolyCompile public import Complexity.Computability.PolyTime.Int.ExactDiv diff --git a/Complexity/Computability/PolyTime/CNF/GreedyParityContext.lean b/Complexity/Computability/PolyTime/CNF/GreedyParityContext.lean new file mode 100644 index 0000000000..4163736968 --- /dev/null +++ b/Complexity/Computability/PolyTime/CNF/GreedyParityContext.lean @@ -0,0 +1,682 @@ +/- +Copyright (c) 2026 Anton Kovsharov, Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Anton Kovsharov, Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Complexity.Computability.PolyTime.CNF.GreedyParitySearch +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat.BinaryDiv +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Plain +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Option + +/-! +# Greedy parity search with an encoded context + +The selector and greedy program are those of `GreedyParitySearch`; this module supplies one +native machine for a counter on `(context, rows)`. The context is part of every live state and +counter input. Its encoding is charged even when candidate enumeration or the outer loop is empty. +This closes the fixed-counter interface gap without constructing the common-zero counter or the +initial Tanner matrix of [OpenAI2026ETH], §§6.2–6.3 (pp. 42–43). + +## Main definitions + +* `GreedyContext.selectCertificate`: uniform context-dependent candidate selection. +* `GreedyContext.greedyCertificate`: the existing `greedyParityRows` on a runtime context. + +## Main results + +* `GreedyContext.computableInO_greedy`: independent time and polynomial-workspace bounds. +* `GreedyContext.count_greedy_eq_card_sat`: the fixed-counter correctness theorem pointwise. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH], §§6.2–6.3. + +The certificate construction extends Anton Kovsharov's `GreedyParitySearch` implementation. +-/ + +@[expose] public section + +namespace Cslib.Logic.PL.CNF.GreedyContext + +open Finset Cslib.BitString Cslib.GenericRAM Turing.MultiTapeTM + +variable {C : Type} (encC : C ↪ List Bool) + +/-- Full selector input: context, unary width and tuple length, and retained rows. -/ +def encInput : (C × ℕ × ℕ × List (List Bool)) ↪ List Bool := + encPair encC encSelectBestIn + +/-- Full greedy input: context, unary round parameter, width and tuple length, and rows. -/ +def encGreedyInput : (C × ℕ × ℕ × ℕ × List (List Bool)) ↪ List Bool := + encPair encC encGreedyIn + +/-- The original selector accumulator with the counter's runtime context retained. -/ +private def encAcc : (C × SelectBestAcc) ↪ List Bool := encPair encC encSelectBestAcc + +variable {encC} + +end Cslib.Logic.PL.CNF.GreedyContext +end + +public section +namespace Cslib.Logic.PL.CNF.GreedyContext + +open Finset Cslib.BitString Cslib.GenericRAM Turing.MultiTapeTM +variable {C : Type} {encC : C ↪ List Bool} + +private def candRowOfState (s : ℕ × SelectBestAcc) : List Bool := + materializeTestRow s.2.1.1 (decodeTestTuple s.2.1.1 s.2.1.2 s.1) + +private theorem computableInPolyTime_candRowOfState : + ComputableInPolyTime candRowOfState (encPair encNat encSelectBestAcc) encBits := by + unfold candRowOfState encSelectBestAcc + let encRest := encPair (encList encBits) (encOption (encPair encBits encNat)) + let encAcc := encPair (encPair encUnary encUnary) encRest + have hj := ComputableInPolyTime.fst encNat encAcc + have hmP := (ComputableInPolyTime.fst (encPair encUnary encUnary) encRest).comp + (ComputableInPolyTime.snd encNat encAcc) + have hm := (ComputableInPolyTime.fst encUnary encUnary).comp hmP + have hP := (ComputableInPolyTime.snd encUnary encUnary).comp hmP + exact computableInPolyTime_materializeTestRow' hm (computableInPolyTime_decodeTestTuple' hm hP hj) + +private def augCandidateRows (s : ℕ × SelectBestAcc) : List (List Bool) := + s.2.2.1 ++ [candRowOfState s] + +private theorem computableInPolyTime_augCandidateRows : + ComputableInPolyTime augCandidateRows (encPair encNat encSelectBestAcc) (encList encBits) := by + refine .of_tidy ?_; unfold augCandidateRows + have hcand := computableInPolyTime_candRowOfState.tidy + unfold encSelectBestAcc at hcand ⊢ + let encRest := encPair (encList encBits) (encOption (encPair encBits encNat)) + have hrows := (ComputableTidilyInPolyTime.fst (encList encBits) _).comp + ((ComputableTidilyInPolyTime.snd (encPair encUnary encUnary) encRest).comp + (ComputableTidilyInPolyTime.snd encNat (encPair (encPair encUnary encUnary) encRest))) + exact ComputableTidilyInPolyTime.listAppend' hrows + (ComputableTidilyInPolyTime.cons' hcand (ComputableTidilyInPolyTime.const [])) + +private def finishSelectBestStep (p : (ℕ × SelectBestAcc) × ℕ) : SelectBestAcc := + let candRow := candRowOfState p.1 + let nextBest := p.1.2.2.2.elim (some (candRow, p.2)) fun best => + if decide (p.2 < best.2) then some (candRow, p.2) else some best + (p.1.2.1, (p.1.2.2.1, nextBest)) + +private theorem computableInPolyTime_finishSelectBestStep : + ComputableInPolyTime finishSelectBestStep + (encPair (encPair encNat encSelectBestAcc) encNat) encSelectBestAcc := by + refine .of_tidy ?_; unfold finishSelectBestStep + have hcand0 := computableInPolyTime_candRowOfState.tidy + unfold encSelectBestAcc at hcand0 ⊢ + let eBest := encPair encBits encNat; let encRest := encPair (encList encBits) (encOption eBest) + let encAcc := encPair (encPair encUnary encUnary) encRest + let encIn := encPair (encPair encNat encAcc) encNat + have hs := ComputableTidilyInPolyTime.fst (encPair encNat encAcc) encNat + have hval := ComputableTidilyInPolyTime.snd (encPair encNat encAcc) encNat + have hacc := (ComputableTidilyInPolyTime.snd encNat encAcc).comp hs + have hmP := (ComputableTidilyInPolyTime.fst (encPair encUnary encUnary) encRest).comp hacc + have hrest := (ComputableTidilyInPolyTime.snd (encPair encUnary encUnary) encRest).comp hacc + have hrows := (ComputableTidilyInPolyTime.fst (encList encBits) (encOption eBest)).comp hrest + have hbestOpt := (ComputableTidilyInPolyTime.snd (encList encBits) (encOption eBest)).comp hrest + have hsomeCand := (ComputableTidilyInPolyTime.optionSome eBest).comp ((hcand0.comp hs).prod hval) + refine hmP.prod (hrows.prod + (ComputableTidilyInPolyTime.optionElimWithInput hbestOpt hsomeCand ?_)) + have hq_in := ComputableTidilyInPolyTime.fst encIn eBest + have hq_best := ComputableTidilyInPolyTime.snd encIn eBest + exact (ComputableTidilyInPolyTime.binary_lt' (hval.comp hq_in) + ((ComputableTidilyInPolyTime.snd encBits encNat).comp hq_best)).ite' (hsomeCand.comp hq_in) + ((ComputableTidilyInPolyTime.optionSome eBest).comp hq_best) + +private def augment (s : ℕ × C × SelectBestAcc) : C × List (List Bool) := + (s.2.1, augCandidateRows (s.1, s.2.2)) + +private theorem augment_polyTime : ComputableInPolyTime (augment (C := C)) + (encPair encNat (encAcc encC)) (encPair encC (encList encBits)) := by + unfold augment encAcc + exact (by fun_prop : ComputableInPolyTime (fun s : ℕ × C × SelectBestAcc => s.2.1) + (encPair encNat (encPair encC encSelectBestAcc)) encC).prod + (computableInPolyTime_augCandidateRows.comp (by fun_prop)) + +private def finish (p : (ℕ × C × SelectBestAcc) × ℕ) : C × SelectBestAcc := + (p.1.2.1, finishSelectBestStep ((p.1.1, p.1.2.2), p.2)) + +private theorem finish_polyTime : ComputableInPolyTime (finish (C := C)) + (encPair (encPair encNat (encAcc encC)) encNat) (encAcc encC) := by + unfold finish encAcc + exact (by fun_prop : ComputableInPolyTime + (fun p : (ℕ × C × SelectBestAcc) × ℕ => p.1.2.1) + (encPair (encPair encNat (encPair encC encSelectBestAcc)) encNat) encC).prod + (computableInPolyTime_finishSelectBestStep.comp (by fun_prop)) + +private def step (count : C × List (List Bool) → ℕ) + (s : ℕ × C × SelectBestAcc) : C × SelectBestAcc := + (s.2.1, selectBestStep (fun rows => count (s.2.1, rows)) (s.1, s.2.2)) + +private noncomputable def stepCertificate {count : C × List (List Bool) → ℕ} + (cCount : NativeCertificate count (encPair encC (encList encBits)) encNat) : + NativeCertificate (step count) (encPair encNat (encAcc encC)) (encAcc encC) := + let cId := NativeCertificate.linear (ComputableInO.of_linear + (.of_eq fun _ : ℕ × C × SelectBestAcc => rfl)) + ((NativeCertificate.ofPolyTime finish_polyTime).comp + (cId.prod (cCount.comp (NativeCertificate.ofPolyTime augment_polyTime)))).congr fun _ => rfl + +private theorem run_eq (count : C × List (List Bool) → ℕ) (c : C) + (acc : SelectBestAcc) (j : ℕ) : + IndexFold.run (step count) j (c, acc) = + (j, c, (IndexFold.run (selectBestStep (fun rows => count (c, rows))) j acc).2) := by + induction j with + | zero => rfl + | succ j ih => + rw [IndexFold.run_succ, ih, IndexFold.run_succ] + simp only [IndexFold.step, step, IndexFold.run_index] + congr 3 + exact Prod.ext (IndexFold.run_index _ _ _).symm rfl + +private theorem run_params (count : C × List (List Bool) → ℕ) (c : C) + (m P j : ℕ) (rows : List (List Bool)) : + let s := IndexFold.run (step count) j (c, (m, P), rows, none) + s.2.1 = c ∧ s.2.2.1 = (m, P) ∧ s.2.2.2.1 = rows := by + induction j with + | zero => exact ⟨rfl, rfl, rfl⟩ + | succ j ih => + simp only [IndexFold.run_succ, IndexFold.step, step, selectBestStep, + ih.1, ih.2.1, ih.2.2, and_self] + +private def countIn (a : C × ℕ × ℕ × List (List Bool)) : ℕ := + candidateTupleCount a.2.1 a.2.2.1 + +private theorem countIn_polyTime : + ComputableInPolyTime (countIn (C := C)) (encInput encC) encNat := by + unfold countIn encInput encSelectBestIn + exact computableInPolyTime_candidateTupleCount' (by fun_prop) (by fun_prop) + +private def initIn (a : C × ℕ × ℕ × List (List Bool)) : C × SelectBestAcc := + (a.1, (a.2.1, a.2.2.1), a.2.2.2, none) + +private theorem initIn_polyTime : + ComputableInPolyTime (initIn (C := C)) (encInput encC) (encAcc encC) := by + refine .of_tidy ?_ + unfold initIn encInput encSelectBestIn encAcc encSelectBestAcc + fun_prop + +private def extract (r : ℕ × C × SelectBestAcc) : List Bool := + r.2.2.2.2.elim (List.replicate r.2.2.1.1 false) Prod.fst + +private theorem extract_polyTime : + ComputableInPolyTime (extract (C := C)) (encPair encNat (encAcc encC)) encBits := by + refine .of_tidy ?_ + unfold extract encAcc encSelectBestAcc + let eBest := encPair encBits encNat + let encRest := encPair (encList encBits) (encOption eBest) + let encS := encPair (encPair encUnary encUnary) encRest + have hacc := (ComputableTidilyInPolyTime.snd encC encS).comp + (ComputableTidilyInPolyTime.snd encNat (encPair encC encS)) + have hm := (ComputableTidilyInPolyTime.fst encUnary encUnary).comp + ((ComputableTidilyInPolyTime.fst (encPair encUnary encUnary) encRest).comp hacc) + have hopt := (ComputableTidilyInPolyTime.snd (encList encBits) (encOption eBest)).comp + ((ComputableTidilyInPolyTime.snd (encPair encUnary encUnary) encRest).comp hacc) + exact ComputableTidilyInPolyTime.optionElimWithInput hopt + ((ComputableTidilyInPolyTime.bitsReplicate false).comp hm) + ((ComputableTidilyInPolyTime.fst encBits encNat).comp + (ComputableTidilyInPolyTime.snd (encPair encNat (encPair encC encS)) eBest)) + +/-- One fixed native selector for all encoded contexts. Empty candidate spaces return the zero +row, exactly as `selectBestTestRow`; otherwise its first-minimum semantics are unchanged. -/ +noncomputable def selectCertificate {count : C × List (List Bool) → ℕ} + (cCount : NativeCertificate count (encPair encC (encList encBits)) encNat) : + NativeCertificate (fun a : C × ℕ × ℕ × List (List Bool) => + selectBestTestRow (fun rows => count (a.1, rows)) a.2.1 a.2.2.1 a.2.2.2) + (encInput encC) encBits := + ((NativeCertificate.ofPolyTime extract_polyTime).comp + (IndexFold.certificate (NativeCertificate.ofPolyTime countIn_polyTime) + (NativeCertificate.ofPolyTime initIn_polyTime) (stepCertificate cCount))).congr fun a => by + have hp := (run_params count a.1 a.2.1 a.2.2.1 + (candidateTupleCount a.2.1 a.2.2.1) a.2.2.2).2.1 + rw [run_eq] at hp + simp only [Function.comp_apply, countIn, initIn, run_eq, extract, + selectBestTestRow, show (IndexFold.run + (selectBestStep (fun rows => count (a.1, rows))) + (candidateTupleCount a.2.1 a.2.2.1) ((a.2.1, a.2.2.1), a.2.2.2, none)).2.1.1 = a.2.1 + by rw [hp]] + +private def appendRow (p : (C × ℕ × ℕ × List (List Bool)) × List Bool) : + C × ℕ × ℕ × List (List Bool) := + (p.1.1, p.1.2.1, p.1.2.2.1, p.1.2.2.2 ++ [p.2]) + +private theorem appendRow_polyTime : ComputableInPolyTime (appendRow (C := C)) + (encPair (encInput encC) encBits) (encInput encC) := by + refine .of_tidy ?_ + unfold appendRow encInput encSelectBestIn + fun_prop + +private def outerStep (count : C × List (List Bool) → ℕ) + (s : C × ℕ × ℕ × List (List Bool)) : C × ℕ × ℕ × List (List Bool) := + (s.1, s.2.1, s.2.2.1, s.2.2.2 ++ + [selectBestTestRow (fun rows => count (s.1, rows)) s.2.1 s.2.2.1 s.2.2.2]) + +private theorem outerStep_iterate (count : C × List (List Bool) → ℕ) (c : C) + (m P i : ℕ) (rows : List (List Bool)) : + (outerStep count)^[i] (c, m, P, rows) = + (c, m, P, greedyParityRowsAt (fun rows => count (c, rows)) m P rows i) := by + induction i with + | zero => rfl + | succ i ih => rw [Function.iterate_succ_apply', ih, outerStep, greedyParityRowsAt_succ] + +private noncomputable def outerCertificate {count : C × List (List Bool) → ℕ} + (cCount : NativeCertificate count (encPair encC (encList encBits)) encNat) : + NativeCertificate (outerStep count) (encInput encC) (encInput encC) := + let cId := NativeCertificate.linear (ComputableInO.of_linear + (.of_eq fun _ : C × ℕ × ℕ × List (List Bool) => rfl)) + ((NativeCertificate.ofPolyTime appendRow_polyTime).comp + (cId.prod (selectCertificate cCount))).congr fun _ => rfl + +private def roundCount (a : C × ℕ × ℕ × ℕ × List (List Bool)) : ℕ := 3 * a.2.1 + +private theorem roundCount_polyTime : + ComputableInPolyTime (roundCount (C := C)) (encGreedyInput encC) encNat := by + refine .of_tidy ?_ + unfold roundCount encGreedyInput encGreedyIn + exact ComputableTidilyInPolyTime.unary_to_binary.comp (by fun_prop) + +private def outerInit (a : C × ℕ × ℕ × ℕ × List (List Bool)) : + C × ℕ × ℕ × List (List Bool) := (a.1, a.2.2) + +private theorem outerInit_polyTime : + ComputableInPolyTime (outerInit (C := C)) (encGreedyInput encC) (encInput encC) := by + unfold outerInit encGreedyInput encGreedyIn encInput + fun_prop + +/-- One fixed machine runs `3n` greedy rounds for the runtime context. The context survives every +candidate and round; the returned rows are the original `greedyParityRows` specialization. -/ +noncomputable def greedyCertificate {count : C × List (List Bool) → ℕ} + (cCount : NativeCertificate count (encPair encC (encList encBits)) encNat) : + NativeCertificate (fun a : C × ℕ × ℕ × ℕ × List (List Bool) => + greedyParityRows (fun rows => count (a.1, rows)) a.2.1 a.2.2.1 a.2.2.2.1 a.2.2.2.2) + (encGreedyInput encC) (encList encBits) := + (NativeCertificate.snd (NativeCertificate.snd (NativeCertificate.snd + (NativeCertificate.iterate (NativeCertificate.ofPolyTime roundCount_polyTime) + (NativeCertificate.ofPolyTime outerInit_polyTime) (outerCertificate cCount))))).congr + fun ⟨_, _, _, _, _⟩ => by simp only [roundCount, outerInit, outerStep_iterate]; rfl + +/-- The existing Lemma 6.2 correctness theorem, specialized at a runtime context. Positivity, +initial support, and counter agreement on the stated bounded candidate systems are essential. +This statement constructs neither that counter nor the initial rows. -/ +theorem count_greedy_eq_card_sat {V : Type*} [Fintype V] [DecidableEq V] + (count : C × List (List Bool) → ℕ) (c : C) (φ : CNF V) {P : ℕ} + (hn : 1 ≤ Fintype.card V) (hP : 1 ≤ P) {initRows : List (List Bool)} + (hinit_len : ∀ r ∈ initRows, r.length = φ.length) + (hcount : ∀ rows, (∀ r ∈ rows, r.length = φ.length) → + rows.length ≤ initRows.length + 3 * Fintype.card V → + (∀ r ∈ rows, r ∈ initRows ∨ r.count true ≤ P) → + count (c, rows) = numPassing φ.violations (rows.map (ofList φ.length))) + (hinit : ∀ σ : V → Bool, + PassesTests φ.violations (initRows.map (ofList φ.length)) σ → + φ.eval σ = false → φ.length ≤ P * weight (φ.violations σ)) : + count (c, greedyParityRows (fun rows => count (c, rows)) + (Fintype.card V) φ.length P initRows) = + #{σ : V → Bool | φ.eval σ = true} := + countSys_greedyParityRows_eq_card_sat φ hn hP hinit_len hcount hinit + +private theorem exists_mono_poly {α : Type} {size u : α → ℕ} (hu : PolyBounded size u) : + ∃ p : ℕ → ℕ, Cslib.PolynomiallyBounded p ∧ Monotone p ∧ ∀ a, u a ≤ p (size a) := by + obtain ⟨c, d, h⟩ := hu.exists_le + exact ⟨fun n => c * (n + 1) ^ d, by fun_prop, + fun _ _ hxy => Nat.mul_le_mul_left c (Nat.pow_le_pow_left (Nat.add_le_add_right hxy 1) d), h⟩ + +private theorem exists_mono_poly_cert {α β : Type} {f : α → β} + {ei : α ↪ List Bool} {eo : β ↪ List Bool} (hf : ComputableInPolyTime f ei eo) : + ∃ p : ℕ → ℕ, Cslib.PolynomiallyBounded p ∧ Monotone p ∧ + ∀ a, (NativeCertificate.ofPolyTime hf).time a ≤ p (ei a).length ∧ + (NativeCertificate.ofPolyTime hf).space a ≤ p (ei a).length := by + have hc := NativeCertificate.ofPolyTime_isPolynomial hf + obtain ⟨p, hp, hmono, hle⟩ := exists_mono_poly (hc.1.add hc.2) + exact ⟨p, hp, hmono, fun a => ⟨by have := hle a; omega, by have := hle a; omega⟩⟩ + +private theorem input_le (c : C) (m P : ℕ) (rows : List (List Bool)) : + (encC c).length ≤ (encInput encC (c, m, P, rows)).length ∧ + m ≤ (encInput encC (c, m, P, rows)).length ∧ + P ≤ (encInput encC (c, m, P, rows)).length ∧ + (encList encBits rows).length ≤ (encInput encC (c, m, P, rows)).length := by + simp only [encInput, encSelectBestIn, length_encPair, encUnary_apply, List.length_replicate] + exact ⟨by omega, by omega, by omega, by omega⟩ + +private theorem select_bounds {count : C × List (List Bool) → ℕ} + (cCount : NativeCertificate count (encPair encC (encList encBits)) encNat) + (hSpace : PolyBounded (fun r => (encPair encC (encList encBits) r).length) cCount.space) : + ∃ poly : ℕ → ℕ, Cslib.PolynomiallyBounded poly ∧ Monotone poly ∧ + (∀ a : C × ℕ × ℕ × List (List Bool), + (∀ R : ℕ, + (∀ j ≤ candidateTupleCount a.2.1 a.2.2.1, + cCount.time (a.1, a.2.2.2 ++ + [materializeTestRow a.2.1 (decodeTestTuple a.2.1 a.2.2.1 j)]) ≤ R) → + (selectCertificate cCount).time a ≤ + (candidateTupleCount a.2.1 a.2.2.1 + 1) * (poly (encInput encC a).length + R)) ∧ + (selectCertificate cCount).space a ≤ poly (encInput encC a).length) := by + obtain ⟨cOut, _, hOutLen⟩ := cCount.computes + obtain ⟨pSp, hpSp, hmSp, hSp⟩ := exists_mono_poly hSpace + obtain ⟨pCn, hpCn, _, hCn⟩ := exists_mono_poly_cert (countIn_polyTime (encC := encC)) + obtain ⟨pCi, hpCi, _, hCi⟩ := exists_mono_poly_cert (initIn_polyTime (encC := encC)) + obtain ⟨pAug, hpAug, hmAug, hAug⟩ := exists_mono_poly_cert (augment_polyTime (encC := encC)) + obtain ⟨pFin, hpFin, hmFin, hFin⟩ := + exists_mono_poly_cert (finish_polyTime (encC := encC)) + obtain ⟨pExt, hpExt, hmExt, hExt⟩ := + exists_mono_poly_cert (extract_polyTime (encC := encC)) + let qSys (L : ℕ) := pSp (5 * L + 2); let vBound (L : ℕ) := cOut * (qSys L + 1) + let bAcc (L : ℕ) := 12 * L + vBound L + 7; let lIdx (L : ℕ) := 2 * L * L + 1 + let bFull (L : ℕ) := 2 * lIdx L + 1 + bAcc L; let finIn (L : ℕ) := 2 * bFull L + 1 + vBound L + let cbOver (L : ℕ) := bFull L + pAug (bFull L) + qSys L + pFin (finIn L) + let roundOver (L : ℕ) := 2 * bFull L + 2 * (lIdx L + 1) ^ 2 + cbOver L + let initExt (L : ℕ) := pCn L + L + pCi L + pExt (bFull L) + lIdx L + have hraw : Cslib.PolynomiallyBounded (fun L => initExt L + roundOver L) := by + dsimp only [initExt, roundOver, cbOver, finIn, bFull, lIdx, bAcc, vBound, qSys]; fun_prop + obtain ⟨poly, hpoly, hmpoly, hle_poly⟩ := exists_mono_poly (.of_polynomiallyBounded id hraw) + refine ⟨poly, hpoly, hmpoly, fun ⟨c, m, P, rows⟩ => ?_⟩; dsimp only + obtain ⟨hc_le, hm_le, hP_le, hrows_le⟩ := input_le (encC := encC) c m P rows + set L := (encInput encC (c, m, P, rows)).length; set K := candidateTupleCount m P + have hpolyL : initExt L + roundOver L ≤ poly L := hle_poly L + let candRow (j : ℕ) := materializeTestRow m (decodeTestTuple m P j) + let runAt (j : ℕ) := IndexFold.run (step count) j (c, (m, P), rows, none) + have hq_j (j : ℕ) : cCount.space (c, rows ++ [candRow j]) ≤ qSys L := + (hSp _).trans (hmSp (by + simp only [length_encPair, candRow, encList_append, List.length_append, + encList_cons, encList_nil, + List.append_nil, length_frame, encBits_apply, length_materializeTestRow]; omega)) + have hv_j (j : ℕ) : (encNat (count (c, rows ++ [candRow j]))).length ≤ vBound L := + (hOutLen _).trans (Nat.mul_le_mul_left cOut (Nat.add_le_add_right (hq_j j) 1)) + have hlIdx : (encNat K).length ≤ lIdx L := by + refine (length_encNat_candidateTupleCount_le m P).trans ?_ + have := Nat.mul_le_mul hP_le (show 2 * m ≤ 2 * L by omega); dsimp only [lIdx]; nlinarith + have hbAcc (j : ℕ) : (encAcc encC (runAt j).2).length ≤ bAcc L := by + have hb := length_encSelectBestAcc_run_le (fun r => count (c, r)) m P j rows + (fun i _ => hv_j i) + simp only [runAt, run_eq, encAcc, length_encPair] + dsimp only [bAcc] + omega + have hbFull (j : ℕ) (hj : j ≤ K) : (encPair encNat (encAcc encC) (runAt j)).length ≤ bFull L := + (IndexFold.length_run_le (step count) (c, (m, P), rows, none) + hj (hbAcc j)).trans (by dsimp only [bFull]; omega) + let cb := stepCertificate cCount; let rstep := IndexFold.roundCertificate cb + let initPair : ℕ × C × SelectBestAcc := (0, c, (m, P), rows, none) + let cn := NativeCertificate.ofPolyTime (countIn_polyTime (encC := encC)) + let ci := NativeCertificate.ofPolyTime (initIn_polyTime (encC := encC)) + let cExt := NativeCertificate.ofPolyTime (extract_polyTime (encC := encC)) + set M := NativeCertificate.maxStateLength (encPair encNat (encAcc encC)) + (IndexFold.step (step count)) K initPair + set RT := NativeCertificate.maxRoundTime rstep K initPair + set RS := NativeCertificate.maxRoundSpace rstep K initPair + have hmaxS : M ≤ bFull L := NativeCertificate.maxStateLength_le _ _ _ _ hbFull + have hcb_bounds (j : ℕ) (hj : j ≤ K) : + cb.time (runAt j) ≤ cbOver L + cCount.time (c, rows ++ [candRow j]) ∧ + cb.space (runAt j) ≤ cbOver L := by + let st := runAt j + have hparams : st.2.1 = c ∧ st.2.2.1 = (m, P) ∧ st.2.2.2.1 = rows := + run_params count c m P j rows + have hidx : st.1 = j := IndexFold.run_index (step count) j (c, (m, P), rows, none) + have haug_st : augment st = (c, rows ++ [candRow j]) := by + simp only [augment, augCandidateRows, candRowOfState, candRow] + rw [hparams.1, hparams.2.2, hidx, + show st.2.2.1.1 = m by rw [hparams.2.1], + show st.2.2.1.2 = P by rw [hparams.2.1]] + have hst_len : (encPair encNat (encAcc encC) st).length ≤ bFull L := hbFull j hj + have hfin_len : (encPair (encPair encNat (encAcc encC)) encNat + (st, count (augment st))).length ≤ finIn L := by + rw [length_encPair, haug_st]; have := hv_j j; dsimp only [finIn]; omega + have haugT := (hAug st).1.trans (hmAug hst_len); have haugS := (hAug st).2.trans (hmAug hst_len) + have hfinT := (hFin (st, count (augment st))).1.trans (hmFin hfin_len) + have hfinS := (hFin (st, count (augment st))).2.trans (hmFin hfin_len) + have hcntT : cCount.time (augment st) = cCount.time (c, rows ++ [candRow j]) := + congrArg cCount.time haug_st + have hcntS : cCount.space (augment st) ≤ qSys L := haug_st ▸ hq_j j + refine ⟨?_, ?_⟩ + · change ((encPair encNat (encAcc encC) st).length + + ((NativeCertificate.ofPolyTime (augment_polyTime (encC := encC))).time st + + cCount.time (augment st))) + + (NativeCertificate.ofPolyTime (finish_polyTime (encC := encC))).time + (st, count (augment st)) ≤ _; dsimp only [cbOver]; omega + · change ((encPair encNat (encAcc encC) st).length + + ((NativeCertificate.ofPolyTime (augment_polyTime (encC := encC))).space st + + cCount.space (augment st))) + + (NativeCertificate.ofPolyTime (finish_polyTime (encC := encC))).space + (st, count (augment st)) ≤ _; dsimp only [cbOver]; omega + have hcn_a : cn.time (c, m, P, rows) ≤ pCn L ∧ cn.space (c, m, P, rows) ≤ pCn L := + hCn (c, m, P, rows) + have hci_a : ci.time (c, m, P, rows) ≤ pCi L ∧ ci.space (c, m, P, rows) ≤ pCi L := + hCi (c, m, P, rows) + have hextT : cExt.time (runAt K) ≤ pExt (bFull L) := + (hExt (runAt K)).1.trans (hmExt (hbFull K le_rfl)) + have hextS : cExt.space (runAt K) ≤ pExt (bFull L) := + (hExt (runAt K)).2.trans (hmExt (hbFull K le_rfl)) + refine ⟨fun R hR => ?_, ?_⟩ + · have hmaxRT : RT ≤ bFull L + (lIdx L + 1) ^ 2 + cbOver L + R := by + refine NativeCertificate.maxRoundTime_le _ _ _ fun j hj => ?_ + have hst_len := hbFull j hj + have hj_len : (encNat (runAt j).1).length ≤ lIdx L := by + rw [IndexFold.run_index]; exact (ComputableTidilyInO.length_encNat_mono hj).trans hlIdx + have hsq : ((encNat (runAt j).1).length + 1) ^ 2 ≤ (lIdx L + 1) ^ 2 := + Nat.pow_le_pow_left (by omega) 2 + have hcbT := (hcb_bounds j hj).1 + have hRJ : cCount.time (c, rows ++ [candRow j]) ≤ R := hR j hj + change (encPair encNat (encAcc encC) (runAt j)).length + + ((encNat (runAt j).1).length + 1) ^ 2 + cb.time (runAt j) ≤ _; omega + have hsqN : ((encNat K).length + 1) ^ 2 ≤ (lIdx L + 1) ^ 2 := Nat.pow_le_pow_left (by omega) 2 + have hround_sum : RT + M + ((encNat K).length + 1) ^ 2 ≤ roundOver L + R := by + dsimp only [roundOver]; omega + calc (selectCertificate cCount).time (c, m, P, rows) + = cn.time (c, m, P, rows) + (L + ci.time (c, m, P, rows)) + + (K + 1) * (RT + M + ((encNat K).length + 1) ^ 2) + cExt.time (runAt K) := rfl + _ ≤ initExt L + (K + 1) * (roundOver L + R) := by + have h1 : cn.time (c, m, P, rows) + (L + ci.time (c, m, P, rows)) + + cExt.time (runAt K) ≤ + initExt L := by dsimp only [initExt]; omega + have h2 := Nat.mul_le_mul_left (K + 1) hround_sum; omega + _ ≤ (K + 1) * initExt L + (K + 1) * (roundOver L + R) := + Nat.add_le_add_right (Nat.le_mul_of_pos_left _ (Nat.succ_pos _)) _ + _ = (K + 1) * (initExt L + roundOver L + R) := by ring + _ ≤ (K + 1) * (poly L + R) := Nat.mul_le_mul_left _ (by omega) + · have hmaxRS : RS ≤ bFull L + (lIdx L + 1) + cbOver L := by + refine NativeCertificate.maxRoundSpace_le _ _ _ fun j hj => ?_ + have hst_len := hbFull j hj + have hj_len : (encNat (runAt j).1).length ≤ lIdx L := by + rw [IndexFold.run_index]; exact (ComputableTidilyInO.length_encNat_mono hj).trans hlIdx + have hcbS := (hcb_bounds j hj).2 + change (encPair encNat (encAcc encC) (runAt j)).length + + ((encNat (runAt j).1).length + 1) + cb.space (runAt j) ≤ _; omega + have hlin_sq : lIdx L + 1 ≤ 2 * (lIdx L + 1) ^ 2 := by nlinarith + calc (selectCertificate cCount).space (c, m, P, rows) + = cn.space (c, m, P, rows) + (L + ci.space (c, m, P, rows)) + RS + M + (encNat K).length + + cExt.space (runAt K) := rfl + _ ≤ initExt L + roundOver L := by dsimp only [initExt, roundOver]; omega + _ ≤ poly L := hpolyL + +/-- Uniform selector time and polynomial workspace, measured in the full context/input encoding. +The counter-time premise includes `j = (2m)^P`: the native iterator uses inclusive prefix maxima, +so this endpoint is charged even though that candidate is not executed. -/ +theorem computableInO_select {count : C × List (List Bool) → ℕ} + (cCount : NativeCertificate count (encPair encC (encList encBits)) encNat) + (hSpace : PolyBounded (fun r => (encPair encC (encList encBits) r).length) cCount.space) + (R : C × ℕ × ℕ × List (List Bool) → ℕ) + (hR : ∀ a, ∀ j ≤ candidateTupleCount a.2.1 a.2.2.1, + cCount.time + (a.1, a.2.2.2 ++ [materializeTestRow a.2.1 (decodeTestTuple a.2.1 a.2.2.1 j)]) ≤ R a) : + ∃ poly : ℕ → ℕ, Cslib.PolynomiallyBounded poly ∧ + ComputableInO + (fun a : C × ℕ × ℕ × List (List Bool) => + selectBestTestRow (fun r => count (a.1, r)) a.2.1 a.2.2.1 a.2.2.2) + (encInput encC) encBits + (fun a => (candidateTupleCount a.2.1 a.2.2.1 + 1) * (poly (encInput encC a).length + R a)) + (fun a => poly (encInput encC a).length) := by + obtain ⟨poly, hpoly, _, hB⟩ := select_bounds cCount hSpace + exact ⟨poly, hpoly, (selectCertificate cCount).computes.mono_le + (fun a => (hB a).1 (R a) (hR a)) (fun a => (hB a).2)⟩ + +private theorem greedy_input_le (c : C) (n m P : ℕ) (rows : List (List Bool)) : + (encC c).length ≤ (encGreedyInput encC (c, n, m, P, rows)).length ∧ + n ≤ (encGreedyInput encC (c, n, m, P, rows)).length ∧ + m ≤ (encGreedyInput encC (c, n, m, P, rows)).length ∧ + P ≤ (encGreedyInput encC (c, n, m, P, rows)).length ∧ + (encList encBits rows).length ≤ (encGreedyInput encC (c, n, m, P, rows)).length := by + simp only [encGreedyInput, encGreedyIn, encSelectBestIn, length_encPair, + encUnary_apply, List.length_replicate] + exact ⟨by omega, by omega, by omega, by omega, by omega⟩ + +/-- One uniform machine and polynomial for all encoded contexts, with independent native time +`O((3n + 1) ((2m)^P + 1) (poly(L) + R))` and workspace `O(poly(L))`, where `L` is the full +context/input encoding length. The binary clock, retained rows/context, incumbent count, and final +output are charged; workspace is not multiplied by either loop length. + +The inclusive resource maxima require `hR` also at `i = 3n` and `j = (2m)^P`. These are resource +obligations on extra endpoint candidates, not extra calls in the executable program. In particular, +their row-length bound can exceed the correctness theorem's candidate bound by one. Polynomial +counter workspace is a separate, global joint-input assumption; counter time need not be polynomial. +This supplies the context interface for [OpenAI2026ETH], §6.3, not its common-zero counter. -/ +theorem computableInO_greedy {count : C × List (List Bool) → ℕ} + (cCount : NativeCertificate count (encPair encC (encList encBits)) encNat) + (hSpace : PolyBounded (fun r => (encPair encC (encList encBits) r).length) cCount.space) + (R : C × ℕ × ℕ × ℕ × List (List Bool) → ℕ) + (hR : ∀ a, ∀ i ≤ 3 * a.2.1, ∀ j ≤ candidateTupleCount a.2.2.1 a.2.2.2.1, + cCount.time (a.1, + greedyParityRowsAt (fun r => count (a.1, r)) a.2.2.1 a.2.2.2.1 a.2.2.2.2 i ++ + [materializeTestRow a.2.2.1 (decodeTestTuple a.2.2.1 a.2.2.2.1 j)]) ≤ R a) : + ∃ poly : ℕ → ℕ, Cslib.PolynomiallyBounded poly ∧ + ComputableInO + (fun a : C × ℕ × ℕ × ℕ × List (List Bool) => + greedyParityRows (fun r => count (a.1, r)) a.2.1 a.2.2.1 a.2.2.2.1 a.2.2.2.2) + (encGreedyInput encC) (encList encBits) + (fun a => (3 * a.2.1 + 1) * (candidateTupleCount a.2.2.1 a.2.2.2.1 + 1) * + (poly (encGreedyInput encC a).length + R a)) + (fun a => poly (encGreedyInput encC a).length) := by + obtain ⟨polySel, hpolySel, hmpolySel, hSelB⟩ := select_bounds cCount hSpace + obtain ⟨pCn, hpCn, _, hCn⟩ := exists_mono_poly_cert (roundCount_polyTime (encC := encC)) + obtain ⟨pCi, hpCi, _, hCi⟩ := exists_mono_poly_cert (outerInit_polyTime (encC := encC)) + obtain ⟨pApp, hpApp, hmApp, hApp⟩ := exists_mono_poly_cert (appendRow_polyTime (encC := encC)) + let bOuter (L : ℕ) := 6 * L * L + 10 * L + 3; let selDom (L : ℕ) := polySel (bOuter L) + let bAppIn (L : ℕ) := 2 * bOuter L + 1 + L + let outerFixed (L : ℕ) := 2 * bOuter L + pApp (bAppIn L) + (3 * L + 1) ^ 2 + let initOuter (L : ℕ) := pCn L + pCi L + 3 * L + let poly (L : ℕ) := initOuter L + outerFixed L + selDom L + have hpoly : Cslib.PolynomiallyBounded poly := by + dsimp only [poly, initOuter, outerFixed, bAppIn, selDom, bOuter]; fun_prop + have hboth : ∀ a : C × ℕ × ℕ × ℕ × List (List Bool), + (greedyCertificate cCount).time a ≤ + (3 * a.2.1 + 1) * (candidateTupleCount a.2.2.1 a.2.2.2.1 + 1) * + (poly (encGreedyInput encC a).length + R a) ∧ + (greedyCertificate cCount).space a ≤ poly (encGreedyInput encC a).length := by + rintro ⟨c, n, m, P, rows₀⟩; dsimp only + obtain ⟨hc_le, hn_le, hm_le, hP_le, hrows0_le⟩ := greedy_input_le (encC := encC) c n m P rows₀ + set L := (encGreedyInput encC (c, n, m, P, rows₀)).length; set K := candidateTupleCount m P + set R₀ := R (c, n, m, P, rows₀) + have hbOuter (i : ℕ) (hi : i ≤ 3 * n) : + (encInput encC ((outerStep count)^[i] (c, m, P, rows₀))).length ≤ bOuter L := by + simp only [outerStep_iterate, encInput, encSelectBestIn, length_encPair, + encUnary_apply, List.length_replicate, length_encList_greedyParityRowsAt] + have hgrow : i * (2 * m + 1) ≤ (3 * L) * (2 * L + 1) := + Nat.mul_le_mul (by omega) (by omega) + have hxp : (3 * L) * (2 * L + 1) = 6 * L * L + 3 * L := by ring + dsimp only [bOuter]; omega + let cstep := outerCertificate cCount + let cn := NativeCertificate.ofPolyTime (roundCount_polyTime (encC := encC)) + let ci := NativeCertificate.ofPolyTime (outerInit_polyTime (encC := encC)) + set MO := NativeCertificate.maxStateLength (encInput encC) + (outerStep count) (3 * n) (c, m, P, rows₀) + set RTO := NativeCertificate.maxRoundTime cstep (3 * n) (c, m, P, rows₀) + set RSO := NativeCertificate.maxRoundSpace cstep (3 * n) (c, m, P, rows₀) + have hmaxS : MO ≤ bOuter L := NativeCertificate.maxStateLength_le _ _ _ _ hbOuter + have hstep_b (i : ℕ) (hi : i ≤ 3 * n) : + let st := (outerStep count)^[i] (c, m, P, rows₀) + cstep.time st ≤ bOuter L + (K + 1) * (selDom L + R₀) + pApp (bAppIn L) ∧ + cstep.space st ≤ bOuter L + selDom L + pApp (bAppIn L) := by + dsimp only; set st := (outerStep count)^[i] (c, m, P, rows₀) + have hst_eq : st = (c, m, P, greedyParityRowsAt (fun r => count (c, r)) m P rows₀ i) := + outerStep_iterate count c m P i rows₀ + have hst_len : (encInput encC st).length ≤ bOuter L := hbOuter i hi + have hpoly_sel_le : polySel (encInput encC st).length ≤ selDom L := hmpolySel hst_len + have hsel_t : (selectCertificate cCount).time st ≤ (K + 1) * (selDom L + R₀) := by + have h1 := (hSelB st).1 R₀ (by simpa only [hst_eq] using hR (c, n, m, P, rows₀) i hi) + rw [show candidateTupleCount st.2.1 st.2.2.1 = K by simp only [hst_eq, K]] at h1 + exact h1.trans (Nat.mul_le_mul_left _ (Nat.add_le_add_right hpoly_sel_le _)) + have hsel_s : (selectCertificate cCount).space st ≤ selDom L := + (hSelB st).2.trans hpoly_sel_le + set selRow := selectBestTestRow (fun r => count (st.1, r)) st.2.1 st.2.2.1 st.2.2.2 + have happ_in_len : (encPair (encInput encC) encBits (st, selRow)).length ≤ bAppIn L := by + rw [length_encPair] + have hrow_len : (encBits selRow).length ≤ L := by + simp only [selRow, hst_eq, encBits_apply, length_selectBestTestRow]; exact hm_le + dsimp only [bAppIn]; omega + have happ_t := (hApp (st, selRow)).1.trans (hmApp happ_in_len) + have happ_s := (hApp (st, selRow)).2.trans (hmApp happ_in_len) + refine ⟨?_, ?_⟩ + · change ((encInput encC st).length + (selectCertificate cCount).time st) + + (NativeCertificate.ofPolyTime (appendRow_polyTime (encC := encC))).time + (st, selRow) ≤ _; omega + · change ((encInput encC st).length + (selectCertificate cCount).space st) + + (NativeCertificate.ofPolyTime (appendRow_polyTime (encC := encC))).space + (st, selRow) ≤ _; omega + have h3n_len : (encNat (3 * n)).length ≤ 3 * L := by + rw [encNat_apply, Nat.size_eq_bits_len]; exact (Nat.size_le_self (3 * n)).trans (by omega) + have hcn_a : cn.time (c, n, m, P, rows₀) ≤ pCn L ∧ cn.space (c, n, m, P, rows₀) ≤ pCn L := + hCn (c, n, m, P, rows₀) + have hci_a : ci.time (c, n, m, P, rows₀) ≤ pCi L ∧ + ci.space (c, n, m, P, rows₀) ≤ pCi L := hCi (c, n, m, P, rows₀) + refine ⟨?_, ?_⟩ + · have hmaxRT : RTO ≤ bOuter L + (K + 1) * (selDom L + R₀) + pApp (bAppIn L) := + NativeCertificate.maxRoundTime_le cstep (3 * n) (c, m, P, rows₀) fun i hi => + (hstep_b i hi).1 + have h3n_sq : ((encNat (3 * n)).length + 1) ^ 2 ≤ (3 * L + 1) ^ 2 := + Nat.pow_le_pow_left (by omega) 2 + have hper : RTO + MO + ((encNat (3 * n)).length + 1) ^ 2 ≤ + (K + 1) * (outerFixed L + selDom L + R₀) := + calc RTO + MO + ((encNat (3 * n)).length + 1) ^ 2 + ≤ outerFixed L + (K + 1) * (selDom L + R₀) := by dsimp only [outerFixed]; omega + _ ≤ (K + 1) * outerFixed L + (K + 1) * (selDom L + R₀) := + Nat.add_le_add_right (Nat.le_mul_of_pos_left _ (Nat.succ_pos _)) _ + _ = (K + 1) * (outerFixed L + selDom L + R₀) := by ring + calc (greedyCertificate cCount).time (c, n, m, P, rows₀) + = cn.time (c, n, m, P, rows₀) + ci.time (c, n, m, P, rows₀) + + (3 * n + 1) * (RTO + MO + ((encNat (3 * n)).length + 1) ^ 2) := rfl + _ ≤ initOuter L + (3 * n + 1) * ((K + 1) * (outerFixed L + selDom L + R₀)) := + Nat.add_le_add (by dsimp only [initOuter]; omega) (Nat.mul_le_mul_left _ hper) + _ = initOuter L + ((3 * n + 1) * (K + 1)) * (outerFixed L + selDom L + R₀) := by ring + _ ≤ ((3 * n + 1) * (K + 1)) * initOuter L + + ((3 * n + 1) * (K + 1)) * (outerFixed L + selDom L + R₀) := + Nat.add_le_add_right + (Nat.le_mul_of_pos_left _ (Nat.mul_pos (Nat.succ_pos _) (Nat.succ_pos _))) _ + _ = (3 * n + 1) * (K + 1) * (poly L + R₀) := by dsimp only [poly]; ring + · have hmaxRS : RSO ≤ bOuter L + selDom L + pApp (bAppIn L) := + NativeCertificate.maxRoundSpace_le cstep (3 * n) (c, m, P, rows₀) fun i hi => + (hstep_b i hi).2 + calc (greedyCertificate cCount).space (c, n, m, P, rows₀) + = cn.space (c, n, m, P, rows₀) + ci.space (c, n, m, P, rows₀) + + RSO + MO + (encNat (3 * n)).length := rfl + _ ≤ poly L := by dsimp only [poly, initOuter, outerFixed]; omega + exact ⟨poly, hpoly, (greedyCertificate cCount).computes.mono_le + (fun a => (hboth a).1) (fun a => (hboth a).2)⟩ + +/-- Whenever `cCount` uses polynomial workspace, `greedyParityRows` runs in polynomial space. -/ +theorem computableInPolySpace_greedy {count : C × List (List Bool) → ℕ} + (cCount : NativeCertificate count (encPair encC (encList encBits)) encNat) + (hSpace : PolyBounded (fun r => (encPair encC (encList encBits) r).length) cCount.space) : + ComputableInPolySpace + (fun a : C × ℕ × ℕ × ℕ × List (List Bool) => + greedyParityRows (fun r => count (a.1, r)) a.2.1 a.2.2.1 a.2.2.2.1 a.2.2.2.2) + (encGreedyInput encC) (encList encBits) := by + let cell (a : C × ℕ × ℕ × ℕ × List (List Bool)) (i j : ℕ) : ℕ := + cCount.time (a.1, + greedyParityRowsAt (fun r => count (a.1, r)) a.2.2.1 a.2.2.2.1 a.2.2.2.2 i ++ + [materializeTestRow a.2.2.1 (decodeTestTuple a.2.2.1 a.2.2.2.1 j)]) + let rowMax (a : C × ℕ × ℕ × ℕ × List (List Bool)) (i : ℕ) : ℕ := + listMax (cell a i) (List.range (candidateTupleCount a.2.2.1 a.2.2.2.1 + 1)) + let R (a : C × ℕ × ℕ × ℕ × List (List Bool)) : ℕ := + listMax (rowMax a) (List.range (3 * a.2.1 + 1)) + have hR : ∀ a, ∀ i ≤ 3 * a.2.1, ∀ j ≤ candidateTupleCount a.2.2.1 a.2.2.2.1, cell a i j ≤ R a := + fun a i hi j hj => (le_listMax (f := cell a i) (List.mem_range.mpr (by omega))).trans + (le_listMax (f := rowMax a) (List.mem_range.mpr (by omega))) + obtain ⟨poly, hpoly, c, hc, hl⟩ := computableInO_greedy cCount hSpace R hR + exact ⟨_, fun n => c * (poly n + 1), by fun_prop, hc, hl⟩ + +end Cslib.Logic.PL.CNF.GreedyContext + +end diff --git a/ComplexityTests/CNFGreedyParityContext.lean b/ComplexityTests/CNFGreedyParityContext.lean new file mode 100644 index 0000000000..a705b9832d --- /dev/null +++ b/ComplexityTests/CNFGreedyParityContext.lean @@ -0,0 +1,157 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module + +public import Complexity.Computability.PolyTime.CNF.GreedyParityContext +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Plain + +public section + +open Finset Cslib.BitString Cslib.GenericRAM Cslib.Logic.PL Cslib.Logic.PL.CNF Turing.MultiTapeTM + +namespace ComplexityTests.CNFGreedyParityContext + +-- The first context component chooses a unit clause or a tautology; the remaining bits are +-- retained runtime data. The same native counter handles both choices and all payload lengths. +private abbrev Context := Bool × List Bool +private def encContext : Context ↪ List Bool := encPair encBool encBits + +private def count (p : Context × List (List Bool)) : ℕ := + if p.1.1 then 2 else if p.2.flatten.count true = 0 then 2 else 1 + +private theorem count_polyTime : + ComputableInPolyTime count (encPair encContext (encList encBits)) encNat := by + refine .of_tidy ?_ + unfold count encContext + fun_prop + +private noncomputable def cert := NativeCertificate.ofPolyTime count_polyTime + +private def unitContext : Context := (false, []) +private def tautContext : Context := (true, [true, false]) + +example : + selectBestTestRow (fun r => count (unitContext, r)) 1 1 [] = [true] := by decide +example : selectBestTestRow (fun r => count (tautContext, r)) 1 1 [] = [false] := by decide + +-- Context changes actual execution, including the final count. Enumeration ties still retain +-- the first row after the unit clause has been enforced. +example : greedyParityRows (fun r => count (unitContext, r)) 1 1 1 [] = + [[true], [false], [false]] := by decide +example : greedyParityRows (fun r => count (tautContext, r)) 1 1 1 [] = + [[false], [false], [false]] := by decide +example : count (unitContext, greedyParityRows (fun r => count (unitContext, r)) 1 1 1 []) = 1 ∧ + count (tautContext, greedyParityRows (fun r => count (tautContext, r)) 1 1 1 []) = 2 := by + decide + +-- The joint runtime encoding charges every captured payload bit even with no outer rounds. +theorem context_encoding_charges_payload (payload : List Bool) : + (GreedyContext.encGreedyInput (encPair encBool encBits) + ((false, payload), 0, 1, 1, [])).length = + (GreedyContext.encGreedyInput (encPair encBool encBits) + ((false, []), 0, 1, 1, [])).length + 2 * payload.length := by + simp only [GreedyContext.encGreedyInput, length_encPair, encBits_apply, List.length_nil] + omega + +-- Arbitrarily long captured payloads remain in the encoded joint input even at zero rounds. +example : + (GreedyContext.encGreedyInput encContext + ((false, List.replicate 11 true), 0, 1, 1, [])).length = + (GreedyContext.encGreedyInput encContext (unitContext, 0, 1, 1, [])).length + 22 := by decide +example : + greedyParityRows (fun r => count ((false, List.replicate 11 true), r)) 0 1 1 [[true]] = + [[true]] := rfl + +-- The empty tuple space has a zero-row fallback; P = 0 has exactly one empty tuple. +example : selectBestTestRow (fun r => count (unitContext, r)) 0 1 [[true]] = [] := by decide +example : selectBestTestRow (fun r => count (tautContext, r)) 3 0 [] = + [false, false, false] := by decide + +-- This is one joint-input certificate, not a separate certificate selected for each context. +example : ComputableInPolySpace + (fun a : Context × ℕ × ℕ × ℕ × List (List Bool) => + greedyParityRows (fun r => count (a.1, r)) a.2.1 a.2.2.1 a.2.2.2.1 a.2.2.2.2) + (GreedyContext.encGreedyInput encContext) (encList encBits) := + GreedyContext.computableInPolySpace_greedy cert + (NativeCertificate.ofPolyTime_isPolynomial count_polyTime).2 + +private def formula (c : Context) : CNF (Fin 1) := + if c.1 then [[(0, true), (0, false)]] else [[(0, true)]] + +-- A finite bounded-candidate agreement check, including all retained-row prefixes of this run. +private theorem count_agrees (c : Context) (rows : List (List Bool)) + (hwidth : ∀ r ∈ rows, r.length = (formula c).length) (hlen : rows.length ≤ 3) : + count (c, rows) = numPassing (formula c).violations (rows.map (ofList (formula c).length)) := by + rcases c with ⟨b, payload⟩ + cases b <;> simp only [formula, Bool.false_eq_true, ↓reduceIte, List.length_cons, + List.length_nil, Nat.zero_add] at hwidth ⊢ + all_goals + have hrow (r : List Bool) (hr : r ∈ rows) : r = [false] ∨ r = [true] := by + have h := hwidth r hr + rcases r with _ | ⟨x, r⟩ + · simp only [List.length_nil] at h; omega + · have hrnil : r = [] := List.eq_nil_of_length_eq_zero (by simpa using Nat.succ.inj h) + subst r + cases x <;> simp + clear hwidth + rcases rows with _ | ⟨r₁, rows⟩ + · dsimp only [count]; rfl + · rcases hrow r₁ (by simp) with rfl | rfl + all_goals + rcases rows with _ | ⟨r₂, rows⟩ + · dsimp only [count]; rfl + · rcases hrow r₂ (by simp) with rfl | rfl + all_goals + rcases rows with _ | ⟨r₃, rows⟩ + · dsimp only [count]; rfl + · rcases hrow r₃ (by simp) with rfl | rfl + all_goals + have : rows = [] := List.eq_nil_of_length_eq_zero (by + simp only [List.length_cons] at hlen + omega) + subst rows + dsimp only [count] + rfl + +private theorem support (c : Context) (σ : Fin 1 → Bool) (heval : (formula c).eval σ = false) : + (formula c).length ≤ 1 * weight ((formula c).violations σ) := by + have hne : (formula c).violations σ ≠ 0 := fun h => + Bool.false_ne_true (heval.symm.trans ((CNF.violations_eq_zero_iff _ _).mp h)) + have hw : weight ((formula c).violations σ) ≠ 0 := by + rw [weight_eq_hammingNorm, ne_eq, hammingNorm_eq_zero] + exact hne + have hlen : (formula c).length = 1 := by unfold formula; split <;> rfl + omega + +-- Pointwise correctness is reused on both formulas, with the same executable counter. +example (c : Context) : + count (c, greedyParityRows (fun r => count (c, r)) 1 (formula c).length 1 []) = + #{σ : Fin 1 → Bool | (formula c).eval σ = true} := by + exact GreedyContext.count_greedy_eq_card_sat count c (formula c) (by decide) (by decide) + (fun _ h => nomatch h) (fun rows hw hl _ => count_agrees c rows hw (by simpa using hl)) + (fun σ _ he => support c σ he) + +-- A mismatched counter cannot supply the correctness premise. +example : + let bad := fun _ : Context × List (List Bool) => 9 + bad (unitContext, greedyParityRows (fun r => bad (unitContext, r)) 1 1 1 []) = 9 ∧ + #{σ : Fin 1 → Bool | (formula unitContext).eval σ = true} = 1 := by decide + +-- Omitting P ≥ 1 leaves the unit-clause model count unchanged at 2 instead of its true value 1. +example : count (unitContext, greedyParityRows (fun r => count (unitContext, r)) 1 1 0 []) = 2 ∧ + #{σ : Fin 1 → Bool | (formula unitContext).eval σ = true} = 1 := by decide + +-- Omitting |V| ≥ 1: the false zero-variable formula has no models, but zero rounds retain +-- the unique empty assignment as passing. This is not a counter-agreement failure. +example : + let φ : CNF (Fin 0) := [[]] + let ctr := fun rows : List (List Bool) => numPassing φ.violations (rows.map (ofList φ.length)) + ctr (greedyParityRows ctr 0 1 1 []) = 1 ∧ + #{σ : Fin 0 → Bool | φ.eval σ = true} = 0 := by decide + +end ComplexityTests.CNFGreedyParityContext diff --git a/ROADMAP.md b/ROADMAP.md index fbf1cec3ab..0d66d66b46 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -438,6 +438,12 @@ still to read them for the mathematics and write proofs against Complexitylib's adapted code keeps its Apache 2.0 attribution. The rewrites state no license and are only read and credited. +- **Native sparse-formula counting** ([#1226](https://github.com/cslib-community/complexitylib/issues/1226)): + `CNF.GreedyContext` certifies the existing greedy parity search uniformly from a supplied counter + on encoded `(context, rows)`, retaining and charging the context through both loops. Theorem 2.2 + counting and Tanner initialization remain separate prerequisites; the complete native counting + endpoint is still open. This conditional interface does not establish their algorithms or rates. + ## Documentation and repository - **The guide** ([#585]): a chapter per area, including Foundations (bit strings, Fourier From b58be072ff400c12c3c2edd572d891909b1e4ea3 Mon Sep 17 00:00:00 2001 From: Samuel Schlesinger Date: Tue, 27 Oct 2026 05:48:19 +0000 Subject: [PATCH 3/7] feat(SharpP): #3SAT and #3SAT-3 are #P-complete (#1280) Certify CNF.toExact3CNF in native polynomial time (mirroring the to3CNF certificate), and use it, renumbered by splitVarToNat, as a parsimonious reduction from #SAT to #3SAT. With the parsimonious Cook-Levin reduction and the existing occurrence compiler this gives #P-completeness of SharpThreeSAT and SharpThreeSATThree [Valiant1979]. Part of #1226. --- Complexity.lean | 2 + .../Complexity/Problems/SharpThreeSAT.lean | 116 +++++++++++ .../CNF/ThreeCNF/Exact/PolyTime.lean | 190 ++++++++++++++++++ ComplexityTests/SharpThreeSAT.lean | 50 +++++ 4 files changed, 358 insertions(+) create mode 100644 Complexity/Computability/Complexity/Problems/SharpThreeSAT.lean create mode 100644 Complexity/Logics/Propositional/CNF/ThreeCNF/Exact/PolyTime.lean create mode 100644 ComplexityTests/SharpThreeSAT.lean diff --git a/Complexity.lean b/Complexity.lean index 78bf4adda0..b894ae44c1 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -848,6 +848,7 @@ public import Complexity.Computability.Complexity.Problems.STCON.Completeness public import Complexity.Computability.Complexity.Problems.STCON.Reduction public import Complexity.Computability.Complexity.Problems.STCON.Search public import Complexity.Computability.Complexity.Problems.SharpSAT +public import Complexity.Computability.Complexity.Problems.SharpThreeSAT public import Complexity.Computability.Complexity.Problems.SubsetSum public import Complexity.Computability.Complexity.Problems.SubsetSum.Digits public import Complexity.Computability.Complexity.Problems.SubsetSum.Mask @@ -2209,6 +2210,7 @@ public import Complexity.Logics.Propositional.CNF.PolynomialTests public import Complexity.Logics.Propositional.CNF.SparseParityCounter public import Complexity.Logics.Propositional.CNF.ThreeCNF public import Complexity.Logics.Propositional.CNF.ThreeCNF.Exact +public import Complexity.Logics.Propositional.CNF.ThreeCNF.Exact.PolyTime public import Complexity.Logics.Propositional.CNF.ThreeCNF.PolyTime public import Complexity.Logics.Propositional.CNF.ThreeOccurrence public import Complexity.Logics.Propositional.CNF.ThreeOccurrence.PolyTime diff --git a/Complexity/Computability/Complexity/Problems/SharpThreeSAT.lean b/Complexity/Computability/Complexity/Problems/SharpThreeSAT.lean new file mode 100644 index 0000000000..d946153b99 --- /dev/null +++ b/Complexity/Computability/Complexity/Problems/SharpThreeSAT.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: candidate + +public import Complexity.Computability.Complexity.Problems.ThreeSAT +public import Complexity.Computability.Complexity.Problems.ThreeSATThree.Counting +public import Complexity.Logics.Propositional.CNF.ThreeCNF.Exact +import Complexity.Computability.Complexity.CookLevin.Counting +import Complexity.Computability.Complexity.FineGrained.BruteForce +import Complexity.Computability.Complexity.Problems.ThreeSATThree.Syntax +import Complexity.Logics.Propositional.CNF.ModelCount.Relabel +import Complexity.Logics.Propositional.CNF.PolyTime +import Complexity.Logics.Propositional.CNF.ThreeCNF.Exact.PolyTime +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Basic + +/-! +# #3SAT and #3SAT-3 are #P-complete + +Counting the satisfying assignments of a formula of width at most three (`SharpThreeSAT`) is +`#P`-complete under parsimonious reductions, and so is counting them under the additional bound of +three occurrences per variable (`SharpThreeSATThree`) [Valiant][Valiant1979], +[Arora and Barak, Section 17.3][AroraBarak09]. + +Hardness: `#SAT` is `#P`-hard (`Cslib.Complexity.CookLevin.SharpSAT_sharpPHard`), and the +count-preserving split into OR gates (`Cslib.Logic.PL.CNF.toExact3CNF`), with the variables +renumbered by `Cslib.Complexity.splitVarToNat`, is a parsimonious reduction from `#SAT` to `#3SAT`. +The existing occurrence compiler `Cslib.Complexity.toThreeSATThree` then reduces `#3SAT` to +`#3SAT-3`. Membership: on inputs passing their syntactic guard both counts equal `#SAT`, and +otherwise they are zero, which is the count of the formula `[[]]`; guarding the input is a +parsimonious reduction to `#SAT ∈ #P`. + +## Main definitions + +* `Cslib.Complexity.toSharpThreeSAT`: the parsimonious reduction from `#SAT` to `#3SAT`. + +## Main results + +* `Cslib.Complexity.sharpThreeSAT_toSharpThreeSAT`: the reduction preserves counts. +* `Cslib.Complexity.computableInPolyTime_toSharpThreeSAT`: it is polynomial-time. +* `Cslib.Complexity.parsimoniousReducible_SharpSAT_SharpThreeSAT`: `#SAT` reduces + parsimoniously to `#3SAT`. +* `Cslib.Complexity.SharpThreeSAT_sharpPComplete`: `#3SAT` is `#P`-complete. +* `Cslib.Complexity.SharpThreeSATThree_sharpPComplete`: `#3SAT-3` is `#P`-complete. + +## References + +* [L. Valiant, *The complexity of computing the permanent*][Valiant1979] +* [S. Arora, B. Barak, *Computational Complexity: A Modern Approach*][AroraBarak09] +-/ + +@[expose] public section + +namespace Cslib.Complexity + +open Cslib.Logic.PL Turing.MultiTapeTM + +/-- The count-preserving reduction from `#SAT` to `#3SAT`: split every clause into OR gates and +number the original and gate variables injectively. -/ +def toSharpThreeSAT (φ : CNF ℕ) : CNF ℕ := φ.toExact3CNF.relabel splitVarToNat + +/-- `toSharpThreeSAT` has width at most three and the same number of satisfying assignments. -/ +theorem sharpThreeSAT_toSharpThreeSAT (φ : CNF ℕ) : + SharpThreeSAT (toSharpThreeSAT φ) = SharpSAT φ := by + have hw : ((toSharpThreeSAT φ).all fun c => decide (c.length ≤ 3)) = true := by + simp only [List.all_eq_true, decide_eq_true_eq, toSharpThreeSAT, CNF.relabel, List.mem_map] + rintro _ ⟨c, hc, rfl⟩ + simpa using CNF.length_le_three_of_mem_toExact3CNF hc + simp only [SharpThreeSAT, hw, ↓reduceIte] + simp only [SharpSAT, toSharpThreeSAT, CNF.modelCount_relabel splitVarToNat_injective, + CNF.modelCount_toExact3CNF] + +/-- The reduction `toSharpThreeSAT` is computable in polynomial time under `encCNF`. -/ +@[fun_prop] +theorem computableInPolyTime_toSharpThreeSAT : + ComputableInPolyTime toSharpThreeSAT encCNF encCNF := + .of_tidy ((CNF.computableInPolyTime_relabel computableInPolyTime_splitVarToNat).tidy.comp + (CNF.isPolyTime_toExact3CNF encNat).tidy) + +/-- **`#SAT` reduces parsimoniously to `#3SAT`.** -/ +theorem parsimoniousReducible_SharpSAT_SharpThreeSAT : + ParsimoniousReducible encCNF encCNF SharpSAT SharpThreeSAT := + ⟨toSharpThreeSAT, computableInPolyTime_toSharpThreeSAT, + fun φ => (sharpThreeSAT_toSharpThreeSAT φ).symm⟩ + +/-- A guarded count reduces parsimoniously to `#SAT`: replace a rejected input by the +unsatisfiable formula `[[]]`. -/ +private theorem parsimoniousReducible_guard {g : CNF ℕ → Bool} + (hg : ComputableInPolyTime g encCNF encBool) : + ParsimoniousReducible encCNF encCNF (fun φ => if g φ then SharpSAT φ else 0) SharpSAT := by + refine ⟨fun φ => if g φ then φ else [[]], ?_, fun φ => ?_⟩ + · exact .of_tidy (ComputableTidilyInPolyTime.cond (encB := encCNF) (yes := fun φ => φ) + (no := fun _ => [[]]) hg.tidy (by fun_prop) (by fun_prop)) + · by_cases h : g φ = true + · simp only [h, ↓reduceIte] + · simp only [h, Bool.false_eq_true, ↓reduceIte] + decide + +/-- **`#3SAT` is `#P`-complete** [Valiant][Valiant1979]. -/ +theorem SharpThreeSAT_sharpPComplete : SharpPComplete encCNF SharpThreeSAT := + ⟨(parsimoniousReducible_guard (computableInPolyTime_all_length_le 3)).mem_SharpP + SharpSAT.mem_SharpP, + CookLevin.SharpSAT_sharpPHard.trans parsimoniousReducible_SharpSAT_SharpThreeSAT⟩ + +/-- **`#3SAT-3` is `#P`-complete**: counting stays `#P`-complete when every variable occurs at +most three times. -/ +theorem SharpThreeSATThree_sharpPComplete : SharpPComplete encCNF SharpThreeSATThree := + ⟨(parsimoniousReducible_guard ThreeSATThree.computableInPolyTime_syntaxCheck).mem_SharpP + SharpSAT.mem_SharpP, + SharpThreeSAT_sharpPComplete.2.trans parsimoniousReducible_SharpThreeSAT_SharpThreeSATThree⟩ + +end Cslib.Complexity diff --git a/Complexity/Logics/Propositional/CNF/ThreeCNF/Exact/PolyTime.lean b/Complexity/Logics/Propositional/CNF/ThreeCNF/Exact/PolyTime.lean new file mode 100644 index 0000000000..a7c89843be --- /dev/null +++ b/Complexity/Logics/Propositional/CNF/ThreeCNF/Exact/PolyTime.lean @@ -0,0 +1,190 @@ +/- +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.Logics.Propositional.CNF.ThreeCNF.Exact +public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Basic +public import Complexity.Computability.Machines.Turing.MultiTape.Encodings.Sum +public import Complexity.Computability.Machines.Turing.MultiTape.PolyTime +import Complexity.Logics.Propositional.CNF.PolyTime +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Sum +import Mathlib.Data.List.GetD +import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Plain + +/-! +# Polynomial-time count-preserving clause splitting + +The recursive OR-gate chain agrees with an indexed traversal of the input clause, with three +clauses per gate. The certificate charges every lookup, fresh variable, copied literal and list +delimiter. Original variables keep their supplied encoding; gate variables use their unary clause +and gate indices. This mirrors the certificate for `Cslib.Logic.PL.CNF.to3CNF`. + +## Main results + +* `Cslib.Logic.PL.Clause.isPolyTime_orChainAux`: efficient OR-gate chains of clause tails. +* `Cslib.Logic.PL.Clause.isPolyTime_orSplit3`: efficient chains with a certified fresh-variable + routine, retaining its captured input. +* `Cslib.Logic.PL.CNF.isPolyTime_toExact3CNF`: a uniform certificate for the count-preserving + 3CNF reduction. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH] +-/ + +@[expose] public section + +namespace Cslib.Logic.PL + +open Turing.MultiTapeTM ComputableTidilyInPolyTime + +variable {V W Input : Type} + +/-- The three clauses of the gate `z ↔ u ∨ v`. -/ +private def gateClauses (u v : Literal W) (z : W) : CNF W := + [[(u.1, !u.2), (z, true)], [(v.1, !v.2), (z, true)], [u, v, (z, false)]] + +private theorem orChainAux_eq_flatMap (fresh : ℕ → W) (j : ℕ) (carry : Literal W) + (rest : Clause W) : + Clause.orChainAux fresh j carry rest = + (List.range (rest.length - 2)).flatMap (fun i => + gateClauses (if i = 0 then carry else (fresh (j + (i - 1)), true)) (rest.getD i carry) + (fresh (j + i))) ++ + [(if rest.length - 2 = 0 then carry else (fresh (j + (rest.length - 2 - 1)), true)) :: + rest.drop (rest.length - 2)] := by + induction j, carry, rest using Clause.orChainAux.induct fresh with + | case1 j carry b c d rest ih => + rw [Clause.orChainAux, ih] + have htwo : (c :: d :: rest).length - 2 = rest.length := by + simp [Nat.add_assoc] + have hthree : (b :: c :: d :: rest).length - 2 = rest.length + 1 := by + simp [Nat.add_assoc] + simp only [htwo, hthree, List.range_succ_eq_map, List.flatMap_cons, List.flatMap_map, + ite_true, List.getD_cons_zero, Nat.add_zero, List.cons_append, List.drop_succ_cons, + Nat.succ_ne_zero, ite_false, Nat.add_one_sub_one, gateClauses, + List.nil_append] + have hcarry (i : ℕ) : + (if i = 0 then (fresh j, true) else (fresh (j + 1 + (i - 1)), true)) = + ((fresh (j + i), true) : Literal W) := by + cases i <;> simp [Nat.add_comm, Nat.add_left_comm] + congr 3 + apply congrArg₂ List.append + · apply List.flatMap_congr + intro i hi + have hi' : i < (c :: d :: rest).length := by + simp only [List.mem_range] at hi + simp only [List.length_cons] + lia + rw [hcarry i, List.getD_cons_succ, List.getD_eq_getElem _ _ hi', + List.getD_eq_getElem _ _ hi'] + simp [Nat.add_right_comm, Nat.add_assoc, Nat.succ_eq_add_one] + · cases rest <;> simp [Nat.add_assoc, Nat.add_comm 1] + | case2 j carry rest hrest => + match rest, hrest with + | [], _ | [_], _ | [_, _], _ => simp [Clause.orChainAux] + | _ :: _ :: _ :: _, hrest => exact absurd rfl (hrest _ _ _ _) + +private theorem gateClauses_tidy {In : Type} {encIn : In ↪ List Bool} {enc : W ↪ List Bool} + {u v : In → Literal W} {z : In → W} + (hu : ComputableTidilyInPolyTime u encIn (encPair enc encBool)) + (hv : ComputableTidilyInPolyTime v encIn (encPair enc encBool)) + (hz : ComputableTidilyInPolyTime z encIn enc) : + ComputableTidilyInPolyTime (fun x => gateClauses (u x) (v x) (z x)) encIn + (encList (encList (encPair enc encBool))) := by + unfold gateClauses + fun_prop + +/-- The OR-gate chain of a clause tail, with a certified fresh-variable routine and runtime gate +index. -/ +@[fun_prop] +theorem Clause.isPolyTime_orChainAux {input : Input ↪ List Bool} {enc : W ↪ List Bool} + {fresh : Input → ℕ → W} {index : Input → ℕ} {carry : Input → Literal W} + {rest : Input → Clause W} + (hrest : ComputableInPolyTime rest input (encList (encPair enc encBool))) + (hcarry : ComputableInPolyTime carry input (encPair enc encBool)) + (hindex : ComputableInPolyTime index input encUnary) + (hfresh : ComputableInPolyTime (fun p : Input × ℕ => fresh p.1 p.2) + (encPair input encUnary) enc) : + ComputableInPolyTime (fun x => Clause.orChainAux (fresh x) (index x) (carry x) (rest x)) + input (encList (encList (encPair enc encBool))) := by + have hrest := hrest.tidy + have hcarry := hcarry.tidy + have hindex := hindex.tidy + have hfresh := hfresh.tidy + refine .of_tidy ?_ + have hdrop : ComputableTidilyInPolyTime (fun x => (rest x).drop ((rest x).length - 2)) + input (encList (encPair enc encBool)) := + (dropUnary (encA := encPair enc encBool)).comp (hrest.prod (by fun_prop)) + have hu : ComputableTidilyInPolyTime (fun p : Input × ℕ => + if p.2 = 0 then carry p.1 else ((fresh p.1 (index p.1 + (p.2 - 1)), true) : Literal W)) + (encPair input encUnary) (encPair enc encBool) := by + fun_prop + have hv : ComputableTidilyInPolyTime (fun p : Input × ℕ => ((rest p.1)[p.2]?).getD (carry p.1)) + (encPair input encUnary) (encPair enc encBool) := by + fun_prop + have hz : ComputableTidilyInPolyTime (fun p : Input × ℕ => fresh p.1 (index p.1 + p.2)) + (encPair input encUnary) enc := by + fun_prop + have hflat := ComputableTidilyInPolyTime.flatMapWithInput + (list := fun x => List.range ((rest x).length - 2)) + (f := fun x i => gateClauses + (if i = 0 then carry x else ((fresh x (index x + (i - 1)), true) : Literal W)) + (((rest x)[i]?).getD (carry x)) (fresh x (index x + i))) + (by fun_prop) (gateClauses_tidy hu hv hz) + simp_rw [orChainAux_eq_flatMap, List.getD_eq_getElem?_getD] + fun_prop + +/-- The OR-gate chain of a computed clause retains the input captured by its fresh-variable +routine. -/ +@[fun_prop] +theorem Clause.isPolyTime_orSplit3 {input : Input ↪ List Bool} {enc : W ↪ List Bool} + {fresh : Input → ℕ → W} {index : Input → ℕ} {clause : Input → Clause W} + (hclause : ComputableInPolyTime clause input (encList (encPair enc encBool))) + (hindex : ComputableInPolyTime index input encUnary) + (hfresh : ComputableInPolyTime (fun p : Input × ℕ => fresh p.1 p.2) + (encPair input encUnary) enc) : + ComputableInPolyTime (fun x => Clause.orSplit3 (fresh x) (index x) (clause x)) + input (encList (encList (encPair enc encBool))) := by + have hclause := hclause.tidy + have hindex := hindex.tidy + have hfresh := hfresh.tidy + refine .of_tidy ?_ + have heq (f : ℕ → W) (j : ℕ) (c : Clause W) : Clause.orSplit3 f j c = + c.head?.elim [[]] (fun carry => Clause.orChainAux f j carry c.tail) := by + cases c <;> rfl + simp_rw [heq] + apply optionElimWithInput (encB := encPair enc encBool) (by fun_prop) (by fun_prop) + exact (Clause.isPolyTime_orChainAux (input := encPair input (encPair enc encBool)) + (fresh := fun p => fresh p.1) (index := fun p => index p.1) (carry := fun p => p.2) + (rest := fun p => (clause p.1).tail) (.of_tidy (by fun_prop)) (.of_tidy (by fun_prop)) + (.of_tidy (by fun_prop)) (.of_tidy (by fun_prop))).tidy + +/-- Convert an arbitrary CNF formula to 3CNF with the same number of models, encoding gate +variables by unary clause and gate indices. The certificate includes relabelling, clause +traversal and all output copying. -/ +@[fun_prop] +theorem CNF.isPolyTime_toExact3CNF (enc : V ↪ List Bool) : + ComputableInPolyTime (CNF.toExact3CNF (V := V)) + (encList (encList (encPair enc encBool))) + (encList (encList (encPair (encSum enc (encPair encUnary encUnary)) encBool))) := by + refine .of_tidy ?_ + have hsplit : ComputableTidilyInPolyTime + (fun p : CNF V × (Clause V × ℕ) => Clause.orSplit3 (fun j => Sum.inr (p.2.2, j)) 0 + (Clause.relabel Sum.inl p.2.1)) + (encPair (encList (encList (encPair enc encBool))) + (encPair (encList (encPair enc encBool)) encUnary)) + (encList (encList (encPair (encSum enc (encPair encUnary encUnary)) encBool))) := + ComputableInPolyTime.tidy <| Clause.isPolyTime_orSplit3 + ((Clause.computableInPolyTime_relabel (.of_tidy (by fun_prop))).comp + (.of_tidy (by fun_prop))) (.of_tidy (by fun_prop)) (.of_tidy (by fun_prop)) + unfold CNF.toExact3CNF + fun_prop + +end Cslib.Logic.PL diff --git a/ComplexityTests/SharpThreeSAT.lean b/ComplexityTests/SharpThreeSAT.lean new file mode 100644 index 0000000000..e26268caa8 --- /dev/null +++ b/ComplexityTests/SharpThreeSAT.lean @@ -0,0 +1,50 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.Complexity.Problems.SharpThreeSAT +import Complexity.Logics.Propositional.CNF.ThreeCNF.Exact.PolyTime + +/-! +# Tests for the #P-completeness of #3SAT and #3SAT-3 +-/ + +namespace ComplexityTests.SharpThreeSAT + +open Cslib.Logic.PL Cslib.Complexity Turing.MultiTapeTM + +/-- The reduction keeps the fifteen models of one clause of width four, now as a width-three +instance. -/ +example : SharpThreeSAT (toSharpThreeSAT [[(0, true), (1, true), (2, true), (3, true)]]) = 15 := by + rw [sharpThreeSAT_toSharpThreeSAT] + decide + +/-- A formula with a clause of width four fails the `#3SAT` guard, so its guarded count is zero, +while the reduction recovers its fifteen models. -/ +example : SharpThreeSAT [[(0, true)], [(1, true), (2, true), (3, true), (4, true)]] = 0 := by + decide + +example : + SharpThreeSAT (toSharpThreeSAT [[(0, true)], [(1, true), (2, true), (3, true), (4, true)]]) = + 15 := by + rw [sharpThreeSAT_toSharpThreeSAT] + decide + +/-- The certificate for the OR-gate split applies to any variable encoding. -/ +example : ComputableInPolyTime (CNF.toExact3CNF (V := ℕ)) + (encList (encList (encPair encNat encBool))) + (encList (encList (encPair (encSum encNat (encPair encUnary encUnary)) encBool))) := + CNF.isPolyTime_toExact3CNF encNat + +/-- Hardness passes on: anything `#SAT` reduces to parsimoniously, `#3SAT-3` also receives. -/ +example {β : Type} {encB : β ↪ List Bool} {g : β → ℕ} + (h : ParsimoniousReducible encCNF encB SharpThreeSATThree g) : SharpPHard encB g := + SharpThreeSATThree_sharpPComplete.2.trans h + +/-- Both counting problems belong to `#P`. -/ +example : SharpThreeSAT ∈ SharpP encCNF ∧ SharpThreeSATThree ∈ SharpP encCNF := + ⟨SharpThreeSAT_sharpPComplete.1, SharpThreeSATThree_sharpPComplete.1⟩ + +end ComplexityTests.SharpThreeSAT From de7022d2b5977b3e5169571f7ca42931ca3eecfa Mon Sep 17 00:00:00 2001 From: Samuel Schlesinger Date: Tue, 27 Oct 2026 05:48:22 +0000 Subject: [PATCH 4/7] feat(FineGrained): add Section 6.3 compiled parity-row schedule feasibility and examination bounds (#1226) (#1304) * feat(FineGrained): add Section 6.3 compiled parity-row schedule feasibility and examination bounds (#1226) * doc(FineGrained): distinguish equation ratio from source sparsity parameter --- Complexity.lean | 1 + .../FineGrained/SparseParitySchedule.lean | 780 ++++++++++++++++++ .../FineGrained/SparseParitySchedule.lean | 279 +++++++ 3 files changed, 1060 insertions(+) create mode 100644 Complexity/Computability/Complexity/FineGrained/SparseParitySchedule.lean create mode 100644 ComplexityTests/Computability/Complexity/FineGrained/SparseParitySchedule.lean diff --git a/Complexity.lean b/Complexity.lean index b894ae44c1..c1042045d7 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -775,6 +775,7 @@ public import Complexity.Computability.Complexity.FineGrained.ReplacementParams public import Complexity.Computability.Complexity.FineGrained.ReplacementScheduleDesign public import Complexity.Computability.Complexity.FineGrained.Schoening public import Complexity.Computability.Complexity.FineGrained.SparseBranchSchedule +public import Complexity.Computability.Complexity.FineGrained.SparseParitySchedule public import Complexity.Computability.Complexity.FineGrained.Sparsification public import Complexity.Computability.Complexity.HardnessEncoding.PolyTime public import Complexity.Computability.Complexity.HardnessEncoding.Program diff --git a/Complexity/Computability/Complexity/FineGrained/SparseParitySchedule.lean b/Complexity/Computability/Complexity/FineGrained/SparseParitySchedule.lean new file mode 100644 index 0000000000..95ba9d34b3 --- /dev/null +++ b/Complexity/Computability/Complexity/FineGrained/SparseParitySchedule.lean @@ -0,0 +1,780 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Complexity.Computability.Complexity.FineGrained.SparseBranchSchedule +public import Complexity.Logics.Propositional.CNF.SparseParityCounter + +/-! +# Section 6.3 compiled CNF parity-row schedule feasibility and domain-examination bounds + +This module connects the compiled CNF parity-row systems and greedy model-counting reduction of +`Complexity.Logics.Propositional.CNF.SparseParityCounter` (Sections 6.1–6.3, Lemmas 6.1–6.2, +printed pp. 39–43 of [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH]) with the +per-branch schedule feasibility, `ReplacementDesign` existence, and domain-examination bounds of +`Complexity.Computability.Complexity.FineGrained.SparseBranchSchedule` (Section 5.5, Proof of +Theorem 2.2, printed pp. 38–39): + +1. **Incidence, free-variable, and scope-width bounds for `CNF.parityRowSystem` (§6.3, p. 43)**: + In Lemma 6.2 and Section 6.3, every candidate parity-row list `rows : List (Fin φ.length → Bool)` + (as well as the final list `A (3 * n)`) has at most `6 * Fintype.card V` equations, giving + equation-count ratio `6`, and row Hamming weight at most `W` (with `W = max D₀ P`). Theorem 2.2 + uses a sufficiently large `c = c(k)` to bound both the equation count and monomial sparsity. + For any width-`≤ k` CNF + `φ : CNF V`: + - `CNF.totalIncidence_parityRowSystem_le_six_mul`: when `rows.length ≤ 6 * Fintype.card V` and + `∀ r ∈ rows, weight r ≤ W`, total variable-equation incidence across `φ.parityRowSystem rows` + is at most `(6 * (k * (W * 2 ^ k))) * Fintype.card V`. + - `CNF.card_le_two_mul_freeVar_parityRowSystem`: when `1 ≤ b₀`, `2 ≤ s_div`, and + `(6 * (k * (W * 2 ^ k))) * s_div ≤ b₀`, removing the heavy variables + `S = heavyVars univ univ (fun j => equationScope (φ.parityRowSystem rows j)) b₀` leaves + `Fintype.card V ≤ 2 * n'`, `Fintype.card V / 2 ≤ n'`, and at most + `2 ^ (Fintype.card V / s_div)` branches in `branchAssignments S`. + - `CNF.card_branchAugmentedScope_parityRowSystem_le`: every factor in + `branchAugmentedScope S e univ (φ.parityRowSystem rows) b` has scope width at most + `max (k * (W * 2 ^ k)) 1`. +2. **Per-branch and uniform `ReplacementDesign` existence and `(16 * k + 4)` domain-examination + bounds for `CNF.parityRowSystem` (§6.3, p. 43)**: + - `CNF.exists_branch_replacementDesign_parityRowSystem` and + `CNF.exists_branch_schedule_parityRowSystem`: instantiate + `Feasible.exists_branch_replacementDesign` and `exists_branch_schedule_replacementDesign` + directly on `φ.parityRowSystem rows` with scope width bound `w = k * (W * 2 ^ k)`. + - `CNF.branch_domainExaminationBound_parityRowSystem_le` and + `CNF.sum_branch_domainExaminationBound_parityRowSystem_le`: specialize + `Feasible.branch_domainExaminationBound_le` and `sum_branch_domainExaminationBound_le` at the + Lemma 6.2 equation-count ratio `c = 6` (`rows.length ≤ 6 * Fintype.card V`), yielding the + explicit single-branch examination bound + `2 * numNodes n' * numGroups n' * exp (((16 * k + 4) : ℝ) * n' * rowCost β n')` (since + `k * (2 * 6 + 4) + 4 = 16 * k + 4`) and the branch-summed bound + `2 ^ (Fintype.card V / s_div) * (2 * numNodes n' * numGroups n' *` + `exp (((16 * k + 4) : ℝ) * n' * rowCost β n'))`. +3. **End-to-end Lemma 6.1 + Lemma 6.2 + Section 6.3 greedy parity-test schedule theorems**: + - `CNF.exists_greedy_parityRowSystem_branch_schedule`: combines + `CNF.exists_initialTests_commonZeroCount_greedy` with + `CNF.exists_branch_schedule_parityRowSystem`, establishing that at every greedy stage + `i ≤ 3 * Fintype.card V` (with `W = max D₀ P`), `(A i).length ≤ 6 * Fintype.card V`, every + branch of `φ.parityRowSystem (A i)` is `Feasible` and admits a concrete `ReplacementDesign` in + the polylogarithmic regime, and at stage `3 * Fintype.card V`, + `commonZeroCount univ (φ.parityRowSystem (A (3 * Fintype.card V))) =` + `φ.modelCountOn Finset.univ`. + - `CNF.initialRowWeightBound_le_mul_pow_clog`, + `CNF.exists_branch_schedule_parityRowSystem_of_le_clog_pow`, and + `CNF.exists_greedy_parityRowSystem_branch_schedule_of_le_clog_pow`: discharge the free-variable + polylogarithmic schedule hypotheses `n₀ ≤ n'`, `b₀ + 1 ≤ (Nat.clog 2 n') ^ β`, and + `max (k * (W * 2 ^ k)) 1 ≤ (Nat.clog 2 n') ^ β` unconditionally at the canonical Section 5.5 / + Section 6.3 heavy-variable branching schedule `s_div = (Nat.clog 2 n).sqrt`, + `b₀ = CNF.parityBranchThreshold k W n`, and `β = 2 * β_B + 5` whenever + `B ≤ C_B * (Nat.clog 2 n) ^ β_B` and `N₀ ≤ n`. + +## Main definitions + +* `Cslib.Logic.PL.CNF.initialRowWeightCoeff` +* `Cslib.Logic.PL.CNF.parityBranchThreshold` +* `Cslib.Logic.PL.CNF.parityScheduleCoeff` + +## Main results + +* `Cslib.Logic.PL.CNF.totalIncidence_parityRowSystem_le_six_mul` +* `Cslib.Logic.PL.CNF.card_le_two_mul_freeVar_parityRowSystem` +* `Cslib.Logic.PL.CNF.card_branchAugmentedScope_parityRowSystem_le` +* `Cslib.Logic.PL.CNF.exists_branch_replacementDesign_parityRowSystem` +* `Cslib.Logic.PL.CNF.exists_branch_schedule_parityRowSystem` +* `Cslib.Logic.PL.CNF.branch_domainExaminationBound_parityRowSystem_le` +* `Cslib.Logic.PL.CNF.sum_branch_domainExaminationBound_parityRowSystem_le` +* `Cslib.Logic.PL.CNF.exists_greedy_parityRowSystem_branch_schedule` +* `Cslib.Logic.PL.CNF.initialRowWeightBound_le_mul_pow_clog` +* `Cslib.Logic.PL.CNF.exists_branch_schedule_parityRowSystem_of_le_clog_pow` +* `Cslib.Logic.PL.CNF.exists_greedy_parityRowSystem_branch_schedule_of_le_clog_pow` + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH] + (Section 5.5, pp. 38–39; Sections 6.1–6.3, pp. 39–43) +-/ + +public section + +namespace Cslib.Logic.PL + +open Finset +open Cslib.BitString +open Cslib.LinearAlgebra +open Cslib.Complexity.SubexponentialSAT.ReplacementParams + +variable {V : Type*} {n' : ℕ} + +/-! ### 1. Incidence, free-variable, and scope-width bounds for `CNF.parityRowSystem` -/ + +/-- **Section 6.2 & 6.3 ($M \le 6 k W 2^k n$ for candidate parity-row systems):** +when every clause of `φ` has width at most `k`, every row in `rows` has Hamming weight at most `W`, +and `rows.length ≤ 6 * Fintype.card V`, the total variable-equation incidence of +`φ.parityRowSystem rows` is at most `(6 * (k * (W * 2 ^ k))) * Fintype.card V`. -/ +theorem CNF.totalIncidence_parityRowSystem_le_six_mul [Fintype V] [DecidableEq V] + {φ : CNF V} {k W : ℕ} (hk : ∀ c ∈ φ, c.length ≤ k) + {rows : List (Fin φ.length → Bool)} (hW : ∀ r ∈ rows, weight r ≤ W) + (hlen : rows.length ≤ 6 * Fintype.card V) : + totalIncidence (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) ≤ + (6 * (k * (W * 2 ^ k))) * Fintype.card V := by + calc totalIncidence (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) + ≤ (k * (W * 2 ^ k)) * rows.length := CNF.totalIncidence_parityRowSystem_le hk hW + _ ≤ (k * (W * 2 ^ k)) * (6 * Fintype.card V) := Nat.mul_le_mul_left _ hlen + _ = (6 * (k * (W * 2 ^ k))) * Fintype.card V := by ring + +/-- **Section 5.5 & 6.3 ($n \le 2n'$ and branch count $\le 2^{n / s}$ for `CNF.parityRowSystem`):** +when `∀ c ∈ φ, c.length ≤ k`, `∀ r ∈ rows, weight r ≤ W`, `rows.length ≤ 6 * Fintype.card V`, +`1 ≤ b₀`, `2 ≤ s_div`, and `(6 * (k * (W * 2 ^ k))) * s_div ≤ b₀`, removing the heavy variables +`S = heavyVars univ univ (fun j => equationScope (φ.parityRowSystem rows j)) b₀` leaves +`Fintype.card V ≤ 2 * n'`, `Fintype.card V / 2 ≤ n'`, and at most `2 ^ (Fintype.card V / s_div)` +branches in `branchAssignments S`. -/ +theorem CNF.card_le_two_mul_freeVar_parityRowSystem [Fintype V] [DecidableEq V] + {φ : CNF V} {k W b₀ s_div : ℕ} (hk : ∀ c ∈ φ, c.length ≤ k) + {rows : List (Fin φ.length → Bool)} (hW : ∀ r ∈ rows, weight r ≤ W) + (hlen : rows.length ≤ 6 * Fintype.card V) + (hb₀ : 1 ≤ b₀) (hs_div : 2 ≤ s_div) + (h_thresh : (6 * (k * (W * 2 ^ k))) * s_div ≤ b₀) + (e : ↥(univ \ heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) ≃ Fin n') : + (Fintype.card V ≤ 2 * n' ∧ Fintype.card V / 2 ≤ n') ∧ + #(branchAssignments + (heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀)) ≤ + 2 ^ (Fintype.card V / s_div) := by + have hM : + totalIncidence (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) * s_div ≤ + b₀ * Fintype.card V := by + calc totalIncidence (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) * s_div + ≤ ((6 * (k * (W * 2 ^ k))) * Fintype.card V) * s_div := + Nat.mul_le_mul_right s_div (CNF.totalIncidence_parityRowSystem_le_six_mul hk hW hlen) + _ = ((6 * (k * (W * 2 ^ k))) * s_div) * Fintype.card V := by ring + _ ≤ b₀ * Fintype.card V := Nat.mul_le_mul_right _ h_thresh + exact ⟨card_le_two_mul_freeVar_of_heavyVars univ + (fun j => equationScope (φ.parityRowSystem rows j)) hb₀ hs_div hM e, + card_branchAssignments_heavyVars_le_two_pow_div univ + (fun j => equationScope (φ.parityRowSystem rows j)) hb₀ (by omega) hM⟩ + +/-- **Section 5.5 & 6.3 (per-factor scope width $\le \max(k W 2^k, 1)$ for `CNF.parityRowSystem`):** +when `∀ c ∈ φ, c.length ≤ k` and `∀ r ∈ rows, weight r ≤ W`, every factor `f` of +`branchAugmentedScope S e univ (φ.parityRowSystem rows) b` has scope cardinality at most +`max (k * (W * 2 ^ k)) 1`. -/ +theorem CNF.card_branchAugmentedScope_parityRowSystem_le [Fintype V] [DecidableEq V] + {φ : CNF V} {k W : ℕ} (hk : ∀ c ∈ φ, c.length ≤ k) + {rows : List (Fin φ.length → Bool)} (hW : ∀ r ∈ rows, weight r ≤ W) + (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') (b : V → Bool) + (f : {j : Fin rows.length // j ∈ (univ : Finset (Fin rows.length))} ⊕ Fin n') : + #(branchAugmentedScope S e univ (φ.parityRowSystem rows) b f) ≤ + max (k * (W * 2 ^ k)) 1 := + card_branchAugmentedScope_le S e univ (φ.parityRowSystem rows) b + (fun j _ => CNF.card_equationScope_parityRowSystem_le hk hW j) f + +/-! ### 2. Per-branch and uniform `ReplacementDesign` and domain-examination bounds -/ + +/-- **Section 6.3 per-branch `ReplacementDesign` existence for `CNF.parityRowSystem`**: +Given a width-`≤ k` CNF `φ : CNF V`, a parity-row list `rows : List (Fin φ.length → Bool)` with +`∀ r ∈ rows, weight r ≤ W`, +`S = heavyVars univ univ (fun j => equationScope (φ.parityRowSystem rows j)) b₀`, +`e : ↥(univ \ S) ≃ Fin n'`, and +`hF : Feasible β n' (b₀ + 1) (max (k * (W * 2 ^ k)) 1)` +`(∑ f, #(branchAugmentedScope S e univ (φ.parityRowSystem rows) b f))`, +there exists a concrete Section 4 `ReplacementDesign` for +`branchAugmentedScope S e univ (φ.parityRowSystem rows) b` at the Lemma 4.8 schedule. -/ +theorem CNF.exists_branch_replacementDesign_parityRowSystem [Fintype V] [DecidableEq V] + {φ : CNF V} {β k W b₀ : ℕ} (hk : ∀ c ∈ φ, c.length ≤ k) + {rows : List (Fin φ.length → Bool)} (hW : ∀ r ∈ rows, weight r ≤ W) + (e : ↥(univ \ heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) ≃ Fin n') + (b : V → Bool) + (hF : Feasible β n' (b₀ + 1) (max (k * (W * 2 ^ k)) 1) + (∑ f : {j : Fin rows.length // j ∈ (univ : Finset (Fin rows.length))} ⊕ Fin n', + #(branchAugmentedScope + (heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) + e univ (φ.parityRowSystem rows) b f))) : + Nonempty + (ReplacementDesign + (branchAugmentedScope + (heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) + e univ (φ.parityRowSystem rows) b) + (numNodes n') (numGroups n') (intervalStart β n') (rowBound β n') (tauRat n')) := + hF.exists_branch_replacementDesign univ univ (φ.parityRowSystem rows) (Subset.refl _) e b + (fun j _ => CNF.card_equationScope_parityRowSystem_le hk hW j) + +/-- **Uniform Section 6.3 schedule feasibility and `ReplacementDesign` existence for +`CNF.parityRowSystem`**: +For every `1 ≤ β`, there exists `n₀ ≥ 20` such that for any width-`≤ k` CNF `φ : CNF V` and +parity-row list `rows : List (Fin φ.length → Bool)` with `∀ r ∈ rows, weight r ≤ W`, whenever the +free-variable count `n'` after removing +`S = heavyVars univ univ (fun j => equationScope (φ.parityRowSystem rows j)) b₀` satisfies +`n₀ ≤ n'`, `b₀ + 1 ≤ (Nat.clog 2 n') ^ β`, and `max (k * (W * 2 ^ k)) 1 ≤ (Nat.clog 2 n') ^ β`, +**every** branch assignment `b : V → Bool` simultaneously satisfies `Feasible` and admits a +concrete `ReplacementDesign`. -/ +theorem CNF.exists_branch_schedule_parityRowSystem {β : ℕ} (hβ : 1 ≤ β) : + ∃ n₀ : ℕ, 20 ≤ n₀ ∧ + ∀ {V : Type*} [Fintype V] [DecidableEq V] + {φ : CNF V} ⦃n' k W b₀ : ℕ⦄, + (∀ c ∈ φ, c.length ≤ k) → + ∀ {rows : List (Fin φ.length → Bool)}, + (∀ r ∈ rows, weight r ≤ W) → + ∀ (e : ↥(univ \ heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) ≃ Fin n'), + n₀ ≤ n' → + b₀ + 1 ≤ Nat.clog 2 n' ^ β → + max (k * (W * 2 ^ k)) 1 ≤ Nat.clog 2 n' ^ β → + ∀ b : V → Bool, + Feasible β n' (b₀ + 1) (max (k * (W * 2 ^ k)) 1) + (∑ f : {j : Fin rows.length // j ∈ (univ : Finset (Fin rows.length))} ⊕ Fin n', + #(branchAugmentedScope + (heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) + e univ (φ.parityRowSystem rows) b f)) ∧ + Nonempty + (ReplacementDesign + (branchAugmentedScope + (heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) + e univ (φ.parityRowSystem rows) b) + (numNodes n') (numGroups n') (intervalStart β n') + (rowBound β n') (tauRat n')) := by + obtain ⟨n₀, hn₀20, hsched⟩ := exists_branch_schedule_replacementDesign hβ + refine ⟨n₀, hn₀20, + fun {V _ _ φ n' k W b₀} hk {rows} hW e hn' hb₀ hw_pow b => ?_⟩ + exact hsched univ univ (φ.parityRowSystem rows) (Subset.refl _) e hn' hb₀ hw_pow + (fun j _ => CNF.card_equationScope_parityRowSystem_le hk hW j) b + +/-- **Section 6.3 single-branch domain-examination bound for `CNF.parityRowSystem` ($c = 6$):** +For any parity-row list `rows : List (Fin φ.length → Bool)` with `rows.length ≤ 6 * Fintype.card V` +and `Fintype.card V ≤ 2 * n'`, and any branch `b` with `hF : Feasible β n' b_occ w_width M'` and +concrete schedule design `D`, at the Section 5.3 / 6.3 degree budget +`d = k * (rows.length + 2 * (branchPrecision (Fintype.card V) - 1))` and canonical truncation order +`q = (d * rowBound β n') / numNodes n'`, the induced Section 3 counting design +`C := hF.toCountingDesign D s` satisfies +`(∑ t : Fin (numNodes n'), (#{p : Finset (Fin n') × Finset (Fin n') | #p.1 ≤ q ∧ p.2 ⊆ p.1} : ℝ) * + (2 * (#(C.measurementBox t) : ℝ) * + ∑ g : Fin (numGroups n'), (Fintype.card (C.LocalCube t g) : ℝ))) + ≤ 2 * numNodes n' * numGroups n' * exp (((16 * k + 4 : ℕ) : ℝ) * n' * rowCost β n')`. -/ +theorem CNF.branch_domainExaminationBound_parityRowSystem_le [Fintype V] + {F_idx : Type*} [Fintype F_idx] {scope : F_idx → Finset (Fin n')} + {φ : CNF V} {β b_occ w_width M' k : ℕ} + (rows : List (Fin φ.length → Bool)) + (hF : Feasible β n' b_occ w_width M') + (D : ReplacementDesign scope (numNodes n') (numGroups n') (intervalStart β n') + (rowBound β n') (tauRat n')) + (s : Finset F_idx) (hlen : rows.length ≤ 6 * Fintype.card V) (hn : Fintype.card V ≤ 2 * n') : + let d := k * (rows.length + 2 * (branchPrecision (Fintype.card V) - 1)) + let q := (d * rowBound β n') / numNodes n' + let C := hF.toCountingDesign D s + (∑ t : Fin (numNodes n'), + (#{p : Finset (Fin n') × Finset (Fin n') | #p.1 ≤ q ∧ p.2 ⊆ p.1} : ℝ) * + (2 * (#(C.measurementBox t) : ℝ) * + ∑ g : Fin (numGroups n'), (Fintype.card (C.LocalCube t g) : ℝ))) ≤ + 2 * (numNodes n' : ℝ) * (numGroups n' : ℝ) * + Real.exp (((16 * k + 4 : ℕ) : ℝ) * (n' : ℝ) * rowCost β n') ∧ + 2 * (numNodes n' : ℝ) * (numGroups n' : ℝ) * + Real.exp (((16 * k + 4 : ℕ) : ℝ) * (n' : ℝ) * rowCost β n') ≤ + 2 * (numNodes n' : ℝ) * (numGroups n' : ℝ) * + Real.exp (((16 * k + 4 : ℕ) : ℝ) * (n' : ℝ) * + (1028 * ((β : ℝ) + 3) * (Nat.clog 2 (Nat.clog 2 n') : ℝ) ^ 2 / + Real.sqrt (Nat.clog 2 n'))) := by + intro d q C + have hcard : #(univ : Finset (Fin rows.length)) ≤ 6 * Fintype.card V := by + simpa only [card_univ, Fintype.card_fin] using hlen + have hbound := hF.branch_domainExaminationBound_le (ι := V) (k := k) (c := 6) + (univ : Finset (Fin rows.length)) D s hcard hn + have h16 : k * (2 * 6 + 4) + 4 = 16 * k + 4 := by ring + simpa only [card_univ, Fintype.card_fin, h16] using hbound + +/-- **Section 6.3 multi-branch domain-examination bound across `branchAssignments` for +`CNF.parityRowSystem`**: +When `∀ c ∈ φ, c.length ≤ k`, `∀ r ∈ rows, weight r ≤ W`, `rows.length ≤ 6 * Fintype.card V`, +`1 ≤ b₀`, `2 ≤ s_div`, `(6 * (k * (W * 2 ^ k))) * s_div ≤ b₀`, and `e : ↥(univ \ S) ≃ Fin n'`, +the free-variable dimension satisfies `Fintype.card V ≤ 2 * n'`, and if each branch +`b ∈ branchAssignments S` has its real examination sum bounded via +`CNF.branch_domainExaminationBound_parityRowSystem_le`, then the sum of those examination bounds +across all `b ∈ branchAssignments S` is at most +`(2 : ℝ) ^ (Fintype.card V / s_div) * (2 * numNodes n' * numGroups n' * + exp (((16 * k + 4 : ℕ) : ℝ) * n' * rowCost β n'))`. -/ +theorem CNF.sum_branch_domainExaminationBound_parityRowSystem_le [Fintype V] [DecidableEq V] + {φ : CNF V} {β k W b₀ s_div : ℕ} (hk : ∀ c ∈ φ, c.length ≤ k) + {rows : List (Fin φ.length → Bool)} (hW : ∀ r ∈ rows, weight r ≤ W) + (hlen : rows.length ≤ 6 * Fintype.card V) + (hb₀ : 1 ≤ b₀) (hs_div : 2 ≤ s_div) + (h_thresh : (6 * (k * (W * 2 ^ k))) * s_div ≤ b₀) + (e : ↥(univ \ heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) ≃ Fin n') + (examSum : (V → Bool) → ℝ) + (hexam : ∀ b ∈ branchAssignments + (heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀), + examSum b ≤ + 2 * (numNodes n' : ℝ) * (numGroups n' : ℝ) * + Real.exp (((16 * k + 4 : ℕ) : ℝ) * (n' : ℝ) * rowCost β n')) : + Fintype.card V ≤ 2 * n' ∧ + (∑ b ∈ branchAssignments + (heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀), + examSum b) ≤ + (2 : ℝ) ^ (Fintype.card V / s_div) * + (2 * (numNodes n' : ℝ) * (numGroups n' : ℝ) * + Real.exp (((16 * k + 4 : ℕ) : ℝ) * (n' : ℝ) * rowCost β n')) := by + have hM : + totalIncidence (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) * s_div ≤ + b₀ * Fintype.card V := by + calc totalIncidence (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) * s_div + ≤ ((6 * (k * (W * 2 ^ k))) * Fintype.card V) * s_div := + Nat.mul_le_mul_right s_div (CNF.totalIncidence_parityRowSystem_le_six_mul hk hW hlen) + _ = ((6 * (k * (W * 2 ^ k))) * s_div) * Fintype.card V := by ring + _ ≤ b₀ * Fintype.card V := Nat.mul_le_mul_right _ h_thresh + have h16 : k * (2 * 6 + 4) + 4 = 16 * k + 4 := by ring + simpa only [h16] using + sum_branch_domainExaminationBound_le (β := β) (k := k) (c := 6) + (univ : Finset (Fin rows.length)) (φ.parityRowSystem rows) hb₀ hs_div hM e examSum + (by simpa only [h16] using hexam) + +/-! ### 3. End-to-end Lemma 6.1 + Lemma 6.2 + Section 6.3 greedy parity-test schedule theorem -/ + +private theorem greedyRowListLength_le_initialRowWeightBound (B m : ℕ) : + CNF.greedyRowListLength B m ≤ CNF.initialRowWeightBound B m := by + unfold CNF.greedyRowListLength CNF.initialRowWeightBound + have h1 : 20000 * B ^ 2 ≤ 320000 * B ^ 2 := Nat.mul_le_mul_right _ (by decide) + have h2 : (Nat.log 2 m + 1) ^ 2 ≤ (Nat.log 2 m + 1) ^ 3 := + Nat.pow_le_pow_right (Nat.succ_pos _) (by decide) + exact Nat.mul_le_mul h1 h2 + +private theorem max_initialRowWeightBound_greedyRowListLength (B m : ℕ) : + max (CNF.initialRowWeightBound B m) (CNF.greedyRowListLength B m) = + CNF.initialRowWeightBound B m := + max_eq_left (greedyRowListLength_le_initialRowWeightBound B m) + +/-- **Lemmas 6.1–6.2 & Section 6.3 (greedy parity-row selection with uniform per-branch +`Feasible` and `ReplacementDesign` guarantees at every stage `i ≤ 3 * n`)**: +For any `1 ≤ β`, there exists `n₀ ≥ 20` such that for any width-`≤ k` CNF `φ : CNF V` on +`n = Fintype.card V ≥ 1` variables with `φ.length ≤ B * n` (`1 ≤ B`), there exist initial parity +rows `A₀` (`A₀.length ≤ 3 * n`, each of weight at most `D₀ = CNF.initialRowWeightBound B φ.length`) +such that for any `3 * n`-step greedy sequence `A (i + 1) = A i ++ [testRow (γ i)]` with `A 0 = A₀` +minimizing `commonZeroCount` over candidate rows of list length +`P = CNF.greedyRowListLength B φ.length`: +1. After `3 * n` rounds, + `commonZeroCount univ (φ.parityRowSystem (A (3 * n))) = φ.modelCountOn Finset.univ`. +2. At every stage `i ≤ 3 * n`, `(A i).length ≤ 6 * n`, and whenever the free-variable count `n'` + after removing + `S_i = heavyVars univ univ (fun j => equationScope (φ.parityRowSystem (A i) j)) b₀` satisfies + `n₀ ≤ n'`, `b₀ + 1 ≤ (Nat.clog 2 n') ^ β`, and + `max (k * (max D₀ P * 2 ^ k)) 1 ≤ (Nat.clog 2 n') ^ β`, every branch `b : V → Bool` of + `φ.parityRowSystem (A i)` satisfies + `Feasible β n' (b₀ + 1) (max (k * (max D₀ P * 2 ^ k)) 1) M'_b` + and admits a concrete `ReplacementDesign`. -/ +theorem CNF.exists_greedy_parityRowSystem_branch_schedule {β : ℕ} (hβ : 1 ≤ β) : + ∃ n₀ : ℕ, 20 ≤ n₀ ∧ + ∀ {V : Type*} [Fintype V] [DecidableEq V] + (φ : CNF V) {k B : ℕ}, + (∀ c ∈ φ, c.length ≤ k) → + 1 ≤ Fintype.card V → + 1 ≤ B → + φ.length ≤ B * Fintype.card V → + let D₀ := CNF.initialRowWeightBound B φ.length + let P := CNF.greedyRowListLength B φ.length + ∃ A₀ : List (Fin φ.length → Bool), A₀.length ≤ 3 * Fintype.card V ∧ + (∀ r ∈ A₀, weight r ≤ D₀) ∧ + ∀ (A : ℕ → List (Fin φ.length → Bool)) + (γ : ℕ → Fin P → Fin φ.length × Bool), + A 0 = A₀ → (∀ i, A (i + 1) = A i ++ [testRow (γ i)]) → + (∀ i (γ' : Fin P → Fin φ.length × Bool), + commonZeroCount univ (φ.parityRowSystem (A i ++ [testRow (γ i)])) ≤ + commonZeroCount univ (φ.parityRowSystem (A i ++ [testRow γ']))) → + commonZeroCount univ (φ.parityRowSystem (A (3 * Fintype.card V))) = + φ.modelCountOn Finset.univ ∧ + ∀ i ≤ 3 * Fintype.card V, + (A i).length ≤ 6 * Fintype.card V ∧ + ∀ ⦃n' b₀ : ℕ⦄ + (e : ↥(univ \ heavyVars univ (univ : Finset (Fin (A i).length)) + (fun j => equationScope (φ.parityRowSystem (A i) j)) b₀) ≃ Fin n'), + n₀ ≤ n' → + b₀ + 1 ≤ Nat.clog 2 n' ^ β → + max (k * (max D₀ P * 2 ^ k)) 1 ≤ Nat.clog 2 n' ^ β → + ∀ b : V → Bool, + Feasible β n' (b₀ + 1) (max (k * (max D₀ P * 2 ^ k)) 1) + (∑ f : {j : Fin (A i).length // + j ∈ (univ : Finset (Fin (A i).length))} ⊕ Fin n', + #(branchAugmentedScope + (heavyVars univ (univ : Finset (Fin (A i).length)) + (fun j => equationScope (φ.parityRowSystem (A i) j)) b₀) + e univ (φ.parityRowSystem (A i)) b f)) ∧ + Nonempty + (ReplacementDesign + (branchAugmentedScope + (heavyVars univ (univ : Finset (Fin (A i).length)) + (fun j => equationScope (φ.parityRowSystem (A i) j)) b₀) + e univ (φ.parityRowSystem (A i)) b) + (numNodes n') (numGroups n') (intervalStart β n') + (rowBound β n') (tauRat n')) := by + obtain ⟨n₀, hn₀20, hsched⟩ := exists_branch_schedule_replacementDesign hβ + refine ⟨n₀, hn₀20, fun {V _ _} φ {k B} hk hn hB hm => ?_⟩ + intro D₀ P + obtain ⟨A₀, hlen0, hw0, hgreedy⟩ := φ.exists_initialTests_commonZeroCount_greedy hk hn hB hm + refine ⟨A₀, hlen0, hw0, fun A γ h0 hA hγ => ?_⟩ + obtain ⟨hcount, hstage⟩ := hgreedy A γ h0 hA hγ + refine ⟨hcount, fun i hi => ?_⟩ + obtain ⟨hlen_i, hscope_i⟩ := hstage i hi + refine ⟨hlen_i, fun {n' b₀} e hn' hb₀ hw_pow b => ?_⟩ + exact hsched univ univ (φ.parityRowSystem (A i)) (Subset.refl _) e hn' hb₀ hw_pow + (fun j _ => (hscope_i j).2.2) b + +/-! ### 4. Unconditional polylogarithmic schedule discharge for `B ≤ C_B * (Nat.clog 2 n) ^ β_B` -/ + +private theorem clog_two_le_two_mul_of_le_two_mul {n n' : ℕ} (hn : n ≤ 2 * n') (hn' : 2 ≤ n') : + Nat.clog 2 n ≤ 2 * Nat.clog 2 n' := by + have h1 : 1 ≤ Nat.clog 2 n' := + (Nat.lt_clog_iff_pow_lt one_lt_two).mpr (by omega : 2 ^ 0 < n') + have h2 : n ≤ 2 ^ (Nat.clog 2 n' + 1) := by + calc n ≤ 2 * n' := hn + _ ≤ 2 * 2 ^ Nat.clog 2 n' := Nat.mul_le_mul_left 2 (Nat.le_pow_clog one_lt_two n') + _ = 2 ^ (Nat.clog 2 n' + 1) := (pow_succ' 2 _).symm + have h3 : Nat.clog 2 n ≤ Nat.clog 2 n' + 1 := + (Nat.clog_le_iff_le_pow one_lt_two).mpr h2 + omega + +/-- Coefficient for the polynomial bound on `CNF.initialRowWeightBound B m` when +`B ≤ C_B * (Nat.clog 2 n) ^ β_B` and `m ≤ B * n`. -/ +def CNF.initialRowWeightCoeff (C_B β_B : ℕ) : ℕ := + 320000 * C_B ^ 2 * (C_B + β_B + 2) ^ 3 + +/-- **Section 6.1 & 6.3 ($D_0 \le C \cdot (\lceil \log_2 n \rceil)^{2\beta_B + 3}$):** +when `2 ≤ n`, `m ≤ B * n`, and `B ≤ C_B * (Nat.clog 2 n) ^ β_B`, the initial parity-row weight bound +`CNF.initialRowWeightBound B m` is at most +`CNF.initialRowWeightCoeff C_B β_B * (Nat.clog 2 n) ^ (2 * β_B + 3)`. -/ +theorem CNF.initialRowWeightBound_le_mul_pow_clog {n m B C_B β_B : ℕ} + (hn : 2 ≤ n) (hm : m ≤ B * n) (hB : B ≤ C_B * Nat.clog 2 n ^ β_B) : + CNF.initialRowWeightBound B m ≤ + CNF.initialRowWeightCoeff C_B β_B * Nat.clog 2 n ^ (2 * β_B + 3) := by + set lam := Nat.clog 2 n + have hlam1 : 1 ≤ lam := + (Nat.lt_clog_iff_pow_lt one_lt_two).mpr (by omega : 2 ^ 0 < n) + have hn_pow : n ≤ 2 ^ lam := Nat.le_pow_clog one_lt_two n + have hlam_pow : lam ^ β_B ≤ 2 ^ (lam * β_B) := by + calc lam ^ β_B ≤ (2 ^ lam) ^ β_B := Nat.pow_le_pow_left lam.lt_two_pow_self.le β_B + _ = 2 ^ (lam * β_B) := (pow_mul 2 lam β_B).symm + have hCB_pow : C_B ≤ 2 ^ (C_B * lam) := by + calc C_B ≤ 2 ^ C_B := C_B.lt_two_pow_self.le + _ ≤ 2 ^ (C_B * lam) := Nat.pow_le_pow_right two_pos (Nat.le_mul_of_pos_right C_B hlam1) + have hm_pow : m ≤ 2 ^ ((C_B + β_B + 1) * lam) := by + calc m ≤ B * n := hm + _ ≤ C_B * lam ^ β_B * n := Nat.mul_le_mul_right n hB + _ ≤ 2 ^ (C_B * lam) * 2 ^ (lam * β_B) * 2 ^ lam := + Nat.mul_le_mul (Nat.mul_le_mul hCB_pow hlam_pow) hn_pow + _ = 2 ^ ((C_B + β_B + 1) * lam) := by rw [← pow_add, ← pow_add]; ring_nf + have hlog : Nat.log 2 m + 1 ≤ (C_B + β_B + 2) * lam := by + have hlog_le : Nat.log 2 m ≤ (C_B + β_B + 1) * lam := by + calc Nat.log 2 m ≤ Nat.log 2 (2 ^ ((C_B + β_B + 1) * lam)) := Nat.log_mono_right hm_pow + _ = (C_B + β_B + 1) * lam := Nat.log_pow one_lt_two _ + nlinarith + have hB2 : B ^ 2 ≤ C_B ^ 2 * lam ^ (2 * β_B) := by + calc B ^ 2 ≤ (C_B * lam ^ β_B) ^ 2 := Nat.pow_le_pow_left hB 2 + _ = C_B ^ 2 * lam ^ (2 * β_B) := by rw [mul_pow, ← pow_mul]; ring + have hlog3 : (Nat.log 2 m + 1) ^ 3 ≤ (C_B + β_B + 2) ^ 3 * lam ^ 3 := by + calc (Nat.log 2 m + 1) ^ 3 ≤ ((C_B + β_B + 2) * lam) ^ 3 := Nat.pow_le_pow_left hlog 3 + _ = (C_B + β_B + 2) ^ 3 * lam ^ 3 := mul_pow _ _ 3 + unfold CNF.initialRowWeightBound CNF.initialRowWeightCoeff + calc 320000 * B ^ 2 * (Nat.log 2 m + 1) ^ 3 + ≤ 320000 * (C_B ^ 2 * lam ^ (2 * β_B)) * ((C_B + β_B + 2) ^ 3 * lam ^ 3) := + Nat.mul_le_mul (Nat.mul_le_mul_left 320000 hB2) hlog3 + _ = 320000 * C_B ^ 2 * (C_B + β_B + 2) ^ 3 * lam ^ (2 * β_B + 3) := by + rw [pow_add]; ring + +/-- Canonical Section 5.5 & 6.3 heavy-variable branching threshold +`b₀ = (6 * (k * (W * 2 ^ k))) * (Nat.clog 2 n).sqrt + 1` for a width-`≤ k` CNF with row-weight +bound `W` on `n` variables. -/ +def CNF.parityBranchThreshold (k W n : ℕ) : ℕ := + (6 * (k * (W * 2 ^ k))) * (Nat.clog 2 n).sqrt + 1 + +/-- Schedule coefficient bounding `CNF.parityBranchThreshold k W n + 1` and +`max (k * (W * 2 ^ k)) 1` against `(Nat.clog 2 n') ^ (2 * β_B + 4)` when `n ≤ 2 * n'`. -/ +def CNF.parityScheduleCoeff (k C_B β_B : ℕ) : ℕ := + (6 * k * 2 ^ k * CNF.initialRowWeightCoeff C_B β_B + 2) * 2 ^ (2 * β_B + 4) + +private theorem parityBranchThreshold_succ_and_width_le_clog_pow {n n' m B W k C_B β_B : ℕ} + (hn : 2 ≤ n) (hn_le : n ≤ 2 * n') (hn' : 2 ≤ n') + (hm : m ≤ B * n) (hB : B ≤ C_B * Nat.clog 2 n ^ β_B) + (hW : W ≤ CNF.initialRowWeightBound B m) + (hcoeff : CNF.parityScheduleCoeff k C_B β_B ≤ Nat.clog 2 n') : + CNF.parityBranchThreshold k W n + 1 ≤ Nat.clog 2 n' ^ (2 * β_B + 5) ∧ + max (k * (W * 2 ^ k)) 1 ≤ Nat.clog 2 n' ^ (2 * β_B + 5) := by + set lam := Nat.clog 2 n + set lam' := Nat.clog 2 n' + have hlam1 : 1 ≤ lam := + (Nat.lt_clog_iff_pow_lt one_lt_two).mpr (by omega : 2 ^ 0 < n) + have hsqrt1 : 1 ≤ lam.sqrt := Nat.le_sqrt.mpr (by omega) + have hmax_le : max (k * (W * 2 ^ k)) 1 ≤ CNF.parityBranchThreshold k W n + 1 := by + unfold CNF.parityBranchThreshold + have hmul : k * (W * 2 ^ k) ≤ (6 * (k * (W * 2 ^ k))) * (Nat.clog 2 n).sqrt := by + calc k * (W * 2 ^ k) = (k * (W * 2 ^ k)) * 1 := (mul_one _).symm + _ ≤ (6 * (k * (W * 2 ^ k))) * (Nat.clog 2 n).sqrt := + Nat.mul_le_mul (Nat.le_mul_of_pos_left _ (by decide)) hsqrt1 + omega + have hW_le : W ≤ CNF.initialRowWeightCoeff C_B β_B * lam ^ (2 * β_B + 3) := + hW.trans (CNF.initialRowWeightBound_le_mul_pow_clog hn hm hB) + have hterm : + (6 * (k * (W * 2 ^ k))) * lam.sqrt ≤ + (6 * k * 2 ^ k * CNF.initialRowWeightCoeff C_B β_B) * lam ^ (2 * β_B + 4) := by + calc (6 * (k * (W * 2 ^ k))) * lam.sqrt + = (6 * k * 2 ^ k) * W * lam.sqrt := by ring + _ ≤ (6 * k * 2 ^ k) * (CNF.initialRowWeightCoeff C_B β_B * lam ^ (2 * β_B + 3)) * lam := + Nat.mul_le_mul (Nat.mul_le_mul_left _ hW_le) (Nat.sqrt_le_self lam) + _ = (6 * k * 2 ^ k * CNF.initialRowWeightCoeff C_B β_B) * lam ^ (2 * β_B + 4) := by + rw [pow_succ]; ring + have h1_pow : 1 ≤ lam ^ (2 * β_B + 4) := Nat.one_le_pow _ lam hlam1 + have hthresh_lam : + CNF.parityBranchThreshold k W n + 1 ≤ + (6 * k * 2 ^ k * CNF.initialRowWeightCoeff C_B β_B + 2) * lam ^ (2 * β_B + 4) := by + unfold CNF.parityBranchThreshold + calc (6 * (k * (W * 2 ^ k))) * lam.sqrt + 1 + 1 + ≤ (6 * k * 2 ^ k * CNF.initialRowWeightCoeff C_B β_B) * lam ^ (2 * β_B + 4) + + 2 * lam ^ (2 * β_B + 4) := by linarith + _ = (6 * k * 2 ^ k * CNF.initialRowWeightCoeff C_B β_B + 2) * lam ^ (2 * β_B + 4) := by + ring + have hlam_pow : lam ^ (2 * β_B + 4) ≤ 2 ^ (2 * β_B + 4) * lam' ^ (2 * β_B + 4) := by + calc lam ^ (2 * β_B + 4) ≤ (2 * lam') ^ (2 * β_B + 4) := + Nat.pow_le_pow_left (clog_two_le_two_mul_of_le_two_mul hn_le hn') _ + _ = 2 ^ (2 * β_B + 4) * lam' ^ (2 * β_B + 4) := mul_pow 2 lam' _ + have hthresh_lam' : + CNF.parityBranchThreshold k W n + 1 ≤ Nat.clog 2 n' ^ (2 * β_B + 5) := by + calc CNF.parityBranchThreshold k W n + 1 + ≤ (6 * k * 2 ^ k * CNF.initialRowWeightCoeff C_B β_B + 2) * + (2 ^ (2 * β_B + 4) * lam' ^ (2 * β_B + 4)) := + hthresh_lam.trans (Nat.mul_le_mul_left _ hlam_pow) + _ = CNF.parityScheduleCoeff k C_B β_B * lam' ^ (2 * β_B + 4) := by + unfold CNF.parityScheduleCoeff; ring + _ ≤ lam' * lam' ^ (2 * β_B + 4) := Nat.mul_le_mul_right _ hcoeff + _ = lam' ^ (2 * β_B + 5) := (pow_succ' lam' (2 * β_B + 4)).symm + exact ⟨hthresh_lam', hmax_le.trans hthresh_lam'⟩ + +/-- **Unconditional Section 6.3 branch-schedule feasibility and `ReplacementDesign` existence for +`CNF.parityRowSystem` with `B ≤ C_B * (Nat.clog 2 n) ^ β_B`**: +For any `k C_B β_B : ℕ`, there exists an explicit threshold `N₀ ≥ 20` such that for any width-`≤ k` +CNF `φ : CNF V` with `N₀ ≤ Fintype.card V`, `φ.length ≤ B * Fintype.card V`, and +`B ≤ C_B * (Nat.clog 2 (Fintype.card V)) ^ β_B`, and any parity-row list +`rows : List (Fin φ.length → Bool)` with `rows.length ≤ 6 * Fintype.card V`, +`∀ r ∈ rows, weight r ≤ W`, and `W ≤ CNF.initialRowWeightBound B φ.length`, at the canonical +threshold `b₀ = CNF.parityBranchThreshold k W (Fintype.card V)` and exponent `β = 2 * β_B + 5`: +1. `2 ≤ (Nat.clog 2 (Fintype.card V)).sqrt`, +2. `Fintype.card V ≤ 2 * n'` and `Fintype.card V / 2 ≤ n'`, +3. `#(branchAssignments S) ≤ 2 ^ (Fintype.card V / (Nat.clog 2 (Fintype.card V)).sqrt)`, and +4. **every** branch `b : V → Bool` unconditionally satisfies `Feasible (2 * β_B + 5) n' (b₀ + 1)` + `(max (k * (W * 2 ^ k)) 1) M'_b` and admits a concrete `ReplacementDesign`. -/ +theorem CNF.exists_branch_schedule_parityRowSystem_of_le_clog_pow (k C_B β_B : ℕ) : + ∃ N₀ : ℕ, 20 ≤ N₀ ∧ + ∀ {V : Type*} [Fintype V] [DecidableEq V] + {φ : CNF V} ⦃n' B W : ℕ⦄, + (∀ c ∈ φ, c.length ≤ k) → + N₀ ≤ Fintype.card V → + φ.length ≤ B * Fintype.card V → + B ≤ C_B * Nat.clog 2 (Fintype.card V) ^ β_B → + W ≤ CNF.initialRowWeightBound B φ.length → + ∀ {rows : List (Fin φ.length → Bool)}, + (∀ r ∈ rows, weight r ≤ W) → + rows.length ≤ 6 * Fintype.card V → + let b₀ := CNF.parityBranchThreshold k W (Fintype.card V) + ∀ (e : ↥(univ \ heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) ≃ Fin n'), + 2 ≤ (Nat.clog 2 (Fintype.card V)).sqrt ∧ + (Fintype.card V ≤ 2 * n' ∧ Fintype.card V / 2 ≤ n') ∧ + #(branchAssignments + (heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀)) ≤ + 2 ^ (Fintype.card V / (Nat.clog 2 (Fintype.card V)).sqrt) ∧ + ∀ b : V → Bool, + Feasible (2 * β_B + 5) n' (b₀ + 1) (max (k * (W * 2 ^ k)) 1) + (∑ f : {j : Fin rows.length // j ∈ (univ : Finset (Fin rows.length))} ⊕ Fin n', + #(branchAugmentedScope + (heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) + e univ (φ.parityRowSystem rows) b f)) ∧ + Nonempty + (ReplacementDesign + (branchAugmentedScope + (heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) + e univ (φ.parityRowSystem rows) b) + (numNodes n') (numGroups n') (intervalStart (2 * β_B + 5) n') + (rowBound (2 * β_B + 5) n') (tauRat n')) := by + obtain ⟨n₀, hn₀20, hsched⟩ := + CNF.exists_branch_schedule_parityRowSystem (β := 2 * β_B + 5) (by omega) + set C := CNF.parityScheduleCoeff k C_B β_B + refine ⟨max (2 * n₀) (2 ^ (C + 1)), by omega, + fun {V _ _ φ n' B W} hk hN₀ hm hB hW_bound {rows} hW hlen => ?_⟩ + intro b₀ e + have hn20 : 20 ≤ Fintype.card V := by + have : 2 * n₀ ≤ Fintype.card V := (le_max_left _ _).trans hN₀ + omega + have hsqrt : 2 ≤ (Nat.clog 2 (Fintype.card V)).sqrt := by + refine Nat.le_sqrt.mpr ?_ + have h4 : 2 ^ 3 < Fintype.card V := by omega + have hclog : 3 < Nat.clog 2 (Fintype.card V) := (Nat.lt_clog_iff_pow_lt one_lt_two).mpr h4 + omega + have hb₀_pos : 1 ≤ b₀ := Nat.succ_pos _ + have hthresh : (6 * (k * (W * 2 ^ k))) * (Nat.clog 2 (Fintype.card V)).sqrt ≤ b₀ := + Nat.le_succ _ + obtain ⟨⟨hn_le, hn_div⟩, hbranches⟩ := + CNF.card_le_two_mul_freeVar_parityRowSystem hk hW hlen hb₀_pos hsqrt hthresh e + have hn₀_le : n₀ ≤ n' := by + have : 2 * n₀ ≤ Fintype.card V := (le_max_left _ _).trans hN₀ + omega + have hcoeff : C ≤ Nat.clog 2 n' := by + have hpow : 2 ^ (C + 1) ≤ 2 * n' := ((le_max_right _ _).trans hN₀).trans hn_le + have hpow' : 2 ^ C ≤ n' := by rw [pow_succ'] at hpow; omega + exact (Nat.clog_pow 2 C one_lt_two).symm.le.trans (Nat.clog_mono_right 2 hpow') + obtain ⟨hb₀_pow, hw_pow⟩ := + parityBranchThreshold_succ_and_width_le_clog_pow (by omega) hn_le (by omega) hm hB hW_bound + hcoeff + exact ⟨hsqrt, ⟨hn_le, hn_div⟩, hbranches, + fun b => hsched hk hW e hn₀_le hb₀_pow hw_pow b⟩ + +/-- **Unconditional Section 6.3 greedy parity-test schedule theorem for +`B ≤ C_B * (Nat.clog 2 n) ^ β_B`**: +For any `k C_B β_B : ℕ`, there exists `N₀ ≥ 20` such that for any width-`≤ k` CNF `φ : CNF V` with +`N₀ ≤ Fintype.card V`, `1 ≤ B ≤ C_B * (Nat.clog 2 (Fintype.card V)) ^ β_B`, and +`φ.length ≤ B * Fintype.card V`, there exist initial parity rows `A₀` (`A₀.length ≤ 3 * n`, each of +weight at most `D₀ = CNF.initialRowWeightBound B φ.length`) such that for any `3 * n`-step greedy +sequence `A (i + 1) = A i ++ [testRow (γ i)]` with `A 0 = A₀` minimizing `commonZeroCount`: +1. `commonZeroCount univ (φ.parityRowSystem (A (3 * Fintype.card V))) = φ.modelCountOn Finset.univ`. +2. At every stage `i ≤ 3 * Fintype.card V`, `(A i).length ≤ 6 * Fintype.card V`, and at the + canonical threshold `b₀ = CNF.parityBranchThreshold k D₀ (Fintype.card V)`: + - `Fintype.card V ≤ 2 * n'` and `Fintype.card V / 2 ≤ n'`, + - `#(branchAssignments S_i) ≤ 2 ^ (Fintype.card V / (Nat.clog 2 (Fintype.card V)).sqrt)`, and + - **every** branch `b : V → Bool` unconditionally satisfies + `Feasible (2 * β_B + 5) n' (b₀ + 1) (max (k * (D₀ * 2 ^ k)) 1) M'_b` and admits a concrete + `ReplacementDesign`. -/ +theorem CNF.exists_greedy_parityRowSystem_branch_schedule_of_le_clog_pow (k C_B β_B : ℕ) : + ∃ N₀ : ℕ, 20 ≤ N₀ ∧ + ∀ {V : Type*} [Fintype V] [DecidableEq V] + (φ : CNF V) {B : ℕ}, + (∀ c ∈ φ, c.length ≤ k) → + N₀ ≤ Fintype.card V → + 1 ≤ B → + B ≤ C_B * Nat.clog 2 (Fintype.card V) ^ β_B → + φ.length ≤ B * Fintype.card V → + let D₀ := CNF.initialRowWeightBound B φ.length + let P := CNF.greedyRowListLength B φ.length + let b₀ := CNF.parityBranchThreshold k D₀ (Fintype.card V) + ∃ A₀ : List (Fin φ.length → Bool), A₀.length ≤ 3 * Fintype.card V ∧ + (∀ r ∈ A₀, weight r ≤ D₀) ∧ + ∀ (A : ℕ → List (Fin φ.length → Bool)) + (γ : ℕ → Fin P → Fin φ.length × Bool), + A 0 = A₀ → (∀ i, A (i + 1) = A i ++ [testRow (γ i)]) → + (∀ i (γ' : Fin P → Fin φ.length × Bool), + commonZeroCount univ (φ.parityRowSystem (A i ++ [testRow (γ i)])) ≤ + commonZeroCount univ (φ.parityRowSystem (A i ++ [testRow γ']))) → + commonZeroCount univ (φ.parityRowSystem (A (3 * Fintype.card V))) = + φ.modelCountOn Finset.univ ∧ + ∀ i ≤ 3 * Fintype.card V, + (A i).length ≤ 6 * Fintype.card V ∧ + ∀ ⦃n' : ℕ⦄ + (e : ↥(univ \ heavyVars univ (univ : Finset (Fin (A i).length)) + (fun j => equationScope (φ.parityRowSystem (A i) j)) b₀) ≃ Fin n'), + (Fintype.card V ≤ 2 * n' ∧ Fintype.card V / 2 ≤ n') ∧ + #(branchAssignments + (heavyVars univ (univ : Finset (Fin (A i).length)) + (fun j => equationScope (φ.parityRowSystem (A i) j)) b₀)) ≤ + 2 ^ (Fintype.card V / (Nat.clog 2 (Fintype.card V)).sqrt) ∧ + ∀ b : V → Bool, + Feasible (2 * β_B + 5) n' (b₀ + 1) (max (k * (D₀ * 2 ^ k)) 1) + (∑ f : {j : Fin (A i).length // + j ∈ (univ : Finset (Fin (A i).length))} ⊕ Fin n', + #(branchAugmentedScope + (heavyVars univ (univ : Finset (Fin (A i).length)) + (fun j => equationScope (φ.parityRowSystem (A i) j)) b₀) + e univ (φ.parityRowSystem (A i)) b f)) ∧ + Nonempty + (ReplacementDesign + (branchAugmentedScope + (heavyVars univ (univ : Finset (Fin (A i).length)) + (fun j => equationScope (φ.parityRowSystem (A i) j)) b₀) + e univ (φ.parityRowSystem (A i)) b) + (numNodes n') (numGroups n') (intervalStart (2 * β_B + 5) n') + (rowBound (2 * β_B + 5) n') (tauRat n')) := by + obtain ⟨n₀, hn₀20, hsched⟩ := + exists_branch_schedule_replacementDesign (β := 2 * β_B + 5) (by omega) + set C := CNF.parityScheduleCoeff k C_B β_B + refine ⟨max (2 * n₀) (2 ^ (C + 1)), by omega, + fun {V _ _} φ {B} hk hN₀ hB1 hB_le hm => ?_⟩ + intro D₀ P b₀ + obtain ⟨A₀, hlen0, hw0, hgreedy⟩ := + φ.exists_initialTests_commonZeroCount_greedy hk (by omega) hB1 hm + refine ⟨A₀, hlen0, hw0, fun A γ h0 hA hγ => ?_⟩ + obtain ⟨hcount, hstage⟩ := hgreedy A γ h0 hA hγ + refine ⟨hcount, fun i hi => ?_⟩ + obtain ⟨hlen_i, hscope_i⟩ := hstage i hi + refine ⟨hlen_i, fun {n'} e => ?_⟩ + have hscope_D₀ : ∀ j : Fin (A i).length, + #(equationScope (φ.parityRowSystem (A i) j)) ≤ k * (D₀ * 2 ^ k) := fun j => by + simpa only [D₀, P, max_initialRowWeightBound_greedyRowListLength] using (hscope_i j).2.2 + have hn20 : 20 ≤ Fintype.card V := by + have : 2 * n₀ ≤ Fintype.card V := (le_max_left _ _).trans hN₀ + omega + have hsqrt : 2 ≤ (Nat.clog 2 (Fintype.card V)).sqrt := by + refine Nat.le_sqrt.mpr ?_ + have h4 : 2 ^ 3 < Fintype.card V := by omega + have hclog : 3 < Nat.clog 2 (Fintype.card V) := (Nat.lt_clog_iff_pow_lt one_lt_two).mpr h4 + omega + have hM_le : + totalIncidence (univ : Finset (Fin (A i).length)) + (fun j => equationScope (φ.parityRowSystem (A i) j)) * + (Nat.clog 2 (Fintype.card V)).sqrt ≤ + b₀ * Fintype.card V := by + have htot : + totalIncidence (univ : Finset (Fin (A i).length)) + (fun j => equationScope (φ.parityRowSystem (A i) j)) ≤ + (6 * (k * (D₀ * 2 ^ k))) * Fintype.card V := by + calc totalIncidence (univ : Finset (Fin (A i).length)) + (fun j => equationScope (φ.parityRowSystem (A i) j)) + ≤ ∑ _j ∈ (univ : Finset (Fin (A i).length)), (k * (D₀ * 2 ^ k)) := + sum_le_sum fun j _ => hscope_D₀ j + _ = (A i).length * (k * (D₀ * 2 ^ k)) := by + simp only [sum_const, card_univ, Fintype.card_fin, smul_eq_mul] + _ ≤ (6 * Fintype.card V) * (k * (D₀ * 2 ^ k)) := Nat.mul_le_mul_right _ hlen_i + _ = (6 * (k * (D₀ * 2 ^ k))) * Fintype.card V := by ring + calc totalIncidence (univ : Finset (Fin (A i).length)) + (fun j => equationScope (φ.parityRowSystem (A i) j)) * + (Nat.clog 2 (Fintype.card V)).sqrt + ≤ ((6 * (k * (D₀ * 2 ^ k))) * Fintype.card V) * + (Nat.clog 2 (Fintype.card V)).sqrt := Nat.mul_le_mul_right _ htot + _ = ((6 * (k * (D₀ * 2 ^ k))) * (Nat.clog 2 (Fintype.card V)).sqrt) * + Fintype.card V := by ring + _ ≤ b₀ * Fintype.card V := Nat.mul_le_mul_right _ (Nat.le_succ _) + have hb₀_pos : 1 ≤ b₀ := Nat.succ_pos _ + have hcard : Fintype.card V ≤ 2 * n' ∧ Fintype.card V / 2 ≤ n' := + card_le_two_mul_freeVar_of_heavyVars univ + (fun j => equationScope (φ.parityRowSystem (A i) j)) hb₀_pos hsqrt hM_le e + have hbr : + #(branchAssignments + (heavyVars univ (univ : Finset (Fin (A i).length)) + (fun j => equationScope (φ.parityRowSystem (A i) j)) b₀)) ≤ + 2 ^ (Fintype.card V / (Nat.clog 2 (Fintype.card V)).sqrt) := + card_branchAssignments_heavyVars_le_two_pow_div univ + (fun j => equationScope (φ.parityRowSystem (A i) j)) hb₀_pos (by omega) hM_le + have hn₀_le : n₀ ≤ n' := by + have : 2 * n₀ ≤ Fintype.card V := (le_max_left _ _).trans hN₀ + omega + have hcoeff : C ≤ Nat.clog 2 n' := by + have hpow : 2 ^ (C + 1) ≤ 2 * n' := ((le_max_right _ _).trans hN₀).trans hcard.1 + have hpow' : 2 ^ C ≤ n' := by rw [pow_succ'] at hpow; omega + exact (Nat.clog_pow 2 C one_lt_two).symm.le.trans (Nat.clog_mono_right 2 hpow') + obtain ⟨hb₀_pow, hw_pow⟩ := + parityBranchThreshold_succ_and_width_le_clog_pow (by omega) hcard.1 (by omega) hm hB_le + le_rfl hcoeff + refine ⟨hcard, hbr, fun b => ?_⟩ + exact hsched univ univ (φ.parityRowSystem (A i)) (Subset.refl _) e hn₀_le hb₀_pow hw_pow + (fun j _ => hscope_D₀ j) b + +end Cslib.Logic.PL diff --git a/ComplexityTests/Computability/Complexity/FineGrained/SparseParitySchedule.lean b/ComplexityTests/Computability/Complexity/FineGrained/SparseParitySchedule.lean new file mode 100644 index 0000000000..382d82aa4c --- /dev/null +++ b/ComplexityTests/Computability/Complexity/FineGrained/SparseParitySchedule.lean @@ -0,0 +1,279 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Complexity.Computability.Complexity.FineGrained.SparseParitySchedule +import Mathlib.Tactic.NormNum +import Mathlib.Tactic.Positivity + +/-! +# Tests for `Complexity.Computability.Complexity.FineGrained.SparseParitySchedule` + +Concrete expected-value checks on a 2-variable 2-CNF formula `testCNF` (`(x₀ ∨ ¬x₁) ∧ (x₁)`) for +`CNF.totalIncidence_parityRowSystem_le_six_mul`, `CNF.card_le_two_mul_freeVar_parityRowSystem`, +`CNF.card_branchAugmentedScope_parityRowSystem_le`, and +`CNF.sum_branch_domainExaminationBound_parityRowSystem_le`, together with a nonvacuous witness at +`n = 2 ^ (2 ^ 50)` instantiating `CNF.exists_branch_replacementDesign_parityRowSystem` and +`CNF.branch_domainExaminationBound_parityRowSystem_le`. +-/ + +@[expose] public noncomputable section + +namespace Cslib.Complexity.SubexponentialSAT.SparseParityScheduleTests + +open Finset +open scoped BigOperators +open Cslib.BitString Cslib.LinearAlgebra Cslib.ReplacementCounting Cslib.Logic.PL +open Cslib.Complexity.SubexponentialSAT.ReplacementParams + +/-- Concrete 2-variable 2-CNF formula `(x₀ ∨ ¬x₁) ∧ (x₁)` on `Fin 2`. -/ +def testCNF : CNF (Fin 2) := + [[(0, true), (1, false)], [(1, true)]] + +/-- Concrete two-row unit parity check matrix selecting clause `0` and clause `1` of `testCNF`. -/ +def testRows : List (Fin testCNF.length → Bool) := + [Pi.single ⟨0, by decide⟩ true, Pi.single ⟨1, by decide⟩ true] + +/-- Concrete check of `CNF.totalIncidence_parityRowSystem_le_six_mul` on `testCNF` and `testRows` +(`k = 2`, `W = 1`, `Fintype.card (Fin 2) = 2`): +`totalIncidence ≤ (6 * (2 * (1 * 2 ^ 2))) * 2 = 96`. -/ +example : + totalIncidence (univ : Finset (Fin testRows.length)) + (fun j => equationScope (testCNF.parityRowSystem testRows j)) ≤ + (6 * (2 * (1 * 2 ^ 2))) * Fintype.card (Fin 2) := + testCNF.totalIncidence_parityRowSystem_le_six_mul (k := 2) (W := 1) + (by decide) (by decide) (by decide) + +/-- At threshold `b₀ = 96` (`s_div = 2`), `heavyVars` on `testCNF.parityRowSystem testRows` is +empty, so `n' = 2`. -/ +def testEquiv96 : + ↥((univ : Finset (Fin 2)) \ + heavyVars univ (univ : Finset (Fin testRows.length)) + (fun j => equationScope (testCNF.parityRowSystem testRows j)) 96) ≃ Fin 2 where + toFun v := v.1 + invFun v := ⟨v, by decide +revert⟩ + left_inv v := by ext; rfl + right_inv v := rfl + +/-- Concrete check of `CNF.card_le_two_mul_freeVar_parityRowSystem`, +`CNF.card_branchAugmentedScope_parityRowSystem_le`, and +`CNF.sum_branch_domainExaminationBound_parityRowSystem_le` on `testCNF` and `testRows`. -/ +example (b : Fin 2 → Bool) + (f : {j : Fin testRows.length // j ∈ (univ : Finset (Fin testRows.length))} ⊕ Fin 2) : + let S := heavyVars univ (univ : Finset (Fin testRows.length)) + (fun j => equationScope (testCNF.parityRowSystem testRows j)) 96 + (Fintype.card (Fin 2) ≤ 2 * 2 ∧ Fintype.card (Fin 2) / 2 ≤ 2) ∧ + #(branchAssignments S) ≤ 2 ^ (Fintype.card (Fin 2) / 2) ∧ + #(branchAugmentedScope S testEquiv96 univ (testCNF.parityRowSystem testRows) b f) ≤ + max (2 * (1 * 2 ^ 2)) 1 ∧ + (∑ _b ∈ branchAssignments S, (0 : ℝ)) ≤ + (2 : ℝ) ^ (Fintype.card (Fin 2) / 2) * + (2 * (numNodes 2 : ℝ) * (numGroups 2 : ℝ) * + Real.exp (((16 * 2 + 4 : ℕ) : ℝ) * (2 : ℝ) * rowCost 1 2)) := by + intro S + have hfree := testCNF.card_le_two_mul_freeVar_parityRowSystem (k := 2) (W := 1) (b₀ := 96) + (s_div := 2) (by decide) (by decide) (by decide) (by decide) (by decide) (by decide) + testEquiv96 + have hscope := testCNF.card_branchAugmentedScope_parityRowSystem_le (k := 2) (W := 1) + (by decide) (by decide) S testEquiv96 b f + have hsum := testCNF.sum_branch_domainExaminationBound_parityRowSystem_le + (β := 1) (k := 2) (W := 1) (b₀ := 96) (s_div := 2) + (by decide) (by decide) (by decide) (by decide) (by decide) (by decide) + testEquiv96 (fun _ => 0) (fun _ _ => by positivity) + exact ⟨hfree.1, hfree.2, hscope, hsum.2⟩ + +/-- Nonvacuous concrete witness at `n = 2 ^ (2 ^ 50)` for an empty 3-CNF `emptyCNF n : CNF (Fin n)` +and empty parity-row list `[]`, instantiating `CNF.exists_branch_replacementDesign_parityRowSystem` +and `CNF.branch_domainExaminationBound_parityRowSystem_le`. -/ +def emptyCNF (n : ℕ) : CNF (Fin n) := + [] + +theorem heavyVars_emptyCNF_eq_empty (n b₀ : ℕ) : + heavyVars (univ : Finset (Fin n)) (univ : Finset (Fin ([] : List (Fin 0 → Bool)).length)) + (fun j => equationScope ((emptyCNF n).parityRowSystem [] j)) b₀ = ∅ := by + ext v + simp [heavyVars, varIncidence] + +def emptyCNFEquiv (n b₀ : ℕ) : + ↥((univ : Finset (Fin n)) \ + heavyVars (univ : Finset (Fin n)) (univ : Finset (Fin ([] : List (Fin 0 → Bool)).length)) + (fun j => equationScope ((emptyCNF n).parityRowSystem [] j)) b₀) ≃ Fin n where + toFun v := v.1 + invFun v := ⟨v, by rw [heavyVars_emptyCNF_eq_empty]; simp⟩ + left_inv v := by ext; rfl + right_inv v := rfl + +example : + ∃ m n : ℕ, + m = 2 ^ 50 ∧ + n = 2 ^ m ∧ + rowCost 1 n < 1 / 3 ∧ + ∀ b : Fin n → Bool, + ∃ (hF : Feasible 1 n (1 + 1) (max (3 * (0 * 2 ^ 3)) 1) + (∑ f : {j : Fin ([] : List (Fin (emptyCNF n).length → Bool)).length // + j ∈ (univ : Finset (Fin ([] : List (Fin (emptyCNF n).length → Bool)).length))} ⊕ + Fin n, + #(branchAugmentedScope + (heavyVars (univ : Finset (Fin n)) + (univ : Finset (Fin ([] : List (Fin (emptyCNF n).length → Bool)).length)) + (fun j => equationScope ((emptyCNF n).parityRowSystem [] j)) 1) + (emptyCNFEquiv n 1) univ ((emptyCNF n).parityRowSystem []) b f))) + (D : ReplacementDesign + (branchAugmentedScope + (heavyVars (univ : Finset (Fin n)) + (univ : Finset (Fin ([] : List (Fin (emptyCNF n).length → Bool)).length)) + (fun j => equationScope ((emptyCNF n).parityRowSystem [] j)) 1) + (emptyCNFEquiv n 1) univ ((emptyCNF n).parityRowSystem []) b) + (numNodes n) (numGroups n) (intervalStart 1 n) (rowBound 1 n) (tauRat n)), + let d := 3 * (([] : List (Fin (emptyCNF n).length → Bool)).length + + 2 * (branchPrecision (Fintype.card (Fin n)) - 1)) + let q := (d * rowBound 1 n) / numNodes n + let C := hF.toCountingDesign D univ + (∑ t : Fin (numNodes n), + (#{p : Finset (Fin n) × Finset (Fin n) | #p.1 ≤ q ∧ p.2 ⊆ p.1} : ℝ) * + (2 * (#(C.measurementBox t) : ℝ) * + ∑ g : Fin (numGroups n), (Fintype.card (C.LocalCube t g) : ℝ))) ≤ + 2 * (numNodes n : ℝ) * (numGroups n : ℝ) * + Real.exp (((16 * 3 + 4 : ℕ) : ℝ) * (n : ℝ) * rowCost 1 n) := by + obtain ⟨m, hm⟩ : ∃ m : ℕ, m = 2 ^ 50 := ⟨2 ^ 50, rfl⟩ + obtain ⟨n, hn⟩ : ∃ n : ℕ, n = 2 ^ m := ⟨2 ^ m, rfl⟩ + have hcl_n : Nat.clog 2 n = 2 ^ 50 := by + rw [hn, Nat.clog_pow 2 m one_lt_two, hm] + have hcl_cl : Nat.clog 2 (Nat.clog 2 n) = 50 := by + rw [hcl_n] + exact Nat.clog_pow 2 50 one_lt_two + have hr_eq : numGroups n = 13421 := by + unfold numGroups + rw [hcl_cl, hcl_n] + have hsq : (2 ^ 50 : ℕ).sqrt = 2 ^ 25 := + le_antisymm (Nat.lt_succ_iff.mp (Nat.sqrt_lt.mpr (by norm_num))) + (Nat.le_sqrt.mpr (by norm_num)) + rw [hsq] + norm_num + have hQ_eq : numNodes n = 175921860444 := by + unfold numNodes + rw [hcl_cl, hcl_n] + norm_num + have hR_eq : rowBound 1 n = 134217728 := by + unfold rowBound + rw [hcl_n] + have hsq : (2 ^ 50 - 1 : ℕ).sqrt = 2 ^ 25 - 1 := + le_antisymm (Nat.lt_succ_iff.mp (Nat.sqrt_lt.mpr (by norm_num))) + (Nat.le_sqrt.mpr (by norm_num)) + simp only [show (2 ^ 50 : ℕ) ≠ 0 by positivity, ↓reduceIte, hsq] + norm_num + have hr : 2 ≤ numGroups n := by rw [hr_eq]; norm_num + have hR : 32 ≤ rowBound 1 n := by rw [hR_eq]; norm_num + have hRQ : 6 * rowBound 1 n ≤ numNodes n := by rw [hR_eq, hQ_eq]; norm_num + have hsub := interval_subset_range (β := 1) hr (by omega) (by omega) + have hrc := rowCost_le hr hR hRQ + have hrc_lt : rowCost 1 n < 1 / 3 := hrc.trans_lt <| by + rw [hcl_cl, hcl_n, show ((2 ^ 50 : ℕ) : ℝ) = ((2 : ℝ) ^ 25) ^ 2 by norm_num, + Real.sqrt_sq (by positivity)] + norm_num + refine ⟨m, n, hm, hn, hrc_lt, fun b => ?_⟩ + let S := heavyVars (univ : Finset (Fin n)) + (univ : Finset (Fin ([] : List (Fin (emptyCNF n).length → Bool)).length)) + (fun j => equationScope ((emptyCNF n).parityRowSystem [] j)) 1 + have hsum_eq : + (∑ f : {j : Fin ([] : List (Fin (emptyCNF n).length → Bool)).length // + j ∈ (univ : Finset (Fin ([] : List (Fin (emptyCNF n).length → Bool)).length))} ⊕ Fin n, + #(branchAugmentedScope S (emptyCNFEquiv n 1) univ + ((emptyCNF n).parityRowSystem []) b f)) = n := by + rw [sum_card_branchAugmentedScope] + simp [totalIncidence] + have hF : Feasible 1 n (1 + 1) (max (3 * (0 * 2 ^ 3)) 1) + (∑ f : {j : Fin ([] : List (Fin (emptyCNF n).length → Bool)).length // + j ∈ (univ : Finset (Fin ([] : List (Fin (emptyCNF n).length → Bool)).length))} ⊕ Fin n, + #(branchAugmentedScope S (emptyCNFEquiv n 1) univ + ((emptyCNF n).parityRowSystem []) b f)) := by + rw [hsum_eq] + exact { + twenty_le := by + rw [hn] + exact (by norm_num : 20 ≤ 2 ^ 5).trans + (Nat.pow_le_pow_right two_pos (by rw [hm]; norm_num)) + two_le_numGroups := hr + thirtyTwo_le_rowBound := hR + six_mul_rowBound_le_numNodes := hRQ + tau_pos := by unfold tau; rw [hr_eq, hcl_n]; positivity + tau_le_half := by unfold tau; rw [hr_eq, hcl_n]; norm_num + auxDim_le := auxDim_le_two_pow n + aux_balance := + (Nat.mul_le_mul (by norm_num : 2 * (1 + 1 + max (3 * (0 * 2 ^ 3)) 1) ≤ 2 ^ 3) + (Nat.pow_le_pow_left (auxDim_le_two_pow n) 2)).trans <| by + rw [← pow_mul, ← pow_add, hn] + exact Nat.pow_le_pow_right two_pos + (by rw [Nat.clog_pow 2 m one_lt_two, hm]; norm_num) + var_balance := by + rw [hQ_eq, hr_eq, hn] + exact (by norm_num : 2 * 175921860444 * 13421 ^ 2 ≤ 2 ^ 66).trans + (Nat.pow_le_pow_right two_pos (by rw [hm]; norm_num)) + card_interval_eq := card_interval (by omega) + interval_subset := hsub + mem_interval_of_close := fun k hk => + ⟨mem_interval_of_abs_sub_le hr hR (by omega) hk, + Nat.lt_succ_iff.mp + (mem_range.mp (hsub (mem_interval_of_abs_sub_le hr hR (by omega) hk)))⟩ + binomTail_lt := + binomTail_lt_tailBudget le_rfl hr hR hRQ (by norm_num) (by rw [hcl_n]; norm_num) + rowCost_bound := hrc + } + have hk : ∀ c ∈ emptyCNF n, c.length ≤ 3 := by simp [emptyCNF] + have hW : ∀ r ∈ ([] : List (Fin (emptyCNF n).length → Bool)), weight r ≤ 0 := by simp + obtain ⟨D⟩ := (emptyCNF n).exists_branch_replacementDesign_parityRowSystem hk hW + (emptyCNFEquiv n 1) b hF + refine ⟨hF, D, ?_⟩ + have hlen : ([] : List (Fin (emptyCNF n).length → Bool)).length ≤ 6 * Fintype.card (Fin n) := by + simp + have hn_card : Fintype.card (Fin n) ≤ 2 * n := by + rw [Fintype.card_fin] + omega + exact ((emptyCNF n).branch_domainExaminationBound_parityRowSystem_le (k := 3) + ([] : List (Fin (emptyCNF n).length → Bool)) hF D univ hlen hn_card).1 + +-- 6. Concrete check of `CNF.initialRowWeightBound_le_mul_pow_clog` on `testCNF` (`n = 2`, `m = 2`, +-- `B = 1`, `C_B = 1`, `β_B = 0`). +example : + CNF.initialRowWeightBound 1 testCNF.length ≤ + CNF.initialRowWeightCoeff 1 0 * Nat.clog 2 (Fintype.card (Fin 2)) ^ (2 * 0 + 3) := + CNF.initialRowWeightBound_le_mul_pow_clog (by decide) (by decide) (by decide) + +-- 7. Unconditional schedule discharge `CNF.exists_branch_schedule_parityRowSystem_of_le_clog_pow` +-- at `k = 3`, `C_B = 1`, `β_B = 1`. +example : + ∃ N₀ : ℕ, 20 ≤ N₀ ∧ + ∀ n ≥ N₀, + let φ := emptyCNF n + let W := CNF.initialRowWeightBound 1 φ.length + let b₀ := CNF.parityBranchThreshold 3 W (Fintype.card (Fin n)) + ∀ b : Fin n → Bool, + Nonempty + (ReplacementDesign + (branchAugmentedScope + (heavyVars univ (univ : Finset (Fin ([] : List (Fin φ.length → Bool)).length)) + (fun j => equationScope (φ.parityRowSystem [] j)) b₀) + (emptyCNFEquiv n b₀) univ (φ.parityRowSystem []) b) + (numNodes n) (numGroups n) (intervalStart (2 * 1 + 5) n) + (rowBound (2 * 1 + 5) n) (tauRat n)) := by + obtain ⟨N₀, hN₀20, hsched⟩ := CNF.exists_branch_schedule_parityRowSystem_of_le_clog_pow 3 1 1 + refine ⟨N₀, hN₀20, fun n hn => ?_⟩ + intro φ W b₀ b + have hk : ∀ c ∈ φ, c.length ≤ 3 := by simp [φ, emptyCNF] + have hcard : N₀ ≤ Fintype.card (Fin n) := by simpa only [Fintype.card_fin] using hn + have hm : φ.length ≤ 1 * Fintype.card (Fin n) := by simp [φ, emptyCNF] + have hB : 1 ≤ 1 * Nat.clog 2 (Fintype.card (Fin n)) ^ 1 := by + rw [Fintype.card_fin, one_mul, pow_one] + have : 2 ^ 0 < n := by omega + exact (Nat.lt_clog_iff_pow_lt one_lt_two).mpr this + have hW : ∀ r ∈ ([] : List (Fin φ.length → Bool)), weight r ≤ W := by simp + have hlen : ([] : List (Fin φ.length → Bool)).length ≤ 6 * Fintype.card (Fin n) := by simp + exact (hsched hk hcard hm hB le_rfl hW hlen (emptyCNFEquiv n b₀)).2.2.2 b |>.2 + +end Cslib.Complexity.SubexponentialSAT.SparseParityScheduleTests From 7a46ddd903064e0036689aae836735a355bc8cd0 Mon Sep 17 00:00:00 2001 From: Samuel Schlesinger Date: Tue, 27 Oct 2026 05:48:25 +0000 Subject: [PATCH 5/7] feat(Logics/Propositional/CNF): bridge sparse replacement design with CNF parity counter (#1226) (#1302) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(Foundations/Data/Polynomial): integer parity amplifier and shared-order truncation (#1226) * doc(references): pin the subexponential SAT manuscript * refactor(Polynomial): reuse Mathlib helpers and cite OpenAI2026ETH in ParityAmplifier (#1235) * feat(BooleanAnalysis): sparse F2 equation zero counting, cleared denominators, and heavy-variable branching (#1226) * feat(BooleanAnalysis): univariate parity-product specialization and heavy-variable branch reduction (#1226) * feat(BooleanAnalysis): add §5.6 shared-precision branch recovery and linear degree bound (#1253) * refactor(Polynomial,BooleanAnalysis): extract shared CoeffNorm1 and address review on #1248 * refactor(Polynomial,BooleanAnalysis): extract ParityAmplifierDegree and refine §5.5 thresholds (#1226) * feat(Foundations/BooleanAnalysis): add cleared-denominator specialization and exact division recovery * docs,test(BooleanAnalysis,Polynomial): qualify clearing premises and drop signature mirror (#1273) * feat(Foundations/BooleanAnalysis): instantiate cleared parity specialization with Section 4 replacement designs (#1226) * test(BooleanAnalysis): add concrete expected-value and checked branch-recovery clients for SparseReplacementDesign * fix(BooleanAnalysis/SparseReplacementDesign): address #1294 review items - Replace duplicate augmentedEquationScope with SparseBranching.withNeutralScope and withNeutralFactors - Add scoped locality theorems eval_nodePoint_designReplacementMap_eq_of_eq_on_varGroup and eval_univariateMonomialSumPoly_designReplacementMap_nodePoint_eq - Add nonconstSampleDesign test exercising nonconstant degree-1 replacement rows and factor-level locality - Document eq. (5.5) degree strengthening, exists_replacementDesign_withNeutralScope numerical hypotheses, and emod_sum_cramer_ediv_design_eq_commonZeroCount hcramer premise * doc(BooleanAnalysis): correct replacement degree provenance * feat(Logics/Propositional/CNF): bridge sparse replacement design with CNF parity counter (#1226) --- Complexity.lean | 1 + .../CNF/SparseDesignCounter.lean | 602 ++++++++++++++++++ ComplexityTests/SparseDesignCounter.lean | 474 ++++++++++++++ 3 files changed, 1077 insertions(+) create mode 100644 Complexity/Logics/Propositional/CNF/SparseDesignCounter.lean create mode 100644 ComplexityTests/SparseDesignCounter.lean diff --git a/Complexity.lean b/Complexity.lean index c1042045d7..fcc52bbcea 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2208,6 +2208,7 @@ public import Complexity.Logics.Propositional.CNF.Planar.PairedCrossover public import Complexity.Logics.Propositional.CNF.Planar.PairedWire public import Complexity.Logics.Propositional.CNF.PolyTime public import Complexity.Logics.Propositional.CNF.PolynomialTests +public import Complexity.Logics.Propositional.CNF.SparseDesignCounter public import Complexity.Logics.Propositional.CNF.SparseParityCounter public import Complexity.Logics.Propositional.CNF.ThreeCNF public import Complexity.Logics.Propositional.CNF.ThreeCNF.Exact diff --git a/Complexity/Logics/Propositional/CNF/SparseDesignCounter.lean b/Complexity/Logics/Propositional/CNF/SparseDesignCounter.lean new file mode 100644 index 0000000000..c84c6d6c7e --- /dev/null +++ b/Complexity/Logics/Propositional/CNF/SparseDesignCounter.lean @@ -0,0 +1,602 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Complexity.Foundations.BooleanAnalysis.SparseReplacementDesign +public import Complexity.Logics.Propositional.CNF.SparseParityCounter + +/-! +# Replacement-Design Specialization of Sparse Parity Model Counting for CNF Formulas + +This module connects the compiled CNF parity-test equation systems and greedy zero-counting +reduction of Sections 6.1–6.3 (`Complexity.Logics.Propositional.CNF.SparseParityCounter`) directly +with the Section 4 algebraic replacement design specialization of Sections 5.3–5.5 and Appendix B.2 +(`Complexity.Foundations.BooleanAnalysis.SparseReplacementDesign`) of +[OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH] (printed pp. 37–43, 48–50): + +1. **Branch-restricted CNF parity systems and neutral singleton scopes (§5.5 & §6.2–6.3)**: + Given `φ : CNF V`, parity-check rows `rows : List (Fin φ.length → Bool)`, a heavy-variable subset + `S : Finset V`, a free-variable equivalence `e : ↥(univ \ S) ≃ Fin n'`, and a branch assignment + `b : V → Bool`: + - `CNF.branchParityRowSystem φ rows S e b : Fin rows.length → Finset (Finset (Fin n'))` restricts + each compiled parity equation `φ.parityRowSystem rows j` to `S` under `b` (cancelling duplicate + monomials modulo `2`) and relabels the remaining free variables along `e`. + - `CNF.branchParityRowScope φ rows S e b` is its variable-scope family on `Fin n'`, and + `CNF.branchParityRowNeutralScope φ rows S e b : Fin rows.length ⊕ Fin n' → Finset (Fin n')` + augments those scopes with one neutral singleton scope `{v}` per free variable `v : Fin n'` via + `withNeutralScope`. + - When every clause of `φ` has width at most `k` and every row in `rows` has Hamming weight at + most `W`, each branch equation has degree at most `k`, at most `W * 2 ^ k` monomials, and + scope size at most `k * (W * 2 ^ k)`. On the canonical heavy-variable set + `S = heavyVars univ univ (fun j => equationScope (φ.parityRowSystem rows j)) b₀`, every free + variable `v : Fin n'` belongs to at most `b₀ + 1` scopes of `branchParityRowNeutralScope`, + total branch incidence satisfies `n' ≤ M'_b ≤ (b₀ + 1) * n'`, and Lemma 4.7 supplies a + concrete `ReplacementDesign` (`CNF.exists_replacementDesign_branchParityRowNeutralScope`). + +2. **Per-branch factor locality, polynomial degree, and coordinate `coeffNorm1` bounds (§5.3–5.4 & + Appendix B.2)**: + For any `D : ReplacementDesign (φ.branchParityRowNeutralScope rows S e b) Q r A₀ R τ`, + instantiating `SparseReplacementDesign` with `emb = Sum.inl` automatically discharges the + monomial-degree (`#M ≤ k`), monomial-count (`#(P j) ≤ W * 2 ^ k`), and scope-containment + (`equationScope (P j) ⊆ scope (Sum.inl j)`) premises from `∀ c ∈ φ, c.length ≤ k` and + `∀ r ∈ rows, weight r ≤ W`, yielding: + - node-point factor locality + (`CNF.eval_univariateMonomialSumPoly_branchParityRowSystem_nodePoint_eq`), + - the equation (5.5) degree bound `(k * (R - 1)) * (rows.length + 2 * (ℓ - 1))` + (`CNF.natDegree_univariateClearedParityCoord_branchParityRowSystem_le`, + `CNF.natDegree_univariateClearedParityTruncatedSum_branchParityRowSystem_le`), and + - the Appendix B.2 coordinate `ℓ₁`-norm bound with `S_mon = W * 2 ^ k` + (`CNF.coeffNorm1_univariateClearedParityCoord_branchParityRowSystem_le`). + +3. **Exact branch-summed `ReplacementDesign` recovery of `commonZeroCount`, `numPassing`, and + `φ.modelCountOn Finset.univ` (§5.4–5.5 & §6.2–6.3)**: + Eliminating the abstract replacement-map hypotheses `d_den`, `θ`, and `hθ` of + `SparseParityCounter`, any per-branch family of replacement designs `D b` recovers + `commonZeroCount univ (φ.parityRowSystem rows) = numPassing φ.violations rows` via + branch-summed evaluation at `T = 0` + (`CNF.commonZeroCount_parityRowSystem_eq_sum_branch_emod_ediv_design`, + `CNF.numPassing_eq_sum_branch_emod_ediv_design`), and recovers `φ.modelCountOn Finset.univ` on + the nose after `3 * Fintype.card V` greedy rounds + (`CNF.modelCountOn_eq_sum_branch_emod_ediv_design_greedy`, + `CNF.exists_initialTests_modelCountOn_eq_sum_branch_emod_ediv_design_greedy`), with + `max D₀ P` simplified unconditionally to `D₀ = CNF.initialRowWeightBound B φ.length` + (`CNF.max_initialRowWeightBound_greedyRowListLength`). + +## Main definitions + +* `Cslib.Logic.PL.CNF.branchParityRowSystem`: branch-restricted and relabeled `𝔽₂` polynomial system + `fun j => relabelRestrictedMonomials S e b (φ.parityRowSystem rows j)`. +* `Cslib.Logic.PL.CNF.branchParityRowScope`: variable-scope family of `branchParityRowSystem`. +* `Cslib.Logic.PL.CNF.branchParityRowNeutralScope`: neutral-singleton augmented scope family + `withNeutralScope (φ.branchParityRowScope rows S e b)`. + +## Main results + +* `Cslib.Logic.PL.CNF.card_mem_branchParityRowSystem_le`, + `Cslib.Logic.PL.CNF.card_branchParityRowSystem_le`, + `Cslib.Logic.PL.CNF.card_branchParityRowScope_le`, + `Cslib.Logic.PL.CNF.card_branchParityRowNeutralScope_le`: per-branch monomial degree, sparsity, + and scope-width bounds from clause width `≤ k` and row weight `≤ W`. +* `Cslib.Logic.PL.CNF.varIncidence_branchParityRowNeutralScope_heavyVars_le`, + `Cslib.Logic.PL.CNF.totalIncidence_branchParityRowNeutralScope_heavyVars_le`, + `Cslib.Logic.PL.CNF.exists_replacementDesign_branchParityRowNeutralScope`: post-branching + free-variable incidence bounds and Lemma 4.7 `ReplacementDesign` existence on each branch. +* `Cslib.Logic.PL.CNF.eval_univariateMonomialSumPoly_branchParityRowSystem_nodePoint_eq`, + `Cslib.Logic.PL.CNF.natDegree_univariateClearedParityCoord_branchParityRowSystem_le`, + `Cslib.Logic.PL.CNF.natDegree_univariateClearedParityTruncatedSum_branchParityRowSystem_le`, + `Cslib.Logic.PL.CNF.coeffNorm1_univariateClearedParityCoord_branchParityRowSystem_le`: per-branch + locality, degree, and `coeffNorm1` bounds for a `ReplacementDesign`. +* `Cslib.Logic.PL.CNF.commonZeroCount_parityRowSystem_eq_sum_branch_emod_ediv_design`, + `Cslib.Logic.PL.CNF.numPassing_eq_sum_branch_emod_ediv_design`, + `Cslib.Logic.PL.CNF.modelCountOn_eq_sum_branch_emod_ediv_design_greedy`, + `Cslib.Logic.PL.CNF.max_initialRowWeightBound_greedyRowListLength`, + `Cslib.Logic.PL.CNF.exists_initialTests_modelCountOn_eq_sum_branch_emod_ediv_design_greedy`: + end-to-end `ReplacementDesign` recovery of `commonZeroCount`, `numPassing`, and + `φ.modelCountOn Finset.univ`. + +## References + +* [OpenAI, *The Exponential Time Hypothesis is False*][OpenAI2026ETH] (Sections 5.3–5.5 and 6.1–6.3, + printed pp. 37–43, 48–50) +-/ + +@[expose] public noncomputable section + +namespace Cslib.Logic.PL + +open Finset Polynomial Cslib.BitString Cslib.BalancedSchedules Cslib.LinearAlgebra +open scoped BigOperators + +variable {V : Type*} + +/-! ### 1. Branch-restricted CNF parity systems and neutral singleton scopes -/ + +section BranchParityRowSystem + +variable [Fintype V] [DecidableEq V] {n' : ℕ} + +/-- The `Fin rows.length`-indexed sparse `𝔽₂` polynomial system on `Fin n'` obtained from +`φ.parityRowSystem rows` by fixing `S : Finset V` to `b : V → Bool` (cancelling duplicate +monomials modulo `2`) and relabeling `univ \ S ≃ Fin n'` along `e` +([OpenAI2026ETH], §5.5 & §6.3, pp. 38–39, 43). -/ +def CNF.branchParityRowSystem (φ : CNF V) (rows : List (Fin φ.length → Bool)) + (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') (b : V → Bool) + (j : Fin rows.length) : Finset (Finset (Fin n')) := + relabelRestrictedMonomials S e b (φ.parityRowSystem rows j) + +/-- The variable scope `equationScope (φ.branchParityRowSystem rows S e b j) : Finset (Fin n')` of +restricted parity equation `j` on branch `b`. -/ +def CNF.branchParityRowScope (φ : CNF V) (rows : List (Fin φ.length → Bool)) + (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') (b : V → Bool) + (j : Fin rows.length) : Finset (Fin n') := + equationScope (φ.branchParityRowSystem rows S e b j) + +/-- The neutral-singleton augmented scope family on `Fin rows.length ⊕ Fin n'` associated to +`φ.branchParityRowScope rows S e b` ([OpenAI2026ETH], §5.3 & §5.5, pp. 37–38). -/ +def CNF.branchParityRowNeutralScope (φ : CNF V) (rows : List (Fin φ.length → Bool)) + (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') (b : V → Bool) : + Fin rows.length ⊕ Fin n' → Finset (Fin n') := + withNeutralScope (φ.branchParityRowScope rows S e b) + +/-- Every monomial in `φ.branchParityRowSystem rows S e b j` has degree at most `k` whenever every +clause of `φ` has width at most `k`. -/ +theorem CNF.card_mem_branchParityRowSystem_le {φ : CNF V} {k : ℕ} + (hk : ∀ c ∈ φ, c.length ≤ k) (rows : List (Fin φ.length → Bool)) + (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') (b : V → Bool) + (j : Fin rows.length) {M : Finset (Fin n')} + (hM : M ∈ φ.branchParityRowSystem rows S e b j) : + #M ≤ k := + card_mem_relabelRestrictedMonomials_le + (fun _M₀ hM₀ => CNF.card_mem_parityRowSystem_le hk rows j hM₀) M hM + +/-- Every branch equation `φ.branchParityRowSystem rows S e b j` contains at most `W * 2 ^ k` +monomials whenever every clause of `φ` has width at most `k` and every row in `rows` has Hamming +weight at most `W`. -/ +theorem CNF.card_branchParityRowSystem_le {φ : CNF V} {k W : ℕ} + (hk : ∀ c ∈ φ, c.length ≤ k) {rows : List (Fin φ.length → Bool)} + (hW : ∀ r ∈ rows, weight r ≤ W) (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') + (b : V → Bool) (j : Fin rows.length) : + #(φ.branchParityRowSystem rows S e b j) ≤ W * 2 ^ k := + (card_relabelRestrictedMonomials_le S e b (φ.parityRowSystem rows j)).trans + (CNF.card_parityRowSystem_le hk hW j) + +/-- Every branch equation scope `φ.branchParityRowScope rows S e b j` has size at most +`k * (W * 2 ^ k)` whenever every clause of `φ` has width at most `k` and every row in `rows` has +Hamming weight at most `W`. -/ +theorem CNF.card_branchParityRowScope_le {φ : CNF V} {k W : ℕ} + (hk : ∀ c ∈ φ, c.length ≤ k) {rows : List (Fin φ.length → Bool)} + (hW : ∀ r ∈ rows, weight r ≤ W) (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') + (b : V → Bool) (j : Fin rows.length) : + #(φ.branchParityRowScope rows S e b j) ≤ k * (W * 2 ^ k) := + (card_equationScope_le (fun _ hM => + CNF.card_mem_branchParityRowSystem_le hk rows S e b j hM)).trans + (Nat.mul_le_mul_left k (CNF.card_branchParityRowSystem_le hk hW S e b j)) + +/-- Every factor in `φ.branchParityRowNeutralScope rows S e b` has scope width at most +`max 1 (k * (W * 2 ^ k))` whenever every clause of `φ` has width at most `k` and every row in +`rows` has Hamming weight at most `W`. -/ +theorem CNF.card_branchParityRowNeutralScope_le {φ : CNF V} {k W : ℕ} + (hk : ∀ c ∈ φ, c.length ≤ k) {rows : List (Fin φ.length → Bool)} + (hW : ∀ r ∈ rows, weight r ≤ W) (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') + (b : V → Bool) (f : Fin rows.length ⊕ Fin n') : + #(φ.branchParityRowNeutralScope rows S e b f) ≤ max 1 (k * (W * 2 ^ k)) := + card_withNeutralScope_le (le_max_left 1 _) + (fun j => (CNF.card_branchParityRowScope_le hk hW S e b j).trans (le_max_right 1 _)) f + +/-- Total factor–variable incidence of `φ.branchParityRowNeutralScope rows S e b` equals +`totalIncidence univ (φ.branchParityRowScope rows S e b) + n'`. -/ +theorem CNF.totalIncidence_branchParityRowNeutralScope_eq (φ : CNF V) + (rows : List (Fin φ.length → Bool)) (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') + (b : V → Bool) : + totalIncidence (univ : Finset (Fin rows.length ⊕ Fin n')) + (φ.branchParityRowNeutralScope rows S e b) = + totalIncidence (univ : Finset (Fin rows.length)) + (φ.branchParityRowScope rows S e b) + n' := by + rw [CNF.branchParityRowNeutralScope, ← withNeutralFactors_univ, + totalIncidence_withNeutralFactors, Fintype.card_fin] + +/-- Neutral singleton factors ensure `n' ≤ M'_b` on every branch `b` ([OpenAI2026ETH], §5.5, +p. 38). -/ +theorem CNF.le_totalIncidence_branchParityRowNeutralScope (φ : CNF V) + (rows : List (Fin φ.length → Bool)) (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') + (b : V → Bool) : + n' ≤ totalIncidence (univ : Finset (Fin rows.length ⊕ Fin n')) + (φ.branchParityRowNeutralScope rows S e b) := by + rw [CNF.totalIncidence_branchParityRowNeutralScope_eq] + exact Nat.le_add_left n' _ + +/-- When `S = heavyVars univ univ (fun j => equationScope (φ.parityRowSystem rows j)) b₀`, every +free variable `v : Fin n'` occurs in at most `b₀` restricted equation scopes of +`φ.branchParityRowScope rows S e b`. -/ +theorem CNF.varIncidence_branchParityRowScope_heavyVars_le (φ : CNF V) + (rows : List (Fin φ.length → Bool)) (b₀ : ℕ) + (e : ↥(univ \ heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) ≃ Fin n') + (b : V → Bool) (v : Fin n') : + varIncidence (univ : Finset (Fin rows.length)) + (φ.branchParityRowScope rows + (heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) + e b) v ≤ b₀ := + varIncidence_relabelRestrictedMonomials_heavyVars_le univ univ + (φ.parityRowSystem rows) b₀ subset_rfl e b v + +/-- When `S = heavyVars univ univ (fun j => equationScope (φ.parityRowSystem rows j)) b₀`, every +free variable `v : Fin n'` occurs in at most `b₀ + 1` scopes of +`φ.branchParityRowNeutralScope rows S e b`. -/ +theorem CNF.varIncidence_branchParityRowNeutralScope_heavyVars_le (φ : CNF V) + (rows : List (Fin φ.length → Bool)) (b₀ : ℕ) + (e : ↥(univ \ heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) ≃ Fin n') + (b : V → Bool) (v : Fin n') : + varIncidence (univ : Finset (Fin rows.length ⊕ Fin n')) + (φ.branchParityRowNeutralScope rows + (heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) + e b) v ≤ b₀ + 1 := by + rw [CNF.branchParityRowNeutralScope, ← withNeutralFactors_univ] + exact varIncidence_withNeutralFactors_le + (CNF.varIncidence_branchParityRowScope_heavyVars_le φ rows b₀ e b v) + +/-- When `S = heavyVars univ univ (fun j => equationScope (φ.parityRowSystem rows j)) b₀`, total +branch incidence of `φ.branchParityRowNeutralScope rows S e b` is at most `(b₀ + 1) * n'`. -/ +theorem CNF.totalIncidence_branchParityRowNeutralScope_heavyVars_le (φ : CNF V) + (rows : List (Fin φ.length → Bool)) (b₀ : ℕ) + (e : ↥(univ \ heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) ≃ Fin n') + (b : V → Bool) : + totalIncidence (univ : Finset (Fin rows.length ⊕ Fin n')) + (φ.branchParityRowNeutralScope rows + (heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) + e b) ≤ (b₀ + 1) * n' := by + rw [CNF.branchParityRowNeutralScope, ← withNeutralFactors_univ] + simpa only [Fintype.card_fin] using + totalIncidence_withNeutralFactors_le + (CNF.varIncidence_branchParityRowScope_heavyVars_le φ rows b₀ e b) + +/-- **§5.5 & §6.3 (Lemma 4.7 `ReplacementDesign` existence on each heavy-variable branch of a +compiled CNF parity system)**: if every clause of `φ` has width at most `k` and every row in `rows` +has Hamming weight at most `W`, then on every branch `b : V → Bool` of +`S = heavyVars univ univ (fun j => equationScope (φ.parityRowSystem rows j)) b₀` with free-variable +relabeling `e : ↥(univ \ S) ≃ Fin n'`, the neutral-augmented scope family +`φ.branchParityRowNeutralScope rows S e b` admits a Section 4 +`ReplacementDesign _ Q r A₀ R τ` whenever the numerical balance and binomial-tail hypotheses of +Lemma 4.7 hold for `b_occ = b₀ + 1` and `w_width = max 1 (k * (W * 2 ^ k))`. -/ +theorem CNF.exists_replacementDesign_branchParityRowNeutralScope {φ : CNF V} + {k W b₀ Q r A₀ R : ℕ} {τ : ℚ} (hk : ∀ c ∈ φ, c.length ≤ k) + {rows : List (Fin φ.length → Bool)} (hW : ∀ r_row ∈ rows, weight r_row ≤ W) + (e : ↥(univ \ heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) ≃ Fin n') + (b : V → Bool) (hn' : 1 ≤ n') (hr : 2 ≤ r) (hR : 1 ≤ R) (hRQ : R < Q) (hτ : 0 ≤ τ) + (hI : ∀ a < R - 1, A₀ + a ≤ Q) + (hbal₁ : + 2 * (b₀ + 1 + max 1 (k * (W * 2 ^ k))) * (r ^ (2 * Q)) ^ 2 ≤ + totalIncidence (univ : Finset (Fin rows.length)) + (φ.branchParityRowScope rows + (heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) + e b) + n') + (hbal₂ : 2 * Q * r ^ 2 ≤ n') + (htail : binomTailProb Q r A₀ R ≤ τ / ((r : ℚ) * ((b₀ + 1 : ℕ) : ℚ))) : + Nonempty + (ReplacementDesign + (φ.branchParityRowNeutralScope rows + (heavyVars univ (univ : Finset (Fin rows.length)) + (fun j => equationScope (φ.parityRowSystem rows j)) b₀) + e b) Q r A₀ R τ) := + exists_replacementDesign_withNeutralScope _ hn' (le_max_left 1 _) hr hR hRQ hτ hI + (CNF.varIncidence_branchParityRowScope_heavyVars_le φ rows b₀ e b) + (fun j => (CNF.card_branchParityRowScope_le hk hW _ e b j).trans (le_max_right 1 _)) + hbal₁ hbal₂ htail + +end BranchParityRowSystem + +/-! ### 2. Per-branch locality, degree, and `coeffNorm1` bounds under a `ReplacementDesign` -/ + +section BranchDesignBounds + +variable [Fintype V] [DecidableEq V] {n' Q r A₀ R : ℕ} {τ : ℚ} + +/-- **§5.3 factor-level locality on a branch-restricted CNF parity equation**: for any +`D : ReplacementDesign (φ.branchParityRowNeutralScope rows S e b) Q r A₀ R τ`, the substituted +branch equation factor `(univariateMonomialSumPoly ℚ (designReplacementMap D Sum.inl x j) +(φ.branchParityRowSystem rows S e b j)).eval (nodePoint t)` depends only on `x` restricted to +`varGroup D.varAssign t (D.factorAssign (Sum.inl j) t)` and the measurement vector +`(varMeasurementMatrix Q r A₀ R ℚ D.place +(scopeVarOf (φ.branchParityRowNeutralScope rows S e b))).mulVec (fun u => bitVal ℚ (x u))`. -/ +theorem CNF.eval_univariateMonomialSumPoly_branchParityRowSystem_nodePoint_eq + (φ : CNF V) (rows : List (Fin φ.length → Bool)) + (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') (b : V → Bool) + (D : ReplacementDesign (φ.branchParityRowNeutralScope rows S e b) Q r A₀ R τ) + (j : Fin rows.length) (t : Fin Q) (x x' : Fin n' → Bool) + (hgrp : ∀ u ∈ varGroup D.varAssign t (D.factorAssign (Sum.inl j) t), x u = x' u) + (hmeas : + (varMeasurementMatrix Q r A₀ R ℚ D.place + (scopeVarOf (φ.branchParityRowNeutralScope rows S e b))).mulVec + (fun u => bitVal ℚ (x u)) = + (varMeasurementMatrix Q r A₀ R ℚ D.place + (scopeVarOf (φ.branchParityRowNeutralScope rows S e b))).mulVec + (fun u => bitVal ℚ (x' u))) : + (univariateMonomialSumPoly ℚ (designReplacementMap D Sum.inl x j) + (φ.branchParityRowSystem rows S e b j)).eval (nodePoint t) = + (univariateMonomialSumPoly ℚ (designReplacementMap D Sum.inl x' j) + (φ.branchParityRowSystem rows S e b j)).eval (nodePoint t) := + eval_univariateMonomialSumPoly_designReplacementMap_nodePoint_eq D Sum.inl + (φ.branchParityRowSystem rows S e b) subset_rfl t x x' hgrp hmeas + +/-- **Equation (5.5) degree bound on a branch-restricted CNF parity system**: if every clause of `φ` +has width at most `k`, then for any replacement design `D` on branch `b` (with factor embedding +`emb : Fin rows.length → F`) and `s < ℓ`, the cleared parity coordinate `s` on +`φ.branchParityRowSystem rows S e b` has +`natDegree ≤ (k * (R - 1)) * (rows.length + 2 * (ℓ - 1))` (strengthening the manuscript's coarser +`k * R * (rows.length + 2 * (ℓ - 1))` display in (5.5) via `natDegree ≤ R - 1` from Lemma 4.3 and +§4.5 (4.14)). -/ +theorem CNF.natDegree_univariateClearedParityCoord_branchParityRowSystem_le + {F : Type*} [Fintype F] {scope : F → Finset (Fin n')} {φ : CNF V} {k ℓ s : ℕ} + (hk : ∀ c ∈ φ, c.length ≤ k) (rows : List (Fin φ.length → Bool)) + (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') (b : V → Bool) + (D : ReplacementDesign scope Q r A₀ R τ) (emb : Fin rows.length → F) + (hs : s < ℓ) : + (univariateClearedParityCoord (univ : Finset (Fin rows.length)) + (φ.branchParityRowSystem rows S e b) (commonDenom Q R : ℤ) k ℓ + (designReplacementScaledIntMap D emb) s).natDegree ≤ + (k * (R - 1)) * (rows.length + 2 * (ℓ - 1)) := by + have hdeg := natDegree_univariateClearedParityCoord_design_le D emb + (univ : Finset (Fin rows.length)) (φ.branchParityRowSystem rows S e b) + (fun j _ _M hM => CNF.card_mem_branchParityRowSystem_le hk rows S e b j hM) hs + simpa only [card_univ, Fintype.card_fin] using hdeg + +/-- **Equation (5.5) degree bound for `univariateClearedParityTruncatedSum` on a branch-restricted +CNF parity system**: under `∀ c ∈ φ, c.length ≤ k`, the truncated sum has +`natDegree ≤ (k * (R - 1)) * (rows.length + 2 * (ℓ - 1))` (strengthening the manuscript's coarser +`k * R * (rows.length + 2 * (ℓ - 1))` display in (5.5) via `natDegree ≤ R - 1` from Lemma 4.3 and +§4.5 (4.14)). -/ +theorem CNF.natDegree_univariateClearedParityTruncatedSum_branchParityRowSystem_le + {F : Type*} [Fintype F] {scope : F → Finset (Fin n')} {φ : CNF V} {k : ℕ} + (hk : ∀ c ∈ φ, c.length ≤ k) (rows : List (Fin φ.length → Bool)) + (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') (b : V → Bool) + (D : ReplacementDesign scope Q r A₀ R τ) (emb : Fin rows.length → F) + (ℓ : ℕ) : + (univariateClearedParityTruncatedSum (univ : Finset (Fin rows.length)) + (φ.branchParityRowSystem rows S e b) (commonDenom Q R : ℤ) k ℓ + (designReplacementScaledIntMap D emb)).natDegree ≤ + (k * (R - 1)) * (rows.length + 2 * (ℓ - 1)) := by + have hdeg := natDegree_univariateClearedParityTruncatedSum_design_le D emb + (univ : Finset (Fin rows.length)) (φ.branchParityRowSystem rows S e b) k ℓ + (fun j _ _M hM => CNF.card_mem_branchParityRowSystem_le hk rows S e b j hM) + simpa only [card_univ, Fintype.card_fin] using hdeg + +/-- **Appendix B.2 coordinate `coeffNorm1` bound on a branch-restricted CNF parity system**: if +every clause of `φ` has width at most `k` and every row in `rows` has Hamming weight at most `W`, +then for any replacement design `D` on branch `b`, every cleared parity coordinate `s` on +`φ.branchParityRowSystem rows S e b` satisfies the Appendix B.2 `ℓ₁`-norm bound with +`S_mon = W * 2 ^ k`. -/ +theorem CNF.coeffNorm1_univariateClearedParityCoord_branchParityRowSystem_le + {F : Type*} [Fintype F] {scope : F → Finset (Fin n')} {φ : CNF V} {k W ℓ E_ℓ : ℕ} + (hk : ∀ c ∈ φ, c.length ≤ k) {rows : List (Fin φ.length → Bool)} + (hW : ∀ r_row ∈ rows, weight r_row ≤ W) (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') + (b : V → Bool) (D : ReplacementDesign scope Q r A₀ R τ) + (emb : Fin rows.length → F) + (hE : ∀ j < ℓ, (Polynomial.parityBaseDigit ℓ j).coeffNorm1 ≤ E_ℓ) (s : ℕ) : + (univariateClearedParityCoord (univ : Finset (Fin rows.length)) + (φ.branchParityRowSystem rows S e b) (commonDenom Q R : ℤ) k ℓ + (designReplacementScaledIntMap D emb) s).coeffNorm1 ≤ + 2 ^ n' * + (ℓ * E_ℓ * + ((W * 2 ^ k) * designRowNormBound Q R ^ k + commonDenom Q R ^ k) ^ (2 * ℓ - 1)) ^ + rows.length := by + have hnorm := coeffNorm1_univariateClearedParityCoord_design_le D emb + (univ : Finset (Fin rows.length)) (φ.branchParityRowSystem rows S e b) k ℓ + (S_mon := W * 2 ^ k) + (fun j _ _M hM => CNF.card_mem_branchParityRowSystem_le hk rows S e b j hM) + (fun j _ => CNF.card_branchParityRowSystem_le hk hW S e b j) + hE s + simpa only [card_univ, Fintype.card_fin] using hnorm + +end BranchDesignBounds + +/-! ### 3. End-to-end `ReplacementDesign` recovery of `commonZeroCount`, `numPassing`, and `#F` -/ + +section DesignRecovery + +variable [Fintype V] [DecidableEq V] {n' Q r A₀ R : ℕ} {τ : ℚ} + +/-- **§5.4–5.5 single-branch `ReplacementDesign` recovery for `φ.branchParityRowSystem`**: when +`∀ c ∈ φ, c.length ≤ k` and `n' < ℓ`, evaluating `univariateClearedParityTruncatedSum` at `T = 0`, +dividing by `clearedProductDenom`, and reducing modulo `2 ^ ℓ` recovers the branch common-zero count +`commonZeroCount univ (φ.branchParityRowSystem rows S e b)`. -/ +theorem CNF.emod_ediv_eval_zero_branchParityRowSystem_design_eq_commonZeroCount + {F : Type*} [Fintype F] {scope : F → Finset (Fin n')} {φ : CNF V} {k ℓ : ℕ} + (hk : ∀ c ∈ φ, c.length ≤ k) (rows : List (Fin φ.length → Bool)) + (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') (b : V → Bool) + (D : ReplacementDesign scope Q r A₀ R τ) (emb : Fin rows.length → F) + (hℓ : n' < ℓ) : + ((univariateClearedParityTruncatedSum (univ : Finset (Fin rows.length)) + (φ.branchParityRowSystem rows S e b) (commonDenom Q R : ℤ) k ℓ + (designReplacementScaledIntMap D emb)).eval 0 / + clearedProductDenom (commonDenom Q R : ℤ) k ℓ + #(univ : Finset (Fin rows.length))) % ((2 : ℤ) ^ ℓ) = + (commonZeroCount (univ : Finset (Fin rows.length)) + (φ.branchParityRowSystem rows S e b) : ℤ) := + emod_ediv_eval_zero_clearedParityTruncatedSum_design_eq_commonZeroCount D emb + (univ : Finset (Fin rows.length)) (φ.branchParityRowSystem rows S e b) hℓ + (fun j _ _M hM => CNF.card_mem_branchParityRowSystem_le hk rows S e b j hM) + +/-- **§5.4–5.5 & §6 (branch-summed `ReplacementDesign` recovery of `commonZeroCount` on +`φ.parityRowSystem rows`)**: when every clause of `φ` has width at most `k`, summing the per-branch +`ReplacementDesign` modular residues of `univariateClearedParityTruncatedSum` at shared precision +`branchPrecision (Fintype.card V)` across all `b ∈ branchAssignments S` recovers +`commonZeroCount univ (φ.parityRowSystem rows)`. -/ +theorem CNF.commonZeroCount_parityRowSystem_eq_sum_branch_emod_ediv_design + {F : (V → Bool) → Type*} [∀ b, Fintype (F b)] {φ : CNF V} {k : ℕ} + (hk : ∀ c ∈ φ, c.length ≤ k) (rows : List (Fin φ.length → Bool)) + (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') + (scope : (b : V → Bool) → F b → Finset (Fin n')) + (D : (b : V → Bool) → ReplacementDesign (scope b) Q r A₀ R τ) + (emb : (b : V → Bool) → Fin rows.length → F b) : + (commonZeroCount (univ : Finset (Fin rows.length)) (φ.parityRowSystem rows) : ℤ) = + ∑ b ∈ branchAssignments S, + ((univariateClearedParityTruncatedSum (univ : Finset (Fin rows.length)) + (φ.branchParityRowSystem rows S e b) (commonDenom Q R : ℤ) k + (branchPrecision (Fintype.card V)) + (designReplacementScaledIntMap (D b) (emb b))).eval 0 / + clearedProductDenom (commonDenom Q R : ℤ) k + (branchPrecision (Fintype.card V)) #(univ : Finset (Fin rows.length))) % + ((2 : ℤ) ^ branchPrecision (Fintype.card V)) := + commonZeroCount_eq_sum_branch_emod_ediv_cleared S e (univ : Finset (Fin rows.length)) + (φ.parityRowSystem rows) (commonDenom_intCast_ne_zero Q R) + (fun b => designReplacementScaledIntMap (D b) (emb b)) + (fun j _ _M hM => CNF.card_mem_parityRowSystem_le hk rows j hM) + (fun b _ x j _ _ _ v _ => eval_zero_designReplacementScaledIntMap (D b) (emb b) x j v) + +/-- **§5.4–5.5 & §6 (branch-summed `ReplacementDesign` recovery of +`numPassing φ.violations rows`)**: when every clause of `φ` has width at most `k`, the +branch-summed `ReplacementDesign` evaluation at `T = 0` equals +`(numPassing φ.violations rows : ℤ)`. -/ +theorem CNF.numPassing_eq_sum_branch_emod_ediv_design + {F : (V → Bool) → Type*} [∀ b, Fintype (F b)] {φ : CNF V} {k : ℕ} + (hk : ∀ c ∈ φ, c.length ≤ k) (rows : List (Fin φ.length → Bool)) + (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') + (scope : (b : V → Bool) → F b → Finset (Fin n')) + (D : (b : V → Bool) → ReplacementDesign (scope b) Q r A₀ R τ) + (emb : (b : V → Bool) → Fin rows.length → F b) : + (numPassing φ.violations rows : ℤ) = + ∑ b ∈ branchAssignments S, + ((univariateClearedParityTruncatedSum (univ : Finset (Fin rows.length)) + (φ.branchParityRowSystem rows S e b) (commonDenom Q R : ℤ) k + (branchPrecision (Fintype.card V)) + (designReplacementScaledIntMap (D b) (emb b))).eval 0 / + clearedProductDenom (commonDenom Q R : ℤ) k + (branchPrecision (Fintype.card V)) #(univ : Finset (Fin rows.length))) % + ((2 : ℤ) ^ branchPrecision (Fintype.card V)) := by + rw [← CNF.commonZeroCount_parityRowSystem] + exact CNF.commonZeroCount_parityRowSystem_eq_sum_branch_emod_ediv_design hk rows S e scope D emb + +/-- **§5.4–5.5 & §6.2–6.3 (branch-summed `ReplacementDesign` recovery of +`φ.modelCountOn Finset.univ` after `3 * n` greedy rounds)**: under the hypotheses of +`CNF.commonZeroCount_parityRowSystem_greedy`, for any heavy-variable set `S : Finset V`, +free-variable relabeling `e : ↥(univ \ S) ≃ Fin n'`, and per-branch replacement design family +`D b`, summing the branch modular residues of `univariateClearedParityTruncatedSum` at `T = 0` +across `b ∈ branchAssignments S` recovers `(φ.modelCountOn Finset.univ : ℤ)` without any auxiliary +replacement hypotheses. -/ +theorem CNF.modelCountOn_eq_sum_branch_emod_ediv_design_greedy + {F : (V → Bool) → Type*} [∀ b, Fintype (F b)] (φ : CNF V) {k P : ℕ} + (hk : ∀ c ∈ φ, c.length ≤ k) (hn : 1 ≤ Fintype.card V) (hP : 1 ≤ P) + (A : ℕ → List (Fin φ.length → Bool)) + (hinit : ∀ σ, PassesTests φ.violations (A 0) σ → φ.eval σ = false → + φ.length ≤ P * weight (φ.violations σ)) + (γ : ℕ → Fin P → Fin φ.length × Bool) (hA : ∀ i, A (i + 1) = A i ++ [testRow (γ i)]) + (hγ : ∀ i (γ' : Fin P → Fin φ.length × Bool), + commonZeroCount univ (φ.parityRowSystem (A i ++ [testRow (γ i)])) ≤ + commonZeroCount univ (φ.parityRowSystem (A i ++ [testRow γ']))) + (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') + (scope : (b : V → Bool) → F b → Finset (Fin n')) + (D : (b : V → Bool) → ReplacementDesign (scope b) Q r A₀ R τ) + (emb : (b : V → Bool) → Fin (A (3 * Fintype.card V)).length → F b) : + (φ.modelCountOn Finset.univ : ℤ) = + ∑ b ∈ branchAssignments S, + ((univariateClearedParityTruncatedSum + (univ : Finset (Fin (A (3 * Fintype.card V)).length)) + (φ.branchParityRowSystem (A (3 * Fintype.card V)) S e b) + (commonDenom Q R : ℤ) k (branchPrecision (Fintype.card V)) + (designReplacementScaledIntMap (D b) (emb b))).eval 0 / + clearedProductDenom (commonDenom Q R : ℤ) k + (branchPrecision (Fintype.card V)) + #(univ : Finset (Fin (A (3 * Fintype.card V)).length))) % + ((2 : ℤ) ^ branchPrecision (Fintype.card V)) := by + rw [← CNF.commonZeroCount_parityRowSystem_greedy φ hn hP A hinit γ hA hγ] + exact CNF.commonZeroCount_parityRowSystem_eq_sum_branch_emod_ediv_design hk + (A (3 * Fintype.card V)) S e scope D emb + +/-- For all `B, m : ℕ`, the Lemma 6.2 greedy list length +`P = 20000 * B ^ 2 * (Nat.log 2 m + 1) ^ 2` is at most the Lemma 6.1 initial row weight bound +`D₀ = 320000 * B ^ 2 * (Nat.log 2 m + 1) ^ 3`. -/ +theorem CNF.greedyRowListLength_le_initialRowWeightBound (B m : ℕ) : + CNF.greedyRowListLength B m ≤ CNF.initialRowWeightBound B m := by + unfold CNF.greedyRowListLength CNF.initialRowWeightBound + have h1 : 20000 * B ^ 2 ≤ 320000 * B ^ 2 := + Nat.mul_le_mul_right (B ^ 2) (by decide) + have h2 : (Nat.log 2 m + 1) ^ 2 ≤ (Nat.log 2 m + 1) ^ 3 := + Nat.pow_le_pow_right (Nat.succ_pos _) (by decide) + exact Nat.mul_le_mul h1 h2 + +/-- Consequently `max (CNF.initialRowWeightBound B m) (CNF.greedyRowListLength B m)` simplifies to +`CNF.initialRowWeightBound B m`. -/ +@[simp] +theorem CNF.max_initialRowWeightBound_greedyRowListLength (B m : ℕ) : + max (CNF.initialRowWeightBound B m) (CNF.greedyRowListLength B m) = + CNF.initialRowWeightBound B m := + max_eq_left (CNF.greedyRowListLength_le_initialRowWeightBound B m) + +/-- **Lemmas 6.1–6.2 & §§5.4–5.5, 6.3 (end-to-end initial + greedy parity-test compilation and +branch-summed `ReplacementDesign` recovery of `φ.modelCountOn Finset.univ`)**: for any width-`≤ k` +CNF `φ` on `n = Fintype.card V ≥ 1` variables with `φ.length ≤ B * n` (`B ≥ 1`), there exist +initial parity rows `A_init` (`A_init.length ≤ 3 * n`, each of weight at most +`D₀ = CNF.initialRowWeightBound B φ.length`) such that for any greedy sequence +`A (i + 1) = A i ++ [testRow (γ i)]` with `A 0 = A_init` minimizing +`commonZeroCount univ (φ.parityRowSystem (A i ++ [testRow γ']))` over candidate rows of list length +`P = CNF.greedyRowListLength B φ.length`: +1. At every stage `i ≤ 3 * n`, `(A i).length ≤ 6 * n`, and every compiled equation + `φ.parityRowSystem (A i) j` has degree at most `k`, at most `D₀ * 2 ^ k` monomials, and + variable-scope size `#(equationScope (φ.parityRowSystem (A i) j)) ≤ k * (D₀ * 2 ^ k)`. +2. After `3 * n` rounds, for any heavy-variable set `S : Finset V`, free-variable relabeling + `e : ↥(univ \ S) ≃ Fin n'`, and per-branch replacement design family `D b`, the branch-summed + `ReplacementDesign` evaluation at `T = 0` equals `(φ.modelCountOn Finset.univ : ℤ)`. -/ +theorem CNF.exists_initialTests_modelCountOn_eq_sum_branch_emod_ediv_design_greedy + {F : (V → Bool) → Type*} [∀ b, Fintype (F b)] (φ : CNF V) {k B : ℕ} + (hk : ∀ c ∈ φ, c.length ≤ k) (hn : 1 ≤ Fintype.card V) (hB : 1 ≤ B) + (hm : φ.length ≤ B * Fintype.card V) : + let D₀ := CNF.initialRowWeightBound B φ.length + let P := CNF.greedyRowListLength B φ.length + ∃ A_init : List (Fin φ.length → Bool), A_init.length ≤ 3 * Fintype.card V ∧ + (∀ r_row ∈ A_init, weight r_row ≤ D₀) ∧ + ∀ (A : ℕ → List (Fin φ.length → Bool)) + (γ : ℕ → Fin P → Fin φ.length × Bool), + A 0 = A_init → (∀ i, A (i + 1) = A i ++ [testRow (γ i)]) → + (∀ i (γ' : Fin P → Fin φ.length × Bool), + commonZeroCount univ (φ.parityRowSystem (A i ++ [testRow (γ i)])) ≤ + commonZeroCount univ (φ.parityRowSystem (A i ++ [testRow γ']))) → + (∀ i ≤ 3 * Fintype.card V, + (A i).length ≤ 6 * Fintype.card V ∧ + ∀ j : Fin (A i).length, + (∀ M ∈ φ.parityRowSystem (A i) j, M ⊆ φ.vars.toFinset ∧ #M ≤ k) ∧ + #(φ.parityRowSystem (A i) j) ≤ D₀ * 2 ^ k ∧ + #(equationScope (φ.parityRowSystem (A i) j)) ≤ k * (D₀ * 2 ^ k)) ∧ + ∀ (S : Finset V) (e : ↥(univ \ S) ≃ Fin n') + (scope : (b : V → Bool) → F b → Finset (Fin n')) + (D : (b : V → Bool) → ReplacementDesign (scope b) Q r A₀ R τ) + (emb : (b : V → Bool) → Fin (A (3 * Fintype.card V)).length → F b), + (φ.modelCountOn Finset.univ : ℤ) = + ∑ b ∈ branchAssignments S, + ((univariateClearedParityTruncatedSum + (univ : Finset (Fin (A (3 * Fintype.card V)).length)) + (φ.branchParityRowSystem (A (3 * Fintype.card V)) S e b) + (commonDenom Q R : ℤ) k (branchPrecision (Fintype.card V)) + (designReplacementScaledIntMap (D b) (emb b))).eval 0 / + clearedProductDenom (commonDenom Q R : ℤ) k + (branchPrecision (Fintype.card V)) + #(univ : Finset (Fin (A (3 * Fintype.card V)).length))) % + ((2 : ℤ) ^ branchPrecision (Fintype.card V)) := by + obtain ⟨A_init, hlen, hw, hgreedy⟩ := CNF.exists_initialTests_commonZeroCount_greedy φ hk hn hB hm + refine ⟨A_init, hlen, hw, fun A γ h0 hA hγ => ?_⟩ + obtain ⟨hcount, hbounds⟩ := hgreedy A γ h0 hA hγ + refine ⟨fun i hi => ?_, fun S e scope D emb => ?_⟩ + · obtain ⟨hlen_i, hj⟩ := hbounds i hi + refine ⟨hlen_i, fun j => ?_⟩ + obtain ⟨hM, hcard, hscope⟩ := hj j + rw [CNF.max_initialRowWeightBound_greedyRowListLength] at hcard hscope + exact ⟨hM, hcard, hscope⟩ + · rw [← hcount] + exact CNF.commonZeroCount_parityRowSystem_eq_sum_branch_emod_ediv_design hk + (A (3 * Fintype.card V)) S e scope D emb + +end DesignRecovery + +end Cslib.Logic.PL diff --git a/ComplexityTests/SparseDesignCounter.lean b/ComplexityTests/SparseDesignCounter.lean new file mode 100644 index 0000000000..3663158a41 --- /dev/null +++ b/ComplexityTests/SparseDesignCounter.lean @@ -0,0 +1,474 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Complexity.Logics.Propositional.CNF.SparseDesignCounter + +/-! +# Tests for `Complexity.Logics.Propositional.CNF.SparseDesignCounter` +-/ + +@[expose] public noncomputable section + +namespace ComplexityTests.SparseDesignCounter + +open Finset Polynomial Cslib.BitString Cslib.BalancedSchedules Cslib.LinearAlgebra Cslib.Logic.PL + +/-- Concrete 2-variable 2-CNF formula `(x₀ ∨ ¬x₁) ∧ (x₁)` on `Fin 2`, with unique satisfying +assignment `x₀ = true, x₁ = true`. -/ +def testCNF : CNF (Fin 2) := + [[(0, true), (1, false)], [(1, true)]] + +/-- Concrete two-row unit parity check matrix selecting clause `0` and clause `1` of `testCNF`. -/ +def testRows : List (Fin testCNF.length → Bool) := + [Pi.single ⟨0, by decide⟩ true, Pi.single ⟨1, by decide⟩ true] + +/-- Concrete 3-variable 2-CNF formula `(x₀ ∨ ¬x₁) ∧ (x₁ ∨ ¬x₂)` on `Fin 3`, with `4` satisfying +assignments (`(false, false, false)`, `(true, false, false)`, `(true, true, false)`, and +`(true, true, true)`). -/ +def testCNF3 : CNF (Fin 3) := + [[(0, true), (1, false)], [(1, true), (2, false)]] + +/-- Unit parity check rows for `testCNF3`. -/ +def testRows3 : List (Fin testCNF3.length → Bool) := + [Pi.single ⟨0, by decide⟩ true, Pi.single ⟨1, by decide⟩ true] + +/-- Explicit equivalence between the unbranched variables `{1, 2}` (`univ \ {0}` in `Fin 3`) and +`Fin 2`. -/ +def freeVarEquiv3 : ↥((univ : Finset (Fin 3)) \ ({0} : Finset (Fin 3))) ≃ Fin 2 where + toFun v := if v.1 = 1 then 0 else 1 + invFun i := if i = 0 then ⟨1, by decide⟩ else ⟨2, by decide⟩ + left_inv := by decide + right_inv := by decide + +/-- Expected-value and sparsity controls on `testCNF3.branchParityRowSystem`, +`branchParityRowScope`, and `branchParityRowNeutralScope` for branch `b = fun _ => false` on +`S = {0}`: +- Clause `0` (`x₀ ∨ ¬x₁`) has violation monomial `(1 + x₀) x₁ = x₁ + x₀ x₁`, which under + `x₀ = false` restricts to `x₁` (free variable `0 : Fin 2`, scope `{0}`). +- Clause `1` (`x₁ ∨ ¬x₂`) has violation monomial `(1 + x₁) x₂ = x₂ + x₁ x₂`, which restricts to + `{1}, {0, 1}` on `Fin 2` (scope `{0, 1}`). +- Neutral singleton factors add `{0}` and `{1}`, so total neutral-augmented incidence is + `(1 + 2) + 2 = 5 ≥ 2`. -/ +example (j : Fin testRows3.length) (f : Fin testRows3.length ⊕ Fin 2) : + #(testCNF3.branchParityRowSystem testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 + (fun _ => false) j) ≤ 1 * 2 ^ 2 ∧ + #(testCNF3.branchParityRowScope testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 + (fun _ => false) j) ≤ 2 * (1 * 2 ^ 2) ∧ + #(testCNF3.branchParityRowNeutralScope testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 + (fun _ => false) f) ≤ max 1 (2 * (1 * 2 ^ 2)) ∧ + totalIncidence (univ : Finset (Fin testRows3.length ⊕ Fin 2)) + (testCNF3.branchParityRowNeutralScope testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 + (fun _ => false)) = 5 ∧ + 2 ≤ totalIncidence (univ : Finset (Fin testRows3.length ⊕ Fin 2)) + (testCNF3.branchParityRowNeutralScope testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 + (fun _ => false)) := by + refine ⟨?_, ?_, ?_, by decide, ?_⟩ + · exact testCNF3.card_branchParityRowSystem_le (k := 2) (W := 1) (by decide) (by decide) + ({0} : Finset (Fin 3)) freeVarEquiv3 (fun _ => false) j + · exact testCNF3.card_branchParityRowScope_le (k := 2) (W := 1) (by decide) (by decide) + ({0} : Finset (Fin 3)) freeVarEquiv3 (fun _ => false) j + · exact testCNF3.card_branchParityRowNeutralScope_le (k := 2) (W := 1) (by decide) (by decide) + ({0} : Finset (Fin 3)) freeVarEquiv3 (fun _ => false) f + · exact testCNF3.le_totalIncidence_branchParityRowNeutralScope testRows3 + ({0} : Finset (Fin 3)) freeVarEquiv3 (fun _ => false) + +/-- Canonical `Q = 1, r = 1, A₀ = 0, R = 1, τ = 0` replacement design for any scope family on +`Fin n`. -/ +def unitReplacementDesign {F : Type*} [Fintype F] {n : ℕ} + (scope : F → Finset (Fin n)) : ReplacementDesign scope 1 1 0 1 0 := + let h := Fintype.card (ScopeIncidence scope) + let eFin := Fintype.equivFin (ScopeIncidence scope) + let place : ScopeIncidence scope → Fin h × AuxLabel 1 1 := + fun e => (eFin e, (fun _ => 0, fun _ => 0)) + have hinj : Function.Injective place := fun _ _ hEq => + eFin.injective (congrArg Prod.fst hEq) + { varAssign := fun _ _ => 0 + factorAssign := fun _ _ => 0 + h := h + place := place + card_varGroup_le := fun t g => by + have hsub : #(varGroup (fun (_ : Fin n) (_ : Fin 1) => (0 : Fin 1)) t g) ≤ n := by + simpa only [card_univ, Fintype.card_fin] using + card_le_univ (varGroup (fun (_ : Fin n) (_ : Fin 1) => (0 : Fin 1)) t g) + omega + factorAssign_of_empty := fun _ _ _ => rfl + injective_place := hinj + place_snd := fun _ => rfl + h_mul_D_le := by + change Fintype.card (ScopeIncidence scope) * 1 ^ (2 * 1) ≤ 2 * ∑ f : F, #(scope f) + rw [card_scopeIncidence] + omega + total_measurement_rows_le := by + have hmeas : #(measurementIndices 1 1 0 1) = 0 := by decide + simp [hmeas] + sum_abs_measurement_row_le := + sum_abs_varMeasurementMatrix_le 1 1 0 1 hinj (scopeVarOf scope) + natDegree_replacementRow_le := + natDegree_varReplacementRow_le 1 1 0 1 place (scopeVarOf scope) + eval_replacementRow_zero := + eval_varReplacementRow_zero 1 1 0 1 hinj (scopeVarOf scope) + evalReplacementRow_nodePoint_eq_zero_of_kernel := fun e t g hg ξ hsupp hker => + evalVarReplacementRow_nodePoint_eq_zero_of_kernel 1 1 0 1 place (scopeVarOf scope) + (fun _ _ => 0) (fun _ => rfl) e t g hg ξ hsupp hker + map_replacementRowScaledInt := + map_varReplacementRowScaledInt 1 1 0 1 place (scopeVarOf scope) + sum_coeffNorm1_replacementRowScaledInt_le := + sum_coeffNorm1_varReplacementRowScaledInt_le (by decide) hinj (scopeVarOf scope) + (by intro a ha; omega) } + +/-- Scope family on `Fin testRows3.length ⊕ Fin 2` for branch `b = fun _ => false` of `testCNF3`. -/ +abbrev sampleBranchNeutralScope : Fin testRows3.length ⊕ Fin 2 → Finset (Fin 2) := + testCNF3.branchParityRowNeutralScope testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 + (fun _ => false) + +/-- Variable group schedule for `nonconstBranchDesign`: free variable `0` in group `0`, free +variable `1` in group `1`. -/ +def nonconstBranchVarAssign : Fin 2 → Fin 1 → Fin 2 := + fun v _ => v + +/-- Factor group schedule for `nonconstBranchDesign`: equation factors in group `0`, neutral +factors in group `1`. -/ +def nonconstBranchFactorAssign : Fin testRows3.length ⊕ Fin 2 → Fin 1 → Fin 2 + | Sum.inl _, _ => 0 + | Sum.inr _, _ => 1 + +/-- Copy index in `Fin 2` separating the `5` incidences of `sampleBranchNeutralScope`. -/ +def nonconstBranchCopy (e : ScopeIncidence sampleBranchNeutralScope) : Fin 2 := + match e.1 with + | Sum.inl ⟨0, _⟩ => 0 + | Sum.inl ⟨1, _⟩ => if e.2.1 = 0 then 1 else 0 + | Sum.inr _ => 0 + +/-- Injective placement for `nonconstBranchDesign`. -/ +def nonconstBranchPlace (e : ScopeIncidence sampleBranchNeutralScope) : + Fin 2 × AuxLabel 1 2 := + (nonconstBranchCopy e, + (nonconstBranchVarAssign (scopeVarOf sampleBranchNeutralScope e), + nonconstBranchFactorAssign (scopeFactorOf sampleBranchNeutralScope e))) + +theorem injective_nonconstBranchPlace : Function.Injective nonconstBranchPlace := by + decide + +/-- Concrete `Q = 1, r = 2, A₀ = 0, R = 2, τ = 0` replacement design on `sampleBranchNeutralScope` +with nonconstant degree-`1` replacement rows. -/ +def nonconstBranchDesign : + ReplacementDesign sampleBranchNeutralScope 1 2 0 2 0 where + varAssign := nonconstBranchVarAssign + factorAssign := nonconstBranchFactorAssign + h := 2 + place := nonconstBranchPlace + card_varGroup_le := by decide + factorAssign_of_empty := by decide + injective_place := injective_nonconstBranchPlace + place_snd := fun _ => rfl + h_mul_D_le := by decide + total_measurement_rows_le := by + have hmeas : #(measurementIndices 1 2 0 2) = 0 := by decide + simp [hmeas] + sum_abs_measurement_row_le := + sum_abs_varMeasurementMatrix_le 1 2 0 2 injective_nonconstBranchPlace + (scopeVarOf sampleBranchNeutralScope) + natDegree_replacementRow_le := + natDegree_varReplacementRow_le 1 2 0 2 nonconstBranchPlace + (scopeVarOf sampleBranchNeutralScope) + eval_replacementRow_zero := + eval_varReplacementRow_zero 1 2 0 2 injective_nonconstBranchPlace + (scopeVarOf sampleBranchNeutralScope) + evalReplacementRow_nodePoint_eq_zero_of_kernel := fun e t g hg ξ hsupp hker => + evalVarReplacementRow_nodePoint_eq_zero_of_kernel 1 2 0 2 nonconstBranchPlace + (scopeVarOf sampleBranchNeutralScope) nonconstBranchVarAssign (fun _ => rfl) e t g hg ξ + hsupp hker + map_replacementRowScaledInt := + map_varReplacementRowScaledInt 1 2 0 2 nonconstBranchPlace + (scopeVarOf sampleBranchNeutralScope) + sum_coeffNorm1_replacementRowScaledInt_le := + sum_coeffNorm1_varReplacementRowScaledInt_le (by decide) injective_nonconstBranchPlace + (scopeVarOf sampleBranchNeutralScope) (by intro a ha; omega) + +/-- Assignment `x₁ = (false, true)` on `Fin 2` placing `true` on free variable `1 : Fin 2` in +group `1`. -/ +def branchAssign₁ : Fin 2 → Bool := fun v => decide (v = 1) + +/-- All-false assignment `x₂ = (false, false)` on `Fin 2` agreeing with `branchAssign₁` on group +`0 = {0}`. -/ +def branchAssign₂ : Fin 2 → Bool := fun _ => false + +/-- Checked client of `CNF.eval_univariateMonomialSumPoly_branchParityRowSystem_nodePoint_eq`, +`CNF.natDegree_univariateClearedParityCoord_branchParityRowSystem_le`, +`CNF.natDegree_univariateClearedParityTruncatedSum_branchParityRowSystem_le`, and +`CNF.coeffNorm1_univariateClearedParityCoord_branchParityRowSystem_le` on `nonconstBranchDesign`: +1. The replacement polynomial for equation `1` and free variable `1 : Fin 2` on `branchAssign₁` has + exact nonconstant degree `1` (`eval 0 = 1`, `eval (nodePoint 0) = 0`). +2. `CNF.eval_univariateMonomialSumPoly_branchParityRowSystem_nodePoint_eq` proves that the + substituted equation-`1` factors for `branchAssign₁` and `branchAssign₂` coincide at + `nodePoint (0 : Fin 1)`. +3. The cleared coordinate and truncated-sum polynomials at `k = 2, ℓ = 4` have `natDegree ≤ 16` + and satisfy the Appendix B.2 `coeffNorm1` bound. -/ +example {E_ℓ : ℕ} (hE : ∀ j < 4, (Polynomial.parityBaseDigit 4 j).coeffNorm1 ≤ E_ℓ) : + (designReplacementMap nonconstBranchDesign Sum.inl branchAssign₁ ⟨1, by decide⟩ 1).eval 0 = 1 ∧ + (designReplacementMap nonconstBranchDesign Sum.inl branchAssign₁ ⟨1, by decide⟩ 1).eval + (nodePoint (0 : Fin 1)) = 0 ∧ + (univariateMonomialSumPoly ℚ + (designReplacementMap nonconstBranchDesign Sum.inl branchAssign₁ ⟨1, by decide⟩) + (testCNF3.branchParityRowSystem testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 + (fun _ => false) ⟨1, by decide⟩)).eval (nodePoint (0 : Fin 1)) = + (univariateMonomialSumPoly ℚ + (designReplacementMap nonconstBranchDesign Sum.inl branchAssign₂ ⟨1, by decide⟩) + (testCNF3.branchParityRowSystem testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 + (fun _ => false) ⟨1, by decide⟩)).eval (nodePoint (0 : Fin 1)) ∧ + (univariateClearedParityCoord (univ : Finset (Fin testRows3.length)) + (testCNF3.branchParityRowSystem testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 + (fun _ => false)) + (commonDenom 1 2 : ℤ) 2 4 (designReplacementScaledIntMap nonconstBranchDesign Sum.inl) + 0).natDegree ≤ 16 ∧ + (univariateClearedParityTruncatedSum (univ : Finset (Fin testRows3.length)) + (testCNF3.branchParityRowSystem testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 + (fun _ => false)) + (commonDenom 1 2 : ℤ) 2 4 + (designReplacementScaledIntMap nonconstBranchDesign Sum.inl)).natDegree ≤ 16 ∧ + (univariateClearedParityCoord (univ : Finset (Fin testRows3.length)) + (testCNF3.branchParityRowSystem testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 + (fun _ => false)) + (commonDenom 1 2 : ℤ) 2 4 (designReplacementScaledIntMap nonconstBranchDesign Sum.inl) + 0).coeffNorm1 ≤ + 2 ^ 2 * + (4 * E_ℓ * + ((1 * 2 ^ 2) * designRowNormBound 1 2 ^ 2 + commonDenom 1 2 ^ 2) ^ (2 * 4 - 1)) ^ 2 := by + have hgrp : ∀ u ∈ varGroup nonconstBranchDesign.varAssign (0 : Fin 1) + (nonconstBranchDesign.factorAssign (Sum.inl ⟨1, by decide⟩) (0 : Fin 1)), + branchAssign₁ u = branchAssign₂ u := by + decide + have hmeas : + (varMeasurementMatrix 1 2 0 2 ℚ nonconstBranchDesign.place + (scopeVarOf sampleBranchNeutralScope)).mulVec (fun u => bitVal ℚ (branchAssign₁ u)) = + (varMeasurementMatrix 1 2 0 2 ℚ nonconstBranchDesign.place + (scopeVarOf sampleBranchNeutralScope)).mulVec (fun u => bitVal ℚ (branchAssign₂ u)) := by + funext p + exact absurd p.2.2 (by decide +revert) + have heval0 : + (designReplacementMap nonconstBranchDesign Sum.inl branchAssign₁ ⟨1, by decide⟩ 1).eval 0 = + 1 := by + rw [eval_zero_designReplacementMap] + rfl + have heval1 : + (designReplacementMap nonconstBranchDesign Sum.inl branchAssign₁ ⟨1, by decide⟩ 1).eval + (nodePoint (0 : Fin 1)) = 0 := by + have hloc := eval_nodePoint_designReplacementMap_eq_of_eq_on_varGroup + nonconstBranchDesign Sum.inl (i := (⟨1, by decide⟩ : Fin testRows3.length)) + (v := (1 : Fin 2)) (by decide) (0 : Fin 1) branchAssign₁ branchAssign₂ hgrp hmeas + have hzero : + designReplacementMap nonconstBranchDesign Sum.inl branchAssign₂ ⟨1, by decide⟩ 1 = 0 := by + simp [branchAssign₂, designReplacementMap, evalVarReplacementRowPoly] + rw [hloc, hzero, Polynomial.eval_zero] + refine ⟨heval0, heval1, ?_, ?_, ?_, ?_⟩ + · exact testCNF3.eval_univariateMonomialSumPoly_branchParityRowSystem_nodePoint_eq + testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 (fun _ => false) nonconstBranchDesign + ⟨1, by decide⟩ (0 : Fin 1) branchAssign₁ branchAssign₂ hgrp hmeas + · simpa [testRows3] using testCNF3.natDegree_univariateClearedParityCoord_branchParityRowSystem_le + (k := 2) (ℓ := 4) (s := 0) (by decide) testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 + (fun _ => false) nonconstBranchDesign Sum.inl (by decide) + · simpa [testRows3] using + testCNF3.natDegree_univariateClearedParityTruncatedSum_branchParityRowSystem_le + (k := 2) (by decide) testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 + (fun _ => false) nonconstBranchDesign Sum.inl 4 + · simpa [testRows3] using + testCNF3.coeffNorm1_univariateClearedParityCoord_branchParityRowSystem_le + (k := 2) (W := 1) (ℓ := 4) (by decide) (by decide) ({0} : Finset (Fin 3)) freeVarEquiv3 + (fun _ => false) nonconstBranchDesign Sum.inl hE 0 + +/-- Checked client of `CNF.commonZeroCount_parityRowSystem_eq_sum_branch_emod_ediv_design` and +`CNF.numPassing_eq_sum_branch_emod_ediv_design` on `testCNF3` and `testRows3` (`S = {0}`): +the branch-summed `ReplacementDesign` recovery equals `4`. -/ +example : + ∑ b ∈ branchAssignments ({0} : Finset (Fin 3)), + ((univariateClearedParityTruncatedSum (univ : Finset (Fin testRows3.length)) + (testCNF3.branchParityRowSystem testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 b) + (commonDenom 1 1 : ℤ) 2 (branchPrecision (Fintype.card (Fin 3))) + (designReplacementScaledIntMap + (unitReplacementDesign + (testCNF3.branchParityRowNeutralScope testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 b)) + Sum.inl)).eval 0 / + clearedProductDenom (commonDenom 1 1 : ℤ) 2 (branchPrecision (Fintype.card (Fin 3))) + #(univ : Finset (Fin testRows3.length))) % + ((2 : ℤ) ^ branchPrecision (Fintype.card (Fin 3))) = 4 := by + have hrec := testCNF3.numPassing_eq_sum_branch_emod_ediv_design (k := 2) (by decide) testRows3 + ({0} : Finset (Fin 3)) freeVarEquiv3 + (fun b => testCNF3.branchParityRowNeutralScope testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 b) + (fun b => unitReplacementDesign + (testCNF3.branchParityRowNeutralScope testRows3 ({0} : Finset (Fin 3)) freeVarEquiv3 b)) + (fun _ => Sum.inl) + have hpass : numPassing testCNF3.violations testRows3 = 4 := by decide + rw [← hrec, hpass] + rfl + +/-- Explicit equivalence between `{1}` (`univ \ {0}` in `Fin 2`) and `Fin 1`. -/ +def freeVarEquiv2 : ↥((univ : Finset (Fin 2)) \ ({0} : Finset (Fin 2))) ≃ Fin 1 where + toFun _ := 0 + invFun _ := ⟨1, by decide⟩ + left_inv := by decide + right_inv := by decide + +/-- Concrete candidate tuple of length `P = 2` for `testCNF`. -/ +def testCand : Fin 2 → Fin testCNF.length × Bool := + fun _ => (⟨0, by decide⟩, false) + +/-- Concrete `3 * n`-step greedy row sequence starting from `testRows`. -/ +def testGreedySeq : ℕ → List (Fin testCNF.length → Bool) + | 0 => testRows + | i + 1 => testGreedySeq i ++ [testRow testCand] + +private theorem passesTests_testGreedySeq_imp_passesTests_testRows : + ∀ (i : ℕ) (σ : Fin 2 → Bool), + PassesTests testCNF.violations (testGreedySeq i) σ → + PassesTests testCNF.violations testRows σ + | 0, _, h => h + | i + 1, σ, h => + passesTests_testGreedySeq_imp_passesTests_testRows i σ + ((passesTests_append_singleton _ _ _ _).mp h).1 + +private theorem violations_eq_zero_of_passesTests_testRows : + ∀ σ : Fin 2 → Bool, PassesTests testCNF.violations testRows σ → testCNF.violations σ = 0 := by + decide + +private theorem commonZeroCount_testGreedySeq_step_le (i : ℕ) + (γ' : Fin 2 → Fin testCNF.length × Bool) : + commonZeroCount univ (testCNF.parityRowSystem (testGreedySeq i ++ [testRow testCand])) ≤ + commonZeroCount univ (testCNF.parityRowSystem (testGreedySeq i ++ [testRow γ'])) := by + rw [testCNF.commonZeroCount_parityRowSystem, testCNF.commonZeroCount_parityRowSystem] + apply card_le_card + intro σ hσ + rw [mem_filter] at hσ ⊢ + refine ⟨mem_univ σ, ?_⟩ + have hpasses_i := ((passesTests_append_singleton _ _ _ _).mp hσ.2).1 + have hzero := violations_eq_zero_of_passesTests_testRows σ + (passesTests_testGreedySeq_imp_passesTests_testRows i σ hpasses_i) + rw [passesTests_append_singleton] + refine ⟨hpasses_i, ?_⟩ + rw [hzero, dotProduct_zero] + rfl + +/-- Checked client of `CNF.modelCountOn_eq_sum_branch_emod_ediv_design_greedy`: recovers +`testCNF.modelCountOn Finset.univ = 1` via `unitReplacementDesign` without any auxiliary `θ` or +`hθ` hypotheses. -/ +example : + ∑ b ∈ branchAssignments ({0} : Finset (Fin 2)), + ((univariateClearedParityTruncatedSum + (univ : Finset (Fin (testGreedySeq (3 * Fintype.card (Fin 2))).length)) + (testCNF.branchParityRowSystem (testGreedySeq (3 * Fintype.card (Fin 2))) + ({0} : Finset (Fin 2)) freeVarEquiv2 b) + (commonDenom 1 1 : ℤ) 2 (branchPrecision (Fintype.card (Fin 2))) + (designReplacementScaledIntMap + (unitReplacementDesign + (testCNF.branchParityRowNeutralScope (testGreedySeq (3 * Fintype.card (Fin 2))) + ({0} : Finset (Fin 2)) freeVarEquiv2 b)) + Sum.inl)).eval 0 / + clearedProductDenom (commonDenom 1 1 : ℤ) 2 (branchPrecision (Fintype.card (Fin 2))) + #(univ : Finset (Fin (testGreedySeq (3 * Fintype.card (Fin 2))).length))) % + ((2 : ℤ) ^ branchPrecision (Fintype.card (Fin 2))) = 1 := by + have hrec := testCNF.modelCountOn_eq_sum_branch_emod_ediv_design_greedy + (k := 2) (P := 2) (by decide) (by decide) (by decide) testGreedySeq (by decide) + (fun _ => testCand) (fun _ => rfl) commonZeroCount_testGreedySeq_step_le + ({0} : Finset (Fin 2)) freeVarEquiv2 + (fun b => testCNF.branchParityRowNeutralScope (testGreedySeq (3 * Fintype.card (Fin 2))) + ({0} : Finset (Fin 2)) freeVarEquiv2 b) + (fun b => unitReplacementDesign + (testCNF.branchParityRowNeutralScope (testGreedySeq (3 * Fintype.card (Fin 2))) + ({0} : Finset (Fin 2)) freeVarEquiv2 b)) + (fun _ => Sum.inl) + have hcount : testCNF.modelCountOn Finset.univ = 1 := by decide + rw [← hrec, hcount] + rfl + +/-- Concrete check of `CNF.max_initialRowWeightBound_greedyRowListLength` at `B = 2, m = 8`. -/ +example : + max (CNF.initialRowWeightBound 2 8) (CNF.greedyRowListLength 2 8) = + 320000 * 2 ^ 2 * (3 + 1) ^ 3 := by + rw [CNF.max_initialRowWeightBound_greedyRowListLength] + decide + +/-- Empty CNF on `Fin 16384` used to witness +`CNF.exists_replacementDesign_branchParityRowNeutralScope` at `b₀ = 0`, `Q = 3`, `r = 2`, +`A₀ = 0`, `R = 2`, `τ = 1 / 2`. -/ +def emptyCNF16384 : CNF (Fin 16384) := [] + +/-- Equivalence between `↥(univ \ heavyVars univ univ ... 0)` and `Fin 16384` for `emptyCNF16384` +with `rows = []`. -/ +def emptyFreeVarEquiv16384 : + ↥((univ : Finset (Fin 16384)) \ + heavyVars univ (univ : Finset (Fin ([] : List (Fin emptyCNF16384.length → Bool)).length)) + (fun j => equationScope (emptyCNF16384.parityRowSystem [] j)) 0) ≃ Fin 16384 where + toFun v := v.1 + invFun v := ⟨v, by + rw [mem_sdiff] + refine ⟨mem_univ v, ?_⟩ + simp [heavyVars, varIncidence]⟩ + left_inv v := by ext; rfl + right_inv _ := rfl + +/-- Nonvacuous witness of `CNF.exists_replacementDesign_branchParityRowNeutralScope` and +single-branch recovery `CNF.emod_ediv_eval_zero_branchParityRowSystem_design_eq_commonZeroCount` on +`emptyCNF16384` (`commonZeroCount = 2 ^ 16384` at `ℓ = 16385`). -/ +theorem exists_design_and_recover_emptyCNF16384 : + ∃ D : ReplacementDesign + (emptyCNF16384.branchParityRowNeutralScope [] + (heavyVars univ (univ : Finset (Fin ([] : List (Fin emptyCNF16384.length → Bool)).length)) + (fun j => equationScope (emptyCNF16384.parityRowSystem [] j)) 0) + emptyFreeVarEquiv16384 (fun _ => false)) 3 2 0 2 (1 / 2), + ((univariateClearedParityTruncatedSum + (univ : Finset (Fin ([] : List (Fin emptyCNF16384.length → Bool)).length)) + (emptyCNF16384.branchParityRowSystem [] + (heavyVars univ + (univ : Finset (Fin ([] : List (Fin emptyCNF16384.length → Bool)).length)) + (fun j => equationScope (emptyCNF16384.parityRowSystem [] j)) 0) + emptyFreeVarEquiv16384 (fun _ => false)) + (commonDenom 3 2 : ℤ) 3 16385 (designReplacementScaledIntMap D Sum.inl)).eval 0 / + clearedProductDenom (commonDenom 3 2 : ℤ) 3 16385 + #(univ : Finset (Fin ([] : List (Fin emptyCNF16384.length → Bool)).length))) % + ((2 : ℤ) ^ 16385) = (2 : ℤ) ^ 16384 := by + have hcard : #(measurementIndices 3 2 0 2) = 10 := by + rw [card_measurementIndices_eq] + decide + have htail : binomTailProb 3 2 0 2 ≤ (1 / 2 : ℚ) / ((2 : ℚ) * ((0 + 1 : ℕ) : ℚ)) := by + rw [binomTailProb_eq_div 3 2 0 2 (by decide), hcard] + norm_num + have hI : ∀ a < 2 - 1, 0 + a ≤ 3 := by decide + have hbal₁_arith : 2 * (0 + 1 + max 1 (3 * (0 * 2 ^ 3))) * (2 ^ (2 * 3)) ^ 2 ≤ 0 + 16384 := by + decide + have hbal₂ : 2 * 3 * 2 ^ 2 ≤ 16384 := by decide + have hne := emptyCNF16384.exists_replacementDesign_branchParityRowNeutralScope + (k := 3) (W := 0) (b₀ := 0) (Q := 3) (r := 2) (A₀ := 0) (R := 2) (τ := 1 / 2) + (rows := []) (fun _ hc => nomatch hc) (fun _ hr => nomatch hr) + emptyFreeVarEquiv16384 (fun _ => false) + (by decide) (by decide) (by decide) (by decide) (by norm_num) + hI hbal₁_arith hbal₂ htail + obtain ⟨D⟩ := hne + refine ⟨D, ?_⟩ + have hrec := emptyCNF16384.emod_ediv_eval_zero_branchParityRowSystem_design_eq_commonZeroCount + (k := 3) (ℓ := 16385) (fun _ hc => nomatch hc) [] + (heavyVars univ (univ : Finset (Fin ([] : List (Fin emptyCNF16384.length → Bool)).length)) + (fun j => equationScope (emptyCNF16384.parityRowSystem [] j)) 0) + emptyFreeVarEquiv16384 (fun _ => false) D Sum.inl (by decide) + have hcount : + (commonZeroCount + (univ : Finset (Fin ([] : List (Fin emptyCNF16384.length → Bool)).length)) + (emptyCNF16384.branchParityRowSystem [] + (heavyVars univ (univ : Finset (Fin ([] : List (Fin emptyCNF16384.length → Bool)).length)) + (fun j => equationScope (emptyCNF16384.parityRowSystem [] j)) 0) + emptyFreeVarEquiv16384 (fun _ => false)) : ℤ) = (2 : ℤ) ^ 16384 := by + change + (commonZeroCount (∅ : Finset (Fin ([] : List (Fin emptyCNF16384.length → Bool)).length)) + (emptyCNF16384.branchParityRowSystem [] + (heavyVars univ (univ : Finset (Fin ([] : List (Fin emptyCNF16384.length → Bool)).length)) + (fun j => equationScope (emptyCNF16384.parityRowSystem [] j)) 0) + emptyFreeVarEquiv16384 (fun _ => false)) : ℤ) = (2 : ℤ) ^ 16384 + rw [commonZeroCount_empty, Fintype.card_fin, Nat.cast_pow, Nat.cast_ofNat] + exact hrec.trans hcount + +end ComplexityTests.SparseDesignCounter From e5962d60bf3933e9ec84e6cd4fce69e136f210a1 Mon Sep 17 00:00:00 2001 From: Samuel Schlesinger Date: Tue, 27 Oct 2026 05:19:23 +0000 Subject: [PATCH 6/7] feat(Sparsification): polynomial-time steps for runtime thresholds Generalize the certificates for the heavy-heart test, the choice, the test for sparseness, the step and the counting step to thresholds theta p read from a runtime parameter p, charging its encoding (the ...With theorems). The fixed-threshold certificates are now their constant-parameter specializations, with unchanged statements. Add the counting thresholds M ^ j for a base M given in binary at runtime, with the sparseness test and counting step they induce. Part of #1226. --- .../Sparsification/CountingPolyTime.lean | 74 +++++++- .../ClauseSet/Sparsification/PolyTime.lean | 159 ++++++++++++++---- .../SparsificationRuntimeThresholds.lean | 61 +++++++ 3 files changed, 251 insertions(+), 43 deletions(-) create mode 100644 ComplexityTests/SparsificationRuntimeThresholds.lean diff --git a/Complexity/Logics/Propositional/ClauseSet/Sparsification/CountingPolyTime.lean b/Complexity/Logics/Propositional/ClauseSet/Sparsification/CountingPolyTime.lean index b4041e3106..9843d82f9a 100644 --- a/Complexity/Logics/Propositional/ClauseSet/Sparsification/CountingPolyTime.lean +++ b/Complexity/Logics/Propositional/ClauseSet/Sparsification/CountingPolyTime.lean @@ -31,8 +31,14 @@ on the framed encoding of formulas, for any encoding of the variables [OpenAI202 * `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_removeTautologies`, `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_countingNormalize`: tautology removal and nontautological normalization. -* `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_countingNext`: the list-level counting - step. +* `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_countingNextWith`, + `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_countingNext`: the list-level counting + step, for thresholds read from a runtime parameter and for fixed thresholds. +* `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_countingThreshold_le_binary`, + `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_isSparse_countingThreshold`, + `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_countingNext_countingThreshold`: the + thresholds `countingThreshold M`, the test for sparseness and the step for a base `M` given in + binary at runtime. ## References @@ -83,18 +89,72 @@ theorem computableInPolyTime_countingNormalize : have hnorm := (computableInPolyTime_normalize encV).tidy exact .of_tidy (hnorm.comp hrem) +/-- A counting child of a branching node, for a supplied choice, is polynomial-time. -/ +private theorem computableTidilyInPolyTime_countingChild : + ComputableTidilyInPolyTime + (fun x : CNF V × ((ℕ × Clause V) × Bool) => countingChild x.1 x.2.1.1 x.2.1.2 x.2.2) + (encPair encF (encPair (encPair encUnary encC) encBool)) encF := by + have := (computableInPolyTime_reduce encV).tidy + have := (computableInPolyTime_dedupClauses encV).tidy + have := (computableInPolyTime_flower encV).tidy + unfold countingChild countingIntro petals negatedHeartUnits Std.Sat.Literal.negate + fun_prop + +/-- **The counting step for runtime thresholds** `θ p` is polynomial-time, charging the encoding of +the parameter `p`. -/ +theorem computableInPolyTime_countingNextWith {P : Type} {encP : P ↪ List Bool} + {θ : P → ℕ → ℕ} + (hθ : ComputableInPolyTime (fun x : P × ℕ × ℕ => decide (θ x.1 x.2.1 ≤ x.2.2)) + (encPair encP (encPair encUnary encUnary)) encBool) (k : ℕ) : + ComputableInPolyTime (fun x : P × (CNF V × Bool) => countingNext (θ x.1) k x.2.1 x.2.2) + (encPair encP (encPair encF encBool)) encF := by + refine .of_tidy ?_ + have hchild := computableTidilyInPolyTime_countingChild encV + have hc0 := (computableInPolyTime_choiceWith encV hθ k).tidy + have hchoice : ComputableTidilyInPolyTime + (fun x : P × (CNF V × Bool) => choice (θ x.1) k x.2.1) + (encPair encP (encPair encF encBool)) (encPair encUnary encC) := by + simpa only [Function.comp_def] using + hc0.comp ((fst encP (encPair encF encBool)).prod (snd encP (encPair encF encBool)).fst') + have h := hchild.comp ((snd encP (encPair encF encBool)).fst'.prod + (hchoice.prod (snd encP (encPair encF encBool)).snd')) + simpa only [Function.comp_def, countingNext] using h + /-- **The list-level counting-sparsification step is polynomial-time.** -/ theorem computableInPolyTime_countingNext {θ : ℕ → ℕ} (hθ : ComputableInPolyTime (fun p : ℕ × ℕ => decide (θ p.1 ≤ p.2)) (encPair encUnary encUnary) encBool) (k : ℕ) : ComputableInPolyTime (fun p : CNF V × Bool => countingNext θ k p.1 p.2) (encPair encF encBool) encF := by + have h := (computableInPolyTime_countingNextWith encV (P := ℕ) (encP := encUnary) + (θ := fun _ => θ) (computableInPolyTime_const_threshold hθ) k).tidy + exact .of_tidy (by + simpa only [Function.comp_def] using h.comp ((const (encMid := encUnary) 0).prod id')) + +/-! ### The counting thresholds for a runtime base -/ + +/-- Comparing the counting thresholds `countingThreshold M j = M ^ j` with a number is +polynomial-time for a runtime base `M` in binary, with `j` and the compared number in unary. -/ +theorem computableInPolyTime_countingThreshold_le_binary : + ComputableInPolyTime (fun x : ℕ × ℕ × ℕ => decide (countingThreshold x.1 x.2.1 ≤ x.2.2)) + (encPair encNat (encPair encUnary encUnary)) encBool := by refine .of_tidy ?_ - have := (computableInPolyTime_choice encV hθ k).tidy - have := (computableInPolyTime_reduce encV).tidy - have := (computableInPolyTime_dedupClauses encV).tidy - have := (computableInPolyTime_flower encV).tidy - unfold countingNext countingChild countingIntro petals negatedHeartUnits Std.Sat.Literal.negate + unfold countingThreshold fun_prop +/-- **Sparseness for a runtime base.** The test for sparseness against the thresholds +`countingThreshold M`, for a base `M` given in binary at runtime, is polynomial-time. -/ +theorem computableInPolyTime_isSparse_countingThreshold (k : ℕ) : + ComputableInPolyTime (fun x : ℕ × CNF V => isSparse (countingThreshold x.1) k x.2) + (encPair encNat encF) encBool := + computableInPolyTime_isSparseWith encV computableInPolyTime_countingThreshold_le_binary k + +/-- **The counting step for a runtime base.** The counting-sparsification step for the thresholds +`countingThreshold M`, for a base `M` given in binary at runtime, is polynomial-time. -/ +theorem computableInPolyTime_countingNext_countingThreshold (k : ℕ) : + ComputableInPolyTime + (fun x : ℕ × (CNF V × Bool) => countingNext (countingThreshold x.1) k x.2.1 x.2.2) + (encPair encNat (encPair encF encBool)) encF := + computableInPolyTime_countingNextWith encV computableInPolyTime_countingThreshold_le_binary k + end Cslib.Logic.PL.CNF.Sparsification diff --git a/Complexity/Logics/Propositional/ClauseSet/Sparsification/PolyTime.lean b/Complexity/Logics/Propositional/ClauseSet/Sparsification/PolyTime.lean index 9e1a3cd24e..820dcb5285 100644 --- a/Complexity/Logics/Propositional/ClauseSet/Sparsification/PolyTime.lean +++ b/Complexity/Logics/Propositional/ClauseSet/Sparsification/PolyTime.lean @@ -28,11 +28,21 @@ number of candidate hearts; the thresholds enter only through comparisons `θ j themselves be polynomial-time in unary. They are for the explicit thresholds (`Cslib.Logic.PL.ClauseSet.Sparsification.computableInPolyTime_threshold_le`). +The thresholds may also be read from a runtime parameter `p`, as `θ p`: the `…With` certificates +take the parameter as part of the input and charge its encoding, so one machine serves every +parameter value. The certificates for fixed thresholds are their specializations to a constant +parameter. + ## Main results -* `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_next`: the step. -* `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_isSparse`: the test for - sparseness. +* `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_nextWith`, + `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_next`: the step, for runtime and fixed + thresholds. +* `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_isSparseWith`, + `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_isSparse`: the test for sparseness. +* `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_heavyWith`, + `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_choiceWith`: the heavy-heart test and the + choice, for runtime thresholds. * `Cslib.Logic.PL.CNF.Sparsification.computableInPolyTime_normalize`: normalization. * `Cslib.Logic.PL.ClauseSet.Sparsification.computableInPolyTime_threshold_le`: comparisons with the explicit thresholds. @@ -95,25 +105,48 @@ theorem computableInPolyTime_flower : unfold flower subclause fun_prop -/-- Testing whether a candidate `(c, H)` is heavy is polynomial-time. -/ -theorem computableInPolyTime_heavy {θ : ℕ → ℕ} - (hθ : ComputableInPolyTime (fun p : ℕ × ℕ => decide (θ p.1 ≤ p.2)) - (encPair encUnary encUnary) encBool) : - ComputableInPolyTime (fun p : CNF V × (ℕ × Clause V) => heavy θ p.1 p.2) - (encPair encF (encPair encUnary encC)) encBool := by +/-- **Testing a candidate against runtime thresholds.** Testing whether a candidate `(c, H)` is +heavy for thresholds `θ p` read from a runtime parameter `p` is polynomial-time, charging the +encoding of `p`. -/ +theorem computableInPolyTime_heavyWith {P : Type} {encP : P ↪ List Bool} {θ : P → ℕ → ℕ} + (hθ : ComputableInPolyTime (fun x : P × ℕ × ℕ => decide (θ x.1 x.2.1 ≤ x.2.2)) + (encPair encP (encPair encUnary encUnary)) encBool) : + ComputableInPolyTime (fun x : P × (CNF V × (ℕ × Clause V)) => heavy (θ x.1) x.2.1 x.2.2) + (encPair encP (encPair encF (encPair encUnary encC))) encBool := by have hθ := hθ.tidy refine .of_tidy ?_ have := (computableInPolyTime_flower encV).tidy - have h₁ : ComputableTidilyInPolyTime (fun p : CNF V × (ℕ × Clause V) => - decide (1 ≤ p.2.2.length) && decide (p.2.2.length < p.2.1)) - (encPair encF (encPair encUnary encC)) encBool := by + have h₁ : ComputableTidilyInPolyTime (fun x : P × (CNF V × (ℕ × Clause V)) => + decide (1 ≤ x.2.2.2.length) && decide (x.2.2.2.length < x.2.2.1)) + (encPair encP (encPair encF (encPair encUnary encC))) encBool := by fun_prop - have h₂ : ComputableTidilyInPolyTime (fun p : CNF V × (ℕ × Clause V) => - (p.2.1 - p.2.2.length, (flower p.1 p.2.1 p.2.2).length)) - (encPair encF (encPair encUnary encC)) (encPair encUnary encUnary) := by + have h₂ : ComputableTidilyInPolyTime (fun x : P × (CNF V × (ℕ × Clause V)) => + (x.1, (x.2.2.1 - x.2.2.2.length, (flower x.2.1 x.2.2.1 x.2.2.2).length))) + (encPair encP (encPair encF (encPair encUnary encC))) + (encPair encP (encPair encUnary encUnary)) := by fun_prop exact bool_and.comp (h₁.prod (hθ.comp h₂)) +/-- A fixed threshold comparison, read through a constant runtime parameter. This specializes the +`…With` certificates to fixed thresholds. -/ +theorem computableInPolyTime_const_threshold {θ : ℕ → ℕ} + (hθ : ComputableInPolyTime (fun p : ℕ × ℕ => decide (θ p.1 ≤ p.2)) + (encPair encUnary encUnary) encBool) : + ComputableInPolyTime (fun x : ℕ × ℕ × ℕ => decide ((fun _ : ℕ => θ) x.1 x.2.1 ≤ x.2.2)) + (encPair encUnary (encPair encUnary encUnary)) encBool := + .of_tidy (hθ.tidy.comp (snd encUnary (encPair encUnary encUnary))) + +/-- Testing whether a candidate `(c, H)` is heavy is polynomial-time. -/ +theorem computableInPolyTime_heavy {θ : ℕ → ℕ} + (hθ : ComputableInPolyTime (fun p : ℕ × ℕ => decide (θ p.1 ≤ p.2)) + (encPair encUnary encUnary) encBool) : + ComputableInPolyTime (fun p : CNF V × (ℕ × Clause V) => heavy θ p.1 p.2) + (encPair encF (encPair encUnary encC)) encBool := by + have h := (computableInPolyTime_heavyWith encV (P := ℕ) (encP := encUnary) (θ := fun _ => θ) + (computableInPolyTime_const_threshold hθ)).tidy + exact .of_tidy (by + simpa only [Function.comp_def] using h.comp ((const (encMid := encUnary) 0).prod id')) + omit [DecidableEq V] in /-- Listing the candidate pairs `(c, H)` of a formula is polynomial-time. -/ theorem computableInPolyTime_candidates (k : ℕ) : @@ -125,37 +158,93 @@ theorem computableInPolyTime_candidates (k : ℕ) : unfold candidates fun_prop -/-- The computed choice is polynomial-time. -/ -theorem computableInPolyTime_choice {θ : ℕ → ℕ} - (hθ : ComputableInPolyTime (fun p : ℕ × ℕ => decide (θ p.1 ≤ p.2)) - (encPair encUnary encUnary) encBool) (k : ℕ) : - ComputableInPolyTime (choice (V := V) θ k) encF (encPair encUnary encC) := by +/-- **The choice for runtime thresholds** is polynomial-time, charging the encoding of the +parameter. -/ +theorem computableInPolyTime_choiceWith {P : Type} {encP : P ↪ List Bool} {θ : P → ℕ → ℕ} + (hθ : ComputableInPolyTime (fun x : P × ℕ × ℕ => decide (θ x.1 x.2.1 ≤ x.2.2)) + (encPair encP (encPair encUnary encUnary)) encBool) (k : ℕ) : + ComputableInPolyTime (fun x : P × CNF V => choice (θ x.1) k x.2) + (encPair encP encF) (encPair encUnary encC) := by refine .of_tidy ?_ - have hc : ComputableTidilyInPolyTime (fun ψ : CNF V => (candidates k ψ).filter (heavy θ ψ)) - encF (encList (encPair encUnary encC)) := - filterWithInput (computableInPolyTime_candidates encV k).tidy - (computableInPolyTime_heavy encV hθ).tidy + have hheavy := (computableInPolyTime_heavyWith encV hθ).tidy + have hc : ComputableTidilyInPolyTime + (fun x : P × CNF V => (candidates k x.2).filter (heavy (θ x.1) x.2)) + (encPair encP encF) (encList (encPair encUnary encC)) := + filterWithInput ((computableInPolyTime_candidates encV k).tidy.comp (snd _ _)) + (hheavy.comp ((fst _ _).fst'.prod ((fst _ _).snd'.prod (snd _ _)))) have hr : ComputableTidilyInPolyTime (fun x : ℕ × Clause V => rank k x) (encPair encUnary encC) encUnary := by unfold rank fun_prop - have hb : ComputableTidilyInPolyTime (fun ψ : CNF V => + have hb : ComputableTidilyInPolyTime (fun x : P × CNF V => (List.range ((k + 1) * (k + 1) + 1)).flatMap fun r => - ((candidates k ψ).filter (heavy θ ψ)).filter fun x => rank k x == r) encF - (encList (encPair encUnary encC)) := + ((candidates k x.2).filter (heavy (θ x.1) x.2)).filter fun x => rank k x == r) + (encPair encP encF) (encList (encPair encUnary encC)) := flatMapConst _ fun r _ => filterWithInput hc (unary_beq (hr.comp (snd _ _)) (const r)) exact optionGetDWithInput ((head? (encPair encUnary encC) (encL := encList (encPair encUnary encC))).comp hb) (const (0, [])) +/-- The computed choice is polynomial-time. -/ +theorem computableInPolyTime_choice {θ : ℕ → ℕ} + (hθ : ComputableInPolyTime (fun p : ℕ × ℕ => decide (θ p.1 ≤ p.2)) + (encPair encUnary encUnary) encBool) (k : ℕ) : + ComputableInPolyTime (choice (V := V) θ k) encF (encPair encUnary encC) := by + have h := (computableInPolyTime_choiceWith encV (P := ℕ) (encP := encUnary) (θ := fun _ => θ) + (computableInPolyTime_const_threshold hθ) k).tidy + exact .of_tidy (by + simpa only [Function.comp_def] using h.comp ((const (encMid := encUnary) 0).prod id')) + +/-- **The test for sparseness against runtime thresholds** is polynomial-time, charging the +encoding of the parameter. -/ +theorem computableInPolyTime_isSparseWith {P : Type} {encP : P ↪ List Bool} {θ : P → ℕ → ℕ} + (hθ : ComputableInPolyTime (fun x : P × ℕ × ℕ => decide (θ x.1 x.2.1 ≤ x.2.2)) + (encPair encP (encPair encUnary encUnary)) encBool) (k : ℕ) : + ComputableInPolyTime (fun x : P × CNF V => isSparse (θ x.1) k x.2) + (encPair encP encF) encBool := by + refine .of_tidy ?_ + have hheavy := (computableInPolyTime_heavyWith encV hθ).tidy + unfold isSparse + exact bool_not.comp (anyWithInput ((computableInPolyTime_candidates encV k).tidy.comp (snd _ _)) + (hheavy.comp ((fst _ _).fst'.prod ((fst _ _).snd'.prod (snd _ _))))) + /-- The list-level test for sparseness is polynomial-time. -/ theorem computableInPolyTime_isSparse {θ : ℕ → ℕ} (hθ : ComputableInPolyTime (fun p : ℕ × ℕ => decide (θ p.1 ≤ p.2)) (encPair encUnary encUnary) encBool) (k : ℕ) : ComputableInPolyTime (isSparse (V := V) θ k) encF encBool := by + have h := (computableInPolyTime_isSparseWith encV (P := ℕ) (encP := encUnary) (θ := fun _ => θ) + (computableInPolyTime_const_threshold hθ) k).tidy + exact .of_tidy (by + simpa only [Function.comp_def] using h.comp ((const (encMid := encUnary) 0).prod id')) + +/-- A child of a branching node, for a supplied choice, is polynomial-time. -/ +private theorem computableTidilyInPolyTime_child : + ComputableTidilyInPolyTime + (fun x : CNF V × ((ℕ × Clause V) × Bool) => child x.1 x.2.1.1 x.2.1.2 x.2.2) + (encPair encF (encPair (encPair encUnary encC) encBool)) encF := by + have := (computableInPolyTime_reduce encV).tidy + have := (computableInPolyTime_flower encV).tidy + unfold child intro petals + fun_prop + +/-- **The list-level step for runtime thresholds** is polynomial-time, charging the encoding of the +parameter. -/ +theorem computableInPolyTime_nextWith {P : Type} {encP : P ↪ List Bool} {θ : P → ℕ → ℕ} + (hθ : ComputableInPolyTime (fun x : P × ℕ × ℕ => decide (θ x.1 x.2.1 ≤ x.2.2)) + (encPair encP (encPair encUnary encUnary)) encBool) (k : ℕ) : + ComputableInPolyTime (fun x : P × (CNF V × Bool) => next (θ x.1) k x.2.1 x.2.2) + (encPair encP (encPair encF encBool)) encF := by refine .of_tidy ?_ - unfold isSparse - exact bool_not.comp (anyWithInput (computableInPolyTime_candidates encV k).tidy - (computableInPolyTime_heavy encV hθ).tidy) + have hchild := computableTidilyInPolyTime_child encV + have hc0 := (computableInPolyTime_choiceWith encV hθ k).tidy + have hchoice : ComputableTidilyInPolyTime + (fun x : P × (CNF V × Bool) => choice (θ x.1) k x.2.1) + (encPair encP (encPair encF encBool)) (encPair encUnary encC) := by + simpa only [Function.comp_def] using + hc0.comp ((fst encP (encPair encF encBool)).prod (snd encP (encPair encF encBool)).fst') + have h := hchild.comp ((snd encP (encPair encF encBool)).fst'.prod + (hchoice.prod (snd encP (encPair encF encBool)).snd')) + simpa only [Function.comp_def, next] using h /-- **The list-level step is polynomial-time.** -/ theorem computableInPolyTime_next {θ : ℕ → ℕ} @@ -163,12 +252,10 @@ theorem computableInPolyTime_next {θ : ℕ → ℕ} (encPair encUnary encUnary) encBool) (k : ℕ) : ComputableInPolyTime (fun p : CNF V × Bool => next θ k p.1 p.2) (encPair encF encBool) encF := by - refine .of_tidy ?_ - have := (computableInPolyTime_choice encV hθ k).tidy - have := (computableInPolyTime_reduce encV).tidy - have := (computableInPolyTime_flower encV).tidy - unfold next child intro petals - fun_prop + have h := (computableInPolyTime_nextWith encV (P := ℕ) (encP := encUnary) (θ := fun _ => θ) + (computableInPolyTime_const_threshold hθ) k).tidy + exact .of_tidy (by + simpa only [Function.comp_def] using h.comp ((const (encMid := encUnary) 0).prod id')) /-- Normalization is polynomial-time. -/ theorem computableInPolyTime_normalize : diff --git a/ComplexityTests/SparsificationRuntimeThresholds.lean b/ComplexityTests/SparsificationRuntimeThresholds.lean new file mode 100644 index 0000000000..6375562957 --- /dev/null +++ b/ComplexityTests/SparsificationRuntimeThresholds.lean @@ -0,0 +1,61 @@ +/- +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.Logics.Propositional.ClauseSet.Sparsification.CountingPolyTime + +/-! +# Tests for sparsification certificates with runtime thresholds +-/ + +namespace ComplexityTests.SparsificationRuntimeThresholds + +open Cslib.Logic.PL Cslib.Logic.PL.CNF.Sparsification Turing.MultiTapeTM + +/-- Two clauses sharing the heart `x₁`. -/ +abbrev ψ : CNF ℕ := [[(1, true), (2, true)], [(1, true), (3, true)]] + +/-- The runtime parameter matters: with base `1` the heart `x₁` is heavy, with base `100` the +formula is sparse. -/ +example : isSparse (countingThreshold 1) 2 ψ = false := by decide + +example : isSparse (countingThreshold 100) 2 ψ = true := by decide + +/-- The two counting steps for base `1` add the heart, or the petals and the negated heart. -/ +example : countingNext (countingThreshold 1) 2 ψ false = [[(1, true)]] := by decide + +/-- Width zero has no candidate hearts, and the empty formula is sparse, for every base. -/ +example (M : ℕ) : isSparse (countingThreshold M) 0 ψ = true := by + simp [isSparse, candidates, hearts, heavy] + +example (M : ℕ) : isSparse (countingThreshold M) 2 ([] : CNF ℕ) = true := by + simp [isSparse, candidates] + +/-- One machine tests sparseness for every runtime base, given in binary. -/ +example : ComputableInPolyTime (fun x : ℕ × CNF ℕ => isSparse (countingThreshold x.1) 2 x.2) + (encPair encNat (encList (encList (encPair encNat encBool)))) encBool := + computableInPolyTime_isSparse_countingThreshold encNat 2 + +/-- One machine takes the counting step for every runtime base, given in binary. -/ +example : ComputableInPolyTime + (fun x : ℕ × (CNF ℕ × Bool) => countingNext (countingThreshold x.1) 2 x.2.1 x.2.2) + (encPair encNat (encPair (encList (encList (encPair encNat encBool))) encBool)) + (encList (encList (encPair encNat encBool))) := + computableInPolyTime_countingNext_countingThreshold encNat 2 + +/-- A runtime parameter of another type: a pair `(M, a)` selecting the thresholds `M ^ j + a`. -/ +example : ComputableInPolyTime + (fun x : (ℕ × ℕ) × CNF ℕ => isSparse (fun j => x.1.1 ^ j + x.1.2) 2 x.2) + (encPair (encPair encNat encNat) (encList (encList (encPair encNat encBool)))) encBool := by + refine computableInPolyTime_isSparseWith encNat (θ := fun (p : ℕ × ℕ) j => p.1 ^ j + p.2) ?_ 2 + refine .of_tidy ?_ + fun_prop + +/-- The fixed-threshold certificates remain available for their existing clients. -/ +example (M : ℕ) : ComputableInPolyTime (isSparse (V := ℕ) (countingThreshold M) 2) + (encList (encList (encPair encNat encBool))) encBool := + computableInPolyTime_isSparse encNat (computableInPolyTime_countingThreshold_le M) 2 + +end ComplexityTests.SparsificationRuntimeThresholds From da091329b6dc131f5fd9fe7c313e56c6692cbbd8 Mon Sep 17 00:00:00 2001 From: Samuel Schlesinger Date: Tue, 27 Oct 2026 06:29:36 +0000 Subject: [PATCH 7/7] test(Sparsification): replace runtime-threshold certificate mirrors by a composition --- .../SparsificationRuntimeThresholds.lean | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/ComplexityTests/SparsificationRuntimeThresholds.lean b/ComplexityTests/SparsificationRuntimeThresholds.lean index 6375562957..f21aea3253 100644 --- a/ComplexityTests/SparsificationRuntimeThresholds.lean +++ b/ComplexityTests/SparsificationRuntimeThresholds.lean @@ -12,7 +12,7 @@ import Complexity.Logics.Propositional.ClauseSet.Sparsification.CountingPolyTime namespace ComplexityTests.SparsificationRuntimeThresholds -open Cslib.Logic.PL Cslib.Logic.PL.CNF.Sparsification Turing.MultiTapeTM +open Cslib.Logic.PL Cslib.Logic.PL.CNF.Sparsification Turing.MultiTapeTM ComputableTidilyInPolyTime /-- Two clauses sharing the heart `x₁`. -/ abbrev ψ : CNF ℕ := [[(1, true), (2, true)], [(1, true), (3, true)]] @@ -33,17 +33,27 @@ example (M : ℕ) : isSparse (countingThreshold M) 0 ψ = true := by example (M : ℕ) : isSparse (countingThreshold M) 2 ([] : CNF ℕ) = true := by simp [isSparse, candidates] -/-- One machine tests sparseness for every runtime base, given in binary. -/ -example : ComputableInPolyTime (fun x : ℕ × CNF ℕ => isSparse (countingThreshold x.1) 2 x.2) - (encPair encNat (encList (encList (encPair encNat encBool)))) encBool := - computableInPolyTime_isSparse_countingThreshold encNat 2 - -/-- One machine takes the counting step for every runtime base, given in binary. -/ +/-- A composition with a computed base: one machine branches on a formula with base `M` and then +tests the resulting child for sparseness with base `2 * M`, for every runtime `M` in binary. -/ example : ComputableInPolyTime - (fun x : ℕ × (CNF ℕ × Bool) => countingNext (countingThreshold x.1) 2 x.2.1 x.2.2) - (encPair encNat (encPair (encList (encList (encPair encNat encBool))) encBool)) - (encList (encList (encPair encNat encBool))) := - computableInPolyTime_countingNext_countingThreshold encNat 2 + (fun x : ℕ × CNF ℕ => + isSparse (countingThreshold (2 * x.1)) 2 (countingNext (countingThreshold x.1) 2 x.2 true)) + (encPair encNat (encList (encList (encPair encNat encBool)))) encBool := by + have hs := (computableInPolyTime_isSparse_countingThreshold encNat 2).tidy + have hn := (computableInPolyTime_countingNext_countingThreshold encNat 2).tidy + have hchild : ComputableTidilyInPolyTime + (fun x : ℕ × CNF ℕ => countingNext (countingThreshold x.1) 2 x.2 true) + (encPair encNat (encList (encList (encPair encNat encBool)))) + (encList (encList (encPair encNat encBool))) := + by + simpa only [Function.comp_def] using hn.comp + ((fst encNat (encList (encList (encPair encNat encBool)))).prod + ((snd encNat (encList (encList (encPair encNat encBool)))).prod + (const (encMid := encBool) true))) + have hbase : ComputableTidilyInPolyTime (fun x : ℕ × CNF ℕ => 2 * x.1) + (encPair encNat (encList (encList (encPair encNat encBool)))) encNat := by + fun_prop + exact .of_tidy (by simpa only [Function.comp_def] using hs.comp (hbase.prod hchild)) /-- A runtime parameter of another type: a pair `(M, a)` selecting the thresholds `M ^ j + a`. -/ example : ComputableInPolyTime @@ -53,9 +63,4 @@ example : ComputableInPolyTime refine .of_tidy ?_ fun_prop -/-- The fixed-threshold certificates remain available for their existing clients. -/ -example (M : ℕ) : ComputableInPolyTime (isSparse (V := ℕ) (countingThreshold M) 2) - (encList (encList (encPair encNat encBool))) encBool := - computableInPolyTime_isSparse encNat (computableInPolyTime_countingThreshold_le M) 2 - end ComplexityTests.SparsificationRuntimeThresholds