From 1dee1ecc2f422a8fe52a0dd2fd1d98c1dcd79578 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 12:16:05 -0400 Subject: [PATCH 1/8] feat(Circuit/Boolean): Tseitin formula of De Morgan gate lists (#1539 M3a) Uniform 3-literal Tseitin clauses for each De Morgan gate, the Tseitin formula of a gate list, and satisfiability with a fixed input prefix and output wire. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Circuit/Boolean/Tseitin.lean | 330 ++++++++++++++++++ ComplexityTests/CircuitTseitin.lean | 58 +++ 3 files changed, 389 insertions(+) create mode 100644 Complexity/Computability/Circuit/Boolean/Tseitin.lean create mode 100644 ComplexityTests/CircuitTseitin.lean diff --git a/Complexity.lean b/Complexity.lean index 779f199e6..d4ed7bed7 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -835,6 +835,7 @@ public import Complexity.Computability.Circuit.Boolean.ThresholdNC1 public import Complexity.Computability.Circuit.Boolean.TopDown public import Complexity.Computability.Circuit.Boolean.Tournament public import Complexity.Computability.Circuit.Boolean.Tribes +public import Complexity.Computability.Circuit.Boolean.Tseitin public import Complexity.Computability.Circuit.Boolean.Unbounded public import Complexity.Computability.Circuit.Boolean.Unbounded.Restriction public import Complexity.Computability.Circuit.Boolean.WeightedThreshold diff --git a/Complexity/Computability/Circuit/Boolean/Tseitin.lean b/Complexity/Computability/Circuit/Boolean/Tseitin.lean new file mode 100644 index 000000000..e0304e4b9 --- /dev/null +++ b/Complexity/Computability/Circuit/Boolean/Tseitin.lean @@ -0,0 +1,330 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Boolean.EvaluationTime +public import Complexity.Logics.Propositional.CNF +import Mathlib.Data.List.GetD + +/-! +# The Tseitin formula of a De Morgan circuit + +Tseitin's encoding turns a circuit into a formula in conjunctive normal form with one variable +per wire [Tseitin][Tseitin1983], [Arora and Barak, Section 6.1][AroraBarak09]. A gate +`w = op(a, b)` contributes the clauses `tseitinClause op w a b`, which hold together exactly when +`w` carries the value of the gate (`forall_eval_tseitinClause_iff`). The clauses of the gates of a +De Morgan gate list (`Cslib.Circuits.Program.code`) form `tseitinCNF n gates`, with gate `g` at +wire `n + g`; an assignment satisfies it exactly when it is the evaluation of the list on its own +values at the inputs (`eval_tseitinCNF_eq_true_iff_evalCode`). + +`tseitinSatCNF x n gates o` adds unit clauses that fix the first inputs to the bits of `x` and +the wire `o` to `true`. It is satisfiable exactly when some input extending `x` makes wire `o` +true (`satisfiable_tseitinSatCNF_iff`); for `x = []` and the output of a circuit, this is circuit +satisfiability (`satisfiable_tseitinSatCNF_circuit_iff`). + +The formula is laid out uniformly: every gate has exactly three clauses, and every clause has +exactly three literals (`length_of_mem_tseitinSatCNF`), shorter clauses being padded by repeating a +literal. Clause `j` of a gate depends only on its operation, on `j` and on the variables of the +gate, so a circuit that computes a clause from its number (a succinct description of the formula) +has a fixed output width. The frontier compiler (`Cslib.Circuits.Frontier.Compiler.toCNF`) +instead numbers only the reachable wires, which preserves the number of models but is not +uniform. + +## Main definitions + +* `Cslib.Circuits.Boolean.Op.value`: the value of a De Morgan gate on its first two arguments. +* `Cslib.Circuits.Boolean.tseitinClause`: the three clauses of a gate. +* `Cslib.Circuits.Boolean.tseitinCNF`: the clauses of a gate list. +* `Cslib.Circuits.Boolean.tseitinSatCNF`: the clauses of a gate list, with unit clauses fixing an + input prefix and an output wire. + +## Main results + +* `Cslib.Circuits.Boolean.forall_eval_tseitinClause_iff`: the semantics of the clauses of a gate. +* `Cslib.Circuits.Boolean.eval_tseitinCNF_eq_true_iff`, + `Cslib.Circuits.Boolean.eval_tseitinCNF_eq_true_iff_evalCode`: the assignments satisfying the + Tseitin formula are those consistent with every gate, which are the evaluations of the list. +* `Cslib.Circuits.Boolean.satisfiable_tseitinSatCNF_iff`: satisfiability with a fixed input prefix. +* `Cslib.Circuits.Boolean.satisfiable_tseitinSatCNF_circuit_iff`: the same for circuits. +* `Cslib.Circuits.Boolean.length_of_mem_tseitinSatCNF`, + `Cslib.Circuits.Boolean.length_tseitinSatCNF`: every clause has three literals, and there are + `1 + x.length + 3 * gates.length` clauses. + +## References + +* [G. S. Tseitin, *On the complexity of derivation in propositional calculus*][Tseitin1983] +* [S. Arora, B. Barak, *Computational Complexity: A Modern Approach*, Section 6.1][AroraBarak09] +-/ + +@[expose] public section + +namespace Cslib.Circuits.Boolean + +open Cslib.Logic.PL + +variable {V : Type*} + +/-! ### The clauses of a gate -/ + +/-- The value of a De Morgan operation whose first two arguments carry `a` and `b`; arguments +beyond its arity are ignored. -/ +def Op.value : Op → Bool → Bool → Bool + | .const v, _, _ => v + | .not, a, _ => !a + | .and, a, b => a && b + | .or, a, b => a || b + +/-- The value of a gate does not depend on the arguments beyond its arity. -/ +theorem Op.value_congr (op : Op) {a a' b b' : Bool} (ha : 1 ≤ signature.Arity op → a = a') + (hb : 2 ≤ signature.Arity op → b = b') : op.value a b = op.value a' b' := by + cases op with + | const v => rfl + | not => simp only [Op.value, ha (by decide)] + | and => simp only [Op.value, ha (by decide), hb (by decide)] + | or => simp only [Op.value, ha (by decide), hb (by decide)] + +/-- A gate code with as many arguments as its arity computes its operation on the values of its +first two listed arguments. -/ +theorem interpretation_eq_value (gate : Op × List ℕ) (h : gate.2.length = signature.Arity gate.1) + (f : ℕ → Bool) (d : Bool) : + interpretation gate.1 (fun i => gate.2[i.val]?.elim d f) = + gate.1.value (f (gate.2.getD 0 0)) (f (gate.2.getD 1 0)) := by + obtain ⟨op, args⟩ := gate + cases op <;> rcases args with _ | ⟨a, _ | ⟨b, _ | ⟨c, args⟩⟩⟩ <;> + simp only [List.length_nil, List.length_cons] at h <;> first | rfl | omega + +/-- **The Tseitin clauses of a gate** `w = op(a, b)`: three clauses of exactly three literals, +which hold together exactly when `w` carries the value of the gate +(`forall_eval_tseitinClause_iff`). A constant mentions only `w`, and a negation only `w` and +`a`; shorter clauses are padded by repeating a literal. -/ +def tseitinClause (op : Op) (w a b : V) (j : Fin 3) : Clause V := + match op, j.val with + | .const v, _ => [(w, v), (w, v), (w, v)] + | .not, 1 => [(w, false), (a, false), (a, false)] + | .not, _ => [(w, true), (a, true), (a, true)] + | .and, 0 => [(w, false), (a, true), (a, true)] + | .and, 1 => [(w, false), (b, true), (b, true)] + | .and, _ => [(w, true), (a, false), (b, false)] + | .or, 0 => [(w, true), (a, false), (a, false)] + | .or, 1 => [(w, true), (b, false), (b, false)] + | .or, _ => [(w, false), (a, true), (b, true)] + +/-- Every clause of a gate has three literals. -/ +@[simp] theorem length_tseitinClause (op : Op) (w a b : V) (j : Fin 3) : + (tseitinClause op w a b j).length = 3 := by + fin_cases j <;> cases op <;> rfl + +/-- **The clauses of a gate hold** exactly when its output carries its value. -/ +theorem forall_eval_tseitinClause_iff (op : Op) (w a b : V) (σ : V → Bool) : + (∀ j, (tseitinClause op w a b j).eval σ = true) ↔ σ w = op.value (σ a) (σ b) := by + have h₃ : (∀ j, (tseitinClause op w a b j).eval σ = true) ↔ + (tseitinClause op w a b 0).eval σ = true ∧ (tseitinClause op w a b 1).eval σ = true ∧ + (tseitinClause op w a b 2).eval σ = true := + ⟨fun h => ⟨h 0, h 1, h 2⟩, fun ⟨h₀, h₁, h₂⟩ j => by fin_cases j <;> assumption⟩ + rw [h₃] + rcases op with v | _ | _ | _ <;> + simp only [tseitinClause, Fin.isValue, Fin.val_zero, Fin.val_one, Fin.val_two, + Clause.eval_cons, Clause.eval_nil, Literal.eval_mk, Op.value] <;> + cases σ w <;> cases σ a <;> cases σ b <;> (try cases v) <;> decide + +/-- Relabelling the clauses of a gate relabels its variables. -/ +@[simp] theorem relabel_tseitinClause {W : Type*} (f : V → W) (op : Op) (w a b : V) (j : Fin 3) : + (tseitinClause op w a b j).relabel f = tseitinClause op (f w) (f a) (f b) j := by + fin_cases j <;> cases op <;> rfl + +/-! ### The Tseitin formula of a gate list -/ + +/-- The clauses of a gate code `gate` at the wire `w`, reading its first two listed arguments. -/ +def gateClauses (w : ℕ) (gate : Op × List ℕ) : CNF ℕ := + List.ofFn (tseitinClause gate.1 w (gate.2.getD 0 0) (gate.2.getD 1 0)) + +/-- **The Tseitin formula of a De Morgan gate list** on `n` inputs: the clauses of gate `g` say +that wire `n + g` carries its value. -/ +def tseitinCNF (n : ℕ) (gates : List (Op × List ℕ)) : CNF ℕ := + (List.finRange gates.length).flatMap fun g : Fin gates.length => gateClauses (n + g) gates[g] + +/-- The clauses of a gate code hold exactly when its wire carries its value. -/ +theorem eval_gateClauses_eq_true_iff (w : ℕ) (gate : Op × List ℕ) (σ : ℕ → Bool) : + (gateClauses w gate).eval σ = true ↔ + σ w = gate.1.value (σ (gate.2.getD 0 0)) (σ (gate.2.getD 1 0)) := by + rw [← forall_eval_tseitinClause_iff, gateClauses, CNF.eval_eq_true_iff] + simp only [List.mem_ofFn, forall_exists_index, forall_apply_eq_imp_iff] + +/-- **An assignment satisfies the Tseitin formula** exactly when every gate wire carries the value +of its gate. -/ +theorem eval_tseitinCNF_eq_true_iff (n : ℕ) (gates : List (Op × List ℕ)) (σ : ℕ → Bool) : + (tseitinCNF n gates).eval σ = true ↔ ∀ g (hg : g < gates.length), + σ (n + g) = gates[g].1.value (σ (gates[g].2.getD 0 0)) (σ (gates[g].2.getD 1 0)) := by + simp only [tseitinCNF, CNF.eval_flatMap_eq_true_iff, List.mem_finRange, true_implies, + eval_gateClauses_eq_true_iff] + exact ⟨fun h g hg => h ⟨g, hg⟩, fun h g => h g g.isLt⟩ + +/-- The listed arguments of a well-formed gate within its arity read earlier wires. -/ +private theorem getD_lt_of_isCodeFrom {n : ℕ} {gates : List (Op × List ℕ)} + (h : Program.IsCodeFrom signature n 0 gates) {g : ℕ} (hg : g < gates.length) : + (1 ≤ signature.Arity gates[g].1 → gates[g].2.getD 0 0 < n + g) ∧ + (2 ≤ signature.Arity gates[g].1 → gates[g].2.getD 1 0 < n + g) := by + obtain ⟨hlen, hlt⟩ := h.getElem g hg + refine ⟨fun h₁ => ?_, fun h₂ => ?_⟩ + · have h₀ : 0 < gates[g].2.length := by omega + rw [List.getD_eq_getElem _ _ h₀] + simpa using hlt _ (List.getElem_mem h₀) + · have h₀ : 1 < gates[g].2.length := by omega + rw [List.getD_eq_getElem _ _ h₀] + simpa using hlt _ (List.getElem_mem h₀) + +/-- **The satisfying assignments of the Tseitin formula** of a well-formed gate list are its +evaluations: every wire carries the value that the list computes from the inputs. -/ +theorem eval_tseitinCNF_eq_true_iff_evalCode {n : ℕ} {gates : List (Op × List ℕ)} + (h : Program.IsCodeFrom signature n 0 gates) (σ : ℕ → Bool) : + (tseitinCNF n gates).eval σ = true ↔ ∀ w < n + gates.length, σ w = + (evalCode interpretation false (List.ofFn fun i : Fin n => σ i) gates).getD w false := by + rw [eval_tseitinCNF_eq_true_iff] + set x : List Bool := List.ofFn fun i : Fin n => σ i with hx + have hxlen : x.length = n := by simp [hx] + set vals := evalCode interpretation false x gates with hvals + have hgate (g : ℕ) (hg : g < gates.length) : vals.getD (n + g) false = + gates[g].1.value (vals.getD (gates[g].2.getD 0 0) false) + (vals.getD (gates[g].2.getD 1 0) false) := by + have := getD_evalCode interpretation false x (hxlen ▸ h) (List.getElem?_eq_getElem hg) + rw [hxlen] at this + rw [hvals, this, interpretation_eq_value _ (h.getElem g hg).1] + have hin (w : ℕ) (hw : w < n) : vals.getD w false = σ w := by + obtain ⟨r, hr⟩ := prefix_evalCode interpretation false x gates + rw [hvals, ← hr, List.getD_append _ _ _ _ (by omega), hx, List.getD_eq_getElem _ _ (by simpa), + List.getElem_ofFn] + constructor + · intro hσ w hw + induction w using Nat.strong_induction_on with + | _ w ih => + by_cases hwn : w < n + · exact (hin w hwn).symm + · obtain ⟨g, rfl⟩ : ∃ g, w = n + g := ⟨w - n, by omega⟩ + have hg : g < gates.length := by omega + have hargs := getD_lt_of_isCodeFrom h hg + rw [hσ g hg, hgate g hg] + exact Op.value_congr _ (fun h₁ => ih _ (hargs.1 h₁) (by omega)) + (fun h₂ => ih _ (hargs.2 h₂) (by omega)) + · intro hσ g hg + have hargs := getD_lt_of_isCodeFrom h hg + rw [hσ (n + g) (by omega), hgate g hg] + exact Op.value_congr _ (fun h₁ => (hσ _ (by have := hargs.1 h₁; omega)).symm) + (fun h₂ => (hσ _ (by have := hargs.2 h₂; omega)).symm) + +/-! ### Satisfiability with a fixed input prefix -/ + +/-- The unit clause of a literal, padded to three literals. -/ +def unitClause (l : Literal V) : Clause V := [l, l, l] + +/-- The unit clause of a literal holds when the literal does. -/ +@[simp] theorem eval_unitClause (σ : V → Bool) (l : Literal V) : + (unitClause l).eval σ = l.eval σ := by + simp [unitClause] + +/-- **Circuit satisfiability with a fixed input prefix**, as a formula: unit clauses fix the wire +`o` to `true` and the inputs `i < x.length` to `x[i]`, and the Tseitin clauses of `gates` follow. -/ +def tseitinSatCNF (x : List Bool) (n : ℕ) (gates : List (Op × List ℕ)) (o : ℕ) : CNF ℕ := + unitClause (o, true) :: (x.mapIdx fun i b => unitClause (i, b)) ++ tseitinCNF n gates + +/-- The unit clauses fixing the inputs `i < x.length` to `x[i]`. -/ +theorem eval_mapIdx_unitClause_eq_true_iff (x : List Bool) (σ : ℕ → Bool) : + CNF.eval σ (x.mapIdx fun i b => unitClause (i, b)) = true ↔ + ∀ i (hi : i < x.length), σ i = x[i] := by + rw [CNF.eval_eq_true_iff] + simp only [List.mem_mapIdx, forall_exists_index] + constructor + · intro h i hi + simpa using h _ i hi rfl + · rintro h c i hi rfl + simpa using h i hi + +/-- An assignment satisfies `tseitinSatCNF` when it makes wire `o` true, agrees with `x` on the +first inputs and satisfies the Tseitin formula. -/ +theorem eval_tseitinSatCNF_eq_true_iff (x : List Bool) (n : ℕ) (gates : List (Op × List ℕ)) + (o : ℕ) (σ : ℕ → Bool) : + (tseitinSatCNF x n gates o).eval σ = true ↔ + σ o = true ∧ (∀ i (hi : i < x.length), σ i = x[i]) ∧ (tseitinCNF n gates).eval σ = true := by + simp only [tseitinSatCNF, List.cons_append, CNF.eval_cons, CNF.eval_append, Bool.and_eq_true, + eval_unitClause, Literal.eval_eq_true_iff, eval_mapIdx_unitClause_eq_true_iff] + +/-- **Satisfiability with a fixed input prefix**: for a well-formed gate list on `n` inputs and a +wire `o` of it, `tseitinSatCNF x n gates o` is satisfiable exactly when some input of length `n` +extending `x` makes wire `o` true. -/ +theorem satisfiable_tseitinSatCNF_iff {x : List Bool} {n : ℕ} {gates : List (Op × List ℕ)} + {o : ℕ} (h : Program.IsCodeFrom signature n 0 gates) (hx : x.length ≤ n) + (ho : o < n + gates.length) : + (tseitinSatCNF x n gates o).Satisfiable ↔ + ∃ y : List Bool, y.length = n ∧ x <+: y ∧ evalOutput y gates o = true := by + constructor + · rintro ⟨σ, hσ⟩ + obtain ⟨hσo, hσx, hσφ⟩ := (eval_tseitinSatCNF_eq_true_iff x n gates o σ).1 hσ + have hval := (eval_tseitinCNF_eq_true_iff_evalCode h σ).1 hσφ + refine ⟨List.ofFn fun i : Fin n => σ i, by simp, ?_, ?_⟩ + · rw [List.prefix_iff_eq_take] + refine List.ext_getElem (by simp; omega) fun i h₁ h₂ => ?_ + simpa using (hσx i h₁).symm + · rw [evalOutput, ← hval o ho, hσo] + · rintro ⟨y, hy, hxy, hout⟩ + set σ : ℕ → Bool := fun w => (evalCode interpretation false y gates).getD w false with hσ + have hσy (i : ℕ) (hi : i < n) : σ i = y[i]'(by omega) := by + obtain ⟨r, hr⟩ := prefix_evalCode interpretation false y gates + change (evalCode interpretation false y gates).getD i false = _ + rw [← hr, List.getD_append _ _ _ _ (by omega), List.getD_eq_getElem] + have hyσ : (List.ofFn fun i : Fin n => σ i) = y := + List.ext_getElem (by simp [hy]) fun i h₁ h₂ => by simp [hσy i (by simpa using h₁)] + refine ⟨σ, (eval_tseitinSatCNF_eq_true_iff x n gates o σ).2 ⟨hout, fun i hi => ?_, ?_⟩⟩ + · rw [hσy i (by omega), hxy.getElem hi] + · rw [eval_tseitinCNF_eq_true_iff_evalCode h, hyσ] + exact fun _ _ => rfl + +/-- **Circuit satisfiability as a formula**: the Tseitin formula of a single-output De Morgan +circuit, with the output fixed to `true` and the inputs fixed to the bits of `x`, is satisfiable +exactly when some input extending `x` makes the circuit output `true`. -/ +theorem satisfiable_tseitinSatCNF_circuit_iff {n : ℕ} (c : Circuit signature n 1) + {x : List Bool} (hx : x.length ≤ n) : + (tseitinSatCNF x n c.code.1 (c.outputs 0).toNat).Satisfiable ↔ + ∃ y : BitString n, x <+: List.ofFn y ∧ c.eval interpretation y 0 = true := by + have hcode := (Circuit.isSome_ofCode_iff (n := n) (m := 1) (code := c.code)).1 + (by simp [Circuit.ofCode_code]) + rw [satisfiable_tseitinSatCNF_iff hcode.1 hx + (by simpa using hcode.2.2 _ (by simp [Circuit.code]))] + constructor + · rintro ⟨y, hy, hxy, hout⟩ + refine ⟨fun i => y[i.val]'(by omega), ?_, ?_⟩ + · have : (List.ofFn fun i : Fin n => y[i.val]'(by omega)) = y := + List.ext_getElem (by simp [hy]) fun i _ _ => by simp + rwa [this] + · rw [Circuit.eval_eq_evalOutput] + have : (List.ofFn fun i : Fin n => y[i.val]'(by omega)) = y := + List.ext_getElem (by simp [hy]) fun i _ _ => by simp + rwa [this] + · rintro ⟨y, hxy, hout⟩ + exact ⟨List.ofFn y, by simp, hxy, by rwa [← Circuit.eval_eq_evalOutput]⟩ + +/-! ### Size -/ + +/-- **Every clause of `tseitinSatCNF` has exactly three literals.** -/ +theorem length_of_mem_tseitinSatCNF {x : List Bool} {n : ℕ} {gates : List (Op × List ℕ)} {o : ℕ} + {C : Clause ℕ} (hC : C ∈ tseitinSatCNF x n gates o) : C.length = 3 := by + simp only [tseitinSatCNF, List.cons_append, List.mem_cons, List.mem_append, List.mem_mapIdx, + tseitinCNF, List.mem_flatMap, gateClauses, List.mem_ofFn] at hC + rcases hC with rfl | ⟨i, -, rfl⟩ | ⟨g, -, j, rfl⟩ + · rfl + · rfl + · exact length_tseitinClause _ _ _ _ _ + +/-- `tseitinSatCNF` has one clause for the output, one for each fixed input and three for each +gate. -/ +theorem length_tseitinSatCNF (x : List Bool) (n : ℕ) (gates : List (Op × List ℕ)) (o : ℕ) : + (tseitinSatCNF x n gates o).length = 1 + x.length + 3 * gates.length := by + simp only [tseitinSatCNF, tseitinCNF, gateClauses, List.cons_append, List.length_cons, + List.length_append, List.length_mapIdx, List.length_flatMap, List.length_ofFn, + List.map_const', List.sum_replicate, List.length_finRange, smul_eq_mul] + omega + +end Cslib.Circuits.Boolean diff --git a/ComplexityTests/CircuitTseitin.lean b/ComplexityTests/CircuitTseitin.lean new file mode 100644 index 000000000..b0d64c81f --- /dev/null +++ b/ComplexityTests/CircuitTseitin.lean @@ -0,0 +1,58 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.Circuit.Boolean.Tseitin + +/-! +# Tseitin formulas of De Morgan circuits + +The gate list below computes `x₀ ∧ ¬x₁` on two inputs, with the negation at wire `2` and the +conjunction at wire `3`. Its Tseitin formula with the output fixed is satisfiable, and fixing the +first input to `false` makes it unsatisfiable, both through `satisfiable_tseitinSatCNF_iff`. +-/ + +namespace ComplexityTests.CircuitTseitin + +open Cslib.Circuits Cslib.Circuits.Boolean Cslib.Logic.PL + +/-- `x₀ ∧ ¬x₁`: the negation at wire `2`, the conjunction at wire `3`. -/ +private def gates : List (Op × List ℕ) := [(.not, [1]), (.and, [0, 2])] + +private theorem isCodeFrom_gates : Program.IsCodeFrom signature 2 0 gates := by decide + +-- One unit clause for the output, one for each fixed input, three clauses per gate. +example : tseitinSatCNF [false] 2 gates 3 = + [[(3, true), (3, true), (3, true)], [(0, false), (0, false), (0, false)], + [(2, true), (1, true), (1, true)], [(2, false), (1, false), (1, false)], + [(2, true), (1, true), (1, true)], + [(3, false), (0, true), (0, true)], [(3, false), (2, true), (2, true)], + [(3, true), (0, false), (2, false)]] := rfl + +example : (tseitinSatCNF [false] 2 gates 3).length = 8 := by + rw [length_tseitinSatCNF]; rfl + +-- With the output fixed to `true`, the formula is satisfiable: the input `[true, false]`. +example : (tseitinSatCNF [] 2 gates 3).Satisfiable := + (satisfiable_tseitinSatCNF_iff isCodeFrom_gates (by simp) (by decide)).2 + ⟨[true, false], rfl, List.nil_prefix, by decide⟩ + +-- Fixing the first input to `false` leaves no satisfying input, so no satisfying assignment. +example : ¬ (tseitinSatCNF [false] 2 gates 3).Satisfiable := by + rw [satisfiable_tseitinSatCNF_iff isCodeFrom_gates (by simp) (by decide)] + rintro ⟨y, hy, ⟨r, rfl⟩, hout⟩ + rcases r with _ | ⟨b, _ | ⟨_, _⟩⟩ <;> simp at hy + cases b <;> revert hout <;> decide + +-- The assignment that evaluates the gates on `[true, false]` satisfies the Tseitin formula. +example : (tseitinCNF 2 gates).eval (fun w => [true, false, true, true].getD w false) = true := by + decide + +-- An assignment that is not consistent with the negation gate does not. +example : + (tseitinCNF 2 gates).eval (fun w => [true, false, false, false].getD w false) = false := by + decide + +end ComplexityTests.CircuitTseitin From 78367689219501687d7a7f96406e81053aa88e7d Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 12:27:58 -0400 Subject: [PATCH 2/8] feat(Circuit/Boolean/Tableau): the tableau formula on bit-field labels (#1539 M3b) The Tseitin formula of the uniform tableau circuit with variables labelled by fixed-width fields (tag, slot, column, block), each clause decoded from its number by bit-field arithmetic; satisfiable exactly when some completion of the fixed input prefix is accepted within T steps. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Circuit/Boolean/Tableau/Succinct.lean | 834 ++++++++++++++++++ ComplexityTests/TableauSuccinct.lean | 73 ++ 3 files changed, 908 insertions(+) create mode 100644 Complexity/Computability/Circuit/Boolean/Tableau/Succinct.lean create mode 100644 ComplexityTests/TableauSuccinct.lean diff --git a/Complexity.lean b/Complexity.lean index d4ed7bed7..e0b2182b9 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -829,6 +829,7 @@ public import Complexity.Computability.Circuit.Boolean.Tableau.Basic public import Complexity.Computability.Circuit.Boolean.Tableau.Correct public import Complexity.Computability.Circuit.Boolean.Tableau.Layout public import Complexity.Computability.Circuit.Boolean.Tableau.Space +public import Complexity.Computability.Circuit.Boolean.Tableau.Succinct public import Complexity.Computability.Circuit.Boolean.Threshold public import Complexity.Computability.Circuit.Boolean.Threshold.Parity public import Complexity.Computability.Circuit.Boolean.ThresholdNC1 diff --git a/Complexity/Computability/Circuit/Boolean/Tableau/Succinct.lean b/Complexity/Computability/Circuit/Boolean/Tableau/Succinct.lean new file mode 100644 index 000000000..4778a8e5a --- /dev/null +++ b/Complexity/Computability/Circuit/Boolean/Tableau/Succinct.lean @@ -0,0 +1,834 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Boolean.Tableau.Correct +public import Complexity.Computability.Circuit.Boolean.Tseitin +public import Mathlib.Data.Nat.Bits +import Complexity.Foundations.Data.List.Lookup +import Complexity.Foundations.Data.Nat.Div +import Mathlib.Data.Nat.Size + +/-! +# The tableau formula on bit-field labels + +The Tseitin formula (`Complexity/Computability/Circuit/Boolean/Tseitin.lean`) of the uniform +tableau circuit of a multi-tape machine +(`Complexity/Computability/Circuit/Boolean/Tableau/Basic.lean`), written with variables that are +*labels* made of fixed-width binary fields rather than wire numbers. Each clause is computed from +its number by bit-field arithmetic (`Cslib.Circuits.Boolean.Tableau.clauseAt`), with no +multiplication by the block size, so that a circuit of size linear in the field widths can decode +it. This is the succinct tableau formula of the sketch of +[Williams, Theorem 6.1, p. 20][Williams2010]: the clauses of a cell are the same for every cell, +except that the block and the column enter the labels of their variables. The sketch first +converts the machine to a single-tape one; this formula uses the multi-tape tableau directly. + +A label is a two-bit *tag* followed by a payload (`label = tag + 4 * payload`): + +* tag `0`, the constant gate `b` (`constLabel`), payload `b`; +* tag `1`, the input `i` (`inputLabel`), payload `i`; +* tag `2`, the position `l` of block `t` in column `c` (`posLabel`), payload + `slot + 2 ^ sb * (c + 2 ^ cb * t)`, where `slot` is the number of `l` among the positions of a + block (`Loc.slot`), `sb = slotBits k State` depends only on the machine, + `cb = colBits T = Nat.size (2 * T)` and `t` takes the remaining high bits. + +A reference of the tableau (`Ref`), relative to the block and the column of its gate, becomes the +label `Ref.label`: the block `t` or `t - 1`, the column `c`, `c - 1`, `c + 1` or `2 T` (`0` for the +global positions, which are not repeated across the columns), and the slot of the position. A +clause number is `j + 4 * (tag + 4 * payload)`: clause `j < 3` of the gate of a constant or a +position, the unit clause fixing an input, or (tag `3`, payload `0`) the unit clause fixing the +output; every other number gives a tautology. The formula `succinctCNF tm x n T` lists the clauses +of all numbers below `2 ^ clauseBits k State T`. + +The formula is satisfiable exactly when some input of length `n` extending `x` makes the machine +write exactly `[true]` within `T` steps (`satisfiable_succinctCNF_iff`). The proof compares it +with `tseitinSatCNF` of the tableau gate list, through the labelling of wires (`wireLabel`) and +the inverse decoding of labels (`wireOf`); the labels of references agree with their resolved wire +numbers (`wireLabel_resolve`, `wireOf_label`). + +## Main definitions + +* `Cslib.Circuits.Boolean.Tableau.Loc.slot`, `Cslib.Circuits.Boolean.Tableau.slotBits`, + `Cslib.Circuits.Boolean.Tableau.colBits`, `Cslib.Circuits.Boolean.Tableau.timeBits`: the fields. +* `Cslib.Circuits.Boolean.Tableau.constLabel`, `Cslib.Circuits.Boolean.Tableau.inputLabel`, + `Cslib.Circuits.Boolean.Tableau.posLabel`, `Cslib.Circuits.Boolean.Tableau.Ref.label`: labels. +* `Cslib.Circuits.Boolean.Tableau.posClause`, `Cslib.Circuits.Boolean.Tableau.clauseOf`, + `Cslib.Circuits.Boolean.Tableau.clauseAt`: the clause with a number, from its fields. +* `Cslib.Circuits.Boolean.Tableau.clauseBits`, `Cslib.Circuits.Boolean.Tableau.labelBits`: the + widths of clause numbers and labels. +* `Cslib.Circuits.Boolean.Tableau.succinctCNF`: the formula. + +## Main results + +* `Cslib.Circuits.Boolean.Tableau.wireLabel_resolve`, `Cslib.Circuits.Boolean.Tableau.wireOf_label`: + the label of a reference is the label of the wire it resolves to, and decodes to that wire. +* `Cslib.Circuits.Boolean.Tableau.satisfiable_succinctCNF_iff_tseitin`: the formula is + equisatisfiable with the Tseitin formula of the tableau. +* `Cslib.Circuits.Boolean.Tableau.satisfiable_succinctCNF_iff`: the formula is satisfiable exactly + when some completion of `x` is accepted within `T` steps. +* `Cslib.Circuits.Boolean.Tableau.length_succinctCNF`, + `Cslib.Circuits.Boolean.Tableau.length_of_mem_succinctCNF`, + `Cslib.Circuits.Boolean.Tableau.lt_two_pow_labelBits_of_mem_vars`: there are + `2 ^ clauseBits k State T` clauses of three literals, on variables below + `2 ^ labelBits k State T`. + +## References + +* [R. Williams, *Improving exhaustive search implies superpolynomial lower bounds*][Williams2010] +* [S. Arora, B. Barak, *Computational Complexity: A Modern Approach*, Section 6.1][AroraBarak09] +-/ + +@[expose] public section + +namespace Cslib.Circuits.Boolean.Tableau + +open Turing MultiTapeTM Cslib.Logic.PL + +variable {k : ℕ} {State : Type} [Fintype State] [DecidableEq State] + +/-! ### Fields -/ + +/-- The slot of a position: its number among the positions of a block. -/ +noncomputable def Loc.slot (l : Loc k State) : ℕ := Fintype.equivFin (Loc k State) l + +/-- The position with a slot number, if any. -/ +noncomputable def Loc.ofSlot (s : ℕ) : Option (Loc k State) := + if h : s < Fintype.card (Loc k State) then some ((Fintype.equivFin (Loc k State)).symm ⟨s, h⟩) + else none + +omit [DecidableEq State] in +/-- Slots are below the number of positions. -/ +theorem Loc.slot_lt (l : Loc k State) : l.slot < Fintype.card (Loc k State) := + (Fintype.equivFin (Loc k State) l).isLt + +omit [DecidableEq State] in +/-- Decoding a slot gives its position. -/ +@[simp] theorem Loc.ofSlot_slot (l : Loc k State) : Loc.ofSlot l.slot = some l := by + simp [Loc.ofSlot, Loc.slot] + +variable (k State) in +/-- The width of the slot field. -/ +noncomputable def slotBits : ℕ := Nat.size (Fintype.card (Loc k State)) + +/-- The width of the column field, for the columns `c ≤ 2 T`. -/ +def colBits (T : ℕ) : ℕ := Nat.size (2 * T) + +/-- The width of the time field, for the blocks `t ≤ T`. -/ +def timeBits (T : ℕ) : ℕ := Nat.size T + +omit [DecidableEq State] in +/-- The slots fit in their field. -/ +theorem slot_lt_two_pow (l : Loc k State) : l.slot < 2 ^ slotBits k State := + l.slot_lt.trans (Nat.lt_size_self _) + +/-- The columns fit in their field. -/ +theorem numCols_le_two_pow (T : ℕ) : numCols T ≤ 2 ^ colBits T := + Nat.lt_size_self (2 * T) + +/-- The blocks fit in their field. -/ +theorem lt_two_pow_timeBits {t T : ℕ} (ht : t ≤ T) : t < 2 ^ timeBits T := + lt_of_le_of_lt ht (Nat.lt_size_self T) + +/-- Three fields of widths `a` and `b` and a high field decode into their values. -/ +private theorem fields {a b x y z : ℕ} (hx : x < 2 ^ a) (hy : y < 2 ^ b) : + (x + 2 ^ a * (y + 2 ^ b * z)) % 2 ^ a = x ∧ + (x + 2 ^ a * (y + 2 ^ b * z)) / 2 ^ a % 2 ^ b = y ∧ + (x + 2 ^ a * (y + 2 ^ b * z)) / 2 ^ a / 2 ^ b = z := by + have ha : 0 < 2 ^ a := Nat.two_pow_pos a + have hb : 0 < 2 ^ b := Nat.two_pow_pos b + have h₁ : (x + 2 ^ a * (y + 2 ^ b * z)) / 2 ^ a = y + 2 ^ b * z := by + rw [Nat.add_mul_div_left _ _ ha, Nat.div_eq_of_lt hx, Nat.zero_add] + refine ⟨by rw [Nat.add_mul_mod_self_left, Nat.mod_eq_of_lt hx], ?_, ?_⟩ + · rw [h₁, Nat.add_mul_mod_self_left, Nat.mod_eq_of_lt hy] + · rw [h₁, Nat.add_mul_div_left _ _ hb, Nat.div_eq_of_lt hy, Nat.zero_add] + +/-- Three fields of widths `a`, `b` and `d` fit in `a + b + d` bits. -/ +private theorem fields_lt {a b d x y z : ℕ} (hx : x < 2 ^ a) (hy : y < 2 ^ b) (hz : z < 2 ^ d) : + x + 2 ^ a * (y + 2 ^ b * z) < 2 ^ (a + b + d) := by + have h₁ : y + 2 ^ b * z < 2 ^ (b + d) := by + rw [Nat.pow_add] + calc y + 2 ^ b * z < 2 ^ b * (z + 1) := by rw [Nat.mul_succ]; omega + _ ≤ 2 ^ b * 2 ^ d := Nat.mul_le_mul_left _ hz + rw [Nat.add_assoc, Nat.pow_add] + calc x + 2 ^ a * (y + 2 ^ b * z) < 2 ^ a * (y + 2 ^ b * z + 1) := by rw [Nat.mul_succ]; omega + _ ≤ 2 ^ a * 2 ^ (b + d) := Nat.mul_le_mul_left _ h₁ + +/-- A tag below `4` and a payload decode into their values. -/ +private theorem tag_fields {tag p : ℕ} (htag : tag < 4) : + (tag + 4 * p) % 4 = tag ∧ (tag + 4 * p) / 4 = p := by + omega + +/-! ### Labels -/ + +/-- The label of the constant gate `b`: tag `0`. -/ +def constLabel (b : Bool) : ℕ := 4 * b.toNat + +/-- The label of the input `i`: tag `1`. -/ +def inputLabel (i : ℕ) : ℕ := 1 + 4 * i + +variable (k State) in +/-- **The label of the position `l` of block `t` in column `c`**: tag `2`, and the slot, the column +and the block in fields of widths `slotBits k State`, `colBits T` and the remaining high bits. -/ +noncomputable def posLabel (T t c : ℕ) (l : Loc k State) : ℕ := + 2 + 4 * (l.slot + 2 ^ slotBits k State * (c + 2 ^ colBits T * t)) + +/-- The column under which a position is labelled: `0` for the global positions, which do not +depend on the column. -/ +def Loc.labelCol : Loc k State → ℕ → ℕ + | .glob _, _ => 0 + | .gf _ _ _, _ => 0 + | _, c => c + +omit [DecidableEq State] in +/-- Labelling a column does not change the offset of a position. -/ +theorem Loc.offset_labelCol (l : Loc k State) (T c : ℕ) : + l.offset T (l.labelCol c) = l.offset T c := by + cases l <;> rfl + +omit [DecidableEq State] in +/-- The labelled column of a column of the window is admissible. -/ +theorem Loc.colOK_labelCol {l : Loc k State} {T c : ℕ} (hc : c < numCols T) : + l.ColOK T (l.labelCol c) := by + cases l <;> simp [Loc.labelCol, Loc.ColOK, hc] + +omit [DecidableEq State] in +/-- The labelled column is at most the column. -/ +theorem Loc.labelCol_le (l : Loc k State) (c : ℕ) : l.labelCol c ≤ c := by + cases l <;> simp [Loc.labelCol] + +/-- **The label of a reference** from block `t` and column `c`: `Ref.resolve` with labels in place +of wire numbers. -/ +noncomputable def Ref.label (n T t c : ℕ) : Ref k State → ℕ + | .fls => constLabel false + | .tru => constLabel true + | .inp => if 1 ≤ c ∧ c ≤ n then inputLabel (c - 1) else constLabel false + | .node p l cs g => + if g.test n T c then posLabel k State T (if p then t - 1 else t) (l.labelCol (cs.apply T c)) l + else constLabel false + +/-! ### From labels to wires -/ + +/-- The wire with a label: inverts the labelling on the labels of wires. -/ +noncomputable def wireOf (n T lab : ℕ) : ℕ := + if lab % 4 = 0 then n + lab / 4 + else if lab % 4 = 1 then lab / 4 + else + match Loc.ofSlot (k := k) (State := State) (lab / 4 % 2 ^ slotBits k State) with + | some l => wire n T (lab / 4 / 2 ^ slotBits k State / 2 ^ colBits T) l + (lab / 4 / 2 ^ slotBits k State % 2 ^ colBits T) + | none => n + +omit [DecidableEq State] in +/-- Decoding the label of a constant gives its wire. -/ +@[simp] theorem wireOf_constLabel (n T : ℕ) (b : Bool) : + wireOf (k := k) (State := State) n T (constLabel b) = n + b.toNat := by + simp [wireOf, constLabel] + +omit [DecidableEq State] in +/-- Decoding the label of an input gives its wire. -/ +@[simp] theorem wireOf_inputLabel (n T i : ℕ) : + wireOf (k := k) (State := State) n T (inputLabel i) = i := by + have h₁ : inputLabel i % 4 = 1 := by unfold inputLabel; omega + have h₂ : inputLabel i / 4 = i := by unfold inputLabel; omega + simp [wireOf, h₁, h₂] + +omit [DecidableEq State] in +/-- Decoding the label of a position gives its wire. -/ +theorem wireOf_posLabel {n T t c : ℕ} (l : Loc k State) (hc : c < 2 ^ colBits T) : + wireOf (k := k) (State := State) n T (posLabel k State T t c l) = wire n T t l c := by + obtain ⟨h₁, h₂, h₃⟩ := fields (z := t) (slot_lt_two_pow l) hc + obtain ⟨h₄, h₅⟩ := tag_fields (tag := 2) + (p := l.slot + 2 ^ slotBits k State * (c + 2 ^ colBits T * t)) (by omega) + simp only [wireOf, posLabel, h₄, h₅, h₁, h₂, h₃, Loc.ofSlot_slot] + rfl + +/-! ### From wires to labels -/ + +/-- The column and the position at an offset of a block: inverts `Loc.offset`. -/ +noncomputable def posOf (T r : ℕ) : ℕ × Loc k State := + if r < globStart k T then (r / scanWidth k, .scan (ScanLoc.ofIdx (r % scanWidth k))) + else if r < gfStart k State T then (0, .glob (GlobLoc.ofIdx (r - globStart k T))) + else if r < colStart k State T then + (0, .gf (GFeat.ofIdx ((r - gfStart k State T) / 16)) (gadgetB (r - gfStart k State T)) + (gadgetJ (r - gfStart k State T))) + else ((r - colStart k State T) / colWidth k, + .col (ColFeat.ofIdx ((r - colStart k State T) % colWidth k / 16)) + (gadgetB ((r - colStart k State T) % colWidth k)) + (gadgetJ ((r - colStart k State T) % colWidth k))) + +omit [DecidableEq State] in +/-- **Decoding the offset of a position** gives the position and its column. -/ +theorem posOf_offset {T c : ℕ} {l : Loc k State} (hl : l.ColOK T c) : + posOf T (l.offset T c) = (c, l) := by + have hgs : globStart k T ≤ gfStart k State T := Nat.le_add_right _ _ + have hgc : gfStart k State T ≤ colStart k State T := Nat.le_add_right _ _ + rcases l with l | l | ⟨f, b, j⟩ | ⟨f, b, j⟩ <;> simp only [Loc.ColOK] at hl <;> + simp only [Loc.offset, posOf] + · have hi := l.idx_lt + have h₁ : c * scanWidth k + l.idx < globStart k T := scan_offset_lt_globStart hl l + have h₂ : (c * scanWidth k + l.idx) / scanWidth k = c := Nat.mul_add_div_of_lt c hi + have h₃ : (c * scanWidth k + l.idx) % scanWidth k = l.idx := by + rw [Nat.add_comm, Nat.add_mul_mod_self_right, Nat.mod_eq_of_lt hi] + simp only [h₁, ↓reduceIte, h₂, h₃, ScanLoc.ofIdx_idx] + · have hi := l.idx_lt + subst hl + have h₁ : ¬ globStart k T + l.idx < globStart k T := by omega + have h₂ : globStart k T + l.idx < gfStart k State T := by simp only [gfStart]; omega + simp only [h₁, h₂, ↓reduceIte, Nat.add_sub_cancel_left, GlobLoc.ofIdx_idx] + · subst hl + have := gfStart_add_gadgetIdx_lt (k := k) (T := T) f b j + have h₁ : ¬ gfStart k State T + gadgetIdx f.idx b j < globStart k T := by omega + have h₂ : ¬ gfStart k State T + gadgetIdx f.idx b j < gfStart k State T := by omega + simp only [h₁, h₂, this, ↓reduceIte, Nat.add_sub_cancel_left, gadgetIdx_div, + GFeat.ofIdx_idx, gadgetB_gadgetIdx, gadgetJ_gadgetIdx] + · have h₁ := gadgetIdx_lt f.idx b j + have h₂ := f.idx_lt + have hw : gadgetIdx f.idx b j < colWidth k := by simp only [colWidth]; omega + have h₃ : ¬ colStart k State T + c * colWidth k + gadgetIdx f.idx b j < globStart k T := by + omega + have h₄ : ¬ colStart k State T + c * colWidth k + gadgetIdx f.idx b j < gfStart k State T := by + omega + have h₅ : ¬ colStart k State T + c * colWidth k + gadgetIdx f.idx b j < + colStart k State T := by omega + have h₆ : colStart k State T + c * colWidth k + gadgetIdx f.idx b j - colStart k State T = + gadgetIdx f.idx b j + c * colWidth k := by omega + have h₇ : (gadgetIdx f.idx b j + c * colWidth k) / colWidth k = c := + Nat.add_mul_div_of_lt c hw + have h₈ : (gadgetIdx f.idx b j + c * colWidth k) % colWidth k = gadgetIdx f.idx b j := by + rw [Nat.add_mul_mod_self_right, Nat.mod_eq_of_lt hw] + simp only [h₃, h₄, h₅, ↓reduceIte, h₆, h₇, h₈, gadgetIdx_div, ColFeat.ofIdx_idx, + gadgetB_gadgetIdx, gadgetJ_gadgetIdx] + +variable (k State) in +/-- **The label of a wire** of the tableau on `n` inputs: an input, a constant, or the position of +a gate decoded from its number. -/ +noncomputable def wireLabel (n T w : ℕ) : ℕ := + if w < n then inputLabel w + else if w < n + 2 then constLabel (decide (w = n + 1)) + else + posLabel k State T ((w - n - 2) / blockSize k State T) + (posOf (k := k) (State := State) T ((w - n - 2) % blockSize k State T)).1 + (posOf T ((w - n - 2) % blockSize k State T)).2 + +omit [DecidableEq State] in +/-- The label of an input wire. -/ +theorem wireLabel_of_lt {n T w : ℕ} (hw : w < n) : wireLabel k State n T w = inputLabel w := by + simp [wireLabel, hw] + +omit [DecidableEq State] in +/-- The labels of the constant wires. -/ +theorem wireLabel_const (n T : ℕ) (b : Bool) : + wireLabel k State n T (n + b.toNat) = constLabel b := by + cases b <;> simp [wireLabel] + +omit [DecidableEq State] in +/-- **The label of the wire of a position** is the label of the position. -/ +theorem wireLabel_wire {n T t c : ℕ} {l : Loc k State} (hl : l.ColOK T c) : + wireLabel k State n T (wire n T t l c) = posLabel k State T t c l := by + obtain ⟨hdiv, hmod⟩ := div_mod_wire (n := n) (t := t) hl + have h₁ : ¬ wire n T t l c < n := by simp only [wire]; omega + have h₂ : ¬ wire n T t l c < n + 2 := by simp only [wire]; omega + simp only [wireLabel, h₁, h₂, ↓reduceIte, hdiv, hmod, posOf_offset hl] + +/-! ### Labels of references -/ + +/-- A reference reads a column of the window whenever its guard holds. -/ +def Ref.InWindow (n T c : ℕ) : Ref k State → Prop + | .node _ _ cs g => g.test n T c = true → cs.apply T c < numCols T + | _ => True + +omit [DecidableEq State] in +/-- **The label of a reference is the label of its wire.** -/ +theorem wireLabel_resolve {n T t c : ℕ} {r : Ref k State} (hr : r.InWindow n T c) : + wireLabel k State n T (r.resolve n T t c) = r.label n T t c := by + rcases r with _ | _ | _ | ⟨p, l, cs, g⟩ + · simpa [Ref.resolve, Ref.label] using wireLabel_const (k := k) (State := State) n T false + · simpa [Ref.resolve, Ref.label] using wireLabel_const (k := k) (State := State) n T true + · simp only [Ref.resolve, Ref.label] + split_ifs with h + · exact wireLabel_of_lt (by omega) + · simpa using wireLabel_const (k := k) (State := State) n T false + · simp only [Ref.InWindow] at hr + by_cases hg : g.test n T c = true + · simp only [Ref.resolve, Ref.label, hg, ↓reduceIte] + rw [← wire_labelCol, wireLabel_wire (Loc.colOK_labelCol (hr hg))] + · simp only [Ref.resolve, Ref.label, hg, Bool.false_eq_true, ↓reduceIte] + simpa using wireLabel_const (k := k) (State := State) n T false +where + wire_labelCol {n T t c : ℕ} {l : Loc k State} : wire n T t l (l.labelCol c) = wire n T t l c := by + simp only [wire, Loc.offset_labelCol] + +omit [DecidableEq State] in +/-- **Decoding the label of a reference gives its wire.** -/ +theorem wireOf_label {n T t c : ℕ} {r : Ref k State} (hr : r.InWindow n T c) : + wireOf (k := k) (State := State) n T (r.label n T t c) = r.resolve n T t c := by + rcases r with _ | _ | _ | ⟨p, l, cs, g⟩ + · simp [Ref.label, Ref.resolve] + · simp [Ref.label, Ref.resolve] + · simp only [Ref.resolve, Ref.label] + split_ifs <;> simp + · simp only [Ref.InWindow] at hr + by_cases hg : g.test n T c = true + · simp only [Ref.resolve, Ref.label, hg, ↓reduceIte] + rw [wireOf_posLabel _ (lt_of_le_of_lt (Loc.labelCol_le l _) + (lt_of_lt_of_le (hr hg) (numCols_le_two_pow T)))] + exact wireLabel_resolve.wire_labelCol + · simp [Ref.resolve, Ref.label, hg] + +omit [DecidableEq State] in +private theorem inWindow_of_cs {n T c : ℕ} (hc : c < numCols T) {p : Bool} {l : Loc k State} + {cs : ColSel} {g : Guard} (h : cs = .next → g = .ltLast) : + (Ref.node p l cs g).InWindow n T c := by + intro hg + cases cs with + | same => exact hc + | prev => simp only [ColSel.apply]; omega + | next => + obtain rfl := h rfl + simpa [Guard.test, ColSel.apply] using hg + | last => simp [ColSel.apply, numCols] + +/-- **The references of the gates of the tableau read the window.** -/ +theorem table_inWindow {n T c : ℕ} (hc : c < numCols T) (tm : MultiTapeTM k Bool State) + (l : Loc k State) : + (table tm l).2.1.InWindow n T c ∧ (table tm l).2.2.InWindow n T c := by + have hw {r : Ref k State} (h : ∀ p l g, r = .node p l .next g → g = .ltLast) : + r.InWindow n T c := by + rcases r with _ | _ | _ | ⟨p, l, cs, g⟩ + · trivial + · trivial + · trivial + · exact inWindow_of_cs hc fun hcs => h p l g (by rw [hcs]) + have hterm {L : List (Ref k State × Ref k State)} + (hL : ∀ q ∈ L, ∀ p l g, (q.1 = .node p l .next g → g = .ltLast) ∧ + (q.2 = .node p l .next g → g = .ltLast)) (j : ℕ) : + (L.getD j (.fls, .fls)).1.InWindow n T c ∧ (L.getD j (.fls, .fls)).2.InWindow n T c := by + rcases List.getD_mem_or L j (.fls, .fls) with hj | hj + · exact ⟨hw fun p l g => (hL _ hj p l g).1, hw fun p l g => (hL _ hj p l g).2⟩ + · rw [hj]; exact ⟨trivial, trivial⟩ + rcases l with l | l | ⟨f, b, j⟩ | ⟨f, b, j⟩ + · rcases l with i | _ | q | q + · exact ⟨hw (by simp [table, colF]), trivial⟩ + · exact ⟨trivial, trivial⟩ + · rcases q with s | ⟨i, s⟩ + · rcases s with _ | _ | _ <;> + exact ⟨hw (by simp [table, scanTerm, colF, scanAt]), + hw (by simp [table, scanTerm, colF, scanAt])⟩ + · exact ⟨hw (by simp [table, scanTerm, colF]), hw (by simp [table, scanTerm, colF])⟩ + · exact ⟨hw (by simp [table, scanAt]), hw (by simp [table, scanAt])⟩ + · rcases l with ⟨j, i⟩ | ⟨v, j⟩ + · cases i using Fin.cases <;> + exact ⟨hw (by simp [table, gfF]), hw (by simp [table, scanAt])⟩ + · cases j using Fin.cases with + | zero => exact ⟨trivial, trivial⟩ + | succ j => + refine ⟨hw (by simp [table]), hw ?_⟩ + simp only [table, Fin.cases_succ] + split_ifs <;> simp + · rcases b with _ | _ + · refine hterm (fun q hq p l g => ?_) j + rcases f with (_ | q') | ((_ | _) | _) | _ <;> + simp only [gfTerms, List.mem_cons, List.not_mem_nil, or_false] at hq <;> + rcases hq with rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> + simp [halted, gfF, act] + · cases j using Fin.cases <;> exact ⟨hw (by simp [table]), hw (by simp [table])⟩ + · rcases b with _ | _ + · refine hterm (fun q hq p l g => ?_) j + rcases f with _ | i | ⟨i, s⟩ <;> + simp only [colTerms, List.mem_cons, List.not_mem_nil, or_false] at hq <;> + rcases hq with rfl | rfl | rfl | rfl | rfl | rfl <;> + simp +contextual [halted, gfF, act, colF, scanAt] + · cases j using Fin.cases <;> exact ⟨hw (by simp [table]), hw (by simp [table])⟩ + +/-! ### The clauses -/ + +/-- A clause that every assignment satisfies. -/ +def tautology : Clause ℕ := [(0, true), (0, false), (0, true)] + +/-- Every assignment satisfies `tautology`. -/ +@[simp] theorem eval_tautology (σ : ℕ → Bool) : tautology.eval σ = true := by + cases h : σ 0 <;> simp [tautology, h] + +omit [DecidableEq State] in +/-- Admissibility of a column is decidable. -/ +instance Loc.decidableColOK (T c : ℕ) (l : Loc k State) : Decidable (l.ColOK T c) := by + cases l <;> unfold Loc.ColOK <;> infer_instance + +variable (tm : MultiTapeTM k Bool State) + +/-- Clause `j` of the gate at the position `l` of block `t` in column `c`, on labels: the initial +constant in block `0`, and otherwise the gate of `table` on the labels of its references. -/ +noncomputable def posClause (n T t c : ℕ) (l : Loc k State) (j : Fin 3) : Clause ℕ := + if t = 0 then tseitinClause (.const (initBit tm T c l)) (posLabel k State T t c l) 0 0 j + else tseitinClause (table tm l).1 (posLabel k State T t c l) ((table tm l).2.1.label n T t c) + ((table tm l).2.2.label n T t c) j + +variable (k State) in +/-- The label of the output wire: the output view `[true]` at time `T`. -/ +noncomputable def outLabel (T : ℕ) : ℕ := posLabel k State T T 0 (.gf (.out (.one true)) true 7) + +/-- **The clause with the fields `j`, `tag` and `p`** of its number `j + 4 * (tag + 4 * p)`: +clause `j` of the constant gate `p` (tag `0`), the unit clause fixing input `p` to `x[p]` +(tag `1`, `j = 0`), clause `j` of the gate at the position whose slot, column and block are the +fields of `p` (tag `2`), or the unit clause fixing the output (tag `3`, `j = p = 0`). Every other +number gives a tautology. -/ +noncomputable def clauseOf (x : List Bool) (n T j tag p : ℕ) : Clause ℕ := + if hj : j < 3 then + if tag = 0 then + if p < 2 then + tseitinClause (.const (decide (p = 1))) (constLabel (decide (p = 1))) 0 0 ⟨j, hj⟩ + else tautology + else if tag = 1 then + if hp : j = 0 ∧ p < x.length then unitClause (inputLabel p, x[p]) else tautology + else if tag = 2 then + match Loc.ofSlot (k := k) (State := State) (p % 2 ^ slotBits k State) with + | some l => + if p / 2 ^ slotBits k State / 2 ^ colBits T ≤ T ∧ + l.ColOK T (p / 2 ^ slotBits k State % 2 ^ colBits T) then + posClause tm n T (p / 2 ^ slotBits k State / 2 ^ colBits T) + (p / 2 ^ slotBits k State % 2 ^ colBits T) l ⟨j, hj⟩ + else tautology + | none => tautology + else if j = 0 ∧ p = 0 then unitClause (outLabel k State T, true) else tautology + else tautology + +/-- **The clause with number `idx`**, from the fields of `idx`. -/ +noncomputable def clauseAt (x : List Bool) (n T idx : ℕ) : Clause ℕ := + clauseOf tm x n T (idx % 4) (idx / 4 % 4) (idx / 16) + +variable (k State) in +/-- The width of a clause number: the clause and the tag, then the slot, the column and the +block. -/ +noncomputable def clauseBits (T : ℕ) : ℕ := 4 + (slotBits k State + colBits T + timeBits T) + +/-- **The tableau formula on bit-field labels**: the clauses of all numbers below +`2 ^ clauseBits k State T`, for the tableau of `tm` for `T` steps on `n` inputs, with the inputs +`i < x.length` fixed to `x[i]` and the output fixed to `true`. -/ +noncomputable def succinctCNF (x : List Bool) (n T : ℕ) : CNF ℕ := + (List.range (2 ^ clauseBits k State T)).map (clauseAt tm x n T) + +/-! ### Clauses at their numbers -/ + +/-- The clause of a number with the fields `j`, `tag` and `p`. -/ +theorem clauseAt_fields (x : List Bool) (n T : ℕ) {j tag : ℕ} (p : ℕ) (hj : j < 4) + (htag : tag < 4) : clauseAt tm x n T (j + 4 * (tag + 4 * p)) = clauseOf tm x n T j tag p := by + have h₁ : (j + 4 * (tag + 4 * p)) % 4 = j := by omega + have h₂ : (j + 4 * (tag + 4 * p)) / 4 % 4 = tag := by omega + have h₃ : (j + 4 * (tag + 4 * p)) / 16 = p := by omega + rw [clauseAt, h₁, h₂, h₃] + +omit [DecidableEq State] in +/-- The numbers of the clauses with a payload of `slotBits + colBits + timeBits` bits are below +`2 ^ clauseBits`. -/ +theorem fields_lt_clauseBits {T j tag p : ℕ} (hj : j < 4) (htag : tag < 4) + (hp : p < 2 ^ (slotBits k State + colBits T + timeBits T)) : + j + 4 * (tag + 4 * p) < 2 ^ clauseBits k State T := by + rw [clauseBits, Nat.pow_add] + have : 2 ^ 4 * (p + 1) ≤ 2 ^ 4 * 2 ^ (slotBits k State + colBits T + timeBits T) := + Nat.mul_le_mul_left _ hp + omega + +/-- The clauses of a position. -/ +theorem clauseOf_pos (x : List Bool) {n T t c : ℕ} {l : Loc k State} (j : Fin 3) (ht : t ≤ T) + (hl : l.ColOK T c) : + clauseOf tm x n T j 2 (l.slot + 2 ^ slotBits k State * (c + 2 ^ colBits T * t)) = + posClause tm n T t c l j := by + obtain ⟨h₁, h₂, h₃⟩ := fields (z := t) (slot_lt_two_pow l) + (lt_of_lt_of_le hl.lt (numCols_le_two_pow T)) + simp only [clauseOf, j.isLt, ↓reduceDIte, OfNat.ofNat_ne_zero, ↓reduceIte, + OfNat.ofNat_ne_one, h₁, h₂, h₃, Loc.ofSlot_slot, ht, hl, and_self] + +/-- The clauses of a position hold exactly when its label carries the value of its gate. -/ +theorem forall_eval_posClause_iff (σ : ℕ → Bool) (n T t c : ℕ) (l : Loc k State) : + (∀ j, (posClause tm n T t c l j).eval σ = true) ↔ + σ (posLabel k State T t c l) = if t = 0 then initBit tm T c l + else (table tm l).1.value (σ ((table tm l).2.1.label n T t c)) + (σ ((table tm l).2.2.label n T t c)) := by + unfold posClause + split_ifs <;> exact forall_eval_tseitinClause_iff _ _ _ _ _ + +/-- **Every clause holds** under an assignment of labels that gives the constants their values, +the fixed inputs the bits of `x` and the output `true`, and every position the value of its gate on +the labels of its references. -/ +theorem eval_clauseOf {x : List Bool} {n T : ℕ} {σ : ℕ → Bool} + (hconst : ∀ b, σ (constLabel b) = b) (hin : ∀ i (hi : i < x.length), σ (inputLabel i) = x[i]) + (hout : σ (outLabel k State T) = true) + (hpos : ∀ t ≤ T, ∀ c (l : Loc k State), l.ColOK T c → + σ (posLabel k State T t c l) = if t = 0 then initBit tm T c l + else (table tm l).1.value (σ ((table tm l).2.1.label n T t c)) + (σ ((table tm l).2.2.label n T t c))) + (j tag p : ℕ) : (clauseOf tm x n T j tag p).eval σ = true := by + unfold clauseOf + split_ifs with hj h₀ hp h₁ hp' h₂ h₃ + · exact (forall_eval_tseitinClause_iff _ _ _ _ σ).2 (hconst _) _ + · exact eval_tautology σ + · simpa using hin p hp'.2 + · exact eval_tautology σ + · split + · split_ifs with h + · exact (forall_eval_posClause_iff tm σ _ _ _ _ _).2 (hpos _ h.1 _ _ h.2) _ + · exact eval_tautology σ + · exact eval_tautology σ + · simpa using hout + · exact eval_tautology σ + · exact eval_tautology σ + +/-! ### Comparison with the Tseitin formula of the tableau -/ + +omit [DecidableEq State] in +private theorem wire_sub_lt_numGates {n T t c : ℕ} {l : Loc k State} (hl : l.ColOK T c) + (ht : t ≤ T) : wire n T t l c - n < numGates k State T := by + rw [wire_sub] + have := Loc.offset_lt hl + have : t * blockSize k State T + blockSize k State T ≤ (T + 1) * blockSize k State T := by + rw [← Nat.succ_mul]; exact Nat.mul_le_mul_right _ (by omega) + simp only [numGates] + omega + +/-- The value of the gate at a position, on the values of its arguments. -/ +private theorem value_gateAt (f : ℕ → Bool) (n T t c : ℕ) (l : Loc k State) : + (gateAt tm n T t c l).1.value (f ((gateAt tm n T t c l).2.getD 0 0)) + (f ((gateAt tm n T t c l).2.getD 1 0)) = + if t = 0 then initBit tm T c l + else (table tm l).1.value (f ((table tm l).2.1.resolve n T t c)) + (f ((table tm l).2.2.resolve n T t c)) := by + unfold gateAt + split_ifs with ht + · rfl + · generalize table tm l = e + obtain ⟨op, a, b⟩ := e + cases op <;> rfl + +private theorem getElem_gates {n T g : ℕ} (hg : g < (gates tm n T).length) : + (gates tm n T)[g] = gateCode tm n T g := by + simp [gates] + +private theorem length_gates (n T : ℕ) : (gates tm n T).length = numGates k State T := by + simp [gates] + +/-- **The tableau formula on labels is equisatisfiable with the Tseitin formula of the tableau** +with the same fixed inputs and output. -/ +theorem satisfiable_succinctCNF_iff_tseitin {n T : ℕ} (hn : n < 2 * T) {x : List Bool} + (hx : x.length ≤ n) : + (succinctCNF tm x n T).Satisfiable ↔ + (tseitinSatCNF x n (gates tm n T) (outWire k State n T)).Satisfiable := by + have hcb : 2 * T < 2 ^ colBits T := Nat.lt_size_self _ + have hpay {p : ℕ} (hp : p < 2 ^ colBits T) : + p < 2 ^ (slotBits k State + colBits T + timeBits T) := by + have : 2 ^ colBits T ≤ 2 ^ (slotBits k State + colBits T + timeBits T) := + Nat.pow_le_pow_right (by norm_num) (by omega) + omega + have hout_ok : (Loc.gf (k := k) (State := State) (.out (.one true)) true 7).ColOK T 0 := rfl + constructor + · rintro ⟨σ, hσ⟩ + rw [succinctCNF, CNF.eval_map_eq_true_iff] at hσ + have hfield {j tag p : ℕ} (hj : j < 4) (htag : tag < 4) + (hp : p < 2 ^ (slotBits k State + colBits T + timeBits T)) : + (clauseOf tm x n T j tag p).eval σ = true := by + rw [← clauseAt_fields tm x n T p hj htag] + exact hσ _ (List.mem_range.2 (fields_lt_clauseBits hj htag hp)) + refine ⟨fun w => σ (wireLabel k State n T w), + (eval_tseitinSatCNF_eq_true_iff _ _ _ _ _).2 ⟨?_, fun i hi => ?_, ?_⟩⟩ + · have := hfield (j := 0) (tag := 3) (p := 0) (by omega) (by omega) (Nat.two_pow_pos _) + simpa [clauseOf, outWire, wireLabel_wire hout_ok, outLabel] using this + · have := hfield (j := 0) (tag := 1) (p := i) (by omega) (by omega) (hpay (by omega)) + simpa [clauseOf, hi, wireLabel_of_lt (show i < n by omega)] using this + · rw [eval_tseitinCNF_eq_true_iff] + intro g hg + rw [getElem_gates] + rw [length_gates] at hg + by_cases hg₂ : g < 2 + · have hcode : gateCode tm n T g = (.const (decide (g = 1)), []) := by + simp [gateCode, hg₂] + have hc := (forall_eval_tseitinClause_iff (.const (decide (g = 1))) + (constLabel (decide (g = 1))) 0 0 σ).1 fun j => by + have := hfield (j := j) (tag := 0) (p := g) (by omega) (by omega) (hpay (by omega)) + simpa [clauseOf, j.isLt, hg₂] using this + have hw : n + g = n + (decide (g = 1)).toNat := by + rcases (show g = 0 ∨ g = 1 by omega) with rfl | rfl <;> rfl + rw [hcode, hw, wireLabel_const] + exact hc + · obtain ⟨t, ht, l, c, hl, hw⟩ := exists_wire_eq (k := k) (State := State) (n := n) + (Nat.le_of_not_lt hg₂) hg + have hwn : n + (wire n T t l c - n) = wire n T t l c := by simp only [wire]; omega + have hp := fields_lt (d := timeBits T) (slot_lt_two_pow l) + (lt_of_lt_of_le hl.lt (numCols_le_two_pow T)) (lt_two_pow_timeBits ht) + have hpos := (forall_eval_posClause_iff tm σ n T t c l).1 fun j => by + rw [← clauseOf_pos tm x j ht hl] + exact hfield (by omega) (by omega) hp + obtain ⟨hra, hrb⟩ := table_inWindow (n := n) hl.lt tm l + have hv := value_gateAt tm (fun w => σ (wireLabel k State n T w)) n T t c l + rw [← hw, gateCode_wire tm hl, hv, hwn, wireLabel_wire hl, hpos, + wireLabel_resolve hra, wireLabel_resolve hrb] + · rintro ⟨τ, hτ⟩ + obtain ⟨hτo, hτx, hτφ⟩ := (eval_tseitinSatCNF_eq_true_iff _ _ _ _ _).1 hτ + rw [eval_tseitinCNF_eq_true_iff] at hτφ + have hgate {g : ℕ} (hg : g < numGates k State T) : + τ (n + g) = (gateCode tm n T g).1.value (τ ((gateCode tm n T g).2.getD 0 0)) + (τ ((gateCode tm n T g).2.getD 1 0)) := by + have := hτφ g (by rwa [length_gates]) + rwa [getElem_gates] at this + have h2 : 2 ≤ numGates k State T := by simp only [numGates]; omega + have hwire {t c : ℕ} {l : Loc k State} (hl : l.ColOK T c) (ht : t ≤ T) : + τ (wire n T t l c) = if t = 0 then initBit tm T c l + else (table tm l).1.value (τ ((table tm l).2.1.resolve n T t c)) + (τ ((table tm l).2.2.resolve n T t c)) := by + have hwn : n + (wire n T t l c - n) = wire n T t l c := by simp only [wire]; omega + rw [← hwn, hgate (wire_sub_lt_numGates hl ht), gateCode_wire tm hl, value_gateAt] + refine ⟨fun lab => τ (wireOf (k := k) (State := State) n T lab), ?_⟩ + rw [succinctCNF, CNF.eval_map_eq_true_iff] + intro idx _ + refine eval_clauseOf tm (fun b => ?_) (fun i hi => ?_) ?_ (fun t ht c l hl => ?_) _ _ _ + · rw [wireOf_constLabel] + cases b + · simpa [gateCode, Op.value] using hgate (g := 0) (by omega) + · simpa [gateCode, Op.value] using hgate (g := 1) (by omega) + · simpa using hτx i hi + · simpa [outLabel, outWire, wireOf_posLabel _ (show 0 < 2 ^ colBits T from Nat.two_pow_pos _)] + using hτo + · obtain ⟨hra, hrb⟩ := table_inWindow (n := n) hl.lt tm l + rw [wireOf_posLabel _ (lt_of_lt_of_le hl.lt (numCols_le_two_pow T)), hwire hl ht, + wireOf_label hra, wireOf_label hrb] + +/-- **The tableau formula on labels is satisfiable** exactly when some input of length `n` +extending `x` makes `tm` write exactly `[true]` within `T` steps, for `n < 2 T`. -/ +theorem satisfiable_succinctCNF_iff {n T : ℕ} (hn : n < 2 * T) {x : List Bool} + (hx : x.length ≤ n) : + (succinctCNF tm x n T).Satisfiable ↔ ∃ y : List Bool, y.length = n ∧ x <+: y ∧ + OutView.ofList (tm.runFrom (tm.initCfg y) T).output = .one true := by + rw [satisfiable_succinctCNF_iff_tseitin tm hn hx, + satisfiable_tseitinSatCNF_iff (isCodeFrom_gates tm n T) hx + (by simpa [length_gates] using outWire_lt (k := k) (State := State) n T)] + refine exists_congr fun y => and_congr_right fun hy => and_congr_right fun _ => ?_ + subst hy + rw [evalOutput_gates tm (by omega), decide_eq_true_iff] + +/-! ### Size -/ + +/-- **Every clause of the formula has three literals.** -/ +theorem length_of_mem_succinctCNF {x : List Bool} {n T : ℕ} {C : Clause ℕ} + (hC : C ∈ succinctCNF tm x n T) : C.length = 3 := by + obtain ⟨idx, -, rfl⟩ := List.mem_map.1 hC + unfold clauseAt clauseOf + split_ifs + · exact length_tseitinClause _ _ _ _ _ + · rfl + · rfl + · rfl + · split + · split_ifs + · unfold posClause + split_ifs <;> exact length_tseitinClause _ _ _ _ _ + · rfl + · rfl + · rfl + · rfl + · rfl + +/-- The formula has `2 ^ clauseBits k State T` clauses. -/ +theorem length_succinctCNF (x : List Bool) (n T : ℕ) : + (succinctCNF tm x n T).length = 2 ^ clauseBits k State T := by + simp [succinctCNF] + +variable (k State) in +/-- The width of a label: the tag, then the slot, the column and the block. -/ +noncomputable def labelBits (T : ℕ) : ℕ := 2 + (slotBits k State + colBits T + timeBits T) + +omit [DecidableEq State] in +/-- The labels of positions of blocks `t ≤ T` fit in `labelBits` bits. -/ +theorem posLabel_lt {T t c : ℕ} (l : Loc k State) (hc : c < 2 ^ colBits T) (ht : t ≤ T) : + posLabel k State T t c l < 2 ^ labelBits k State T := by + have := fields_lt (slot_lt_two_pow l) hc (lt_two_pow_timeBits ht) + rw [labelBits, Nat.pow_add] + simp only [posLabel] + omega + +omit [DecidableEq State] in +/-- The labels of the inputs `i < 2 T` fit in `labelBits` bits. -/ +theorem inputLabel_lt {T i : ℕ} (hi : i < 2 * T) : inputLabel i < 2 ^ labelBits k State T := by + have hcb : 2 * T < 2 ^ colBits T := Nat.lt_size_self _ + have : 2 ^ colBits T ≤ 2 ^ (slotBits k State + colBits T + timeBits T) := + Nat.pow_le_pow_right (by norm_num) (by omega) + rw [labelBits, Nat.pow_add] + simp only [inputLabel] + omega + +omit [DecidableEq State] in +/-- The labels of the constants fit in `labelBits` bits, for `1 ≤ T`. -/ +theorem constLabel_lt {T : ℕ} (hT : 1 ≤ T) (b : Bool) : constLabel b < 2 ^ labelBits k State T := by + have h := inputLabel_lt (k := k) (State := State) (T := T) (i := 1) (by omega) + cases b <;> simp only [constLabel, inputLabel, Bool.toNat_false, Bool.toNat_true] at h ⊢ <;> omega + +omit [DecidableEq State] in +/-- The labels of the references of a gate in a column of the window fit in `labelBits` bits. -/ +theorem Ref.label_lt {n T t c : ℕ} (hn : n < 2 * T) (ht : t ≤ T) {r : Ref k State} + (hr : r.InWindow n T c) : r.label n T t c < 2 ^ labelBits k State T := by + have hT : 1 ≤ T := by omega + rcases r with _ | _ | _ | ⟨p, l, cs, g⟩ + · exact constLabel_lt hT _ + · exact constLabel_lt hT _ + · simp only [Ref.label] + split_ifs + · exact inputLabel_lt (by omega) + · exact constLabel_lt hT _ + · simp only [Ref.InWindow] at hr + by_cases hg : g.test n T c = true + · simp only [Ref.label, hg, ↓reduceIte] + exact posLabel_lt _ (lt_of_le_of_lt (Loc.labelCol_le l _) + (lt_of_lt_of_le (hr hg) (numCols_le_two_pow T))) (by split <;> omega) + · simp only [Ref.label, hg, Bool.false_eq_true, ↓reduceIte] + exact constLabel_lt hT _ + +omit [DecidableEq State] in +private theorem mem_tseitinClause {op : Op} {w a b v : ℕ} {s : Bool} {j : Fin 3} + (h : (v, s) ∈ tseitinClause op w a b j) : v = w ∨ v = a ∨ v = b := by + fin_cases j <;> cases op <;> simp [tseitinClause] at h <;> omega + +/-- **Every variable of the formula is below `2 ^ labelBits k State T`**, for `n < 2 T`: labels +have `labelBits k State T` bits. -/ +theorem lt_two_pow_labelBits_of_mem_vars {n T : ℕ} (hn : n < 2 * T) {x : List Bool} + (hx : x.length ≤ n) {v : ℕ} (hv : v ∈ (succinctCNF tm x n T).vars) : + v < 2 ^ labelBits k State T := by + have hT : 1 ≤ T := by omega + have h0 : 0 < 2 ^ labelBits k State T := Nat.two_pow_pos _ + obtain ⟨C, hC, s, hvC⟩ := CNF.mem_vars.1 hv + obtain ⟨idx, -, rfl⟩ := List.mem_map.1 hC + unfold clauseAt clauseOf at hvC + split_ifs at hvC with hj h₀ hp h₁ hp' h₂ h₃ + · rcases mem_tseitinClause hvC with rfl | rfl | rfl + · exact constLabel_lt hT _ + · exact h0 + · exact h0 + · simp [tautology] at hvC; omega + · simp only [unitClause, List.mem_cons, Prod.mk.injEq, List.not_mem_nil, or_false, + or_self] at hvC + rw [hvC.1] + exact inputLabel_lt (by omega) + · simp [tautology] at hvC; omega + · split at hvC + · rename_i l _ + split_ifs at hvC with h + · have hc := lt_of_lt_of_le h.2.lt (numCols_le_two_pow T) + obtain ⟨hra, hrb⟩ := table_inWindow (n := n) h.2.lt tm l + unfold posClause at hvC + split_ifs at hvC <;> rcases mem_tseitinClause hvC with rfl | rfl | rfl <;> + first + | exact posLabel_lt _ hc h.1 + | exact h0 + | exact Ref.label_lt hn h.1 hra + | exact Ref.label_lt hn h.1 hrb + · simp [tautology] at hvC; omega + · simp [tautology] at hvC; omega + · simp only [unitClause, List.mem_cons, Prod.mk.injEq, List.not_mem_nil, or_false, + or_self] at hvC + rw [hvC.1] + exact posLabel_lt _ (Nat.two_pow_pos _) le_rfl + · simp [tautology] at hvC; omega + · simp [tautology] at hvC; omega + +end Cslib.Circuits.Boolean.Tableau diff --git a/ComplexityTests/TableauSuccinct.lean b/ComplexityTests/TableauSuccinct.lean new file mode 100644 index 000000000..92094396c --- /dev/null +++ b/ComplexityTests/TableauSuccinct.lean @@ -0,0 +1,73 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.Circuit.Boolean.Tableau.Succinct + +/-! +# The tableau formula on bit-field labels + +Both sides of `Tableau.satisfiable_succinctCNF_iff` occur. A machine that writes `true` in its +first step gives a satisfiable formula for one step on one input, whatever the input is fixed to. +A machine that never writes gives an unsatisfiable one. The size results apply to any machine. +-/ + +namespace ComplexityTests.TableauSuccinct + +open Cslib Cslib.Circuits Cslib.Circuits.Boolean Cslib.Logic.PL Turing Turing.MultiTapeTM + +/-- Write `true` in the first step, then run forever without writing. -/ +private def emitLoop : MultiTapeTM 0 Bool Bool := + MultiTapeTM.ofTr false fun q _ _ => + { inputTape := 0, workTapes := Fin.elim0, output := if q then none else some true, + state := some true } + +private theorem output_emitLoop (y : List Bool) : + (emitLoop.runFrom (emitLoop.initCfg y) 1).output = [true] := by + change (emitLoop.step (emitLoop.initCfg y)).output = _ + rw [MultiTapeTM.step_of_state (q := false) rfl] + simp [emitLoop, Action.apply] + +-- One step on one input fixed to `b`: the machine has written `[true]`. +example (b : Bool) : (Tableau.succinctCNF emitLoop [b] 1 1).Satisfiable := + (Tableau.satisfiable_succinctCNF_iff emitLoop (by norm_num) (by simp)).2 + ⟨[b], rfl, List.prefix_refl _, by rw [output_emitLoop]; rfl⟩ + +/-- Never write. -/ +private def silent : MultiTapeTM 0 Bool Bool := + MultiTapeTM.ofTr false fun _ _ _ => + { inputTape := 0, workTapes := Fin.elim0, output := none, state := some false } + +-- The silent machine has written nothing after one step, so no input is accepted. +example : ¬ (Tableau.succinctCNF silent [] 1 1).Satisfiable := by + rw [Tableau.satisfiable_succinctCNF_iff silent (by norm_num) (by simp)] + rintro ⟨y, -, -, h⟩ + have hout : (silent.runFrom (silent.initCfg y) 1).output = [] := by + change (silent.step (silent.initCfg y)).output = _ + rw [MultiTapeTM.step_of_state (q := false) rfl] + simp [silent, Action.apply] + rw [hout] at h + exact absurd h (by decide) + +variable {k : ℕ} {State : Type} [Fintype State] [DecidableEq State] + (tm : MultiTapeTM k Bool State) + +-- The formula has `2 ^ clauseBits` clauses of three literals, on labels of `labelBits` bits. +example (x : List Bool) (n T : ℕ) (hn : n < 2 * T) (hx : x.length ≤ n) : + (Tableau.succinctCNF tm x n T).length = 2 ^ Tableau.clauseBits k State T ∧ + (∀ C ∈ Tableau.succinctCNF tm x n T, C.length = 3) ∧ + ∀ v ∈ (Tableau.succinctCNF tm x n T).vars, v < 2 ^ Tableau.labelBits k State T := + ⟨Tableau.length_succinctCNF tm x n T, fun _ hC => Tableau.length_of_mem_succinctCNF tm hC, + fun _ hv => Tableau.lt_two_pow_labelBits_of_mem_vars tm hn hx hv⟩ + +-- The clause numbered `j + 4 * (2 + 4 * payload)` is clause `j` of the position in the payload. +example (x : List Bool) (n T t c : ℕ) (l : Tableau.Loc k State) (j : Fin 3) (ht : t ≤ T) + (hl : l.ColOK T c) : + Tableau.clauseAt tm x n T (j + 4 * (2 + 4 * + (l.slot + 2 ^ Tableau.slotBits k State * (c + 2 ^ Tableau.colBits T * t)))) = + Tableau.posClause tm n T t c l j := by + rw [Tableau.clauseAt_fields tm x n T _ (by omega) (by omega), Tableau.clauseOf_pos tm x j ht hl] + +end ComplexityTests.TableauSuccinct From 5ba1a7b5e4ed7484ab79e401c6505ab41c906722 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 13:25:11 -0400 Subject: [PATCH 3/8] refactor(Circuit/Boolean): share Op.value across gate evaluators Move `Op.value` and `Op.value_congr` from the Tseitin module into a small `Circuit/Boolean/OpValue` module, add `Op.interpretation_eq_value`, and make `EvaluationTime` and `Tableau.Correct` use them instead of their private copies `gateValue` and `opVal` (review of #2082). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Circuit/Boolean/EvaluationTime.lean | 25 +++----- .../Circuit/Boolean/OpValue.lean | 59 +++++++++++++++++++ .../Circuit/Boolean/Tableau/Correct.lean | 42 ++++++------- .../Circuit/Boolean/Tseitin.lean | 27 ++------- 5 files changed, 91 insertions(+), 63 deletions(-) create mode 100644 Complexity/Computability/Circuit/Boolean/OpValue.lean diff --git a/Complexity.lean b/Complexity.lean index d4ed7bed7..4c27fcc14 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -785,6 +785,7 @@ public import Complexity.Computability.Circuit.Boolean.Nechiporuk public import Complexity.Computability.Circuit.Boolean.NegationNormalForm public import Complexity.Computability.Circuit.Boolean.NonconstantCost public import Complexity.Computability.Circuit.Boolean.Nondeterminism +public import Complexity.Computability.Circuit.Boolean.OpValue public import Complexity.Computability.Circuit.Boolean.Oracle public import Complexity.Computability.Circuit.Boolean.PPoly public import Complexity.Computability.Circuit.Boolean.PPolyHard diff --git a/Complexity/Computability/Circuit/Boolean/EvaluationTime.lean b/Complexity/Computability/Circuit/Boolean/EvaluationTime.lean index db5d2f6fb..ee71b4d39 100644 --- a/Complexity/Computability/Circuit/Boolean/EvaluationTime.lean +++ b/Complexity/Computability/Circuit/Boolean/EvaluationTime.lean @@ -11,6 +11,7 @@ public import Complexity.Computability.Circuit.Boolean.Encoding public import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime public import Complexity.Foundations.Data.BitString.List public import Complexity.Computability.Machines.Turing.MultiTape.PolyTime +import Complexity.Computability.Circuit.Boolean.OpValue import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Fold import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.BitString @@ -199,17 +200,6 @@ open Turing.MultiTapeTM ComputableInPolyTime /-! ### Polynomial time -/ -/-- The value of a De Morgan gate from its operation and its first two arguments. -/ -private def gateValue : Op × Bool × Bool → Bool - | (.const b, _, _) => b - | (.not, a, _) => !a - | (.and, a, b) => a && b - | (.or, a, b) => a || b - -private theorem interpretation_eq_gateValue (op : Op) (f : ℕ → Bool) : - (interpretation op fun a => f a) = gateValue (op, f 0, f 1) := by - cases op <;> rfl - /-- One evaluation step: append the value of a gate. -/ private theorem computableInPolyTime_step : ComputableInPolyTime (fun p : List Bool × (Op × List ℕ) => @@ -217,20 +207,21 @@ private theorem computableInPolyTime_step : (encPair encBits encGate) encBits := by have heq : (fun p : List Bool × (Op × List ℕ) => p.1 ++ [interpretation p.2.1 fun a => p.2.2[a.val]?.elim false (p.1.getD · false)]) = - fun p => p.1 ++ [gateValue (p.2.1, p.2.2[0]?.elim false (p.1.getD · false), - p.2.2[1]?.elim false (p.1.getD · false))] := + fun p => p.1 ++ [p.2.1.value (p.2.2[0]?.elim false (p.1.getD · false)) + (p.2.2[1]?.elim false (p.1.getD · false))] := funext fun p => by - rw [interpretation_eq_gateValue p.2.1 fun i => p.2.2[i]?.elim false (p.1.getD · false)] + rw [Op.interpretation_eq_value p.2.1 fun i => p.2.2[i]?.elim false (p.1.getD · false)] rw [heq] have hargs (i : ℕ) : ComputableInPolyTime (fun p : List Bool × (Op × List ℕ) => p.2.2[i]?.elim false (p.1.getD · false)) (encPair encBits encGate) encBool := optionElimWithInput (getElem? (snd _ _).snd' (const i)) (const false) (listGetD_binary false (fst _ _).fst' (snd _ _)) have hvalue : ComputableInPolyTime (fun p : List Bool × (Op × List ℕ) => - gateValue (p.2.1, p.2.2[0]?.elim false (p.1.getD · false), - p.2.2[1]?.elim false (p.1.getD · false))) + p.2.1.value (p.2.2[0]?.elim false (p.1.getD · false)) + (p.2.2[1]?.elim false (p.1.getD · false))) (encPair encBits encGate) encBool := - (of_finite (encIn := encPair encOp (encPair encBool encBool)) gateValue).comp + (of_finite (encIn := encPair encOp (encPair encBool encBool)) + fun p : Op × Bool × Bool => p.1.value p.2.1 p.2.2).comp ((snd _ _).fst'.prod ((hargs 0).prod (hargs 1))) exact bitsAppend (fst _ _) (bitsCons hvalue (const [])) diff --git a/Complexity/Computability/Circuit/Boolean/OpValue.lean b/Complexity/Computability/Circuit/Boolean/OpValue.lean new file mode 100644 index 000000000..5f6ab3f60 --- /dev/null +++ b/Complexity/Computability/Circuit/Boolean/OpValue.lean @@ -0,0 +1,59 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Cslib.Computability.Circuit.Boolean.Basic + +/-! +# The value of a De Morgan gate + +A De Morgan operation has at most two arguments, so its value is a function of two bits. +`Op.value op a b` is that function, with the arguments beyond the arity of `op` ignored +(`Op.value_congr`). It agrees with the Boolean interpretation of `op` +(`Op.interpretation_eq_value`), and gives evaluators and encoders of gate lists one shared +semantics of the gates. + +## Main definitions + +* `Cslib.Circuits.Boolean.Op.value`: the value of a De Morgan gate on its first two arguments. + +## Main results + +* `Cslib.Circuits.Boolean.Op.value_congr`: the value ignores the arguments beyond the arity. +* `Cslib.Circuits.Boolean.Op.interpretation_eq_value`: the Boolean interpretation of a gate is its + value on its first two arguments. +-/ + +@[expose] public section + +namespace Cslib.Circuits.Boolean + +/-- The value of a De Morgan operation whose first two arguments carry `a` and `b`; arguments +beyond its arity are ignored. -/ +def Op.value : Op → Bool → Bool → Bool + | .const v, _, _ => v + | .not, a, _ => !a + | .and, a, b => a && b + | .or, a, b => a || b + +/-- The value of a gate does not depend on the arguments beyond its arity. -/ +theorem Op.value_congr (op : Op) {a a' b b' : Bool} (ha : 1 ≤ signature.Arity op → a = a') + (hb : 2 ≤ signature.Arity op → b = b') : op.value a b = op.value a' b' := by + cases op with + | const v => rfl + | not => simp only [Op.value, ha (by decide)] + | and => simp only [Op.value, ha (by decide), hb (by decide)] + | or => simp only [Op.value, ha (by decide), hb (by decide)] + +/-- The Boolean interpretation of a gate whose `i`th argument carries `f i` is its value on +`f 0` and `f 1`. -/ +theorem Op.interpretation_eq_value (op : Op) (f : ℕ → Bool) : + (interpretation op fun i => f i) = op.value (f 0) (f 1) := by + cases op <;> rfl + +end Cslib.Circuits.Boolean diff --git a/Complexity/Computability/Circuit/Boolean/Tableau/Correct.lean b/Complexity/Computability/Circuit/Boolean/Tableau/Correct.lean index f239b899b..10049ca82 100644 --- a/Complexity/Computability/Circuit/Boolean/Tableau/Correct.lean +++ b/Complexity/Computability/Circuit/Boolean/Tableau/Correct.lean @@ -9,6 +9,7 @@ module public import Complexity.Computability.Circuit.Boolean.Tableau.Basic public import Complexity.Computability.Circuit.Boolean.EvaluationTime +import Complexity.Computability.Circuit.Boolean.OpValue import Complexity.Foundations.Data.List.Lookup import Mathlib.Data.List.GetD @@ -60,15 +61,8 @@ variable {k : ℕ} {State : Type} [Fintype State] [DecidableEq State] /-! ### Values of wires -/ -/-- The value of a De Morgan gate from its operation and its first two arguments. -/ -private def opVal : Op → Bool → Bool → Bool - | .const b, _, _ => b - | .not, a, _ => !a - | .and, a, b => a && b - | .or, a, b => a || b - private theorem interpretation_argsOf (op : Op) (a b : ℕ) (f : ℕ → Bool) : - (interpretation op fun i => (argsOf op a b)[i.val]?.elim false f) = opVal op (f a) (f b) := by + (interpretation op fun i => (argsOf op a b)[i.val]?.elim false f) = op.value (f a) (f b) := by cases op <;> rfl /-- The value of wire `w` of the tableau of `tm` for `T` steps, on the input `x`. -/ @@ -113,7 +107,7 @@ the operation of the gate applied to the values of its references. -/ private theorem val_wire {t c : ℕ} {l : Loc k State} (hl : l.ColOK T c) (ht : t ≤ T) : val tm T x (wire x.length T t l c) = if t = 0 then initBit tm T c l - else opVal (table tm l).1 (val tm T x ((table tm l).2.1.resolve x.length T t c)) + else (table tm l).1.value (val tm T x ((table tm l).2.1.resolve x.length T t c)) (val tm T x ((table tm l).2.2.resolve x.length T t c)) := by have hg : wire x.length T t l c - x.length < numGates k State T := by rw [wire_sub] @@ -283,7 +277,7 @@ private theorem val_nh (htT : t + 1 ≤ T) {c : ℕ} (hc : c < numCols T) (i : F val tm T x (wire (State := State) x.length T (t + 1) (.scan (.nh i)) c) = !colSem T (tm.runFrom (tm.initCfg x) t) c (.head i) := by rw [val_wire tm (l := .scan (.nh i)) hc htT] - simp only [Nat.add_one_ne_zero, ↓reduceIte, table, opVal] + simp only [Nat.add_one_ne_zero, ↓reduceIte, table, Op.value] rw [val_colF_same tm hinv hc] simp [Guard.test] @@ -293,7 +287,7 @@ private theorem val_nx (htT : t + 1 ≤ T) {c : ℕ} (hc : c < numCols T) : val tm T x (wire (State := State) x.length T (t + 1) (.scan (.nx : ScanLoc k)) c) = !(inputAt x c).getD false := by rw [val_wire tm (l := .scan .nx) hc htT] - simp only [Nat.add_one_ne_zero, ↓reduceIte, table, opVal] + simp only [Nat.add_one_ne_zero, ↓reduceIte, table, Op.value] rw [val_resolve_inp] /-- The contribution of a column to a scanned quantity. -/ @@ -301,7 +295,7 @@ private theorem val_term (htT : t + 1 ≤ T) {c : ℕ} (hc : c < numCols T) (q : val tm T x (wire (State := State) x.length T (t + 1) (.scan (.term q)) c) = true ↔ termSem T x (tm.runFrom (tm.initCfg x) t) c q := by rw [val_wire tm (l := .scan (.term q)) hc htT] - simp only [Nat.add_one_ne_zero, ↓reduceIte, table, opVal] + simp only [Nat.add_one_ne_zero, ↓reduceIte, table, Op.value] rcases q with (_ | (_ | _)) | ⟨i, s⟩ · simp only [scanTerm] rw [val_colF_same tm hinv hc, val_resolve_tru] @@ -335,7 +329,7 @@ private theorem val_acc (htT : t + 1 ≤ T) (q : ScanQ k) : induction c with | zero => rw [val_wire tm (l := .scan (.acc q)) hc htT] - simp only [Nat.add_one_ne_zero, ↓reduceIte, table, opVal, scanAt] + simp only [Nat.add_one_ne_zero, ↓reduceIte, table, Op.value, scanAt] rw [val_resolve_node, val_resolve_node] simp only [Guard.test, Bool.true_and, ↓reduceIte, ColSel.apply, Bool.false_eq_true] simp only [show decide (1 ≤ 0) = false from rfl, Bool.false_and, Bool.false_or] @@ -345,7 +339,7 @@ private theorem val_acc (htT : t + 1 ≤ T) (q : ScanQ k) : exact h⟩ | succ c ih => rw [val_wire tm (l := .scan (.acc q)) hc htT] - simp only [Nat.add_one_ne_zero, ↓reduceIte, table, opVal, scanAt] + simp only [Nat.add_one_ne_zero, ↓reduceIte, table, Op.value, scanAt] rw [val_resolve_node, val_resolve_node] simp only [Guard.test, Bool.false_eq_true, ↓reduceIte, Bool.true_and, ColSel.apply, Nat.add_sub_cancel, show 1 ≤ c + 1 by omega, decide_true, Bool.or_eq_true] @@ -400,7 +394,7 @@ private theorem val_sit (hn : x.length < 2 * T) (htT : t + 1 ≤ T) (j : Fin (nu induction i using Fin.induction with | zero => rw [val_wire tm (l := .glob (.sit j 0)) rfl htT] - simp only [Nat.add_one_ne_zero, ↓reduceIte, table, opVal, Fin.cases_zero] + simp only [Nat.add_one_ne_zero, ↓reduceIte, table, Op.value, Fin.cases_zero] rw [val_gfF tm hinv, scanAt, val_resolve_node, Bool.and_eq_true] simp only [Guard.test, Bool.true_and, Bool.false_eq_true, ↓reduceIte, ColSel.apply] rw [val_acc_last tm hinv hn htT] @@ -408,7 +402,7 @@ private theorem val_sit (hn : x.length < 2 * T) (htT : t + 1 ≤ T) (j : Fin (nu simp [gfSem, readSem] | succ i ih => rw [val_wire tm (l := .glob (.sit j i.succ)) rfl htT] - simp only [Nat.add_one_ne_zero, ↓reduceIte, table, opVal, Fin.cases_succ] + simp only [Nat.add_one_ne_zero, ↓reduceIte, table, Op.value, Fin.cases_succ] rw [val_resolve_node, scanAt, val_resolve_node] simp only [Guard.test, Bool.true_and, Bool.false_eq_true, ↓reduceIte, ColSel.apply, Bool.and_eq_true] @@ -440,11 +434,11 @@ private theorem val_attr (hn : x.length < 2 * T) (htT : t + 1 ≤ T) (v : Attr k induction j using Fin.induction with | zero => rw [val_wire tm (l := .glob (.attr v 0)) rfl htT] - simp only [Nat.add_one_ne_zero, ↓reduceIte, table, opVal, Fin.cases_zero, val_resolve_fls, + simp only [Nat.add_one_ne_zero, ↓reduceIte, table, Op.value, Fin.cases_zero, val_resolve_fls, Bool.or_false, Bool.false_eq_true, Fin.val_zero, Nat.not_lt_zero, false_and, exists_false] | succ j ih => rw [val_wire tm (l := .glob (.attr v j.succ)) rfl htT] - simp only [Nat.add_one_ne_zero, ↓reduceIte, table, opVal, Fin.cases_succ] + simp only [Nat.add_one_ne_zero, ↓reduceIte, table, Op.value, Fin.cases_succ] rw [val_resolve_node, Bool.or_eq_true] simp only [Guard.test, Bool.true_and, Bool.false_eq_true, ↓reduceIte, ColSel.apply] rw [ih] @@ -499,7 +493,7 @@ private theorem val_gf_and (htT : t + 1 ≤ T) (f : GFeat State) (j : Fin 8) : val tm T x (wire (k := k) x.length T (t + 1) (.gf f false j) 0) = termVal tm T x (t + 1) 0 ((gfTerms f).getD j (.fls, .fls)) := by rw [val_wire tm (l := .gf f false j) rfl htT] - simp only [Nat.add_one_ne_zero, ↓reduceIte, table, opVal, termVal] + simp only [Nat.add_one_ne_zero, ↓reduceIte, table, Op.value, termVal] omit hinv in private theorem val_gf_or (htT : t + 1 ≤ T) (f : GFeat State) (j : Fin 8) : @@ -509,7 +503,7 @@ private theorem val_gf_or (htT : t + 1 ≤ T) (f : GFeat State) (j : Fin 8) : induction j using Fin.induction with | zero => rw [val_wire tm (l := .gf f true 0) rfl htT] - simp only [Nat.add_one_ne_zero, ↓reduceIte, table, opVal, Fin.cases_zero, val_resolve_fls, + simp only [Nat.add_one_ne_zero, ↓reduceIte, table, Op.value, Fin.cases_zero, val_resolve_fls, Bool.false_or] rw [val_resolve_node] simp only [Guard.test, Bool.true_and, Bool.false_eq_true, ↓reduceIte, ColSel.apply] @@ -517,7 +511,7 @@ private theorem val_gf_or (htT : t + 1 ≤ T) (f : GFeat State) (j : Fin 8) : exact ⟨fun h => ⟨0, le_rfl, h⟩, fun ⟨j', hj', h⟩ => by rwa [Fin.le_zero_iff.1 hj'] at h⟩ | succ j ih => rw [val_wire tm (l := .gf f true j.succ) rfl htT] - simp only [Nat.add_one_ne_zero, ↓reduceIte, table, opVal, Fin.cases_succ] + simp only [Nat.add_one_ne_zero, ↓reduceIte, table, Op.value, Fin.cases_succ] rw [val_resolve_node, val_resolve_node, Bool.or_eq_true] simp only [Guard.test, Bool.true_and, Bool.false_eq_true, ↓reduceIte, ColSel.apply] rw [ih, val_gf_and tm htT] @@ -538,7 +532,7 @@ private theorem val_col_and (htT : t + 1 ≤ T) {c : ℕ} (hc : c < numCols T) val tm T x (wire (State := State) x.length T (t + 1) (.col f false j) c) = termVal tm T x (t + 1) c ((colTerms f).getD j (.fls, .fls)) := by rw [val_wire tm (l := .col f false j) hc htT] - simp only [Nat.add_one_ne_zero, ↓reduceIte, table, opVal, termVal] + simp only [Nat.add_one_ne_zero, ↓reduceIte, table, Op.value, termVal] omit hinv in private theorem val_col_or (htT : t + 1 ≤ T) {c : ℕ} (hc : c < numCols T) @@ -549,7 +543,7 @@ private theorem val_col_or (htT : t + 1 ≤ T) {c : ℕ} (hc : c < numCols T) induction j using Fin.induction with | zero => rw [val_wire tm (l := .col f true 0) hc htT] - simp only [Nat.add_one_ne_zero, ↓reduceIte, table, opVal, Fin.cases_zero, val_resolve_fls, + simp only [Nat.add_one_ne_zero, ↓reduceIte, table, Op.value, Fin.cases_zero, val_resolve_fls, Bool.false_or] rw [val_resolve_node] simp only [Guard.test, Bool.true_and, Bool.false_eq_true, ↓reduceIte, ColSel.apply] @@ -557,7 +551,7 @@ private theorem val_col_or (htT : t + 1 ≤ T) {c : ℕ} (hc : c < numCols T) exact ⟨fun h => ⟨0, le_rfl, h⟩, fun ⟨j', hj', h⟩ => by rwa [Fin.le_zero_iff.1 hj'] at h⟩ | succ j ih => rw [val_wire tm (l := .col f true j.succ) hc htT] - simp only [Nat.add_one_ne_zero, ↓reduceIte, table, opVal, Fin.cases_succ] + simp only [Nat.add_one_ne_zero, ↓reduceIte, table, Op.value, Fin.cases_succ] rw [val_resolve_node, val_resolve_node, Bool.or_eq_true] simp only [Guard.test, Bool.true_and, Bool.false_eq_true, ↓reduceIte, ColSel.apply] rw [ih, val_col_and tm htT hc] diff --git a/Complexity/Computability/Circuit/Boolean/Tseitin.lean b/Complexity/Computability/Circuit/Boolean/Tseitin.lean index e0304e4b9..ad2bc08ce 100644 --- a/Complexity/Computability/Circuit/Boolean/Tseitin.lean +++ b/Complexity/Computability/Circuit/Boolean/Tseitin.lean @@ -8,6 +8,7 @@ module -- upstream: candidate public import Complexity.Computability.Circuit.Boolean.EvaluationTime +public import Complexity.Computability.Circuit.Boolean.OpValue public import Complexity.Logics.Propositional.CNF import Mathlib.Data.List.GetD @@ -17,10 +18,10 @@ import Mathlib.Data.List.GetD Tseitin's encoding turns a circuit into a formula in conjunctive normal form with one variable per wire [Tseitin][Tseitin1983], [Arora and Barak, Section 6.1][AroraBarak09]. A gate `w = op(a, b)` contributes the clauses `tseitinClause op w a b`, which hold together exactly when -`w` carries the value of the gate (`forall_eval_tseitinClause_iff`). The clauses of the gates of a -De Morgan gate list (`Cslib.Circuits.Program.code`) form `tseitinCNF n gates`, with gate `g` at -wire `n + g`; an assignment satisfies it exactly when it is the evaluation of the list on its own -values at the inputs (`eval_tseitinCNF_eq_true_iff_evalCode`). +`w` carries the value `Op.value op a b` of the gate (`forall_eval_tseitinClause_iff`). The clauses +of the gates of a De Morgan gate list (`Cslib.Circuits.Program.code`) form `tseitinCNF n gates`, +with gate `g` at wire `n + g`; an assignment satisfies it exactly when it is the evaluation of the +list on its own values at the inputs (`eval_tseitinCNF_eq_true_iff_evalCode`). `tseitinSatCNF x n gates o` adds unit clauses that fix the first inputs to the bits of `x` and the wire `o` to `true`. It is satisfiable exactly when some input extending `x` makes wire `o` @@ -37,7 +38,6 @@ uniform. ## Main definitions -* `Cslib.Circuits.Boolean.Op.value`: the value of a De Morgan gate on its first two arguments. * `Cslib.Circuits.Boolean.tseitinClause`: the three clauses of a gate. * `Cslib.Circuits.Boolean.tseitinCNF`: the clauses of a gate list. * `Cslib.Circuits.Boolean.tseitinSatCNF`: the clauses of a gate list, with unit clauses fixing an @@ -71,23 +71,6 @@ variable {V : Type*} /-! ### The clauses of a gate -/ -/-- The value of a De Morgan operation whose first two arguments carry `a` and `b`; arguments -beyond its arity are ignored. -/ -def Op.value : Op → Bool → Bool → Bool - | .const v, _, _ => v - | .not, a, _ => !a - | .and, a, b => a && b - | .or, a, b => a || b - -/-- The value of a gate does not depend on the arguments beyond its arity. -/ -theorem Op.value_congr (op : Op) {a a' b b' : Bool} (ha : 1 ≤ signature.Arity op → a = a') - (hb : 2 ≤ signature.Arity op → b = b') : op.value a b = op.value a' b' := by - cases op with - | const v => rfl - | not => simp only [Op.value, ha (by decide)] - | and => simp only [Op.value, ha (by decide), hb (by decide)] - | or => simp only [Op.value, ha (by decide), hb (by decide)] - /-- A gate code with as many arguments as its arity computes its operation on the values of its first two listed arguments. -/ theorem interpretation_eq_value (gate : Op × List ℕ) (h : gate.2.length = signature.Arity gate.1) From 56b98c928fd25337a8333cb5e42d509143027f48 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 13:34:03 -0400 Subject: [PATCH 4/8] refactor(Circuit/Boolean/Tableau): share the offset decoder and name table components Review of #2083: - Move `posOf` and `posOf_offset` to `Tableau/Layout.lean`, beside `Loc.offset`. `gateCode` and `wireLabel` now decode through `posOf`, and `gateCode_wire` follows from `posOf_offset` instead of repeating its proof. The space certificate `gateCode_inSpace` is updated to match. - Bind the components of `table tm l` by name in `posClause`, `table_inWindow`, `forall_eval_posClause_iff` and `eval_clauseOf`. - Add `Foundations/Data/Nat/Fields.lean` with the two-field packing facts `Nat.add_mul_mod_of_lt`, `Nat.add_mul_div_left_of_lt` and `Nat.add_two_pow_mul_lt`; the private three-field `fields` and `fields_lt` are now compositions of them. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Circuit/Boolean/Tableau/Basic.lean | 58 +------- .../Circuit/Boolean/Tableau/Layout.lean | 61 +++++++- .../Circuit/Boolean/Tableau/Space.lean | 2 +- .../Circuit/Boolean/Tableau/Succinct.lean | 134 ++++++------------ Complexity/Foundations/Data/Nat/Fields.lean | 46 ++++++ 6 files changed, 157 insertions(+), 145 deletions(-) create mode 100644 Complexity/Foundations/Data/Nat/Fields.lean diff --git a/Complexity.lean b/Complexity.lean index 84e01b9fc..f5de9d4b8 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -2955,6 +2955,7 @@ public import Complexity.Foundations.Data.Nat.Choose public import Complexity.Foundations.Data.Nat.Choose.Real public import Complexity.Foundations.Data.Nat.Div public import Complexity.Foundations.Data.Nat.Factorial +public import Complexity.Foundations.Data.Nat.Fields public import Complexity.Foundations.Data.Nat.IntermediateValue public import Complexity.Foundations.Data.Nat.Log public import Complexity.Foundations.Data.Nat.OfDigits diff --git a/Complexity/Computability/Circuit/Boolean/Tableau/Basic.lean b/Complexity/Computability/Circuit/Boolean/Tableau/Basic.lean index b4869b174..ebc016ca4 100644 --- a/Complexity/Computability/Circuit/Boolean/Tableau/Basic.lean +++ b/Complexity/Computability/Circuit/Boolean/Tableau/Basic.lean @@ -201,24 +201,12 @@ noncomputable def gateAt (n T t c : ℕ) (l : Loc k State) : Op × List ℕ := /-- **The gate with number `g`** of the tableau of `tm` for `T` steps on inputs of length `n`: gates `0` and `1` are the constants `false` and `true`, and gate `2 + t * B + r` is the gate at -offset `r` of block `t`, for blocks of `B` gates. -/ +offset `r` of block `t`, for blocks of `B` gates, at the column and position decoded by `posOf`. -/ noncomputable def gateCode (n T g : ℕ) : Op × List ℕ := if g < 2 then (.const (decide (g = 1)), []) else - let t := (g - 2) / blockSize k State T - let r := (g - 2) % blockSize k State T - if r < globStart k T then - gateAt tm n T t (r / scanWidth k) (.scan (ScanLoc.ofIdx (r % scanWidth k))) - else if r < gfStart k State T then - gateAt tm n T t 0 (.glob (GlobLoc.ofIdx (r - globStart k T))) - else if r < colStart k State T then - gateAt tm n T t 0 (.gf (GFeat.ofIdx ((r - gfStart k State T) / 16)) - (gadgetB (r - gfStart k State T)) (gadgetJ (r - gfStart k State T))) - else - gateAt tm n T t ((r - colStart k State T) / colWidth k) - (.col (ColFeat.ofIdx ((r - colStart k State T) % colWidth k / 16)) - (gadgetB ((r - colStart k State T) % colWidth k)) - (gadgetJ ((r - colStart k State T) % colWidth k))) + let (c, l) := posOf T ((g - 2) % blockSize k State T) + gateAt tm n T ((g - 2) / blockSize k State T) c l /-- **The gate list of the tableau** of `tm` for `T` steps on inputs of length `n`. -/ noncomputable def gates (n T : ℕ) : List (Op × List ℕ) := @@ -238,45 +226,7 @@ theorem gateCode_wire {n T t : ℕ} {l : Loc k State} {c : ℕ} (hl : l.ColOK T obtain ⟨hdiv, hmod⟩ := div_mod_wire (n := n) (t := t) hl have h2 : ¬ wire n T t l c - n < 2 := by rw [wire_sub]; omega rw [gateCode, ite_eq_right h2] - simp only [hdiv, hmod] - have hgs : globStart k T ≤ gfStart k State T := Nat.le_add_right _ _ - have hgc : gfStart k State T ≤ colStart k State T := Nat.le_add_right _ _ - rcases l with l | l | ⟨f, b, j⟩ | ⟨f, b, j⟩ <;> simp only [Loc.ColOK] at hl <;> - simp only [Loc.offset] - · have hi := l.idx_lt - have h₁ : c * scanWidth k + l.idx < globStart k T := scan_offset_lt_globStart hl l - have h₂ : (c * scanWidth k + l.idx) / scanWidth k = c := Nat.mul_add_div_of_lt c hi - have h₃ : (c * scanWidth k + l.idx) % scanWidth k = l.idx := by - rw [Nat.add_comm, Nat.add_mul_mod_self_right, Nat.mod_eq_of_lt hi] - simp only [h₁, ↓reduceIte, h₂, h₃, ScanLoc.ofIdx_idx] - · have hi := l.idx_lt - subst hl - have h₁ : ¬ globStart k T + l.idx < globStart k T := by omega - have h₂ : globStart k T + l.idx < gfStart k State T := by simp only [gfStart]; omega - simp only [h₁, h₂, ↓reduceIte, Nat.add_sub_cancel_left, GlobLoc.ofIdx_idx] - · subst hl - have := gfStart_add_gadgetIdx_lt (k := k) (T := T) f b j - have h₁ : ¬ gfStart k State T + gadgetIdx f.idx b j < globStart k T := by omega - have h₂ : ¬ gfStart k State T + gadgetIdx f.idx b j < gfStart k State T := by omega - simp only [h₁, h₂, this, ↓reduceIte, Nat.add_sub_cancel_left, gadgetIdx_div, - GFeat.ofIdx_idx, gadgetB_gadgetIdx, gadgetJ_gadgetIdx] - · have h₁ := gadgetIdx_lt f.idx b j - have h₂ := f.idx_lt - have hw : gadgetIdx f.idx b j < colWidth k := by simp only [colWidth]; omega - have h₃ : ¬ colStart k State T + c * colWidth k + gadgetIdx f.idx b j < globStart k T := by - omega - have h₄ : ¬ colStart k State T + c * colWidth k + gadgetIdx f.idx b j < gfStart k State T := by - omega - have h₅ : ¬ colStart k State T + c * colWidth k + gadgetIdx f.idx b j < - colStart k State T := by omega - have h₆ : colStart k State T + c * colWidth k + gadgetIdx f.idx b j - colStart k State T = - gadgetIdx f.idx b j + c * colWidth k := by omega - have h₇ : (gadgetIdx f.idx b j + c * colWidth k) / colWidth k = c := - Nat.add_mul_div_of_lt c hw - have h₈ : (gadgetIdx f.idx b j + c * colWidth k) % colWidth k = gadgetIdx f.idx b j := by - rw [Nat.add_mul_mod_self_right, Nat.mod_eq_of_lt hw] - simp only [h₃, h₄, h₅, ↓reduceIte, h₆, h₇, h₈, gadgetIdx_div, ColFeat.ofIdx_idx, - gadgetB_gadgetIdx, gadgetJ_gadgetIdx] + simp only [hdiv, hmod, posOf_offset hl] /-! ### Well-formedness -/ diff --git a/Complexity/Computability/Circuit/Boolean/Tableau/Layout.lean b/Complexity/Computability/Circuit/Boolean/Tableau/Layout.lean index 955b5e4fd..16420554f 100644 --- a/Complexity/Computability/Circuit/Boolean/Tableau/Layout.lean +++ b/Complexity/Computability/Circuit/Boolean/Tableau/Layout.lean @@ -59,14 +59,16 @@ block position (`div_mod_wire`, `exists_wire_eq`). `Cslib.Circuits.Boolean.Tableau.argsOf`: numerical layout and wire resolution. * `Cslib.Circuits.Boolean.Tableau.symOf`, `Cslib.Circuits.Boolean.Tableau.ScanQ.ofIdx`, `Cslib.Circuits.Boolean.Tableau.ScanLoc.ofIdx`, `Cslib.Circuits.Boolean.Tableau.GlobLoc.ofIdx`, - `Cslib.Circuits.Boolean.Tableau.GFeat.ofIdx`, `Cslib.Circuits.Boolean.Tableau.ColFeat.ofIdx`: - arithmetic decoders for block offsets. + `Cslib.Circuits.Boolean.Tableau.GFeat.ofIdx`, `Cslib.Circuits.Boolean.Tableau.ColFeat.ofIdx`, + `Cslib.Circuits.Boolean.Tableau.posOf`: arithmetic decoders for block offsets. ## Main results * `Cslib.Circuits.Boolean.Tableau.Loc.offset_lt`: every admissible position lies within its block. * `Cslib.Circuits.Boolean.Tableau.div_mod_wire`: dividing and reducing a wire index modulo `blockSize` recovers its time block and intra-block offset. +* `Cslib.Circuits.Boolean.Tableau.posOf_offset`: decoding the offset of a position recovers its + column and the position. * `Cslib.Circuits.Boolean.Tableau.exists_wire_eq`: every gate index after the two constants is the wire of an admissible block position. @@ -425,6 +427,19 @@ def gadgetB (m : ℕ) : Bool := decide (8 ≤ m % 16) /-- The term of the gate at a position within the gates of a feature. -/ def gadgetJ (m : ℕ) : Fin 8 := ⟨m % 8, Nat.mod_lt _ (by norm_num)⟩ +/-- **The column and the position at an offset `r` of a block**: the scan, global gate, global +feature or column feature whose region contains `r`. It inverts `Loc.offset` (`posOf_offset`). -/ +noncomputable def posOf (T r : ℕ) : ℕ × Loc k State := + if r < globStart k T then (r / scanWidth k, .scan (ScanLoc.ofIdx (r % scanWidth k))) + else if r < gfStart k State T then (0, .glob (GlobLoc.ofIdx (r - globStart k T))) + else if r < colStart k State T then + (0, .gf (GFeat.ofIdx ((r - gfStart k State T) / 16)) (gadgetB (r - gfStart k State T)) + (gadgetJ (r - gfStart k State T))) + else ((r - colStart k State T) / colWidth k, + .col (ColFeat.ofIdx ((r - colStart k State T) % colWidth k / 16)) + (gadgetB ((r - colStart k State T) % colWidth k)) + (gadgetJ ((r - colStart k State T) % colWidth k))) + /-! ### Decoding inverts the layout -/ private theorem symOf_symIdx (s : Option Bool) : symOf (symIdx s) = s := by @@ -681,6 +696,48 @@ theorem div_mod_wire {n T t : ℕ} {l : Loc k State} {c : ℕ} (hl : l.ColOK T c exact ⟨Nat.add_mul_div_of_lt t h, by rw [Nat.add_mul_mod_self_right, Nat.mod_eq_of_lt h]⟩ +/-- **Decoding the offset of a position** gives its column and the position. -/ +theorem posOf_offset {c : ℕ} {l : Loc k State} (hl : l.ColOK T c) : + posOf T (l.offset T c) = (c, l) := by + have hgs : globStart k T ≤ gfStart k State T := Nat.le_add_right _ _ + have hgc : gfStart k State T ≤ colStart k State T := Nat.le_add_right _ _ + rcases l with l | l | ⟨f, b, j⟩ | ⟨f, b, j⟩ <;> simp only [Loc.ColOK] at hl <;> + simp only [Loc.offset, posOf] + · have hi := l.idx_lt + have h₁ : c * scanWidth k + l.idx < globStart k T := scan_offset_lt_globStart hl l + have h₂ : (c * scanWidth k + l.idx) / scanWidth k = c := Nat.mul_add_div_of_lt c hi + have h₃ : (c * scanWidth k + l.idx) % scanWidth k = l.idx := by + rw [Nat.add_comm, Nat.add_mul_mod_self_right, Nat.mod_eq_of_lt hi] + simp only [h₁, ↓reduceIte, h₂, h₃, ScanLoc.ofIdx_idx] + · have hi := l.idx_lt + subst hl + have h₁ : ¬ globStart k T + l.idx < globStart k T := by omega + have h₂ : globStart k T + l.idx < gfStart k State T := by simp only [gfStart]; omega + simp only [h₁, h₂, ↓reduceIte, Nat.add_sub_cancel_left, GlobLoc.ofIdx_idx] + · subst hl + have := gfStart_add_gadgetIdx_lt (k := k) (T := T) f b j + have h₁ : ¬ gfStart k State T + gadgetIdx f.idx b j < globStart k T := by omega + have h₂ : ¬ gfStart k State T + gadgetIdx f.idx b j < gfStart k State T := by omega + simp only [h₁, h₂, this, ↓reduceIte, Nat.add_sub_cancel_left, gadgetIdx_div, + GFeat.ofIdx_idx, gadgetB_gadgetIdx, gadgetJ_gadgetIdx] + · have h₁ := gadgetIdx_lt f.idx b j + have h₂ := f.idx_lt + have hw : gadgetIdx f.idx b j < colWidth k := by simp only [colWidth]; omega + have h₃ : ¬ colStart k State T + c * colWidth k + gadgetIdx f.idx b j < globStart k T := by + omega + have h₄ : ¬ colStart k State T + c * colWidth k + gadgetIdx f.idx b j < gfStart k State T := by + omega + have h₅ : ¬ colStart k State T + c * colWidth k + gadgetIdx f.idx b j < + colStart k State T := by omega + have h₆ : colStart k State T + c * colWidth k + gadgetIdx f.idx b j - colStart k State T = + gadgetIdx f.idx b j + c * colWidth k := by omega + have h₇ : (gadgetIdx f.idx b j + c * colWidth k) / colWidth k = c := + Nat.add_mul_div_of_lt c hw + have h₈ : (gadgetIdx f.idx b j + c * colWidth k) % colWidth k = gadgetIdx f.idx b j := by + rw [Nat.add_mul_mod_self_right, Nat.mod_eq_of_lt hw] + simp only [h₃, h₄, h₅, ↓reduceIte, h₆, h₇, h₈, gadgetIdx_div, ColFeat.ofIdx_idx, + gadgetB_gadgetIdx, gadgetJ_gadgetIdx] + /-! ### Every gate sits at a position -/ private theorem symIdx_symOf {m : ℕ} (hm : m < 3) : symIdx (symOf m) = m := by diff --git a/Complexity/Computability/Circuit/Boolean/Tableau/Space.lean b/Complexity/Computability/Circuit/Boolean/Tableau/Space.lean index 10316ba64..29ebe94bb 100644 --- a/Complexity/Computability/Circuit/Boolean/Tableau/Space.lean +++ b/Complexity/Computability/Circuit/Boolean/Tableau/Space.lean @@ -389,7 +389,7 @@ theorem gateCode_inSpace {g : α → ℕ} (hg : ComputableInSpace g encIn encNat congr 1 have hgs' : globStart k (T x) ≤ gfStart k State (T x) := Nat.le_add_right _ _ have hgc' : gfStart k State (T x) ≤ colStart k State (T x) := Nat.le_add_right _ _ - simp only [gateCode, decide_eq_true_eq] + simp only [gateCode, posOf, decide_eq_true_eq] have hgw : gfStart k State (T x) = globStart k (T x) + globWidth k State := rfl have hcw : colStart k State (T x) = gfStart k State (T x) + gfWidth State := rfl split_ifs with h₀ h₁ h₂ h₃ h₄ diff --git a/Complexity/Computability/Circuit/Boolean/Tableau/Succinct.lean b/Complexity/Computability/Circuit/Boolean/Tableau/Succinct.lean index 4778a8e5a..86a6eb4d0 100644 --- a/Complexity/Computability/Circuit/Boolean/Tableau/Succinct.lean +++ b/Complexity/Computability/Circuit/Boolean/Tableau/Succinct.lean @@ -12,6 +12,7 @@ public import Complexity.Computability.Circuit.Boolean.Tseitin public import Mathlib.Data.Nat.Bits import Complexity.Foundations.Data.List.Lookup import Complexity.Foundations.Data.Nat.Div +import Complexity.Foundations.Data.Nat.Fields import Mathlib.Data.Nat.Size /-! @@ -139,24 +140,14 @@ private theorem fields {a b x y z : ℕ} (hx : x < 2 ^ a) (hy : y < 2 ^ b) : (x + 2 ^ a * (y + 2 ^ b * z)) % 2 ^ a = x ∧ (x + 2 ^ a * (y + 2 ^ b * z)) / 2 ^ a % 2 ^ b = y ∧ (x + 2 ^ a * (y + 2 ^ b * z)) / 2 ^ a / 2 ^ b = z := by - have ha : 0 < 2 ^ a := Nat.two_pow_pos a - have hb : 0 < 2 ^ b := Nat.two_pow_pos b - have h₁ : (x + 2 ^ a * (y + 2 ^ b * z)) / 2 ^ a = y + 2 ^ b * z := by - rw [Nat.add_mul_div_left _ _ ha, Nat.div_eq_of_lt hx, Nat.zero_add] - refine ⟨by rw [Nat.add_mul_mod_self_left, Nat.mod_eq_of_lt hx], ?_, ?_⟩ - · rw [h₁, Nat.add_mul_mod_self_left, Nat.mod_eq_of_lt hy] - · rw [h₁, Nat.add_mul_div_left _ _ hb, Nat.div_eq_of_lt hy, Nat.zero_add] + rw [Nat.add_mul_div_left_of_lt _ hx] + exact ⟨Nat.add_mul_mod_of_lt _ hx, Nat.add_mul_mod_of_lt _ hy, Nat.add_mul_div_left_of_lt _ hy⟩ /-- Three fields of widths `a`, `b` and `d` fit in `a + b + d` bits. -/ private theorem fields_lt {a b d x y z : ℕ} (hx : x < 2 ^ a) (hy : y < 2 ^ b) (hz : z < 2 ^ d) : x + 2 ^ a * (y + 2 ^ b * z) < 2 ^ (a + b + d) := by - have h₁ : y + 2 ^ b * z < 2 ^ (b + d) := by - rw [Nat.pow_add] - calc y + 2 ^ b * z < 2 ^ b * (z + 1) := by rw [Nat.mul_succ]; omega - _ ≤ 2 ^ b * 2 ^ d := Nat.mul_le_mul_left _ hz - rw [Nat.add_assoc, Nat.pow_add] - calc x + 2 ^ a * (y + 2 ^ b * z) < 2 ^ a * (y + 2 ^ b * z + 1) := by rw [Nat.mul_succ]; omega - _ ≤ 2 ^ a * 2 ^ (b + d) := Nat.mul_le_mul_left _ h₁ + rw [Nat.add_assoc] + exact Nat.add_two_pow_mul_lt hx (Nat.add_two_pow_mul_lt hy hz) /-- A tag below `4` and a payload decode into their values. -/ private theorem tag_fields {tag p : ℕ} (htag : tag < 4) : @@ -249,61 +240,6 @@ theorem wireOf_posLabel {n T t c : ℕ} (l : Loc k State) (hc : c < 2 ^ colBits /-! ### From wires to labels -/ -/-- The column and the position at an offset of a block: inverts `Loc.offset`. -/ -noncomputable def posOf (T r : ℕ) : ℕ × Loc k State := - if r < globStart k T then (r / scanWidth k, .scan (ScanLoc.ofIdx (r % scanWidth k))) - else if r < gfStart k State T then (0, .glob (GlobLoc.ofIdx (r - globStart k T))) - else if r < colStart k State T then - (0, .gf (GFeat.ofIdx ((r - gfStart k State T) / 16)) (gadgetB (r - gfStart k State T)) - (gadgetJ (r - gfStart k State T))) - else ((r - colStart k State T) / colWidth k, - .col (ColFeat.ofIdx ((r - colStart k State T) % colWidth k / 16)) - (gadgetB ((r - colStart k State T) % colWidth k)) - (gadgetJ ((r - colStart k State T) % colWidth k))) - -omit [DecidableEq State] in -/-- **Decoding the offset of a position** gives the position and its column. -/ -theorem posOf_offset {T c : ℕ} {l : Loc k State} (hl : l.ColOK T c) : - posOf T (l.offset T c) = (c, l) := by - have hgs : globStart k T ≤ gfStart k State T := Nat.le_add_right _ _ - have hgc : gfStart k State T ≤ colStart k State T := Nat.le_add_right _ _ - rcases l with l | l | ⟨f, b, j⟩ | ⟨f, b, j⟩ <;> simp only [Loc.ColOK] at hl <;> - simp only [Loc.offset, posOf] - · have hi := l.idx_lt - have h₁ : c * scanWidth k + l.idx < globStart k T := scan_offset_lt_globStart hl l - have h₂ : (c * scanWidth k + l.idx) / scanWidth k = c := Nat.mul_add_div_of_lt c hi - have h₃ : (c * scanWidth k + l.idx) % scanWidth k = l.idx := by - rw [Nat.add_comm, Nat.add_mul_mod_self_right, Nat.mod_eq_of_lt hi] - simp only [h₁, ↓reduceIte, h₂, h₃, ScanLoc.ofIdx_idx] - · have hi := l.idx_lt - subst hl - have h₁ : ¬ globStart k T + l.idx < globStart k T := by omega - have h₂ : globStart k T + l.idx < gfStart k State T := by simp only [gfStart]; omega - simp only [h₁, h₂, ↓reduceIte, Nat.add_sub_cancel_left, GlobLoc.ofIdx_idx] - · subst hl - have := gfStart_add_gadgetIdx_lt (k := k) (T := T) f b j - have h₁ : ¬ gfStart k State T + gadgetIdx f.idx b j < globStart k T := by omega - have h₂ : ¬ gfStart k State T + gadgetIdx f.idx b j < gfStart k State T := by omega - simp only [h₁, h₂, this, ↓reduceIte, Nat.add_sub_cancel_left, gadgetIdx_div, - GFeat.ofIdx_idx, gadgetB_gadgetIdx, gadgetJ_gadgetIdx] - · have h₁ := gadgetIdx_lt f.idx b j - have h₂ := f.idx_lt - have hw : gadgetIdx f.idx b j < colWidth k := by simp only [colWidth]; omega - have h₃ : ¬ colStart k State T + c * colWidth k + gadgetIdx f.idx b j < globStart k T := by - omega - have h₄ : ¬ colStart k State T + c * colWidth k + gadgetIdx f.idx b j < gfStart k State T := by - omega - have h₅ : ¬ colStart k State T + c * colWidth k + gadgetIdx f.idx b j < - colStart k State T := by omega - have h₆ : colStart k State T + c * colWidth k + gadgetIdx f.idx b j - colStart k State T = - gadgetIdx f.idx b j + c * colWidth k := by omega - have h₇ : (gadgetIdx f.idx b j + c * colWidth k) / colWidth k = c := - Nat.add_mul_div_of_lt c hw - have h₈ : (gadgetIdx f.idx b j + c * colWidth k) % colWidth k = gadgetIdx f.idx b j := by - rw [Nat.add_mul_mod_self_right, Nat.mod_eq_of_lt hw] - simp only [h₃, h₄, h₅, ↓reduceIte, h₆, h₇, h₈, gadgetIdx_div, ColFeat.ofIdx_idx, - gadgetB_gadgetIdx, gadgetJ_gadgetIdx] - variable (k State) in /-- **The label of a wire** of the tableau on `n` inputs: an input, a constant, or the position of a gate decoded from its number. -/ @@ -311,9 +247,8 @@ noncomputable def wireLabel (n T w : ℕ) : ℕ := if w < n then inputLabel w else if w < n + 2 then constLabel (decide (w = n + 1)) else - posLabel k State T ((w - n - 2) / blockSize k State T) - (posOf (k := k) (State := State) T ((w - n - 2) % blockSize k State T)).1 - (posOf T ((w - n - 2) % blockSize k State T)).2 + let (c, l) := posOf (k := k) (State := State) T ((w - n - 2) % blockSize k State T) + posLabel k State T ((w - n - 2) / blockSize k State T) c l omit [DecidableEq State] in /-- The label of an input wire. -/ @@ -396,7 +331,9 @@ private theorem inWindow_of_cs {n T c : ℕ} (hc : c < numCols T) {p : Bool} {l /-- **The references of the gates of the tableau read the window.** -/ theorem table_inWindow {n T c : ℕ} (hc : c < numCols T) (tm : MultiTapeTM k Bool State) (l : Loc k State) : - (table tm l).2.1.InWindow n T c ∧ (table tm l).2.2.InWindow n T c := by + let (_, left, right) := table tm l + left.InWindow n T c ∧ right.InWindow n T c := by + change (table tm l).2.1.InWindow n T c ∧ (table tm l).2.2.InWindow n T c have hw {r : Ref k State} (h : ∀ p l g, r = .node p l .next g → g = .ltLast) : r.InWindow n T c := by rcases r with _ | _ | _ | ⟨p, l, cs, g⟩ @@ -465,8 +402,9 @@ variable (tm : MultiTapeTM k Bool State) constant in block `0`, and otherwise the gate of `table` on the labels of its references. -/ noncomputable def posClause (n T t c : ℕ) (l : Loc k State) (j : Fin 3) : Clause ℕ := if t = 0 then tseitinClause (.const (initBit tm T c l)) (posLabel k State T t c l) 0 0 j - else tseitinClause (table tm l).1 (posLabel k State T t c l) ((table tm l).2.1.label n T t c) - ((table tm l).2.2.label n T t c) j + else + let (op, left, right) := table tm l + tseitinClause op (posLabel k State T t c l) (left.label n T t c) (right.label n T t c) j variable (k State) in /-- The label of the output wire: the output view `[true]` at time `T`. -/ @@ -547,10 +485,15 @@ theorem clauseOf_pos (x : List Bool) {n T t c : ℕ} {l : Loc k State} (j : Fin theorem forall_eval_posClause_iff (σ : ℕ → Bool) (n T t c : ℕ) (l : Loc k State) : (∀ j, (posClause tm n T t c l j).eval σ = true) ↔ σ (posLabel k State T t c l) = if t = 0 then initBit tm T c l - else (table tm l).1.value (σ ((table tm l).2.1.label n T t c)) - (σ ((table tm l).2.2.label n T t c)) := by + else + let (op, left, right) := table tm l + op.value (σ (left.label n T t c)) (σ (right.label n T t c)) := by unfold posClause - split_ifs <;> exact forall_eval_tseitinClause_iff _ _ _ _ _ + split_ifs + · exact forall_eval_tseitinClause_iff _ _ _ _ _ + · generalize table tm l = e + obtain ⟨op, left, right⟩ := e + exact forall_eval_tseitinClause_iff _ _ _ _ _ /-- **Every clause holds** under an assignment of labels that gives the constants their values, the fixed inputs the bits of `x` and the output `true`, and every position the value of its gate on @@ -560,8 +503,9 @@ theorem eval_clauseOf {x : List Bool} {n T : ℕ} {σ : ℕ → Bool} (hout : σ (outLabel k State T) = true) (hpos : ∀ t ≤ T, ∀ c (l : Loc k State), l.ColOK T c → σ (posLabel k State T t c l) = if t = 0 then initBit tm T c l - else (table tm l).1.value (σ ((table tm l).2.1.label n T t c)) - (σ ((table tm l).2.2.label n T t c))) + else + let (op, left, right) := table tm l + op.value (σ (left.label n T t c)) (σ (right.label n T t c))) (j tag p : ℕ) : (clauseOf tm x n T j tag p).eval σ = true := by unfold clauseOf split_ifs with hj h₀ hp h₁ hp' h₂ h₃ @@ -661,10 +605,14 @@ theorem satisfiable_succinctCNF_iff_tseitin {n T : ℕ} (hn : n < 2 * T) {x : Li have hpos := (forall_eval_posClause_iff tm σ n T t c l).1 fun j => by rw [← clauseOf_pos tm x j ht hl] exact hfield (by omega) (by omega) hp - obtain ⟨hra, hrb⟩ := table_inWindow (n := n) hl.lt tm l + have hwin := table_inWindow (n := n) hl.lt tm l have hv := value_gateAt tm (fun w => σ (wireLabel k State n T w)) n T t c l - rw [← hw, gateCode_wire tm hl, hv, hwn, wireLabel_wire hl, hpos, - wireLabel_resolve hra, wireLabel_resolve hrb] + rw [← hw, gateCode_wire tm hl, hv, hwn, wireLabel_wire hl, hpos] + generalize table tm l = e at hwin ⊢ + obtain ⟨op, left, right⟩ := e + obtain ⟨hra, hrb⟩ := hwin + dsimp only + rw [wireLabel_resolve hra, wireLabel_resolve hrb] · rintro ⟨τ, hτ⟩ obtain ⟨hτo, hτx, hτφ⟩ := (eval_tseitinSatCNF_eq_true_iff _ _ _ _ _).1 hτ rw [eval_tseitinCNF_eq_true_iff] at hτφ @@ -691,9 +639,13 @@ theorem satisfiable_succinctCNF_iff_tseitin {n T : ℕ} (hn : n < 2 * T) {x : Li · simpa using hτx i hi · simpa [outLabel, outWire, wireOf_posLabel _ (show 0 < 2 ^ colBits T from Nat.two_pow_pos _)] using hτo - · obtain ⟨hra, hrb⟩ := table_inWindow (n := n) hl.lt tm l - rw [wireOf_posLabel _ (lt_of_lt_of_le hl.lt (numCols_le_two_pow T)), hwire hl ht, - wireOf_label hra, wireOf_label hrb] + · have hwin := table_inWindow (n := n) hl.lt tm l + rw [wireOf_posLabel _ (lt_of_lt_of_le hl.lt (numCols_le_two_pow T)), hwire hl ht] + generalize table tm l = e at hwin ⊢ + obtain ⟨op, left, right⟩ := e + obtain ⟨hra, hrb⟩ := hwin + dsimp only + rw [wireOf_label hra, wireOf_label hrb] /-- **The tableau formula on labels is satisfiable** exactly when some input of length `n` extending `x` makes `tm` write exactly `[true]` within `T` steps, for `n < 2 T`. -/ @@ -723,7 +675,10 @@ theorem length_of_mem_succinctCNF {x : List Bool} {n T : ℕ} {C : Clause ℕ} · split · split_ifs · unfold posClause - split_ifs <;> exact length_tseitinClause _ _ _ _ _ + split_ifs + · exact length_tseitinClause _ _ _ _ _ + · split + exact length_tseitinClause _ _ _ _ _ · rfl · rfl · rfl @@ -814,8 +769,11 @@ theorem lt_two_pow_labelBits_of_mem_vars {n T : ℕ} (hn : n < 2 * T) {x : List · rename_i l _ split_ifs at hvC with h · have hc := lt_of_lt_of_le h.2.lt (numCols_le_two_pow T) - obtain ⟨hra, hrb⟩ := table_inWindow (n := n) h.2.lt tm l + have hwin := table_inWindow (n := n) h.2.lt tm l unfold posClause at hvC + generalize table tm l = e at hwin hvC + obtain ⟨op, left, right⟩ := e + obtain ⟨hra, hrb⟩ := hwin split_ifs at hvC <;> rcases mem_tseitinClause hvC with rfl | rfl | rfl <;> first | exact posLabel_lt _ hc h.1 diff --git a/Complexity/Foundations/Data/Nat/Fields.lean b/Complexity/Foundations/Data/Nat/Fields.lean new file mode 100644 index 000000000..bcda61f7b --- /dev/null +++ b/Complexity/Foundations/Data/Nat/Fields.lean @@ -0,0 +1,46 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate (to Mathlib's `Mathlib/Data/Nat/Defs.lean`) + +public import Complexity.Init +import Batteries.Data.Nat.Lemmas + +/-! +# Packing numbers into fields + +A low field `x < m` and a high field `y` are packed into the number `x + m * y`. Reducing modulo +`m` recovers `x` (`Nat.add_mul_mod_of_lt`), and dividing by `m` recovers `y` +(`Nat.add_mul_div_left_of_lt`). For fields of `a` and `b` bits, the packed number has `a + b` bits +(`Nat.add_two_pow_mul_lt`). Packing a packed number again as the high field gives any fixed number +of fields, which these facts decode one at a time. + +## Main results + +* `Nat.add_mul_mod_of_lt`, `Nat.add_mul_div_left_of_lt`: decoding the two fields. +* `Nat.add_two_pow_mul_lt`: fields of `a` and `b` bits fit in `a + b` bits. +-/ + +@[expose] public section + +namespace Nat + +/-- Reducing the packed number `x + m * y` modulo `m` gives the low field `x < m`. -/ +theorem add_mul_mod_of_lt {x m : ℕ} (y : ℕ) (hx : x < m) : (x + m * y) % m = x := by + rw [Nat.add_mul_mod_self_left, Nat.mod_eq_of_lt hx] + +/-- Dividing the packed number `x + m * y` by `m` gives the high field `y`, for `x < m`. -/ +theorem add_mul_div_left_of_lt {x m : ℕ} (y : ℕ) (hx : x < m) : (x + m * y) / m = y := by + rw [Nat.add_mul_div_left _ _ (Nat.zero_lt_of_lt hx), Nat.div_eq_of_lt hx, Nat.zero_add] + +/-- Fields `x < 2 ^ a` and `y < 2 ^ b` pack into a number `x + 2 ^ a * y` of `a + b` bits. -/ +theorem add_two_pow_mul_lt {a b x y : ℕ} (hx : x < 2 ^ a) (hy : y < 2 ^ b) : + x + 2 ^ a * y < 2 ^ (a + b) := by + rw [Nat.pow_add] + exact Nat.add_mul_lt_mul_of_lt_of_lt hx hy + +end Nat From 14bab6892516d80e24335e3d3c578da9545e49b7 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 13:34:06 -0400 Subject: [PATCH 5/8] feat(Circuit/Boolean/Tableau): linear-size decoder of the succinct tableau (#1539 M3c) A De Morgan circuit that computes, from the bits of a clause number, the bits of `Tableau.clauseAt` (label of each of the three literals in `labelBits` bits, least significant first, then the sign), with `O(colBits T + timeBits T + x.length)` gates and an explicit code `decoderCode` built with `compCode`/`appendCode`/`appendCodes`/`formulaCode`. - Decoder/Fields: explicit gate list with fourteen bit-serial comparison chains (also the borrows/carries of c - 1, c + 1, t - 1), a decoder tree and an OR chain that looks up the hardwired prefix; proved wire by wire from a consistent valuation. - Decoder/Shape: the clause of a number is the evaluation of a symbolic shape determined by finitely many comparisons (`clauseOf_eq_map_shapeOf`), and the output-bit formula computes the bits of each shape (`outBit_shape`). - Decoder/Circuit: the constant stage (`tableCircuit`), the wiring of the 63-gate output formula, and the key/wire value lemmas. - Decoder/Assembly: `decoder`, `ofFn_eval_decoder`, `size_decoder_le`, `code_decoder`, `exists_decoder`. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 4 + .../Boolean/Tableau/Decoder/Assembly.lean | 398 +++++++++ .../Boolean/Tableau/Decoder/Circuit.lean | 628 ++++++++++++++ .../Boolean/Tableau/Decoder/Fields.lean | 644 ++++++++++++++ .../Boolean/Tableau/Decoder/Shape.lean | 811 ++++++++++++++++++ ComplexityTests/TableauDecoder.lean | 63 ++ 6 files changed, 2548 insertions(+) create mode 100644 Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Assembly.lean create mode 100644 Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Circuit.lean create mode 100644 Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Fields.lean create mode 100644 Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Shape.lean create mode 100644 ComplexityTests/TableauDecoder.lean diff --git a/Complexity.lean b/Complexity.lean index e0b2182b9..a9ab1aad0 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -827,6 +827,10 @@ public import Complexity.Computability.Circuit.Boolean.Synthesis public import Complexity.Computability.Circuit.Boolean.TC0 public import Complexity.Computability.Circuit.Boolean.Tableau.Basic public import Complexity.Computability.Circuit.Boolean.Tableau.Correct +public import Complexity.Computability.Circuit.Boolean.Tableau.Decoder.Assembly +public import Complexity.Computability.Circuit.Boolean.Tableau.Decoder.Circuit +public import Complexity.Computability.Circuit.Boolean.Tableau.Decoder.Fields +public import Complexity.Computability.Circuit.Boolean.Tableau.Decoder.Shape public import Complexity.Computability.Circuit.Boolean.Tableau.Layout public import Complexity.Computability.Circuit.Boolean.Tableau.Space public import Complexity.Computability.Circuit.Boolean.Tableau.Succinct diff --git a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Assembly.lean b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Assembly.lean new file mode 100644 index 000000000..a9d6c999f --- /dev/null +++ b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Assembly.lean @@ -0,0 +1,398 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Boolean.Tableau.Decoder.Circuit +import Mathlib.Data.Nat.Size + +/-! +# The decoder circuit of the succinct tableau + +The three stages of the decoder of the tableau formula on bit-field labels +(`Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Circuit.lean`) composed into one +De Morgan circuit (`decoder`). From the bits of a clause number below `2 ^ clauseBits`, it computes +the bits of `Cslib.Circuits.Boolean.Tableau.clauseAt` (`ofFn_eval_decoder`). It has +`O(colBits T + timeBits T + x.length)` gates (`size_decoder_le`): the field stage has +`2 + N + 14 P + (2 ^ (Nat.size x.length + 1) - 1) + x.length` gates for `N = clauseBits` and +`P = N - 4`, the constant stage a number depending only on the machine, and the output stage `63` +gates per output bit. There is no carry-lookahead adder, whose size is not linear in the width. + +Its code is `decoderCode`, written with the code combinators `compCode`, `appendCode`, +`appendCodes` and `formulaCode` on the explicit gate list of the field stage and the constant code +of the constant stage (`code_decoder`). This is the decoder of the sketch of +[Williams, Theorem 6.1, p. 20][Williams2010]: the variables of the clauses of a cell differ from +cell to cell only by the block and the column, which enter through comparisons, increments and +decrements of fields. The sketch first converts the machine to a single-tape machine; this decoder +is built for the multi-tape tableau directly. + +## Main definitions + +* `Cslib.Circuits.Boolean.Tableau.Decoder.decoder`: the decoder circuit. +* `Cslib.Circuits.Boolean.Tableau.Decoder.decoderCode`: its code. +* `Cslib.Circuits.Boolean.Tableau.Decoder.sizeConst`: the constant of its size bound. + +## Main results + +* `Cslib.Circuits.Boolean.Tableau.Decoder.ofFn_eval_decoder`: the decoder computes the clause with + a given number, bit by bit. +* `Cslib.Circuits.Boolean.Tableau.Decoder.size_decoder_le`: it has + `O(colBits T + timeBits T + x.length)` gates. +* `Cslib.Circuits.Boolean.Tableau.Decoder.code_decoder`: its code is `decoderCode`. +* `Cslib.Circuits.Boolean.Tableau.Decoder.exists_decoder`: the three together. + +## References + +* [R. Williams, *Improving exhaustive search implies superpolynomial lower bounds*][Williams2010] +-/ + +@[expose] public section + +namespace Cslib.Circuits.Boolean.Tableau.Decoder + +open Turing MultiTapeTM Cslib.Logic.PL + +variable {k : ℕ} {State : Type} [Fintype State] [DecidableEq State] + (tm : MultiTapeTM k Bool State) + +omit [DecidableEq State] in +/-- There is at least one slot bit. -/ +theorem one_le_slotBits : 1 ≤ slotBits k State := + Nat.size_pos.2 (Fintype.card_pos_iff.2 ⟨Loc.scan .nx⟩) + +/-- The selection of the inputs of the constant stage among the wires of the field stage. -/ +def keySel (s cb tb : ℕ) (x : List Bool) (n T : ℕ) : + Fin (4 + s + 14) → Fin (aWidth s cb tb x n T) := + fun i => ⟨keyWire s cb tb i, keyWire_lt i.isLt⟩ + +/-- The constant stage on the wires of the field stage, beside those wires. -/ +noncomputable def middleCircuit (x : List Bool) (n T : ℕ) : + Circuit signature (aWidth (slotBits k State) (colBits T) (timeBits T) x n T) + (aWidth (slotBits k State) (colBits T) (timeBits T) x n T + 3 * (13 + slotBits k State)) := + (Circuit.wiring signature id).append ((tableCircuit tm).comp + (Circuit.wiring signature (keySel (slotBits k State) (colBits T) (timeBits T) x n T))) + +/-- **The output stage**: one copy of `outTemplate` per output bit, on the wires `wireNat`. -/ +def outputCircuit (s cb tb : ℕ) (x : List Bool) (n T : ℕ) : + Circuit signature (aWidth s cb tb x n T + 3 * (13 + s)) (3 * (2 + (s + cb + tb) + 1)) := + Circuit.pi fun o => (outTemplate.rename fun j => + ⟨wireNat s cb tb x n T o j, wireNat_lt o.isLt j.isLt⟩).toCircuit + +/-- **The decoder circuit** of the tableau formula of `tm` on bit-field labels, for the fixed +prefix `x`, `n` inputs and `T` steps. -/ +noncomputable def decoder {x : List Bool} {n T : ℕ} + (hm : Nat.size x.length ≤ slotBits k State + colBits T + timeBits T) : + Circuit signature (4 + (slotBits k State + colBits T + timeBits T)) + (3 * (2 + (slotBits k State + colBits T + timeBits T) + 1)) := + (outputCircuit (slotBits k State) (colBits T) (timeBits T) x n T).comp + ((middleCircuit tm x n T).comp (fieldCircuit hm)) + +/-! ### The code -/ + +/-- **The code of the decoder**, written with the code combinators `compCode`, `appendCode`, +`appendCodes` and `formulaCode`: the field stage, the constant code of the constant stage, and one +instance of `outTemplate` per output bit (`code_decoder`). -/ +noncomputable def decoderCode (a : List Bool × ℕ × ℕ) : List (Op × List ℕ) × List ℕ := + compCode (4 + (slotBits k State + colBits a.2.2 + timeBits a.2.2)) + (appendCodes (aWidth (slotBits k State) (colBits a.2.2) (timeBits a.2.2) a.1 a.2.1 a.2.2 + + 3 * (13 + slotBits k State)) + ((List.range (3 * (2 + (slotBits k State + colBits a.2.2 + timeBits a.2.2) + 1))).map + fun o => formulaCode (aWidth (slotBits k State) (colBits a.2.2) (timeBits a.2.2) a.1 a.2.1 + a.2.2 + 3 * (13 + slotBits k State)) outTemplate + (fun j => wireNat (slotBits k State) (colBits a.2.2) (timeBits a.2.2) a.1 a.2.1 a.2.2 o + j))) + (compCode (4 + (slotBits k State + colBits a.2.2 + timeBits a.2.2)) + (appendCode (aWidth (slotBits k State) (colBits a.2.2) (timeBits a.2.2) a.1 a.2.1 a.2.2) + ([], List.range + (aWidth (slotBits k State) (colBits a.2.2) (timeBits a.2.2) a.1 a.2.1 a.2.2)) + (compCode (aWidth (slotBits k State) (colBits a.2.2) (timeBits a.2.2) a.1 a.2.1 a.2.2) + (tableCircuit tm).code + ([], (List.range (4 + slotBits k State + 14)).map + (keyWire (slotBits k State) (colBits a.2.2) (timeBits a.2.2))))) + (fieldGates (slotBits k State) (colBits a.2.2) (timeBits a.2.2) a.1 a.2.1 a.2.2, + List.range (aWidth (slotBits k State) (colBits a.2.2) (timeBits a.2.2) a.1 a.2.1 a.2.2))) + +/-- **The code of the decoder circuit is `decoderCode`.** -/ +theorem code_decoder {x : List Bool} {n T : ℕ} + (hm : Nat.size x.length ≤ slotBits k State + colBits T + timeBits T) : + (decoder (n := n) tm hm).code = decoderCode tm (x, n, T) := by + rw [decoder, ← compCode_code, middleCircuit, ← compCode_code, ← appendCode_code, + ← compCode_code, Circuit.code_wiring_id, code_fieldCircuit, outputCircuit, ← appendCodes_code, + Circuit.code_wiring_eq_map (g := keyWire (slotBits k State) (colBits T) (timeBits T)) + (fun j => rfl)] + simp only [decoderCode] + congr 2 + apply List.ext_getElem (by simp; omega) + intro o h₁ h₂ + simp only [List.getElem_ofFn, List.getElem_map, List.getElem_range] + exact (formulaCode_eq_code outTemplate _).symm + +/-! ### Size -/ + +/-- `2 ^ Nat.size X ≤ 2 X + 1`. -/ +theorem two_pow_size_le (X : ℕ) : 2 ^ Nat.size X ≤ 2 * X + 1 := by + rcases Nat.eq_zero_or_pos X with rfl | hX + · simp + · obtain ⟨m, hm⟩ : ∃ m, Nat.size X = m + 1 := ⟨Nat.size X - 1, by + have := Nat.size_pos.2 hX; omega⟩ + have : ¬ X < 2 ^ m := fun h => by have := Nat.size_le.2 h; omega + rw [hm, Nat.pow_succ] + omega + +theorem size_outputCircuit_le (s cb tb : ℕ) (x : List Bool) (n T : ℕ) : + (outputCircuit s cb tb x n T).size ≤ 3 * (2 + (s + cb + tb) + 1) * 63 := by + simp only [outputCircuit, Circuit.size_pi, Formula.size_toCircuit] + calc ∑ o : Fin (3 * (2 + (s + cb + tb) + 1)), (outTemplate.rename _).size + ≤ ∑ _o : Fin (3 * (2 + (s + cb + tb) + 1)), 63 := Finset.sum_le_sum fun o _ => by + have := Formula.size_bind_le (c := 0) (s := fun j => Formula.input + (⟨wireNat s cb tb x n T o j, wireNat_lt o.isLt j.isLt⟩ : + Fin (aWidth s cb tb x n T + 3 * (13 + s)))) (fun _ => le_rfl) outTemplate + simpa [Formula.rename, size_outTemplate] using this + _ = 3 * (2 + (s + cb + tb) + 1) * 63 := by simp + +/-- **The decoder has `O(colBits T + timeBits T + x.length)` gates**, with a constant depending +only on the machine. -/ +theorem size_decoder_le {x : List Bool} {n T : ℕ} + (hm : Nat.size x.length ≤ slotBits k State + colBits T + timeBits T) : + (decoder (n := n) tm hm).size ≤ (204 * slotBits k State + 600 + (tableCircuit tm).size) * + (colBits T + timeBits T + x.length + 1) := by + have hX := two_pow_size_le x.length + have hpow : 2 ^ (Nat.size x.length + 1) = 2 * 2 ^ Nat.size x.length := Nat.pow_succ' + have hout := size_outputCircuit_le (slotBits k State) (colBits T) (timeBits T) x n T + rw [decoder, Circuit.size_comp, Circuit.size_comp, middleCircuit, Circuit.size_append, + Circuit.size_comp, Circuit.size_wiring, Circuit.size_wiring, size_fieldCircuit, + length_fieldGates] + clear hm + generalize (tableCircuit tm).size = S at * + generalize (outputCircuit (slotBits k State) (colBits T) (timeBits T) x n T).size = O at * + generalize slotBits k State = s at * + generalize colBits T = cb at * + generalize timeBits T = tb at * + generalize x.length = X at * + generalize 2 ^ Nat.size X = Y at * + have h1 : 204 * s + 600 + S ≤ (204 * s + 600 + S) * (cb + tb + X + 1) := + Nat.le_mul_of_pos_right _ (by omega) + have h2 : 204 * (cb + tb + X) ≤ (204 * s + 600 + S) * (cb + tb + X) := + Nat.mul_le_mul_right _ (by omega) + rw [Nat.mul_add (204 * s + 600 + S) (cb + tb + X) 1, Nat.mul_one] at h1 ⊢ + rw [hpow] + omega + +/-! ### Correctness -/ + +theorem outBit_congr {s cb tb : ℕ} {ts ts' sel sel' : ℕ → Bool} + (hts : ∀ f < 2 + s, ts f = ts' f) (hsel : ∀ q < 11, sel q = sel' q) (p c t T : ℕ) (lk : Bool) + (i : ℕ) : outBit s cb tb ts sel p c t T lk i = outBit s cb tb ts' sel' p c t T lk i := by + unfold outBit payTerm colTerm timeTerm + simp only [hsel 0 (by omega), hsel 1 (by omega), hsel 2 (by omega), hsel 3 (by omega), + hsel 4 (by omega), hsel 5 (by omega), hsel 6 (by omega), hsel 7 (by omega), hsel 8 (by omega), + hsel 9 (by omega), hsel 10 (by omega)] + split_ifs with h1 h2 + · rw [hts i (by omega)] + · by_cases hq : i - 2 < s + · simp [hq, hts (2 + (i - 2)) (by omega)] + · simp [hq] + · rfl + +/-- An entry of a concatenation of lists of a common length `m`. -/ +theorem getElem?_flatMap_of_length {α β : Type} {f : α → List β} {m : ℕ} + (hf : ∀ a, (f a).length = m) (L : List α) (o : ℕ) (ho : o < L.length * m) : + (L.flatMap f)[o]? = (f (L[o / m]'(Nat.div_lt_of_lt_mul (by rwa [Nat.mul_comm]))))[o % m]? := by + induction L generalizing o with + | nil => simp at ho + | cons a L ih => + have hm : 0 < m := by + rcases Nat.eq_zero_or_pos m with rfl | hm + · simp at ho + · exact hm + simp only [List.flatMap_cons] + by_cases hlt : o < m + · rw [List.getElem?_append_left (by rwa [hf])] + have h₁ : o / m = 0 := Nat.div_eq_of_lt hlt + have h₂ : o % m = o := Nat.mod_eq_of_lt hlt + simp only [h₁, h₂, List.getElem_cons_zero] + · rw [List.getElem?_append_right (by rw [hf]; omega), hf, + ih (o - m) (by + simp only [List.length_cons, Nat.succ_mul] at ho + omega)] + have h₁ : (o - m) / m = o / m - 1 := by + have := Nat.sub_mul_div o m 1 + simpa using this + have h₂ : (o - m) % m = o % m := (Nat.mod_eq_sub_mod (by omega)).symm + have h₃ : 1 ≤ o / m := (Nat.le_div_iff_mul_le hm).2 (by omega) + simp only [h₁, h₂] + congr 2 + rw [List.getElem_cons] + simp [show o / m ≠ 0 by omega] + +/-- The values of the wires of the field and constant stages. -/ +theorem eval_middle_field {x : List Bool} {n T : ℕ} + (hm : Nat.size x.length ≤ slotBits k State + colBits T + timeBits T) (idx : ℕ) + (w : Fin (aWidth (slotBits k State) (colBits T) (timeBits T) x n T + + 3 * (13 + slotBits k State))) : + ((middleCircuit tm x n T).comp (fieldCircuit hm)).eval interpretation + (fun i => idx.testBit i) w = + if h : w.val < aWidth (slotBits k State) (colBits T) (timeBits T) x n T then + fieldVal (slotBits k State) (colBits T) (timeBits T) x n T idx w + else tableFn tm (fun i => fieldVal (slotBits k State) (colBits T) (timeBits T) x n T idx + (keyWire (slotBits k State) (colBits T) (timeBits T) i)) + ⟨w.val - aWidth (slotBits k State) (colBits T) (timeBits T) x n T, by omega⟩ := by + rw [Circuit.eval_comp, middleCircuit, Circuit.eval_append] + split + · rename_i h + have hw : w = Fin.castAdd _ ⟨w.val, h⟩ := rfl + conv_lhs => rw [hw] + rw [Fin.append_left, Circuit.eval_wiring, Function.comp_apply, id, eval_fieldCircuit] + · rename_i h + have hw : w = Fin.natAdd _ (⟨w.val - aWidth (slotBits k State) (colBits T) (timeBits T) x n T, + by omega⟩ : Fin (3 * (13 + slotBits k State))) := by ext; simp; omega + conv_lhs => rw [hw] + rw [Fin.append_right, Circuit.eval_comp, Circuit.eval_wiring, eval_tableCircuit] + congr 1 + funext i + simp [keySel, eval_fieldCircuit] + +theorem getElem?_litBits (w : ℕ) (l : Literal ℕ) {i : ℕ} (hi : i < w + 1) : + (litBits w l)[i]? = some (if i < w then l.1.testBit i else l.2) := by + unfold litBits + split + · rename_i h + rw [List.getElem?_append_left (by simpa using h)] + simp [h] + · rename_i h + rw [List.getElem?_append_right (by simp; omega)] + simp [show i - w = 0 by omega] + +/-- **The decoder computes the clause with a given number**: its outputs are the bits of the +three literals of `clauseAt`, for `n < 2 T` and a fixed prefix of length at most `n`. -/ +theorem ofFn_eval_decoder {x : List Bool} {n T : ℕ} (hn : n < 2 * T) (hx : x.length ≤ n) + (hm : Nat.size x.length ≤ slotBits k State + colBits T + timeBits T) {idx : ℕ} + (hidx : idx < 2 ^ (4 + (slotBits k State + colBits T + timeBits T))) : + List.ofFn ((decoder (n := n) tm hm).eval interpretation (fun i => idx.testBit i)) = + (clauseAt tm x n T idx).flatMap + (litBits (2 + (slotBits k State + colBits T + timeBits T))) := by + have hs := one_le_slotBits (k := k) (State := State) + have h2T : 2 * T < 2 ^ colBits T := Nat.lt_size_self _ + have hT : T < 2 ^ timeBits T := Nat.lt_size_self _ + have hkey := ofBits_keyWire (x := x) (n := n) (T := T) hidx hs h2T hT hn hx + have hcl := clauseOf_eq_map_shapeOf tm x n T (idx % 4) (idx / 4 % 4) (idx / 16) + rw [clauseAt, hcl] + generalize hκ : Key.ofFields (slotBits k State) (colBits T) x.length n T (idx % 4) (idx / 4 % 4) + (idx / 16) = κ at hkey + have hlen := length_shapeOf tm κ + apply List.ext_getElem? + intro o + rw [List.getElem?_ofFn] + by_cases ho : o < 3 * (2 + (slotBits k State + colBits T + timeBits T) + 1) + · rw [dite_eq_left ho, getElem?_flatMap_of_length (m := 2 + (slotBits k State + colBits T + + timeBits T) + 1) (fun _ => by simp [litBits]) _ o (by simp [hlen]; omega)] + have hr : o / (2 + (slotBits k State + colBits T + timeBits T) + 1) < 3 := + Nat.div_lt_of_lt_mul (by rwa [Nat.mul_comm]) + have hi : o % (2 + (slotBits k State + colBits T + timeBits T) + 1) < + 2 + (slotBits k State + colBits T + timeBits T) + 1 := Nat.mod_lt _ (by omega) + rw [List.getElem_map, getElem?_litBits _ _ hi] + congr 1 + -- the left side + rw [decoder, Circuit.eval_comp] + simp only [outputCircuit, Circuit.eval_pi, Formula.eval_toCircuit, Formula.eval_rename] + set z' := ((middleCircuit tm x n T).comp (fieldCircuit hm)).eval interpretation + (fun i => idx.testBit i) with hz' + let z : ℕ → Bool := fun w => if h : w < aWidth (slotBits k State) (colBits T) (timeBits T) + x n T + 3 * (13 + slotBits k State) then z' ⟨w, h⟩ else false + have hzz : (z' ∘ fun j : Fin 28 => (⟨wireNat (slotBits k State) (colBits T) (timeBits T) x n T + o j, wireNat_lt ho j.isLt⟩ : Fin _)) = + fun j : Fin 28 => z (wireNat (slotBits k State) (colBits T) (timeBits T) x n T o j) := by + funext j + simp [z, wireNat_lt ho j.isLt] + have hF : ∀ w < aWidth (slotBits k State) (colBits T) (timeBits T) x n T, + z w = fieldVal (slotBits k State) (colBits T) (timeBits T) x n T idx w := by + intro w hw + simp only [z, show w < aWidth (slotBits k State) (colBits T) (timeBits T) x n T + + 3 * (13 + slotBits k State) by omega, ↓reduceDIte, hz', eval_middle_field, hw] + rw [hzz, eval_outTemplate_wireNat hidx hF] + set r := o / (2 + (slotBits k State + colBits T + timeBits T) + 1) with hrdef + set i := o % (2 + (slotBits k State + colBits T + timeBits T) + 1) with hidef + set sh := (shapeOf tm κ).getD r defaultShape with hsh + have hsh' : sh = (shapeOf tm κ)[r]'(by omega) := by + rw [hsh, List.getD_eq_getElem?_getD, List.getElem?_eq_getElem (by omega)] + rfl + have htab (f : ℕ) (hf : f < 13 + slotBits k State) : + z (aWidth (slotBits k State) (colBits T) (timeBits T) x n T + r * (13 + slotBits k State) + + f) = if f < 2 + slotBits k State then tagSlot sh f + else selOf κ.c0 κ.t0 sh (f - (2 + slotBits k State)) := by + have hlt : r * (13 + slotBits k State) + f < 3 * (13 + slotBits k State) := by + have : r * (13 + slotBits k State) + (13 + slotBits k State) ≤ + 3 * (13 + slotBits k State) := by + rw [← Nat.succ_mul]; exact Nat.mul_le_mul_right _ hr + omega + simp only [z, show aWidth (slotBits k State) (colBits T) (timeBits T) x n T + + r * (13 + slotBits k State) + f < aWidth (slotBits k State) (colBits T) (timeBits T) x n T + + 3 * (13 + slotBits k State) by omega, ↓reduceDIte, hz', eval_middle_field, + show ¬ aWidth (slotBits k State) (colBits T) (timeBits T) x n T + + r * (13 + slotBits k State) + f < aWidth (slotBits k State) (colBits T) (timeBits T) x n T + by omega] + simp only [tableFn, hkey] + have h₁ : (aWidth (slotBits k State) (colBits T) (timeBits T) x n T + + r * (13 + slotBits k State) + f - aWidth (slotBits k State) (colBits T) (timeBits T) + x n T) = r * (13 + slotBits k State) + f := by omega + have h₂ : (r * (13 + slotBits k State) + f) / (13 + slotBits k State) = r := by + rw [Nat.mul_comm, Nat.mul_add_div (by omega), Nat.div_eq_of_lt hf, Nat.add_zero] + have h₃ : (r * (13 + slotBits k State) + f) % (13 + slotBits k State) = f := by + rw [Nat.mul_comm, Nat.mul_add_mod, Nat.mod_eq_of_lt hf] + simp only [h₁, h₂, h₃, hsh] + rw [outBit_congr (ts' := tagSlot sh) (sel' := selOf κ.c0 κ.t0 sh) + (fun f hf => by rw [htab f (by omega), ite_eq_left hf]) + (fun q hq => by rw [htab _ (by omega), ite_eq_right (by omega), Nat.add_sub_cancel_left])] + have hmem : sh ∈ shapeOf tm κ := by rw [hsh']; exact List.getElem_mem _ + have hc0 : κ.c0 = decide (colOf (slotBits k State) (colBits T) (idx / 16) = 0) := by + rw [← hκ]; rfl + have ht0 : κ.t0 = decide (timeOf (slotBits k State) (colBits T) (idx / 16) = 0) := by + rw [← hκ]; rfl + rw [hc0, ht0] + rw [← hκ] at hmem + rw [outBit_shape (x := x) hs (inRange_of_mem_shapeOf tm hmem) ?_ (by omega), ← hsh'] + · rfl + · intro hl + have hp := lt_of_mem_shapeOf tm hmem hl + have hX := Nat.lt_size_self x.length + have hlk : lookW (slotBits k State) (colBits T) (timeBits T) x < + aWidth (slotBits k State) (colBits T) (timeBits T) x n T := lookW_lt + rw [hF _ hlk, lookW, ite_eq_right (by omega), fieldVal_or, + Nat.mod_eq_of_lt (show idx / 16 < 2 ^ Nat.size x.length by omega)] + simp [show idx / 16 ≤ x.length - 1 by omega] + · rw [dite_eq_right ho, eq_comm, List.getElem?_eq_none] + rw [List.length_flatMap, List.map_congr_left (fun a _ => show (litBits + (2 + (slotBits k State + colBits T + timeBits T)) a).length = + 2 + (slotBits k State + colBits T + timeBits T) + 1 by simp [litBits])] + simp only [List.map_const', List.sum_replicate, smul_eq_mul, List.length_map, hlen] + omega + +/-! ### The decoder of the succinct tableau -/ + +/-- The constant of the size bound of the decoder, which depends only on the machine. -/ +noncomputable def sizeConst : ℕ := 204 * slotBits k State + 600 + (tableCircuit tm).size + +/-- **The decoder of the succinct tableau formula**: for `n < 2 T` and a fixed prefix `x` of length +at most `n`, a De Morgan circuit with code `decoderCode tm (x, n, T)` and at most +`sizeConst tm * (colBits T + timeBits T + x.length + 1)` gates computes, from the bits of a clause +number below `2 ^ clauseBits`, the bits of the clause `clauseAt tm x n T idx` of +`Cslib.Circuits.Boolean.Tableau.succinctCNF`: the label of each of its three literals in +`labelBits` bits, least significant first, then its sign. -/ +theorem exists_decoder (x : List Bool) {n T : ℕ} (hn : n < 2 * T) (hx : x.length ≤ n) : + ∃ c : Circuit signature (clauseBits k State T) (3 * (labelBits k State T + 1)), + c.code = decoderCode tm (x, n, T) ∧ + c.size ≤ sizeConst tm * (colBits T + timeBits T + x.length + 1) ∧ + ∀ idx < 2 ^ clauseBits k State T, + List.ofFn (c.eval interpretation fun i => idx.testBit i) = + (clauseAt tm x n T idx).flatMap (litBits (labelBits k State T)) := by + have hm : Nat.size x.length ≤ slotBits k State + colBits T + timeBits T := by + have : Nat.size x.length ≤ colBits T := Nat.size_le_size (by omega) + omega + exact ⟨decoder (n := n) tm hm, code_decoder tm hm, size_decoder_le tm hm, + fun idx hidx => ofFn_eval_decoder tm hn hx hm hidx⟩ + +end Cslib.Circuits.Boolean.Tableau.Decoder diff --git a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Circuit.lean b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Circuit.lean new file mode 100644 index 000000000..ce1ced92e --- /dev/null +++ b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Circuit.lean @@ -0,0 +1,628 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Boolean.Tableau.Decoder.Fields +public import Complexity.Computability.Circuit.Boolean.Tableau.Decoder.Shape +public import Complexity.Computability.Circuit.Boolean.CodeTemplate +public import Complexity.Computability.Circuit.Pi +import Mathlib.Data.Nat.Size + +/-! +# The stages of the decoder circuit of the succinct tableau + +The decoder computes, from the bits of a clause number, the bits of the clause of the tableau +formula on bit-field labels (`Cslib.Circuits.Boolean.Tableau.clauseAt`): for each of its three +literals, the label in `labelBits` bits, least significant first, then the sign (`litBits`). It +composes three stages: + +* the field stage (`Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Fields.lean`): + comparison chains, borrows and carries, and the lookup of the fixed prefix `x`; +* a circuit of constant size (`tableCircuit`) that computes the shapes of the three literals + (`Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Shape.lean`) from the clause and tag + bits, the slot bits and the comparisons (`ofBits_keyWire`); +* the output stage: one copy of a fixed formula of 63 gates (`outTemplate`) per output bit, reading + the wires `wireNat`, which selects the bit of the label from the fields chosen by the shape + (`eval_outTemplate_wireNat`). + +The stages are assembled, with the correctness and the linear size of the whole circuit, in +`Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Assembly.lean`. + +## Main definitions + +* `Cslib.Circuits.Boolean.Tableau.Decoder.litBits`: the bits of a literal. +* `Cslib.Circuits.Boolean.Tableau.Decoder.outTemplate`: the formula of an output bit. +* `Cslib.Circuits.Boolean.Tableau.Decoder.tableFn`, + `Cslib.Circuits.Boolean.Tableau.Decoder.tableCircuit`: the constant stage. +* `Cslib.Circuits.Boolean.Tableau.Decoder.wireNat`, + `Cslib.Circuits.Boolean.Tableau.Decoder.keyWire`: the wires read by the output stage and by the + constant stage. +* `Cslib.Circuits.Boolean.Tableau.Decoder.fieldCircuit`: the field stage as a circuit. + +## Main results + +* `Cslib.Circuits.Boolean.Tableau.Decoder.eval_fieldCircuit`: the field stage computes the intended + values. +* `Cslib.Circuits.Boolean.Tableau.Decoder.ofBits_keyWire`: the constant stage reads the key of the + fields of the clause number. +* `Cslib.Circuits.Boolean.Tableau.Decoder.eval_outTemplate_wireNat`: each output bit computes + `outBit`. + +## References + +* [R. Williams, *Improving exhaustive search implies superpolynomial lower bounds*][Williams2010] +-/ + +@[expose] public section + +namespace Cslib.Circuits.Boolean.Tableau.Decoder + +open Turing MultiTapeTM Cslib.Logic.PL + +/-! ### The formula of an output bit -/ + +/-- Input `j` of the formula of an output bit. -/ +def tIn (j : ℕ) (h : j < 28 := by omega) : Formula signature 28 := .input ⟨j, h⟩ + +/-- Term `k` of the formula of an output bit: a selector and the exclusive or of two bits. -/ +def term (k : ℕ) (h : 3 * k + 3 < 28 := by omega) : Formula signature 28 := + (tIn (1 + 3 * k)).and ((tIn (2 + 3 * k)).xor (tIn (3 + 3 * k))) + +/-- **The formula of an output bit**: the input `0`, or one of nine terms, each the conjunction +of a selector with the exclusive or of two bits. -/ +def outTemplate : Formula signature 28 := + (tIn 0).or ((term 0).or ((term 1).or ((term 2).or ((term 3).or ((term 4).or ((term 5).or + ((term 6).or ((term 7).or (term 8))))))))) + +theorem eval_outTemplate (y : Fin 28 → Bool) : + outTemplate.eval interpretation y = + (y 0 || (y 1 && (y 2 ^^ y 3)) || (y 4 && (y 5 ^^ y 6)) || (y 7 && (y 8 ^^ y 9)) || + (y 10 && (y 11 ^^ y 12)) || (y 13 && (y 14 ^^ y 15)) || (y 16 && (y 17 ^^ y 18)) || + (y 19 && (y 20 ^^ y 21)) || (y 22 && (y 23 ^^ y 24)) || (y 25 && (y 26 ^^ y 27))) := by + simp only [outTemplate, term, tIn, Formula.eval_or, Formula.eval_and, Formula.eval_xor, + Formula.eval_input, Bool.or_assoc] + rfl + +/-- The formula of an output bit on the values of numbered wires. -/ +theorem eval_outTemplate_nat (y : ℕ → Bool) : + outTemplate.eval interpretation (fun j => y j) = + (y 0 || (y 1 && (y 2 ^^ y 3)) || (y 4 && (y 5 ^^ y 6)) || (y 7 && (y 8 ^^ y 9)) || + (y 10 && (y 11 ^^ y 12)) || (y 13 && (y 14 ^^ y 15)) || (y 16 && (y 17 ^^ y 18)) || + (y 19 && (y 20 ^^ y 21)) || (y 22 && (y 23 ^^ y 24)) || (y 25 && (y 26 ^^ y 27))) := + eval_outTemplate _ + +theorem size_outTemplate : outTemplate.size = 63 := by + simp [outTemplate, term, tIn] + +/-! ### The bits of a literal -/ + +/-- **The bits of a literal**: its label in `w` bits, least significant first, then its sign. -/ +def litBits (w : ℕ) (l : Literal ℕ) : List Bool := + List.ofFn (fun i : Fin w => l.1.testBit i) ++ [l.2] + +/-! ### The constant stage -/ + +/-- The bit `i` of a bit string, or `false`. -/ +def bitAt {m : ℕ} (b : BitString m) (i : ℕ) : Bool := if h : i < m then b ⟨i, h⟩ else false + +/-- The number with the first `w` bits of `f`, least significant first. -/ +def bitsVal (f : ℕ → Bool) : ℕ → ℕ + | 0 => 0 + | w + 1 => bitsVal f w + 2 ^ w * (f w).toNat + +theorem bitsVal_testBit (v w : ℕ) : bitsVal (fun i => v.testBit i) w = v % 2 ^ w := by + induction w with + | zero => simp [bitsVal, Nat.mod_one] + | succ w ih => rw [bitsVal, ih, mod_two_pow_succ] + +theorem bitsVal_congr {f g : ℕ → Bool} {w : ℕ} (h : ∀ i < w, f i = g i) : + bitsVal f w = bitsVal g w := by + induction w with + | zero => rfl + | succ w ih => rw [bitsVal, bitsVal, ih fun i hi => h i (by omega), h w (by omega)] + +/-- The key of the input bits of the constant stage: the clause and tag bits, `s` slot bits, +and the fourteen comparison chains. -/ +def Key.ofBits (s : ℕ) {m : ℕ} (b : BitString m) : Key where + j := (bitAt b 0).toNat + 2 * (bitAt b 1).toNat + tag := (bitAt b 2).toNat + 2 * (bitAt b 3).toNat + slot := bitsVal (fun i => bitAt b (4 + i)) s + c0 := bitAt b (4 + s) + cGt1 := bitAt b (4 + s + 3) + cGtN := bitAt b (4 + s + 4) + cGtN1 := bitAt b (4 + s + 5) + cLt2T := bitAt b (4 + s + 6) + cGt2T := bitAt b (4 + s + 7) + cLtT := bitAt b (4 + s + 8) + cGtT := bitAt b (4 + s + 9) + t0 := bitAt b (4 + s + 2) + tGtT := bitAt b (4 + s + 10) + pLtX := bitAt b (4 + s + 11) + p0 := bitAt b (4 + s + 12) + pGt1 := bitAt b (4 + s + 13) + +variable {k : ℕ} {State : Type} [Fintype State] [DecidableEq State] + (tm : MultiTapeTM k Bool State) + +/-- The default shape. -/ +def defaultShape : Shape := (.const false, .const false) + +/-- **The function of the constant stage**: bit `f` of literal `r` (`f < 13 + s`) is a tag or slot +bit or a selector of the shape of literal `r` of the key of the input bits. -/ +noncomputable def tableFn (b : BitString (4 + slotBits k State + 14)) + (o : Fin (3 * (13 + slotBits k State))) : Bool := + let κ := Key.ofBits (slotBits k State) b + let sh := (shapeOf tm κ).getD (o / (13 + slotBits k State)) defaultShape + if o % (13 + slotBits k State) < 2 + slotBits k State then + tagSlot sh (o % (13 + slotBits k State)) + else selOf κ.c0 κ.t0 sh (o % (13 + slotBits k State) - (2 + slotBits k State)) + +/-- **The constant stage**: a circuit computing `tableFn`, one disjunctive normal form per output. +Its size depends only on the machine. -/ +noncomputable def tableCircuit : + Circuit signature (4 + slotBits k State + 14) (3 * (13 + slotBits k State)) := + Circuit.pi fun o => + (Classical.choose (Formula.exists_computes (fun b => tableFn tm b o))).toCircuit + +theorem eval_tableCircuit (b : BitString (4 + slotBits k State + 14)) : + (tableCircuit tm).eval interpretation b = tableFn tm b := by + funext o + simp only [tableCircuit, Circuit.eval_pi, Formula.eval_toCircuit] + exact Classical.choose_spec (Formula.exists_computes (fun b => tableFn tm b o)) b + +/-! ### The wires of the output stage -/ + +/-- The wire of the constant `b`, after `N` inputs. -/ +def constW (N : ℕ) (b : Bool) : ℕ := if b then N + 1 else N + +/-- The wire of bit `q` of the field of width `w` at offset `off`, or of `false`. -/ +def bitW (N off w q : ℕ) : ℕ := if q < w then off + q else N + +/-- The wire of chain `k` after `q` bits, `true` for `q = 0`. -/ +def chainW (N P k q : ℕ) : ℕ := if q = 0 then N + 1 else chainBase N + k * P + (q - 1) + +/-- The first wire of the term `kk` of the payload bit `q`. -/ +def uWire (s cb tb T q kk : ℕ) : ℕ := + if kk = 0 then 4 + q + else if kk = 1 then bitW (4 + (s + cb + tb)) (4 + s) cb q + else if kk ≤ 5 then + (if s ≤ q ∧ q < s + cb then + (if kk = 5 then constW (4 + (s + cb + tb)) ((2 * T).testBit (q - s)) + else bitW (4 + (s + cb + tb)) (4 + s) cb (q - s)) + else 4 + (s + cb + tb)) + else if kk ≤ 8 then + (if s + cb ≤ q then + (if kk = 8 then constW (4 + (s + cb + tb)) (T.testBit (q - s - cb)) + else bitW (4 + (s + cb + tb)) (4 + s + cb) tb (q - s - cb)) + else 4 + (s + cb + tb)) + else 4 + (s + cb + tb) + +/-- The second wire of the term `kk` of the payload bit `q`: a borrow or a carry. -/ +def vWire (s cb tb q kk : ℕ) : ℕ := + if kk = 1 then chainW (4 + (s + cb + tb)) (s + cb + tb) 0 q + else if kk = 3 then + (if s ≤ q ∧ q < s + cb then chainW (4 + (s + cb + tb)) (s + cb + tb) 0 (q - s) + else 4 + (s + cb + tb)) + else if kk = 4 then + (if s ≤ q ∧ q < s + cb then chainW (4 + (s + cb + tb)) (s + cb + tb) 1 (q - s) + else 4 + (s + cb + tb)) + else if kk = 7 then + (if s + cb ≤ q then chainW (4 + (s + cb + tb)) (s + cb + tb) 2 (q - s - cb) + else 4 + (s + cb + tb)) + else 4 + (s + cb + tb) + +/-- The number of wires of the field stage. -/ +def aWidth (s cb tb : ℕ) (x : List Bool) (n T : ℕ) : ℕ := + 4 + (s + cb + tb) + (fieldGates s cb tb x n T).length + +/-- The wire of the lookup of `x`. -/ +def lookW (s cb tb : ℕ) (x : List Bool) : ℕ := + if x.length = 0 then 4 + (s + cb + tb) + else orBase (4 + (s + cb + tb)) (s + cb + tb) (Nat.size x.length) + (x.length - 1) + +/-- **The wire read at input `j` of the formula of output bit `o`** of the output stage. Output +`o` is bit `o % (3 + P)` of literal `o / (3 + P)`, where `P = s + cb + tb`: two tag bits, `P` +payload bits and the sign. The output `r * (13 + s) + f` of the constant stage is wire +`aWidth + r * (13 + s) + f`. -/ +def wireNat (s cb tb : ℕ) (x : List Bool) (n T o j : ℕ) : ℕ := + if o % (2 + (s + cb + tb) + 1) < 2 then + (if j = 0 then aWidth s cb tb x n T + o / (2 + (s + cb + tb) + 1) * (13 + s) + + o % (2 + (s + cb + tb) + 1) else 4 + (s + cb + tb)) + else if o % (2 + (s + cb + tb) + 1) < 2 + (s + cb + tb) then + (if j = 0 then + (if o % (2 + (s + cb + tb) + 1) - 2 < s then + aWidth s cb tb x n T + o / (2 + (s + cb + tb) + 1) * (13 + s) + o % (2 + (s + cb + tb) + 1) + else 4 + (s + cb + tb)) + else if (j - 1) % 3 = 0 then + aWidth s cb tb x n T + o / (2 + (s + cb + tb) + 1) * (13 + s) + (2 + s + (j - 1) / 3) + else if (j - 1) % 3 = 1 then uWire s cb tb T (o % (2 + (s + cb + tb) + 1) - 2) ((j - 1) / 3) + else vWire s cb tb (o % (2 + (s + cb + tb) + 1) - 2) ((j - 1) / 3)) + else + (if j = 0 then aWidth s cb tb x n T + o / (2 + (s + cb + tb) + 1) * (13 + s) + (2 + s + 9) + else if j = 1 then aWidth s cb tb x n T + o / (2 + (s + cb + tb) + 1) * (13 + s) + (2 + s + 10) + else if j = 2 then lookW s cb tb x + else 4 + (s + cb + tb)) + +/-- The wire of input `i` of the constant stage: a clause, tag or slot bit, or the last gate of a +comparison chain. -/ +def keyWire (s cb tb i : ℕ) : ℕ := + if i < 4 + s then i + else chainBase (4 + (s + cb + tb)) + (i - (4 + s)) * (s + cb + tb) + (s + cb + tb - 1) + +/-! ### Wire bounds -/ + +section Bounds + +variable {s cb tb : ℕ} {x : List Bool} {n T : ℕ} + +theorem chainBase_add_lt_aWidth {k q : ℕ} (hk : k < 14) (hq : q < s + cb + tb) : + chainBase (4 + (s + cb + tb)) + k * (s + cb + tb) + q < aWidth s cb tb x n T := by + have h₁ : k * (s + cb + tb) + (s + cb + tb) ≤ 14 * (s + cb + tb) := by + rw [← Nat.succ_mul]; exact Nat.mul_le_mul_right _ hk + simp only [aWidth, length_fieldGates, chainBase] + omega + +theorem lt_aWidth_of_lt {w : ℕ} (hw : w < 2 * (4 + (s + cb + tb)) + 2) : + w < aWidth s cb tb x n T := by + simp only [aWidth, length_fieldGates] + omega + +theorem constW_lt (b : Bool) : constW (4 + (s + cb + tb)) b < aWidth s cb tb x n T := + lt_aWidth_of_lt (by unfold constW; split <;> omega) + +theorem bitW_lt {off w q : ℕ} (h : off + w ≤ 4 + (s + cb + tb)) : + bitW (4 + (s + cb + tb)) off w q < aWidth s cb tb x n T := + lt_aWidth_of_lt (by unfold bitW; split <;> omega) + +theorem chainW_lt {k q : ℕ} (hk : k < 14) (hq : q ≤ s + cb + tb) : + chainW (4 + (s + cb + tb)) (s + cb + tb) k q < aWidth s cb tb x n T := by + unfold chainW + split + · exact lt_aWidth_of_lt (by omega) + · exact chainBase_add_lt_aWidth hk (by omega) + +theorem uWire_lt (q kk : ℕ) (hq : q < s + cb + tb) : + uWire s cb tb T q kk < aWidth s cb tb x n T := by + unfold uWire + split_ifs <;> first + | exact lt_aWidth_of_lt (by omega) + | exact constW_lt _ + | exact bitW_lt (by omega) + +theorem vWire_lt (q kk : ℕ) (hq : q < s + cb + tb) : + vWire s cb tb q kk < aWidth s cb tb x n T := by + unfold vWire + split_ifs <;> first + | exact lt_aWidth_of_lt (by omega) + | exact chainW_lt (by omega) (by omega) + +theorem lookW_lt : lookW s cb tb x < aWidth s cb tb x n T := by + unfold lookW + split + · exact lt_aWidth_of_lt (by omega) + · simp only [aWidth, length_fieldGates, orBase, treeBase, chainBase] + omega + +/-- The wires of the output stage lie among the wires of the field and constant stages. -/ +theorem wireNat_lt {o j : ℕ} (ho : o < 3 * (2 + (s + cb + tb) + 1)) (hj : j < 28) : + wireNat s cb tb x n T o j < aWidth s cb tb x n T + 3 * (13 + s) := by + have hj3 : (j - 1) / 3 ≤ 8 := by omega + have hr : o / (2 + (s + cb + tb) + 1) < 3 := Nat.div_lt_of_lt_mul (by rwa [Nat.mul_comm]) + have hr' : o / (2 + (s + cb + tb) + 1) * (13 + s) + (13 + s) ≤ 3 * (13 + s) := by + rw [← Nat.succ_mul]; exact Nat.mul_le_mul_right _ hr + have hU := fun kk (hq : o % (2 + (s + cb + tb) + 1) - 2 < s + cb + tb) => + uWire_lt (x := x) (n := n) (T := T) (o % (2 + (s + cb + tb) + 1) - 2) kk hq + have hV := fun kk (hq : o % (2 + (s + cb + tb) + 1) - 2 < s + cb + tb) => + vWire_lt (x := x) (n := n) (T := T) (o % (2 + (s + cb + tb) + 1) - 2) kk hq + have hL := lookW_lt (s := s) (cb := cb) (tb := tb) (x := x) (n := n) (T := T) + have hN : 4 + (s + cb + tb) < aWidth s cb tb x n T := lt_aWidth_of_lt (by omega) + unfold wireNat + split_ifs + all_goals first + | omega + | (have := hU ((j - 1) / 3) (by omega); omega) + | (have := hV ((j - 1) / 3) (by omega); omega) + +theorem keyWire_lt {i : ℕ} (hi : i < 4 + s + 14) : keyWire s cb tb i < aWidth s cb tb x n T := by + unfold keyWire + split + · exact lt_aWidth_of_lt (by omega) + · have h₁ : (i - (4 + s)) * (s + cb + tb) + (s + cb + tb) ≤ 14 * (s + cb + tb) := by + rw [← Nat.succ_mul]; exact Nat.mul_le_mul_right _ (by omega) + have := Nat.one_le_two_pow (n := Nat.size x.length + 1) + simp only [aWidth, length_fieldGates, chainBase] + omega + +end Bounds + +/-! ### The circuit -/ + +section Assembly + +variable {s cb tb : ℕ} {x : List Bool} {n T : ℕ} + +theorem isSome_fieldCode (hm : Nat.size x.length ≤ s + cb + tb) : + (Circuit.ofCode (σ := signature) (4 + (s + cb + tb)) (aWidth s cb tb x n T) + (fieldGates s cb tb x n T, List.range (aWidth s cb tb x n T))).isSome := by + rw [Circuit.isSome_ofCode_iff] + refine ⟨isCodeFrom_of_allGates (allGates_wellFormedAt_fieldGates hm), by simp, fun i hi => ?_⟩ + simpa [aWidth] using hi + +/-- **The field stage** as a circuit whose outputs are all its wires. -/ +noncomputable def fieldCircuit (hm : Nat.size x.length ≤ s + cb + tb) : + Circuit signature (4 + (s + cb + tb)) (aWidth s cb tb x n T) := + (Circuit.ofCode _ _ (fieldGates s cb tb x n T, List.range (aWidth s cb tb x n T))).get + (isSome_fieldCode hm) + +theorem code_fieldCircuit (hm : Nat.size x.length ≤ s + cb + tb) : + (fieldCircuit (n := n) (T := T) hm).code = + (fieldGates s cb tb x n T, List.range (aWidth s cb tb x n T)) := + Circuit.code_of_ofCode_eq_some (Option.some_get _).symm + +theorem size_fieldCircuit (hm : Nat.size x.length ≤ s + cb + tb) : + (fieldCircuit (n := n) (T := T) hm).size = (fieldGates s cb tb x n T).length := by + rw [← Circuit.length_code_fst, code_fieldCircuit] + +theorem eval_fieldCircuit (hm : Nat.size x.length ≤ s + cb + tb) (idx : ℕ) + (w : Fin (aWidth s cb tb x n T)) : + (fieldCircuit hm).eval interpretation (fun i => idx.testBit i) w = + fieldVal s cb tb x n T idx w := by + have hout : ((fieldCircuit (n := n) (T := T) hm).outputs w).toNat = w := by + have h := congrArg Prod.snd (code_fieldCircuit (n := n) (T := T) hm) + simp only [Circuit.code] at h + have := congrArg (fun l => l[w.val]?) h + simpa using this + rw [Circuit.eval_eq_evalOutput, code_fieldCircuit, hout, evalOutput] + exact getD_evalCode_fieldGates hm idx (by simpa [aWidth] using w.isLt) + +end Assembly + +/-! ### Values of the wires read by the output stage -/ + +section Values + +variable {s cb tb : ℕ} {x : List Bool} {n T : ℕ} + +theorem fieldVal_constW (idx : ℕ) (b : Bool) : + fieldVal s cb tb x n T idx (constW (4 + (s + cb + tb)) b) = b := by + cases b + · exact fieldVal_false idx + · exact fieldVal_true idx + +theorem fieldVal_bitW (idx : ℕ) {off w q : ℕ} (h : off + w ≤ 4 + (s + cb + tb)) : + fieldVal s cb tb x n T idx (bitW (4 + (s + cb + tb)) off w q) = + (idx / 2 ^ off % 2 ^ w).testBit q := by + unfold bitW + split + · rw [fieldVal_input idx (by omega), Nat.testBit_mod_two_pow, Nat.testBit_div_two_pow] + simp [*, Nat.add_comm] + · rw [fieldVal_false, Nat.testBit_mod_two_pow] + simp [*] + +theorem fieldVal_payload (idx : ℕ) {q : ℕ} (hq : q < s + cb + tb) : + fieldVal s cb tb x n T idx (4 + q) = (idx / 16).testBit q := by + rw [fieldVal_input idx (by omega), show idx / 16 = idx / 2 ^ 4 from rfl, + Nat.testBit_div_two_pow, Nat.add_comm] + +/-- The value of chain `k` after `q` bits, through `chainW`. -/ +theorem fieldVal_chainW (idx : ℕ) {k q : ℕ} (hk : k < 14) (hq : q ≤ s + cb + tb) + (hinit : (chain s cb tb x.length n T k).2.2.2.2 = true) : + fieldVal s cb tb x n T idx (chainW (4 + (s + cb + tb)) (s + cb + tb) k q) = + chainVal (chain s cb tb x.length n T k) idx q := by + unfold chainW + split + · subst q + rw [fieldVal_true] + simp [chainVal, cmp, hinit, Nat.mod_one] + · rw [fieldVal_chain idx hk (by omega), Nat.sub_add_cancel (by omega)] + +/-- The column of the payload, from the clause number. -/ +theorem div_mod_eq_colOf (idx : ℕ) : + idx / 2 ^ (4 + s) % 2 ^ cb = colOf s cb (idx / 16) := by + rw [colOf, Nat.pow_add, ← Nat.div_div_eq_div_mul] + +/-- The block of the payload, from a clause number of `4 + (s + cb + tb)` bits. -/ +theorem div_mod_eq_timeOf {idx : ℕ} (hidx : idx < 2 ^ (4 + (s + cb + tb))) : + idx / 2 ^ (4 + s + cb) % 2 ^ tb = timeOf s cb (idx / 16) := by + have h₁ : idx / 2 ^ (4 + s + cb) = timeOf s cb (idx / 16) := by + rw [timeOf, Nat.pow_add, Nat.pow_add, ← Nat.div_div_eq_div_mul, ← Nat.div_div_eq_div_mul] + rw [h₁, Nat.mod_eq_of_lt] + rw [← h₁] + apply Nat.div_lt_of_lt_mul + rw [← Nat.pow_add] + calc idx < 2 ^ (4 + (s + cb + tb)) := hidx + _ = 2 ^ (4 + s + cb + tb) := by congr 1; omega + +theorem fieldVal_borrowC (idx : ℕ) {q : ℕ} (hq : q ≤ s + cb + tb) : + fieldVal s cb tb x n T idx (chainW (4 + (s + cb + tb)) (s + cb + tb) 0 q) = + decide (colOf s cb (idx / 16) % 2 ^ q = 0) := by + rw [fieldVal_chainW idx (by omega) hq rfl] + simp only [chainVal, chain, ↓reduceIte, cmp, Nat.zero_mod, div_mod_eq_colOf] + simp + +theorem fieldVal_carryC (idx : ℕ) {q : ℕ} (hq : q ≤ s + cb + tb) : + fieldVal s cb tb x n T idx (chainW (4 + (s + cb + tb)) (s + cb + tb) 1 q) = + decide (colOf s cb (idx / 16) % 2 ^ q = 2 ^ q - 1) := by + rw [fieldVal_chainW idx (by omega) hq rfl] + simp only [chainVal, chain, one_ne_zero, ↓reduceIte, cmp, div_mod_eq_colOf] + have hK : (2 ^ (s + cb + tb) - 1) % 2 ^ q = 2 ^ q - 1 := by + obtain ⟨d, hd⟩ : ∃ d, s + cb + tb = q + d := ⟨s + cb + tb - q, by omega⟩ + rw [hd, Nat.pow_add] + have h1 := Nat.one_le_two_pow (n := q) + have h2 := Nat.one_le_two_pow (n := d) + have h3 : 2 ^ q ≤ 2 ^ q * 2 ^ d := Nat.le_mul_of_pos_right _ (by omega) + rw [show 2 ^ q * 2 ^ d - 1 = 2 ^ q * (2 ^ d - 1) + (2 ^ q - 1) by + rw [Nat.mul_sub_one]; omega, Nat.mul_add_mod, Nat.mod_eq_of_lt (by omega)] + rw [hK] + have := Nat.mod_lt (colOf s cb (idx / 16)) (Nat.two_pow_pos q) + rw [Bool.eq_iff_iff] + simp + omega + +theorem fieldVal_borrowT (idx : ℕ) (hidx : idx < 2 ^ (4 + (s + cb + tb))) {q : ℕ} + (hq : q ≤ s + cb + tb) : + fieldVal s cb tb x n T idx (chainW (4 + (s + cb + tb)) (s + cb + tb) 2 q) = + decide (timeOf s cb (idx / 16) % 2 ^ q = 0) := by + rw [fieldVal_chainW idx (by omega) hq rfl] + simp only [chainVal, chain, OfNat.ofNat_ne_zero, OfNat.ofNat_ne_one, ↓reduceIte, cmp, + Nat.zero_mod, div_mod_eq_timeOf hidx] + simp + +/-- **The formula of an output bit computes `outBit`** on the values of its wires: the values of +the field stage, and the outputs `ts` of the constant stage for its literal. -/ +theorem eval_outTemplate_wireNat {z : ℕ → Bool} {idx : ℕ} (hidx : idx < 2 ^ (4 + (s + cb + tb))) + (hF : ∀ w < aWidth s cb tb x n T, z w = fieldVal s cb tb x n T idx w) (o : ℕ) : + outTemplate.eval interpretation (fun j => z (wireNat s cb tb x n T o j)) = + outBit s cb tb + (fun f => z (aWidth s cb tb x n T + o / (2 + (s + cb + tb) + 1) * (13 + s) + f)) + (fun q => z (aWidth s cb tb x n T + o / (2 + (s + cb + tb) + 1) * (13 + s) + (2 + s + q))) + (idx / 16) (colOf s cb (idx / 16)) (timeOf s cb (idx / 16)) T (z (lookW s cb tb x)) + (o % (2 + (s + cb + tb) + 1)) := by + have hN : z (4 + (s + cb + tb)) = false := + (hF _ (lt_aWidth_of_lt (by omega))).trans (fieldVal_false idx) + have hC (b : Bool) : z (constW (4 + (s + cb + tb)) b) = b := + (hF _ (constW_lt b)).trans (fieldVal_constW idx b) + have hcol (q : ℕ) : + z (bitW (4 + (s + cb + tb)) (4 + s) cb q) = (colOf s cb (idx / 16)).testBit q := by + rw [hF _ (bitW_lt (by omega)), fieldVal_bitW idx (by omega), div_mod_eq_colOf] + have htime (q : ℕ) : + z (bitW (4 + (s + cb + tb)) (4 + s + cb) tb q) = (timeOf s cb (idx / 16)).testBit q := by + rw [hF _ (bitW_lt (by omega)), fieldVal_bitW idx (by omega), div_mod_eq_timeOf hidx] + have hb0 {q : ℕ} (hq : q ≤ s + cb + tb) : + z (chainW (4 + (s + cb + tb)) (s + cb + tb) 0 q) = + decide (colOf s cb (idx / 16) % 2 ^ q = 0) := by + rw [hF _ (chainW_lt (by omega) hq), fieldVal_borrowC idx hq] + have hb1 {q : ℕ} (hq : q ≤ s + cb + tb) : + z (chainW (4 + (s + cb + tb)) (s + cb + tb) 1 q) = + decide (colOf s cb (idx / 16) % 2 ^ q = 2 ^ q - 1) := by + rw [hF _ (chainW_lt (by omega) hq), fieldVal_carryC idx hq] + have hb2 {q : ℕ} (hq : q ≤ s + cb + tb) : + z (chainW (4 + (s + cb + tb)) (s + cb + tb) 2 q) = + decide (timeOf s cb (idx / 16) % 2 ^ q = 0) := by + rw [hF _ (chainW_lt (by omega) hq), fieldVal_borrowT idx hidx hq] + rw [eval_outTemplate_nat (fun j => z (wireNat s cb tb x n T o j))] + simp only [wireNat] + unfold outBit + have hi := Nat.mod_lt o (show 0 < 2 + (s + cb + tb) + 1 by omega) + generalize o % (2 + (s + cb + tb) + 1) = i at hi ⊢ + generalize aWidth s cb tb x n T + o / (2 + (s + cb + tb) + 1) * (13 + s) = B + by_cases h2 : i < 2 + · simp [h2, hN] + · by_cases hP : i < 2 + (s + cb + tb) + · have hq : i - 2 < s + cb + tb := by omega + have hi2 : 2 + (i - 2) = i := by omega + generalize i - 2 = q at hq hi2 + subst hi2 + have hp : z (4 + q) = (idx / 16).testBit q := by + rw [hF _ (lt_aWidth_of_lt (by omega)), fieldVal_payload idx hq] + have h0 : z (if q < s then B + (2 + q) else 4 + (s + cb + tb)) = + (decide (q < s) && z (B + (2 + q))) := by + split <;> simp [*] + simp only [h2, hP, ↓reduceIte, h0, uWire, vWire, payTerm, colTerm, + timeTerm, Nat.reduceSub, Nat.reduceDiv, Nat.reduceMod, one_ne_zero, OfNat.ofNat_ne_zero, + OfNat.ofNat_ne_one, Nat.reduceEqDiff, Nat.reduceLeDiff, hp, hcol, hb0 hq.le, hN, + Bool.xor_false, Nat.add_zero] + by_cases hc : s ≤ q ∧ q < s + cb + · have ht : ¬ s + cb ≤ q := by omega + simp only [hc, ht, and_self, ↓reduceIte, hcol, hC, hb0 (show q - s ≤ s + cb + tb by omega), + hb1 (show q - s ≤ s + cb + tb by omega), hN, Bool.xor_false, Bool.and_false, + Bool.or_false, decide_true, decide_false, Bool.true_and, Bool.false_and] + simp only [Bool.or_assoc] + · simp only [hc, ↓reduceIte, hN, Bool.xor_false, Bool.and_false, Bool.or_false, + decide_false, Bool.false_and] + by_cases ht : s + cb ≤ q + · simp only [ht, ↓reduceIte, htime, hC, hb2 (show q - s - cb ≤ s + cb + tb by omega), + decide_true, Bool.true_and] + simp only [Bool.or_assoc] + · simp only [ht, ↓reduceIte, hN, Bool.xor_false, Bool.and_false, Bool.or_false, + decide_false, Bool.false_and] + · simp [h2, hP, hN] + +private theorem bitsVal_two (f : ℕ → Bool) : bitsVal f 2 = (f 0).toNat + 2 * (f 1).toNat := by + simp only [bitsVal]; omega + +/-- The value of the last gate of chain `k`. -/ +theorem fieldVal_keyWire_chain (idx : ℕ) {k : ℕ} (hk : k < 14) (hP : 1 ≤ s + cb + tb) : + fieldVal s cb tb x n T idx (keyWire s cb tb (4 + s + k)) = + chainVal (chain s cb tb x.length n T k) idx (s + cb + tb) := by + have h := fieldVal_chain (x := x) (n := n) (T := T) idx hk + (show s + cb + tb - 1 < s + cb + tb by omega) + rw [Nat.sub_add_cancel hP] at h + simp only [keyWire, show ¬ 4 + s + k < 4 + s by omega, ↓reduceIte, + show 4 + s + k - (4 + s) = k by omega] + exact h + +/-- **The key read by the constant stage is the key of the fields** of the clause number. -/ +theorem ofBits_keyWire {idx : ℕ} (hidx : idx < 2 ^ (4 + (s + cb + tb))) (hs : 1 ≤ s) + (h2T : 2 * T < 2 ^ cb) (hT : T < 2 ^ tb) (hn : n < 2 * T) (hx : x.length ≤ n) : + Key.ofBits s (fun i : Fin (4 + s + 14) => fieldVal s cb tb x n T idx (keyWire s cb tb i)) = + Key.ofFields s cb x.length n T (idx % 4) (idx / 4 % 4) (idx / 16) := by + have hP : 1 ≤ s + cb + tb := by omega + have hbit (i : ℕ) (hi : i < 4 + s + 14) : bitAt (fun i : Fin (4 + s + 14) => + fieldVal s cb tb x n T idx (keyWire s cb tb i)) i = + fieldVal s cb tb x n T idx (keyWire s cb tb i) := by simp [bitAt, hi] + have hin (i : ℕ) (hi : i < 4 + s) : bitAt (fun i : Fin (4 + s + 14) => + fieldVal s cb tb x n T idx (keyWire s cb tb i)) i = idx.testBit i := by + rw [hbit i (by omega), keyWire, ite_eq_left hi, fieldVal_input idx (by omega)] + have hch (k : ℕ) (hk : k < 14) : bitAt (fun i : Fin (4 + s + 14) => + fieldVal s cb tb x n T idx (keyWire s cb tb i)) (4 + s + k) = + chainVal (chain s cb tb x.length n T k) idx (s + cb + tb) := by + rw [hbit _ (by omega), fieldVal_keyWire_chain idx hk hP] + have hcb : 2 ^ cb ≤ 2 ^ (s + cb + tb) := Nat.pow_le_pow_right (by norm_num) (by omega) + have htb : 2 ^ tb ≤ 2 ^ (s + cb + tb) := Nat.pow_le_pow_right (by norm_num) (by omega) + have hc : colOf s cb (idx / 16) < 2 ^ cb := Nat.mod_lt _ (Nat.two_pow_pos _) + have ht : timeOf s cb (idx / 16) < 2 ^ tb := by + rw [← div_mod_eq_timeOf hidx]; exact Nat.mod_lt _ (Nat.two_pow_pos _) + have hp : idx / 16 < 2 ^ (s + cb + tb) := by + apply Nat.div_lt_of_lt_mul + calc idx < 2 ^ (4 + (s + cb + tb)) := hidx + _ = 16 * 2 ^ (s + cb + tb) := by rw [Nat.pow_add] + have hpmod : idx / 2 ^ 4 % 2 ^ (s + cb + tb) = idx / 16 := Nat.mod_eq_of_lt hp + have h1 : 1 < 2 ^ (s + cb + tb) := Nat.one_lt_two_pow (by omega) + have hcv (gt : Bool) (K : ℕ) (e : Bool) (hK : K < 2 ^ (s + cb + tb)) : + cmp gt (idx / 2 ^ (4 + s) % 2 ^ cb % 2 ^ (s + cb + tb)) (K % 2 ^ (s + cb + tb)) e = + cmp gt (colOf s cb (idx / 16)) K e := by + rw [div_mod_eq_colOf, Nat.mod_eq_of_lt (by omega), Nat.mod_eq_of_lt hK] + have htv (gt : Bool) (K : ℕ) (e : Bool) (hK : K < 2 ^ (s + cb + tb)) : + cmp gt (idx / 2 ^ (4 + s + cb) % 2 ^ tb % 2 ^ (s + cb + tb)) (K % 2 ^ (s + cb + tb)) e = + cmp gt (timeOf s cb (idx / 16)) K e := by + rw [div_mod_eq_timeOf hidx, Nat.mod_eq_of_lt (by omega), Nat.mod_eq_of_lt hK] + have hpv (gt : Bool) (K : ℕ) (e : Bool) (hK : K < 2 ^ (s + cb + tb)) : + cmp gt (idx / 2 ^ 4 % 2 ^ (s + cb + tb) % 2 ^ (s + cb + tb)) (K % 2 ^ (s + cb + tb)) e = + cmp gt (idx / 16) K e := by + rw [Nat.mod_mod, hpmod, Nat.mod_eq_of_lt hK] + have hc0 : bitAt (fun i : Fin (4 + s + 14) => + fieldVal s cb tb x n T idx (keyWire s cb tb i)) (4 + s) = + chainVal (chain s cb tb x.length n T 0) idx (s + cb + tb) := by + simpa using hch 0 (by omega) + simp only [Key.ofBits, Key.ofFields, hc0, + hin 0 (by omega), hin 1 (by omega), hin 2 (by omega), hin 3 (by omega)] + rw [bitsVal_congr (g := fun i => (idx / 16).testBit i) (fun i hi => by + rw [hin _ (by omega), show idx / 16 = idx / 2 ^ 4 from rfl, Nat.testBit_div_two_pow, + Nat.add_comm]), bitsVal_testBit] + have hj : (idx.testBit 0).toNat + 2 * (idx.testBit 1).toNat = idx % 4 := by + have := bitsVal_testBit idx 2 + rw [bitsVal_two] at this + exact this + have htag : (idx.testBit 2).toNat + 2 * (idx.testBit 3).toNat = idx / 4 % 4 := by + have := bitsVal_testBit (idx / 4) 2 + rw [bitsVal_two, show idx / 4 = idx / 2 ^ 2 from rfl, Nat.testBit_div_two_pow, + Nat.testBit_div_two_pow] at this + exact this + rw [hj, htag] + simp only [hch 3 (by omega), hch 4 (by omega), hch 5 (by omega), hch 6 (by omega), + hch 7 (by omega), hch 8 (by omega), hch 9 (by omega), hch 2 (by omega), hch 10 (by omega), + hch 11 (by omega), hch 12 (by omega), hch 13 (by omega), chainVal, chain] + simp only [OfNat.ofNat_ne_zero, OfNat.ofNat_ne_one, ↓reduceIte, Nat.reduceEqDiff] + simp (disch := omega) only [hcv, htv, hpv] + simp [cmp] + +end Values + + +end Cslib.Circuits.Boolean.Tableau.Decoder diff --git a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Fields.lean b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Fields.lean new file mode 100644 index 000000000..becc13b7e --- /dev/null +++ b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Fields.lean @@ -0,0 +1,644 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Boolean.Tseitin +import Mathlib.Data.Nat.Log +import Mathlib.Data.Nat.Size + +/-! +# Field arithmetic for the decoder of the succinct tableau + +The first stage of the decoder of the tableau formula on bit-field labels +(`Complexity/Computability/Circuit/Boolean/Tableau/Succinct.lean`). Its inputs are the bits of a +clause number: two bits of the clause, two bits of the tag, then a payload of `P = s + cb + tb` +bits made of the slot (`s` bits), the column `c` (`cb` bits) and the block `t` (`tb` bits). It is an +explicit De Morgan gate list (`fieldGates`) of + +* two constants and the negations of the inputs; +* fourteen *comparison chains* of `P` gates each (`chainGate`): bit-serial comparisons of a field + with a constant, least significant bit first, one gate per bit. They compare the column with + `0`, `1`, `n`, `n + 1`, `2 T` and `T`, the block with `0` and `T`, the payload with `0`, `1` and + the length of `x`, and give the borrows and carries of `c - 1`, `c + 1` and `t - 1`; +* a *decoder tree* of `2 ^ (m + 1) - 1` gates (`treeGates`), whose last level is the indicator of + the low `m = Nat.size x.length` bits of the payload, and a chain of `x.length` disjunctions + (`orGate`) that looks up the bit of the hardwired `x` at the payload. + +There are `2 + (4 + P) + 14 P + (2 ^ (m + 1) - 1) + x.length` gates, linear in the field widths and +in the length of `x`; in particular there is no carry-lookahead adder, whose size is not linear. + +Correctness is proved wire by wire: the intended value of every wire (`fieldVal`) is consistent +with its gate (`allGates_consistentAt_fieldGates`), so it is the value the list computes +(`getD_evalCode_of_allGates`). + +## Main definitions + +* `Cslib.Circuits.Boolean.Tableau.Decoder.AllGates`: a property of every gate of a list at its + wire. +* `Cslib.Circuits.Boolean.Tableau.Decoder.cmp`: the comparison computed by a chain. +* `Cslib.Circuits.Boolean.Tableau.Decoder.chain`: the fourteen comparison chains. +* `Cslib.Circuits.Boolean.Tableau.Decoder.fieldGates`: the gate list. +* `Cslib.Circuits.Boolean.Tableau.Decoder.fieldVal`: the intended values of its wires. + +## Main results + +* `Cslib.Circuits.Boolean.Tableau.Decoder.getD_evalCode_of_allGates`: a valuation consistent with + every gate of a well-formed list is its evaluation. +* `Cslib.Circuits.Boolean.Tableau.Decoder.getD_evalCode_fieldGates`: the gate list computes the + intended values. +* `Cslib.Circuits.Boolean.Tableau.Decoder.length_fieldGates`: its number of gates. +-/ + +@[expose] public section + +namespace Cslib.Circuits.Boolean.Tableau.Decoder + +/-! ### Gate lists checked gate by gate -/ + +/-- Every gate of `gates`, the first one at wire `base`, satisfies `p` at its wire. -/ +def AllGates (p : ℕ → Op × List ℕ → Prop) (base : ℕ) (gates : List (Op × List ℕ)) : Prop := + ∀ g (hg : g < gates.length), p (base + g) gates[g] + +theorem allGates_append {p : ℕ → Op × List ℕ → Prop} {b : ℕ} {l₁ l₂ : List (Op × List ℕ)} : + AllGates p b (l₁ ++ l₂) ↔ AllGates p b l₁ ∧ AllGates p (b + l₁.length) l₂ := by + constructor + · intro h + refine ⟨fun g hg => ?_, fun g hg => ?_⟩ + · have := h g (by simp only [List.length_append]; omega) + rwa [List.getElem_append_left hg] at this + · have := h (l₁.length + g) (by simp only [List.length_append]; omega) + simpa [List.getElem_append_right, Nat.add_assoc] using this + · rintro ⟨h₁, h₂⟩ g hg + by_cases hg₁ : g < l₁.length + · rw [List.getElem_append_left hg₁] + exact h₁ g hg₁ + · rw [List.getElem_append_right (by omega)] + have := h₂ (g - l₁.length) (by simp only [List.length_append] at hg; omega) + rwa [show b + l₁.length + (g - l₁.length) = b + g by omega] at this + +@[simp] theorem allGates_nil {p : ℕ → Op × List ℕ → Prop} {b : ℕ} : AllGates p b [] := by + simp [AllGates] + +theorem allGates_cons {p : ℕ → Op × List ℕ → Prop} {b : ℕ} {g : Op × List ℕ} + {l : List (Op × List ℕ)} : AllGates p b (g :: l) ↔ p b g ∧ AllGates p (b + 1) l := by + have := allGates_append (p := p) (b := b) (l₁ := [g]) (l₂ := l) + simp only [List.singleton_append, List.length_singleton] at this + rw [this] + refine and_congr_left' ⟨fun h => by simpa using h 0 (by simp), fun h g hg => ?_⟩ + obtain rfl : g = 0 := by simpa using hg + simpa using h + +theorem allGates_map_range {p : ℕ → Op × List ℕ → Prop} {b w : ℕ} {f : ℕ → Op × List ℕ} : + AllGates p b ((List.range w).map f) ↔ ∀ i < w, p (b + i) (f i) := by + simp only [AllGates, List.length_map, List.length_range, List.getElem_map, List.getElem_range] + +theorem allGates_flatMap_range {p : ℕ → Op × List ℕ → Prop} {b w : ℕ} + {f : ℕ → List (Op × List ℕ)} : + AllGates p b ((List.range w).flatMap f) ↔ + ∀ i < w, AllGates p (b + ((List.range i).map fun j => (f j).length).sum) (f i) := by + induction w with + | zero => simp [AllGates] + | succ w ih => + rw [List.range_succ, List.flatMap_append, allGates_append, ih, List.flatMap_singleton, + List.length_flatMap] + constructor + · rintro ⟨h₁, h₂⟩ i hi + rcases Nat.lt_succ_iff_lt_or_eq.1 hi with hi | rfl + · exact h₁ i hi + · exact h₂ + · exact fun h => ⟨fun i hi => h i (by omega), h w (by omega)⟩ + +/-- Blocks of a common length `len` start at multiples of `len`. -/ +theorem sum_range_map_const {len : ℕ} {f : ℕ → List (Op × List ℕ)} (hf : ∀ j, (f j).length = len) + (i : ℕ) : ((List.range i).map fun j => (f j).length).sum = i * len := by + induction i with + | zero => simp + | succ i ih => simp [List.range_succ, hf, Nat.succ_mul] + +/-- Blocks of lengths `2 ^ j` start at `2 ^ i - 1`. -/ +theorem sum_range_two_pow (i : ℕ) : ((List.range i).map fun j => 2 ^ j).sum = 2 ^ i - 1 := by + induction i with + | zero => simp + | succ i ih => + rw [List.range_succ, List.map_append, List.sum_append, ih] + have := Nat.one_le_two_pow (n := i) + simp only [List.map_cons, List.map_nil, List.sum_cons, List.sum_nil, Nat.pow_succ] + omega + +/-- A gate reads only wires below its own, with the arity of its operation. -/ +def WellFormedAt (w : ℕ) (g : Op × List ℕ) : Prop := + g.2.length = signature.Arity g.1 ∧ ∀ i ∈ g.2, i < w + +/-- The wire of a gate carries, under `σ`, the value of the gate on the values of its arguments. -/ +def ConsistentAt (σ : ℕ → Bool) (w : ℕ) (g : Op × List ℕ) : Prop := + σ w = g.1.value (σ (g.2.getD 0 0)) (σ (g.2.getD 1 0)) + +/-- A list of well-formed gates is the code of a program. -/ +theorem isCodeFrom_of_allGates {n : ℕ} {gates : List (Op × List ℕ)} + (h : AllGates WellFormedAt n gates) : Program.IsCodeFrom signature n 0 gates := + Program.isCodeFrom_of_getElem fun j hj => by simpa [WellFormedAt] using h j hj + +/-- **A valuation consistent with every gate is the evaluation** of a well-formed gate list. -/ +theorem getD_evalCode_of_allGates {n : ℕ} {gates : List (Op × List ℕ)} + (hwf : AllGates WellFormedAt n gates) (σ : ℕ → Bool) (hσ : AllGates (ConsistentAt σ) n gates) + {w : ℕ} (hw : w < n + gates.length) : + (evalCode interpretation false (List.ofFn fun i : Fin n => σ i) gates).getD w false = σ w := + ((eval_tseitinCNF_eq_true_iff_evalCode (isCodeFrom_of_allGates hwf) σ).1 + ((eval_tseitinCNF_eq_true_iff n gates σ).2 fun g hg => hσ g hg) w hw).symm + +/-- A conjunction (`false`) or a disjunction (`true`). -/ +def andOr (b : Bool) : Op := if b then .or else .and + +@[simp] theorem value_andOr (b x y : Bool) : + (andOr b).value x y = if b then x || y else x && y := by + cases b <;> rfl + +theorem wellFormedAt_const {w : ℕ} (b : Bool) : WellFormedAt w (.const b, []) := + ⟨rfl, by simp⟩ + +theorem wellFormedAt_not {w a : ℕ} (ha : a < w) : WellFormedAt w (.not, [a]) := + ⟨rfl, by simpa using ha⟩ + +theorem wellFormedAt_two {w a b : ℕ} {op : Op} (hop : signature.Arity op = 2) (ha : a < w) + (hb : b < w) : WellFormedAt w (op, [a, b]) := + ⟨by simpa using hop.symm, by simp [ha, hb]⟩ + +theorem arity_andOr (b : Bool) : signature.Arity (andOr b) = 2 := by + cases b <;> rfl + +theorem consistentAt_const {σ : ℕ → Bool} {w : ℕ} {b : Bool} : + ConsistentAt σ w (.const b, []) ↔ σ w = b := Iff.rfl + +theorem consistentAt_not {σ : ℕ → Bool} {w a : ℕ} : + ConsistentAt σ w (.not, [a]) ↔ σ w = !σ a := Iff.rfl + +theorem consistentAt_two {σ : ℕ → Bool} {w a b : ℕ} {op : Op} : + ConsistentAt σ w (op, [a, b]) ↔ σ w = op.value (σ a) (σ b) := Iff.rfl + +/-! ### Bit-serial comparison -/ + +/-- The comparison computed by a chain: `a < b` (for `gt = false`) or `b < a` (for `gt = true`), +with ties decided by `e`. -/ +def cmp (gt : Bool) (a b : ℕ) (e : Bool) : Bool := + if gt then decide (b < a) || (decide (a = b) && e) else decide (a < b) || (decide (a = b) && e) + +/-- The low `i + 1` bits of a number: its bit `i` above its low `i` bits. -/ +theorem mod_two_pow_succ (v i : ℕ) : v % 2 ^ (i + 1) = v % 2 ^ i + 2 ^ i * (v.testBit i).toNat := by + rw [Nat.mod_pow_succ, Nat.toNat_testBit] + +/-- **One step of a comparison chain**: comparing one more bit is a disjunction or a conjunction of +the literal of the bit with the comparison of the lower bits, by the bit of the constant. -/ +theorem cmp_succ (gt : Bool) (v K i : ℕ) (e : Bool) : + cmp gt (v % 2 ^ (i + 1)) (K % 2 ^ (i + 1)) e = + (andOr (K.testBit i ^^ gt)).value (if gt then v.testBit i else !v.testBit i) + (cmp gt (v % 2 ^ i) (K % 2 ^ i) e) := by + rw [mod_two_pow_succ v, mod_two_pow_succ K] + have hv := Nat.mod_lt v (Nat.two_pow_pos i) + have hK := Nat.mod_lt K (Nat.two_pow_pos i) + generalize v % 2 ^ i = a at hv ⊢ + generalize K % 2 ^ i = b at hK ⊢ + generalize 2 ^ i = X at hv hK ⊢ + cases v.testBit i <;> cases K.testBit i <;> cases gt <;> cases e <;> + simp [cmp] <;> omega + +/-- A comparison of numbers below `2 ^ w` on all `w` bits. -/ +theorem cmp_mod_of_lt {gt : Bool} {v K w : ℕ} {e : Bool} (hv : v < 2 ^ w) (hK : K < 2 ^ w) : + cmp gt (v % 2 ^ w) (K % 2 ^ w) e = cmp gt v K e := by + rw [Nat.mod_eq_of_lt hv, Nat.mod_eq_of_lt hK] + +/-! ### The layout -/ + +/-- The fourteen comparison chains, for slot, column and block widths `s`, `cb` and `tb`, an +input bound `n`, a step bound `T` and a hardwired prefix of length `X`: the offset and the width +of the field read, the direction, the constant and the tie value. Chains `0`, `1` and `2` give +the borrows of `c - 1`, the carries of `c + 1` and the borrows of `t - 1`. -/ +def chain (s cb tb X n T : ℕ) (k : ℕ) : ℕ × ℕ × Bool × ℕ × Bool := + if k = 0 then (4 + s, cb, false, 0, true) -- c % 2 ^ i = 0 + else if k = 1 then (4 + s, cb, true, 2 ^ (s + cb + tb) - 1, true) -- c % 2 ^ i = 2 ^ i - 1 + else if k = 2 then (4 + s + cb, tb, false, 0, true) -- t % 2 ^ i = 0 + else if k = 3 then (4 + s, cb, true, 1, false) -- 1 < c + else if k = 4 then (4 + s, cb, true, n, false) -- n < c + else if k = 5 then (4 + s, cb, true, n + 1, false) -- n + 1 < c + else if k = 6 then (4 + s, cb, false, 2 * T, false) -- c < 2 T + else if k = 7 then (4 + s, cb, true, 2 * T, false) -- 2 T < c + else if k = 8 then (4 + s, cb, false, T, false) -- c < T + else if k = 9 then (4 + s, cb, true, T, false) -- T < c + else if k = 10 then (4 + s + cb, tb, true, T, false) -- T < t + else if k = 11 then (4, s + cb + tb, false, X, false) -- p < X + else if k = 12 then (4, s + cb + tb, false, 0, true) -- p = 0 + else (4, s + cb + tb, true, 1, false) -- 1 < p + +/-- The start of the comparison chains, after the `N` inputs, the two constants and the `N` +negations. -/ +def chainBase (N : ℕ) : ℕ := 2 * N + 2 + +/-- The start of the decoder tree, after the fourteen chains of `P` gates. -/ +def treeBase (N P : ℕ) : ℕ := chainBase N + 14 * P + +/-- The start of the lookup chain, after the `2 ^ (m + 1) - 1` gates of the tree. -/ +def orBase (N P m : ℕ) : ℕ := treeBase N P + (2 ^ (m + 1) - 1) + +/-- Gate `i` of chain `k`, comparing the field of width `w` at input offset `off`, padded with +zeros to `P` bits, with the constant `K`: its literal is the bit of the field (`gt`) or its +negation, and it combines it with the previous gate of the chain (or the tie value `init`). -/ +def chainGate (N P k : ℕ) (spec : ℕ × ℕ × Bool × ℕ × Bool) (i : ℕ) : Op × List ℕ := + (andOr (spec.2.2.2.1.testBit i ^^ spec.2.2.1), + [if i < spec.2.1 then (if spec.2.2.1 then spec.1 + i else N + 2 + (spec.1 + i)) + else (if spec.2.2.1 then N else N + 1), + if i = 0 then (if spec.2.2.2.2 then N + 1 else N) else chainBase N + k * P + (i - 1)]) + +/-- Node `q` of level `ℓ` of the decoder tree: true at level `0`, and otherwise the node `q % 2 ^ ℓ` +of the level below together with bit `ℓ` of the payload, negated when bit `ℓ` of `q` is clear. -/ +def treeGate (N base ℓ q : ℕ) : Op × List ℕ := + if ℓ = 0 then (.const true, []) + else (.and, [base + (2 ^ (ℓ - 1) - 1) + q % 2 ^ (ℓ - 1), + if q.testBit (ℓ - 1) then 4 + (ℓ - 1) else N + 2 + (4 + (ℓ - 1))]) + +/-- The decoder tree on the low `m` bits of the payload, level by level. -/ +def treeGates (N base m : ℕ) : List (Op × List ℕ) := + (List.range (m + 1)).flatMap fun ℓ => (List.range (2 ^ ℓ)).map (treeGate N base ℓ) + +/-- Gate `i` of the lookup chain: the disjunction of the previous gate with the indicator of the +payload `i` when `x[i]` is true. -/ +def orGate (N tbase m obase : ℕ) (x : List Bool) (i : ℕ) : Op × List ℕ := + (.or, [if i = 0 then N else obase + (i - 1), + if x.getD i false then tbase + (2 ^ m - 1) + i else N]) + +/-- **The field stage of the decoder**, for slot, column and block widths `s`, `cb` and `tb`, a +hardwired input prefix `x`, an input length `n` and a step bound `T`. -/ +def fieldGates (s cb tb : ℕ) (x : List Bool) (n T : ℕ) : List (Op × List ℕ) := + [(.const false, []), (.const true, [])] ++ + (List.range (4 + (s + cb + tb))).map (fun i => (.not, [i])) ++ + (List.range 14).flatMap (fun k => (List.range (s + cb + tb)).map + (chainGate (4 + (s + cb + tb)) (s + cb + tb) k (chain s cb tb x.length n T k))) ++ + treeGates (4 + (s + cb + tb)) (treeBase (4 + (s + cb + tb)) (s + cb + tb)) + (Nat.size x.length) ++ + (List.range x.length).map (orGate (4 + (s + cb + tb)) + (treeBase (4 + (s + cb + tb)) (s + cb + tb)) (Nat.size x.length) + (orBase (4 + (s + cb + tb)) (s + cb + tb) (Nat.size x.length)) x) + +theorem length_treeGates (N base m : ℕ) : (treeGates N base m).length = 2 ^ (m + 1) - 1 := by + rw [treeGates, List.length_flatMap] + simpa using sum_range_two_pow (m + 1) + +/-- **The number of gates of the field stage.** -/ +theorem length_fieldGates (s cb tb : ℕ) (x : List Bool) (n T : ℕ) : + (fieldGates s cb tb x n T).length = + 2 + (4 + (s + cb + tb)) + 14 * (s + cb + tb) + (2 ^ (Nat.size x.length + 1) - 1) + + x.length := by + simp only [fieldGates, List.length_append, List.length_cons, List.length_nil, List.length_map, + List.length_range, length_treeGates, List.length_flatMap, List.map_const', List.sum_replicate, + smul_eq_mul] + +/-! ### The intended values -/ + +/-- The comparison of chain `spec` after `j` bits, on the clause number `idx`. -/ +def chainVal (spec : ℕ × ℕ × Bool × ℕ × Bool) (idx j : ℕ) : Bool := + cmp spec.2.2.1 (idx / 2 ^ spec.1 % 2 ^ spec.2.1 % 2 ^ j) (spec.2.2.2.1 % 2 ^ j) spec.2.2.2.2 + +/-- **The intended value of each wire** of the field stage on the bits of the clause number +`idx`: the inputs, the constants, the negations, the chains after each bit, the indicators of +the low bits of the payload `p = idx / 16`, and the lookup of `x` at `p`. -/ +def fieldVal (s cb tb : ℕ) (x : List Bool) (n T idx w : ℕ) : Bool := + if w < 4 + (s + cb + tb) then idx.testBit w + else if w = 4 + (s + cb + tb) then false + else if w = 4 + (s + cb + tb) + 1 then true + else if w < chainBase (4 + (s + cb + tb)) then !idx.testBit (w - (4 + (s + cb + tb) + 2)) + else if w < treeBase (4 + (s + cb + tb)) (s + cb + tb) then + chainVal (chain s cb tb x.length n T ((w - chainBase (4 + (s + cb + tb))) / (s + cb + tb))) + idx ((w - chainBase (4 + (s + cb + tb))) % (s + cb + tb) + 1) + else if w < orBase (4 + (s + cb + tb)) (s + cb + tb) (Nat.size x.length) then + decide (idx / 16 % 2 ^ Nat.log 2 (w - treeBase (4 + (s + cb + tb)) (s + cb + tb) + 1) = + w - treeBase (4 + (s + cb + tb)) (s + cb + tb) + 1 - + 2 ^ Nat.log 2 (w - treeBase (4 + (s + cb + tb)) (s + cb + tb) + 1)) + else decide (idx / 16 % 2 ^ Nat.size x.length ≤ + w - orBase (4 + (s + cb + tb)) (s + cb + tb) (Nat.size x.length)) && + x.getD (idx / 16 % 2 ^ Nat.size x.length) false + +/-! ### Correctness -/ + +section Correctness + +variable {s cb tb : ℕ} {x : List Bool} {n T : ℕ} + +private theorem ite_pred {α : Type} {p : α → Prop} {c : Prop} [Decidable c] {a b : α} (ha : p a) + (hb : p b) : p (if c then a else b) := by + split <;> assumption + +/-- The fields read by the chains lie within the inputs. -/ +theorem chain_off_add_width_le (k : ℕ) : + (chain s cb tb x.length n T k).1 + (chain s cb tb x.length n T k).2.1 ≤ 4 + (s + cb + tb) := by + unfold chain + repeat (refine ite_pred (p := fun spec : ℕ × ℕ × Bool × ℕ × Bool => + spec.1 + spec.2.1 ≤ 4 + (s + cb + tb)) ?_ ?_; · dsimp only; omega) + dsimp only; omega + +private theorem chainWire_lt {k i P : ℕ} (hk : k < 14) (hi : i < P) : k * P + i < 14 * P := by + have : k * P + P ≤ 14 * P := by rw [← Nat.succ_mul]; exact Nat.mul_le_mul_right _ hk + omega + +private theorem chainWire_div {k i P : ℕ} (hi : i < P) : (k * P + i) / P = k := by + rw [Nat.mul_comm, Nat.mul_add_div (by omega), Nat.div_eq_of_lt hi, Nat.add_zero] + +private theorem chainWire_mod {k i P : ℕ} (hi : i < P) : (k * P + i) % P = i := by + rw [Nat.mul_comm, Nat.mul_add_mod, Nat.mod_eq_of_lt hi] + +theorem fieldVal_input (idx : ℕ) {w : ℕ} (hw : w < 4 + (s + cb + tb)) : + fieldVal s cb tb x n T idx w = idx.testBit w := by + simp only [fieldVal, hw, ↓reduceIte] + +theorem fieldVal_false (idx : ℕ) : fieldVal s cb tb x n T idx (4 + (s + cb + tb)) = false := by + simp [fieldVal] + +theorem fieldVal_true (idx : ℕ) : fieldVal s cb tb x n T idx (4 + (s + cb + tb) + 1) = true := by + simp [fieldVal] + +theorem fieldVal_not (idx : ℕ) {j : ℕ} (hj : j < 4 + (s + cb + tb)) : + fieldVal s cb tb x n T idx (4 + (s + cb + tb) + 2 + j) = !idx.testBit j := by + have h₁ : ¬ 4 + (s + cb + tb) + 2 + j < 4 + (s + cb + tb) := by omega + have h₂ : 4 + (s + cb + tb) + 2 + j ≠ 4 + (s + cb + tb) := by omega + have h₃ : 4 + (s + cb + tb) + 2 + j ≠ 4 + (s + cb + tb) + 1 := by omega + have h₄ : 4 + (s + cb + tb) + 2 + j < chainBase (4 + (s + cb + tb)) := by + simp only [chainBase]; omega + simp only [fieldVal, h₁, h₂, h₃, h₄, ↓reduceIte, Nat.add_sub_cancel_left] + +theorem fieldVal_chain (idx : ℕ) {k i : ℕ} (hk : k < 14) (hi : i < s + cb + tb) : + fieldVal s cb tb x n T idx (chainBase (4 + (s + cb + tb)) + k * (s + cb + tb) + i) = + chainVal (chain s cb tb x.length n T k) idx (i + 1) := by + have hw := chainWire_lt hk hi + have h₁ : ¬ chainBase (4 + (s + cb + tb)) + k * (s + cb + tb) + i < 4 + (s + cb + tb) := by + simp only [chainBase]; omega + have h₂ : chainBase (4 + (s + cb + tb)) + k * (s + cb + tb) + i ≠ 4 + (s + cb + tb) := by + simp only [chainBase]; omega + have h₃ : chainBase (4 + (s + cb + tb)) + k * (s + cb + tb) + i ≠ 4 + (s + cb + tb) + 1 := by + simp only [chainBase]; omega + have h₄ : ¬ chainBase (4 + (s + cb + tb)) + k * (s + cb + tb) + i < + chainBase (4 + (s + cb + tb)) := by omega + have h₅ : chainBase (4 + (s + cb + tb)) + k * (s + cb + tb) + i < + treeBase (4 + (s + cb + tb)) (s + cb + tb) := by simp only [treeBase]; omega + have h₆ : chainBase (4 + (s + cb + tb)) + k * (s + cb + tb) + i - + chainBase (4 + (s + cb + tb)) = k * (s + cb + tb) + i := by omega + simp only [fieldVal, h₁, h₂, h₃, h₄, h₅, h₆, ↓reduceIte, chainWire_div hi, + chainWire_mod hi] + +theorem fieldVal_tree (idx : ℕ) {ℓ q : ℕ} (hℓ : ℓ ≤ Nat.size x.length) (hq : q < 2 ^ ℓ) : + fieldVal s cb tb x n T idx (treeBase (4 + (s + cb + tb)) (s + cb + tb) + (2 ^ ℓ - 1) + q) = + decide (idx / 16 % 2 ^ ℓ = q) := by + have hpow : 2 ^ ℓ + q < 2 ^ (Nat.size x.length + 1) := + calc 2 ^ ℓ + q < 2 ^ (ℓ + 1) := by rw [Nat.pow_succ]; omega + _ ≤ 2 ^ (Nat.size x.length + 1) := Nat.pow_le_pow_right (by norm_num) (by omega) + have h1 := Nat.one_le_two_pow (n := ℓ) + have h₀ : ¬ treeBase (4 + (s + cb + tb)) (s + cb + tb) + (2 ^ ℓ - 1) + q < + treeBase (4 + (s + cb + tb)) (s + cb + tb) := by omega + have h₁ : ¬ treeBase (4 + (s + cb + tb)) (s + cb + tb) + (2 ^ ℓ - 1) + q < + 4 + (s + cb + tb) := by simp only [treeBase, chainBase]; omega + have h₂ : treeBase (4 + (s + cb + tb)) (s + cb + tb) + (2 ^ ℓ - 1) + q ≠ 4 + (s + cb + tb) := by + simp only [treeBase, chainBase]; omega + have h₃ : treeBase (4 + (s + cb + tb)) (s + cb + tb) + (2 ^ ℓ - 1) + q ≠ + 4 + (s + cb + tb) + 1 := by simp only [treeBase, chainBase]; omega + have h₄ : ¬ treeBase (4 + (s + cb + tb)) (s + cb + tb) + (2 ^ ℓ - 1) + q < + chainBase (4 + (s + cb + tb)) := by simp only [treeBase]; omega + have h₅ : treeBase (4 + (s + cb + tb)) (s + cb + tb) + (2 ^ ℓ - 1) + q < + orBase (4 + (s + cb + tb)) (s + cb + tb) (Nat.size x.length) := by + simp only [orBase]; omega + have h₆ : treeBase (4 + (s + cb + tb)) (s + cb + tb) + (2 ^ ℓ - 1) + q - + treeBase (4 + (s + cb + tb)) (s + cb + tb) + 1 = 2 ^ ℓ + q := by omega + have hlog : Nat.log 2 (2 ^ ℓ + q) = ℓ := + Nat.log_eq_of_pow_le_of_lt_pow (by omega) (by rw [Nat.pow_succ]; omega) + simp only [fieldVal, h₀, h₁, h₂, h₃, h₄, h₅, h₆, ↓reduceIte, hlog, + Nat.add_sub_cancel_left] + +theorem fieldVal_or (idx : ℕ) (i : ℕ) : + fieldVal s cb tb x n T idx (orBase (4 + (s + cb + tb)) (s + cb + tb) (Nat.size x.length) + i) = + (decide (idx / 16 % 2 ^ Nat.size x.length ≤ i) && + x.getD (idx / 16 % 2 ^ Nat.size x.length) false) := by + have h₀ : ¬ orBase (4 + (s + cb + tb)) (s + cb + tb) (Nat.size x.length) + i < + orBase (4 + (s + cb + tb)) (s + cb + tb) (Nat.size x.length) := by omega + have h₁ : ¬ orBase (4 + (s + cb + tb)) (s + cb + tb) (Nat.size x.length) + i < + 4 + (s + cb + tb) := by simp only [orBase, treeBase, chainBase]; omega + have h₂ : orBase (4 + (s + cb + tb)) (s + cb + tb) (Nat.size x.length) + i ≠ + 4 + (s + cb + tb) := by simp only [orBase, treeBase, chainBase]; omega + have h₃ : orBase (4 + (s + cb + tb)) (s + cb + tb) (Nat.size x.length) + i ≠ + 4 + (s + cb + tb) + 1 := by simp only [orBase, treeBase, chainBase]; omega + have h₄ : ¬ orBase (4 + (s + cb + tb)) (s + cb + tb) (Nat.size x.length) + i < + chainBase (4 + (s + cb + tb)) := by simp only [orBase, treeBase]; omega + have h₅ : ¬ orBase (4 + (s + cb + tb)) (s + cb + tb) (Nat.size x.length) + i < + treeBase (4 + (s + cb + tb)) (s + cb + tb) := by simp only [orBase]; omega + simp only [fieldVal, h₀, h₁, h₂, h₃, h₄, h₅, ↓reduceIte, Nat.add_sub_cancel_left] + +/-- The literal of a chain gate is the bit of its field, or its negation. -/ +private theorem fieldVal_chainLit (idx : ℕ) (k i : ℕ) : + fieldVal s cb tb x n T idx + (if i < (chain s cb tb x.length n T k).2.1 then + (if (chain s cb tb x.length n T k).2.2.1 then (chain s cb tb x.length n T k).1 + i + else 4 + (s + cb + tb) + 2 + ((chain s cb tb x.length n T k).1 + i)) + else (if (chain s cb tb x.length n T k).2.2.1 then 4 + (s + cb + tb) + else 4 + (s + cb + tb) + 1)) = + if (chain s cb tb x.length n T k).2.2.1 then + (idx / 2 ^ (chain s cb tb x.length n T k).1 % + 2 ^ (chain s cb tb x.length n T k).2.1).testBit i + else !(idx / 2 ^ (chain s cb tb x.length n T k).1 % + 2 ^ (chain s cb tb x.length n T k).2.1).testBit i := by + have hle := chain_off_add_width_le (s := s) (cb := cb) (tb := tb) (x := x) (n := n) (T := T) k + generalize chain s cb tb x.length n T k = spec at hle ⊢ + obtain ⟨off, w, gt, K, init⟩ := spec + simp only at hle ⊢ + by_cases hi : i < w + · cases gt + · simp [hi, fieldVal_not idx (show off + i < 4 + (s + cb + tb) by omega), + Nat.testBit_div_two_pow, Nat.add_comm i off] + · simp [hi, fieldVal_input idx (show off + i < 4 + (s + cb + tb) by omega), + Nat.testBit_div_two_pow, Nat.add_comm i off] + · cases gt + · simp [hi, fieldVal_true] + · simp [hi, fieldVal_false] + +/-- **The gates of the field stage, region by region.** -/ +theorem allGates_fieldGates {p : ℕ → Op × List ℕ → Prop} + (hc₀ : p (4 + (s + cb + tb)) (.const false, [])) + (hc₁ : p (4 + (s + cb + tb) + 1) (.const true, [])) + (hnot : ∀ i < 4 + (s + cb + tb), p (4 + (s + cb + tb) + 2 + i) (.not, [i])) + (hchain : ∀ k < 14, ∀ i < s + cb + tb, + p (chainBase (4 + (s + cb + tb)) + k * (s + cb + tb) + i) + (chainGate (4 + (s + cb + tb)) (s + cb + tb) k (chain s cb tb x.length n T k) i)) + (htree : ∀ ℓ ≤ Nat.size x.length, ∀ q < 2 ^ ℓ, + p (treeBase (4 + (s + cb + tb)) (s + cb + tb) + (2 ^ ℓ - 1) + q) + (treeGate (4 + (s + cb + tb)) (treeBase (4 + (s + cb + tb)) (s + cb + tb)) ℓ q)) + (hor : ∀ i < x.length, + p (orBase (4 + (s + cb + tb)) (s + cb + tb) (Nat.size x.length) + i) + (orGate (4 + (s + cb + tb)) (treeBase (4 + (s + cb + tb)) (s + cb + tb)) + (Nat.size x.length) (orBase (4 + (s + cb + tb)) (s + cb + tb) (Nat.size x.length)) x i)) : + AllGates p (4 + (s + cb + tb)) (fieldGates s cb tb x n T) := by + have hlen : ((List.range 14).flatMap fun k => (List.range (s + cb + tb)).map + (chainGate (4 + (s + cb + tb)) (s + cb + tb) k (chain s cb tb x.length n T k))).length = + 14 * (s + cb + tb) := by + simp only [List.length_flatMap, List.length_map, List.length_range, List.map_const', + List.sum_replicate, smul_eq_mul] + simp only [fieldGates, allGates_append, List.length_append, List.length_cons, List.length_nil, + List.length_map, List.length_range, hlen, length_treeGates, allGates_cons, allGates_nil, + and_true] + refine ⟨⟨⟨⟨⟨hc₀, hc₁⟩, ?_⟩, ?_⟩, ?_⟩, ?_⟩ + · rw [allGates_map_range] + intro i hi + simpa [Nat.add_assoc] using hnot i hi + · rw [allGates_flatMap_range] + intro k hk + rw [sum_range_map_const (len := s + cb + tb) (fun j => by simp), allGates_map_range] + intro i hi + have := hchain k hk i hi + convert this using 2 + simp only [chainBase] + omega + · rw [treeGates, allGates_flatMap_range] + intro ℓ hℓ + simp only [List.length_map, List.length_range] + rw [sum_range_two_pow, allGates_map_range] + intro q hq + have := htree ℓ (by omega) q hq + convert this using 2 + simp only [treeBase, chainBase] + omega + · rw [allGates_map_range] + intro i hi + have := hor i hi + convert this using 2 + simp only [orBase, treeBase, chainBase] + omega + +/-- **The field stage is a well-formed gate list**, when the decoder tree reads only payload +bits. -/ +theorem allGates_wellFormedAt_fieldGates (hm : Nat.size x.length ≤ s + cb + tb) : + AllGates WellFormedAt (4 + (s + cb + tb)) (fieldGates s cb tb x n T) := by + have hX := Nat.lt_size_self x.length + refine allGates_fieldGates (wellFormedAt_const _) (wellFormedAt_const _) + (fun i hi => wellFormedAt_not (by omega)) (fun k hk i hi => ?_) (fun ℓ hℓ q hq => ?_) + (fun i hi => ?_) + · have hle := chain_off_add_width_le (s := s) (cb := cb) (tb := tb) (x := x) (n := n) (T := T) k + refine wellFormedAt_two (arity_andOr _) ?_ ?_ + · simp only [chainBase] + split_ifs <;> omega + · simp only [chainBase] + split_ifs <;> omega + · unfold treeGate + split_ifs with h₀ h₁ + · exact wellFormedAt_const _ + · have hq' := Nat.mod_lt q (Nat.two_pow_pos (ℓ - 1)) + have hpow : 2 ^ ℓ = 2 * 2 ^ (ℓ - 1) := by + rw [← Nat.pow_succ']; congr 1; omega + refine wellFormedAt_two rfl ?_ ?_ <;> simp only [treeBase, chainBase] <;> omega + · have hq' := Nat.mod_lt q (Nat.two_pow_pos (ℓ - 1)) + have hpow : 2 ^ ℓ = 2 * 2 ^ (ℓ - 1) := by + rw [← Nat.pow_succ']; congr 1; omega + refine wellFormedAt_two rfl ?_ ?_ <;> simp only [treeBase, chainBase] <;> omega + · have hpow : 2 ^ (Nat.size x.length + 1) = 2 * 2 ^ Nat.size x.length := Nat.pow_succ' + refine wellFormedAt_two rfl ?_ ?_ <;> simp only [orBase, treeBase, chainBase] <;> + split_ifs <;> omega + +/-- The low `ℓ + 1` bits of two numbers agree exactly when their low `ℓ` bits and their bits `ℓ` +agree. -/ +theorem mod_two_pow_succ_eq_iff {p q ℓ : ℕ} (hq : q < 2 ^ (ℓ + 1)) : + p % 2 ^ (ℓ + 1) = q ↔ p % 2 ^ ℓ = q % 2 ^ ℓ ∧ p.testBit ℓ = q.testBit ℓ := by + conv_lhs => rw [← Nat.mod_eq_of_lt hq, mod_two_pow_succ p, mod_two_pow_succ q] + have ha := Nat.mod_lt p (Nat.two_pow_pos ℓ) + have hb := Nat.mod_lt q (Nat.two_pow_pos ℓ) + generalize p % 2 ^ ℓ = a at ha ⊢ + generalize q % 2 ^ ℓ = b at hb ⊢ + generalize 2 ^ ℓ = X at ha hb ⊢ + cases p.testBit ℓ <;> cases q.testBit ℓ <;> simp <;> omega + +/-- **The intended values are consistent with the gates** of the field stage. -/ +theorem allGates_consistentAt_fieldGates (hm : Nat.size x.length ≤ s + cb + tb) (idx : ℕ) : + AllGates (ConsistentAt (fieldVal s cb tb x n T idx)) (4 + (s + cb + tb)) + (fieldGates s cb tb x n T) := by + have hX := Nat.lt_size_self x.length + refine allGates_fieldGates (consistentAt_const.2 (fieldVal_false idx)) + (consistentAt_const.2 (fieldVal_true idx)) + (fun i hi => consistentAt_not.2 (by rw [fieldVal_not idx hi, fieldVal_input idx hi])) + (fun k hk i hi => ?_) (fun ℓ hℓ q hq => ?_) (fun i hi => ?_) + · rw [chainGate, consistentAt_two, fieldVal_chain idx hk hi, fieldVal_chainLit] + have hprev : fieldVal s cb tb x n T idx + (if i = 0 then (if (chain s cb tb x.length n T k).2.2.2.2 then 4 + (s + cb + tb) + 1 + else 4 + (s + cb + tb)) + else chainBase (4 + (s + cb + tb)) + k * (s + cb + tb) + (i - 1)) = + chainVal (chain s cb tb x.length n T k) idx i := by + by_cases h₀ : i = 0 + · subst h₀ + cases h₁ : (chain s cb tb x.length n T k).2.2.2.2 + · simp only [↓reduceIte, Bool.false_eq_true, fieldVal_false, chainVal, cmp, pow_zero, + Nat.mod_one, h₁] + simp + · simp only [↓reduceIte, fieldVal_true, chainVal, cmp, pow_zero, Nat.mod_one, h₁] + simp + · simp only [h₀, ↓reduceIte] + rw [fieldVal_chain idx hk (by omega), Nat.sub_add_cancel (by omega)] + rw [hprev, chainVal, chainVal, cmp_succ] + · rw [treeGate] + by_cases h₀ : ℓ = 0 + · subst h₀ + simp only [↓reduceIte] + rw [consistentAt_const, fieldVal_tree idx hℓ hq] + simp only [pow_zero, Nat.lt_one_iff] at hq + simp [hq, Nat.mod_one] + · simp only [h₀, ↓reduceIte] + obtain ⟨ℓ, rfl⟩ : ∃ ℓ', ℓ = ℓ' + 1 := ⟨ℓ - 1, by omega⟩ + simp only [Nat.add_sub_cancel] + have hlit : fieldVal s cb tb x n T idx (if q.testBit ℓ then 4 + ℓ else 4 + (s + cb + tb) + 2 + + (4 + ℓ)) = decide ((idx / 16).testBit ℓ = q.testBit ℓ) := by + have h16 : idx / 16 = idx / 2 ^ 4 := rfl + rw [h16, Nat.testBit_div_two_pow, Nat.add_comm ℓ 4] + split_ifs with hb + · rw [fieldVal_input idx (by omega), hb]; cases idx.testBit (4 + ℓ) <;> rfl + · rw [fieldVal_not idx (by omega)] + simp only [Bool.not_eq_true] at hb + rw [hb]; cases idx.testBit (4 + ℓ) <;> rfl + rw [consistentAt_two, fieldVal_tree idx hℓ hq, + fieldVal_tree idx (by omega) (Nat.mod_lt q (Nat.two_pow_pos ℓ)), hlit] + simp only [Op.value, Bool.decide_and, mod_two_pow_succ_eq_iff hq] + · rw [orGate, consistentAt_two, fieldVal_or idx] + have hsel : fieldVal s cb tb x n T idx (if x.getD i false then + treeBase (4 + (s + cb + tb)) (s + cb + tb) + (2 ^ Nat.size x.length - 1) + i + else 4 + (s + cb + tb)) = + (x.getD i false && decide (idx / 16 % 2 ^ Nat.size x.length = i)) := by + split_ifs with hxi + · rw [fieldVal_tree idx le_rfl (by omega), hxi, Bool.true_and] + · simp only [Bool.not_eq_true] at hxi + rw [fieldVal_false, hxi, Bool.false_and] + rw [hsel] + by_cases h₀ : i = 0 + · subst h₀ + simp only [↓reduceIte] + rw [fieldVal_false] + cases h : decide (idx / 16 % 2 ^ Nat.size x.length = 0) <;> simp_all [Op.value] + · simp only [h₀, ↓reduceIte] + rw [fieldVal_or idx] + by_cases hlt : idx / 16 % 2 ^ Nat.size x.length < i + · have h₁ : idx / 16 % 2 ^ Nat.size x.length ≤ i - 1 := by omega + have h₂ : idx / 16 % 2 ^ Nat.size x.length ≠ i := by omega + simp [Op.value, h₁, hlt.le, h₂] + · by_cases heq : idx / 16 % 2 ^ Nat.size x.length = i + · have h₁ : ¬ idx / 16 % 2 ^ Nat.size x.length ≤ i - 1 := by omega + simp [Op.value, heq] + · have h₁ : ¬ idx / 16 % 2 ^ Nat.size x.length ≤ i - 1 := by omega + have h₂ : ¬ idx / 16 % 2 ^ Nat.size x.length ≤ i := by omega + simp [Op.value, h₁, h₂, heq] + +/-- **The field stage computes the intended values** of its wires on the bits of a clause +number. -/ +theorem getD_evalCode_fieldGates (hm : Nat.size x.length ≤ s + cb + tb) (idx : ℕ) {w : ℕ} + (hw : w < 4 + (s + cb + tb) + (fieldGates s cb tb x n T).length) : + (evalCode interpretation false (List.ofFn fun i : Fin (4 + (s + cb + tb)) => idx.testBit i) + (fieldGates s cb tb x n T)).getD w false = fieldVal s cb tb x n T idx w := by + have h := getD_evalCode_of_allGates (allGates_wellFormedAt_fieldGates hm) _ + (allGates_consistentAt_fieldGates (n := n) (T := T) hm idx) hw + rwa [show (List.ofFn fun i : Fin (4 + (s + cb + tb)) => fieldVal s cb tb x n T idx i) = + List.ofFn fun i => idx.testBit i from + congrArg List.ofFn (funext fun i => fieldVal_input idx i.isLt)] at h + +end Correctness + +end Cslib.Circuits.Boolean.Tableau.Decoder diff --git a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Shape.lean b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Shape.lean new file mode 100644 index 000000000..08bce7a5f --- /dev/null +++ b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Shape.lean @@ -0,0 +1,811 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Computability.Circuit.Boolean.Tableau.Succinct + +/-! +# The clauses of the succinct tableau as symbolic shapes + +A clause of the tableau formula on bit-field labels (`Cslib.Circuits.Boolean.Tableau.clauseOf`) +is determined by finitely many data: its clause and tag fields, its slot, and thirteen comparisons +of the column `c`, the block `t` and the payload `p` with `0`, `1`, `n`, `n + 1`, `2 T`, `T` and +the length of the fixed prefix `x` (`Key`). From these, the clause is a list of three symbolic +literals (`shapeOf`): the label of each literal is a constant, the input `p`, the input `c - 1`, +or a position whose column is `0`, `c`, `c - 1`, `c + 1` or `2 T` and whose block is `t`, `t - 1` +or `T` (`LabelSrc`); its sign is a constant or the bit of `x` at `p` (`SignSrc`). Evaluating the +shapes on the fields gives the clause (`clauseOf_eq_map_shapeOf`). + +This is the case analysis behind the decoder circuit: a circuit of constant size computes the +shape from the key, and circuits linear in the field widths compute the fields `c - 1`, `c + 1` +and `t - 1` and the comparisons. + +## Main definitions + +* `Cslib.Circuits.Boolean.Tableau.Decoder.LabelSrc`, + `Cslib.Circuits.Boolean.Tableau.Decoder.SignSrc`: symbolic labels and signs. +* `Cslib.Circuits.Boolean.Tableau.Decoder.Key`, + `Cslib.Circuits.Boolean.Tableau.Decoder.Key.ofFields`: the finite data determining a clause. +* `Cslib.Circuits.Boolean.Tableau.Decoder.shapeOf`: the symbolic clause of a key. + +## Main results + +* `Cslib.Circuits.Boolean.Tableau.Decoder.clauseOf_eq_map_shapeOf`: the clause with given fields + is the evaluation of the shape of their key. +* `Cslib.Circuits.Boolean.Tableau.Decoder.inRange_of_mem_shapeOf`: the labels of the shapes stay + within their fields. + +## References + +* [R. Williams, *Improving exhaustive search implies superpolynomial lower bounds*][Williams2010] +-/ + +@[expose] public section + +namespace Cslib.Circuits.Boolean.Tableau.Decoder + +open Turing MultiTapeTM Cslib.Logic.PL + +/-! ### Symbolic labels -/ + +/-- The column field of a symbolic position label. -/ +inductive ColSrc where + /-- The column `0`. -/ + | zero + /-- The column `c`. -/ + | same + /-- The column `c - 1`. -/ + | prev + /-- The column `c + 1`. -/ + | next + /-- The column `2 T`. -/ + | last + deriving DecidableEq + +/-- The block field of a symbolic position label. -/ +inductive TimeSrc where + /-- The block `t`. -/ + | same + /-- The block `t - 1`. -/ + | prev + /-- The block `T`. -/ + | top + deriving DecidableEq + +/-- A symbolic label. -/ +inductive LabelSrc where + /-- The constant gate `b`. -/ + | const (b : Bool) + /-- The input `p` (`false`) or `c - 1` (`true`). -/ + | input (fromCol : Bool) + /-- A position, with its slot and the sources of its column and block. -/ + | pos (slot : ℕ) (col : ColSrc) (time : TimeSrc) + +/-- A symbolic sign. -/ +inductive SignSrc where + /-- A constant sign. -/ + | const (b : Bool) + /-- The bit of `x` at the payload. -/ + | lookup + +/-- A symbolic literal. -/ +abbrev Shape := LabelSrc × SignSrc + +/-- The column of a column source. -/ +def ColSrc.eval (c T : ℕ) : ColSrc → ℕ + | .zero => 0 + | .same => c + | .prev => c - 1 + | .next => c + 1 + | .last => 2 * T + +/-- The block of a block source. -/ +def TimeSrc.eval (t T : ℕ) : TimeSrc → ℕ + | .same => t + | .prev => t - 1 + | .top => T + +/-- The label of a symbolic label, for slot and column widths `s` and `cb`, payload `p`, column +`c` and block `t`. -/ +def LabelSrc.eval (s cb p c t T : ℕ) : LabelSrc → ℕ + | .const b => constLabel b + | .input false => inputLabel p + | .input true => inputLabel (c - 1) + | .pos sl cs ts => 2 + 4 * (sl + 2 ^ s * (cs.eval c T + 2 ^ cb * ts.eval t T)) + +/-- The sign of a symbolic sign. -/ +def SignSrc.eval (x : List Bool) (p : ℕ) : SignSrc → Bool + | .const b => b + | .lookup => x.getD p false + +/-- The literal of a symbolic literal. -/ +def Shape.eval (s cb p c t T : ℕ) (x : List Bool) (sh : Shape) : Literal ℕ := + (sh.1.eval s cb p c t T, sh.2.eval x p) + +/-! ### Keys -/ + +/-- **The finite data that determine a clause**: the clause and tag fields, the slot, and +comparisons of the column `c`, the block `t` and the payload `p`. -/ +structure Key where + /-- The clause field. -/ + j : ℕ + /-- The tag field. -/ + tag : ℕ + /-- The slot. -/ + slot : ℕ + /-- `c = 0`. -/ + c0 : Bool + /-- `1 < c`. -/ + cGt1 : Bool + /-- `n < c`. -/ + cGtN : Bool + /-- `n + 1 < c`. -/ + cGtN1 : Bool + /-- `c < 2 T`. -/ + cLt2T : Bool + /-- `2 T < c`. -/ + cGt2T : Bool + /-- `c < T`. -/ + cLtT : Bool + /-- `T < c`. -/ + cGtT : Bool + /-- `t = 0`. -/ + t0 : Bool + /-- `T < t`. -/ + tGtT : Bool + /-- `p < X`, for the length `X` of the fixed prefix. -/ + pLtX : Bool + /-- `p = 0`. -/ + p0 : Bool + /-- `1 < p`. -/ + pGt1 : Bool + +/-- The column of a payload. -/ +abbrev colOf (s cb p : ℕ) : ℕ := p / 2 ^ s % 2 ^ cb + +/-- The block of a payload. -/ +abbrev timeOf (s cb p : ℕ) : ℕ := p / 2 ^ s / 2 ^ cb + +/-- **The key of the fields** `j`, `tag` and `p` of a clause number, for slot and column widths +`s` and `cb`, a prefix of length `X`, `n` inputs and `T` steps. -/ +def Key.ofFields (s cb X n T j tag p : ℕ) : Key where + j := j + tag := tag + slot := p % 2 ^ s + c0 := decide (colOf s cb p = 0) + cGt1 := decide (1 < colOf s cb p) + cGtN := decide (n < colOf s cb p) + cGtN1 := decide (n + 1 < colOf s cb p) + cLt2T := decide (colOf s cb p < 2 * T) + cGt2T := decide (2 * T < colOf s cb p) + cLtT := decide (colOf s cb p < T) + cGtT := decide (T < colOf s cb p) + t0 := decide (timeOf s cb p = 0) + tGtT := decide (T < timeOf s cb p) + pLtX := decide (p < X) + p0 := decide (p = 0) + pGt1 := decide (1 < p) + +/-- The guard of a reference, from a key. -/ +def Key.guard (κ : Key) : Guard → Bool + | .always => true + | .pos => !κ.c0 + | .ltLast => κ.cLt2T + | .inRange => !κ.c0 && !κ.cGtN + | .outRange => !(!κ.c0 && !κ.cGtN) + | .isZero => κ.c0 + | .isEnd => κ.cGtN && !κ.cGtN1 + | .leEnd => !κ.c0 && !κ.cGtN1 + +variable {k : ℕ} {State : Type} [Fintype State] [DecidableEq State] + +/-- Whether a position is admissible in the column, from a key. -/ +def Key.colOK (κ : Key) : Loc k State → Bool + | .scan _ => !κ.cGt2T + | .col .. => !κ.cGt2T + | .glob _ => κ.c0 + | .gf .. => κ.c0 + +/-- The column source of a reference to a position: `0` for the global positions. -/ +def colSrcOf : Loc k State → ColSel → ColSrc + | .glob _, _ => .zero + | .gf .., _ => .zero + | _, .same => .same + | _, .prev => .prev + | _, .next => .next + | _, .last => .last + +/-- The symbolic label of a reference, from a key. -/ +noncomputable def Key.refSrc (κ : Key) : Ref k State → LabelSrc + | .fls => .const false + | .tru => .const true + | .inp => if !κ.c0 && !κ.cGtN then .input true else .const false + | .node p l cs g => if κ.guard g then .pos l.slot (colSrcOf l cs) (if p then .prev else .same) + else .const false + +/-- The initial value of a column feature, from a key. -/ +def Key.colInit (κ : Key) : ColFeat k → Bool + | .inPos => !κ.c0 && !κ.cGt1 + | .head _ => !κ.cLtT && !κ.cGtT + | .cell _ s => decide (s = none) + +variable (tm : MultiTapeTM k Bool State) + +/-- The initial value of a position, from a key. -/ +def Key.initBit (κ : Key) : Loc k State → Bool + | .gf f true j => decide (j = 7) && gfInit tm f + | .col f true j => decide (j = 7) && κ.colInit f + | _ => false + +/-- Clauses on symbolic labels with constant signs. -/ +def lift (cl : Clause LabelSrc) : List Shape := cl.map fun l => (l.1, .const l.2) + +/-- The symbolic tautology. -/ +def tautShape : List Shape := + [(.const false, .const true), (.const false, .const false), (.const false, .const true)] + +/-- The symbolic clause `j` of a position. -/ +noncomputable def posShape (κ : Key) (l : Loc k State) (j : Fin 3) : Clause LabelSrc := + if κ.t0 then tseitinClause (.const (κ.initBit tm l)) (.pos l.slot .same .same) (.const false) + (.const false) j + else tseitinClause (table tm l).1 (.pos l.slot .same .same) (κ.refSrc (table tm l).2.1) + (κ.refSrc (table tm l).2.2) j + +/-- The symbolic label of the output. -/ +noncomputable def outSrc : LabelSrc := + .pos (Loc.gf (k := k) (State := State) (.out (.one true)) true 7).slot .zero .top + +/-- **The symbolic clause of a key**, following `clauseOf`. -/ +noncomputable def shapeOf (κ : Key) : List Shape := + if hj : κ.j < 3 then + if κ.tag = 0 then + if !κ.pGt1 then + lift (tseitinClause (.const !κ.p0) (.const !κ.p0) (.const false) (.const false) ⟨κ.j, hj⟩) + else tautShape + else if κ.tag = 1 then + if κ.j = 0 ∧ κ.pLtX then [(.input false, .lookup), (.input false, .lookup), + (.input false, .lookup)] + else tautShape + else if κ.tag = 2 then + match Loc.ofSlot (k := k) (State := State) κ.slot with + | some l => if !κ.tGtT && κ.colOK l then lift (posShape tm κ l ⟨κ.j, hj⟩) else tautShape + | none => tautShape + else if κ.j = 0 ∧ κ.p0 then + [(outSrc (k := k) (State := State), .const true), + (outSrc (k := k) (State := State), .const true), + (outSrc (k := k) (State := State), .const true)] + else tautShape + else tautShape + +/-! ### Evaluation -/ + +section Eval + +variable {s cb X n T : ℕ} + +theorem Key.guard_ofFields (j tag p : ℕ) (g : Guard) : + (Key.ofFields s cb X n T j tag p).guard g = g.test n T (colOf s cb p) := by + cases g <;> simp only [Key.guard, Key.ofFields, Guard.test, numCols] <;> + rw [Bool.eq_iff_iff] <;> simp <;> omega + +omit [DecidableEq State] in +theorem Key.colOK_ofFields (j tag p : ℕ) (l : Loc k State) : + (Key.ofFields s cb X n T j tag p).colOK l = decide (l.ColOK T (colOf s cb p)) := by + cases l <;> simp only [Key.colOK, Key.ofFields, Loc.ColOK, numCols] <;> + rw [Bool.eq_iff_iff] <;> simp + +theorem Key.initBit_ofFields (j tag p : ℕ) (l : Loc k State) : + (Key.ofFields s cb X n T j tag p).initBit tm l = Tableau.initBit tm T (colOf s cb p) l := by + rcases l with l | l | ⟨f, (_ | _), j⟩ | ⟨f, (_ | _), j⟩ <;> try rfl + rcases f with _ | i | ⟨i, s⟩ <;> + simp only [Key.initBit, Tableau.initBit, Key.colInit, Tableau.colInit, Key.ofFields] <;> + congr 1 <;> rw [Bool.eq_iff_iff] <;> simp <;> omega + +omit [DecidableEq State] in +theorem colSrcOf_eval (l : Loc k State) (cs : ColSel) (c T : ℕ) : + (colSrcOf l cs).eval c T = l.labelCol (cs.apply T c) := by + rcases l with l | l | ⟨f, b, j⟩ | ⟨f, b, j⟩ <;> cases cs <;> rfl + +omit [DecidableEq State] in +/-- **The symbolic label of a reference evaluates to its label.** -/ +theorem refSrc_eval {t : ℕ} (j tag p : ℕ) (r : Ref k State) : + ((Key.ofFields (slotBits k State) (colBits T) X n T j tag p).refSrc r).eval (slotBits k State) + (colBits T) p (colOf (slotBits k State) (colBits T) p) t T = + r.label n T t (colOf (slotBits k State) (colBits T) p) := by + rcases r with _ | _ | _ | ⟨q, l, cs, g⟩ + · rfl + · rfl + · simp only [Key.refSrc, Ref.label, Key.ofFields] + by_cases h : 1 ≤ colOf (slotBits k State) (colBits T) p ∧ + colOf (slotBits k State) (colBits T) p ≤ n + · have h₁ : (!decide (colOf (slotBits k State) (colBits T) p = 0) && + !decide (n < colOf (slotBits k State) (colBits T) p)) = true := by simp; omega + simp only [h₁, h, ↓reduceIte] + rfl + · have h₁ : ¬ (!decide (colOf (slotBits k State) (colBits T) p = 0) && + !decide (n < colOf (slotBits k State) (colBits T) p)) = true := by simp; omega + simp only [h₁, h, ↓reduceIte, Bool.false_eq_true] + rfl + · simp only [Key.refSrc, Ref.label, Key.guard_ofFields] + by_cases hg : g.test n T (colOf (slotBits k State) (colBits T) p) = true + · simp only [hg, ↓reduceIte, LabelSrc.eval, posLabel, colSrcOf_eval] + cases q <;> rfl + · simp only [hg, ↓reduceIte, Bool.false_eq_true] + rfl + +theorem map_eval_lift (s cb p c t T : ℕ) (x : List Bool) (cl : Clause LabelSrc) : + (lift cl).map (Shape.eval s cb p c t T x) = cl.relabel (LabelSrc.eval s cb p c t T) := by + simp [lift, Clause.relabel, Shape.eval, SignSrc.eval, Function.comp_def, Prod.map] + +/-- **The clause with given fields is the evaluation of the shape of their key.** -/ +theorem clauseOf_eq_map_shapeOf (x : List Bool) (n T j tag p : ℕ) : + clauseOf tm x n T j tag p = + (shapeOf tm (Key.ofFields (slotBits k State) (colBits T) x.length n T j tag p)).map + (Shape.eval (slotBits k State) (colBits T) p (colOf (slotBits k State) (colBits T) p) + (timeOf (slotBits k State) (colBits T) p) T x) := by + set κ := Key.ofFields (slotBits k State) (colBits T) x.length n T j tag p with hκ + have hκj : κ.j = j := rfl + have hκtag : κ.tag = tag := rfl + have hκslot : κ.slot = p % 2 ^ slotBits k State := rfl + rw [clauseOf, shapeOf, hκj, hκtag] + by_cases hj : j < 3 + · simp only [hj, ↓reduceDIte] + by_cases h0 : tag = 0 + · simp only [h0, ↓reduceIte] + by_cases hp : p < 2 + · have h₁ : (!κ.pGt1) = true := by simp [hκ, Key.ofFields]; omega + have h₂ : (!κ.p0) = decide (p = 1) := by + rw [Bool.eq_iff_iff]; simp [hκ, Key.ofFields]; omega + simp only [hp, h₁, ↓reduceIte, h₂, map_eval_lift, relabel_tseitinClause] + rfl + · have h₁ : ¬ (!κ.pGt1) = true := by simp [hκ, Key.ofFields]; omega + simp only [hp, h₁, ↓reduceIte] + rfl + · by_cases h1 : tag = 1 + · simp only [h1, ↓reduceIte, one_ne_zero] + by_cases hp : j = 0 ∧ p < x.length + · have h₁ : (j = 0 ∧ κ.pLtX = true) := ⟨hp.1, by simp [hκ, Key.ofFields, hp.2]⟩ + rw [dite_eq_left hp, ite_eq_left h₁] + simp [unitClause, Shape.eval, LabelSrc.eval, SignSrc.eval, List.getElem?_eq_getElem hp.2] + · have h₁ : ¬ (j = 0 ∧ κ.pLtX = true) := by simp [hκ, Key.ofFields]; omega + rw [dite_eq_right hp, ite_eq_right h₁] + rfl + · by_cases h2 : tag = 2 + · simp only [h2, ↓reduceIte, OfNat.ofNat_ne_zero, OfNat.ofNat_ne_one, hκslot] + rcases hl : Loc.ofSlot (k := k) (State := State) (p % 2 ^ slotBits k State) with _ | l + · rfl + · have hc : κ.colOK l = decide (l.ColOK T (colOf (slotBits k State) (colBits T) p)) := + Key.colOK_ofFields _ _ _ l + have hiff : (!κ.tGtT && κ.colOK l) = true ↔ + (p / 2 ^ slotBits k State / 2 ^ colBits T ≤ T ∧ + l.ColOK T (p / 2 ^ slotBits k State % 2 ^ colBits T)) := by + rw [hc]; simp [hκ, Key.ofFields] + simp only + by_cases hok : p / 2 ^ slotBits k State / 2 ^ colBits T ≤ T ∧ + l.ColOK T (p / 2 ^ slotBits k State % 2 ^ colBits T) + · simp only [hok, hiff.2 hok, and_self, ↓reduceIte, map_eval_lift] + unfold posClause posShape + by_cases ht : p / 2 ^ slotBits k State / 2 ^ colBits T = 0 + · have hκt : κ.t0 = true := by simp [hκ, Key.ofFields, ht] + simp only [ht, hκt, ↓reduceIte, relabel_tseitinClause] + rw [hκ, Key.initBit_ofFields] + rfl + · have hκt : κ.t0 = false := by simp [hκ, Key.ofFields, ht] + simp only [ht, hκt, ↓reduceIte, Bool.false_eq_true, relabel_tseitinClause] + rw [hκ, refSrc_eval, refSrc_eval] + rfl + · have hnot : ¬ (!κ.tGtT && κ.colOK l) = true := fun h => hok (hiff.1 h) + simp only [hok, hnot, ↓reduceIte] + rfl + · simp only [h0, h1, h2, ↓reduceIte] + by_cases hp : j = 0 ∧ p = 0 + · have h₁ : (j = 0 ∧ κ.p0 = true) := ⟨hp.1, by simp [hκ, Key.ofFields, hp.2]⟩ + rw [ite_eq_left hp, ite_eq_left h₁] + simp [unitClause, Shape.eval, LabelSrc.eval, SignSrc.eval, outLabel, outSrc, posLabel, + ColSrc.eval, TimeSrc.eval] + · have h₁ : ¬ (j = 0 ∧ κ.p0 = true) := by simp [hκ, Key.ofFields]; omega + rw [ite_eq_right hp, ite_eq_right h₁] + rfl + · simp only [hj, ↓reduceDIte] + rfl + +end Eval + +/-! ### Bits -/ + +section Bits + +/-- The tag of a symbolic label. -/ +def LabelSrc.tag : LabelSrc → ℕ + | .const _ => 0 + | .input _ => 1 + | .pos .. => 2 + +/-- The constant part of the payload of a symbolic label, in its slot field. -/ +def LabelSrc.slotVal : LabelSrc → ℕ + | .const b => b.toNat + | .input _ => 0 + | .pos sl _ _ => sl + +/-- The selector `q` of a symbolic label: the payload is `p` (`0`) or `c - 1` (`1`), the column is +`c`, `c - 1`, `c + 1` or `2 T` (`2` to `5`), the block is `t`, `t - 1` or `T` (`6` to `8`). +Decrements of `c = 0` and `t = 0` select nothing, giving `0 = 0 - 1`. -/ +def LabelSrc.sel (c0 t0 : Bool) : LabelSrc → ℕ → Bool + | .const _, _ => false + | .input b, q => (decide (q = 0) && !b) || (decide (q = 1) && b && !c0) + | .pos _ cs ts, q => + (decide (q = 2) && decide (cs = .same)) || (decide (q = 3) && decide (cs = .prev) && !c0) || + (decide (q = 4) && decide (cs = .next)) || (decide (q = 5) && decide (cs = .last)) || + (decide (q = 6) && decide (ts = .same)) || (decide (q = 7) && decide (ts = .prev) && !t0) || + (decide (q = 8) && decide (ts = .top)) + +/-- The selector `q` of a symbolic sign: the constant `true` (`9`) or the lookup (`10`). -/ +def SignSrc.sel : SignSrc → ℕ → Bool + | .const b, q => decide (q = 9) && b + | .lookup, q => decide (q = 10) + +/-- **The tag and slot bits of a shape**: two tag bits, then the slot bits. -/ +def tagSlot (sh : Shape) (f : ℕ) : Bool := + if f < 2 then sh.1.tag.testBit f else sh.1.slotVal.testBit (f - 2) + +/-- **The selectors of a shape**, `0` to `10`. -/ +def selOf (c0 t0 : Bool) (sh : Shape) (q : ℕ) : Bool := sh.1.sel c0 t0 q || sh.2.sel q + +/-- The column field selected by `sel`, at bit `q`. -/ +def colTerm (sel : ℕ → Bool) (c T q : ℕ) : Bool := + (sel 2 && c.testBit q) || (sel 3 && (c.testBit q ^^ decide (c % 2 ^ q = 0))) || + (sel 4 && (c.testBit q ^^ decide (c % 2 ^ q = 2 ^ q - 1))) || (sel 5 && (2 * T).testBit q) + +/-- The block field selected by `sel`, at bit `q`. -/ +def timeTerm (sel : ℕ → Bool) (t T q : ℕ) : Bool := + (sel 6 && t.testBit q) || (sel 7 && (t.testBit q ^^ decide (t % 2 ^ q = 0))) || + (sel 8 && T.testBit q) + +/-- The payload bit `q` selected by the tag and slot bits `ts` and the selectors `sel`. -/ +def payTerm (ts sel : ℕ → Bool) (s cb p c t T q : ℕ) : Bool := + (decide (q < s) && ts (2 + q)) || (sel 0 && p.testBit q) || + (sel 1 && (c.testBit q ^^ decide (c % 2 ^ q = 0))) || + (decide (s ≤ q ∧ q < s + cb) && colTerm sel c T (q - s)) || + (decide (s + cb ≤ q) && timeTerm sel t T (q - s - cb)) + +/-- **The output bit `i` of a literal** with tag and slot bits `ts` and selectors `sel`: the tag +bits, then the payload bits, then the sign. This is what the output stage of the decoder +computes. -/ +def outBit (s cb tb : ℕ) (ts sel : ℕ → Bool) (p c t T : ℕ) (lk : Bool) (i : ℕ) : Bool := + if i < 2 then ts i + else if i < 2 + (s + cb + tb) then payTerm ts sel s cb p c t T (i - 2) + else sel 9 || (sel 10 && lk) + +/-- Bit `q` of `c - 1`, for `1 ≤ c`: bit `q` of `c`, flipped when the lower bits of `c` are +zero. -/ +theorem testBit_sub_one {c : ℕ} (hc : 1 ≤ c) (q : ℕ) : + (c - 1).testBit q = (c.testBit q ^^ decide (c % 2 ^ q = 0)) := by + induction q generalizing c with + | zero => + simp only [Nat.testBit_zero, pow_zero, Nat.mod_one, decide_true, Bool.xor_true] + rw [Bool.eq_iff_iff]; simp; omega + | succ q ih => + rw [Nat.testBit_succ, Nat.testBit_succ, Nat.pow_succ', Nat.mod_mul] + rcases Nat.even_or_odd c with ⟨d, rfl⟩ | ⟨d, rfl⟩ + · have hd : 1 ≤ d := by omega + have h₁ : (d + d - 1) / 2 = d - 1 := by omega + have h₂ : (d + d) / 2 = d := by omega + have h₃ : (d + d) % 2 = 0 := by omega + rw [h₁, h₂, ih hd, h₃] + congr 2 + rw [Nat.zero_add, Nat.mul_eq_zero] + simp + · have h₁ : (2 * d + 1 - 1) / 2 = d := by omega + have h₂ : (2 * d + 1) / 2 = d := by omega + have h₃ : (2 * d + 1) % 2 = 1 := by omega + rw [h₁, h₂, h₃] + simp + +/-- Bit `q` of `c + 1`: bit `q` of `c`, flipped when the lower bits of `c` are all one. -/ +theorem testBit_add_one (c q : ℕ) : + (c + 1).testBit q = (c.testBit q ^^ decide (c % 2 ^ q = 2 ^ q - 1)) := by + induction q generalizing c with + | zero => + simp only [Nat.testBit_zero, pow_zero, Nat.mod_one, Nat.sub_self, decide_true, Bool.xor_true] + rw [Bool.eq_iff_iff]; simp; omega + | succ q ih => + rw [Nat.testBit_succ, Nat.testBit_succ, Nat.pow_succ', Nat.mod_mul] + have hq := Nat.one_le_two_pow (n := q) + rcases Nat.even_or_odd c with ⟨d, rfl⟩ | ⟨d, rfl⟩ + · have h₁ : (d + d + 1) / 2 = d := by omega + have h₂ : (d + d) / 2 = d := by omega + have h₃ : (d + d) % 2 = 0 := by omega + rw [h₁, h₂, h₃] + have : ¬ (2 * (d % 2 ^ q) = 2 * 2 ^ q - 1) := by omega + simp [this] + · have h₁ : (2 * d + 1 + 1) / 2 = d + 1 := by omega + have h₂ : (2 * d + 1) / 2 = d := by omega + have h₃ : (2 * d + 1) % 2 = 1 := by omega + rw [h₁, h₂, h₃, ih d] + congr 2 + exact propext ⟨fun h => by omega, fun h => by omega⟩ + +/-- The bits of a tag and a payload. -/ +theorem testBit_tag_add {tag : ℕ} (htag : tag < 4) (pay i : ℕ) : + (tag + 4 * pay).testBit i = if i < 2 then tag.testBit i else pay.testBit (i - 2) := by + rw [Nat.add_comm, show 4 = 2 ^ 2 from rfl, Nat.testBit_two_pow_mul_add _ htag] + +/-- The bits of three fields of widths `s` and `cb` and a high field. -/ +theorem testBit_fields {s cb sl C Tm : ℕ} (hsl : sl < 2 ^ s) (hC : C < 2 ^ cb) (q : ℕ) : + (sl + 2 ^ s * (C + 2 ^ cb * Tm)).testBit q = + if q < s then sl.testBit q else if q < s + cb then C.testBit (q - s) + else Tm.testBit (q - s - cb) := by + rw [Nat.add_comm, Nat.testBit_two_pow_mul_add _ hsl, Nat.add_comm C, + Nat.testBit_two_pow_mul_add _ hC] + split_ifs <;> first | rfl | omega + +/-- A symbolic label whose slot and column fit their fields. -/ +def LabelSrc.InRange (s cb c T : ℕ) : LabelSrc → Prop + | .pos sl cs _ => sl < 2 ^ s ∧ cs.eval c T < 2 ^ cb + | _ => True + +/-- The payload of a symbolic label. -/ +def LabelSrc.payload (s cb p c t T : ℕ) : LabelSrc → ℕ + | .const b => b.toNat + | .input false => p + | .input true => c - 1 + | .pos sl cs ts => sl + 2 ^ s * (cs.eval c T + 2 ^ cb * ts.eval t T) + +theorem LabelSrc.eval_eq (s cb p c t T : ℕ) (l : LabelSrc) : + l.eval s cb p c t T = l.tag + 4 * l.payload s cb p c t T := by + rcases l with b | (_ | _) | ⟨sl, cs, ts⟩ <;> simp [LabelSrc.eval, LabelSrc.tag, + LabelSrc.payload, constLabel, inputLabel] + +theorem LabelSrc.tag_lt (l : LabelSrc) : l.tag < 4 := by + rcases l with b | b | ⟨sl, cs, ts⟩ <;> simp [LabelSrc.tag] + +private theorem colTerm_eq {c T q sl : ℕ} (cs : ColSrc) (ts : TimeSrc) (t0 : Bool) + (sg : SignSrc) : + colTerm (selOf (decide (c = 0)) t0 (.pos sl cs ts, sg)) c T q = (cs.eval c T).testBit q := by + cases sg <;> cases cs <;> simp only [colTerm, selOf, LabelSrc.sel, SignSrc.sel, ColSrc.eval] <;> + simp only [decide_true, reduceCtorEq, decide_false, Bool.and_false, Nat.reduceEqDiff, + Bool.and_self, Bool.false_and, Bool.or_self, Nat.succ_ne_self, Nat.zero_testBit, + Bool.or_false, Bool.true_and, Bool.and_true, Bool.false_or, Bool.or_true] + all_goals first + | rw [testBit_add_one] + | (by_cases hc : c = 0 + · simp [hc] + · rw [testBit_sub_one (by omega)]; simp [hc]) + +private theorem timeTerm_eq {t T q sl : ℕ} (cs : ColSrc) (ts : TimeSrc) (c0 : Bool) + (sg : SignSrc) : + timeTerm (selOf c0 (decide (t = 0)) (.pos sl cs ts, sg)) t T q = (ts.eval t T).testBit q := by + cases sg <;> cases ts <;> simp only [timeTerm, selOf, LabelSrc.sel, SignSrc.sel, TimeSrc.eval] <;> + simp only [Nat.reduceEqDiff, decide_false, Bool.false_and, Bool.or_self, Nat.succ_ne_self, + decide_true, Bool.and_self, Bool.or_true, reduceCtorEq, Bool.or_false, Bool.true_and, + Bool.and_true, Bool.and_false, Bool.false_or] + all_goals + by_cases ht : t = 0 + · simp [ht] + · rw [testBit_sub_one (by omega)]; simp [ht] + +/-- **The output bits of a shape are the bits of its literal**: its label below `2 + P` and its +sign at `2 + P`, for `P = s + cb + tb`, at least one slot bit, a looked-up bit that is the bit of +`x` at `p` whenever it is used, and a label whose slot and column fit their fields. -/ +theorem outBit_shape {s cb tb p c t T : ℕ} {x : List Bool} {lk : Bool} (hs : 1 ≤ s) + {sh : Shape} (hR : sh.1.InRange s cb c T) (hlk : sh.2 = .lookup → lk = x.getD p false) + {i : ℕ} (hi : i ≤ 2 + (s + cb + tb)) : + outBit s cb tb (tagSlot sh) (selOf (decide (c = 0)) (decide (t = 0)) sh) p c t T lk i = + if i < 2 + (s + cb + tb) then (sh.1.eval s cb p c t T).testBit i else sh.2.eval x p := by + obtain ⟨l, sg⟩ := sh + rw [LabelSrc.eval_eq, testBit_tag_add l.tag_lt] + unfold outBit + by_cases h2 : i < 2 + · simp [h2, tagSlot, show i < 2 + (s + cb + tb) by omega] + · by_cases hP : i < 2 + (s + cb + tb) + · simp only [h2, hP, ↓reduceIte] + have hq : i - 2 < s + cb + tb := by omega + have hts : 2 + (i - 2) = i := by omega + generalize i - 2 = q at hq hts ⊢ + subst hts + have h21 : ¬ 2 + q ≤ 1 := by omega + rcases l with b | (_ | _) | ⟨sl, cs, ts⟩ + · cases sg <;> simp only [payTerm, tagSlot, Order.lt_two_iff, h21, ↓reduceIte, + LabelSrc.slotVal, add_tsub_cancel_left, selOf, LabelSrc.sel, SignSrc.sel, + OfNat.zero_ne_ofNat, decide_false, Bool.false_and, Bool.or_self, Bool.or_false, + OfNat.one_ne_ofNat, Bool.decide_and, colTerm, Nat.reduceEqDiff, Bool.and_false, timeTerm, + LabelSrc.payload, Bool.and_eq_right_iff_imp, decide_eq_true_eq] + all_goals + intro hqs + cases b <;> simp [Nat.testBit_one_eq_true_iff_self_eq_zero] at hqs + omega + · cases sg <;> simp [payTerm, tagSlot, selOf, LabelSrc.sel, SignSrc.sel, LabelSrc.payload, + LabelSrc.slotVal, colTerm, timeTerm, h21] + · cases sg <;> simp only [payTerm, tagSlot, Order.lt_two_iff, h21, ↓reduceIte, + LabelSrc.slotVal, add_tsub_cancel_left, Nat.zero_testBit, Bool.and_false, selOf, + LabelSrc.sel, decide_true, Bool.not_true, zero_ne_one, decide_false, Bool.and_true, + Bool.false_and, Bool.or_self, SignSrc.sel, OfNat.zero_ne_ofNat, one_ne_zero, + Bool.and_self, Bool.true_and, Bool.false_or, OfNat.one_ne_ofNat, Bool.or_false, + Bool.decide_and, colTerm, OfNat.ofNat_ne_zero, OfNat.ofNat_ne_one, Nat.reduceEqDiff, + timeTerm, LabelSrc.payload] + all_goals + by_cases hc : c = 0 + · simp [hc] + · rw [testBit_sub_one (by omega)]; simp [hc] + · obtain ⟨hsl, hC⟩ := hR + rw [LabelSrc.payload, testBit_fields hsl hC] + have hcol := colTerm_eq (c := c) (T := T) (q := q - s) (sl := sl) cs ts (decide (t = 0)) sg + have htime := timeTerm_eq (t := t) (T := T) (q := q - s - cb) (sl := sl) cs ts + (decide (c = 0)) sg + simp only [payTerm, hcol, htime] + have h0 : selOf (decide (c = 0)) (decide (t = 0)) (.pos sl cs ts, sg) 0 = false := by + cases sg <;> simp [selOf, LabelSrc.sel, SignSrc.sel] + have h1 : selOf (decide (c = 0)) (decide (t = 0)) (.pos sl cs ts, sg) 1 = false := by + cases sg <;> simp [selOf, LabelSrc.sel, SignSrc.sel] + simp only [h0, h1, Bool.false_and, Bool.or_false, tagSlot, + show ¬ 2 + q < 2 by omega, ↓reduceIte, LabelSrc.slotVal, Nat.add_sub_cancel_left] + by_cases hqs : q < s + · simp [hqs, show ¬ s ≤ q by omega, show ¬ s + cb ≤ q by omega] + · by_cases hqc : q < s + cb + · simp [hqs, hqc, show s ≤ q by omega, show ¬ s + cb ≤ q by omega] + · simp [hqs, hqc, show s + cb ≤ q by omega] + · simp only [h2, hP, ↓reduceIte] + cases sg <;> rcases l with b | b | ⟨sl, cs, ts⟩ <;> + simp only [selOf, LabelSrc.sel, SignSrc.sel, decide_true, Bool.true_and, Bool.false_or, + Nat.succ_ne_self, decide_false, Bool.false_and, Bool.or_self, Bool.or_false, + SignSrc.eval, OfNat.ofNat_ne_zero, OfNat.ofNat_ne_one, Nat.reduceEqDiff, Bool.or_true, + List.getD_eq_getElem?_getD] + all_goals exact hlk rfl + +end Bits + +/-! ### The shapes stay within their fields -/ + +section Range + +theorem fst_mem_tseitinClause {V : Type} {op : Op} {w a b : V} {j : Fin 3} {l : Literal V} + (h : l ∈ tseitinClause op w a b j) : l.1 = w ∨ l.1 = a ∨ l.1 = b := by + obtain ⟨v, s⟩ := l + fin_cases j <;> cases op <;> simp [tseitinClause] at h <;> aesop + +variable {X n T : ℕ} + +omit [DecidableEq State] in +theorem refSrc_inRange {j tag p : ℕ} {r : Ref k State} + (hr : r.InWindow n T (colOf (slotBits k State) (colBits T) p)) : + ((Key.ofFields (slotBits k State) (colBits T) X n T j tag p).refSrc r).InRange + (slotBits k State) (colBits T) (colOf (slotBits k State) (colBits T) p) T := by + rcases r with _ | _ | _ | ⟨q, l, cs, g⟩ + · trivial + · trivial + · simp only [Key.refSrc]; split <;> trivial + · simp only [Key.refSrc, Key.guard_ofFields] + split + · rename_i hg + refine ⟨slot_lt_two_pow l, ?_⟩ + rw [colSrcOf_eval] + exact lt_of_le_of_lt (Loc.labelCol_le l _) (lt_of_lt_of_le (hr hg) (numCols_le_two_pow T)) + · trivial + +/-- A property of every literal of every branch of `shapeOf` holds of its literals. -/ +theorem forall_mem_shapeOf {P : Shape → Prop} (κ : Key) (htaut : ∀ sh ∈ tautShape, P sh) + (h0 : ∀ (hj : κ.j < 3), ∀ sh ∈ lift (tseitinClause (.const !κ.p0) (.const !κ.p0) + (.const false) (.const false) ⟨κ.j, hj⟩), P sh) + (h1 : κ.pLtX = true → P (.input false, .lookup)) + (h2 : ∀ (hj : κ.j < 3) (l : Loc k State), Loc.ofSlot κ.slot = some l → + (!κ.tGtT && κ.colOK l) = true → ∀ sh ∈ lift (posShape tm κ l ⟨κ.j, hj⟩), P sh) + (h3 : P (outSrc (k := k) (State := State), .const true)) : + ∀ sh ∈ shapeOf tm κ, P sh := by + intro sh hsh + unfold shapeOf at hsh + by_cases hj : κ.j < 3 + · rw [dite_eq_left hj] at hsh + by_cases ht0 : κ.tag = 0 + · rw [ite_eq_left ht0] at hsh + split at hsh + · exact h0 hj sh hsh + · exact htaut sh hsh + · rw [ite_eq_right ht0] at hsh + by_cases ht1 : κ.tag = 1 + · rw [ite_eq_left ht1] at hsh + split at hsh + · rename_i hc + simp only [List.mem_cons, List.not_mem_nil, or_false] at hsh + rcases hsh with rfl | rfl | rfl <;> exact h1 hc.2 + · exact htaut sh hsh + · rw [ite_eq_right ht1] at hsh + by_cases ht2 : κ.tag = 2 + · rw [ite_eq_left ht2] at hsh + split at hsh + · rename_i l hl + split at hsh + · rename_i hok + exact h2 hj l hl hok sh hsh + · exact htaut sh hsh + · exact htaut sh hsh + · rw [ite_eq_right ht2] at hsh + split at hsh + · simp only [List.mem_cons, List.not_mem_nil, or_false] at hsh + rcases hsh with rfl | rfl | rfl <;> exact h3 + · exact htaut sh hsh + · rw [dite_eq_right hj] at hsh + exact htaut sh hsh + +/-- **The labels of the shapes fit their fields.** -/ +theorem inRange_of_mem_shapeOf {j tag p : ℕ} {sh : Shape} + (hsh : sh ∈ shapeOf tm (Key.ofFields (slotBits k State) (colBits T) X n T j tag p)) : + sh.1.InRange (slotBits k State) (colBits T) (colOf (slotBits k State) (colBits T) p) T := by + have hc : colOf (slotBits k State) (colBits T) p < 2 ^ colBits T := + Nat.mod_lt _ (Nat.two_pow_pos _) + refine forall_mem_shapeOf (P := fun sh => sh.1.InRange (slotBits k State) (colBits T) + (colOf (slotBits k State) (colBits T) p) T) tm _ ?_ ?_ ?_ ?_ ?_ sh hsh + · intro sh hsh + simp only [tautShape, List.mem_cons, List.not_mem_nil, or_false] at hsh + rcases hsh with rfl | rfl | rfl <;> trivial + · intro hj sh hsh + simp only [lift, List.mem_map] at hsh + obtain ⟨l, hl, rfl⟩ := hsh + rcases fst_mem_tseitinClause hl with h | h | h <;> simp only [h] <;> trivial + · intro _; trivial + · intro hj l _ hok sh hsh + simp only [lift, List.mem_map] at hsh + obtain ⟨lit, hlit, rfl⟩ := hsh + have hok' : l.ColOK T (colOf (slotBits k State) (colBits T) p) := by + have := Key.colOK_ofFields (s := slotBits k State) (cb := colBits T) (X := X) (n := n) + (T := T) j tag p l + simp only [Bool.and_eq_true] at hok + simpa [this] using hok.2 + obtain ⟨hra, hrb⟩ := table_inWindow (n := n) hok'.lt tm l + unfold posShape at hlit + split_ifs at hlit <;> rcases fst_mem_tseitinClause hlit with h | h | h <;> simp only [h] + all_goals first + | trivial + | exact ⟨slot_lt_two_pow _, hc⟩ + | exact refSrc_inRange hra + | exact refSrc_inRange hrb + · exact ⟨slot_lt_two_pow _, Nat.two_pow_pos _⟩ + +/-- **A shape looks up `x` only at a payload within it.** -/ +theorem lt_of_mem_shapeOf {j tag p : ℕ} {sh : Shape} + (hsh : sh ∈ shapeOf tm (Key.ofFields (slotBits k State) (colBits T) X n T j tag p)) + (hlk : sh.2 = .lookup) : p < X := by + revert hlk + refine forall_mem_shapeOf (P := fun sh => sh.2 = .lookup → p < X) tm _ ?_ ?_ ?_ ?_ ?_ sh hsh + · intro sh hsh + simp only [tautShape, List.mem_cons, List.not_mem_nil, or_false] at hsh + rcases hsh with rfl | rfl | rfl <;> simp + · intro hj sh hsh + simp only [lift, List.mem_map] at hsh + obtain ⟨l, -, rfl⟩ := hsh + simp + · intro h _ + simpa [Key.ofFields] using h + · intro hj l _ _ sh hsh + simp only [lift, List.mem_map] at hsh + obtain ⟨l, -, rfl⟩ := hsh + simp + · simp + +/-- A shape has three literals. -/ +theorem length_shapeOf (κ : Key) : (shapeOf tm κ).length = 3 := by + unfold shapeOf + split + · split + · split + · simp [lift] + · rfl + · split + · split <;> rfl + · split + · split + · split + · simp only [lift, List.length_map, posShape] + split <;> simp + · rfl + · rfl + · split <;> rfl + · rfl + +end Range + +end Cslib.Circuits.Boolean.Tableau.Decoder diff --git a/ComplexityTests/TableauDecoder.lean b/ComplexityTests/TableauDecoder.lean new file mode 100644 index 000000000..7dfe19623 --- /dev/null +++ b/ComplexityTests/TableauDecoder.lean @@ -0,0 +1,63 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +import Complexity.Computability.Circuit.Boolean.Tableau.Decoder.Assembly + +/-! +# The decoder of the succinct tableau + +The field stage is evaluated on concrete clause numbers with `decide`: one slot bit, two column +bits, one block bit, `x = [true, false]`, `n = 1` and `T = 1`, so `8` inputs and `75` gates. The +comparison chains, the borrows and carries, and the lookup of `x` give the expected values. The +constant stage depends on the noncomputable numbering of positions (`Loc.slot`), so the whole +decoder is checked through its correctness theorem instead. +-/ + +namespace ComplexityTests.TableauDecoder + +open Cslib Cslib.Circuits Cslib.Circuits.Boolean Cslib.Circuits.Boolean.Tableau +open Cslib.Circuits.Boolean.Tableau.Decoder + +/-- The field stage of the example. -/ +private def gates : List (Op × List ℕ) := fieldGates 1 2 1 [true, false] 1 1 + +/-- The value of wire `w` on the clause number `idx`. -/ +private def wire (idx w : ℕ) : Bool := evalOutput (List.ofFn fun i : Fin 8 => idx.testBit i) gates w + +example : gates.length = 75 := by decide + +-- Chain 4 compares the column `c = idx / 32 % 4` with `n = 1`; its last gate is wire `37`. +example : wire 64 37 = true ∧ wire 32 37 = false ∧ wire 96 37 = true := by decide + +-- Chain 0 gives the borrows of `c - 1`: after one bit, `c % 2 = 0`. +example : wire 64 18 = true ∧ wire 32 18 = false := by decide + +-- Chain 11 compares the payload `p = idx / 16` with the length `2` of `x`. +example : wire 16 (18 + 11 * 4 + 3) = true ∧ wire 32 (18 + 11 * 4 + 3) = false := by decide + +-- The lookup of `x` at the payload is wire `82`: `x[0] = true` and `x[1] = false`. +example : wire 5 82 = true ∧ wire 19 82 = false := by decide + +-- Every wire agrees with its intended value on these clause numbers. +example : (List.range 83).all (fun w => wire 100 w == fieldVal 1 2 1 [true, false] 1 1 100 w) := + by decide +example : (List.range 83).all (fun w => wire 211 w == fieldVal 1 2 1 [true, false] 1 1 211 w) := + by decide + +variable {k : ℕ} {State : Type} [Fintype State] [DecidableEq State] + (tm : Turing.MultiTapeTM k Bool State) + +-- The decoder of any machine: its code, a linear size bound, and the bits of every clause. +example (x : List Bool) (n T : ℕ) (hn : n < 2 * T) (hx : x.length ≤ n) : + ∃ c : Circuit signature (clauseBits k State T) (3 * (labelBits k State T + 1)), + c.code = decoderCode tm (x, n, T) ∧ + c.size ≤ sizeConst tm * (colBits T + timeBits T + x.length + 1) ∧ + ∀ idx < 2 ^ clauseBits k State T, + List.ofFn (c.eval interpretation fun i => idx.testBit i) = + (clauseAt tm x n T idx).flatMap (litBits (labelBits k State T)) := + exists_decoder tm x hn hx + +end ComplexityTests.TableauDecoder From 6286b170251116b006025ba8d45f5e05e1c8f1b9 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sat, 31 Oct 2026 13:36:11 -0400 Subject: [PATCH 6/8] refactor(Circuit/Boolean/Tableau/Decoder): name the chain components; bind table components Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Boolean/Tableau/Decoder/Circuit.lean | 2 +- .../Boolean/Tableau/Decoder/Fields.lean | 90 +++++++++++-------- .../Boolean/Tableau/Decoder/Shape.lean | 15 +++- 3 files changed, 65 insertions(+), 42 deletions(-) diff --git a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Circuit.lean b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Circuit.lean index ce1ced92e..115439690 100644 --- a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Circuit.lean +++ b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Circuit.lean @@ -411,7 +411,7 @@ theorem fieldVal_payload (idx : ℕ) {q : ℕ} (hq : q < s + cb + tb) : /-- The value of chain `k` after `q` bits, through `chainW`. -/ theorem fieldVal_chainW (idx : ℕ) {k q : ℕ} (hk : k < 14) (hq : q ≤ s + cb + tb) - (hinit : (chain s cb tb x.length n T k).2.2.2.2 = true) : + (hinit : (chain s cb tb x.length n T k).init = true) : fieldVal s cb tb x n T idx (chainW (4 + (s + cb + tb)) (s + cb + tb) k q) = chainVal (chain s cb tb x.length n T k) idx q := by unfold chainW diff --git a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Fields.lean b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Fields.lean index becc13b7e..74ce5ab1d 100644 --- a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Fields.lean +++ b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Fields.lean @@ -41,7 +41,8 @@ with its gate (`allGates_consistentAt_fieldGates`), so it is the value the list * `Cslib.Circuits.Boolean.Tableau.Decoder.AllGates`: a property of every gate of a list at its wire. * `Cslib.Circuits.Boolean.Tableau.Decoder.cmp`: the comparison computed by a chain. -* `Cslib.Circuits.Boolean.Tableau.Decoder.chain`: the fourteen comparison chains. +* `Cslib.Circuits.Boolean.Tableau.Decoder.Chain`, `Cslib.Circuits.Boolean.Tableau.Decoder.chain`: + the fourteen comparison chains. * `Cslib.Circuits.Boolean.Tableau.Decoder.fieldGates`: the gate list. * `Cslib.Circuits.Boolean.Tableau.Decoder.fieldVal`: the intended values of its wires. @@ -213,25 +214,39 @@ theorem cmp_mod_of_lt {gt : Bool} {v K w : ℕ} {e : Bool} (hv : v < 2 ^ w) (hK /-! ### The layout -/ +/-- A comparison chain: the offset and the width of the field it reads, its direction (`gt`: +`K < v` rather than `v < K`), the constant `K` and the tie value `init`. -/ +structure Chain where + /-- The input offset of the field. -/ + off : ℕ + /-- The width of the field. -/ + width : ℕ + /-- Whether the chain decides `K < v` rather than `v < K`. -/ + gt : Bool + /-- The constant. -/ + K : ℕ + /-- The value on equal numbers. -/ + init : Bool + /-- The fourteen comparison chains, for slot, column and block widths `s`, `cb` and `tb`, an input bound `n`, a step bound `T` and a hardwired prefix of length `X`: the offset and the width of the field read, the direction, the constant and the tie value. Chains `0`, `1` and `2` give the borrows of `c - 1`, the carries of `c + 1` and the borrows of `t - 1`. -/ -def chain (s cb tb X n T : ℕ) (k : ℕ) : ℕ × ℕ × Bool × ℕ × Bool := - if k = 0 then (4 + s, cb, false, 0, true) -- c % 2 ^ i = 0 - else if k = 1 then (4 + s, cb, true, 2 ^ (s + cb + tb) - 1, true) -- c % 2 ^ i = 2 ^ i - 1 - else if k = 2 then (4 + s + cb, tb, false, 0, true) -- t % 2 ^ i = 0 - else if k = 3 then (4 + s, cb, true, 1, false) -- 1 < c - else if k = 4 then (4 + s, cb, true, n, false) -- n < c - else if k = 5 then (4 + s, cb, true, n + 1, false) -- n + 1 < c - else if k = 6 then (4 + s, cb, false, 2 * T, false) -- c < 2 T - else if k = 7 then (4 + s, cb, true, 2 * T, false) -- 2 T < c - else if k = 8 then (4 + s, cb, false, T, false) -- c < T - else if k = 9 then (4 + s, cb, true, T, false) -- T < c - else if k = 10 then (4 + s + cb, tb, true, T, false) -- T < t - else if k = 11 then (4, s + cb + tb, false, X, false) -- p < X - else if k = 12 then (4, s + cb + tb, false, 0, true) -- p = 0 - else (4, s + cb + tb, true, 1, false) -- 1 < p +def chain (s cb tb X n T : ℕ) (k : ℕ) : Chain := + if k = 0 then ⟨4 + s, cb, false, 0, true⟩ -- c % 2 ^ i = 0 + else if k = 1 then ⟨4 + s, cb, true, 2 ^ (s + cb + tb) - 1, true⟩ -- c % 2 ^ i = 2 ^ i - 1 + else if k = 2 then ⟨4 + s + cb, tb, false, 0, true⟩ -- t % 2 ^ i = 0 + else if k = 3 then ⟨4 + s, cb, true, 1, false⟩ -- 1 < c + else if k = 4 then ⟨4 + s, cb, true, n, false⟩ -- n < c + else if k = 5 then ⟨4 + s, cb, true, n + 1, false⟩ -- n + 1 < c + else if k = 6 then ⟨4 + s, cb, false, 2 * T, false⟩ -- c < 2 T + else if k = 7 then ⟨4 + s, cb, true, 2 * T, false⟩ -- 2 T < c + else if k = 8 then ⟨4 + s, cb, false, T, false⟩ -- c < T + else if k = 9 then ⟨4 + s, cb, true, T, false⟩ -- T < c + else if k = 10 then ⟨4 + s + cb, tb, true, T, false⟩ -- T < t + else if k = 11 then ⟨4, s + cb + tb, false, X, false⟩ -- p < X + else if k = 12 then ⟨4, s + cb + tb, false, 0, true⟩ -- p = 0 + else ⟨4, s + cb + tb, true, 1, false⟩ -- 1 < p /-- The start of the comparison chains, after the `N` inputs, the two constants and the `N` negations. -/ @@ -246,11 +261,11 @@ def orBase (N P m : ℕ) : ℕ := treeBase N P + (2 ^ (m + 1) - 1) /-- Gate `i` of chain `k`, comparing the field of width `w` at input offset `off`, padded with zeros to `P` bits, with the constant `K`: its literal is the bit of the field (`gt`) or its negation, and it combines it with the previous gate of the chain (or the tie value `init`). -/ -def chainGate (N P k : ℕ) (spec : ℕ × ℕ × Bool × ℕ × Bool) (i : ℕ) : Op × List ℕ := - (andOr (spec.2.2.2.1.testBit i ^^ spec.2.2.1), - [if i < spec.2.1 then (if spec.2.2.1 then spec.1 + i else N + 2 + (spec.1 + i)) - else (if spec.2.2.1 then N else N + 1), - if i = 0 then (if spec.2.2.2.2 then N + 1 else N) else chainBase N + k * P + (i - 1)]) +def chainGate (N P k : ℕ) (spec : Chain) (i : ℕ) : Op × List ℕ := + (andOr (spec.K.testBit i ^^ spec.gt), + [if i < spec.width then (if spec.gt then spec.off + i else N + 2 + (spec.off + i)) + else (if spec.gt then N else N + 1), + if i = 0 then (if spec.init then N + 1 else N) else chainBase N + k * P + (i - 1)]) /-- Node `q` of level `ℓ` of the decoder tree: true at level `0`, and otherwise the node `q % 2 ^ ℓ` of the level below together with bit `ℓ` of the payload, negated when bit `ℓ` of `q` is clear. -/ @@ -298,8 +313,8 @@ theorem length_fieldGates (s cb tb : ℕ) (x : List Bool) (n T : ℕ) : /-! ### The intended values -/ /-- The comparison of chain `spec` after `j` bits, on the clause number `idx`. -/ -def chainVal (spec : ℕ × ℕ × Bool × ℕ × Bool) (idx j : ℕ) : Bool := - cmp spec.2.2.1 (idx / 2 ^ spec.1 % 2 ^ spec.2.1 % 2 ^ j) (spec.2.2.2.1 % 2 ^ j) spec.2.2.2.2 +def chainVal (spec : Chain) (idx j : ℕ) : Bool := + cmp spec.gt (idx / 2 ^ spec.off % 2 ^ spec.width % 2 ^ j) (spec.K % 2 ^ j) spec.init /-- **The intended value of each wire** of the field stage on the bits of the clause number `idx`: the inputs, the constants, the negations, the chains after each bit, the indicators of @@ -332,10 +347,11 @@ private theorem ite_pred {α : Type} {p : α → Prop} {c : Prop} [Decidable c] /-- The fields read by the chains lie within the inputs. -/ theorem chain_off_add_width_le (k : ℕ) : - (chain s cb tb x.length n T k).1 + (chain s cb tb x.length n T k).2.1 ≤ 4 + (s + cb + tb) := by + (chain s cb tb x.length n T k).off + (chain s cb tb x.length n T k).width ≤ + 4 + (s + cb + tb) := by unfold chain - repeat (refine ite_pred (p := fun spec : ℕ × ℕ × Bool × ℕ × Bool => - spec.1 + spec.2.1 ≤ 4 + (s + cb + tb)) ?_ ?_; · dsimp only; omega) + repeat (refine ite_pred (p := fun spec : Chain => + spec.off + spec.width ≤ 4 + (s + cb + tb)) ?_ ?_; · dsimp only; omega) dsimp only; omega private theorem chainWire_lt {k i P : ℕ} (hk : k < 14) (hi : i < P) : k * P + i < 14 * P := by @@ -434,16 +450,16 @@ theorem fieldVal_or (idx : ℕ) (i : ℕ) : /-- The literal of a chain gate is the bit of its field, or its negation. -/ private theorem fieldVal_chainLit (idx : ℕ) (k i : ℕ) : fieldVal s cb tb x n T idx - (if i < (chain s cb tb x.length n T k).2.1 then - (if (chain s cb tb x.length n T k).2.2.1 then (chain s cb tb x.length n T k).1 + i - else 4 + (s + cb + tb) + 2 + ((chain s cb tb x.length n T k).1 + i)) - else (if (chain s cb tb x.length n T k).2.2.1 then 4 + (s + cb + tb) + (if i < (chain s cb tb x.length n T k).width then + (if (chain s cb tb x.length n T k).gt then (chain s cb tb x.length n T k).off + i + else 4 + (s + cb + tb) + 2 + ((chain s cb tb x.length n T k).off + i)) + else (if (chain s cb tb x.length n T k).gt then 4 + (s + cb + tb) else 4 + (s + cb + tb) + 1)) = - if (chain s cb tb x.length n T k).2.2.1 then - (idx / 2 ^ (chain s cb tb x.length n T k).1 % - 2 ^ (chain s cb tb x.length n T k).2.1).testBit i - else !(idx / 2 ^ (chain s cb tb x.length n T k).1 % - 2 ^ (chain s cb tb x.length n T k).2.1).testBit i := by + if (chain s cb tb x.length n T k).gt then + (idx / 2 ^ (chain s cb tb x.length n T k).off % + 2 ^ (chain s cb tb x.length n T k).width).testBit i + else !(idx / 2 ^ (chain s cb tb x.length n T k).off % + 2 ^ (chain s cb tb x.length n T k).width).testBit i := by have hle := chain_off_add_width_le (s := s) (cb := cb) (tb := tb) (x := x) (n := n) (T := T) k generalize chain s cb tb x.length n T k = spec at hle ⊢ obtain ⟨off, w, gt, K, init⟩ := spec @@ -562,13 +578,13 @@ theorem allGates_consistentAt_fieldGates (hm : Nat.size x.length ≤ s + cb + tb (fun k hk i hi => ?_) (fun ℓ hℓ q hq => ?_) (fun i hi => ?_) · rw [chainGate, consistentAt_two, fieldVal_chain idx hk hi, fieldVal_chainLit] have hprev : fieldVal s cb tb x n T idx - (if i = 0 then (if (chain s cb tb x.length n T k).2.2.2.2 then 4 + (s + cb + tb) + 1 + (if i = 0 then (if (chain s cb tb x.length n T k).init then 4 + (s + cb + tb) + 1 else 4 + (s + cb + tb)) else chainBase (4 + (s + cb + tb)) + k * (s + cb + tb) + (i - 1)) = chainVal (chain s cb tb x.length n T k) idx i := by by_cases h₀ : i = 0 · subst h₀ - cases h₁ : (chain s cb tb x.length n T k).2.2.2.2 + cases h₁ : (chain s cb tb x.length n T k).init · simp only [↓reduceIte, Bool.false_eq_true, fieldVal_false, chainVal, cmp, pow_zero, Nat.mod_one, h₁] simp diff --git a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Shape.lean b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Shape.lean index 08bce7a5f..c3f32dbeb 100644 --- a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Shape.lean +++ b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Shape.lean @@ -253,8 +253,9 @@ def tautShape : List Shape := noncomputable def posShape (κ : Key) (l : Loc k State) (j : Fin 3) : Clause LabelSrc := if κ.t0 then tseitinClause (.const (κ.initBit tm l)) (.pos l.slot .same .same) (.const false) (.const false) j - else tseitinClause (table tm l).1 (.pos l.slot .same .same) (κ.refSrc (table tm l).2.1) - (κ.refSrc (table tm l).2.2) j + else + let (op, left, right) := table tm l + tseitinClause op (.pos l.slot .same .same) (κ.refSrc left) (κ.refSrc right) j /-- The symbolic label of the output. -/ noncomputable def outSrc : LabelSrc := @@ -396,7 +397,10 @@ theorem clauseOf_eq_map_shapeOf (x : List Bool) (n T j tag p : ℕ) : rw [hκ, Key.initBit_ofFields] rfl · have hκt : κ.t0 = false := by simp [hκ, Key.ofFields, ht] - simp only [ht, hκt, ↓reduceIte, Bool.false_eq_true, relabel_tseitinClause] + simp only [ht, hκt, ↓reduceIte, Bool.false_eq_true] + generalize table tm l = e + obtain ⟨op, a, b⟩ := e + simp only [relabel_tseitinClause] rw [hκ, refSrc_eval, refSrc_eval] rfl · have hnot : ¬ (!κ.tGtT && κ.colOK l) = true := fun h => hok (hiff.1 h) @@ -755,8 +759,11 @@ theorem inRange_of_mem_shapeOf {j tag p : ℕ} {sh : Shape} (T := T) j tag p l simp only [Bool.and_eq_true] at hok simpa [this] using hok.2 - obtain ⟨hra, hrb⟩ := table_inWindow (n := n) hok'.lt tm l + have hw := table_inWindow (n := n) hok'.lt tm l unfold posShape at hlit + generalize table tm l = e at hw hlit + obtain ⟨op, a, b⟩ := e + obtain ⟨hra, hrb⟩ := hw split_ifs at hlit <;> rcases fst_mem_tseitinClause hlit with h | h | h <;> simp only [h] all_goals first | trivial From b9bc489ceda04450a2b0366ad90f7108946c3d3b Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 14:26:42 -0500 Subject: [PATCH 7/8] refactor(Circuit/Boolean/Tableau): reuse shared APIs in the succinct decoder Address the review of the decoder layer: - bind `(x, n, T)` by name in `decoderCode`; - replace the parallel `bitsVal` by `Nat.ofBits` and `Nat.ofBits_testBit`; - use `Nat.two_pow_size_le` from `Foundations/Data/Nat/Doubling`; - move the block-indexing fact of `Reduction/TruthTableVariants` to a public `List.getElem?_flatMap_mul_add` in `Foundations/Data/List/RowMajor` and derive the decoder's indexing step from it; - write the mid-proof `simp` calls as `simp only`; - test the decoder of a concrete machine on the clauses fixing the inputs and the output. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- .../Boolean/Tableau/Decoder/Assembly.lean | 91 ++++++------------- .../Boolean/Tableau/Decoder/Circuit.lean | 43 +++------ .../Boolean/Tableau/Decoder/Shape.lean | 59 +++++++++--- .../Reduction/TruthTableVariants.lean | 22 +---- .../Foundations/Data/List/RowMajor.lean | 22 +++++ ComplexityTests/TableauDecoder.lean | 28 +++++- 6 files changed, 139 insertions(+), 126 deletions(-) diff --git a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Assembly.lean b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Assembly.lean index a9d6c999f..0b510a61b 100644 --- a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Assembly.lean +++ b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Assembly.lean @@ -8,6 +8,8 @@ module -- upstream: candidate public import Complexity.Computability.Circuit.Boolean.Tableau.Decoder.Circuit +import Complexity.Foundations.Data.List.RowMajor +import Complexity.Foundations.Data.Nat.Doubling import Mathlib.Data.Nat.Size /-! @@ -96,25 +98,26 @@ noncomputable def decoder {x : List Bool} {n T : ℕ} /-- **The code of the decoder**, written with the code combinators `compCode`, `appendCode`, `appendCodes` and `formulaCode`: the field stage, the constant code of the constant stage, and one instance of `outTemplate` per output bit (`code_decoder`). -/ -noncomputable def decoderCode (a : List Bool × ℕ × ℕ) : List (Op × List ℕ) × List ℕ := - compCode (4 + (slotBits k State + colBits a.2.2 + timeBits a.2.2)) - (appendCodes (aWidth (slotBits k State) (colBits a.2.2) (timeBits a.2.2) a.1 a.2.1 a.2.2 + - 3 * (13 + slotBits k State)) - ((List.range (3 * (2 + (slotBits k State + colBits a.2.2 + timeBits a.2.2) + 1))).map - fun o => formulaCode (aWidth (slotBits k State) (colBits a.2.2) (timeBits a.2.2) a.1 a.2.1 - a.2.2 + 3 * (13 + slotBits k State)) outTemplate - (fun j => wireNat (slotBits k State) (colBits a.2.2) (timeBits a.2.2) a.1 a.2.1 a.2.2 o - j))) - (compCode (4 + (slotBits k State + colBits a.2.2 + timeBits a.2.2)) - (appendCode (aWidth (slotBits k State) (colBits a.2.2) (timeBits a.2.2) a.1 a.2.1 a.2.2) - ([], List.range - (aWidth (slotBits k State) (colBits a.2.2) (timeBits a.2.2) a.1 a.2.1 a.2.2)) - (compCode (aWidth (slotBits k State) (colBits a.2.2) (timeBits a.2.2) a.1 a.2.1 a.2.2) - (tableCircuit tm).code - ([], (List.range (4 + slotBits k State + 14)).map - (keyWire (slotBits k State) (colBits a.2.2) (timeBits a.2.2))))) - (fieldGates (slotBits k State) (colBits a.2.2) (timeBits a.2.2) a.1 a.2.1 a.2.2, - List.range (aWidth (slotBits k State) (colBits a.2.2) (timeBits a.2.2) a.1 a.2.1 a.2.2))) +noncomputable def decoderCode : List Bool × ℕ × ℕ → List (Op × List ℕ) × List ℕ + | (x, n, T) => + compCode (4 + (slotBits k State + colBits T + timeBits T)) + (appendCodes (aWidth (slotBits k State) (colBits T) (timeBits T) x n T + + 3 * (13 + slotBits k State)) + ((List.range (3 * (2 + (slotBits k State + colBits T + timeBits T) + 1))).map + fun o => formulaCode (aWidth (slotBits k State) (colBits T) (timeBits T) x n + T + 3 * (13 + slotBits k State)) outTemplate + (fun j => wireNat (slotBits k State) (colBits T) (timeBits T) x n T o + j))) + (compCode (4 + (slotBits k State + colBits T + timeBits T)) + (appendCode (aWidth (slotBits k State) (colBits T) (timeBits T) x n T) + ([], List.range + (aWidth (slotBits k State) (colBits T) (timeBits T) x n T)) + (compCode (aWidth (slotBits k State) (colBits T) (timeBits T) x n T) + (tableCircuit tm).code + ([], (List.range (4 + slotBits k State + 14)).map + (keyWire (slotBits k State) (colBits T) (timeBits T))))) + (fieldGates (slotBits k State) (colBits T) (timeBits T) x n T, + List.range (aWidth (slotBits k State) (colBits T) (timeBits T) x n T))) /-- **The code of the decoder circuit is `decoderCode`.** -/ theorem code_decoder {x : List Bool} {n T : ℕ} @@ -133,16 +136,6 @@ theorem code_decoder {x : List Bool} {n T : ℕ} /-! ### Size -/ -/-- `2 ^ Nat.size X ≤ 2 X + 1`. -/ -theorem two_pow_size_le (X : ℕ) : 2 ^ Nat.size X ≤ 2 * X + 1 := by - rcases Nat.eq_zero_or_pos X with rfl | hX - · simp - · obtain ⟨m, hm⟩ : ∃ m, Nat.size X = m + 1 := ⟨Nat.size X - 1, by - have := Nat.size_pos.2 hX; omega⟩ - have : ¬ X < 2 ^ m := fun h => by have := Nat.size_le.2 h; omega - rw [hm, Nat.pow_succ] - omega - theorem size_outputCircuit_le (s cb tb : ℕ) (x : List Bool) (n T : ℕ) : (outputCircuit s cb tb x n T).size ≤ 3 * (2 + (s + cb + tb) + 1) * 63 := by simp only [outputCircuit, Circuit.size_pi, Formula.size_toCircuit] @@ -160,7 +153,7 @@ theorem size_decoder_le {x : List Bool} {n T : ℕ} (hm : Nat.size x.length ≤ slotBits k State + colBits T + timeBits T) : (decoder (n := n) tm hm).size ≤ (204 * slotBits k State + 600 + (tableCircuit tm).size) * (colBits T + timeBits T + x.length + 1) := by - have hX := two_pow_size_le x.length + have hX := Nat.two_pow_size_le x.length have hpow : 2 ^ (Nat.size x.length + 1) = 2 * 2 ^ Nat.size x.length := Nat.pow_succ' have hout := size_outputCircuit_le (slotBits k State) (colBits T) (timeBits T) x n T rw [decoder, Circuit.size_comp, Circuit.size_comp, middleCircuit, Circuit.size_append, @@ -198,37 +191,6 @@ theorem outBit_congr {s cb tb : ℕ} {ts ts' sel sel' : ℕ → Bool} · simp [hq] · rfl -/-- An entry of a concatenation of lists of a common length `m`. -/ -theorem getElem?_flatMap_of_length {α β : Type} {f : α → List β} {m : ℕ} - (hf : ∀ a, (f a).length = m) (L : List α) (o : ℕ) (ho : o < L.length * m) : - (L.flatMap f)[o]? = (f (L[o / m]'(Nat.div_lt_of_lt_mul (by rwa [Nat.mul_comm]))))[o % m]? := by - induction L generalizing o with - | nil => simp at ho - | cons a L ih => - have hm : 0 < m := by - rcases Nat.eq_zero_or_pos m with rfl | hm - · simp at ho - · exact hm - simp only [List.flatMap_cons] - by_cases hlt : o < m - · rw [List.getElem?_append_left (by rwa [hf])] - have h₁ : o / m = 0 := Nat.div_eq_of_lt hlt - have h₂ : o % m = o := Nat.mod_eq_of_lt hlt - simp only [h₁, h₂, List.getElem_cons_zero] - · rw [List.getElem?_append_right (by rw [hf]; omega), hf, - ih (o - m) (by - simp only [List.length_cons, Nat.succ_mul] at ho - omega)] - have h₁ : (o - m) / m = o / m - 1 := by - have := Nat.sub_mul_div o m 1 - simpa using this - have h₂ : (o - m) % m = o % m := (Nat.mod_eq_sub_mod (by omega)).symm - have h₃ : 1 ≤ o / m := (Nat.le_div_iff_mul_le hm).2 (by omega) - simp only [h₁, h₂] - congr 2 - rw [List.getElem_cons] - simp [show o / m ≠ 0 by omega] - /-- The values of the wires of the field and constant stages. -/ theorem eval_middle_field {x : List Bool} {n T : ℕ} (hm : Nat.size x.length ≤ slotBits k State + colBits T + timeBits T) (idx : ℕ) @@ -288,12 +250,13 @@ theorem ofFn_eval_decoder {x : List Bool} {n T : ℕ} (hn : n < 2 * T) (hx : x.l intro o rw [List.getElem?_ofFn] by_cases ho : o < 3 * (2 + (slotBits k State + colBits T + timeBits T) + 1) - · rw [dite_eq_left ho, getElem?_flatMap_of_length (m := 2 + (slotBits k State + colBits T + - timeBits T) + 1) (fun _ => by simp [litBits]) _ o (by simp [hlen]; omega)] - have hr : o / (2 + (slotBits k State + colBits T + timeBits T) + 1) < 3 := + · have hr : o / (2 + (slotBits k State + colBits T + timeBits T) + 1) < 3 := Nat.div_lt_of_lt_mul (by rwa [Nat.mul_comm]) have hi : o % (2 + (slotBits k State + colBits T + timeBits T) + 1) < 2 + (slotBits k State + colBits T + timeBits T) + 1 := Nat.mod_lt _ (by omega) + rw [dite_eq_left ho] + conv_rhs => rw [← Nat.div_add_mod' o (2 + (slotBits k State + colBits T + timeBits T) + 1)] + rw [List.getElem?_flatMap_mul_add _ _ _ (fun _ _ => by simp [litBits]) (by simpa [hlen]) hi] rw [List.getElem_map, getElem?_litBits _ _ hi] congr 1 -- the left side diff --git a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Circuit.lean b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Circuit.lean index 115439690..ce96d301a 100644 --- a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Circuit.lean +++ b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Circuit.lean @@ -110,28 +110,12 @@ def litBits (w : ℕ) (l : Literal ℕ) : List Bool := /-- The bit `i` of a bit string, or `false`. -/ def bitAt {m : ℕ} (b : BitString m) (i : ℕ) : Bool := if h : i < m then b ⟨i, h⟩ else false -/-- The number with the first `w` bits of `f`, least significant first. -/ -def bitsVal (f : ℕ → Bool) : ℕ → ℕ - | 0 => 0 - | w + 1 => bitsVal f w + 2 ^ w * (f w).toNat - -theorem bitsVal_testBit (v w : ℕ) : bitsVal (fun i => v.testBit i) w = v % 2 ^ w := by - induction w with - | zero => simp [bitsVal, Nat.mod_one] - | succ w ih => rw [bitsVal, ih, mod_two_pow_succ] - -theorem bitsVal_congr {f g : ℕ → Bool} {w : ℕ} (h : ∀ i < w, f i = g i) : - bitsVal f w = bitsVal g w := by - induction w with - | zero => rfl - | succ w ih => rw [bitsVal, bitsVal, ih fun i hi => h i (by omega), h w (by omega)] - /-- The key of the input bits of the constant stage: the clause and tag bits, `s` slot bits, and the fourteen comparison chains. -/ def Key.ofBits (s : ℕ) {m : ℕ} (b : BitString m) : Key where j := (bitAt b 0).toNat + 2 * (bitAt b 1).toNat tag := (bitAt b 2).toNat + 2 * (bitAt b 3).toNat - slot := bitsVal (fun i => bitAt b (4 + i)) s + slot := Nat.ofBits fun i : Fin s => bitAt b (4 + i) c0 := bitAt b (4 + s) cGt1 := bitAt b (4 + s + 3) cGtN := bitAt b (4 + s + 4) @@ -461,7 +445,7 @@ theorem fieldVal_carryC (idx : ℕ) {q : ℕ} (hq : q ≤ s + cb + tb) : rw [hK] have := Nat.mod_lt (colOf s cb (idx / 16)) (Nat.two_pow_pos q) rw [Bool.eq_iff_iff] - simp + simp only [Bool.and_true, Bool.or_eq_true, decide_eq_true_eq, or_iff_right_iff_imp] omega theorem fieldVal_borrowT (idx : ℕ) (hidx : idx < 2 ^ (4 + (s + cb + tb))) {q : ℕ} @@ -543,8 +527,9 @@ theorem eval_outTemplate_wireNat {z : ℕ → Bool} {idx : ℕ} (hidx : idx < 2 decide_false, Bool.false_and] · simp [h2, hP, hN] -private theorem bitsVal_two (f : ℕ → Bool) : bitsVal f 2 = (f 0).toNat + 2 * (f 1).toNat := by - simp only [bitsVal]; omega +private theorem ofBits_two (f : Fin 2 → Bool) : Nat.ofBits f = (f 0).toNat + 2 * (f 1).toNat := by + simp only [Nat.ofBits, Fin.foldr_succ, Fin.foldr_zero, Fin.succ_zero_eq_one] + omega /-- The value of the last gate of chain `k`. -/ theorem fieldVal_keyWire_chain (idx : ℕ) {k : ℕ} (hk : k < 14) (hP : 1 ≤ s + cb + tb) : @@ -602,18 +587,16 @@ theorem ofBits_keyWire {idx : ℕ} (hidx : idx < 2 ^ (4 + (s + cb + tb))) (hs : simpa using hch 0 (by omega) simp only [Key.ofBits, Key.ofFields, hc0, hin 0 (by omega), hin 1 (by omega), hin 2 (by omega), hin 3 (by omega)] - rw [bitsVal_congr (g := fun i => (idx / 16).testBit i) (fun i hi => by - rw [hin _ (by omega), show idx / 16 = idx / 2 ^ 4 from rfl, Nat.testBit_div_two_pow, - Nat.add_comm]), bitsVal_testBit] + rw [show (fun i : Fin s => bitAt (fun i : Fin (4 + s + 14) => + fieldVal s cb tb x n T idx (keyWire s cb tb i)) (4 + i)) = + fun i : Fin s => (idx / 16).testBit i from funext fun i => by + rw [hin _ (by omega), show idx / 16 = idx / 2 ^ 4 from rfl, Nat.testBit_div_two_pow, + Nat.add_comm], Nat.ofBits_testBit] have hj : (idx.testBit 0).toNat + 2 * (idx.testBit 1).toNat = idx % 4 := by - have := bitsVal_testBit idx 2 - rw [bitsVal_two] at this - exact this + simpa only [ofBits_two, Fin.val_zero, Fin.val_one, Nat.reducePow] using Nat.ofBits_testBit idx 2 have htag : (idx.testBit 2).toNat + 2 * (idx.testBit 3).toNat = idx / 4 % 4 := by - have := bitsVal_testBit (idx / 4) 2 - rw [bitsVal_two, show idx / 4 = idx / 2 ^ 2 from rfl, Nat.testBit_div_two_pow, - Nat.testBit_div_two_pow] at this - exact this + simpa only [ofBits_two, show idx / 4 = idx / 2 ^ 2 from rfl, Nat.testBit_div_two_pow, + Fin.val_zero, Fin.val_one, Nat.reduceAdd, Nat.reducePow] using Nat.ofBits_testBit (idx / 4) 2 rw [hj, htag] simp only [hch 3 (by omega), hch 4 (by omega), hch 5 (by omega), hch 6 (by omega), hch 7 (by omega), hch 8 (by omega), hch 9 (by omega), hch 2 (by omega), hch 10 (by omega), diff --git a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Shape.lean b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Shape.lean index c3f32dbeb..c5f292af6 100644 --- a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Shape.lean +++ b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Shape.lean @@ -292,7 +292,11 @@ variable {s cb X n T : ℕ} theorem Key.guard_ofFields (j tag p : ℕ) (g : Guard) : (Key.ofFields s cb X n T j tag p).guard g = g.test n T (colOf s cb p) := by cases g <;> simp only [Key.guard, Key.ofFields, Guard.test, numCols] <;> - rw [Bool.eq_iff_iff] <;> simp <;> omega + rw [Bool.eq_iff_iff] <;> + simp only [Bool.not_eq_eq_eq_not, Bool.not_true, decide_eq_false_iff_not, decide_eq_true_eq, + Order.lt_add_one_iff, Order.add_one_le_iff, Bool.and_eq_true, not_lt, Bool.decide_and, + and_congr_left_iff, Bool.not_and, Bool.not_not, Bool.or_eq_true, not_le, Order.lt_one_iff] <;> + omega omit [DecidableEq State] in theorem Key.colOK_ofFields (j tag p : ℕ) (l : Loc k State) : @@ -305,7 +309,10 @@ theorem Key.initBit_ofFields (j tag p : ℕ) (l : Loc k State) : rcases l with l | l | ⟨f, (_ | _), j⟩ | ⟨f, (_ | _), j⟩ <;> try rfl rcases f with _ | i | ⟨i, s⟩ <;> simp only [Key.initBit, Tableau.initBit, Key.colInit, Tableau.colInit, Key.ofFields] <;> - congr 1 <;> rw [Bool.eq_iff_iff] <;> simp <;> omega + congr 1 <;> rw [Bool.eq_iff_iff] <;> + simp only [Bool.and_eq_true, Bool.not_eq_eq_eq_not, Bool.not_true, decide_eq_false_iff_not, + not_lt, decide_eq_true_eq] <;> + omega omit [DecidableEq State] in theorem colSrcOf_eval (l : Loc k State) (cs : ColSel) (c T : ℕ) : @@ -325,11 +332,17 @@ theorem refSrc_eval {t : ℕ} (j tag p : ℕ) (r : Ref k State) : by_cases h : 1 ≤ colOf (slotBits k State) (colBits T) p ∧ colOf (slotBits k State) (colBits T) p ≤ n · have h₁ : (!decide (colOf (slotBits k State) (colBits T) p = 0) && - !decide (n < colOf (slotBits k State) (colBits T) p)) = true := by simp; omega + !decide (n < colOf (slotBits k State) (colBits T) p)) = true := by + simp only [Bool.and_eq_true, Bool.not_eq_eq_eq_not, Bool.not_true, decide_eq_false_iff_not, + not_lt] + omega simp only [h₁, h, ↓reduceIte] rfl · have h₁ : ¬ (!decide (colOf (slotBits k State) (colBits T) p = 0) && - !decide (n < colOf (slotBits k State) (colBits T) p)) = true := by simp; omega + !decide (n < colOf (slotBits k State) (colBits T) p)) = true := by + simp only [Bool.and_eq_true, Bool.not_eq_eq_eq_not, Bool.not_true, decide_eq_false_iff_not, + not_lt, not_and, not_le] + omega simp only [h₁, h, ↓reduceIte, Bool.false_eq_true] rfl · simp only [Key.refSrc, Ref.label, Key.guard_ofFields] @@ -359,12 +372,24 @@ theorem clauseOf_eq_map_shapeOf (x : List Bool) (n T j tag p : ℕ) : by_cases h0 : tag = 0 · simp only [h0, ↓reduceIte] by_cases hp : p < 2 - · have h₁ : (!κ.pGt1) = true := by simp [hκ, Key.ofFields]; omega + · have h₁ : (!κ.pGt1) = true := by + simp only [hκ, Key.ofFields, Nat.div_eq_zero_iff, Nat.pow_eq_zero, OfNat.ofNat_ne_zero, + ne_eq, false_and, false_or, Bool.not_eq_eq_eq_not, Bool.not_true, + decide_eq_false_iff_not, not_lt] + omega have h₂ : (!κ.p0) = decide (p = 1) := by - rw [Bool.eq_iff_iff]; simp [hκ, Key.ofFields]; omega + rw [Bool.eq_iff_iff] + simp only [hκ, Key.ofFields, Nat.div_eq_zero_iff, Nat.pow_eq_zero, OfNat.ofNat_ne_zero, + ne_eq, false_and, false_or, Bool.not_eq_eq_eq_not, Bool.not_true, + decide_eq_false_iff_not, decide_eq_true_eq] + omega simp only [hp, h₁, ↓reduceIte, h₂, map_eval_lift, relabel_tseitinClause] rfl - · have h₁ : ¬ (!κ.pGt1) = true := by simp [hκ, Key.ofFields]; omega + · have h₁ : ¬ (!κ.pGt1) = true := by + simp only [hκ, Key.ofFields, Nat.div_eq_zero_iff, Nat.pow_eq_zero, OfNat.ofNat_ne_zero, + ne_eq, false_and, false_or, Bool.not_eq_eq_eq_not, Bool.not_true, + decide_eq_false_iff_not, not_lt, not_le] + omega simp only [hp, h₁, ↓reduceIte] rfl · by_cases h1 : tag = 1 @@ -373,7 +398,10 @@ theorem clauseOf_eq_map_shapeOf (x : List Bool) (n T j tag p : ℕ) : · have h₁ : (j = 0 ∧ κ.pLtX = true) := ⟨hp.1, by simp [hκ, Key.ofFields, hp.2]⟩ rw [dite_eq_left hp, ite_eq_left h₁] simp [unitClause, Shape.eval, LabelSrc.eval, SignSrc.eval, List.getElem?_eq_getElem hp.2] - · have h₁ : ¬ (j = 0 ∧ κ.pLtX = true) := by simp [hκ, Key.ofFields]; omega + · have h₁ : ¬ (j = 0 ∧ κ.pLtX = true) := by + simp only [hκ, Key.ofFields, Nat.div_eq_zero_iff, Nat.pow_eq_zero, OfNat.ofNat_ne_zero, + ne_eq, false_and, false_or, decide_eq_true_eq, not_and, not_lt] + omega rw [dite_eq_right hp, ite_eq_right h₁] rfl · by_cases h2 : tag = 2 @@ -412,7 +440,10 @@ theorem clauseOf_eq_map_shapeOf (x : List Bool) (n T j tag p : ℕ) : rw [ite_eq_left hp, ite_eq_left h₁] simp [unitClause, Shape.eval, LabelSrc.eval, SignSrc.eval, outLabel, outSrc, posLabel, ColSrc.eval, TimeSrc.eval] - · have h₁ : ¬ (j = 0 ∧ κ.p0 = true) := by simp [hκ, Key.ofFields]; omega + · have h₁ : ¬ (j = 0 ∧ κ.p0 = true) := by + simp only [hκ, Key.ofFields, Nat.div_eq_zero_iff, Nat.pow_eq_zero, + OfNat.ofNat_ne_zero, ne_eq, false_and, false_or, decide_eq_true_eq, not_and] + omega rw [ite_eq_right hp, ite_eq_right h₁] rfl · simp only [hj, ↓reduceDIte] @@ -492,7 +523,10 @@ theorem testBit_sub_one {c : ℕ} (hc : 1 ≤ c) (q : ℕ) : induction q generalizing c with | zero => simp only [Nat.testBit_zero, pow_zero, Nat.mod_one, decide_true, Bool.xor_true] - rw [Bool.eq_iff_iff]; simp; omega + rw [Bool.eq_iff_iff] + simp only [decide_eq_true_eq, Bool.not_eq_eq_eq_not, Bool.not_true, decide_eq_false_iff_not, + Nat.mod_two_not_eq_one] + omega | succ q ih => rw [Nat.testBit_succ, Nat.testBit_succ, Nat.pow_succ', Nat.mod_mul] rcases Nat.even_or_odd c with ⟨d, rfl⟩ | ⟨d, rfl⟩ @@ -516,7 +550,10 @@ theorem testBit_add_one (c q : ℕ) : induction q generalizing c with | zero => simp only [Nat.testBit_zero, pow_zero, Nat.mod_one, Nat.sub_self, decide_true, Bool.xor_true] - rw [Bool.eq_iff_iff]; simp; omega + rw [Bool.eq_iff_iff] + simp only [decide_eq_true_eq, Bool.not_eq_eq_eq_not, Bool.not_true, decide_eq_false_iff_not, + Nat.mod_two_not_eq_one] + omega | succ q ih => rw [Nat.testBit_succ, Nat.testBit_succ, Nat.pow_succ', Nat.mod_mul] have hq := Nat.one_le_two_pow (n := q) diff --git a/Complexity/Computability/Complexity/Reduction/TruthTableVariants.lean b/Complexity/Computability/Complexity/Reduction/TruthTableVariants.lean index bd12a82a6..0e4c8e2a7 100644 --- a/Complexity/Computability/Complexity/Reduction/TruthTableVariants.lean +++ b/Complexity/Computability/Complexity/Reduction/TruthTableVariants.lean @@ -9,6 +9,7 @@ module public import Complexity.Computability.Complexity.Reduction.TruthTable import Complexity.Foundations.Data.List.Filter +import Complexity.Foundations.Data.List.RowMajor import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.List import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Option import Complexity.Computability.Machines.Turing.MultiTape.Complexity.PolyTime.Nat @@ -146,25 +147,6 @@ The composite of `(r, q, d)` from `L` to `B` and `(r', q', d')` from `B` to `C` first-stage query `q (a, i)`, its second-stage queries in a block of `M a ≥ r' (q (a, i))` positions starting at `i * M a`, and evaluates `d'` on each block and `d` on the results. -/ -/-- Indexing a flattened list whose blocks all have length `M`. -/ -private theorem getElem?_flatMap_of_length_eq {ι δ : Type*} (l : List ι) (f : ι → List δ) (M : ℕ) - (hf : ∀ x ∈ l, (f x).length = M) {i j : ℕ} (hi : i < l.length) (hj : j < M) : - (l.flatMap f)[i * M + j]? = (f l[i])[j]? := by - induction l generalizing i with - | nil => simp at hi - | cons x l ih => - have hx := hf x (by simp) - rw [List.flatMap_cons] - cases i with - | zero => - simp only [zero_mul, zero_add, List.getElem_cons_zero] - rw [List.getElem?_append_left (by omega)] - | succ i => - rw [List.getElem?_append_right (by rw [hx]; nlinarith), hx] - have : (i + 1) * M + j - M = i * M + j := by rw [Nat.succ_mul]; omega - rw [this, ih (fun y hy => hf y (by simp [hy])) (by simpa using hi)] - simp - /-- A block of a word of answers. -/ private theorem take_drop_ofFn {n : ℕ} (g : Fin n → Bool) (s c : ℕ) (h : s + c ≤ n) : ((List.ofFn g).drop s).take c = List.ofFn fun j : Fin c => g ⟨s + j, by omega⟩ := by @@ -213,7 +195,7 @@ private theorem ttComp_spec {r : α → ℕ} {q : α × ℕ → β} {d : α × L funext j have hj : j.1 < M a := lt_of_lt_of_le j.2 (hM a i hi) simp only [Set.mem_preimage, ttCompQuery, ttCompQueries] - rw [getElem?_flatMap_of_length_eq _ _ _ hlen (by simpa using hi) hj] + rw [List.getElem?_flatMap_mul_add _ _ _ hlen (by simpa using hi) hj] simp [hj] rw [hblock] cases h : d' (q (a, i), truthTableAnswers (q' ⁻¹' C) r' (q (a, i))) <;> simp [hB, h] diff --git a/Complexity/Foundations/Data/List/RowMajor.lean b/Complexity/Foundations/Data/List/RowMajor.lean index c03ab4023..491b30e8c 100644 --- a/Complexity/Foundations/Data/List/RowMajor.lean +++ b/Complexity/Foundations/Data/List/RowMajor.lean @@ -22,6 +22,8 @@ import Mathlib.Data.List.GetD * `List.flatMap_ofFn_prod`: nested row-major enumeration agrees with the existing product equivalence on finite indices. * `List.getD_flatMap_ofFn`: read a cell at its ordinary flat row-major index. +* `List.getElem?_flatMap_mul_add`: read entry `j` of block `i` of a flattened list whose blocks + all have length `M`. -/ @[expose] public section @@ -61,4 +63,24 @@ theorem getD_flatMap_ofFn {α : Type*} {n d : ℕ} (f : Fin n → Fin d → α) exact (finProdFinEquiv (i, j)).isLt), List.getElem_ofFn] exact congrArg (fun p => f p.1 p.2) (finProdFinEquiv.symm_apply_apply (i, j)) +/-- Indexing a flattened list whose blocks all have length `M`: position `i * M + j` is entry `j` +of block `i`. -/ +theorem getElem?_flatMap_mul_add {ι δ : Type*} (l : List ι) (f : ι → List δ) (M : ℕ) + (hf : ∀ x ∈ l, (f x).length = M) {i j : ℕ} (hi : i < l.length) (hj : j < M) : + (l.flatMap f)[i * M + j]? = (f l[i])[j]? := by + induction l generalizing i with + | nil => simp at hi + | cons x l ih => + have hx := hf x (by simp) + rw [List.flatMap_cons] + cases i with + | zero => + simp only [Nat.zero_mul, Nat.zero_add, List.getElem_cons_zero] + rw [List.getElem?_append_left (by omega)] + | succ i => + rw [List.getElem?_append_right (by rw [hx, Nat.succ_mul]; omega), hx] + have : (i + 1) * M + j - M = i * M + j := by rw [Nat.succ_mul]; omega + rw [this, ih (fun y hy => hf y (by simp [hy])) (by simpa using hi)] + simp + end List diff --git a/ComplexityTests/TableauDecoder.lean b/ComplexityTests/TableauDecoder.lean index 7dfe19623..84bda62d8 100644 --- a/ComplexityTests/TableauDecoder.lean +++ b/ComplexityTests/TableauDecoder.lean @@ -13,7 +13,8 @@ The field stage is evaluated on concrete clause numbers with `decide`: one slot bits, one block bit, `x = [true, false]`, `n = 1` and `T = 1`, so `8` inputs and `75` gates. The comparison chains, the borrows and carries, and the lookup of `x` give the expected values. The constant stage depends on the noncomputable numbering of positions (`Loc.slot`), so the whole -decoder is checked through its correctness theorem instead. +decoder is checked on a concrete machine through its correctness theorem: the clauses fixing the +inputs and the output have explicit literals, which the decoder emits bit by bit. -/ namespace ComplexityTests.TableauDecoder @@ -47,6 +48,31 @@ example : (List.range 83).all (fun w => wire 100 w == fieldVal 1 2 1 [true, fals example : (List.range 83).all (fun w => wire 211 w == fieldVal 1 2 1 [true, false] 1 1 211 w) := by decide +/-- A machine without work tapes that halts at once with output `[true]`. -/ +private def halt : Turing.MultiTapeTM 0 Bool Unit := + Turing.MultiTapeTM.ofTr () fun _ _ _ => + { inputTape := 0, workTapes := Fin.elim0, output := some true, state := none } + +-- The decoder of `halt` for `x = [true, false]`, `n = 2` and `T = 2`. Clause numbers `4` and `20` +-- (tag `1`) fix the inputs `0` and `1` to `x[0] = true` and `x[1] = false`: each of their three +-- literals is the label `inputLabel i = 1 + 4 i` in `labelBits` bits, then the sign `x[i]`. Clause +-- number `12` (tag `3`) fixes the output wire to `true`. +example : ∃ c : Circuit signature (clauseBits 0 Unit 2) (3 * (labelBits 0 Unit 2 + 1)), + List.ofFn (c.eval interpretation fun i => (4 : ℕ).testBit i) = + [(1, true), (1, true), (1, true)].flatMap (litBits (labelBits 0 Unit 2)) ∧ + List.ofFn (c.eval interpretation fun i => (20 : ℕ).testBit i) = + [(5, false), (5, false), (5, false)].flatMap (litBits (labelBits 0 Unit 2)) ∧ + List.ofFn (c.eval interpretation fun i => (12 : ℕ).testBit i) = + (unitClause (outLabel 0 Unit 2, true)).flatMap (litBits (labelBits 0 Unit 2)) := by + obtain ⟨c, -, -, hc⟩ := exists_decoder halt [true, false] (n := 2) (T := 2) (by norm_num) + (by norm_num) + have hbits : 2 ^ 5 ≤ 2 ^ clauseBits 0 Unit 2 := Nat.pow_le_pow_right (by norm_num) (by + have := one_le_slotBits (k := 0) (State := Unit) + simp only [clauseBits] + omega) + refine ⟨c, ?_, ?_, ?_⟩ <;> rw [hc _ (by omega)] <;> + simp [clauseAt, clauseOf, unitClause, inputLabel] + variable {k : ℕ} {State : Type} [Fintype State] [DecidableEq State] (tm : Turing.MultiTapeTM k Bool State) From fc5f5bccc5723011acc21d1250b79db86ab42d22 Mon Sep 17 00:00:00 2001 From: Samuel Giuseppe Schlesinger Date: Sun, 1 Nov 2026 15:03:03 -0500 Subject: [PATCH 8/8] refactor(Foundations): move the decoder's bit arithmetic to Nat.Bitwise `testBit_sub_one`, `testBit_add_one`, `testBit_tag_add` and `testBit_fields` are plain natural-number facts. Move them to `Foundations/Data/Nat/Bitwise/Carry.lean` as `Nat.testBit_sub_one_eq_xor`, `Nat.testBit_add_one_eq_xor`, `Nat.testBit_add_four_mul` and `Nat.testBit_add_two_pow_mul_add_two_pow_mul` (core's `Nat.testBit_add_one` is a different fact), and credit Christian Reitwiessner in `RowMajor` for the moved block-indexing lemma. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015DLg8kwPs8yi7ePPdtoNvG --- Complexity.lean | 1 + .../Boolean/Tableau/Decoder/Shape.lean | 82 ++------------ .../Foundations/Data/List/RowMajor.lean | 4 +- .../Foundations/Data/Nat/Bitwise/Carry.lean | 101 ++++++++++++++++++ 4 files changed, 111 insertions(+), 77 deletions(-) create mode 100644 Complexity/Foundations/Data/Nat/Bitwise/Carry.lean diff --git a/Complexity.lean b/Complexity.lean index c78f97d18..77459f26a 100644 --- a/Complexity.lean +++ b/Complexity.lean @@ -3166,6 +3166,7 @@ public import Complexity.Foundations.Data.Nat.ArithmeticProgression public import Complexity.Foundations.Data.Nat.Asymptotics public import Complexity.Foundations.Data.Nat.BinarySize public import Complexity.Foundations.Data.Nat.Bits +public import Complexity.Foundations.Data.Nat.Bitwise.Carry public import Complexity.Foundations.Data.Nat.Bitwise.HighestDiff public import Complexity.Foundations.Data.Nat.Bitwise.Multiplication public import Complexity.Foundations.Data.Nat.Bitwise.PackedCount diff --git a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Shape.lean b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Shape.lean index c5f292af6..4da1b5af2 100644 --- a/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Shape.lean +++ b/Complexity/Computability/Circuit/Boolean/Tableau/Decoder/Shape.lean @@ -8,6 +8,7 @@ module -- upstream: candidate public import Complexity.Computability.Circuit.Boolean.Tableau.Succinct +import Complexity.Foundations.Data.Nat.Bitwise.Carry /-! # The clauses of the succinct tableau as symbolic shapes @@ -516,75 +517,6 @@ def outBit (s cb tb : ℕ) (ts sel : ℕ → Bool) (p c t T : ℕ) (lk : Bool) ( else if i < 2 + (s + cb + tb) then payTerm ts sel s cb p c t T (i - 2) else sel 9 || (sel 10 && lk) -/-- Bit `q` of `c - 1`, for `1 ≤ c`: bit `q` of `c`, flipped when the lower bits of `c` are -zero. -/ -theorem testBit_sub_one {c : ℕ} (hc : 1 ≤ c) (q : ℕ) : - (c - 1).testBit q = (c.testBit q ^^ decide (c % 2 ^ q = 0)) := by - induction q generalizing c with - | zero => - simp only [Nat.testBit_zero, pow_zero, Nat.mod_one, decide_true, Bool.xor_true] - rw [Bool.eq_iff_iff] - simp only [decide_eq_true_eq, Bool.not_eq_eq_eq_not, Bool.not_true, decide_eq_false_iff_not, - Nat.mod_two_not_eq_one] - omega - | succ q ih => - rw [Nat.testBit_succ, Nat.testBit_succ, Nat.pow_succ', Nat.mod_mul] - rcases Nat.even_or_odd c with ⟨d, rfl⟩ | ⟨d, rfl⟩ - · have hd : 1 ≤ d := by omega - have h₁ : (d + d - 1) / 2 = d - 1 := by omega - have h₂ : (d + d) / 2 = d := by omega - have h₃ : (d + d) % 2 = 0 := by omega - rw [h₁, h₂, ih hd, h₃] - congr 2 - rw [Nat.zero_add, Nat.mul_eq_zero] - simp - · have h₁ : (2 * d + 1 - 1) / 2 = d := by omega - have h₂ : (2 * d + 1) / 2 = d := by omega - have h₃ : (2 * d + 1) % 2 = 1 := by omega - rw [h₁, h₂, h₃] - simp - -/-- Bit `q` of `c + 1`: bit `q` of `c`, flipped when the lower bits of `c` are all one. -/ -theorem testBit_add_one (c q : ℕ) : - (c + 1).testBit q = (c.testBit q ^^ decide (c % 2 ^ q = 2 ^ q - 1)) := by - induction q generalizing c with - | zero => - simp only [Nat.testBit_zero, pow_zero, Nat.mod_one, Nat.sub_self, decide_true, Bool.xor_true] - rw [Bool.eq_iff_iff] - simp only [decide_eq_true_eq, Bool.not_eq_eq_eq_not, Bool.not_true, decide_eq_false_iff_not, - Nat.mod_two_not_eq_one] - omega - | succ q ih => - rw [Nat.testBit_succ, Nat.testBit_succ, Nat.pow_succ', Nat.mod_mul] - have hq := Nat.one_le_two_pow (n := q) - rcases Nat.even_or_odd c with ⟨d, rfl⟩ | ⟨d, rfl⟩ - · have h₁ : (d + d + 1) / 2 = d := by omega - have h₂ : (d + d) / 2 = d := by omega - have h₃ : (d + d) % 2 = 0 := by omega - rw [h₁, h₂, h₃] - have : ¬ (2 * (d % 2 ^ q) = 2 * 2 ^ q - 1) := by omega - simp [this] - · have h₁ : (2 * d + 1 + 1) / 2 = d + 1 := by omega - have h₂ : (2 * d + 1) / 2 = d := by omega - have h₃ : (2 * d + 1) % 2 = 1 := by omega - rw [h₁, h₂, h₃, ih d] - congr 2 - exact propext ⟨fun h => by omega, fun h => by omega⟩ - -/-- The bits of a tag and a payload. -/ -theorem testBit_tag_add {tag : ℕ} (htag : tag < 4) (pay i : ℕ) : - (tag + 4 * pay).testBit i = if i < 2 then tag.testBit i else pay.testBit (i - 2) := by - rw [Nat.add_comm, show 4 = 2 ^ 2 from rfl, Nat.testBit_two_pow_mul_add _ htag] - -/-- The bits of three fields of widths `s` and `cb` and a high field. -/ -theorem testBit_fields {s cb sl C Tm : ℕ} (hsl : sl < 2 ^ s) (hC : C < 2 ^ cb) (q : ℕ) : - (sl + 2 ^ s * (C + 2 ^ cb * Tm)).testBit q = - if q < s then sl.testBit q else if q < s + cb then C.testBit (q - s) - else Tm.testBit (q - s - cb) := by - rw [Nat.add_comm, Nat.testBit_two_pow_mul_add _ hsl, Nat.add_comm C, - Nat.testBit_two_pow_mul_add _ hC] - split_ifs <;> first | rfl | omega - /-- A symbolic label whose slot and column fit their fields. -/ def LabelSrc.InRange (s cb c T : ℕ) : LabelSrc → Prop | .pos sl cs _ => sl < 2 ^ s ∧ cs.eval c T < 2 ^ cb @@ -613,10 +545,10 @@ private theorem colTerm_eq {c T q sl : ℕ} (cs : ColSrc) (ts : TimeSrc) (t0 : B Bool.and_self, Bool.false_and, Bool.or_self, Nat.succ_ne_self, Nat.zero_testBit, Bool.or_false, Bool.true_and, Bool.and_true, Bool.false_or, Bool.or_true] all_goals first - | rw [testBit_add_one] + | rw [Nat.testBit_add_one_eq_xor] | (by_cases hc : c = 0 · simp [hc] - · rw [testBit_sub_one (by omega)]; simp [hc]) + · rw [Nat.testBit_sub_one_eq_xor (by omega)]; simp [hc]) private theorem timeTerm_eq {t T q sl : ℕ} (cs : ColSrc) (ts : TimeSrc) (c0 : Bool) (sg : SignSrc) : @@ -628,7 +560,7 @@ private theorem timeTerm_eq {t T q sl : ℕ} (cs : ColSrc) (ts : TimeSrc) (c0 : all_goals by_cases ht : t = 0 · simp [ht] - · rw [testBit_sub_one (by omega)]; simp [ht] + · rw [Nat.testBit_sub_one_eq_xor (by omega)]; simp [ht] /-- **The output bits of a shape are the bits of its literal**: its label below `2 + P` and its sign at `2 + P`, for `P = s + cb + tb`, at least one slot bit, a looked-up bit that is the bit of @@ -639,7 +571,7 @@ theorem outBit_shape {s cb tb p c t T : ℕ} {x : List Bool} {lk : Bool} (hs : 1 outBit s cb tb (tagSlot sh) (selOf (decide (c = 0)) (decide (t = 0)) sh) p c t T lk i = if i < 2 + (s + cb + tb) then (sh.1.eval s cb p c t T).testBit i else sh.2.eval x p := by obtain ⟨l, sg⟩ := sh - rw [LabelSrc.eval_eq, testBit_tag_add l.tag_lt] + rw [LabelSrc.eval_eq, Nat.testBit_add_four_mul l.tag_lt] unfold outBit by_cases h2 : i < 2 · simp [h2, tagSlot, show i < 2 + (s + cb + tb) by omega] @@ -672,9 +604,9 @@ theorem outBit_shape {s cb tb p c t T : ℕ} {x : List Bool} {lk : Bool} (hs : 1 all_goals by_cases hc : c = 0 · simp [hc] - · rw [testBit_sub_one (by omega)]; simp [hc] + · rw [Nat.testBit_sub_one_eq_xor (by omega)]; simp [hc] · obtain ⟨hsl, hC⟩ := hR - rw [LabelSrc.payload, testBit_fields hsl hC] + rw [LabelSrc.payload, Nat.testBit_add_two_pow_mul_add_two_pow_mul hsl hC] have hcol := colTerm_eq (c := c) (T := T) (q := q - s) (sl := sl) cs ts (decide (t = 0)) sg have htime := timeTerm_eq (t := t) (T := T) (q := q - s - cb) (sl := sl) cs ts (decide (c = 0)) sg diff --git a/Complexity/Foundations/Data/List/RowMajor.lean b/Complexity/Foundations/Data/List/RowMajor.lean index 491b30e8c..e7ffdc992 100644 --- a/Complexity/Foundations/Data/List/RowMajor.lean +++ b/Complexity/Foundations/Data/List/RowMajor.lean @@ -1,7 +1,7 @@ /- -Copyright (c) 2026 Erik Demaine. All rights reserved. +Copyright (c) 2026 Erik Demaine, Christian Reitwiessner. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Erik Demaine +Authors: Erik Demaine, Christian Reitwiessner -/ module diff --git a/Complexity/Foundations/Data/Nat/Bitwise/Carry.lean b/Complexity/Foundations/Data/Nat/Bitwise/Carry.lean new file mode 100644 index 000000000..0b014bc97 --- /dev/null +++ b/Complexity/Foundations/Data/Nat/Bitwise/Carry.lean @@ -0,0 +1,101 @@ +/- +Copyright (c) 2026 Samuel Schlesinger. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Samuel Schlesinger +-/ + +module +-- upstream: candidate + +public import Complexity.Init +public import Mathlib.Data.Nat.Bitwise + +/-! +# Bits of successors, predecessors and packed fields + +Incrementing a number flips bit `q` exactly when all lower bits are one, and decrementing a +positive number flips it exactly when all lower bits are zero. A number packed from fields of +fixed widths has, in each window of bits, the bits of the corresponding field. + +## Main results + +* `Nat.testBit_add_one_eq_xor`, `Nat.testBit_sub_one_eq_xor`: the bits of `c + 1` and `c - 1`. +* `Nat.testBit_add_four_mul`: the bits of a two-bit tag followed by a payload. +* `Nat.testBit_add_two_pow_mul_add_two_pow_mul`: the bits of three packed fields. +-/ + +@[expose] public section + +namespace Nat + +/-- Bit `q` of `c - 1`, for `1 ≤ c`: bit `q` of `c`, flipped when the lower bits of `c` are +zero. -/ +theorem testBit_sub_one_eq_xor {c : ℕ} (hc : 1 ≤ c) (q : ℕ) : + (c - 1).testBit q = (c.testBit q ^^ decide (c % 2 ^ q = 0)) := by + induction q generalizing c with + | zero => + simp only [Nat.testBit_zero, pow_zero, Nat.mod_one, decide_true, Bool.xor_true] + rw [Bool.eq_iff_iff] + simp only [decide_eq_true_eq, Bool.not_eq_eq_eq_not, Bool.not_true, decide_eq_false_iff_not, + Nat.mod_two_not_eq_one] + omega + | succ q ih => + rw [Nat.testBit_succ, Nat.testBit_succ, Nat.pow_succ', Nat.mod_mul] + rcases Nat.even_or_odd c with ⟨d, rfl⟩ | ⟨d, rfl⟩ + · have hd : 1 ≤ d := by omega + have h₁ : (d + d - 1) / 2 = d - 1 := by omega + have h₂ : (d + d) / 2 = d := by omega + have h₃ : (d + d) % 2 = 0 := by omega + rw [h₁, h₂, ih hd, h₃] + congr 2 + rw [Nat.zero_add, Nat.mul_eq_zero] + simp + · have h₁ : (2 * d + 1 - 1) / 2 = d := by omega + have h₂ : (2 * d + 1) / 2 = d := by omega + have h₃ : (2 * d + 1) % 2 = 1 := by omega + rw [h₁, h₂, h₃] + simp + +/-- Bit `q` of `c + 1`: bit `q` of `c`, flipped when the lower bits of `c` are all one. -/ +theorem testBit_add_one_eq_xor (c q : ℕ) : + (c + 1).testBit q = (c.testBit q ^^ decide (c % 2 ^ q = 2 ^ q - 1)) := by + induction q generalizing c with + | zero => + simp only [Nat.testBit_zero, pow_zero, Nat.mod_one, Nat.sub_self, decide_true, Bool.xor_true] + rw [Bool.eq_iff_iff] + simp only [decide_eq_true_eq, Bool.not_eq_eq_eq_not, Bool.not_true, decide_eq_false_iff_not, + Nat.mod_two_not_eq_one] + omega + | succ q ih => + rw [Nat.testBit_succ, Nat.testBit_succ, Nat.pow_succ', Nat.mod_mul] + have hq := Nat.one_le_two_pow (n := q) + rcases Nat.even_or_odd c with ⟨d, rfl⟩ | ⟨d, rfl⟩ + · have h₁ : (d + d + 1) / 2 = d := by omega + have h₂ : (d + d) / 2 = d := by omega + have h₃ : (d + d) % 2 = 0 := by omega + rw [h₁, h₂, h₃] + have : ¬ (2 * (d % 2 ^ q) = 2 * 2 ^ q - 1) := by omega + simp [this] + · have h₁ : (2 * d + 1 + 1) / 2 = d + 1 := by omega + have h₂ : (2 * d + 1) / 2 = d := by omega + have h₃ : (2 * d + 1) % 2 = 1 := by omega + rw [h₁, h₂, h₃, ih d] + congr 2 + exact propext ⟨fun h => by omega, fun h => by omega⟩ + +/-- The bits of a tag and a payload. -/ +theorem testBit_add_four_mul {tag : ℕ} (htag : tag < 4) (pay i : ℕ) : + (tag + 4 * pay).testBit i = if i < 2 then tag.testBit i else pay.testBit (i - 2) := by + rw [Nat.add_comm, show 4 = 2 ^ 2 from rfl, Nat.testBit_two_pow_mul_add _ htag] + +/-- The bits of three fields of widths `s` and `cb` and a high field. -/ +theorem testBit_add_two_pow_mul_add_two_pow_mul {s cb sl C Tm : ℕ} (hsl : sl < 2 ^ s) + (hC : C < 2 ^ cb) (q : ℕ) : + (sl + 2 ^ s * (C + 2 ^ cb * Tm)).testBit q = + if q < s then sl.testBit q else if q < s + cb then C.testBit (q - s) + else Tm.testBit (q - s - cb) := by + rw [Nat.add_comm, Nat.testBit_two_pow_mul_add _ hsl, Nat.add_comm C, + Nat.testBit_two_pow_mul_add _ hC] + split_ifs <;> first | rfl | omega + +end Nat