ThresholdNet
| File | lean/phase5/ThresholdNet.lean |
|---|---|
| SHA-256 (matches the audit) | 15c36f444487a1bdd02793ae1cee53b0472a0c5d99054c52440427cc3a63e16b |
| Lines | 780 |
| Imports | SharedCircuit, FiniteNet |
Paper claims citing this module
- Appendix C: Shared shallow operations: candidate tests, binary index, recurrent update, threshold realisation
- Appendix C, “Realisation with fixed-scale threshold gates”: Compiler from formulas to {0,1}-weight threshold networks: ≤2d+2 layers, ≤(2d+1)S+K gates, widths ≤ S+n
Declarations (72)
- def
ThresholdNet.orGate - def
ThresholdNet.constGate - theorem
ThresholdNet.sum_range_pos - theorem
ThresholdNet.orGate_eval - theorem
ThresholdNet.constGate_eval - theorem
ThresholdNet.orGate_valid - theorem
ThresholdNet.constGate_valid - theorem
ThresholdNet.orGate_fanin - theorem
ThresholdNet.constGate_fanin - def
ThresholdNet.lvlAlg - def
ThresholdNet.lvl - def
ThresholdNet.lvlL - theorem
ThresholdNet.lvlL_eq - theorem
ThresholdNet.lvl_fhom - theorem
ThresholdNet.lvl_le - theorem
ThresholdNet.lvlL_le - theorem
ThresholdNet.run_take - theorem
ThresholdNet.run_getD - theorem
ThresholdNet.rd_take - def
ThresholdNet.vl - def
ThresholdNet.lv - def
ThresholdNet.gate1 - def
ThresholdNet.gateT - def
ThresholdNet.gateT._sparseCasesOn_1 - def
ThresholdNet.layerAt - def
ThresholdNet.ys - theorem
ThresholdNet.lvlAlg_pos - theorem
ThresholdNet.lv_pos - theorem
ThresholdNet.getD_eq_getElem' - theorem
ThresholdNet.vl_eq - theorem
ThresholdNet.lv_eq - theorem
ThresholdNet.rdv_take - theorem
ThresholdNet.rdl_take - theorem
ThresholdNet.evalLayer_at - theorem
ThresholdNet.listMax_ge - theorem
ThresholdNet.vl_inp - theorem
ThresholdNet.vl_cst - theorem
ThresholdNet.vl_neg - theorem
ThresholdNet.vl_disj - theorem
ThresholdNet.vl_conj - theorem
ThresholdNet.lv_inp - theorem
ThresholdNet.lv_cst - theorem
ThresholdNet.lv_neg - theorem
ThresholdNet.lv_disj - theorem
ThresholdNet.lv_conj - theorem
ThresholdNet.child_lv - theorem
ThresholdNet.decide_or1 - theorem
ThresholdNet.ys_spec - def
ThresholdNet.progOf - def
ThresholdNet.outsOf - def
ThresholdNet.dmax - def
ThresholdNet.nLayers - def
ThresholdNet.compileF - theorem
ThresholdNet.evalNet_append - theorem
ThresholdNet.evalNet_layers - theorem
ThresholdNet.outs_range - theorem
ThresholdNet.outs_length - theorem
ThresholdNet.prog_length - theorem
ThresholdNet.valAlg_fhom - theorem
ThresholdNet.outs_val - theorem
ThresholdNet.outs_lv - theorem
ThresholdNet.compileF_eval - theorem
ThresholdNet.compileF_eval_out - theorem
ThresholdNet.compileF_valid - theorem
ThresholdNet.compileF_wired - theorem
ThresholdNet.compileF_length - theorem
ThresholdNet.cs_le_prog - theorem
ThresholdNet.compileF_bounded - def
ThresholdNet.gateCount - theorem
ThresholdNet.compileF_gates - theorem
ThresholdNet.compileF_spec - theorem
ThresholdNet.compileF_fixed_grid
Comment and keyword shading is a reading aid only. It is not a verification marker; the evidence is the build record on the Build page.
1import SharedCircuit2import FiniteNet34/- Phase 5, task 2: compiling bounded-depth Boolean formulas into the paper's5 fixed-scale literal-threshold networks (the existing AC0.Gate / Layer / evalNet6 model), in the direction opposite to Circuit.compileNet. Written by Claude7 (10 October 2026). Lean 4.26.0, core/Std only.89 Gates used (W = 1, i.e. the fixed grid with denominator D = 1):10 * OR of literals: coefficients in {0,1}, one negation flag, threshold 1;11 * constants: no inputs, threshold 0 (true) or 1 (false).12 AND is never realised by a threshold equal to its fan-in: an AND node takes two13 layers, OR of negated children (= NOT AND) followed by OR of the negated result14 (De Morgan). NOT costs one layer (an OR of one negated literal).1516 Construction. The formulas are first laid out as one shared gate program17 (SharedCircuit.emitL, one gate per formula node). Every layer has one wire per18 program gate (width S); gate g is computed at layer lvl(g) (inputs and constants19 at layer 1, NOT and OR one layer above their children, AND two layers above),20 and identity buffers carry each value to all later layers. A final layer copies21 the K output gates to wires 0, …, K−1. Layer 1 reads the n input wires only22 through single-literal buffers of input i (fan-in i + 1 ≤ n).23 Proved for the one compiled network `compileF n cs`: exact outputs on every input,24 NetValid 1, Wired n, exactly 2D + 2 layers for depth bound D, width and fan-in25 ≤ S + n, and gate count (2D+1)·S + K, where S = Σ size(c_k). -/2627namespace NimAudit28namespace ThresholdNet2930open AC0 AC0.Circ Shared FiniteNet3132/-! ## The two gate shapes -/3334/-- OR of the literals (z j XOR ng) over the wires j < K with sel j. -/35def orGate (K : Nat) (sel : Nat → Bool) (ng : Bool) : Gate :=36 ⟨(List.range K).map (fun j => if sel j then 1 else 0), (List.range K).map (fun _ => ng), 1⟩3738/-- Constant gate (no inputs). -/39def constGate (b : Bool) : Gate := ⟨[], [], if b then 0 else 1⟩4041theorem sum_range_pos (f : Nat → Nat) : ∀ K, 1 ≤ ((List.range K).map f).sum ↔ ∃ j < K, 1 ≤ f j42 | 0 => by simp43 | K + 1 => by44 have ih := sum_range_pos f K45 rw [List.range_succ, List.map_append, List.sum_append_nat]46 simp only [List.map_cons, List.map_nil, List.sum_cons, List.sum_nil, Nat.add_zero]47 constructor48 · intro h49 by_cases h1 : 1 ≤ f K50 · exact ⟨K, by omega, h1⟩51 · obtain ⟨j, hj, hf⟩ := ih.1 (by omega)52 exact ⟨j, by omega, hf⟩53 · rintro ⟨j, hj, hf⟩54 by_cases hjK : j = K55 · subst hjK; omega56 · have := ih.2 ⟨j, by omega, hf⟩; omega5758theorem orGate_eval (K : Nat) (sel : Nat → Bool) (ng : Bool) (z : Nat → Bool) :59 (orGate K sel ng).eval z = decide (∃ j < K, sel j = true ∧ xor (z j) ng = true) := by60 unfold Gate.eval Gate.wsum Gate.psum Gate.fanin orGate61 simp only [List.length_map, List.length_range]62 apply Bool.eq_iff_iff.mpr63 simp only [decide_eq_true_iff]64 have e : ∀ j ∈ List.range K, (Gate.coeff ⟨(List.range K).map (fun j => if sel j then 1 else 0),65 (List.range K).map (fun _ => ng), 1⟩ j) * (if Gate.lit ⟨(List.range K).map (fun j => if sel j then 1 else 0),66 (List.range K).map (fun _ => ng), 1⟩ z j then 1 else 0) =67 (if sel j = true ∧ xor (z j) ng = true then 1 else 0) := by68 intro j hj69 have hj' := List.mem_range.mp hj70 simp only [Gate.coeff, Gate.lit, List.getD_eq_getElem?_getD, List.getElem?_map, List.getElem?_range hj',71 Option.map_some, Option.getD_some]72 cases sel j <;> cases xor (z j) ng <;> simp73 rw [List.map_congr_left e, sum_range_pos]74 constructor75 · rintro ⟨j, hj, h⟩; refine ⟨j, hj, ?_⟩; split at h <;> simp_all76 · rintro ⟨j, hj, h⟩; exact ⟨j, hj, by simp [h]⟩7778theorem constGate_eval (b : Bool) (z : Nat → Bool) : (constGate b).eval z = b := by79 cases b <;> simp [constGate, Gate.eval, Gate.wsum, Gate.psum, Gate.fanin]8081theorem orGate_valid (K : Nat) (sel : Nat → Bool) (ng : Bool) : Gate.Valid 1 (orGate K sel ng) := by82 refine ⟨by simp [orGate], ?_⟩83 intro a ha84 simp only [orGate, List.mem_map, List.mem_range] at ha85 obtain ⟨j, _, rfl⟩ := ha86 split <;> omega8788theorem constGate_valid (b : Bool) : Gate.Valid 1 (constGate b) := by89 refine ⟨by cases b <;> simp [constGate], by simp [constGate]⟩9091theorem orGate_fanin (K : Nat) (sel : Nat → Bool) (ng : Bool) : (orGate K sel ng).fanin = K := by92 simp [orGate, Gate.fanin]9394theorem constGate_fanin (b : Bool) : (constGate b).fanin = 0 := rfl9596/-! ## Levels of program gates -/9798/-- Layer at which a program gate is available: 1 for inputs/constants, +1 for NOT99 and OR, +2 for AND (De Morgan). -/100def lvlAlg : Alg Nat := ⟨0, fun _ => 1, fun _ => 1, (· + 1), fun l => listMax l + 2, fun l => listMax l + 1⟩101102mutual103def lvl : Circ → Nat104 | .input _ => 1105 | .const _ => 1106 | .not c => lvl c + 1107 | .and cs => lvlL cs + 2108 | .or cs => lvlL cs + 1109def lvlL : List Circ → Nat110 | [] => 0111 | c :: cs => max (lvl c) (lvlL cs)112end113114theorem lvlL_eq : ∀ cs : List Circ, lvlL cs = listMax (cs.map lvl)115 | [] => rfl116 | c :: cs => by simp only [lvlL, List.map_cons, listMax, List.foldr_cons]; rw [lvlL_eq cs]; rfl117118theorem lvl_fhom : FHom lvlAlg lvl :=119 ⟨fun _ => rfl, fun _ => rfl, fun _ => rfl, fun cs => by simp [lvl, lvlAlg, lvlL_eq],120 fun cs => by simp [lvl, lvlAlg, lvlL_eq]⟩121122mutual123theorem lvl_le : ∀ c : Circ, lvl c ≤ 2 * depth c + 1124 | .input _ => by simp [lvl]125 | .const _ => by simp [lvl]126 | .not c => by have := lvl_le c; simp [lvl]; omega127 | .and cs => by have := lvlL_le cs; simp only [lvl, depth_and]; omega128 | .or cs => by have := lvlL_le cs; simp only [lvl, depth_or]; omega129theorem lvlL_le : ∀ cs : List Circ, lvlL cs ≤ 2 * listMax (cs.map depth) + 1130 | [] => by simp [lvlL]131 | c :: cs => by132 have h1 := lvl_le c133 have h2 := lvlL_le cs134 simp only [lvlL, List.map_cons, listMax, List.foldr_cons] at h2 ⊢135 omega136end137138/-! ## Generic facts on sequential evaluation -/139140section RunFacts141variable {α : Type}142143theorem run_take (A : Alg α) (C : List DGate) (g : Nat) (hg : g ≤ C.length) :144 (run A [] C).take g = run A [] (C.take g) := by145 have e : C = C.take g ++ C.drop g := (List.take_append_drop g C).symm146 conv => lhs; rw [e]147 rw [run_append]148 obtain ⟨W, hW, _⟩ := run_prefix A (run A [] (C.take g)) (C.drop g)149 rw [hW]150 have hl : (run A [] (C.take g)).length = g := by rw [run_length]; simp; omega151 rw [List.take_append_of_le_length (by omega), List.take_of_length_le (by omega)]152153theorem run_getD (A : Alg α) (C : List DGate) (g : Nat) (hg : g < C.length) :154 (run A [] C).getD g A.dflt = gstep A ((run A [] C).take g) C[g] := by155 have e : C = C.take (g + 1) ++ C.drop (g + 1) := (List.take_append_drop (g + 1) C).symm156 have et : C.take (g + 1) = C.take g ++ [C[g]] := by157 rw [List.take_add_one, List.getElem?_eq_getElem hg]; rfl158 have hlen : (run A [] (C.take g)).length = g := by rw [run_length]; simp; omega159 have h1 : (run A [] C).getD g A.dflt = (run A [] (C.take (g + 1))).getD g A.dflt := by160 conv => lhs; rw [e]161 rw [run_append]162 obtain ⟨W, hW, _⟩ := run_prefix A (run A [] (C.take (g + 1))) (C.drop (g + 1))163 rw [hW]164 have hl2 : (run A [] (C.take (g + 1))).length = g + 1 := by rw [run_length]; simp; omega165 exact rd_append_left A _ W (by omega)166 rw [h1, et, run_append]167 simp only [run]168 rw [run_take A C g (by omega)]169 have := rd_snoc A (run A [] (C.take g)) (gstep A (run A [] (C.take g)) C[g])170 rw [hlen] at this171 exact this172173theorem rd_take (A : Alg α) (l : List α) (g a : Nat) (hg : g ≤ l.length) :174 rd A (l.take g) a = if a < g then rd A l a else A.dflt := by175 unfold rd176 simp only [List.getD_eq_getElem?_getD, List.getElem?_take]177 by_cases h : a < g <;> simp [h]178179end RunFacts180181/-! ## The layered network for a gate program -/182183section Prog184variable (prog : List DGate)185186/-- Value of program gate g on input x (false out of range). -/187def vl (x : Nat → Bool) (g : Nat) : Bool := (run (valAlg x) [] prog).getD g false188189/-- Level of program gate g. -/190def lv (g : Nat) : Nat := (run lvlAlg [] prog).getD g 0191192/-- Layer-1 gate for program gate g (reads only input wires). -/193def gate1 (g : Nat) : DGate → Gate194 | .inp i => orGate (i + 1) (fun j => j == i) false195 | .cst b => constGate b196 | .neg a => if a < g then constGate false else constGate true197 | .disj _ => constGate false198 | .conj as => if as.any (fun a => decide (a < g)) then constGate false199 else if as.isEmpty then constGate false else constGate true200201/-- Gate for program gate g at layer t ≥ 2 (reads the S wires of layer t − 1). -/202def gateT (t g : Nat) (d : DGate) : Gate :=203 if lv prog g < t then orGate prog.length (fun j => j == g) false204 else if lv prog g = t then205 match d with206 | .inp _ => constGate false207 | .cst b => constGate b208 | .neg a => if a < g then orGate prog.length (fun j => j == a) true else constGate true209 | .disj as => orGate prog.length (fun j => decide (j ∈ as ∧ j < g)) false210 | .conj _ => orGate prog.length (fun j => j == g) true211 else if lv prog g = t + 1 then212 match d with213 | .conj as => if as.any (fun a => !decide (a < g)) then constGate true214 else orGate prog.length (fun j => decide (j ∈ as)) true215 | _ => constGate false216 else constGate false217218def layerAt (t : Nat) : Layer :=219 if t ≤ 1 then (List.range prog.length).map (fun g => gate1 g (prog.getD g (.cst false)))220 else (List.range prog.length).map (fun g => gateT prog t g (prog.getD g (.cst false)))221222/-- Wire values after t layers. -/223def ys (x : Nat → Bool) : Nat → (Nat → Bool)224 | 0 => x225 | t + 1 => evalLayer (layerAt prog (t + 1)) (ys x t)226227end Prog228229section ProgFacts230variable {prog : List DGate}231232theorem lvlAlg_pos : ∀ v ∈ run lvlAlg [] prog, 1 ≤ v :=233 run_inv lvlAlg (fun v => 1 ≤ v) (fun _ => True)234 (fun acc g _ _ => by cases g <;> simp [gstep, lvlAlg]) prog [] (fun _ _ => trivial) (by simp)235236theorem lv_pos {g : Nat} (hg : g < prog.length) : 1 ≤ lv prog g := by237 unfold lv238 have hl : g < (run lvlAlg [] prog).length := by rw [run_length]; simp; omega239 rw [List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hl]240 exact lvlAlg_pos _ (List.getElem_mem hl)241242theorem getD_eq_getElem' (hg : g < prog.length) : prog.getD g (.cst false) = prog[g] := by243 rw [List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hg]; rfl244245/-- Local value equation. -/246theorem vl_eq (x : Nat → Bool) {g : Nat} (hg : g < prog.length) :247 vl prog x g = gstep (valAlg x) ((run (valAlg x) [] prog).take g) prog[g] :=248 run_getD (valAlg x) prog g hg249250theorem lv_eq {g : Nat} (hg : g < prog.length) :251 lv prog g = gstep lvlAlg ((run lvlAlg [] prog).take g) prog[g] :=252 run_getD lvlAlg prog g hg253254theorem rdv_take (x : Nat → Bool) {g a : Nat} (hg : g < prog.length) :255 rd (valAlg x) ((run (valAlg x) [] prog).take g) a = if a < g then vl prog x a else false := by256 rw [rd_take _ _ _ _ (by rw [run_length]; simp; omega)]; rfl257258theorem rdl_take {g a : Nat} (hg : g < prog.length) :259 rd lvlAlg ((run lvlAlg [] prog).take g) a = if a < g then lv prog a else 0 := by260 rw [rd_take _ _ _ _ (by rw [run_length]; simp; omega)]; rfl261262theorem evalLayer_at {l : Layer} {z : Nat → Bool} {g : Nat} (f : Nat → Gate) (K : Nat) (hl : l = (List.range K).map f)263 (hg : g < K) : evalLayer l z g = (f g).eval z := by264 subst hl; simp [evalLayer, List.getElem?_range hg]265266theorem listMax_ge {l : List Nat} {a : Nat} (h : a ∈ l) : a ≤ listMax l := le_listMax h267268theorem vl_inp (x : Nat → Bool) {g i : Nat} (hg : g < prog.length) (hd : prog[g] = .inp i) :269 vl prog x g = x i := by rw [vl_eq x hg, hd]; rfl270271theorem vl_cst (x : Nat → Bool) {g : Nat} {b : Bool} (hg : g < prog.length) (hd : prog[g] = .cst b) :272 vl prog x g = b := by rw [vl_eq x hg, hd]; rfl273274theorem vl_neg (x : Nat → Bool) {g a : Nat} (hg : g < prog.length) (hd : prog[g] = .neg a) :275 vl prog x g = !(if a < g then vl prog x a else false) := by276 rw [vl_eq x hg, hd]; simp only [gstep]; rw [rdv_take x hg]; rfl277278theorem vl_disj (x : Nat → Bool) {g : Nat} {as : List Nat} (hg : g < prog.length) (hd : prog[g] = .disj as) :279 vl prog x g = as.any (fun a => if a < g then vl prog x a else false) := by280 rw [vl_eq x hg, hd]; simp only [gstep]281 rw [List.map_congr_left (fun a _ => rdv_take (a := a) x hg)]282 simp [valAlg, List.any_map, Function.comp_def]283284theorem vl_conj (x : Nat → Bool) {g : Nat} {as : List Nat} (hg : g < prog.length) (hd : prog[g] = .conj as) :285 vl prog x g = as.all (fun a => if a < g then vl prog x a else false) := by286 rw [vl_eq x hg, hd]; simp only [gstep]287 rw [List.map_congr_left (fun a _ => rdv_take (a := a) x hg)]288 simp [valAlg, List.all_map, Function.comp_def]289290theorem lv_inp {g i : Nat} (hg : g < prog.length) (hd : prog[g] = .inp i) : lv prog g = 1 := by291 rw [lv_eq hg, hd]; rfl292293theorem lv_cst {g : Nat} {b : Bool} (hg : g < prog.length) (hd : prog[g] = .cst b) : lv prog g = 1 := by294 rw [lv_eq hg, hd]; rfl295296theorem lv_neg {g a : Nat} (hg : g < prog.length) (hd : prog[g] = .neg a) :297 lv prog g = (if a < g then lv prog a else 0) + 1 := by298 rw [lv_eq hg, hd]; simp only [gstep]; rw [rdl_take hg]; rfl299300theorem lv_disj {g : Nat} {as : List Nat} (hg : g < prog.length) (hd : prog[g] = .disj as) :301 lv prog g = listMax (as.map (fun a => if a < g then lv prog a else 0)) + 1 := by302 rw [lv_eq hg, hd]; simp only [gstep]303 rw [List.map_congr_left (fun a _ => rdl_take (a := a) hg)]; rfl304305theorem lv_conj {g : Nat} {as : List Nat} (hg : g < prog.length) (hd : prog[g] = .conj as) :306 lv prog g = listMax (as.map (fun a => if a < g then lv prog a else 0)) + 2 := by307 rw [lv_eq hg, hd]; simp only [gstep]308 rw [List.map_congr_left (fun a _ => rdl_take (a := a) hg)]; rfl309310theorem child_lv {g a : Nat} {as : List Nat} (ha : a ∈ as) (hag : a < g) :311 lv prog a ≤ listMax (as.map (fun a => if a < g then lv prog a else 0)) := by312 apply le_listMax; rw [List.mem_map]; exact ⟨a, ha, by rw [if_pos hag]⟩313314theorem decide_or1 {K i : Nat} (hi : i < K) (z : Nat → Bool) (ng : Bool) :315 decide (∃ j < K, (j == i) = true ∧ xor (z j) ng = true) = xor (z i) ng := by316 apply Bool.eq_iff_iff.mpr317 simp only [decide_eq_true_iff]318 constructor319 · rintro ⟨j, _, hj, h⟩; simp at hj; subst hj; exact h320 · intro h; exact ⟨i, hi, by simp, h⟩321322/-- THE LAYER INVARIANT: after t ≥ 1 layers, wire g carries the value of program gate g323 as soon as t ≥ lvl(g); an AND gate carries its complement one layer earlier. -/324theorem ys_spec (x : Nat → Bool) : ∀ t, 1 ≤ t → ∀ g (hg : g < prog.length),325 (lv prog g ≤ t → ys prog x t g = vl prog x g) ∧326 (∀ as, prog[g] = .conj as → lv prog g = t + 1 → ys prog x t g = !vl prog x g)327 | 0, h, _, _ => absurd h (by omega)328 | 1, _, g, hg => by329 have hy : ys prog x 1 g = (gate1 g prog[g]).eval x := by330 simp only [ys]331 rw [evalLayer_at (fun g => gate1 g (prog.getD g (.cst false))) prog.length332 (by simp [layerAt]) hg, getD_eq_getElem' hg]333 rw [hy]334 cases hd : prog[g] with335 | inp i =>336 refine ⟨fun _ => ?_, fun as h => by cases h⟩337 rw [vl_inp x hg hd]; simp only [gate1, orGate_eval]338 rw [decide_or1 (by omega : i < i + 1)]; simp339 | cst b =>340 refine ⟨fun _ => ?_, fun as h => by cases h⟩341 rw [vl_cst x hg hd]; simp [gate1, constGate_eval]342 | neg a =>343 refine ⟨fun hle => ?_, fun as h => by cases h⟩344 have hl := lv_neg hg hd345 by_cases ha : a < g346 · exfalso; rw [if_pos ha] at hl347 have := lv_pos (prog := prog) (show a < prog.length by omega); omega348 · rw [vl_neg x hg hd]; simp [gate1, ha, constGate_eval]349 | disj as =>350 refine ⟨fun hle => ?_, fun as' h => by cases h⟩351 have hl := lv_disj hg hd352 rw [vl_disj x hg hd]353 simp only [gate1, constGate_eval]354 symm355 apply List.any_eq_false.mpr356 intro a ha357 by_cases hag : a < g358 · exfalso359 have h1 := child_lv (prog := prog) ha hag360 have := lv_pos (prog := prog) (show a < prog.length by omega); omega361 · simp [hag]362 | conj as =>363 have hl := lv_conj hg hd364 refine ⟨fun hle => by omega, fun as' h hlv => ?_⟩365 cases h366 have hnone : ∀ a ∈ as, ¬ a < g := by367 intro a ha hag368 have h1 := child_lv (prog := prog) ha hag369 have := lv_pos (prog := prog) (show a < prog.length by omega); omega370 have hany : as.any (fun a => decide (a < g)) = false := by371 apply List.any_eq_false.mpr; intro a ha; simpa using hnone a ha372 rw [vl_conj x hg hd]373 simp only [gate1, hany, Bool.false_eq_true, if_false]374 cases as with375 | nil => simp [constGate_eval]376 | cons a as =>377 simp only [List.isEmpty_cons, Bool.false_eq_true, if_false, constGate_eval]378 have : (a :: as).all (fun a => if a < g then vl prog x a else false) = false := by379 apply List.all_eq_false.mpr380 exact ⟨a, by simp, by simp [hnone a (by simp)]⟩381 rw [this]; rfl382 | t + 2, _, g, hg => by383 have IH := fun g hg => ys_spec x (t + 1) (by omega) g hg384 have hy : ys prog x (t + 2) g = (gateT prog (t + 2) g prog[g]).eval (ys prog x (t + 1)) := by385 simp only [ys]386 rw [evalLayer_at (fun g => gateT prog (t + 2) g (prog.getD g (.cst false))) prog.length387 (by simp [layerAt]) hg, getD_eq_getElem' hg]388 rw [hy]389 constructor390 · intro hle391 by_cases hlt : lv prog g < t + 2392 · -- buffer393 unfold gateT; rw [if_pos hlt, orGate_eval, decide_or1 hg, (IH g hg).1 (by omega)]; simp394 · have heq : lv prog g = t + 2 := by omega395 unfold gateT; rw [if_neg hlt, if_pos heq]396 cases hd : prog[g] with397 | inp i => exfalso; have := lv_inp hg hd; omega398 | cst b => exfalso; have := lv_cst hg hd; omega399 | neg a =>400 have hl := lv_neg hg hd401 by_cases ha : a < g402 · rw [if_pos ha] at hl403 simp only [ha, if_true, orGate_eval]404 rw [vl_neg x hg hd, if_pos ha, decide_or1 (show a < prog.length by omega),405 (IH a (by omega)).1 (by omega)]406 cases vl prog x a <;> rfl407 · exfalso; rw [if_neg ha] at hl; omega408 | disj as =>409 have hl := lv_disj hg hd410 simp only [orGate_eval]411 rw [vl_disj x hg hd]412 have hch : ∀ a ∈ as, a < g → lv prog a ≤ t + 1 := by413 intro a ha hag; have := child_lv (prog := prog) ha hag; omega414 apply Bool.eq_iff_iff.mpr415 simp only [decide_eq_true_iff, List.any_eq_true]416 constructor417 · rintro ⟨j, hj, hsel, h⟩418 try simp only [decide_eq_true_iff] at hsel419 refine ⟨j, hsel.1, ?_⟩420 rw [if_pos hsel.2, ← (IH j hj).1 (hch j hsel.1 hsel.2)]; simpa using h421 · rintro ⟨a, ha, h⟩422 by_cases hag : a < g423 · rw [if_pos hag] at h424 refine ⟨a, by omega, by simp [ha, hag], ?_⟩425 rw [(IH a (by omega)).1 (hch a ha hag)]; simpa using h426 · rw [if_neg hag] at h; cases h427 | conj as =>428 simp only [orGate_eval]429 rw [decide_or1 hg, (IH g hg).2 as hd (by omega)]430 cases vl prog x g <;> rfl431 · intro as hd hlv432 have hl := lv_conj hg hd433 unfold gateT434 rw [if_neg (show ¬ lv prog g < t + 2 by omega), if_neg (show ¬ lv prog g = t + 2 by omega),435 if_pos hlv, hd]436 simp only437 rw [vl_conj x hg hd]438 by_cases hout : as.any (fun a => !decide (a < g)) = true439 · simp only [hout, if_true, constGate_eval]440 obtain ⟨a, ha, hna⟩ := List.any_eq_true.mp hout441 simp only [Bool.not_eq_true', decide_eq_false_iff_not] at hna442 have : as.all (fun a => if a < g then vl prog x a else false) = false := by443 apply List.all_eq_false.mpr444 exact ⟨a, ha, by simp [hna]⟩445 rw [this]; rfl446 · simp only [Bool.not_eq_true] at hout447 simp only [hout, Bool.false_eq_true, if_false, orGate_eval]448 have hin : ∀ a ∈ as, a < g := by449 intro a ha450 have := List.any_eq_false.mp hout a ha451 simpa using this452 have hch : ∀ a ∈ as, lv prog a ≤ t + 1 := by453 intro a ha; have := child_lv (prog := prog) ha (hin a ha); omega454 apply Bool.eq_iff_iff.mpr455 simp only [decide_eq_true_iff, Bool.not_eq_true', List.all_eq_false]456 constructor457 · rintro ⟨j, hj, hsel, h⟩458 try simp only [decide_eq_true_iff] at hsel459 refine ⟨j, hsel, ?_⟩460 rw [if_pos (hin j hsel), ← (IH j hj).1 (hch j hsel)]; simpa using h461 · rintro ⟨a, ha, h⟩462 rw [if_pos (hin a ha)] at h463 refine ⟨a, by have := hin a ha; omega, by simp [ha], ?_⟩464 rw [(IH a (by have := hin a ha; omega)).1 (hch a ha)]; simpa using h465466end ProgFacts467468/-! ## Compiling a list of formulas -/469470/-- The shared program and output gates of a formula list. -/471def progOf (cs : List Circ) : List DGate := (emitL 0 cs).1472def outsOf (cs : List Circ) : List Nat := (emitL 0 cs).2473474/-- Depth bound used for the layer count. -/475def dmax (cs : List Circ) : Nat := listMax (cs.map depth)476477/-- Number of computing layers: 2·D + 1. -/478def nLayers (cs : List Circ) : Nat := 2 * dmax cs + 1479480/-- THE COMPILED NETWORK for formulas cs on n input wires: 2·D + 1 computing layers of481 width S, then one output layer of width K = cs.length. -/482def compileF (cs : List Circ) : List Layer :=483 (List.range (nLayers cs)).map (fun t => layerAt (progOf cs) (t + 1)) ++484 [(outsOf cs).map (fun o => orGate (progOf cs).length (fun j => j == o) false)]485486theorem evalNet_append (ls : List Layer) (l : Layer) (z : Nat → Bool) :487 evalNet (ls ++ [l]) z = evalLayer l (evalNet ls z) := by488 induction ls generalizing z with489 | nil => rfl490 | cons l' ls ih => simp only [List.cons_append, evalNet]; exact ih _491492theorem evalNet_layers (prog : List DGate) (x : Nat → Bool) : ∀ L,493 evalNet ((List.range L).map (fun t => layerAt prog (t + 1))) x = ys prog x L494 | 0 => rfl495 | L + 1 => by rw [List.range_succ, List.map_append, List.map_cons, List.map_nil, evalNet_append,496 evalNet_layers prog x L]; rfl497498theorem outs_range : ∀ (cs : List Circ) (base : Nat),499 ∀ a ∈ (emitL base cs).2, base ≤ a ∧ a < base + (emitL base cs).1.length500 | [], _ => by simp [emitL]501 | c :: cs, base => by502 intro a ha503 have hpos := size_pos' c504 have hlc := emit_len base c505 simp only [emitL, List.mem_cons] at ha506 have hlen : (emitL base (c :: cs)).1.length =507 (emit base c).length + (emitL (base + (emit base c).length) cs).1.length := by simp [emitL]508 rw [hlen]509 rcases ha with rfl | ha510 · omega511 · have := outs_range cs (base + (emit base c).length) a ha; omega512513theorem outs_length (cs : List Circ) : (outsOf cs).length = cs.length := (emitL_len 0 cs).2514515theorem prog_length (cs : List Circ) : (progOf cs).length = (cs.map size).sum := (emitL_len 0 cs).1516517theorem valAlg_fhom (x : Nat → Bool) : FHom (valAlg x) (eval x) :=518 ⟨fun _ => by simp [valAlg], fun _ => by simp [valAlg], fun _ => by simp [valAlg],519 fun _ => by simp [valAlg, List.all_map], fun _ => by simp [valAlg, List.any_map]⟩520521theorem outs_val (cs : List Circ) (x : Nat → Bool) {k : Nat} (hk : k < cs.length) :522 vl (progOf cs) x ((outsOf cs)[k]'(by rw [outs_length]; exact hk)) = eval x cs[k] := by523 have h := emitL_val (valAlg x) (eval x) (valAlg_fhom x) 0 cs [] rfl524 have h2 := congrArg (fun l => l[k]?) h525 simp only [List.getElem?_map] at h2526 rw [List.getElem?_eq_getElem (by rw [(emitL_len 0 cs).2]; exact hk), List.getElem?_eq_getElem hk] at h2527 simp only [Option.map_some, Option.some.injEq] at h2528 exact h2529530theorem outs_lv (cs : List Circ) {k : Nat} (hk : k < cs.length) :531 lv (progOf cs) ((outsOf cs)[k]'(by rw [outs_length]; exact hk)) ≤ nLayers cs := by532 have h := emitL_val lvlAlg lvl lvl_fhom 0 cs [] rfl533 have h2 := congrArg (fun l => l[k]?) h534 simp only [List.getElem?_map] at h2535 rw [List.getElem?_eq_getElem (by rw [(emitL_len 0 cs).2]; exact hk), List.getElem?_eq_getElem hk] at h2536 simp only [Option.map_some, Option.some.injEq] at h2537 unfold lv538 have e : (run lvlAlg [] (progOf cs)).getD ((outsOf cs)[k]'(by rw [outs_length]; exact hk)) 0 =539 lvl cs[k] := h2540 rw [e]541 have h3 := lvl_le cs[k]542 have h4 : depth cs[k] ≤ dmax cs := le_listMax (List.mem_map_of_mem (List.getElem_mem hk))543 unfold nLayers; omega544545/-- OUTPUT CORRECTNESS: output k of the compiled network is formula k, on every input. -/546theorem compileF_eval (cs : List Circ) (x : Nat → Bool) {k : Nat} (hk : k < cs.length) :547 evalNet (compileF cs) x k = eval x cs[k] := by548 unfold compileF549 rw [evalNet_append, evalNet_layers]550 have hk' : k < (outsOf cs).length := by rw [outs_length]; exact hk551 rw [evalLayer_at (fun k => orGate (progOf cs).length (fun j => j == (outsOf cs).getD k 0) false)552 (outsOf cs).length (by553 apply List.ext_getElem (by simp)554 intro n h1 h2; simp [List.getD_eq_getElem?_getD, List.getElem?_eq_getElem (by simpa using h1)]) hk']555 simp only [orGate_eval]556 have ho : (outsOf cs).getD k 0 = (outsOf cs)[k] := by557 rw [List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hk']; rfl558 have hor := outs_range cs 0 (outsOf cs)[k] (List.getElem_mem hk')559 have hS : (outsOf cs)[k] < (progOf cs).length := by simpa [progOf] using hor.2560 have hspec := (ys_spec (prog := progOf cs) x (nLayers cs) (by unfold nLayers; omega) _ hS).1 (outs_lv cs hk)561 rw [ho]562 apply Bool.eq_iff_iff.mpr563 simp only [decide_eq_true_iff]564 constructor565 · rintro ⟨j, _, hj, h⟩; simp at hj; subst hj; rw [hspec, outs_val cs x hk] at h; simpa using h566 · intro h; exact ⟨_, hS, by simp, by rw [hspec, outs_val cs x hk]; simpa using h⟩567568/-- Outputs beyond K are false. -/569theorem compileF_eval_out (cs : List Circ) (x : Nat → Bool) {k : Nat} (hk : cs.length ≤ k) :570 evalNet (compileF cs) x k = false := by571 unfold compileF572 rw [evalNet_append]573 simp [evalLayer, List.getElem?_eq_none (show (outsOf cs).length ≤ k by rw [outs_length]; exact hk)]574575/-! ## Resources of the compiled network -/576577theorem compileF_valid (cs : List Circ) : NetValid 1 (compileF cs) := by578 intro l hl g hg579 simp only [compileF, List.mem_append, List.mem_map, List.mem_range, List.mem_singleton] at hl580 rcases hl with ⟨t, _, rfl⟩ | rfl581 · simp only [layerAt] at hg582 split at hg583 · simp only [List.mem_map, List.mem_range] at hg584 obtain ⟨g', _, rfl⟩ := hg585 generalize (progOf cs).getD g' (.cst false) = d586 cases d <;> simp only [gate1] <;> (try split) <;> (try split) <;>587 first | exact orGate_valid _ _ _ | exact constGate_valid _588 · simp only [List.mem_map, List.mem_range] at hg589 obtain ⟨g', _, rfl⟩ := hg590 generalize (progOf cs).getD g' (.cst false) = d591 unfold gateT592 split593 · exact orGate_valid _ _ _594 · split595 · cases d <;> simp only <;> (try split) <;> first | exact orGate_valid _ _ _ | exact constGate_valid _596 · split597 · cases d <;> simp only <;> (try split) <;> first | exact orGate_valid _ _ _ | exact constGate_valid _598 · exact constGate_valid _599 · simp only [List.mem_map] at hg600 obtain ⟨o, _, rfl⟩ := hg601 exact orGate_valid _ _ _602603/-- WIRED: if every formula reads only inputs below n, every first-layer gate has604 fan-in ≤ n. -/605theorem compileF_wired (cs : List Circ) (n : Nat) (hn : ∀ c ∈ cs, maxInput c ≤ n) :606 Wired n (compileF cs) := by607 have hlay : nLayers cs = (nLayers cs - 1) + 1 := by unfold nLayers; omega608 unfold compileF609 rw [hlay, List.range_succ_eq_map, List.map_cons, List.cons_append]610 intro g hg611 simp only [show (0 : Nat) + 1 ≤ 1 by omega, layerAt, if_true, List.mem_map, List.mem_range] at hg612 obtain ⟨g', hg', rfl⟩ := hg613 have hmem : (progOf cs).getD g' (.cst false) ∈ progOf cs := by614 rw [getD_eq_getElem' hg']; exact List.getElem_mem hg'615 have hr := emitL_reads 0 cs _ hmem616 have hmx : listMax (cs.map maxInput) ≤ n := listMax_le (fun a ha => by617 obtain ⟨c, hc, rfl⟩ := List.mem_map.mp ha; exact hn c hc)618 generalize (progOf cs).getD g' (.cst false) = d at hr619 cases d with620 | inp i => simp only [gate1, orGate_fanin]; simp [gmax] at hr; omega621 | cst b => simp [gate1, constGate_fanin]622 | neg a => simp only [gate1]; split <;> simp [constGate_fanin]623 | disj as => simp [gate1, constGate_fanin]624 | conj as => simp only [gate1]; split <;> (try split) <;> simp [constGate_fanin]625626theorem compileF_length (cs : List Circ) : (compileF cs).length = 2 * dmax cs + 2 := by627 simp [compileF, nLayers]628629theorem cs_le_prog (cs : List Circ) : cs.length ≤ (progOf cs).length := by630 rw [prog_length]631 have h : ∀ c ∈ cs, 1 ≤ size c := fun c _ => size_pos' c632 induction cs with633 | nil => simp634 | cons c cs ih =>635 simp only [List.length_cons, List.map_cons, List.sum_cons]636 have := ih (fun c' hc' => h c' (by simp [hc']))637 have := h c (by simp); omega638639/-- BOUNDED WIDTH AND FAN-IN: every layer has ≤ S + n gates, every gate fan-in ≤ S + n,640 where S = Σ size(c_k) is the program size. -/641theorem compileF_bounded (cs : List Circ) (n : Nat) (hn : ∀ c ∈ cs, maxInput c ≤ n) :642 NetBounded ((cs.map size).sum + n) (compileF cs) := by643 have hmx : listMax (cs.map maxInput) ≤ n := listMax_le (fun a ha => by644 obtain ⟨c, hc, rfl⟩ := List.mem_map.mp ha; exact hn c hc)645 have hS := prog_length cs646 intro l hl647 simp only [compileF, List.mem_append, List.mem_map, List.mem_range, List.mem_singleton] at hl648 rcases hl with ⟨t, _, rfl⟩ | rfl649 · refine ⟨by simp only [layerAt]; split <;> simp [hS], ?_⟩650 intro g hg651 simp only [layerAt] at hg652 split at hg653 · simp only [List.mem_map, List.mem_range] at hg654 obtain ⟨g', hg', rfl⟩ := hg655 have hmem : (progOf cs).getD g' (.cst false) ∈ progOf cs := by656 rw [getD_eq_getElem' hg']; exact List.getElem_mem hg'657 have hr := emitL_reads 0 cs _ hmem658 generalize (progOf cs).getD g' (.cst false) = d at hr659 cases d with660 | inp i => simp only [gate1, orGate_fanin]; simp [gmax] at hr; omega661 | cst b => simp [gate1, constGate_fanin]662 | neg a => simp only [gate1]; split <;> simp [constGate_fanin]663 | disj as => simp [gate1, constGate_fanin]664 | conj as => simp only [gate1]; split <;> (try split) <;> simp [constGate_fanin]665 · simp only [List.mem_map, List.mem_range] at hg666 obtain ⟨g', _, rfl⟩ := hg667 generalize (progOf cs).getD g' (.cst false) = d668 unfold gateT669 split670 · rw [orGate_fanin]; omega671 · split672 · cases d <;> simp only <;> (try split) <;>673 first | (rw [orGate_fanin]; omega) | (rw [constGate_fanin]; omega)674 · split675 · cases d <;> simp only <;> (try split) <;>676 first | (rw [orGate_fanin]; omega) | (rw [constGate_fanin]; omega)677 · rw [constGate_fanin]; omega678 · refine ⟨by simp [outs_length]; have := cs_le_prog cs; omega, ?_⟩679 intro g hg680 simp only [List.mem_map] at hg681 obtain ⟨o, _, rfl⟩ := hg682 rw [orGate_fanin]; omega683684/-- Total number of gates: (2D + 1)·S computing gates and K output buffers. -/685def gateCount (ls : List Layer) : Nat := (ls.map List.length).sum686687theorem compileF_gates (cs : List Circ) :688 gateCount (compileF cs) = (2 * dmax cs + 1) * (cs.map size).sum + cs.length := by689 unfold gateCount compileF690 simp only [List.map_append, List.map_map, List.sum_append_nat, List.map_cons, List.map_nil, List.sum_cons,691 List.sum_nil, List.length_map, outs_length, Nat.add_zero]692 have e : ∀ t ∈ List.range (nLayers cs), (List.length ∘ fun t => layerAt (progOf cs) (t + 1)) t =693 (cs.map size).sum := by694 intro t _; simp only [Function.comp_apply, layerAt]; split <;> simp [prog_length]695 rw [List.map_congr_left e, List.map_const', List.sum_replicate_nat, List.length_range]696 unfold nLayers; rfl697698/-- THE COMPILER THEOREM (all properties of the one network `compileF cs`). -/699theorem compileF_spec (cs : List Circ) (n D : Nat) (hn : ∀ c ∈ cs, maxInput c ≤ n)700 (hD : ∀ c ∈ cs, depth c ≤ D) :701 (∀ x : Nat → Bool, ∀ k (hk : k < cs.length), evalNet (compileF cs) x k = eval x cs[k]) ∧702 (∀ x : Nat → Bool, ∀ k, cs.length ≤ k → evalNet (compileF cs) x k = false) ∧703 NetValid 1 (compileF cs) ∧ Wired n (compileF cs) ∧704 (compileF cs).length ≤ 2 * D + 2 ∧705 NetBounded ((cs.map size).sum + n) (compileF cs) ∧706 gateCount (compileF cs) ≤ (2 * D + 1) * (cs.map size).sum + cs.length := by707 have hd : dmax cs ≤ D := listMax_le (fun a ha => by obtain ⟨c, hc, rfl⟩ := List.mem_map.mp ha; exact hD c hc)708 refine ⟨fun x k hk => compileF_eval cs x hk, fun x k hk => compileF_eval_out cs x hk, compileF_valid cs,709 compileF_wired cs n hn, by rw [compileF_length]; omega, compileF_bounded cs n hn, ?_⟩710 rw [compileF_gates]711 have : (2 * dmax cs + 1) * (cs.map size).sum ≤ (2 * D + 1) * (cs.map size).sum :=712 Nat.mul_le_mul_right _ (by omega)713 omega714715/-- FIXED GRID: every gate of a compiled network fires in the paper's rational form with716 denominator D = 1 exactly when it fires in integer form (weights in {0,1}, threshold717 in {0,1}). -/718theorem compileF_fixed_grid (cs : List Circ) :719 ∀ l ∈ compileF cs, ∀ g ∈ l, Gate.Valid 1 g ∧ ∀ z, (ratFires g 1 z ↔ g.eval z = true) :=720 fun l hl g hg => ⟨compileF_valid cs l hl g hg, fun z => gate_rat_iff g (by decide) z⟩721722723/-! ## Axiom audit -/724725#print axioms sum_range_pos726#print axioms orGate_eval727#print axioms constGate_eval728#print axioms orGate_valid729#print axioms constGate_valid730#print axioms orGate_fanin731#print axioms constGate_fanin732#print axioms lvlL_eq733#print axioms lvl_fhom734#print axioms lvl_le735#print axioms lvlL_le736#print axioms run_take737#print axioms run_getD738#print axioms rd_take739#print axioms lvlAlg_pos740#print axioms lv_pos741#print axioms getD_eq_getElem'742#print axioms vl_eq743#print axioms lv_eq744#print axioms rdv_take745#print axioms rdl_take746#print axioms evalLayer_at747#print axioms listMax_ge748#print axioms vl_inp749#print axioms vl_cst750#print axioms vl_neg751#print axioms vl_disj752#print axioms vl_conj753#print axioms lv_inp754#print axioms lv_cst755#print axioms lv_neg756#print axioms lv_disj757#print axioms lv_conj758#print axioms child_lv759#print axioms decide_or1760#print axioms ys_spec761#print axioms evalNet_append762#print axioms evalNet_layers763#print axioms outs_range764#print axioms outs_length765#print axioms prog_length766#print axioms valAlg_fhom767#print axioms outs_val768#print axioms outs_lv769#print axioms compileF_eval770#print axioms compileF_eval_out771#print axioms compileF_valid772#print axioms compileF_wired773#print axioms compileF_length774#print axioms cs_le_prog775#print axioms compileF_bounded776#print axioms compileF_gates777#print axioms compileF_spec778#print axioms compileF_fixed_grid779end ThresholdNet780end NimAudit